/////////////////////////////////////////////////////////// // GGBuildTools // // Набор утилит для сборки проектов Gipat Group // // Copyright (C) 2007 Gipat Group // // Распространяется на условиях // // Gipat Group's opened EI-editor-utility license // // версии 1.0 // // // // www.gipatgroup.org // /////////////////////////////////////////////////////////// //К работе над данным файлом приложили руки, ноги.... короче аффтары: // 1) Sagrer (sagrer@yandex.ru) //////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // Класс для логики (т.е. того что не зависит // // от интерфейса) софтины вроде IssBuilder // /////////////////////////////////////////////////////////////// //Рассчитано на работу с InnoSetup 5. unit IssBuilderLogic; {$mode objfpc}{$H+} interface uses Classes, SysUtils, SconfFormat, IssFormat, VerInfoTxtFormat, ExtraFileUtilsLCL, Registry, FlistFormat, process, forms, FileUtil, ExtraFunctionsLCL; const ISSB_CompilerExecutableName = 'ISCC.exe'; type FPostExternalMessage = procedure(const TextMessage : AnsiString) of object; //Тип функции для вызова внешней функции вывода мессаги. TIssBuilderLogic = class //Класс, реально выполняющий работу софтины, по идее независим от интерфейса private Registr : TRegistry; //Объект класса для работы с вендовым реестром. protected //Защищенные методы. public //Переменные. ErrorMessage : AnsiString; //Мессага про ошибку. PostExternalMessage : FPostExternalMessage; //Функция, воспринимающая сообщения от выполняющегося процесса. //Переменные с "рабочей инфой". SconfFilePath : AnsiString; //Полный путь к загруженному sconf-файлу. ProjectName : AnsiString; //Имя проекта. ProjectLocalRoot : AnsiString; //Локальный путь к проекту. ProjectGlobalRoot : AnsiString; //Глобальный путь к проекту. ProjectSconf : TSconfFormat; //Для инфы из sconf проекта. ProjectVit : TVerInfoTxtFormat; //Для инфы из\для *.vit DefaultIss : TIssFormat; //Для инфы из дефолтного *.iss CurrentIss : TIssFormat; //Для текущей iss-ки. CurrentFlist : TFlistFormat; //Для списка файлов текущей версии. CurrentIssFileName : AnsiString; //Полный путь к файлу с текущей iss. NoCacheWrite : Boolean; //Если true - то не будет записывать в кеш вообще ничего. По умолчанию false. ISCC_Path : AnsiString; //Путь к компилеру InnoSetup. GlobalCachePath : AnsiString; //Путь к каталогу с кешем (глобальный). GlobalOutputPath : AnsiString; //Глобальный путь куда будет ложиться собранная инсталляха. GlobalSourcePath : AnsiString; //Глобальный путь откуда будут браться файлы дистрибутива. LocalOutputPath : AnsiString; //Локальный (!)относительно кеша(!) путь куда будет ложиться собранная инсталляха. LocalSourcePath : AnsiString; //Локальный (!)относительно кеша(!) путь откуда будут браться файлы дистрибутива. OldestVerFN : AnsiString; //Сюда функция AddUpdates сбрасывает самое старое имя версии (в FN-формате) которое было добавлено в iss-ку. //Конструкторы-деструкторы... constructor Create; virtual; destructor Destroy; override; //Открытые методы... function LoadConfigs() : Boolean; //Прочитать привязанные к Sconf файлы - *.vit и *.iss. Получить инфу по компилеру Iss. И такой еще момент - в sconf должно быть [Main] ProjectVer=Project, иначе прога не сможет понимать номера версий а без этого не получится собирать iss. procedure NewCurrentIssFromDef(); //Получить CurrentIss на основе DefaultIss procedure AddAppInfo(); //Вбить в iss текущую инфу проги. function AddVersionUpdate(var VitInfo : TVerInfoTxtFormat) : Boolean; //Вбить в iss апдейт с указанной версии. function AddVersionUpdate(const VerStr : AnsiString) : Boolean; //Вбить в iss апдейт с указанной версии. Версия в виде строки - 4 цифры разделенные подчеркиванием + буквы статуса - вроде 0.1.0.10rc1 function AddUpdates(const UpdVers : AnsiString; const SeparateBuilderMode : Boolean = false) : Boolean; //По умолчанию - просто добавить апдейты, указанные в строке UpdVers (в формате как в Sconf, например для [IssBuilder].CreateSeparateUpds_UpdVers). В режиме раздельной сборки (SeparateBuilderMode) - сейвит отдельные iss-ки и сразу же билдяет их. function GenFullInstall() : Boolean; //Сгенерить полную инсталляху по Sconf, который уже должен быть загружен. function GenSeparateUpdates() : Boolean; //Сгенерить раздельные обновлялки по Sconf, который уже должен быть загружен. function GenCumulateUpdate() : Boolean; //Сгенерить кумулятивный апдейт по Sconf, который уже должен быть загружен. function ProcessSconf() : Boolean; //Работать по правилам, описанным в Sconf - он должен быть уже загружен. function BuildIssFile(const FileName : AnsiString) : Boolean; //Запустить сборку iss консольным компилером InnoSetup. end; implementation ///////////////////////////////////////////// // Внутренние функции // ///////////////////////////////////////////// function CompareVersionStrs(List: TStringList; Index1: Integer; Index2: Integer) : Integer; //Функция для кастомной сортировки TStringList с версиями. var Vit1, Vit2 : TVerInfoTxtFormat; begin //Инициализация. Result := 0; Vit1 := TVerInfoTxtFormat.Create; Vit2 := TVerInfoTxtFormat.Create; //Закидываем инфу. Vit1.ReadFromFNString(List.Strings[Index1]); Vit2.ReadFromFNString(List.Strings[Index2]); //Сравниваем. if Vit1.IIsNewerThenOldVit(Vit2) = true then begin Result := 1; end else if Vit1.IsSameVersion(Vit2) = false then begin Result := -1; end; //Выкинуть мусор. Vit1.Free; Vit2.Free; end; ///////////////////////////////////////////// // TIssBuilderLogic // ///////////////////////////////////////////// //-----------------------------------------// // Конструкторы-деструкторы... // //-----------------------------------------// constructor TIssBuilderLogic.Create; begin //Проставить дефолтные значения... //Просто забить инфу в переменные... SconfFilePath := ''; ErrorMessage := ''; CurrentIssFileName := ''; NoCacheWrite := false; ISCC_Path := ''; GlobalCachePath := ''; GlobalOutputPath := ''; GlobalSourcePath := ''; LocalOutputPath := ''; LocalSourcePath := ''; OldestVerFN := ''; //Создать вложенные объекты классов... ProjectSconf := TSconfFormat.Create; ProjectVit := TVerInfoTxtFormat.Create; DefaultIss := TIssFormat.Create; CurrentIss := TIssFormat.Create; Registr := TRegistry.Create; CurrentFlist := TFlistFormat.Create; end; destructor TIssBuilderLogic.Destroy; begin //Выкидываем мусор ProjectSconf.Free; ProjectVit.Free; DefaultIss.Free; CurrentIss.Free; Registr.Free; CurrentFlist.Free; //Выполнить унаследованный деструктор inherited; end; //------------------------------------------// // Защищенные методы... // //------------------------------------------// //------------------------------------------// // Открытые методы... // //------------------------------------------// function TIssBuilderLogic.LoadConfigs() : Boolean; //Прочитать привязанные к Sconf файлы - *.vit и *.iss. //Получить инфу по компилеру Iss. //И такой еще момент - в sconf должно быть [Main] ProjectVer=Project, иначе прога //не сможет понимать номера версий а без этого не получится собирать iss. var TempStr : AnsiString; begin //Инициализация. Result := true; //Для начала.... проверим, удастся ли найти InnoSetup Compuler. //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 5_is1 - тут в реестре должна по идее валяться //инфа про расположение установленной софтины. Self.Registr.RootKey := HKEY_LOCAL_MACHINE; if Registr.OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 5_is1',false) = true then begin //Есть такой ключик. Он уже вроде как открыт. Смотрим, есть ли там значение с ключегом. if Registr.ValueExists('InstallLocation') = true then begin Self.ISCC_Path := AddLastSlash(Registr.ReadString('InstallLocation'))+ISSB_CompilerExecutableName; end; Registr.CloseKey; //Смотрим - существует ли файл компилера. if FileExists(Self.ISCC_Path) = true then begin //Есть такое дело. Self.PostExternalMessage('InnoSetup 5 compiler found at path '+Self.ISCC_Path); end else begin //Нема компилера. Result := false; Self.ErrorMessage := 'InnoSetup 5 compiler at path '+Self.ISCC_Path+' is not exists!'; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end else begin //Нема. Видать не установлен InnoSetup. Result := false; Self.ErrorMessage := 'InnoSetup 5 seems not to be installed.'; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; //Если компилер найден - то попытаемси прочитать *.vit файл. if Result = true then begin TempStr := AddLastSlash(Self.ProjectGlobalRoot)+Self.ProjectName+'.vit'; //Имя *.vit файла. if FileExists(AddLastSlash(Self.ProjectGlobalRoot)+Self.ProjectName+'.vit') = true then begin //Есть. Читаем. Self.PostExternalMessage('Reading file '+TempStr); if Self.ProjectVit.Load(TempStr) = true then begin //Прочитано. Self.PostExternalMessage('Readed.'); end else begin //Хз какая ошибка. Result := false; Self.ErrorMessage := 'VIT format error : '+Self.ProjectVit.ErrorMessage; Self.PostExternalMessage(Self.ErrorMessage); end; end else begin //Нема такого файла. Result := false; Self.ErrorMessage := 'Can''t find file '+TempStr; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; //Аналогично - читаем *.iss. Сначала надо получить абсолютное имя файла. if Result = true then begin TempStr := Self.ProjectSconf.IssBuilderSection.DefaultIssName; //Превращаем локальный путь в глобальный. TempStr := ResolveLocalPath(TempStr,Self.ProjectGlobalRoot); //Теперь собсно если файлег существует - читаем. if FileExists(TempStr) = true then begin //Есть. Читаем. Self.PostExternalMessage('Reading file '+TempStr); if Self.DefaultIss.Load(TempStr) = true then begin //Прочитано. Self.PostExternalMessage('Readed.'); end else begin //Хз какая ошибка. Result := false; Self.ErrorMessage := 'VIT format error : '+Self.DefaultIss.ErrorMessage; Self.PostExternalMessage(Self.ErrorMessage); end; end else begin //Нема такого файла. Result := false; Self.ErrorMessage := 'Can''t find file '+TempStr; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; end; procedure TIssBuilderLogic.NewCurrentIssFromDef(); //Получить CurrentIss на основе DefaultIss begin Self.CurrentIss.MakNewFile(); Self.CurrentIss.TextFil1.FileString := Self.DefaultIss.TextFil1.FileString; end; procedure TIssBuilderLogic.AddAppInfo(); //Вбить в iss текущую инфу проги. //var //TempStr : AnsiString; begin //[Setup] //OutputDir Self.CurrentIss.WriteString('Setup','OutputDir',Self.LocalOutputPath); //SourceDir Self.CurrentIss.WriteString('Setup','SourceDir',Self.LocalSourcePath); //VersionInfoVersion Self.CurrentIss.WriteString('Setup','VersionInfoVersion',Self.ProjectVit.GenerateDotsVersionString()); //VersionInfoTextVersion Self.CurrentIss.WriteString('Setup','VersionInfoTextVersion',Self.ProjectName+' '+Self.ProjectVit.GenerateShortVersionString_nosvn()); //VersionInfoDescription Self.CurrentIss.WriteString('Setup','VersionInfoDescription',Self.ProjectName+' '+Self.ProjectVit.GenerateShortVersionString_nosvn()); //AppVerName Self.CurrentIss.WriteString('Setup','AppVerName',Self.ProjectName+' '+Self.ProjectVit.GenerateShortVersionString_nosvn()); //[Code] //AppName Self.CurrentIss.WriteCodeConstValue('AppName',''''+Self.ProjectName+''''); //AppVer Self.CurrentIss.WriteCodeConstValue('AppVer',''''+Self.ProjectVit.GenerateDotsVersionString()+''''); end; function TIssBuilderLogic.AddVersionUpdate(var VitInfo : TVerInfoTxtFormat) : Boolean; //Вбить в iss апдейт с указанной версии. var OldIss : TIssFormat; OldFlist : TFlistFormat; ComponName, FuncName, OldFileName, NewFileName : AnsiString; begin //Инициализация. Result := true; OldIss := TIssFormat.Create; OldFlist := TFlistFormat.Create; OldFileName := Self.GlobalCachePath+Self.ProjectName+'_'+VitInfo.GenerateFNVersionString(); NewFileName := Self.GlobalCachePath+Self.ProjectName+'_'+Self.ProjectVit.GenerateFNVersionString(); //Так. Вбить надо следующие везчи - компонент версии, затем разницу с версии, затем файлы разницы версий. //Вбиваем компонент... ComponName := 'UpdFrom_'+VitInfo.GenerateFNVersionString_nostatus()+'_Main'; FuncName := 'CheckUpdate_'+VitInfo.GenerateFNVersionString_nostatus(); Self.CurrentIss.WriteComponent(ComponName, 'Name: '+ComponName+'; Description: Обновление с версии '+VitInfo.GenerateShortVersionString_nosvn()+ '; Flags: fixed; Check: '+FuncName+'; Types: StandardInstall'); //Вбиваем функцию-проверялку обновления... Self.CurrentIss.WriteCodeFunction(FuncName,'Function '+FuncName+'() : boolean;'+#13+#10+ 'begin'+#13+#10+' if (CurrentVer = '''+VitInfo.GenerateDotsVersionString()+''') then Result := true;'+ #13+#10+'end;'); //Вбиваем разницу между версиями... OldFileName := Self.GlobalCachePath+Self.ProjectName+'_'+VitInfo.GenerateFNVersionString(); if FileExists(OldFileName+'.iss') = true then begin Self.CurrentIss.UpdateInfoToVerUpdate(OldFileName+'.iss',ComponName); end else begin Result := false; Self.ErrorMessage := 'Can''t load file '+OldFileName+'.iss'; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; //Вбиваем разницу в файлах. if Result = true then begin Result := OldFlist.Load(OldFileName+'.flist'); if Result = true then Result := Self.CurrentIss.AddNewFilesFromFlists(OldFlist,Self.CurrentFlist,ComponName); if Result = false then begin Self.ErrorMessage := 'Can''t compare flists '+OldFileName+'.flist and '+NewFileName+'.flist'; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; //Выкинуть мусор. OldIss.Free; OldFlist.Free; end; function TIssBuilderLogic.AddVersionUpdate(const VerStr : AnsiString) : Boolean; //Вбить в iss апдейт с указанной версии. Версия в виде строки - 4 цифры разделенные подчеркиванием + буквы статуса - вроде 0.1.0.10rc1 var TempVitInfo : TVerInfoTxtFormat; begin //Функция - практически заглушка, все её назначение - перевести версию в формат инфы класса из строки и вызвать реальную функцию. //Инициализация. TempVitInfo := TVerInfoTxtFormat.Create; //Прочитать инфу... Result := TempVitInfo.ReadFromFNString(VerStr); if Result = true then begin //Вызвать реально работающую функцию. Result := Self.AddVersionUpdate(TempVitInfo); end else begin Self.ErrorMessage := TempVitInfo.ErrorMessage; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; //Выкинуть мусор. TempVitInfo.Free; end; function TIssBuilderLogic.AddUpdates(const UpdVers : AnsiString; const SeparateBuilderMode : Boolean = false) : Boolean; //По умолчанию - просто добавить апдейты, указанные в строке UpdVers (в формате как в //Sconf, например для [IssBuilder].CreateSeparateUpds_UpdVers). В режиме раздельной //сборки (SeparateBuilderMode) - сейвит отдельные iss-ки и сразу же билдяет их. var CurrIssBackUp, FindedName, TempStr, VersionsStr : AnsiString; VersionsList : TStringList; SearchRec : TSearchRec; I, UpdatesNum, J : Integer; TempVit1, TempVit2 : TVerInfoTxtFormat; Finded : Boolean; begin //Инициализация. Result := true; VersionsList := TStringList.Create; UpdatesNum := 0; TempVit1 := TVerInfoTxtFormat.Create; TempVit2 := TVerInfoTxtFormat.Create; //Существует ли каталог с кешем? if DirectoryExists(Self.GlobalCachePath) = false then begin Result := false; Self.ErrorMessage := 'Can''t find directory '+Self.GlobalCachePath; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; //Если нужно - сейвануть текущее состояние iss-ки. if (SeparateBuilderMode = true) and (Result = true) then begin CurrIssBackUp := Self.CurrentIss.TextFil1.FileString; end; //Теперь надо составить список версий из кеша. if Result = true then begin VersionsList.Clear; if FindFirst(AddLastSlash(Self.GlobalCachePath)+Self.ProjectName+'_*.flist', faAnyFile, SearchRec) = 0 then begin //Если поиск успешно начат. repeat //Повторяем поиск пока не закончится то что можно искать %). if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') and (IsDirectory(AddLastSlash(Self.GlobalCachePath)+SearchRec.Name) = false) then begin //Чего-то нашли. Убираем у файла расширение... FindedName := GetFileNameWithoutExtension(SearchRec.Name); //Кромсаем начало строки чтобы осталась только версия. Delete(FindedName,1,Length(Self.ProjectName+'_')); //Проверяем формат версии... if TempVit1.ReadFromFNString(FindedName) = true then begin; //Версия хотя-бы правильного формата... Сравниваем с текущей... if Self.ProjectVit.IIsNewerThenOldVit(TempVit1) = true then begin //Найденная версия старее чем текущая - её можно добавить в список. //Но сначала надо проверить - существует ли *.iss-файлег с тем же именем. if FileExists(AddLastSlash(Self.GlobalCachePath)+GetFileNameWithoutExtension(SearchRec.Name)+'.iss') = true then begin //Собсно есть такой файлег - можно добавлять. VersionsList.Add(FindedName); end; end; end; end; until FindNext(SearchRec) <> 0; //Если нужно - отсортировать список. if VersionsList.Count > 1 then begin VersionsList.CustomSort(@CompareVersionStrs); end; end else begin //Это не ошибка - просто в кеше нет ни 1 версии. Self.PostExternalMessage('Can''t found any version in directory '+Self.GlobalCachePath); end; end; //Подсчитать количество апдейтов. UpdatesNum := VersionsList.Count; //По умолчанию - обрабатываем все. if IsNumericStr(UpdVers) = true then begin //Просто обработать StrToInt(UpdVers) последних версий. if VersionsList.Count > StrToInt(UpdVers) then UpdatesNum := StrToInt(UpdVers); end else begin //Либо All либо From. if LowerCase(UpdVers) = 'all' then begin //All. Просто обрабатываем все что есть. //UpdatesNum := VersionsList.Count; //Уже присвоено выше. end else begin //Скорее-всего From. if Length(UpdVers) > Length('From:') then begin //гм, вполне вероятно... TempStr := UpdVers; if LowerCase(Parse(TempStr,':')) = 'from' then begin if TempVit1.ReadFromString(TempStr) = true then begin //Если версия прочиталась - то теперь сравниваем её с версиями в списке. UpdatesNum := 0; for I := 0 to VersionsList.Count-1 do begin //Смотрим - не меньше ли субж чем минимальная версия. TempVit2.ReadFromFNString(VersionsList.Strings[I]); if TempVit1.IIsNewerThenOldVit(TempVit2) = false then begin //Субж больше или равен минимальной версии. UpdatesNum := UpdatesNum+1; end;; end; end; end; end; end; end; //Подсчитано. //Теперь - запускаем добавление нужного количества апдейтов в iss. VersionsStr := ''; //Тут будет список версий для константы кода в *.iss. if SeparateBuilderMode = false then begin //При работе в обычном режиме - сообщить о записи апдейтов. Self.PostExternalMessage('Adding updates:'); end; For I := UpdatesNum-1 downto 0 do begin J := (VersionsList.Count-1)-I; //Текущая добавляемая версия. //Если это самая первая, а значит самая старая добавляемая версия - то запомнить её. if I = UpdatesNum-1 then begin Self.OldestVerFN := VersionsList.Strings[J]; end; //Добавляем файлы версии, компонент, прочие различия... TempVit1.ReadFromFNString(VersionsList.Strings[J]); //Результат функции не проверяется - по алгоритму список уже проверен на правильность. Result := Self.AddVersionUpdate(TempVit1); //В зависимости от механизма работы - либо обновляем список версий, либо собираем iss-ку. if Result = true then begin if SeparateBuilderMode = false then begin //Обычный режим работы. Добавляем версию. if Length(VersionsStr) > 0 then begin //Если в строке уже что-то есть - надо добавить разделитель. VersionsStr := VersionsStr+';'; end; VersionsStr := VersionsStr+TempVit1.GenerateDotsVersionString(); end else begin //"Раздельный" режим работы. //Вписываем в iss-ку номер апдейтабельной версии... Self.CurrentIss.WriteCodeConstValue('UpdateFrom',''''+TempVit1.GenerateDotsVersionString()+''''); //Генерим имя Iss-ки... Self.CurrentIssFileName := AddLastSlash(Self.GlobalCachePath)+Self.ProjectName+'_'+TempVit1.GenerateFNVersionString()+'_to_'+Self.ProjectVit.GenerateFNVersionString()+'.iss'; //Вбиваем имя Output... Self.CurrentIss.WriteString('Setup','OutputBaseFilename',GetFileNameWithoutExtension(Self.CurrentIssFileName)); //Сейвим Iss-ку. Result := Self.CurrentIss.Save(Self.CurrentIssFileName); if Result = true then begin Self.PostExternalMessage('Saved file '+Self.CurrentIssFileName); //Запускаем сборку Iss-ки. Result := Self.BuildIssFile(Self.CurrentIssFileName); if Self.NoCacheWrite = true then begin //Если в кеш ничО нельзя писать - убить нафег iss-ку if DeleteFile(Self.CurrentIssFileName) = true then begin Self.PostExternalMessage('Deleted file '+Self.CurrentIssFileName); end else begin Self.PostExternalMessage('Achtung!!! Can''t delete file '+Self.CurrentIssFileName); end; end; end else begin //Облом сохранения. Self.ErrorMessage := 'Can''t save file '+Self.CurrentIssFileName; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; //Засейвлено и скомпилено - восстанавливаем забекапленный текст iss-ки. Self.CurrentIss.TextFil1.FileString := CurrIssBackUp; end; end else begin //Ошибк при добавлении файлов. В принципе о ошибке уже сообщила AddVersionUpdate. end; //Если все ок - и работаем в обычном режиме - оставляем сообщение о добавленной версии. if (Result = true) and (SeparateBuilderMode = false) then begin Writeln(' '+VersionsList.Strings[J]); end; //В случае ошибки в интерации - остановка. if Result = false then Break; end; if (Result = true) and (SeparateBuilderMode = false) then begin //Если работали в обычном режиме - закинуть список версий в константу. Self.CurrentIss.WriteCodeConstValue('UpdateFrom',''''+VersionsStr+''''); //Сообщаем о успешном завершении записи апдейтов. Writeln('Updates added.'); end; //Выкинуть мусор. VersionsList.Free; TempVit1.Free; TempVit2.Free; end; function TIssBuilderLogic.GenFullInstall() : Boolean; //Сгенерить полную инсталляху по Sconf, который уже должен быть загружен. begin //Инициализация. Result := true; //Готовим Iss-ку... Self.NewCurrentIssFromDef(); Self.CurrentIssFileName := AddLastSlash(Self.GlobalCachePath)+Self.ProjectName+'_'+Self.ProjectVit.GenerateFNVersionString()+'.iss'; Self.AddAppInfo(); //Проставляем настройки установки. Self.CurrentIss.WriteCodeConstValue('NoFullInstall','false'); Self.CurrentIss.WriteString('Setup','OutputBaseFilename',Self.ProjectName+'_'+Self.ProjectVit.GenerateFNVersionString()); if Self.ProjectSconf.IssBuilderSection.CreateFull_AllowUpdate = true then begin //Если разрешено добавление апдейтов - то собсно разрешить и их. Self.CurrentIss.WriteCodeConstValue('NoUpdate','false'); //Константу со списком обновлябельных версий пока что обнулить - она будет //заполнена при генерации собсно апдейтов. Self.CurrentIss.WriteCodeConstValue('UpdateFrom',''''''); end else begin //Если апдейты не разрешены - проставить запрет. Self.CurrentIss.WriteCodeConstValue('NoUpdate','true'); end; //Добавляем полную версию установки. //Добавляем компонент... Self.CurrentIss.WriteComponent('CurrVersion_Main','Name: CurrVersion_Main; Description: Основные файлы полной версии; Flags: fixed; Check: CheckDoNewInstall; Types: StandardInstall'); //Добавляем файлы для этого компонента... Self.CurrentIss.AddFilesFromFlist(Self.CurrentFlist,'CurrVersion_Main'); //Добавляем апдейты... Self.AddUpdates(Self.ProjectSconf.IssBuilderSection.CreateFull_UpdVers); //Сейвим Iss-ку. Result := Self.CurrentIss.Save(Self.CurrentIssFileName); if Result = true then begin Self.PostExternalMessage('Saved file '+Self.CurrentIssFileName); //Запускаем сборку Iss-ки. Result := Self.BuildIssFile(Self.CurrentIssFileName); if Self.NoCacheWrite = true then begin //Если в кеш ничО нельзя писать - убить нафег iss-ку if DeleteFile(Self.CurrentIssFileName) = true then begin Self.PostExternalMessage('Deleted file '+Self.CurrentIssFileName); end else begin Self.PostExternalMessage('Achtung!!! Can''t delete file '+Self.CurrentIssFileName); end; end; end else begin //Облом сохранения. Self.ErrorMessage := 'Can''t save file '+Self.CurrentIssFileName; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; function TIssBuilderLogic.GenSeparateUpdates() : Boolean; //Сгенерить раздельные обновлялки по Sconf, который уже должен быть загружен. begin //Инициализация. Result := true; //Готовим Iss-ку... Self.NewCurrentIssFromDef(); Self.AddAppInfo(); //Проставляем настройки установки. Self.CurrentIss.WriteCodeConstValue('NoFullInstall','true'); Self.CurrentIss.WriteCodeConstValue('NoUpdate','false'); //Константу со списком обновлябельных версий пока что обнулить - она будет //заполнена при генерации собсно апдейтов. Self.CurrentIss.WriteCodeConstValue('UpdateFrom',''''''); //Запускаем добавление апдейтов с одновременной раздельной сборкой. Self.AddUpdates(Self.ProjectSconf.IssBuilderSection.CreateSeparateUpds_UpdVers,true); end; function TIssBuilderLogic.GenCumulateUpdate() : Boolean; //Сгенерить кумулятивный апдейт по Sconf, который уже должен быть загружен. begin //Инициализация. Result := true; //Готовим Iss-ку... Self.NewCurrentIssFromDef(); Self.AddAppInfo(); //Проставляем настройки установки. Self.CurrentIss.WriteCodeConstValue('NoFullInstall','true'); Self.CurrentIss.WriteCodeConstValue('NoUpdate','false'); //Константу со списком обновлябельных версий пока что обнулить - она будет //заполнена при генерации собсно апдейтов. Self.CurrentIss.WriteCodeConstValue('UpdateFrom',''''''); //Добавляем апдейты... Self.AddUpdates(Self.ProjectSconf.IssBuilderSection.CreateCumulateUpdate_UpdVers); //Смотрим - были ли добавлены какие-либо версии? if Length(Self.CurrentIss.ReadCodeConstValue('UpdateFrom')) > 2 then begin //Реально, какие-то версии были добавлены - значит есть смысл это собирать. //Генерим имена файлов. Self.CurrentIssFileName := AddLastSlash(Self.GlobalCachePath)+Self.ProjectName+'_'+Self.OldestVerFN+'_to_'+Self.ProjectVit.GenerateFNVersionString()+'_CumulateUpdate.iss'; Self.CurrentIss.WriteString('Setup','OutputBaseFilename',Self.ProjectName+'_'+Self.OldestVerFN+'_to_'+Self.ProjectVit.GenerateFNVersionString()+'_CumulateUpdate'); //Сейвим Iss-ку. Result := Self.CurrentIss.Save(Self.CurrentIssFileName); if Result = true then begin Self.PostExternalMessage('Saved file '+Self.CurrentIssFileName); //Запускаем сборку Iss-ки. Result := Self.BuildIssFile(Self.CurrentIssFileName); if Self.NoCacheWrite = true then begin //Если в кеш ничО нельзя писать - убить нафег iss-ку if DeleteFile(Self.CurrentIssFileName) = true then begin Self.PostExternalMessage('Deleted file '+Self.CurrentIssFileName); end else begin Self.PostExternalMessage('Achtung!!! Can''t delete file '+Self.CurrentIssFileName); end; end; end else begin //Облом сохранения. Self.ErrorMessage := 'Can''t save file '+Self.CurrentIssFileName; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end else begin //Облом - не добавлено ни одной версии - собирать кумулятивный апдейт нет смысла. Self.PostExternalMessage('Nothing to update - cumulative update is not needable.'); end; end; function TIssBuilderLogic.ProcessSconf() : Boolean; //Работать по правилам, описанным в Sconf - он должен быть уже загружен. var TempStr : AnsiString; begin //Инициализация. Result := true; //Получаем имя проекта, глобальный и локальный пути. Self.ProjectName := GetFileNameWithoutExtension(Self.SconfFilePath); Self.ProjectLocalRoot := AddLastSlash(Self.ProjectSconf.MainSection.ProjectRootDir); Self.ProjectGlobalRoot := ResolveLocalPath(Self.ProjectLocalRoot,ExtractFilePath(Self.SconfFilePath)); //Подгрузим конфиги. Result := Self.LoadConfigs(); //Подгрузилось? if Result = true then begin //Ага, все ок. //Генерим глобальные пути. Self.GlobalCachePath := ResolveLocalPath(Self.ProjectSconf.IssBuilderSection.VersionsCacheDir,Self.ProjectGlobalRoot); Self.GlobalOutputPath := ResolveLocalPath(Self.ProjectSconf.IssBuilderSection.OutputDir,Self.ProjectGlobalRoot); Self.GlobalSourcePath := ResolveLocalPath(Self.ProjectSconf.IssBuilderSection.FilesDir,Self.ProjectGlobalRoot); Self.LocalOutputPath := GetLocalPath(Self.GlobalCachePath,Self.GlobalOutputPath); Self.LocalSourcePath := GetLocalPath(Self.GlobalCachePath,Self.GlobalSourcePath); //Проверить - существует ли пути под output и cache. Если не существуют - создать. if DirectoryExists(Self.GlobalCachePath) = false then begin Result := ForceDirectories(Self.GlobalCachePath); if Result = true then begin //Сообщить о создании директории. Self.PostExternalMessage('Created directory '+Self.GlobalCachePath); end else begin //Сообщить о обломе. Self.ErrorMessage := 'Can''t сreate directory '+Self.GlobalCachePath; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; if (DirectoryExists(Self.GlobalOutputPath) = false) and (Result = true) then begin Result := ForceDirectories(Self.GlobalOutputPath); if Result = true then begin //Сообщить о создании директории. Self.PostExternalMessage('Created directory '+Self.GlobalOutputPath); end else begin //Сообщить о обломе. Self.ErrorMessage := 'Can''t сreate directory '+Self.GlobalOutputPath; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; //Теперь сгенерить flist-файл для данной версии. if Result = true then begin Self.PostExternalMessage('Creating distributive file list...'); Self.CurrentFlist.Clear(); Self.CurrentFlist.GlobalRoot := Self.GlobalSourcePath; Self.CurrentFlist.PathsAreAbsolute := false; Self.CurrentFlist.AddSize := true; Self.CurrentFlist.AddCRC32 := true; Result := Self.CurrentFlist.AddRealDirectory(Self.GlobalSourcePath,true); if Result = true then begin if Self.NoCacheWrite = false then begin //Удачно. Сохраняем. TempStr := AddLastSlash(Self.GlobalCachePath)+Self.ProjectName+'_'+Self.ProjectVit.GenerateFNVersionString()+'.flist'; Result := Self.CurrentFlist.Save(TempStr); if Result = true then begin //Сохранено. Self.PostExternalMessage('Saved file '+TempStr); //И засавлить директорию с файлами версии если была такая команда. if Self.ProjectSconf.IssBuilderSection.SaveToCache = true then begin //Надо савлить. Предварительно если такая дира была - убить. TempStr := AddLastSlash(Self.GlobalCachePath)+Self.ProjectName+'_'+Self.ProjectVit.GenerateFNVersionString(); if DirectoryExists(TempStr) = true then begin Self.PostExternalMessage('Removing old files cache from '+TempStr); DeleteDirectory(TempStr,true); Self.PostExternalMessage('Done.'); end; //И копируем. Self.PostExternalMessage('Saving files cache to '+TempStr); CopyDirectory(Self.GlobalSourcePath, TempStr, true, true); Self.PostExternalMessage('Done.'); end; end else begin //Облом сохранения. Self.ErrorMessage := 'Can''t save file '+TempStr; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; end else begin //Облом создания списка. Self.ErrorMessage := 'Can''t create file list for directory '+Self.GlobalSourcePath+'. Error message was: '+Self.CurrentFlist.ErrorMessage; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end; end; //Теперь - запускаем генерацию тех инсталлях, которые в Sconf указано сгенерить. if Result = true then begin //Полная сборка. if Self.ProjectSconf.IssBuilderSection.CreateFull = true then begin Result := Self.GenFullInstall(); end; //Отдельный апдейт. if (Self.ProjectSconf.IssBuilderSection.CreateSeparateUpds = true) and (Result = true) then begin Result := Result and Self.GenSeparateUpdates(); end; //Кумулятивная сборка апдейтов. if (Self.ProjectSconf.IssBuilderSection.CreateCumulateUpdate = true) and (Result = true) then begin Result := Result and Self.GenCumulateUpdate(); end; end; end; end; function TIssBuilderLogic.BuildIssFile(const FileName : AnsiString) : Boolean; //Запустить сборку iss консольным компилером InnoSetup. var IssCompiler : TProcess; begin //Инициализация. Result := true; IssCompiler := TProcess.Create(Application); if FileExists(FileName) = true then begin Self.PostExternalMessage('Compilling file '+FileName); //Собсно настраиваем... IssCompiler.CommandLine := Self.ISCC_Path+' '+FileName; IssCompiler.CurrentDirectory := Self.GlobalCachePath; IssCompiler.Options := [poNoConsole, poWaitOnExit]; //И запускаем. IssCompiler.Execute; if IssCompiler.ExitStatus <> 0 then begin //Ошибк при компилляции. Result := false; Self.ErrorMessage := 'Inno Setup compiler returned ExitStatus='+IntToStr(IssCompiler.ExitStatus)+' - seems that it was any error! Try to compile '+FileName+' manually to check for errors in script.'; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end else begin Self.PostExternalMessage('Compilled succesfully!'); end; end else begin //Нечего компилять. Result := false; Self.ErrorMessage := 'File not exists: '+FileName; Self.PostExternalMessage('Error! '+Self.ErrorMessage); end;; //Выкинуть мусор. IssCompiler.Free; end; end.