пятница, 4 мая 2012 г.

Анализируем это

Сегодня я предлагаю отвлечься от платформы Android и заняться чем нибудь полезным ... для всех Java (и не только) разработчиков.

Анализируем это


Известно, что языки выполняющие компиляцию кода значительно повышают производительность труда программиста, просто за счет того, что обнаруживают часть ошибок еще на этапе сборки. 
Разумеется, на этот счет имеется особое мнение у апологетов Perl-а, Python-а и даже Java, которое, в целом сводится к тому, что ошибки просто не надо делать, а код необходимо равномерно покрывать UnitTest-ами :)

Ничего не имея против UnitTest-ов, я все-же считаю, что в борьбе с ошибками все средства хороши и никакая помощь (в том числе и оказываемая компиляторами) не будет лишней. Несколько дальше чем компиляторы идут всевозможные анализаторы кода. 
Они помогают не просто найти ошибки, препятствующие сборке приложения, а позволяют обнаружить фрагменты кода небезупречные с точки зрения принятого Code Style или потенциально способные привести к ошибкам. Особенно полезными эти инструменты становятся при групповой разработке.

Инструментов, выполняющих подобный анализ, довольно много, и, на мой взгляд, грех ими не пользоваться. В этой статье я хочу остановиться на Yasca. Это свободно распространяемое ПО является не анализатором кода, а интегрирующей платформой, поддерживающей интеграцию с множеством различных кодечекеров таких как PMD, JLint или RATS.
Оно будет полезно не только Java-разработчику (хотя наиболее эффективный анализ кода возможен именно для Java), но и разработчикам использующим C, С++, PHP или JavaScript.

Попробуем скачать и запустить Yasca "из коробки" (благо код для анализа у нас где-то завалялся). Идем на страницу закачки и скачиваем собственно yasca, а также какой нибудь из прилагающихся анализаторов кода, например PMD или все сразу :)

Скачав архивы, создаем локальный каталог (например c:/yasca) и просто распаковываем в него все архивы по очереди. Сразу же после этого, инструмент готов к работе :) 
Набираем в командной строке (или сохраняем в cmd-файл, чтобы не набирать одно и то-же сто раз) команду:

yasca.exe -o ./Report C:\work\Games\tags\src\puzzle

и ждем пару минут (здесь флаг -o указывает имя файла в котором следует формировать отчет, а далее следует каталог анализируемого проекта), открываем сформировавшийся Report.html и тихо наслаждаемся :)

В принципе, все довольно неплохо. Единственное, что напрягает, это то, что ссылки на код загружают непосредственно java-файлы, искомую строку в которых приходится искать самому по номеру :(

Давайте это исправим :) Пользуясь тем, что Yasca, фактически, представляет собой специализированный PHP-интерпретатор (не самый удачный выбор языка, но сойдет), заходим в папку plugins и пишем там следующий скрипт (обозвав его, например, Mirror.php):

<?php

/**
 * @extends Plugin
 * @package Yasca
 */
class Plugin_Mirror extends Plugin {
    public $valid_file_types = array("java", "c", "cpp", "h", "cs", "sql");
        function rmdir_recurse($path) {
            $path= rtrim($path, '/').'/';
            $handle = opendir($path);
            for (;false !== ($file = readdir($handle));)
                if($file != "." and $file != ".." ) {
                    $fullpath= $path.$file;
                    if( is_dir($fullpath) ) {
                        $this->rmdir_recurse($fullpath);
                        rmdir($fullpath);
                    }
                    else
                      unlink($fullpath);
                }
            closedir($handle);
        }
    function execute() {
        $yasca =& Yasca::getInstance();
                static $once = true;
                if ($once) {
                    $this->rmdir_recurse('./Mirror');
                }
                $once = false;
                if (!check_in_filetype($this->filename, $this->valid_file_types)) {
                    return;
                }
                $filename = preg_replace('/\w:/', './Mirror', $this->filename) . ".html";
                $dir_name = preg_replace('/[\\\\\\/][^\\\\\\/]+$/', '', $filename);
                if (!file_exists($dir_name)) {
                    if (!mkdir($dir_name, 0777, true)) return;
                }
                if (file_exists($filename)) {
                    unlink($filename);
                }
                if (!$handle = fopen($filename, 'w+', true) ) return;
                fwrite($handle,"<html><meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1251\" /><head></head><body><pre>\n");
                $line = 1;
                foreach ($this->file_contents as $file_line) {
                        $str = $line;
                        while (strlen($str)<5) {
                           $str = " " . $str;
                        }
            fwrite($handle,"<a name=$line></a>$str: $file_line<br>\n");
                        $line++;
                }
                fwrite($handle,"</pre></body></html>");
                fclose($handle);
    }
}
?>


Здесь нет какой-то тайной магии. Мы просто копируем все анализируемые файлы, имеющие заданное расширение в некое подобие html-файла, расставив закладки и, для удобства, пронумеровав строки, в каталог Mirror (который заблаговременно создаем руками).

Теперь, после очередного запуска yasca мы получим копии всех проанализированных файлов в каталоге Mirror в препарированном в html виде.
Прикольно и бесполезно :) Для того чтобы этот скрипт стал полезным, нам необходимо внести изменения в формируемый файл отчета. И это тоже возможно :)

Идем в каталог lib и находим там файл HTMLGroupReport.php используемый для создания отчета, по умолчанию. В этом файле, находим фрагмент:

fwrite($handle,    
       "<a style=\"margin-right: 12px;\" source_code_link=\"true\" href=\"file://$filename\" target=\"_blank\" title=\"$filename\">$filename_base$line_number_field</a>" .
       "</td>");

он там такой один. И заменяем его на:

$chg_file_name = getcwd();
$chg_file_name = preg_replace('/\\\\/', '/', $chg_file_name);
$chg_file_name = preg_replace('/^\w:/', $chg_file_name . '/Mirror', $filename);
       
if (preg_match('/\.java$|\.c$|\.cpp$|\.h$|\.cs$|\.sql$/i', $chg_file_name)) {
    fwrite($handle,       
          "<a style=\"margin-right: 12px;\" source_code_link=\"true\" href=\"file://$chg_file_name.html#$line_number\" target=\"code\" title=\"$filename\">$filename_base$line_number_field</a>" .
          "</td>");
} else {
    fwrite($handle,    
          "<a style=\"margin-right: 12px;\" source_code_link=\"true\" href=\"file://$filename\" target=\"_blank\" title=\"$filename\">$filename_base$line_number_field</a>" .
          "</td>");
}


Код возможно не безупречен, зато вполне понятен :) Если файл, на который должна указывать ссылка, имеет нужное нам расширение, мы формируем ссылку на закладку в html-копии файла в каталоге Mirror. В противном случае, оставляем как было.

Запускаем анализ еще раз и убеждаемся, что навигация работает.

В принципе, в плане расширения функционала yasca мы мало чем ограничены. В частности, никто не помешает нам дописать плагин, автоматизирующий процесс сборки исходников при помощи Ant или Maven получив, тем самым, интегрированную среду сборки и анализа кода (вывод ant или maven будет полезно распарсить, добавляя сообщения об ошибках компиляции в общий отчет). Туда-же можно добавить и UnitTest-ы.

Полезный инструмент, я считаю :)

P.S. Кстати, вместо каталога с исходниками, можно анализировать Jar-ник :)
P.P.S. Правда наш Mirror.php, в этом случае, работать не будет, но это уже совсем другая история ...

Комментариев нет:

Отправить комментарий