mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-04-05 13:37:47 +02:00
876 lines
27 KiB
ObjectPascal
876 lines
27 KiB
ObjectPascal
program Pas2jsReleaseCreator;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
uses
|
|
{$IFDEF UNIX}
|
|
cthreads,
|
|
{$ENDIF}
|
|
Classes, SysUtils, Types, CustApp, IniFiles, process,
|
|
FindWriteln, PRCUtils;
|
|
|
|
const
|
|
DefaultCfgFilename = 'pas2jsrelease.ini';
|
|
|
|
type
|
|
TGetDefaultEvent = function(): string of object;
|
|
|
|
{ TPas2jsReleaseCreator }
|
|
|
|
TPas2jsReleaseCreator = class(TCustomApplication)
|
|
protected
|
|
procedure DoLog(EventType: TEventType; const Msg: String); override;
|
|
procedure DoRun; override;
|
|
procedure Err(const Msg: string);
|
|
public
|
|
BuildDir: string;
|
|
BuildDir_Sources: string;
|
|
BuildDir_Bin: string;
|
|
CfgFilename: string;
|
|
FPCReleaseFilename: string; // released compiler binary
|
|
FPCDevelFilename: string; // development compiler binary
|
|
FPC2Filename: string; // optional second compiler for a second libpas2js
|
|
FPC2TargetCPU: string;
|
|
FPC2TargetOS: string;
|
|
Ini: TIniFile;
|
|
GitFilename: string; // 'git' binary
|
|
MakeFilename: string; // 'make' binary
|
|
ZipFilename: string; // 'zip' binary
|
|
Pas2jsVersion: string;
|
|
Simulate: boolean;
|
|
SourceDir: string; // cloned git release
|
|
FPCSrcDir: string;
|
|
Verbosity: integer;
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure WriteHelp; virtual;
|
|
|
|
procedure ReadPas2jsVersion;
|
|
procedure CheckForgottenWriteln;
|
|
procedure ParseFPCTargetOption(const LongOpt: string; out TargetCPU, TargetOS: string);
|
|
procedure CleanSources;
|
|
procedure CreateBuildSourceDir(const TargetOS, TargetCPU: string);
|
|
procedure BuildTools(const TargetOS, TargetCPU: string);
|
|
procedure CopySourceFolders;
|
|
procedure CopyRTLjs;
|
|
procedure CreatePas2jsCfg;
|
|
procedure CreateZip;
|
|
|
|
procedure RunTool(WorkDir, Exe: string; const ProcParams: TStringDynArray); overload;
|
|
procedure RunTool(WorkDir, Exe: string; ProcParams: TStringList); overload;
|
|
procedure ForceDir(Dir, DirTitle: string);
|
|
function Quote(const s: string): string;
|
|
function GetDefaultCfgFilename: string;
|
|
function GetDefaultBuildDir: string;
|
|
function GetDefaultTool(const Filename: string; Expanded: boolean): string;
|
|
function GetDefaultGit: string;
|
|
function GetDefaultMake: string;
|
|
function GetDefaultZip: string;
|
|
function GetOption_String(ShortOption: char; const LongOption: string): string;
|
|
function GetOption_Directory(ShortOption: char; const LongOption: string; const GetDefaultFunc: TGetDefaultEvent): string;
|
|
function GetOption_Executable(ShortOption: char; const LongOption: string; const GetDefaultFunc: TGetDefaultEvent): string;
|
|
procedure CheckExecutable(const Filename, ParamName: string);
|
|
end;
|
|
|
|
{ TPas2jsReleaseCreator }
|
|
|
|
procedure TPas2jsReleaseCreator.DoLog(EventType: TEventType; const Msg: String);
|
|
begin
|
|
case EventType of
|
|
etInfo: write('Info: ');
|
|
etWarning: write('Warning: ');
|
|
etError: write('Error: ');
|
|
etDebug: write('Debug: ');
|
|
else
|
|
write('Custom: ');
|
|
end;
|
|
writeln(Msg);
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.DoRun;
|
|
var
|
|
ErrorMsg: String;
|
|
TargetOS, TargetCPU: String;
|
|
begin
|
|
// quick check parameters
|
|
ErrorMsg:=CheckOptions('hb:c:s:l:qvx', ['help', 'config:',
|
|
'builddir:', 'sourcedir:', 'quiet', 'verbose', 'execute',
|
|
'fpcrelease:', 'fpcdevel:', 'fpcdir:', 'fpc2:', 'fpc2target:',
|
|
'git:', 'make:', 'zip:']);
|
|
if ErrorMsg<>'' then
|
|
Err(ErrorMsg);
|
|
|
|
// parse basic parameters
|
|
if HasOption('h', 'help') then begin
|
|
WriteHelp;
|
|
Terminate;
|
|
Exit;
|
|
end;
|
|
|
|
Simulate:=true;
|
|
if HasOption('q','quiet') then
|
|
dec(Verbosity);
|
|
if HasOption('v','verbose') then
|
|
inc(Verbosity);
|
|
|
|
// read config file
|
|
if HasOption('c','config') then begin
|
|
CfgFilename:=ExpandFileName(GetOptionValue('c','config'));
|
|
if not FileExists(CfgFilename) then
|
|
Err('Config file not found: "'+CfgFilename+'"');
|
|
end else begin
|
|
CfgFilename:=GetDefaultCfgFilename;
|
|
end;
|
|
if FileExists(CfgFilename) then begin
|
|
if Verbosity>=0 then
|
|
Log(etInfo,'Reading config file "'+CfgFilename+'" ...');
|
|
Ini:=TIniFile.Create(CfgFilename);
|
|
end;
|
|
|
|
BuildDir:=GetOption_Directory('b','builddir',@GetDefaultBuildDir);
|
|
SourceDir:=GetOption_Directory('s','sourcedir',nil);
|
|
if SourceDir='' then
|
|
Err('missing source directory');
|
|
FPCSrcDir:=GetOption_Directory(' ','fpcdir',nil);
|
|
FPCReleaseFilename:=GetOption_Executable(' ','fpcrelease',nil);
|
|
FPCDevelFilename:=GetOption_Executable(' ','fpcdevel',nil);
|
|
FPC2Filename:=GetOption_Executable(' ','fpc2',nil);
|
|
ParseFPCTargetOption('fpc2target',FPC2TargetCPU,FPC2TargetOS);
|
|
GitFilename:=GetOption_Executable(' ','git',@GetDefaultGit);
|
|
MakeFilename:=GetOption_Executable(' ','make',@GetDefaultMake);
|
|
ZipFilename:=GetOption_Executable(' ','zip',@GetDefaultZip);
|
|
|
|
if FPCSrcDir='' then begin
|
|
FPCSrcDir:=GetEnvironmentVariable('FPCDIR');
|
|
if FPCSrcDir<>'' then
|
|
FPCSrcDir:=AppendPathDelim(ExpandFileName(FPCSrcDir));
|
|
end;
|
|
if FPCSrcDir='' then
|
|
FPCSrcDir:=SourceDir+'compiler'+PathDelim;
|
|
|
|
// write options
|
|
if Verbosity>=0 then begin
|
|
Log(etInfo,'SourceDir: "'+SourceDir+'"');
|
|
Log(etInfo,'BuildDir: "'+BuildDir+'"');
|
|
Log(etInfo,'FPCDir: "'+FPCSrcDir+'"');
|
|
Log(etInfo,'FPCRelease: "'+FPCReleaseFilename+'"');
|
|
Log(etInfo,'FPCDevel: "'+FPCDevelFilename+'"');
|
|
Log(etInfo,'FPC2: "'+FPC2Filename+'"');
|
|
Log(etInfo,'FPC2Target: "'+FPC2TargetCPU+'-'+FPC2TargetOS+'"');
|
|
Log(etInfo,'git: "'+GitFilename+'"');
|
|
Log(etInfo,'make: "'+MakeFilename+'"');
|
|
Log(etInfo,'zip: "'+ZipFilename+'"');
|
|
end;
|
|
|
|
if HasOption('x','execute') then
|
|
Simulate:=false
|
|
else
|
|
Log(etInfo,'Simulating...');
|
|
|
|
// preflight checks
|
|
if not DirectoryExists(BuildDir) then
|
|
Err('BuildDir missing: "'+BuildDir+'"');
|
|
if not DirectoryExists(SourceDir) then
|
|
Err('SourceDir missing: "'+SourceDir+'"');
|
|
if not DirectoryExists(FPCSrcDir) then
|
|
Err('FPCDir missing: "'+FPCSrcDir+'"');
|
|
CheckExecutable(FPCReleaseFilename,'fpcrelease');
|
|
CheckExecutable(FPCDevelFilename,'fpcdevel');
|
|
if FPC2Filename<>'' then
|
|
CheckExecutable(FPC2Filename,'fpc2');
|
|
CheckExecutable(GitFilename,'git');
|
|
CheckExecutable(MakeFilename,'make');
|
|
CheckExecutable(ZipFilename,'zip');
|
|
|
|
ReadPas2jsVersion;
|
|
CheckForgottenWriteln;
|
|
|
|
// build
|
|
CleanSources;
|
|
|
|
TargetOS:=GetCompiledTargetOS;
|
|
TargetCPU:=GetCompiledTargetCPU;
|
|
CreateBuildSourceDir(TargetOS,TargetCPU);
|
|
BuildTools(TargetOS,TargetCPU);
|
|
CopySourceFolders;
|
|
CopyRTLjs;
|
|
CreatePas2jsCfg;
|
|
CreateZip;
|
|
|
|
// stop program loop
|
|
Terminate;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.Err(const Msg: string);
|
|
begin
|
|
Log(etError,Msg);
|
|
Halt(1);
|
|
end;
|
|
|
|
constructor TPas2jsReleaseCreator.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
StopOnException:=True;
|
|
end;
|
|
|
|
destructor TPas2jsReleaseCreator.Destroy;
|
|
begin
|
|
FreeAndNil(Ini);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.WriteHelp;
|
|
begin
|
|
writeln('Usage: ', ExeName, ' -h');
|
|
writeln;
|
|
writeln('-h, --help: Write this help and exit');
|
|
writeln;
|
|
writeln('Required parameters:');
|
|
writeln('-s <filename>, --sourcedir=<filename>: git directory of the pas2js release');
|
|
writeln('--fpcdir=<filename>: Path of fpc devel sources.');
|
|
writeln(' Used for compiling pas2js and libpas2js.');
|
|
writeln('--fpcrelease=<filename>: Path of released version fpc executable.');
|
|
writeln(' Used for compiling pas2js and libpas2js.');
|
|
writeln('--fpcdevel=<filename>: Path of development version fpc executable.');
|
|
writeln(' Used for compiling the other tools.');
|
|
writeln('--fpc2=<filename>: Path of a secondary fpc for building a second libpas2js.');
|
|
writeln('--fpc2target=<targetcpu>-<targetos>: Target CPU and OS for fpc2.');
|
|
writeln('-x, --execute: Do not simulate, execute the commands');
|
|
writeln;
|
|
writeln('Optional parameters:');
|
|
writeln('-q, --quiet: Less verbose');
|
|
writeln('-v, --verbose: More verbose');
|
|
writeln('-c <filename>, --config=<filename>: Path of ini file with a Main section.');
|
|
writeln(' Default: '+GetDefaultCfgFilename);
|
|
writeln('-b <filename>, --builddir=<filename>: Output directory where to build the zip.');
|
|
writeln(' Default: '+GetDefaultBuildDir);
|
|
writeln('--git=<filename>: Path of gnu make executable.');
|
|
writeln(' Default: '+GetDefaultGit);
|
|
writeln('--make=<filename>: Path of gnu make executable.');
|
|
writeln(' Default: '+GetDefaultMake);
|
|
writeln('--zip=<filename>: Path of zip executable.');
|
|
writeln(' Default: '+GetDefaultZip);
|
|
writeln;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.ReadPas2jsVersion;
|
|
|
|
function CheckPascalConstInt(const Line, Identifier: string; var aValue: integer): boolean;
|
|
var
|
|
s: String;
|
|
p, StartP: SizeInt;
|
|
begin
|
|
Result:=false;
|
|
s:=' '+Identifier+' = ';
|
|
if not SameText(LeftStr(Line,length(s)),s) then exit;
|
|
p:=length(s)+1;
|
|
StartP:=p;
|
|
aValue:=0;
|
|
while (p<=length(Line)) and (Line[p] in ['0'..'9']) do begin
|
|
aValue:=aValue*10+ord(Line[p])-ord('0');
|
|
inc(p);
|
|
end;
|
|
Result:=p>StartP;
|
|
end;
|
|
|
|
function CheckJSConstInt(const Line, Identifier: string; var aValue: integer): boolean;
|
|
var
|
|
s: String;
|
|
p, StartP: SizeInt;
|
|
begin
|
|
Result:=false;
|
|
s:=' '+Identifier+': ';
|
|
if LeftStr(Line,length(s))<>s then exit;
|
|
p:=length(s)+1;
|
|
StartP:=p;
|
|
aValue:=0;
|
|
while (p<=length(Line)) and (Line[p] in ['0'..'9']) do begin
|
|
aValue:=aValue*10+ord(Line[p])-ord('0');
|
|
inc(p);
|
|
end;
|
|
Result:=p>StartP;
|
|
end;
|
|
|
|
type
|
|
TVersionPart = (vMajor,vMinor,vRelease);
|
|
const
|
|
PartNames: array[TVersionPart] of string = ('VersionMajor','VersionMinor','VersionRelease');
|
|
var
|
|
Filename, Line: String;
|
|
sl: TStringList;
|
|
i, JSVersion: Integer;
|
|
Parts: array[TVersionPart] of integer;
|
|
PartFound: array[TVersionPart] of boolean;
|
|
p: TVersionPart;
|
|
begin
|
|
sl:=TStringList.Create;
|
|
try
|
|
// read pas2js version number from Pascal sources
|
|
Filename:=FPCSrcDir+SetDirSeparators('packages/pastojs/src/pas2jscompiler.pp');
|
|
if Verbosity>0 then
|
|
Log(etInfo,'Reading version from "'+Filename+'" ...');
|
|
if not FileExists(Filename) then
|
|
Err('Missing source file: "'+Filename+'"');
|
|
sl.LoadFromFile(Filename);
|
|
|
|
// parse source and find all three version constants
|
|
for p in TVersionPart do begin
|
|
Parts[p]:=-1;
|
|
PartFound[p]:=false;
|
|
end;
|
|
for i:=0 to sl.Count-1 do begin
|
|
Line:=sl[i];
|
|
for p in TVersionPart do
|
|
if not PartFound[p] then
|
|
PartFound[p]:=CheckPascalConstInt(Line,PartNames[p],Parts[p]);
|
|
if PartFound[High(TVersionPart)] then begin
|
|
// last constant found
|
|
if Verbosity>0 then
|
|
Log(etInfo,'Found const '+PartNames[High(TVersionPart)]+' = '+IntToStr(Parts[High(TVersionPart)]));
|
|
break;
|
|
end;
|
|
end;
|
|
for p in TVersionPart do
|
|
if not PartFound[p] then
|
|
Err('Missing '+PartNames[p]+' in "'+Filename+'"'); // one constant missing
|
|
|
|
Pas2jsVersion:=IntToStr(Parts[vMajor])+'.'+IntToStr(Parts[vMinor])+'.'+IntToStr(Parts[vRelease]);
|
|
if Verbosity>=0 then
|
|
Log(etInfo,'Pas2js version is '+Pas2jsVersion);
|
|
|
|
// read version number from rtl.js
|
|
Filename:=FPCSrcDir+SetDirSeparators('utils/pas2js/dist/rtl.js');
|
|
if Verbosity>0 then
|
|
Log(etInfo,'Reading version from "'+Filename+'" ...');
|
|
if not FileExists(Filename) then
|
|
Err('Missing source file: "'+Filename+'"');
|
|
sl.LoadFromFile(Filename);
|
|
|
|
JSVersion:=-1;
|
|
for i:=0 to sl.Count-1 do begin
|
|
Line:=sl[i];
|
|
if CheckJSConstInt(Line,'version',JSVersion) then break;
|
|
end;
|
|
if JSVersion<0 then
|
|
Err('Missing version in "'+Filename+'"');
|
|
i:=(Parts[vMajor]*100+Parts[vMinor])*100+Parts[vRelease];
|
|
if i<>JSVersion then
|
|
Err('Expected version '+IntToStr(i)+', but found '+IntToStr(JSVersion)+' in "'+Filename+'"');
|
|
finally
|
|
sl.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CheckForgottenWriteln;
|
|
|
|
procedure Check(const SrcDir: string);
|
|
begin
|
|
if not DirectoryExists(SrcDir) then
|
|
Err('Missing dource directory: "'+SrcDir+'"');
|
|
if Verbosity>=0 then
|
|
Log(etInfo,'Checking for forgotten writeln: '+SrcDir+' ...');
|
|
FindWritelnInDirectory(SrcDir,false,@DoLog);
|
|
end;
|
|
|
|
begin
|
|
Check(FPCSrcDir+'packages'+PathDelim+'fcl-js'+PathDelim+'src');
|
|
Check(FPCSrcDir+'packages'+PathDelim+'fcl-json'+PathDelim+'src');
|
|
Check(FPCSrcDir+'packages'+PathDelim+'fcl-passrc'+PathDelim+'src');
|
|
Check(FPCSrcDir+'packages'+PathDelim+'pastojs'+PathDelim+'src');
|
|
Check(FPCSrcDir+'utils'+PathDelim+'pas2js');
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.ParseFPCTargetOption(const LongOpt: string; out
|
|
TargetCPU, TargetOS: string);
|
|
var
|
|
Opt: String;
|
|
p: SizeInt;
|
|
begin
|
|
TargetOS:='';
|
|
TargetCPU:='';
|
|
Opt:=lowercase(GetOption_String(' ',LongOpt));
|
|
if Opt='' then exit;
|
|
p:=Pos('-',Opt);
|
|
if p<1 then
|
|
Err('Expected TargetCPU-TargetOS, but found "--'+LongOpt+'='+Opt+'"');
|
|
TargetCPU:=LeftStr(Opt,p-1);
|
|
TargetOS:=copy(Opt,p+1,length(Opt));
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CleanSources;
|
|
|
|
procedure Clean(Dir: string);
|
|
var
|
|
Info: TRawByteSearchRec;
|
|
Ext, Filename: String;
|
|
begin
|
|
Dir:=AppendPathDelim(Dir);
|
|
if FindFirst(Dir+AllFilesMask,faAnyFile,Info)=0 then begin
|
|
repeat
|
|
if (Info.Name='') or (Info.Name='.') or (Info.Name='..') then continue;
|
|
if (Info.Attr and faDirectory)>0 then begin
|
|
Clean(Dir+Info.Name);
|
|
end
|
|
else begin
|
|
Ext:=lowercase(ExtractFileExt(Info.Name));
|
|
case Ext of
|
|
'.ppu','.o','.rsj','.lib','.dylib':
|
|
begin
|
|
Filename:=Dir+Info.Name;
|
|
if Simulate then begin
|
|
if Verbosity>0 then
|
|
Log(etInfo,'Simulate Deleting "'+Filename+'"');
|
|
end
|
|
else begin
|
|
if DeleteFile(Filename) then begin
|
|
if Verbosity>0 then
|
|
Log(etInfo,'Deleted "'+Filename+'"');
|
|
end else begin
|
|
Err('Unable to delete "'+Filename+'"');
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
until FindNext(Info)<>0;
|
|
FindClose(Info);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
// make clean
|
|
RunTool(SourceDir,MakeFilename,['clean']);
|
|
// delete files
|
|
Clean(SourceDir+'packages');
|
|
Clean(SourceDir+'demo');
|
|
Clean(SourceDir+'tools');
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CreateBuildSourceDir(const TargetOS,
|
|
TargetCPU: string);
|
|
begin
|
|
BuildDir_Sources:=BuildDir+'pas2js-'+TargetOS+'-'+TargetCPU+'-'+Pas2jsVersion;
|
|
if DirectoryExists(BuildDir_Sources) then begin
|
|
if Simulate then begin
|
|
if Verbosity>=0 then
|
|
Log(etInfo,'Simulate: Deleting directory "'+BuildDir_Sources+'"');
|
|
end else begin
|
|
if Verbosity>=0 then
|
|
Log(etInfo,'Deleting directory "'+BuildDir_Sources+'"');
|
|
if not DeleteDirectory(BuildDir_Sources,false) then
|
|
Err('Unable to delete directory "'+BuildDir_Sources+'"');
|
|
end;
|
|
end;
|
|
if Simulate then begin
|
|
Log(etInfo,'Simulate: Creating directory "'+BuildDir_Sources+'"')
|
|
end else begin
|
|
if not ForceDirectory(BuildDir_Sources) then
|
|
Err('Unable to create directory "'+BuildDir_Sources+'"');
|
|
Log(etInfo,'Created directory "'+BuildDir_Sources+'"')
|
|
end;
|
|
BuildDir_Sources+=PathDelim;
|
|
|
|
BuildDir_Bin:=BuildDir_Sources+'bin';
|
|
if not ForceDirectory(BuildDir_Bin) then
|
|
Err('Unable to create directory "'+BuildDir_Bin+'"');
|
|
BuildDir_Bin+=PathDelim;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.BuildTools(const TargetOS, TargetCPU: string);
|
|
var
|
|
WorkDir, PkgSrcDir, UnitOutDir, CurBinDir: String;
|
|
SharedParams, TheParams: TStringList;
|
|
begin
|
|
SharedParams:=TStringList.Create;
|
|
TheParams:=TStringList.Create;
|
|
try
|
|
WorkDir:=FPCSrcDir+'utils'+PathDelim+'pas2js';
|
|
|
|
PkgSrcDir:=FPCSrcDir+'packages'+PathDelim;
|
|
SharedParams.Add('-Fu'+PkgSrcDir+'fcl-js'+PathDelim+'src');
|
|
SharedParams.Add('-Fu'+PkgSrcDir+'fcl-json'+PathDelim+'src');
|
|
SharedParams.Add('-Fu'+PkgSrcDir+'fcl-passrc'+PathDelim+'src');
|
|
SharedParams.Add('-Fu'+PkgSrcDir+'pastojs'+PathDelim+'src');
|
|
SharedParams.Add('-Fu'+PkgSrcDir+'fcl-web'+PathDelim+'src'+PathDelim+'base');
|
|
|
|
SharedParams.Add('-B');
|
|
SharedParams.Add('-MObjFPC');
|
|
SharedParams.Add('-O1');
|
|
SharedParams.Add('-Schi');
|
|
SharedParams.Add('-vew');
|
|
SharedParams.Add('-XX');
|
|
SharedParams.Add('-Xs');
|
|
|
|
UnitOutDir:=SourceDir+'units'+PathDelim+TargetCPU+'-'+TargetOS;
|
|
ForceDir(UnitOutDir,'unit output');
|
|
SharedParams.Add('-FU'+UnitOutDir);
|
|
|
|
// compile pas2js exe using release fpc
|
|
TheParams.Assign(SharedParams);
|
|
TheParams.Add('-o'+BuildDir_Bin+'pas2js'+GetExeExt);
|
|
TheParams.Add('pas2js.pp');
|
|
RunTool(WorkDir,FPCReleaseFilename,TheParams);
|
|
|
|
// compile libpas2js using release fpc
|
|
TheParams.Assign(SharedParams);
|
|
if SameText(TargetOS,'linux') then
|
|
TheParams.Add('-fPIC');
|
|
TheParams.Add('-o'+BuildDir_Bin+'libpas2js'+GetLibExt(TargetOS));
|
|
TheParams.Add('pas2jslib.pp');
|
|
RunTool(WorkDir,FPCReleaseFilename,TheParams);
|
|
|
|
if FPC2Filename<>'' then begin
|
|
// compile second libpas2js
|
|
CurBinDir:=BuildDir_Bin+FPC2TargetCPU+'-'+FPC2TargetOS+PathDelim;
|
|
ForceDir(CurBinDir,'sub folder for second libpas2js');
|
|
TheParams.Assign(SharedParams);
|
|
if SameText(FPC2TargetOS,'linux') then
|
|
TheParams.Add('-fPIC');
|
|
TheParams.Add('-o'+CurBinDir+'libpas2js'+GetLibExt(TargetOS));
|
|
TheParams.Add('-P'+FPC2TargetCPU);
|
|
TheParams.Add('-T'+FPC2TargetOS);
|
|
TheParams.Add('pas2jslib.pp');
|
|
RunTool(WorkDir,FPC2Filename,TheParams);
|
|
end;
|
|
|
|
// compile compileserver using devel fpc
|
|
TheParams.Assign(SharedParams);
|
|
TheParams.Add('-o'+BuildDir_Bin+'compileserver'+GetExeExt);
|
|
TheParams.Add('compileserver.pp');
|
|
RunTool(WorkDir,FPCDevelFilename,TheParams);
|
|
|
|
// compile webidl2pas using devel fpc
|
|
TheParams.Assign(SharedParams);
|
|
TheParams.Add('-o'+BuildDir_Bin+'webidl2pas'+GetExeExt);
|
|
TheParams.Add('webidl2pas.pp');
|
|
RunTool(WorkDir,FPCDevelFilename,TheParams);
|
|
|
|
// compile makestub using devel fpc
|
|
TheParams.Assign(SharedParams);
|
|
TheParams.Add('-o'+BuildDir_Bin+'makestub'+GetExeExt);
|
|
TheParams.Add('makestub.pp');
|
|
RunTool(WorkDir,FPCDevelFilename,TheParams);
|
|
|
|
finally
|
|
TheParams.Free;
|
|
SharedParams.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CopySourceFolders;
|
|
|
|
procedure CopyFolder(const Dir: string);
|
|
var
|
|
SrcDir, DestDir: String;
|
|
begin
|
|
SrcDir:=SourceDir+Dir;
|
|
DestDir:=BuildDir_Sources+Dir;
|
|
if not DirectoryExists(SrcDir) then
|
|
Err('Unable to copy missing source folder "'+SrcDir+'"');
|
|
|
|
// git restore SrcDir
|
|
RunTool(SourceDir,GitFilename,['restore',SrcDir]);
|
|
// copy
|
|
if Simulate then begin
|
|
Log(etInfo,'Simulate: Copying folder "'+SrcDir+'" -> "'+DestDir+'"');
|
|
end else begin
|
|
Log(etInfo,'Copying folder "'+SrcDir+'" -> "'+DestDir+'"');
|
|
CopyDirTree(SrcDir,DestDir,[cffCreateDestDirectory,cffPreserveTime,cffExceptionOnError]);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
Info: TRawByteSearchRec;
|
|
begin
|
|
CopyFolder('demo');
|
|
CopyFolder('packages');
|
|
|
|
// copy all tools except releasecreator
|
|
if not Simulate then begin
|
|
if not CreateDir(BuildDir_Sources+'tools') then
|
|
Err('Unable to create directory: '+BuildDir_Sources+'tools');
|
|
end;
|
|
if FindFirst(SourceDir+'tools'+PathDelim+AllFilesMask,faAnyFile,Info)=0 then begin
|
|
repeat
|
|
if (Info.Name='') or (Info.Name='.') or (Info.Name='..') then continue;
|
|
if (Info.Name='releasecreator') then continue;
|
|
if (Info.Attr and faDirectory)>0 then begin
|
|
CopyFolder('tools'+PathDelim+Info.Name);
|
|
end
|
|
until FindNext(Info)<>0;
|
|
FindClose(Info);
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CopyRTLjs;
|
|
var
|
|
SrcFilename, DestFilename: String;
|
|
begin
|
|
SrcFilename:=FPCSrcDir+SetDirSeparators('utils/pas2js/dist/rtl.js');
|
|
DestFilename:=BuildDir_Sources+SetDirSeparators('packages/rtl/src/rtl.js');
|
|
if Simulate then begin
|
|
Log(etInfo,'Simulate: Copying "'+SrcFilename+'" -> "'+DestFilename+'"');
|
|
end else begin
|
|
Log(etInfo,'Copying "'+SrcFilename+'" -> "'+DestFilename+'"');
|
|
CopyFile(SrcFilename,DestFilename,[cffOverwriteFile,cffPreserveTime,cffExceptionOnError]);
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CreatePas2jsCfg;
|
|
var
|
|
Dir, SrcFilename, ExeFilename, Pas2jsCfgFilename: String;
|
|
NeedBuild: Boolean;
|
|
begin
|
|
// build createconfig
|
|
Dir:=SourceDir+SetDirSeparators('tools/createconfig/');
|
|
SrcFilename:=Dir+'createconfig.pp';
|
|
ExeFilename:=Dir+'createconfig'+GetExeExt;
|
|
if not FileExists(SrcFilename) then
|
|
Err('File not found: "'+SrcFilename+'"');
|
|
NeedBuild:=true;
|
|
if not FileExists(ExeFilename) then
|
|
log(etInfo,'Missing tool createconfig, building ...')
|
|
else if FileAge(SrcFilename)>FileAge(ExeFilename) then
|
|
log(etInfo,'createconfig.pp changed, building ...')
|
|
else
|
|
NeedBuild:=false;
|
|
|
|
if NeedBuild then begin
|
|
RunTool(Dir,FPCReleaseFilename,['-O1','Schi','-vew','-XX','-Xs','createconfig.pp']);
|
|
end;
|
|
|
|
// run createconfig
|
|
Pas2jsCfgFilename:=BuildDir_Bin+'pas2js.cfg';
|
|
if Simulate then begin
|
|
Log(etInfo,'Simulate: run createconfig to create "'+Pas2jsCfgFilename+'"');
|
|
end else begin
|
|
RunTool(Dir,ExeFilename,[Pas2jsCfgFilename,'..']);
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CreateZip;
|
|
var
|
|
Dir, Filename, s: String;
|
|
begin
|
|
if not DirectoryExists(BuildDir_Sources) then
|
|
Err('TPas2jsReleaseCreator.CreateZip: Empty BuildDir_Sources');
|
|
Dir:=ExtractFilename(ChompPathDelim(BuildDir_Sources));
|
|
Filename:=BuildDir+Dir+'.zip';
|
|
if FileExists(Filename) and not Simulate then
|
|
if not DeleteFile(Filename) then
|
|
Err('Unable to delete "'+Filename+'"');
|
|
|
|
RunTool(BuildDir,ZipFilename,['-r',Filename,Dir]);
|
|
|
|
s:=IntToStr(FileSize(Filename));
|
|
if Simulate then
|
|
Log(etInfo,'Simulate: Created '+Filename+' Size='+s)
|
|
else
|
|
Log(etInfo,'Created '+Filename+' Size='+s);
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.RunTool(WorkDir, Exe: string;
|
|
const ProcParams: TStringDynArray);
|
|
var
|
|
sl: TStringList;
|
|
i: Integer;
|
|
begin
|
|
sl:=TStringList.Create;
|
|
try
|
|
for i:=0 to length(ProcParams)-1 do
|
|
sl.Add(ProcParams[i]);
|
|
RunTool(WorkDir,Exe,sl);
|
|
finally
|
|
sl.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.RunTool(WorkDir, Exe: string;
|
|
ProcParams: TStringList);
|
|
var
|
|
TheProcess: TProcess;
|
|
i, OutLen, LineStart: Integer;
|
|
OutputLine, buf, CmdLine: String;
|
|
begin
|
|
WorkDir:=ChompPathDelim(WorkDir);
|
|
if not FileIsExecutable(Exe) then
|
|
Err('Not an executable: '+Exe);
|
|
if DirectoryExists(Exe) then
|
|
Err('Not an executable: '+Exe);
|
|
if (not Simulate) and (not DirectoryExists(WorkDir)) then
|
|
Err('Workdir missing: '+WorkDir);
|
|
|
|
TheProcess:=TProcess.Create(nil);
|
|
try
|
|
TheProcess.Executable := Exe;
|
|
TheProcess.Parameters := ProcParams;
|
|
TheProcess.Options := [poUsePipes, poStdErrToOutput];
|
|
TheProcess.ShowWindow := swoHide;
|
|
TheProcess.CurrentDirectory := WorkDir;
|
|
|
|
CmdLine:=Quote(Exe);
|
|
for i:=0 to ProcParams.Count-1 do
|
|
CmdLine+=' '+Quote(ProcParams[i]);
|
|
if Simulate then begin
|
|
Log(etInfo,'Simulate: Running: WorkDir="'+WorkDir+'" Cmd: '+CmdLine);
|
|
exit;
|
|
end;
|
|
Log(etInfo,'Running: WorkDir="'+WorkDir+'" Cmd: '+CmdLine);
|
|
|
|
TheProcess.Execute;
|
|
OutputLine:='';
|
|
SetLength(buf{%H-},4096);
|
|
repeat
|
|
if (TheProcess.Output<>nil) then begin
|
|
OutLen:=TheProcess.Output.Read(Buf[1],length(Buf));
|
|
end else
|
|
OutLen:=0;
|
|
LineStart:=1;
|
|
i:=1;
|
|
while i<=OutLen do begin
|
|
if Buf[i] in [#10,#13] then begin
|
|
OutputLine:=OutputLine+copy(Buf,LineStart,i-LineStart);
|
|
writeln(OutputLine);
|
|
OutputLine:='';
|
|
if (i<OutLen) and (Buf[i+1] in [#10,#13]) and (Buf[i]<>Buf[i+1]) then
|
|
inc(i);
|
|
LineStart:=i+1;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
OutputLine:=OutputLine+copy(Buf,LineStart,OutLen-LineStart+1);
|
|
until OutLen=0;
|
|
if OutputLine<>'' then
|
|
writeln(OutputLine);
|
|
TheProcess.WaitOnExit;
|
|
if TheProcess.ExitStatus<>0 then
|
|
Err('ExitStatus: '+IntToStr(TheProcess.ExitStatus));
|
|
if TheProcess.ExitCode<>0 then
|
|
Err('ExitCode: '+IntToStr(TheProcess.ExitCode));
|
|
finally
|
|
TheProcess.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.ForceDir(Dir, DirTitle: string);
|
|
begin
|
|
Dir:=ChompPathDelim(Dir);
|
|
if DirectoryExists(Dir) then exit;
|
|
if Simulate then exit;
|
|
if ForceDirectories(Dir) then exit;
|
|
Err('Unable to create '+DirTitle+' directory "'+Dir+'"');
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.Quote(const s: string): string;
|
|
begin
|
|
Result:=s;
|
|
if Pos(' ',Result)<1 then exit;
|
|
Result:=QuotedStr(s);
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetDefaultCfgFilename: string;
|
|
begin
|
|
Result:=ExpandFileName(DefaultCfgFilename);
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetDefaultBuildDir: string;
|
|
begin
|
|
Result:=AppendPathDelim(ExpandFileName(GetTempDir(false)));
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetDefaultTool(const Filename: string;
|
|
Expanded: boolean): string;
|
|
begin
|
|
Result:=Filename;
|
|
if Expanded then begin
|
|
if FilenameIsAbsolute(Result) then exit;
|
|
if ExtractFilePath(Result)<>'' then exit;
|
|
Result:=FindDefaultExecutablePath(Result);
|
|
if Result='' then
|
|
Result:=Filename;
|
|
end;
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetDefaultGit: string;
|
|
begin
|
|
Result:=GetDefaultTool('git'+GetExeExt,true);
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetDefaultMake: string;
|
|
begin
|
|
Result:=GetDefaultTool('make'+GetExeExt,true);
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetDefaultZip: string;
|
|
begin
|
|
Result:=GetDefaultTool('zip'+GetExeExt,true);
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetOption_String(ShortOption: char;
|
|
const LongOption: string): string;
|
|
begin
|
|
if ShortOption<=' ' then begin
|
|
if HasOption(LongOption) then begin
|
|
Result:=GetOptionValue(LongOption);
|
|
exit;
|
|
end;
|
|
end else begin
|
|
if HasOption(ShortOption,LongOption) then begin
|
|
Result:=GetOptionValue(ShortOption,LongOption);
|
|
exit;
|
|
end;
|
|
end;
|
|
if Ini<>nil then begin
|
|
Result:=Ini.ReadString('Main',LongOption,'');
|
|
exit;
|
|
end;
|
|
Result:='';
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetOption_Directory(ShortOption: char;
|
|
const LongOption: string; const GetDefaultFunc: TGetDefaultEvent): string;
|
|
begin
|
|
Result:=GetOption_String(ShortOption,LongOption);
|
|
if (Result='') and Assigned(GetDefaultFunc) then
|
|
Result:=GetDefaultFunc();
|
|
if Result<>'' then
|
|
Result:=AppendPathDelim(ExpandFileName(Result));
|
|
end;
|
|
|
|
function TPas2jsReleaseCreator.GetOption_Executable(ShortOption: char;
|
|
const LongOption: string; const GetDefaultFunc: TGetDefaultEvent): string;
|
|
begin
|
|
if ShortOption<=' ' then
|
|
Result:=GetOption_String(ShortOption,LongOption)
|
|
else
|
|
Result:=GetOption_String(ShortOption,LongOption);
|
|
if (Result='') and Assigned(GetDefaultFunc) then
|
|
Result:=GetDefaultFunc();
|
|
if Result='' then exit;
|
|
if FilenameIsAbsolute(Result) then exit;
|
|
if ExtractFilePath(Result)<>'' then
|
|
Result:=ExpandFileName(Result)
|
|
else if Result<>'' then
|
|
Result:=FindDefaultExecutablePath(Result);
|
|
end;
|
|
|
|
procedure TPas2jsReleaseCreator.CheckExecutable(const Filename, ParamName: string);
|
|
begin
|
|
if Filename='' then
|
|
Err('Missing parameter '+ParamName);
|
|
if not FileExists(Filename) then
|
|
Err('File '+ParamName+' not found: "'+Filename+'"');
|
|
if not FileIsExecutable(Filename) then
|
|
Err('File '+ParamName+' not executable: "'+Filename+'"');
|
|
end;
|
|
|
|
var
|
|
Application: TPas2jsReleaseCreator;
|
|
begin
|
|
Application:=TPas2jsReleaseCreator.Create(nil);
|
|
Application.Title:='Pas2js Release Creator';
|
|
Application.Run;
|
|
Application.Free;
|
|
end.
|
|
|