是否有去除c++多餘頭文件的工具?

如題,我利用clang的庫寫了這麼一個工具,打算髮布出來

發之前想先問問看是不是已經有這種工具了,我之前一直沒找到才寫的,不過還是確認看看

如果已經有做得很好的工具我就不發了,自己用用就好

沒有的話我就發到github,注釋寫詳細點作為clang語法樹入門小範例

比如:假設一個文件hello.cpp,裡面的內容是:

#include "a.h"

#include "b.h"

#include "c.h"

#include "d.h"

#include "useful.h"

... ...

而其中只有useful.h被用到,那麼a.h、b.h、c.h、d.h的#include語句將被移除掉,最終hello.cpp變為:

#include "useful.h"

... ...


有一個 include-what-you-use:

http://include-what-you-use.org/

https://github.com/include-what-you-use/include-what-you-use

這個是 Google 的內部工具開源出來的,也是基於 llvm/clang 實現的,跟題主的實現思路估計比較像。


Resharper有代碼分析clean up code的功能,但要Visual Studio


從@曉楚那裡看到的:

有款工具:

http://catb.org/~esr/deheader/

Find and optionally remove unneeded includes in C or C++ sourcefiles.

deheader analyzes C and C++ files to determine which header inclusions can be removed while still allowing them to compile. This may result in substantial improvements in compilation time, especially on large C++ projects; it also sometimes exposes dependencies and cohesions of which developers were unaware.


2016-03-10

工具已經上傳到 GitHub - cxxclean/cxx-clean-include

目前支持對visual studio單個c++項目作清理,以及清理整個文件夾下的c++文件

舉個例子,比如現在有visual studio 2005項目hello.vcproj,那麼使用命令:

cxxclean -clean hello.vcproj --

將會清理項目內的cpp文件以及cpp文件所#include到的頭文件

再比如有一個文件夾hello,那麼使用

cxxclean -clean ./hello/ --

將清理hello文件夾下的cpp文件以及cpp文件所#include到的頭文件

github頁面上我上傳了hello.sln工程,在嘗試使用本工具清理自己的項目之前,可以先在hello.vcxproj項目作測試

另外,清理c++項目前請務必使用版本工具備份!

其餘的看github說明

--------------

2016-04-20

我是提問者,上面是我之前在3月10號的回答,由於當時工具初步完成(沒錯,我提問的時候其實工具壓根還沒寫好),自己本身也較忙,所以回答並不詳細,現在一個多月過去了,工具已經完善,文檔、代碼注釋、說明文件也都已經補齊。我覺得應該再補充說明一下,同時,各答主們推薦的工具作說明我也試用了一些,試用情況我也會稍作說明,以方便有需要的人選擇合適的工具。

這裡要說的一點是,如果你的項目編譯比較久,在使用各答主們推薦的工具之前,最好先試一下預編譯、聯合編譯、ccache之類的緩存工具這3種方法,因為這3種方法對編譯速度的提升是非常巨大的,僅當你在採用後仍然對編譯時間不滿意,且沒有充足的時間和精力和金錢來解決,才有必要考慮清理c++文件內多餘的#include。

首先是 @chys 答主提到的include-what-you-use工具,該工具目前可以在windows和Ubuntu下直接使用,其他平台上則需要再下載llvm+clang進行編譯(編譯時間會很長,應該在1個小時左右),根據我的試用和猜測,include-what-you應該是本問題的各回答中處理單個cpp文件時功能最強大的一個工具,支持刪除無用的#include,支持自動用前置聲明替換掉#include語句,支持在#include語句後添加註釋,但個人認為上手較為困難,需要安裝python環境,而且在windows下較難自動化處理,參數使用困難,而且很遺憾的是,include-what-you-use似乎每次都只單獨對一個cpp進行分析,這也意味著它只能採用最保守的只清理cpp不清理h文件的方式來保證清理完後不會產生一堆編譯錯誤。推薦在linux系統下採用該工具。

還有匿名答主提到vs插件Resharper For C++,這個插件很易用而且提示效果友好,當你用vs開發某個cpp文件時,用這個插件可以點一下就清理掉cpp文件內多餘的#include,而且如果某行#include無效,#include整行會自動變灰暗,看上去非常直觀,因此推薦用vs的朋友使用該插件來清理單個的cpp文件。缺點也很明顯,如果想要清理大批量的cpp文件會點到手酸,並且只清理當前文件,無法清理當前文件包含的其他文件內的#include,也不支持include-what-you-use可以做到的自動替換前置聲明。推測其是根據單詞匹配來清理的。

還有 @陳曉輝 提到的deheader工具,採用python開發,因此也需要python環境,看了下源碼,原理應該也是基本的單詞匹配。所能達到的效果估計和Resharper For C++較為類似。

此外還有 @jiangcy 提到的clion這款ide以及 @韓葆-逸松 提到的Coverity,由於沒有合適的環境試用所以不清楚實際效果,希望有了解的知友能告知一下。

