[Bug] 关于framework和include的处理的bug
Brief Issue Summary
这些bug是关于cmake、vscode-cmake-tools、cpptools在处理framework和include的路径的若干问题
1、我们先创建一个测试工程bug-sample,工程文件如下
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文件
-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
/Users/admin/cmake-3.31.5/Source/cmFileAPICodemodel.cxx
我们看到cmake通过判断includePathList 表中的路径尾部是不是.framework然后把includePathList分成2部分存放到json中,分别是Frameworks和Includes 这样处理会丢失了优先级吗?
一般是不会的,因为cmake 最后处理framework类型的引入,前面先引入include_directories和interface_include_directories (继承)
不管是这样引入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 中类似
并且cmake-tools是直接把Frameworks附加在Includes后面
/Users/admin/Downloads/vscode-cmake-tools-main/src/cpptools.ts
也就是说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中,这里会导致优先级错误
如果我们这样 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" ) 就会导致这样的优先级
并且这里面还隐藏这一个cpptools的问题,前面的链接里面没人发现问题的根本
好了,设计一开始就是不对的,导致优先级存在问题,cmake-tools直接把framework当include使用的错误,cpptools解析单元跟踪丢失
问题2、cxx_flags部分手动-I添加的路径应该在cxx_includes部分中 -isystem和-iframework添加的路径的前面,因为这种设计导致,cpptools根本不可能知道正确的优先级
CMake Tools Diagnostics
.
Debug Log
.
Additional Information
No response
@debugee Thanks for reporting this issue. In order for us to investigate this further, could you share this sample project with us? Thank you.
@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 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前面!
上面就是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使用
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自然就找不到了
@yanghhhhhhh 我不小心关闭了这个问题,请重新打开它
please reopen this issue,i close this issue careless
https://github.com/microsoft/vscode-cpptools/issues/13993
@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.
@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.
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
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 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
@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 Thank you for your reply. As you say, it is ‘/A, /C, /E, /B, /build /D’. We can reproduce the issue now. Thank you.
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:
- Clone the project from https://github.com/debugee/bug-sample.git or download the attached project(bug-sample-main.zip).
- Press F1 and run command "CMake: Select a Kit", choose clang compiler.
- Run command "CMake: Configure" and "CMake: Build". Observe the result.
- Run command "C/C++: Log Diagnostics". Observe the result.
Repro video: repro.zip
FYI: @gcampbell-msft