Автоматизация сборки и контроля версий проекта

© 2002 Юрий Жуков

Для чего это нужно.

При разработке проекта группой программистов, типичной задачей является сборка очередной версии проекта, далее тестирование этой версии, затем снова сборка и т.п. Что бы собрать очередную версию, ответственному за сборку придется взять последние версии файлов, увеличить номер версии, собрать проект и если все пройдет успешно выложить для тестирования в определенное место готовый файл или файлы. Как правило, приходится вести историю версий. При этом нужно иметь Delphi с установленными библиотеками – соответственно переход на новую версию Delphi или какой-либо из библиотек может вызвать множество проблем: сборка на новой версии может оказаться неработоспособной и придется откатываться на старую версию Delphi и т.п.

Вообщем, это рассказ о том, как можно автоматизировать процесс сборки и отправки версий.

Где и как собирать проект.

Хочу сразу оговориться, все нижеописанные рецепты работают на Delphi 6 (Update1, Update2).

Если мы говорим об автоматической сборке, то естественно будем пользоваться командной строкой. В стандартной поставке Delphi есть две программы с помощью которых можно собирать проекты: dcc32 и make. Обе они находятся по пути $Delphi\Bin.

DCC32 собирает один файл проекта переданный в качестве командной строки. Остальные параметры (пути к библиотекам, параметры компиляции и т.п.) можно также указать в командной строке либо в файле dcc32.cfg в $Delphi\Bin.

Make собирает все проекты которые указаны в make-файле(на самом деле он последовательно вызывает dcc32 для них). Более того, в этом обычном текстовом файле можно удобно править пути к библиотекам, а также параметры для компиляции.

Простой проект

Итак, создаем директорию где будут находится файлы. Например

C:\Delphi\D6U2RTL

Расшифровываю: Delphi 6 + Update 2 + RTL update 1.

Копируем туда из соответствующей директории Delphi в такие же директории следующие файлы:

BIN\brc32.exe
BIN\brcc32.exe
BIN\dcc32.cfg
BIN\DCC32.EXE
BIN\lnkdfm60.dll
BIN\make.exe
BIN \rlink32.dll
BIN \rw32core.dll
SOURCE\*.*

Итого, около 30М, и, в принципе, этого достаточно, но для простоты и быстроты скопируйте также директории «IMPORTS» и «LIB». Получается около 110М, но кого сейчас волнуют такие размеры? Осталось поправить файл dcc32.cfg:

-aWinTypes=Windows;WinProcs=Windows;DbiProcs=BDE;DbiTypes=BDE;DbiErrs=BDE
-uC:\Delphi\D6U2RTL1\Lib;C:\Delphi\D6U2Rtl1\Bin
-iC:\Delphi\D6U2RTL1\Lib

Все это можно записать на болванку и всегда поднять систему для сборки проектов на Delphi за 5 минут на любом компьютере.

Создадим в Delphi проект для того чтобы откомпилировать с помощью нашего компилятора: Delphi-New appliсation. Затем сохраним его C:\Delphi\TestVer\testVer.dpr. Туда же сохраним наш файл группы проектов (например из Project Manager – «Save Prgect Group as»). Теперь правим строчку в файле testver.bpg

ROOT = C:\Delphi\D6U2RTL1

Компилируем наш проект:

C:\Delphi\D6U2RTL1\Bin\make.exe -fTestVer.bpg

Итак, если вы сделали все правильно, то должны были получить Testver.exe

Правила хорошего тона

Не люблю я, когда все файлы в куче, а поэтому немного модернизируем структуру каталогов -создадим отдельные папки для исходников, скомпилированных модулей и бинарников. Соответсвенно SOURCE, DCU, BIN.

C:\Delphi\TESTVER\BIN
C:\Delphi\TESTVER\DCU
C:\Delphi\TESTVER\SOURCE\
C:\Delphi\TESTVER\SOURCE\TestVer.bpg
C:\Delphi\TESTVER\SOURCE\TestVer.dpr
C:\Delphi\TESTVER\SOURCE\TestVer.res
C:\Delphi\TESTVER\SOURCE\Unit1.dfm
C:\Delphi\TESTVER\SOURCE\Unit1.pas

Теперь укажем в TestVer.bpg куда раскладывать файлы:

#------------------------------------------------------------------------------
VERSION = BWS.01
#------------------------------------------------------------------------------
!ifndef ROOT
ROOT = C:\Delphi\D6U2RTL1
!endif
#------------------------------------------------------------------------------
ProjectPath=C:\Delphi\TestVer
#------------------------------------------------------------------------------
MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$**
BRCC = $(ROOT)\bin\brcc32.exe $**
DCU = $(ProjectPath)\DCU
BIN = $(ProjectPath)\BIN
# -B = Build all units
# -E = EXE output directory
# -I = Include directories
# -N = DCU output directory
# -W = Output warning messages
# -Q = Quiet compile
# -CG = GUI target
DCC = $(ROOT)\bin\dcc32.exe $** -B -e$(BIN) -n$(DCU) -u$(LIB) -w -q -CG

#------------------------------------------------------------------------------
PROJECTS = TestVer.exe
#------------------------------------------------------------------------------

TestVer.exe: $(ProjectPath)\Source\TestVer.dpr
   cd $(ProjectPath)\Source
   $(DCC)

Теперь можно и собрать:

C:\Delphi\TestVer\Source>C:\Delphi\D6U2RTL1\Bin\make.exe -fTestVer.bpg

Подключаем дополнительные библиотеки.

Для начала давайте включим информацию о версии файла в проект и будем её выводить. В Delphi делаем следующее Project-Options

Когда вы включает эту опцию, Delphi автоматически создает скомпилированный файл ресурсов с расширением «res». Если посмотреть на файл проекта (TestVer.dpr), то можно заметить директиву компилятора {$R *.res}, которая сообщает компилятору, что нужно подключить файл с именем проекта и расширением «res».

Для вывода этой информации я воспользуюсь библиотекой Jedi Code Library, хотя это не принципиально (долгое время я пользовался RXLib, но времена меняются…). Исходники библиотеки я просто скопирую в C:\Delphi\D6U2RTL\Lib_AddOn\JCL. Теперь правим немного исходники (Source1.rar) напишем обработчик события на FormCreate:

// Пропущено
uses
  JclFileUtils;