最後就是我開發的工具了,現在的名字叫cxx-clean-include,目前僅提供win32可執行文件(linux下也能編譯),是基於llvm+clang開發的,可以做到清除無用#include、用前置聲明替換#include、盡量用深層#include替換淺層#include,同時該工具額外的一個效果是:如果只給1個cpp,可以把cpp包含的其他頭文件一起清理(位於項目文件夾下的頭文件才會被清理),如果給多個cpp,則會先把這些cpp文件的分析結果匯總起來再逐一清理。比如,假設2個cpp文件都包含頭文件a,分析第1個cpp文件時認為頭文件a的第1行#include沒用,分析第2個cpp文件時卻認為頭文件a的第1行#include有用,最終結果就是,頭文件a的第一行#include不應被刪除。

具體本工具達到的效果在上面3月10號的回答里已經講了,下面要講講是本工具的原理,這樣大家在使用的時候可以有個整體的概念,也希望對有興趣的人有所幫助。

cxx-clean-include的簡要原理如下(此外還有很多細節未一一說明,但下面已經涵蓋了對單個文件的基本分析流程):

1.
清除多餘的#include(這一步最簡單)

以下圖為例

上圖表示,雖然[主文件.cpp]包含了很多文件,但只用到了B2.h、D2.h、E1.h頭文件

處理辦法:保留有用文件的所有祖先文件

處理的結果如下圖

可以看出,B2.h、D2.h、E1.h的所有祖先文件均被保留下來

2.
採用前置聲明替換#include

以下圖為例

上圖表示,文件B2中的類或結構體僅被C1用來聲明指針或引用,除此之外B2文件中的內容根本不被其他文件關心。

處理辦法:找到包含B2的那條#include語句,直接把那條語句替換成前置聲明。

比如,假設B2.h中僅定義了class B2_Class{ int n; },則把B1文件中的#include 「B2.h」語句替換成class
B2_Class;前置聲明語句

3. 深層#include取代淺層#include

以下圖為例

上圖表示,雖然[主文件.cpp]包含了很多文件,但只用到了B2.h、D2.h、E2.h、F2.h頭文件

處理辦法:對於每個頭文件,若其可被後代取代,則進行替換

處理之後的結果如下圖

如上圖,可以看到:

#include 「B.h」被替換成了#include
「B2.h」

#include 「C.h」被替換成了#include
「D1.h」


感謝軟體作者給了這麼好的一個工具,文檔也寫得好。我下載了一個視頻Codec的開源項目,由於我只需要解碼(decode)部分,那些龐大繁複的「編碼(encode)」過程可以不要,所以用這個工具試了一下,可以看出軟體是強大的,可惜卡在系統頭文件( windows.h 之類)上了,雖然可以用 -include 參數來補充,但是不妨這樣想,一來 這類系統頭文件本身就不允許改寫的,你去掃描分析並沒有現實意義。二來系統自帶的頭文件 總是調用系統SDK的,這類SDK文件同樣也是 分析出來也不能改寫,所以完全可以略過不管,應該把軟體設定為「僅掃描項目dir下的文件」,這樣既簡單又安全。

其實想優化SDK頭文件是很簡單的,我們只需手工加上 // 注釋掉 include & 語句,然後看看編譯會否出錯就行了,如果出錯,老老實實 include 吧,難道我們還敢打開 windows.h 去改它嗎??另外還有一個實際情況,SDK裡面基本就是 結構/類 定義,然後就是大量調用系統DLL函數,也就是一條一條的聲明語句而已,實際上並沒有多少執行代碼。反之,如果有執行代碼,那麼往往就是很常用的,例如 printf 之類,所以SDK的「優化」並沒有現實意義。

另外,這個軟體不能識別 def 文件,對於 DLL 項目是一個缺陷。譬如在 def 文件中我 只需 輸出 2個 DLL函數,這樣 dll.cpp 裡面其它「無關」的函數是可以優化的。當然折衷辦法還是有的,可以另外寫一個 fake.cpp,裡面僅包含這兩個函數,然後單獨優化這個 fake.cpp 即可。

所以我建議作者優先解決「略過 系統SDK文件」的問題,讓這個軟體在「自然狀態」下能夠跑起來,而不是頻繁中斷在 windows.h 等一堆 SDK 之下,拋出一個接一個error,用戶被迫不斷修改命令行(變得「水蛇春咁長」),意興闌珊。


Coverity有一條規則檢測這種頭文件重複或者循環引用。


github不是說方便你「發布」一個軟體,而是放你願意分享的源碼,有人一起做就更好了的平台。所以放上去就是了


開源輪子不嫌多。

為什麼不發布那?


我程序 用到Qt的, 和包含預編譯頭文件stdafx.h 會有很多問題。

比如老說 stdafx.h可以去掉 qt也有很多問題

我用了Resharper很強大, 可以一下去掉一個工程的無用include, 點一個無用的include時有選項的,一下去掉一個解決方案的

試多了下, 還是不行, 往往將有用的頭文件也會去掉, 不聰明


我去......所以C++還是得微服務架構~better than docker。。。【捂


有沒有這個功能,將可以前置聲明的非必要頭文件也去掉,然後添加前置聲明?


個人試過,CLion可行,雖然有時候會有些奇怪的現象出現


難道沒有一款ide幫你做這個?jetbrains家的好像可以,但是沒試過他們的c ide


推薦閱讀:

用 Visual Studio 2013 能學好 C++ 嗎?
使用Visual studio 2015 找不見C++?
C++類型問題,typeid與sizeof運算符的差異?
C++11(VC++) 中支持多種for循環寫法,哪種比較好?

TAG:程序 | C | CC | Clang | LLVM |