From patchwork Mon Oct 30 21:31:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33134 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E5FFCC00144 for ; Mon, 30 Oct 2023 21:32:15 +0000 (UTC) Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) by mx.groups.io with SMTP id smtpd.web11.171848.1698701532657332157 for ; Mon, 30 Oct 2023 14:32:12 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=bLLQjynD; spf=pass (domain: gmail.com, ip: 209.85.128.51, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4079ed65582so36240145e9.1 for ; Mon, 30 Oct 2023 14:32:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701531; x=1699306331; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rUlTsoCRb7c61ci7ZVsjEUWCfwa7t3TQHbk7FZjqxn4=; b=bLLQjynDyUuRPPY4LzPw85xVNqn48m2G4VqlbS433YUcfT0HtQLXrUAQ4h45dvmK1z 6tJQg4rMc0iDERt+JKECn5HfZCLHRn0pURuceO3QX/IvXabrvCy4u19Wc3rQK12eLfTU uMF195zy6wCY2eEqaH/kAWDHMLHjvVtr5gQNCJJdwcyYb0ENAofVoI9f+zGNrUDB+Wr5 2t6FdWY3oFXFs/Lryar3o72qMwAe6+YD6xZUSeYTrGPb5TTnGKjRxgM6tadCqQmRL6oH UKxk2WwL7nfdDbwegJe/8ear4TC5opbHJuUDMx6b5024yWb1dF2BtWNEt2/psh2R4rfT krAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701531; x=1699306331; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=rUlTsoCRb7c61ci7ZVsjEUWCfwa7t3TQHbk7FZjqxn4=; b=fTzJqrFIpib5KEHyOG9maSp1xoMwSSNgB6Z8WjnT9ehloA7Kxpy06XhgKs1+O0lGor SyVcUngFzegzRq0b1tyvDp4+TStLOpvbFZf+Il1Gxrpm+Rb0UwxWIQGBfmcDBxldYwq+ kr3frUYuhXD9telzThXQgBwSkJTozzT0Xa2uLunWbZR2JxsQUjQYktS3s0ggbFR2c7Zu Mq/E3IpgDHu83g7ZgnRnSF/zeIhtEn9vD82ohhYZDRgfaN2XyzzIgkIn3AN1RjktZhHS 3YLdr7Kq01DQz5jcqvIZXWanefLIquZ7WcPKWejJDqXOste2xN+S8AJLFypuJRLRiHOL olFQ== X-Gm-Message-State: AOJu0Yx/+aCl0VcynI+U0t6AaaitBJk15kDdzI/UUfW9Ged7cCLsEyCn 8MHz6w6b4OF0d5/7102FBSo5NbeXxDg= X-Google-Smtp-Source: AGHT+IEqRNSIJb90fp4FMluQGuJe2CrwGuNq5gfM+P6f8iQfjmMb4XNGFp+6gn5/6GOy8CYsM46Imw== X-Received: by 2002:a05:600c:4ecb:b0:407:58e1:24ed with SMTP id g11-20020a05600c4ecb00b0040758e124edmr8664487wmq.39.1698701530675; Mon, 30 Oct 2023 14:32:10 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:10 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 1/8] vscode: add minimal configuration Date: Mon, 30 Oct 2023 22:31:58 +0100 Message-ID: <20231030213205.2824790-2-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:15 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189813 It is essential to configure VSCode indexer plugins to ignore the build folder of bitbake. Otherwise, the indexer plugins run with 100% CPU load until an OOM exception occurs. In practice, this makes VSCode more or less unusable for working with Yocto until a file like the one added by this commit is deployed before VSCode starts. From the user's point of view, it is not obvious why the system runs at 100% CPU load and eventually crashes. It is even more misleading that VSCode starts the indexers immediately, but does not stop or reconfigure them when the ignore list is updated. In practice, this means that every time the ignore list is changed, VSCode immediately starts indexing the build folder until the OOM exception stops it. Depending on the system's OOM handler, the entire build machine may crash. Particularly annoying is the Python plugin that ignores the general ignore list and requires an extra ignore section. The settings are suitable for workflows like bitbake, devtool modify, devtool reset. The settings are not intended to work on the source code of a recipe. It is assumed that a separate instance of VSCode is used per workspace folder. These per workspace instances can have different settings depending on the details of the sources that come with the recipe. The new devtool ide plugin will generate settings to match this. Signed-off-by: Adrian Freihofer --- .gitignore | 2 ++ .vscode/settings.json | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 8f48d452dab..f6ce090b5fc 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ _toaster_clones/ downloads/ sstate-cache/ toaster.sqlite +.vscode/ +vscode-bitbake-build/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..517a86d1bfa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + "files.watcherExclude": { + "**/.git/**": true, + "**/cache/**": true, + "**/tmp*/**": true, + "**/downloads/**": true, + "**/sstate-cache/**": true, + "**/vscode-bitbake-build/**": true, + "**/workspace/sources/**": true, + "**/workspace/attic/**": true + }, + "files.exclude": { + "**/.git/**": true, + "**/cache/**": true, + "**/tmp*/**": true, + "**/downloads/**": true, + "**/sstate-cache/**": true, + "**/vscode-bitbake-build/**": true, + "**/workspace/sources/**": true, + "**/workspace/attic/**": true + }, + "python.analysis.exclude": [ + "**/.git/**", + "**/cache/**", + "**/tmp*/**", + "**/downloads/**", + "**/sstate-cache/**", + "**/vscode-bitbake-build/**", + "**/workspace/sources/**", + "**/workspace/attic/**" + ] +} From patchwork Mon Oct 30 21:31:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33132 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D9499C41535 for ; Mon, 30 Oct 2023 21:32:15 +0000 (UTC) Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) by mx.groups.io with SMTP id smtpd.web10.171473.1698701533375043471 for ; Mon, 30 Oct 2023 14:32:13 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=O/YmzKwt; spf=pass (domain: gmail.com, ip: 209.85.128.47, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-40859c46447so33023135e9.1 for ; Mon, 30 Oct 2023 14:32:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701531; x=1699306331; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xSqyakKWBqyAbj66llYYtVkov0kjtdI2bTgvrxP9Dfo=; b=O/YmzKwtfqNFL8J089orFriH3ou7oGD+r/XPI43SxUrvjq4M7f49rIueFyQ6TDDV8d 0qtz3DQLb2HRq8WdCYvoEsZS0k13DWbctPP7JCKvyVZxcTmpiV/OQ0/GBJqnH6Gjzqqo ABhZYlZXFEwZWA5RDeYLr7plM5Ni7OJ8otmGGaSflWuhAE3o6BAJsMdF0fG62dCrx3Rr Ygehe24D01qoRstu8eE0Df3Am2d12f/FiXbaVhpp9enEmbFgGfR2nzd9giFyAXGvT8xW 2R2hVcHnvIPyOgh9+4oQSUAKP6IqZqLpy+rqc6T2n36ipRhN35P5Mtj/XkS+mnUqSwvr lPcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701531; x=1699306331; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xSqyakKWBqyAbj66llYYtVkov0kjtdI2bTgvrxP9Dfo=; b=ICD37IfCY1QIdSTLNWtOpvCj/Ga7KHYl7dNDaFXQMiKdaJd7uwTRQ0EXLZ1tphuOCF jgZI+sEm1C83ZBchrDGg2m8ycs0bQoi2eHs5Zm06fTIr3MKi/ZypSGkUqqf2vKKF5MtO zBBdux+7CXSalPLh2nK3HYq7W4s7FLvFOZXf3NyxpdDbyLq6OihTh5Xo9YHR4UtpZmEk eyVA7B/spGq7NRYXw8jneVGYvTwIuAMrmWWXkHRVbI0xj/vIhiGAbl0dYkGnn6Ga9WXj swfb7+lIdBIpt/O5MEAB9my/MQskV8f6S7MK5yKQ2xa0K79qN3aCWgtDg3WOf7IZJD+W DjOA== X-Gm-Message-State: AOJu0Yw+JO2DZBfZ5U9m//2qhh0bQZCEO6FTiDn75enqQNEHmv3e0hZq F9+dmp0hk4Eq3JtRghjaer5VVLbnLZg= X-Google-Smtp-Source: AGHT+IGCjaf9oNKpGWao0yl6QIMTu/B4VprQllMO0db2/lIOlm5l5QAgoYyN01dOk3/t4+sKF3WMvw== X-Received: by 2002:a05:600c:1d95:b0:409:375:5a44 with SMTP id p21-20020a05600c1d9500b0040903755a44mr9076476wms.24.1698701531284; Mon, 30 Oct 2023 14:32:11 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:10 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 2/8] cmake.bbclass: support qemu Date: Mon, 30 Oct 2023 22:31:59 +0100 Message-ID: <20231030213205.2824790-3-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:15 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189814 Define the CMAKE_CROSSCOMPILING_EMULATOR variable similar to what the meson bbclass does. This allows for example to execute cross compilied unit tests on the build machine. CMAKE_CROSSCOMPILING_EMULATOR is a semi colon separated list of paramters which could directly handle the -L and the -E parameters. Creating a wrapper script is not absolutely mandatory. But anyway lets do it similar to what the meson.bbclass does and also disable pseudo. Signed-off-by: Adrian Freihofer --- meta/classes-recipe/cmake.bbclass | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/meta/classes-recipe/cmake.bbclass b/meta/classes-recipe/cmake.bbclass index d978b889440..911c237a3fd 100644 --- a/meta/classes-recipe/cmake.bbclass +++ b/meta/classes-recipe/cmake.bbclass @@ -4,6 +4,13 @@ # SPDX-License-Identifier: MIT # +inherit qemu + +EXEWRAPPER_ENABLED:class-native = "False" +EXEWRAPPER_ENABLED:class-nativesdk = "False" +EXEWRAPPER_ENABLED ?= "${@bb.utils.contains('MACHINE_FEATURES', 'qemu-usermode', 'True', 'False', d)}" +DEPENDS:append = "${@' qemu-native' if d.getVar('EXEWRAPPER_ENABLED') == 'True' else ''}" + # Path to the CMake file to process. OECMAKE_SOURCEPATH ??= "${S}" @@ -156,6 +163,19 @@ EOF addtask generate_toolchain_file after do_patch before do_configure +cmake_do_generate_toolchain_file:append:class-target() { + if [ "${EXEWRAPPER_ENABLED}" = "True" ]; then + # Write out a qemu wrapper that will be used as exe_wrapper so that camake + # can run target helper binaries through that. This also allows to execute ctest. + qemu_binary="${@qemu_wrapper_cmdline(d, '${STAGING_DIR_HOST}', ['${STAGING_DIR_HOST}/${libdir}','${STAGING_DIR_HOST}/${base_libdir}'])}" + echo "#!/bin/sh" > "${WORKDIR}/cmake-qemuwrapper" + echo "$qemu_binary \"\$@\"" >> "${WORKDIR}/cmake-qemuwrapper" + chmod +x "${WORKDIR}/cmake-qemuwrapper" + echo "set( CMAKE_CROSSCOMPILING_EMULATOR ${WORKDIR}/cmake-qemuwrapper)" \ + >> ${WORKDIR}/toolchain.cmake + fi +} + CONFIGURE_FILES = "CMakeLists.txt *.cmake" do_configure[cleandirs] = "${@d.getVar('B') if d.getVar('S') != d.getVar('B') else ''}" From patchwork Mon Oct 30 21:32:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33133 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D1FF0C00142 for ; Mon, 30 Oct 2023 21:32:15 +0000 (UTC) Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) by mx.groups.io with SMTP id smtpd.web10.171474.1698701533900853805 for ; Mon, 30 Oct 2023 14:32:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=lBtlzC+3; spf=pass (domain: gmail.com, ip: 209.85.128.48, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-4083740f92dso39104115e9.3 for ; Mon, 30 Oct 2023 14:32:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701532; x=1699306332; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zxrbPbfpK1eBWsLP1yeiaQ0JzzZ3oWLRU89NKUZZJQg=; b=lBtlzC+3/9KjDxZwMlMtxVhrfTl5H5AtTeXPQm+GRUwW60n3gy7TtGTEQoJMrmSA1n bo5FS4fWB+xnnIqXRbwHPajSopc49mVojt4XVbsnmeAlQQYVtKvmhs4Yo/OVyg6PU2y0 4vtEu5dv0jXXgvdOqayuAlDKjQw8NDeN03k6gYKkkNs2RSEziuWtx64gyF7LFOkiZH35 pLgPvtDTghMpYIKX+xN1QyGhJ8CF4eWnDymgiUCZCWkpx5TSBcKku/yQoNC2y/B3V6S9 KYcqvsYYGusi0jZr2CPVjn2iz6oDFJ9Kb4vgv562grm4+TpyQ35l/6Tkxz6m8iPxl+ph JDXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701532; x=1699306332; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zxrbPbfpK1eBWsLP1yeiaQ0JzzZ3oWLRU89NKUZZJQg=; b=L94HOMJacxlr3I2SDxtnO30+B03aCrwQDesTJcIvGGZrtl1BEHveOpCjTCcjVyRLeP GrU4qurL+/bsFhjnn6TOFkc/wVw6PIrKMW9L9o0AXyvzsjv96Bl35uSlcIkQ2vMNTpd6 Oag5CaJ1dTEosWEueW76hoMlPch/tAAXjZHeeHAtkAFvkO2WKGNruhfHZIJKHE0HZUGc DANfDBfQHMi5218K93CJO94F3mg6shuW9MDR/6fpeS6caT5U+rmrciVpOC6+hor2DkBi OK4Lj6B5WScTKck1n0kYpPa7SdgkUIUkwrhdIhuuvghn6qgDS65oJRJehlP0R6v1NYd4 eRUg== X-Gm-Message-State: AOJu0YwvrYoLNFaZRgfYftRC+mC/9goO1KMiSC6dE5thnUDErBULDH6q 0qsDTYsyMuZMPiBisj/lGIxwmStMVcw= X-Google-Smtp-Source: AGHT+IGZ7hboAdSQP/t+3hlwEWcGdvVFZ/si+JkJLBfWi/9mnTDYtpNjPun22yTJgGEjW8B1Wr9HUA== X-Received: by 2002:a05:600c:3d8a:b0:406:8498:3d3a with SMTP id bi10-20020a05600c3d8a00b0040684983d3amr9075030wmb.14.1698701531879; Mon, 30 Oct 2023 14:32:11 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:11 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 3/8] image-combined-dbg: make this the default Date: Mon, 30 Oct 2023 22:32:00 +0100 Message-ID: <20231030213205.2824790-4-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:15 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189815 Remove the image-combined-dbg.bbclass and make this the default behavior for the rootfs-dbg. A rootfs-dbg with only debug symbols but no executable binaries also causes problems with gdb, which is probably the most common use case for the roofs-dbg. This change simplifies and improves the user experience for a slightly larger rootfs-dbg. If the rootfs-dbg contains a complete copy of the rootfs, it is also usable for booting the target device over the network. This in turn simplifies other use cases with e.g. the use of perf on a device booted over the network. Signed-off-by: Adrian Freihofer --- .../classes-recipe/image-combined-dbg.bbclass | 15 -------- meta/lib/oe/rootfs.py | 35 ++++--------------- scripts/crosstap | 28 +-------------- 3 files changed, 7 insertions(+), 71 deletions(-) delete mode 100644 meta/classes-recipe/image-combined-dbg.bbclass diff --git a/meta/classes-recipe/image-combined-dbg.bbclass b/meta/classes-recipe/image-combined-dbg.bbclass deleted file mode 100644 index 729313739c1..00000000000 --- a/meta/classes-recipe/image-combined-dbg.bbclass +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright OpenEmbedded Contributors -# -# SPDX-License-Identifier: MIT -# - -IMAGE_PREPROCESS_COMMAND:append = " combine_dbg_image" - -combine_dbg_image () { - if [ "${IMAGE_GEN_DEBUGFS}" = "1" -a -e ${IMAGE_ROOTFS}-dbg ]; then - # copy target files into -dbg rootfs, so it can be used for - # debug purposes directly - tar -C ${IMAGE_ROOTFS} -cf - . | tar -C ${IMAGE_ROOTFS}-dbg -xf - - fi -} diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py index 1a48ed10b3f..a1cc28a6dd0 100644 --- a/meta/lib/oe/rootfs.py +++ b/meta/lib/oe/rootfs.py @@ -111,40 +111,17 @@ class Rootfs(object, metaclass=ABCMeta): if gen_debugfs != '1': return + rootfs_orig = self.image_rootfs + '-orig' + bb.note(" Renaming the original rootfs...") try: - shutil.rmtree(self.image_rootfs + '-orig') + shutil.rmtree(rootfs_orig) except: pass - bb.utils.rename(self.image_rootfs, self.image_rootfs + '-orig') + bb.utils.rename(self.image_rootfs, rootfs_orig) bb.note(" Creating debug rootfs...") - bb.utils.mkdirhier(self.image_rootfs) - - bb.note(" Copying back package database...") - for path in package_paths: - bb.utils.mkdirhier(self.image_rootfs + os.path.dirname(path)) - if os.path.isdir(self.image_rootfs + '-orig' + path): - shutil.copytree(self.image_rootfs + '-orig' + path, self.image_rootfs + path, symlinks=True) - elif os.path.isfile(self.image_rootfs + '-orig' + path): - shutil.copyfile(self.image_rootfs + '-orig' + path, self.image_rootfs + path) - - # Copy files located in /usr/lib/debug or /usr/src/debug - for dir in ["/usr/lib/debug", "/usr/src/debug"]: - src = self.image_rootfs + '-orig' + dir - if os.path.exists(src): - dst = self.image_rootfs + dir - bb.utils.mkdirhier(os.path.dirname(dst)) - shutil.copytree(src, dst) - - # Copy files with suffix '.debug' or located in '.debug' dir. - for root, dirs, files in os.walk(self.image_rootfs + '-orig'): - relative_dir = root[len(self.image_rootfs + '-orig'):] - for f in files: - if f.endswith('.debug') or '/.debug' in relative_dir: - bb.utils.mkdirhier(self.image_rootfs + relative_dir) - shutil.copy(os.path.join(root, f), - self.image_rootfs + relative_dir) + shutil.copytree(rootfs_orig, self.image_rootfs, symlinks=True) bb.note(" Install complementary '*-dbg' packages...") self.pm.install_complementary('*-dbg') @@ -178,7 +155,7 @@ class Rootfs(object, metaclass=ABCMeta): bb.utils.rename(self.image_rootfs, self.image_rootfs + '-dbg') bb.note(" Restoring original rootfs...") - bb.utils.rename(self.image_rootfs + '-orig', self.image_rootfs) + bb.utils.rename(rootfs_orig, self.image_rootfs) def _exec_shell_cmd(self, cmd): try: diff --git a/scripts/crosstap b/scripts/crosstap index 5aa72f14d44..87dac33e064 100755 --- a/scripts/crosstap +++ b/scripts/crosstap @@ -170,18 +170,6 @@ class BitbakeEnv(object): return ret class ParamDiscovery(object): - SYMBOLS_CHECK_MESSAGE = """ -WARNING: image '%s' does not have dbg-pkgs IMAGE_FEATURES enabled and no -"image-combined-dbg" in inherited classes is specified. As result the image -does not have symbols for user-land processes DWARF based probes. Consider -adding 'dbg-pkgs' to EXTRA_IMAGE_FEATURES or adding "image-combined-dbg" to -USER_CLASSES. I.e add this line 'USER_CLASSES += "image-combined-dbg"' to -local.conf file. - -Or you may use IMAGE_GEN_DEBUGFS="1" option, and then after build you need -recombine/unpack image and image-dbg tarballs and pass resulting dir location -with --sysroot option. -""" def __init__(self, image): self.image = image @@ -204,8 +192,6 @@ with --sysroot option. self.staging_dir_native = None - self.image_combined_dbg = False - def discover(self): if self.image: benv_image = BitbakeEnv(self.image) @@ -248,10 +234,6 @@ with --sysroot option. (self.staging_dir_native ) = benv_systemtap.get_vars(["STAGING_DIR_NATIVE"]) - if self.inherit: - if "image-combined-dbg" in self.inherit.split(): - self.image_combined_dbg = True - def check(self, sysroot_option): ret = True if self.image_rootfs: @@ -280,10 +262,6 @@ with --sysroot option. if "dbg-pkgs" in image_features: dbg_pkgs_found = True - if not dbg_pkgs_found \ - and not self.image_combined_dbg: - print(ParamDiscovery.SYMBOLS_CHECK_MESSAGE % (self.image)) - if not ret: print("") @@ -310,10 +288,7 @@ with --sysroot option. stap.stap = self.staging_dir_native + "/usr/bin/stap" if not stap.sysroot: if self.image_rootfs: - if self.image_combined_dbg: - stap.sysroot = self.image_rootfs + "-dbg" - else: - stap.sysroot = self.image_rootfs + stap.sysroot = self.image_rootfs + "-dbg" stap.runtime = self.staging_dir_native + "/usr/share/systemtap/runtime" stap.tapset = self.staging_dir_native + "/usr/share/systemtap/tapset" stap.arch = self.__map_systemtap_arch() @@ -362,7 +337,6 @@ configuration is recommended: # enables symbol + target binaries rootfs-dbg in workspace IMAGE_GEN_DEBUGFS = "1" IMAGE_FSTYPES_DEBUGFS = "tar.bz2" -USER_CLASSES += "image-combined-dbg" # enables kernel debug symbols KERNEL_EXTRA_FEATURES:append = " features/debug/debug-kernel.scc" From patchwork Mon Oct 30 21:32:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33135 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4798C4167B for ; Mon, 30 Oct 2023 21:32:15 +0000 (UTC) Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) by mx.groups.io with SMTP id smtpd.web10.171475.1698701534399499878 for ; Mon, 30 Oct 2023 14:32:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=M5IHkxi4; spf=pass (domain: gmail.com, ip: 209.85.128.54, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-40842752c6eso39010645e9.1 for ; Mon, 30 Oct 2023 14:32:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701532; x=1699306332; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=D0qtTiiTupyyGMOCWyNE3lQftOrjLeNFlTAfbjafvsE=; b=M5IHkxi4CLq1hBUnk30YfKxsk7etsYMxv+zO7qB+M8GAStIelZB859Pe8iw5JXaLBn xkaunFWG2IUXVlDp5M6bnLzgGG9NXHIpGKY6DDoq7jbBFWFU9fnyW4mjZQ81cDG3MXTq d/lUV1c2rEjD0OYrQWD0tQGMsZtSns+vD6AgXju4z8K6HxptCrPzB6s5t/6RQiCR1ILE wAK0+2MSiucHSnk5z45mFTZFgjyIodMZ8ncEXoZ+r2P2EkMcGwqYdmO/dyuLGARcdjY+ a3rtY7gNNrVrngXQei4Jh79kp09IbH7kOj5y03+4n0el2TJwFh6C+HL3M7f2I3Tt4E9C Sivw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701532; x=1699306332; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=D0qtTiiTupyyGMOCWyNE3lQftOrjLeNFlTAfbjafvsE=; b=HWZRDiiVrrxb8JO9itWI3PGX3hiT7g64x99y7PirFoG8jlMH+f1ZXCqNNSpa9DHXCy FX0u+rOFFzD/KOX7POt7csyWSVw6x+DxogbJ5ho/h/t7XRFUOmGTC3TBToKuS+Dkltn8 1iBA4w2cqsztGEcFfWGVd//AH2XrRXq45cO4t3QGUNzAgZMXHPxzwdXFjN4XuO2WWWhA 2488CGWpLEG44e4yJZWkV+aE5X+Z6aHrkH+8M9/PJGGVArfhb3QKUUN8lutPaBDRRQ/7 fqcoRssCVPrAv4CBcuXfbHsdHjA838nx7cASTTEOpxFWrtsgGugslFVhTsWVteB1wtbU vBJA== X-Gm-Message-State: AOJu0Yw9xXKWhD2tlARill6I+h9NOu2T97wpiJrQpjLjni6X0ROcLJeT lAkfQVo99Kx89ApBdYkx9pIkRUtxy0k= X-Google-Smtp-Source: AGHT+IG36kZozEq+5GpDHqXmOBYQegxkukC75dcaezRP0roreQN1+C8BkKHe9lAeY0eB7Wv7py+rcQ== X-Received: by 2002:a05:600c:1992:b0:406:53c0:3c71 with SMTP id t18-20020a05600c199200b0040653c03c71mr9351380wmq.37.1698701532502; Mon, 30 Oct 2023 14:32:12 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:12 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 4/8] tests: add a C++ example recipe Date: Mon, 30 Oct 2023 22:32:01 +0100 Message-ID: <20231030213205.2824790-5-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:15 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189816 This simple C++ project supports compilation with cmake, meson and autotools. It's supposed to be used with oe-selftest for the devtool ide plugin. Signed-off-by: Adrian Freihofer --- meta-selftest/recipes-test/cpp/.gitignore | 1 + .../recipes-test/cpp/autotools-example.bb | 18 ++++++ .../cpp/autotools-example/run-ptest | 10 ++++ .../recipes-test/cpp/cmake-example.bb | 17 ++++++ .../recipes-test/cpp/cmake-example/run-ptest | 10 ++++ .../recipes-test/cpp/cpp-example.inc | 24 ++++++++ .../recipes-test/cpp/files/CMakeLists.txt | 60 +++++++++++++++++++ .../recipes-test/cpp/files/Makefile.am | 13 ++++ .../recipes-test/cpp/files/configure.ac | 11 ++++ .../cpp/files/cpp-example-lib.cpp | 17 ++++++ .../cpp/files/cpp-example-lib.hpp | 16 +++++ .../recipes-test/cpp/files/cpp-example.cpp | 16 +++++ .../recipes-test/cpp/files/meson.build | 34 +++++++++++ .../cpp/files/test-cpp-example.cpp | 20 +++++++ .../recipes-test/cpp/meson-example.bb | 17 ++++++ .../recipes-test/cpp/meson-example/run-ptest | 10 ++++ 16 files changed, 294 insertions(+) create mode 100644 meta-selftest/recipes-test/cpp/.gitignore create mode 100644 meta-selftest/recipes-test/cpp/autotools-example.bb create mode 100644 meta-selftest/recipes-test/cpp/autotools-example/run-ptest create mode 100644 meta-selftest/recipes-test/cpp/cmake-example.bb create mode 100644 meta-selftest/recipes-test/cpp/cmake-example/run-ptest create mode 100644 meta-selftest/recipes-test/cpp/cpp-example.inc create mode 100644 meta-selftest/recipes-test/cpp/files/CMakeLists.txt create mode 100644 meta-selftest/recipes-test/cpp/files/Makefile.am create mode 100644 meta-selftest/recipes-test/cpp/files/configure.ac create mode 100644 meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp create mode 100644 meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp create mode 100644 meta-selftest/recipes-test/cpp/files/cpp-example.cpp create mode 100644 meta-selftest/recipes-test/cpp/files/meson.build create mode 100644 meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp create mode 100644 meta-selftest/recipes-test/cpp/meson-example.bb create mode 100644 meta-selftest/recipes-test/cpp/meson-example/run-ptest diff --git a/meta-selftest/recipes-test/cpp/.gitignore b/meta-selftest/recipes-test/cpp/.gitignore new file mode 100644 index 00000000000..30d388a12b7 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/.gitignore @@ -0,0 +1 @@ +build* \ No newline at end of file diff --git a/meta-selftest/recipes-test/cpp/autotools-example.bb b/meta-selftest/recipes-test/cpp/autotools-example.bb new file mode 100644 index 00000000000..f5d8aa48154 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/autotools-example.bb @@ -0,0 +1,18 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +SUMMARY = "A C++ example compiled with autotools." + +inherit autotools + +require cpp-example.inc + +SRC_URI += "\ + file://configure.ac \ + file://Makefile.am \ +" + +FILES:${PN}-ptest += "${bindir}/test-autotools-example" diff --git a/meta-selftest/recipes-test/cpp/autotools-example/run-ptest b/meta-selftest/recipes-test/cpp/autotools-example/run-ptest new file mode 100644 index 00000000000..51548259886 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/autotools-example/run-ptest @@ -0,0 +1,10 @@ +#!/bin/sh +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +testautoexample + +# Note: run-ptests exits with exit value from test-cmake-example diff --git a/meta-selftest/recipes-test/cpp/cmake-example.bb b/meta-selftest/recipes-test/cpp/cmake-example.bb new file mode 100644 index 00000000000..96d543180b4 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/cmake-example.bb @@ -0,0 +1,17 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +SUMMARY = "A C++ example compiled with cmake." + +inherit cmake + +require cpp-example.inc + +SRC_URI += "\ + file://CMakeLists.txt \ +" + +FILES:${PN}-ptest += "${bindir}/test-cmake-example" diff --git a/meta-selftest/recipes-test/cpp/cmake-example/run-ptest b/meta-selftest/recipes-test/cpp/cmake-example/run-ptest new file mode 100644 index 00000000000..94b620a1984 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/cmake-example/run-ptest @@ -0,0 +1,10 @@ +#!/bin/sh +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +test-cmake-example + +# Note: run-ptests exits with exit value from test-cmake-example diff --git a/meta-selftest/recipes-test/cpp/cpp-example.inc b/meta-selftest/recipes-test/cpp/cpp-example.inc new file mode 100644 index 00000000000..39c61cf4ceb --- /dev/null +++ b/meta-selftest/recipes-test/cpp/cpp-example.inc @@ -0,0 +1,24 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +inherit ptest + +DEPENDS += "json-c" + +PV = "1.0" + +S = "${WORKDIR}" + +SRC_URI = "\ + file://cpp-example.cpp \ + file://cpp-example-lib.hpp \ + file://cpp-example-lib.cpp \ + file://test-cpp-example.cpp \ + file://run-ptest \ +" diff --git a/meta-selftest/recipes-test/cpp/files/CMakeLists.txt b/meta-selftest/recipes-test/cpp/files/CMakeLists.txt new file mode 100644 index 00000000000..839aa59b5e3 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/CMakeLists.txt @@ -0,0 +1,60 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +cmake_minimum_required(VERSION 3.22) + +project(cmake-example + VERSION 1.0.0 + LANGUAGES CXX +) + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED On) +set(CMAKE_CXX_EXTENSIONS Off) + +include(GNUInstallDirs) + +# Find json-c +# find_package(PkgConfig REQUIRED) +# pkg_check_modules(JSONC REQUIRED json-c) +find_package(json-c) + +# A simple library linking json-c library found by pkgconfig +add_library(cmake-example-lib cpp-example-lib.cpp cpp-example-lib.hpp) +set_target_properties(cmake-example-lib PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) +target_link_libraries(cmake-example-lib PRIVATE json-c::json-c) +# target_link_libraries(cmake-example-lib ${JSONC_LIBRARIES}) +# target_include_directories(cmake-example-lib PUBLIC ${JSONC_INCLUDE_DIRS}) +# target_compile_options(cmake-example-lib PUBLIC ${JSONC_CFLAGS_OTHER}) +install(TARGETS cmake-example-lib + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +# A simple executable linking the library +add_executable(cmake-example cpp-example.cpp) +target_link_libraries(cmake-example PRIVATE cmake-example-lib) + +install(TARGETS cmake-example + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +# A simple test executable for testing the library +add_executable(test-cmake-example test-cpp-example.cpp) +target_link_libraries(test-cmake-example PRIVATE cmake-example-lib) + +install(TARGETS test-cmake-example + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +include(CTest) +add_test(NAME test-cmake-example COMMAND test-cmake-example) diff --git a/meta-selftest/recipes-test/cpp/files/Makefile.am b/meta-selftest/recipes-test/cpp/files/Makefile.am new file mode 100644 index 00000000000..39cff91d2ac --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/Makefile.am @@ -0,0 +1,13 @@ +noinst_LIBRARIES = libautoexample.a +libautoexample_a_SOURCES = cpp-example-lib.cpp + +bin_PROGRAMS = autoexample testautoexample + +autoexample_SOURCES = cpp-example.cpp +autoexample_LDADD = libautoexample.a + +testautoexample_SOURCES = test-cpp-example.cpp +testautoexample_LDADD = libautoexample.a +TESTS=testautoexample + +AM_LDFLAGS = -ljson-c diff --git a/meta-selftest/recipes-test/cpp/files/configure.ac b/meta-selftest/recipes-test/cpp/files/configure.ac new file mode 100644 index 00000000000..29c6d1ca6fc --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/configure.ac @@ -0,0 +1,11 @@ + +AC_INIT([autoexample], [1.0]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_PROG_CXX +AX_CXX_COMPILE_STDCXX_17 +AC_PROG_RANLIB +AM_PROG_AR +AC_CONFIG_FILES([ + Makefile +]) +AC_OUTPUT diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp new file mode 100644 index 00000000000..29fd10fb7e1 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp @@ -0,0 +1,17 @@ +/* +* Copyright OpenEmbedded Contributors +* +* SPDX-License-Identifier: MIT +*/ + +#include +#include +#include "cpp-example-lib.hpp" + +const std::string& CppExample::get_string() { + return test_string; +} + +const char* CppExample::get_json_c_version() { + return json_c_version(); +} diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp new file mode 100644 index 00000000000..8a87e6b510a --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp @@ -0,0 +1,16 @@ +/* +* Copyright OpenEmbedded Contributors +* +* SPDX-License-Identifier: MIT +*/ + +#pragma once + +#include + +struct CppExample { + inline static const std::string test_string = "cpp-example-lib Magic: 123456789"; + + const std::string& get_string(); + const char* get_json_c_version(); +}; diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example.cpp b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp new file mode 100644 index 00000000000..de7f2106dec --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp @@ -0,0 +1,16 @@ +/* +* Copyright OpenEmbedded Contributors +* +* SPDX-License-Identifier: MIT +*/ + +#include "cpp-example-lib.hpp" + +#include + +int main() { + auto cpp_example = CppExample(); + std::cout << "C++ example linking " << cpp_example.get_string() << std::endl; + std::cout << "Linking json-c version " << cpp_example.get_json_c_version() << std::endl; + return 0; +} diff --git a/meta-selftest/recipes-test/cpp/files/meson.build b/meta-selftest/recipes-test/cpp/files/meson.build new file mode 100644 index 00000000000..7753da69721 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/meson.build @@ -0,0 +1,34 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +project('meson-example', 'cpp', + version: '1.0.0', + default_options: ['cpp_std=c++17'] + ) + + +jsoncdep = dependency('json-c') + +mesonexlib = shared_library('mesonexlib', + 'cpp-example-lib.cpp', 'cpp-example-lib.hpp', + version: meson.project_version(), + soversion: meson.project_version().split('.')[0], + dependencies : jsoncdep, + install : true + ) + +executable('mesonex', + 'cpp-example.cpp', + link_with : mesonexlib, + install : true + ) + +test_mesonex = executable('test-mesonex', + 'test-cpp-example.cpp', + link_with : mesonexlib, + install : true +) +test('meson example test', test_mesonex) diff --git a/meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp b/meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp new file mode 100644 index 00000000000..f9257e1aa2f --- /dev/null +++ b/meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp @@ -0,0 +1,20 @@ +/* +* Copyright OpenEmbedded Contributors +* +* SPDX-License-Identifier: MIT +*/ + +#include "cpp-example-lib.hpp" + +#include + +int main() { + auto cpp_example = CppExample(); + auto ret_string = cpp_example.get_string(); + if(0 == ret_string.compare(CppExample::test_string)) { + std::cout << "PASS: " << ret_string << " = " << CppExample::test_string << std::endl; + } else { + std::cout << "FAIL: " << ret_string << " != " << CppExample::test_string << std::endl; + return 1; + } +} diff --git a/meta-selftest/recipes-test/cpp/meson-example.bb b/meta-selftest/recipes-test/cpp/meson-example.bb new file mode 100644 index 00000000000..f15dbb50731 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/meson-example.bb @@ -0,0 +1,17 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +SUMMARY = "A C++ example compiled with meson." + +inherit pkgconfig meson + +require cpp-example.inc + +SRC_URI += "\ + file://meson.build \ +" + +FILES:${PN}-ptest += "${bindir}/test-mesonex" diff --git a/meta-selftest/recipes-test/cpp/meson-example/run-ptest b/meta-selftest/recipes-test/cpp/meson-example/run-ptest new file mode 100644 index 00000000000..b1804f00961 --- /dev/null +++ b/meta-selftest/recipes-test/cpp/meson-example/run-ptest @@ -0,0 +1,10 @@ +#!/bin/sh +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +test-mesonex + +# Note: run-ptests exits with exit value from test-mesonex From patchwork Mon Oct 30 21:32:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33131 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B5078C4332F for ; Mon, 30 Oct 2023 21:32:15 +0000 (UTC) Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) by mx.groups.io with SMTP id smtpd.web10.171476.1698701534900977728 for ; Mon, 30 Oct 2023 14:32:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=LwTyNnBb; spf=pass (domain: gmail.com, ip: 209.85.128.52, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-40839807e82so29619335e9.0 for ; Mon, 30 Oct 2023 14:32:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701533; x=1699306333; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zMOkJ4USCozMz4GgWrp5l2yO5esj7tOcKNzaOJUDY8Y=; b=LwTyNnBb89O97E6wu5mIE6l3YzIoZYKtIW3vBM/AmDiyEg6MmzSSbs9nAA1iPxB47S /oM5/zM8v+lxu5h2GqCey30FadPEPQ6oYX1kWiR2EdSB6jxeQafFhEmOdayOmFXvPE1N QrOatjpvxLZZk5YtuslprLMFPtbIMSfQOGnIP+kOWyR0DQ7925TtP8YWgX91ax4gC5yy FxInpgWKVHjP/lbD2j38VScYYBs3ggHRAKfSzaRnbzX3pzfEF0pF8X6J0HVc3YmoeVH2 Sdm8GYmFktYwtr/NOIAxjVegqmBUwch1vNbf+bBtED5i6UZ39wzlnCfbWl86ihuy4toU +Qng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701533; x=1699306333; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zMOkJ4USCozMz4GgWrp5l2yO5esj7tOcKNzaOJUDY8Y=; b=ZG/qJ5D2DRqGVPZ8JiQijUhZYY/tplmdGmLZ2pXM2JTgBkdLOYKqN/4CrN+XZqLEKz a5rk8rP3k3zoj2saAQBHu+ZUNtyJoXtf0BqS0Xni5ZOIwJ7Xchi8SAsIlU2z29q0ykUh YCoIYwxkIV4tTYvUf5OUjcgP04Zu84DhqskpAJh3ICB/2QdrfXYbwVZkjOfx79JMyYG8 j81SZzTl2ewZLeLoRC9wTY/MhZ2ahmqiDBT8p0dMrCkwzDTD8xxDMaBMjdUF260L0lRP qKRtMBC8cxKrQGWvb7TRnHPiEZIk4tMXeJxx3q7nF/PpbzJJilstd5NcLppx+Zu85gUJ CDuQ== X-Gm-Message-State: AOJu0YxEDwwymWkgHIEvKJau/38U+r33KQ39IWBdMEh47ht+41qLhLC6 GuS/QvtrJIQAp18LizxKXW//B33NvdA= X-Google-Smtp-Source: AGHT+IG2GoVPQL4COdyphQHs2c6vWjOQjw5Q4zrPM+nIXHoQFGC9guSOaYwpoZT/OWcOyJZzfR9nAA== X-Received: by 2002:a05:600c:4e05:b0:405:3be0:c78d with SMTP id b5-20020a05600c4e0500b004053be0c78dmr855956wmq.3.1698701533084; Mon, 30 Oct 2023 14:32:13 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:12 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 5/8] devtool: refactor deploy-target Date: Mon, 30 Oct 2023 22:32:02 +0100 Message-ID: <20231030213205.2824790-6-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:15 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189817 Make the deploy function independent from d. This allows to call the function also from Python code not running in bitbake. This is needed to for the devtool ide plugin which will call the do_install task and the code from devtool deploy-target independently from a bitbake server. This allows a much quicker workflow. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/__init__.py | 5 +- scripts/lib/devtool/deploy.py | 230 +++++++++++++++++--------------- 2 files changed, 124 insertions(+), 111 deletions(-) diff --git a/scripts/lib/devtool/__init__.py b/scripts/lib/devtool/__init__.py index 702db669de3..7d64547616e 100644 --- a/scripts/lib/devtool/__init__.py +++ b/scripts/lib/devtool/__init__.py @@ -78,12 +78,15 @@ def exec_fakeroot(d, cmd, **kwargs): """Run a command under fakeroot (pseudo, in fact) so that it picks up the appropriate file permissions""" # Grab the command and check it actually exists fakerootcmd = d.getVar('FAKEROOTCMD') + fakerootenv = d.getVar('FAKEROOTENV') + exec_fakeroot_no_d(fakerootcmd, fakerootenv, cmd, kwargs) + +def exec_fakeroot_no_d(fakerootcmd, fakerootenv, cmd, **kwargs): if not os.path.exists(fakerootcmd): logger.error('pseudo executable %s could not be found - have you run a build yet? pseudo-native should install this and if you have run any build then that should have been built') return 2 # Set up the appropriate environment newenv = dict(os.environ) - fakerootenv = d.getVar('FAKEROOTENV') for varvalue in fakerootenv.split(): if '=' in varvalue: splitval = varvalue.split('=', 1) diff --git a/scripts/lib/devtool/deploy.py b/scripts/lib/devtool/deploy.py index e14a5874177..ea7e2cb1ae8 100644 --- a/scripts/lib/devtool/deploy.py +++ b/scripts/lib/devtool/deploy.py @@ -16,7 +16,7 @@ import bb.utils import argparse_oe import oe.types -from devtool import exec_fakeroot, setup_tinfoil, check_workspace_recipe, DevtoolError +from devtool import exec_fakeroot_no_d, setup_tinfoil, check_workspace_recipe, DevtoolError logger = logging.getLogger('devtool') @@ -133,16 +133,11 @@ def _prepare_remote_script(deploy, verbose=False, dryrun=False, undeployall=Fals return '\n'.join(lines) - - -def deploy(args, config, basepath, workspace): - """Entry point for the devtool 'deploy' subcommand""" +def deploy_cached(srcdir, workdir, path, strip_cmd, libdir, base_libdir, max_process, fakerootcmd, fakerootenv, args): import math import oe.recipeutils import oe.package - check_workspace_recipe(workspace, args.recipename, checksrc=False) - try: host, destdir = args.target.split(':') except ValueError: @@ -152,116 +147,131 @@ def deploy(args, config, basepath, workspace): if not destdir.endswith('/'): destdir += '/' + recipe_outdir = srcdir + if not os.path.exists(recipe_outdir) or not os.listdir(recipe_outdir): + raise DevtoolError('No files to deploy - have you built the %s ' + 'recipe? If so, the install step has not installed ' + 'any files.' % args.recipename) + + if args.strip and not args.dry_run: + # Fakeroot copy to new destination + srcdir = recipe_outdir + recipe_outdir = os.path.join(workdir, 'devtool-deploy-target-stripped') + if os.path.isdir(recipe_outdir): + exec_fakeroot_no_d(fakerootcmd, fakerootenv, "rm -rf %s" % recipe_outdir, shell=True) + exec_fakeroot_no_d(fakerootcmd, fakerootenv, "cp -af %s %s" % (os.path.join(srcdir, '.'), recipe_outdir), shell=True) + os.environ['PATH'] = ':'.join([os.environ['PATH'], path or '']) + oe.package.strip_execs(args.recipename, recipe_outdir, strip_cmd, libdir, base_libdir, max_process) + + filelist = [] + inodes = set({}) + ftotalsize = 0 + for root, _, files in os.walk(recipe_outdir): + for fn in files: + fstat = os.lstat(os.path.join(root, fn)) + # Get the size in kiB (since we'll be comparing it to the output of du -k) + # MUST use lstat() here not stat() or getfilesize() since we don't want to + # dereference symlinks + if fstat.st_ino in inodes: + fsize = 0 + else: + fsize = int(math.ceil(float(fstat.st_size)/1024)) + inodes.add(fstat.st_ino) + ftotalsize += fsize + # The path as it would appear on the target + fpath = os.path.join(destdir, os.path.relpath(root, recipe_outdir), fn) + filelist.append((fpath, fsize)) + + if args.dry_run: + print('Files to be deployed for %s on target %s:' % (args.recipename, args.target)) + for item, _ in filelist: + print(' %s' % item) + return 0 + + extraoptions = '' + if args.no_host_check: + extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' + if not args.show_status: + extraoptions += ' -q' + + scp_sshexec = '' + ssh_sshexec = 'ssh' + if args.ssh_exec: + scp_sshexec = "-S %s" % args.ssh_exec + ssh_sshexec = args.ssh_exec + scp_port = '' + ssh_port = '' + if args.port: + scp_port = "-P %s" % args.port + ssh_port = "-p %s" % args.port + + if args.key: + extraoptions += ' -i %s' % args.key + + # In order to delete previously deployed files and have the manifest file on + # the target, we write out a shell script and then copy it to the target + # so we can then run it (piping tar output to it). + # (We cannot use scp here, because it doesn't preserve symlinks.) + tmpdir = tempfile.mkdtemp(prefix='devtool') + try: + tmpscript = '/tmp/devtool_deploy.sh' + tmpfilelist = os.path.join(os.path.dirname(tmpscript), 'devtool_deploy.list') + shellscript = _prepare_remote_script(deploy=True, + verbose=args.show_status, + nopreserve=args.no_preserve, + nocheckspace=args.no_check_space) + # Write out the script to a file + with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f: + f.write(shellscript) + # Write out the file list + with open(os.path.join(tmpdir, os.path.basename(tmpfilelist)), 'w') as f: + f.write('%d\n' % ftotalsize) + for fpath, fsize in filelist: + f.write('%s %d\n' % (fpath, fsize)) + # Copy them to the target + ret = subprocess.call("scp %s %s %s %s/* %s:%s" % (scp_sshexec, scp_port, extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True) + if ret != 0: + raise DevtoolError('Failed to copy script to %s - rerun with -s to ' + 'get a complete error message' % args.target) + finally: + shutil.rmtree(tmpdir) + + # Now run the script + ret = exec_fakeroot_no_d(fakerootcmd, fakerootenv, 'tar cf - . | %s %s %s %s \'sh %s %s %s %s\'' % (ssh_sshexec, ssh_port, extraoptions, args.target, tmpscript, args.recipename, destdir, tmpfilelist), cwd=recipe_outdir, shell=True) + if ret != 0: + raise DevtoolError('Deploy failed - rerun with -s to get a complete ' + 'error message') + + logger.info('Successfully deployed %s' % recipe_outdir) + + files_list = [] + for root, _, files in os.walk(recipe_outdir): + for filename in files: + filename = os.path.relpath(os.path.join(root, filename), recipe_outdir) + files_list.append(os.path.join(destdir, filename)) + +def deploy(args, config, basepath, workspace): + """Entry point for the devtool 'deploy' subcommand""" + check_workspace_recipe(workspace, args.recipename, checksrc=False) + tinfoil = setup_tinfoil(basepath=basepath) try: try: rd = tinfoil.parse_recipe(args.recipename) + + srcdir = rd.getVar('D') + workdir = rd.getVar('WORKDIR') + path = rd.getVar('PATH') + strip_cmd = rd.getVar('STRIP') + libdir = rd.getVar('libdir'), + base_libdir = rd.getVar('base_libdir') + max_process = int(rd.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1) + fakerootcmd = rd.getVar('FAKEROOTCMD') + fakerootenv = rd.getVar('FAKEROOTENV') + deploy_cached(srcdir, workdir, path, strip_cmd, libdir, base_libdir, max_process, fakerootcmd, fakerootenv, args) except Exception as e: raise DevtoolError('Exception parsing recipe %s: %s' % (args.recipename, e)) - recipe_outdir = rd.getVar('D') - if not os.path.exists(recipe_outdir) or not os.listdir(recipe_outdir): - raise DevtoolError('No files to deploy - have you built the %s ' - 'recipe? If so, the install step has not installed ' - 'any files.' % args.recipename) - - if args.strip and not args.dry_run: - # Fakeroot copy to new destination - srcdir = recipe_outdir - recipe_outdir = os.path.join(rd.getVar('WORKDIR'), 'devtool-deploy-target-stripped') - if os.path.isdir(recipe_outdir): - exec_fakeroot(rd, "rm -rf %s" % recipe_outdir, shell=True) - exec_fakeroot(rd, "cp -af %s %s" % (os.path.join(srcdir, '.'), recipe_outdir), shell=True) - os.environ['PATH'] = ':'.join([os.environ['PATH'], rd.getVar('PATH') or '']) - oe.package.strip_execs(args.recipename, recipe_outdir, rd.getVar('STRIP'), rd.getVar('libdir'), - rd.getVar('base_libdir'), rd) - - filelist = [] - inodes = set({}) - ftotalsize = 0 - for root, _, files in os.walk(recipe_outdir): - for fn in files: - fstat = os.lstat(os.path.join(root, fn)) - # Get the size in kiB (since we'll be comparing it to the output of du -k) - # MUST use lstat() here not stat() or getfilesize() since we don't want to - # dereference symlinks - if fstat.st_ino in inodes: - fsize = 0 - else: - fsize = int(math.ceil(float(fstat.st_size)/1024)) - inodes.add(fstat.st_ino) - ftotalsize += fsize - # The path as it would appear on the target - fpath = os.path.join(destdir, os.path.relpath(root, recipe_outdir), fn) - filelist.append((fpath, fsize)) - - if args.dry_run: - print('Files to be deployed for %s on target %s:' % (args.recipename, args.target)) - for item, _ in filelist: - print(' %s' % item) - return 0 - - extraoptions = '' - if args.no_host_check: - extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - if not args.show_status: - extraoptions += ' -q' - - scp_sshexec = '' - ssh_sshexec = 'ssh' - if args.ssh_exec: - scp_sshexec = "-S %s" % args.ssh_exec - ssh_sshexec = args.ssh_exec - scp_port = '' - ssh_port = '' - if args.port: - scp_port = "-P %s" % args.port - ssh_port = "-p %s" % args.port - - if args.key: - extraoptions += ' -i %s' % args.key - - # In order to delete previously deployed files and have the manifest file on - # the target, we write out a shell script and then copy it to the target - # so we can then run it (piping tar output to it). - # (We cannot use scp here, because it doesn't preserve symlinks.) - tmpdir = tempfile.mkdtemp(prefix='devtool') - try: - tmpscript = '/tmp/devtool_deploy.sh' - tmpfilelist = os.path.join(os.path.dirname(tmpscript), 'devtool_deploy.list') - shellscript = _prepare_remote_script(deploy=True, - verbose=args.show_status, - nopreserve=args.no_preserve, - nocheckspace=args.no_check_space) - # Write out the script to a file - with open(os.path.join(tmpdir, os.path.basename(tmpscript)), 'w') as f: - f.write(shellscript) - # Write out the file list - with open(os.path.join(tmpdir, os.path.basename(tmpfilelist)), 'w') as f: - f.write('%d\n' % ftotalsize) - for fpath, fsize in filelist: - f.write('%s %d\n' % (fpath, fsize)) - # Copy them to the target - ret = subprocess.call("scp %s %s %s %s/* %s:%s" % (scp_sshexec, scp_port, extraoptions, tmpdir, args.target, os.path.dirname(tmpscript)), shell=True) - if ret != 0: - raise DevtoolError('Failed to copy script to %s - rerun with -s to ' - 'get a complete error message' % args.target) - finally: - shutil.rmtree(tmpdir) - - # Now run the script - ret = exec_fakeroot(rd, 'tar cf - . | %s %s %s %s \'sh %s %s %s %s\'' % (ssh_sshexec, ssh_port, extraoptions, args.target, tmpscript, args.recipename, destdir, tmpfilelist), cwd=recipe_outdir, shell=True) - if ret != 0: - raise DevtoolError('Deploy failed - rerun with -s to get a complete ' - 'error message') - - logger.info('Successfully deployed %s' % recipe_outdir) - - files_list = [] - for root, _, files in os.walk(recipe_outdir): - for filename in files: - filename = os.path.relpath(os.path.join(root, filename), recipe_outdir) - files_list.append(os.path.join(destdir, filename)) finally: tinfoil.shutdown() From patchwork Mon Oct 30 21:32:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33138 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF399C41535 for ; Mon, 30 Oct 2023 21:32:25 +0000 (UTC) Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) by mx.groups.io with SMTP id smtpd.web11.171852.1698701536286193996 for ; Mon, 30 Oct 2023 14:32:16 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=O9qWmsBJ; spf=pass (domain: gmail.com, ip: 209.85.128.44, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-40837ebba42so33165595e9.0 for ; Mon, 30 Oct 2023 14:32:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701534; x=1699306334; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Wz/uIZ1N2bo+JaqnIpUr6oM4uFT3XOTFND4AsNSTkTs=; b=O9qWmsBJttbcxQC/kNItFjNiDD9wXdQCLDpXBaUBcGri82muz5bkGwhiZyKJa+sVKL H2i99yUMdObnXpxxrua57OTU5vB36ODiadaKllUqI9G1JMIPPs1bhq1xnzr2BhJ/6K7u wNvZM3Sb29xR1FLawsWXscsRti9rpnksbqV1yGlhjDv5EHj86cbvCtnipbnRzOh/N7DD oiHT93GyBW/1nMR/L7944eh/8XbLwT7ZtJjvKJvSCcmZskyFVxVw4t0aOz2F6XBm5/Tb sw3NLLgpM5RQEu8e+NZOomQquUXIM6B51SMRxlXjFFoPOhpt4hWcaYPThXJ3eiz3RCDy vD1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701534; x=1699306334; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Wz/uIZ1N2bo+JaqnIpUr6oM4uFT3XOTFND4AsNSTkTs=; b=dPiS1Mx+6aSNIHtGrYbB8gdF5nNYeZHPrTOyp/5D7/Ww0Lzyt4/HMk1afeOBdnh1jI mTOKa59DV7dZojZy1wQrS5mAZjWqWAecRj9NcOUZgzudqPHiPxHg/dzbDQdEO3s7yoLs s2+qVZ66aJ4aGEvFNE0OkzbvkwQgD8cV9YNAd4w0EDBQlKznu5RWDTUJCi7gzYJ6cn11 jxyFNWh+76OTSaVnG9uPRnEHyjDJYMWgA1zlQgKK+rECxYpsPITW7YKfHqgYJi7XtYic Z3S2cv28AXPBYggpeiv0Bt1qttVxn02+pkXPagnAgCpg2hDP2FnsfPtwD1DgKIBuwT8r UOhw== X-Gm-Message-State: AOJu0Yy20F1VUY/eYzA6wY43k/MHgVe7rI68xTiVO4s1W8zFpgp/36tP HNSmFxYvC4FJJExzXPrFlmhDHBptWmw= X-Google-Smtp-Source: AGHT+IF0vIodxo4u/5ZOf+cKbFatqGMK7Jj5Mc0k4Skz9UfaWf3X+aEcfDBz+s/cfeUVnieiXvlzJQ== X-Received: by 2002:a05:600c:4708:b0:408:434c:dae7 with SMTP id v8-20020a05600c470800b00408434cdae7mr9600429wmo.2.1698701533957; Mon, 30 Oct 2023 14:32:13 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:13 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 6/8] devtool: new ide plugin Date: Mon, 30 Oct 2023 22:32:03 +0100 Message-ID: <20231030213205.2824790-7-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:25 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189818 The new devtool ide plugin configures an IDE to work with the eSDK. With this initial implementation VSCode is the default IDE. The plugin works for recipes inheriting the cmake or the meson bbclass. Support for more programming languages and build tools may be added in the future. Using the plugin in recipe modes: $ devtool modify a-recipe $ devtool ide a-recipe a-image $ code "$BUILDDIR/workspace/sources/a-recipe" Work in VSCode, after installing the proposed plugins Using the plugin without a recipe $ devtool ide none a-image vscode where/the/sources/are Use the cross tool-chain which is provided as a cmake-kit. The goal of this implementation is to create a configuration for VSCode (or other IDEs) that allows to work on the code of a recipe completely independent from bitbake. bitbake is only called if the configuration or the whole SDK has to be regenerated. But bitbake should not need to be called while working in the IDE. This has two major advantages over calling devtool build from the IDE: - The IDE provides plugins for integration with cmake, for example. These features are usable, which would not be the case if bitbake or devtool are called from within the IDE. - It is much faster. Many thanks to Enguerrand de Ribaucourt for testing and bug fixing. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide.py | 1104 ++++++++++++++++++ scripts/lib/devtool/ide_handlers/__init__.py | 10 + scripts/lib/devtool/ide_handlers/ide_base.py | 46 + scripts/lib/devtool/ide_handlers/ide_code.py | 420 +++++++ scripts/lib/devtool/ide_handlers/ide_none.py | 91 ++ 5 files changed, 1671 insertions(+) create mode 100755 scripts/lib/devtool/ide.py create mode 100644 scripts/lib/devtool/ide_handlers/__init__.py create mode 100644 scripts/lib/devtool/ide_handlers/ide_base.py create mode 100644 scripts/lib/devtool/ide_handlers/ide_code.py create mode 100644 scripts/lib/devtool/ide_handlers/ide_none.py diff --git a/scripts/lib/devtool/ide.py b/scripts/lib/devtool/ide.py new file mode 100755 index 00000000000..2067e72ceb0 --- /dev/null +++ b/scripts/lib/devtool/ide.py @@ -0,0 +1,1104 @@ +#! /usr/bin/env python3 +# +# Copyright (C) 2023 Siemens AG +# +# SPDX-License-Identifier: GPL-2.0-only +# + +"""Devtool ide plugin""" + +import os +import stat +import sys +import logging +import json +import re +import shutil +import subprocess +from argparse import RawTextHelpFormatter +from enum import IntEnum, auto + +import bb +from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, DevtoolError, parse_recipe +from devtool.standard import get_real_srctree +import devtool.ide_handlers + +SHARED_SYSROOT_RECIPES = ['shared', 'none', + 'meta-ide-support', 'build-sysroots'] +SUPPORTED_IDES = ['code', 'none'] + +logger = logging.getLogger('devtool') + + +class DevtoolIdeMode(IntEnum): + UNDEFINED = auto() + DEVTOOL_MODIFY = auto() + SHARED_SYSROOT = auto() + + +class TargetDevice: + """SSH remote login parameters""" + + def __init__(self, args): + self.extraoptions = '' + if args.no_host_check: + self.extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' + self.ssh_sshexec = 'ssh' + if args.ssh_exec: + self.ssh_sshexec = args.ssh_exec + self.ssh_port = '' + if args.port: + self.ssh_port = "-p %s" % args.port + if args.key: + self.extraoptions += ' -i %s' % args.key + + self.target = args.target + target_sp = args.target.split('@') + if len(target_sp) == 1: + self.login = "" + self.host = target_sp[0] + elif len(target_sp) == 2: + self.login = target_sp[0] + self.host = target_sp[1] + else: + logger.error("Invalid target argument: %s" % args.target) + + @staticmethod + def get_devtool_deploy_opts(args): + """Filter args for devtool deploy-target args""" + if not args.target: + return None + devtool_deploy_opts = [args.target] + if args.no_host_check: + devtool_deploy_opts += ["-c"] + if args.show_status: + devtool_deploy_opts += ["-s"] + if args.no_preserve: + devtool_deploy_opts += ["-p"] + if args.no_check_space: + devtool_deploy_opts += ["--no-check-space"] + if args.ssh_exec: + devtool_deploy_opts += ["-e", args.ssh.exec] + if args.port: + devtool_deploy_opts += ["-P", args.port] + if args.key: + devtool_deploy_opts += ["-I", args.key] + if args.strip is False: + devtool_deploy_opts += ["--no-strip"] + return devtool_deploy_opts + + +class RecipeNative: + """Base class for calling bitbake to provide a -native recipe""" + + def __init__(self, name, target_arch=None): + self.name = name + self.target_arch = target_arch + self.bootstrap_tasks = [self.name + ':do_addto_recipe_sysroot'] + self.staging_bindir_native = None + self.target_sys = None + self.__native_bin = None + + def _initialize(self, config, workspace, tinfoil): + """Get the parsed recipe""" + recipe_d = parse_recipe( + config, tinfoil, self.name, appends=True, filter_workspace=False) + if not recipe_d: + raise DevtoolError("Parsing %s recipe failed" % self.name) + self.staging_bindir_native = os.path.realpath( + recipe_d.getVar('STAGING_BINDIR_NATIVE')) + self.target_sys = recipe_d.getVar('TARGET_SYS') + return recipe_d + + def initialize(self, config, workspace, tinfoil): + """Basic initialization that can be overridden by a derived class""" + self._initialize(config, workspace, tinfoil) + + @property + def native_bin(self): + if not self.__native_bin: + raise DevtoolError("native binary name is not defined.") + return self.__native_bin + + +class RecipeGdbCross(RecipeNative): + """Handle handle gdb-cross on the host and the gdbserver on the target device""" + + def __init__(self, args, target_arch, target_device, gdbserver_multi=True): + super().__init__('gdb-cross-' + target_arch, target_arch) + self.target_device = target_device + self.gdb = None + self.gdbserver_port_next = int(args.gdbserver_port_start) + self.gdbserver_multi = gdbserver_multi + self.config_db = {} + + def __find_gdbserver(self, config, tinfoil): + """Absolute path of the gdbserver""" + recipe_d_gdb = parse_recipe( + config, tinfoil, 'gdb', appends=True, filter_workspace=False) + if not recipe_d_gdb: + raise DevtoolError("Parsing gdb recipe failed") + return os.path.join(recipe_d_gdb.getVar('bindir'), 'gdbserver') + + def initialize(self, config, workspace, tinfoil): + super()._initialize(config, workspace, tinfoil) + gdb_bin = self.target_sys + '-gdb' + gdb_path = os.path.join( + self.staging_bindir_native, self.target_sys, gdb_bin) + self.gdb = gdb_path + self.gdbserver_path = self.__find_gdbserver(config, tinfoil) + + @property + def host(self): + return self.target_device.host + + def __gdbserver_start_cmd(self, binary, port): + """Returns a shell command starting the gdbserver on the remote device + + GDB supports two modes: + multi: gdbserver remains running over several debug sessions + once: gdbserver terminates after the debugged process terminates + """ + if self.gdbserver_multi: + gdbserver_cmd = "%s --multi :%s" % ( + self.gdbserver_path, port) + else: + gdbserver_cmd = "%s --once :%s %s" % ( + self.gdbserver_path, port, binary) + return "%s %s %s %s 'sh -c \"%s\"'" % ( + self.target_device.ssh_sshexec, self.target_device.ssh_port, self.target_device.extraoptions, self.target_device.target, gdbserver_cmd) + + def setup_gdbserver_config(self, binary, script_dir): + """Generate a GDB configuration for a binary on the target device + + This function adds a GDB configuration for the binary to the internal config_db. + This allows to allocate a TCP port per binary. + Optionally a shell script is generated which starts the gedbserver on the + target device as well as the gdb-cross on the host. + """ + if binary in self.config_db: + raise DevtoolError( + "gdbserver config for binary %s is already generated" % binary) + + port = self.gdbserver_port_next + self.gdbserver_port_next += 1 + config_entry = { + "port": port, + } + if script_dir: + cmd_lines = ['#!/bin/sh'] + cmd_lines.append(self.__gdbserver_start_cmd(binary, port)) + binary_name_pretty = binary.replace(os.sep, '-') + start_script_name = 'gdbserver_start_%d_%s' % ( + port, binary_name_pretty) + if self.gdbserver_multi: + start_script_name += "_m" + start_script_file = os.path.join(script_dir, start_script_name) + config_entry['gdbserver_start_script'] = start_script_file + config_entry['pretty_id'] = 'gdbserver start %d %s' % ( + port, binary) + + bb.utils.mkdirhier(script_dir) + with open(start_script_file, 'w') as script_f: + script_f.write(os.linesep.join(cmd_lines)) + script_f.write(os.linesep) + st = os.stat(start_script_file) + os.chmod(start_script_file, st.st_mode | stat.S_IEXEC) + + self.config_db[binary] = config_entry + return config_entry + + def get_gdbserver_pretty_id(self, binary): + """Unique ID for the GDB configuration""" + return self.config_db[binary]['pretty_id'] + + def get_gdbserver_port(self, binary): + """TCP port used by gdbserver""" + return self.config_db[binary]['port'] + + def get_gdbserver_start_script(self, binary): + """Path to the script starting the debug session""" + return self.config_db[binary]['gdbserver_start_script'] + + def get_gdbserver_start_scripts(self): + """Get the paths of all debug session start scripts""" + for conf in self.config_db.values(): + yield (conf['pretty_id'], conf['gdbserver_start_script']) + + +class RecipeImage: + """Handle some image recipe related properties + + Most workflows require firmware that runs on the target device. + This firmware must be consistent with the setup of the host system. + In particular, the debug symbols must be compatible. For this, the + rootfs must be created as part of the SDK. + """ + + def __init__(self, name): + self.gdbserver_missing = False + self.name = name + self.rootfs = None + self.__rootfs_dbg = None + self.bootstrap_tasks = [self.name + ':do_build'] + + def initialize(self, config, tinfoil): + image_d = parse_recipe( + config, tinfoil, self.name, appends=True, filter_workspace=False) + if not image_d: + raise DevtoolError( + "Parsing image recipe %s failed" % self.name) + + workdir = image_d.getVar('WORKDIR') + self.rootfs = os.path.join(workdir, 'rootfs') + if image_d.getVar('IMAGE_GEN_DEBUGFS') == "1": + self.__rootfs_dbg = os.path.join(workdir, 'rootfs-dbg') + + self.gdbserver_missing = 'gdbserver' not in image_d.getVar( + 'IMAGE_INSTALL') + + @property + def debug_support(self): + return bool(self.rootfs_dbg) + + @property + def rootfs_dbg(self): + if self.__rootfs_dbg and os.path.isdir(self.__rootfs_dbg): + return self.__rootfs_dbg + return None + + +class RecipeMetaIdeSupport: + """For the shared sysroots mode meta-ide-support is needed + + For use cases where just a cross tool-chain is required but + no recipe is used, devtool ide abstracts calling bitbake meta-ide-support + and bitbake build-sysroots. This also allows to expose the cross-toolchains + to IDEs. For example VSCode support different tool-chains with e.g. cmake-kits. + """ + + def __init__(self): + self.bootstrap_tasks = ['meta-ide-support:do_build'] + self.topdir = None + self.datadir = None + self.deploy_dir_image = None + self.build_sys = None + # From toolchain-scripts + self.real_multimach_target_sys = None + + def initialize(self, config, tinfoil): + meta_ide_support_d = parse_recipe( + config, tinfoil, 'meta-ide-support', appends=True, filter_workspace=False) + if not meta_ide_support_d: + raise DevtoolError("Parsing meta-ide-support recipe failed") + + self.topdir = meta_ide_support_d.getVar('TOPDIR') + self.datadir = meta_ide_support_d.getVar('datadir') + self.deploy_dir_image = meta_ide_support_d.getVar( + 'DEPLOY_DIR_IMAGE') + self.build_sys = meta_ide_support_d.getVar('BUILD_SYS') + self.real_multimach_target_sys = meta_ide_support_d.getVar( + 'REAL_MULTIMACH_TARGET_SYS') + + +class RecipeBuildSysroots: + """For the shared sysroots mode build-sysroots is needed""" + + def __init__(self): + self.standalone_sysroot = None + self.standalone_sysroot_native = None + self.bootstrap_tasks = [ + 'build-sysroots:do_build_target_sysroot', + 'build-sysroots:do_build_native_sysroot' + ] + + def initialize(self, config, tinfoil): + build_sysroots_d = parse_recipe( + config, tinfoil, 'build-sysroots', appends=True, filter_workspace=False) + if not build_sysroots_d: + raise DevtoolError("Parsing build-sysroots recipe failed") + self.standalone_sysroot = build_sysroots_d.getVar( + 'STANDALONE_SYSROOT') + self.standalone_sysroot_native = build_sysroots_d.getVar( + 'STANDALONE_SYSROOT_NATIVE') + + +class SharedSysrootsEnv: + """Handle the shared sysroots based workflow + + Support the workflow with just a tool-chain without a recipe. + It's basically like: + bitbake some-dependencies + bitbake meta-ide-support + bitbake build-sysroots + Use the environment-* file found in the deploy folder + """ + + def __init__(self): + self.ide_support = None + self.build_sysroots = None + + def initialize(self, ide_support, build_sysroots): + self.ide_support = ide_support + self.build_sysroots = build_sysroots + + def setup_ide(self, ide): + ide.setup(self) + + +class BuildTool(IntEnum): + UNDEFINED = auto() + CMAKE = auto() + MESON = auto() + + +class RecipeModified: + """Handling af recipes in the workspace created by devtool modify""" + OE_INIT_BUILD_ENV = 'oe-init-build-env' + + def __init__(self, name): + self.name = name + self.bootstrap_tasks = [name + ':do_install'] + # workspace + self.real_srctree = None + self.srctree = None + self.temp_dir = None + self.bbappend = None + # recipe variables from d.getVar + self.b = None + self.base_libdir = None + self.bblayers = None + self.bpn = None + self.d = None + self.fakerootcmd = None + self.fakerootenv = None + self.libdir = None + self.max_process = None + self.package_arch = None + self.package_debug_split_style = None + self.path = None + self.pn = None + self.recipe_sysroot = None + self.recipe_sysroot_native = None + self.staging_incdir = None + self.strip_cmd = None + self.target_arch = None + self.workdir = None + self.recipe_id = None + # recipe variables from d.getVarFlags + self.f_do_install_cleandirs = None + self.f_do_install_dirs = None + # replicate bitbake build environment + self.exported_vars = None + self.cmd_compile = None + self.__oe_init_dir = None + # main build tool used by this recipe + self.build_tool = BuildTool.UNDEFINED + # build_tool = cmake + self.oecmake_generator = None + self.cmake_cache_vars = None + # build_tool = meson + self.meson_buildtype = None + self.meson_wrapper = None + self.mesonopts = None + self.extra_oemeson = None + self.meson_cross_file = None + + def initialize(self, config, workspace, tinfoil): + recipe_d = parse_recipe( + config, tinfoil, self.name, appends=True, filter_workspace=False) + if not recipe_d: + raise DevtoolError("Parsing %s recipe failed" % self.name) + + # Verify this recipe is built as externalsrc setup by devtool modify + workspacepn = check_workspace_recipe( + workspace, self.name, bbclassextend=True) + self.srctree = workspace[workspacepn]['srctree'] + # Need to grab this here in case the source is within a subdirectory + self.real_srctree = get_real_srctree( + self.srctree, recipe_d.getVar('S'), recipe_d.getVar('WORKDIR')) + self.bbappend = workspace[workspacepn]['bbappend'] + + self.temp_dir = os.path.join(config.workspace_path, 'temp', self.name) + if os.path.exists(self.temp_dir): + shutil.rmtree(self.temp_dir) + + self.b = recipe_d.getVar('B') + self.base_libdir = recipe_d.getVar('base_libdir') + self.bblayers = recipe_d.getVar('BBLAYERS').split() + self.bpn = recipe_d.getVar('BPN') + self.d = recipe_d.getVar('D') + self.fakerootcmd = recipe_d.getVar('FAKEROOTCMD') + self.fakerootenv = recipe_d.getVar('FAKEROOTENV') + self.libdir = recipe_d.getVar('libdir') + self.max_process = int(recipe_d.getVar( + "BB_NUMBER_THREADS") or os.cpu_count() or 1) + self.package_arch = recipe_d.getVar('PACKAGE_ARCH') + self.package_debug_split_style = recipe_d.getVar( + 'PACKAGE_DEBUG_SPLIT_STYLE') + self.path = recipe_d.getVar('PATH') + self.pn = recipe_d.getVar('PN') + self.recipe_sysroot = os.path.realpath( + recipe_d.getVar('RECIPE_SYSROOT')) + self.recipe_sysroot_native = os.path.realpath( + recipe_d.getVar('RECIPE_SYSROOT_NATIVE')) + self.staging_incdir = os.path.realpath( + recipe_d.getVar('STAGING_INCDIR')) + self.strip_cmd = recipe_d.getVar('STRIP') + self.target_arch = recipe_d.getVar('TARGET_ARCH') + self.workdir = os.path.realpath(recipe_d.getVar('WORKDIR')) + + self.f_do_install_cleandirs = recipe_d.getVarFlag( + 'do_install', 'cleandirs').split() + self.f_do_install_dirs = recipe_d.getVarFlag( + 'do_install', 'dirs').split() + + self.__init_exported_variables(recipe_d) + + if bb.data.inherits_class('cmake', recipe_d): + self.oecmake_generator = recipe_d.getVar('OECMAKE_GENERATOR') + self.__init_cmake_preset_cache(recipe_d) + self.build_tool = BuildTool.CMAKE + elif bb.data.inherits_class('meson', recipe_d): + self.meson_buildtype = recipe_d.getVar('MESON_BUILDTYPE') + self.mesonopts = recipe_d.getVar('MESONOPTS') + self.extra_oemeson = recipe_d.getVar('EXTRA_OEMESON') + self.meson_cross_file = recipe_d.getVar('MESON_CROSS_FILE') + self.build_tool = BuildTool.MESON + + # Recipe ID is the identifier for IDE config sections + self.recipe_id = self.bpn + "-" + self.package_arch + self.recipe_id_pretty = self.bpn + ": " + self.package_arch + + def is_recipe_cross(self): + if self.pn.startswith('nativesdk-') or self.pn.endswith('-native'): + return False + return True + + def append_to_bbappend(self, append_text): + with open(self.bbappend, 'a') as bbap: + bbap.write(append_text) + + def remove_from_bbappend(self, append_text): + with open(self.bbappend, 'r') as bbap: + text = bbap.read() + new_text = text.replace(append_text, '') + with open(self.bbappend, 'w') as bbap: + bbap.write(new_text) + + def debug_build_config(self, args): + """Explicitely set for example CMAKE_BUILD_TYPE to Debug if not defined otherwise""" + if self.build_tool == BuildTool.CMAKE: + append_text = os.linesep + \ + 'OECMAKE_ARGS:append = " -DCMAKE_BUILD_TYPE:STRING=Debug"' + os.linesep + if args.debug_build_config and not 'CMAKE_BUILD_TYPE' in self.cmake_cache_vars: + self.cmake_cache_vars['CMAKE_BUILD_TYPE'] = { + "type": "STRING", + "value": "Debug", + } + self.append_to_bbappend(append_text) + elif 'CMAKE_BUILD_TYPE' in self.cmake_cache_vars: + del self.cmake_cache_vars['CMAKE_BUILD_TYPE'] + self.remove_from_bbappend(append_text) + elif self.build_tool == BuildTool.MESON: + append_text = os.linesep + 'MESON_BUILDTYPE = "debug"' + os.linesep + if args.debug_build_config and self.meson_buildtype != "debug": + self.mesonopts.replace( + '--buildtype ' + self.meson_buildtype, '--buildtype debug') + self.append_to_bbappend(append_text) + elif self.meson_buildtype == "debug": + self.mesonopts.replace( + '--buildtype debug', '--buildtype plain') + self.remove_from_bbappend(append_text) + elif args.debug_build_config: + logger.warn( + "--debug-build-config is not implemented for this build tool yet.") + + def solib_search_path(self, image): + """Search for debug symbols in the rootfs-dbg + + The debug symbols of shared libraries which are provided by other packages + are grabbed from the -dbg packages in the rootfs-dbg. + + Note: For the devtool modified recipe compiled from the IDE, the debug + symbols are taken from the unstripped binaries in the image folder. + The image folder is created by the do_install task. + Also, devtool deploy-target takes the files from the image folder. + Running the do_package task is not required when working with the IDE. + """ + so_paths = [] + if self.package_debug_split_style in ['debug-with-srcpkg', '.debug']: + so_paths = [ + self.base_libdir, os.path.join(self.base_libdir, ".debug"), + self.libdir, os.path.join(self.libdir, ".debug") + ] + elif self.package_debug_split_style == 'debug-file-directory': + so_paths = ["/usr/lib/debug"] + else: + logger.warning( + "Cannot find solib search path for a rootfs built with PACKAGE_DEBUG_SPLIT_STYLE=%s." % self.package_debug_split_style) + return [os.path.join(image.rootfs_dbg, dbgdir.lstrip('/')) for dbgdir in so_paths] + + def solib_search_path_str(self, image): + """Return a : separated list of paths usable by GDB's set solib-search-path""" + return ':'.join(self.solib_search_path(image)) + + def __init_exported_variables(self, d): + """Find all variables with export flag set. + + This allows to generate IDE configurations which compile with the same + environment as bitbake does. That's at least a reasonable default behavior. + """ + exported_vars = {} + + vars = (key for key in d.keys() if not key.startswith( + "__") and not d.getVarFlag(key, "func", False)) + for var in vars: + func = d.getVarFlag(var, "func", False) + if d.getVarFlag(var, 'python', False) and func: + continue + export = d.getVarFlag(var, "export", False) + unexport = d.getVarFlag(var, "unexport", False) + if not export and not unexport and not func: + continue + if unexport: + continue + + val = d.getVar(var) + if val is None: + continue + if set(var) & set("-.{}+"): + logger.warn( + "Warning: Found invalid character in variable name %s", str(var)) + continue + varExpanded = d.expand(var) + val = str(val) + + if varExpanded.startswith("BASH_FUNC_"): + varExpanded = varExpanded[10:-2] + val = val[3:] # Strip off "() " + logger.warn("Warning: BASH_FUNC_ is not exported to cmake presets (%s() %s)" % ( + varExpanded, val)) + continue + + if func: + code_line = "line: {0}, file: {1}\n".format( + d.getVarFlag(var, "lineno", False), + d.getVarFlag(var, "filename", False)) + val = val.rstrip('\n') + logger.warn("Warning: exported shell function %s() is not exported (%s)" % + (varExpanded, code_line)) + continue + + if export: + exported_vars[varExpanded] = val.strip() + continue + + self.exported_vars = exported_vars + + def __init_cmake_preset_cache(self, d): + """Get the arguments passed to cmake + + Replicate the cmake configure arguments with all details to + share on build folder between bitbake and SDK. + """ + site_file = os.path.join(self.workdir, 'site-file.cmake') + if os.path.exists(site_file): + print("Warning: site-file.cmake is not supported") + + cache_vars = {} + oecmake_args = d.getVar('OECMAKE_ARGS').split() + extra_oecmake = d.getVar('EXTRA_OECMAKE').split() + for param in oecmake_args + extra_oecmake: + d_pref = "-D" + if param.startswith(d_pref): + param = param[len(d_pref):] + else: + print("Error: expected a -D") + param_s = param.split('=', 1) + param_nt = param_s[0].split(':', 1) + + def handle_undefined_variable(var): + if var.startswith('${') and var.endswith('}'): + return '' + else: + return var + # Example: FOO=ON + if len(param_nt) == 1: + cache_vars[param_s[0]] = handle_undefined_variable(param_s[1]) + # Example: FOO:PATH=/tmp + elif len(param_nt) == 2: + cache_vars[param_nt[0]] = { + "type": param_nt[1], + "value": handle_undefined_variable(param_s[1]), + } + else: + print("Error: cannot parse %s" % param) + self.cmake_cache_vars = cache_vars + + def cmake_preset(self): + """Create a preset for cmake that mimics how bitbake calls cmake""" + toolchain_file = os.path.join(self.workdir, 'toolchain.cmake') + cmake_executable = os.path.join( + self.recipe_sysroot_native, 'usr', 'bin', 'cmake') + self.cmd_compile = cmake_executable + " --build --preset " + self.recipe_id + + preset_dict_configure = { + "name": self.recipe_id, + "displayName": self.recipe_id_pretty, + "description": "Bitbake build environment for the recipe %s compiled for %s" % (self.bpn, self.package_arch), + "binaryDir": self.b, + "generator": self.oecmake_generator, + "toolchainFile": toolchain_file, + "cacheVariables": self.cmake_cache_vars, + "environment": self.exported_vars, + "cmakeExecutable": cmake_executable + } + + preset_dict_build = { + "name": self.recipe_id, + "displayName": self.recipe_id_pretty, + "description": "Bitbake build environment for the recipe %s compiled for %s" % (self.bpn, self.package_arch), + "configurePreset": self.recipe_id, + "inheritConfigureEnvironment": True + } + + preset_dict_test = { + "name": self.recipe_id, + "displayName": self.recipe_id_pretty, + "description": "Bitbake build environment for the recipe %s compiled for %s" % (self.bpn, self.package_arch), + "configurePreset": self.recipe_id, + "inheritConfigureEnvironment": True + } + + preset_dict = { + "version": 3, # cmake 3.21, backward compatible with kirkstone + "configurePresets": [preset_dict_configure], + "buildPresets": [preset_dict_build], + "testPresets": [preset_dict_test] + } + + logger.info("generating cmake preset for recipe %s" % self.bpn) + + # Finally write the json file + json_file = 'CMakeUserPresets.json' + json_path = os.path.join(self.real_srctree, json_file) + logger.info("Updating CMake preset: %s (%s)" % (json_file, json_path)) + if not os.path.exists(self.real_srctree): + os.makedirs(self.real_srctree) + try: + with open(json_path) as f: + orig_dict = json.load(f) + except json.decoder.JSONDecodeError: + logger.info( + "Decoding %s failed. Probably because of comments in the json file" % json_path) + orig_dict = {} + except FileNotFoundError: + orig_dict = {} + + # Add or update the presets for the recipe and keep other presets + for k, v in preset_dict.items(): + if isinstance(v, list): + update_preset = v[0] + preset_added = False + if k in orig_dict: + for index, orig_preset in enumerate(orig_dict[k]): + if 'name' in orig_preset: + if orig_preset['name'] == update_preset['name']: + logger.debug("Updating preset: %s" % + orig_preset['name']) + orig_dict[k][index] = update_preset + preset_added = True + break + else: + logger.debug("keeping preset: %s" % + orig_preset['name']) + else: + logger.warn("preset without a name found") + if not preset_added: + if not k in orig_dict: + orig_dict[k] = [] + orig_dict[k].append(update_preset) + logger.debug("Added preset: %s" % + update_preset['name']) + else: + orig_dict[k] = v + + with open(json_path, 'w') as f: + json.dump(orig_dict, f, indent=4) + + def gen_meson_wrapper(self): + """Generate a wrapper script to call meson with the cross environment""" + bb.utils.mkdirhier(self.temp_dir) + meson_wrapper = os.path.join(self.temp_dir, 'meson') + meson_real = os.path.join( + self.recipe_sysroot_native, 'usr', 'bin', 'meson.real') + with open(meson_wrapper, 'w') as mwrap: + mwrap.write("#!/bin/sh" + os.linesep) + for var, val in self.exported_vars.items(): + mwrap.write('export %s="%s"' % (var, val) + os.linesep) + mwrap.write("unset CC CXX CPP LD AR NM STRIP" + os.linesep) + private_temp = os.path.join(self.b, "meson-private", "tmp") + mwrap.write('mkdir -p "%s"' % private_temp + os.linesep) + mwrap.write('export TMPDIR="%s"' % private_temp + os.linesep) + mwrap.write('exec "%s" "$@"' % meson_real + os.linesep) + st = os.stat(meson_wrapper) + os.chmod(meson_wrapper, st.st_mode | stat.S_IEXEC) + self.meson_wrapper = meson_wrapper + self.cmd_compile = meson_wrapper + " compile -C " + self.b + + def which(self, executable): + bin_path = shutil.which(executable, path=self.path) + if not bin_path: + raise DevtoolError( + 'Cannot find %s. Probably the recipe %s is not built yet.' % (executable, self.bpn)) + return bin_path + + @staticmethod + def is_elf_file(file_path): + with open(file_path, "rb") as f: + data = f.read(4) + if data == b'\x7fELF': + return True + return False + + def find_installed_binaries(self): + """find all executable elf files in the image directory""" + binaries = [] + d_len = len(self.d) + re_so = re.compile('.*\.so[.0-9]*$') + for root, _, files in os.walk(self.d, followlinks=False): + for file in files: + if os.path.islink(file): + continue + if re_so.match(file): + continue + abs_name = os.path.join(root, file) + if os.access(abs_name, os.X_OK) and RecipeModified.is_elf_file(abs_name): + binaries.append(abs_name[d_len:]) + return binaries + + def gen_fakeroot_install_script(self): + """Generate a helper script to execute make install with pseudo + + For the deployment to the target device the do_install task must be + executed out of the IDE as well. This function generates a script which + runs the run.do_install script from bitbake under pseudo so that it picks + up the appropriate file permissions. Generating a self-contained script + is much quicker than calling bitbake or devtool build from an IDE. + """ + cmd_lines = ['#!/bin/sh'] + # Ensure the do compile step gets always executed without pseuso before do install + # Running do_compile always without pseudo is probably better than trying to have + # all the paths referred by compiling added to PSEUDO_IGNORE_PATHS. + if self.cmd_compile: + cmd_compile = "( cd %s && %s)" % ( + self.real_srctree, self.cmd_compile) + cmd_lines.append(cmd_compile) + if not os.access(self.fakerootcmd, os.X_OK): + raise DevtoolError( + "pseudo executable %s could not be found" % self.fakerootcmd) + run_do_install = os.path.join(self.workdir, 'temp', 'run.do_install') + + if not os.access(run_do_install, os.X_OK): + raise DevtoolError( + "run script does not exists: %s" % run_do_install) + + # Set up the appropriate environment + newenv = dict(os.environ) + for varvalue in self.fakerootenv.split(): + if '=' in varvalue: + splitval = varvalue.split('=', 1) + newenv[splitval[0]] = splitval[1] + + # Replicate the environment variables from bitbake + for var, val in newenv.items(): + cmd_lines.append('export %s="%s"' % (var, val)) + + # Setup the task environment as bitbake would do it based on the varFlags + for d in self.f_do_install_cleandirs: + cmd_lines.append('%s rm -rf %s' % (self.fakerootcmd, d)) + for d in self.f_do_install_dirs: + cmd_lines.append('%s mkdir -p %s' % (self.fakerootcmd, d)) + if len(self.f_do_install_dirs) > 0: + cmd = "cd %s" % self.f_do_install_dirs[-1] + cmd_lines.append('%s || { "%s failed"; exit 1; }' % (cmd, cmd)) + + # Remove the package* folders from TMPDIR. These folders might contain the sources for the -src packages. + # This likely breaks pseudo like: + # path mismatch [3 links]: ino 79147802 db + # .../build/tmp/.../cmake-example/1.0/package/usr/src/debug/cmake-example/1.0-r0/oe-local-files/cpp-example-lib.cpp + # .../build/workspace/sources/cmake-example/oe-local-files/cpp-example-lib.cpp + # Since the files are anyway outdated lets deleted them (also from pseudo's db) to workaround this issue. + pkg_dirs = ' '.join([os.path.join(self.workdir, d) for d in [ + "package", "packages-split", "pkgdata", "sstate-install-package", "debugsources.list", "*.spec"]]) + cmd = "%s rm -rf %s" % (self.fakerootcmd, pkg_dirs) + cmd_lines.append('%s || { "%s failed"; exit 1; }' % (cmd, cmd)) + + # Finally call run.do_install on pseudo + cmd = "%s %s" % (self.fakerootcmd, run_do_install) + cmd_lines.append('%s || { "%s failed"; exit 1; }' % (cmd, cmd)) + + return self.write_script(cmd_lines, 'bb_run_do_install') + + def gen_deploy_target_script(self, args): + """Generate a script which does what devtool deploy-target does + + This script is much quicker than devtool target-deploy. Because it + does not need to start a bitbake server. All information from tinfoil + is hard-coded in the generated script. + """ + cmd_lines = ['#!/usr/bin/env python3'] + cmd_lines.append('import sys') + cmd_lines.append('devtool_sys_path = %s' % str(sys.path)) + cmd_lines.append('devtool_sys_path.reverse()') + cmd_lines.append('for p in devtool_sys_path:') + cmd_lines.append(' if p not in sys.path:') + cmd_lines.append(' sys.path.insert(0, p)') + cmd_lines.append('from devtool.deploy import deploy_cached') + args_filter = ['debug', 'dry_run', 'key', 'no_check_space', 'no_host_check', + 'no_preserve', 'port', 'show_status', 'ssh_exec', 'strip', 'target'] + filtered_args_dict = {key: value for key, value in vars( + args).items() if key in args_filter} + cmd_lines.append('filtered_args_dict = %s' % str(filtered_args_dict)) + cmd_lines.append('class Dict2Class(object):') + cmd_lines.append(' def __init__(self, my_dict):') + cmd_lines.append(' for key in my_dict:') + cmd_lines.append(' setattr(self, key, my_dict[key])') + cmd_lines.append('filtered_args = Dict2Class(filtered_args_dict)') + cmd_lines.append( + 'setattr(filtered_args, "recipename", "%s")' % self.bpn) + cmd_lines.append('deploy_cached("%s", "%s", "%s", "%s", "%s", "%s", %d, "%s", "%s", filtered_args)' % + (self.d, self.workdir, self.path, self.strip_cmd, + self.libdir, self.base_libdir, self.max_process, + self.fakerootcmd, self.fakerootenv)) + return self.write_script(cmd_lines, 'deploy_target') + + def gen_install_deploy_script(self, args): + """Generate a script which does install and deploy""" + cmd_lines = ['#!/bin/sh -e'] + cmd_lines.append(self.gen_fakeroot_install_script()) + cmd_lines.append(self.gen_deploy_target_script(args)) + return self.write_script(cmd_lines, 'install_and_deploy') + + def write_script(self, cmd_lines, script_name): + bb.utils.mkdirhier(self.temp_dir) + script_name_arch = script_name + '_' + self.recipe_id + script_file = os.path.join(self.temp_dir, script_name_arch) + with open(script_file, 'w') as script_f: + script_f.write(os.linesep.join(cmd_lines)) + st = os.stat(script_file) + os.chmod(script_file, st.st_mode | stat.S_IEXEC) + return script_file + + @property + def oe_init_build_env(self): + """Find the oe-init-build-env used for this setup""" + oe_init_dir = self.oe_init_dir + if oe_init_dir: + return os.path.join(oe_init_dir, RecipeModified.OE_INIT_BUILD_ENV) + return None + + @property + def oe_init_dir(self): + """Find the directory where the oe-init-build-env is located + + Assumption: There might be a layer with higher priority than poky + which provides to oe-init-build-env in the layer's toplevel folder. + """ + if not self.__oe_init_dir: + for layer in reversed(self.bblayers): + result = subprocess.run( + ['git', 'rev-parse', '--show-toplevel'], cwd=layer, capture_output=True) + if result.returncode == 0: + oe_init_dir = result.stdout.decode('utf-8').strip() + oe_init_path = os.path.join( + oe_init_dir, RecipeModified.OE_INIT_BUILD_ENV) + if os.path.exists(oe_init_path): + logger.debug("Using %s from: %s" % ( + RecipeModified.OE_INIT_BUILD_ENV, oe_init_path)) + self.__oe_init_dir = oe_init_dir + break + if not self.__oe_init_dir: + logger.error("Cannot find the bitbake top level folder") + return self.__oe_init_dir + + +def ide_setup(args, config, basepath, workspace): + """Generate the IDE configuration for the workspace""" + bootstrap_tasks = [] + bootstrap_tasks_late = [] + devtool_ide_mode = DevtoolIdeMode.UNDEFINED + ide = devtool.ide_handlers.create_ide(args) + + tinfoil = setup_tinfoil(config_only=False, basepath=basepath) + try: + # define mode depending on recipes which need to be processed + recipes_shared_names = [] + recipes_image_names = [] + recipes_modified_names = [] + for recipe in args.recipenames: + if recipe in SHARED_SYSROOT_RECIPES: + recipes_shared_names.append(recipe) + else: + try: + check_workspace_recipe( + workspace, recipe, bbclassextend=True) + recipes_modified_names.append(recipe) + except DevtoolError: + recipe_d = parse_recipe( + config, tinfoil, recipe, appends=True, filter_workspace=False) + if not recipe_d: + raise DevtoolError("Parsing recipe %s failed" % recipe) + if bb.data.inherits_class('image', recipe_d): + recipes_image_names.append(recipe) + else: + raise DevtoolError( + "Recipe is not an image and not a recipe in the workspace.") + + if len(recipes_image_names) > 1: + raise DevtoolError("Max one image recipe must be passed.") + if recipes_image_names and recipes_modified_names: + devtool_ide_mode = DevtoolIdeMode.DEVTOOL_MODIFY + if recipes_shared_names: + raise DevtoolError("Mixing modified recipes (%s) and shared recipes (%s) is not possible." + % (str(recipes_modified_names), str(recipes_shared_names))) + logger.info("Mode: devtool modify") + elif recipes_image_names and recipes_shared_names: + devtool_ide_mode = DevtoolIdeMode.SHARED_SYSROOT + else: + raise DevtoolError("Invalid recipes passed.") + + # Provide a rootfs and the corresponding debug symbols via rootfs-dbg + if devtool_ide_mode == DevtoolIdeMode.DEVTOOL_MODIFY or devtool_ide_mode == DevtoolIdeMode.SHARED_SYSROOT: + logger.info("Using image: %s" % recipes_image_names[0]) + image_config = RecipeImage(recipes_image_names[0]) + image_config.initialize(config, tinfoil) + bootstrap_tasks += image_config.bootstrap_tasks + + if devtool_ide_mode == DevtoolIdeMode.SHARED_SYSROOT: + logger.info("Generating a shared sysroot SDK.") + ide_support = RecipeMetaIdeSupport() + ide_support.initialize(config, tinfoil) + bootstrap_tasks += ide_support.bootstrap_tasks + + build_sysroots = RecipeBuildSysroots() + build_sysroots.initialize(config, tinfoil) + bootstrap_tasks_late += build_sysroots.bootstrap_tasks + shared_env = SharedSysrootsEnv() + shared_env.initialize(ide_support, build_sysroots) + + recipes_modified = [] + if devtool_ide_mode == DevtoolIdeMode.DEVTOOL_MODIFY: + logger.info("Using modified recipe: %s" % + recipes_modified_names[0]) + for modified_recipe_name in recipes_modified_names: + recipe_modified = RecipeModified(modified_recipe_name) + recipe_modified.initialize(config, workspace, tinfoil) + bootstrap_tasks += recipe_modified.bootstrap_tasks + recipes_modified.append(recipe_modified) + + target_device = TargetDevice(args) + gdb_cross = RecipeGdbCross( + args, recipes_modified[0].target_arch, target_device, ide.gdb_multi_mode) + gdb_cross.initialize(config, workspace, tinfoil) + bootstrap_tasks += gdb_cross.bootstrap_tasks + finally: + tinfoil.shutdown() + + if devtool_ide_mode == DevtoolIdeMode.DEVTOOL_MODIFY: + for recipe_modified in recipes_modified: + if not recipe_modified.is_recipe_cross(): + raise DevtoolError( + "Only cross compiled recipes are currently supported.") + recipe_modified.debug_build_config(args) + + if devtool_ide_mode == DevtoolIdeMode.DEVTOOL_MODIFY or devtool_ide_mode == DevtoolIdeMode.SHARED_SYSROOT: + if not args.skip_bitbake: + bb_cmd = 'bitbake ' + if args.bitbake_k: + bb_cmd += "-k " + bb_cmd_early = bb_cmd + ' '.join(bootstrap_tasks) + exec_build_env_command( + config.init_path, basepath, bb_cmd_early, watch=True) + if bootstrap_tasks_late: + bb_cmd_late = bb_cmd + ' '.join(bootstrap_tasks_late) + exec_build_env_command( + config.init_path, basepath, bb_cmd_late, watch=True) + + if (image_config.gdbserver_missing): + logger.warning( + "gdbserver not installed in image. Remote debugging will not be available") + + if devtool_ide_mode == DevtoolIdeMode.SHARED_SYSROOT: + ide.setup_shared_sysroots(shared_env) + elif devtool_ide_mode == DevtoolIdeMode.DEVTOOL_MODIFY: + for recipe_modified in recipes_modified: + ide.setup_modified_recipe( + args, image_config, gdb_cross, recipe_modified) + else: + raise DevtoolError("Must not end up here.") + + +def get_default_ide(): + """Check which IDEs are installed and return a reasonable default setting""" + for an_ide in SUPPORTED_IDES[:-1]: + if shutil.which(an_ide): + return an_ide + return SUPPORTED_IDES[-1:] + + +def register_commands(subparsers, context): + """Register devtool subcommands from this plugin""" + parser_ide = subparsers.add_parser('ide', help='Setup the IDE (VSCode)', + description='Configure the IDE to work with the source code of a recipe.', + group='working', order=50, formatter_class=RawTextHelpFormatter) + parser_ide.add_argument( + 'recipenames', nargs='+', help='Generate an IDE configuration suitable to work on the given recipes.\n' + 'Different types of recipes lead to different types of IDE configurations.\n' + '- devtool modify mode:\n' + ' At least one devtool modified recipe + one image recipe are required:\n' + ' Usage example:\n' + ' devtool modify cmake-example\n' + ' devtool ide cmake-example core-image-minimal\n' + ' The workspace for all package recipes gets configured to use the corresponding per recipe sysroot(s).\n' + ' The image recipes is used to generate the target image, the corresponding remote debug configuration as well as the corresponding SDK.\n' + '- Shared sysroot mode:\n' + ' Usage example:\n' + ' devtool ide ' + SHARED_SYSROOT_RECIPES[0] + '\n' + ' This command generates a bare cross-toolchain as well as the corresponding shared sysroot directories.\n' + ' To use this tool-chain the environment-* file found in the deploy..image folder needs to be sourced into a shell.\n' + ' In case of VSCode and cmake the tool-chain is also exposed as a cmake-kit') + parser_ide.add_argument( + '-i', '--ide', choices=SUPPORTED_IDES, default=get_default_ide(), + help='Setup the configuration for this IDE') + parser_ide.add_argument( + '-t', '--target', default='root@192.168.7.2', + help='Live target machine running an ssh server: user@hostname.') + parser_ide.add_argument( + '-G', '--gdbserver-port-start', default="1234", help='port where gdbserver is listening.') + parser_ide.add_argument( + '-c', '--no-host-check', help='Disable ssh host key checking', action='store_true') + parser_ide.add_argument( + '-e', '--ssh-exec', help='Executable to use in place of ssh') + parser_ide.add_argument( + '-P', '--port', help='Specify ssh port to use for connection to the target') + parser_ide.add_argument( + '-I', '--key', help='Specify ssh private key for connection to the target') + parser_ide.add_argument( + '--skip-bitbake', help='Generate IDE configuration but skip calling bibtake to update the SDK.', action='store_true') + parser_ide.add_argument( + '-k', '--bitbake-k', help='Pass -k parameter to bitbake', action='store_true') + parser_ide.add_argument( + '--no-strip', help='Do not strip executables prior to deploy', dest='strip', action='store_false') + parser_ide.add_argument( + '-n', '--dry-run', help='List files to be undeployed only', action='store_true') + parser_ide.add_argument( + '-s', '--show-status', help='Show progress/status output', action='store_true') + parser_ide.add_argument( + '-p', '--no-preserve', help='Do not preserve existing files', action='store_true') + parser_ide.add_argument( + '--no-check-space', help='Do not check for available space before deploying', action='store_true') + parser_ide.add_argument( + '--debug-build-config', help='Use debug build flags, for example set CMAKE_BUILD_TYPE=Debug', action='store_true') + parser_ide.set_defaults(func=ide_setup) diff --git a/scripts/lib/devtool/ide_handlers/__init__.py b/scripts/lib/devtool/ide_handlers/__init__.py new file mode 100644 index 00000000000..3071b9211a6 --- /dev/null +++ b/scripts/lib/devtool/ide_handlers/__init__.py @@ -0,0 +1,10 @@ + +from devtool.ide_handlers.ide_none import IdeNone +from devtool.ide_handlers.ide_code import IdeVSCode + +def create_ide(args): + """Simple factory for the IDE""" + if args.ide == 'code': + return IdeVSCode(args.ide) + elif args.ide == 'none': + return IdeNone(args.ide) diff --git a/scripts/lib/devtool/ide_handlers/ide_base.py b/scripts/lib/devtool/ide_handlers/ide_base.py new file mode 100644 index 00000000000..b9563166e42 --- /dev/null +++ b/scripts/lib/devtool/ide_handlers/ide_base.py @@ -0,0 +1,46 @@ +import os +import json +import logging + +logger = logging.getLogger('devtool') + + +class IdeBase: + """Base class defining an interface for IDE plugins""" + + def __init__(self, ide_name): + self.ide_name = ide_name + self.gdb_multi_mode = True + + def setup_shared_sysroots(self, shared_env): + logger.warn("Shared sysroot mode is not supported for IDE %s" % + self.ide_name) + + def setup_modified_recipe(self, args, image_config, gdb_cross, modified_recipe): + logger.warn("Modified recipe mode is not supported for IDE %s" % + self.ide_name) + + @staticmethod + def update_json_file(json_dir, json_file, update_dict): + """Update a json file + + By default it uses the dict.update function. If this is not sutiable + the update function might be passed via update_func parameter. + """ + json_path = os.path.join(json_dir, json_file) + logger.info("Updating IDE config file: %s (%s)" % + (json_file, json_path)) + if not os.path.exists(json_dir): + os.makedirs(json_dir) + try: + with open(json_path) as f: + orig_dict = json.load(f) + except json.decoder.JSONDecodeError: + logger.info( + "Decoding %s failed. Probably because of comments in the json file" % json_path) + orig_dict = {} + except FileNotFoundError: + orig_dict = {} + orig_dict.update(update_dict) + with open(json_path, 'w') as f: + json.dump(orig_dict, f, indent=4) diff --git a/scripts/lib/devtool/ide_handlers/ide_code.py b/scripts/lib/devtool/ide_handlers/ide_code.py new file mode 100644 index 00000000000..8496f0a5ce5 --- /dev/null +++ b/scripts/lib/devtool/ide_handlers/ide_code.py @@ -0,0 +1,420 @@ +import os +import json +import logging +from devtool.ide_handlers.ide_base import IdeBase +from devtool.ide import BuildTool + +logger = logging.getLogger('devtool') + + +class IdeVSCode(IdeBase): + """Manage IDE configurations for VSCode + + Recipe mode: + - cmake: generates a cmake-preset + - meson: workspace settings callin the cross meson are gernerated. + + Shared sysroot mode: + In shared sysroot mode, the cross tool-chain is exported to the user's global configuration. + A workspace cannot be created because there is no recipe that defines how a workspace could be set up. + - cmake: adds a cmake-kit to .local/share/CMakeTools/cmake-tools-kits.json + The cmake-kit uses the environment script and the tool-chain file + generated by meta-ide-support. + - meson: Meson needs manual workspace configuration. + """ + + def __init__(self, ide_name): + self.gdb_multi_mode = False + + def setup_shared_sysroots(self, shared_env): + """Expose the toolchain of the dSDK""" + datadir = shared_env.ide_support.datadir + deploy_dir_image = shared_env.ide_support.deploy_dir_image + real_multimach_target_sys = shared_env.ide_support.real_multimach_target_sys + standalone_sysroot_native = shared_env.build_sysroots.standalone_sysroot_native + vscode_ws_path = os.path.join( + os.environ['HOME'], '.local', 'share', 'CMakeTools') + cmake_kits_path = os.path.join(vscode_ws_path, 'cmake-tools-kits.json') + oecmake_generator = "Ninja" + env_script = os.path.join( + deploy_dir_image, 'environment-setup-' + real_multimach_target_sys) + + if not os.path.isdir(vscode_ws_path): + os.makedirs(vscode_ws_path) + cmake_kits_old = [] + if os.path.exists(cmake_kits_path): + with open(cmake_kits_path, 'r', encoding='utf-8') as cmake_kits_file: + cmake_kits_old = json.load(cmake_kits_file) + cmake_kits = cmake_kits_old.copy() + + cmake_kit_new = { + "name": "OE " + real_multimach_target_sys, + "environmentSetupScript": env_script, + "toolchainFile": standalone_sysroot_native + datadir + "/cmake/OEToolchainConfig.cmake", + "preferredGenerator": { + "name": oecmake_generator + } + } + + def merge_kit(cmake_kits, cmake_kit_new): + i = 0 + while i < len(cmake_kits): + if 'environmentSetupScript' in cmake_kits[i] and \ + cmake_kits[i]['environmentSetupScript'] == cmake_kit_new['environmentSetupScript']: + cmake_kits[i] = cmake_kit_new + return + i += 1 + cmake_kits.append(cmake_kit_new) + merge_kit(cmake_kits, cmake_kit_new) + + if cmake_kits != cmake_kits_old: + logger.info("Updating: %s" % cmake_kits_path) + with open(cmake_kits_path, 'w', encoding='utf-8') as cmake_kits_file: + json.dump(cmake_kits, cmake_kits_file, indent=4) + else: + logger.info("Already up to date: %s" % cmake_kits_path) + + cmake_native = os.path.join( + shared_env.build_sysroots.standalone_sysroot_native, 'usr', 'bin', 'cmake') + if os.path.isfile(cmake_native): + logger.info('cmake-kits call cmake by default. If the cmake provided by this SDK should be used, please add the following line to ".vscode/settings.json" file: "cmake.cmakePath": "%s"' % cmake_native) + else: + logger.error("Cannot find cmake native at: %s" % cmake_native) + + def dot_code_dir(self, modified_recipe): + return os.path.join(modified_recipe.srctree, '.vscode') + + def __vscode_settings_meson(self, settings_dict, modified_recipe): + if modified_recipe.build_tool != BuildTool.MESON: + return + settings_dict["mesonbuild.mesonPath"] = modified_recipe.meson_wrapper + + confopts = modified_recipe.mesonopts.split() + confopts += modified_recipe.meson_cross_file.split() + confopts += modified_recipe.extra_oemeson.split() + settings_dict["mesonbuild.configureOptions"] = confopts + settings_dict["mesonbuild.buildFolder"] = modified_recipe.b + + def __vscode_settings_cmake(self, settings_dict, modified_recipe): + """Add cmake specific settings to settings.json. + + Note: most settings are passed to the cmake preset. + """ + if modified_recipe.build_tool != BuildTool.CMAKE: + return + settings_dict["cmake.configureOnOpen"] = True + settings_dict["cmake.sourceDirectory"] = modified_recipe.real_srctree + + def vscode_settings(self, modified_recipe): + files_excludes = { + "**/.git/**": True, + "**/oe-logs/**": True, + "**/oe-workdir/**": True, + "**/source-date-epoch/**": True + } + python_exclude = [ + "**/.git/**", + "**/oe-logs/**", + "**/oe-workdir/**", + "**/source-date-epoch/**" + ] + settings_dict = { + "files.watcherExclude": files_excludes, + "files.exclude": files_excludes, + "python.analysis.exclude": python_exclude + } + self.__vscode_settings_cmake(settings_dict, modified_recipe) + self.__vscode_settings_meson(settings_dict, modified_recipe) + + settings_file = 'settings.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), settings_file, settings_dict) + + def __vscode_extensions_cmake(self, modified_recipe, recommendations): + if modified_recipe.build_tool != BuildTool.CMAKE: + return + recommendations += [ + "twxs.cmake", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.cpptools-themes" + ] + + def __vscode_extensions_meson(self, modified_recipe, recommendations): + if modified_recipe.build_tool != BuildTool.MESON: + return + recommendations += [ + 'mesonbuild.mesonbuild', + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.cpptools-themes" + ] + + def vscode_extensions(self, modified_recipe): + recommendations = [] + self.__vscode_extensions_cmake(modified_recipe, recommendations) + self.__vscode_extensions_meson(modified_recipe, recommendations) + extensions_file = 'extensions.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), extensions_file, {"recommendations": recommendations}) + + def vscode_c_cpp_properties(self, modified_recipe): + properties_dict = { + "name": modified_recipe.recipe_id_pretty, + } + if modified_recipe.build_tool == BuildTool.CMAKE: + properties_dict["configurationProvider"] = "ms-vscode.cmake-tools" + elif modified_recipe.build_tool == BuildTool.MESON: + properties_dict["configurationProvider"] = "mesonbuild.mesonbuild" + else: # no C/C++ build + return + + properties_dicts = { + "configurations": [ + properties_dict + ], + "version": 4 + } + prop_file = 'c_cpp_properties.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), prop_file, properties_dicts) + + def vscode_launch_bin_dbg(self, modified_recipe, image, gdb_cross, binary): + gdb_cross.setup_gdbserver_config(binary, modified_recipe.temp_dir) + pretty_id = gdb_cross.get_gdbserver_pretty_id(binary) + gdbserver_port = gdb_cross.get_gdbserver_port(binary) + + launch_config = { + "name": pretty_id, + "type": "cppdbg", + "request": "launch", + "program": os.path.join(modified_recipe.d, binary.lstrip('/')), + "stopAtEntry": True, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": False, + "MIMode": "gdb", + "preLaunchTask": pretty_id, + "miDebuggerPath": gdb_cross.gdb, + "miDebuggerServerAddress": "%s:%d" % (gdb_cross.host, gdbserver_port) + } + + # Search for header files in recipe-sysroot. + src_file_map = { + "/usr/include": os.path.join(modified_recipe.recipe_sysroot, "usr", "include") + } + # First of all search for not stripped binaries in the image folder. + # These binaries are copied (and optionally stripped) by deploy-target + setup_commands = [ + { + "description": "sysroot", + "text": "set sysroot " + modified_recipe.d + } + ] + + if image.rootfs_dbg: + launch_config['additionalSOLibSearchPath'] = modified_recipe.solib_search_path_str( + image) + src_file_map["/usr/src/debug"] = os.path.join( + image.rootfs_dbg, "usr", "src", "debug") + else: + logger.warning( + "Cannot setup debug symbols configuration for GDB. IMAGE_GEN_DEBUGFS is not enabled.") + + launch_config['sourceFileMap'] = src_file_map + launch_config['setupCommands'] = setup_commands + return launch_config + + def vscode_launch_bin(self, modified_recipe, image, gdb_cross): + """GDB Launch configuration for binaries (elf files)""" + binaries = modified_recipe.find_installed_binaries() + configurations = [self.vscode_launch_bin_dbg(modified_recipe, + image, gdb_cross, binary) for binary in binaries] + launch_dict = { + "version": "0.2.0", + "configurations": configurations + } + launch_file = 'launch.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), launch_file, launch_dict) + + def vscode_launch(self, modified_recipe, image, gdb_cross): + if modified_recipe.build_tool in [BuildTool.CMAKE, BuildTool.MESON, BuildTool.UNDEFINED]: + self.vscode_launch_bin(modified_recipe, image, gdb_cross) + else: + logger.info( + "Generating a launch configuration for this recipe is not yet suported") + + def vscode_tasks_cpp(self, args, modified_recipe, gdb_cross): + run_install_deploy = modified_recipe.gen_install_deploy_script(args) + install_task_name = "install && deploy-target %s" % modified_recipe.recipe_id_pretty + tasks_dict = { + "version": "2.0.0", + "tasks": [ + { + "label": install_task_name, + "type": "shell", + "command": run_install_deploy, + "problemMatcher": [] + } + ] + } + for pretty_id, start_script in gdb_cross.get_gdbserver_start_scripts(): + tasks_dict['tasks'].append( + { + "label": pretty_id, + "type": "shell", + "isBackground": True, + "dependsOn": [ + install_task_name + ], + "command": start_script, + "problemMatcher": [ + { + "pattern": [ + { + "regexp": ".", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": True, + "beginsPattern": ".", + "endsPattern": ".", + } + } + ] + }) + tasks_file = 'tasks.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), tasks_file, tasks_dict) + + def vscode_tasks_fallback(self, args, modified_recipe, gdb_cross=None): + oe_init_dir = modified_recipe.oe_init_dir + oe_init = ". %s > /dev/null && " % modified_recipe.oe_init_build_env + task_devtool_build = "devtool build %s" % modified_recipe.recipe_id_pretty + task_devtool_build_clean = "devtool build %s --clean" % modified_recipe.recipe_id_pretty + task_devtool_deploy = "devtool deploy-target %s" % modified_recipe.recipe_id_pretty + task_devtool_build_deploy = "devtool build & deploy-target %s" % modified_recipe.recipe_id_pretty + deploy_opts = ' '.join(TargetDevice.get_devtool_deploy_opts(args)) + tasks_dict = { + "version": "2.0.0", + "tasks": [ + { + "label": task_devtool_build, + "type": "shell", + "command": "bash", + "linux": { + "options": { + "cwd": oe_init_dir + } + }, + "args": [ + "--login", + "-c", + "%s%s" % (oe_init, task_devtool_build) + ], + "problemMatcher": [] + }, + { + "label": task_devtool_deploy, + "type": "shell", + "command": "bash", + "linux": { + "options": { + "cwd": oe_init_dir + } + }, + "args": [ + "--login", + "-c", + "%s%s %s" % ( + oe_init, task_devtool_deploy, deploy_opts) + ], + "problemMatcher": [] + }, + { + "label": task_devtool_build_deploy, + "dependsOrder": "sequence", + "dependsOn": [ + task_devtool_build, + task_devtool_deploy + ], + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": True + } + }, + { + "label": task_devtool_build_clean, + "type": "shell", + "command": "bash", + "linux": { + "options": { + "cwd": oe_init_dir + } + }, + "args": [ + "--login", + "-c", + "%s%s" % (oe_init, task_devtool_build_clean) + ], + "problemMatcher": [] + } + ] + } + if gdb_cross: + for pretty_id, start_script in gdb_cross.get_gdbserver_start_scripts(): + tasks_dict['tasks'].append( + { + "label": pretty_id, + "type": "shell", + "isBackground": True, + "dependsOn": [ + task_devtool_build_deploy + ], + "command": start_script, + "problemMatcher": [ + { + "pattern": [ + { + "regexp": ".", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": True, + "beginsPattern": ".", + "endsPattern": ".", + } + } + ] + }) + tasks_file = 'tasks.json' + IdeBase.update_json_file( + self.dot_code_dir(modified_recipe), tasks_file, tasks_dict) + + def vscode_tasks(self, args, modified_recipe, gdb_cross): + if modified_recipe.build_tool in [BuildTool.CMAKE, BuildTool.MESON]: + self.vscode_tasks_cpp(args, modified_recipe, gdb_cross) + else: + self.vscode_tasks_fallback(args, modified_recipe, gdb_cross) + + def setup_modified_recipe(self, args, image_config, gdb_cross, modified_recipe): + if modified_recipe.build_tool == BuildTool.CMAKE: + modified_recipe.cmake_preset() + if modified_recipe.build_tool == BuildTool.MESON: + modified_recipe.gen_meson_wrapper() + + self.vscode_settings(modified_recipe) + self.vscode_extensions(modified_recipe) + self.vscode_c_cpp_properties(modified_recipe) + if args.target: + self.vscode_launch(modified_recipe, image_config, gdb_cross) + self.vscode_tasks(args, modified_recipe, gdb_cross) diff --git a/scripts/lib/devtool/ide_handlers/ide_none.py b/scripts/lib/devtool/ide_handlers/ide_none.py new file mode 100644 index 00000000000..6a2e9d4a655 --- /dev/null +++ b/scripts/lib/devtool/ide_handlers/ide_none.py @@ -0,0 +1,91 @@ + +import os +import logging +from devtool.ide_handlers.ide_base import IdeBase +from devtool.ide import BuildTool + +logger = logging.getLogger('devtool') + + +class IdeNone(IdeBase): + """Generate some generic helpers for other IDEs + + Recipe mode: + Generate some helper scripts for femote debugging with GDB + + Shared sysroot mode: + A wrapper for bitbake meta-ide-support and bitbake build-sysroots + """ + + def setup_shared_sysroots(self, shared_env): + real_multimach_target_sys = shared_env.ide_support.real_multimach_target_sys + deploy_dir_image = shared_env.ide_support.deploy_dir_image + env_script = os.path.join( + deploy_dir_image, 'environment-setup-' + real_multimach_target_sys) + logger.info( + "To use this SDK please source this: %s" % env_script) + + @staticmethod + def get_unique_gdbinit_name(binary): + return 'gdbinit' + binary.replace(os.sep, '-') + + def none_launch(self, args, modified_recipe, image, gdb_cross): + """generate some helper scripts + + - an install (running on pseudo) and deploy script + - a gdbinit file per executable + """ + # install and deploy helper scripts + script_path = modified_recipe.gen_install_deploy_script(args) + logger.info("Created helper script: %s" % script_path) + + # gdbinit + binaries = modified_recipe.find_installed_binaries() + for binary in binaries: + gdb_cross.setup_gdbserver_config(binary, modified_recipe.temp_dir) + gdbserver_port = str(gdb_cross.get_gdbserver_port(binary)) + if gdb_cross.gdbserver_multi: + target_help = '# gdbserver --multi :' + gdbserver_port + remote_cmd = 'target extended-remote ' + else: + target_help = '# gdbserver :' + gdbserver_port + ' ' + binary + remote_cmd = 'target remote ' + gdbinit_path = os.path.join( + modified_recipe.real_srctree, IdeNone.get_unique_gdbinit_name(binary)) + + gdbinit_lines = ['# This file is generated by devtool ide'] + gdbinit_lines.append('# On the remote target:') + gdbinit_lines.append(target_help) + gdbinit_lines.append('# On the build machine:') + gdbinit_lines.append('# cd ' + modified_recipe.real_srctree) + gdbinit_lines.append( + '# ' + gdb_cross.gdb + ' -ix ' + gdbinit_path) + + gdbinit_lines.append('set sysroot ' + modified_recipe.d) + gdbinit_lines.append('set substitute-path "/usr/include" "' + + os.path.join(modified_recipe.recipe_sysroot, 'usr', 'include') + '"') + # Disable debuginfod for now, the IDE configuration uses rootfs-dbg from the image workdir. + gdbinit_lines.append('set debuginfod enabled off') + if image.rootfs_dbg: + gdbinit_lines.append( + 'set solib-search-path "' + modified_recipe.solib_search_path_str(image) + '"') + gdbinit_lines.append('set substitute-path "/usr/src/debug" "' + os.path.join( + image.rootfs_dbg, 'usr', 'src', 'debug') + '"') + gdbinit_lines.append( + remote_cmd + gdb_cross.host + ':' + gdbserver_port) + gdbinit_lines.append('set remote exec-file ' + binary) + gdbinit_lines.append( + 'run ' + os.path.join(modified_recipe.d, binary)) + + with open(gdbinit_path, 'w') as gdbinit_file: + gdbinit_file.write('\n'.join(gdbinit_lines)) + logger.info("Created .gdbinit: %s" % gdbinit_path) + + def setup_modified_recipe(self, args, image_config, gdb_cross, modified_recipe): + if modified_recipe.build_tool == BuildTool.CMAKE: + modified_recipe.cmake_preset() + if modified_recipe.build_tool == BuildTool.MESON: + modified_recipe.gen_meson_wrapper() + + if modified_recipe.build_tool == BuildTool.CMAKE or modified_recipe.build_tool == BuildTool.MESON: + self.none_launch(args, modified_recipe, image_config, gdb_cross) From patchwork Mon Oct 30 21:32:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33137 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 07D3AC4167B for ; Mon, 30 Oct 2023 21:32:26 +0000 (UTC) Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) by mx.groups.io with SMTP id smtpd.web11.171853.1698701536831139895 for ; Mon, 30 Oct 2023 14:32:17 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=KutDc4zP; spf=pass (domain: gmail.com, ip: 209.85.128.51, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4084de32db5so41008025e9.0 for ; Mon, 30 Oct 2023 14:32:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701535; x=1699306335; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=p7l7G9b1MZ0WQ6L577JbeipKYmQCIk2VhImoMxWD7oU=; b=KutDc4zPmy7kSSBVWWiH1ofDhbn5Nt0dlQvtgR3Ne/3VpO4NiByMtxKfPHzmarg2YF m67ehyMEriVwBzE8CNN8Meq54gU739YnGRzUvIcVQCbzobXVh5zt9yu6g6D04BaZMWBw Q4nj0A4bL/c+JI3cw+xhG2K8yj9+GH1BoLe51TJ0kcNXdUNgpVj31SossGbAosRnjzGV ZXlAbwhq2CWfW6jOnl9MEW6nqspikAQmYEnXUG+dqDPWdfyiW+sZanGPyDyRjyovl5v1 CaASBZc2qkmgqK4hOj/aJA1Dv0c2+l9cjr20INCpS0BA1ztqbaZkfK/PIc8FegMSORWY e3jg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701535; x=1699306335; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=p7l7G9b1MZ0WQ6L577JbeipKYmQCIk2VhImoMxWD7oU=; b=J2UdTGBrxktyMqcHzU9YC4ZgA/U5v7/12xCc/gppE4Pp1JnvZUf4m+VVYfPrLUujE/ Kl2txEP1l9JMCJcPFAQweZjJDNTuRuNcU/PeQGI8m4HxkItng1HV4/fGq3YS4zqKxqW2 Ys6xAzh7KeI4UgrfXTgBPLQPyHye2K7rrUvZqXUbkE3oSWqZ6MrdhdZbiscSlSL/2syJ P8wTsYcp3Vnz6SvduKUiDftBJ70LYoZh2Xdz9eqRieE/MEtO84ycCPXpDStahY4AXMCV lW7r7IBvIJUXRMCcObGaXWM1dmySYUj570LqEYsBvWf39zIWaJnqFOCliqvV2cjyYfp4 +Yhg== X-Gm-Message-State: AOJu0YxWR9cfmRzymctiKw+RDmS8H86KnsX+zgXPE0iv3XfPvcUzx6QZ b8KpUsPPJQ8u87KNZp2xQg0GkbGQBko= X-Google-Smtp-Source: AGHT+IEiE33NKj388rby6LDG2k+njIt1cI+nFxr8A1YR5ZlKJmooZy1TrtSQxcZFEjDDU+MN5Hgnzg== X-Received: by 2002:a05:600c:5493:b0:408:3ab3:a029 with SMTP id iv19-20020a05600c549300b004083ab3a029mr9708996wmb.38.1698701534886; Mon, 30 Oct 2023 14:32:14 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:14 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 7/8] oe-selftest devtool: ide tests Date: Mon, 30 Oct 2023 22:32:04 +0100 Message-ID: <20231030213205.2824790-8-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:25 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189819 Add some oe-selftests for the new devtool ide plugin. Most of the workflows are covered. Many thanks to Enguerrand de Ribaucourt for testing and bug fixing. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 274 ++++++++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index b5c488be8e8..f7c478423ce 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -11,6 +11,7 @@ import tempfile import glob import fnmatch import unittest +import json from oeqa.selftest.case import OESelftestTestCase from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer @@ -2199,3 +2200,276 @@ class DevtoolUpgradeTests(DevtoolBase): #Step 4.5 runCmd("grep %s %s" % (modconfopt, codeconfigfile)) + + + +class DevtoolIdeTests(DevtoolBase): + def __write_bb_config(self, recipe_names): + """Helper to write the bitbake local.conf file""" + conf_lines = [ + 'IMAGE_GEN_DEBUGFS = "1"', + 'IMAGE_INSTALL:append = " gdbserver %s"' % ' '.join([r + '-ptest' for r in recipe_names]) + ] + self.write_config("\n".join(conf_lines)) + + def __devtool_ide_recipe(self, recipe_name, build_file, testimage): + """Setup a recipe for working with devtool ide + + Basically devtool modify -x followed by some tests + """ + tempdir = tempfile.mkdtemp(prefix='devtoolqa') + self.track_for_cleanup(tempdir) + self.track_for_cleanup(self.workspacedir) + self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name) + self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') + + result = runCmd('devtool modify %s -x %s' % (recipe_name, tempdir)) + self.assertExists(os.path.join(tempdir, build_file), + 'Extracted source could not be found') + self.assertExists(os.path.join(self.workspacedir, 'conf', + 'layer.conf'), 'Workspace directory not created') + matches = glob.glob(os.path.join(self.workspacedir, + 'appends', recipe_name + '.bbappend')) + self.assertTrue(matches, 'bbappend not created %s' % result.output) + + # Test devtool status + result = runCmd('devtool status') + self.assertIn(recipe_name, result.output) + self.assertIn(tempdir, result.output) + self._check_src_repo(tempdir) + + # Usually devtool ide would initiate the build of the SDK. + # But there is a circular dependency with starting Qemu and passing the IP of runqemu to devtool ide. + bitbake("%s qemu-native qemu-helper-native" % testimage) + deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') + self.add_command_to_tearDown('bitbake -c clean %s' % testimage) + self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage)) + + return tempdir + + def __get_recipe_ids(self, recipe_name): + bb_vars = get_bb_vars(['PACKAGE_ARCH']) + package_arch = bb_vars['PACKAGE_ARCH'] + recipe_id = recipe_name + "-" + package_arch + recipe_id_pretty = recipe_name + ": " + package_arch + return (recipe_id, recipe_id_pretty) + + def __verify_install_script_code(self, tempdir, recipe_name): + """Verify the scripts referred by the tasks.json file are fine. + + This function does not depend on Qemu. Therefore it verifies the scripts + exists and the install step works as expected. But it does not try to + deploy to Qemu. + """ + recipe_id, recipe_id_pretty = self.__get_recipe_ids(recipe_name) + with open(os.path.join(tempdir, '.vscode', 'tasks.json')) as tasks_j: + tasks_d = json.load(tasks_j) + tasks = tasks_d["tasks"] + task_install = next((task for task in tasks if task["label"] == "install && deploy-target %s" % recipe_id_pretty), None) + self.assertIsNot(task_install, None) + install_deploy_cmd = task_install["command"] + # execute only the bb_run_do_install script since the deploy would require e.g. Qemu running. + workspace_temp = os.path.join(self.builddir, 'workspace', 'temp', recipe_name) + i_and_d_script = "install_and_deploy_" + recipe_id + i_and_d_script_path = os.path.join(workspace_temp, i_and_d_script) + self.assertExists(i_and_d_script_path) + i_script = "bb_run_do_install_" + recipe_id + install_cmd = install_deploy_cmd.replace(i_and_d_script, i_script) + install_cmd_path = os.path.join(workspace_temp, install_cmd) + self.assertExists(install_cmd_path) + runCmd(install_cmd, cwd=tempdir) + + def __devtool_ide_qemu(self, tempdir, qemu, recipe_name, example_exe): + """Verify deployment and execution in Qemu system work for one recipe. + + This function checks the entire SDK workflow: changing the code, recompiling + it and deploying it back to Qemu, and checking that the changes have been + incorporated into the provided binaries. It also runs the tests of the recipe. + """ + recipe_id, _ = self.__get_recipe_ids(recipe_name) + i_and_d_script = "install_and_deploy_" + recipe_id + install_deploy_cmd = os.path.join(self.builddir, 'workspace', 'temp', recipe_name, i_and_d_script) + self.assertExists(install_deploy_cmd, '%s script not found' % install_deploy_cmd) + runCmd(install_deploy_cmd) + + MAGIC_STRING_ORIG = "Magic: 123456789" + MAGIC_STRING_NEW = "Magic: 987654321" + ptest_cmd = "ptest-runner " + recipe_name + + # validate that SSH is working + status, _ = qemu.run("uname") + self.assertEqual(status, 0, msg="Failed to connect to the SSH server on Qemu") + + # Verify the unmodified example prints the magic string + status, output = qemu.run(example_exe) + self.assertEqual(status, 0, msg="%s failed: %s" % (example_exe, output)) + self.assertIn(MAGIC_STRING_ORIG, output) + + # Verify the unmodified ptests work + status, output = qemu.run(ptest_cmd) + self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output)) + self.assertIn("PASS: cpp-example-lib", output) + + # Replace the Magic String in the code, compile and deploy to Qemu + cpp_example_lib_hpp = os.path.join(tempdir, 'cpp-example-lib.hpp') + with open(cpp_example_lib_hpp, 'r') as file: + cpp_code = file.read() + cpp_code = cpp_code.replace(MAGIC_STRING_ORIG, MAGIC_STRING_NEW) + with open(cpp_example_lib_hpp, 'w') as file: + file.write(cpp_code) + runCmd(install_deploy_cmd, cwd=tempdir) + + # Verify the modified example prints the modified magic string + status, output = qemu.run(example_exe) + self.assertEqual(status, 0, msg="%s failed: %s" % (example_exe, output)) + self.assertNotIn(MAGIC_STRING_ORIG, output) + self.assertIn(MAGIC_STRING_NEW, output) + + # Verify the modified example ptests work + status, output = qemu.run(ptest_cmd) + self.assertEqual(status, 0, msg="%s failed: %s" % (ptest_cmd, output)) + self.assertIn("PASS: cpp-example-lib", output) + + def __verify_cmake_preset(self, tempdir): + """Verify the generated cmake preset works as expected + + Check if compiling works + Check if unit tests can be executed in qemu (not qemu-system) + """ + with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j: + cmake_preset_d = json.load(cmake_preset_j) + config_presets = cmake_preset_d["configurePresets"] + self.assertEqual(len(config_presets), 1) + cmake_exe = config_presets[0]["cmakeExecutable"] + preset_name = config_presets[0]["name"] + + # Verify the wrapper for cmake native is available + self.assertExists(cmake_exe) + + # Verify the cmake preset generated by devtool ide is available + result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir) + self.assertIn(preset_name, result.output) + + # Verify cmake re-uses the o files compiled by bitbake + result = runCmd('%s --build --preset %s' % (cmake_exe, preset_name), cwd=tempdir) + self.assertIn("ninja: no work to do.", result.output) + + # Verify the unit tests work (in Qemu user mode) + result = runCmd('%s --build --preset %s --target test' % (cmake_exe, preset_name), cwd=tempdir) + self.assertIn("100% tests passed", result.output) + + # Verify re-building and testing works again + result = runCmd('%s --build --preset %s --target clean' % (cmake_exe, preset_name), cwd=tempdir) + self.assertIn("Cleaning", result.output) + result = runCmd('%s --build --preset %s' % (cmake_exe, preset_name), cwd=tempdir) + self.assertIn("Building", result.output) + self.assertIn("Linking", result.output) + result = runCmd('%s --build --preset %s --target test' % (cmake_exe, preset_name), cwd=tempdir) + self.assertIn("Running tests...", result.output) + self.assertIn("100% tests passed", result.output) + + @OETestTag("runqemu") + def test_devtool_ide_none_qemu(self): + """Start qemu-system and run tests for multiple recipes. ide=none is used.""" + recipe_names = ["cmake-example", "meson-example"] + testimage = "oe-selftest-image" + + self.__write_bb_config(recipe_names) + self._check_runqemu_prerequisites() + + # Verify deployment to Qemu (system mode) works + bitbake(testimage) + with runqemu(testimage, runqemuparams="nographic") as qemu: + # cmake-example recipe + recipe_name = "cmake-example" + example_exe = "cmake-example" + build_file = "CMakeLists.txt" + tempdir = self.__devtool_ide_recipe(recipe_name, build_file, testimage) + bitbake_sdk_cmd = 'devtool ide %s %s -t root@%s -c --ide=none' % (recipe_name, testimage, qemu.ip) + runCmd(bitbake_sdk_cmd) + self.__verify_cmake_preset(tempdir) + self.__devtool_ide_qemu(tempdir, qemu, recipe_name, example_exe) + + # meson-example recipe + recipe_name = "meson-example" + example_exe = "mesonex" + build_file = "meson.build" + tempdir = self.__devtool_ide_recipe(recipe_name, build_file, testimage) + bitbake_sdk_cmd = 'devtool ide %s %s -t root@%s -c --ide=none' % (recipe_name, testimage, qemu.ip) + runCmd(bitbake_sdk_cmd) + self.__devtool_ide_qemu(tempdir, qemu, recipe_name, example_exe) + + + def test_devtool_ide_code_cmake(self): + """Verify a cmake recipe works with ide=code mode""" + recipe_name = "cmake-example" + build_file = "CMakeLists.txt" + testimage = "oe-selftest-image" + + self.__write_bb_config([recipe_name]) + tempdir = self.__devtool_ide_recipe(recipe_name, build_file, testimage) + bitbake_sdk_cmd = 'devtool ide %s %s -t root@192.168.17.17 -c --ide=code' % (recipe_name, testimage) + runCmd(bitbake_sdk_cmd) + self.__verify_cmake_preset(tempdir) + self.__verify_install_script_code(tempdir, recipe_name) + + def test_devtool_ide_code_meson(self): + """Verify a meson recipe works with ide=code mode""" + recipe_name = "meson-example" + build_file = "meson.build" + testimage = "oe-selftest-image" + + self.__write_bb_config([recipe_name]) + tempdir = self.__devtool_ide_recipe(recipe_name, build_file, testimage) + bitbake_sdk_cmd = 'devtool ide %s %s -t root@192.168.17.17 -c --ide=code' % (recipe_name, testimage) + runCmd(bitbake_sdk_cmd) + + with open(os.path.join(tempdir, '.vscode', 'settings.json')) as settings_j: + settings_d = json.load(settings_j) + meson_exe = settings_d["mesonbuild.mesonPath"] + meson_build_folder = settings_d["mesonbuild.buildFolder"] + + # Verify the wrapper for meson native is available + self.assertExists(meson_exe) + + # Verify meson re-uses the o files compiled by bitbake + result = runCmd('%s compile -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) + self.assertIn("ninja: no work to do.", result.output) + + # Verify the unit tests work (in Qemu) + runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) + + # Verify re-building and testing works again + result = runCmd('%s compile -C %s --clean' % (meson_exe, meson_build_folder), cwd=tempdir) + self.assertIn("Cleaning...", result.output) + result = runCmd('%s compile -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) + self.assertIn("Linking target", result.output) + runCmd('%s test -C %s' % (meson_exe, meson_build_folder), cwd=tempdir) + + self.__verify_install_script_code(tempdir, recipe_name) + + def test_devtool_ide_shared_sysroots(self): + """Verify the shared sysroot SDK without a recipe works fine.""" + result = runCmd('devtool ide none oe-selftest-image') + bb_vars = get_bb_vars(['MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE']) + environment_script = 'environment-setup-%s' % bb_vars['MULTIMACH_TARGET_SYS'] + deploydir = bb_vars['DEPLOY_DIR_IMAGE'] + environment_script_path = os.path.join(deploydir, environment_script) + + # Verify the printed note really referres to a cmake executable + cmake_native = "" + for line in result.output.splitlines(): + m = re.search(r'"cmake.cmakePath": "(.*)"', line) + if m: + cmake_native = m.group(1) + break + + self.assertExists(cmake_native) + self.assertExists(environment_script_path) + + # Verify building the cmake-example works + cmake_example_src = os.path.join(bb_vars['COREBASE'], 'meta-selftest', 'recipes-test', 'cpp', 'files') + tempdir = tempfile.mkdtemp(prefix='devtoolqa') + self.track_for_cleanup(tempdir) + result = runCmd('%s %s' % (cmake_native, cmake_example_src), cwd=tempdir) + result = runCmd('%s --build %s' % (cmake_native, tempdir), cwd=tempdir) From patchwork Mon Oct 30 21:32:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 33136 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E1796C4167D for ; Mon, 30 Oct 2023 21:32:25 +0000 (UTC) Received: from mail-lj1-f182.google.com (mail-lj1-f182.google.com [209.85.208.182]) by mx.groups.io with SMTP id smtpd.web11.171854.1698701537959593326 for ; Mon, 30 Oct 2023 14:32:18 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=aZxu3FBC; spf=pass (domain: gmail.com, ip: 209.85.208.182, mailfrom: adrian.freihofer@gmail.com) Received: by mail-lj1-f182.google.com with SMTP id 38308e7fff4ca-2c50cd16f3bso69272281fa.2 for ; Mon, 30 Oct 2023 14:32:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698701536; x=1699306336; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=HTHpTb/9HDayDKDafdZ//W3sAFKlkff/Fkn6MaPNR+Y=; b=aZxu3FBCQkEcmtuWUgfhtuwhmpVwHsGBS4VjSekYQtC4zKTu53L9i5pl7qdgd5gCBC J4yL0gy002HXmo3OeObKuGbIPDUabbWNhUxfi5ytGqe+whfQwE+K8P+dK/DitY7LIDod BVxg4RuPAoXvNdEfN1H4VT3EmJ2vIDvUvGtfYztTe6FYj0DdXVTa5LkhaKOngVuQC96X 7zU9rkabszDR4IaH2TE5gnQbBJbuf2Oremz9j8RjTGQ318uGkXEuL80Hz9USsvTLoOiT upsUvWGdkBfof+l5RQ4mUdnpgY3goYqXAHXBruRtUM2ENM3jnKTVatUT2YjzzW9sgT/s uYlQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698701536; x=1699306336; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HTHpTb/9HDayDKDafdZ//W3sAFKlkff/Fkn6MaPNR+Y=; b=HdMggeDEUR8sYxHOh36Usp/F20aHvnd1AaX3UFQoXm44fpDHonHZ9514mmE/Eygsm3 gqsFZsT+N7Sr2zA1HynyVdt6vGi3G3x8qqk2lblw9RpFcW69Jai4XSNjbKPrnXTWeISx ejnMVKHWYIWXjkzMLvas4f3J7VzwrYEDi/vAV62P1i6XXwT0v2iTZosNQeEeOcdxPQXR Mw8Rep2MJwC9ugEU/3HI6szmIs3FR6FPo8SVKRShOeIZtlPi0x26AyyfuC6Pq7kp9n2d ghwCyuEazeP4TtVg0+b55QRVx9Jg1puQvWuVITYrTHHU5wgn7LSQRGWxjGieqzjW+XXb AL4A== X-Gm-Message-State: AOJu0YwKIEj/qwHZouS0xXLyDPq5SlpyhFR/oFm00Qg/RfSAPIQOla2i 2R6LhpAQl2rIPj22n9m1uZ51i7QrxVA= X-Google-Smtp-Source: AGHT+IGnVq/LGNKQ8DnAH8FQEd3CnCtjIPleVUvlgeaHD9UxnNj7IRT5hsK9P/+MFqRU0rWM3G+chg== X-Received: by 2002:a2e:95d7:0:b0:2c5:11e7:6d20 with SMTP id y23-20020a2e95d7000000b002c511e76d20mr8826613ljh.35.1698701535583; Mon, 30 Oct 2023 14:32:15 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id h6-20020a05600c350600b003fc0505be19sm304811wmq.37.2023.10.30.14.32.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Oct 2023 14:32:15 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v7 8/8] docs: cover devtool ide Date: Mon, 30 Oct 2023 22:32:05 +0100 Message-ID: <20231030213205.2824790-9-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231030213205.2824790-1-adrian.freihofer@siemens.com> References: <20231030213205.2824790-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 30 Oct 2023 21:32:25 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/189820 Cover the new devtool ide plugin in the extensible sdk section. Many thanks to Enguerrand de Ribaucourt for his re-view and contributions. Signed-off-by: Adrian Freihofer --- documentation/sdk-manual/extensible.rst | 158 +++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) diff --git a/documentation/sdk-manual/extensible.rst b/documentation/sdk-manual/extensible.rst index 355c6cb0e4a..361ca091fbf 100644 --- a/documentation/sdk-manual/extensible.rst +++ b/documentation/sdk-manual/extensible.rst @@ -230,13 +230,15 @@ all the commands. See the ":doc:`/ref-manual/devtool-reference`" section in the Yocto Project Reference Manual. -Three ``devtool`` subcommands provide entry-points into development: +Four ``devtool`` subcommands provide entry-points into development: - *devtool add*: Assists in adding new software to be built. - *devtool modify*: Sets up an environment to enable you to modify the source of an existing component. +- *devtool ide*: Generates a configuration for an IDE. + - *devtool upgrade*: Updates an existing recipe so that you can build it for an updated set of source files. @@ -614,6 +616,160 @@ command: decide you do not want to proceed with your work. If you do use this command, realize that the source tree is preserved. +Use ``devtool ide`` to generate a configuration for the IDE +----------------------------------------------------------- + +``devtool ide`` automatically configures IDEs for cross-compiling and remote debugging. + +Two different use cases are supported: + +#. *Recipe mode*: Generate the IDE configuration for a workspace created by ``devtool modify``. + + In order to use the tool, a few settings must be made. + As a starting example, the following lines of code can be added to the local.conf file. + + .. code-block:: + + # Build the companion debug file system + IMAGE_GEN_DEBUGFS = "1" + # Optimize build time: with devtool ide the dbg tar is not needed + IMAGE_FSTYPES_DEBUGFS = "" + + # ssh is mandatory, no password simplifies the usage + EXTRA_IMAGE_FEATURES += "\ + ssh-server-openssh \ + debug-tweaks \ + " + + # Remote debugging needs the gdbserver on the target device + IMAGE_INSTALL:append = " gdbserver" + + Assuming the development environment is set up correctly and a workspace has been created + for the recipe using ``devtool modify recipe``, the following command can create the + configuration for VSCode in the recipe workspace: + + .. code-block:: + + $ devtool ide recipe core-image-minimal --target root@192.168.7.2 + + What this command does exactly depends on the recipe or the build tool used by the recipe. + Currently, only CMake and Meson are supported natively. + + For a recipe which inherits ``cmake`` it does: + + - Prepare the SDK by calling ``bitbake core-image-minimal``, ``gdb-cross``, ``qemu-native``... + + - Generate a cmake-preset with configures CMake to use exactly the same environent and + the same cmake-cache configuration as used by ``bitbake recipe``. The cmake-preset referres + to the per-recipe-sysroot of the recipe. + + Currently Configure, Build and Test presets are supported. Test presets execute the test + binaries with Qemu. + + - Generates a helper script to handle the ``do_install`` with pseudo + + - Generates some helper scripts to start ``gdbserver`` on the target device + + - Generates the ``.vscode`` folder containing the following files: + + - ``c_ccp_properties.json``: configure the code navigation + + - ``extensions.json``: Recommend the extensions which are used. + + - ``launch.json``: Provide a configuration for remote debugging with ``gdb-cross`` and ``gdbserver``. + The debug-symbols are searched in the build-folder, the per-recipe-sysroot and the rootfs-dbg + folder which is provided by the image. + + - ``settings.json``: configure the indexer to ignore the build folders. + + - ``tasks.json``: Provide some helpers for running + + - do_install and ``devtool deploy-target`` + + - start the ``gdbserver`` via ssh + + For a recipe which inherits meson a similar configuration is generated. + Because there is nothing like a meson-preset a wrapper script for meson is generated. + + It's possible to pass multiple recipes to the ``devtool ide`` command. + ``devtool ide`` tries to handle the recipes in a reasonable way if possible. + + ``devtool ide`` aims to support multiple programming languages and multiple IDEs natively. + Native means that the IDE is configured to call the build tool (e.g. cmake or meson) directly. + This has several advantages. First of all, it is much faster than ``devtool build``, for example. + But it also allows to use the very good integration of tools like CMake or GDB directly with VSCode or other IDEs. + However, supporting many programming languages and multiple IDEs is quite an elaborate and constantly evolving thing. + To handle combinations that are not natively supported, devtool ide creates a configuration that falls back + to ``devtool build`` and ``devtool deploy-target`` if there is no native support available. + + The default IDE is VSCode. Some hints about using VSCode: + + - To work with CMake press ``Ctrl + Shift + p``, type ``cmake``. + This will show some possible commands like selecting a CMake preset, compiling or running ctest. + A cmake-kit might be activated by ``Ctrl + Shift + p``, type ``cmake`` quick start, + if not preset file is in the wokspace. + + - To work with meson press ``Ctrl + Shift + p``, type meson. + This will show some possible commands like compiling or executing the unit tests. + + - For the deployment to the target device, just press ``Ctrl + Shift + p``, type task. + Select the install & deploy task. + + - For remote debugging, switch to the debugging view by pressing the play button with the bug on the left side. + This will provide a green play button with a drop-down list where a debug configuration can be selected. + After selecting one of the generated configurations, press the play button. + + Additionally ``--ide=none`` is supported. + With the none IDE some generic configurations files like ``.gdbinit`` files and some helper scripts + are generated. + + .. note:: + + To ensure that the debug symbols on the build machine match the binaries running on the target system, + it is essential that the image built by ``devtool ide`` is running on the target system. + +#. *Shared sysroots mode*: Generate the IDE configuration for using a cross-toolchain as provided by + ``bitbake meta-ide-support build-sysroots``. + + For some special recipes and use cases a per-recipe-sysroot based SDK is not suitable. + Therefore ``devtool ide`` also supports setting up the shared sysroots environment and generating + a IDE configurations referring to the shared sysroots. Recipes leading to a shared sysroot + are for example meta-ide-support or shared-sysroots. Also passing none as a recipe name leads + to a shared sysroot SDK. + + .. code-block:: + + $ devtool ide none core-image-minimal + + In case of a shared sysroot SDK the configuration which gets generated for VSCode exposes the + cross-tool-chain as a cmake-kit. If a cmake project is loaded into VSCode the cross-toolchain + can be selected for compiling. + + For some special recipes and use cases a recipe-sysroot based SDK is not suitable. + Therefore ``devtool ide`` also supports setting up the shared sysroots environment and generating + a IDE configurations referring to the shared sysroots. Recipes leading to a shared sysroot + are for example meta-ide-support or shared-sysroots. Also passing none as a recipe name leads + to a shared sysroot SDK. + + .. code-block:: + + $ devtool ide none core-image-minimal + + In case of a shared sysroot SDK the configuration which gets generated for VSCode exposes the + cross-tool-chain as a cmake-kit. If a CMake project is loaded into VSCode the cross-toolchain + can be selected for compiling. The following example shows how the cross-toolchain can be + selected in VSCode. + + + .. code-block:: + + mkdir kit-test + echo "project(foo VERSION 0.1)" > kit-test/CMakeLists.txt + code kit-test + + Ctrl + Shift + P --> CMake: Scan for Kits + Ctrl + Shift + P --> CMake: Select a Kit + Use ``devtool upgrade`` to Create a Version of the Recipe that Supports a Newer Version of the Software -------------------------------------------------------------------------------------------------------