From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E979245019; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=AeGqyWfBaFzC5RKKRC+P46FkuBqZYoxp44dkEFh2o0B1XqveOpddZhiAfOZIjQY52TSvBFMoIzobyPqlucsZGqRo9UUWREGiNoG/WJGg3SfHzyAHD9bJGEpJMybeXp0WBsJAN9yiFTHOyQFJ60mXAHIt/zjg7GalrqLNcvK8bps= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=aeuqk5Ez6XBK1bcBiwdwu3D8K0LeqXVto+XLcOhJeWA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=G/c5dfBdbfA8o3iFlEcNujEkZHd3OgZZTNt8zEaKA2Hv742hvp5bwoxr4hO3PsjdcEkHrFptjQ7hxS/TPa4MBdn+5gJ/I9gYnoNCUu9RT6B7cAWesiEX7YZbYlI+prlgpO2Hhl3GWbrNFAIIalTguHRxpB2wK0cReECz1B4YwW4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BH7uhf5h; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BH7uhf5h" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C0513C4CED6; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=aeuqk5Ez6XBK1bcBiwdwu3D8K0LeqXVto+XLcOhJeWA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BH7uhf5hdCSjBbtEmHZxu97pqEwi708sPDnqlCChf7VeABCdQb5l8WqNHLCVAWVH2 nGOrmEVc4cwu5nEKQrr1c+sXSt9amTOFhqm3ZeWFCCAlS2TUoMRtwjHL79qe8/Jx53 qhdcWnJbrggcogk7KZTXZf/NGNisKGXs6Rm96hVdalJYKMyZ2lTYhvuFG5udczm/Ai mWLfesW+eiZsp2pymw/Vj8T2ZdSLDVyeUyZSaPeAPpYOGt50dGuzLXpYbzfTsTgcGY /YR4Rje51xJ75BY2FOEvjv4OhtJ26kMniBYXd35917ByAOWsgkkFFGvgaHiOWh4zlT CTiBei3J5b99A== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3U-3Nqo; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , Arnd Bergmann , linux-arch@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 01/39] include/asm-generic/io.h: fix kerneldoc markup Date: Mon, 24 Feb 2025 10:08:07 +0100 Message-ID: <066968c00196ed88f6dc97e3d317926fc4ab7d52.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Kerneldoc requires a "-" after the name of a function for it to be recognized as a function. Add it. Fix those kernel-doc warnings: include/asm-generic/io.h:1215: warning: This comment starts with '/**', but= isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * memset_io Set a range of I/O memory to a constant value include/asm-generic/io.h:1227: warning: This comment starts with '/**', but= isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * memcpy_fromio Copy a block of data from I/O memory include/asm-generic/io.h:1239: warning: This comment starts with '/**', but= isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * memcpy_toio Copy a block of data into I/O memory Signed-off-by: Mauro Carvalho Chehab Acked-by: Arnd Bergmann --- include/asm-generic/io.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index a5cbbf3e26ec..3c61c29ff6ab 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -1212,7 +1212,7 @@ static inline void unxlate_dev_mem_ptr(phys_addr_t ph= ys, void *addr) =20 #ifndef memset_io /** - * memset_io Set a range of I/O memory to a constant value + * memset_io - Set a range of I/O memory to a constant value * @addr: The beginning of the I/O-memory range to set * @val: The value to set the memory to * @count: The number of bytes to set @@ -1224,7 +1224,7 @@ void memset_io(volatile void __iomem *addr, int val, = size_t count); =20 #ifndef memcpy_fromio /** - * memcpy_fromio Copy a block of data from I/O memory + * memcpy_fromio - Copy a block of data from I/O memory * @dst: The (RAM) destination for the copy * @src: The (I/O memory) source for the data * @count: The number of bytes to copy @@ -1236,7 +1236,7 @@ void memcpy_fromio(void *dst, const volatile void __i= omem *src, size_t count); =20 #ifndef memcpy_toio /** - * memcpy_toio Copy a block of data into I/O memory + * memcpy_toio - Copy a block of data into I/O memory * @dst: The (I/O memory) destination for the copy * @src: The (RAM) source for the data * @count: The number of bytes to copy --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E760245014; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=T1+SzYiNWDbHRWwx2dtxN1T6/XAxaFHLvfwNj/xt0NNZWMzzMgvq82aVrB29Q3M3HPUOwcWPdIDwTHAmmLlp2MIPVu2CmWPcTUS3PdZxuxN1JH4YA+Rh41ldCOuSoXcFHaKI5y8oXWgR0L5ahLTEK8ST3L+HxLZyKjU7TI1zWsg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=dpp4qyIMyF9Zjj8hBit/kiPWhv8Tsh4yLBwdy/oLV7M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jRv5uvk0H6Vs/fT9G479FRsUFKKHxbz0vG0T+7nwE2k+KAkmCdz/Hx8VhIbro8waXarqotTE3SjVDnLHyDuhJQFpxQNZJAW8vNjrcDYrDBfPMk/8rDCxp1FQ09IOUlT2Jolq/5Rq29EN6w5B2e9rj7q5FF777LWXmIpm4TuN+Bg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=a/9u3+jR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="a/9u3+jR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BB1B1C4CEE6; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=dpp4qyIMyF9Zjj8hBit/kiPWhv8Tsh4yLBwdy/oLV7M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a/9u3+jRgg6iZd8RCVDU+IBEo+pwPqDyjRQVV2RdAGuGPfurluMpA+Y2BBJjlXrWG 7uOAuoV/LB/ccvKauo+xSZ9RLRIYFvUIySR+SdVqXHGJehljnHZ75cCTzzLEMOkgsN YddazHOJSy9OBTaBlBt6dDDjz/kiE1dCRbm8cLF8rBSWdby1F+U/6SzwsRKV1Ikpe/ 3QpPCpmnB2650/O9z7K2Gm7v/aVnLXk1W4Z9h0UFe3e6fGMdOkyMkUNMo4QMJERHbo uiOnqLOlo9FR1k/ceIZvs5ihY71TM/MRpvLpIa9LUu028gRG7K97RvDUWxSJzj90uN 7K4e7TDpZM9rg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3Y-3V3g; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , Bingbu Cao , Greg Kroah-Hartman , Sakari Ailus , Tianshu Qiu , linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-staging@lists.linux.dev Subject: [PATCH v2 02/39] drivers: media: intel-ipu3.h: fix identation on a kernel-doc markup Date: Mon, 24 Feb 2025 10:08:08 +0100 Message-ID: <96cd1068aa8ec0a072528082670ccb8aa3704f70.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The "Rule" description is part of y_calc parameter. Having a line starting at the beginning makes it part of the function description instead, which is not the original intent. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/include/uapi/intel-ipu3.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/ipu3/include/uapi/intel-ipu3.h b/drivers= /staging/media/ipu3/include/uapi/intel-ipu3.h index 4aa2797f5e3c..8b85524beb59 100644 --- a/drivers/staging/media/ipu3/include/uapi/intel-ipu3.h +++ b/drivers/staging/media/ipu3/include/uapi/intel-ipu3.h @@ -322,7 +322,8 @@ struct ipu3_uapi_ae_config { * 0: positive, 1: negative, default 0. * @y_calc: Pre-processing that converts Bayer quad to RGB+Y values to be * used for building histogram. Range [0, 32], default 8. - * Rule: + * + * Rule: * y_gen_rate_gr + y_gen_rate_r + y_gen_rate_b + y_gen_rate_gb =3D 32 * A single Y is calculated based on sum of Gr/R/B/Gb based on * their contribution ratio. --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8764924503B; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=IT8IM52mvbuGeMKLqzWQ1S3E/ycOGrkkb4OFkwHCg5SA4KQSNbwtR+l4MyWr48/DyLoGyH9BJVM+LF43af2itdudyKkFoH5MalWcjNr+gJQUpl69cSqSCvDXnYcEpDQsSUh4ljIbGbnEZe0Z1ukdE9oIgO2IevJ1IFZFcD3AbmA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=EuSV6D3szJ0VW+HjV2NwWusmhdj/JnS4ByP03OXscaU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LIxCofcEr8FMnMIjLnJMndBwuZdElDNiTcGyi+pokhQIbAv7ErONtKXKND6+773/jBVeGzlhzdaRQheCgcuBsd23iOZUtBCjxAcJ19mRjr38pU3HyH1fJqUM1bgTS3yRGJZTbP6Z7r06yuAp0k2fjuBKjHAk7IDGMJv7Cv2wh6Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bsOZrf5n; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bsOZrf5n" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D0054C4CEEE; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=EuSV6D3szJ0VW+HjV2NwWusmhdj/JnS4ByP03OXscaU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bsOZrf5nwEoDs4PAkYNZQTExuSUaayjHTC6H/oIgO/6PMkQD5GWDteUkRDrMDA7ti sLWAdADhYmAzDLUvKbKdfufuVJ4TgM1a5Gehkwg9GR9FxtQFa7KUJC/e+bNopQdPn/ RA7OxnKnDxGADrYA9n4ufY2b7SMXQ0ydroUcnNIM6TZIBccVkYeMyPdPQ4ZRX89lz3 8gtuKy1z4fsBt4Sx7be61sX8hTBECfUhKjW3fwLTwuJYhGInb1UYl8Szec9uyJFYB6 ll/scl0iG7LEDAgI0ScFz4Ye9s2p0uchmZzO6FQxwTec6WISiUmW+wASWCvZPU8P+k wFL37vt5In6TA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3c-3c3Y; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , Takashi Sakamoto , linux-kernel@vger.kernel.org, linux1394-devel@lists.sourceforge.net Subject: [PATCH v2 03/39] drivers: firewire: firewire-cdev.h: fix identation on a kernel-doc markup Date: Mon, 24 Feb 2025 10:08:09 +0100 Message-ID: <8238bed1c0375e6b389a8cafe1ad99fdeb1cb1f2.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The description of @tstamp parameter has one line that starts at the beginning. This moves such line to the description, which is not the intent here. Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/firewire-cdev.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/firewire-cdev.h b/include/uapi/linux/firewi= re-cdev.h index 1f2c9469f921..05e3aa8fa8bc 100644 --- a/include/uapi/linux/firewire-cdev.h +++ b/include/uapi/linux/firewire-cdev.h @@ -449,7 +449,8 @@ struct fw_cdev_event_phy_packet { * which the packet arrived. For %FW_CDEV_EVENT_PHY_PACKET_SENT2 and non-= ping packet, * the time stamp of isochronous cycle at which the packet was sent. For = ping packet, * the tick count for round-trip time measured by 1394 OHCI controller. - * The time stamp of isochronous cycle at which either the response was se= nt for + * + * The time stamp of isochronous cycle at which either the response was s= ent for * %FW_CDEV_EVENT_PHY_PACKET_SENT2 or the request arrived for * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2. * @data: Incoming data --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E854245016; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=n1P5vc3NzoN9hXXmg9hl11xK0LtirnDBhKFvInA+ncrut37o8Sa+LS9ur2CTElFqAx0Tb4qptAaaEyZCI959ip+yKoM2W92X3bL1mgSqpHvU/hODwzGnOYfyE91S0ajOLTsOsMpAw3DNXlards585LVD/8ZzV8lAlttyHuDyTIA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=UcUtD1ZVE/t/e49mIRjHjGIlObJeJfXBstuSw82d2G4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QasaxGPM5AYmI1aww5hJvvw3Rbzyze9ISDNw2hEs+ejOlL41vk8MYXDhvy5C5OArnMZNLhmqKnV8E0zh3KLXSOvNReh1o6aJnUeekEDYnpJZlRtlxCWZn1oxiHuUAYMXmRr9ivpH0ug2OAq1ItSjU1HpXzWkU5jMEIVs879MkQs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Y0fY0Vr7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Y0fY0Vr7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C97AEC4CEEA; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=UcUtD1ZVE/t/e49mIRjHjGIlObJeJfXBstuSw82d2G4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Y0fY0Vr7r35L49wncFdUWrtJI3o5U77qS87mf8aKhhwzES20EOSeFLzyypoE1q0ky TclNGUhMfdCpmnw0GI8G65red6UhcFs366zAlh9WKf8cSs6iG11n6kfgVnxmRqjvzA TDs0GTA+Rk74Ga27QDS5XfLfwUBPhLwtDjBFhHNyQ/UhLc9OR1vBlTsxyCX4fWOqjO SlhE1hJ/LiH+IeImj44mcYq2jy+ZOwDhaRozfeii+6FwsNomC5KNwHncllVZQ53s5x cV+XfYy5Kq0mCHijyYHwshrS0DUf+BZphGXbUjfKAIyTN57ZPnn0zibG4zRzN9qs+4 aS6VsY3MOUWvg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3g-3izA; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 04/39] docs: driver-api/infiniband.rst: fix Kerneldoc markup Date: Mon, 24 Feb 2025 10:08:10 +0100 Message-ID: <64e7949439c5fc39f2c9441027ad02bc60cbf0dd.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" kerneldoc.py extension doesn't handle a "\" character at the end of the line: it will just merge it to the cmd line, producing this command: scripts/kernel-doc -rst -enable-lineno -function iscsi_iser_pdu_alloc -fun= ction iser_initialize_task_headers -function \ -function iscsi_iser_task_in= it -function iscsi_iser_mtask_xmit -function iscsi_iser_task_xmit -function= \ -function iscsi_iser_cleanup_task -function iscsi_iser_check_protection = -function \ -function iscsi_iser_conn_create -function iscsi_iser_conn_bind= -function \ -function iscsi_iser_conn_start -function iscsi_iser_conn_stop= -function \ -function iscsi_iser_session_destroy -function iscsi_iser_sess= ion_create -function \ -function iscsi_iser_set_param -function iscsi_iser_= ep_connect -function iscsi_iser_ep_poll -function \ -function iscsi_iser_ep= _disconnect ./drivers/infiniband/ulp/iser/iscsi_iser.c which may not work as expected. Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/infiniband.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/driver-api/infiniband.rst b/Documentation/driver= -api/infiniband.rst index 30e142ccbee9..10d8be9e74fe 100644 --- a/Documentation/driver-api/infiniband.rst +++ b/Documentation/driver-api/infiniband.rst @@ -77,14 +77,14 @@ iSCSI Extensions for RDMA (iSER) :internal: =20 .. kernel-doc:: drivers/infiniband/ulp/iser/iscsi_iser.c - :functions: iscsi_iser_pdu_alloc iser_initialize_task_headers \ - iscsi_iser_task_init iscsi_iser_mtask_xmit iscsi_iser_task_xmit \ - iscsi_iser_cleanup_task iscsi_iser_check_protection \ - iscsi_iser_conn_create iscsi_iser_conn_bind \ - iscsi_iser_conn_start iscsi_iser_conn_stop \ - iscsi_iser_session_destroy iscsi_iser_session_create \ - iscsi_iser_set_param iscsi_iser_ep_connect iscsi_iser_ep_poll \ - iscsi_iser_ep_disconnect + :functions: iscsi_iser_pdu_alloc iser_initialize_task_headers + iscsi_iser_task_init iscsi_iser_mtask_xmit iscsi_iser_task_= xmit + iscsi_iser_cleanup_task iscsi_iser_check_protection + iscsi_iser_conn_create iscsi_iser_conn_bind + iscsi_iser_conn_start iscsi_iser_conn_stop + iscsi_iser_session_destroy iscsi_iser_session_create + iscsi_iser_set_param iscsi_iser_ep_connect iscsi_iser_ep_po= ll + iscsi_iser_ep_disconnect =20 .. kernel-doc:: drivers/infiniband/ulp/iser/iser_initiator.c :internal: --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E8E9245018; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=pCu80fOpjZNsnhBZDWuEVtwb0EQAtWQfLmDzs/zQlqyGKXVtXpB4thDBTRw42z0M8Omv+wSIm7iuCGf32URZlHugAA5L/G3lO65WTOwOiXNfbm9xaT+aYKc70Gz8gsv45er007OpEwkdcp0n76csYaiOxKEU5bRdenVhhmWMFlQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=yvf7sytYLZ4Yo2xKDIwz8bS24pWVxbZ/2mOY1uefT38=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vt+goMIrfvw306wYfbUoWNSmTcOYqedTDiGXpDw9XvbkeCNv3bektINDUPb7LcG6OFrpB3cQ+tjQ/faYyvhauUJwxj8ewb1O9Jnrphgga/HhsKwWjIlohuGsKIQpQtnvoWdOYJSZ1LFnZrPVFiFmaPEP6UvF3Zo/1YIg+OcY9UQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lU4H75v9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lU4H75v9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C7207C4CEEB; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=yvf7sytYLZ4Yo2xKDIwz8bS24pWVxbZ/2mOY1uefT38=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lU4H75v9JaktPHhJiCK+DSMdgiPZGmwv8v6gIUYcDgCClPS+aIks6D4DpvRNqbNwc JIMI+K8Mk7n04jJkl8kWmOPKtOlOFjjmM6vnuZqgTx0SkQUFYnCfaUl3+cLAUhK/rt N5m5RtyVyoxjCwi0NyQnezMZnS73SUsGZQY7dtG1nkx8om9aEh9MwEYTI83QAI+/kI mF7Q0GVWkklzlJgLfk6W4x5s6Sozmr0RN3DwWAvVwYh9T+ip8m3w8Eb5X1VPZqTyXf U4wt7O24xBREmxkcDmsFGNB1za7c0kKtjkokZGM2Sp/ITtoFH5YAaGIl3W/JOtv0pO ExNPnBmeERzVA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3k-3rKn; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 05/39] scripts/kernel-doc: don't add not needed new lines Date: Mon, 24 Feb 2025 10:08:11 +0100 Message-ID: <6b036ef7d746f26d7d0044626b04d1f0880a2188.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" This helps comparing kernel-doc output with the new .py version of it. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 2c77b914d017..d59552e1a31d 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -760,6 +760,10 @@ sub output_highlight_rst { if ($block) { $output .=3D highlight_block($block); } + + $output =3D~ s/^\n+//g; + $output =3D~ s/\n+$//g; + foreach $line (split "\n", $output) { print $lineprefix . $line . "\n"; } --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7506724502E; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=boj20LlHZvgHgSLhZRQCJ7uuwH+Nu+w36IEGSR745cvIPQMZpq7/BkV2jMvnRbERXUg6MF3DHFfAPaTOEsrOBM3E/O5pwYpAe+mqMdhJFg4Djc8JPQWjZvfN6k4oSne5/2rvAxqQOV1MgpWa61mCog9LOGuTekJlC2aS8bYfdVw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=o+1xc6xKU/lADwcvw9syISNuAVlcBEcy5nbY0N3w4kc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aDYWv8L8+fFp2sQc3vd1JabBrSpwKQYBJejEfIFFRQZCSUB7eO+ZWOgN8PR2jrMr/McWXGNeshJ4tB7HSPfQYhGyKFqvjWe0Fm5C5xq9QiK9V5tpfviX8MchiTirQ/cL8ZHZwybigaU/mE1odkbzfD9m+dwF9Ntr6QkqbXoIscw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=d2r7O0oW; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="d2r7O0oW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D2923C4CEED; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=o+1xc6xKU/lADwcvw9syISNuAVlcBEcy5nbY0N3w4kc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=d2r7O0oWeInb6b4ufMZt2RTSkkEqMALf9aH9Gp9F3HbTf5JWVMLj2QkMY79pAQ6wV kB47vnkGkxz7YnHbnhsH7XWEjqsC3+lDdGkUjHBYfiII21hbDJb2HroiiSjO3JXI6i xYtl3V1pRllhgwOAlFPMtbrkTxo4Hn2dWP6AP6v9IaK4Gr7o7H8ljE8WtdL1RYzwXZ aKAwXDZl+7VmkPBV0a1tsLqbQSpq9QWisaKcOf077diMwVFW/ypk416GKsN3g8+HS8 F6y0yLOmm7YTbh3qO/hXLB6AG/TG0b7Ct5A3cgQxKNw3GW+Bs4LvB7ybgcaUSQCCfn BxPnG/6L3WbDQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3o-3zXF; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 06/39] scripts/kernel-doc: drop dead code for Wcontents_before_sections Date: Mon, 24 Feb 2025 10:08:12 +0100 Message-ID: <174a15607fd057c736dc9123c53d0835ce20e68b.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" There is a warning about contents before sections, which doesn't work, since in_doc_sect variable is always true at the point it is checked. Drop the dead code. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index d59552e1a31d..af6cf408b96d 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -137,7 +137,6 @@ my $verbose =3D 0; my $Werror =3D 0; my $Wreturn =3D 0; my $Wshort_desc =3D 0; -my $Wcontents_before_sections =3D 0; my $output_mode =3D "rst"; my $output_preformatted =3D 0; my $no_doc_sections =3D 0; @@ -223,7 +222,6 @@ use constant { STATE_INLINE =3D> 7, # gathering doc outside main block }; my $state; -my $in_doc_sect; my $leading_space; =20 # Inline documentation state @@ -332,12 +330,9 @@ while ($ARGV[0] =3D~ m/^--?(.*)/) { $Wreturn =3D 1; } elsif ($cmd eq "Wshort-desc" or $cmd eq "Wshort-description") { $Wshort_desc =3D 1; - } elsif ($cmd eq "Wcontents-before-sections") { - $Wcontents_before_sections =3D 1; } elsif ($cmd eq "Wall") { $Wreturn =3D 1; $Wshort_desc =3D 1; - $Wcontents_before_sections =3D 1; } elsif (($cmd eq "h") || ($cmd eq "help")) { pod2usage(-exitval =3D> 0, -verbose =3D> 2); } elsif ($cmd eq 'no-doc-sections') { @@ -1963,7 +1958,6 @@ sub process_export_file($) { sub process_normal() { if (/$doc_start/o) { $state =3D STATE_NAME; # next line is always the function n= ame - $in_doc_sect =3D 0; $declaration_start_line =3D $. + 1; } } @@ -2068,7 +2062,6 @@ sub process_body($$) { } =20 if (/$doc_sect/i) { # case insensitive for supported section names - $in_doc_sect =3D 1; $newsection =3D $1; $newcontents =3D $2; =20 @@ -2085,14 +2078,10 @@ sub process_body($$) { } =20 if (($contents ne "") && ($contents ne "\n")) { - if (!$in_doc_sect && $Wcontents_before_sections) { - emit_warning("${file}:$.", "contents before sections\n"); - } dump_section($file, $section, $contents); $section =3D $section_default; } =20 - $in_doc_sect =3D 1; $state =3D STATE_BODY; $contents =3D $newcontents; $new_start_line =3D $.; --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6E1F324502A; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=PHBFHUK4cXqc6ZzZF+GKcL2qXSHPMKgcaif9iGk9qXKlAigoPzw9qlVDBbA0QrOVLvKE+T5ahwmSeVGONsv9gMDFn2cCEFtSYNnB16372qft7iNCkFKU35EYhjak2UWEFgtUMwk/PaF4myHDbyyrfwg/fiLYKgetLbhomdFw8z4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=pT2AurFk7T5HHxyb+zGbm0v00i4Pdq52KmG0zBAistg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BX5+y1DEfLlKl79lTHNgKkjP7JxLoVznCNk0+GFlDv5uqra1VUF+xLwgfiOnkiKyLyZbbWyyeLL21uNCiYGZOl4kr3RcSeR44nh/Z9kvIohuFxuk47Oy/QtV4iSkG4RjETHyhry68VDDwItWPuh2BcCmC1p7bj8ku9NM+3c2kzI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Sv8vYuVQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Sv8vYuVQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CC8E3C4CEEC; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=pT2AurFk7T5HHxyb+zGbm0v00i4Pdq52KmG0zBAistg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Sv8vYuVQyJwlQu80wHvaXf9C/JDwd+l1EnPRZHF4x/8Mpfy6WjfTv7SM4wga/ummr E/UZU2VhHlsPnqxyAGY+SLCR6I1O3z7RpEP+3R4kHlZTn7wO1TCJUa5b8Pvead3YeU eji/WzXo3usb6Ct07Hq50GYPuaNneceSphPCvFEH35mMHlwkfYZ2qUF1AmdIS1fUvG W+V7X30cBtKmLTSUNNPmXzlxS4nIyG1it6Ok4i58vLMIaqwLLSQt9dX0vOzHwg18is +SIDwauYBaVMwhynVeb6sDeBrvEYMQDHeS7xhLQLSwWHkkWNdgyyvYura1CMHiOpeu snlcuccbGh2+A== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUSS-00000003p3s-46K2; Mon, 24 Feb 2025 10:09:00 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 07/39] scripts/kernel-doc: rename it to scripts/kernel-doc.pl Date: Mon, 24 Feb 2025 10:08:13 +0100 Message-ID: <52d0e3a45dac63af3dfad23103cd4365fb12686c.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" In preparation for deprecating scripts/kernel-doc in favor of a new version written in Perl, rename it to scripts/kernel-doc.pl. Signed-off-by: Mauro Carvalho Chehab --- scripts/{kernel-doc =3D> kernel-doc.pl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{kernel-doc =3D> kernel-doc.pl} (100%) diff --git a/scripts/kernel-doc b/scripts/kernel-doc.pl similarity index 100% rename from scripts/kernel-doc rename to scripts/kernel-doc.pl --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 83071245038; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=ZPTARBsYEhhaoBZ/vqnFngUeDZ1tKASBQq9CeajOD5YzDvxLvIxAU/rbmNVKsOIF7S8+ShqNrS4DQXoVOgpNFsc4mM8zToJ/7onpEbbNeV6S7ATYZfw0z8EQ76ioROW4jpL3+eDUZB3rQPwfvBvnSWOZsAQd/sHqToulpM48sBI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=ojXYm2ybn18UU1+Rz9pniQpO3g763W9s/DzLh/PxuxA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Q2aYmhSD4/KZ/JLq/a3G7V5sdiIMKNXTUFQe1WBdatmpSQQ8+EN2dQjDGixhv5kZlVGOU4wcV2CEEUglUrv/WALUBIWspL26hEFSkXnWDiAj3yTIOI5nxg9k1/O7su0xMP0OjCZcBGHRnoszWN0xTlbQXbHpK90YdcbQBYpeTwc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=sSshbgZs; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="sSshbgZs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D5EB2C4CEF1; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=ojXYm2ybn18UU1+Rz9pniQpO3g763W9s/DzLh/PxuxA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sSshbgZsMeokStFUJi1eBlmUK7wr57fVcHoaVLXSipJPDsre+Cd9hkhQ6JolQU/Or 1h87qo8nvw17NEUh5ougQ7HxWdS1Jv5MuGO/1ZewoSb7B8gD7rDGFZJ5SubomQ7AaK xLWiCCGu8LiEus5gh2kydHoahF2sfmfAnNC/Mtj5AlY6A9WQNWnboYOzpBNRAhQBAy 6GOFFK/MGHzzeRs3wcfF7DoLMNRr0+xcGjBRpykyj2sYPtSiKEwfA43MGtmTUg3ehj RqPwI1TsNFAYS9VfjKKtmp2b0a/2tiZZY60mjVgNYemQ38rdwW3Rx2QfTFhtVE8a5F CF2ivkb+iN3bA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p3w-01Dp; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 08/39] scripts/kernel-doc: add a symlink to the Perl version of kernel-doc Date: Mon, 24 Feb 2025 10:08:14 +0100 Message-ID: <1471724418841cf10ad61d2e502e3302417b7f77.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Preserve kernel-doc name, associating with the curent version in Perl. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc | 1 + 1 file changed, 1 insertion(+) create mode 120000 scripts/kernel-doc diff --git a/scripts/kernel-doc b/scripts/kernel-doc new file mode 120000 index 000000000000..f175155c1e66 --- /dev/null +++ b/scripts/kernel-doc @@ -0,0 +1 @@ +kernel-doc.pl \ No newline at end of file --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F07FD24A07D; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=Sd9nfFy783MLGwUmq65SgzWxAAfu2wjjt5sOHOBij/fLbY59RhHiBZNwfhFKWJswskc+cjXKEdnksTtneS9p+rG8PcruU3SH8hTEvSIZ7sMoN+WP5uksFuXaIVIdWL5qQw2ALfJeeUEVZ2hptk9QQCwmXqCDyEVr+Zg/CNXVH7E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=Pl6oSKiWCpoo2+dE9Fw6KFNver/u1CwO5ptehFE4p+w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LifovpFDPvJlIPb5JKRILQj2UeOieQFM8U+ehqlZSz1/ymFKOUpHLwQI+kiSIumWS0yfgoBcIpsxv+pPZ6C3K9wrisDJmv1BLv6ganNmA4o2SW60hgcxCWGfCI4RGQLGTE51BwKJ1Ug0/ldIyJ65ZxySr5OYVdoT2GqgfbqtBhU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AKRUiAPW; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AKRUiAPW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D847AC4CEEF; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=Pl6oSKiWCpoo2+dE9Fw6KFNver/u1CwO5ptehFE4p+w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AKRUiAPWNhWsXGCUZU2t54RG3aU81pgpSnQZNV7PBZojBhRRIE3QAxK+cqIjjTv08 v8xR0kJ8LEG48fWygwDs2sQU5TIo4PoVJcpiW4gJDYR8n0tfn8Y+4pNanhh6ZHd71X nWmrta0bIhvMwSzcp4pxMgakrBmOKRRAUvABXWII36Zg7L9UgH8HlhmO8bHkJInj8k /zadnYycKcx+6YasLgI5NOkJOSw3RDNIBv4RgBvNh/d3SIilsiC6bK127J2twkgZhG MXnx0GxFBwIQtEBOcgati3wakE3kPXKPzBzR5SX1HmW5npng+C/rcwAmYrN8ZxzRmD tLE6Kj1Knm2fQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p41-09FT; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Gustavo A. R. Silva" , "Mauro Carvalho Chehab" , Kees Cook , linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 09/39] scripts/kernel-doc.py: add a Python parser Date: Mon, 24 Feb 2025 10:08:15 +0100 Message-ID: <3905b7386d5f1bfa76639cdf1108a46f0bccbbea.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Maintaining kernel-doc has been a challenge, as there aren't many perl developers among maintainers. Also, the logic there is too complex. Having lots of global variables and using pure functions doesn't help. Rewrite the script in Python, placing most global variables inside classes. This should help maintaining the script in long term. It also allows a better integration with kernel-doc Sphinx extension in the future. I opted to keep this version as close as possible to what we have already in Perl. There are some differences though: 1. There is one regular expression that required a rewrite: /\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/ As this one uses two features that aren't available by the native Python regular expression module (re): - recursive patterns: ?1 - atomic grouping (?>...) Rewrite it to use a much simpler regular expression: /\bSTRUCT_GROUP\(([^\)]+)\)[^;]*;/ Extra care should be taken when validating this script, as such replacement might cause some regressions. 2. The filters are now applied only during output generation. In particular, "nosymbol" argument is only handled there. It means that, if the same file is processed twice for different symbols, the warnings will be duplicated. I opted to use this behavior as it allows the Sphinx extension to read the file(s) only once, and apply the filtering only when producing the ReST output. This hopefully will help to speed up doc generation 3. This version can handle multiple files and multiple directories. So, if one just wants to produce a big output with everything inside a file, this could be done with $ time ./scripts/kernel-doc.py -man . 2>/dev/null >new real 0m54.592s user 0m53.345s sys 0m0.997s 4. I tried to replicate as much as possible the same arguments from kernel-doc, with about the same behavior, for the command line parameters starting with a single dash (-parameter). I also added one letter aliases for each parameter, and a --parameter (sometimes with a better name). 5. There are some sutile nuances between how Perl handles certain regular expressions. In special, the qr operatior, which compiles a regular expression also works as a non-capturing group. It means that some regexes like this one: my $type1 =3D qr{[\w\s]+}; needs to be mapped as: type1 =3D r'(?:[\w\s]+)?' Signed-off-by: Mauro Carvalho Chehab --- TODO: - on this RFC, the man output doesn't match yet the same output of kernel-doc. The ReST output matches, except for some whitespaces and suppressed empty sectionsl - this version lacks support for -W parameters: it will just output all warnings. - all classes are at the same file. I want to split the classes on multiple files for the final version, but, during development time, it is easier to have everything on a single file, but I plan to split classes on different files to help maintaining the script. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 2757 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2757 insertions(+) create mode 100755 scripts/kernel-doc.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py new file mode 100755 index 000000000000..5cf5ed63f215 --- /dev/null +++ b/scripts/kernel-doc.py @@ -0,0 +1,2757 @@ +#!/usr/bin/env python3 +# pylint: disable=3DR0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,= R1702 +# pylint: disable=3DC0302,C0103,C0301 +# pylint: disable=3DC0116,C0115,W0511,W0613 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + +# TODO: implement warning filtering + +""" +kernel_doc +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Print formatted kernel documentation to stdout + +Read C language source or header FILEs, extract embedded +documentation comments, and print formatted documentation +to standard output. + +The documentation comments are identified by the "/**" +opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the +documentation comment syntax. +""" + +import argparse +import logging +import os +import re +import sys + +from datetime import datetime +from pprint import pformat + +from dateutil import tz + +# Local cache for regular expressions +re_cache =3D {} + + +class Re: + """ + Helper class to simplify regex declaration and usage, + + It calls re.compile for a given pattern. It also allows adding + regular expressions and define sub at class init time. + + Regular expressions can be cached via an argument, helping to speedup + searches. + """ + + def _add_regex(self, string, flags): + if string in re_cache: + self.regex =3D re_cache[string] + else: + self.regex =3D re.compile(string, flags=3Dflags) + + if self.cache: + re_cache[string] =3D self.regex + + def __init__(self, string, cache=3DTrue, flags=3D0): + self.cache =3D cache + self.last_match =3D None + + self._add_regex(string, flags) + + def __str__(self): + return self.regex.pattern + + def __add__(self, other): + return Re(str(self) + str(other), cache=3Dself.cache or other.cach= e, + flags=3Dself.regex.flags | other.regex.flags) + + def match(self, string): + self.last_match =3D self.regex.match(string) + return self.last_match + + def search(self, string): + self.last_match =3D self.regex.search(string) + return self.last_match + + def findall(self, string): + return self.regex.findall(string) + + def split(self, string): + return self.regex.split(string) + + def sub(self, sub, string, count=3D0): + return self.regex.sub(sub, string, count=3Dcount) + + def group(self, num): + return self.last_match.group(num) + +# +# Regular expressions used to parse kernel-doc markups at KernelDoc class. +# +# Let's declare them in lowercase outside any class to make easier to +# convert from the python script. +# +# As those are evaluated at the beginning, no need to cache them +# + + +# Allow whitespace at end of comment start. +doc_start =3D Re(r'^/\*\*\s*$', cache=3DFalse) + +doc_end =3D Re(r'\*/', cache=3DFalse) +doc_com =3D Re(r'\s*\*\s*', cache=3DFalse) +doc_com_body =3D Re(r'\s*\* ?', cache=3DFalse) +doc_decl =3D doc_com + Re(r'(\w+)', cache=3DFalse) + +# @params and a strictly limited set of supported section names +# Specifically: +# Match @word: +# @...: +# @{section-name}: +# while trying to not match literal block starts like "example::" +# +doc_sect =3D doc_com + \ + Re(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?= |examples?)\s*:([^:].*)?$', + flags=3Dre.I, cache=3DFalse) + +doc_content =3D doc_com_body + Re(r'(.*)', cache=3DFalse) +doc_block =3D doc_com + Re(r'DOC:\s*(.*)?', cache=3DFalse) +doc_inline_start =3D Re(r'^\s*/\*\*\s*$', cache=3DFalse) +doc_inline_sect =3D Re(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=3DFalse) +doc_inline_end =3D Re(r'^\s*\*/\s*$', cache=3DFalse) +doc_inline_oneline =3D Re(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cac= he=3DFalse) +function_pointer =3D Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=3DFalse) +attribute =3D Re(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", + flags=3Dre.I | re.S, cache=3DFalse) + +# match expressions used to find embedded type information +type_constant =3D Re(r"\b``([^\`]+)``\b", cache=3DFalse) +type_constant2 =3D Re(r"\%([-_*\w]+)", cache=3DFalse) +type_func =3D Re(r"(\w+)\(\)", cache=3DFalse) +type_param =3D Re(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=3DFalse) +type_param_ref =3D Re(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cac= he=3DFalse) + +# Special RST handling for func ptr params +type_fp_param =3D Re(r"\@(\w+)\(\)", cache=3DFalse) + +# Special RST handling for structs with func ptr params +type_fp_param2 =3D Re(r"\@(\w+->\S+)\(\)", cache=3DFalse) + +type_env =3D Re(r"(\$\w+)", cache=3DFalse) +type_enum =3D Re(r"\&(enum\s*([_\w]+))", cache=3DFalse) +type_struct =3D Re(r"\&(struct\s*([_\w]+))", cache=3DFalse) +type_typedef =3D Re(r"\&(typedef\s*([_\w]+))", cache=3DFalse) +type_union =3D Re(r"\&(union\s*([_\w]+))", cache=3DFalse) +type_member =3D Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache=3DFalse) +type_fallback =3D Re(r"\&([_\w]+)", cache=3DFalse) +type_member_func =3D type_member + Re(r"\(\)", cache=3DFalse) + +export_symbol =3D Re(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cac= he=3DFalse) +export_symbol_ns =3D Re(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"= \S+"\)\s*', cache=3DFalse) + +class KernelDoc: + # Parser states + STATE_NORMAL =3D 0 # normal code + STATE_NAME =3D 1 # looking for function name + STATE_BODY_MAYBE =3D 2 # body - or maybe more description + STATE_BODY =3D 3 # the body of the comment + STATE_BODY_WITH_BLANK_LINE =3D 4 # the body which has a blank line + STATE_PROTO =3D 5 # scanning prototype + STATE_DOCBLOCK =3D 6 # documentation block + STATE_INLINE =3D 7 # gathering doc outside main block + + st_name =3D [ + "NORMAL", + "NAME", + "BODY_MAYBE", + "BODY", + "BODY_WITH_BLANK_LINE", + "PROTO", + "DOCBLOCK", + "INLINE", + ] + + # Inline documentation state + STATE_INLINE_NA =3D 0 # not applicable ($state !=3D STATE_INLINE) + STATE_INLINE_NAME =3D 1 # looking for member name (@foo:) + STATE_INLINE_TEXT =3D 2 # looking for member documentation + STATE_INLINE_END =3D 3 # done + STATE_INLINE_ERROR =3D 4 # error - Comment without header was found. + # Spit a warning as it's not + # proper kernel-doc and ignore the rest. + + st_inline_name =3D [ + "", + "_NAME", + "_TEXT", + "_END", + "_ERROR", + ] + + # Section names + + section_default =3D "Description" # default section + section_intro =3D "Introduction" + section_context =3D "Context" + section_return =3D "Return" + + undescribed =3D "-- undescribed --" + + def __init__(self, config, fname): + """Initialize internal variables""" + + self.fname =3D fname + self.config =3D config + + # Initial state for the state machines + self.state =3D self.STATE_NORMAL + self.inline_doc_state =3D self.STATE_INLINE_NA + + # Store entry currently being processed + self.entry =3D None + + # Place all potential outputs into an array + self.entries =3D [] + + def show_warnings(self, dtype, declaration_name): + # TODO: implement it + + return True + + # TODO: rename to emit_message + def emit_warning(self, ln, msg, warning=3DTrue): + """Emit a message""" + + if warning: + self.config.log.warning("%s:%d %s", self.fname, ln, msg) + else: + self.config.log.info("%s:%d %s", self.fname, ln, msg) + + def dump_section(self, start_new=3DTrue): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + + name =3D self.entry.section + contents =3D self.entry.contents + + if type_param.match(name): + name =3D type_param.group(1) + + self.entry.parameterdescs[name] =3D contents + self.entry.parameterdesc_start_lines[name] =3D self.entry.new_= start_line + + self.entry.sectcheck +=3D name + " " + self.entry.new_start_line =3D 0 + + elif name =3D=3D "@...": + name =3D "..." + self.entry.parameterdescs[name] =3D contents + self.entry.sectcheck +=3D name + " " + self.entry.parameterdesc_start_lines[name] =3D self.entry.new_= start_line + self.entry.new_start_line =3D 0 + + else: + if name in self.entry.sections and self.entry.sections[name] != =3D "": + # Only warn on user-specified duplicate section names + if name !=3D self.section_default: + self.emit_warning(self.entry.new_start_line, + f"duplicate section name '{name}'\n") + self.entry.sections[name] +=3D contents + else: + self.entry.sections[name] =3D contents + self.entry.sectionlist.append(name) + self.entry.section_start_lines[name] =3D self.entry.new_st= art_line + self.entry.new_start_line =3D 0 + +# self.config.log.debug("Section: %s : %s", name, pformat(vars(self= .entry))) + + if start_new: + self.entry.section =3D self.section_default + self.entry.contents =3D "" + + # TODO: rename it to store_declaration + def output_declaration(self, dtype, name, **args): + """ + Stores the entry into an entry array. + + The actual output and output filters will be handled elsewhere + """ + + # The implementation here is different than the original kernel-do= c: + # instead of checking for output filters or actually output anythi= ng, + # it just stores the declaration content at self.entries, as the + # output will happen on a separate class. + # + # For now, we're keeping the same name of the function just to make + # easier to compare the source code of both scripts + + if "declaration_start_line" not in args: + args["declaration_start_line"] =3D self.entry.declaration_star= t_line + + args["type"] =3D dtype + + self.entries.append((name, args)) + + self.config.log.debug("Output: %s:%s =3D %s", dtype, name, pformat= (args)) + + def reset_state(self, ln): + """ + Ancillary routine to create a new entry. It initializes all + variables used by the state machine. + """ + + self.entry =3D argparse.Namespace + + self.entry.contents =3D "" + self.entry.function =3D "" + self.entry.sectcheck =3D "" + self.entry.struct_actual =3D "" + self.entry.prototype =3D "" + + self.entry.parameterlist =3D [] + self.entry.parameterdescs =3D {} + self.entry.parametertypes =3D {} + self.entry.parameterdesc_start_lines =3D {} + + self.entry.section_start_lines =3D {} + self.entry.sectionlist =3D [] + self.entry.sections =3D {} + + self.entry.anon_struct_union =3D False + + self.entry.leading_space =3D None + + # State flags + self.state =3D self.STATE_NORMAL + self.inline_doc_state =3D self.STATE_INLINE_NA + self.entry.brcount =3D 0 + + self.entry.in_doc_sect =3D False + self.entry.declaration_start_line =3D ln + + def push_parameter(self, ln, decl_type, param, dtype, + org_arg, declaration_name): + if self.entry.anon_struct_union and dtype =3D=3D "" and param =3D= =3D "}": + return # Ignore the ending }; from anonymous struct/union + + self.entry.anon_struct_union =3D False + + param =3D Re(r'[\[\)].*').sub('', param, count=3D1) + + if dtype =3D=3D "" and param.endswith("..."): + if Re(r'\w\.\.\.$').search(param): + # For named variable parameters of the form `x...`, + # remove the dots + param =3D param[:-3] + else: + # Handles unnamed variable parameters + param =3D "..." + + if param not in self.entry.parameterdescs or \ + not self.entry.parameterdescs[param]: + + self.entry.parameterdescs[param] =3D "variable arguments" + + elif dtype =3D=3D "" and (not param or param =3D=3D "void"): + param =3D "void" + self.entry.parameterdescs[param] =3D "no arguments" + + elif dtype =3D=3D "" and param in ["struct", "union"]: + # Handle unnamed (anonymous) union or struct + dtype =3D param + param =3D "{unnamed_" + param + "}" + self.entry.parameterdescs[param] =3D "anonymous\n" + self.entry.anon_struct_union =3D True + + # Handle cache group enforcing variables: they do not need + # to be described in header files + elif "__cacheline_group" in param: + # Ignore __cacheline_group_begin and __cacheline_group_end + return + + # Warn if parameter has no description + # (but ignore ones starting with # as these are not parameters + # but inline preprocessor statements) + if param not in self.entry.parameterdescs and not param.startswith= ("#"): + self.entry.parameterdescs[param] =3D self.undescribed + + if self.show_warnings(dtype, declaration_name) and "." not in = param: + if decl_type =3D=3D 'function': + dname =3D f"{decl_type} parameter" + else: + dname =3D f"{decl_type} member" + + self.emit_warning(ln, + f"{dname} '{param}' not described in '{d= eclaration_name}'") + + # Strip spaces from param so that it is one continuous string on + # parameterlist. This fixes a problem where check_sections() + # cannot find a parameter like "addr[6 + 2]" because it actually + # appears as "addr[6", "+", "2]" on the parameter list. + # However, it's better to maintain the param string unchanged for + # output, so just weaken the string compare in check_sections() + # to ignore "[blah" in a parameter string. + + self.entry.parameterlist.append(param) + org_arg =3D Re(r'\s\s+').sub(' ', org_arg, count=3D1) + self.entry.parametertypes[param] =3D org_arg + + def save_struct_actual(self, actual): + """ + Strip all spaces from the actual param so that it looks like + one string item. + """ + + actual =3D Re(r'\s*').sub("", actual, count=3D1) + + self.entry.struct_actual +=3D actual + " " + + def create_parameter_list(self, ln, decl_type, args, splitter, declara= tion_name): + + # temporarily replace all commas inside function pointer definition + arg_expr =3D Re(r'(\([^\),]+),') + while arg_expr.search(args): + args =3D arg_expr.sub(r"\1#", args) + + for arg in args.split(splitter): + # Strip comments + arg =3D Re(r'\/\*.*\*\/').sub('', arg) + + # Ignore argument attributes + arg =3D Re(r'\sPOS0?\s').sub(' ', arg) + + # Strip leading/trailing spaces + arg =3D arg.strip() + arg =3D Re(r'\s+').sub(' ', arg, count=3D1) + + if arg.startswith('#'): + # Treat preprocessor directive as a typeless variable just= to fill + # corresponding data structures "correctly". Catch it late= r in + # output_* subs. + + # Treat preprocessor directive as a typeless variable + self.push_parameter(ln, decl_type, arg, "", + "", declaration_name) + + elif Re(r'\(.+\)\s*\(').search(arg): + # Pointer-to-function + + arg =3D arg.replace('#', ',') + + r =3D Re(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') + if r.match(arg): + param =3D r.group(1) + else: + self.emit_warning(ln, f"Invalid param: {arg}") + param =3D arg + + dtype =3D Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r= '\1', arg) + self.save_struct_actual(param) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif Re(r'\(.+\)\s*\[').search(arg): + # Array-of-pointers + + arg =3D arg.replace('#', ',') + r =3D Re(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+= \s*\]\s*)*\)') + if r.match(arg): + param =3D r.group(1) + else: + self.emit_warning(ln, f"Invalid param: {arg}") + param =3D arg + + dtype =3D Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r= '\1', arg) + + self.save_struct_actual(param) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif arg: + arg =3D Re(r'\s*:\s*').sub(":", arg) + arg =3D Re(r'\s*\[').sub('[', arg) + + args =3D Re(r'\s*,\s*').split(arg) + if args[0] and '*' in args[0]: + args[0] =3D re.sub(r'(\*+)\s*', r' \1', args[0]) + + first_arg =3D [] + r =3D Re(r'^(.*\s+)(.*?\[.*\].*)$') + if args[0] and r.match(args[0]): + args.pop(0) + first_arg.extend(r.group(1)) + first_arg.append(r.group(2)) + else: + first_arg =3D Re(r'\s+').split(args.pop(0)) + + args.insert(0, first_arg.pop()) + dtype =3D ' '.join(first_arg) + + for param in args: + if Re(r'^(\*+)\s*(.*)').match(param): + r =3D Re(r'^(\*+)\s*(.*)') + if not r.match(param): + self.emit_warning(ln, f"Invalid param: {param}= ") + continue + + param =3D r.group(1) + + self.save_struct_actual(r.group(2)) + self.push_parameter(ln, decl_type, r.group(2), + f"{dtype} {r.group(1)}", + arg, declaration_name) + + elif Re(r'(.*?):(\w+)').search(param): + r =3D Re(r'(.*?):(\w+)') + if not r.match(param): + self.emit_warning(ln, f"Invalid param: {param}= ") + continue + + if dtype !=3D "": # Skip unnamed bit-fields + self.save_struct_actual(r.group(1)) + self.push_parameter(ln, decl_type, r.group(1), + f"{dtype}:{r.group(2)}", + arg, declaration_name) + else: + self.save_struct_actual(param) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + def check_sections(self, ln, decl_name, decl_type, sectcheck, prmschec= k): + sects =3D sectcheck.split() + prms =3D prmscheck.split() + err =3D False + + for sx in range(len(sects)): # pylint: disable=3D= C0200 + err =3D True + for px in range(len(prms)): # pylint: disable=3D= C0200 + prm_clean =3D prms[px] + prm_clean =3D Re(r'\[.*\]').sub('', prm_clean) + prm_clean =3D attribute.sub('', prm_clean) + + # ignore array size in a parameter string; + # however, the original param string may contain + # spaces, e.g.: addr[6 + 2] + # and this appears in @prms as "addr[6" since the + # parameter list is split at spaces; + # hence just ignore "[..." for the sections check; + prm_clean =3D Re(r'\[.*').sub('', prm_clean) + + if prm_clean =3D=3D sects[sx]: + err =3D False + break + + if err: + if decl_type =3D=3D 'function': + dname =3D f"{decl_type} parameter" + else: + dname =3D f"{decl_type} member" + + self.emit_warning(ln, + f"Excess {dname} '{sects[sx]}' descripti= on in '{decl_name}'") + + def check_return_section(self, ln, declaration_name, return_type): + + if not self.config.wreturn: + return + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type (but not "void *") + if not return_type or Re(r'void\s*\w*\s*$').search(return_type): + return + + if not self.entry.sections.get("Return", None): + self.emit_warning(ln, + f"No description found for return value of '= {declaration_name}'") + + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + + type_pattern =3D r'(struct|union)' + + qualifiers =3D [ + "__attribute__", + "__packed", + "__aligned", + "____cacheline_aligned_in_smp", + "____cacheline_aligned", + ] + + definition_body =3D r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) = + ")?" + struct_members =3D Re(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\= })([^\{\}\;]*)(\;)') + + # Extract struct/union definition + members =3D None + declaration_name =3D None + decl_type =3D None + + r =3D Re(type_pattern + r'\s+(\w+)\s*' + definition_body) + if r.search(proto): + decl_type =3D r.group(1) + declaration_name =3D r.group(2) + members =3D r.group(3) + else: + r =3D Re(r'typedef\s+' + type_pattern + r'\s*' + definition_bo= dy + r'\s*(\w+)\s*;') + + if r.search(proto): + decl_type =3D r.group(1) + declaration_name =3D r.group(3) + members =3D r.group(2) + + if not members: + self.emit_warning(ln, f"{proto} error: Cannot parse struct or = union!") + self.config.errors +=3D 1 + return + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, + f"expecting prototype for {decl_type} {self.= entry.identifier}. Prototype was for {decl_type} {declaration_name} instead= \n") + return + + args_pattern =3Dr'([^,)]+)' + + sub_prefixes =3D [ + (Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), = ''), + (Re(r'\/\*\s*private:.*', re.S| re.I), ''), + + # Strip comments + (Re(r'\/\*.*?\*\/', re.S), ''), + + # Strip attributes + (attribute, ' '), + (Re(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), + (Re(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), + (Re(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), + (Re(r'\s*__packed\s*', re.S), ' '), + (Re(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), + (Re(r'\s*____cacheline_aligned_in_smp', re.S), ' '), + (Re(r'\s*____cacheline_aligned', re.S), ' '), + + # Unwrap struct_group() based on this definition: + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) + # which has variants like: struct_group(NAME, MEMBERS...) + + (Re(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), + (Re(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_G= ROUP('), + (Re(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r's= truct \1 \2; STRUCT_GROUP('), + (Re(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROU= P('), + + # This is incompatible with Python re, as it uses: + # recursive patterns ((?1)) and atomic grouping ((?>...)): + # '\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;' + # Let's see if this works instead: + (Re(r'\bSTRUCT_GROUP\(([^\)]+)\)[^;]*;', re.S), r'\1'), + + # Replace macros + (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),= r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), + (Re(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DE= CLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), + (Re(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pat= tern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), + (Re(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_= pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), + (Re(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_patt= ern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (Re(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_= pattern + r'\)', re.S), r'\2 *\1'), + (Re(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*'= + args_pattern + r'\)', re.S), r'\1 \2[]'), + (Re(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S= ), r'dma_addr_t \1'), + (Re(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S)= , r'__u32 \1'), + ] + + for search, sub in sub_prefixes: + members =3D search.sub(sub, members) + + # Keeps the original declaration as-is + declaration =3D members + + # Split nested struct/union elements + # + # This loop was simpler at the original kernel-doc perl version, as + # while ($members =3D~ m/$struct_members/) { ... } + # reads 'members' string on each interaction. + # + # Python behavior is different: it parses 'members' only once, + # creating a list of tuples from the first interaction. + # + # On other words, this won't get nested structs. + # + # So, we need to have an extra loop on Python to override such + # re limitation. + + while True: + tuples =3D struct_members.findall(members) + if not tuples: + break + + for t in tuples: + newmember =3D "" + maintype =3D t[0] + s_ids =3D t[5] + content =3D t[3] + + oldmember =3D "".join(t) + + for s_id in s_ids.split(','): + s_id =3D s_id.strip() + + newmember +=3D f"{maintype} {s_id}; " + s_id =3D Re(r'[:\[].*').sub('', s_id) + s_id =3D Re(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) + + for arg in content.split(';'): + arg =3D arg.strip() + + if not arg: + continue + + r =3D Re(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') + if r.match(arg): + # Pointer-to-function + dtype =3D r.group(1) + name =3D r.group(2) + extra =3D r.group(3) + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember +=3D f"{dtype}{name}{extra}; " + else: + newmember +=3D f"{dtype}{s_id}.{name}{extr= a}; " + + else: + arg =3D arg.strip() + # Handle bitmaps + arg =3D Re(r':\s*\d+\s*').sub('', arg) + + # Handle arrays + arg =3D Re(r'\[.*\]').sub('', arg) + + # Handle multiple IDs + arg =3D Re(r'\s*,\s*').sub(',', arg) + + + r =3D Re(r'(.*)\s+([\S+,]+)') + + if r.search(arg): + dtype =3D r.group(1) + names =3D r.group(2) + else: + newmember +=3D f"{arg}; " + continue + + for name in names.split(','): + name =3D Re(r'^\s*\**(\S+)\s*').sub(r'\1',= name).strip() + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember +=3D f"{dtype} {name}; " + else: + newmember +=3D f"{dtype} {s_id}.{name}= ; " + + members =3D members.replace(oldmember, newmember) + + # Ignore other nested elements, like enums + members =3D re.sub(r'(\{[^\{\}]*\})', '', members) + + self.create_parameter_list(ln, decl_type, members, ';', + declaration_name) + self.check_sections(ln, declaration_name, decl_type, + self.entry.sectcheck, self.entry.struct_actual) + + # Adjust declaration for better display + declaration =3D Re(r'([\{;])').sub(r'\1\n', declaration) + declaration =3D Re(r'\}\s+;').sub('};', declaration) + + # Better handle inlined enums + while True: + r =3D Re(r'(enum\s+\{[^\}]+),([^\n])') + if not r.search(declaration): + break + + declaration =3D r.sub(r'\1,\n\2', declaration) + + def_args =3D declaration.split('\n') + level =3D 1 + declaration =3D "" + for clause in def_args: + + clause =3D clause.strip() + clause =3D Re(r'\s+').sub(' ', clause, count=3D1) + + if not clause: + continue + + if '}' in clause and level > 1: + level -=3D 1 + + if not Re(r'^\s*#').match(clause): + declaration +=3D "\t" * level + + declaration +=3D "\t" + clause + "\n" + if "{" in clause and "}" not in clause: + level +=3D 1 + + self.output_declaration(decl_type, declaration_name, + struct=3Ddeclaration_name, + module=3Dself.entry.modulename, + definition=3Ddeclaration, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + parametertypes=3Dself.entry.parametertypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose) + + def dump_enum(self, ln, proto): + + # Ignore members marked private + proto =3D Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=3Dr= e.S).sub('', proto) + proto =3D Re(r'\/\*\s*private:.*}', flags=3Dre.S).sub('}', proto) + + # Strip comments + proto =3D Re(r'\/\*.*?\*\/', flags=3Dre.S).sub('', proto) + + # Strip #define macros inside enums + proto =3D Re(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=3Dre= .S).sub('', proto) + + members =3D None + declaration_name =3D None + + r =3D Re(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') + if r.search(proto): + declaration_name =3D r.group(2) + members =3D r.group(1).rstrip() + else: + r =3D Re(r'enum\s+(\w*)\s*\{(.*)\}') + if r.match(proto): + declaration_name =3D r.group(1) + members =3D r.group(2).rstrip() + + if not members: + self.emit_warning(ln, f"{proto}: error: Cannot parse enum!") + self.config.errors +=3D 1 + return + + if self.entry.identifier !=3D declaration_name: + if self.entry.identifier =3D=3D "": + self.emit_warning(ln, + f"{proto}: wrong kernel-doc identifier o= n prototype") + else: + self.emit_warning(ln, + f"expecting prototype for enum {self.ent= ry.identifier}. Prototype was for enum {declaration_name} instead") + return + + if not declaration_name: + declaration_name =3D "(anonymous)" + + member_set =3D set() + + members =3D Re(r'\([^;]*?[\)]').sub('', members) + + for arg in members.split(','): + if not arg: + continue + arg =3D Re(r'^\s*(\w+).*').sub(r'\1', arg) + self.entry.parameterlist.append(arg) + if arg not in self.entry.parameterdescs: + self.entry.parameterdescs[arg] =3D self.undescribed + if self.show_warnings("enum", declaration_name): + self.emit_warning(ln, + f"Enum value '{arg}' not described i= n enum '{declaration_name}'") + member_set.add(arg) + + for k in self.entry.parameterdescs: + if k not in member_set: + if self.show_warnings("enum", declaration_name): + self.emit_warning(ln, + f"Excess enum value '%{k}' descripti= on in '{declaration_name}'") + + self.output_declaration('enum', declaration_name, + enum=3Ddeclaration_name, + module=3Dself.config.modulename, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose) + + def dump_declaration(self, ln, prototype): + if self.entry.decl_type =3D=3D "enum": + self.dump_enum(ln, prototype) + return + + if self.entry.decl_type =3D=3D "typedef": + self.dump_typedef(ln, prototype) + return + + if self.entry.decl_type in ["union", "struct"]: + self.dump_struct(ln, prototype) + return + + # TODO: handle other types + self.output_declaration(self.entry.decl_type, prototype, + entry=3Dself.entry) + + def dump_function(self, ln, prototype): + + func_macro =3D False + return_type =3D '' + decl_type =3D 'function' + + # Prefixes that would be removed + sub_prefixes =3D [ + (r"^static +", "", 0), + (r"^extern +", "", 0), + (r"^asmlinkage +", "", 0), + (r"^inline +", "", 0), + (r"^__inline__ +", "", 0), + (r"^__inline +", "", 0), + (r"^__always_inline +", "", 0), + (r"^noinline +", "", 0), + (r"^__FORTIFY_INLINE +", "", 0), + (r"__init +", "", 0), + (r"__init_or_module +", "", 0), + (r"__deprecated +", "", 0), + (r"__flatten +", "", 0), + (r"__meminit +", "", 0), + (r"__must_check +", "", 0), + (r"__weak +", "", 0), + (r"__sched +", "", 0), + (r"_noprof", "", 0), + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", = 0), + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2= ", 0), + (r"__attribute_const__ +", "", 0), + + # It seems that Python support for re.X is broken: + # At least for me (Python 3.13), this didn't work +# (r""" +# __attribute__\s*\(\( +# (?: +# [\w\s]+ # attribute name +# (?:\([^)]*\))? # attribute arguments +# \s*,? # optional comma at the end +# )+ +# \)\)\s+ +# """, "", re.X), + + # So, remove whitespaces and comments from it + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"= , "", 0), + ] + + for search, sub, flags in sub_prefixes: + prototype =3D Re(search, flags).sub(sub, prototype) + + # Macros are a special case, as they change the prototype format + new_proto =3D Re(r"^#\s*define\s+").sub("", prototype) + if new_proto !=3D prototype: + is_define_proto =3D True + prototype =3D new_proto + else: + is_define_proto =3D False + + # Yes, this truly is vile. We are looking for: + # 1. Return type (may be nothing if we're looking at a macro) + # 2. Function name + # 3. Function parameters. + # + # All the while we have to watch out for function pointer paramete= rs + # (which IIRC is what the two sections are for), C types (these + # regexps don't even start to express all the possibilities), and + # so on. + # + # If you mess with these regexps, it's a good idea to check that + # the following functions' documentation still comes out right: + # - parport_register_device (function pointer parameters) + # - atomic_set (macro) + # - pci_match_device, __copy_to_user (long return type) + + name =3D r'[a-zA-Z0-9_~:]+' + prototype_end1 =3D r'[^\(]*' + prototype_end2 =3D r'[^\{]*' + prototype_end =3D fr'\(({prototype_end1}|{prototype_end2})\)' + + # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing gro= up. + # So, this needs to be mapped in Python with (?:...)? or (?:...)+ + + type1 =3D r'(?:[\w\s]+)?' + type2 =3D r'(?:[\w\s]+\*+)+' + + found =3D False + + if is_define_proto: + r =3D Re(r'^()(' + name + r')\s+') + + if r.search(prototype): + return_type =3D '' + declaration_name =3D r.group(2) + func_macro =3D True + + found =3D True + + if not found: + patterns =3D [ + rf'^()({name})\s*{prototype_end}', + rf'^({type1})\s+({name})\s*{prototype_end}', + rf'^({type2})\s*({name})\s*{prototype_end}', + ] + + for p in patterns: + r =3D Re(p) + + if r.match(prototype): + + return_type =3D r.group(1) + declaration_name =3D r.group(2) + args =3D r.group(3) + + self.create_parameter_list(ln, decl_type, args, ',', + declaration_name) + + found =3D True + break + if not found: + self.emit_warning(ln, + f"cannot understand function prototype: '{pr= ototype}'") + return + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, + f"expecting prototype for {self.entry.identi= fier}(). Prototype was for {declaration_name}() instead") + return + + prms =3D " ".join(self.entry.parameterlist) + self.check_sections(ln, declaration_name, "function", + self.entry.sectcheck, prms) + + self.check_return_section(ln, declaration_name, return_type) + + if 'typedef' in return_type: + self.output_declaration(decl_type, declaration_name, + function=3Ddeclaration_name, + typedef=3DTrue, + module=3Dself.config.modulename, + functiontype=3Dreturn_type, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + parametertypes=3Dself.entry.parametertypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose, + func_macro=3Dfunc_macro) + else: + self.output_declaration(decl_type, declaration_name, + function=3Ddeclaration_name, + typedef=3DFalse, + module=3Dself.config.modulename, + functiontype=3Dreturn_type, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + parametertypes=3Dself.entry.parametertypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose, + func_macro=3Dfunc_macro) + + def dump_typedef(self, ln, proto): + typedef_type =3D r'((?:\s+[\w\*]+\b){1,8})\s*' + typedef_ident =3D r'\*?\s*(\w\S+)\s*' + typedef_args =3D r'\s*\((.*)\);' + + typedef1 =3D Re(r'typedef' + typedef_type + r'\(' + typedef_ident = + r'\)' + typedef_args) + typedef2 =3D Re(r'typedef' + typedef_type + typedef_ident + typede= f_args) + + # Strip comments + proto =3D Re(r'/\*.*?\*/', flags=3Dre.S).sub('', proto) + + # Parse function typedef prototypes + for r in [typedef1, typedef2]: + if not r.match(proto): + continue + + return_type =3D r.group(1).strip() + declaration_name =3D r.group(2) + args =3D r.group(3) + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, + f"expecting prototype for typedef {self.= entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + decl_type =3D 'function' + self.create_parameter_list(ln, decl_type, args, ',', declarati= on_name) + + self.output_declaration(decl_type, declaration_name, + function=3Ddeclaration_name, + typedef=3DTrue, + module=3Dself.entry.modulename, + functiontype=3Dreturn_type, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + parametertypes=3Dself.entry.parametertypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose) + return + + # Handle nested parentheses or brackets + r =3D Re(r'(\(*.\)\s*|\[*.\]\s*);$') + while r.search(proto): + proto =3D r.sub('', proto) + + # Parse simple typedefs + r =3D Re(r'typedef.*\s+(\w+)\s*;') + if r.match(proto): + declaration_name =3D r.group(1) + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, f"expecting prototype for typedef {s= elf.entry.identifier}. Prototype was for typedef {declaration_name} instead= \n") + return + + self.output_declaration('typedef', declaration_name, + typedef=3Ddeclaration_name, + module=3Dself.entry.modulename, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose) + return + + self.emit_warning(ln, "error: Cannot parse typedef!") + self.config.errors +=3D 1 + + @staticmethod + def process_export(function_table, line): + """ + process EXPORT_SYMBOL* tags + + This method is called both internally and externally, so, it + doesn't use self. + """ + + if export_symbol.search(line): + symbol =3D export_symbol.group(2) + function_table.add(symbol) + + if export_symbol_ns.search(line): + symbol =3D export_symbol_ns.group(2) + function_table.add(symbol) + + def process_normal(self, ln, line): + """ + STATE_NORMAL: looking for the /** to begin everything. + """ + + if not doc_start.match(line): + return + + # start a new entry + self.reset_state(ln + 1) + self.entry.in_doc_sect =3D False + + # next line is always the function name + self.state =3D self.STATE_NAME + + def process_name(self, ln, line): + """ + STATE_NAME: Looking for the "name - description" line + """ + + if doc_block.search(line): + self.entry.new_start_line =3D ln + + if not doc_block.group(1): + self.entry.section =3D self.section_intro + else: + self.entry.section =3D doc_block.group(1) + + self.state =3D self.STATE_DOCBLOCK + return + + if doc_decl.search(line): + self.entry.identifier =3D doc_decl.group(1) + self.entry.is_kernel_comment =3D False + + decl_start =3D str(doc_com) # comment block asterisk + fn_type =3D r"(?:\w+\s*\*\s*)?" # type (for non-functions) + parenthesis =3D r"(?:\(\w*\))?" # optional parenthesis on fu= nction + decl_end =3D r"(?:[-:].*)" # end of the name part + + # test for pointer declaration type, foo * bar() - desc + r =3D Re(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}= ?$") + if r.search(line): + self.entry.identifier =3D r.group(1) + + # Test for data declaration + r =3D Re(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") + if r.search(line): + self.entry.decl_type =3D r.group(1) + self.entry.identifier =3D r.group(2) + self.entry.is_kernel_comment =3D True + else: + # Look for foo() or static void foo() - description; + # or misspelt identifier + + r1 =3D Re(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s= *{decl_end}?$") + r2 =3D Re(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis= }\s*{decl_end}$") + + for r in [r1, r2]: + if r.search(line): + self.entry.identifier =3D r.group(1) + self.entry.decl_type =3D "function" + + r =3D Re(r"define\s+") + self.entry.identifier =3D r.sub("", self.entry.ide= ntifier) + self.entry.is_kernel_comment =3D True + break + + self.entry.identifier =3D self.entry.identifier.strip(" ") + + self.state =3D self.STATE_BODY + + # if there's no @param blocks need to set up default section h= ere + self.entry.section =3D self.section_default + self.entry.new_start_line =3D ln + 1 + + r =3D Re("[-:](.*)") + if r.search(line): + # strip leading/trailing/multiple spaces + self.entry.descr =3D r.group(1).strip(" ") + + r =3D Re(r"\s+") + self.entry.descr =3D r.sub(" ", self.entry.descr) + self.entry.declaration_purpose =3D self.entry.descr + self.state =3D self.STATE_BODY_MAYBE + else: + self.entry.declaration_purpose =3D "" + + if not self.entry.is_kernel_comment: + self.emit_warning(ln, + f"This comment starts with '/**', but is= n't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{li= ne}") + self.state =3D self.STATE_NORMAL + + if not self.entry.declaration_purpose and self.config.wshort_d= esc: + self.emit_warning(ln, + f"missing initial short description on l= ine:\n{line}") + + if not self.entry.identifier and self.entry.decl_type !=3D "en= um": + self.emit_warning(ln, + f"wrong kernel-doc identifier on line:\n= {line}") + self.state =3D self.STATE_NORMAL + + if self.config.verbose: + self.emit_warning(ln, + f"Scanning doc for {self.entry.decl_type= } {self.entry.identifier}", + warning=3DFalse) + + return + + # Failed to find an identifier. Emit a warning + self.emit_warning(ln, f"Cannot find identifier on line:\n{line}") + + def process_body(self, ln, line): + """ + STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. + """ + + if self.state =3D=3D self.STATE_BODY_WITH_BLANK_LINE: + r =3D Re(r"\s*\*\s?\S") + if r.match(line): + self.dump_section() + self.entry.section =3D self.section_default + self.entry.new_start_line =3D line + self.entry.contents =3D "" + + if doc_sect.search(line): + self.entry.in_doc_sect =3D True + newsection =3D doc_sect.group(1) + + if newsection.lower() in ["description", "context"]: + newsection =3D newsection.title() + + # Special case: @return is a section, not a param description + if newsection.lower() in ["@return", "@returns", + "return", "returns"]: + newsection =3D "Return" + + # Perl kernel-doc has a check here for contents before section= s. + # the logic there is always false, as in_doc_sect variable is + # always true. So, just don't implement Wcontents_before_secti= ons + + # .title() + newcontents =3D doc_sect.group(2) + if not newcontents: + newcontents =3D "" + + if self.entry.contents.strip("\n"): + self.dump_section() + + self.entry.new_start_line =3D ln + self.entry.section =3D newsection + self.entry.leading_space =3D None + + self.entry.contents =3D newcontents.lstrip() + if self.entry.contents: + self.entry.contents +=3D "\n" + + self.state =3D self.STATE_BODY + return + + if doc_end.search(line): + if self.entry.contents.strip("\n"): + self.dump_section() + + # Look for doc_com + + doc_end: + r =3D Re(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + if r.match(line): + self.emit_warning(ln, f"suspicious ending line: {line}") + + self.entry.prototype =3D "" + self.entry.new_start_line =3D ln + 1 + + self.state =3D self.STATE_PROTO + return + + if doc_content.search(line): + cont =3D doc_content.group(1) + + if cont =3D=3D "": + if self.entry.section =3D=3D self.section_context: + self.dump_section() + + self.entry.new_start_line =3D ln + self.state =3D self.STATE_BODY + else: + if self.entry.section !=3D self.section_default: + self.state =3D self.STATE_BODY_WITH_BLANK_LINE + else: + self.state =3D self.STATE_BODY + + self.entry.contents +=3D "\n" + + elif self.state =3D=3D self.STATE_BODY_MAYBE: + + # Continued declaration purpose + self.entry.declaration_purpose =3D self.entry.declaration_= purpose.rstrip() + self.entry.declaration_purpose +=3D " " + cont + + r =3D Re(r"\s+") + self.entry.declaration_purpose =3D r.sub(' ', + self.entry.declarat= ion_purpose) + + else: + if self.entry.section.startswith('@') or \ + self.entry.section =3D=3D self.section_context: + if self.entry.leading_space is None: + r =3D Re(r'^(\s+)') + if r.match(cont): + self.entry.leading_space =3D len(r.group(1)) + else: + self.entry.leading_space =3D 0 + + # Double-check if leading space are realy spaces + pos =3D 0 + for i in range(0, self.entry.leading_space): + if cont[i] !=3D " ": + break + pos +=3D 1 + + cont =3D cont[pos:] + + # NEW LOGIC: + # In case it is different, update it + if self.entry.leading_space !=3D pos: + self.entry.leading_space =3D pos + + self.entry.contents +=3D cont + "\n" + return + + # Unknown line, ignore + self.emit_warning(ln, f"bad line: {line}") + + def process_inline(self, ln, line): + """STATE_INLINE: docbook comments within a prototype.""" + + if self.inline_doc_state =3D=3D self.STATE_INLINE_NAME and \ + doc_inline_sect.search(line): + self.entry.section =3D doc_inline_sect.group(1) + self.entry.new_start_line =3D ln + + self.entry.contents =3D doc_inline_sect.group(2).lstrip() + if self.entry.contents !=3D "": + self.entry.contents +=3D "\n" + + self.inline_doc_state =3D self.STATE_INLINE_TEXT + # Documentation block end */ + return + + if doc_inline_end.search(line): + if self.entry.contents not in ["", "\n"]: + self.dump_section() + + self.state =3D self.STATE_PROTO + self.inline_doc_state =3D self.STATE_INLINE_NA + return + + if doc_content.search(line): + if self.inline_doc_state =3D=3D self.STATE_INLINE_TEXT: + self.entry.contents +=3D doc_content.group(1) + "\n" + if not self.entry.contents.strip(" ").rstrip("\n"): + self.entry.contents =3D "" + + elif self.inline_doc_state =3D=3D self.STATE_INLINE_NAME: + self.emit_warning(ln, + f"Incorrect use of kernel-doc format: {l= ine}") + + self.inline_doc_state =3D self.STATE_INLINE_ERROR + + def syscall_munge(self, ln, proto): + """ + Handle syscall definitions + """ + + is_void =3D False + + # Strip newlines/CR's + proto =3D re.sub(r'[\r\n]+', ' ', proto) + + # Check if it's a SYSCALL_DEFINE0 + if 'SYSCALL_DEFINE0' in proto: + is_void =3D True + + # Replace SYSCALL_DEFINE with correct return type & function name + proto =3D Re(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) + + r =3D Re(r'long\s+(sys_.*?),') + if r.search(proto): + proto =3D proto.replace(',', '(', count=3D1) + elif is_void: + proto =3D proto.replace(')', '(void)', count=3D1) + + # Now delete all of the odd-numbered commas in the proto + # so that argument types & names don't have a comma between them + count =3D 0 + length =3D len(proto) + + if is_void: + length =3D 0 # skip the loop if is_void + + for ix in range(length): + if proto[ix] =3D=3D ',': + count +=3D 1 + if count % 2 =3D=3D 1: + proto =3D proto[:ix] + ' ' + proto[ix+1:] + + return proto + + def tracepoint_munge(self, ln, proto): + """ + Handle tracepoint definitions + """ + + tracepointname =3D None + tracepointargs =3D None + + # Match tracepoint name based on different patterns + r =3D Re(r'TRACE_EVENT\((.*?),') + if r.search(proto): + tracepointname =3D r.group(1) + + r =3D Re(r'DEFINE_SINGLE_EVENT\((.*?),') + if r.search(proto): + tracepointname =3D r.group(1) + + r =3D Re(r'DEFINE_EVENT\((.*?),(.*?),') + if r.search(proto): + tracepointname =3D r.group(2) + + if tracepointname: + tracepointname =3D tracepointname.lstrip() + + r =3D Re(r'TP_PROTO\((.*?)\)') + if r.search(proto): + tracepointargs =3D r.group(1) + + if not tracepointname or not tracepointargs: + self.emit_warning(ln, + f"Unrecognized tracepoint format:\n{proto}\n= ") + else: + proto =3D f"static inline void trace_{tracepointname}({tracepo= intargs})" + self.entry.identifier =3D f"trace_{self.entry.identifier}" + + return proto + + def process_proto_function(self, ln, line): + """Ancillary routine to process a function prototype""" + + # strip C99-style comments to end of line + r =3D Re(r"\/\/.*$", re.S) + line =3D r.sub('', line) + + if Re(r'\s*#\s*define').match(line): + self.entry.prototype =3D line + elif line.startswith('#'): + # Strip other macros like #ifdef/#ifndef/#endif/... + pass + else: + r =3D Re(r'([^\{]*)') + if r.match(line): + self.entry.prototype +=3D r.group(1) + " " + + if '{' in line or ';' in line or Re(r'\s*#\s*define').match(line): + # strip comments + r =3D Re(r'/\*.*?\*/') + self.entry.prototype =3D r.sub('', self.entry.prototype) + + # strip newlines/cr's + r =3D Re(r'[\r\n]+') + self.entry.prototype =3D r.sub(' ', self.entry.prototype) + + # strip leading spaces + r =3D Re(r'^\s+') + self.entry.prototype =3D r.sub('', self.entry.prototype) + + # Handle self.entry.prototypes for function pointers like: + # int (*pcs_config)(struct foo) + + r =3D Re(r'^(\S+\s+)\(\s*\*(\S+)\)') + self.entry.prototype =3D r.sub(r'\1\2', self.entry.prototype) + + if 'SYSCALL_DEFINE' in self.entry.prototype: + self.entry.prototype =3D self.syscall_munge(ln, + self.entry.proto= type) + + r =3D Re(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype =3D self.tracepoint_munge(ln, + self.entry.pr= ototype) + + self.dump_function(ln, self.entry.prototype) + self.reset_state(ln) + + def process_proto_type(self, ln, line): + """Ancillary routine to process a type""" + + # Strip newlines/cr's. + line =3D Re(r'[\r\n]+', re.S).sub(' ', line) + + # Strip leading spaces + line =3D Re(r'^\s+', re.S).sub('', line) + + # Strip trailing spaces + line =3D Re(r'\s+$', re.S).sub('', line) + + # Strip C99-style comments to the end of the line + line =3D Re(r"\/\/.*$", re.S).sub('', line) + + # To distinguish preprocessor directive from regular declaration l= ater. + if line.startswith('#'): + line +=3D ";" + + r =3D Re(r'([^\{\};]*)([\{\};])(.*)') + while True: + if r.search(line): + if self.entry.prototype: + self.entry.prototype +=3D " " + self.entry.prototype +=3D r.group(1) + r.group(2) + + self.entry.brcount +=3D r.group(2).count('{') + self.entry.brcount -=3D r.group(2).count('}') + + self.entry.brcount =3D max(self.entry.brcount, 0) + + if r.group(2) =3D=3D ';' and self.entry.brcount =3D=3D 0: + self.dump_declaration(ln, self.entry.prototype) + self.reset_state(ln) + break + + line =3D r.group(3) + else: + self.entry.prototype +=3D line + break + + def process_proto(self, ln, line): + """STATE_PROTO: reading a function/whatever prototype.""" + + if doc_inline_oneline.search(line): + self.entry.section =3D doc_inline_oneline.group(1) + self.entry.contents =3D doc_inline_oneline.group(2) + + if self.entry.contents !=3D "": + self.entry.contents +=3D "\n" + self.dump_section(start_new=3DFalse) + + elif doc_inline_start.search(line): + self.state =3D self.STATE_INLINE + self.inline_doc_state =3D self.STATE_INLINE_NAME + + elif self.entry.decl_type =3D=3D 'function': + self.process_proto_function(ln, line) + + else: + self.process_proto_type(ln, line) + + def process_docblock(self, ln, line): + """STATE_DOCBLOCK: within a DOC: block.""" + + if doc_end.search(line): + self.dump_section() + self.output_declaration("doc", None, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, = module=3Dself.config.modulename) + self.reset_state(ln) + + elif doc_content.search(line): + self.entry.contents +=3D doc_content.group(1) + "\n" + + def run(self): + """ + Open and process each line of a C source file. + he parsing is controlled via a state machine, and the line is pass= ed + to a different process function depending on the state. The process + function may update the state as needed. + """ + + cont =3D False + prev =3D "" + prev_ln =3D None + + try: + with open(self.fname, "r", encoding=3D"utf8", + errors=3D"backslashreplace") as fp: + for ln, line in enumerate(fp): + + line =3D line.expandtabs().strip("\n") + + # Group continuation lines on prototypes + if self.state =3D=3D self.STATE_PROTO: + if line.endswith("\\"): + prev +=3D line.removesuffix("\\") + cont =3D True + + if not prev_ln: + prev_ln =3D ln + + continue + + if cont: + ln =3D prev_ln + line =3D prev + line + prev =3D "" + cont =3D False + prev_ln =3D None + + self.config.log.debug("%d %s%s: %s", + ln, self.st_name[self.state], + self.st_inline_name[self.inline_= doc_state], + line) + + # TODO: not all states allow EXPORT_SYMBOL*, so this + # can be optimized later on to speedup parsing + self.process_export(self.config.function_table, line) + + # Hand this line to the appropriate state handler + if self.state =3D=3D self.STATE_NORMAL: + self.process_normal(ln, line) + elif self.state =3D=3D self.STATE_NAME: + self.process_name(ln, line) + elif self.state in [self.STATE_BODY, self.STATE_BODY_M= AYBE, + self.STATE_BODY_WITH_BLANK_LINE]: + self.process_body(ln, line) + elif self.state =3D=3D self.STATE_INLINE: # scanning = for inline parameters + self.process_inline(ln, line) + elif self.state =3D=3D self.STATE_PROTO: + self.process_proto(ln, line) + elif self.state =3D=3D self.STATE_DOCBLOCK: + self.process_docblock(ln, line) + except OSError: + self.config.log.error(f"Error: Cannot open file {self.fname}") + self.config.errors +=3D 1 + + +class GlobSourceFiles: + """ + Parse C source code file names and directories via an Interactor. + + """ + + def __init__(self, srctree=3DNone, valid_extensions=3DNone): + """ + Initialize valid extensions with a tuple. + + If not defined, assume default C extensions (.c and .h) + + It would be possible to use python's glob function, but it is + very slow, and it is not interactive. So, it would wait to read all + directories before actually do something. + + So, let's use our own implementation. + """ + + if not valid_extensions: + self.extensions =3D (".c", ".h") + else: + self.extensions =3D valid_extensions + + self.srctree =3D srctree + + def _parse_dir(self, dirname): + """Internal function to parse files recursively""" + + with os.scandir(dirname) as obj: + for entry in obj: + name =3D os.path.join(dirname, entry.name) + + if entry.is_dir(): + yield from self._parse_dir(name) + + if not entry.is_file(): + continue + + basename =3D os.path.basename(name) + + if not basename.endswith(self.extensions): + continue + + yield name + + def parse_files(self, file_list, file_not_found_cb): + for fname in file_list: + if self.srctree: + f =3D os.path.join(self.srctree, fname) + else: + f =3D fname + + if os.path.isdir(f): + yield from self._parse_dir(f) + elif os.path.isfile(f): + yield f + elif file_not_found_cb: + file_not_found_cb(fname) + + +class KernelFiles(): + + def parse_file(self, fname): + + doc =3D KernelDoc(self.config, fname) + doc.run() + + return doc + + def process_export_file(self, fname): + try: + with open(fname, "r", encoding=3D"utf8", + errors=3D"backslashreplace") as fp: + for line in fp: + KernelDoc.process_export(self.config.function_table, l= ine) + + except IOError: + print(f"Error: Cannot open fname {fname}", fname=3Dsys.stderr) + self.config.errors +=3D 1 + + def file_not_found_cb(self, fname): + self.config.log.error("Cannot find file %s", fname) + self.config.errors +=3D 1 + + def __init__(self, files=3DNone, verbose=3DFalse, out_style=3DNone, + werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, + wcontents_before_sections=3DFalse, + logger=3DNone, modulename=3DNone, export_file=3DNone): + """Initialize startup variables and parse all files""" + + + if not verbose: + verbose =3D bool(os.environ.get("KBUILD_VERBOSE", 0)) + + if not modulename: + modulename =3D "Kernel API" + + dt =3D datetime.now() + if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): + # use UTC TZ + to_zone =3D tz.gettz('UTC') + dt =3D dt.astimezone(to_zone) + + if not werror: + kcflags =3D os.environ.get("KCFLAGS", None) + if kcflags: + match =3D re.search(r"(\s|^)-Werror(\s|$)/", kcflags) + if match: + werror =3D True + + # reading this variable is for backwards compat just in case + # someone was calling it with the variable from outside the + # kernel's build system + kdoc_werror =3D os.environ.get("KDOC_WERROR", None) + if kdoc_werror: + werror =3D kdoc_werror + + # Set global config data used on all files + self.config =3D argparse.Namespace + + self.config.verbose =3D verbose + self.config.werror =3D werror + self.config.wreturn =3D wreturn + self.config.wshort_desc =3D wshort_desc + self.config.wcontents_before_sections =3D wcontents_before_sections + self.config.modulename =3D modulename + + self.config.function_table =3D set() + self.config.source_map =3D {} + + if not logger: + self.config.log =3D logging.getLogger("kernel-doc") + else: + self.config.log =3D logger + + self.config.kernel_version =3D os.environ.get("KERNELVERSION", + "unknown kernel versio= n'") + self.config.src_tree =3D os.environ.get("SRCTREE", None) + + self.out_style =3D out_style + self.export_file =3D export_file + + # Initialize internal variables + + self.config.errors =3D 0 + self.results =3D [] + + self.file_list =3D files + self.files =3D set() + + def parse(self): + """ + Parse all files + """ + + glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) + + # Let's use a set here to avoid duplicating files + + for fname in glob.parse_files(self.file_list, self.file_not_found_= cb): + if fname in self.files: + continue + + self.files.add(fname) + + res =3D self.parse_file(fname) + self.results.append((res.fname, res.entries)) + + if not self.files: + sys.exit(1) + + # If a list of export files was provided, parse EXPORT_SYMBOL* + # from the ones not already parsed + + if self.export_file: + files =3D self.files + + glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) + + for fname in glob.parse_files(self.export_file, + self.file_not_found_cb): + if fname not in files: + files.add(fname) + + self.process_export_file(fname) + + def out_msg(self, fname, name, arg): + # TODO: filter out unwanted parts + + return self.out_style.msg(fname, name, arg) + + def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, + symbol=3DNone, nosymbol=3DNone): + + function_table =3D self.config.function_table + + if symbol: + for s in symbol: + function_table.add(s) + + # Output none mode: only warnings will be shown + if not self.out_style: + return + + self.out_style.set_config(self.config) + + self.out_style.set_filter(export, internal, symbol, nosymbol, + function_table, enable_lineno) + + for fname, arg_tuple in self.results: + for name, arg in arg_tuple: + if self.out_msg(fname, name, arg): + ln =3D arg.get("ln", 0) + dtype =3D arg.get('type', "") + + self.config.log.warning("%s:%d Can't handle %s", + fname, ln, dtype) + + +class OutputFormat: + # output mode. + OUTPUT_ALL =3D 0 # output all symbols and doc sections + OUTPUT_INCLUDE =3D 1 # output only specified symbols + OUTPUT_EXPORTED =3D 2 # output exported symbols + OUTPUT_INTERNAL =3D 3 # output non-exported symbols + + # Virtual member to be overriden at the inherited classes + highlights =3D [] + + def __init__(self): + """Declare internal vars and set mode to OUTPUT_ALL""" + + self.out_mode =3D self.OUTPUT_ALL + self.enable_lineno =3D None + self.nosymbol =3D {} + self.symbol =3D None + self.function_table =3D set() + self.config =3D None + + def set_config(self, config): + self.config =3D config + + def set_filter(self, export, internal, symbol, nosymbol, function_tabl= e, + enable_lineno): + """ + Initialize filter variables according with the requested mode. + + Only one choice is valid between export, internal and symbol. + + The nosymbol filter can be used on all modes. + """ + + self.enable_lineno =3D enable_lineno + + if symbol: + self.out_mode =3D self.OUTPUT_INCLUDE + function_table =3D symbol + elif export: + self.out_mode =3D self.OUTPUT_EXPORTED + elif internal: + self.out_mode =3D self.OUTPUT_INTERNAL + else: + self.out_mode =3D self.OUTPUT_ALL + + if nosymbol: + self.nosymbol =3D set(nosymbol) + + if function_table: + self.function_table =3D function_table + + def highlight_block(self, block): + """ + Apply the RST highlights to a sub-block of text. + """ + + for r, sub in self.highlights: + block =3D r.sub(sub, block) + + return block + + def check_doc(self, name): + """Check if DOC should be output""" + + if self.out_mode =3D=3D self.OUTPUT_ALL: + return True + + if self.out_mode =3D=3D self.OUTPUT_INCLUDE: + if name in self.nosymbol: + return False + + if name in self.function_table: + return True + + return False + + def check_declaration(self, dtype, name): + if name in self.nosymbol: + return False + + if self.out_mode =3D=3D self.OUTPUT_ALL: + return True + + if self.out_mode in [ self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED ]: + if name in self.function_table: + return True + + if self.out_mode =3D=3D self.OUTPUT_INTERNAL: + if dtype !=3D "function": + return True + + if name not in self.function_table: + return True + + return False + + def check_function(self, fname, name, args): + return True + + def check_enum(self, fname, name, args): + return True + + def check_typedef(self, fname, name, args): + return True + + def msg(self, fname, name, args): + + dtype =3D args.get('type', "") + + if dtype =3D=3D "doc": + self.out_doc(fname, name, args) + return False + + if not self.check_declaration(dtype, name): + return False + + if dtype =3D=3D "function": + self.out_function(fname, name, args) + return False + + if dtype =3D=3D "enum": + self.out_enum(fname, name, args) + return False + + if dtype =3D=3D "typedef": + self.out_typedef(fname, name, args) + return False + + if dtype in ["struct", "union"]: + self.out_struct(fname, name, args) + return False + + # Warn if some type requires an output logic + self.config.log.warning("doesn't now how to output '%s' block", + dtype) + + return True + + # Virtual methods to be overridden by inherited classes + def out_doc(self, fname, name, args): + pass + + def out_function(self, fname, name, args): + pass + + def out_enum(self, fname, name, args): + pass + + def out_typedef(self, fname, name, args): + pass + + def out_struct(self, fname, name, args): + pass + + +class RestFormat(OutputFormat): + # """Consts and functions used by ReST output""" + + highlights =3D [ + (type_constant, r"``\1``"), + (type_constant2, r"``\1``"), + + # Note: need to escape () to avoid func matching later + (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), + (type_member, r":c:type:`\1\2\3 <\1>`"), + (type_fp_param, r"**\1\\(\\)**"), + (type_fp_param2, r"**\1\\(\\)**"), + (type_func, r"\1()"), + (type_enum, r":c:type:`\1 <\2>`"), + (type_struct, r":c:type:`\1 <\2>`"), + (type_typedef, r":c:type:`\1 <\2>`"), + (type_union, r":c:type:`\1 <\2>`"), + + # in rst this can refer to any type + (type_fallback, r":c:type:`\1`"), + (type_param_ref, r"**\1\2**") + ] + blankline =3D "\n" + + sphinx_literal =3D Re(r'^[^.].*::$', cache=3DFalse) + sphinx_cblock =3D Re(r'^\.\.\ +code-block::', cache=3DFalse) + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.lineprefix =3D "" + + def print_lineno (self, ln): + """Outputs a line number""" + + if self.enable_lineno and ln: + print(f".. LINENO {ln}") + + def output_highlight(self, args): + input_text =3D args + output =3D "" + in_literal =3D False + litprefix =3D "" + block =3D "" + + for line in input_text.strip("\n").split("\n"): + + # If we're in a literal block, see if we should drop out of it. + # Otherwise, pass the line straight through unmunged. + if in_literal: + if line.strip(): # If the line is not blank + # If this is the first non-blank line in a literal blo= ck, + # figure out the proper indent. + if not litprefix: + r =3D Re(r'^(\s*)') + if r.match(line): + litprefix =3D '^' + r.group(1) + else: + litprefix =3D "" + + output +=3D line + "\n" + elif not Re(litprefix).match(line): + in_literal =3D False + else: + output +=3D line + "\n" + else: + output +=3D line + "\n" + + # Not in a literal block (or just dropped out) + if not in_literal: + block +=3D line + "\n" + if self.sphinx_literal.match(line) or self.sphinx_cblock.m= atch(line): + in_literal =3D True + litprefix =3D "" + output +=3D self.highlight_block(block) + block =3D "" + + # Handle any remaining block + if block: + output +=3D self.highlight_block(block) + + # Print the output with the line prefix + for line in output.strip("\n").split("\n"): + print(self.lineprefix + line) + + def out_section(self, args, out_reference=3DFalse): + """ + Outputs a block section. + + This could use some work; it's used to output the DOC: sections, a= nd + starts by putting out the name of the doc section itself, but that + tends to duplicate a header already in the template file. + """ + + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + section_start_lines =3D args.get('section_start_lines', {}) + + for section in sectionlist: + # Skip sections that are in the nosymbol_table + if section in self.nosymbol: + continue + + if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: + if out_reference: + print(f".. _{section}:\n") + + if not self.symbol: + print(f'{self.lineprefix}**{section}**\n') + + self.print_lineno(section_start_lines.get(section, 0)) + self.output_highlight(sections[section]) + print() + print() + + def out_doc(self, fname, name, args): + if not self.check_doc(name): + return + + self.out_section(args, out_reference=3DTrue) + + def out_function(self, fname, name, args): + + oldprefix =3D self.lineprefix + signature =3D "" + + func_macro =3D args.get('func_macro', False) + if func_macro: + signature =3D args['function'] + else: + if args.get('functiontype'): + signature =3D args['functiontype'] + " " + signature +=3D args['function'] + " (" + + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) + + ln =3D args.get('ln', 0) + + count =3D 0 + for parameter in parameterlist: + if count !=3D 0: + signature +=3D ", " + count +=3D 1 + dtype =3D args['parametertypes'].get(parameter, "") + + if function_pointer.search(dtype): + signature +=3D function_pointer.group(1) + parameter + fun= ction_pointer.group(3) + else: + signature +=3D dtype + + if not func_macro: + signature +=3D ")" + + if args.get('typedef') or not args.get('functiontype'): + print(f".. c:macro:: {args['function']}\n") + + if args.get('typedef'): + self.print_lineno(ln) + print(" **Typedef**: ", end=3D"") + self.lineprefix =3D "" + self.output_highlight(args.get('purpose', "")) + print("\n\n**Syntax**\n") + print(f" ``{signature}``\n") + else: + print(f"``{signature}``\n") + else: + print(f".. c:function:: {signature}\n") + + if not args.get('typedef'): + self.print_lineno(ln) + self.lineprefix =3D " " + self.output_highlight(args.get('purpose', "")) + print() + + # Put descriptive text into a container (HTML
) to help set + # function prototypes apart + self.lineprefix =3D " " + + if parameterlist: + print(".. container:: kernelindent\n") + print(f"{self.lineprefix}**Parameters**\n") + + for parameter in parameterlist: + parameter_name =3D Re(r'\[.*').sub('', parameter) + dtype =3D args['parametertypes'].get(parameter, "") + + if dtype: + print(f"{self.lineprefix}``{dtype}``") + else: + print(f"{self.lineprefix}``{parameter}``") + + self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) + + self.lineprefix =3D " " + if parameter_name in parameterdescs and \ + parameterdescs[parameter_name] !=3D KernelDoc.undescribed: + + self.output_highlight(parameterdescs[parameter_name]) + print() + else: + print(f"{self.lineprefix}*undescribed*\n") + self.lineprefix =3D " " + + self.out_section(args) + self.lineprefix =3D oldprefix + + def out_enum(self, fname, name, args): + + oldprefix =3D self.lineprefix + name =3D args.get('enum', '') + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + ln =3D args.get('ln', 0) + + print(f"\n\n.. c:enum:: {name}\n") + + self.print_lineno(ln) + self.lineprefix =3D " " + self.output_highlight(args.get('purpose', '')) + print() + + print(".. container:: kernelindent\n") + outer =3D self.lineprefix + " " + self.lineprefix =3D outer + " " + print(f"{outer}**Constants**\n") + + for parameter in parameterlist: + print(f"{outer}``{parameter}``") + + if parameterdescs.get(parameter, '') !=3D KernelDoc.undescribe= d: + self.output_highlight(parameterdescs[parameter]) + else: + print(f"{self.lineprefix}*undescribed*\n") + print() + + self.lineprefix =3D oldprefix + self.out_section(args) + + def out_typedef(self, fname, name, args): + + oldprefix =3D self.lineprefix + name =3D args.get('typedef', '') + ln =3D args.get('ln', 0) + + print(f"\n\n.. c:type:: {name}\n") + + self.print_lineno(ln) + self.lineprefix =3D " " + + self.output_highlight(args.get('purpose', '')) + + print() + + self.lineprefix =3D oldprefix + self.out_section(args) + + def out_struct(self, fname, name, args): + + name =3D args.get('struct', "") + purpose =3D args.get('purpose', "") + declaration =3D args.get('definition', "") + dtype =3D args.get('type', "struct") + ln =3D args.get('ln', 0) + + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) + + print(f"\n\n.. c:{dtype}:: {name}\n") + + self.print_lineno(ln) + + oldprefix =3D self.lineprefix + self.lineprefix +=3D " " + + self.output_highlight(purpose) + print() + + print(".. container:: kernelindent\n") + print(f"{self.lineprefix}**Definition**::\n") + + self.lineprefix =3D self.lineprefix + " " + + declaration =3D declaration.replace("\t", self.lineprefix) + + print(f"{self.lineprefix}{dtype} {name}" + ' {') + print(f"{declaration}{self.lineprefix}" + "};\n") + + self.lineprefix =3D " " + print(f"{self.lineprefix}**Members**\n") + for parameter in parameterlist: + if not parameter or parameter.startswith("#"): + continue + + parameter_name =3D parameter.split("[", maxsplit=3D1)[0] + + if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: + continue + + self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) + + print(f"{self.lineprefix}``{parameter}``") + + self.lineprefix =3D " " + self.output_highlight(parameterdescs[parameter_name]) + self.lineprefix =3D " " + + print() + + print() + + self.lineprefix =3D oldprefix + self.out_section(args) + + +class ManFormat(OutputFormat): + """Consts and functions used by man pages output""" + + highlights =3D ( + (type_constant, r"\1"), + (type_constant2, r"\1"), + (type_func, r"\\fB\1\\fP"), + (type_enum, r"\\fI\1\\fP"), + (type_struct, r"\\fI\1\\fP"), + (type_typedef, r"\\fI\1\\fP"), + (type_union, r"\\fI\1\\fP"), + (type_param, r"\\fI\1\\fP"), + (type_param_ref, r"\\fI\1\2\\fP"), + (type_member, r"\\fI\1\2\3\\fP"), + (type_fallback, r"\\fI\1\\fP") + ) + blankline =3D "" + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + + dt =3D datetime.now() + if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): + # use UTC TZ + to_zone =3D tz.gettz('UTC') + dt =3D dt.astimezone(to_zone) + + self.man_date =3D dt.strftime("%B %Y") + + def output_highlight(self, block): + + contents =3D self.highlight_block(block) + + if isinstance(contents, list): + contents =3D "\n".join(contents) + + for line in contents.strip("\n").split("\n"): + line =3D Re(r"^\s*").sub("", line) + + if line and line[0] =3D=3D ".": + print("\\&" + line) + else: + print(line) + + def out_doc(self, fname, name, args): + module =3D args.get('module') + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{module}" 9 "{module}" "{self.man_date}" "API Manual"= LINUX') + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections.get(section)) + + def out_function(self, fname, name, args): + """output function in man""" + + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{args['function']}" 9 "{args['function']}" "{self.man= _date}" "Kernel Hacker\'s Manual" LINUX') + + print(".SH NAME") + print(f"{args['function']} \\- {args['purpose']}") + + print(".SH SYNOPSIS") + if args.get('functiontype', ''): + print(f'.B "{args['functiontype']}" {args['function']}') + else: + print(f'.B "{args['function']}') + + count =3D 0 + parenth =3D "(" + post =3D "," + + for parameter in parameterlist: + if count =3D=3D len(parameterlist) - 1: + post =3D ");" + + dtype =3D args['parametertypes'].get(parameter, "") + if function_pointer.match(dtype): + # Pointer-to-function + print(f'".BI "{parenth}{function_pointer.group(1)}" " ") (= {function_pointer.group(2)}){post}"') + else: + dtype =3D Re(r'([^\*])$').sub(r'\1 ', dtype) + + print(f'.BI "{parenth}{dtype}" "{post}"') + count +=3D 1 + parenth =3D "" + + if parameterlist: + print(".SH ARGUMENTS") + + for parameter in parameterlist: + parameter_name =3D re.sub(r'\[.*', '', parameter) + + print(f'.IP "{parameter}" 12') + self.output_highlight(parameterdescs.get(parameter_name, "")) + + for section in sectionlist: + print(f'.SH "{section.upper()}"') + self.output_highlight(sections[section]) + + def out_enum(self, fname, name, args): + + name =3D args.get('enum', '') + parameterlist =3D args.get('parameterlist', []) + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{args['module']}" 9 "enum {args['enum']}" "{self.man_= date}" "API Manual" LINUX') + + print(".SH NAME") + print(f"enum {args['enum']} \\- {args['purpose']}") + + print(".SH SYNOPSIS") + print(f"enum {args['enum']}" + " {") + + count =3D 0 + for parameter in parameterlist: + print(f'.br\n.BI " {parameter}"') + if count =3D=3D len(parameterlist) - 1: + print("\n};") + else: + print(", \n.br") + + count +=3D 1 + + print(".SH Constants") + + for parameter in parameterlist: + parameter_name =3D Re(r'\[.*').sub('', parameter) + print(f'.IP "{parameter}" 12') + self.output_highlight(args['parameterdescs'].get(parameter_nam= e, "")) + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections[section]) + + def out_typedef(self, fname, name, args): + module =3D args.get('module') + typedef =3D args.get('typedef') + purpose =3D args.get('purpose') + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual= " LINUX') + + print(".SH NAME") + print(f"typedef {typedef} \\- {purpose}") + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections.get(section)) + + def out_struct(self, fname, name, args): + module =3D args.get('module') + struct_type =3D args.get('type') + struct_name =3D args.get('struct') + purpose =3D args.get('purpose') + definition =3D args.get('definition') + sectionlist =3D args.get('sectionlist', []) + parameterlist =3D args.get('parameterlist', []) + sections =3D args.get('sections', {}) + parameterdescs =3D args.get('parameterdescs', {}) + + print(f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_= date}" "API Manual" LINUX') + + print(".SH NAME") + print(f"{struct_type} {struct_name} \\- {purpose}") + + # Replace tabs with two spaces and handle newlines + declaration =3D definition.replace("\t", " ") + declaration =3D Re(r"\n").sub('"\n.br\n.BI "', declaration) + + print(".SH SYNOPSIS") + print(f"{struct_type} {struct_name} " + "{" +"\n.br") + print(f'.BI "{declaration}\n' + "};\n.br\n") + + print(".SH Members") + for parameter in parameterlist: + if parameter.startswith("#"): + continue + + parameter_name =3D re.sub(r"\[.*", "", parameter) + + if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: + continue + + print(f'.IP "{parameter}" 12') + self.output_highlight(parameterdescs.get(parameter_name)) + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections.get(section)) + + +# Command line interface + + +DESC =3D """ +Read C language source or header FILEs, extract embedded documentation com= ments, +and print formatted documentation to standard output. + +The documentation comments are identified by the "/**" opening comment mar= k. + +See Documentation/doc-guide/kernel-doc.rst for the documentation comment s= yntax. +""" + +EXPORT_FILE_DESC =3D """ +Specify an additional FILE in which to look for EXPORT_SYMBOL information. + +May be used multiple times. +""" + +EXPORT_DESC =3D """ +Only output documentation for the symbols that have been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +INTERNAL_DESC =3D """ +Only output documentation for the symbols that have NOT been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +FUNCTION_DESC =3D """ +Only output documentation for the given function or DOC: section +title. All other functions and DOC: sections are ignored. + +May be used multiple times. +""" + +NOSYMBOL_DESC =3D """ +Exclude the specified symbol from the output documentation. + +May be used multiple times. +""" + +FILES_DESC =3D """ +Header and C source files to be parsed. +""" + +WARN_CONTENTS_BEFORE_SECTIONS_DESC =3D """ +Warns if there are contents before sections (deprecated). + +This option is kept just for backward-compatibility, but it does nothing, +neither here nor at the original Perl script. +""" + + +def main(): + """Main program""" + + parser =3D argparse.ArgumentParser(formatter_class=3Dargparse.RawTextH= elpFormatter, + description=3DDESC) + + # Normal arguments + + parser.add_argument("-v", "-verbose", "--verbose", action=3D"store_tru= e", + help=3D"Verbose output, more warnings and other in= formation.") + + parser.add_argument("-d", "-debug", "--debug", action=3D"store_true", + help=3D"Enable debug messages") + + parser.add_argument("-M", "-modulename", "--modulename", + help=3D"Allow setting a module name at the output.= ") + + parser.add_argument("-l", "-enable-lineno", "--enable_lineno", + action=3D"store_true", + help=3D"Enable line number output (only in ReST mo= de)") + + # Arguments to control the warning behavior + + parser.add_argument("-Wreturn", "--wreturn", action=3D"store_true", + help=3D"Warns about the lack of a return markup on= functions.") + + parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-d= esc", + action=3D"store_true", + help=3D"Warns if initial short description is miss= ing") + + parser.add_argument("-Wcontents-before-sections", + "--wcontents-before-sections", action=3D"store_tru= e", + help=3DWARN_CONTENTS_BEFORE_SECTIONS_DESC) + + parser.add_argument("-Wall", "--wall", action=3D"store_true", + help=3D"Enable all types of warnings") + + parser.add_argument("-Werror", "--werror", action=3D"store_true", + help=3D"Treat warnings as errors.") + + parser.add_argument("-export-file", "--export-file", action=3D'append', + help=3DEXPORT_FILE_DESC) + + # Output format mutually-exclusive group + + out_group =3D parser.add_argument_group("Output format selection (mutu= ally exclusive)") + + out_fmt =3D out_group.add_mutually_exclusive_group() + + out_fmt.add_argument("-m", "-man", "--man", action=3D"store_true", + help=3D"Output troff manual page format.") + out_fmt.add_argument("-r", "-rst", "--rst", action=3D"store_true", + help=3D"Output reStructuredText format (default).= ") + out_fmt.add_argument("-N", "-none", "--none", action=3D"store_true", + help=3D"Do not output documentation, only warning= s.") + + # Output selection mutually-exclusive group + + sel_group =3D parser.add_argument_group("Output selection (mutually ex= clusive)") + sel_mut =3D sel_group.add_mutually_exclusive_group() + + sel_mut.add_argument("-e", "-export", "--export", action=3D'store_true= ', + help=3DEXPORT_DESC) + + sel_mut.add_argument("-i", "-internal", "--internal", action=3D'store_= true', + help=3DINTERNAL_DESC) + + sel_mut.add_argument("-s", "-function", "--symbol", action=3D'append', + help=3DFUNCTION_DESC) + + # This one is valid for all 3 types of filter + parser.add_argument("-n", "-nosymbol", "--nosymbol", action=3D'append', + help=3DNOSYMBOL_DESC) + + parser.add_argument("files", metavar=3D"FILE", + nargs=3D"+", help=3DFILES_DESC) + + args =3D parser.parse_args() + + if args.wall: + args.wreturn =3D True + args.wshort_desc =3D True + args.wcontents_before_sections =3D True + + if not args.debug: + level =3D logging.INFO + else: + level =3D logging.DEBUG + + if args.man: + out_style =3D ManFormat() + elif args.none: + out_style =3D None + else: + out_style =3D RestFormat() + + logging.basicConfig(level=3Dlevel, format=3D"%(levelname)s: %(message)= s") + + kfiles =3D KernelFiles(files=3Dargs.files, verbose=3Dargs.verbose, + out_style=3Dout_style, werror=3Dargs.werror, + wreturn=3Dargs.wreturn, wshort_desc=3Dargs.wshort= _desc, + wcontents_before_sections=3Dargs.wcontents_before= _sections, + modulename=3Dargs.modulename, + export_file=3Dargs.export_file) + + kfiles.parse() + + kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.export, + internal=3Dargs.internal, symbol=3Dargs.symbol, + nosymbol=3Dargs.nosymbol) + + +# Call main method +if __name__ =3D=3D "__main__": + main() --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8600724503A; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=GaRdVbf5On3Uic7TNHf2xvjK0iZQHfcejZUh5h7Xt8vN1bztPDPJ9dsw7WbBBBtF/De0E0qk2BZPJ+YP/yRHrS7sM9axBv9yQ7nLimXQLHn+dusf/X1ZfOJT4XtE8HEOPPmXWJxZ0gt0VDmuY3trF7IcSf/BHbp1WM9XMRiY+20= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=lpGcciKaov3Y13LlsgFK4uZcLIWPLc8PIxEf/5bFm3o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WifcTYk7jy6R478p9Gtg8nf1Ef/zwY8ycV5IKBKN9sItAg01GC25qSYQHN0eTVH7KfVbxTAs9Rs89wy/tCcHIlrPkFGCSfQILvIISy4ldhQOlwQ4fHnbwQwRTP5jHy0HZyaX99Td3tKbFIk4MEw66arZQWjmaeUbbxiHUS9rNVc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=S4XWhXqB; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="S4XWhXqB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DCDC0C4CEF2; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=lpGcciKaov3Y13LlsgFK4uZcLIWPLc8PIxEf/5bFm3o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S4XWhXqBtMDQRmkQZKsQMsdrXlQ+NW2QUaT7k+r0EqZ5kEZ/JkHuEq1jCCKFbN6SU xe1Xy+Sgdl61JrdQBliaqCk2KphAexH8x674UWQvpXIT7mwwz6t+D1P4o4RgQXnNCG XjIXYxcnjBTr2kiycyR0Irq0HXiyzqqECF1mT+vMHrpru5+D+9IEsoM1SozccMP3SV UvGl9g9ddwxCLbn4hkdOmIci91zxu1nsEBtj1wVSPuq6EUQY0M6FDrwH1QgUxr9nZ5 o53othZ0DRVaq55V9LPOk7q0ONClfaG1IUSfoWcZ5x7XMmCvTXpaA8o1/cMVXkUvGe FcOOLQWoWbF1Q== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p44-0GIa; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 10/39] scripts/kernel-doc.py: output warnings the same way as kerneldoc Date: Mon, 24 Feb 2025 10:08:16 +0100 Message-ID: <1ee48bdad5030aebe5c2442d805ac6ca922a86d1.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Add a formatter to logging to produce outputs in a similar way to kernel-doc. This should help making it more compatible with existing scripts. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 5cf5ed63f215..8bc0470d3720 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -2640,6 +2640,11 @@ neither here nor at the original Perl script. """ =20 =20 +class MsgFormatter(logging.Formatter): + def format(self, record): + record.levelname =3D record.levelname.capitalize() + return logging.Formatter.format(self, record) + def main(): """Main program""" =20 @@ -2724,10 +2729,19 @@ def main(): args.wshort_desc =3D True args.wcontents_before_sections =3D True =20 + logger =3D logging.getLogger() + if not args.debug: - level =3D logging.INFO + logger.setLevel(logging.INFO) else: - level =3D logging.DEBUG + logger.setLevel(logging.DEBUG) + + formatter =3D MsgFormatter('%(levelname)s: %(message)s') + + handler =3D logging.StreamHandler() + handler.setFormatter(formatter) + + logger.addHandler(handler) =20 if args.man: out_style =3D ManFormat() @@ -2736,8 +2750,6 @@ def main(): else: out_style =3D RestFormat() =20 - logging.basicConfig(level=3Dlevel, format=3D"%(levelname)s: %(message)= s") - kfiles =3D KernelFiles(files=3Dargs.files, verbose=3Dargs.verbose, out_style=3Dout_style, werror=3Dargs.werror, wreturn=3Dargs.wreturn, wshort_desc=3Dargs.wshort= _desc, --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 848E1245039; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=CEXG2lDYWizWXpifM9JYMo5Cc8iXYzuQaPznmsnvxFukT8SPS5SIr6nbOqpz2mdAEZgEKn8UaELg+M01D4TQtx5sBJ+NSRyv4Ni8d0q84Y5SJ+3Nm2+XWmnHkG4WtNPOB83sM/J+SwstLTJyjMk0SEAr9dsPxN6gUM+se5RR/XY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=g3yjKUYPJng0/MFnsVcxoDKnYWevbS21H1bMLGz3lcY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fe87NIVi3pOB6xZGFWK1G0XcO6BoJCldXHnil215OywwF0A0G9HxMcET1dFg1H0wvpFYEP5ytlZWNnqZKcVo16paSwIKP+nO+GJCyPWqomtGRt60zRiXOvGPNXVCJDVzg7FMhbK4WZWuwjUfKIxgXg4NgTQcSl5/sA2i/1RdiWM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=s/3bMDI1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="s/3bMDI1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D8F7AC4AF09; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388142; bh=g3yjKUYPJng0/MFnsVcxoDKnYWevbS21H1bMLGz3lcY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=s/3bMDI1lwgqp6ZprTksJ5lTicEc+aV3eqlJ0aGAk6W1wvHcUpojedbSZ1wHv/sNr ZgJ7gcOqGA2fXxU0kOG8Bzy3E04ivqPv+5RkeMvGsbvkrNMPiko/ngSp6R3YRsAeSZ 8hWNFo6jR7INl+hSh279x9KJeALqSmq1eQMONNdkJjBPvd0iIhDmBHz6CpYA/VThfZ dWKIQkKjRabYvw+avP5SQ19BEHwO30jIrpdrRRvlAgqnw59PCODNT20bJyhWPa1UFS 9r5HUmh6JwP5Ec2G5ey02EyDq6rHRQYs1yIR928N+/F2zpHL4nX/aLz3T9czj+vb55 tta8Wh8uhWkwg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p48-0NA9; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 11/39] scripts/kernel-doc.py: better handle empty sections Date: Mon, 24 Feb 2025 10:08:17 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" While doing the conversion, we opted to skip empty sections (description, return), but this makes harder to see the differences between kernel-doc (Perl) and kernel-doc.py. Also, the logic doesn't always work properly. So, change the way this is done by adding an extra step to remove such sections, doing it only for Return and Description. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 8bc0470d3720..886256e87692 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -242,6 +242,19 @@ class KernelDoc: name =3D self.entry.section contents =3D self.entry.contents =20 + # TODO: we can prevent dumping empty sections here with: + # + # if self.entry.contents.strip("\n"): + # if start_new: + # self.entry.section =3D self.section_default + # self.entry.contents =3D "" + # + # return + # + # But, as we want to be producing the same output of the + # venerable kernel-doc Perl tool, let's just output everything, + # at least for now + if type_param.match(name): name =3D type_param.group(1) =20 @@ -298,6 +311,19 @@ class KernelDoc: =20 args["type"] =3D dtype =20 + # TODO: use colletions.OrderedDict + + sections =3D args.get('sections', {}) + sectionlist =3D args.get('sectionlist', []) + + # Drop empty sections + # TODO: improve it to emit warnings + for section in [ "Description", "Return" ]: + if section in sectionlist: + if not sections[section].rstrip(): + del sections[section] + sectionlist.remove(section) + self.entries.append((name, args)) =20 self.config.log.debug("Output: %s:%s =3D %s", dtype, name, pformat= (args)) @@ -401,7 +427,7 @@ class KernelDoc: # to ignore "[blah" in a parameter string. =20 self.entry.parameterlist.append(param) - org_arg =3D Re(r'\s\s+').sub(' ', org_arg, count=3D1) + org_arg =3D Re(r'\s\s+').sub(' ', org_arg) self.entry.parametertypes[param] =3D org_arg =20 def save_struct_actual(self, actual): @@ -1309,8 +1335,7 @@ class KernelDoc: return =20 if doc_end.search(line): - if self.entry.contents.strip("\n"): - self.dump_section() + self.dump_section() =20 # Look for doc_com + + doc_end: r =3D Re(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8EE02246348; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=IM1CFZeMa/2pTNQNDJykTtSEk9Miw2Agw6125RadBPsbcc6FvIt7bRl9bk1fzS/triVMdi07OfS419FUPfBFPRG3TsY279GMcZjsyJnWciN5jU7lq8DP8EuXcB8eEBgGqMXa4AF6jc7AQ+5S7HfKgKL4i9ukKGyCxbHwPi9vqws= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=TgQCSO8WmP+NxroK7/npEWybYRuZ/nrFGN7hk4txen8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KGVHHsQGtLOFGx12ajcV/V4EHNdgvVOW1MqFGM4ZNXAcdpvYuA0/vWO8GYrW7SdXxBVeAlTTQ5kEtSyIRpWb39Roa3ZxNn3z+Ud32xcv45lNyf7bCEfSIUT29lAznEOXTf3MvEziw5R2vXrS7wJd9nXTc3ZxrCduZJ975H+bSQk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WqFm8978; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WqFm8978" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E6B5AC4CEF5; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=TgQCSO8WmP+NxroK7/npEWybYRuZ/nrFGN7hk4txen8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WqFm8978HeGXbmxTMt3UeTWMJZQK4Rvn3p6z8d6eVZ+sNQsYiWUPuyMqWIqiMsWPq UxiKGwFUDLMLd5znXT0+bHIC/QpBiD0LNmNI4w//nv7QHLq+peVhtTMjoSrNYSiyk/ JCFAE962zm7nTzVqLREg+oM3/DgN07Dc3dQZIq3hX9NY3eFAgi0x4Suoq0pRlI1f+o vjn1u6Ph5CLWO2rycs98YSYkeKYMQ7gHoFe+6H+3nnwThF7NLRxUbo7e4UZtO+VTVP 1huw8D4/1w0scCG4ZgMpzHZiHw4Ys54mYw/m/ICtm0j4qbZ8mVhkyuqU5PrPUgRQQ1 D7bbtW5pFXAeQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4C-0U74; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 12/39] scripts/kernel-doc.py: properly handle struct_group macros Date: Mon, 24 Feb 2025 10:08:18 +0100 Message-ID: <1253e7fb947b3e53f961ab100e2c26b92450c445.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Handing nested parenthesis with regular expressions is not an easy task. It is even harder with Python's re module, as it has a limited subset of regular expressions, missing more advanced features. We might use instead Python regex module, but still the regular expressions are very hard to understand. So, instead, add a logic to properly match delimiters. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 220 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 7 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 886256e87692..3decc94edb1e 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -92,6 +92,172 @@ class Re: def group(self, num): return self.last_match.group(num) =20 +class NestedMatch: + """ + Finding nested delimiters is hard with regular expressions. It is + even harder on Python with its normal re module, as there are several + advanced regular expressions that are missing. + + This is the case of this pattern: + + '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' + + which is used to properly match open/close parenthesis of the + string search STRUCT_GROUP(), + + Add a class that counts pairs of delimiters, using it to match and + replace nested expressions. + + The original approach was suggested by: + https://stackoverflow.com/questions/5454322/python-how-to-match-ne= sted-parentheses-with-regex + + Although I re-implemented it to make it more generic and match 3 types + of delimiters. The logic checks if delimiters are paired. If not, it + will ignore the search string. + """ + + # TODO: + # Right now, regular expressions to match it are defined only up to + # the start delimiter, e.g.: + # + # \bSTRUCT_GROUP\( + # + # is similar to: STRUCT_GROUP\((.*)\) + # except that the content inside the match group is delimiter's aligne= d. + # + # The content inside parenthesis are converted into a single replace + # group (e.g. r`\1'). + # + # It would be nice to change such definition to support multiple + # match groups, allowing a regex equivalent to. + # + # FOO\((.*), (.*), (.*)\) + # + # it is probably easier to define it not as a regular expression, but + # with some lexical definition like: + # + # FOO(arg1, arg2, arg3) + + + DELIMITER_PAIRS =3D { + '{': '}', + '(': ')', + '[': ']', + } + + RE_DELIM =3D re.compile(r'[\{\}\[\]\(\)]') + + def _search(self, regex, line): + """ + Finds paired blocks for a regex that ends with a delimiter. + + The suggestion of using finditer to match pairs came from: + https://stackoverflow.com/questions/5454322/python-how-to-match-ne= sted-parentheses-with-regex + but I ended using a different implementation to align all three ty= pes + of delimiters and seek for an initial regular expression. + + The algorithm seeks for open/close paired delimiters and place them + into a stack, yielding a start/stop position of each match when t= he + stack is zeroed. + + The algorithm shoud work fine for properly paired lines, but will + silently ignore end delimiters that preceeds an start delimiter. + This should be OK for kernel-doc parser, as unaligned delimiters + would cause compilation errors. So, we don't need to rise exceptio= ns + to cover such issues. + """ + + stack =3D [] + + for match_re in regex.finditer(line): + start =3D match_re.start() + offset =3D match_re.end() + + d =3D line[offset -1] + if d not in self.DELIMITER_PAIRS: + continue + + end =3D self.DELIMITER_PAIRS[d] + stack.append(end) + + for match in self.RE_DELIM.finditer(line[offset:]): + pos =3D match.start() + offset + + d =3D line[pos] + + if d in self.DELIMITER_PAIRS: + end =3D self.DELIMITER_PAIRS[d] + + stack.append(end) + continue + + # Does the end delimiter match what it is expected? + if stack and d =3D=3D stack[-1]: + stack.pop() + + if not stack: + yield start, offset, pos + 1 + break + + def search(self, regex, line): + """ + This is similar to re.search: + + It matches a regex that it is followed by a delimiter, + returning occurrences only if all delimiters are paired. + """ + + for t in self._search(regex, line): + + yield line[t[0]:t[2]] + + def sub(self, regex, sub, line, count=3D0): + """ + This is similar to re.sub: + + It matches a regex that it is followed by a delimiter, + replacing occurrences only if all delimiters are paired. + + if r'\1' is used, it works just like re: it places there the + matched paired data with the delimiter stripped. + + If count is different than zero, it will replace at most count + items. + """ + out =3D "" + + cur_pos =3D 0 + n =3D 0 + + found =3D False + for start, end, pos in self._search(regex, line): + out +=3D line[cur_pos:start] + + # Value, ignoring start/end delimiters + value =3D line[end:pos - 1] + + # replaces \1 at the sub string, if \1 is used there + new_sub =3D sub + new_sub =3D new_sub.replace(r'\1', value) + + out +=3D new_sub + + # Drop end ';' if any + if line[pos] =3D=3D ';': + pos +=3D 1 + + cur_pos =3D pos + n +=3D 1 + + if count and count >=3D n: + break + + # Append the remaining string + l =3D len(line) + out +=3D line[cur_pos:l] + + return out + # # Regular expressions used to parse kernel-doc markups at KernelDoc class. # @@ -663,22 +829,49 @@ class KernelDoc: (Re(r'\s*____cacheline_aligned_in_smp', re.S), ' '), (Re(r'\s*____cacheline_aligned', re.S), ' '), =20 - # Unwrap struct_group() based on this definition: + # Unwrap struct_group macros based on this definition: # __struct_group(TAG, NAME, ATTRS, MEMBERS...) # which has variants like: struct_group(NAME, MEMBERS...) + # Only MEMBERS arguments require documentation. + # + # Parsing them happens on two steps: + # + # 1. drop struct group arguments that aren't at MEMBERS, + # storing them as STRUCT_GROUP(MEMBERS) + # + # 2. remove STRUCT_GROUP() ancillary macro. + # + # The original logic used to remove STRUCT_GROUP() using an + # advanced regex: + # + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; + # + # with two patterns that are incompatible with + # Python re module, as it has: + # + # - a recursive pattern: (?1) + # - an atomic grouping: (?>...) + # + # I tried a simpler version: but it didn't work either: + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; + # + # As it doesn't properly match the end parenthesis on some cas= es. + # + # So, a better solution was crafted: there's now a NestedMatch + # class that ensures that delimiters after a search are proper= ly + # matched. So, the implementation to drop STRUCT_GROUP() will = be + # handled in separate. =20 (Re(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), (Re(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_G= ROUP('), (Re(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r's= truct \1 \2; STRUCT_GROUP('), (Re(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROU= P('), =20 - # This is incompatible with Python re, as it uses: - # recursive patterns ((?1)) and atomic grouping ((?>...)): - # '\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;' - # Let's see if this works instead: - (Re(r'\bSTRUCT_GROUP\(([^\)]+)\)[^;]*;', re.S), r'\1'), - # Replace macros + # + # TODO: it is better to also move those to the NestedMatch log= ic, + # to ensure that parenthesis will be properly matched. + (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),= r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), (Re(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DE= CLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), (Re(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pat= tern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), @@ -690,9 +883,22 @@ class KernelDoc: (Re(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S)= , r'__u32 \1'), ] =20 + # Regexes here are guaranteed to have the end limiter matching + # the start delimiter. Yet, right now, only one replace group + # is allowed. + + sub_nested_prefixes =3D [ + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), + ] + for search, sub in sub_prefixes: members =3D search.sub(sub, members) =20 + nested =3D NestedMatch() + + for search, sub in sub_nested_prefixes: + members =3D nested.sub(search, sub, members) + # Keeps the original declaration as-is declaration =3D members =20 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AFF8924A04E; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=ZPRUPh6RCCaLPrK1LQ2RDslRdfNf2y9WAqg7qVey3YPPumA1W9xyGh2W+rHOncu1fBWj9yH0+EVJiqyy5fBBg2h2unrErrVhWj23BgjxRcbNMJ7sk2LQjprivQGI3cSx01Vg93Yh6ZC2s+wD/W+p1G4hjhQTeFNIB1QTWE7CoAo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=2to40pLeg8q0+VRpVnj4qXIaza6aM03uTe1Bu/KQtIo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u9q6CvFVWtndt530iLM3yazInw6KUUIBTpEJLeWmo14FEsj784mFFzHA//c9bthCowxd7cCiLRbu3KI5PbxJWGK8+Nh0P5Vvnw9zCuKPTud80xRTHek5Yxu5VFS/6R94KcANvm8TNXHKagzej74PZbbV5lcenG+FeMlhDrJWYKs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kbzxkpIl; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kbzxkpIl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E9606C4CEFA; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=2to40pLeg8q0+VRpVnj4qXIaza6aM03uTe1Bu/KQtIo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kbzxkpIl2YAxrMBEIRj9dDBT5nYy4iHyG2gAOsC1LYNfGnarih3L4Dv4UE7iF/YlJ vESgy27JopNcQUSF6FpbEI0nGVnaiJ/quFGV309RP0b2Dizd0VRmTCY3GX5REL/EAv DZ6fYmyA78xcehuIBcWVovc8dL8ydIOZMa2lqUFcH4rseqVm7y7q4j18yxqWQb1rEd DYf/n3m3JbR0Ltaxk5Iu65H5HKpiKo4f8+P9aFV7FJvoLS982DNc8XaHspXxS8GuJ6 hiJTLVSASwi1La3cg73Cn7VWLTL2o/U8bqpdyQ//oV9aol0n5OtR8i7UU67zSYYb7m z6B4r5lNlLk1A== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4G-0bAo; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 13/39] scripts/kernel-doc.py: move regex methods to a separate file Date: Mon, 24 Feb 2025 10:08:19 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" In preparation for letting kerneldoc Sphinx extension to import Python libraries, move regex ancillary classes to a separate file. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 223 +---------------------------- scripts/lib/kdoc/kdoc_re.py | 272 ++++++++++++++++++++++++++++++++++++ 2 files changed, 277 insertions(+), 218 deletions(-) create mode 100755 scripts/lib/kdoc/kdoc_re.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 3decc94edb1e..faae66aa6ead 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -35,228 +35,15 @@ from pprint import pformat =20 from dateutil import tz =20 -# Local cache for regular expressions -re_cache =3D {} +# Import Python modules =20 +LIB_DIR =3D "lib/kdoc" +SRC_DIR =3D os.path.dirname(os.path.realpath(__file__)) =20 -class Re: - """ - Helper class to simplify regex declaration and usage, +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) =20 - It calls re.compile for a given pattern. It also allows adding - regular expressions and define sub at class init time. +from kdoc_re import Re, NestedMatch =20 - Regular expressions can be cached via an argument, helping to speedup - searches. - """ - - def _add_regex(self, string, flags): - if string in re_cache: - self.regex =3D re_cache[string] - else: - self.regex =3D re.compile(string, flags=3Dflags) - - if self.cache: - re_cache[string] =3D self.regex - - def __init__(self, string, cache=3DTrue, flags=3D0): - self.cache =3D cache - self.last_match =3D None - - self._add_regex(string, flags) - - def __str__(self): - return self.regex.pattern - - def __add__(self, other): - return Re(str(self) + str(other), cache=3Dself.cache or other.cach= e, - flags=3Dself.regex.flags | other.regex.flags) - - def match(self, string): - self.last_match =3D self.regex.match(string) - return self.last_match - - def search(self, string): - self.last_match =3D self.regex.search(string) - return self.last_match - - def findall(self, string): - return self.regex.findall(string) - - def split(self, string): - return self.regex.split(string) - - def sub(self, sub, string, count=3D0): - return self.regex.sub(sub, string, count=3Dcount) - - def group(self, num): - return self.last_match.group(num) - -class NestedMatch: - """ - Finding nested delimiters is hard with regular expressions. It is - even harder on Python with its normal re module, as there are several - advanced regular expressions that are missing. - - This is the case of this pattern: - - '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' - - which is used to properly match open/close parenthesis of the - string search STRUCT_GROUP(), - - Add a class that counts pairs of delimiters, using it to match and - replace nested expressions. - - The original approach was suggested by: - https://stackoverflow.com/questions/5454322/python-how-to-match-ne= sted-parentheses-with-regex - - Although I re-implemented it to make it more generic and match 3 types - of delimiters. The logic checks if delimiters are paired. If not, it - will ignore the search string. - """ - - # TODO: - # Right now, regular expressions to match it are defined only up to - # the start delimiter, e.g.: - # - # \bSTRUCT_GROUP\( - # - # is similar to: STRUCT_GROUP\((.*)\) - # except that the content inside the match group is delimiter's aligne= d. - # - # The content inside parenthesis are converted into a single replace - # group (e.g. r`\1'). - # - # It would be nice to change such definition to support multiple - # match groups, allowing a regex equivalent to. - # - # FOO\((.*), (.*), (.*)\) - # - # it is probably easier to define it not as a regular expression, but - # with some lexical definition like: - # - # FOO(arg1, arg2, arg3) - - - DELIMITER_PAIRS =3D { - '{': '}', - '(': ')', - '[': ']', - } - - RE_DELIM =3D re.compile(r'[\{\}\[\]\(\)]') - - def _search(self, regex, line): - """ - Finds paired blocks for a regex that ends with a delimiter. - - The suggestion of using finditer to match pairs came from: - https://stackoverflow.com/questions/5454322/python-how-to-match-ne= sted-parentheses-with-regex - but I ended using a different implementation to align all three ty= pes - of delimiters and seek for an initial regular expression. - - The algorithm seeks for open/close paired delimiters and place them - into a stack, yielding a start/stop position of each match when t= he - stack is zeroed. - - The algorithm shoud work fine for properly paired lines, but will - silently ignore end delimiters that preceeds an start delimiter. - This should be OK for kernel-doc parser, as unaligned delimiters - would cause compilation errors. So, we don't need to rise exceptio= ns - to cover such issues. - """ - - stack =3D [] - - for match_re in regex.finditer(line): - start =3D match_re.start() - offset =3D match_re.end() - - d =3D line[offset -1] - if d not in self.DELIMITER_PAIRS: - continue - - end =3D self.DELIMITER_PAIRS[d] - stack.append(end) - - for match in self.RE_DELIM.finditer(line[offset:]): - pos =3D match.start() + offset - - d =3D line[pos] - - if d in self.DELIMITER_PAIRS: - end =3D self.DELIMITER_PAIRS[d] - - stack.append(end) - continue - - # Does the end delimiter match what it is expected? - if stack and d =3D=3D stack[-1]: - stack.pop() - - if not stack: - yield start, offset, pos + 1 - break - - def search(self, regex, line): - """ - This is similar to re.search: - - It matches a regex that it is followed by a delimiter, - returning occurrences only if all delimiters are paired. - """ - - for t in self._search(regex, line): - - yield line[t[0]:t[2]] - - def sub(self, regex, sub, line, count=3D0): - """ - This is similar to re.sub: - - It matches a regex that it is followed by a delimiter, - replacing occurrences only if all delimiters are paired. - - if r'\1' is used, it works just like re: it places there the - matched paired data with the delimiter stripped. - - If count is different than zero, it will replace at most count - items. - """ - out =3D "" - - cur_pos =3D 0 - n =3D 0 - - found =3D False - for start, end, pos in self._search(regex, line): - out +=3D line[cur_pos:start] - - # Value, ignoring start/end delimiters - value =3D line[end:pos - 1] - - # replaces \1 at the sub string, if \1 is used there - new_sub =3D sub - new_sub =3D new_sub.replace(r'\1', value) - - out +=3D new_sub - - # Drop end ';' if any - if line[pos] =3D=3D ';': - pos +=3D 1 - - cur_pos =3D pos - n +=3D 1 - - if count and count >=3D n: - break - - # Append the remaining string - l =3D len(line) - out +=3D line[cur_pos:l] - - return out =20 # # Regular expressions used to parse kernel-doc markups at KernelDoc class. diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py new file mode 100755 index 000000000000..45ddba8090e5 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_re.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + +""" +Regular expression ancillary classes. + +Those help caching regular expressions and do matching for kernel-doc. +""" + +import re + +# Local cache for regular expressions +re_cache =3D {} + + +class Re: + """ + Helper class to simplify regex declaration and usage, + + It calls re.compile for a given pattern. It also allows adding + regular expressions and define sub at class init time. + + Regular expressions can be cached via an argument, helping to speedup + searches. + """ + + def _add_regex(self, string, flags): + """ + Adds a new regex or re-use it from the cache. + """ + + if string in re_cache: + self.regex =3D re_cache[string] + else: + self.regex =3D re.compile(string, flags=3Dflags) + + if self.cache: + re_cache[string] =3D self.regex + + def __init__(self, string, cache=3DTrue, flags=3D0): + """ + Compile a regular expression and initialize internal vars. + """ + + self.cache =3D cache + self.last_match =3D None + + self._add_regex(string, flags) + + def __str__(self): + """ + Return the regular expression pattern. + """ + return self.regex.pattern + + def __add__(self, other): + """ + Allows adding two regular expressions into one. + """ + + return Re(str(self) + str(other), cache=3Dself.cache or other.cach= e, + flags=3Dself.regex.flags | other.regex.flags) + + def match(self, string): + """ + Handles a re.match storing its results + """ + + self.last_match =3D self.regex.match(string) + return self.last_match + + def search(self, string): + """ + Handles a re.search storing its results + """ + + self.last_match =3D self.regex.search(string) + return self.last_match + + def findall(self, string): + """ + Alias to re.findall + """ + + return self.regex.findall(string) + + def split(self, string): + """ + Alias to re.split + """ + + return self.regex.split(string) + + def sub(self, sub, string, count=3D0): + """ + Alias to re.sub + """ + + return self.regex.sub(sub, string, count=3Dcount) + + def group(self, num): + """ + Returns the group results of the last match + """ + + return self.last_match.group(num) + + +class NestedMatch: + """ + Finding nested delimiters is hard with regular expressions. It is + even harder on Python with its normal re module, as there are several + advanced regular expressions that are missing. + + This is the case of this pattern: + + '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' + + which is used to properly match open/close parenthesis of the + string search STRUCT_GROUP(), + + Add a class that counts pairs of delimiters, using it to match and + replace nested expressions. + + The original approach was suggested by: + https://stackoverflow.com/questions/5454322/python-how-to-match-ne= sted-parentheses-with-regex + + Although I re-implemented it to make it more generic and match 3 types + of delimiters. The logic checks if delimiters are paired. If not, it + will ignore the search string. + """ + + # TODO: + # Right now, regular expressions to match it are defined only up to + # the start delimiter, e.g.: + # + # \bSTRUCT_GROUP\( + # + # is similar to: STRUCT_GROUP\((.*)\) + # except that the content inside the match group is delimiter's aligne= d. + # + # The content inside parenthesis are converted into a single replace + # group (e.g. r`\1'). + # + # It would be nice to change such definition to support multiple + # match groups, allowing a regex equivalent to. + # + # FOO\((.*), (.*), (.*)\) + # + # it is probably easier to define it not as a regular expression, but + # with some lexical definition like: + # + # FOO(arg1, arg2, arg3) + + DELIMITER_PAIRS =3D { + '{': '}', + '(': ')', + '[': ']', + } + + RE_DELIM =3D re.compile(r'[\{\}\[\]\(\)]') + + def _search(self, regex, line): + """ + Finds paired blocks for a regex that ends with a delimiter. + + The suggestion of using finditer to match pairs came from: + https://stackoverflow.com/questions/5454322/python-how-to-match-ne= sted-parentheses-with-regex + but I ended using a different implementation to align all three ty= pes + of delimiters and seek for an initial regular expression. + + The algorithm seeks for open/close paired delimiters and place them + into a stack, yielding a start/stop position of each match when t= he + stack is zeroed. + + The algorithm shoud work fine for properly paired lines, but will + silently ignore end delimiters that preceeds an start delimiter. + This should be OK for kernel-doc parser, as unaligned delimiters + would cause compilation errors. So, we don't need to rise exceptio= ns + to cover such issues. + """ + + stack =3D [] + + for match_re in regex.finditer(line): + start =3D match_re.start() + offset =3D match_re.end() + + d =3D line[offset - 1] + if d not in self.DELIMITER_PAIRS: + continue + + end =3D self.DELIMITER_PAIRS[d] + stack.append(end) + + for match in self.RE_DELIM.finditer(line[offset:]): + pos =3D match.start() + offset + + d =3D line[pos] + + if d in self.DELIMITER_PAIRS: + end =3D self.DELIMITER_PAIRS[d] + + stack.append(end) + continue + + # Does the end delimiter match what it is expected? + if stack and d =3D=3D stack[-1]: + stack.pop() + + if not stack: + yield start, offset, pos + 1 + break + + def search(self, regex, line): + """ + This is similar to re.search: + + It matches a regex that it is followed by a delimiter, + returning occurrences only if all delimiters are paired. + """ + + for t in self._search(regex, line): + + yield line[t[0]:t[2]] + + def sub(self, regex, sub, line, count=3D0): + """ + This is similar to re.sub: + + It matches a regex that it is followed by a delimiter, + replacing occurrences only if all delimiters are paired. + + if r'\1' is used, it works just like re: it places there the + matched paired data with the delimiter stripped. + + If count is different than zero, it will replace at most count + items. + """ + out =3D "" + + cur_pos =3D 0 + n =3D 0 + + for start, end, pos in self._search(regex, line): + out +=3D line[cur_pos:start] + + # Value, ignoring start/end delimiters + value =3D line[end:pos - 1] + + # replaces \1 at the sub string, if \1 is used there + new_sub =3D sub + new_sub =3D new_sub.replace(r'\1', value) + + out +=3D new_sub + + # Drop end ';' if any + if line[pos] =3D=3D ';': + pos +=3D 1 + + cur_pos =3D pos + n +=3D 1 + + if count and count >=3D n: + break + + # Append the remaining string + l =3D len(line) + out +=3D line[cur_pos:l] + + return out --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 203F624BBF6; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=rufJMHUYfs0j2ClRgMYnluuYyaHGoP2dnTHHV51jeFl+azwmzKojodW/S76T2jkFqxP5PtpUzp9hU1NMHFDU3P+jvhHwGHrITo7V40nuXcfhGj934K7wfIh19gQGvPwdqFbrl3UORsdUydj9boweLHLdiJIcYwQfOnTzinCHVUI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=oGmptKFwK8sfoy5nmo7fNyWjrlXUopST/S8FlqSqVmQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fh/DMzovKLz62dhwokHb5WQkj9LiStMYyclHJwm7gKFAGPEw0wi0vdm9vQtu0bJBke/iRgpdueYx51GU0kiPA5ugVw/0fJ/e6z3NB0C8wvC92wBntAZXPfFWohSJYMojwyK2s+7MFn5bHWljHxjNV1EXrxZNggSEvnCAX+kQuaY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=S+V10AJ1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="S+V10AJ1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E9D57C4CEFB; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=oGmptKFwK8sfoy5nmo7fNyWjrlXUopST/S8FlqSqVmQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S+V10AJ10+66zGDvFvWDGfAAhZ3fe0KFvPU0WGPHGD/Hj5m6gBoLIHlEXb9X349gm f8suxD6MzGy8/BIF6MwxmCVJYXtvLiD30ojYm569duODoN78y51DOL3R7pQ9rGw6Re Q53PhSZ3EF5iYdP7X7Yf1us2G1ym2Ufmm1WJzHpcEOhBAH8vS1jTzwJuFeohfFTWLG U16q/tYrvg4X2YDBXJuhKMLGLoT980DARWQufNqR0FLeHcRbvOkfcilZLTD1Ra3dJi PcI9DbyozSsyWi1H3Vsw3LEPVlzcbhox/g/ejLMzo3fc3+6JSIRz0SsdXfjB3Wkp+E QSaPvOAqaVjPg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4L-0jQk; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Gustavo A. R. Silva" , "Mauro Carvalho Chehab" , Kees Cook , linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 14/39] scripts/kernel-doc.py: move KernelDoc class to a separate file Date: Mon, 24 Feb 2025 10:08:20 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" In preparation for letting kerneldoc Sphinx extension to import Python libraries, move regex ancillary classes to a separate file. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 1634 +----------------------------- scripts/lib/kdoc/kdoc_parser.py | 1689 +++++++++++++++++++++++++++++++ 2 files changed, 1691 insertions(+), 1632 deletions(-) create mode 100755 scripts/lib/kdoc/kdoc_parser.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index faae66aa6ead..193a30fcfb7c 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -42,53 +42,15 @@ SRC_DIR =3D os.path.dirname(os.path.realpath(__file__)) =20 sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) =20 -from kdoc_re import Re, NestedMatch +from kdoc_parser import KernelDoc, type_param +from kdoc_re import Re =20 - -# -# Regular expressions used to parse kernel-doc markups at KernelDoc class. -# -# Let's declare them in lowercase outside any class to make easier to -# convert from the python script. -# -# As those are evaluated at the beginning, no need to cache them -# - - -# Allow whitespace at end of comment start. -doc_start =3D Re(r'^/\*\*\s*$', cache=3DFalse) - -doc_end =3D Re(r'\*/', cache=3DFalse) -doc_com =3D Re(r'\s*\*\s*', cache=3DFalse) -doc_com_body =3D Re(r'\s*\* ?', cache=3DFalse) -doc_decl =3D doc_com + Re(r'(\w+)', cache=3DFalse) - -# @params and a strictly limited set of supported section names -# Specifically: -# Match @word: -# @...: -# @{section-name}: -# while trying to not match literal block starts like "example::" -# -doc_sect =3D doc_com + \ - Re(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?= |examples?)\s*:([^:].*)?$', - flags=3Dre.I, cache=3DFalse) - -doc_content =3D doc_com_body + Re(r'(.*)', cache=3DFalse) -doc_block =3D doc_com + Re(r'DOC:\s*(.*)?', cache=3DFalse) -doc_inline_start =3D Re(r'^\s*/\*\*\s*$', cache=3DFalse) -doc_inline_sect =3D Re(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=3DFalse) -doc_inline_end =3D Re(r'^\s*\*/\s*$', cache=3DFalse) -doc_inline_oneline =3D Re(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cac= he=3DFalse) function_pointer =3D Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=3DFalse) -attribute =3D Re(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", - flags=3Dre.I | re.S, cache=3DFalse) =20 # match expressions used to find embedded type information type_constant =3D Re(r"\b``([^\`]+)``\b", cache=3DFalse) type_constant2 =3D Re(r"\%([-_*\w]+)", cache=3DFalse) type_func =3D Re(r"(\w+)\(\)", cache=3DFalse) -type_param =3D Re(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=3DFalse) type_param_ref =3D Re(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cac= he=3DFalse) =20 # Special RST handling for func ptr params @@ -106,1598 +68,6 @@ type_member =3D Re(r"\&([_\w]+)(\.|->)([_\w]+)", cach= e=3DFalse) type_fallback =3D Re(r"\&([_\w]+)", cache=3DFalse) type_member_func =3D type_member + Re(r"\(\)", cache=3DFalse) =20 -export_symbol =3D Re(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cac= he=3DFalse) -export_symbol_ns =3D Re(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"= \S+"\)\s*', cache=3DFalse) - -class KernelDoc: - # Parser states - STATE_NORMAL =3D 0 # normal code - STATE_NAME =3D 1 # looking for function name - STATE_BODY_MAYBE =3D 2 # body - or maybe more description - STATE_BODY =3D 3 # the body of the comment - STATE_BODY_WITH_BLANK_LINE =3D 4 # the body which has a blank line - STATE_PROTO =3D 5 # scanning prototype - STATE_DOCBLOCK =3D 6 # documentation block - STATE_INLINE =3D 7 # gathering doc outside main block - - st_name =3D [ - "NORMAL", - "NAME", - "BODY_MAYBE", - "BODY", - "BODY_WITH_BLANK_LINE", - "PROTO", - "DOCBLOCK", - "INLINE", - ] - - # Inline documentation state - STATE_INLINE_NA =3D 0 # not applicable ($state !=3D STATE_INLINE) - STATE_INLINE_NAME =3D 1 # looking for member name (@foo:) - STATE_INLINE_TEXT =3D 2 # looking for member documentation - STATE_INLINE_END =3D 3 # done - STATE_INLINE_ERROR =3D 4 # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. - - st_inline_name =3D [ - "", - "_NAME", - "_TEXT", - "_END", - "_ERROR", - ] - - # Section names - - section_default =3D "Description" # default section - section_intro =3D "Introduction" - section_context =3D "Context" - section_return =3D "Return" - - undescribed =3D "-- undescribed --" - - def __init__(self, config, fname): - """Initialize internal variables""" - - self.fname =3D fname - self.config =3D config - - # Initial state for the state machines - self.state =3D self.STATE_NORMAL - self.inline_doc_state =3D self.STATE_INLINE_NA - - # Store entry currently being processed - self.entry =3D None - - # Place all potential outputs into an array - self.entries =3D [] - - def show_warnings(self, dtype, declaration_name): - # TODO: implement it - - return True - - # TODO: rename to emit_message - def emit_warning(self, ln, msg, warning=3DTrue): - """Emit a message""" - - if warning: - self.config.log.warning("%s:%d %s", self.fname, ln, msg) - else: - self.config.log.info("%s:%d %s", self.fname, ln, msg) - - def dump_section(self, start_new=3DTrue): - """ - Dumps section contents to arrays/hashes intended for that purpose. - """ - - name =3D self.entry.section - contents =3D self.entry.contents - - # TODO: we can prevent dumping empty sections here with: - # - # if self.entry.contents.strip("\n"): - # if start_new: - # self.entry.section =3D self.section_default - # self.entry.contents =3D "" - # - # return - # - # But, as we want to be producing the same output of the - # venerable kernel-doc Perl tool, let's just output everything, - # at least for now - - if type_param.match(name): - name =3D type_param.group(1) - - self.entry.parameterdescs[name] =3D contents - self.entry.parameterdesc_start_lines[name] =3D self.entry.new_= start_line - - self.entry.sectcheck +=3D name + " " - self.entry.new_start_line =3D 0 - - elif name =3D=3D "@...": - name =3D "..." - self.entry.parameterdescs[name] =3D contents - self.entry.sectcheck +=3D name + " " - self.entry.parameterdesc_start_lines[name] =3D self.entry.new_= start_line - self.entry.new_start_line =3D 0 - - else: - if name in self.entry.sections and self.entry.sections[name] != =3D "": - # Only warn on user-specified duplicate section names - if name !=3D self.section_default: - self.emit_warning(self.entry.new_start_line, - f"duplicate section name '{name}'\n") - self.entry.sections[name] +=3D contents - else: - self.entry.sections[name] =3D contents - self.entry.sectionlist.append(name) - self.entry.section_start_lines[name] =3D self.entry.new_st= art_line - self.entry.new_start_line =3D 0 - -# self.config.log.debug("Section: %s : %s", name, pformat(vars(self= .entry))) - - if start_new: - self.entry.section =3D self.section_default - self.entry.contents =3D "" - - # TODO: rename it to store_declaration - def output_declaration(self, dtype, name, **args): - """ - Stores the entry into an entry array. - - The actual output and output filters will be handled elsewhere - """ - - # The implementation here is different than the original kernel-do= c: - # instead of checking for output filters or actually output anythi= ng, - # it just stores the declaration content at self.entries, as the - # output will happen on a separate class. - # - # For now, we're keeping the same name of the function just to make - # easier to compare the source code of both scripts - - if "declaration_start_line" not in args: - args["declaration_start_line"] =3D self.entry.declaration_star= t_line - - args["type"] =3D dtype - - # TODO: use colletions.OrderedDict - - sections =3D args.get('sections', {}) - sectionlist =3D args.get('sectionlist', []) - - # Drop empty sections - # TODO: improve it to emit warnings - for section in [ "Description", "Return" ]: - if section in sectionlist: - if not sections[section].rstrip(): - del sections[section] - sectionlist.remove(section) - - self.entries.append((name, args)) - - self.config.log.debug("Output: %s:%s =3D %s", dtype, name, pformat= (args)) - - def reset_state(self, ln): - """ - Ancillary routine to create a new entry. It initializes all - variables used by the state machine. - """ - - self.entry =3D argparse.Namespace - - self.entry.contents =3D "" - self.entry.function =3D "" - self.entry.sectcheck =3D "" - self.entry.struct_actual =3D "" - self.entry.prototype =3D "" - - self.entry.parameterlist =3D [] - self.entry.parameterdescs =3D {} - self.entry.parametertypes =3D {} - self.entry.parameterdesc_start_lines =3D {} - - self.entry.section_start_lines =3D {} - self.entry.sectionlist =3D [] - self.entry.sections =3D {} - - self.entry.anon_struct_union =3D False - - self.entry.leading_space =3D None - - # State flags - self.state =3D self.STATE_NORMAL - self.inline_doc_state =3D self.STATE_INLINE_NA - self.entry.brcount =3D 0 - - self.entry.in_doc_sect =3D False - self.entry.declaration_start_line =3D ln - - def push_parameter(self, ln, decl_type, param, dtype, - org_arg, declaration_name): - if self.entry.anon_struct_union and dtype =3D=3D "" and param =3D= =3D "}": - return # Ignore the ending }; from anonymous struct/union - - self.entry.anon_struct_union =3D False - - param =3D Re(r'[\[\)].*').sub('', param, count=3D1) - - if dtype =3D=3D "" and param.endswith("..."): - if Re(r'\w\.\.\.$').search(param): - # For named variable parameters of the form `x...`, - # remove the dots - param =3D param[:-3] - else: - # Handles unnamed variable parameters - param =3D "..." - - if param not in self.entry.parameterdescs or \ - not self.entry.parameterdescs[param]: - - self.entry.parameterdescs[param] =3D "variable arguments" - - elif dtype =3D=3D "" and (not param or param =3D=3D "void"): - param =3D "void" - self.entry.parameterdescs[param] =3D "no arguments" - - elif dtype =3D=3D "" and param in ["struct", "union"]: - # Handle unnamed (anonymous) union or struct - dtype =3D param - param =3D "{unnamed_" + param + "}" - self.entry.parameterdescs[param] =3D "anonymous\n" - self.entry.anon_struct_union =3D True - - # Handle cache group enforcing variables: they do not need - # to be described in header files - elif "__cacheline_group" in param: - # Ignore __cacheline_group_begin and __cacheline_group_end - return - - # Warn if parameter has no description - # (but ignore ones starting with # as these are not parameters - # but inline preprocessor statements) - if param not in self.entry.parameterdescs and not param.startswith= ("#"): - self.entry.parameterdescs[param] =3D self.undescribed - - if self.show_warnings(dtype, declaration_name) and "." not in = param: - if decl_type =3D=3D 'function': - dname =3D f"{decl_type} parameter" - else: - dname =3D f"{decl_type} member" - - self.emit_warning(ln, - f"{dname} '{param}' not described in '{d= eclaration_name}'") - - # Strip spaces from param so that it is one continuous string on - # parameterlist. This fixes a problem where check_sections() - # cannot find a parameter like "addr[6 + 2]" because it actually - # appears as "addr[6", "+", "2]" on the parameter list. - # However, it's better to maintain the param string unchanged for - # output, so just weaken the string compare in check_sections() - # to ignore "[blah" in a parameter string. - - self.entry.parameterlist.append(param) - org_arg =3D Re(r'\s\s+').sub(' ', org_arg) - self.entry.parametertypes[param] =3D org_arg - - def save_struct_actual(self, actual): - """ - Strip all spaces from the actual param so that it looks like - one string item. - """ - - actual =3D Re(r'\s*').sub("", actual, count=3D1) - - self.entry.struct_actual +=3D actual + " " - - def create_parameter_list(self, ln, decl_type, args, splitter, declara= tion_name): - - # temporarily replace all commas inside function pointer definition - arg_expr =3D Re(r'(\([^\),]+),') - while arg_expr.search(args): - args =3D arg_expr.sub(r"\1#", args) - - for arg in args.split(splitter): - # Strip comments - arg =3D Re(r'\/\*.*\*\/').sub('', arg) - - # Ignore argument attributes - arg =3D Re(r'\sPOS0?\s').sub(' ', arg) - - # Strip leading/trailing spaces - arg =3D arg.strip() - arg =3D Re(r'\s+').sub(' ', arg, count=3D1) - - if arg.startswith('#'): - # Treat preprocessor directive as a typeless variable just= to fill - # corresponding data structures "correctly". Catch it late= r in - # output_* subs. - - # Treat preprocessor directive as a typeless variable - self.push_parameter(ln, decl_type, arg, "", - "", declaration_name) - - elif Re(r'\(.+\)\s*\(').search(arg): - # Pointer-to-function - - arg =3D arg.replace('#', ',') - - r =3D Re(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') - if r.match(arg): - param =3D r.group(1) - else: - self.emit_warning(ln, f"Invalid param: {arg}") - param =3D arg - - dtype =3D Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r= '\1', arg) - self.save_struct_actual(param) - self.push_parameter(ln, decl_type, param, dtype, - arg, declaration_name) - - elif Re(r'\(.+\)\s*\[').search(arg): - # Array-of-pointers - - arg =3D arg.replace('#', ',') - r =3D Re(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+= \s*\]\s*)*\)') - if r.match(arg): - param =3D r.group(1) - else: - self.emit_warning(ln, f"Invalid param: {arg}") - param =3D arg - - dtype =3D Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r= '\1', arg) - - self.save_struct_actual(param) - self.push_parameter(ln, decl_type, param, dtype, - arg, declaration_name) - - elif arg: - arg =3D Re(r'\s*:\s*').sub(":", arg) - arg =3D Re(r'\s*\[').sub('[', arg) - - args =3D Re(r'\s*,\s*').split(arg) - if args[0] and '*' in args[0]: - args[0] =3D re.sub(r'(\*+)\s*', r' \1', args[0]) - - first_arg =3D [] - r =3D Re(r'^(.*\s+)(.*?\[.*\].*)$') - if args[0] and r.match(args[0]): - args.pop(0) - first_arg.extend(r.group(1)) - first_arg.append(r.group(2)) - else: - first_arg =3D Re(r'\s+').split(args.pop(0)) - - args.insert(0, first_arg.pop()) - dtype =3D ' '.join(first_arg) - - for param in args: - if Re(r'^(\*+)\s*(.*)').match(param): - r =3D Re(r'^(\*+)\s*(.*)') - if not r.match(param): - self.emit_warning(ln, f"Invalid param: {param}= ") - continue - - param =3D r.group(1) - - self.save_struct_actual(r.group(2)) - self.push_parameter(ln, decl_type, r.group(2), - f"{dtype} {r.group(1)}", - arg, declaration_name) - - elif Re(r'(.*?):(\w+)').search(param): - r =3D Re(r'(.*?):(\w+)') - if not r.match(param): - self.emit_warning(ln, f"Invalid param: {param}= ") - continue - - if dtype !=3D "": # Skip unnamed bit-fields - self.save_struct_actual(r.group(1)) - self.push_parameter(ln, decl_type, r.group(1), - f"{dtype}:{r.group(2)}", - arg, declaration_name) - else: - self.save_struct_actual(param) - self.push_parameter(ln, decl_type, param, dtype, - arg, declaration_name) - - def check_sections(self, ln, decl_name, decl_type, sectcheck, prmschec= k): - sects =3D sectcheck.split() - prms =3D prmscheck.split() - err =3D False - - for sx in range(len(sects)): # pylint: disable=3D= C0200 - err =3D True - for px in range(len(prms)): # pylint: disable=3D= C0200 - prm_clean =3D prms[px] - prm_clean =3D Re(r'\[.*\]').sub('', prm_clean) - prm_clean =3D attribute.sub('', prm_clean) - - # ignore array size in a parameter string; - # however, the original param string may contain - # spaces, e.g.: addr[6 + 2] - # and this appears in @prms as "addr[6" since the - # parameter list is split at spaces; - # hence just ignore "[..." for the sections check; - prm_clean =3D Re(r'\[.*').sub('', prm_clean) - - if prm_clean =3D=3D sects[sx]: - err =3D False - break - - if err: - if decl_type =3D=3D 'function': - dname =3D f"{decl_type} parameter" - else: - dname =3D f"{decl_type} member" - - self.emit_warning(ln, - f"Excess {dname} '{sects[sx]}' descripti= on in '{decl_name}'") - - def check_return_section(self, ln, declaration_name, return_type): - - if not self.config.wreturn: - return - - # Ignore an empty return type (It's a macro) - # Ignore functions with a "void" return type (but not "void *") - if not return_type or Re(r'void\s*\w*\s*$').search(return_type): - return - - if not self.entry.sections.get("Return", None): - self.emit_warning(ln, - f"No description found for return value of '= {declaration_name}'") - - def dump_struct(self, ln, proto): - """ - Store an entry for an struct or union - """ - - type_pattern =3D r'(struct|union)' - - qualifiers =3D [ - "__attribute__", - "__packed", - "__aligned", - "____cacheline_aligned_in_smp", - "____cacheline_aligned", - ] - - definition_body =3D r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) = + ")?" - struct_members =3D Re(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\= })([^\{\}\;]*)(\;)') - - # Extract struct/union definition - members =3D None - declaration_name =3D None - decl_type =3D None - - r =3D Re(type_pattern + r'\s+(\w+)\s*' + definition_body) - if r.search(proto): - decl_type =3D r.group(1) - declaration_name =3D r.group(2) - members =3D r.group(3) - else: - r =3D Re(r'typedef\s+' + type_pattern + r'\s*' + definition_bo= dy + r'\s*(\w+)\s*;') - - if r.search(proto): - decl_type =3D r.group(1) - declaration_name =3D r.group(3) - members =3D r.group(2) - - if not members: - self.emit_warning(ln, f"{proto} error: Cannot parse struct or = union!") - self.config.errors +=3D 1 - return - - if self.entry.identifier !=3D declaration_name: - self.emit_warning(ln, - f"expecting prototype for {decl_type} {self.= entry.identifier}. Prototype was for {decl_type} {declaration_name} instead= \n") - return - - args_pattern =3Dr'([^,)]+)' - - sub_prefixes =3D [ - (Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), = ''), - (Re(r'\/\*\s*private:.*', re.S| re.I), ''), - - # Strip comments - (Re(r'\/\*.*?\*\/', re.S), ''), - - # Strip attributes - (attribute, ' '), - (Re(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), - (Re(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), - (Re(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), - (Re(r'\s*__packed\s*', re.S), ' '), - (Re(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), - (Re(r'\s*____cacheline_aligned_in_smp', re.S), ' '), - (Re(r'\s*____cacheline_aligned', re.S), ' '), - - # Unwrap struct_group macros based on this definition: - # __struct_group(TAG, NAME, ATTRS, MEMBERS...) - # which has variants like: struct_group(NAME, MEMBERS...) - # Only MEMBERS arguments require documentation. - # - # Parsing them happens on two steps: - # - # 1. drop struct group arguments that aren't at MEMBERS, - # storing them as STRUCT_GROUP(MEMBERS) - # - # 2. remove STRUCT_GROUP() ancillary macro. - # - # The original logic used to remove STRUCT_GROUP() using an - # advanced regex: - # - # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; - # - # with two patterns that are incompatible with - # Python re module, as it has: - # - # - a recursive pattern: (?1) - # - an atomic grouping: (?>...) - # - # I tried a simpler version: but it didn't work either: - # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; - # - # As it doesn't properly match the end parenthesis on some cas= es. - # - # So, a better solution was crafted: there's now a NestedMatch - # class that ensures that delimiters after a search are proper= ly - # matched. So, the implementation to drop STRUCT_GROUP() will = be - # handled in separate. - - (Re(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), - (Re(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_G= ROUP('), - (Re(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r's= truct \1 \2; STRUCT_GROUP('), - (Re(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROU= P('), - - # Replace macros - # - # TODO: it is better to also move those to the NestedMatch log= ic, - # to ensure that parenthesis will be properly matched. - - (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),= r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), - (Re(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DE= CLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), - (Re(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pat= tern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), - (Re(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_= pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), - (Re(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_patt= ern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), - (Re(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_= pattern + r'\)', re.S), r'\2 *\1'), - (Re(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*'= + args_pattern + r'\)', re.S), r'\1 \2[]'), - (Re(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S= ), r'dma_addr_t \1'), - (Re(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S)= , r'__u32 \1'), - ] - - # Regexes here are guaranteed to have the end limiter matching - # the start delimiter. Yet, right now, only one replace group - # is allowed. - - sub_nested_prefixes =3D [ - (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), - ] - - for search, sub in sub_prefixes: - members =3D search.sub(sub, members) - - nested =3D NestedMatch() - - for search, sub in sub_nested_prefixes: - members =3D nested.sub(search, sub, members) - - # Keeps the original declaration as-is - declaration =3D members - - # Split nested struct/union elements - # - # This loop was simpler at the original kernel-doc perl version, as - # while ($members =3D~ m/$struct_members/) { ... } - # reads 'members' string on each interaction. - # - # Python behavior is different: it parses 'members' only once, - # creating a list of tuples from the first interaction. - # - # On other words, this won't get nested structs. - # - # So, we need to have an extra loop on Python to override such - # re limitation. - - while True: - tuples =3D struct_members.findall(members) - if not tuples: - break - - for t in tuples: - newmember =3D "" - maintype =3D t[0] - s_ids =3D t[5] - content =3D t[3] - - oldmember =3D "".join(t) - - for s_id in s_ids.split(','): - s_id =3D s_id.strip() - - newmember +=3D f"{maintype} {s_id}; " - s_id =3D Re(r'[:\[].*').sub('', s_id) - s_id =3D Re(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) - - for arg in content.split(';'): - arg =3D arg.strip() - - if not arg: - continue - - r =3D Re(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') - if r.match(arg): - # Pointer-to-function - dtype =3D r.group(1) - name =3D r.group(2) - extra =3D r.group(3) - - if not name: - continue - - if not s_id: - # Anonymous struct/union - newmember +=3D f"{dtype}{name}{extra}; " - else: - newmember +=3D f"{dtype}{s_id}.{name}{extr= a}; " - - else: - arg =3D arg.strip() - # Handle bitmaps - arg =3D Re(r':\s*\d+\s*').sub('', arg) - - # Handle arrays - arg =3D Re(r'\[.*\]').sub('', arg) - - # Handle multiple IDs - arg =3D Re(r'\s*,\s*').sub(',', arg) - - - r =3D Re(r'(.*)\s+([\S+,]+)') - - if r.search(arg): - dtype =3D r.group(1) - names =3D r.group(2) - else: - newmember +=3D f"{arg}; " - continue - - for name in names.split(','): - name =3D Re(r'^\s*\**(\S+)\s*').sub(r'\1',= name).strip() - - if not name: - continue - - if not s_id: - # Anonymous struct/union - newmember +=3D f"{dtype} {name}; " - else: - newmember +=3D f"{dtype} {s_id}.{name}= ; " - - members =3D members.replace(oldmember, newmember) - - # Ignore other nested elements, like enums - members =3D re.sub(r'(\{[^\{\}]*\})', '', members) - - self.create_parameter_list(ln, decl_type, members, ';', - declaration_name) - self.check_sections(ln, declaration_name, decl_type, - self.entry.sectcheck, self.entry.struct_actual) - - # Adjust declaration for better display - declaration =3D Re(r'([\{;])').sub(r'\1\n', declaration) - declaration =3D Re(r'\}\s+;').sub('};', declaration) - - # Better handle inlined enums - while True: - r =3D Re(r'(enum\s+\{[^\}]+),([^\n])') - if not r.search(declaration): - break - - declaration =3D r.sub(r'\1,\n\2', declaration) - - def_args =3D declaration.split('\n') - level =3D 1 - declaration =3D "" - for clause in def_args: - - clause =3D clause.strip() - clause =3D Re(r'\s+').sub(' ', clause, count=3D1) - - if not clause: - continue - - if '}' in clause and level > 1: - level -=3D 1 - - if not Re(r'^\s*#').match(clause): - declaration +=3D "\t" * level - - declaration +=3D "\t" + clause + "\n" - if "{" in clause and "}" not in clause: - level +=3D 1 - - self.output_declaration(decl_type, declaration_name, - struct=3Ddeclaration_name, - module=3Dself.entry.modulename, - definition=3Ddeclaration, - parameterlist=3Dself.entry.parameterlist, - parameterdescs=3Dself.entry.parameterdescs, - parametertypes=3Dself.entry.parametertypes, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, - purpose=3Dself.entry.declaration_purpose) - - def dump_enum(self, ln, proto): - - # Ignore members marked private - proto =3D Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=3Dr= e.S).sub('', proto) - proto =3D Re(r'\/\*\s*private:.*}', flags=3Dre.S).sub('}', proto) - - # Strip comments - proto =3D Re(r'\/\*.*?\*\/', flags=3Dre.S).sub('', proto) - - # Strip #define macros inside enums - proto =3D Re(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=3Dre= .S).sub('', proto) - - members =3D None - declaration_name =3D None - - r =3D Re(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') - if r.search(proto): - declaration_name =3D r.group(2) - members =3D r.group(1).rstrip() - else: - r =3D Re(r'enum\s+(\w*)\s*\{(.*)\}') - if r.match(proto): - declaration_name =3D r.group(1) - members =3D r.group(2).rstrip() - - if not members: - self.emit_warning(ln, f"{proto}: error: Cannot parse enum!") - self.config.errors +=3D 1 - return - - if self.entry.identifier !=3D declaration_name: - if self.entry.identifier =3D=3D "": - self.emit_warning(ln, - f"{proto}: wrong kernel-doc identifier o= n prototype") - else: - self.emit_warning(ln, - f"expecting prototype for enum {self.ent= ry.identifier}. Prototype was for enum {declaration_name} instead") - return - - if not declaration_name: - declaration_name =3D "(anonymous)" - - member_set =3D set() - - members =3D Re(r'\([^;]*?[\)]').sub('', members) - - for arg in members.split(','): - if not arg: - continue - arg =3D Re(r'^\s*(\w+).*').sub(r'\1', arg) - self.entry.parameterlist.append(arg) - if arg not in self.entry.parameterdescs: - self.entry.parameterdescs[arg] =3D self.undescribed - if self.show_warnings("enum", declaration_name): - self.emit_warning(ln, - f"Enum value '{arg}' not described i= n enum '{declaration_name}'") - member_set.add(arg) - - for k in self.entry.parameterdescs: - if k not in member_set: - if self.show_warnings("enum", declaration_name): - self.emit_warning(ln, - f"Excess enum value '%{k}' descripti= on in '{declaration_name}'") - - self.output_declaration('enum', declaration_name, - enum=3Ddeclaration_name, - module=3Dself.config.modulename, - parameterlist=3Dself.entry.parameterlist, - parameterdescs=3Dself.entry.parameterdescs, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, - purpose=3Dself.entry.declaration_purpose) - - def dump_declaration(self, ln, prototype): - if self.entry.decl_type =3D=3D "enum": - self.dump_enum(ln, prototype) - return - - if self.entry.decl_type =3D=3D "typedef": - self.dump_typedef(ln, prototype) - return - - if self.entry.decl_type in ["union", "struct"]: - self.dump_struct(ln, prototype) - return - - # TODO: handle other types - self.output_declaration(self.entry.decl_type, prototype, - entry=3Dself.entry) - - def dump_function(self, ln, prototype): - - func_macro =3D False - return_type =3D '' - decl_type =3D 'function' - - # Prefixes that would be removed - sub_prefixes =3D [ - (r"^static +", "", 0), - (r"^extern +", "", 0), - (r"^asmlinkage +", "", 0), - (r"^inline +", "", 0), - (r"^__inline__ +", "", 0), - (r"^__inline +", "", 0), - (r"^__always_inline +", "", 0), - (r"^noinline +", "", 0), - (r"^__FORTIFY_INLINE +", "", 0), - (r"__init +", "", 0), - (r"__init_or_module +", "", 0), - (r"__deprecated +", "", 0), - (r"__flatten +", "", 0), - (r"__meminit +", "", 0), - (r"__must_check +", "", 0), - (r"__weak +", "", 0), - (r"__sched +", "", 0), - (r"_noprof", "", 0), - (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), - (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", = 0), - (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), - (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2= ", 0), - (r"__attribute_const__ +", "", 0), - - # It seems that Python support for re.X is broken: - # At least for me (Python 3.13), this didn't work -# (r""" -# __attribute__\s*\(\( -# (?: -# [\w\s]+ # attribute name -# (?:\([^)]*\))? # attribute arguments -# \s*,? # optional comma at the end -# )+ -# \)\)\s+ -# """, "", re.X), - - # So, remove whitespaces and comments from it - (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"= , "", 0), - ] - - for search, sub, flags in sub_prefixes: - prototype =3D Re(search, flags).sub(sub, prototype) - - # Macros are a special case, as they change the prototype format - new_proto =3D Re(r"^#\s*define\s+").sub("", prototype) - if new_proto !=3D prototype: - is_define_proto =3D True - prototype =3D new_proto - else: - is_define_proto =3D False - - # Yes, this truly is vile. We are looking for: - # 1. Return type (may be nothing if we're looking at a macro) - # 2. Function name - # 3. Function parameters. - # - # All the while we have to watch out for function pointer paramete= rs - # (which IIRC is what the two sections are for), C types (these - # regexps don't even start to express all the possibilities), and - # so on. - # - # If you mess with these regexps, it's a good idea to check that - # the following functions' documentation still comes out right: - # - parport_register_device (function pointer parameters) - # - atomic_set (macro) - # - pci_match_device, __copy_to_user (long return type) - - name =3D r'[a-zA-Z0-9_~:]+' - prototype_end1 =3D r'[^\(]*' - prototype_end2 =3D r'[^\{]*' - prototype_end =3D fr'\(({prototype_end1}|{prototype_end2})\)' - - # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing gro= up. - # So, this needs to be mapped in Python with (?:...)? or (?:...)+ - - type1 =3D r'(?:[\w\s]+)?' - type2 =3D r'(?:[\w\s]+\*+)+' - - found =3D False - - if is_define_proto: - r =3D Re(r'^()(' + name + r')\s+') - - if r.search(prototype): - return_type =3D '' - declaration_name =3D r.group(2) - func_macro =3D True - - found =3D True - - if not found: - patterns =3D [ - rf'^()({name})\s*{prototype_end}', - rf'^({type1})\s+({name})\s*{prototype_end}', - rf'^({type2})\s*({name})\s*{prototype_end}', - ] - - for p in patterns: - r =3D Re(p) - - if r.match(prototype): - - return_type =3D r.group(1) - declaration_name =3D r.group(2) - args =3D r.group(3) - - self.create_parameter_list(ln, decl_type, args, ',', - declaration_name) - - found =3D True - break - if not found: - self.emit_warning(ln, - f"cannot understand function prototype: '{pr= ototype}'") - return - - if self.entry.identifier !=3D declaration_name: - self.emit_warning(ln, - f"expecting prototype for {self.entry.identi= fier}(). Prototype was for {declaration_name}() instead") - return - - prms =3D " ".join(self.entry.parameterlist) - self.check_sections(ln, declaration_name, "function", - self.entry.sectcheck, prms) - - self.check_return_section(ln, declaration_name, return_type) - - if 'typedef' in return_type: - self.output_declaration(decl_type, declaration_name, - function=3Ddeclaration_name, - typedef=3DTrue, - module=3Dself.config.modulename, - functiontype=3Dreturn_type, - parameterlist=3Dself.entry.parameterlist, - parameterdescs=3Dself.entry.parameterdescs, - parametertypes=3Dself.entry.parametertypes, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, - purpose=3Dself.entry.declaration_purpose, - func_macro=3Dfunc_macro) - else: - self.output_declaration(decl_type, declaration_name, - function=3Ddeclaration_name, - typedef=3DFalse, - module=3Dself.config.modulename, - functiontype=3Dreturn_type, - parameterlist=3Dself.entry.parameterlist, - parameterdescs=3Dself.entry.parameterdescs, - parametertypes=3Dself.entry.parametertypes, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, - purpose=3Dself.entry.declaration_purpose, - func_macro=3Dfunc_macro) - - def dump_typedef(self, ln, proto): - typedef_type =3D r'((?:\s+[\w\*]+\b){1,8})\s*' - typedef_ident =3D r'\*?\s*(\w\S+)\s*' - typedef_args =3D r'\s*\((.*)\);' - - typedef1 =3D Re(r'typedef' + typedef_type + r'\(' + typedef_ident = + r'\)' + typedef_args) - typedef2 =3D Re(r'typedef' + typedef_type + typedef_ident + typede= f_args) - - # Strip comments - proto =3D Re(r'/\*.*?\*/', flags=3Dre.S).sub('', proto) - - # Parse function typedef prototypes - for r in [typedef1, typedef2]: - if not r.match(proto): - continue - - return_type =3D r.group(1).strip() - declaration_name =3D r.group(2) - args =3D r.group(3) - - if self.entry.identifier !=3D declaration_name: - self.emit_warning(ln, - f"expecting prototype for typedef {self.= entry.identifier}. Prototype was for typedef {declaration_name} instead\n") - return - - decl_type =3D 'function' - self.create_parameter_list(ln, decl_type, args, ',', declarati= on_name) - - self.output_declaration(decl_type, declaration_name, - function=3Ddeclaration_name, - typedef=3DTrue, - module=3Dself.entry.modulename, - functiontype=3Dreturn_type, - parameterlist=3Dself.entry.parameterlist, - parameterdescs=3Dself.entry.parameterdescs, - parametertypes=3Dself.entry.parametertypes, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, - purpose=3Dself.entry.declaration_purpose) - return - - # Handle nested parentheses or brackets - r =3D Re(r'(\(*.\)\s*|\[*.\]\s*);$') - while r.search(proto): - proto =3D r.sub('', proto) - - # Parse simple typedefs - r =3D Re(r'typedef.*\s+(\w+)\s*;') - if r.match(proto): - declaration_name =3D r.group(1) - - if self.entry.identifier !=3D declaration_name: - self.emit_warning(ln, f"expecting prototype for typedef {s= elf.entry.identifier}. Prototype was for typedef {declaration_name} instead= \n") - return - - self.output_declaration('typedef', declaration_name, - typedef=3Ddeclaration_name, - module=3Dself.entry.modulename, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, - purpose=3Dself.entry.declaration_purpose) - return - - self.emit_warning(ln, "error: Cannot parse typedef!") - self.config.errors +=3D 1 - - @staticmethod - def process_export(function_table, line): - """ - process EXPORT_SYMBOL* tags - - This method is called both internally and externally, so, it - doesn't use self. - """ - - if export_symbol.search(line): - symbol =3D export_symbol.group(2) - function_table.add(symbol) - - if export_symbol_ns.search(line): - symbol =3D export_symbol_ns.group(2) - function_table.add(symbol) - - def process_normal(self, ln, line): - """ - STATE_NORMAL: looking for the /** to begin everything. - """ - - if not doc_start.match(line): - return - - # start a new entry - self.reset_state(ln + 1) - self.entry.in_doc_sect =3D False - - # next line is always the function name - self.state =3D self.STATE_NAME - - def process_name(self, ln, line): - """ - STATE_NAME: Looking for the "name - description" line - """ - - if doc_block.search(line): - self.entry.new_start_line =3D ln - - if not doc_block.group(1): - self.entry.section =3D self.section_intro - else: - self.entry.section =3D doc_block.group(1) - - self.state =3D self.STATE_DOCBLOCK - return - - if doc_decl.search(line): - self.entry.identifier =3D doc_decl.group(1) - self.entry.is_kernel_comment =3D False - - decl_start =3D str(doc_com) # comment block asterisk - fn_type =3D r"(?:\w+\s*\*\s*)?" # type (for non-functions) - parenthesis =3D r"(?:\(\w*\))?" # optional parenthesis on fu= nction - decl_end =3D r"(?:[-:].*)" # end of the name part - - # test for pointer declaration type, foo * bar() - desc - r =3D Re(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}= ?$") - if r.search(line): - self.entry.identifier =3D r.group(1) - - # Test for data declaration - r =3D Re(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") - if r.search(line): - self.entry.decl_type =3D r.group(1) - self.entry.identifier =3D r.group(2) - self.entry.is_kernel_comment =3D True - else: - # Look for foo() or static void foo() - description; - # or misspelt identifier - - r1 =3D Re(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s= *{decl_end}?$") - r2 =3D Re(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis= }\s*{decl_end}$") - - for r in [r1, r2]: - if r.search(line): - self.entry.identifier =3D r.group(1) - self.entry.decl_type =3D "function" - - r =3D Re(r"define\s+") - self.entry.identifier =3D r.sub("", self.entry.ide= ntifier) - self.entry.is_kernel_comment =3D True - break - - self.entry.identifier =3D self.entry.identifier.strip(" ") - - self.state =3D self.STATE_BODY - - # if there's no @param blocks need to set up default section h= ere - self.entry.section =3D self.section_default - self.entry.new_start_line =3D ln + 1 - - r =3D Re("[-:](.*)") - if r.search(line): - # strip leading/trailing/multiple spaces - self.entry.descr =3D r.group(1).strip(" ") - - r =3D Re(r"\s+") - self.entry.descr =3D r.sub(" ", self.entry.descr) - self.entry.declaration_purpose =3D self.entry.descr - self.state =3D self.STATE_BODY_MAYBE - else: - self.entry.declaration_purpose =3D "" - - if not self.entry.is_kernel_comment: - self.emit_warning(ln, - f"This comment starts with '/**', but is= n't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{li= ne}") - self.state =3D self.STATE_NORMAL - - if not self.entry.declaration_purpose and self.config.wshort_d= esc: - self.emit_warning(ln, - f"missing initial short description on l= ine:\n{line}") - - if not self.entry.identifier and self.entry.decl_type !=3D "en= um": - self.emit_warning(ln, - f"wrong kernel-doc identifier on line:\n= {line}") - self.state =3D self.STATE_NORMAL - - if self.config.verbose: - self.emit_warning(ln, - f"Scanning doc for {self.entry.decl_type= } {self.entry.identifier}", - warning=3DFalse) - - return - - # Failed to find an identifier. Emit a warning - self.emit_warning(ln, f"Cannot find identifier on line:\n{line}") - - def process_body(self, ln, line): - """ - STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. - """ - - if self.state =3D=3D self.STATE_BODY_WITH_BLANK_LINE: - r =3D Re(r"\s*\*\s?\S") - if r.match(line): - self.dump_section() - self.entry.section =3D self.section_default - self.entry.new_start_line =3D line - self.entry.contents =3D "" - - if doc_sect.search(line): - self.entry.in_doc_sect =3D True - newsection =3D doc_sect.group(1) - - if newsection.lower() in ["description", "context"]: - newsection =3D newsection.title() - - # Special case: @return is a section, not a param description - if newsection.lower() in ["@return", "@returns", - "return", "returns"]: - newsection =3D "Return" - - # Perl kernel-doc has a check here for contents before section= s. - # the logic there is always false, as in_doc_sect variable is - # always true. So, just don't implement Wcontents_before_secti= ons - - # .title() - newcontents =3D doc_sect.group(2) - if not newcontents: - newcontents =3D "" - - if self.entry.contents.strip("\n"): - self.dump_section() - - self.entry.new_start_line =3D ln - self.entry.section =3D newsection - self.entry.leading_space =3D None - - self.entry.contents =3D newcontents.lstrip() - if self.entry.contents: - self.entry.contents +=3D "\n" - - self.state =3D self.STATE_BODY - return - - if doc_end.search(line): - self.dump_section() - - # Look for doc_com + + doc_end: - r =3D Re(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') - if r.match(line): - self.emit_warning(ln, f"suspicious ending line: {line}") - - self.entry.prototype =3D "" - self.entry.new_start_line =3D ln + 1 - - self.state =3D self.STATE_PROTO - return - - if doc_content.search(line): - cont =3D doc_content.group(1) - - if cont =3D=3D "": - if self.entry.section =3D=3D self.section_context: - self.dump_section() - - self.entry.new_start_line =3D ln - self.state =3D self.STATE_BODY - else: - if self.entry.section !=3D self.section_default: - self.state =3D self.STATE_BODY_WITH_BLANK_LINE - else: - self.state =3D self.STATE_BODY - - self.entry.contents +=3D "\n" - - elif self.state =3D=3D self.STATE_BODY_MAYBE: - - # Continued declaration purpose - self.entry.declaration_purpose =3D self.entry.declaration_= purpose.rstrip() - self.entry.declaration_purpose +=3D " " + cont - - r =3D Re(r"\s+") - self.entry.declaration_purpose =3D r.sub(' ', - self.entry.declarat= ion_purpose) - - else: - if self.entry.section.startswith('@') or \ - self.entry.section =3D=3D self.section_context: - if self.entry.leading_space is None: - r =3D Re(r'^(\s+)') - if r.match(cont): - self.entry.leading_space =3D len(r.group(1)) - else: - self.entry.leading_space =3D 0 - - # Double-check if leading space are realy spaces - pos =3D 0 - for i in range(0, self.entry.leading_space): - if cont[i] !=3D " ": - break - pos +=3D 1 - - cont =3D cont[pos:] - - # NEW LOGIC: - # In case it is different, update it - if self.entry.leading_space !=3D pos: - self.entry.leading_space =3D pos - - self.entry.contents +=3D cont + "\n" - return - - # Unknown line, ignore - self.emit_warning(ln, f"bad line: {line}") - - def process_inline(self, ln, line): - """STATE_INLINE: docbook comments within a prototype.""" - - if self.inline_doc_state =3D=3D self.STATE_INLINE_NAME and \ - doc_inline_sect.search(line): - self.entry.section =3D doc_inline_sect.group(1) - self.entry.new_start_line =3D ln - - self.entry.contents =3D doc_inline_sect.group(2).lstrip() - if self.entry.contents !=3D "": - self.entry.contents +=3D "\n" - - self.inline_doc_state =3D self.STATE_INLINE_TEXT - # Documentation block end */ - return - - if doc_inline_end.search(line): - if self.entry.contents not in ["", "\n"]: - self.dump_section() - - self.state =3D self.STATE_PROTO - self.inline_doc_state =3D self.STATE_INLINE_NA - return - - if doc_content.search(line): - if self.inline_doc_state =3D=3D self.STATE_INLINE_TEXT: - self.entry.contents +=3D doc_content.group(1) + "\n" - if not self.entry.contents.strip(" ").rstrip("\n"): - self.entry.contents =3D "" - - elif self.inline_doc_state =3D=3D self.STATE_INLINE_NAME: - self.emit_warning(ln, - f"Incorrect use of kernel-doc format: {l= ine}") - - self.inline_doc_state =3D self.STATE_INLINE_ERROR - - def syscall_munge(self, ln, proto): - """ - Handle syscall definitions - """ - - is_void =3D False - - # Strip newlines/CR's - proto =3D re.sub(r'[\r\n]+', ' ', proto) - - # Check if it's a SYSCALL_DEFINE0 - if 'SYSCALL_DEFINE0' in proto: - is_void =3D True - - # Replace SYSCALL_DEFINE with correct return type & function name - proto =3D Re(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) - - r =3D Re(r'long\s+(sys_.*?),') - if r.search(proto): - proto =3D proto.replace(',', '(', count=3D1) - elif is_void: - proto =3D proto.replace(')', '(void)', count=3D1) - - # Now delete all of the odd-numbered commas in the proto - # so that argument types & names don't have a comma between them - count =3D 0 - length =3D len(proto) - - if is_void: - length =3D 0 # skip the loop if is_void - - for ix in range(length): - if proto[ix] =3D=3D ',': - count +=3D 1 - if count % 2 =3D=3D 1: - proto =3D proto[:ix] + ' ' + proto[ix+1:] - - return proto - - def tracepoint_munge(self, ln, proto): - """ - Handle tracepoint definitions - """ - - tracepointname =3D None - tracepointargs =3D None - - # Match tracepoint name based on different patterns - r =3D Re(r'TRACE_EVENT\((.*?),') - if r.search(proto): - tracepointname =3D r.group(1) - - r =3D Re(r'DEFINE_SINGLE_EVENT\((.*?),') - if r.search(proto): - tracepointname =3D r.group(1) - - r =3D Re(r'DEFINE_EVENT\((.*?),(.*?),') - if r.search(proto): - tracepointname =3D r.group(2) - - if tracepointname: - tracepointname =3D tracepointname.lstrip() - - r =3D Re(r'TP_PROTO\((.*?)\)') - if r.search(proto): - tracepointargs =3D r.group(1) - - if not tracepointname or not tracepointargs: - self.emit_warning(ln, - f"Unrecognized tracepoint format:\n{proto}\n= ") - else: - proto =3D f"static inline void trace_{tracepointname}({tracepo= intargs})" - self.entry.identifier =3D f"trace_{self.entry.identifier}" - - return proto - - def process_proto_function(self, ln, line): - """Ancillary routine to process a function prototype""" - - # strip C99-style comments to end of line - r =3D Re(r"\/\/.*$", re.S) - line =3D r.sub('', line) - - if Re(r'\s*#\s*define').match(line): - self.entry.prototype =3D line - elif line.startswith('#'): - # Strip other macros like #ifdef/#ifndef/#endif/... - pass - else: - r =3D Re(r'([^\{]*)') - if r.match(line): - self.entry.prototype +=3D r.group(1) + " " - - if '{' in line or ';' in line or Re(r'\s*#\s*define').match(line): - # strip comments - r =3D Re(r'/\*.*?\*/') - self.entry.prototype =3D r.sub('', self.entry.prototype) - - # strip newlines/cr's - r =3D Re(r'[\r\n]+') - self.entry.prototype =3D r.sub(' ', self.entry.prototype) - - # strip leading spaces - r =3D Re(r'^\s+') - self.entry.prototype =3D r.sub('', self.entry.prototype) - - # Handle self.entry.prototypes for function pointers like: - # int (*pcs_config)(struct foo) - - r =3D Re(r'^(\S+\s+)\(\s*\*(\S+)\)') - self.entry.prototype =3D r.sub(r'\1\2', self.entry.prototype) - - if 'SYSCALL_DEFINE' in self.entry.prototype: - self.entry.prototype =3D self.syscall_munge(ln, - self.entry.proto= type) - - r =3D Re(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') - if r.search(self.entry.prototype): - self.entry.prototype =3D self.tracepoint_munge(ln, - self.entry.pr= ototype) - - self.dump_function(ln, self.entry.prototype) - self.reset_state(ln) - - def process_proto_type(self, ln, line): - """Ancillary routine to process a type""" - - # Strip newlines/cr's. - line =3D Re(r'[\r\n]+', re.S).sub(' ', line) - - # Strip leading spaces - line =3D Re(r'^\s+', re.S).sub('', line) - - # Strip trailing spaces - line =3D Re(r'\s+$', re.S).sub('', line) - - # Strip C99-style comments to the end of the line - line =3D Re(r"\/\/.*$", re.S).sub('', line) - - # To distinguish preprocessor directive from regular declaration l= ater. - if line.startswith('#'): - line +=3D ";" - - r =3D Re(r'([^\{\};]*)([\{\};])(.*)') - while True: - if r.search(line): - if self.entry.prototype: - self.entry.prototype +=3D " " - self.entry.prototype +=3D r.group(1) + r.group(2) - - self.entry.brcount +=3D r.group(2).count('{') - self.entry.brcount -=3D r.group(2).count('}') - - self.entry.brcount =3D max(self.entry.brcount, 0) - - if r.group(2) =3D=3D ';' and self.entry.brcount =3D=3D 0: - self.dump_declaration(ln, self.entry.prototype) - self.reset_state(ln) - break - - line =3D r.group(3) - else: - self.entry.prototype +=3D line - break - - def process_proto(self, ln, line): - """STATE_PROTO: reading a function/whatever prototype.""" - - if doc_inline_oneline.search(line): - self.entry.section =3D doc_inline_oneline.group(1) - self.entry.contents =3D doc_inline_oneline.group(2) - - if self.entry.contents !=3D "": - self.entry.contents +=3D "\n" - self.dump_section(start_new=3DFalse) - - elif doc_inline_start.search(line): - self.state =3D self.STATE_INLINE - self.inline_doc_state =3D self.STATE_INLINE_NAME - - elif self.entry.decl_type =3D=3D 'function': - self.process_proto_function(ln, line) - - else: - self.process_proto_type(ln, line) - - def process_docblock(self, ln, line): - """STATE_DOCBLOCK: within a DOC: block.""" - - if doc_end.search(line): - self.dump_section() - self.output_declaration("doc", None, - sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, = module=3Dself.config.modulename) - self.reset_state(ln) - - elif doc_content.search(line): - self.entry.contents +=3D doc_content.group(1) + "\n" - - def run(self): - """ - Open and process each line of a C source file. - he parsing is controlled via a state machine, and the line is pass= ed - to a different process function depending on the state. The process - function may update the state as needed. - """ - - cont =3D False - prev =3D "" - prev_ln =3D None - - try: - with open(self.fname, "r", encoding=3D"utf8", - errors=3D"backslashreplace") as fp: - for ln, line in enumerate(fp): - - line =3D line.expandtabs().strip("\n") - - # Group continuation lines on prototypes - if self.state =3D=3D self.STATE_PROTO: - if line.endswith("\\"): - prev +=3D line.removesuffix("\\") - cont =3D True - - if not prev_ln: - prev_ln =3D ln - - continue - - if cont: - ln =3D prev_ln - line =3D prev + line - prev =3D "" - cont =3D False - prev_ln =3D None - - self.config.log.debug("%d %s%s: %s", - ln, self.st_name[self.state], - self.st_inline_name[self.inline_= doc_state], - line) - - # TODO: not all states allow EXPORT_SYMBOL*, so this - # can be optimized later on to speedup parsing - self.process_export(self.config.function_table, line) - - # Hand this line to the appropriate state handler - if self.state =3D=3D self.STATE_NORMAL: - self.process_normal(ln, line) - elif self.state =3D=3D self.STATE_NAME: - self.process_name(ln, line) - elif self.state in [self.STATE_BODY, self.STATE_BODY_M= AYBE, - self.STATE_BODY_WITH_BLANK_LINE]: - self.process_body(ln, line) - elif self.state =3D=3D self.STATE_INLINE: # scanning = for inline parameters - self.process_inline(ln, line) - elif self.state =3D=3D self.STATE_PROTO: - self.process_proto(ln, line) - elif self.state =3D=3D self.STATE_DOCBLOCK: - self.process_docblock(ln, line) - except OSError: - self.config.log.error(f"Error: Cannot open file {self.fname}") - self.config.errors +=3D 1 - - class GlobSourceFiles: """ Parse C source code file names and directories via an Interactor. diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py new file mode 100755 index 000000000000..6d6395e32093 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -0,0 +1,1689 @@ +#!/usr/bin/env python3 +# pylint: disable=3DC0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + +""" +kdoc_parser +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Read a C language source or header FILE and extract embedded +documentation comments +""" + +import argparse +import re +from pprint import pformat + +from kdoc_re import NestedMatch, Re + + +# +# Regular expressions used to parse kernel-doc markups at KernelDoc class. +# +# Let's declare them in lowercase outside any class to make easier to +# convert from the python script. +# +# As those are evaluated at the beginning, no need to cache them +# + +# Allow whitespace at end of comment start. +doc_start =3D Re(r'^/\*\*\s*$', cache=3DFalse) + +doc_end =3D Re(r'\*/', cache=3DFalse) +doc_com =3D Re(r'\s*\*\s*', cache=3DFalse) +doc_com_body =3D Re(r'\s*\* ?', cache=3DFalse) +doc_decl =3D doc_com + Re(r'(\w+)', cache=3DFalse) + +# @params and a strictly limited set of supported section names +# Specifically: +# Match @word: +# @...: +# @{section-name}: +# while trying to not match literal block starts like "example::" +# +doc_sect =3D doc_com + \ + Re(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?= |examples?)\s*:([^:].*)?$', + flags=3Dre.I, cache=3DFalse) + +doc_content =3D doc_com_body + Re(r'(.*)', cache=3DFalse) +doc_block =3D doc_com + Re(r'DOC:\s*(.*)?', cache=3DFalse) +doc_inline_start =3D Re(r'^\s*/\*\*\s*$', cache=3DFalse) +doc_inline_sect =3D Re(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=3DFalse) +doc_inline_end =3D Re(r'^\s*\*/\s*$', cache=3DFalse) +doc_inline_oneline =3D Re(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cac= he=3DFalse) +attribute =3D Re(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", + flags=3Dre.I | re.S, cache=3DFalse) + +export_symbol =3D Re(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cac= he=3DFalse) +export_symbol_ns =3D Re(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"= \S+"\)\s*', cache=3DFalse) + +type_param =3D Re(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=3DFalse) + + +class KernelDoc: + """ + Read a C language source or header FILE and extract embedded + documentation comments. + """ + + # Parser states + STATE_NORMAL =3D 0 # normal code + STATE_NAME =3D 1 # looking for function name + STATE_BODY_MAYBE =3D 2 # body - or maybe more description + STATE_BODY =3D 3 # the body of the comment + STATE_BODY_WITH_BLANK_LINE =3D 4 # the body which has a blank line + STATE_PROTO =3D 5 # scanning prototype + STATE_DOCBLOCK =3D 6 # documentation block + STATE_INLINE =3D 7 # gathering doc outside main block + + st_name =3D [ + "NORMAL", + "NAME", + "BODY_MAYBE", + "BODY", + "BODY_WITH_BLANK_LINE", + "PROTO", + "DOCBLOCK", + "INLINE", + ] + + # Inline documentation state + STATE_INLINE_NA =3D 0 # not applicable ($state !=3D STATE_INLINE) + STATE_INLINE_NAME =3D 1 # looking for member name (@foo:) + STATE_INLINE_TEXT =3D 2 # looking for member documentation + STATE_INLINE_END =3D 3 # done + STATE_INLINE_ERROR =3D 4 # error - Comment without header was found. + # Spit a warning as it's not + # proper kernel-doc and ignore the rest. + + st_inline_name =3D [ + "", + "_NAME", + "_TEXT", + "_END", + "_ERROR", + ] + + # Section names + + section_default =3D "Description" # default section + section_intro =3D "Introduction" + section_context =3D "Context" + section_return =3D "Return" + + undescribed =3D "-- undescribed --" + + def __init__(self, config, fname): + """Initialize internal variables""" + + self.fname =3D fname + self.config =3D config + + # Initial state for the state machines + self.state =3D self.STATE_NORMAL + self.inline_doc_state =3D self.STATE_INLINE_NA + + # Store entry currently being processed + self.entry =3D None + + # Place all potential outputs into an array + self.entries =3D [] + + def show_warnings(self, dtype, declaration_name): # pylint: disable= =3DW0613 + """ + Allow filtering out warnings + """ + + # TODO: implement it + + return True + + # TODO: rename to emit_message + def emit_warning(self, ln, msg, warning=3DTrue): + """Emit a message""" + + if warning: + self.config.log.warning("%s:%d %s", self.fname, ln, msg) + else: + self.config.log.info("%s:%d %s", self.fname, ln, msg) + + def dump_section(self, start_new=3DTrue): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + + name =3D self.entry.section + contents =3D self.entry.contents + + # TODO: we can prevent dumping empty sections here with: + # + # if self.entry.contents.strip("\n"): + # if start_new: + # self.entry.section =3D self.section_default + # self.entry.contents =3D "" + # + # return + # + # But, as we want to be producing the same output of the + # venerable kernel-doc Perl tool, let's just output everything, + # at least for now + + if type_param.match(name): + name =3D type_param.group(1) + + self.entry.parameterdescs[name] =3D contents + self.entry.parameterdesc_start_lines[name] =3D self.entry.new_= start_line + + self.entry.sectcheck +=3D name + " " + self.entry.new_start_line =3D 0 + + elif name =3D=3D "@...": + name =3D "..." + self.entry.parameterdescs[name] =3D contents + self.entry.sectcheck +=3D name + " " + self.entry.parameterdesc_start_lines[name] =3D self.entry.new_= start_line + self.entry.new_start_line =3D 0 + + else: + if name in self.entry.sections and self.entry.sections[name] != =3D "": + # Only warn on user-specified duplicate section names + if name !=3D self.section_default: + self.emit_warning(self.entry.new_start_line, + f"duplicate section name '{name}'\n") + self.entry.sections[name] +=3D contents + else: + self.entry.sections[name] =3D contents + self.entry.sectionlist.append(name) + self.entry.section_start_lines[name] =3D self.entry.new_st= art_line + self.entry.new_start_line =3D 0 + +# self.config.log.debug("Section: %s : %s", name, pformat(vars(self= .entry))) + + if start_new: + self.entry.section =3D self.section_default + self.entry.contents =3D "" + + # TODO: rename it to store_declaration + def output_declaration(self, dtype, name, **args): + """ + Stores the entry into an entry array. + + The actual output and output filters will be handled elsewhere + """ + + # The implementation here is different than the original kernel-do= c: + # instead of checking for output filters or actually output anythi= ng, + # it just stores the declaration content at self.entries, as the + # output will happen on a separate class. + # + # For now, we're keeping the same name of the function just to make + # easier to compare the source code of both scripts + + if "declaration_start_line" not in args: + args["declaration_start_line"] =3D self.entry.declaration_star= t_line + + args["type"] =3D dtype + + # TODO: use colletions.OrderedDict + + sections =3D args.get('sections', {}) + sectionlist =3D args.get('sectionlist', []) + + # Drop empty sections + # TODO: improve it to emit warnings + for section in ["Description", "Return"]: + if section in sectionlist: + if not sections[section].rstrip(): + del sections[section] + sectionlist.remove(section) + + self.entries.append((name, args)) + + self.config.log.debug("Output: %s:%s =3D %s", dtype, name, pformat= (args)) + + def reset_state(self, ln): + """ + Ancillary routine to create a new entry. It initializes all + variables used by the state machine. + """ + + self.entry =3D argparse.Namespace + + self.entry.contents =3D "" + self.entry.function =3D "" + self.entry.sectcheck =3D "" + self.entry.struct_actual =3D "" + self.entry.prototype =3D "" + + self.entry.parameterlist =3D [] + self.entry.parameterdescs =3D {} + self.entry.parametertypes =3D {} + self.entry.parameterdesc_start_lines =3D {} + + self.entry.section_start_lines =3D {} + self.entry.sectionlist =3D [] + self.entry.sections =3D {} + + self.entry.anon_struct_union =3D False + + self.entry.leading_space =3D None + + # State flags + self.state =3D self.STATE_NORMAL + self.inline_doc_state =3D self.STATE_INLINE_NA + self.entry.brcount =3D 0 + + self.entry.in_doc_sect =3D False + self.entry.declaration_start_line =3D ln + + def push_parameter(self, ln, decl_type, param, dtype, + org_arg, declaration_name): + """ + Store parameters and their descriptions at self.entry. + """ + + if self.entry.anon_struct_union and dtype =3D=3D "" and param =3D= =3D "}": + return # Ignore the ending }; from anonymous struct/union + + self.entry.anon_struct_union =3D False + + param =3D Re(r'[\[\)].*').sub('', param, count=3D1) + + if dtype =3D=3D "" and param.endswith("..."): + if Re(r'\w\.\.\.$').search(param): + # For named variable parameters of the form `x...`, + # remove the dots + param =3D param[:-3] + else: + # Handles unnamed variable parameters + param =3D "..." + + if param not in self.entry.parameterdescs or \ + not self.entry.parameterdescs[param]: + + self.entry.parameterdescs[param] =3D "variable arguments" + + elif dtype =3D=3D "" and (not param or param =3D=3D "void"): + param =3D "void" + self.entry.parameterdescs[param] =3D "no arguments" + + elif dtype =3D=3D "" and param in ["struct", "union"]: + # Handle unnamed (anonymous) union or struct + dtype =3D param + param =3D "{unnamed_" + param + "}" + self.entry.parameterdescs[param] =3D "anonymous\n" + self.entry.anon_struct_union =3D True + + # Handle cache group enforcing variables: they do not need + # to be described in header files + elif "__cacheline_group" in param: + # Ignore __cacheline_group_begin and __cacheline_group_end + return + + # Warn if parameter has no description + # (but ignore ones starting with # as these are not parameters + # but inline preprocessor statements) + if param not in self.entry.parameterdescs and not param.startswith= ("#"): + self.entry.parameterdescs[param] =3D self.undescribed + + if self.show_warnings(dtype, declaration_name) and "." not in = param: + if decl_type =3D=3D 'function': + dname =3D f"{decl_type} parameter" + else: + dname =3D f"{decl_type} member" + + self.emit_warning(ln, + f"{dname} '{param}' not described in '{d= eclaration_name}'") + + # Strip spaces from param so that it is one continuous string on + # parameterlist. This fixes a problem where check_sections() + # cannot find a parameter like "addr[6 + 2]" because it actually + # appears as "addr[6", "+", "2]" on the parameter list. + # However, it's better to maintain the param string unchanged for + # output, so just weaken the string compare in check_sections() + # to ignore "[blah" in a parameter string. + + self.entry.parameterlist.append(param) + org_arg =3D Re(r'\s\s+').sub(' ', org_arg) + self.entry.parametertypes[param] =3D org_arg + + def save_struct_actual(self, actual): + """ + Strip all spaces from the actual param so that it looks like + one string item. + """ + + actual =3D Re(r'\s*').sub("", actual, count=3D1) + + self.entry.struct_actual +=3D actual + " " + + def create_parameter_list(self, ln, decl_type, args, + splitter, declaration_name): + """ + Creates a list of parameters, storing them at self.entry. + """ + + # temporarily replace all commas inside function pointer definition + arg_expr =3D Re(r'(\([^\),]+),') + while arg_expr.search(args): + args =3D arg_expr.sub(r"\1#", args) + + for arg in args.split(splitter): + # Strip comments + arg =3D Re(r'\/\*.*\*\/').sub('', arg) + + # Ignore argument attributes + arg =3D Re(r'\sPOS0?\s').sub(' ', arg) + + # Strip leading/trailing spaces + arg =3D arg.strip() + arg =3D Re(r'\s+').sub(' ', arg, count=3D1) + + if arg.startswith('#'): + # Treat preprocessor directive as a typeless variable just= to fill + # corresponding data structures "correctly". Catch it late= r in + # output_* subs. + + # Treat preprocessor directive as a typeless variable + self.push_parameter(ln, decl_type, arg, "", + "", declaration_name) + + elif Re(r'\(.+\)\s*\(').search(arg): + # Pointer-to-function + + arg =3D arg.replace('#', ',') + + r =3D Re(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') + if r.match(arg): + param =3D r.group(1) + else: + self.emit_warning(ln, f"Invalid param: {arg}") + param =3D arg + + dtype =3D Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r= '\1', arg) + self.save_struct_actual(param) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif Re(r'\(.+\)\s*\[').search(arg): + # Array-of-pointers + + arg =3D arg.replace('#', ',') + r =3D Re(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+= \s*\]\s*)*\)') + if r.match(arg): + param =3D r.group(1) + else: + self.emit_warning(ln, f"Invalid param: {arg}") + param =3D arg + + dtype =3D Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r= '\1', arg) + + self.save_struct_actual(param) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif arg: + arg =3D Re(r'\s*:\s*').sub(":", arg) + arg =3D Re(r'\s*\[').sub('[', arg) + + args =3D Re(r'\s*,\s*').split(arg) + if args[0] and '*' in args[0]: + args[0] =3D re.sub(r'(\*+)\s*', r' \1', args[0]) + + first_arg =3D [] + r =3D Re(r'^(.*\s+)(.*?\[.*\].*)$') + if args[0] and r.match(args[0]): + args.pop(0) + first_arg.extend(r.group(1)) + first_arg.append(r.group(2)) + else: + first_arg =3D Re(r'\s+').split(args.pop(0)) + + args.insert(0, first_arg.pop()) + dtype =3D ' '.join(first_arg) + + for param in args: + if Re(r'^(\*+)\s*(.*)').match(param): + r =3D Re(r'^(\*+)\s*(.*)') + if not r.match(param): + self.emit_warning(ln, f"Invalid param: {param}= ") + continue + + param =3D r.group(1) + + self.save_struct_actual(r.group(2)) + self.push_parameter(ln, decl_type, r.group(2), + f"{dtype} {r.group(1)}", + arg, declaration_name) + + elif Re(r'(.*?):(\w+)').search(param): + r =3D Re(r'(.*?):(\w+)') + if not r.match(param): + self.emit_warning(ln, f"Invalid param: {param}= ") + continue + + if dtype !=3D "": # Skip unnamed bit-fields + self.save_struct_actual(r.group(1)) + self.push_parameter(ln, decl_type, r.group(1), + f"{dtype}:{r.group(2)}", + arg, declaration_name) + else: + self.save_struct_actual(param) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + def check_sections(self, ln, decl_name, decl_type, sectcheck, prmschec= k): + """ + Check for errors inside sections, emitting warnings if not found + parameters are described. + """ + + sects =3D sectcheck.split() + prms =3D prmscheck.split() + err =3D False + + for sx in range(len(sects)): # pylint: disable=3D= C0200 + err =3D True + for px in range(len(prms)): # pylint: disable=3D= C0200 + prm_clean =3D prms[px] + prm_clean =3D Re(r'\[.*\]').sub('', prm_clean) + prm_clean =3D attribute.sub('', prm_clean) + + # ignore array size in a parameter string; + # however, the original param string may contain + # spaces, e.g.: addr[6 + 2] + # and this appears in @prms as "addr[6" since the + # parameter list is split at spaces; + # hence just ignore "[..." for the sections check; + prm_clean =3D Re(r'\[.*').sub('', prm_clean) + + if prm_clean =3D=3D sects[sx]: + err =3D False + break + + if err: + if decl_type =3D=3D 'function': + dname =3D f"{decl_type} parameter" + else: + dname =3D f"{decl_type} member" + + self.emit_warning(ln, + f"Excess {dname} '{sects[sx]}' descripti= on in '{decl_name}'") + + def check_return_section(self, ln, declaration_name, return_type): + """ + If the function doesn't return void, warns about the lack of a + return description. + """ + + if not self.config.wreturn: + return + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type (but not "void *") + if not return_type or Re(r'void\s*\w*\s*$').search(return_type): + return + + if not self.entry.sections.get("Return", None): + self.emit_warning(ln, + f"No description found for return value of '= {declaration_name}'") + + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + + type_pattern =3D r'(struct|union)' + + qualifiers =3D [ + "__attribute__", + "__packed", + "__aligned", + "____cacheline_aligned_in_smp", + "____cacheline_aligned", + ] + + definition_body =3D r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) = + ")?" + struct_members =3D Re(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\= })([^\{\}\;]*)(\;)') + + # Extract struct/union definition + members =3D None + declaration_name =3D None + decl_type =3D None + + r =3D Re(type_pattern + r'\s+(\w+)\s*' + definition_body) + if r.search(proto): + decl_type =3D r.group(1) + declaration_name =3D r.group(2) + members =3D r.group(3) + else: + r =3D Re(r'typedef\s+' + type_pattern + r'\s*' + definition_bo= dy + r'\s*(\w+)\s*;') + + if r.search(proto): + decl_type =3D r.group(1) + declaration_name =3D r.group(3) + members =3D r.group(2) + + if not members: + self.emit_warning(ln, f"{proto} error: Cannot parse struct or = union!") + self.config.errors +=3D 1 + return + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, + f"expecting prototype for {decl_type} {self.= entry.identifier}. Prototype was for {decl_type} {declaration_name} instead= \n") + return + + args_pattern =3D r'([^,)]+)' + + sub_prefixes =3D [ + (Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), = ''), + (Re(r'\/\*\s*private:.*', re.S | re.I), ''), + + # Strip comments + (Re(r'\/\*.*?\*\/', re.S), ''), + + # Strip attributes + (attribute, ' '), + (Re(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), + (Re(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), + (Re(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), + (Re(r'\s*__packed\s*', re.S), ' '), + (Re(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), + (Re(r'\s*____cacheline_aligned_in_smp', re.S), ' '), + (Re(r'\s*____cacheline_aligned', re.S), ' '), + + # Unwrap struct_group macros based on this definition: + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) + # which has variants like: struct_group(NAME, MEMBERS...) + # Only MEMBERS arguments require documentation. + # + # Parsing them happens on two steps: + # + # 1. drop struct group arguments that aren't at MEMBERS, + # storing them as STRUCT_GROUP(MEMBERS) + # + # 2. remove STRUCT_GROUP() ancillary macro. + # + # The original logic used to remove STRUCT_GROUP() using an + # advanced regex: + # + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; + # + # with two patterns that are incompatible with + # Python re module, as it has: + # + # - a recursive pattern: (?1) + # - an atomic grouping: (?>...) + # + # I tried a simpler version: but it didn't work either: + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; + # + # As it doesn't properly match the end parenthesis on some cas= es. + # + # So, a better solution was crafted: there's now a NestedMatch + # class that ensures that delimiters after a search are proper= ly + # matched. So, the implementation to drop STRUCT_GROUP() will = be + # handled in separate. + + (Re(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), + (Re(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GR= OUP('), + (Re(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'st= ruct \1 \2; STRUCT_GROUP('), + (Re(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP= ('), + + # Replace macros + # + # TODO: it is better to also move those to the NestedMatch log= ic, + # to ensure that parenthesis will be properly matched. + + (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),= r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), + (Re(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DEC= LARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), + (Re(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pat= tern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), + (Re(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_= pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), + (Re(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_patt= ern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (Re(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_= pattern + r'\)', re.S), r'\2 *\1'), + (Re(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*'= + args_pattern + r'\)', re.S), r'\1 \2[]'), + (Re(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S= ), r'dma_addr_t \1'), + (Re(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S)= , r'__u32 \1'), + ] + + # Regexes here are guaranteed to have the end limiter matching + # the start delimiter. Yet, right now, only one replace group + # is allowed. + + sub_nested_prefixes =3D [ + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), + ] + + for search, sub in sub_prefixes: + members =3D search.sub(sub, members) + + nested =3D NestedMatch() + + for search, sub in sub_nested_prefixes: + members =3D nested.sub(search, sub, members) + + # Keeps the original declaration as-is + declaration =3D members + + # Split nested struct/union elements + # + # This loop was simpler at the original kernel-doc perl version, as + # while ($members =3D~ m/$struct_members/) { ... } + # reads 'members' string on each interaction. + # + # Python behavior is different: it parses 'members' only once, + # creating a list of tuples from the first interaction. + # + # On other words, this won't get nested structs. + # + # So, we need to have an extra loop on Python to override such + # re limitation. + + while True: + tuples =3D struct_members.findall(members) + if not tuples: + break + + for t in tuples: + newmember =3D "" + maintype =3D t[0] + s_ids =3D t[5] + content =3D t[3] + + oldmember =3D "".join(t) + + for s_id in s_ids.split(','): + s_id =3D s_id.strip() + + newmember +=3D f"{maintype} {s_id}; " + s_id =3D Re(r'[:\[].*').sub('', s_id) + s_id =3D Re(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) + + for arg in content.split(';'): + arg =3D arg.strip() + + if not arg: + continue + + r =3D Re(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') + if r.match(arg): + # Pointer-to-function + dtype =3D r.group(1) + name =3D r.group(2) + extra =3D r.group(3) + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember +=3D f"{dtype}{name}{extra}; " + else: + newmember +=3D f"{dtype}{s_id}.{name}{extr= a}; " + + else: + arg =3D arg.strip() + # Handle bitmaps + arg =3D Re(r':\s*\d+\s*').sub('', arg) + + # Handle arrays + arg =3D Re(r'\[.*\]').sub('', arg) + + # Handle multiple IDs + arg =3D Re(r'\s*,\s*').sub(',', arg) + + r =3D Re(r'(.*)\s+([\S+,]+)') + + if r.search(arg): + dtype =3D r.group(1) + names =3D r.group(2) + else: + newmember +=3D f"{arg}; " + continue + + for name in names.split(','): + name =3D Re(r'^\s*\**(\S+)\s*').sub(r'\1',= name).strip() + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember +=3D f"{dtype} {name}; " + else: + newmember +=3D f"{dtype} {s_id}.{name}= ; " + + members =3D members.replace(oldmember, newmember) + + # Ignore other nested elements, like enums + members =3D re.sub(r'(\{[^\{\}]*\})', '', members) + + self.create_parameter_list(ln, decl_type, members, ';', + declaration_name) + self.check_sections(ln, declaration_name, decl_type, + self.entry.sectcheck, self.entry.struct_actual) + + # Adjust declaration for better display + declaration =3D Re(r'([\{;])').sub(r'\1\n', declaration) + declaration =3D Re(r'\}\s+;').sub('};', declaration) + + # Better handle inlined enums + while True: + r =3D Re(r'(enum\s+\{[^\}]+),([^\n])') + if not r.search(declaration): + break + + declaration =3D r.sub(r'\1,\n\2', declaration) + + def_args =3D declaration.split('\n') + level =3D 1 + declaration =3D "" + for clause in def_args: + + clause =3D clause.strip() + clause =3D Re(r'\s+').sub(' ', clause, count=3D1) + + if not clause: + continue + + if '}' in clause and level > 1: + level -=3D 1 + + if not Re(r'^\s*#').match(clause): + declaration +=3D "\t" * level + + declaration +=3D "\t" + clause + "\n" + if "{" in clause and "}" not in clause: + level +=3D 1 + + self.output_declaration(decl_type, declaration_name, + struct=3Ddeclaration_name, + module=3Dself.entry.modulename, + definition=3Ddeclaration, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + parametertypes=3Dself.entry.parametertypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose) + + def dump_enum(self, ln, proto): + """ + Stores an enum inside self.entries array. + """ + + # Ignore members marked private + proto =3D Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=3Dr= e.S).sub('', proto) + proto =3D Re(r'\/\*\s*private:.*}', flags=3Dre.S).sub('}', proto) + + # Strip comments + proto =3D Re(r'\/\*.*?\*\/', flags=3Dre.S).sub('', proto) + + # Strip #define macros inside enums + proto =3D Re(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=3Dre= .S).sub('', proto) + + members =3D None + declaration_name =3D None + + r =3D Re(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') + if r.search(proto): + declaration_name =3D r.group(2) + members =3D r.group(1).rstrip() + else: + r =3D Re(r'enum\s+(\w*)\s*\{(.*)\}') + if r.match(proto): + declaration_name =3D r.group(1) + members =3D r.group(2).rstrip() + + if not members: + self.emit_warning(ln, f"{proto}: error: Cannot parse enum!") + self.config.errors +=3D 1 + return + + if self.entry.identifier !=3D declaration_name: + if self.entry.identifier =3D=3D "": + self.emit_warning(ln, + f"{proto}: wrong kernel-doc identifier o= n prototype") + else: + self.emit_warning(ln, + f"expecting prototype for enum {self.ent= ry.identifier}. Prototype was for enum {declaration_name} instead") + return + + if not declaration_name: + declaration_name =3D "(anonymous)" + + member_set =3D set() + + members =3D Re(r'\([^;]*?[\)]').sub('', members) + + for arg in members.split(','): + if not arg: + continue + arg =3D Re(r'^\s*(\w+).*').sub(r'\1', arg) + self.entry.parameterlist.append(arg) + if arg not in self.entry.parameterdescs: + self.entry.parameterdescs[arg] =3D self.undescribed + if self.show_warnings("enum", declaration_name): + self.emit_warning(ln, + f"Enum value '{arg}' not described i= n enum '{declaration_name}'") + member_set.add(arg) + + for k in self.entry.parameterdescs: + if k not in member_set: + if self.show_warnings("enum", declaration_name): + self.emit_warning(ln, + f"Excess enum value '%{k}' descripti= on in '{declaration_name}'") + + self.output_declaration('enum', declaration_name, + enum=3Ddeclaration_name, + module=3Dself.config.modulename, + parameterlist=3Dself.entry.parameterlist, + parameterdescs=3Dself.entry.parameterdescs, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpose) + + def dump_declaration(self, ln, prototype): + """ + Stores a data declaration inside self.entries array. + """ + + if self.entry.decl_type =3D=3D "enum": + self.dump_enum(ln, prototype) + return + + if self.entry.decl_type =3D=3D "typedef": + self.dump_typedef(ln, prototype) + return + + if self.entry.decl_type in ["union", "struct"]: + self.dump_struct(ln, prototype) + return + + # TODO: handle other types + self.output_declaration(self.entry.decl_type, prototype, + entry=3Dself.entry) + + def dump_function(self, ln, prototype): + """ + Stores a function of function macro inside self.entries array. + """ + + func_macro =3D False + return_type =3D '' + decl_type =3D 'function' + + # Prefixes that would be removed + sub_prefixes =3D [ + (r"^static +", "", 0), + (r"^extern +", "", 0), + (r"^asmlinkage +", "", 0), + (r"^inline +", "", 0), + (r"^__inline__ +", "", 0), + (r"^__inline +", "", 0), + (r"^__always_inline +", "", 0), + (r"^noinline +", "", 0), + (r"^__FORTIFY_INLINE +", "", 0), + (r"__init +", "", 0), + (r"__init_or_module +", "", 0), + (r"__deprecated +", "", 0), + (r"__flatten +", "", 0), + (r"__meminit +", "", 0), + (r"__must_check +", "", 0), + (r"__weak +", "", 0), + (r"__sched +", "", 0), + (r"_noprof", "", 0), + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", = 0), + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2= ", 0), + (r"__attribute_const__ +", "", 0), + + # It seems that Python support for re.X is broken: + # At least for me (Python 3.13), this didn't work +# (r""" +# __attribute__\s*\(\( +# (?: +# [\w\s]+ # attribute name +# (?:\([^)]*\))? # attribute arguments +# \s*,? # optional comma at the end +# )+ +# \)\)\s+ +# """, "", re.X), + + # So, remove whitespaces and comments from it + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"= , "", 0), + ] + + for search, sub, flags in sub_prefixes: + prototype =3D Re(search, flags).sub(sub, prototype) + + # Macros are a special case, as they change the prototype format + new_proto =3D Re(r"^#\s*define\s+").sub("", prototype) + if new_proto !=3D prototype: + is_define_proto =3D True + prototype =3D new_proto + else: + is_define_proto =3D False + + # Yes, this truly is vile. We are looking for: + # 1. Return type (may be nothing if we're looking at a macro) + # 2. Function name + # 3. Function parameters. + # + # All the while we have to watch out for function pointer paramete= rs + # (which IIRC is what the two sections are for), C types (these + # regexps don't even start to express all the possibilities), and + # so on. + # + # If you mess with these regexps, it's a good idea to check that + # the following functions' documentation still comes out right: + # - parport_register_device (function pointer parameters) + # - atomic_set (macro) + # - pci_match_device, __copy_to_user (long return type) + + name =3D r'[a-zA-Z0-9_~:]+' + prototype_end1 =3D r'[^\(]*' + prototype_end2 =3D r'[^\{]*' + prototype_end =3D fr'\(({prototype_end1}|{prototype_end2})\)' + + # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing gro= up. + # So, this needs to be mapped in Python with (?:...)? or (?:...)+ + + type1 =3D r'(?:[\w\s]+)?' + type2 =3D r'(?:[\w\s]+\*+)+' + + found =3D False + + if is_define_proto: + r =3D Re(r'^()(' + name + r')\s+') + + if r.search(prototype): + return_type =3D '' + declaration_name =3D r.group(2) + func_macro =3D True + + found =3D True + + if not found: + patterns =3D [ + rf'^()({name})\s*{prototype_end}', + rf'^({type1})\s+({name})\s*{prototype_end}', + rf'^({type2})\s*({name})\s*{prototype_end}', + ] + + for p in patterns: + r =3D Re(p) + + if r.match(prototype): + + return_type =3D r.group(1) + declaration_name =3D r.group(2) + args =3D r.group(3) + + self.create_parameter_list(ln, decl_type, args, ',', + declaration_name) + + found =3D True + break + if not found: + self.emit_warning(ln, + f"cannot understand function prototype: '{pr= ototype}'") + return + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, + f"expecting prototype for {self.entry.identi= fier}(). Prototype was for {declaration_name}() instead") + return + + prms =3D " ".join(self.entry.parameterlist) + self.check_sections(ln, declaration_name, "function", + self.entry.sectcheck, prms) + + self.check_return_section(ln, declaration_name, return_type) + + if 'typedef' in return_type: + self.output_declaration(decl_type, declaration_name, + function=3Ddeclaration_name, + typedef=3DTrue, + module=3Dself.config.modulename, + functiontype=3Dreturn_type, + parameterlist=3Dself.entry.parameterli= st, + parameterdescs=3Dself.entry.parameterd= escs, + parametertypes=3Dself.entry.parametert= ypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpo= se, + func_macro=3Dfunc_macro) + else: + self.output_declaration(decl_type, declaration_name, + function=3Ddeclaration_name, + typedef=3DFalse, + module=3Dself.config.modulename, + functiontype=3Dreturn_type, + parameterlist=3Dself.entry.parameterli= st, + parameterdescs=3Dself.entry.parameterd= escs, + parametertypes=3Dself.entry.parametert= ypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpo= se, + func_macro=3Dfunc_macro) + + def dump_typedef(self, ln, proto): + """ + Stores a typedef inside self.entries array. + """ + + typedef_type =3D r'((?:\s+[\w\*]+\b){1,8})\s*' + typedef_ident =3D r'\*?\s*(\w\S+)\s*' + typedef_args =3D r'\s*\((.*)\);' + + typedef1 =3D Re(r'typedef' + typedef_type + r'\(' + typedef_ident = + r'\)' + typedef_args) + typedef2 =3D Re(r'typedef' + typedef_type + typedef_ident + typede= f_args) + + # Strip comments + proto =3D Re(r'/\*.*?\*/', flags=3Dre.S).sub('', proto) + + # Parse function typedef prototypes + for r in [typedef1, typedef2]: + if not r.match(proto): + continue + + return_type =3D r.group(1).strip() + declaration_name =3D r.group(2) + args =3D r.group(3) + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, + f"expecting prototype for typedef {self.= entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + decl_type =3D 'function' + self.create_parameter_list(ln, decl_type, args, ',', declarati= on_name) + + self.output_declaration(decl_type, declaration_name, + function=3Ddeclaration_name, + typedef=3DTrue, + module=3Dself.entry.modulename, + functiontype=3Dreturn_type, + parameterlist=3Dself.entry.parameterli= st, + parameterdescs=3Dself.entry.parameterd= escs, + parametertypes=3Dself.entry.parametert= ypes, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpo= se) + return + + # Handle nested parentheses or brackets + r =3D Re(r'(\(*.\)\s*|\[*.\]\s*);$') + while r.search(proto): + proto =3D r.sub('', proto) + + # Parse simple typedefs + r =3D Re(r'typedef.*\s+(\w+)\s*;') + if r.match(proto): + declaration_name =3D r.group(1) + + if self.entry.identifier !=3D declaration_name: + self.emit_warning(ln, f"expecting prototype for typedef {s= elf.entry.identifier}. Prototype was for typedef {declaration_name} instead= \n") + return + + self.output_declaration('typedef', declaration_name, + typedef=3Ddeclaration_name, + module=3Dself.entry.modulename, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, + purpose=3Dself.entry.declaration_purpo= se) + return + + self.emit_warning(ln, "error: Cannot parse typedef!") + self.config.errors +=3D 1 + + @staticmethod + def process_export(function_table, line): + """ + process EXPORT_SYMBOL* tags + + This method is called both internally and externally, so, it + doesn't use self. + """ + + if export_symbol.search(line): + symbol =3D export_symbol.group(2) + function_table.add(symbol) + + if export_symbol_ns.search(line): + symbol =3D export_symbol_ns.group(2) + function_table.add(symbol) + + def process_normal(self, ln, line): + """ + STATE_NORMAL: looking for the /** to begin everything. + """ + + if not doc_start.match(line): + return + + # start a new entry + self.reset_state(ln + 1) + self.entry.in_doc_sect =3D False + + # next line is always the function name + self.state =3D self.STATE_NAME + + def process_name(self, ln, line): + """ + STATE_NAME: Looking for the "name - description" line + """ + + if doc_block.search(line): + self.entry.new_start_line =3D ln + + if not doc_block.group(1): + self.entry.section =3D self.section_intro + else: + self.entry.section =3D doc_block.group(1) + + self.state =3D self.STATE_DOCBLOCK + return + + if doc_decl.search(line): + self.entry.identifier =3D doc_decl.group(1) + self.entry.is_kernel_comment =3D False + + decl_start =3D str(doc_com) # comment block asterisk + fn_type =3D r"(?:\w+\s*\*\s*)?" # type (for non-functions) + parenthesis =3D r"(?:\(\w*\))?" # optional parenthesis on fu= nction + decl_end =3D r"(?:[-:].*)" # end of the name part + + # test for pointer declaration type, foo * bar() - desc + r =3D Re(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}= ?$") + if r.search(line): + self.entry.identifier =3D r.group(1) + + # Test for data declaration + r =3D Re(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") + if r.search(line): + self.entry.decl_type =3D r.group(1) + self.entry.identifier =3D r.group(2) + self.entry.is_kernel_comment =3D True + else: + # Look for foo() or static void foo() - description; + # or misspelt identifier + + r1 =3D Re(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s= *{decl_end}?$") + r2 =3D Re(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis= }\s*{decl_end}$") + + for r in [r1, r2]: + if r.search(line): + self.entry.identifier =3D r.group(1) + self.entry.decl_type =3D "function" + + r =3D Re(r"define\s+") + self.entry.identifier =3D r.sub("", self.entry.ide= ntifier) + self.entry.is_kernel_comment =3D True + break + + self.entry.identifier =3D self.entry.identifier.strip(" ") + + self.state =3D self.STATE_BODY + + # if there's no @param blocks need to set up default section h= ere + self.entry.section =3D self.section_default + self.entry.new_start_line =3D ln + 1 + + r =3D Re("[-:](.*)") + if r.search(line): + # strip leading/trailing/multiple spaces + self.entry.descr =3D r.group(1).strip(" ") + + r =3D Re(r"\s+") + self.entry.descr =3D r.sub(" ", self.entry.descr) + self.entry.declaration_purpose =3D self.entry.descr + self.state =3D self.STATE_BODY_MAYBE + else: + self.entry.declaration_purpose =3D "" + + if not self.entry.is_kernel_comment: + self.emit_warning(ln, + f"This comment starts with '/**', but is= n't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{li= ne}") + self.state =3D self.STATE_NORMAL + + if not self.entry.declaration_purpose and self.config.wshort_d= esc: + self.emit_warning(ln, + f"missing initial short description on l= ine:\n{line}") + + if not self.entry.identifier and self.entry.decl_type !=3D "en= um": + self.emit_warning(ln, + f"wrong kernel-doc identifier on line:\n= {line}") + self.state =3D self.STATE_NORMAL + + if self.config.verbose: + self.emit_warning(ln, + f"Scanning doc for {self.entry.decl_type= } {self.entry.identifier}", + warning=3DFalse) + + return + + # Failed to find an identifier. Emit a warning + self.emit_warning(ln, f"Cannot find identifier on line:\n{line}") + + def process_body(self, ln, line): + """ + STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. + """ + + if self.state =3D=3D self.STATE_BODY_WITH_BLANK_LINE: + r =3D Re(r"\s*\*\s?\S") + if r.match(line): + self.dump_section() + self.entry.section =3D self.section_default + self.entry.new_start_line =3D line + self.entry.contents =3D "" + + if doc_sect.search(line): + self.entry.in_doc_sect =3D True + newsection =3D doc_sect.group(1) + + if newsection.lower() in ["description", "context"]: + newsection =3D newsection.title() + + # Special case: @return is a section, not a param description + if newsection.lower() in ["@return", "@returns", + "return", "returns"]: + newsection =3D "Return" + + # Perl kernel-doc has a check here for contents before section= s. + # the logic there is always false, as in_doc_sect variable is + # always true. So, just don't implement Wcontents_before_secti= ons + + # .title() + newcontents =3D doc_sect.group(2) + if not newcontents: + newcontents =3D "" + + if self.entry.contents.strip("\n"): + self.dump_section() + + self.entry.new_start_line =3D ln + self.entry.section =3D newsection + self.entry.leading_space =3D None + + self.entry.contents =3D newcontents.lstrip() + if self.entry.contents: + self.entry.contents +=3D "\n" + + self.state =3D self.STATE_BODY + return + + if doc_end.search(line): + self.dump_section() + + # Look for doc_com + + doc_end: + r =3D Re(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + if r.match(line): + self.emit_warning(ln, f"suspicious ending line: {line}") + + self.entry.prototype =3D "" + self.entry.new_start_line =3D ln + 1 + + self.state =3D self.STATE_PROTO + return + + if doc_content.search(line): + cont =3D doc_content.group(1) + + if cont =3D=3D "": + if self.entry.section =3D=3D self.section_context: + self.dump_section() + + self.entry.new_start_line =3D ln + self.state =3D self.STATE_BODY + else: + if self.entry.section !=3D self.section_default: + self.state =3D self.STATE_BODY_WITH_BLANK_LINE + else: + self.state =3D self.STATE_BODY + + self.entry.contents +=3D "\n" + + elif self.state =3D=3D self.STATE_BODY_MAYBE: + + # Continued declaration purpose + self.entry.declaration_purpose =3D self.entry.declaration_= purpose.rstrip() + self.entry.declaration_purpose +=3D " " + cont + + r =3D Re(r"\s+") + self.entry.declaration_purpose =3D r.sub(' ', + self.entry.declarat= ion_purpose) + + else: + if self.entry.section.startswith('@') or \ + self.entry.section =3D=3D self.section_context: + if self.entry.leading_space is None: + r =3D Re(r'^(\s+)') + if r.match(cont): + self.entry.leading_space =3D len(r.group(1)) + else: + self.entry.leading_space =3D 0 + + # Double-check if leading space are realy spaces + pos =3D 0 + for i in range(0, self.entry.leading_space): + if cont[i] !=3D " ": + break + pos +=3D 1 + + cont =3D cont[pos:] + + # NEW LOGIC: + # In case it is different, update it + if self.entry.leading_space !=3D pos: + self.entry.leading_space =3D pos + + self.entry.contents +=3D cont + "\n" + return + + # Unknown line, ignore + self.emit_warning(ln, f"bad line: {line}") + + def process_inline(self, ln, line): + """STATE_INLINE: docbook comments within a prototype.""" + + if self.inline_doc_state =3D=3D self.STATE_INLINE_NAME and \ + doc_inline_sect.search(line): + self.entry.section =3D doc_inline_sect.group(1) + self.entry.new_start_line =3D ln + + self.entry.contents =3D doc_inline_sect.group(2).lstrip() + if self.entry.contents !=3D "": + self.entry.contents +=3D "\n" + + self.inline_doc_state =3D self.STATE_INLINE_TEXT + # Documentation block end */ + return + + if doc_inline_end.search(line): + if self.entry.contents not in ["", "\n"]: + self.dump_section() + + self.state =3D self.STATE_PROTO + self.inline_doc_state =3D self.STATE_INLINE_NA + return + + if doc_content.search(line): + if self.inline_doc_state =3D=3D self.STATE_INLINE_TEXT: + self.entry.contents +=3D doc_content.group(1) + "\n" + if not self.entry.contents.strip(" ").rstrip("\n"): + self.entry.contents =3D "" + + elif self.inline_doc_state =3D=3D self.STATE_INLINE_NAME: + self.emit_warning(ln, + f"Incorrect use of kernel-doc format: {l= ine}") + + self.inline_doc_state =3D self.STATE_INLINE_ERROR + + def syscall_munge(self, ln, proto): # pylint: disable=3DW0613 + """ + Handle syscall definitions + """ + + is_void =3D False + + # Strip newlines/CR's + proto =3D re.sub(r'[\r\n]+', ' ', proto) + + # Check if it's a SYSCALL_DEFINE0 + if 'SYSCALL_DEFINE0' in proto: + is_void =3D True + + # Replace SYSCALL_DEFINE with correct return type & function name + proto =3D Re(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) + + r =3D Re(r'long\s+(sys_.*?),') + if r.search(proto): + proto =3D proto.replace(',', '(', count=3D1) + elif is_void: + proto =3D proto.replace(')', '(void)', count=3D1) + + # Now delete all of the odd-numbered commas in the proto + # so that argument types & names don't have a comma between them + count =3D 0 + length =3D len(proto) + + if is_void: + length =3D 0 # skip the loop if is_void + + for ix in range(length): + if proto[ix] =3D=3D ',': + count +=3D 1 + if count % 2 =3D=3D 1: + proto =3D proto[:ix] + ' ' + proto[ix + 1:] + + return proto + + def tracepoint_munge(self, ln, proto): + """ + Handle tracepoint definitions + """ + + tracepointname =3D None + tracepointargs =3D None + + # Match tracepoint name based on different patterns + r =3D Re(r'TRACE_EVENT\((.*?),') + if r.search(proto): + tracepointname =3D r.group(1) + + r =3D Re(r'DEFINE_SINGLE_EVENT\((.*?),') + if r.search(proto): + tracepointname =3D r.group(1) + + r =3D Re(r'DEFINE_EVENT\((.*?),(.*?),') + if r.search(proto): + tracepointname =3D r.group(2) + + if tracepointname: + tracepointname =3D tracepointname.lstrip() + + r =3D Re(r'TP_PROTO\((.*?)\)') + if r.search(proto): + tracepointargs =3D r.group(1) + + if not tracepointname or not tracepointargs: + self.emit_warning(ln, + f"Unrecognized tracepoint format:\n{proto}\n= ") + else: + proto =3D f"static inline void trace_{tracepointname}({tracepo= intargs})" + self.entry.identifier =3D f"trace_{self.entry.identifier}" + + return proto + + def process_proto_function(self, ln, line): + """Ancillary routine to process a function prototype""" + + # strip C99-style comments to end of line + r =3D Re(r"\/\/.*$", re.S) + line =3D r.sub('', line) + + if Re(r'\s*#\s*define').match(line): + self.entry.prototype =3D line + elif line.startswith('#'): + # Strip other macros like #ifdef/#ifndef/#endif/... + pass + else: + r =3D Re(r'([^\{]*)') + if r.match(line): + self.entry.prototype +=3D r.group(1) + " " + + if '{' in line or ';' in line or Re(r'\s*#\s*define').match(line): + # strip comments + r =3D Re(r'/\*.*?\*/') + self.entry.prototype =3D r.sub('', self.entry.prototype) + + # strip newlines/cr's + r =3D Re(r'[\r\n]+') + self.entry.prototype =3D r.sub(' ', self.entry.prototype) + + # strip leading spaces + r =3D Re(r'^\s+') + self.entry.prototype =3D r.sub('', self.entry.prototype) + + # Handle self.entry.prototypes for function pointers like: + # int (*pcs_config)(struct foo) + + r =3D Re(r'^(\S+\s+)\(\s*\*(\S+)\)') + self.entry.prototype =3D r.sub(r'\1\2', self.entry.prototype) + + if 'SYSCALL_DEFINE' in self.entry.prototype: + self.entry.prototype =3D self.syscall_munge(ln, + self.entry.proto= type) + + r =3D Re(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype =3D self.tracepoint_munge(ln, + self.entry.pr= ototype) + + self.dump_function(ln, self.entry.prototype) + self.reset_state(ln) + + def process_proto_type(self, ln, line): + """Ancillary routine to process a type""" + + # Strip newlines/cr's. + line =3D Re(r'[\r\n]+', re.S).sub(' ', line) + + # Strip leading spaces + line =3D Re(r'^\s+', re.S).sub('', line) + + # Strip trailing spaces + line =3D Re(r'\s+$', re.S).sub('', line) + + # Strip C99-style comments to the end of the line + line =3D Re(r"\/\/.*$", re.S).sub('', line) + + # To distinguish preprocessor directive from regular declaration l= ater. + if line.startswith('#'): + line +=3D ";" + + r =3D Re(r'([^\{\};]*)([\{\};])(.*)') + while True: + if r.search(line): + if self.entry.prototype: + self.entry.prototype +=3D " " + self.entry.prototype +=3D r.group(1) + r.group(2) + + self.entry.brcount +=3D r.group(2).count('{') + self.entry.brcount -=3D r.group(2).count('}') + + self.entry.brcount =3D max(self.entry.brcount, 0) + + if r.group(2) =3D=3D ';' and self.entry.brcount =3D=3D 0: + self.dump_declaration(ln, self.entry.prototype) + self.reset_state(ln) + break + + line =3D r.group(3) + else: + self.entry.prototype +=3D line + break + + def process_proto(self, ln, line): + """STATE_PROTO: reading a function/whatever prototype.""" + + if doc_inline_oneline.search(line): + self.entry.section =3D doc_inline_oneline.group(1) + self.entry.contents =3D doc_inline_oneline.group(2) + + if self.entry.contents !=3D "": + self.entry.contents +=3D "\n" + self.dump_section(start_new=3DFalse) + + elif doc_inline_start.search(line): + self.state =3D self.STATE_INLINE + self.inline_doc_state =3D self.STATE_INLINE_NAME + + elif self.entry.decl_type =3D=3D 'function': + self.process_proto_function(ln, line) + + else: + self.process_proto_type(ln, line) + + def process_docblock(self, ln, line): + """STATE_DOCBLOCK: within a DOC: block.""" + + if doc_end.search(line): + self.dump_section() + self.output_declaration("doc", None, + sectionlist=3Dself.entry.sectionlist, + sections=3Dself.entry.sections, module= =3Dself.config.modulename) + self.reset_state(ln) + + elif doc_content.search(line): + self.entry.contents +=3D doc_content.group(1) + "\n" + + def run(self): + """ + Open and process each line of a C source file. + he parsing is controlled via a state machine, and the line is pass= ed + to a different process function depending on the state. The process + function may update the state as needed. + """ + + cont =3D False + prev =3D "" + prev_ln =3D None + + try: + with open(self.fname, "r", encoding=3D"utf8", + errors=3D"backslashreplace") as fp: + for ln, line in enumerate(fp): + + line =3D line.expandtabs().strip("\n") + + # Group continuation lines on prototypes + if self.state =3D=3D self.STATE_PROTO: + if line.endswith("\\"): + prev +=3D line.removesuffix("\\") + cont =3D True + + if not prev_ln: + prev_ln =3D ln + + continue + + if cont: + ln =3D prev_ln + line =3D prev + line + prev =3D "" + cont =3D False + prev_ln =3D None + + self.config.log.debug("%d %s%s: %s", + ln, self.st_name[self.state], + self.st_inline_name[self.inline_= doc_state], + line) + + # TODO: not all states allow EXPORT_SYMBOL*, so this + # can be optimized later on to speedup parsing + self.process_export(self.config.function_table, line) + + # Hand this line to the appropriate state handler + if self.state =3D=3D self.STATE_NORMAL: + self.process_normal(ln, line) + elif self.state =3D=3D self.STATE_NAME: + self.process_name(ln, line) + elif self.state in [self.STATE_BODY, self.STATE_BODY_M= AYBE, + self.STATE_BODY_WITH_BLANK_LINE]: + self.process_body(ln, line) + elif self.state =3D=3D self.STATE_INLINE: # scanning = for inline parameters + self.process_inline(ln, line) + elif self.state =3D=3D self.STATE_PROTO: + self.process_proto(ln, line) + elif self.state =3D=3D self.STATE_DOCBLOCK: + self.process_docblock(ln, line) + except OSError: + self.config.log.error(f"Error: Cannot open file {self.fname}") + self.config.errors +=3D 1 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B744224A055; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=gWyCn2BBud7mmlOY31iJNmmAmeN11lcVP4wDFVQ4sZeIyb2+LxR/1HzaelohwJePAw2U7Yh+p0kBzPRqUhPZhEPxfO2UOHrcqRNgIgxJSM1GJV+0naQ9z+zsEO9qcmoIZA3MP0PjnYzTAdKpodPObAu5nntNwdrD3pC5f7tNYlw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=2+Yr/2kIpnCitwTGtyjeQMnDH8wv+vGuRcW8RKqXkZ0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CjG8Gi/YXi6Og3G61lhbc512wjSr87feaEVN37Z+ym6fvpw5H0GFkzav4h+gHNz03cqPlEKTFxV2jge1mlcgsaDrbfj2XFEDPhVoUSmac30z+IIc8I0Ro0+4o6NRPxXAhoLUrV4n8OknZspYjiLzYRBSY9ZA9HCm18jcif9/gm8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mbIwrVM7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mbIwrVM7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id ECF46C4CEFD; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=2+Yr/2kIpnCitwTGtyjeQMnDH8wv+vGuRcW8RKqXkZ0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mbIwrVM7qQp2WnPBK9P5+sHvMw8VsROQjvWaZCDH7rNkBmICKmtPypmZ0kst5dICD +mN7TpSzgzHkbwvhsLISHV54NC64qgcbaGKKhUbM6Zll0MSZGDQS7hFSELq3nF8MKd Vc/j5MO+x6qu8q3oUWQtitBtBDl8Mij9A0/rO0h+HgvCtCryZMjxfeCkqAPar9dHxy U1Ez1XyDHQMzHgTSVYXpPXE2yCZZK/07gFNeisbJOxWEDJlkOU1SAmca953trtYDlF mBSLTu7gIDG+jCyAERTHWqPvYOKZpHP528ytJlK9jUDYaiLVsRKWfmzuADb++pKN2j UB3huG5jS2jCQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4O-0qYb; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 15/39] scripts/kernel-doc.py: move KernelFiles class to a separate file Date: Mon, 24 Feb 2025 10:08:21 +0100 Message-ID: <12a54f1b8f4afd2e70a87195a2aa34f96d736b77.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The KernelFiles class is the main dispatcher which parses each source file. In preparation for letting kerneldoc Sphinx extension to import Python libraries, move regex ancillary classes to a separate file. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 220 +-------------------------- scripts/lib/kdoc/kdoc_files.py | 269 +++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 219 deletions(-) create mode 100755 scripts/lib/kdoc/kdoc_files.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 193a30fcfb7c..cd79b2c1b746 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -44,6 +44,7 @@ sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) =20 from kdoc_parser import KernelDoc, type_param from kdoc_re import Re +from kdoc_files import KernelFiles =20 function_pointer =3D Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=3DFalse) =20 @@ -68,225 +69,6 @@ type_member =3D Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache= =3DFalse) type_fallback =3D Re(r"\&([_\w]+)", cache=3DFalse) type_member_func =3D type_member + Re(r"\(\)", cache=3DFalse) =20 -class GlobSourceFiles: - """ - Parse C source code file names and directories via an Interactor. - - """ - - def __init__(self, srctree=3DNone, valid_extensions=3DNone): - """ - Initialize valid extensions with a tuple. - - If not defined, assume default C extensions (.c and .h) - - It would be possible to use python's glob function, but it is - very slow, and it is not interactive. So, it would wait to read all - directories before actually do something. - - So, let's use our own implementation. - """ - - if not valid_extensions: - self.extensions =3D (".c", ".h") - else: - self.extensions =3D valid_extensions - - self.srctree =3D srctree - - def _parse_dir(self, dirname): - """Internal function to parse files recursively""" - - with os.scandir(dirname) as obj: - for entry in obj: - name =3D os.path.join(dirname, entry.name) - - if entry.is_dir(): - yield from self._parse_dir(name) - - if not entry.is_file(): - continue - - basename =3D os.path.basename(name) - - if not basename.endswith(self.extensions): - continue - - yield name - - def parse_files(self, file_list, file_not_found_cb): - for fname in file_list: - if self.srctree: - f =3D os.path.join(self.srctree, fname) - else: - f =3D fname - - if os.path.isdir(f): - yield from self._parse_dir(f) - elif os.path.isfile(f): - yield f - elif file_not_found_cb: - file_not_found_cb(fname) - - -class KernelFiles(): - - def parse_file(self, fname): - - doc =3D KernelDoc(self.config, fname) - doc.run() - - return doc - - def process_export_file(self, fname): - try: - with open(fname, "r", encoding=3D"utf8", - errors=3D"backslashreplace") as fp: - for line in fp: - KernelDoc.process_export(self.config.function_table, l= ine) - - except IOError: - print(f"Error: Cannot open fname {fname}", fname=3Dsys.stderr) - self.config.errors +=3D 1 - - def file_not_found_cb(self, fname): - self.config.log.error("Cannot find file %s", fname) - self.config.errors +=3D 1 - - def __init__(self, files=3DNone, verbose=3DFalse, out_style=3DNone, - werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, - wcontents_before_sections=3DFalse, - logger=3DNone, modulename=3DNone, export_file=3DNone): - """Initialize startup variables and parse all files""" - - - if not verbose: - verbose =3D bool(os.environ.get("KBUILD_VERBOSE", 0)) - - if not modulename: - modulename =3D "Kernel API" - - dt =3D datetime.now() - if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): - # use UTC TZ - to_zone =3D tz.gettz('UTC') - dt =3D dt.astimezone(to_zone) - - if not werror: - kcflags =3D os.environ.get("KCFLAGS", None) - if kcflags: - match =3D re.search(r"(\s|^)-Werror(\s|$)/", kcflags) - if match: - werror =3D True - - # reading this variable is for backwards compat just in case - # someone was calling it with the variable from outside the - # kernel's build system - kdoc_werror =3D os.environ.get("KDOC_WERROR", None) - if kdoc_werror: - werror =3D kdoc_werror - - # Set global config data used on all files - self.config =3D argparse.Namespace - - self.config.verbose =3D verbose - self.config.werror =3D werror - self.config.wreturn =3D wreturn - self.config.wshort_desc =3D wshort_desc - self.config.wcontents_before_sections =3D wcontents_before_sections - self.config.modulename =3D modulename - - self.config.function_table =3D set() - self.config.source_map =3D {} - - if not logger: - self.config.log =3D logging.getLogger("kernel-doc") - else: - self.config.log =3D logger - - self.config.kernel_version =3D os.environ.get("KERNELVERSION", - "unknown kernel versio= n'") - self.config.src_tree =3D os.environ.get("SRCTREE", None) - - self.out_style =3D out_style - self.export_file =3D export_file - - # Initialize internal variables - - self.config.errors =3D 0 - self.results =3D [] - - self.file_list =3D files - self.files =3D set() - - def parse(self): - """ - Parse all files - """ - - glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) - - # Let's use a set here to avoid duplicating files - - for fname in glob.parse_files(self.file_list, self.file_not_found_= cb): - if fname in self.files: - continue - - self.files.add(fname) - - res =3D self.parse_file(fname) - self.results.append((res.fname, res.entries)) - - if not self.files: - sys.exit(1) - - # If a list of export files was provided, parse EXPORT_SYMBOL* - # from the ones not already parsed - - if self.export_file: - files =3D self.files - - glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) - - for fname in glob.parse_files(self.export_file, - self.file_not_found_cb): - if fname not in files: - files.add(fname) - - self.process_export_file(fname) - - def out_msg(self, fname, name, arg): - # TODO: filter out unwanted parts - - return self.out_style.msg(fname, name, arg) - - def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, - symbol=3DNone, nosymbol=3DNone): - - function_table =3D self.config.function_table - - if symbol: - for s in symbol: - function_table.add(s) - - # Output none mode: only warnings will be shown - if not self.out_style: - return - - self.out_style.set_config(self.config) - - self.out_style.set_filter(export, internal, symbol, nosymbol, - function_table, enable_lineno) - - for fname, arg_tuple in self.results: - for name, arg in arg_tuple: - if self.out_msg(fname, name, arg): - ln =3D arg.get("ln", 0) - dtype =3D arg.get('type', "") - - self.config.log.warning("%s:%d Can't handle %s", - fname, ln, dtype) - =20 class OutputFormat: # output mode. diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py new file mode 100755 index 000000000000..76dd53611c08 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_files.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 +# pylint: disable=3DR0903,R0913,R0914,R0917 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + +# TODO: implement warning filtering + +""" +Parse lernel-doc tags on multiple kernel source files. +""" + +import argparse +import logging +import os +import re +import sys +from datetime import datetime + +from dateutil import tz + +from kdoc_parser import KernelDoc + + +class GlobSourceFiles: + """ + Parse C source code file names and directories via an Interactor. + """ + + def __init__(self, srctree=3DNone, valid_extensions=3DNone): + """ + Initialize valid extensions with a tuple. + + If not defined, assume default C extensions (.c and .h) + + It would be possible to use python's glob function, but it is + very slow, and it is not interactive. So, it would wait to read all + directories before actually do something. + + So, let's use our own implementation. + """ + + if not valid_extensions: + self.extensions =3D (".c", ".h") + else: + self.extensions =3D valid_extensions + + self.srctree =3D srctree + + def _parse_dir(self, dirname): + """Internal function to parse files recursively""" + + with os.scandir(dirname) as obj: + for entry in obj: + name =3D os.path.join(dirname, entry.name) + + if entry.is_dir(): + yield from self._parse_dir(name) + + if not entry.is_file(): + continue + + basename =3D os.path.basename(name) + + if not basename.endswith(self.extensions): + continue + + yield name + + def parse_files(self, file_list, file_not_found_cb): + """ + Define an interator to parse all source files from file_list, + handling directories if any + """ + + for fname in file_list: + if self.srctree: + f =3D os.path.join(self.srctree, fname) + else: + f =3D fname + + if os.path.isdir(f): + yield from self._parse_dir(f) + elif os.path.isfile(f): + yield f + elif file_not_found_cb: + file_not_found_cb(fname) + + +class KernelFiles(): + """ + Parse lernel-doc tags on multiple kernel source files. + """ + + def parse_file(self, fname): + """ + Parse a single Kernel source. + """ + + doc =3D KernelDoc(self.config, fname) + doc.run() + + return doc + + def process_export_file(self, fname): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + try: + with open(fname, "r", encoding=3D"utf8", + errors=3D"backslashreplace") as fp: + for line in fp: + KernelDoc.process_export(self.config.function_table, l= ine) + + except IOError: + print(f"Error: Cannot open fname {fname}", fname=3Dsys.stderr) + self.config.errors +=3D 1 + + def file_not_found_cb(self, fname): + """ + Callback to warn if a file was not found. + """ + + self.config.log.error("Cannot find file %s", fname) + self.config.errors +=3D 1 + + def __init__(self, files=3DNone, verbose=3DFalse, out_style=3DNone, + werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, + wcontents_before_sections=3DFalse, + logger=3DNone, modulename=3DNone, export_file=3DNone): + """ + Initialize startup variables and parse all files + """ + + if not verbose: + verbose =3D bool(os.environ.get("KBUILD_VERBOSE", 0)) + + if not modulename: + modulename =3D "Kernel API" + + dt =3D datetime.now() + if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): + # use UTC TZ + to_zone =3D tz.gettz('UTC') + dt =3D dt.astimezone(to_zone) + + if not werror: + kcflags =3D os.environ.get("KCFLAGS", None) + if kcflags: + match =3D re.search(r"(\s|^)-Werror(\s|$)/", kcflags) + if match: + werror =3D True + + # reading this variable is for backwards compat just in case + # someone was calling it with the variable from outside the + # kernel's build system + kdoc_werror =3D os.environ.get("KDOC_WERROR", None) + if kdoc_werror: + werror =3D kdoc_werror + + # Set global config data used on all files + self.config =3D argparse.Namespace + + self.config.verbose =3D verbose + self.config.werror =3D werror + self.config.wreturn =3D wreturn + self.config.wshort_desc =3D wshort_desc + self.config.wcontents_before_sections =3D wcontents_before_sections + self.config.modulename =3D modulename + + self.config.function_table =3D set() + self.config.source_map =3D {} + + if not logger: + self.config.log =3D logging.getLogger("kernel-doc") + else: + self.config.log =3D logger + + self.config.kernel_version =3D os.environ.get("KERNELVERSION", + "unknown kernel versio= n'") + self.config.src_tree =3D os.environ.get("SRCTREE", None) + + self.out_style =3D out_style + self.export_file =3D export_file + + # Initialize internal variables + + self.config.errors =3D 0 + self.results =3D [] + + self.file_list =3D files + self.files =3D set() + + def parse(self): + """ + Parse all files + """ + + glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) + + # Let's use a set here to avoid duplicating files + + for fname in glob.parse_files(self.file_list, self.file_not_found_= cb): + if fname in self.files: + continue + + self.files.add(fname) + + res =3D self.parse_file(fname) + self.results.append((res.fname, res.entries)) + + if not self.files: + sys.exit(1) + + # If a list of export files was provided, parse EXPORT_SYMBOL* + # from the ones not already parsed + + if self.export_file: + files =3D self.files + + glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) + + for fname in glob.parse_files(self.export_file, + self.file_not_found_cb): + if fname not in files: + files.add(fname) + + self.process_export_file(fname) + + def out_msg(self, fname, name, arg): + """ + Output messages from a file name using the output style filtering. + + If output type was not handled by the syler, return False. + """ + + # NOTE: we can add rules here to filter out unwanted parts, + # although OutputFormat.msg already does that. + + return self.out_style.msg(fname, name, arg) + + def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, + symbol=3DNone, nosymbol=3DNone): + """ + Interacts over the kernel-doc results and output messages. + """ + + function_table =3D self.config.function_table + + if symbol: + for s in symbol: + function_table.add(s) + + # Output none mode: only warnings will be shown + if not self.out_style: + return + + self.out_style.set_config(self.config) + + self.out_style.set_filter(export, internal, symbol, nosymbol, + function_table, enable_lineno) + + for fname, arg_tuple in self.results: + for name, arg in arg_tuple: + if self.out_msg(fname, name, arg): + ln =3D arg.get("ln", 0) + dtype =3D arg.get('type', "") + + self.config.log.warning("%s:%d Can't handle %s", + fname, ln, dtype) --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7E9324A073; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=GnYtE4FCZp9x0vmLDN84CSDAMCP3Tkj59teZs9EhsWSiwWioaVETkQypV1+ZbW3VzTWusB15nXLQU1QJt+vEf+u5Ml910B6C09VpcJV9ol1fdJXGVlB5eAPR5PWTf4gquuV/3Y+Ev8115gHLz95bpICkyI4Dc3IBlqMdBn7oFEA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=hPv6z9fZFY9mkuOaJfh9tzYOURGM+7iDxiBMcb2e6xg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WnxwMOWGWj6Xs8Km0ZuDkdTdWTtqwmwpMUYAqT1CsrRS0nj2qiWEo0Al3U5LL+RCKIkCOf1spockXlexdGlKpSYNkgkqfjxfLR8EAPDsjil+/e4TemLG8mBzxdHSz4Dvxj4C5qwNXZjyREJ3nkpk6ONdVvjz9ReNV2l4H21qxSE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=V9LeS8+Q; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="V9LeS8+Q" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E55A8C4CEF7; Mon, 24 Feb 2025 09:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=hPv6z9fZFY9mkuOaJfh9tzYOURGM+7iDxiBMcb2e6xg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V9LeS8+QQ5GQw5KSCYiGQYiyP1w1oTDde5VkL1lA4kEDQaSG/DfjvGGj5Trt+zEil hsVaPKybv2xFtjcsmzRdWf3E5rAXacJbv+xxbZcWloxnJqRdQYhSeFN73d8RSksRV2 nKQttj6qrlehqux0UnPwuYeneqSMdJ3qFXPvCZtMB9w/HPpK+o9LRl77YoM1ZGy2qp 3vCe/1xPcoPEn6+HL3wG+hl5NSrBS2b4oYfyg0kpJ2FrbGLLFF6bLTnoKVoh2kc7uq EBtenGKtQrs1DJJkEj6PjgKGR2CfZx8FSV+Zj4QBYMJZFBUF6c2C31xhviJ1X3ZJhe g3BqpMk3lMn3Q== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4S-0y8A; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 16/39] scripts/kernel-doc.py: move output classes to a separate file Date: Mon, 24 Feb 2025 10:08:22 +0100 Message-ID: <5e798b99d7ca667ce279901a4bb6b3c8d3aa1fdc.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" In preparation for letting kerneldoc Sphinx extension to import Python libraries, move kernel-doc output logic to a separate file. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 727 +------------------------------ scripts/lib/kdoc/kdoc_output.py | 735 ++++++++++++++++++++++++++++++++ 2 files changed, 738 insertions(+), 724 deletions(-) create mode 100755 scripts/lib/kdoc/kdoc_output.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index cd79b2c1b746..0596c711d448 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -# pylint: disable=3DR0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,= R1702 -# pylint: disable=3DC0302,C0103,C0301 -# pylint: disable=3DC0116,C0115,W0511,W0613 +# pylint: disable=3DC0103, # Copyright(c) 2025: Mauro Carvalho Chehab . # SPDX-License-Identifier: GPL-2.0 =20 @@ -27,14 +25,8 @@ documentation comment syntax. import argparse import logging import os -import re import sys =20 -from datetime import datetime -from pprint import pformat - -from dateutil import tz - # Import Python modules =20 LIB_DIR =3D "lib/kdoc" @@ -42,721 +34,8 @@ SRC_DIR =3D os.path.dirname(os.path.realpath(__file__)) =20 sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) =20 -from kdoc_parser import KernelDoc, type_param -from kdoc_re import Re -from kdoc_files import KernelFiles - -function_pointer =3D Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=3DFalse) - -# match expressions used to find embedded type information -type_constant =3D Re(r"\b``([^\`]+)``\b", cache=3DFalse) -type_constant2 =3D Re(r"\%([-_*\w]+)", cache=3DFalse) -type_func =3D Re(r"(\w+)\(\)", cache=3DFalse) -type_param_ref =3D Re(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cac= he=3DFalse) - -# Special RST handling for func ptr params -type_fp_param =3D Re(r"\@(\w+)\(\)", cache=3DFalse) - -# Special RST handling for structs with func ptr params -type_fp_param2 =3D Re(r"\@(\w+->\S+)\(\)", cache=3DFalse) - -type_env =3D Re(r"(\$\w+)", cache=3DFalse) -type_enum =3D Re(r"\&(enum\s*([_\w]+))", cache=3DFalse) -type_struct =3D Re(r"\&(struct\s*([_\w]+))", cache=3DFalse) -type_typedef =3D Re(r"\&(typedef\s*([_\w]+))", cache=3DFalse) -type_union =3D Re(r"\&(union\s*([_\w]+))", cache=3DFalse) -type_member =3D Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache=3DFalse) -type_fallback =3D Re(r"\&([_\w]+)", cache=3DFalse) -type_member_func =3D type_member + Re(r"\(\)", cache=3DFalse) - - -class OutputFormat: - # output mode. - OUTPUT_ALL =3D 0 # output all symbols and doc sections - OUTPUT_INCLUDE =3D 1 # output only specified symbols - OUTPUT_EXPORTED =3D 2 # output exported symbols - OUTPUT_INTERNAL =3D 3 # output non-exported symbols - - # Virtual member to be overriden at the inherited classes - highlights =3D [] - - def __init__(self): - """Declare internal vars and set mode to OUTPUT_ALL""" - - self.out_mode =3D self.OUTPUT_ALL - self.enable_lineno =3D None - self.nosymbol =3D {} - self.symbol =3D None - self.function_table =3D set() - self.config =3D None - - def set_config(self, config): - self.config =3D config - - def set_filter(self, export, internal, symbol, nosymbol, function_tabl= e, - enable_lineno): - """ - Initialize filter variables according with the requested mode. - - Only one choice is valid between export, internal and symbol. - - The nosymbol filter can be used on all modes. - """ - - self.enable_lineno =3D enable_lineno - - if symbol: - self.out_mode =3D self.OUTPUT_INCLUDE - function_table =3D symbol - elif export: - self.out_mode =3D self.OUTPUT_EXPORTED - elif internal: - self.out_mode =3D self.OUTPUT_INTERNAL - else: - self.out_mode =3D self.OUTPUT_ALL - - if nosymbol: - self.nosymbol =3D set(nosymbol) - - if function_table: - self.function_table =3D function_table - - def highlight_block(self, block): - """ - Apply the RST highlights to a sub-block of text. - """ - - for r, sub in self.highlights: - block =3D r.sub(sub, block) - - return block - - def check_doc(self, name): - """Check if DOC should be output""" - - if self.out_mode =3D=3D self.OUTPUT_ALL: - return True - - if self.out_mode =3D=3D self.OUTPUT_INCLUDE: - if name in self.nosymbol: - return False - - if name in self.function_table: - return True - - return False - - def check_declaration(self, dtype, name): - if name in self.nosymbol: - return False - - if self.out_mode =3D=3D self.OUTPUT_ALL: - return True - - if self.out_mode in [ self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED ]: - if name in self.function_table: - return True - - if self.out_mode =3D=3D self.OUTPUT_INTERNAL: - if dtype !=3D "function": - return True - - if name not in self.function_table: - return True - - return False - - def check_function(self, fname, name, args): - return True - - def check_enum(self, fname, name, args): - return True - - def check_typedef(self, fname, name, args): - return True - - def msg(self, fname, name, args): - - dtype =3D args.get('type', "") - - if dtype =3D=3D "doc": - self.out_doc(fname, name, args) - return False - - if not self.check_declaration(dtype, name): - return False - - if dtype =3D=3D "function": - self.out_function(fname, name, args) - return False - - if dtype =3D=3D "enum": - self.out_enum(fname, name, args) - return False - - if dtype =3D=3D "typedef": - self.out_typedef(fname, name, args) - return False - - if dtype in ["struct", "union"]: - self.out_struct(fname, name, args) - return False - - # Warn if some type requires an output logic - self.config.log.warning("doesn't now how to output '%s' block", - dtype) - - return True - - # Virtual methods to be overridden by inherited classes - def out_doc(self, fname, name, args): - pass - - def out_function(self, fname, name, args): - pass - - def out_enum(self, fname, name, args): - pass - - def out_typedef(self, fname, name, args): - pass - - def out_struct(self, fname, name, args): - pass - - -class RestFormat(OutputFormat): - # """Consts and functions used by ReST output""" - - highlights =3D [ - (type_constant, r"``\1``"), - (type_constant2, r"``\1``"), - - # Note: need to escape () to avoid func matching later - (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), - (type_member, r":c:type:`\1\2\3 <\1>`"), - (type_fp_param, r"**\1\\(\\)**"), - (type_fp_param2, r"**\1\\(\\)**"), - (type_func, r"\1()"), - (type_enum, r":c:type:`\1 <\2>`"), - (type_struct, r":c:type:`\1 <\2>`"), - (type_typedef, r":c:type:`\1 <\2>`"), - (type_union, r":c:type:`\1 <\2>`"), - - # in rst this can refer to any type - (type_fallback, r":c:type:`\1`"), - (type_param_ref, r"**\1\2**") - ] - blankline =3D "\n" - - sphinx_literal =3D Re(r'^[^.].*::$', cache=3DFalse) - sphinx_cblock =3D Re(r'^\.\.\ +code-block::', cache=3DFalse) - - def __init__(self): - """ - Creates class variables. - - Not really mandatory, but it is a good coding style and makes - pylint happy. - """ - - super().__init__() - self.lineprefix =3D "" - - def print_lineno (self, ln): - """Outputs a line number""" - - if self.enable_lineno and ln: - print(f".. LINENO {ln}") - - def output_highlight(self, args): - input_text =3D args - output =3D "" - in_literal =3D False - litprefix =3D "" - block =3D "" - - for line in input_text.strip("\n").split("\n"): - - # If we're in a literal block, see if we should drop out of it. - # Otherwise, pass the line straight through unmunged. - if in_literal: - if line.strip(): # If the line is not blank - # If this is the first non-blank line in a literal blo= ck, - # figure out the proper indent. - if not litprefix: - r =3D Re(r'^(\s*)') - if r.match(line): - litprefix =3D '^' + r.group(1) - else: - litprefix =3D "" - - output +=3D line + "\n" - elif not Re(litprefix).match(line): - in_literal =3D False - else: - output +=3D line + "\n" - else: - output +=3D line + "\n" - - # Not in a literal block (or just dropped out) - if not in_literal: - block +=3D line + "\n" - if self.sphinx_literal.match(line) or self.sphinx_cblock.m= atch(line): - in_literal =3D True - litprefix =3D "" - output +=3D self.highlight_block(block) - block =3D "" - - # Handle any remaining block - if block: - output +=3D self.highlight_block(block) - - # Print the output with the line prefix - for line in output.strip("\n").split("\n"): - print(self.lineprefix + line) - - def out_section(self, args, out_reference=3DFalse): - """ - Outputs a block section. - - This could use some work; it's used to output the DOC: sections, a= nd - starts by putting out the name of the doc section itself, but that - tends to duplicate a header already in the template file. - """ - - sectionlist =3D args.get('sectionlist', []) - sections =3D args.get('sections', {}) - section_start_lines =3D args.get('section_start_lines', {}) - - for section in sectionlist: - # Skip sections that are in the nosymbol_table - if section in self.nosymbol: - continue - - if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: - if out_reference: - print(f".. _{section}:\n") - - if not self.symbol: - print(f'{self.lineprefix}**{section}**\n') - - self.print_lineno(section_start_lines.get(section, 0)) - self.output_highlight(sections[section]) - print() - print() - - def out_doc(self, fname, name, args): - if not self.check_doc(name): - return - - self.out_section(args, out_reference=3DTrue) - - def out_function(self, fname, name, args): - - oldprefix =3D self.lineprefix - signature =3D "" - - func_macro =3D args.get('func_macro', False) - if func_macro: - signature =3D args['function'] - else: - if args.get('functiontype'): - signature =3D args['functiontype'] + " " - signature +=3D args['function'] + " (" - - parameterlist =3D args.get('parameterlist', []) - parameterdescs =3D args.get('parameterdescs', {}) - parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) - - ln =3D args.get('ln', 0) - - count =3D 0 - for parameter in parameterlist: - if count !=3D 0: - signature +=3D ", " - count +=3D 1 - dtype =3D args['parametertypes'].get(parameter, "") - - if function_pointer.search(dtype): - signature +=3D function_pointer.group(1) + parameter + fun= ction_pointer.group(3) - else: - signature +=3D dtype - - if not func_macro: - signature +=3D ")" - - if args.get('typedef') or not args.get('functiontype'): - print(f".. c:macro:: {args['function']}\n") - - if args.get('typedef'): - self.print_lineno(ln) - print(" **Typedef**: ", end=3D"") - self.lineprefix =3D "" - self.output_highlight(args.get('purpose', "")) - print("\n\n**Syntax**\n") - print(f" ``{signature}``\n") - else: - print(f"``{signature}``\n") - else: - print(f".. c:function:: {signature}\n") - - if not args.get('typedef'): - self.print_lineno(ln) - self.lineprefix =3D " " - self.output_highlight(args.get('purpose', "")) - print() - - # Put descriptive text into a container (HTML
) to help set - # function prototypes apart - self.lineprefix =3D " " - - if parameterlist: - print(".. container:: kernelindent\n") - print(f"{self.lineprefix}**Parameters**\n") - - for parameter in parameterlist: - parameter_name =3D Re(r'\[.*').sub('', parameter) - dtype =3D args['parametertypes'].get(parameter, "") - - if dtype: - print(f"{self.lineprefix}``{dtype}``") - else: - print(f"{self.lineprefix}``{parameter}``") - - self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) - - self.lineprefix =3D " " - if parameter_name in parameterdescs and \ - parameterdescs[parameter_name] !=3D KernelDoc.undescribed: - - self.output_highlight(parameterdescs[parameter_name]) - print() - else: - print(f"{self.lineprefix}*undescribed*\n") - self.lineprefix =3D " " - - self.out_section(args) - self.lineprefix =3D oldprefix - - def out_enum(self, fname, name, args): - - oldprefix =3D self.lineprefix - name =3D args.get('enum', '') - parameterlist =3D args.get('parameterlist', []) - parameterdescs =3D args.get('parameterdescs', {}) - ln =3D args.get('ln', 0) - - print(f"\n\n.. c:enum:: {name}\n") - - self.print_lineno(ln) - self.lineprefix =3D " " - self.output_highlight(args.get('purpose', '')) - print() - - print(".. container:: kernelindent\n") - outer =3D self.lineprefix + " " - self.lineprefix =3D outer + " " - print(f"{outer}**Constants**\n") - - for parameter in parameterlist: - print(f"{outer}``{parameter}``") - - if parameterdescs.get(parameter, '') !=3D KernelDoc.undescribe= d: - self.output_highlight(parameterdescs[parameter]) - else: - print(f"{self.lineprefix}*undescribed*\n") - print() - - self.lineprefix =3D oldprefix - self.out_section(args) - - def out_typedef(self, fname, name, args): - - oldprefix =3D self.lineprefix - name =3D args.get('typedef', '') - ln =3D args.get('ln', 0) - - print(f"\n\n.. c:type:: {name}\n") - - self.print_lineno(ln) - self.lineprefix =3D " " - - self.output_highlight(args.get('purpose', '')) - - print() - - self.lineprefix =3D oldprefix - self.out_section(args) - - def out_struct(self, fname, name, args): - - name =3D args.get('struct', "") - purpose =3D args.get('purpose', "") - declaration =3D args.get('definition', "") - dtype =3D args.get('type', "struct") - ln =3D args.get('ln', 0) - - parameterlist =3D args.get('parameterlist', []) - parameterdescs =3D args.get('parameterdescs', {}) - parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) - - print(f"\n\n.. c:{dtype}:: {name}\n") - - self.print_lineno(ln) - - oldprefix =3D self.lineprefix - self.lineprefix +=3D " " - - self.output_highlight(purpose) - print() - - print(".. container:: kernelindent\n") - print(f"{self.lineprefix}**Definition**::\n") - - self.lineprefix =3D self.lineprefix + " " - - declaration =3D declaration.replace("\t", self.lineprefix) - - print(f"{self.lineprefix}{dtype} {name}" + ' {') - print(f"{declaration}{self.lineprefix}" + "};\n") - - self.lineprefix =3D " " - print(f"{self.lineprefix}**Members**\n") - for parameter in parameterlist: - if not parameter or parameter.startswith("#"): - continue - - parameter_name =3D parameter.split("[", maxsplit=3D1)[0] - - if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: - continue - - self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) - - print(f"{self.lineprefix}``{parameter}``") - - self.lineprefix =3D " " - self.output_highlight(parameterdescs[parameter_name]) - self.lineprefix =3D " " - - print() - - print() - - self.lineprefix =3D oldprefix - self.out_section(args) - - -class ManFormat(OutputFormat): - """Consts and functions used by man pages output""" - - highlights =3D ( - (type_constant, r"\1"), - (type_constant2, r"\1"), - (type_func, r"\\fB\1\\fP"), - (type_enum, r"\\fI\1\\fP"), - (type_struct, r"\\fI\1\\fP"), - (type_typedef, r"\\fI\1\\fP"), - (type_union, r"\\fI\1\\fP"), - (type_param, r"\\fI\1\\fP"), - (type_param_ref, r"\\fI\1\2\\fP"), - (type_member, r"\\fI\1\2\3\\fP"), - (type_fallback, r"\\fI\1\\fP") - ) - blankline =3D "" - - def __init__(self): - """ - Creates class variables. - - Not really mandatory, but it is a good coding style and makes - pylint happy. - """ - - super().__init__() - - dt =3D datetime.now() - if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): - # use UTC TZ - to_zone =3D tz.gettz('UTC') - dt =3D dt.astimezone(to_zone) - - self.man_date =3D dt.strftime("%B %Y") - - def output_highlight(self, block): - - contents =3D self.highlight_block(block) - - if isinstance(contents, list): - contents =3D "\n".join(contents) - - for line in contents.strip("\n").split("\n"): - line =3D Re(r"^\s*").sub("", line) - - if line and line[0] =3D=3D ".": - print("\\&" + line) - else: - print(line) - - def out_doc(self, fname, name, args): - module =3D args.get('module') - sectionlist =3D args.get('sectionlist', []) - sections =3D args.get('sections', {}) - - print(f'.TH "{module}" 9 "{module}" "{self.man_date}" "API Manual"= LINUX') - - for section in sectionlist: - print(f'.SH "{section}"') - self.output_highlight(sections.get(section)) - - def out_function(self, fname, name, args): - """output function in man""" - - parameterlist =3D args.get('parameterlist', []) - parameterdescs =3D args.get('parameterdescs', {}) - sectionlist =3D args.get('sectionlist', []) - sections =3D args.get('sections', {}) - - print(f'.TH "{args['function']}" 9 "{args['function']}" "{self.man= _date}" "Kernel Hacker\'s Manual" LINUX') - - print(".SH NAME") - print(f"{args['function']} \\- {args['purpose']}") - - print(".SH SYNOPSIS") - if args.get('functiontype', ''): - print(f'.B "{args['functiontype']}" {args['function']}') - else: - print(f'.B "{args['function']}') - - count =3D 0 - parenth =3D "(" - post =3D "," - - for parameter in parameterlist: - if count =3D=3D len(parameterlist) - 1: - post =3D ");" - - dtype =3D args['parametertypes'].get(parameter, "") - if function_pointer.match(dtype): - # Pointer-to-function - print(f'".BI "{parenth}{function_pointer.group(1)}" " ") (= {function_pointer.group(2)}){post}"') - else: - dtype =3D Re(r'([^\*])$').sub(r'\1 ', dtype) - - print(f'.BI "{parenth}{dtype}" "{post}"') - count +=3D 1 - parenth =3D "" - - if parameterlist: - print(".SH ARGUMENTS") - - for parameter in parameterlist: - parameter_name =3D re.sub(r'\[.*', '', parameter) - - print(f'.IP "{parameter}" 12') - self.output_highlight(parameterdescs.get(parameter_name, "")) - - for section in sectionlist: - print(f'.SH "{section.upper()}"') - self.output_highlight(sections[section]) - - def out_enum(self, fname, name, args): - - name =3D args.get('enum', '') - parameterlist =3D args.get('parameterlist', []) - sectionlist =3D args.get('sectionlist', []) - sections =3D args.get('sections', {}) - - print(f'.TH "{args['module']}" 9 "enum {args['enum']}" "{self.man_= date}" "API Manual" LINUX') - - print(".SH NAME") - print(f"enum {args['enum']} \\- {args['purpose']}") - - print(".SH SYNOPSIS") - print(f"enum {args['enum']}" + " {") - - count =3D 0 - for parameter in parameterlist: - print(f'.br\n.BI " {parameter}"') - if count =3D=3D len(parameterlist) - 1: - print("\n};") - else: - print(", \n.br") - - count +=3D 1 - - print(".SH Constants") - - for parameter in parameterlist: - parameter_name =3D Re(r'\[.*').sub('', parameter) - print(f'.IP "{parameter}" 12') - self.output_highlight(args['parameterdescs'].get(parameter_nam= e, "")) - - for section in sectionlist: - print(f'.SH "{section}"') - self.output_highlight(sections[section]) - - def out_typedef(self, fname, name, args): - module =3D args.get('module') - typedef =3D args.get('typedef') - purpose =3D args.get('purpose') - sectionlist =3D args.get('sectionlist', []) - sections =3D args.get('sections', {}) - - print(f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual= " LINUX') - - print(".SH NAME") - print(f"typedef {typedef} \\- {purpose}") - - for section in sectionlist: - print(f'.SH "{section}"') - self.output_highlight(sections.get(section)) - - def out_struct(self, fname, name, args): - module =3D args.get('module') - struct_type =3D args.get('type') - struct_name =3D args.get('struct') - purpose =3D args.get('purpose') - definition =3D args.get('definition') - sectionlist =3D args.get('sectionlist', []) - parameterlist =3D args.get('parameterlist', []) - sections =3D args.get('sections', {}) - parameterdescs =3D args.get('parameterdescs', {}) - - print(f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_= date}" "API Manual" LINUX') - - print(".SH NAME") - print(f"{struct_type} {struct_name} \\- {purpose}") - - # Replace tabs with two spaces and handle newlines - declaration =3D definition.replace("\t", " ") - declaration =3D Re(r"\n").sub('"\n.br\n.BI "', declaration) - - print(".SH SYNOPSIS") - print(f"{struct_type} {struct_name} " + "{" +"\n.br") - print(f'.BI "{declaration}\n' + "};\n.br\n") - - print(".SH Members") - for parameter in parameterlist: - if parameter.startswith("#"): - continue - - parameter_name =3D re.sub(r"\[.*", "", parameter) - - if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: - continue - - print(f'.IP "{parameter}" 12') - self.output_highlight(parameterdescs.get(parameter_name)) - - for section in sectionlist: - print(f'.SH "{section}"') - self.output_highlight(sections.get(section)) - - -# Command line interface - +from kdoc_files import KernelFiles # pylint: disable= =3DC0413 +from kdoc_output import RestFormat, ManFormat # pylint: disable= =3DC0413 =20 DESC =3D """ Read C language source or header FILEs, extract embedded documentation com= ments, diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py new file mode 100755 index 000000000000..d080440caa1c --- /dev/null +++ b/scripts/lib/kdoc/kdoc_output.py @@ -0,0 +1,735 @@ +#!/usr/bin/env python3 +# pylint: disable=3DC0301,R0911,R0912,R0913,R0914,R0915,R0917 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + +# TODO: implement warning filtering + +""" +Implement output filters to print kernel-doc documentation. + +The implementation uses a virtual base class (OutputFormat) which +contains a dispatches to virtual methods, and some code to filter +out output messages. + +The actual implementation is done on one separate class per each type +of output. Currently, there are output classes for ReST and man/troff. +""" + +import os +import re +from datetime import datetime + +from dateutil import tz + +from kdoc_parser import KernelDoc, type_param +from kdoc_re import Re + + +function_pointer =3D Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=3DFalse) + +# match expressions used to find embedded type information +type_constant =3D Re(r"\b``([^\`]+)``\b", cache=3DFalse) +type_constant2 =3D Re(r"\%([-_*\w]+)", cache=3DFalse) +type_func =3D Re(r"(\w+)\(\)", cache=3DFalse) +type_param_ref =3D Re(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cac= he=3DFalse) + +# Special RST handling for func ptr params +type_fp_param =3D Re(r"\@(\w+)\(\)", cache=3DFalse) + +# Special RST handling for structs with func ptr params +type_fp_param2 =3D Re(r"\@(\w+->\S+)\(\)", cache=3DFalse) + +type_env =3D Re(r"(\$\w+)", cache=3DFalse) +type_enum =3D Re(r"\&(enum\s*([_\w]+))", cache=3DFalse) +type_struct =3D Re(r"\&(struct\s*([_\w]+))", cache=3DFalse) +type_typedef =3D Re(r"\&(typedef\s*([_\w]+))", cache=3DFalse) +type_union =3D Re(r"\&(union\s*([_\w]+))", cache=3DFalse) +type_member =3D Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache=3DFalse) +type_fallback =3D Re(r"\&([_\w]+)", cache=3DFalse) +type_member_func =3D type_member + Re(r"\(\)", cache=3DFalse) + + +class OutputFormat: + # output mode. + OUTPUT_ALL =3D 0 # output all symbols and doc sections + OUTPUT_INCLUDE =3D 1 # output only specified symbols + OUTPUT_EXPORTED =3D 2 # output exported symbols + OUTPUT_INTERNAL =3D 3 # output non-exported symbols + + # Virtual member to be overriden at the inherited classes + highlights =3D [] + + def __init__(self): + """Declare internal vars and set mode to OUTPUT_ALL""" + + self.out_mode =3D self.OUTPUT_ALL + self.enable_lineno =3D None + self.nosymbol =3D {} + self.symbol =3D None + self.function_table =3D set() + self.config =3D None + + def set_config(self, config): + self.config =3D config + + def set_filter(self, export, internal, symbol, nosymbol, function_tabl= e, + enable_lineno): + """ + Initialize filter variables according with the requested mode. + + Only one choice is valid between export, internal and symbol. + + The nosymbol filter can be used on all modes. + """ + + self.enable_lineno =3D enable_lineno + + if symbol: + self.out_mode =3D self.OUTPUT_INCLUDE + function_table =3D symbol + elif export: + self.out_mode =3D self.OUTPUT_EXPORTED + elif internal: + self.out_mode =3D self.OUTPUT_INTERNAL + else: + self.out_mode =3D self.OUTPUT_ALL + + if nosymbol: + self.nosymbol =3D set(nosymbol) + + if function_table: + self.function_table =3D function_table + + def highlight_block(self, block): + """ + Apply the RST highlights to a sub-block of text. + """ + + for r, sub in self.highlights: + block =3D r.sub(sub, block) + + return block + + def check_doc(self, name): + """Check if DOC should be output""" + + if self.out_mode =3D=3D self.OUTPUT_ALL: + return True + + if self.out_mode =3D=3D self.OUTPUT_INCLUDE: + if name in self.nosymbol: + return False + + if name in self.function_table: + return True + + return False + + def check_declaration(self, dtype, name): + if name in self.nosymbol: + return False + + if self.out_mode =3D=3D self.OUTPUT_ALL: + return True + + if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]: + if name in self.function_table: + return True + + if self.out_mode =3D=3D self.OUTPUT_INTERNAL: + if dtype !=3D "function": + return True + + if name not in self.function_table: + return True + + return False + + def check_function(self, fname, name, args): + return True + + def check_enum(self, fname, name, args): + return True + + def check_typedef(self, fname, name, args): + return True + + def msg(self, fname, name, args): + + dtype =3D args.get('type', "") + + if dtype =3D=3D "doc": + self.out_doc(fname, name, args) + return False + + if not self.check_declaration(dtype, name): + return False + + if dtype =3D=3D "function": + self.out_function(fname, name, args) + return False + + if dtype =3D=3D "enum": + self.out_enum(fname, name, args) + return False + + if dtype =3D=3D "typedef": + self.out_typedef(fname, name, args) + return False + + if dtype in ["struct", "union"]: + self.out_struct(fname, name, args) + return False + + # Warn if some type requires an output logic + self.config.log.warning("doesn't now how to output '%s' block", + dtype) + + return True + + # Virtual methods to be overridden by inherited classes + def out_doc(self, fname, name, args): + pass + + def out_function(self, fname, name, args): + pass + + def out_enum(self, fname, name, args): + pass + + def out_typedef(self, fname, name, args): + pass + + def out_struct(self, fname, name, args): + pass + + +class RestFormat(OutputFormat): + # """Consts and functions used by ReST output""" + + highlights =3D [ + (type_constant, r"``\1``"), + (type_constant2, r"``\1``"), + + # Note: need to escape () to avoid func matching later + (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), + (type_member, r":c:type:`\1\2\3 <\1>`"), + (type_fp_param, r"**\1\\(\\)**"), + (type_fp_param2, r"**\1\\(\\)**"), + (type_func, r"\1()"), + (type_enum, r":c:type:`\1 <\2>`"), + (type_struct, r":c:type:`\1 <\2>`"), + (type_typedef, r":c:type:`\1 <\2>`"), + (type_union, r":c:type:`\1 <\2>`"), + + # in rst this can refer to any type + (type_fallback, r":c:type:`\1`"), + (type_param_ref, r"**\1\2**") + ] + blankline =3D "\n" + + sphinx_literal =3D Re(r'^[^.].*::$', cache=3DFalse) + sphinx_cblock =3D Re(r'^\.\.\ +code-block::', cache=3DFalse) + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.lineprefix =3D "" + + def print_lineno(self, ln): + """Outputs a line number""" + + if self.enable_lineno and ln: + print(f".. LINENO {ln}") + + def output_highlight(self, args): + input_text =3D args + output =3D "" + in_literal =3D False + litprefix =3D "" + block =3D "" + + for line in input_text.strip("\n").split("\n"): + + # If we're in a literal block, see if we should drop out of it. + # Otherwise, pass the line straight through unmunged. + if in_literal: + if line.strip(): # If the line is not blank + # If this is the first non-blank line in a literal blo= ck, + # figure out the proper indent. + if not litprefix: + r =3D Re(r'^(\s*)') + if r.match(line): + litprefix =3D '^' + r.group(1) + else: + litprefix =3D "" + + output +=3D line + "\n" + elif not Re(litprefix).match(line): + in_literal =3D False + else: + output +=3D line + "\n" + else: + output +=3D line + "\n" + + # Not in a literal block (or just dropped out) + if not in_literal: + block +=3D line + "\n" + if self.sphinx_literal.match(line) or self.sphinx_cblock.m= atch(line): + in_literal =3D True + litprefix =3D "" + output +=3D self.highlight_block(block) + block =3D "" + + # Handle any remaining block + if block: + output +=3D self.highlight_block(block) + + # Print the output with the line prefix + for line in output.strip("\n").split("\n"): + print(self.lineprefix + line) + + def out_section(self, args, out_reference=3DFalse): + """ + Outputs a block section. + + This could use some work; it's used to output the DOC: sections, a= nd + starts by putting out the name of the doc section itself, but that + tends to duplicate a header already in the template file. + """ + + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + section_start_lines =3D args.get('section_start_lines', {}) + + for section in sectionlist: + # Skip sections that are in the nosymbol_table + if section in self.nosymbol: + continue + + if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: + if out_reference: + print(f".. _{section}:\n") + + if not self.symbol: + print(f'{self.lineprefix}**{section}**\n') + + self.print_lineno(section_start_lines.get(section, 0)) + self.output_highlight(sections[section]) + print() + print() + + def out_doc(self, fname, name, args): + if not self.check_doc(name): + return + + self.out_section(args, out_reference=3DTrue) + + def out_function(self, fname, name, args): + + oldprefix =3D self.lineprefix + signature =3D "" + + func_macro =3D args.get('func_macro', False) + if func_macro: + signature =3D args['function'] + else: + if args.get('functiontype'): + signature =3D args['functiontype'] + " " + signature +=3D args['function'] + " (" + + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) + + ln =3D args.get('ln', 0) + + count =3D 0 + for parameter in parameterlist: + if count !=3D 0: + signature +=3D ", " + count +=3D 1 + dtype =3D args['parametertypes'].get(parameter, "") + + if function_pointer.search(dtype): + signature +=3D function_pointer.group(1) + parameter + fun= ction_pointer.group(3) + else: + signature +=3D dtype + + if not func_macro: + signature +=3D ")" + + if args.get('typedef') or not args.get('functiontype'): + print(f".. c:macro:: {args['function']}\n") + + if args.get('typedef'): + self.print_lineno(ln) + print(" **Typedef**: ", end=3D"") + self.lineprefix =3D "" + self.output_highlight(args.get('purpose', "")) + print("\n\n**Syntax**\n") + print(f" ``{signature}``\n") + else: + print(f"``{signature}``\n") + else: + print(f".. c:function:: {signature}\n") + + if not args.get('typedef'): + self.print_lineno(ln) + self.lineprefix =3D " " + self.output_highlight(args.get('purpose', "")) + print() + + # Put descriptive text into a container (HTML
) to help set + # function prototypes apart + self.lineprefix =3D " " + + if parameterlist: + print(".. container:: kernelindent\n") + print(f"{self.lineprefix}**Parameters**\n") + + for parameter in parameterlist: + parameter_name =3D Re(r'\[.*').sub('', parameter) + dtype =3D args['parametertypes'].get(parameter, "") + + if dtype: + print(f"{self.lineprefix}``{dtype}``") + else: + print(f"{self.lineprefix}``{parameter}``") + + self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) + + self.lineprefix =3D " " + if parameter_name in parameterdescs and \ + parameterdescs[parameter_name] !=3D KernelDoc.undescribed: + + self.output_highlight(parameterdescs[parameter_name]) + print() + else: + print(f"{self.lineprefix}*undescribed*\n") + self.lineprefix =3D " " + + self.out_section(args) + self.lineprefix =3D oldprefix + + def out_enum(self, fname, name, args): + + oldprefix =3D self.lineprefix + name =3D args.get('enum', '') + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + ln =3D args.get('ln', 0) + + print(f"\n\n.. c:enum:: {name}\n") + + self.print_lineno(ln) + self.lineprefix =3D " " + self.output_highlight(args.get('purpose', '')) + print() + + print(".. container:: kernelindent\n") + outer =3D self.lineprefix + " " + self.lineprefix =3D outer + " " + print(f"{outer}**Constants**\n") + + for parameter in parameterlist: + print(f"{outer}``{parameter}``") + + if parameterdescs.get(parameter, '') !=3D KernelDoc.undescribe= d: + self.output_highlight(parameterdescs[parameter]) + else: + print(f"{self.lineprefix}*undescribed*\n") + print() + + self.lineprefix =3D oldprefix + self.out_section(args) + + def out_typedef(self, fname, name, args): + + oldprefix =3D self.lineprefix + name =3D args.get('typedef', '') + ln =3D args.get('ln', 0) + + print(f"\n\n.. c:type:: {name}\n") + + self.print_lineno(ln) + self.lineprefix =3D " " + + self.output_highlight(args.get('purpose', '')) + + print() + + self.lineprefix =3D oldprefix + self.out_section(args) + + def out_struct(self, fname, name, args): + + name =3D args.get('struct', "") + purpose =3D args.get('purpose', "") + declaration =3D args.get('definition', "") + dtype =3D args.get('type', "struct") + ln =3D args.get('ln', 0) + + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) + + print(f"\n\n.. c:{dtype}:: {name}\n") + + self.print_lineno(ln) + + oldprefix =3D self.lineprefix + self.lineprefix +=3D " " + + self.output_highlight(purpose) + print() + + print(".. container:: kernelindent\n") + print(f"{self.lineprefix}**Definition**::\n") + + self.lineprefix =3D self.lineprefix + " " + + declaration =3D declaration.replace("\t", self.lineprefix) + + print(f"{self.lineprefix}{dtype} {name}" + ' {') + print(f"{declaration}{self.lineprefix}" + "};\n") + + self.lineprefix =3D " " + print(f"{self.lineprefix}**Members**\n") + for parameter in parameterlist: + if not parameter or parameter.startswith("#"): + continue + + parameter_name =3D parameter.split("[", maxsplit=3D1)[0] + + if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: + continue + + self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) + + print(f"{self.lineprefix}``{parameter}``") + + self.lineprefix =3D " " + self.output_highlight(parameterdescs[parameter_name]) + self.lineprefix =3D " " + + print() + + print() + + self.lineprefix =3D oldprefix + self.out_section(args) + + +class ManFormat(OutputFormat): + """Consts and functions used by man pages output""" + + highlights =3D ( + (type_constant, r"\1"), + (type_constant2, r"\1"), + (type_func, r"\\fB\1\\fP"), + (type_enum, r"\\fI\1\\fP"), + (type_struct, r"\\fI\1\\fP"), + (type_typedef, r"\\fI\1\\fP"), + (type_union, r"\\fI\1\\fP"), + (type_param, r"\\fI\1\\fP"), + (type_param_ref, r"\\fI\1\2\\fP"), + (type_member, r"\\fI\1\2\3\\fP"), + (type_fallback, r"\\fI\1\\fP") + ) + blankline =3D "" + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + + dt =3D datetime.now() + if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): + # use UTC TZ + to_zone =3D tz.gettz('UTC') + dt =3D dt.astimezone(to_zone) + + self.man_date =3D dt.strftime("%B %Y") + + def output_highlight(self, block): + + contents =3D self.highlight_block(block) + + if isinstance(contents, list): + contents =3D "\n".join(contents) + + for line in contents.strip("\n").split("\n"): + line =3D Re(r"^\s*").sub("", line) + + if line and line[0] =3D=3D ".": + print("\\&" + line) + else: + print(line) + + def out_doc(self, fname, name, args): + module =3D args.get('module') + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{module}" 9 "{module}" "{self.man_date}" "API Manual"= LINUX') + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections.get(section)) + + def out_function(self, fname, name, args): + """output function in man""" + + parameterlist =3D args.get('parameterlist', []) + parameterdescs =3D args.get('parameterdescs', {}) + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{args['function']}" 9 "{args['function']}" "{self.man= _date}" "Kernel Hacker\'s Manual" LINUX') + + print(".SH NAME") + print(f"{args['function']} \\- {args['purpose']}") + + print(".SH SYNOPSIS") + if args.get('functiontype', ''): + print(f'.B "{args['functiontype']}" {args['function']}') + else: + print(f'.B "{args['function']}') + + count =3D 0 + parenth =3D "(" + post =3D "," + + for parameter in parameterlist: + if count =3D=3D len(parameterlist) - 1: + post =3D ");" + + dtype =3D args['parametertypes'].get(parameter, "") + if function_pointer.match(dtype): + # Pointer-to-function + print(f'".BI "{parenth}{function_pointer.group(1)}" " ") (= {function_pointer.group(2)}){post}"') + else: + dtype =3D Re(r'([^\*])$').sub(r'\1 ', dtype) + + print(f'.BI "{parenth}{dtype}" "{post}"') + count +=3D 1 + parenth =3D "" + + if parameterlist: + print(".SH ARGUMENTS") + + for parameter in parameterlist: + parameter_name =3D re.sub(r'\[.*', '', parameter) + + print(f'.IP "{parameter}" 12') + self.output_highlight(parameterdescs.get(parameter_name, "")) + + for section in sectionlist: + print(f'.SH "{section.upper()}"') + self.output_highlight(sections[section]) + + def out_enum(self, fname, name, args): + + name =3D args.get('enum', '') + parameterlist =3D args.get('parameterlist', []) + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{args['module']}" 9 "enum {args['enum']}" "{self.man_= date}" "API Manual" LINUX') + + print(".SH NAME") + print(f"enum {args['enum']} \\- {args['purpose']}") + + print(".SH SYNOPSIS") + print(f"enum {args['enum']}" + " {") + + count =3D 0 + for parameter in parameterlist: + print(f'.br\n.BI " {parameter}"') + if count =3D=3D len(parameterlist) - 1: + print("\n};") + else: + print(", \n.br") + + count +=3D 1 + + print(".SH Constants") + + for parameter in parameterlist: + parameter_name =3D Re(r'\[.*').sub('', parameter) + print(f'.IP "{parameter}" 12') + self.output_highlight(args['parameterdescs'].get(parameter_nam= e, "")) + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections[section]) + + def out_typedef(self, fname, name, args): + module =3D args.get('module') + typedef =3D args.get('typedef') + purpose =3D args.get('purpose') + sectionlist =3D args.get('sectionlist', []) + sections =3D args.get('sections', {}) + + print(f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual= " LINUX') + + print(".SH NAME") + print(f"typedef {typedef} \\- {purpose}") + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections.get(section)) + + def out_struct(self, fname, name, args): + module =3D args.get('module') + struct_type =3D args.get('type') + struct_name =3D args.get('struct') + purpose =3D args.get('purpose') + definition =3D args.get('definition') + sectionlist =3D args.get('sectionlist', []) + parameterlist =3D args.get('parameterlist', []) + sections =3D args.get('sections', {}) + parameterdescs =3D args.get('parameterdescs', {}) + + print(f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_= date}" "API Manual" LINUX') + + print(".SH NAME") + print(f"{struct_type} {struct_name} \\- {purpose}") + + # Replace tabs with two spaces and handle newlines + declaration =3D definition.replace("\t", " ") + declaration =3D Re(r"\n").sub('"\n.br\n.BI "', declaration) + + print(".SH SYNOPSIS") + print(f"{struct_type} {struct_name} " + "{" + "\n.br") + print(f'.BI "{declaration}\n' + "};\n.br\n") + + print(".SH Members") + for parameter in parameterlist: + if parameter.startswith("#"): + continue + + parameter_name =3D re.sub(r"\[.*", "", parameter) + + if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: + continue + + print(f'.IP "{parameter}" 12') + self.output_highlight(parameterdescs.get(parameter_name)) + + for section in sectionlist: + print(f'.SH "{section}"') + self.output_highlight(sections.get(section)) --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D065B24A066; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=b8+EA7rm8izNn34gP5YNDh7CpsNcdhs+hwMYd8EYOkwy4JBURy2ATv7wPtCE8Up2Dix6uCJ0M6mNSs6h6x3lWcfEI5dik7l4CTpPgQ1+ez9Q1fnmu+zYWv3Odh18oipM4Fj9yyDGP8wcGiURZ5+NtVOzeBLH0JSbJdHjeahrFr0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=+233tNh0yGNXfYZxQx+uxFiZ7ugw73yF3W1jWprPI1A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hbEUk0TfUaXfRkCSpKh7lRDmIqSkOj/3llBeOZmpPiyZOidxofJhqqkhgJT20unX9Fi8MFTVvcigldAfvCDlztvKRsTQtzMBc96Gatx1y1x3Qnwz4L8AnvNt9rORkW000/V8o0himmS0wbN7fWna0HHFmWq2U+2un0ZCislGXmw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ao+6ySxy; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ao+6ySxy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0096FC113CF; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=+233tNh0yGNXfYZxQx+uxFiZ7ugw73yF3W1jWprPI1A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ao+6ySxy2zzGXgZD5+bRUiuFjIt4gIU67lH3Cnkw2+Hgzm2kHOClv7YkHaKyKuatd YPMfrjhEKuztSCohG59M4AxWSr2xNHwyB7O4hhWCiVbogD31ZyeKgLZ0MwaCzcwYFF 16q3a3aK/5k89B0pXIC+jZpMTL52tnPBZU1gXtwoytF3OfKhRSdb86qdeUZhyJmsQB wPH31iLtnc4RifEPHc3P7T9PjCRZHrxtM2UCz89lTmM+1ianlvJz4V02oPvzWcV+/F yHL2pIHi9i2pOt9Sk2P/E/PZeJqTR4SDL5Mnz5iTaG1RnPcTwt+GyEVMvNISXGrk5T q0s8rhHAMerNA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4W-15Mq; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 17/39] scripts/kernel-doc.py: convert message output to an interactor Date: Mon, 24 Feb 2025 10:08:23 +0100 Message-ID: <2b5ecc8328cdb8a37e7ff084dae89a82a532e49a.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Instead of directly printing output messages, change kdoc classes to return an interactor with the output message, letting the actual display to happen at the command-line command. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 9 +- scripts/lib/kdoc/kdoc_files.py | 15 ++- scripts/lib/kdoc/kdoc_output.py | 171 ++++++++++++++++---------------- 3 files changed, 104 insertions(+), 91 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 0596c711d448..6c3179a2da65 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -208,9 +208,12 @@ def main(): =20 kfiles.parse() =20 - kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.export, - internal=3Dargs.internal, symbol=3Dargs.symbol, - nosymbol=3Dargs.nosymbol) + for t in kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.= export, + internal=3Dargs.internal, symbol=3Dargs.symbol, + nosymbol=3Dargs.nosymbol): + msg =3D t[1] + if msg: + print(msg) =20 =20 # Call main method diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 76dd53611c08..434fc66a9dad 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -228,9 +228,10 @@ class KernelFiles(): =20 def out_msg(self, fname, name, arg): """ - Output messages from a file name using the output style filtering. + Return output messages from a file name using the output style + filtering. =20 - If output type was not handled by the syler, return False. + If output type was not handled by the syler, return None. """ =20 # NOTE: we can add rules here to filter out unwanted parts, @@ -241,7 +242,8 @@ class KernelFiles(): def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, symbol=3DNone, nosymbol=3DNone): """ - Interacts over the kernel-doc results and output messages. + Interacts over the kernel-doc results and output messages, + returning kernel-doc markups on each interaction """ =20 function_table =3D self.config.function_table @@ -260,10 +262,15 @@ class KernelFiles(): function_table, enable_lineno) =20 for fname, arg_tuple in self.results: + msg =3D "" for name, arg in arg_tuple: - if self.out_msg(fname, name, arg): + msg +=3D self.out_msg(fname, name, arg) + + if msg is None: ln =3D arg.get("ln", 0) dtype =3D arg.get('type', "") =20 self.config.log.warning("%s:%d Can't handle %s", fname, ln, dtype) + if msg: + yield fname, msg diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index d080440caa1c..91f6e356d03d 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -70,6 +70,8 @@ class OutputFormat: self.function_table =3D set() self.config =3D None =20 + self.data =3D "" + def set_config(self, config): self.config =3D config =20 @@ -156,37 +158,38 @@ class OutputFormat: return True =20 def msg(self, fname, name, args): + self.data =3D "" =20 dtype =3D args.get('type', "") =20 if dtype =3D=3D "doc": self.out_doc(fname, name, args) - return False + return self.data =20 if not self.check_declaration(dtype, name): - return False + return self.data =20 if dtype =3D=3D "function": self.out_function(fname, name, args) - return False + return self.data =20 if dtype =3D=3D "enum": self.out_enum(fname, name, args) - return False + return self.data =20 if dtype =3D=3D "typedef": self.out_typedef(fname, name, args) - return False + return self.data =20 if dtype in ["struct", "union"]: self.out_struct(fname, name, args) - return False + return self.data =20 # Warn if some type requires an output logic self.config.log.warning("doesn't now how to output '%s' block", dtype) =20 - return True + return None =20 # Virtual methods to be overridden by inherited classes def out_doc(self, fname, name, args): @@ -247,7 +250,7 @@ class RestFormat(OutputFormat): """Outputs a line number""" =20 if self.enable_lineno and ln: - print(f".. LINENO {ln}") + self.data +=3D f".. LINENO {ln}\n" =20 def output_highlight(self, args): input_text =3D args @@ -294,7 +297,7 @@ class RestFormat(OutputFormat): =20 # Print the output with the line prefix for line in output.strip("\n").split("\n"): - print(self.lineprefix + line) + self.data +=3D self.lineprefix + line + "\n" =20 def out_section(self, args, out_reference=3DFalse): """ @@ -316,15 +319,15 @@ class RestFormat(OutputFormat): =20 if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: if out_reference: - print(f".. _{section}:\n") + self.data +=3D f".. _{section}:\n\n" =20 if not self.symbol: - print(f'{self.lineprefix}**{section}**\n') + self.data +=3D f'{self.lineprefix}**{section}**\n\n' =20 self.print_lineno(section_start_lines.get(section, 0)) self.output_highlight(sections[section]) - print() - print() + self.data +=3D "\n" + self.data +=3D "\n" =20 def out_doc(self, fname, name, args): if not self.check_doc(name): @@ -367,42 +370,42 @@ class RestFormat(OutputFormat): signature +=3D ")" =20 if args.get('typedef') or not args.get('functiontype'): - print(f".. c:macro:: {args['function']}\n") + self.data +=3D f".. c:macro:: {args['function']}\n\n" =20 if args.get('typedef'): self.print_lineno(ln) - print(" **Typedef**: ", end=3D"") + self.data +=3D " **Typedef**: " self.lineprefix =3D "" self.output_highlight(args.get('purpose', "")) - print("\n\n**Syntax**\n") - print(f" ``{signature}``\n") + self.data +=3D "\n\n**Syntax**\n\n" + self.data +=3D f" ``{signature}``\n\n" else: - print(f"``{signature}``\n") + self.data +=3D f"``{signature}``\n\n" else: - print(f".. c:function:: {signature}\n") + self.data +=3D f".. c:function:: {signature}\n\n" =20 if not args.get('typedef'): self.print_lineno(ln) self.lineprefix =3D " " self.output_highlight(args.get('purpose', "")) - print() + self.data +=3D "\n" =20 # Put descriptive text into a container (HTML
) to help set # function prototypes apart self.lineprefix =3D " " =20 if parameterlist: - print(".. container:: kernelindent\n") - print(f"{self.lineprefix}**Parameters**\n") + self.data +=3D ".. container:: kernelindent\n\n" + self.data +=3D f"{self.lineprefix}**Parameters**\n\n" =20 for parameter in parameterlist: parameter_name =3D Re(r'\[.*').sub('', parameter) dtype =3D args['parametertypes'].get(parameter, "") =20 if dtype: - print(f"{self.lineprefix}``{dtype}``") + self.data +=3D f"{self.lineprefix}``{dtype}``\n" else: - print(f"{self.lineprefix}``{parameter}``") + self.data +=3D f"{self.lineprefix}``{parameter}``\n" =20 self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) =20 @@ -411,9 +414,9 @@ class RestFormat(OutputFormat): parameterdescs[parameter_name] !=3D KernelDoc.undescribed: =20 self.output_highlight(parameterdescs[parameter_name]) - print() + self.data +=3D "\n" else: - print(f"{self.lineprefix}*undescribed*\n") + self.data +=3D f"{self.lineprefix}*undescribed*\n\n" self.lineprefix =3D " " =20 self.out_section(args) @@ -427,26 +430,26 @@ class RestFormat(OutputFormat): parameterdescs =3D args.get('parameterdescs', {}) ln =3D args.get('ln', 0) =20 - print(f"\n\n.. c:enum:: {name}\n") + self.data +=3D f"\n\n.. c:enum:: {name}\n\n" =20 self.print_lineno(ln) self.lineprefix =3D " " self.output_highlight(args.get('purpose', '')) - print() + self.data +=3D "\n" =20 - print(".. container:: kernelindent\n") + self.data +=3D ".. container:: kernelindent\n\n" outer =3D self.lineprefix + " " self.lineprefix =3D outer + " " - print(f"{outer}**Constants**\n") + self.data +=3D f"{outer}**Constants**\n\n" =20 for parameter in parameterlist: - print(f"{outer}``{parameter}``") + self.data +=3D f"{outer}``{parameter}``\n" =20 if parameterdescs.get(parameter, '') !=3D KernelDoc.undescribe= d: self.output_highlight(parameterdescs[parameter]) else: - print(f"{self.lineprefix}*undescribed*\n") - print() + self.data +=3D f"{self.lineprefix}*undescribed*\n\n" + self.data +=3D "\n" =20 self.lineprefix =3D oldprefix self.out_section(args) @@ -457,14 +460,14 @@ class RestFormat(OutputFormat): name =3D args.get('typedef', '') ln =3D args.get('ln', 0) =20 - print(f"\n\n.. c:type:: {name}\n") + self.data +=3D f"\n\n.. c:type:: {name}\n\n" =20 self.print_lineno(ln) self.lineprefix =3D " " =20 self.output_highlight(args.get('purpose', '')) =20 - print() + self.data +=3D "\n" =20 self.lineprefix =3D oldprefix self.out_section(args) @@ -481,7 +484,7 @@ class RestFormat(OutputFormat): parameterdescs =3D args.get('parameterdescs', {}) parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) =20 - print(f"\n\n.. c:{dtype}:: {name}\n") + self.data +=3D f"\n\n.. c:{dtype}:: {name}\n\n" =20 self.print_lineno(ln) =20 @@ -489,20 +492,20 @@ class RestFormat(OutputFormat): self.lineprefix +=3D " " =20 self.output_highlight(purpose) - print() + self.data +=3D "\n" =20 - print(".. container:: kernelindent\n") - print(f"{self.lineprefix}**Definition**::\n") + self.data +=3D ".. container:: kernelindent\n\n" + self.data +=3D f"{self.lineprefix}**Definition**::\n\n" =20 self.lineprefix =3D self.lineprefix + " " =20 declaration =3D declaration.replace("\t", self.lineprefix) =20 - print(f"{self.lineprefix}{dtype} {name}" + ' {') - print(f"{declaration}{self.lineprefix}" + "};\n") + self.data +=3D f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" + self.data +=3D f"{declaration}{self.lineprefix}" + "};\n\n" =20 self.lineprefix =3D " " - print(f"{self.lineprefix}**Members**\n") + self.data +=3D f"{self.lineprefix}**Members**\n\n" for parameter in parameterlist: if not parameter or parameter.startswith("#"): continue @@ -514,15 +517,15 @@ class RestFormat(OutputFormat): =20 self.print_lineno(parameterdesc_start_lines.get(parameter_name= , 0)) =20 - print(f"{self.lineprefix}``{parameter}``") + self.data +=3D f"{self.lineprefix}``{parameter}``\n" =20 self.lineprefix =3D " " self.output_highlight(parameterdescs[parameter_name]) self.lineprefix =3D " " =20 - print() + self.data +=3D "\n" =20 - print() + self.data +=3D "\n" =20 self.lineprefix =3D oldprefix self.out_section(args) @@ -575,19 +578,19 @@ class ManFormat(OutputFormat): line =3D Re(r"^\s*").sub("", line) =20 if line and line[0] =3D=3D ".": - print("\\&" + line) + self.data +=3D "\\&" + line + "\n" else: - print(line) + self.data +=3D line + "\n" =20 def out_doc(self, fname, name, args): module =3D args.get('module') sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - print(f'.TH "{module}" 9 "{module}" "{self.man_date}" "API Manual"= LINUX') + self.data +=3D f'.TH "{module}" 9 "{module}" "{self.man_date}" "AP= I Manual" LINUX' + "\n" =20 for section in sectionlist: - print(f'.SH "{section}"') + self.data +=3D f'.SH "{section}"' + "\n" self.output_highlight(sections.get(section)) =20 def out_function(self, fname, name, args): @@ -598,16 +601,16 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - print(f'.TH "{args['function']}" 9 "{args['function']}" "{self.man= _date}" "Kernel Hacker\'s Manual" LINUX') + self.data +=3D f'.TH "{args['function']}" 9 "{args['function']}" "= {self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" =20 - print(".SH NAME") - print(f"{args['function']} \\- {args['purpose']}") + self.data +=3D ".SH NAME\n" + self.data +=3D f"{args['function']} \\- {args['purpose']}\n" =20 - print(".SH SYNOPSIS") + self.data +=3D ".SH SYNOPSIS\n" if args.get('functiontype', ''): - print(f'.B "{args['functiontype']}" {args['function']}') + self.data +=3D f'.B "{args['functiontype']}" {args['function']= }' + "\n" else: - print(f'.B "{args['function']}') + self.data +=3D f'.B "{args['function']}' + "\n" =20 count =3D 0 parenth =3D "(" @@ -620,25 +623,25 @@ class ManFormat(OutputFormat): dtype =3D args['parametertypes'].get(parameter, "") if function_pointer.match(dtype): # Pointer-to-function - print(f'".BI "{parenth}{function_pointer.group(1)}" " ") (= {function_pointer.group(2)}){post}"') + self.data +=3D f'".BI "{parenth}{function_pointer.group(1)= }" " ") ({function_pointer.group(2)}){post}"' + "\n" else: dtype =3D Re(r'([^\*])$').sub(r'\1 ', dtype) =20 - print(f'.BI "{parenth}{dtype}" "{post}"') + self.data +=3D f'.BI "{parenth}{dtype}" "{post}"' + "\n" count +=3D 1 parenth =3D "" =20 if parameterlist: - print(".SH ARGUMENTS") + self.data +=3D ".SH ARGUMENTS\n" =20 for parameter in parameterlist: parameter_name =3D re.sub(r'\[.*', '', parameter) =20 - print(f'.IP "{parameter}" 12') + self.data +=3D f'.IP "{parameter}" 12' + "\n" self.output_highlight(parameterdescs.get(parameter_name, "")) =20 for section in sectionlist: - print(f'.SH "{section.upper()}"') + self.data +=3D f'.SH "{section.upper()}"' + "\n" self.output_highlight(sections[section]) =20 def out_enum(self, fname, name, args): @@ -648,33 +651,33 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - print(f'.TH "{args['module']}" 9 "enum {args['enum']}" "{self.man_= date}" "API Manual" LINUX') + self.data +=3D f'.TH "{args['module']}" 9 "enum {args['enum']}" "{= self.man_date}" "API Manual" LINUX' + "\n" =20 - print(".SH NAME") - print(f"enum {args['enum']} \\- {args['purpose']}") + self.data +=3D ".SH NAME\n" + self.data +=3D f"enum {args['enum']} \\- {args['purpose']}\n" =20 - print(".SH SYNOPSIS") - print(f"enum {args['enum']}" + " {") + self.data +=3D ".SH SYNOPSIS\n" + self.data +=3D f"enum {args['enum']}" + " {\n" =20 count =3D 0 for parameter in parameterlist: - print(f'.br\n.BI " {parameter}"') + self.data +=3D f'.br\n.BI " {parameter}"' + "\n" if count =3D=3D len(parameterlist) - 1: - print("\n};") + self.data +=3D "\n};\n" else: - print(", \n.br") + self.data +=3D ", \n.br\n" =20 count +=3D 1 =20 - print(".SH Constants") + self.data +=3D ".SH Constants\n" =20 for parameter in parameterlist: parameter_name =3D Re(r'\[.*').sub('', parameter) - print(f'.IP "{parameter}" 12') + self.data +=3D f'.IP "{parameter}" 12' + "\n" self.output_highlight(args['parameterdescs'].get(parameter_nam= e, "")) =20 for section in sectionlist: - print(f'.SH "{section}"') + self.data +=3D f'.SH "{section}"' + "\n" self.output_highlight(sections[section]) =20 def out_typedef(self, fname, name, args): @@ -684,13 +687,13 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - print(f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual= " LINUX') + self.data +=3D f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "A= PI Manual" LINUX' + "\n" =20 - print(".SH NAME") - print(f"typedef {typedef} \\- {purpose}") + self.data +=3D ".SH NAME\n" + self.data +=3D f"typedef {typedef} \\- {purpose}\n" =20 for section in sectionlist: - print(f'.SH "{section}"') + self.data +=3D f'.SH "{section}"' + "\n" self.output_highlight(sections.get(section)) =20 def out_struct(self, fname, name, args): @@ -704,20 +707,20 @@ class ManFormat(OutputFormat): sections =3D args.get('sections', {}) parameterdescs =3D args.get('parameterdescs', {}) =20 - print(f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_= date}" "API Manual" LINUX') + self.data +=3D f'.TH "{module}" 9 "{struct_type} {struct_name}" "{= self.man_date}" "API Manual" LINUX' + "\n" =20 - print(".SH NAME") - print(f"{struct_type} {struct_name} \\- {purpose}") + self.data +=3D ".SH NAME\n" + self.data +=3D f"{struct_type} {struct_name} \\- {purpose}\n" =20 # Replace tabs with two spaces and handle newlines declaration =3D definition.replace("\t", " ") declaration =3D Re(r"\n").sub('"\n.br\n.BI "', declaration) =20 - print(".SH SYNOPSIS") - print(f"{struct_type} {struct_name} " + "{" + "\n.br") - print(f'.BI "{declaration}\n' + "};\n.br\n") + self.data +=3D ".SH SYNOPSIS\n" + self.data +=3D f"{struct_type} {struct_name} " + "{" + "\n.br\n" + self.data +=3D f'.BI "{declaration}\n' + "};\n.br\n\n" =20 - print(".SH Members") + self.data +=3D ".SH Members\n" for parameter in parameterlist: if parameter.startswith("#"): continue @@ -727,9 +730,9 @@ class ManFormat(OutputFormat): if parameterdescs.get(parameter_name) =3D=3D KernelDoc.undescr= ibed: continue =20 - print(f'.IP "{parameter}" 12') + self.data +=3D f'.IP "{parameter}" 12' + "\n" self.output_highlight(parameterdescs.get(parameter_name)) =20 for section in sectionlist: - print(f'.SH "{section}"') + self.data +=3D f'.SH "{section}"' + "\n" self.output_highlight(sections.get(section)) --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8E8D62451F1; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=FPQWM5aqZsukR3P/UUvrsWKCHCBvP1/JPI3XzzNMzKLmHZrbKAPvxYibBIDUm7CuA00ynFfLVoVlpTENrjFXDqcxGxblibfkbRoQvNUw+3kdoPhm//OcRUg2R3+hw+ohHEomGtfyQaF3FtRhmyZg/lqEfOtU5NXH4Ggpc3VPj8c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=Q2bW4rHBvXUepqtRYw3jrHwqyqtoRshTMOAzsCvXTFo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZM0XZv6TtIJEX2tbDr0tfqld2zEYddrU8c0qlsGVp3p3TLYTmhBUzeklaGveWVdhAm++nu1UuPpIoqCRrh7mWeVA3omkHZO/YnXqEEqZI4pNpbyvWCB1wj44rbHJjxFWP0/8wuP2+ika3RqcJ8bsWtDPZYVl1hxr94P9TDd93RU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=U+ukiRqk; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="U+ukiRqk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 07319C116B1; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=Q2bW4rHBvXUepqtRYw3jrHwqyqtoRshTMOAzsCvXTFo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U+ukiRqkDheAfPTbdOd1Rp1TLQIOKK6x+IkXEjtY6cC5zP0J3nBeysHhgUTsHCdEE 1K22KwOwwCVap320NOiLJjrdzeV4M0F8OWHKX4LodbzwjLwLjeqPOSOF5uxXB37ztC EiO18FDTxRWOHN4kbaa56AoB55PP3pp9QwZMYj0cmMiwDiG1t+xGN7Q/WHbLcAFeUa XBVLAW0Hk0ncwc8vK05jIyHCZrQ+i7cz3pte9eGUo1nN9lnLIryr7WZOYaCKwSxhIm pdnGuc3XIHHAEI3UdlzBqtVQTf08Oqud6R6AUEW87ru9Mx3FsRA7kY9Z+Y5w6k5HwM 09oje9xOIPWdA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4a-1CAZ; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 18/39] scripts/kernel-doc.py: move file lists to the parser function Date: Mon, 24 Feb 2025 10:08:24 +0100 Message-ID: <2080b6bb99a30146a7fe4eb8527aa78121b988eb.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Instead of setting file lists at __init__ time, move it to the actual parsing function. This allows adding more files to be parsed in real time, by calling parse function multiple times. With the new way, the export_files logic was rewritten to avoid parsing twice EXPORT_SYMBOL for partial matches. Please notice that, with this logic, it can still read the same file twice when export_file is used. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 7 +++---- scripts/lib/kdoc/kdoc_files.py | 37 ++++++++++++++++------------------ 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 6c3179a2da65..a687a7abb3b9 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -199,14 +199,13 @@ def main(): else: out_style =3D RestFormat() =20 - kfiles =3D KernelFiles(files=3Dargs.files, verbose=3Dargs.verbose, + kfiles =3D KernelFiles(verbose=3Dargs.verbose, out_style=3Dout_style, werror=3Dargs.werror, wreturn=3Dargs.wreturn, wshort_desc=3Dargs.wshort= _desc, wcontents_before_sections=3Dargs.wcontents_before= _sections, - modulename=3Dargs.modulename, - export_file=3Dargs.export_file) + modulename=3Dargs.modulename) =20 - kfiles.parse() + kfiles.parse(args.files, export_file=3Dargs.export_file) =20 for t in kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.= export, internal=3Dargs.internal, symbol=3Dargs.symbol, diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 434fc66a9dad..4a6e75dbdbdd 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -123,7 +123,7 @@ class KernelFiles(): self.config.log.error("Cannot find file %s", fname) self.config.errors +=3D 1 =20 - def __init__(self, files=3DNone, verbose=3DFalse, out_style=3DNone, + def __init__(self, verbose=3DFalse, out_style=3DNone, werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, wcontents_before_sections=3DFalse, logger=3DNone, modulename=3DNone, export_file=3DNone): @@ -180,51 +180,48 @@ class KernelFiles(): self.config.src_tree =3D os.environ.get("SRCTREE", None) =20 self.out_style =3D out_style - self.export_file =3D export_file =20 # Initialize internal variables =20 self.config.errors =3D 0 self.results =3D [] =20 - self.file_list =3D files self.files =3D set() + self.export_files =3D set() =20 - def parse(self): + def parse(self, file_list, export_file=3DNone): """ Parse all files """ =20 glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) =20 - # Let's use a set here to avoid duplicating files + # Prevent parsing the same file twice to speedup parsing and + # avoid reporting errors multiple times =20 - for fname in glob.parse_files(self.file_list, self.file_not_found_= cb): + for fname in glob.parse_files(file_list, self.file_not_found_cb): if fname in self.files: continue =20 - self.files.add(fname) - res =3D self.parse_file(fname) + self.results.append((res.fname, res.entries)) - - if not self.files: - sys.exit(1) + self.files.add(fname) =20 # If a list of export files was provided, parse EXPORT_SYMBOL* - # from the ones not already parsed + # from files that weren't fully parsed =20 - if self.export_file: - files =3D self.files + if not export_file: + return =20 - glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) + self.export_files |=3D self.files =20 - for fname in glob.parse_files(self.export_file, - self.file_not_found_cb): - if fname not in files: - files.add(fname) + glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) =20 - self.process_export_file(fname) + for fname in glob.parse_files(export_file, self.file_not_found_cb): + if fname not in self.export_files: + self.process_export_file(fname) + self.export_files.add(fname) =20 def out_msg(self, fname, name, arg): """ --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8EFCA2475C7; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=FXlvfxN3uMkSSFKDu0WxqRxlSNyzETNZ7YI9HRafT93/b9U6XpDTDGLU5ZXVBrQF6CGJRgL3ZEIgD5qinPyIvGFEhvZ+cByyr6ZlIOMlsnGoXAXBNsluRs0RdooLyfCdM1XqrCy3PTuw26/Cpacz40lf9e9wVq5r0hd1yqjTPfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=IpThprTfJ/pbtHwAeyiILQ0MdhJxWk9SeTU5TEeUoNg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nc2UDfUjdJjqew/QZcF349bPHo+BIdQqzfd+hTPdP0Dewca04+FyAQz9c19fWfRnch7AQr8yIevUsXLk47auVyXg3qLel03qeeXkzv6g1L23tOzjsA/vkgw0VgXn/s+5NhaRlBU+ZgWADkZiFRZuD8ho9DhX8zYhedwVX1igJuw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IsrVLo0G; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IsrVLo0G" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1BD52C116C6; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=IpThprTfJ/pbtHwAeyiILQ0MdhJxWk9SeTU5TEeUoNg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IsrVLo0G7Rglf1TxWKWKtqL8JAhipBdKRAcqPlq1E9nn4d8A6eYulJjQRBPVngDak ly4Xh15mZ5Yj0IJPpPvN5oiv0B5LSrfN2VF7J0ITYlSTyYICP3JyaS1XT9ef5IAdyJ 1QGRcp+2yEkw0PUEb8bg5C4jNp/9M2h81LNlY0bAKJlN9P3JrfvlJ8rExuAoAYbvGX 1L1wScwAV4MS2u0rMcS9o5oNts/aroPlQihN2yPpuDrbRLiDJG8ompRSNFEHHrRTza jm2dg1s+xaSjtphN8M3pCAFCBVO8FxbW3eHJ8ranRYcuygLgYYG7tcdAUfwXo7ARJv BzMUZHVsDXY5A== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4e-1JGU; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 19/39] scripts/kernel-doc.py: implement support for -no-doc-sections Date: Mon, 24 Feb 2025 10:08:25 +0100 Message-ID: <5292a5419c95f7754706e4d7ee59f74bf8d876b5.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The venerable kernel-doc Perl script has a number of options that aren't properly documented. Among them, there is -no-doc-sections, which is used by the Sphinx extension. Implement support for it. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 8 ++++++-- scripts/lib/kdoc/kdoc_files.py | 5 +++-- scripts/lib/kdoc/kdoc_output.py | 7 ++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index a687a7abb3b9..d700451e9541 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -164,10 +164,13 @@ def main(): sel_mut.add_argument("-s", "-function", "--symbol", action=3D'append', help=3DFUNCTION_DESC) =20 - # This one is valid for all 3 types of filter + # Those are valid for all 3 types of filter parser.add_argument("-n", "-nosymbol", "--nosymbol", action=3D'append', help=3DNOSYMBOL_DESC) =20 + parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", + action=3D'store_true', help=3D"Don't outputt DOC s= ections") + parser.add_argument("files", metavar=3D"FILE", nargs=3D"+", help=3DFILES_DESC) =20 @@ -209,7 +212,8 @@ def main(): =20 for t in kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.= export, internal=3Dargs.internal, symbol=3Dargs.symbol, - nosymbol=3Dargs.nosymbol): + nosymbol=3Dargs.nosymbol, + no_doc_sections=3Dargs.no_doc_sections): msg =3D t[1] if msg: print(msg) diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 4a6e75dbdbdd..c215ae3047b8 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -237,7 +237,7 @@ class KernelFiles(): return self.out_style.msg(fname, name, arg) =20 def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, - symbol=3DNone, nosymbol=3DNone): + symbol=3DNone, nosymbol=3DNone, no_doc_sections=3DFalse): """ Interacts over the kernel-doc results and output messages, returning kernel-doc markups on each interaction @@ -256,7 +256,8 @@ class KernelFiles(): self.out_style.set_config(self.config) =20 self.out_style.set_filter(export, internal, symbol, nosymbol, - function_table, enable_lineno) + function_table, enable_lineno, + no_doc_sections) =20 for fname, arg_tuple in self.results: msg =3D "" diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 91f6e356d03d..8729dc58e13c 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -69,6 +69,7 @@ class OutputFormat: self.symbol =3D None self.function_table =3D set() self.config =3D None + self.no_doc_sections =3D False =20 self.data =3D "" =20 @@ -76,7 +77,7 @@ class OutputFormat: self.config =3D config =20 def set_filter(self, export, internal, symbol, nosymbol, function_tabl= e, - enable_lineno): + enable_lineno, no_doc_sections): """ Initialize filter variables according with the requested mode. =20 @@ -86,6 +87,7 @@ class OutputFormat: """ =20 self.enable_lineno =3D enable_lineno + self.no_doc_sections =3D no_doc_sections =20 if symbol: self.out_mode =3D self.OUTPUT_INCLUDE @@ -116,6 +118,9 @@ class OutputFormat: def check_doc(self, name): """Check if DOC should be output""" =20 + if self.no_doc_sections: + return False + if self.out_mode =3D=3D self.OUTPUT_ALL: return True =20 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A4EB224A045; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=s022n2eDIHtyl7PmJIM/TCXVRqqdj+CJks1wL7jetV0IzHkYP4k+zj4LC3qs0hkxTv8vADCT90xoENgergMW4CqvXMtMMg3H3sLXJiFZvj8IeFdQ0SRPCNolC/ZdfUMp52ZBDSXyufXPVGotsKPds26KCN/EIy24JSKMCpt6pUU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=06ZoIn6KWo8lW7Vli577BGyd08sXznBzolIIOLad8Zg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CCgv7bh3AyUUlc1VOWf05YYqTG4rCnNXU+M/Y9NQwjoe9jPshYvBXFWl9R8y4y5OAQfDQkNvnDsHP9fModSQ4t4eaZc1vljkEFxCUAhI5GQ1kLOISZYtC2R0KOo07UJflFTJxS8T9ac5lAEcazhEMeyE1NGRsaetB4QeG8kJoIY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZXBi/n4a; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ZXBi/n4a" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0F55DC4CEFE; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=06ZoIn6KWo8lW7Vli577BGyd08sXznBzolIIOLad8Zg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZXBi/n4awk/IrB1CBGqnLwmNIYcwYlqTZgw0S3l4hhlMq5eycVVzXJvAaFJ7s6DTM RiUzFI7ahxHEY3lrBdeRUXW5BKRUfg0H5dNOGircdaeTpWz+WYfxeMJQAoCM80wRuk VNHGEiiJburH/kKH+aXbz/OPZMGzEZE4m0WY54llQLdtVI7emWuB+NrJ13L8UVTtk+ Cu7woMbaYVcw90/tHA0cyrJOPv+EQWhJOFqLiJdvS54sCUmzwk2Rjy1e0f1xqZk7+a WQ6AcTk3cb0CVkXnI4q5xxCACsE2gIzxDb6hiTA679ICnD/q2HJRvJpXjFO/3AuPBW srD/xv4ffFz8Q== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4i-1Pvw; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 20/39] scripts/kernel-doc.py: fix line number output Date: Mon, 24 Feb 2025 10:08:26 +0100 Message-ID: <7200fc2b7fc32a085cea994822f69a92d0561a29.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" With the Pyhton version, the actual output happens after parsing, from records stored at self.entries. Ensure that line numbers will be properly stored there and that they'll produce the desired results at the ReST output. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_output.py | 13 +++++++------ scripts/lib/kdoc/kdoc_parser.py | 21 +++++++++++++++++---- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 8729dc58e13c..b9b39bc29463 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -254,7 +254,8 @@ class RestFormat(OutputFormat): def print_lineno(self, ln): """Outputs a line number""" =20 - if self.enable_lineno and ln: + if self.enable_lineno and ln is not None: + ln +=3D 1 self.data +=3D f".. LINENO {ln}\n" =20 def output_highlight(self, args): @@ -357,7 +358,7 @@ class RestFormat(OutputFormat): parameterdescs =3D args.get('parameterdescs', {}) parameterdesc_start_lines =3D args.get('parameterdesc_start_lines'= , {}) =20 - ln =3D args.get('ln', 0) + ln =3D args.get('declaration_start_line', 0) =20 count =3D 0 for parameter in parameterlist: @@ -374,11 +375,11 @@ class RestFormat(OutputFormat): if not func_macro: signature +=3D ")" =20 + self.print_lineno(ln) if args.get('typedef') or not args.get('functiontype'): self.data +=3D f".. c:macro:: {args['function']}\n\n" =20 if args.get('typedef'): - self.print_lineno(ln) self.data +=3D " **Typedef**: " self.lineprefix =3D "" self.output_highlight(args.get('purpose', "")) @@ -433,7 +434,7 @@ class RestFormat(OutputFormat): name =3D args.get('enum', '') parameterlist =3D args.get('parameterlist', []) parameterdescs =3D args.get('parameterdescs', {}) - ln =3D args.get('ln', 0) + ln =3D args.get('declaration_start_line', 0) =20 self.data +=3D f"\n\n.. c:enum:: {name}\n\n" =20 @@ -463,7 +464,7 @@ class RestFormat(OutputFormat): =20 oldprefix =3D self.lineprefix name =3D args.get('typedef', '') - ln =3D args.get('ln', 0) + ln =3D args.get('declaration_start_line', 0) =20 self.data +=3D f"\n\n.. c:type:: {name}\n\n" =20 @@ -483,7 +484,7 @@ class RestFormat(OutputFormat): purpose =3D args.get('purpose', "") declaration =3D args.get('definition', "") dtype =3D args.get('type', "struct") - ln =3D args.get('ln', 0) + ln =3D args.get('declaration_start_line', 0) =20 parameterlist =3D args.get('parameterlist', []) parameterdescs =3D args.get('parameterdescs', {}) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index 6d6395e32093..633c95164b0c 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -275,7 +275,7 @@ class KernelDoc: self.entry.brcount =3D 0 =20 self.entry.in_doc_sect =3D False - self.entry.declaration_start_line =3D ln + self.entry.declaration_start_line =3D ln + 1 =20 def push_parameter(self, ln, decl_type, param, dtype, org_arg, declaration_name): @@ -805,8 +805,10 @@ class KernelDoc: parameterlist=3Dself.entry.parameterlist, parameterdescs=3Dself.entry.parameterdescs, parametertypes=3Dself.entry.parametertypes, + parameterdesc_start_lines=3Dself.entry.par= ameterdesc_start_lines, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.section_s= tart_lines, purpose=3Dself.entry.declaration_purpose) =20 def dump_enum(self, ln, proto): @@ -881,8 +883,10 @@ class KernelDoc: module=3Dself.config.modulename, parameterlist=3Dself.entry.parameterlist, parameterdescs=3Dself.entry.parameterdescs, + parameterdesc_start_lines=3Dself.entry.par= ameterdesc_start_lines, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.section_s= tart_lines, purpose=3Dself.entry.declaration_purpose) =20 def dump_declaration(self, ln, prototype): @@ -1053,8 +1057,10 @@ class KernelDoc: parameterlist=3Dself.entry.parameterli= st, parameterdescs=3Dself.entry.parameterd= escs, parametertypes=3Dself.entry.parametert= ypes, + parameterdesc_start_lines=3Dself.entry= .parameterdesc_start_lines, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.secti= on_start_lines, purpose=3Dself.entry.declaration_purpo= se, func_macro=3Dfunc_macro) else: @@ -1066,8 +1072,10 @@ class KernelDoc: parameterlist=3Dself.entry.parameterli= st, parameterdescs=3Dself.entry.parameterd= escs, parametertypes=3Dself.entry.parametert= ypes, + parameterdesc_start_lines=3Dself.entry= .parameterdesc_start_lines, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.secti= on_start_lines, purpose=3Dself.entry.declaration_purpo= se, func_macro=3Dfunc_macro) =20 @@ -1111,8 +1119,10 @@ class KernelDoc: parameterlist=3Dself.entry.parameterli= st, parameterdescs=3Dself.entry.parameterd= escs, parametertypes=3Dself.entry.parametert= ypes, + parameterdesc_start_lines=3Dself.entry= .parameterdesc_start_lines, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.secti= on_start_lines, purpose=3Dself.entry.declaration_purpo= se) return =20 @@ -1135,6 +1145,7 @@ class KernelDoc: module=3Dself.entry.modulename, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.secti= on_start_lines, purpose=3Dself.entry.declaration_purpo= se) return =20 @@ -1167,7 +1178,7 @@ class KernelDoc: return =20 # start a new entry - self.reset_state(ln + 1) + self.reset_state(ln) self.entry.in_doc_sect =3D False =20 # next line is always the function name @@ -1280,7 +1291,7 @@ class KernelDoc: if r.match(line): self.dump_section() self.entry.section =3D self.section_default - self.entry.new_start_line =3D line + self.entry.new_start_line =3D ln self.entry.contents =3D "" =20 if doc_sect.search(line): @@ -1618,7 +1629,9 @@ class KernelDoc: self.dump_section() self.output_declaration("doc", None, sectionlist=3Dself.entry.sectionlist, - sections=3Dself.entry.sections, module= =3Dself.config.modulename) + sections=3Dself.entry.sections, + section_start_lines=3Dself.entry.secti= on_start_lines, + module=3Dself.config.modulename) self.reset_state(ln) =20 elif doc_content.search(line): --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8EEE224634F; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=dbdN20jMoJkAXPGheObRtlCiJiqGp+2kUoU3rpga0nBdgSc8FtUNBtfjKoMUZ+nR3nTixzXzeO4GJoH7heWQFG2ym/yEHw7e62al9dgRE0ab9e4ICMBeQ5FfV+MPp4sFMmK14ueLWuSt7PKwDJ63VP5KzTpftnLfo3HjIeNTKJg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=AdyCuuoiDzM5JN3vT/qTkSEQUe1tw1eDWso5VHl1JSo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LVP45RuKQHaeQlEj+IW25s5nw1NGwW46iMu0nBQlc0AFpts3yc2rl+rpkbEBH/23VjWmlqtQnekmP2hMpucnTC72v4Xu4NgRaVXHZGW5cYbfrGGmY3YIVHX2PoSO2nLhr68xpa+bHfeTXB16nrHJnas8A1SAqzGnfRf/X1Bq0oI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IwV4W9Ur; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IwV4W9Ur" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 279E2C4CEDD; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=AdyCuuoiDzM5JN3vT/qTkSEQUe1tw1eDWso5VHl1JSo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IwV4W9Ura7fETii+nwWIxwDXMTqsO74p7XU7Wi+FmVB7ECIIAsxb/bP7sMcrqWYvv PFDih0KYAAYumhyg269+dHQwPQfOL+7REELMpoeXvuf/E9jOGDMhKgXiFM3imnrd2x v3ND+/w2A4g3JDPUrtacGwuElUMUyf1Uzb/hPYLJiwTEwCJU2Af8tdimf7H2BPFCHh mD2suKdUd8GJjLqvSK3slvcs/sriixjkJiNUzImpHgt0G+NnVibOc2Cu0Zv9BKlpLr M7KfD1LqPz7dCDU1wG1IQ4l3xEwRB9SPEZYO78dI0sE5xyaUVcmlnP/g7L/n0TA35a iyl+p7bLqLsUQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4m-1Wtb; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 21/39] scripts/kernel-doc.py: fix handling of doc output check Date: Mon, 24 Feb 2025 10:08:27 +0100 Message-ID: <0bc4f5860a633f177dbbafb0f42da601ad4935c3.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The filtering logic was seeking for the DOC name to check for symbols, but such data is stored only inside a section. Add it to the output_declaration, as it is quicker/easier to check the declaration name than to check inside each section. While here, make sure that the output for both ReST and man after filtering will be similar to what kernel-doc Perl version does. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_output.py | 29 ++++++++++++----------------- scripts/lib/kdoc/kdoc_parser.py | 3 ++- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index b9b39bc29463..6a392dad2e9d 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -121,13 +121,13 @@ class OutputFormat: if self.no_doc_sections: return False =20 + if name in self.nosymbol: + return False + if self.out_mode =3D=3D self.OUTPUT_ALL: return True =20 if self.out_mode =3D=3D self.OUTPUT_INCLUDE: - if name in self.nosymbol: - return False - if name in self.function_table: return True =20 @@ -153,15 +153,6 @@ class OutputFormat: =20 return False =20 - def check_function(self, fname, name, args): - return True - - def check_enum(self, fname, name, args): - return True - - def check_typedef(self, fname, name, args): - return True - def msg(self, fname, name, args): self.data =3D "" =20 @@ -305,7 +296,7 @@ class RestFormat(OutputFormat): for line in output.strip("\n").split("\n"): self.data +=3D self.lineprefix + line + "\n" =20 - def out_section(self, args, out_reference=3DFalse): + def out_section(self, args, out_docblock=3DFalse): """ Outputs a block section. =20 @@ -324,7 +315,7 @@ class RestFormat(OutputFormat): continue =20 if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: - if out_reference: + if out_docblock: self.data +=3D f".. _{section}:\n\n" =20 if not self.symbol: @@ -338,8 +329,7 @@ class RestFormat(OutputFormat): def out_doc(self, fname, name, args): if not self.check_doc(name): return - - self.out_section(args, out_reference=3DTrue) + self.out_section(args, out_docblock=3DTrue) =20 def out_function(self, fname, name, args): =20 @@ -582,8 +572,10 @@ class ManFormat(OutputFormat): =20 for line in contents.strip("\n").split("\n"): line =3D Re(r"^\s*").sub("", line) + if not line: + continue =20 - if line and line[0] =3D=3D ".": + if line[0] =3D=3D ".": self.data +=3D "\\&" + line + "\n" else: self.data +=3D line + "\n" @@ -593,6 +585,9 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 + if not self.check_doc(name): + return + self.data +=3D f'.TH "{module}" 9 "{module}" "{self.man_date}" "AP= I Manual" LINUX' + "\n" =20 for section in sectionlist: diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index 633c95164b0c..116289622f2c 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1197,6 +1197,7 @@ class KernelDoc: else: self.entry.section =3D doc_block.group(1) =20 + self.entry.identifier =3D self.entry.section self.state =3D self.STATE_DOCBLOCK return =20 @@ -1627,7 +1628,7 @@ class KernelDoc: =20 if doc_end.search(line): self.dump_section() - self.output_declaration("doc", None, + self.output_declaration("doc", self.entry.identifier, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, section_start_lines=3Dself.entry.secti= on_start_lines, --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AFEEE24A04D; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=UY6cjCFZhKAtk+a4YXXkNKDniMVdliJxNjNVTGFWSXEcMqZ6GqbGFCzw0iUZSDeqoWfLat0WphzKTg/714pTMuCHGxKc5bnKDDNKr3jjBJ11X7zX+3HeeiWKYMf71aYsy+JnWEITLzEKk90wffesBIQeAljDvnyLaXSywMY4y8E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=NkNgWuYdW6yz0X6IJafkeOcfMUGHd/fARN30vGbl73o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q4/WQsIFSYBiP8lckSQMBoxvCPoDMdUhWCLAItmffYDRDtKs88CR8eRFJZ4QnUFax+yUccNcGCGpRW7qsa7O1vjh08c33zmbGUHhPUyTtc3j/lxgZa/Dqa3GK6DyEn7QWHxmXrqAdiCG4IOrOg+p87hdNtAxLdPmhk+C8TThb7c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dv0+z3Nx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dv0+z3Nx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3ABE1C19423; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=NkNgWuYdW6yz0X6IJafkeOcfMUGHd/fARN30vGbl73o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dv0+z3NxqRu85FfNj/0yT+ObMfjemzT+w5W/raZSleZBUW7IgcVF5Ym3tLvIUoLaD XhbXf0EyO8QbzH0aSmU/Y9JP4085F5cUzI1msrGoWFIdEScIYhPnY8zS+d4G5QpxMI c55JpqvqE01K+7rPKz5lygO4cuXd219zEkmgywY7rojOsXyjQhVYoAoS0vfYSGvWzu 4DLWcIm6FmG49y4gaoTJDjrcXMX/3RaepX5OMu3norGEZvwMxL4R/+J2jh/kH+W9kS UFa5rN3Br5S75O3Dz+j0UE5kwg5sQcm1BUl22NbzeI733T/Nzp9ZjGzSeK1u4iMRXV oxXLJJcPCXELw== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4q-1deJ; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 22/39] scripts/kernel-doc.py: properly handle out_section for ReST Date: Mon, 24 Feb 2025 10:08:28 +0100 Message-ID: <166bb5d78d804371e548a758dfd143aff2077758.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" There is a difference at the way DOC sections are output with the include mode. Handle such difference properly. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_output.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 6a392dad2e9d..ca21cd856be4 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -314,12 +314,12 @@ class RestFormat(OutputFormat): if section in self.nosymbol: continue =20 - if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: - if out_docblock: + if out_docblock: + if not self.out_mode =3D=3D self.OUTPUT_INCLUDE: self.data +=3D f".. _{section}:\n\n" - - if not self.symbol: self.data +=3D f'{self.lineprefix}**{section}**\n\n' + else: + self.data +=3D f'{self.lineprefix}**{section}**\n\n' =20 self.print_lineno(section_start_lines.get(section, 0)) self.output_highlight(sections[section]) --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AFFF824A04F; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=a6cUdcRUpkRP0XQ99nYYln9bTwf7pUjoNilXGRUj8lGbZQ2QdlNWdjHxL/UXTR6YkBr7t09+sfPFbMjaY4z8AafSbNVgSRMvHUVBPmUk67NNR120/ITGcMDURHd7d094bky3vrwjcvv55+OoNgHHRKs13mumohWMOKMXFWMtwCY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=f3hhbbpUGqP6w/O5kQrl+GO4MbXaX4mT/0fXNELBwgs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rbcNDnwPqdphLkudKh4ohvdfyjIAYQ+aC0FTs7FhK6s1ZffYUd0qSmxgBVGcoZxP8W4vHDK0Y05NEu7rHAWBmnAUVikYE37aYUvNimGpKFQ/toAK2mfiRS7aVmfQX+HNjs5fTx5Cte1dygm+/Xwv1jhXPc5MDahM/ofJeY438rQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YB5y2++5; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YB5y2++5" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 42EFBC19424; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=f3hhbbpUGqP6w/O5kQrl+GO4MbXaX4mT/0fXNELBwgs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YB5y2++5kEkhN3MH3+2K+mEfRgleCCW9kWxGhma3hQUC0VZ/gL9Kox4f/CKtMhEIh alzztNiReSngs4HEIkE6PUyIF0Bxjtrc4LnfroxsvDuGBul2/NlbV2st7NNK3QyX3+ E6MG2ZPxgrtpYec13cIC6dyYoayAH1uqE92peuQhsh45RcSpfTi/zXFGxIib1e7VPH eJprxVZj/1xNIBU1g7d4p87zu9S//C24QqibIov2hyqoyE0iXjDZHJX9ljpL0xTmAz Y8WOEvxOiel3YkGPcbbj9xkPGzXMid2FKF/+Hk8R3vvy4ysCGItAES82CONedw9vbF zGP0yGMItyclg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4u-1kit; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 23/39] scripts/kernel-doc.py: postpone warnings to the output plugin Date: Mon, 24 Feb 2025 10:08:29 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" We don't want to have warnings displayed for symbols that weren't output. So, postpone warnings print to the output plugin, where symbol output is validated. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_output.py | 24 +++++++++++++++---- scripts/lib/kdoc/kdoc_parser.py | 41 ++++++++++++++++----------------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index ca21cd856be4..7aeaec884545 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -115,7 +115,16 @@ class OutputFormat: =20 return block =20 - def check_doc(self, name): + def out_warnings(self, args): + warnings =3D args.get('warnings', []) + + for warning, log_msg in warnings: + if warning: + self.config.log.warning(log_msg) + else: + self.config.log.info(log_msg) + + def check_doc(self, name, args): """Check if DOC should be output""" =20 if self.no_doc_sections: @@ -125,19 +134,22 @@ class OutputFormat: return False =20 if self.out_mode =3D=3D self.OUTPUT_ALL: + self.out_warnings(args) return True =20 if self.out_mode =3D=3D self.OUTPUT_INCLUDE: if name in self.function_table: + self.out_warnings(args) return True =20 return False =20 - def check_declaration(self, dtype, name): + def check_declaration(self, dtype, name, args): if name in self.nosymbol: return False =20 if self.out_mode =3D=3D self.OUTPUT_ALL: + self.out_warnings(args) return True =20 if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]: @@ -146,9 +158,11 @@ class OutputFormat: =20 if self.out_mode =3D=3D self.OUTPUT_INTERNAL: if dtype !=3D "function": + self.out_warnings(args) return True =20 if name not in self.function_table: + self.out_warnings(args) return True =20 return False @@ -162,7 +176,7 @@ class OutputFormat: self.out_doc(fname, name, args) return self.data =20 - if not self.check_declaration(dtype, name): + if not self.check_declaration(dtype, name, args): return self.data =20 if dtype =3D=3D "function": @@ -327,7 +341,7 @@ class RestFormat(OutputFormat): self.data +=3D "\n" =20 def out_doc(self, fname, name, args): - if not self.check_doc(name): + if not self.check_doc(name, args): return self.out_section(args, out_docblock=3DTrue) =20 @@ -585,7 +599,7 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - if not self.check_doc(name): + if not self.check_doc(name, args): return =20 self.data +=3D f'.TH "{module}" 9 "{module}" "{self.man_date}" "AP= I Manual" LINUX' + "\n" diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index 116289622f2c..a71145d531f2 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -130,23 +130,23 @@ class KernelDoc: # Place all potential outputs into an array self.entries =3D [] =20 - def show_warnings(self, dtype, declaration_name): # pylint: disable= =3DW0613 - """ - Allow filtering out warnings - """ - - # TODO: implement it - - return True - # TODO: rename to emit_message def emit_warning(self, ln, msg, warning=3DTrue): """Emit a message""" =20 + log_msg =3D f"{self.fname}:{ln} {msg}" + + if self.entry: + # Delegate warning output to output logic, as this way it + # will report warnings/info only for symbols that are output + + self.entry.warnings.append((warning, log_msg)) + return + if warning: - self.config.log.warning("%s:%d %s", self.fname, ln, msg) + self.config.log.warning(log_msg) else: - self.config.log.info("%s:%d %s", self.fname, ln, msg) + self.config.log.info(log_msg) =20 def dump_section(self, start_new=3DTrue): """ @@ -220,10 +220,9 @@ class KernelDoc: # For now, we're keeping the same name of the function just to make # easier to compare the source code of both scripts =20 - if "declaration_start_line" not in args: - args["declaration_start_line"] =3D self.entry.declaration_star= t_line - + args["declaration_start_line"] =3D self.entry.declaration_start_li= ne args["type"] =3D dtype + args["warnings"] =3D self.entry.warnings =20 # TODO: use colletions.OrderedDict =20 @@ -256,6 +255,8 @@ class KernelDoc: self.entry.struct_actual =3D "" self.entry.prototype =3D "" =20 + self.entry.warnings =3D [] + self.entry.parameterlist =3D [] self.entry.parameterdescs =3D {} self.entry.parametertypes =3D {} @@ -327,7 +328,7 @@ class KernelDoc: if param not in self.entry.parameterdescs and not param.startswith= ("#"): self.entry.parameterdescs[param] =3D self.undescribed =20 - if self.show_warnings(dtype, declaration_name) and "." not in = param: + if "." not in param: if decl_type =3D=3D 'function': dname =3D f"{decl_type} parameter" else: @@ -867,16 +868,14 @@ class KernelDoc: self.entry.parameterlist.append(arg) if arg not in self.entry.parameterdescs: self.entry.parameterdescs[arg] =3D self.undescribed - if self.show_warnings("enum", declaration_name): - self.emit_warning(ln, - f"Enum value '{arg}' not described i= n enum '{declaration_name}'") + self.emit_warning(ln, + f"Enum value '{arg}' not described in en= um '{declaration_name}'") member_set.add(arg) =20 for k in self.entry.parameterdescs: if k not in member_set: - if self.show_warnings("enum", declaration_name): - self.emit_warning(ln, - f"Excess enum value '%{k}' descripti= on in '{declaration_name}'") + self.emit_warning(ln, + f"Excess enum value '%{k}' description i= n '{declaration_name}'") =20 self.output_declaration('enum', declaration_name, enum=3Ddeclaration_name, --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B629024A054; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=mX02FZO0NJYZAiIy/fSC9IJ9Wzn8UUH5EQYxqoOuO0nFuhukELykUZjdKeTb0Obe0LapESqkw8mVXifwvoTWQfVmZR4gmKNgwOQX9/VTIrIWAwnizr0F2hIezqFdKy4lCKmUgE2lv7AXkdmGMCmXPRXLHuthXaXMIXIbMeobxqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=pOTBzlS7I7jZeyW/hygW6cvMdWH+uYsFy5DDt8PknnA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OyNv4M4DMtD+//J1KjmOvgGQZr6Dw61fL7RUoMa2yOdFjawL41ryARl/Gz9XiYbkbWlzsrFXmA6UirWIW3++peNMYITHPjLH/pFG/mCaeEwPuo1NchMnlSEz1HImIFRBMWse+bWw7QU3tIdPOjNoPsIqyYwSK1Cd9i2fLNM/LZA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fxmuszss; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fxmuszss" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 46D21C19425; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=pOTBzlS7I7jZeyW/hygW6cvMdWH+uYsFy5DDt8PknnA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fxmuszssznMv7P7YOO5J8q07gJP6bvjuQVxOGVT+x1T8N4nbPfZkqeu3nl/MNG8IP 6QcMq8c1Cty1Q6QJh0USRlw/M633YR4lJpHAL2h7uv2rn+01lH8V9cLB9/CmW3PMCr 1Wl8EL+UVm5ATsHK3QsvZBJ0lE8m70EqlPURMHI3WDXkfzKikPeGghd8dQgrOGBv/a +t1ev3eXLYKoSQL0mCsf5e2eeDIexm7prTzjgr5UVjChCLbPgwxzwFX9qHQ6YYJS2I eXGd66ikQBcAWYpTuYCEJBrvu0F7vY80MtuJ+eiHzlMYj7kvFyCiRQh2N5eMXlI2vb R9MflnkJTB6kQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p4y-1rQR; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 24/39] docs: add a .pylintrc file with sys path for docs scripts Date: Mon, 24 Feb 2025 10:08:30 +0100 Message-ID: <184b5b90b3ee06c50c05888b5df82abac4ea7ee0.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The docs scripts that are used by Documentation/sphinx are using scripts/lib/* directories to place classes that will be used by both extensions and scripts. When pylint is used, it needs to identify the path where such scripts are, otherwise it will bail out. Add a simple RC file placing the location of such files. Signed-off-by: Mauro Carvalho Chehab --- .pylintrc | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000000..30b8ae1659f8 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,2 @@ +[MASTER] +init-hook=3D'import sys; sys.path +=3D ["scripts/lib/kdoc", "scripts/lib/a= bi"]' --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B961624A059; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=MYtJQunr1t7dEeIepsG21znRQLbo7T98Fu3E0vX6snpyXcy0lzKb9Py9Hnz9vz6gfzxIPRpJpk8QsFnrlIp3c4tnJOJ6xxGeOJ6YQDdUKtqbbhz7mScqShSxy4MpsBefAXJ1avIxSU89wo57Wc8RYyjunKbVwmio4DsvLFUufPI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=visFeBdmwWEXGD09mdOdO30j7sy6oK5qUh63NaeMIjk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qXVVLS40twCGnjFj1qYnSQam2AiT7OxHO4gUNp7CFksnw+jjSLkoUyRBTPZ/Yw5qfhhF3IXashddFRzRg612XvOBTOHgYYJP28Gbm5ga26NHEzCg8doa+D4PIVWcN00QsZnQiIM3+re0kpxI9Yq/aj9H5lmzHihtB+1mVNQPrMs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UYNoedzr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UYNoedzr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4CAF1C4CEF0; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=visFeBdmwWEXGD09mdOdO30j7sy6oK5qUh63NaeMIjk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UYNoedzrk4yrV8BKWtMh6MleKLHnYVN+RvNQjy643BNEae3Jxrr6d6ScSfdmtY/oR Lp6qEOtUAI9Hi87Ms0G4YI8Tn9K82cUaQv1T1GzZoUC641o31tT1A7FQ+rR6lil4na BQ8CQdC8QzF9YshTHKm4ocSLfDgoPzJ1ina35cn8a6xa2qqHBlLWi1dlpOSGtwHpuW 02AnxSFYndaazymwY5Jjn3XJEB/NNhUY+II5bi0PsIc/hMfh95gaUxWpE8aLzh2UFO iEK9sa3aFx783TXoczEHSr+fl6Kla2K3/E/AqiE4NDfU3zXDr9IRrJkV9kSBAlM1RH M9/QTfHeSCHKg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p52-1yTx; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , Kees Cook , linux-kernel@vger.kernel.org Subject: [PATCH v2 25/39] docs: sphinx: kerneldoc: verbose kernel-doc command if V=1 Date: Mon, 24 Feb 2025 10:08:31 +0100 Message-ID: <299b7351d1f395031c79521cf3503e47baa1d4a1.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" It is useful to know what kernel-doc command was used during document build time, as it allows one to check the output the same way as Sphinx extension does. Signed-off-by: Mauro Carvalho Chehab --- Documentation/sphinx/kerneldoc.py | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/sphinx/kerneldoc.py b/Documentation/sphinx/kerne= ldoc.py index 39ddae6ae7dd..d206eb2be10a 100644 --- a/Documentation/sphinx/kerneldoc.py +++ b/Documentation/sphinx/kerneldoc.py @@ -43,6 +43,29 @@ from sphinx.util import logging =20 __version__ =3D '1.0' =20 +def cmd_str(cmd): + """ + Helper function to output a command line that can be used to produce + the same records via command line. Helpful to debug troubles at the + script. + """ + + cmd_line =3D "" + + for w in cmd: + if w =3D=3D "" or " " in w: + esc_cmd =3D "'" + w + "'" + else: + esc_cmd =3D w + + if cmd_line: + cmd_line +=3D " " + esc_cmd + continue + else: + cmd_line =3D esc_cmd + + return cmd_line + class KernelDocDirective(Directive): """Extract kernel-doc comments from the specified file""" required_argument =3D 1 @@ -57,6 +80,7 @@ class KernelDocDirective(Directive): } has_content =3D False logger =3D logging.getLogger('kerneldoc') + verbose =3D 0 =20 def run(self): env =3D self.state.document.settings.env @@ -65,6 +89,13 @@ class KernelDocDirective(Directive): filename =3D env.config.kerneldoc_srctree + '/' + self.arguments[0] export_file_patterns =3D [] =20 + verbose =3D os.environ.get("V") + if verbose: + try: + self.verbose =3D int(verbose) + except ValueError: + pass + # Tell sphinx of the dependency env.note_dependency(os.path.abspath(filename)) =20 @@ -104,6 +135,9 @@ class KernelDocDirective(Directive): =20 cmd +=3D [filename] =20 + if self.verbose >=3D 1: + print(cmd_str(cmd)) + try: self.logger.verbose("calling kernel-doc '%s'" % (" ".join(cmd)= )) =20 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA47024A06E; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=LBs4QqaRTP3IOk889nmHp7VvOBdyhG4CymyJgkXrPg1H7S1yUifsSMp7MAq9aIJKJ0+6pGKMutV8O6VOFc2KWyLuEuOW5IINWZoxw9uk39ZCu0WHe+9GUVvqHbtGhh1ra0j9DxILXYZkNNeZnKgRYctvjPV83TzZ6xOtLFGKUY4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=eHvxtpg5BcZjO8Wx7MxCSiuYJ025kjhp7csGrstaq+8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BOBZkuGP9CiFVM/FUjsSHVCtLqIGnsl/e56Ov///WFN1Fv8VYkYjes3rAdMnwIHdUL0q5AYz0RjnN+jg0lG7edIZtOGSm3DpKECMeeh/p5IaQ5n8BjhwvhCaQ4h4EdFPeePIQDuadUhVMsSyHOnGgVmh6EpuloW1xYkoqIDJgwA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hIzOAyNR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hIzOAyNR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5E1D1C4AF0F; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=eHvxtpg5BcZjO8Wx7MxCSiuYJ025kjhp7csGrstaq+8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hIzOAyNRO8+easKwXcS05uOtYtJnTHBBWFLpiNlAs5UvytYWn6cZO3Y7bhVyqHISa 7JFlcLRh36k3qyfkOCV/ppnxcZV7+4HEozZc+/iJmCLfo+HFVwAwC3bpZM6bIOvi++ 9Llc/UNSaAwYTzXUCweyEqSwCuhoN+L7k5i3z8j15Yhz5YkVsWmkDR+tdoaEJ66zOY uluvw/17fyFLIfdacYYSnQSrk3nlVt1mowzH8Zavcrcv88phRfWuKP1aycO2kWhVe6 MduYoApakXGUUEK8+Fj1Jw8SnvXzQU/2A5UVZf1V5VTA78Vn8WFb5D+XqyUz0cavh6 lHZ0xvNr9StNg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p56-25Ff; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , Kees Cook , linux-kernel@vger.kernel.org Subject: [PATCH v2 26/39] docs: sphinx: kerneldoc: ignore "\" characters from options Date: Mon, 24 Feb 2025 10:08:32 +0100 Message-ID: <3f2738d2e077232f5ee4c7a9f15ba6a59bdeab72.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Documentation/driver-api/infiniband.rst has a kernel-doc tag with "\" characters at the end: .. kernel-doc:: drivers/infiniband/ulp/iser/iscsi_iser.c :functions: iscsi_iser_pdu_alloc iser_initialize_task_headers \ iscsi_iser_task_init iscsi_iser_mtask_xmit iscsi_iser_task_xmit \ iscsi_iser_cleanup_task iscsi_iser_check_protection \ iscsi_iser_conn_create iscsi_iser_conn_bind \ iscsi_iser_conn_start iscsi_iser_conn_stop \ iscsi_iser_session_destroy iscsi_iser_session_create \ iscsi_iser_set_param iscsi_iser_ep_connect iscsi_iser_ep_poll \ iscsi_iser_ep_disconnect This is not handled well, as the "\" strings will be just stored inside Sphinx options. While the actual problem deserves being fixed, better to relax the keneldoc.py extension to silently strip "\" from the end of strings, as otherwise this may cause troubles when preparing arguments to be executed by kernel-doc. Signed-off-by: Mauro Carvalho Chehab --- Documentation/sphinx/kerneldoc.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/sphinx/kerneldoc.py b/Documentation/sphinx/kerne= ldoc.py index d206eb2be10a..344789ed9ea2 100644 --- a/Documentation/sphinx/kerneldoc.py +++ b/Documentation/sphinx/kerneldoc.py @@ -118,6 +118,10 @@ class KernelDocDirective(Directive): identifiers =3D self.options.get('identifiers').split() if identifiers: for i in identifiers: + i =3D i.rstrip("\\").strip() + if not i: + continue + cmd +=3D ['-function', i] else: cmd +=3D ['-no-doc-sections'] @@ -126,9 +130,17 @@ class KernelDocDirective(Directive): no_identifiers =3D self.options.get('no-identifiers').split() if no_identifiers: for i in no_identifiers: + i =3D i.rstrip("\\").strip() + if not i: + continue + cmd +=3D ['-nosymbol', i] =20 for pattern in export_file_patterns: + pattern =3D pattern.rstrip("\\").strip() + if not pattern: + continue + for f in glob.glob(env.config.kerneldoc_srctree + '/' + patter= n): env.note_dependency(os.path.abspath(f)) cmd +=3D ['-export-file', f] --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA51524A06F; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=kWOj8I60jeNLR+IDoFaFg14CI3pb6v0nYK4lhfu7TbWIt2xp9JfRXi/AZAWADmKSUiRk5YLsEiFyuyf6+frsqycTO/FKwT7e2Mpiyl/LILGYs0GReLjfuycujqiGnJCC1EvjgUDe5xlhwg4lgqlKgr8FDvlryLk5xKc2Dy3pWx8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=nv2FWo3LGyCll6dWzmoWsroR9XM0kzMdOA2DYqUHwCw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EriE5YuF1IHSV0nqgwiGBOKsYw2IK8akdYS7x6pUGImMpwqFInqO5JmCnTrPfjcCGn4ss9o/8RQYf0VWtkFGbaAJIRaeh6vHlvF7yvZvrCKf6jMAdJ0eWZQ8r2wvkKW0JFwlVgWzdZOWok98Hm8tzv5CTtBKyeD9cDb9z4pArhY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=al+Zq2Id; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="al+Zq2Id" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5CB1BC2BCAF; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=nv2FWo3LGyCll6dWzmoWsroR9XM0kzMdOA2DYqUHwCw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=al+Zq2Id6hZVKqX6SFl78E7mwKEUDlYGzUQ751uJ5CxgoxU1b8tM7dSa1cQQSbmIR YcZCatMiC/qnIJSlaOlBtfkJBtDwK4tqZ28GGV3n2sCLQ5NcEFqadt7zH8w3cicJo9 v+yT7VQbObQvYWn3nhfE7jgq4xhL+Xmnl6KnyEKmElWRNTLeRnRKMhfC1gB4era1zg l65LQFizPFyTCtawJkc3Ki3hIQ0tDB1mxSZaU3FCQvWB3loYcLqeF1Yyz9gw577cNs D/9F2yja4dTV5SQUpTMbbvZO5PuRqHxQct9CRaK5MiT5FIDkKZ1DNvdTVFACBPSgCO vDZr/pgpi/jfA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5A-2BzD; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 27/39] docs: sphinx: kerneldoc: use kernel-doc.py script Date: Mon, 24 Feb 2025 10:08:33 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Switch to the new version when producing documentation. Signed-off-by: Mauro Carvalho Chehab --- Documentation/Makefile | 2 +- Documentation/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index 63094646df28..c022b97c487e 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -60,7 +60,7 @@ endif #HAVE_LATEXMK # Internal variables. PAPEROPT_a4 =3D -D latex_paper_size=3Da4 PAPEROPT_letter =3D -D latex_paper_size=3Dletter -KERNELDOC =3D $(srctree)/scripts/kernel-doc +KERNELDOC =3D $(srctree)/scripts/kernel-doc.py KERNELDOC_CONF =3D -D kerneldoc_srctree=3D$(srctree) -D kerneldoc_bin=3D$= (KERNELDOC) ALLSPHINXOPTS =3D $(KERNELDOC_CONF) $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) ifneq ($(wildcard $(srctree)/.config),) diff --git a/Documentation/conf.py b/Documentation/conf.py index 3dad1f90b098..b126f6760b5f 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -540,7 +540,7 @@ pdf_documents =3D [ # kernel-doc extension configuration for running Sphinx directly (e.g. by = Read # the Docs). In a normal build, these are supplied from the Makefile via c= ommand # line arguments. -kerneldoc_bin =3D '../scripts/kernel-doc' +kerneldoc_bin =3D '../scripts/kernel-doc.py' kerneldoc_srctree =3D '..' =20 # ------------------------------------------------------------------------= ------ --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DC22424A070; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; cv=none; b=PjK9G5RExRFmINlFreAqHFoA5Fqbedsc23vdLT2oQy3SY/Do5TDdfj2NK2RutBlO0piN3CTSXriJUKgWnuremEYzbwMoV2KahWmdpJ4MvpRkY/jkWj53ILCX4A/0O0xtoARTvemoWve71ujcSPfx+xA/4T8YtHR0Vw/5mav+sRI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388143; c=relaxed/simple; bh=RPy+c9M9XnSF7fnvH42jTZeZw448FaaVZdh7+FOlT+M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TkK2nwwwFuzUEncBbwmTYVP0Z1bKSIklSRNciX6Nm8RM6QPVe4ALcFWtRO2pHhvEHXqZsyuKBmzvjUAwIBTtv4bq2E0XiIDmS+S1gSQ9knK3l2lvD9SfUQal+TQS5FAZX9t22wEmcqompVBqoZo+mac15cL9ChYzGMPQRhtKUro= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LZ8IKALf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LZ8IKALf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 62551C2BCB5; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=RPy+c9M9XnSF7fnvH42jTZeZw448FaaVZdh7+FOlT+M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LZ8IKALflLotBxCoEeyv2AbjhjosmzsfIY9bKWgjxDYBsYNB+GB0zQ3cJrZW7IuVa 7QgQ3JgO3hNPaJ/s0xz22XkXG1toLR2S7wUL9bBj/NQufW1cj9Yq8rpAq3D7/064hn FVofkdkWRHG1CBDspmhj7arlreXGMAwgyYwmHfQMnfHj4GcYeUur22p1TJtwi2Ab+0 J0fgpN8DYHWMSWcZSiTlmCmDy2LHTBQ59+67akbb6AGv/TynfqebfAXUx/9SkKx95b 2IsKdPVvddl+vRV3HMmmNeR2PoNPcFfR6vK+POJQ1xdtNQiO2XBA7xNIEu6js48Ypw momNAPq+0wdhQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5E-2J2i; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 28/39] scripts/kernel-doc.py: Set an output format for --none Date: Mon, 24 Feb 2025 10:08:34 +0100 Message-ID: <8c4b7ce0e79c20d6b5804e6e3cb86e3d47fc09f5.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Now that warnings output is deferred to the output plugin, we need to have an output style for none as well. So, use the OutputFormat base class on such cases. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_files.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index c215ae3047b8..957aaeaed7e6 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -19,6 +19,7 @@ from datetime import datetime from dateutil import tz =20 from kdoc_parser import KernelDoc +from kdoc_output import OutputFormat =20 =20 class GlobSourceFiles: @@ -137,6 +138,9 @@ class KernelFiles(): if not modulename: modulename =3D "Kernel API" =20 + if out_style is None: + out_style =3D OutputFormat() + dt =3D datetime.now() if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): # use UTC TZ --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E797124A072; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=s5TfLsfzB6jGGaB16RH5E8XqpJSuH/Kbrm77hDswXgK3Iy+TZdv1jbQvMQfIUCu2R7yr44EbfKld84GL3cfgWNwvnsOTr0KOXN9wyXLNDEcGeotq1GiZXAEI54XzC5Sui6x+LWHv4lNi5cccMp3nO9/EAg3nHUHEL+soja+Se5w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=6Zn2EKfxJ7Vkt0h37tV3CPuGsXDQh4z4UJHEkZnGtBU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LXYAm26vp9fdFiW2pt/OJJPK8co34sq1J2k4zZlO9YEbnGRq+gVkE1WdptJnCQbilBExk/lQL7CHcfmOZq0niA1mXNGFfGLNg0Sqmo9ss/C3HpJiv8R0EDjj1jzx8ujSMq7j8tywglv5s9L4+8mmRyrLARM5mGf6SVnYm8b/wPc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TZXKezhR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TZXKezhR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6D213C4CEE6; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=6Zn2EKfxJ7Vkt0h37tV3CPuGsXDQh4z4UJHEkZnGtBU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TZXKezhRCWrPHNJ6trrfgHP5zygH1/n/1mrk7C9h6dRpwIQYALoe9HPRl3kFolPi6 ILEYi/S3G4F7pkxUFFt5NQ1enQ1lSgc3LUb4I6DAZoxOIxAB8yTTxOBtg3MDlSwF5p E90YkCVlwELulaBELLNNLTMiS6mZfmQ5pWBm1kmF9LaMZ79Z3apQrs/akaPlgzAyC2 nsljM2WMvgk7F9wazacDFx6lWN0Kx6VGXVgvOIaEgreRgb20DL6Ydovr1D0AHjO4fZ Gq3fec9mSHEHBBbI627I4uzyNuGk2gjFBOUtVazoJqMcAFpefsFu5TwoTZWCn9zX6/ TKTEvqW+ysbuQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5J-2PsT; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 29/39] scripts/kernel-doc.py: adjust some coding style issues Date: Mon, 24 Feb 2025 10:08:35 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Make pylint happier by adding some missing documentation and addressing a couple of pylint warnings. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 2 ++ scripts/lib/kdoc/kdoc_files.py | 4 +-- scripts/lib/kdoc/kdoc_output.py | 51 ++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index d700451e9541..daae2b4f3307 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -90,6 +90,8 @@ neither here nor at the original Perl script. =20 =20 class MsgFormatter(logging.Formatter): + """Helper class to format warnings on a similar way to kernel-doc.pl""" + def format(self, record): record.levelname =3D record.levelname.capitalize() return logging.Formatter.format(self, record) diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 957aaeaed7e6..e1ed2f6dae94 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -3,8 +3,6 @@ # Copyright(c) 2025: Mauro Carvalho Chehab . # SPDX-License-Identifier: GPL-2.0 =20 -# TODO: implement warning filtering - """ Parse lernel-doc tags on multiple kernel source files. """ @@ -127,7 +125,7 @@ class KernelFiles(): def __init__(self, verbose=3DFalse, out_style=3DNone, werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, wcontents_before_sections=3DFalse, - logger=3DNone, modulename=3DNone, export_file=3DNone): + logger=3DNone, modulename=3DNone): """ Initialize startup variables and parse all files """ diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 7aeaec884545..97ec671e0b6e 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 -# pylint: disable=3DC0301,R0911,R0912,R0913,R0914,R0915,R0917 +# pylint: disable=3DC0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917 # Copyright(c) 2025: Mauro Carvalho Chehab . # SPDX-License-Identifier: GPL-2.0 =20 -# TODO: implement warning filtering - """ Implement output filters to print kernel-doc documentation. =20 @@ -51,6 +49,11 @@ type_member_func =3D type_member + Re(r"\(\)", cache=3DF= alse) =20 =20 class OutputFormat: + """ + Base class for OutputFormat. If used as-is, it means that only + warnings will be displayed. + """ + # output mode. OUTPUT_ALL =3D 0 # output all symbols and doc sections OUTPUT_INCLUDE =3D 1 # output only specified symbols @@ -74,6 +77,10 @@ class OutputFormat: self.data =3D "" =20 def set_config(self, config): + """ + Setup global config variables used by both parser and output. + """ + self.config =3D config =20 def set_filter(self, export, internal, symbol, nosymbol, function_tabl= e, @@ -116,6 +123,10 @@ class OutputFormat: return block =20 def out_warnings(self, args): + """ + Output warnings for identifiers that will be displayed. + """ + warnings =3D args.get('warnings', []) =20 for warning, log_msg in warnings: @@ -145,6 +156,11 @@ class OutputFormat: return False =20 def check_declaration(self, dtype, name, args): + """ + Checks if a declaration should be output or not based on the + filtering criteria. + """ + if name in self.nosymbol: return False =20 @@ -168,6 +184,10 @@ class OutputFormat: return False =20 def msg(self, fname, name, args): + """ + Handles a single entry from kernel-doc parser + """ + self.data =3D "" =20 dtype =3D args.get('type', "") @@ -202,24 +222,24 @@ class OutputFormat: return None =20 # Virtual methods to be overridden by inherited classes + # At the base class, those do nothing. def out_doc(self, fname, name, args): - pass + """Outputs a DOC block""" =20 def out_function(self, fname, name, args): - pass + """Outputs a function""" =20 def out_enum(self, fname, name, args): - pass + """Outputs an enum""" =20 def out_typedef(self, fname, name, args): - pass + """Outputs a typedef""" =20 def out_struct(self, fname, name, args): - pass - + """Outputs a struct""" =20 class RestFormat(OutputFormat): - # """Consts and functions used by ReST output""" + """Consts and functions used by ReST output""" =20 highlights =3D [ (type_constant, r"``\1``"), @@ -264,6 +284,11 @@ class RestFormat(OutputFormat): self.data +=3D f".. LINENO {ln}\n" =20 def output_highlight(self, args): + """ + Outputs a C symbol that may require being converted to ReST using + the self.highlights variable + """ + input_text =3D args output =3D "" in_literal =3D False @@ -578,6 +603,10 @@ class ManFormat(OutputFormat): self.man_date =3D dt.strftime("%B %Y") =20 def output_highlight(self, block): + """ + Outputs a C symbol that may require being highlighted with + self.highlights variable using troff syntax + """ =20 contents =3D self.highlight_block(block) =20 @@ -600,7 +629,7 @@ class ManFormat(OutputFormat): sections =3D args.get('sections', {}) =20 if not self.check_doc(name, args): - return + return =20 self.data +=3D f'.TH "{module}" 9 "{module}" "{self.man_date}" "AP= I Manual" LINUX' + "\n" =20 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ED34224A07C; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=CZX60vt8pBFZfSvCwenp+J/mw5nuLwftHTRu76Zvm+m9DxK+TBUV8pSZ+Q6qMWVqnJ2R6Tr8XD9FeWZqL1yQHI06ebBhT3ZUS9+W+KbLdDq741dkoXOFbJVX9hruJ2c/bjsOvI1rV9gIT/tkCivtMyAUQLltJ0bb4Y5YeRtitoQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=cgbg7nED/bJ/uBnZ2iDb/1x2SRXvvmDXvNCZuMR+Pis=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=F73Y15F1RdyIB6l6+Xtd03g+BHLatsqRDTOTHyBghsMN5gLp08mLQa0vAyrrTCA8WgiHdagL7mlL/Lq77rFo2FpspkcJS9OlaDEZ01c7P/XnT1s/8WEgjzH0YBDh+yaFo5T76p0eAqx94QpiePpNsxoN79dlsSe5jzBeNVtEETg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=k4muGANG; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="k4muGANG" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 767B6C4CEE8; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=cgbg7nED/bJ/uBnZ2iDb/1x2SRXvvmDXvNCZuMR+Pis=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k4muGANGC+6eAxs7YsqmhiQm/1Fdsp31mLBuKUKpr0iPfmw5ADIM/bHMBYPUyIaza lkqplnqjVgih4Bq0YsZSEn9UTBEkRyLnR+ZDd/VtTpfE9pdt/PP0BN0ULYPfkodDVZ pW3xb4u2vF1vqqbm4CzTMIg5oUglwTr9+CNKoejDqyu8Na4kZApCgqROgGRMYCbxTW e3B+JsBGmv6FwaDKHvQvpqhoXNQoTcKLJiG85VXHxj4GrCb2yESe8AUBuA3bjGGjDY tog2yhGv03C3FTlkb2upjK3bMr/9ASsQFT6lw0MyLqhVmr8sMeCebYYHavhOtsix8y vjSAlQ7n24JDA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5N-2Ws9; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 30/39] scripts/lib/kdoc/kdoc_parser.py: fix Python compat with < v3.13 Date: Mon, 24 Feb 2025 10:08:36 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" - str.replace count was introduced only in Python 3.13; - before Python 3.13, f-string dict arguments can't use the same delimiter of the main string. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_output.py | 8 ++++---- scripts/lib/kdoc/kdoc_parser.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 97ec671e0b6e..df3c15bb1c10 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -645,16 +645,16 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - self.data +=3D f'.TH "{args['function']}" 9 "{args['function']}" "= {self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" + self.data +=3D f'.TH "{args["function"]}" 9 "{args["function"]}" "= {self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" =20 self.data +=3D ".SH NAME\n" self.data +=3D f"{args['function']} \\- {args['purpose']}\n" =20 self.data +=3D ".SH SYNOPSIS\n" if args.get('functiontype', ''): - self.data +=3D f'.B "{args['functiontype']}" {args['function']= }' + "\n" + self.data +=3D f'.B "{args["functiontype"]}" {args["function"]= }' + "\n" else: - self.data +=3D f'.B "{args['function']}' + "\n" + self.data +=3D f'.B "{args["function"]}' + "\n" =20 count =3D 0 parenth =3D "(" @@ -695,7 +695,7 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - self.data +=3D f'.TH "{args['module']}" 9 "enum {args['enum']}" "{= self.man_date}" "API Manual" LINUX' + "\n" + self.data +=3D f'.TH "{args["module"]}" 9 "enum {args["enum"]}" "{= self.man_date}" "API Manual" LINUX' + "\n" =20 self.data +=3D ".SH NAME\n" self.data +=3D f"enum {args['enum']} \\- {args['purpose']}\n" diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index a71145d531f2..51ac2d69a587 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1455,9 +1455,9 @@ class KernelDoc: =20 r =3D Re(r'long\s+(sys_.*?),') if r.search(proto): - proto =3D proto.replace(',', '(', count=3D1) + proto =3D Re(',').sub('(', proto, count=3D1) elif is_void: - proto =3D proto.replace(')', '(void)', count=3D1) + proto =3D Re(r'\)').sub('(void)', proto, count=3D1) =20 # Now delete all of the odd-numbered commas in the proto # so that argument types & names don't have a comma between them --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2236A24BBFC; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=OiTGVb9O6rkfIPd6wSQBhGWWVBwufMkk5KCv19WP7DRFc95EIyP1u64zRuZaLxNecYfw2a7IoUMJRLgDtP1+2R6z1/kfVwAUiqKh0LqFJTM0HK/bgh+vbEtRwE3katYHybLlrSd3PtgsG4KxWzY6b9EGaOPDNaVD7njT2crHbm8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=4i9tMFsRvYQf8CGO56ooCSwnWGYo0A/a2tTzIVmQWtE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cCcxY3sHq+k6qZC/JmYmv5zeFP9wBtiYtlOqoKzUHU2lCGcFjESnbPmN5yyG1nBOnDWtsnq9Qt0GlL5qiPC31O2VfZXvXvUe/q6/tH6x2GMAoU8lYrPm32dUSsaXq6NIpBdxQPb3AZ7AsCGEDBRYR4LaFfrgNDBiyAEGkmBWN/M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ms3OBXsq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ms3OBXsq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7FFB0C4CEF9; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=4i9tMFsRvYQf8CGO56ooCSwnWGYo0A/a2tTzIVmQWtE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ms3OBXsqm2yisM/rJhvonJ1uqy3UA5V+Dicp/c47x5pE11GmIp/b/Ycws+BTedQzb k8u61FIA8ScasigdqiWTJFvdKBRpjzy7ImvDVo0xNmCrn0NBPiLCA9Z9vjApeQE69z aj7giQ5NrcZcyp4hfYm58jV2uLjlty0lVnmTw5eMaHowlz6eXppdYwVG/m8sFWzUNM 1HT/+1pK/bEGDpP8tNHZAjTXJlz+ZW7eVgLCIukbM3yhVUVxU8m3H3yWLE76h8rtH/ s1PBU5ARD+XceaVeAKsusoLGGBI0JWXsTUurpM/fqsD56G+14mQ4JDrfe84x9i/INF 7YQmkjJsOveCQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5S-2dft; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 31/39] scripts/kernel-doc.py: move modulename to man class Date: Mon, 24 Feb 2025 10:08:37 +0100 Message-ID: <096291e4eaddd666de7a6178d340f575d60ed9d9.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Only man output requires a modulename. Move its definition to the man class. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 6 +++--- scripts/lib/kdoc/kdoc_files.py | 6 +----- scripts/lib/kdoc/kdoc_output.py | 12 ++++++------ scripts/lib/kdoc/kdoc_parser.py | 9 +-------- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index daae2b4f3307..064106c18d8b 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -111,6 +111,7 @@ def main(): help=3D"Enable debug messages") =20 parser.add_argument("-M", "-modulename", "--modulename", + default=3D"Kernel API", help=3D"Allow setting a module name at the output.= ") =20 parser.add_argument("-l", "-enable-lineno", "--enable_lineno", @@ -198,7 +199,7 @@ def main(): logger.addHandler(handler) =20 if args.man: - out_style =3D ManFormat() + out_style =3D ManFormat(modulename=3Dargs.modulename) elif args.none: out_style =3D None else: @@ -207,8 +208,7 @@ def main(): kfiles =3D KernelFiles(verbose=3Dargs.verbose, out_style=3Dout_style, werror=3Dargs.werror, wreturn=3Dargs.wreturn, wshort_desc=3Dargs.wshort= _desc, - wcontents_before_sections=3Dargs.wcontents_before= _sections, - modulename=3Dargs.modulename) + wcontents_before_sections=3Dargs.wcontents_before= _sections) =20 kfiles.parse(args.files, export_file=3Dargs.export_file) =20 diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index e1ed2f6dae94..a2417cafb1c8 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -125,7 +125,7 @@ class KernelFiles(): def __init__(self, verbose=3DFalse, out_style=3DNone, werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, wcontents_before_sections=3DFalse, - logger=3DNone, modulename=3DNone): + logger=3DNone): """ Initialize startup variables and parse all files """ @@ -133,9 +133,6 @@ class KernelFiles(): if not verbose: verbose =3D bool(os.environ.get("KBUILD_VERBOSE", 0)) =20 - if not modulename: - modulename =3D "Kernel API" - if out_style is None: out_style =3D OutputFormat() =20 @@ -167,7 +164,6 @@ class KernelFiles(): self.config.wreturn =3D wreturn self.config.wshort_desc =3D wshort_desc self.config.wcontents_before_sections =3D wcontents_before_sections - self.config.modulename =3D modulename =20 self.config.function_table =3D set() self.config.source_map =3D {} diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index df3c15bb1c10..13a74a687f89 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -584,7 +584,7 @@ class ManFormat(OutputFormat): ) blankline =3D "" =20 - def __init__(self): + def __init__(self, modulename): """ Creates class variables. =20 @@ -593,6 +593,7 @@ class ManFormat(OutputFormat): """ =20 super().__init__() + self.modulename =3D modulename =20 dt =3D datetime.now() if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): @@ -624,14 +625,13 @@ class ManFormat(OutputFormat): self.data +=3D line + "\n" =20 def out_doc(self, fname, name, args): - module =3D args.get('module') sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 if not self.check_doc(name, args): return =20 - self.data +=3D f'.TH "{module}" 9 "{module}" "{self.man_date}" "AP= I Manual" LINUX' + "\n" + self.data +=3D f'.TH "{self.modulename}" 9 "{self.modulename}" "{s= elf.man_date}" "API Manual" LINUX' + "\n" =20 for section in sectionlist: self.data +=3D f'.SH "{section}"' + "\n" @@ -695,7 +695,7 @@ class ManFormat(OutputFormat): sectionlist =3D args.get('sectionlist', []) sections =3D args.get('sections', {}) =20 - self.data +=3D f'.TH "{args["module"]}" 9 "enum {args["enum"]}" "{= self.man_date}" "API Manual" LINUX' + "\n" + self.data +=3D f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "= {self.man_date}" "API Manual" LINUX' + "\n" =20 self.data +=3D ".SH NAME\n" self.data +=3D f"enum {args['enum']} \\- {args['purpose']}\n" @@ -725,7 +725,7 @@ class ManFormat(OutputFormat): self.output_highlight(sections[section]) =20 def out_typedef(self, fname, name, args): - module =3D args.get('module') + module =3D self.modulename typedef =3D args.get('typedef') purpose =3D args.get('purpose') sectionlist =3D args.get('sectionlist', []) @@ -741,7 +741,7 @@ class ManFormat(OutputFormat): self.output_highlight(sections.get(section)) =20 def out_struct(self, fname, name, args): - module =3D args.get('module') + module =3D self.modulename struct_type =3D args.get('type') struct_name =3D args.get('struct') purpose =3D args.get('purpose') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index 51ac2d69a587..0c0fa10b942b 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -801,7 +801,6 @@ class KernelDoc: =20 self.output_declaration(decl_type, declaration_name, struct=3Ddeclaration_name, - module=3Dself.entry.modulename, definition=3Ddeclaration, parameterlist=3Dself.entry.parameterlist, parameterdescs=3Dself.entry.parameterdescs, @@ -879,7 +878,6 @@ class KernelDoc: =20 self.output_declaration('enum', declaration_name, enum=3Ddeclaration_name, - module=3Dself.config.modulename, parameterlist=3Dself.entry.parameterlist, parameterdescs=3Dself.entry.parameterdescs, parameterdesc_start_lines=3Dself.entry.par= ameterdesc_start_lines, @@ -1051,7 +1049,6 @@ class KernelDoc: self.output_declaration(decl_type, declaration_name, function=3Ddeclaration_name, typedef=3DTrue, - module=3Dself.config.modulename, functiontype=3Dreturn_type, parameterlist=3Dself.entry.parameterli= st, parameterdescs=3Dself.entry.parameterd= escs, @@ -1066,7 +1063,6 @@ class KernelDoc: self.output_declaration(decl_type, declaration_name, function=3Ddeclaration_name, typedef=3DFalse, - module=3Dself.config.modulename, functiontype=3Dreturn_type, parameterlist=3Dself.entry.parameterli= st, parameterdescs=3Dself.entry.parameterd= escs, @@ -1113,7 +1109,6 @@ class KernelDoc: self.output_declaration(decl_type, declaration_name, function=3Ddeclaration_name, typedef=3DTrue, - module=3Dself.entry.modulename, functiontype=3Dreturn_type, parameterlist=3Dself.entry.parameterli= st, parameterdescs=3Dself.entry.parameterd= escs, @@ -1141,7 +1136,6 @@ class KernelDoc: =20 self.output_declaration('typedef', declaration_name, typedef=3Ddeclaration_name, - module=3Dself.entry.modulename, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, section_start_lines=3Dself.entry.secti= on_start_lines, @@ -1630,8 +1624,7 @@ class KernelDoc: self.output_declaration("doc", self.entry.identifier, sectionlist=3Dself.entry.sectionlist, sections=3Dself.entry.sections, - section_start_lines=3Dself.entry.secti= on_start_lines, - module=3Dself.config.modulename) + section_start_lines=3Dself.entry.secti= on_start_lines) self.reset_state(ln) =20 elif doc_content.search(line): --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 07DBA24BBE9; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=i9x4wqtlF0tO+0q+FGZSeRgTKB4rGQyTi/q3IOef3OOhXjsNi4yipTLn0jsfAhidKOH45bEDQEknvLzGONZE4DETBcdTFhmRebnpFvVzXv1To5MxBXmbMFEb1u3yPlXtcukKqTYDaGfugOXBHrlk0+xidWz7sBT5YxmmfZJcrFk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=7R366oYKmHOPpm8w8SRMstX38EO00rzyOjDKwrc/HvM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nklvPXcZqQLZp5WmF6cRgmlGwwH5Exb9u3hDMD6VgUN6cgBNrMqKMtbpZwmaMHLNI0rvP1qk8tdd6ykksZYgAsjFpGlBWTpM3HVkMiSvVFPBeCRibbV2U/qZUvGddel91QvOV2jKizShVjgSbZRkBzyy6ElXT5ZILW4t1nnmX3g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lSJPSms3; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lSJPSms3" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 87E09C2BCB9; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=7R366oYKmHOPpm8w8SRMstX38EO00rzyOjDKwrc/HvM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lSJPSms3Ep2i557KE8/hclS9V7fRG56iKieWL8elcnDGR0cY/XiNgbtKbefz4PcKG l48Rz4GU3chECWP/oFb2TBpF7RU3zAv6fQ70G3x6iXFb2r81GwGkuO48d6sEEQxfhK 95HI2cigsEcFUnyhhxwsIQJ8PILP/gs6FMj7xzVflyBtYaFPRan7+iCA5FaV3wUjR3 FQy2MuampgGzkf1XXSyYlyx+kbZxhnTEdWMxukoxWgFno72a2FdPwI8vtdvDjr0jV5 wFS5DRfvplT+VgUCvXRiJ6hV5+qjYjVlUIi65WKCujzV4vI8HBQGbECCz3JN+iG5mI y/tKhj08I95zg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5W-2kh5; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 32/39] scripts/kernel-doc.py: properly handle KBUILD_BUILD_TIMESTAMP Date: Mon, 24 Feb 2025 10:08:38 +0100 Message-ID: <8a8b63cfbb14a98d8bd1790d502b73162079789e.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The logic that handles KBUILD_BUILD_TIMESTAMP is wrong, and adds a dependency of a third party module (dateutil). Fix it. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_files.py | 9 --------- scripts/lib/kdoc/kdoc_output.py | 28 +++++++++++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index a2417cafb1c8..19fe9c6f5352 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -12,9 +12,6 @@ import logging import os import re import sys -from datetime import datetime - -from dateutil import tz =20 from kdoc_parser import KernelDoc from kdoc_output import OutputFormat @@ -136,12 +133,6 @@ class KernelFiles(): if out_style is None: out_style =3D OutputFormat() =20 - dt =3D datetime.now() - if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): - # use UTC TZ - to_zone =3D tz.gettz('UTC') - dt =3D dt.astimezone(to_zone) - if not werror: kcflags =3D os.environ.get("KCFLAGS", None) if kcflags: diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 13a74a687f89..166fcabbb4cf 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -18,8 +18,6 @@ import os import re from datetime import datetime =20 -from dateutil import tz - from kdoc_parser import KernelDoc, type_param from kdoc_re import Re =20 @@ -584,6 +582,15 @@ class ManFormat(OutputFormat): ) blankline =3D "" =20 + date_formats =3D [ + "%a %b %d %H:%M:%S %Z %Y", + "%a %b %d %H:%M:%S %Y", + "%Y-%m-%d", + "%b %d %Y", + "%B %d %Y", + "%m %d %Y", + ] + def __init__(self, modulename): """ Creates class variables. @@ -595,11 +602,18 @@ class ManFormat(OutputFormat): super().__init__() self.modulename =3D modulename =20 - dt =3D datetime.now() - if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): - # use UTC TZ - to_zone =3D tz.gettz('UTC') - dt =3D dt.astimezone(to_zone) + dt =3D None + tstamp =3D os.environ.get("KBUILD_BUILD_TIMESTAMP") + if tstamp: + for fmt in self.date_formats: + try: + dt =3D datetime.strptime(tstamp, fmt) + break + except ValueError: + pass + + if not dt: + dt =3D datetime.now() =20 self.man_date =3D dt.strftime("%B %Y") =20 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0881824BBEA; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=hnwJAkkvnEJoZ9naJf+eAw2uDfYdQDKTSnx8smHPJ3U74PHkYhQ/q+KTbzJwKni0cZZdmhq7JQ6yx/L0XEYMRT/etDKkIMXEHeTrTw/+fuDJ1ot9Jvu23rCekp1HGWbsUIEAN7Z8YO82POPHIF2a0zUQttSuVEGqrfDLwPyZv7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=rBaRLeMqhVMdPKl/arUfcC4DaRWWHoHCgWfY19slLGU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dxzQhqnyMXwj/KCbDJC7kCc4aR0VIxFawn7TvehW3GcrP7kej2fJQixOKPsb7sp7LDMPiUgFwvoAwuK0m2+KN/fRy25EmI8qVYnGvHk9RYN4pTstYkKx2hBZ8CS9iEf+mLj2SBdQtQJaLL0S7vNteffpYyEpI4w/ZwAnxskMy7k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bUueVpxX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bUueVpxX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 87A1EC2BCB7; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=rBaRLeMqhVMdPKl/arUfcC4DaRWWHoHCgWfY19slLGU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bUueVpxX+YhqcRo5mGSCQWROzC9oMicdHC9DsZo7F1Ux8VixS4k5lCNh0osv59C/L Td9csDXGroKJHNHSaVqCCAhHA5tUbUoq56hJAnOrlKy0SCgONDrfCzblGzyiu+Fr0k ojoKxUww1Osbc3TzH1V469leXdi3zhS2LOlccsg8WYXqnqvhlgi4rZncGUrSCCen7q 8zOmX77kbm21T1rtVejw7YKhxGSPtgZVfqCxMewz8zcKQQ1Bq+YBqwqmzPsdl7Nzv9 ZwXq3utZEDIg3kKRiWnxQK+v3JE+OvkUCKxQaUBKub50Cmuv9KTDHp3E/NkYlQj7TP amgAVv/jn9JEw== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5b-2rRH; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 33/39] scripts/lib/kdoc/kdoc_parser.py: remove a python 3.9 dependency Date: Mon, 24 Feb 2025 10:08:39 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" str.removesuffix() was added on Python 3.9, but rstrip() actually does the same thing, as we just want to remove a single character. It is also shorter. So, use it. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index 0c0fa10b942b..feac699a3893 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1652,7 +1652,7 @@ class KernelDoc: # Group continuation lines on prototypes if self.state =3D=3D self.STATE_PROTO: if line.endswith("\\"): - prev +=3D line.removesuffix("\\") + prev +=3D line.rstrip("\\") cont =3D True =20 if not prev_ln: --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 11EC324BBEF; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=QYdE2N08Fum2N4kqqaWSqH+S2BNLAndxe2G0M4fSMxT8wFgPTqz0nm3evbxEu6yikUv/MFfS02viBxSrdoYkuO7nRK+MVjYoDzLBkkTZwFohTHiXbw7SutwCs8VQO4Z24Qw+UMR1joO85XJv8UOZnoXnkELnGiL08JrdRtlzWgE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=kkF62LinWQ1IkT47CUMzLT2NmYxiRtevRju9Af+8QaQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YhE6OcmE3CSAtUCGPQT+WUVvJe0ja601Hqtlm29k1C0tIDeE0bgXF3wyjV8OdE3wIziASVk1VAr3s5A380Ein1KnfaHx4aGwS4WpezcQ6V3GuP+X+VEef1aw++5Rb2eYsVOW/XApZ4czXcTVAXFl/eSN+HRtva57lA08/4CVvX8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FphbtCHM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FphbtCHM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9320EC4CEFC; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=kkF62LinWQ1IkT47CUMzLT2NmYxiRtevRju9Af+8QaQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FphbtCHMhvRpx5D3dDodEX/9NpLXJwgF2caoxEwWxh10p5F5E3hQTrYk1xdlw9565 HFZZOEA0SGuFY88tf7lgueWzRuWS4Kx15rsFcV88WaNg1Zv2Mmq3vBFKdV/4XC9Upu Ko2SREcTrrcMV8hlEe9yu4qZ7xX1FEX3QKjIMANgTSVPChmf6jB5R/SwqlbwIdpSoI q74TMzWeCmglOGf1/CLOt59pXotDDaDMBsbzVS2VUbowBm9ZnXhyFcL1KpJwY+XRy9 kkqo79aKdJSRUOHMA/XxTnGY/dHO71kmjRgaH5I81U/XaF7NM+Y7MRn8t15vKKjVbY 8ZlWn2eZjmM1g== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5f-2ySh; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 34/39] scripts/kernel-doc.py: Properly handle Werror and exit codes Date: Mon, 24 Feb 2025 10:08:40 +0100 Message-ID: <4d1cb32e557b82a53d91219494e2f019d5c26000.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" The original kernel-doc script has a logic to return warnings as errors, and to report the number of warnings found, if in verbose mode. Implement it to be fully compatible with the original script. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 18 ++++++++++++++++-- scripts/lib/kdoc/kdoc_files.py | 11 ++++++++++- scripts/lib/kdoc/kdoc_output.py | 8 +++----- scripts/lib/kdoc/kdoc_parser.py | 15 ++++++--------- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 064106c18d8b..7683fb4ffc59 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -3,8 +3,6 @@ # Copyright(c) 2025: Mauro Carvalho Chehab . # SPDX-License-Identifier: GPL-2.0 =20 -# TODO: implement warning filtering - """ kernel_doc =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D @@ -220,6 +218,22 @@ def main(): if msg: print(msg) =20 + error_count =3D kfiles.errors + if not error_count: + sys.exit(0) + + if args.werror: + print(f"{error_count} warnings as errors") + sys.exit(error_count) + + if args.verbose: + print(f"{error_count} errors") + + if args.none: + sys.exit(0) + + sys.exit(error_count) + =20 # Call main method if __name__ =3D=3D "__main__": diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 19fe9c6f5352..e130e5e32806 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -108,7 +108,7 @@ class KernelFiles(): KernelDoc.process_export(self.config.function_table, l= ine) =20 except IOError: - print(f"Error: Cannot open fname {fname}", fname=3Dsys.stderr) + self.config.log.error("Error: Cannot open fname %s", fname) self.config.errors +=3D 1 =20 def file_not_found_cb(self, fname): @@ -261,3 +261,12 @@ class KernelFiles(): fname, ln, dtype) if msg: yield fname, msg + + @property + def errors(self): + """ + Return a count of the number of warnings found, including + the ones displayed while interacting over self.msg. + """ + + return self.config.errors diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 166fcabbb4cf..d0cb7f511f87 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -127,11 +127,9 @@ class OutputFormat: =20 warnings =3D args.get('warnings', []) =20 - for warning, log_msg in warnings: - if warning: - self.config.log.warning(log_msg) - else: - self.config.log.info(log_msg) + for log_msg in warnings: + self.config.log.warning(log_msg) + self.config.errors +=3D 1 =20 def check_doc(self, name, args): """Check if DOC should be output""" diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index feac699a3893..6cebc32e18f2 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -136,17 +136,18 @@ class KernelDoc: =20 log_msg =3D f"{self.fname}:{ln} {msg}" =20 + if not warning: + self.config.log.info(log_msg) + return + if self.entry: # Delegate warning output to output logic, as this way it # will report warnings/info only for symbols that are output =20 - self.entry.warnings.append((warning, log_msg)) + self.entry.warnings.append(log_msg) return =20 - if warning: - self.config.log.warning(log_msg) - else: - self.config.log.info(log_msg) + self.config.log.warning(log_msg) =20 def dump_section(self, start_new=3DTrue): """ @@ -568,7 +569,6 @@ class KernelDoc: =20 if not members: self.emit_warning(ln, f"{proto} error: Cannot parse struct or = union!") - self.config.errors +=3D 1 return =20 if self.entry.identifier !=3D declaration_name: @@ -841,7 +841,6 @@ class KernelDoc: =20 if not members: self.emit_warning(ln, f"{proto}: error: Cannot parse enum!") - self.config.errors +=3D 1 return =20 if self.entry.identifier !=3D declaration_name: @@ -1143,7 +1142,6 @@ class KernelDoc: return =20 self.emit_warning(ln, "error: Cannot parse typedef!") - self.config.errors +=3D 1 =20 @staticmethod def process_export(function_table, line): @@ -1692,4 +1690,3 @@ class KernelDoc: self.process_docblock(ln, line) except OSError: self.config.log.error(f"Error: Cannot open file {self.fname}") - self.config.errors +=3D 1 --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1538224BBF2; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=CluUICEX8eHDBInpch8bX9q3ucwkHM3khECXbHW/XEJ3BY15jiz8BzkggHfG29fFkz8DdAvoKjW8zeC6aZTwmmzr/gsrZB0pyXDGR5XNxfsAcTZ2WplsOkg52ByVYpdgV1X4KlOJ3zWCdtM+X/GDJBmA/eC6Um6x2Klx7wfSlwo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=07FCqO/8Qxm5lSe7sCG8lMGcmip/PZdZVuFoGS9C284=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mvJKuDIgbXOzfpxQM6ksAw2pbxeTUsNR6LGKoxtT+U53HgACEjGcjgE9Kto9ejzpEhT+mNRFTkuuxZGNHzHCStA/+07eDJLUvcKIMnPuhPQNt2q7bOB4sYx3ICBzLn6Sw4LVW/Qv/N4ltecqLNpi96kAXje8pZqGwEd8Sov1Q7Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Eq5ynI/H; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Eq5ynI/H" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9F4D1C2BCC7; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=07FCqO/8Qxm5lSe7sCG8lMGcmip/PZdZVuFoGS9C284=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Eq5ynI/HGk1dxhEr8zv3/xWVea4TpFu3HvJ5N1q08cZ4pRMVAia2IRwrxqaoseNEN RgklGXBnC0iFL8v7MjW7uqUWFyAaCMqd9a504xOt05hWMaigjMdpG5MAqg1PoQWoqQ U/A10Ya/o2Xsxo15hcS9L+/Hinuja2HnpZ1GMAQhMtPN73g0dBVVLRv9Ig6DXwZEXl JgpmlWla4pApo+/tx7je9UG9EUp6swW3aDbNImrgZArtAyLjcgGcn5S/6c9ratmo5O WPqMHczHKHLMdrS6DMaUiBASTCf3i585Hqqer0J5hqlVr+2Ayzfj36vYUq5YoWAoI2 u2b5pGoKryaZw== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5j-35Dy; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 35/39] scripts/kernel-doc.py: some coding style cleanups Date: Mon, 24 Feb 2025 10:08:41 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Remove some warnings generated by pylint. Among them, cleanup TODO messages, removing the ones that were already handled, as now all TODOs that are required to be fully compatible with kernel-doc were already addressed. So, the existing TODOs are pointing possible future cleanups and improvements. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 10 +++++----- scripts/lib/kdoc/kdoc_files.py | 1 - scripts/lib/kdoc/kdoc_output.py | 1 + scripts/lib/kdoc/kdoc_parser.py | 30 +++++++----------------------- scripts/lib/kdoc/kdoc_re.py | 3 ++- 5 files changed, 15 insertions(+), 30 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 7683fb4ffc59..5e1e1839438c 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# pylint: disable=3DC0103, +# pylint: disable=3DC0103,R0915 # Copyright(c) 2025: Mauro Carvalho Chehab . # SPDX-License-Identifier: GPL-2.0 =20 @@ -167,7 +167,7 @@ def main(): =20 # Those are valid for all 3 types of filter parser.add_argument("-n", "-nosymbol", "--nosymbol", action=3D'append', - help=3DNOSYMBOL_DESC) + help=3DNOSYMBOL_DESC) =20 parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", action=3D'store_true', help=3D"Don't outputt DOC s= ections") @@ -211,9 +211,9 @@ def main(): kfiles.parse(args.files, export_file=3Dargs.export_file) =20 for t in kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.= export, - internal=3Dargs.internal, symbol=3Dargs.symbol, - nosymbol=3Dargs.nosymbol, - no_doc_sections=3Dargs.no_doc_sections): + internal=3Dargs.internal, symbol=3Dargs.symbol, + nosymbol=3Dargs.nosymbol, + no_doc_sections=3Dargs.no_doc_sections): msg =3D t[1] if msg: print(msg) diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index e130e5e32806..fc14bd6f9863 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -11,7 +11,6 @@ import argparse import logging import os import re -import sys =20 from kdoc_parser import KernelDoc from kdoc_output import OutputFormat diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index d0cb7f511f87..487068753b53 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -234,6 +234,7 @@ class OutputFormat: def out_struct(self, fname, name, args): """Outputs a struct""" =20 + class RestFormat(OutputFormat): """Consts and functions used by ReST output""" =20 diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index 6cebc32e18f2..cf4bf7cedcbc 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -130,7 +130,7 @@ class KernelDoc: # Place all potential outputs into an array self.entries =3D [] =20 - # TODO: rename to emit_message + # TODO: rename to emit_message after removal of kernel-doc.pl def emit_warning(self, ln, msg, warning=3DTrue): """Emit a message""" =20 @@ -157,19 +157,6 @@ class KernelDoc: name =3D self.entry.section contents =3D self.entry.contents =20 - # TODO: we can prevent dumping empty sections here with: - # - # if self.entry.contents.strip("\n"): - # if start_new: - # self.entry.section =3D self.section_default - # self.entry.contents =3D "" - # - # return - # - # But, as we want to be producing the same output of the - # venerable kernel-doc Perl tool, let's just output everything, - # at least for now - if type_param.match(name): name =3D type_param.group(1) =20 @@ -205,7 +192,7 @@ class KernelDoc: self.entry.section =3D self.section_default self.entry.contents =3D "" =20 - # TODO: rename it to store_declaration + # TODO: rename it to store_declaration after removal of kernel-doc.pl def output_declaration(self, dtype, name, **args): """ Stores the entry into an entry array. @@ -225,13 +212,13 @@ class KernelDoc: args["type"] =3D dtype args["warnings"] =3D self.entry.warnings =20 - # TODO: use colletions.OrderedDict + # TODO: use colletions.OrderedDict to remove sectionlist =20 sections =3D args.get('sections', {}) sectionlist =3D args.get('sectionlist', []) =20 # Drop empty sections - # TODO: improve it to emit warnings + # TODO: improve empty sections logic to emit warnings for section in ["Description", "Return"]: if section in sectionlist: if not sections[section].rstrip(): @@ -635,7 +622,9 @@ class KernelDoc: =20 # Replace macros # - # TODO: it is better to also move those to the NestedMatch log= ic, + # TODO: use NestedMatch for FOO($1, $2, ...) matches + # + # it is better to also move those to the NestedMatch logic, # to ensure that parenthesis will be properly matched. =20 (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),= r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), @@ -902,7 +891,6 @@ class KernelDoc: self.dump_struct(ln, prototype) return =20 - # TODO: handle other types self.output_declaration(self.entry.decl_type, prototype, entry=3Dself.entry) =20 @@ -1670,10 +1658,6 @@ class KernelDoc: self.st_inline_name[self.inline_= doc_state], line) =20 - # TODO: not all states allow EXPORT_SYMBOL*, so this - # can be optimized later on to speedup parsing - self.process_export(self.config.function_table, line) - # Hand this line to the appropriate state handler if self.state =3D=3D self.STATE_NORMAL: self.process_normal(ln, line) diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py index 45ddba8090e5..de362ec38db7 100755 --- a/scripts/lib/kdoc/kdoc_re.py +++ b/scripts/lib/kdoc/kdoc_re.py @@ -131,7 +131,8 @@ class NestedMatch: will ignore the search string. """ =20 - # TODO: + # TODO: make NestedMatch handle multiple match groups + # # Right now, regular expressions to match it are defined only up to # the start delimiter, e.g.: # --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1214824BBF0; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=A3VpHzd17A276aEJwo6zR6VByPlab7ZnXLdLvqnkhBK+TnjNH2bCIdj8MLsKg+X7kEVglrwFOHsm5sJ8tC0D+YH0VOMzXqQp2boimTd4TIDmkuFCVeViZMBW0YOHVc+sxA5ZYm6qakXE6PG4PiTnryb+IFbcuvhhrf2fmdORkP0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=dA/lwXoRISFanQzES1j4+YBcD/qyLfU1x/1xle+1MdY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rwEF6+gF6mRtrC6Tr1QPzngRCI/NhD9hgwVRsONWyEWtQNk68JOescXfwEXlosvpt4uNFh/j1YuuOsN5Yrzyl41Huov6QVhATu8bOb/5PkVGPusLh/RNIPt8AgRtSRUs2/nMhlU9RkU8s9b80T5pSm/r9WgmNE59xmcaZcnOvvo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=P1qu1IiW; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="P1qu1IiW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9A117C2BCC6; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=dA/lwXoRISFanQzES1j4+YBcD/qyLfU1x/1xle+1MdY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=P1qu1IiWGCGCgSi8NxNsQqFqHYTxZx5GQab2cZ8n4BrUUM/J+yZyeJIDaQNRAykcR bCh+6QTRQUonMyP4doygMsjjz7vu/1ehWsSq/eCA+qr0kSuCRaaV8LGHBtx9iCdJLs fe5R/Ij3TCxcKe0fN2jU9JvuU74Q9LhVQGe7XZiY2wfSbW2L2AK9ej3Z89AXUrTCzk p/fgr4lvxBd7lGj5a3Ti0uwwJtgqFau5dbjMsAT15QVPZTFVriAFZQ1awNYTuRiu13 Dxcw8InNDVPBYSAKF7VkM1s3Ei69ZX5aUelLsxPtkJ4z6NwN+wk1B0/nGAwZHXz7Sq e7oZXtBvNoGSA== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5n-3CKt; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 36/39] scripts/kernel-doc: switch to use kernel-doc.py Date: Mon, 24 Feb 2025 10:08:42 +0100 Message-ID: <67b7ece3fae241361e0ae2fdd1536b34dbacf8b2.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Now that all features are in place, change the kernel-doc alias to point to kernel-doc.py. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index f175155c1e66..3b6ef807791a 120000 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -1 +1 @@ -kernel-doc.pl \ No newline at end of file +kernel-doc.py \ No newline at end of file --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2045D24BBF7; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=C4kuJiO3UDkCmSIkEQ21xQRR2wcMykTmHWP7MuKuPwqjm0SGaUYk1uqQe+9XSLEcbUfMVyErEWyzXHy199KCIPIfI7SDbKNPjD8AcR+nQJUTTGFLsFP+pdpRngsTdxGEEcMPKpLqVBbPDYumFU0DPZasjK1wDk7ORsU1e+GYWyU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=eT42PMb7NLIYUy+TPfhbrtIZmR9In4PQ32hCJ1qwQcU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p9FfP7Qyyhf8H1hZZ8pt1WEV/4FLQ32/mCFdHHjQs3WhAFlAGtI1nwCo0XKXEdKeUI7nYrX01Uyfz8CBmzOQA7sKw/OTecatCWVcUd4utSDqT0aPA0hpKLT7pk7pDkHEs1oLhmIcOhUZtLuNH/AkgWcIfx70Ddpe9GWTNC3fy1A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EN+i1Vkq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EN+i1Vkq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A197DC4CEEC; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=eT42PMb7NLIYUy+TPfhbrtIZmR9In4PQ32hCJ1qwQcU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EN+i1Vkqf8dNxWk55wQuKMixRCxBwXjO1eYvgWzToAqoFqaWa9a/MhWCnRgpgl+JR rNYTQwyDhJgQvI4Z1I8yRw234yiw8unXvy/Y4peeOheNBXsVedjT4inn4mk0HrHsRM uFUclJK8LQLbfUS8XNCoqmcfKDTGY5wrovx6rB2QZl1++VVJArzei2EA3PQ+2uNVyd yVOVDTpOFa4B0wzF+8vsKagc/XRCb/yznKmt/NnhRcL0AsQSFfCaLKlRTnZ/o4lRwX FpGiqHDXmUjYYCFzXpvqy6kQomh4OcycYhFtraZp8nPfdHV63kkSt/lPLZ2GyygKXs 8AxsLi9/QN5zQ== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5r-3J3e; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 37/39] scripts/lib/kdoc/kdoc_files.py: allow filtering output per fname Date: Mon, 24 Feb 2025 10:08:43 +0100 Message-ID: <89c8d2f9f3afc56cde2132bc073cbc71b5ad64c1.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" For kerneldoc Sphinx extension, it is useful to display parsed results only from a single file. Change the logic at KernelFiles.msg() to allow such usage. Signed-off-by: Mauro Carvalho Chehab --- scripts/lib/kdoc/kdoc_files.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index fc14bd6f9863..8935a8603b44 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -94,7 +94,7 @@ class KernelFiles(): doc =3D KernelDoc(self.config, fname) doc.run() =20 - return doc + return doc.entries =20 def process_export_file(self, fname): """ @@ -172,7 +172,7 @@ class KernelFiles(): # Initialize internal variables =20 self.config.errors =3D 0 - self.results =3D [] + self.results =3D {} =20 self.files =3D set() self.export_files =3D set() @@ -188,13 +188,9 @@ class KernelFiles(): # avoid reporting errors multiple times =20 for fname in glob.parse_files(file_list, self.file_not_found_cb): - if fname in self.files: - continue - - res =3D self.parse_file(fname) - - self.results.append((res.fname, res.entries)) - self.files.add(fname) + if fname not in self.files: + self.results[fname] =3D self.parse_file(fname) + self.files.add(fname) =20 # If a list of export files was provided, parse EXPORT_SYMBOL* # from files that weren't fully parsed @@ -225,7 +221,8 @@ class KernelFiles(): return self.out_style.msg(fname, name, arg) =20 def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, - symbol=3DNone, nosymbol=3DNone, no_doc_sections=3DFalse): + symbol=3DNone, nosymbol=3DNone, no_doc_sections=3DFalse, + filenames=3DNone): """ Interacts over the kernel-doc results and output messages, returning kernel-doc markups on each interaction @@ -247,9 +244,12 @@ class KernelFiles(): function_table, enable_lineno, no_doc_sections) =20 - for fname, arg_tuple in self.results: + if not filenames: + filenames =3D sorted(self.results.keys()) + + for fname in filenames: msg =3D "" - for name, arg in arg_tuple: + for name, arg in self.results[fname]: msg +=3D self.out_msg(fname, name, arg) =20 if msg is None: --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 55D0E24BC1D; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=Ev79LJIRkfBXMKg/nM6MK+e+O7OZgYxrw545a0AFYCd/yT27KEJDv4zt0uuGB0KgYD64GEc3IHJDKg7tOR6HlhnBHe39HAvATGS0X6EZEz6p5eHYVjoUc/N9qoQP38JJus/j9zUxQV6ZJfsqhQdpXOZ1MYlzJ3YF158TSGZEdRM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=+EJ3SDteq48iBweClenCe9Dykw6QAlbaFdmLf8wgy00=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZbOlpqnSL4J5BBiEbydHV/77rjL71nXwXDMvGgXhw321LuvMVpLRL+XeRnUtdI3GRd/elo2LN6cgiWR4Tf3oJkgKsFCIWBNk9wakwhgZbXXrJo4eslS9qFF9i5QXkUvdPJTikkC5uAyib/mArcafzxzIHtSkAWnsFJtM0kNkh4I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ag3s7dYs; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ag3s7dYs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A5BFDC2BCF4; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388144; bh=+EJ3SDteq48iBweClenCe9Dykw6QAlbaFdmLf8wgy00=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ag3s7dYsXuag7uuTCvZ0n71LLyCTe5B6By8OwSe30B7t9bzlfPXt8QEuIgIN5Bxia 3rIkQ8I5sgdjPSPWExFflttj75o5JNcfo3eCkwxo1vVCruGKqXvM5FiJMUvSu4kpmW ELztUYy+s7mNKNo/bcPid2cqOSMpeEacptZZ+r9iGJQyfUfStpJBbiT8XIgmOvp9Mr BBoUBmldA1cdCjXPxt8t94TfHp3+PwQ0+0Ir0wnXZPWdYgywgio7EWUI1zE+67tOG6 Bt+EYv43uk+H/usL+yYd3vOsRXnH0WoEnmXEmC/R7V4FFA/JOy44dxEl5pFRXZ6nMA fXEuHy1EWoQsg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5v-3QD2; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 38/39] scripts/kernel_doc.py: better handle exported symbols Date: Mon, 24 Feb 2025 10:08:44 +0100 Message-ID: <8578e6ff3a639ca82dfd7300609391edcbc64889.1740387599.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Change the logic which detects internal/external symbols in a way that we can re-use it when calling via Sphinx extension. While here, remove an unused self.config var and let it clearer that self.config variables are read-only. This helps to allow handling multiple times in parallel if ever needed. Signed-off-by: Mauro Carvalho Chehab --- scripts/kernel-doc.py | 2 +- scripts/lib/kdoc/kdoc_files.py | 142 +++++++++++++++++--------------- scripts/lib/kdoc/kdoc_output.py | 9 +- scripts/lib/kdoc/kdoc_parser.py | 52 ++++++++++-- 4 files changed, 125 insertions(+), 80 deletions(-) diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 5e1e1839438c..eb308c938717 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -212,7 +212,7 @@ def main(): =20 for t in kfiles.msg(enable_lineno=3Dargs.enable_lineno, export=3Dargs.= export, internal=3Dargs.internal, symbol=3Dargs.symbol, - nosymbol=3Dargs.nosymbol, + nosymbol=3Dargs.nosymbol, export_file=3Dargs.expor= t_file, no_doc_sections=3Dargs.no_doc_sections): msg =3D t[1] if msg: diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 8935a8603b44..6da93febdb01 100755 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -67,6 +67,9 @@ class GlobSourceFiles: handling directories if any """ =20 + if not file_list: + return + for fname in file_list: if self.srctree: f =3D os.path.join(self.srctree, fname) @@ -83,40 +86,70 @@ class GlobSourceFiles: =20 class KernelFiles(): """ - Parse lernel-doc tags on multiple kernel source files. + Parse kernel-doc tags on multiple kernel source files. + + There are two type of parsers defined here: + - self.parse_file(): parses both kernel-doc markups and + EXPORT_SYMBOL* macros; + - self.process_export_file(): parses only EXPORT_SYMBOL* macros. """ =20 + def warning(self, msg): + """Ancillary routine to output a warning and increment error count= """ + + self.config.log.warning(msg) + self.errors +=3D 1 + + def error(self, msg): + """Ancillary routine to output an error and increment error count"= "" + + self.config.log.error(msg) + self.errors +=3D 1 + def parse_file(self, fname): """ Parse a single Kernel source. """ =20 + # Prevent parsing the same file twice if results are cached + if fname in self.files: + return + doc =3D KernelDoc(self.config, fname) - doc.run() + export_table, entries =3D doc.parse_kdoc() =20 - return doc.entries + self.export_table[fname] =3D export_table + + self.files.add(fname) + self.export_files.add(fname) # parse_kdoc() already check exp= orts + + self.results[fname] =3D entries =20 def process_export_file(self, fname): """ Parses EXPORT_SYMBOL* macros from a single Kernel source file. """ - try: - with open(fname, "r", encoding=3D"utf8", - errors=3D"backslashreplace") as fp: - for line in fp: - KernelDoc.process_export(self.config.function_table, l= ine) - - except IOError: - self.config.log.error("Error: Cannot open fname %s", fname) - self.config.errors +=3D 1 + + # Prevent parsing the same file twice if results are cached + if fname in self.export_files: + return + + doc =3D KernelDoc(self.config, fname) + export_table =3D doc.parse_export() + + if not export_table: + self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}") + export_table =3D set() + + self.export_table[fname] =3D export_table + self.export_files.add(fname) =20 def file_not_found_cb(self, fname): """ Callback to warn if a file was not found. """ =20 - self.config.log.error("Cannot find file %s", fname) - self.config.errors +=3D 1 + self.error(f"Cannot find file {fname}") =20 def __init__(self, verbose=3DFalse, out_style=3DNone, werror=3DFalse, wreturn=3DFalse, wshort_desc=3DFalse, @@ -146,7 +179,9 @@ class KernelFiles(): if kdoc_werror: werror =3D kdoc_werror =20 - # Set global config data used on all files + # Some variables are global to the parser logic as a whole as they= are + # used to send control configuration to KernelDoc class. As such, + # those variables are read-only inside the KernelDoc. self.config =3D argparse.Namespace =20 self.config.verbose =3D verbose @@ -155,27 +190,25 @@ class KernelFiles(): self.config.wshort_desc =3D wshort_desc self.config.wcontents_before_sections =3D wcontents_before_sections =20 - self.config.function_table =3D set() - self.config.source_map =3D {} - if not logger: self.config.log =3D logging.getLogger("kernel-doc") else: self.config.log =3D logger =20 - self.config.kernel_version =3D os.environ.get("KERNELVERSION", - "unknown kernel versio= n'") + self.config.warning =3D self.warning + self.config.src_tree =3D os.environ.get("SRCTREE", None) =20 + # Initialize variables that are internal to KernelFiles + self.out_style =3D out_style =20 - # Initialize internal variables - - self.config.errors =3D 0 + self.errors =3D 0 self.results =3D {} =20 self.files =3D set() self.export_files =3D set() + self.export_table =3D {} =20 def parse(self, file_list, export_file=3DNone): """ @@ -184,28 +217,11 @@ class KernelFiles(): =20 glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) =20 - # Prevent parsing the same file twice to speedup parsing and - # avoid reporting errors multiple times - for fname in glob.parse_files(file_list, self.file_not_found_cb): - if fname not in self.files: - self.results[fname] =3D self.parse_file(fname) - self.files.add(fname) - - # If a list of export files was provided, parse EXPORT_SYMBOL* - # from files that weren't fully parsed - - if not export_file: - return - - self.export_files |=3D self.files - - glob =3D GlobSourceFiles(srctree=3Dself.config.src_tree) + self.parse_file(fname) =20 for fname in glob.parse_files(export_file, self.file_not_found_cb): - if fname not in self.export_files: - self.process_export_file(fname) - self.export_files.add(fname) + self.process_export_file(fname) =20 def out_msg(self, fname, name, arg): """ @@ -222,32 +238,35 @@ class KernelFiles(): =20 def msg(self, enable_lineno=3DFalse, export=3DFalse, internal=3DFalse, symbol=3DNone, nosymbol=3DNone, no_doc_sections=3DFalse, - filenames=3DNone): + filenames=3DNone, export_file=3DNone): """ Interacts over the kernel-doc results and output messages, returning kernel-doc markups on each interaction """ =20 - function_table =3D self.config.function_table - - if symbol: - for s in symbol: - function_table.add(s) - - # Output none mode: only warnings will be shown - if not self.out_style: - return - self.out_style.set_config(self.config) =20 - self.out_style.set_filter(export, internal, symbol, nosymbol, - function_table, enable_lineno, - no_doc_sections) - if not filenames: filenames =3D sorted(self.results.keys()) =20 for fname in filenames: + function_table =3D set() + + if internal or export: + if not export_file: + export_file =3D [fname] + + for f in export_file: + function_table |=3D self.export_table[f] + + if symbol: + for s in symbol: + function_table.add(s) + + self.out_style.set_filter(export, internal, symbol, nosymbol, + function_table, enable_lineno, + no_doc_sections) + msg =3D "" for name, arg in self.results[fname]: msg +=3D self.out_msg(fname, name, arg) @@ -260,12 +279,3 @@ class KernelFiles(): fname, ln, dtype) if msg: yield fname, msg - - @property - def errors(self): - """ - Return a count of the number of warnings found, including - the ones displayed while interacting over self.msg. - """ - - return self.config.errors diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output= .py index 487068753b53..c07ca749a82f 100755 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -68,7 +68,7 @@ class OutputFormat: self.enable_lineno =3D None self.nosymbol =3D {} self.symbol =3D None - self.function_table =3D set() + self.function_table =3D None self.config =3D None self.no_doc_sections =3D False =20 @@ -93,10 +93,10 @@ class OutputFormat: =20 self.enable_lineno =3D enable_lineno self.no_doc_sections =3D no_doc_sections + self.function_table =3D function_table =20 if symbol: self.out_mode =3D self.OUTPUT_INCLUDE - function_table =3D symbol elif export: self.out_mode =3D self.OUTPUT_EXPORTED elif internal: @@ -107,8 +107,6 @@ class OutputFormat: if nosymbol: self.nosymbol =3D set(nosymbol) =20 - if function_table: - self.function_table =3D function_table =20 def highlight_block(self, block): """ @@ -128,8 +126,7 @@ class OutputFormat: warnings =3D args.get('warnings', []) =20 for log_msg in warnings: - self.config.log.warning(log_msg) - self.config.errors +=3D 1 + self.config.warning(log_msg) =20 def check_doc(self, name, args): """Check if DOC should be output""" diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser= .py index cf4bf7cedcbc..7c8fdb469676 100755 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1132,21 +1132,25 @@ class KernelDoc: self.emit_warning(ln, "error: Cannot parse typedef!") =20 @staticmethod - def process_export(function_table, line): + def process_export(function_set, line): """ process EXPORT_SYMBOL* tags =20 - This method is called both internally and externally, so, it - doesn't use self. + This method doesn't use any variable from the class, so declare it + with a staticmethod decorator. """ =20 + # Note: it accepts only one EXPORT_SYMBOL* per line, as having + # multiple export lines would violate Kernel coding style. + if export_symbol.search(line): symbol =3D export_symbol.group(2) - function_table.add(symbol) + function_set.add(symbol) + return =20 if export_symbol_ns.search(line): symbol =3D export_symbol_ns.group(2) - function_table.add(symbol) + function_set.add(symbol) =20 def process_normal(self, ln, line): """ @@ -1616,17 +1620,39 @@ class KernelDoc: elif doc_content.search(line): self.entry.contents +=3D doc_content.group(1) + "\n" =20 - def run(self): + def parse_export(self): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + export_table =3D set() + + try: + with open(self.fname, "r", encoding=3D"utf8", + errors=3D"backslashreplace") as fp: + + for line in fp: + self.process_export(export_table, line) + + except IOError: + return None + + return export_table + + def parse_kdoc(self): """ Open and process each line of a C source file. - he parsing is controlled via a state machine, and the line is pass= ed + The parsing is controlled via a state machine, and the line is pas= sed to a different process function depending on the state. The process function may update the state as needed. + + Besides parsing kernel-doc tags, it also parses export symbols. """ =20 cont =3D False prev =3D "" prev_ln =3D None + export_table =3D set() =20 try: with open(self.fname, "r", encoding=3D"utf8", @@ -1658,6 +1684,16 @@ class KernelDoc: self.st_inline_name[self.inline_= doc_state], line) =20 + # This is an optimization over the original script. + # There, when export_file was used for the same file, + # it was read twice. Here, we use the already-existing + # loop to parse exported symbols as well. + # + # TODO: It should be noticed that not all states are + # needed here. On a future cleanup, process export only + # at the states that aren't handling comment markups. + self.process_export(export_table, line) + # Hand this line to the appropriate state handler if self.state =3D=3D self.STATE_NORMAL: self.process_normal(ln, line) @@ -1674,3 +1710,5 @@ class KernelDoc: self.process_docblock(ln, line) except OSError: self.config.log.error(f"Error: Cannot open file {self.fname}") + + return export_table, self.entries --=20 2.48.1 From nobody Sun Feb 8 05:27:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4D1C224BC17; Mon, 24 Feb 2025 09:09:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; cv=none; b=ph785kG3PWY65bUIjDlAbYalv7QP1MlRUZt7pwGN0RwrUoQZ5mQWatduwUvATAL6ac7Xu+QxZTjC+RmAViqG6I99O5l3RJApvjAqaq5jHS/jtl7ZihDHfd6/ncnQVZ1uCp0sAjaGdQAHhhSlRO9AIR1l7GHDOAuOxf6LKCECtiI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740388144; c=relaxed/simple; bh=Y6ml/YtNkV6raER5boYfRIgmGo0cwmvZzsMQsLjY99A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pFarKa0qMePx+B2vq8P0A6hG8A6hvPFagDUkldkg5bDlvGSuPLhY1l+QLCNjj2iCbr+8vR1s49f7fENhXZQp+ZxO6ZjxYrEewxEQdNsxrntdriZSK1nU0TaZTDfbJSoykTfjQANjNagXvlU/iQpAQfp8FERHbjNkt9i6akPX6Ww= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IRI2W4iy; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IRI2W4iy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AFA46C4AF0C; Mon, 24 Feb 2025 09:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740388143; bh=Y6ml/YtNkV6raER5boYfRIgmGo0cwmvZzsMQsLjY99A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IRI2W4iyC77qHKENraoY4/pLd7Q9/QvbbVA4ErJmAKlMhfhIUCqqBrjsDQk0sBlNL 3lzzKVZG2gAEHieMbf7Rrn9qMypfEQJtATqb0cS1WIoWfhjWxD3QksLDiy8oHKNm2Y FLwPM6evbsR0l07retZRhVUZmYTjgA7E1XIDxvPpo2KJXhrW84oYLbpFbgMSYOgrk0 3MUqHx8FCS705oW5esuCdyDFMn1ywFx8vZrbS7wsxObIbdt7WI2B9peeFD+ErZiChG Dmp+H2C+m2DqjXFu0KCQ/2I7RB+prpmoDvjg8E7vrdCcMZK4AzrHcnGi7cp7IYKQMN Yg30Dr0uKIjGg== Received: from mchehab by mail.kernel.org with local (Exim 4.98) (envelope-from ) id 1tmUST-00000003p5z-3XLo; Mon, 24 Feb 2025 10:09:01 +0100 From: Mauro Carvalho Chehab To: Linux Doc Mailing List , Jonathan Corbet Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , Kees Cook , linux-kernel@vger.kernel.org Subject: [PATCH v2 39/39] docs: sphinx: kerneldoc: Use python class if available Date: Mon, 24 Feb 2025 10:08:45 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Better integrate with the new kernel-doc tool by calling the Python classes directly if KERNELDOC=3Dscripts/kernel-doc.py. This way, warnings won't be duplicated anymore, as files will be parsed only once. Signed-off-by: Mauro Carvalho Chehab --- Documentation/sphinx/kerneldoc.py | 137 +++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 12 deletions(-) diff --git a/Documentation/sphinx/kerneldoc.py b/Documentation/sphinx/kerne= ldoc.py index 344789ed9ea2..0758d49ee07c 100644 --- a/Documentation/sphinx/kerneldoc.py +++ b/Documentation/sphinx/kerneldoc.py @@ -41,7 +41,15 @@ import sphinx from sphinx.util.docutils import switch_source_input from sphinx.util import logging =20 +srctree =3D os.path.abspath(os.environ["srctree"]) +sys.path.insert(0, os.path.join(srctree, "scripts/lib/kdoc")) + +from kdoc_files import KernelFiles +from kdoc_output import RestFormat + __version__ =3D '1.0' +kfiles =3D None +logger =3D logging.getLogger('kerneldoc') =20 def cmd_str(cmd): """ @@ -79,14 +87,30 @@ class KernelDocDirective(Directive): 'functions': directives.unchanged, } has_content =3D False - logger =3D logging.getLogger('kerneldoc') verbose =3D 0 =20 - def run(self): + parse_args =3D {} + msg_args =3D {} + + def handle_args(self): + env =3D self.state.document.settings.env cmd =3D [env.config.kerneldoc_bin, '-rst', '-enable-lineno'] =20 filename =3D env.config.kerneldoc_srctree + '/' + self.arguments[0] + + # Arguments used by KernelFiles.parse() function + self.parse_args["file_list"] =3D [filename] + self.parse_args["export_file"] =3D [] + + # Arguments used by KernelFiles.msg() function + self.msg_args["enable_lineno"] =3D True + self.msg_args["export"] =3D False + self.msg_args["internal"] =3D False + self.msg_args["symbol"] =3D [] + self.msg_args["nosymbol"] =3D [] + self.msg_args["no_doc_sections"] =3D False + export_file_patterns =3D [] =20 verbose =3D os.environ.get("V") @@ -99,7 +123,8 @@ class KernelDocDirective(Directive): # Tell sphinx of the dependency env.note_dependency(os.path.abspath(filename)) =20 - tab_width =3D self.options.get('tab-width', self.state.document.se= ttings.tab_width) + self.tab_width =3D self.options.get('tab-width', + self.state.document.settings.tab= _width) =20 # 'function' is an alias of 'identifiers' if 'functions' in self.options: @@ -109,11 +134,15 @@ class KernelDocDirective(Directive): if 'export' in self.options: cmd +=3D ['-export'] export_file_patterns =3D str(self.options.get('export')).split= () + self.msg_args["export"] =3D True elif 'internal' in self.options: cmd +=3D ['-internal'] + self.msg_args["internal"] =3D True export_file_patterns =3D str(self.options.get('internal')).spl= it() elif 'doc' in self.options: - cmd +=3D ['-function', str(self.options.get('doc'))] + i =3D str(self.options.get('doc')) + cmd +=3D ['-function', i] + self.msg_args["symbol"].append(i) elif 'identifiers' in self.options: identifiers =3D self.options.get('identifiers').split() if identifiers: @@ -123,8 +152,10 @@ class KernelDocDirective(Directive): continue =20 cmd +=3D ['-function', i] + self.msg_args["symbol"].append(i) else: cmd +=3D ['-no-doc-sections'] + self.msg_args["no_doc_sections"] =3D True =20 if 'no-identifiers' in self.options: no_identifiers =3D self.options.get('no-identifiers').split() @@ -135,6 +166,7 @@ class KernelDocDirective(Directive): continue =20 cmd +=3D ['-nosymbol', i] + self.msg_args["nosymbol"].append(i) =20 for pattern in export_file_patterns: pattern =3D pattern.rstrip("\\").strip() @@ -144,14 +176,32 @@ class KernelDocDirective(Directive): for f in glob.glob(env.config.kerneldoc_srctree + '/' + patter= n): env.note_dependency(os.path.abspath(f)) cmd +=3D ['-export-file', f] + self.parse_args["export_file"].append(f) + + # As parse is cached, we need to pass the export_file again, + # to let the msg filter to do the right thing + + self.msg_args["export_file"] =3D self.parse_args["export_file"] + =20 cmd +=3D [filename] =20 + return cmd + + def run_cmd(self): + """ + Execute an external kernel-doc command. + """ + global logger + + env =3D self.state.document.settings.env + cmd =3D self.handle_args() + if self.verbose >=3D 1: print(cmd_str(cmd)) =20 try: - self.logger.verbose("calling kernel-doc '%s'" % (" ".join(cmd)= )) + logger.verbose("calling kernel-doc '%s'" % (" ".join(cmd))) =20 p =3D subprocess.Popen(cmd, stdout=3Dsubprocess.PIPE, stderr= =3Dsubprocess.PIPE) out, err =3D p.communicate() @@ -161,13 +211,34 @@ class KernelDocDirective(Directive): if p.returncode !=3D 0: sys.stderr.write(err) =20 - self.logger.warning("kernel-doc '%s' failed with return co= de %d" + logger.warning("kernel-doc '%s' failed with return code %d" % (" ".join(cmd), p.returncode)) return [nodes.error(None, nodes.paragraph(text =3D "kernel= -doc missing"))] elif env.config.kerneldoc_verbosity > 0: sys.stderr.write(err) =20 - lines =3D statemachine.string2lines(out, tab_width, convert_wh= itespace=3DTrue) + except Exception as e: # pylint: disable=3DW0703 + logger.warning("kernel-doc '%s' processing failed with: %s" % + (" ".join(cmd), str(e))) + return [nodes.error(None, nodes.paragraph(text =3D "kernel-doc= missing"))] + + node =3D nodes.section() + + filename =3D env.config.kerneldoc_srctree + '/' + self.arguments[0] + self.parse_msg(filename, node, out, cmd) + return node.children + + def parse_msg(self, filename, node, out, cmd): + """ + Handles a kernel-doc output for a given file + """ + + global logger + env =3D self.state.document.settings.env + + try: + lines =3D statemachine.string2lines(out, self.tab_width, + convert_whitespace=3DTrue) result =3D ViewList() =20 lineoffset =3D 0; @@ -183,20 +254,60 @@ class KernelDocDirective(Directive): result.append(line, doc + ": " + filename, lineoffset) lineoffset +=3D 1 =20 - node =3D nodes.section() self.do_parse(result, node) =20 - return node.children - except Exception as e: # pylint: disable=3DW0703 - self.logger.warning("kernel-doc '%s' processing failed with: %= s" % - (" ".join(cmd), str(e))) + logger.warning("kernel-doc '%s' processing failed with: %s" % + (cmd_str(cmd), str(e))) return [nodes.error(None, nodes.paragraph(text =3D "kernel-doc= missing"))] =20 + def run_kdoc(self, kfiles): + """ + Execute kernel-doc classes directly instead of running as a separa= te + command. + """ + + cmd =3D self.handle_args() + env =3D self.state.document.settings.env + + node =3D nodes.section() + + kfiles.parse(**self.parse_args) + filenames =3D self.parse_args["file_list"] + + for filename, out in kfiles.msg(**self.msg_args, filenames=3Dfilen= ames): + if self.verbose >=3D 1: + print(cmd_str(cmd)) + + self.parse_msg(filename, node, out, cmd) + + return node.children + + def run(self): + global kfiles + + if kfiles: + return self.run_kdoc(kfiles) + else: + return self.run_cmd() + def do_parse(self, result, node): with switch_source_input(self.state, result): self.state.nested_parse(result, 0, node, match_titles=3D1) =20 +def setup_kfiles(app): + global kfiles + + kerneldoc_bin =3D app.env.config.kerneldoc_bin + + if kerneldoc_bin and kerneldoc_bin.endswith("kernel-doc.py"): + print("Using Python kernel-doc") + out_style =3D RestFormat() + kfiles =3D KernelFiles(out_style=3Dout_style, logger=3Dlogger) + else: + print(f"Using {kerneldoc_bin}") + + def setup(app): app.add_config_value('kerneldoc_bin', None, 'env') app.add_config_value('kerneldoc_srctree', None, 'env') @@ -204,6 +315,8 @@ def setup(app): =20 app.add_directive('kernel-doc', KernelDocDirective) =20 + app.connect('builder-inited', setup_kfiles) + return dict( version =3D __version__, parallel_read_safe =3D True, --=20 2.48.1