Отчётность IBM Rational ClearCase: Модуль формирования отчётов с вычислением метрик размера и сложности программ

Comments

Метрики сложности программ принято разделять на четыре основные группы:

  1. метрики размера программ;
  2. метрики стилистики и понятности программ;
  3. метрики сложности потока управления программ;
  4. метрики сложности потока данных программ.

Метрики первой группы базируются на определении количественных характеристик, связанных с размером программы, и отличаются относительной простотой. К наиболее известным метрикам данной группы относятся число операторов программы, число строк исходного текста, набор метрик Холстеда. Метрики этой группы ориентированы на анализ исходного текста программ, поэтому они могут использоваться для оценки сложности промежуточных продуктов разработки.

Метрики второй группы показывают полноту комментирования исходного кода. При этом учитывается не просто число строк комментариев, а насколько плотно комментирован исходный код. Например, код сначала был документирован хорошо, затем – плохо; или так: шапка функции или класса документирована и комментирована, а код – нет. Суть метрики проста: код разбивается на n-равные куски и для каждого из них определяется коэффициент.

Метрики третьей группы базируются на анализе управляющего графа программы. Представителем данной группы является метрика Мак-Кейба. Управляющий граф программы, который используют метрики данной группы, может быть построен на основе алгоритмов модулей. В процессе автоматизированного вычисления показателя цикломатической сложности, как правило, применяется упрощенный подход, в соответствии с которым построение графа не осуществляется, а вычисление показателя производится на основании подсчета числа операторов управляющей логики (if, switch и т.д.) и возможного количества путей исполнения программы. Цикломатическое число Мак-Кейба показывает требуемое число проходов для покрытия всех контуров сильносвязанного графа или число тестовых прогонов программы, необходимых для исчерпывающего тестирования по принципу "работает каждая ветвь".

Метрики четвёртой группы базируются на оценке использования, конфигурации и размещения данных в программе. В первую очередь это касается глобальных переменных. К данной группе относятся метрики Чепина.

Здесь мы считаем необходимым заметить, что применение любых метрик должно выполнятся с учётом контекста. Другими словами, сами по себе метрики не являются законченными и универсальными средствами оценки трудоёмкости, применение которых всегда давало бы однозначный результат. Скорее, они указывают, в каком направлении необходимо провести детальный анализ. Или используются в конкретном случае, когда их применение твёрдо обосновано.

Итак, далее речь пойдёт собственно о получении отчётов по изменённым версиям исходного кода и вычислении метрик по ним. В ClearCase есть инструмент формирования отчётности – Report Builder. Он содержит ряд предустановленных отчётов, позволяет экспортировать полученные отчёты в различных форматах. Инструмент Report Builder вполне удовлетворяет потребности в начале использования ClearCase. Но со временем, рано или поздно, появится потребность расширения возможностей системы отчётности, например, понадобится получать метрики размера и сложности исходного кода.

Архитектура описываемого решения изображена на рисунке 1.

Рисунок 1. Архитектура решения
Рисунок 1. Архитектура решения
Рисунок 1. Архитектура решения

Инициацию формирования отчёта мы выполняем из контекстного меню версии каталога ClearCase. Из контекстного меню происходит запуск скрипта, выполняющего, собственно, запрос остальных входных данных (путь к каталогу уже введён – им является тот самый каталог, из контекстного меню которого запущен отчёт). Скрипт размещаем в каталоге с общим доступом, например на сервере ClearCase. Это избавит от необходимости обновления на клиентских местах в случае дальнейших доработок и изменений.

Скрипт выполняет следующие функции:

  • собирает входящую информацию для отчёта (временной промежуток, метки, обозначающие базовые линии, список разработчиков);
  • формирует строку запроса cleartool find для выборки нужных версий;
  • производит анализ выбранных версий – описание, подсчёт числа изменённых версий, рассчитывает метрики, создаёт атрибуты со значениями полученных метрик на обработанных версиях;
  • формирует результат отчёта в удобочитаемом виде (мы будем использовать формат MS Excel).

Для расчёта метрик мы используем бесплатно распространяемую утилиту сссс.exe. Для получения метрик необходимо запустить утилиту с указанием в аргументах пути к анализируемому файлу (версии элемента в нашем случае). Результат работы утилиты выводится в xml-файл.

