123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- {
- Copyright (C) 2017 - 2020 by Michael Van Canneyt [email protected]
- pas2js Delphi stub generator - component
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- }
- unit stubcreator;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, strutils, inifiles, pscanner, pparser, pastree, iostream, paswrite;
- type
- { We have to override abstract TPasTreeContainer methods }
- TSimpleEngine = class(TPasTreeContainer)
- public
- function CreateElement(AClass: TPTreeElement; const AName: String;
- AParent: TPasElement; AVisibility: TPasMemberVisibility;
- const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
- override;
- function FindElement(const AName: String): TPasElement; override;
- end;
- TWriteCallBack = Procedure (Data : Pointer; AFileData : PAnsiChar; AFileDataLen: Int32); stdcall;
- TWriteEvent = Procedure(AFileData : String) of object;
- TUnitAliasCallBack = Function (Data: Pointer; AUnitName: PAnsiChar;
- var AUnitNameMaxLen: Int32): boolean; {$IFDEF UseCDecl}cdecl{$ELSE}stdcall{$ENDIF};
- { TStubCreator }
- TStubCreator = Class(TComponent)
- private
- FConfigFile: String;
- FHeaderStream: TStream;
- FIncludePaths: TStrings;
- FInputFile: String;
- FOnUnitAliasData: Pointer;
- FOnWrite: TWriteEvent;
- FOnWriteCallBack: TWriteCallBack;
- FOutputFile: String;
- FDefines : TStrings;
- FOptions: TPasWriterOptions;
- FLineNumberWidth,
- FIndentSize : Integer;
- FExtraUnits : String;
- FForwardClasses : String;
- FHeaderFile : String;
- FOutputStream: TStream;
- FWriteStream : TStringStream;
- FCallBackData : Pointer;
- FLastErrorClass : String;
- FLastError : String;
- FOnUnitAlias : TUnitAliasCallBack;
- procedure SetDefines(AValue: TStrings);
- procedure SetIncludePaths(AValue: TStrings);
- procedure SetOnWrite(AValue: TWriteEvent);
- procedure SetWriteCallback(AValue: TWriteCallBack);
- function CheckUnitAlias(const AUnitName: String): String;
- Protected
- procedure DoExecute;virtual;
- Procedure DoWriteEvent; virtual;
- procedure ReadConfig(const aFileName: String); virtual;
- procedure ReadConfig(const aIni: TIniFile); virtual;
- procedure WriteModule(M: TPasModule); virtual;
- function GetModule: TPasModule; virtual;
- Function MaybeGetFileStream(AStream : TStream; Const AFileName : String; aFileMode : Word) : TStream; virtual;
- Public
- Constructor Create(AOwner : TComponent); override;
- Destructor Destroy; override;
- Function Execute: Boolean;
- Procedure GetLastError(Out AError,AErrorClass : String);
- // Streams take precedence over filenames. They will be freed on destroy!
- // OutputStream can be used combined with write callbacks.
- Property OutputStream : TStream Read FOutputStream Write FOutputStream;
- Property HeaderStream : TStream Read FHeaderStream Write FHeaderStream;
- Property OnUnitAlias: TUnitAliasCallBack read FOnUnitAlias Write FOnUnitAlias;
- Property OnUnitAliasData : Pointer Read FOnUnitAliasData Write FOnUnitAliasData;
- Property OnWriteCallBack : TWriteCallBack Read FOnWriteCallBack Write SetWriteCallback;
- Property CallbackData : Pointer Read FCallBackData Write FCallBackData;
- Property ExtraUnits : String Read FExtraUnits write FExtraUnits;
- Published
- Property Defines : TStrings Read FDefines Write SetDefines;
- Property ConfigFileName : String Read FConfigFile Write FConfigFile;
- Property InputFileName : String Read FInputFile write FInputFile;
- Property OutputFileName : String Read FOutputFile write FOutputFile;
- Property HeaderFileName : String Read FHeaderFile write FHeaderFile;
- Property ForwardClasses : String Read FForwardClasses write FForwardClasses;
- Property IncludePaths : TStrings Read FIncludePaths Write SetIncludePaths;
- Property OnWrite : TWriteEvent Read FOnWrite Write SetOnWrite;
- end;
- Implementation
- uses Math;
- ResourceString
- SErrNoDestGiven = 'No destination file specified.';
- SErrNoSourceParsed = 'Parsing produced no file.';
- procedure TStubCreator.SetDefines(AValue: TStrings);
- begin
- if FDefines=AValue then Exit;
- FDefines.Assign(AValue);
- end;
- procedure TStubCreator.SetIncludePaths(AValue: TStrings);
- begin
- if FIncludePaths=AValue then Exit;
- FIncludePaths.Assign(AValue);
- end;
- procedure TStubCreator.SetOnWrite(AValue: TWriteEvent);
- begin
- if FOnWrite=AValue then Exit;
- FOnWrite:=AValue;
- FreeAndNil(FWriteStream);
- if Assigned(AValue) then
- FWriteStream:=TStringStream.Create('');
- end;
- procedure TStubCreator.SetWriteCallback(AValue: TWriteCallBack);
- begin
- if FOnWriteCallBack=AValue then Exit;
- FOnWriteCallBack:=AValue;
- FreeAndNil(FWriteStream);
- if Assigned(AValue) then
- FWriteStream:=TStringStream.Create('');
- end;
- function TStubCreator.CheckUnitAlias(const AUnitName: String): String;
- const
- MAX_UNIT_NAME_LENGTH = 255;
- var
- UnitMaxLenthName: Integer;
- begin
- Result := AUnitName;
- UnitMaxLenthName := Max(MAX_UNIT_NAME_LENGTH, Result.Length);
- SetLength(Result, UnitMaxLenthName);
- if FOnUnitAlias(OnUnitAliasData, @Result[1], UnitMaxLenthName) then
- Result := LeftStr(PChar(Result), UnitMaxLenthName);
- end;
- procedure TStubCreator.DoWriteEvent;
- Var
- S : String;
- begin
- If Assigned(FOnWrite) then
- FOnWrite(FWriteStream.DataString);
- if Assigned(FOnWriteCallBack) then
- begin
- S:=FWriteStream.DataString;
- FOnWriteCallBack(FCallBackData,PChar(S),Length(S));
- end;
- end;
- { TStubCreator }
- procedure TStubCreator.ReadConfig(const aFileName: String);
- Var
- ini : TMemIniFile;
- begin
- ini:=TMemIniFile.Create(AFileName);
- try
- ReadConfig(Ini);
- finally
- Ini.Free;
- end;
- end;
- procedure TStubCreator.ReadConfig(const aIni: TIniFile);
- Const
- DelChars = [',',' '];
- Var
- O : TPaswriterOptions;
- S : String;
- I : Integer;
- begin
- O:=[];
- With aIni do
- begin
- if ReadBool('Config','addlinenumber',False) then
- Include(O,woAddLineNumber);
- if ReadBool('Config','addsourcelinenumber',False) then
- Include(O,woAddLineNumber);
- FOptions:=FOptions+O;
- InputFilename:=ReadString('config','input',InputFilename);
- OutputFilename:=ReadString('config','output',OutputFilename);
- HeaderFilename:=ReadString('config','header',HeaderFilename);
- FIndentSize:=ReadInteger('config','indentsize',FIndentSize);
- FLineNumberWidth:=ReadInteger('config','linenumberwidth',FLineNumberWidth);
- FExtraUnits:=ReadString('config','extra',FExtraUnits);
- FForwardClasses:=ReadString('config','forwardclasses',FForwardClasses);
- S:=ReadString('config','defines','');
- if (S<>'') then
- For I:=1 to WordCount(S,DelChars) do
- FDefines.Add(UpperCase(ExtractWord(I,S,DelChars)));
- S:=ReadString('config','includepaths','');
- if (S<>'') then
- For I:=1 to WordCount(S,[',',';']) do
- FIncludePaths.Add(ExtractWord(I,S,[',',';']));
- end;
- if (FForwardClasses<>'') or (FForwardClasses='all') then
- Include(O,woForwardClasses);
- end;
- function TStubCreator.Execute: Boolean;
- begin
- FLastErrorClass:='';
- FLastError:='';
- Result := False;
- if Defines.IndexOf('MakeStub')=-1 then
- Try
- DoExecute;
- Result := True;
- except
- On E : Exception do
- begin
- FLastErrorClass:=E.Classname;
- FLastError:=E.Message;
- end;
- end;
- end;
- procedure TStubCreator.GetLastError(out AError, AErrorClass: String);
- begin
- AError:=FLastError;
- AErrorClass:=FLastErrorClass;
- end;
- procedure TStubCreator.DoExecute;
- Var
- M : TPasModule;
- begin
- If (ConfigFileName<>'') then
- ReadConfig(ConfigFileName);
- if InputFilename = '' then
- raise Exception.Create(SErrNoSourceGiven);
- if (OutputFilename = '') and (FoutputStream=Nil) and (FWriteStream=Nil) then
- raise Exception.Create(SErrNoDestGiven);
- if CompareText(ForwardClasses,'all')=0 then
- begin
- Include(Foptions,woForwardClasses);
- ForwardClasses:='';
- end
- else if (ForwardClasses<>'') then
- Include(Foptions,woForwardClasses);
- Include(Foptions,woForceOverload);
- M:=GetModule;
- if M=Nil then
- raise Exception.Create(SErrNoSourceParsed);
- try
- WriteModule(M);
- finally
- M.Free;
- end;
- end;
- { TSimpleEngine }
- function TSimpleEngine.CreateElement(AClass: TPTreeElement; const AName: String;
- AParent: TPasElement; AVisibility: TPasMemberVisibility;
- const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
- begin
- Result := AClass.Create(AName, AParent);
- Result.Visibility := AVisibility;
- Result.SourceFilename := ASourceFilename;
- Result.SourceLinenumber := ASourceLinenumber;
- end;
- function TSimpleEngine.FindElement(const AName: String): TPasElement;
- begin
- { dummy implementation, see TFPDocEngine.FindElement for a real example }
- Result := nil;
- if AName<>'' then ; // Keep compiler happy
- end;
- function TStubCreator.GetModule: TPasModule;
- Var
- SE : TSimpleEngine;
- FileResolver: TFileResolver;
- Parser: TPasParser;
- Scanner: TPascalScanner;
- var
- s: String;
- begin
- Result := nil;
- FileResolver := nil;
- Scanner := nil;
- Parser := nil;
- SE:=TSimpleEngine.Create;
- try
- // File resolver
- FileResolver := TFileResolver.Create;
- FileResolver.UseStreams:=True;
- FileResolver.AddIncludePath(ExtractFilePath(InputFileName));
- For S in FIncludePaths do
- FileResolver.AddIncludePath(S);
- // Scanner
- Scanner := TPascalScanner.Create(FileResolver);
- Scanner.Options:=[po_AsmWhole,po_KeepClassForward,po_ExtConstWithoutExpr];
- SCanner.LogEvents:=SE.ScannerLogEvents;
- SCanner.OnLog:=SE.Onlog;
- For S in FDefines do
- Scanner.AddDefine(S);
- if FDefines.IndexOf('MAKESTUB')=-1 then
- Scanner.AddDefine('MAKESTUB');
- Scanner.OpenFile(InputFilename);
- // Parser
- Parser:=TPasParser.Create(Scanner, FileResolver, SE);
- Parser.LogEvents:=SE.ParserLogEvents;
- Parser.OnLog:=SE.Onlog;
- Parser.Options:=Parser.Options+[po_AsmWhole,po_delphi,po_KeepClassForward,po_ExtConstWithoutExpr,po_AsyncProcs];
- Parser.ParseMain(Result);
- finally
- Parser.Free;
- Scanner.Free;
- FileResolver.Free;
- SE.Free;
- end;
- end;
- function TStubCreator.MaybeGetFileStream(AStream: TStream;
- const AFileName: String; aFileMode: Word): TStream;
- begin
- If Assigned(AStream) then
- Result:=AStream
- else if (AFileName<>'') then
- Result:=TFileStream.Create(AFileName,aFileMode)
- else
- Result:=Nil;
- end;
- constructor TStubCreator.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FDefines:=TStringList.Create;
- FIncludePaths:=TStringList.Create;
- FLineNumberWidth:=4;
- FIndentSize:=2;
- FExtraUnits:='';
- FOptions:=[woNoImplementation,woNoExternalClass,woNoExternalVar,woNoExternalFunc,woNoAsm,woSkipPrivateExternals,woAlwaysRecordHelper,woSkipHints];
- end;
- destructor TStubCreator.Destroy;
- begin
- FreeAndNil(FWriteStream);
- FreeAndNil(FOutputStream);
- FreeAndNil(FHeaderStream);
- FreeAndNil(FIncludePaths);
- FreeAndNil(FDefines);
- inherited Destroy;
- end;
- procedure TStubCreator.WriteModule(M: TPasModule);
- Var
- F,H : TStream;
- W : TPasWriter;
- begin
- W:=Nil;
- F:=MaybeGetFileStream(OutputStream,FOutputFile,fmCreate);
- if (F=Nil) then
- if FWriteStream<>nil then
- F:=FWriteStream
- else
- F:=TIOStream.Create(iosOutPut);
- try
- H:=MaybeGetFileStream(HeaderStream,FHeaderFile,fmOpenRead or fmShareDenyWrite);
- if Assigned(h) then
- try
- F.CopyFrom(H,H.Size);
- finally
- if H<>HeaderStream then
- H.Free;
- end;
- W:=TPasWriter.Create(F);
- W.Options:=FOptions;
- W.ExtraUnits:=FExtraUnits;
- if Assigned(FOnUnitAlias) then
- W.OnUnitAlias:=@CheckUnitAlias;
- if FIndentSize<>-1 then
- W.IndentSize:=FIndentSize;
- if FLineNumberWidth>0 then
- W.LineNumberWidth:=FLineNumberWidth;
- W.ForwardClasses.CommaText:=FForwardClasses;
- W.WriteModule(M);
- if Assigned(FWriteStream) then
- DoWriteEvent;
- finally
- W.Free;
- if (F<>OutputStream) and (F<>FWriteStream) then
- F.Free;
- end;
- end;
- end.
|