vscode-cmake-tools icon indicating copy to clipboard operation
vscode-cmake-tools copied to clipboard

[Bug] 关于framework和include的处理的bug

Open debugee opened this issue 4 months ago • 13 comments

Brief Issue Summary

这些bug是关于cmake、vscode-cmake-tools、cpptools在处理framework和include的路径的若干问题

1、我们先创建一个测试工程bug-sample,工程文件如下

Image

2、我们要知道搜索路径的优先级(同级别的标志设置的路径,谁在前面谁先优先级高) -I(或者-F) 添加的路径, -isystem 添加的路径 -iframework 添加的路径 编译器默认路径(c++头文件、编译器内置的头文件、c头文件、工具链额外头文件 、还有默认的sdk framework路径) /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1 /Library/Developer/CommandLineTools/usr/lib/clang/16/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include /Library/Developer/CommandLineTools/usr/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)

3、我们对搜索路径优先级做一下测试 编译命令:clang -F /Users/admin/Desktop/bug-sample/build -I /Users/admin/Desktop/bug-sample/abc sample.cpp -c -v 输出: ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include" ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/Library/Frameworks" #include "..." search starts here: #include <...> search starts here: /Users/admin/Desktop/bug-sample/build (framework directory) /Users/admin/Desktop/bug-sample/abc /usr/local/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1 /Library/Developer/CommandLineTools/usr/lib/clang/16/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include /Library/Developer/CommandLineTools/usr/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory) End of search list. In file included from sample.cpp:3: /Users/admin/Desktop/bug-sample/build/dynamicFramework.framework/Headers/dynamicFramework.h:1:9: warning: framework dir dynamicFramework.h [-W#pragma-messages] 1 | #pragma message ("framework dir dynamicFramework.h") | ^ 1 warning generated. 我们看到sample.cpp中包含的头文件用的是framework目录中的,因为 -F 在 -I前面

编译命令:clang -I /Users/admin/Desktop/bug-sample/abc -F /Users/admin/Desktop/bug-sample/build sample.cpp -c -v 输出: ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include" ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/Library/Frameworks" #include "..." search starts here: #include <...> search starts here: /Users/admin/Desktop/bug-sample/abc /Users/admin/Desktop/bug-sample/build (framework directory) /usr/local/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1 /Library/Developer/CommandLineTools/usr/lib/clang/16/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include /Library/Developer/CommandLineTools/usr/include /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory) End of search list. In file included from sample.cpp:3: /Users/admin/Desktop/bug-sample/abc/dynamicFramework/dynamicFramework.h:1:9: warning: abc dynamicFramework.h [-W#pragma-messages] 1 | #pragma message ("abc dynamicFramework.h") | ^ 1 warning generated.

我们把 -I和-F换位置之后,sample.cpp使用的是abc目录中的dynamicFramework.h文件

Image

-isystem 添加的路径 在 -I添加的路径 的后面 -iframework 添加的路径 在 -isystem添加的路径 的后面 (就算-iframework 标志在 -isystem 前面设置) 我们看到clang还会添加一个默认的/user/local/include (-I级别)

4、好了,我们知道编译器逻辑之后,我们在看看cmake、cmake-tools、cpptools是如何处理错误的 首先我会列出来我使用的各个插件、cmake、vscode等的版本信息,(我保证新版本也有问题,我也测试过新版本) cpptools: 1.28.3 cmaketools: 1.19.52 vscode: Version: 1.102.3 Commit: 488a1f239235055e34e673291fb8d8c810886f81 Date: 2025-07-29T03:00:23.339Z Electron: 35.6.0 ElectronBuildId: 11847422 Chromium: 134.0.6998.205 Node.js: 22.15.1 V8: 13.4.114.21-electron.0 OS: Darwin x64 23.6.0 cmake:3.31.5

5、cmake会生产一些json给cmake-tools使用,cmake-tools使用它来配置cpptools