Для создания атрибутов на элементах версионного хранилища необходимо предварительно создать типы атрибутов. Для этого переходим к списку типов версионного хранилища и открываем каталог attribute types. Создаём типы атрибутов (рисунок 2).

Рисунок 2. Создание типов атрибутов
Рисунок 2. Создание типов атрибутовы
Рисунок 2. Создание типов атрибутовы

Тип значения по умолчанию задаётся Integer, для метрики L_C необходимо указать тип Real (рисунок 3).

Рисунок 3. Меняем тип данных значения атрибута
Рисунок 3. Меняем тип данных значения атрибута
Рисунок 3. Меняем тип данных значения атрибута

После того как типы атрибутов сформированы, можно создавать сами атрибуты на версиях элементов версионного хранилища (рисунок 4).

Рисунок 4. Типы атрибутов для метрик
Рисунок 4. Типы атрибутов для метрик

Итак, по порядку. Сначала необходимо определить дату отчётного периода. За окончание периода примем текущую дату, а начало периода введёт пользователь. Для ввода данных используем интерфейс clearprompt. Можно было бы запросить дату одной строкой, но во избежание ввода некорректной даты (ошибок в формате ввода, или ввода даты позднее текущей) организуем ввод в графическом интерфейсе, из списка. Запрашиваем дату в следующем порядке: год, месяц, день. Для начала определим текущую дату.

use Time::localtime;
use Time::tm;

$tm = localtime;

$cyear = $tm->year+1900;
$cmon = $tm->mon+1;
$cday = $tm->mday;
$chour = $tm->hour;
$cminutes = $tm->min;

$cdato="$cday-$cmon-$cyear.$chour:$cminutes";

Составляем список годов, из которых будет производиться выбор. Начало периода – год создания версионного хранилища.

for ($y = 2005; $y < $cyear+1; $y++)
{
	$years="$years,$y";
};
$years=substr($years,1,length($years));

Далее запрашиваем выбор года отчётного периода и считываем введённое значение. В случае нажатия кнопки "Отмена" завершаем работу скрипта, при вводе пустого значения – запрашиваем ввод данных вновь.

prompty: $lab=system("clearprompt list -outfile date -items \"$years\" -prompt 
		\"Выберите год начала периода (дд-мм-гггг)\"");
if ($lab != 0) 
{
	Win32::MsgBox("Операция прервана пользователем");
	exit(1);
};

open(FL, "date") || die "Can't open file \n";
$i=0;
while (<FL>) 
{
@pas[$i]=split(FL);
$i=$i+1;
};
close FL;

$year=$pas[0];
if ($year eq "") 
{
	goto prompty ;
}
else
{
	chop($year);
};
@pas[0]="";

Описанное диалоговое окно имеет следующий вид (рисунок 5).

Рисунок 5. Диалог ввода даты
Рисунок 5. Диалог ввода даты
Рисунок 5. Диалог ввода даты

Сразу выполняем проверку, является ли введённый год високосным. Эта информация будет нужна далее, при выводе диалога выбора дня текущего периода.

@mdays = qw(31 28 31 30 31 30 31 31 30 31 30 31);
if (($year % 4) == 0)
{
if ($year % 100 != 0) 
	{ 
$mdays[1] = 29;
}
	elsif ($year % 400 == 0)
	{ 
$mdays[1] = 29;
	}
};

Далее выводим диалог выбора месяца отчётного периода. Если ранее был введён текущий год, выводим список месяцев, включая текущий (рисунок 6).

promptm: @mons=('january','february','march','april','may','june','july',
				'august','september','october','november','december');
$monses="";
if ($year == $cyear)
{
	$#mons=$cmon-1;
};

foreach $mons (@mons)
{
	$monses=$monses.$mons.",";
};
chop($monses);

$lab=system("clearprompt list -outfile date -items \"$monses\" -prompt 
			\"Выберите месяц начала периода (дд-мм-$year)\"");
if ($lab != 0) 
{
	Win32::MsgBox("Операция прервана пользователем");
	exit(1);
};
Рисунок 6. Диалог ввода даты
Рисунок 6. Диалог ввода даты
Рисунок 6. Диалог ввода даты

Далее таким же образом считываем введённое значение из файла. Затем по выбранному месяцу определяем число дней в месяце

