O důležitosti snižování počtu #includovaných hlavičkových souborů v C a C++ jsem zde již psal. Důvodem je urychlení překladu, především pak toho inkrementálního během vývoje. V dnešním příspěvku se podíváme na nástroj include-what-you-use, který umí zbytečné #includy detekovat a případně i automaticky odstranit či nahradit za dopředné deklarace.
Popis nástroje
Nástroj include-what-you-use je určen pro vyhledání zbytečných #includů a jejich případné odstranění/nahrazení za dopředné deklarace. Je postaven nad Clangem, což je C/C++/Objective-{C,C++} front-end pro LLVM. Výhoda je, že zdrojáky skutečně parsuje, což má za následek přesnější výsledky, než kdyby se jednalo o řešení postavené na textovém zpracování zdrojáků (regulární výrazy apod.).
Podle oficiálních stránek by tento nástroj měl být použitelný i pro projekty v C. Já jsem jej však zkoušel pouze na projektu napsaném v C++. Ve zbytku příspěvku se budu tedy zabývat jen C++. Nástroj jsem dále zkoušel pouze na Linuxu. Jak je to s jeho podporou na Windows jsem nezjišťoval.
Instalace
Dle návodu je více možností instalace, závisejících na tom, zda již máte někde přeložené LLVM a Clang. Mně se osvědčil následující způsob, který zmínil jeden uživatel v komentářích. Pomocí něj proběhne stáhnutí a přeložení všeho potřebného:
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm cd llvm/tools svn co http://llvm.org/svn/llvm-project/cfe/trunk clang cd clang/tools svn co http://include-what-you-use.googlecode.com/svn/trunk/ include-what-you-use echo "add_subdirectory(include-what-you-use)" >> CMakeLists.txt sed -i 's/^DIRS.*:=/DIRS := include-what-you-use/' Makefile cd ../../.. mkdir build cd build cmake .. make -j$PROC
Za $PROC
jen doplníte počet procesorů/jader, která se pro překlad mají použít. Nyní už stačí jen počkat, než překlad doběhne (v závislosti na výkonnosti vašeho HW počítejte klidně i s hodinou). Po doběhnutí překladu byste v adresáři llvm/build/bin
měli mít (mimo jiné) program include-what-you-use
.
Spuštění
Opět existuje více způsobů, jak program spustit. Mně se osvědčil následující způsob, prezentovaný v jednom komentáři v návodu. Do zvoleného adresáře (např. $HOME/bin
) vložíte spustitelný skript s následujícím obsahem:
#!/bin/bash /where/your/compiler/is/bin/`basename $0` "$@" || exit $? args="$*" if [ "${args#* -c}" != "$args" ]; then out="${args#* -o}" out="${out%.o *}" if [ "x$out" != "x" ]; then /path/to/llvm/build/bin/include-what-you-use "$@" > $out.iwyu.txt 2>&1 fi fi exit 0
Název skriptu by měl korespondovat k překladači, který používáte k překladu (g++
, c++
, clang++
apod.). Poté si v shellu změníte PATH
tak, aby se místo překladače spustil onen skript:
export PATH="$HOME/bin:$PATH"
Co vytvořený skript dělá, tak je, že zavolá překladač s předanými parametry. Pokud překlad proběhl úspěšně, tak následně spustí onen program include-what-you-use
s tím, že výsledky analýzy uloží do souboru se suffixem .iwyu.txt
. Pokud se např. překládá modul x.cpp
do x.o
, tak výsledky pro tento soubor lze nalézt v x.iwyu.txt
.
Nyní už stačí jen spustit překlad vašeho projektu dle použitého build systému (CMake, make atd.). Při překladu se budou překládané soubory průběžně analyzovat a výsledky ukládat.
Samozřejmě, tento skript a úpravu PATH
použijte pouze pro získání výsledků. Po jejich získání si PATH
vraťte zpět. Ona analýza je časově náročná a pravděpodobně nechcete, aby běžela i při běžných překladech.
Interpretace výsledků
Každý z vygenerovaných textových souborů má následující strukturu:
/path/to/file.h should add these lines: [..] /path/to/file.h should remove these lines: [..] The full include-list for /path/to/file.h: [..] --- /path/to/file.cpp should add these lines: [..] /path/to/file.cpp should remove these lines: [..] The full include-list for /path/to/file.cpp: [..] ---
Místo [..]
tam jsou uvedeny hlavičkové soubory a dopředné deklarace. Příklad:
#include <memory> // for shared_ptr #include <utility> // for pair namespace ns1 { namespace ns2 { class CustomClass; } }
kde CustomClass
je třída ve jmenném prostoru ns1::ns2
, u níž není potřeba #includovat hlavičkový soubor, ale stačí použít dopřednou deklaraci.
Poznámka: nástroj ignoruje hlavičkové soubory, ke kterým neexistuje implementační soubor (.cpp
apod.).
Využití výsledků
Výsledky lze využít následujícími způsoby:
- Manuální úpravy. Pokud se jedná o menší projekt, tak může stačit projít výsledky a soubory upravit manuálně.
- Automatické úpravy pomocí přiloženého skriptu. Dle návodu můžete využít Python skript
fix_includes.py
pro automatickou opravu #includů a přidání dopředných deklarací. V případě potřeby si onen skript můžete upravit, aby vyhovoval vašim potřebám.
Jiné nástroje
Kromě popsaného nástroje samozřejmě existují i jiné, které můžete vyzkoušet. Zkuste mrknout na následující odkazy:
- stackoverflow.com: C/C++: Detecting superfluous #includes?
- stackoverflow.com: How should I detect unnecessary #include files in a large C++ project?
- stackoverflow.com: Tools to find included headers which are unused?
- stackoverflow.com: What is the tool to check include file that are useless? (c++)
- stackoverflow.com: Automatically removing unneeded #include statements