Image 我们看到生成了frameworks, 通过下面的讨论链接,说是解决frameworks无法找到头文件的问题,实际并没有处理好 [1](https://github.com/microsoft/vscode-cmake-tools/pull/3247) [2](https://github.com/microsoft/vscode-cmake-tools/issues/2324) [3](https://gitlab.kitware.com/cmake/cmake/-/commit/b3a6a11e95f805ab7411d395bd426561cfb4d4d0) 实际处理逻辑是 参考cmake代码

/Users/admin/cmake-3.31.5/Source/cmFileAPICodemodel.cxx Image

Image

我们看到cmake通过判断includePathList 表中的路径尾部是不是.framework然后把includePathList分成2部分存放到json中,分别是Frameworks和Includes 这样处理会丢失了优先级吗?

一般是不会的,因为cmake 最后处理framework类型的引入,前面先引入include_directories和interface_include_directories (继承) Image

不管是这样引入framework target_link_libraries(sample PRIVATE /Users/admin/Desktop/bug-sample/build/dynamicFramework.framework/dynamicFramework) 还是这样引入 target_link_libraries(sample PRIVATE dynamicFramework) 都会最后处理,并添加一个/Users/admin/Desktop/bug-sample/build/dynamicFramework.framework路径到includes路径中.

并生成CXX_INCLUDES到build/CMakeFiles/flag.cmake中. 然后cmake还会生成CXX_FLAGS到build/CMakeFiles/flag.cmake中, 手动添加的路径(-I 或者 -iframework或者-F),例如target_compile_options(sample PRIVATE -I/Users/admin) 会反应到CXX_FLAGS 上或者cmake fileapi中的json 中类似

Image Image Image

并且cmake-tools是直接把Frameworks附加在Includes后面

/Users/admin/Downloads/vscode-cmake-tools-main/src/cpptools.ts

Image

也就是说includes是分cxx_includes部分和cxx_flags部分,是分开处理的,并由cmake-tools投递给cpptools使用

=========== 问题1、cmake-tools 把framework当简单的include使用,导致cpptools在#include <framework/xxxxx.h>类型的识别有问题. 我们看看这个链接的问题 https://github.com/microsoft/vscode-cmake-tools/issues/2324.其实并没有处理好这个问题,因为framework被当作include使用了.

#include <QMessageBox> 可以正常goto,因为qt的cmake文件全路径/Users/admin/Qt/6.7.2/macos/lib/QtWidgets.framework/Headers会添加INTERFACE_INCLUDE_DIRECTORIES到 这里注意还添加了一个/Users/admin/Qt/6.7.2/macos/lib/QtWidgets.framework到INTERFACE_INCLUDE_DIRECTORIES中,这里会导致优先级错误 Image

Image

如果我们这样 add_library(test INTERFACE) set_target_properties(test PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "/Users/admin/Desktop/bug-sample/build/dynamicFramework.framework;/Users/admin/Desktop/bug-sample/build/dynamicFramework.framework/Headers" ) 就会导致这样的优先级

Image 显然-F不再是cxx_includes的尾部 Image 但是cmake把Frameworks从Includes分离出来了,显然优先级信息丢失了. cmake-tools又直接把Frameworks添加到includes的尾部,显然 [build] /Users/admin/Desktop/bug-sample/build (framework directory) 和 [build] /Users/admin/Desktop/bug-sample/build/dynamicFramework.framework/Headers 的顺序会错误.

并且这里面还隐藏这一个cpptools的问题,前面的链接里面没人发现问题的根本

Image 我们先手动解决一下framework路径/users/admin/qt/6.7.2/macos/lib被当作include使用的问题 Image 但是从main.cpp到QMessageBox没问题,从QMessageBox到qmessagebox.h也没有问题 进入qmessagebox.h就有问题了 因为QMessageBox没有文件后缀,导致后面的goto动作,把QMessageBox当作一个cpp源文件,而这个文件没有在工程的sources 中,也就没有编译参数,然后就出错了,简单点, QMessageBox是一个新的Translation unit Image

好了,设计一开始就是不对的,导致优先级存在问题,cmake-tools直接把framework当include使用的错误,cpptools解析单元跟踪丢失

问题2、cxx_flags部分手动-I添加的路径应该在cxx_includes部分中 -isystem和-iframework添加的路径的前面,因为这种设计导致,cpptools根本不可能知道正确的优先级

Image Image 编译命令: { "directory": "/Users/admin/Desktop/bug-sample/build", "command": "/usr/bin/clang++ -I/Users/admin/Desktop/bug-sample/abc -I/Users -iframework /Users/admin/Desktop/bug-sample/build -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk -mmacosx-version-min=14.6 -v -I/Users/admin -o CMakeFiles/sample.dir/sample.cpp.o -c /Users/admin/Desktop/bug-sample/sample.cpp", "file": "/Users/admin/Desktop/bug-sample/sample.cpp", "output": "CMakeFiles/sample.dir/sample.cpp.o" } 正确的应该是/Users/admin在/Users/admin/Desktop/bug-sample/build前面

CMake Tools Diagnostics

.

Debug Log

.

Additional Information

No response

debugee avatar Oct 19 '25 13:10 debugee

@debugee Thanks for reporting this issue. In order for us to investigate this further, could you share this sample project with us? Thank you.

yanghhhhhhh avatar Oct 20 '25 09:10 yanghhhhhhh

@debugee Thanks for reporting this issue. In order for us to investigate this further, could you share this sample project with us? Thank you.

ok,我会配合你们处理这些bug

https://github.com/debugee/bug-sample

debugee avatar Oct 20 '25 10:10 debugee

@debugee Thanks for reporting this issue. In order for us to investigate this further, could you share this sample project with us? Thank you.

我再描述一下问题, cmake fileapi 返回的includes和frameworks 本来是在一个带有优先级的列表中(includePathList),

这个列表(includePathList)中的路径 可以是这样 路径 添加方式 /A -I /B -I /C framework base dir -F /D -I /E -isystem /F -isystem /G framework base dir -iframework /H framework base dir -iframework /K framework base dir -iframework

1、/A /B /D可以使用 target_include_directories(sample PRIVATE /A /B /D) 添加 2、/C 可以使用 target_include_directories(sample PRIVATE /C/c.framework) 后缀为.framework 自动找到base dir 1和2这两种方式,谁先添加,谁在前面

3、/E /F 可以使用 target_include_directories(sample SYSTEM PRIVATE /E /F) 添加 3添加 目录永远在 1、2后面

4、 /G framework base dir -iframework /H framework base dir -iframework /K framework base dir -iframework

这种可以是用下面的方式添加 set_target_properties(dynamicFramework PROPERTIES FRAMEWORK TRUE SYSTEM TRUE PUBLIC_HEADER dynamicFramework.h ) target_link_libraries(sample PRIVATE dynamicFramework) 永远排在-I -F -isystem后面.

如果把SYSTEM TRUE 改成SYSTEM FALSE 会把-iframework 变成-F, 但是target_include_directories (INCLUDE_DIRECTORIES)的目录优先于target_link_libraries (INTERFACE_INCLUDE_DIRECTORIES)处理,所以-F会设置到-I后面 但是2这种方式可以让-F来到-I前面!

Image Image

上面就是cmake 创建CXX_INCLUDES 的逻辑

但是还有编译选项target_compile_options(sample PRIVATE -I/abc) 这种添加方式 反应在CXX_FLAGS 中

cmake fileapi build/.cmake/api/v1/reply/xxx.json <==> unix makefile generator file flags.make compileCommandFragments <==> CXX_FLAGS frameworks and includes <==> CXX_INCLUDES

这种分开处理 所有includes 方式存在问题

cmake fileapi把高优先级的2方式和4方式直接分离到了frameworks,导致使用者(cmake-tools)无法知道优先级

cmake-tools 直接是把 frameworks 连接到includes 后面 导致前面的顺序变成了 /A -I /B -I /D -I /E -isystem /F -isystem /C framework base dir -F /G framework base dir -iframework /H framework base dir -iframework /K framework base dir -iframework 而且cmake-tools并没有告诉这些路径中有frameworks给cpptools使用

Image

cpptools也无法知道优先级,而且就算cmake-tools告诉了对应的那些是framework,并且优先级也传递过去了, 但是cpptools还有部分compileCommandFragments 中的 选项设置的路径-I -isystem -iframework -F, cpptools无从知道这些在编译选项中的路径应该放在那个优先位置.

编译选项中的-I 、-F的优先级应该在CXX_INCLUDES 中-isystem -iframework 前面 编译选项中的-isystem的优先级应该在CXX_INCLUDES中-iframework的前面

cpptools还需要知道哪些是framework,以方便处理#include <QtWidgets/QMessageBox>这种方式 所以#2324中cmake-tools直接附加frameworks到includes后面的处理方式就是创造bug 以及cmake分离frameworks的做法也是创造bug

https://gitlab.kitware.com/cmake/cmake/-/issues/19897

顺便,cpptools在处理QMessageBox无后缀文件的时候,改变了translate unit 文件,导致丢失了一些跟踪设置,然后QMessageBox里面引用qmessagebox.h, qmessagebox.h的framework自然就找不到了

debugee avatar Oct 20 '25 11:10 debugee

@yanghhhhhhh 我不小心关闭了这个问题,请重新打开它

debugee avatar Oct 20 '25 12:10 debugee

please reopen this issue,i close this issue careless

debugee avatar Oct 21 '25 04:10 debugee

https://github.com/microsoft/vscode-cpptools/issues/13993

debugee avatar Oct 21 '25 04:10 debugee

@debugee Thanks for checking this issue. We verified this issue in a Mac OS darwin environment with CMake version 4.1.0 + VS Code 1.105.1 + CMake tools 1.22.16. When we build the project, it shows "/A, /B (framework directory), /C, /D, /E, /build (framework directory)", modify "SYSTEM TRUE" to "SYSTEM FALSE", it shows "/A, /B (framework directory), /C, /build (framework directory), /D, /E". Please refer to this video. If we missed something, please let us know. What is your expected result? Also, could you check if this issue https://github.com/microsoft/vscode-cmake-tools/issues/4599 is the same as yours? We look forward to your response. Thank you.

Image

yanghhhhhhh avatar Oct 21 '25 09:10 yanghhhhhhh

@debugee Thanks for checking this issue. We verified this issue in a Mac OS darwin environment with CMake version 4.1.0 + VS Code 1.105.1 + CMake tools 1.22.16. When we build the project, it shows "/A, /B (framework directory), /C, /D, /E, /build (framework directory)", modify "SYSTEM TRUE" to "SYSTEM FALSE", it shows "/A, /B (framework directory), /C, /build (framework directory), /D, /E". Please refer to this video. If we missed something, please let us know. What is your expected result? Also, could you check if this issue #4599 is the same as yours? We look forward to your response. Thank you.

Image Image

please look CMakeLists.txt line 38 ~ 148

https://github.com/microsoft/vscode-cmake-tools/issues/4599 is create from https://github.com/microsoft/vscode-cpptools/issues/13993 https://github.com/microsoft/vscode-cpptools/issues/13993 is also create by me so is the same bug

cmake、cmake-tools、cpptools all have some bug

debugee avatar Oct 21 '25 09:10 debugee

I suggest that cmake fileapi does not separate the framework directory from includes into frameworks, so that includes contains all directories, including those included by -I, -F, -iframework, -isystem, etc., because this can retain the priority information, so that when cmake-tools delivers these includes to cpptools, cpptools can insert other directory settings in the compilation command into the correct priority position. If frameworks and includes are processed separately, even if cmake-tools sets the correct parameters to cpptools.cpptools may not know that there is a framework directory with a high priority -F setting in frameworks. Putting the directories together can not only retain the priority information, but cpptools can also determine whether the directory suffix is ​​".framework" to confirm whether it is a framework directory. Cmake itself also determines whether the directory is a framework in this way, so that cpptools can find the framework header file in a standard way (#include <frameworkname/headername.h>), rather than adding a directory like this (/path/to/MyFramework.framework/Headers) to includes.

Thus, CMake's separation is incorrect; it should be combined, and the directories should be tracked, including system and framework directories (identifiable by the suffix), iframework directories, and regular directories. Therefore, the handling described in https://gitlab.kitware.com/cmake/cmake/-/issues/19897 is incorrect, and CMake-Tools's direct addition of frameworks to includes is even more incorrect. CppTools also needs improvement, as it's unaware of the high-priority framework directory and the system directory within includes, and thus doesn't know how to insert the directories specified later in the command into the correct locations.

debugee avatar Oct 21 '25 13:10 debugee

@debugee Thank you for the detailed information. We have rechecked this issue on our side. The build information displays as "/A, /B (framework directory), /C, /D, /E, /build (framework directory)". Running "C/C++: Log Diagnostics" shows "/A, /C, /E, /B, /build’. Could you check if this is the same as your repro steps? If not, please let us know. Thank you.

Repro Video: repro.zip

yanghhhhhhh avatar Oct 22 '25 07:10 yanghhhhhhh

@debugee Thank you for the detailed information. We have rechecked this issue on our side. The build information displays as "/A, /B (framework directory), /C, /D, /E, /build (framework directory)". Running "C/C++: Log Diagnostics" shows "/A, /C, /E, /B, /build’. Could you check if this is the same as your repro steps? If not, please let us know. Thank you.

Repro Video: repro.zip

"/A, /B (framework directory), /C, /D, /E, /build (framework directory)" "/A, /C, /E, /B, /build /D"

debugee avatar Oct 22 '25 07:10 debugee

@debugee Thank you for your reply. As you say, it is ‘/A, /C, /E, /B, /build /D’. We can reproduce the issue now. Thank you.

Image

yanghhhhhhh avatar Oct 22 '25 07:10 yanghhhhhhh

We can reproduce this issue in a Mac OS darwin environment with VS Code 1.105.1 + CMake tools 1.22.16 version. Repro Steps:

  1. Clone the project from https://github.com/debugee/bug-sample.git or download the attached project(bug-sample-main.zip).
  2. Press F1 and run command "CMake: Select a Kit", choose clang compiler.
  3. Run command "CMake: Configure" and "CMake: Build". Observe the result.
  4. Run command "C/C++: Log Diagnostics". Observe the result.

Repro video: repro.zip

FYI: @gcampbell-msft

yanghhhhhhh avatar Oct 22 '25 08:10 yanghhhhhhh