Инструменты для поиска включенных заголовков, которые не используются?[закрыто]
-
18-09-2019 - |
Вопрос
Я знаю ПК-Линт могу рассказать вам о заголовках, которые включены, но не используются.Есть ли какие-нибудь другие инструменты, которые могут это сделать, желательно в Linux?
У нас есть большая база кода, в которой за последние 15 лет было много изменений в функциональности, но оставшиеся директивы #include редко удаляются, когда функциональность перемещается из одного файла реализации в другой, что к этому моменту оставляет нас в довольно хорошем беспорядке.Очевидно, я могу выполнить кропотливую работу по удалению всех директив #include и позволить компилятору сказать мне, какие из них следует повторно включить, но я предпочитаю решить проблему наоборот - найти неиспользуемые - вместо того, чтобы перестраивать список используемых.
Решение
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Моя основная работа — работа в компании, которая разрабатывает инструменты статического анализа.
Я был бы удивлен, если бы большинство (если не все) инструментов статического анализа не имели той или иной формы проверки использования заголовка.Вы могли бы использовать этот страницу в Википедии, чтобы получить список доступных инструментов, а затем отправить электронное письмо компаниям, чтобы задать им вопрос.
Некоторые моменты, которые следует учитывать при оценке инструмента:
Для перегрузок функций вы хотите, чтобы были видны все заголовки, содержащие перегрузки, а не только заголовок, содержащий функцию, выбранную при разрешении перегрузки:
// f1.h
void foo (char);
// f2.h
void foo (int);
// bar.cc
#include "f1.h"
#include "f2.h"
int main ()
{
foo (0); // Calls 'foo(int)' but all functions were in overload set
}
Если вы примените метод грубой силы, сначала удалите все заголовки, а затем снова добавьте их до тех пор, пока они не скомпилируются. Если сначала добавляется «f1.h», то код скомпилируется, но семантика программы будет изменена.
Аналогичное правило применяется, когда у вас есть частичная и специализация.Неважно, выбрана специализация или нет, необходимо убедиться, что все специализации видны:
// f1.h
template <typename T>
void foo (T);
// f2.h
template <>
void foo (int);
// bar.cc
#include "f1.h"
#include "f2.h"
int main ()
{
foo (0); // Calls specialization 'foo<int>(int)'
}
Что касается примера с перегрузкой, то подход грубой силы может привести к тому, что программа все равно компилируется, но ведет себя по-другому.
Другой родственный тип анализа, на который вы можете обратить внимание, — это проверка возможности прямого объявления типов.Учтите следующее:
// A.h
class A { };
// foo.h
#include "A.h"
void foo (A const &);
// bar.cc
#include "foo.h"
void bar (A const & a)
{
foo (a);
}
В приведенном выше примере определение «A» не требуется, поэтому файл заголовка «foo.h» можно изменить так, чтобы в нем было предварительное объявление только для «A»:
// foo.h
class A;
void foo (A const &);
Этот вид проверки также уменьшает зависимости заголовков.
Другие советы
Вот скрипт, который это делает:
#!/bin/bash
# prune include files one at a time, recompile, and put them back if it doesn't compile
# arguments are list of files to check
removeinclude() {
file=$1
header=$2
perl -i -p -e 's+([ \t]*#include[ \t][ \t]*[\"\<]'$2'[\"\>])+//REMOVEINCLUDE $1+' $1
}
replaceinclude() {
file=$1
perl -i -p -e 's+//REMOVEINCLUDE ++' $1
}
for file in $*
do
includes=`grep "^[ \t]*#include" $file | awk '{print $2;}' | sed 's/[\"\<\>]//g'`
echo $includes
for i in $includes
do
touch $file # just to be sure it recompiles
removeinclude $file $i
if make -j10 >/dev/null 2>&1;
then
grep -v REMOVEINCLUDE $file > tmp && mv tmp $file
echo removed $i from $file
else
replaceinclude $file
echo $i was needed in $file
fi
done
done
Посмотри на Дегидра.
С веб-сайта:
Dehydra — это легкий инструмент статического анализа общего назначения с поддержкой сценариев, способный анализировать код C++ для конкретных приложений.В самом простом смысле Dehydra можно рассматривать как инструмент семантической команды grep.
Должна быть возможность создать сценарий, проверяющий наличие неиспользуемых файлов #include.
Google cppclean кажется, неплохо справляется с поиском неиспользуемых заголовочных файлов.Я только начал его использовать.Это дает несколько ложных срабатываний.Он часто находит ненужные включения в файлах заголовков, но он не сообщает вам, что вам нужно предварительное объявление связанного класса и что включение необходимо переместить в связанный исходный файл.
Если вы используете Eclipse CDT, вы можете попробовать Инклюдатор который бесплатен для бета-тестеров (на момент написания статьи) и автоматически удаляет лишние #includes или добавляет недостающие.
Отказ от ответственности:Я работаю в компании, которая разрабатывает Includator, и использую его последние несколько месяцев.У меня это работает очень хорошо, так что попробуйте :-)
Насколько я знаю, его нет (кроме PC-Lint), что досадно и удивительно.Я видел предложение сделать этот фрагмент псевдокода (который по сути автоматизирует ваш «кропотливый процесс»):
для каждого файла cpp
для каждого заголовка включить
закомментируйте включение
скомпилировать файл cpp
если (компиляция_ошибки)
раскомментируйте заголовок
еще
удалить включение заголовка из cpp
Поместите это в ночной cron, и он должен выполнить свою работу, сохраняя рассматриваемый проект свободным от неиспользуемых заголовков (очевидно, вы всегда можете запустить его вручную, но выполнение займет много времени).Единственная проблема заключается в том, что отсутствие заголовка не приводит к ошибке, но все равно создает код.
Я сделал это вручную, и в краткосрочной перспективе оно того стоит (о, а в долгосрочной перспективе?- Это занимает много времени) из-за сокращения времени компиляции:
- Меньше заголовков для анализа для каждого файла cpp.
- Меньше зависимостей - весь мир не нуждается в повторной компилировании после изменения в один заголовок.
Это также рекурсивный процесс: каждый оставшийся заголовочный файл требует проверки на предмет наличия каких-либо заголовочных файлов. это включения можно удалить.Кроме того, иногда вы можете заменить предварительные объявления на включение заголовков.
Затем весь процесс необходимо повторять каждые несколько месяцев/год, чтобы не терять остатки заголовков.
На самом деле, меня немного раздражают компиляторы C++: они должны быть в состоянии сказать вам, что не нужно - компилятор Microsoft может сказать вам, когда изменение в заголовочном файле можно безопасно игнорировать во время компиляции.
Если кому-то интересно, я только что разместил на sourceforge небольшой инструмент командной строки Java, предназначенный именно для этого.Поскольку он написан на Java, его, очевидно, можно запустить и в Linux.
Ссылка на проект есть https://sourceforge.net/projects/chksem/files/chksem-1.0/
Большинство подходов к удалению неиспользуемых включений работают лучше, если вы сначала убедитесь, что каждый файл заголовков компилируется самостоятельно.Я сделал это относительно быстро следующим образом (извиняюсь за опечатки — я пишу это дома:
find . -name '*.h' -exec makeIncluder.sh {} \;
где makeIncluder.sh
содержит:
#!/bin/sh
echo "#include \"$1\"" > $1.cpp
Для каждого файла ./subdir/classname.h
, этот подход создает файл с именем ./subdir/classname.h.cpp
содержащий строку
#include "./subdir/classname.h"
Если ваш makefile
в .каталог компилирует все файлы cpp и содержит -I.
, то простая перекомпиляция проверит, что каждый включаемый файл может скомпилироваться самостоятельно.Скомпилируйте в своей любимой IDE с goto-error и исправьте ошибки.
Когда вы закончите, find . -name '*.h.cpp' -exec rm {} \;