2015-10-24 09:48:42 +00:00
|
|
|
program fppkgrepotest;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
uses
|
|
|
|
{$IFDEF UNIX}
|
|
|
|
cthreads,
|
|
|
|
{$ENDIF}
|
2015-11-05 18:37:58 +00:00
|
|
|
{$IFDEF WINDOWS}
|
|
|
|
ShellApi,
|
|
|
|
windows,
|
|
|
|
{$ENDIF}
|
2015-10-24 09:48:42 +00:00
|
|
|
Classes,
|
|
|
|
SysUtils,
|
2015-11-05 18:37:58 +00:00
|
|
|
process,
|
2015-10-24 09:48:42 +00:00
|
|
|
CustApp,
|
|
|
|
pkgrepos,
|
|
|
|
pkgoptions,
|
|
|
|
pkgcommands,
|
|
|
|
pkgfpmake,
|
|
|
|
pkgglobals,
|
|
|
|
pkghandler,
|
|
|
|
typinfo,
|
|
|
|
fprepos,
|
|
|
|
IniFiles,
|
|
|
|
{$if (defined(unix) and not defined(android)) or defined(windows)}
|
|
|
|
pkgwget,
|
|
|
|
pkglnet,
|
|
|
|
{$endif}
|
|
|
|
fpjson;
|
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
{ TfppkgRepoTest }
|
|
|
|
|
|
|
|
TfppkgRepoTest = class(TCustomApplication)
|
|
|
|
private
|
|
|
|
FJsonResult: TJSONObject;
|
|
|
|
FJsonCommandArr: TJSONArray;
|
|
|
|
FCurrentCommand: TJSONObject;
|
|
|
|
FRepoDir: String;
|
2015-11-05 18:37:58 +00:00
|
|
|
FStartCompiler: String;
|
2015-10-24 09:48:42 +00:00
|
|
|
function GetJsonLogArray(ForceCreate: Boolean): TJSONArray;
|
|
|
|
function SetupRepository(): Boolean;
|
|
|
|
function LastError: String;
|
|
|
|
procedure LoadIniFile();
|
2015-11-05 18:37:58 +00:00
|
|
|
function ExecuteProcess(ACmd: string; AParamList: array of const): boolean;
|
|
|
|
function RemoveTree(APath: String): Boolean;
|
2015-10-24 09:48:42 +00:00
|
|
|
protected
|
|
|
|
procedure DoRun; override;
|
2015-11-05 18:37:58 +00:00
|
|
|
procedure InitializeRepository();
|
2015-10-24 09:48:42 +00:00
|
|
|
procedure TestPackage(APackageName: String);
|
|
|
|
procedure TestSinglePackage(APackage: TFPPackage);
|
|
|
|
public
|
|
|
|
constructor Create(TheOwner: TComponent); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
procedure WriteHelp; virtual;
|
|
|
|
procedure AddLogLine(Level:TLogLevel; Const Msg: String);
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
Application: TfppkgRepoTest;
|
|
|
|
|
|
|
|
procedure LogCmd(Level:TLogLevel; Const Msg: String);
|
|
|
|
begin
|
|
|
|
if (Level in LogLevels) then
|
|
|
|
Application.AddLogLine(Level, Msg);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TfppkgRepoTest }
|
|
|
|
|
|
|
|
function TfppkgRepoTest.GetJsonLogArray(ForceCreate: Boolean): TJSONArray;
|
|
|
|
var
|
|
|
|
Parent: TJSONObject;
|
|
|
|
begin
|
|
|
|
if Assigned(FCurrentCommand) then
|
|
|
|
Parent := FCurrentCommand
|
|
|
|
else
|
|
|
|
Parent := FJsonResult;
|
|
|
|
|
|
|
|
Result := Parent.Find('log', jtArray) as TJSONArray;
|
|
|
|
if ForceCreate and not assigned(Result) then
|
|
|
|
begin
|
|
|
|
Result := TJSONArray.Create;
|
|
|
|
Parent.Add('log', Result);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TfppkgRepoTest.SetupRepository: Boolean;
|
|
|
|
begin
|
|
|
|
Result := False;
|
|
|
|
try
|
|
|
|
LogLevels:=DefaultLogLevels;
|
|
|
|
pkgoptions.LoadGlobalDefaults(FRepoDir+'etc/fppkg.cfg');
|
|
|
|
LoadCompilerDefaults;
|
|
|
|
|
|
|
|
FPMakeCompilerOptions.CheckCompilerValues;
|
|
|
|
CompilerOptions.CheckCompilerValues;
|
|
|
|
|
|
|
|
LoadLocalAvailableRepository;
|
|
|
|
FindInstalledPackages(CompilerOptions);
|
|
|
|
CheckFPMakeDependencies;
|
|
|
|
|
|
|
|
LoadLocalAvailableMirrors;
|
|
|
|
|
|
|
|
Result := True;
|
|
|
|
except
|
|
|
|
on E: Exception do
|
|
|
|
Log(etError, Format('Failed to setup repository: %s', [E.Message]));
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TfppkgRepoTest.LastError: String;
|
|
|
|
var
|
|
|
|
LogArr: TJSONArray;
|
|
|
|
LogItem: TJSONObject;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
result := '';
|
|
|
|
LogArr := GetJsonLogArray(False);
|
|
|
|
if Assigned(LogArr) then
|
|
|
|
begin
|
|
|
|
for i := LogArr.Count-1 downto 0 do
|
|
|
|
begin
|
|
|
|
LogItem := (LogArr.Items[i]) as TJSONObject;
|
|
|
|
if LogItem.Get('LogLevel', '') = GetEnumName(TypeInfo(TLogLevel),LongInt(llError)) then
|
|
|
|
begin
|
|
|
|
result := LogItem.Get('Message', '');
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TfppkgRepoTest.LoadIniFile;
|
|
|
|
var
|
|
|
|
IniFile: TIniFile;
|
|
|
|
CfgFile: String;
|
|
|
|
begin
|
|
|
|
CfgFile:=ChangeFileExt(ParamStr(0), '.ini');
|
|
|
|
IniFile := TIniFile.Create(CfgFile);
|
|
|
|
try
|
|
|
|
FRepoDir := IncludeTrailingPathDelimiter(ExpandFileName(IniFile.ReadString('Settings','repodir','repotest')));
|
2015-11-05 18:37:58 +00:00
|
|
|
FStartCompiler := ExpandFileName(IniFile.ReadString('Settings','startcompiler','ppc386'+ExeExt));
|
2015-10-24 09:48:42 +00:00
|
|
|
finally
|
|
|
|
IniFile.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-11-05 18:37:58 +00:00
|
|
|
function TfppkgRepoTest.ExecuteProcess(ACmd: string; AParamList: array of const): boolean;
|
|
|
|
var
|
|
|
|
P: TProcess;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
result := False;
|
|
|
|
P := TProcess.Create(nil);
|
|
|
|
try
|
|
|
|
P.Executable:=ACmd;
|
|
|
|
for i := 0 to high(AParamList) do
|
|
|
|
begin
|
|
|
|
if AParamList[i].VType=vtAnsiString then
|
|
|
|
P.Parameters.Add(ansistring(AParamList[i].VAnsiString))
|
|
|
|
else
|
|
|
|
raise exception.CreateFmt('parameter type %d not supported',[AParamList[i].VType]);
|
|
|
|
end;
|
|
|
|
P.Options:=[poWaitOnExit];
|
|
|
|
P.Execute;
|
|
|
|
result := P.ExitCode=0;
|
|
|
|
finally
|
|
|
|
P.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TfppkgRepoTest.RemoveTree(APath: String): Boolean;
|
|
|
|
|
|
|
|
var
|
|
|
|
{$ifdef MSWINDOWS}
|
|
|
|
SHFileOpStruct: TSHFileOpStruct;
|
|
|
|
DirBuf: array[0..MAX_PATH+1] of TCHAR;
|
|
|
|
{$else MSWINDOWS}
|
|
|
|
searchRec: TSearchRec;
|
|
|
|
SearchResult: longint;
|
|
|
|
s: string;
|
|
|
|
{$endif MSWINDOWS}
|
|
|
|
|
|
|
|
begin
|
|
|
|
result := true;
|
|
|
|
{$ifdef MSWINDOWS}
|
|
|
|
try
|
|
|
|
FillChar(SHFileOpStruct, Sizeof(SHFileOpStruct), 0);
|
|
|
|
FillChar(DirBuf, Sizeof(DirBuf), 0);
|
|
|
|
StrPCopy(DirBuf, APath);
|
|
|
|
with SHFileOpStruct do
|
|
|
|
begin
|
|
|
|
pFrom := @DirBuf;
|
|
|
|
wFunc := FO_DELETE;
|
|
|
|
fFlags := FOF_NOCONFIRMATION or FOF_SILENT;
|
|
|
|
end;
|
|
|
|
Result := SHFileOperation(SHFileOpStruct) = 0;
|
|
|
|
except
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
{$else MSWINDOWS}
|
|
|
|
SearchResult := FindFirst(IncludeTrailingPathDelimiter(ADirectoryName)+AllFilesMask, faAnyFile+faSymLink, searchRec);
|
|
|
|
try
|
|
|
|
while SearchResult=0 do
|
|
|
|
begin
|
|
|
|
if (searchRec.Name<>'.') and (searchRec.Name<>'..') then
|
|
|
|
begin
|
|
|
|
s := IncludeTrailingPathDelimiter(ADirectoryName)+searchRec.Name;
|
|
|
|
if (searchRec.Attr and faDirectory)=faDirectory then
|
|
|
|
begin
|
|
|
|
if not IntRemoveTree(s) then
|
|
|
|
result := false;
|
|
|
|
end
|
|
|
|
else if not DeleteFile(s) then
|
|
|
|
result := False
|
|
|
|
else
|
|
|
|
log(vldebug, SDbgDeletedFile, [s]);
|
|
|
|
end;
|
|
|
|
SearchResult := FindNext(searchRec);
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
FindClose(searchRec);
|
|
|
|
end;
|
|
|
|
|
|
|
|
// There were reports of RemoveDir failing due to locking-problems. To solve
|
|
|
|
// these the RemoveDir is tried three times, with a delay of 5 seconds. See
|
|
|
|
// bug 21868
|
|
|
|
result := RemoveDir(ADirectoryName);
|
|
|
|
{$endif WINDOWS}
|
|
|
|
end;
|
|
|
|
|
2015-10-24 09:48:42 +00:00
|
|
|
procedure TfppkgRepoTest.DoRun;
|
|
|
|
var
|
|
|
|
ErrorMsg: String;
|
|
|
|
begin
|
|
|
|
// quick check parameters
|
2015-11-05 18:37:58 +00:00
|
|
|
ErrorMsg:=CheckOptions('htidv', ['help','test','initializerepository']);
|
2015-10-24 09:48:42 +00:00
|
|
|
if ErrorMsg<>'' then
|
|
|
|
begin
|
|
|
|
ShowException(Exception.Create(ErrorMsg));
|
|
|
|
Terminate;
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// parse parameters
|
|
|
|
|
|
|
|
if HasOption('v','verbose') then
|
|
|
|
LogLevels:=AllLogLevels;
|
|
|
|
if HasOption('d','debug') then
|
|
|
|
LogLevels:=AllLogLevels+[llDebug];
|
|
|
|
|
|
|
|
LoadIniFile();
|
|
|
|
|
|
|
|
if HasOption('h', 'help') then
|
|
|
|
begin
|
|
|
|
WriteHelp;
|
|
|
|
Terminate;
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if HasOption('t','test') then
|
|
|
|
begin
|
|
|
|
TestPackage(GetOptionValue('t','test'));
|
|
|
|
end;
|
|
|
|
|
2015-11-05 18:37:58 +00:00
|
|
|
if HasOption('i','initializerepository') then
|
|
|
|
begin
|
|
|
|
InitializeRepository();
|
|
|
|
end;
|
|
|
|
|
2015-10-24 09:48:42 +00:00
|
|
|
// stop program loop
|
|
|
|
Terminate;
|
|
|
|
|
|
|
|
WriteLn(FJsonResult.FormatJSON);
|
|
|
|
end;
|
|
|
|
|
2015-11-05 18:37:58 +00:00
|
|
|
procedure TfppkgRepoTest.InitializeRepository;
|
|
|
|
var
|
|
|
|
FpcmkcfgBin: string;
|
|
|
|
FpcPath: string;
|
|
|
|
FpccfgName: string;
|
|
|
|
FpcBin: string;
|
|
|
|
sr: TSearchRec;
|
|
|
|
UnitDir: string;
|
|
|
|
begin
|
|
|
|
if not DirectoryExistsLog(FRepoDir+'fpcsrc') then
|
|
|
|
begin
|
|
|
|
writeln('Not a valid repository-test directory: '+FRepoDir);
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
SetCurrentDir(FRepoDir+'fpcsrc');
|
|
|
|
if not ExecuteProcess('svn'+ExeExt,['update']) then
|
|
|
|
raise exception.create('Failed to run svn update');
|
|
|
|
if not ExecuteProcess('make'+ExeExt, ['clean', 'all', 'PP="'+FStartCompiler+'"', 'FPMAKEOPT="-T 4"']) then
|
|
|
|
raise exception.create('Failed to compile fpc');
|
|
|
|
RemoveDir(FRepoDir+'fpc');
|
|
|
|
if not ExecuteProcess('make'+ExeExt, ['install', 'PREFIX="'+FRepoDir+'fpc"']) then
|
|
|
|
raise exception.create('Failed to install fpc');
|
|
|
|
|
|
|
|
FpcmkcfgBin:=FRepoDir+'fpc'+DirectorySeparator+'bin'+DirectorySeparator+'i386-win32'+DirectorySeparator+'fpcmkcfg'+ExeExt;
|
|
|
|
FpcPath:=FRepoDir+'fpc';
|
|
|
|
FpccfgName:=FRepoDir+'fpc'+DirectorySeparator+'bin'+DirectorySeparator+'i386-win32'+DirectorySeparator+'fpc.cfg';
|
|
|
|
FpcBin:=FRepoDir+'fpc'+DirectorySeparator+'bin'+DirectorySeparator+'i386-win32'+DirectorySeparator+'ppc386'+ExeExt;
|
|
|
|
UnitDir:=FRepoDir+'fpc'+DirectorySeparator+'units'+DirectorySeparator+'i386-win32'+DirectorySeparator;
|
|
|
|
|
|
|
|
if not ExecuteProcess(FpcmkcfgBin, ['-p', '-d "basepath='+FpcPath+'"', '-d "basepath='+FpcPath+'"', '-o "'+FpccfgName+'"']) then
|
|
|
|
raise exception.create('Failed to create fpc.cfg');
|
|
|
|
if not ExecuteProcess(FpcmkcfgBin, ['-p', '-3','-d "LocalRepository='+FRepoDir+'fppkg'+DirectorySeparator+'"', '-o "'+FRepoDir+'etc'+DirectorySeparator+'fppkg.cfg"']) then
|
|
|
|
raise exception.create('Failed to create fppkg.cfg');
|
|
|
|
if not ExecuteProcess(FpcmkcfgBin, ['-p', '-4', '-d "GlobalPrefix='+FpcPath+'"', '-d "FpcBin='+FpcBin+'"', '-o "'+FRepoDir+'fppkg'+DirectorySeparator+'config'+DirectorySeparator+'default"']) then
|
|
|
|
raise exception.create('Failed to create fppkg.cfg');
|
|
|
|
|
|
|
|
RemoveTree(FRepoDir+'fpc'+DirectorySeparator+'fpmkinst');
|
|
|
|
|
|
|
|
if FindFirst(UnitDir+AllFiles, faDirectory, sr) = 0 then
|
|
|
|
begin
|
|
|
|
repeat
|
|
|
|
if (sr.Name <> 'rtl') and (sr.Name <> '.') and (sr.Name <> '..') then
|
|
|
|
begin
|
|
|
|
RemoveTree(UnitDir+sr.Name);
|
|
|
|
end;
|
|
|
|
until FindNext(sr)<>0;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2015-10-24 09:48:42 +00:00
|
|
|
|
|
|
|
procedure TfppkgRepoTest.TestSinglePackage(APackage: TFPPackage);
|
|
|
|
var
|
|
|
|
JsonResult: TJSONObject;
|
|
|
|
ErrMsg: String;
|
|
|
|
begin
|
|
|
|
FCurrentCommand := TJSONObject.Create;
|
|
|
|
try
|
|
|
|
FJsonCommandArr.Add(FCurrentCommand);
|
|
|
|
|
|
|
|
FCurrentCommand.Add('action', 'test');
|
|
|
|
JsonResult := TJSONObject.Create;
|
|
|
|
FCurrentCommand.Add('result', JsonResult);
|
|
|
|
|
|
|
|
if not Assigned(APackage) then
|
|
|
|
begin
|
|
|
|
JsonResult.Add('status','failed');
|
|
|
|
JsonResult.Add('message', 'Package not found');
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if not (CompilerOptions.CompilerCPU in APackage.CPUs) or
|
|
|
|
not (CompilerOptions.CompilerOS in APackage.OSes) then
|
|
|
|
begin
|
|
|
|
JsonResult.Add('status','skipped');
|
|
|
|
JsonResult.Add('message', 'Package '+APackage.Name+' is not supported fot this target');
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
FCurrentCommand.Add('packagename', APackage.Name);
|
|
|
|
|
|
|
|
try
|
|
|
|
pkghandler.ExecuteAction(APackage.Name, 'install');
|
|
|
|
except
|
|
|
|
on E: Exception do
|
|
|
|
pkgglobals.Log(llError, Format('Failed to install package %s: %s', [APackage.Name, E.Message]))
|
|
|
|
end;
|
|
|
|
|
|
|
|
ErrMsg:=LastError;
|
|
|
|
if ErrMsg<>'' then
|
|
|
|
begin
|
|
|
|
JsonResult.Add('status','failed');
|
|
|
|
JsonResult.Add('message', Format('Test failed: %s', [ErrMsg]));
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
JsonResult.Add('status','ok');
|
|
|
|
JsonResult.Add('message', 'Test passed');
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
FCurrentCommand := nil;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TfppkgRepoTest.TestPackage(APackageName: String);
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
if SetupRepository then
|
|
|
|
begin
|
|
|
|
if APackageName='all' then
|
|
|
|
begin
|
|
|
|
for i:=0 to AvailableRepository.PackageCount-1 do
|
|
|
|
TestSinglePackage(AvailableRepository.Packages[i]);
|
|
|
|
end
|
|
|
|
else
|
|
|
|
TestSinglePackage(AvailableRepository.FindPackage(APackageName));
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor TfppkgRepoTest.Create(TheOwner: TComponent);
|
|
|
|
begin
|
|
|
|
inherited Create(TheOwner);
|
|
|
|
LogHandler := @LogCmd;
|
|
|
|
FJsonResult := TJSONObject.Create;
|
|
|
|
FJsonCommandArr := TJSONArray.Create;
|
|
|
|
FJsonResult.Add('commands',FJsonCommandArr);
|
|
|
|
StopOnException:=True;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TfppkgRepoTest.Destroy;
|
|
|
|
begin
|
|
|
|
FJsonResult.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TfppkgRepoTest.WriteHelp;
|
|
|
|
begin
|
|
|
|
{ add your help code here }
|
|
|
|
writeln('Usage: ', ExeName, ' -h');
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TfppkgRepoTest.AddLogLine(Level: TLogLevel; const Msg: String);
|
|
|
|
var
|
|
|
|
AJsonObj: TJSONObject;
|
|
|
|
begin
|
|
|
|
AJsonObj := TJSONObject.Create;
|
|
|
|
GetJsonLogArray(True).Add(AJsonObj);
|
|
|
|
AJsonObj.Add('LogLevel', GetEnumName(TypeInfo(TLogLevel),LongInt(Level)));
|
|
|
|
AJsonObj.Add('Message', Msg);
|
|
|
|
end;
|
|
|
|
|
|
|
|
begin
|
|
|
|
Application:=TfppkgRepoTest.Create(nil);
|
|
|
|
Application.Title:='Fppkg repository tester';
|
|
|
|
Application.Run;
|
|
|
|
Application.Free;
|
|
|
|
end.
|
|
|
|
|