Опыт разработки плагина для Yasca

в 17:10, , рубрики: c++, Code Style, java, php, Проектирование и рефакторинг, метки: , ,

В этой статье я хочу поделиться опытом использования одной полезной утилиты, позволяющей автоматизировать сборку и анализ качества кода. Речь пойдет о Yasca — свободно распространяемом ПО, представляющем собой небольшой PHP движок и набор утилит для выполнения анализа Java, С++ или PHP кода, включающего в себя PMD, JLint и RATS. Сама интеграция выполнения этих утилит осуществляется путем разработки небольших плагинов, на языке PHP. О процессе разработки такого плагина и пойдет речь далее.

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

yasca.exe -o ./Report ...

Проработав некоторое время, Yasca формирует отчет Report.html, содержащий замечания по стилю кодирования нашего проекта. Строки отчета содержат ссылки на исходный код и единственное, что неудобно, это то, что при переходе по ссылке, мы попадаем в начало файла, а искать нужную строку по номеру может быть слегка утомительно. Именно это я и предлагаю исправить. Пусть ссылки позиционируют нас сразу на нужную строку исходного кода.

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

<?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);
    }
}
?>

Теперь, после очередного запуска yasca мы получим копии всех проанализированных файлов в каталоге Mirror в препарированном в html виде. Чтобы сделать эти файлы полезными, нам придется вмешаться в ход построения отчета и подменить формируемые им ссылки на наш вариант. К счастью, этот код также написан на PHP. Идем в каталог 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>");
}

После чего запускаем Yasca снова и убеждаемся, что все работает. В плане дальнейшей автоматизации, наша фантазия мало чем ограничена. У нас, например, был плагин, автоматизирующий сборку большого C++ проекта под Windows посредством MSBuild. Поскольку сама сборка выполнялась около часа, небольшое замедление, связанное с работой Yasca и кодечекеров роли не играло. Зато на выходе мы получали отчет, содержащий и Warning-и и замечания по Code Style, с удобным позиционированием в исходном коде.

Автор: GlukKazan

Источник

Поделиться

* - обязательные к заполнению поля