//Пропущено
{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  Ver : TJclFileVersionInfo;
begin
  Ver := TJclFileVersionInfo.Create(Application.ExeName);
  laVerMain.Caption := Ver.FileVersion;
  laCopyr.Caption := Ver.LegalCopyright;
end;

Внимательный читатель уже заметил, что в make файле уже используется переменная LIB. Немного модифицируем файл TestVer.bpg на предмет указания путей для библиотек

LIBADDON = C:\Delphi\D6U2RTL1\Lib_AddOn
LIB = C:\Delphi\D6U2RTL1\Lib;$(LIBADDON)\JCL

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

Компиляция res файла с помощью brcc32

Все отлично, из Delphi IDE мы уже можем управлять информацией в ресурсном файле. Но как создать такой файл без Delphi IDE? Для это есть утилита brcc32. С помощью этой утилиты можно компилировать ресурсные файлы. Я приведу пример из которого надеюсь, все будет ясно. Итак, создаем файл TestVer.rc:

MAINICON ICON ir.ico
1 VERSIONINFO
FILEVERSION 1,0,0,2
PRODUCTVERSION 1,0,0,2
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP

{
  BLOCK "StringFileInfo"
  {
    BLOCK "041904E3"
    {
      VALUE "CompanyName", "ТФГ \"Test Company\"\000"
      VALUE "FileDescription", "Программа \"TestVer\"\000"
      VALUE "FileVersion", "1.0.0.2\000"
      VALUE "InternalName", "TestVer\000"
      VALUE "LegalCopyright", "\251 2002 ТФГ \"Test Company\"\000"
      VALUE "LegalTrademarks", "TRADEMARKS\000"
      VALUE "OriginalFilename", "TestVer.exe\000"
      VALUE "ProductName", "\000"
      VALUE "ProductVersion", "1.0.0.2\000"
      VALUE "Comments", "No Comments\000"
    }
  }

  BLOCK "VarFileInfo"
  {
    VALUE "Translation", 1049, 1251
  }

}

Первой строкой я указал, что главной иконкой в приложении будет иконка в файле «ir.ico». Остальные параметры, думаю, не требуют комментариев. Итак, собираем:

C:\Delphi\TestVer\Source>C:\Delphi\D6U2RTL1\Bin\brcc32.

В результате вы должны получить файл TestVer.res.

Подключим компиляцию res файла в наш make файл, теперь он будет выглядеть так:

#----------------------------------------------------------------------------
VERSION = BWS.01
#------------------------------------------------------------------------------
!ifndef ROOT
ROOT = C:\Delphi\D6U2RTL1
!endif
#------------------------------------------------------------------------------
PROJECTPATH=C:\Delphi\TestVer
#------------------------------------------------------------------------------
LIBADDON = C:\Delphi\D6U2RTL1\Lib_AddOn
LIB = C:\Delphi\D6U2RTL1\Lib;$(LIBADDON)\JCL
MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$**
DCU = $(PROJECTPATH)\DCU
BIN = $(PROJECTPATH)\BIN
# -B = Build all units
# -E = EXE output directory
# -I = Include directories
# -N = DCU output directory
# -W = Output warning messages
# -Q = Quiet compile
# -CG = GUI target
DCC = $(ROOT)\bin\dcc32.exe $** -B -e$(BIN) -n$(DCU) -u$(LIB) -w -q -CG
BRCC= $(ROOT)\bin\brcc32.exe

#------------------------------------------------------------------------------
PROJECTS = TestVer.exe
#------------------------------------------------------------------------------

TestVer.exe: $(PROJECTPATH)\Source\TestVer.dpr
  cd $(PROJECTPATH)\Source
  $(BRCC) $*
  $(DCC)

Автоматическая смена номера версии в rc файле

Воспользуемся для этого программой, написанной неким Chris Morris(Incverrc.rar). Утянул я ее с CodeCentral, и наверное, её еще можно там найти. Пользоваться этой программой очень просто – нужно в качестве параметра указать имя rc файла. Например, так

C:\Delphi\Incverrc>IncVerrc.exe /Fsample.rc

Определение версии файла в командных файлах

Теперь мы можем написать командный файл, который бы автоматически увеличивал номер версии и собирал проект. Но хотелось бы, что бы этот командный файл умел определять номер версии получившегося проекта и, например, паковать исходники проекта в архив с этим номером. Проще способа, чем написать небольшую программку, я не нашел. Итак, программа ExeVer – выводит в стандартный вывод форматированный номер версии. Для сборки требуются JCL и rxLib. Примеры работы программы ниже:

C:\Delphi\ExeVer>ExeVer.exe C:\Delphi\TestVer\Bin\TestVer.exe %J.%I.%R.%B
1.0.0.2

C:\Delphi\ExeVer>ExeVer.exe C:\Delphi\TestVer\Bin\TestVer.exe %J-%I-%2R-%3B
1-0-00-002

C:\Delphi\ExeVer>ExeVer.exe C:\Delphi\TestVer\Bin\TestVer.exe %3J
001

Алгоритм сборки

Будем действовать по следующему алгоритму.



Рисунок 1. Алгоритм сборки

Для контроля версий исподников можно использовать различные системы (мы используем Visual Source Safe), и для них легко написать свои командные файлы, поэтому я оставил только комментарии. В качестве архиватора в этом скрипте используется rar. Итак результат файл C:\Delphi\TestVer\Build.bat:
echo off
SET MAKE=C:\Delphi\D6U2RTL1\Bin\make.exe
Set EXEVER=C:\Delphi\ExeVer\ExeVer.exe
Set NAMEPROJECT=TestVer
rem SET ProjectROOT=C:\Delphi\TestVer
rem SET VSS_PROJECTROOT=
rem ---------------------------------------------------------------

cd Source

echo -- vss : берем последние версии файлов
rem ----------------------------------------------------------------
echo -- vss : CheckOut %NAMEPROJECT%.rc
echo -- : увеличиваем номер версии %NAMEPROJECT%.rc
C:\Delphi\Incverrc\IncVerrc.exe -f%NAMEPROJECT%.rc
rem ----------------------------------------------------------------
echo -- make: начинаем собирать проект

%MAKE% -f %NAMEPROJECT%.bpg > ..\Bin\Out.txt

if errorlevel 1 goto endError

echo -- make: Exe сделан успешно
rem возвращаем информацию о версии
echo -- vss : CheckIn %NAMEPROJECT%.rc

cd ..
rem ---------------Архивирование и прочее--------------------------------
%EXEVER% .\Bin\%NAMEPROJECT%.exe "set verBuild=%%J-%%I-%%2R-%%3B">t$$.bat

echo rar a -r -ep1 -s -m5 -x*.scc .\Archiv\%NAMEPROJECT%_%%verBuild%%.rar .\Source\*.* >>t$$.bat
echo on
t$$.bat /wait
del t$$.bat
goto End

:endError
rem ------------произошла ошибка во время компиляции------------------

echo -- vss : undoCheckOut %PROJECTNAME%.rc
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
echo ERROR см. out.txt
pause
cd ..\Bin

:End

Заключение

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

Приложения


Copyright© 2002 Юрий Жуков  Специально для Delphi Plus