2011-09-22 00:15:42 +00:00
{
gir2pascal. lpr
Copyright ( C) 2 0 1 1 Andrew Haines andrewd207@ aol. com
This program is free software; you can redistribute it and/ or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or ( at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc. , 5 1 Franklin Street, Fifth Floor, Boston, MA 0 2 1 1 0 - 1 3 0 1 , USA.
}
program gir2pascal;
{$mode objfpc} {$H+}
2012-08-26 20:05:29 +00:00
{ $DEFINE CreatePascalClasses}
2011-09-22 00:15:42 +00:00
uses
{$IFDEF UNIX} {$IFDEF UseCThreads}
cthreads,
{$ENDIF} {$ENDIF}
2012-08-26 20:05:29 +00:00
Classes, SysUtils, CommandLineOptions, DOM, XMLRead, girNameSpaces, girFiles,
girpascalwriter, girErrors, girCTypesMapping, girTokens, girObjects,
girPascalClassWriter, girpascalwritertypes{$IFDEF UNIX} , baseunix, termio{$ENDIF} ;
2011-09-22 00:15:42 +00:00
type
{ TGirConsoleConverter }
2012-08-26 20:05:29 +00:00
TGirConsoleConverter = class
2011-09-22 00:15:42 +00:00
private
2012-08-26 20:05:29 +00:00
FCmdOptions: TCommandLineOptions;
2011-09-22 00:15:42 +00:00
FWriteCount: Integer ;
FPaths: TStringList;
FOutPutDirectory : String ;
FFileToConvert: String ;
FOverWriteFiles: Boolean ;
2012-08-26 20:05:29 +00:00
FOptions: TgirOptions;
2011-09-22 00:15:42 +00:00
procedure AddDefaultPaths;
procedure AddPaths( APaths: String ) ;
procedure VerifyOptions;
procedure Convert;
2012-08-26 20:05:29 +00:00
// options
function CheckOptions: String ;
2011-09-22 00:15:42 +00:00
//callbacks
function NeedGirFile( AGirFile: TObject; NamespaceName: String ) : TXMLDocument;
2011-09-24 00:21:23 +00:00
// AName is the whole name unit.pas or file.c
procedure WriteFile( Sender: TObject; AName: String ; AStream: TStringStream) ;
2012-08-26 20:05:29 +00:00
procedure Terminate;
2011-09-22 00:15:42 +00:00
protected
2012-08-26 20:05:29 +00:00
procedure DoRun; //override;
2011-09-22 00:15:42 +00:00
public
2012-08-26 20:05:29 +00:00
constructor Create;
2011-09-22 00:15:42 +00:00
destructor Destroy; override ;
procedure WriteHelp; virtual ;
2012-08-26 20:05:29 +00:00
procedure Run;
2011-09-22 00:15:42 +00:00
end ;
2012-08-26 20:05:29 +00:00
2011-09-22 00:15:42 +00:00
{ TGirConsoleConverter }
procedure TGirConsoleConverter. AddDefaultPaths;
begin
FPaths. Add( '/usr/share/gir-1.0/' ) ;
end ;
procedure TGirConsoleConverter. AddPaths( APaths: String ) ;
var
Strs: TStringList;
Str: String ;
begin
Strs : = TStringList. Create;
Strs. Delimiter: = ':' ;
Strs. StrictDelimiter: = True ;
Strs. DelimitedText: = APaths;
// so we can add the delimiter
for Str in Strs do
FPaths. Add( IncludeTrailingPathDelimiter( Str) ) ;
Strs. Free;
end ;
procedure TGirConsoleConverter. VerifyOptions;
begin
if not DirectoryExists( FOutPutDirectory) then
begin
WriteLn( Format( 'Output directory "%s" does not exist!' , [ FOutPutDirectory] ) ) ;
Terminate;
end ;
if FFileToConvert = '' then
begin
WriteLn( 'No input file specified! See -h for options.' ) ;
Terminate;
Halt;
end ;
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'objects' ) and FCmdOptions. HasOption( 'classes' ) then
begin
WriteLn( 'Cannot use options ' '--objects' ' and ' '--classes' ' together!.' ) ;
Terminate;
Halt;
end ;
2011-09-22 00:15:42 +00:00
end ;
function TGirConsoleConverter. NeedGirFile( AGirFile: TObject; NamespaceName: String ) : TXMLDocument;
var
Sr: TSearchRec;
Path: String ;
begin
2012-08-26 20:05:29 +00:00
WriteLn( 'Looking for gir file: ' , NamespaceName) ;
2011-09-22 00:15:42 +00:00
Result : = nil ;
for Path in FPaths do
begin
2012-08-26 20:05:29 +00:00
WriteLn( 'Looking in path: ' , Path) ;
2011-09-22 00:15:42 +00:00
if FindFirst( Path+ NamespaceName+ '.gir' , faAnyFile, Sr) = 0 then
begin
ReadXMLFile( Result , Path+ Sr. Name ) ;
Exit;
end ;
FindClose( Sr) ;
end ;
2012-08-26 20:05:29 +00:00
if Result = nil then
2012-08-26 21:39:16 +00:00
WriteLn( 'Fatal: Unable to find gir file: ' , NamespaceName) ;
2011-09-22 00:15:42 +00:00
end ;
2011-09-24 00:21:23 +00:00
procedure TGirConsoleConverter. WriteFile( Sender: TObject; AName: String ; AStream: TStringStream) ;
2011-09-22 00:15:42 +00:00
var
SStream: TFileStream;
OutFileName: String ;
begin
Inc( FWriteCount) ;
2011-09-24 00:21:23 +00:00
OutFileName: = FOutPutDirectory+ LowerCase( AName) ;
2011-09-22 00:15:42 +00:00
if not FileExists( OutFileName)
or ( FileExists( OutFileName) and FOverWriteFiles) then
begin
WriteLn( Format( 'Writing: %s' , [ OutFileName] ) ) ;
AStream. Position: = 0 ;
ForceDirectories( FOutPutDirectory) ;
SStream : = TFileStream. Create( OutFileName, fmCreate or fmOpenReadWrite) ;
SStream. CopyFrom( AStream, AStream. Size) ;
SStream. Free;
AStream. Free;
end
else
begin
WriteLn( Format( 'File %s already exists! Stopping.' , [ OutFileName] ) ) ;
Terminate;
Halt;
end ;
end ;
2012-08-26 20:05:29 +00:00
procedure TGirConsoleConverter. Terminate;
begin
Halt( 1 ) ;
end ;
2011-09-22 00:15:42 +00:00
procedure TGirConsoleConverter. Convert;
var
Doc: TXMLDocument;
girFile: TgirFile;
Writer: TgirPascalWriter;
StartTime, EndTime: TDateTime;
begin
StartTime : = Now;
ReadXMLFile( Doc, FFileToConvert) ;
girFile : = TgirFile. Create( nil ) ;
girFile. OnNeedGirFile: = @ NeedGirFile;
girFile. ParseXMLDocument( Doc) ;
Doc. Free;
2012-08-26 20:05:29 +00:00
Writer : = TgirPascalWriter. Create( girFile. NameSpaces, FOptions) ;
2011-09-24 00:21:23 +00:00
Writer. OnUnitWriteEvent: = @ WriteFile;
2011-09-22 00:15:42 +00:00
Writer. GenerateUnits;
Writer. Free;
EndTime : = Now;
EndTime : = EndTime- StartTime;
WriteLn( Format( 'Converted %d file(s) in %f seconds' , [ FWriteCount, DateTimeToTimeStamp( EndTime) . Time / 1 0 0 0 ] ) ) ;
end ;
2012-08-26 20:05:29 +00:00
function TGirConsoleConverter. CheckOptions: String ;
begin
Result : = '' ;
//FCmdOptions.SetOptions(ShortOpts, LongOpts);
with FCmdOptions do
begin
AddOption( [ 'h' , 'help' ] , False , 'Show this help message.' ) ;
AddOption( [ 'i' , 'input' ] , True , '.gir filename to convert.' ) ;
AddOption( [ 'o' , 'output-directory' ] , True , 'Directory to write the resulting .pas files to. If not specified then the current working directory is used.' ) ;
AddOption( [ 'D' , 'dynamic' ] , False , 'Use unit dynlibs and link at runtime' ) ;
{$IFDEF CreatePascalClasses}
AddOption( [ 's' , 'seperate-units' ] , False , 'Creates seperate units for each gir file: (xConsts, xTypes, xFunctions, [xClasses, xObjects].' ) ;
AddOption( [ 'C' , 'classes' ] , False , 'Create Pascal classes that envelope/wrap the GObjects. Also forces ' '-s' '' ) ;
AddOption( [ 'O' , 'objects' ] , False , 'OPTION NOT IMPLEMENTED YET. See Note below. ' +
'Creates a seperate unit for pascal Objects (not classes). Forces ' '-s' ' ' +
'Note: If -C or -O are not used then pascal Objects and consts ' +
'are in a single unit.' ) ;
{$ENDIF CreatePascalClasses}
2012-08-27 03:16:20 +00:00
AddOption( [ 'N' , 'no-wrappers' ] , False , 'Do not create wrappers for objects.' ) ;
2012-08-26 20:05:29 +00:00
AddOption( [ 'w' , 'overwrite-files' ] , False , 'If the output .pas file(s) already exists then overwrite them.' ) ;
AddOption( [ 'n' , 'no-default' ] , False , '/usr/share/gir-1.0 is not added as a search location for needed .gir files.' ) ;
AddOption( [ 'p' , 'paths' ] , True , 'List of paths seperated by ":" to search for needed .gir files.' ) ;
2012-08-26 21:39:16 +00:00
AddOption( [ 'd' , 'deprecated' ] , False , 'Include fields and methods marked as deprecated.' ) ;
2012-08-26 20:05:29 +00:00
AddOption( [ 't' , 'test' ] , False , 'Creates a test program per unit to verify struct sizes.' ) ;
2014-01-06 04:39:27 +00:00
AddOption( [ 'P' , 'unit-prefix' ] , True , 'Set a prefix to be added to each unitname.' ) ;
2012-08-26 20:05:29 +00:00
end ;
FCmdOptions. ReadOptions;
if FCmdOptions. OptionsMalformed then
REsult : = 'Error reading arguments' ;
end ;
2011-09-22 00:15:42 +00:00
procedure TGirConsoleConverter. DoRun;
begin
// quick check parameters
2012-08-26 20:05:29 +00:00
CheckOptions; //('hnp:o:i:wtDCsO',['help','no-default','paths','output-directory', 'input', 'overwrite-files', 'test', 'dynamic', 'classes', 'seperate-units', 'objects']);
// parse parameters
if FCmdOptions. OptionsMalformed then
begin
WriteLn( 'See -h for options.' ) ;
2011-09-22 00:15:42 +00:00
Terminate;
2012-08-26 20:05:29 +00:00
Halt;
2011-09-22 00:15:42 +00:00
end ;
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'help' ) then begin
2011-09-22 00:15:42 +00:00
WriteHelp;
Terminate;
Exit;
end ;
2012-08-26 20:05:29 +00:00
if not FCmdOptions. HasOption( 'input' ) then
begin
WriteLn( 'No input file specified! See -h for options.' ) ;
Terminate;
Halt;
end ;
if not FCmdOptions. HasOption( 'no-default' ) then
2011-09-22 00:15:42 +00:00
AddDefaultPaths;
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'output-directory' ) then
FOutPutDirectory: = IncludeTrailingPathDelimiter( FCmdOptions. OptionValue( 'output-directory' ) )
2011-09-22 00:15:42 +00:00
else
FOutPutDirectory: = IncludeTrailingPathDelimiter( GetCurrentDir) ;
2012-08-26 20:05:29 +00:00
FFileToConvert: = FCmdOptions. OptionValue( 'input' ) ;
AddPaths( ExtractFilePath( FFileToConvert) ) ;
2011-09-22 00:15:42 +00:00
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'paths' ) then
AddPaths( FCmdOptions. OptionValue( 'paths' ) ) ;
2011-09-22 00:15:42 +00:00
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'overwrite-files' ) then
2011-09-22 00:15:42 +00:00
FOverWriteFiles: = True ;
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'test' ) then
Include( FOptions, goWantTest) ;
if FCmdOptions. HasOption( 'dynamic' ) then
Include( FOptions, goLinkDynamic) ;
2012-08-26 21:39:16 +00:00
if FCmdOptions. HasOption( 'deprecated' ) then
Include( FOptions, goIncludeDeprecated) ;
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'classes' ) then
begin
Include( FOptions, goClasses) ;
Include( FOptions, goSeperateConsts) ;
end ;
2012-08-27 03:16:20 +00:00
if FCmdOptions. HasOption( 'no-wrappers' ) then
Include( FOptions, goNoWrappers) ;
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'objects' ) then
begin
Include( FOptions, goObjects) ;
Include( FOptions, goSeperateConsts) ;
end ;
2011-09-24 00:21:23 +00:00
2012-08-26 20:05:29 +00:00
if FCmdOptions. HasOption( 'seperate-units' ) then
Include( FOptions, goSeperateConsts) ;
2011-10-02 16:47:06 +00:00
2011-09-22 00:15:42 +00:00
VerifyOptions;
// does all the heavy lifting
Convert;
// stop program loop
Terminate;
end ;
2012-08-26 20:05:29 +00:00
constructor TGirConsoleConverter. Create;
2011-09-22 00:15:42 +00:00
begin
2012-08-26 20:05:29 +00:00
//inherited Create(TheOwner);
FCmdOptions : = TCommandLineOptions. Create;
2011-09-22 00:15:42 +00:00
FPaths : = TStringList. Create;
end ;
destructor TGirConsoleConverter. Destroy;
begin
FPaths. Free;
2012-08-26 20:05:29 +00:00
FCmdOptions. Free;
2011-09-22 00:15:42 +00:00
inherited Destroy;
end ;
procedure TGirConsoleConverter. WriteHelp;
2012-08-26 20:05:29 +00:00
var
{$IFDEF UNIX}
w: winsize;
{$ENDIF}
ConsoleWidth: Integer ;
2011-09-22 00:15:42 +00:00
begin
2012-08-26 20:05:29 +00:00
ConsoleWidth: = 8 0 ;
{$IFDEF UNIX}
fpioctl( 0 , TIOCGWINSZ, @ w) ;
ConsoleWidth: = w. ws_col;
{$ENDIF}
Writeln( 'Usage: ' , ExtractFileName( ParamStr( 0 ) ) , ' [options] -i filename' ) ;
2012-08-27 00:55:22 +00:00
with FCmdOptions. PrintHelp( ConsoleWidth) do
begin
WriteLn( Text ) ;
Free;
end ;
2012-08-26 20:05:29 +00:00
{
2011-09-22 00:15:42 +00:00
Writeln( '' ) ;
2012-08-26 20:05:29 +00:00
writeln( ' Usage: ' , ExtractFileName( ParamStr( 0 ) ) , ' [options] -i filename' ) ;
2011-09-22 00:15:42 +00:00
Writeln( '' ) ;
Writeln( '' ) ;
Writeln( ' -i --input= .gir filename to convert.' ) ;
Writeln( ' -o --output-directory= Directory to write the resulting .pas files to. If not' ) ;
2012-08-26 20:05:29 +00:00
Writeln( ' specified then the current working directory is used.' ) ;
2011-10-02 16:47:06 +00:00
WriteLn( ' -D --dynamic Use unit dynlibs and link at runtime' ) ;
2012-08-26 20:05:29 +00:00
WriteLn( ' -s --seperate-units Creates seperate units for each gir file:' ) ;
WriteLn( ' (xConsts, xTypes, xFunctions, [xClasses, xObjects].' ) ;
WriteLn( ' -C --classes Create Pascal classes that envelope/wrap the GObjects.' ) ;
WriteLn( ' Also forces ' '-s' '' ) ;
WriteLn( ' -O --objects OPTION NOT IMPLEMENTED YET. See Note below' ) ;
WriteLn( ' Creates a seperate unit for pascal Objects (not classes). Forces ' '-s' '' ) ;
WriteLn( ' Note: If -C or -O are not used then pascal Objects and consts' ) ;
WriteLn( ' are in a single unit.' ) ;
2011-09-22 00:15:42 +00:00
Writeln( ' -w --overwrite-files If the output .pas file(s) already exists then overwrite them.' ) ;
Writeln( ' -n --no-default /usr/share/gir-1.0 is not added as a search location for ' ) ;
2012-08-26 20:05:29 +00:00
Writeln( ' needed .gir files.' ) ;
2011-09-22 00:15:42 +00:00
Writeln( ' -p --paths= List of paths seperated by ":" to search for needed .gir files.' ) ;
2011-09-24 00:21:23 +00:00
Writeln( ' -t --test Creates a test program and a test c file per unit to verify struct sizes.' ) ;
2011-09-22 00:15:42 +00:00
Writeln( '' ) ;
2012-08-26 20:05:29 +00:00
}
end ;
procedure TGirConsoleConverter. Run;
begin
DoRun;
2011-09-22 00:15:42 +00:00
end ;
var
Application: TGirConsoleConverter;
{$R *.res}
begin
2012-08-26 20:05:29 +00:00
Application: = TGirConsoleConverter. Create;
2011-09-22 00:15:42 +00:00
Application. Run;
Application. Free;
end .