if ($mon eq "january")
{
	$days=@mdays[0]; $imon=1;
}
elsif ($mon eq "february")
{
	$days=@mdays[1]; $imon=2;
}
elsif ($mon eq "march")
{
	$days=@mdays[2]; $imon=3;
}
elsif ($mon eq "april")
{
	$days=@mdays[3]; $imon=4;
}
elsif ($mon eq "may")
{
	$days=@mdays[4]; $imon=5;
}
elsif ($mon eq "june")
{
	$days=@mdays[5]; $imon=6;
}
elsif ($mon eq "july")
{
	$days=@mdays[6]; $imon=7;
}
elsif ($mon eq "august")
{
	$days=@mdays[7]; $imon=8;
}
elsif ($mon eq "september")
{
	$days=@mdays[8]; $imon=9;
}
elsif ($mon eq "october")
{
	$days=@mdays[9]; $imon=10;
}
elsif ($mon eq "november")
{
	$days=@mdays[10]; $imon=11;
}
elsif ($mon eq "december")
{
	$days=@mdays[11]; $imon=12;
};

После этого запрашиваем день отчётного периода. Если ранее были введены текущие год и месяц, ограничиваем список текущим днём.

if (($year == $cyear) && ($imon == $cmon))
{
	for ($d = 1; $d < $cday+1; $d++)
	{
		$dayso="$dayso,$d";
	};
	$dayso=substr($dayso,1,length($dayso));
}
else
{
	for ($d = 1; $d < $days+1; $d++)
	{
		$dayso="$dayso,$d";
	};
	$dayso=substr($dayso,1,length($dayso));
};

Форма этого диалога выглядит следующим образом (рисунок 7).

Рисунок 7. Диалог ввода даты
Рисунок 7. Диалог ввода даты
Рисунок 7. Диалог ввода даты

После считывания из файла введённого значения дня начала отчётного периода формируем строки даты. Одну из них создаём со словесным обозначением месяца (например, "april""), другую – с числовым (соответственно, "4"). Далее пригодятся оба варианта.

Дата отчётного периода определена. Кроме того, мы хотим видеть изменения в разрезе по разработчикам, которые создали эти новые версии. Для этого необходимо передать скрипту с входными параметрами список разработчиков, по которым проводится анализ. Здесь мы не будем выводить дополнительные формы, а поступим проще и, на наш взгляд, эффективнее. Передачу списка персоналий проекта, по которым проводится анализ, выполняем, как и путь к анализируемому каталогу, из самого контекстного меню (рисунок 8).

Рисунок 8. Контекстное меню ClearCase с внесёнными дополнениями
Рисунок 8. Контекстное меню ClearCase с внесёнными дополнениями

Настройки пункта "Отчёт по группе интеграции" контекстного меню приведены на рисунке 9. По второму аргументу – "integr" – скрипт определит имя файла со списком персоналий.

Рисунок 9. Настройка контекстного меню
Рисунок 9. Настройка контекстного меню
Рисунок 9. Настройка контекстного меню

Сохраняем значение второго аргумента в переменную, и формируем путь к файлу, содержащему список персоналий. Файлы созданы заранее и размещены в каталогах с открытым доступом, там же, где и файл самого выполняемого скрипта. Считываем строки файла в массив.

$otdel = $ARGV[1];
$path = "\\\\clearcase\\addins\\context_menu\\reports\\";
$file=$path.$otdel.".txt";

open(FL, $file) || die "Can't open file \n";
$i=0;

while (<FL>) 
{
@users[$i]=split(FL);
$i=$i+1;
};
close FL;

Теперь вся информация для поиска версий подготовлена, и остаётся выполнить запрос, получить выборку нужных версий и рассчитать для них метрики. Для расчета метрик мы используем бесплатно распространяемую утилиту cccc.exe. Чтобы получить метрики по исходному коду, необходимо запустить утилиту с путём к файлу, содержащему исходный код. В нашем случае это будет путь к версии элемента версионного хранилища.

Остаётся выполнить еще одно подготовительное действие. Так как результат мы хотим получить в таблицу MS Excel, нужно инициировать для этого новую книгу.

Добавим библиотеки работы с OLE-объектами и с объектами MS-Excel в заголовок скрипта.

use Win32;
use Win32::OLE;
use Win32::OLE::Const 'Microsoft Excel';

Ловим открытое приложение MS Excel, если таковое имеется, в противном случае создаём новое. Создаём новую книгу:

$Excel = Win32::OLE->GetActiveObject('Excel.Application') 
				|| Win32::OLE->new('Excel.Application'); 
$Excel->{Visible} = 1;

$Book = $Excel->Workbooks->Add;
$Sheet = $Book->Worksheets(1);

Запишем в файл заголовок – название отчёта, и шапку формируемой таблицы

$Sheet->Range("A1")->{Value} = "Отчёт за период с $idato по $cdato в каталоге $where";
$Sheet->Range("A2")->{Value} = "Разработчик";
$Sheet->Range("B2")->{Value} = "Версия файла";
$Sheet->Range("C2")->{Value} = "LOC";
$Sheet->Range("D2")->{Value} = "MVG";
$Sheet->Range("F2")->{Value} = "L_C";
$row=2;

Далее описана процедура, которая ищет по заданным критериям версии и рассчитывает для них метрики. Здесь мы должны сделать небольшое отступление, так как на этом этапе возникает вопрос обработки файлов формата XML. Скрипт, который мы пишем, будет выполняться встроенным в ClearCase языком Perl – ccperl. В поставку ccperl не включены распространённые библиотеки для обработки XML – XML::SAX и XML::Parser. Данные модули являются свободно распространяемыми, найти их можно на сайте CPAN. Там же находятся инструкции по установке дополнительных модулей, сведения о зависимых модулях и вся необходимая информация. Но при установке дополнительных модулей в ccperl возникают проблемы. Можно использовать обычный Perl, но в этом случае возникает необходимость установки его на все рабочие станции, на которых будет выполняться скрипт. Другой вариант – самостоятельно описать анализ XML-файла в скрипте, который, собственно, по своему содержанию является обычным текстовым файлом. В данном примере мы поступим именно таким образом.

Для начала проанализируем файл с исходным кодом и посмотрим, что, собственно, выдаст нам утилита. Для этого выбираем файл, и выполняем строку cccc.exe <путь_к_файлу>. Для того чтобы не запускать используемую утилиту без указания полного пути к ней, мы прописали этот путь в переменную среды окружения PATH. Утилита формирует несколько файлов, но нас интересует файл с именем анализируемого файла и расширением XML. Нас интересуют также следующие метрики: число строк кода (LOC), метрика цикломатической сложности по Мак-Кейбу для всей программы (MVG) и метрика качества комментирования программы – отношение числа строк кода к числу строк комментариев (L_C). Ниже приведён фрагмент полученного XML-файла, упомянутые метрики выделены маркером.

1.	<?xml version="1.0" encoding="utf-8"?>
2.	<!--Detailed report on module BackingBean-->
3.	<CCCC_Project>
4.	<module_summary>
5.	<lines_of_code value="425" level="0" />
6.	<lines_of_code_per_member_function value="******" level="0" />
7.	<McCabes_cyclomatic_complexity value="18" level="0" />
8.	<McCabes_cyclomatic_complexity_per_member_function value="******" level="2" />
9.	<lines_of_code value="112" level="0" />
10.	<lines_of_code_per_member_function value="********" level="2" />
11.	<lines_of_code_per_line_of_comment value="3.795" level="0" />
12.	<McCabes_cyclomatic_complexity_per_line_of_comment value="0.161" level="0" />
13.	<weighted_methods_per_class_unity value="13" level="0" />
14.	<weighted_methods_per_class_visibility value="0" level="0" />
15.	<depth_of_inheritance_tree value="0" level="0" />
16.	<number_of_children value="0" level="0" />
17.	<coupling_between_objects value="8" level="0" />
18.	<IF4 value="0" level="0" />
19.	<IF4_per_member_function value="********" level="0" />
20.	<IF4_visible value="0" level="0" />
21.	<IF4_visible_per_member_function value="********" level="0" />
22.	<IF4_concrete value="0" level="0" />
23.	<IF4_concrete_per_member_function value="********" level="0" />
24.	</module_summary>

Теперь мы можем описать правила для выборки значений метрик скриптом. Выше мы сформировали массив с именами разработчиков, работающих в версионном хранилище. Теперь выполняем поиск созданных каждым разработчиком версий, для каждой найденной версии выполняем расчет выбранных метрик. Далее описан цикл, обрабатывающий значения массива, вызов процедуры расчёта метрик и, естественно, сама процедура.

foreach $user (@users)
{
	chop($user);
	$row++;
	$Sheet->Range("A$row")->{Value} = $user;
	DoCommand("cleartool find . -name \"*.java\" -version 
			\"created_since(25-march-2007)&& created_by($user)\" -print");
	$row++;
};

Мы записали в таблицу имя разработчика и выполняем поиск в выбранном каталоге версионного хранилища ClearCase версий, созданных данным специалистом в течение указанного срока. Поиск выполняется описанной ниже процедурой, она же анализирует полученный результат и записывает его в созданную книгу MS Excel.

sub DoCommand
{
my $cmd = shift;
$count=0;

my $line;
open(CMD, "$cmd 2>&1 |");
foreach $line (<CMD>)

Мы выполнили поиск, и перехватываем результат из консоли в переменную $line. Обрабатываем полученное значение:

		{
		chop($line);
		$end = substr ($line, length($line)-2, length($line));
		if ($end ne "\\0")

Проверяем окончание строки. Мы не будем учитывать нулевые версии, так как они не содержат информации. Остальные версии подвергаются анализу.

		{
			$tmpname = substr ($line, 0, index($line, "@@"));

В переменной $tmpname мы сформировали стандартное имя файла, т.е. завершающееся расширением. Используемая в примере утилита расчета метрик не может оперировать названиями файлов, содержащими полный путь ClearCase, т.е. названиями версий.

			system("cleartool co -unr -nc -version $line ");
			system ("cccc.exe \"$tmpname\" 
					 --xml_outfile=c:\\temp\\cccc\\tmp.xml 
					--outdir=c:\\temp\\cccc");

Мы выполнили операцию CheckOut для обрабатываемой версии, чтобы увидеть её в текущем представлении ClearCase, и вычисляем метрики. Операция CheckOut выполняется без резервирования версии – на тот случай, если в настоящее время кто-то работает с данным элементом. Результат сохраняется во временном файле c:\temp\cccc\tmp.xls

		open(FL, "c:\\temp\\cccc\\tmp.xml") || die "Can't open file \n";
			$i=1;
			while (<FL>) 
			{
				@lines[$i]=split(FL);
				$i=$i+1;
			};
			close FL;

Содержимое файла считано в массив построчно. Далее, находим в нужных нам строках значения метрик (см. выше).

		$LOC=substr(@lines[5],index(@lines[5],"=\"")+2,index(@lines[5],"\"
				 level")-index(@lines[5],"=\"")-1);
		$MVG=substr(@lines[7],index(@lines[7],"=\"")+2,index(@lines[7],"\"
				 level")-index(@lines[7],"=\"")-1);
		$L_C=substr(@lines[11],index(@lines[11],"=\"")+2,index(@lines[11],"\"
				 level")-index(@lines[11],"=\"")-1);

Значения метрик записаны в переменные, и нам остаётся только сохранить полученные результаты в таблицу. Записываем название версии (здесь – полное имя, включая номер версии) и вычисленные метрики.

		$Sheet->Range("B$row")->{Value} = $line;
		$Sheet->Range("C$row")->{Value} = $LOC;
		$Sheet->Range("D$row")->{Value} = $MVG;
		$Sheet->Range("E$row")->{Value} = $L_C;

Далее отменяем CheckOut, создаём на версии атрибуты, содержащие соответствующие значения метрик и добавляем приращение к переменной указателя строки таблицы.

		system("cleartool unco -rm $line ");
		system("cleartool mkattr LOC $LOC \"$line \"");
		system("cleartool mkattr MVG $MVG \"$line \"");
		system("cleartool mkattr L_C $L_C \"$line \"");
$row++;
	};
}
close CMD;
}

Результат работы скрипта представлен в таблице 1. После завершения работы скрипта книга MS Excel остаётся открытой для дальнейшей работы. Можно настроить сохранение сформированной таблицы и рассылку по электронной почте – как угодно, в зависимости от потребностей.

Таблица 1. Результат работы скрипта

РазработчикВерсия файлаLOCMVGL_C
pavel.\controller\BackingBean.java@@\main\dev\12428163.927
.\controller\BackingBean.java@@\main\dev\11439193.658
.\controller\BackingBean.java@@\main\dev\10439193.658
.\controller\BackingBean.java@@\main\dev\9435193.884
.\controller\BackingBean.java@@\main\dev\8435193.884
.\controller\BackingBean.java@@\main\dev\7425183.795
.\controller\Mail.java@@\main\dev\4338375.541
.\controller\Mail.java@@\main\dev\3320375.246
.\controller\Mail.java@@\main\dev\2316365.097
.\controller\Mail.java@@\main\dev\1314364.906
.\controller\MenuSearch.java@@\main\dev\3109185.737
andrey.\controller\ShuttleAuthCheck.java@@\main\dev\493113.207
.\controller\ShuttleAuthCheck.java@@\main\dev\393113.207
.\controller\ShuttleAuthCheck.java@@\main\dev\294113.357
.\model\KEntity.java@@\main\dev\2205338.542
.\ProcessThread.java@@\main\dev\64913.500
.\ProcessThread.java@@\main\dev\548116.000
.\servlet\Crystal.java@@\main\dev\5545313.893
.\servlet\Crystal.java@@\main\dev\4545313.865
.\servlet\Crystal.java@@\main\dev\3535293.794
.\servlet\SecurityFilter.java@@\main\dev\965109.286
.\servlet\SecurityFilter.java@@\main\dev\865109.286

На рисунке 10 показано отображение на дереве версий атрибутов с метками. В дальнейшем, при анализе, метрики могут быть считаны из атрибутов.

Рисунок 10. Дерево версий с атрибутами
Рисунок 10. Дерево версий с атрибутами

Может быть интересен также результат сравнения исходных кодов по метрикам в различных потоках разработки. Например, в случае, когда ведётся совместная работа нескольких подразделений или, например, к проекту привлечён субподрядчик и разрабатываемые им исходные коды хранятся в отдельном потоке версионного хранилища. В этом случае анализ в разрезе версий, созданных каждым отдельным разработчиком, скорее всего уже не интересен (хотя, если нужно получить информацию с учётом вклада каждого из разработчиков, ничто этому не препятствует; это будет означать всего лишь добавление второго вложенного цикла). Если мы рассматриваем первый вариант, то просто заменяем в критериях поиска привязку к имени пользователя на параметр – название потока разработки. Строка принимает следующий вид:

cleartool find . -name \"*.java\" -version 
		\"created_since(25-march-2007)&& brtype($branch)\" –print

Также, для удобочитаемости скрипта, массив @users везде по тексту скрипта заменяем на @branches.

Полученные данные можно далее интерпретировать средствами самого инструмента MS Excel, например, строить графики (рисунки 11 и 12).

Рисунок 11. Изменение метрики LOC
Рисунок 11. Изменение метрики LOC
Рисунок 11. Изменение метрики LOC
Рисунок 12. Изменение метрики MVG
Рисунок 12. Изменение метрики MVG
Рисунок 12. Изменение метрики MVG

Как мы видим, графики на рисунках 11 и 12 коррелируются – был дописан определённый код, причем именно в плане усложнения логики выполнения модуля, но в дальнейшем были внесены корректировки, после которых исполняемый код сократился, но сложность логики по сравнению с начальным состоянием увеличилась. Можно сделать вывод, что на данном этапе было выполнено расширение модуля с последующей оптимизацией.

Метрики, полученные с разных потоков разработки, очень удобно просматривать в графическом виде. На рисунке 13 показано сравнение метрики числа строк исполняемого кода для версий, найденных на потоках разработки dev и sub.

Рисунок 13. Сравнение метрики LOC для двух потоков разработки
Рисунок 13. Сравнение метрики LOC для двух потоков разработки
Рисунок 13. Сравнение метрики LOC для двух потоков разработки

Подведём итог: плюсы решения очевидны – нет необходимости запускать отдельное приложение для создания отчёта, интересующий фрагмент исходного кода подвергается анализу непосредственно из браузера ClearCase Explorer. В стандартных отчётах Report Builder нет возможности формирования отчёта по группе разработчиков, хотя зачастую такая потребность возникает. В описанном решении она реализована (собственно, это и послужило толчком к разработке собственной системы отчётности). Централизованное хранение скрипта избавляет от необходимости обновления модуля отчётности на клиентских местах в случае его изменения. Навыки программирования на Perl позволяют получить практически любые отчёты, а также внести дополнения в сам процесс.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Rational
ArticleID=451588
ArticleTitle=Отчётность IBM Rational ClearCase: Модуль формирования отчётов с вычислением метрик размера и сложности программ
publish-date=12012009