mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 14:19:19 +02:00
674 lines
21 KiB
ObjectPascal
674 lines
21 KiB
ObjectPascal
{ Command line utility to create closed source Lazarus packages.
|
|
Run with -h to see help
|
|
|
|
Copyright (C) 2013 Mattias Gaertner mattias@freepascal.org
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version with the following modification:
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent modules,and
|
|
to copy and distribute the resulting executable under terms of your choice,
|
|
provided that you also meet, for each linked independent module, the terms
|
|
and conditions of the license of that module. An independent module is a
|
|
module which is not derived from or based on this library. If you modify
|
|
this library, you may extend this exception to your version of the library,
|
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your 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 Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
}
|
|
program sourcecloser;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
uses
|
|
Classes, SysUtils, CustApp,
|
|
// LazUtils
|
|
AvgLvlTree, LazLoggerBase, LazFileUtils, Laz2_XMLCfg, LazUTF8,
|
|
// CodeTools
|
|
FileProcs, BasicCodeTools, CodeToolManager, CodeCache, SourceChanger,
|
|
CodeTree, DefineTemplates, LinkScanner;
|
|
|
|
type
|
|
|
|
{ TTarget }
|
|
|
|
TTarget = class
|
|
public
|
|
Names, Values: array of string;
|
|
procedure Add(Name, Value: string);
|
|
function AsString: string;
|
|
end;
|
|
|
|
{ TSourceCloser }
|
|
|
|
TSourceCloser = class(TCustomApplication)
|
|
private
|
|
FClosedSrcError: TStringList;
|
|
FCompilerOptions: string;
|
|
FDefines: TStringToStringTree;
|
|
FDisableCompile: boolean;
|
|
FIncludePath: string;
|
|
FLPKFilenames: TStrings;
|
|
FRemovePrivateSections: boolean;
|
|
FSyntaxMode: TCompilerMode;
|
|
FUndefines: TStringToStringTree;
|
|
FUnitFilenames: TStrings;
|
|
FVerbosity: integer;
|
|
fDefinesApplied: boolean;
|
|
procedure DeleteNode(Tool: TCodeTool; Node: TCodeTreeNode;
|
|
const StartPos, EndPos: Integer;
|
|
Changer: TSourceChangeCache; AddEmptyLine: boolean);
|
|
protected
|
|
procedure DoRun; override;
|
|
procedure ApplyDefines;
|
|
procedure ConvertLPK(LPKFilename: string);
|
|
procedure ConvertUnit(UnitFilename: string);
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure WriteHelp; virtual;
|
|
property Verbosity: integer read FVerbosity write FVerbosity;
|
|
property SyntaxMode: TCompilerMode read FSyntaxMode write FSyntaxMode;
|
|
property Defines: TStringToStringTree read FDefines;
|
|
property Undefines: TStringToStringTree read FUndefines;
|
|
property IncludePath: string read FIncludePath write FIncludePath;
|
|
property LPKFilenames: TStrings read FLPKFilenames;
|
|
property UnitFilenames: TStrings read FUnitFilenames;
|
|
property CompilerOptions: string read FCompilerOptions write FCompilerOptions;
|
|
property RemovePrivateSections: boolean read FRemovePrivateSections write FRemovePrivateSections;
|
|
property ClosedSrcError: TStringList read FClosedSrcError write FClosedSrcError;
|
|
property DisableCompile: boolean read FDisableCompile write FDisableCompile;
|
|
end;
|
|
|
|
function IndexOfFilename(List: TStrings; Filename: string): integer;
|
|
begin
|
|
Result:=List.Count-1;
|
|
while (Result>=0) and (CompareFilenames(List[Result],Filename)<>0) do
|
|
dec(Result);
|
|
end;
|
|
|
|
{ TTarget }
|
|
|
|
procedure TTarget.Add(Name, Value: string);
|
|
var
|
|
Cnt: Integer;
|
|
begin
|
|
Cnt:=length(Names);
|
|
SetLength(Names,Cnt+1);
|
|
Names[Cnt]:=Name;
|
|
SetLength(Values,Cnt+1);
|
|
Values[Cnt]:=Value;
|
|
end;
|
|
|
|
function TTarget.AsString: string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:='';
|
|
for i:=0 to length(Names)-1 do begin
|
|
if Result<>'' then Result+=',';
|
|
Result+=Names[i]+'='+Values[i];
|
|
end;
|
|
end;
|
|
|
|
{ TSourceCloser }
|
|
|
|
procedure TSourceCloser.DeleteNode(Tool: TCodeTool; Node: TCodeTreeNode;
|
|
const StartPos, EndPos: Integer; Changer: TSourceChangeCache;
|
|
AddEmptyLine: boolean);
|
|
|
|
procedure E(Msg: string);
|
|
begin
|
|
writeln('ERROR: '+Msg);
|
|
Halt(1);
|
|
end;
|
|
|
|
var
|
|
EndCodePos: TCodePosition;
|
|
StartCodePos: TCodePosition;
|
|
s: String;
|
|
begin
|
|
//debugln(['TSourceCloser.DeleteNode ',Node.DescAsString,' "',dbgstr(Tool.Src,StartPos,EndPos-StartPos),'"']);
|
|
if not Tool.CleanPosToCodePos(StartPos, StartCodePos) then
|
|
E('unable to delete '+Node.DescAsString+' of "'+Tool.MainFilename+'" (invalid startpos '
|
|
+Tool.CleanPosToStr(StartPos, true)+')');
|
|
if not Tool.CleanPosToCodePos(EndPos, EndCodePos) then
|
|
E('unable to delete '+Node.DescAsString+' of "'+Tool.MainFilename+'" (invalid endpos '+
|
|
Tool.CleanPosToStr(EndPos, true)+')');
|
|
if StartCodePos.Code<>EndCodePos.Code then
|
|
E('unable to delete '+Node.DescAsString+' of "'+Tool.MainFilename+'" from '+
|
|
Tool.CleanPosToStr(StartPos, true)+' to '+Tool.CleanPosToStr(EndPos, true)
|
|
);
|
|
s:='';
|
|
if AddEmptyLine then
|
|
s:=LineEnding;
|
|
if not Changer.ReplaceEx(gtNone, gtNone, 0, 0, StartCodePos.Code,
|
|
StartCodePos.P, EndCodePos.P, s) then
|
|
E('unable to delete '+Node.DescAsString+' of "'+Tool.MainFilename+'"');
|
|
end;
|
|
|
|
procedure TSourceCloser.DoRun;
|
|
const
|
|
ShortOpts = 'hvqc:pM:d:u:i:ke:';
|
|
LongOpts = 'help verbose quiet compileroptions: disablecompile mode: define: undefine: includepath: keepprivate error:';
|
|
var
|
|
ErrMsgIsDefault: Boolean;
|
|
|
|
procedure E(Msg: string; WithHelp: boolean = false);
|
|
begin
|
|
DebugLn(['ERROR: ',Msg]);
|
|
if WithHelp then
|
|
WriteHelp;
|
|
Terminate;
|
|
Halt(1);
|
|
end;
|
|
|
|
procedure ParseValueParam(ShortOpt: char; Value: string);
|
|
var
|
|
m: TCompilerMode;
|
|
begin
|
|
case ShortOpt of
|
|
'c':
|
|
begin
|
|
FCompilerOptions+=Value;
|
|
end;
|
|
'e':
|
|
begin
|
|
if ErrMsgIsDefault then begin
|
|
ErrMsgIsDefault:=false;
|
|
FClosedSrcError.Clear;
|
|
end;
|
|
Value:=UTF8Trim(Value,[]);
|
|
if Value='' then exit;
|
|
FClosedSrcError.Add(Value);
|
|
end;
|
|
'i':
|
|
begin
|
|
Value:=UTF8Trim(Value,[]);
|
|
if Value='' then exit;
|
|
if IncludePath<>'' then
|
|
Value:=';'+Value;
|
|
fIncludePath+=Value;
|
|
end;
|
|
'M':
|
|
begin
|
|
for m in TCompilerMode do
|
|
if SameText(CompilerModeNames[m],Value) then begin
|
|
SyntaxMode:=m;
|
|
exit;
|
|
end;
|
|
E('invalid syntaxmode "'+Value+'"');
|
|
end;
|
|
'd':
|
|
begin
|
|
if not IsValidIdent(Value) then
|
|
E('invalid define:'+Value);
|
|
Defines[Value]:='';
|
|
end;
|
|
'u':
|
|
begin
|
|
if not IsValidIdent(Value) then
|
|
E('invalid define:'+Value);
|
|
Defines[Value]:='';
|
|
end;
|
|
else
|
|
E('invalid option "'+ShortOpt+'"');
|
|
end;
|
|
end;
|
|
|
|
procedure AddFile(aFilename: string);
|
|
var
|
|
Ext: String;
|
|
begin
|
|
debugln(['AddFile ',aFilename]);
|
|
Ext:=lowercase(ExtractFileExt(aFilename));
|
|
if Ext='.lpk' then begin
|
|
if IndexOfFilename(LPKFilenames,aFilename)>=0 then
|
|
E('duplicate lpk:'+aFilename); // duplicate lpk, compilation order is unclear => error
|
|
LPKFilenames.Add(aFilename);
|
|
end
|
|
else if FilenameIsPascalUnit(aFilename) then begin
|
|
if IndexOfFilename(UnitFilenames,aFilename)>=0 then
|
|
exit; // duplicate unit is ok => ignore
|
|
UnitFilenames.Add(aFilename);
|
|
end else
|
|
E('only lpk and units are supported, invalid file:'+aFilename);
|
|
end;
|
|
|
|
procedure AddFiles(Pattern: string);
|
|
var
|
|
Info: TSearchRec;
|
|
begin
|
|
if FindFirstUTF8(Pattern,faAnyFile,Info)=0 then begin
|
|
try
|
|
repeat
|
|
if (Info.Name='.') or (Info.Name='..') then continue;
|
|
if (faDirectory and Info.Attr)>0 then continue;
|
|
AddFile(ExtractFilePath(Pattern)+Info.Name);
|
|
until FindNextUTF8(Info)<>0;
|
|
finally
|
|
FindCloseUTF8(Info);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ErrorMsg: String;
|
|
i: Integer;
|
|
Param: String;
|
|
S2SItem: PStringToStringItem;
|
|
Filename: String;
|
|
Option: string;
|
|
p: SizeInt;
|
|
begin
|
|
// quick check parameters
|
|
ErrorMsg:=CheckOptions(ShortOpts,LongOpts);
|
|
if ErrorMsg<>'' then
|
|
E(ErrorMsg);
|
|
|
|
// parse parameters
|
|
if HasOption('h','help') then begin
|
|
WriteHelp;
|
|
Terminate;
|
|
Exit;
|
|
end;
|
|
|
|
ErrMsgIsDefault:=true;
|
|
i:=1;
|
|
while i<=System.ParamCount do begin
|
|
Param:=ParamStrUTF8(i);
|
|
inc(i);
|
|
if Param='' then continue;
|
|
if (Param='-q') or (Param='--quiet') then
|
|
dec(fVerbosity)
|
|
else if (Param='-v') or (Param='--verbose') then
|
|
inc(fVerbosity)
|
|
else if (Param='-k') or (Param='--keepprivate') then
|
|
RemovePrivateSections:=false
|
|
else if (Param='-p') or (Param='--disablecompile') then
|
|
DisableCompile:=true
|
|
else if Param[1]<>'-' then begin
|
|
Filename:=TrimAndExpandFilename(Param);
|
|
if (Pos('*',ExtractFileName(Filename))>0) or (Pos('?',ExtractFileName(Filename))>0)
|
|
then begin
|
|
AddFiles(Filename);
|
|
end else begin
|
|
if not FileExistsUTF8(Filename) then
|
|
E('file not found: '+Param);
|
|
if DirPathExists(Filename) then
|
|
E('file is a directory: '+Param);
|
|
AddFile(Filename);
|
|
end;
|
|
end else if (length(Param)=2) and (Pos(Param[2]+':',ShortOpts)>0) then begin
|
|
// e.g. -t <target>
|
|
Option:=Param[2];
|
|
if i>System.ParamCount then
|
|
E('missing value for option: '+Param);
|
|
Param:=ParamStrUTF8(i);
|
|
inc(i);
|
|
ParseValueParam(Option[1],Param);
|
|
end else if (copy(Param,1,2)='--') then begin
|
|
p:=Pos('=',Param);
|
|
if p<1 then
|
|
E('invalid long option syntax: '+Param);
|
|
Option:=copy(Param,3,p-3);
|
|
delete(Param,1,p);
|
|
if Option='compileroptions' then Option:='c'
|
|
else if Option='mode' then Option:='M'
|
|
else if Option='define' then Option:='d'
|
|
else if Option='undefine' then Option:='u'
|
|
else if Option='includepath' then Option:='i'
|
|
else if Option='error' then Option:='e'
|
|
else
|
|
E('invalid long option');
|
|
ParseValueParam(Option[1],Param);
|
|
end else
|
|
E('invalid option: '+Param);
|
|
end;
|
|
|
|
if Verbosity>0 then begin
|
|
debugln(['Options:']);
|
|
debugln(['Verbosity=',Verbosity]);
|
|
debugln(['IncludePath=',IncludePath]);
|
|
for S2SItem in Defines do
|
|
debugln('Define:',S2SItem^.Name);
|
|
for S2SItem in Undefines do
|
|
debugln('Undefine:',S2SItem^.Name);
|
|
for i:=0 to LPKFilenames.Count-1 do
|
|
debugln(['LPK[',i+1,']:',LPKFilenames[i]]);
|
|
for i:=0 to UnitFilenames.Count-1 do
|
|
debugln(['Unit[',i+1,']:',UnitFilenames[i]]);
|
|
end;
|
|
if (LPKFilenames.Count=0) and (UnitFilenames.Count=0) then
|
|
E('you must pass at least one lpk or pas file',true);
|
|
|
|
for i:=0 to LPKFilenames.Count-1 do
|
|
ConvertLPK(LPKFilenames[i]);
|
|
for i:=0 to UnitFilenames.Count-1 do
|
|
ConvertUnit(UnitFilenames[i]);
|
|
|
|
// stop program loop
|
|
Terminate;
|
|
end;
|
|
|
|
procedure TSourceCloser.ApplyDefines;
|
|
|
|
procedure AddDefine(const MacroName, Value: string);
|
|
var
|
|
DefTemplate: TDefineTemplate;
|
|
begin
|
|
DefTemplate:=TDefineTemplate.Create('Define '+MacroName,
|
|
'Define '+MacroName,
|
|
MacroName,Value,da_DefineRecurse);
|
|
CodeToolBoss.DefineTree.Add(DefTemplate);
|
|
end;
|
|
|
|
procedure AddUndefine(const MacroName: string);
|
|
var
|
|
DefTemplate: TDefineTemplate;
|
|
begin
|
|
DefTemplate:=TDefineTemplate.Create('Undefine '+MacroName,
|
|
'Undefine '+MacroName,
|
|
MacroName,'',da_UndefineRecurse);
|
|
CodeToolBoss.DefineTree.Add(DefTemplate);
|
|
end;
|
|
|
|
procedure AddDefineUndefine(const AName: string; Define: boolean);
|
|
begin
|
|
if Define then
|
|
AddDefine(AName,'')
|
|
else
|
|
AddUndefine(AName);
|
|
end;
|
|
|
|
var
|
|
IncPathTemplate: TDefineTemplate;
|
|
S2SItem: PStringToStringItem;
|
|
m: Integer;
|
|
begin
|
|
if fDefinesApplied then exit;
|
|
fDefinesApplied:=true;
|
|
|
|
CodeToolBoss.SimpleInit('codetools.cache');
|
|
|
|
if IncludePath<>'' then begin
|
|
IncPathTemplate:=TDefineTemplate.Create('IncPath',
|
|
'extending include search path',
|
|
IncludePathMacroName, // variable name: #IncPath
|
|
'$('+IncludePathMacroName+');'+IncludePath
|
|
,da_DefineRecurse
|
|
);
|
|
CodeToolBoss.DefineTree.Add(IncPathTemplate);
|
|
end;
|
|
|
|
for m:=low(FPCSyntaxModes) to high(FPCSyntaxModes) do
|
|
AddDefineUndefine('FPC_'+FPCSyntaxModes[m],
|
|
SameText(CompilerModeNames[SyntaxMode],FPCSyntaxModes[m]));
|
|
|
|
for S2SItem in Defines do
|
|
AddDefine(S2SItem^.Name,S2SItem^.Value);
|
|
for S2SItem in Undefines do
|
|
AddUndefine(S2SItem^.Name);
|
|
end;
|
|
|
|
procedure TSourceCloser.ConvertLPK(LPKFilename: string);
|
|
// set lpk to compile only manually
|
|
// add -Ur to compiler options
|
|
const
|
|
OtherPath='Package/CompilerOptions/Other/';
|
|
CustomOptionsPath=OtherPath+'CustomOptions/Value';
|
|
var
|
|
xml: TXMLConfig;
|
|
CustomOptions: String;
|
|
NewOptions: String;
|
|
begin
|
|
debugln(['Converting lpk: ',LPKFilename]);
|
|
xml:=TXMLConfig.Create(LPKFilename);
|
|
try
|
|
// set lpk to compile only manually
|
|
xml.SetValue('Package/AutoUpdate/Value','Manually');
|
|
|
|
// add -Ur to compiler options
|
|
CustomOptions:=xml.GetValue(CustomOptionsPath,'');
|
|
NewOptions:=CustomOptions;
|
|
if Pos('-Ur',NewOptions)<1 then begin
|
|
if NewOptions<>'' then NewOptions+=' ';
|
|
NewOptions+='-Ur';
|
|
end;
|
|
if FCompilerOptions<>'' then begin
|
|
if NewOptions<>'' then NewOptions+=' ';
|
|
NewOptions+=FCompilerOptions;
|
|
end;
|
|
xml.SetValue(CustomOptionsPath,NewOptions);
|
|
|
|
// disable compile commands
|
|
if DisableCompile then begin
|
|
xml.SetValue(OtherPath+'CompilerPath/Value','');
|
|
xml.SetDeleteValue(OtherPath+'ExecuteBefore/Command/Value','','');
|
|
xml.SetDeleteValue(OtherPath+'ExecuteAfter/Command/Value','','');
|
|
xml.SetDeleteValue(OtherPath+'CreateMakefileOnBuild/Value','','');
|
|
end;
|
|
|
|
// write
|
|
xml.Flush;
|
|
finally
|
|
xml.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TSourceCloser.ConvertUnit(UnitFilename: string);
|
|
|
|
procedure E(Msg: string);
|
|
begin
|
|
writeln('ERROR: '+Msg);
|
|
Halt(1);
|
|
end;
|
|
|
|
var
|
|
Code: TCodeBuffer;
|
|
Changer: TSourceChangeCache;
|
|
Tool: TCodeTool;
|
|
Node: TCodeTreeNode;
|
|
StartPos: Integer;
|
|
EndPos: Integer;
|
|
CodeList: TFPList;
|
|
i: Integer;
|
|
FromPos: Integer;
|
|
ToPos: Integer;
|
|
AddEmptyLine: Boolean;
|
|
s: String;
|
|
begin
|
|
debugln(['Converting unit: ',UnitFilename]);
|
|
ApplyDefines;
|
|
|
|
// load file
|
|
Code:=CodeToolBoss.LoadFile(UnitFilename,true,false);
|
|
if Code=nil then
|
|
E('unable to read "'+UnitFilename+'"');
|
|
// parse whole unit
|
|
if (not CodeToolBoss.Explore(Code,Tool,false)) or (CodeToolBoss.ErrorMessage<>'') then
|
|
E('parse error');
|
|
if Tool.GetSourceType<>ctnUnit then
|
|
E('not a unit, skipping "'+Code.Filename+'"');
|
|
|
|
// init SourceChangeCache
|
|
Changer:=CodeToolBoss.SourceChangeCache;
|
|
Changer.MainScanner:=Tool.Scanner;
|
|
|
|
// add errors, so that user cannot accidentally compile the unit
|
|
s:='';
|
|
for i:=0 to FClosedSrcError.Count-1 do begin
|
|
s:=s+'{$Error '+FClosedSrcError[i]+'}'+LineEnding;
|
|
end;
|
|
if s<>'' then
|
|
Changer.ReplaceEx(gtNone,gtNone,0,0,Code,1,1,s);
|
|
|
|
if RemovePrivateSections then begin
|
|
// delete private sections in the interface
|
|
Node:=Tool.FindInterfaceNode;
|
|
while Node<>nil do begin
|
|
if Node.Desc=ctnClassPrivate then begin
|
|
FromPos:=Tool.FindLineEndOrCodeInFrontOfPosition(Node.StartPos);
|
|
ToPos:=Tool.FindLineEndOrCodeInFrontOfPosition(Node.EndPos);
|
|
DeleteNode(Tool,Node,FromPos,ToPos,Changer,false);
|
|
Node:=Node.NextSkipChilds;
|
|
end else
|
|
Node:=Node.Next;
|
|
if Node=nil then break;
|
|
if Node.Desc in [ctnImplementation,ctnInitialization,ctnFinalization] then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
// delete implementation, initialization and finalization section
|
|
Node:=Tool.Tree.Root;
|
|
while (Node<>nil) do begin
|
|
if Node.Desc=ctnImplementation then begin
|
|
// delete implementation section excluding the 'implementation' keyword
|
|
StartPos:=Node.StartPos+length('implementation');
|
|
if Node.NextBrother=nil then begin
|
|
writeln('Warning: [20211118223740] TSourceCloser.ConvertUnit "'+Code.Filename+'"');
|
|
EndPos:=Tool.SrcLen+1;
|
|
end
|
|
else if Node.NextBrother.Desc=ctnEndPoint then
|
|
EndPos:=Node.EndPos
|
|
else
|
|
EndPos:=Node.NextBrother.StartPos;
|
|
DeleteNode(Tool, Node, StartPos, EndPos, Changer, true);
|
|
end else if Node.Desc in [ctnInitialization,ctnFinalization] then begin
|
|
// delete the content of the finalization and initialization section
|
|
Tool.MoveCursorToNodeStart(Node);
|
|
Tool.ReadNextAtom; // read 'initialization'
|
|
StartPos:=Tool.CurPos.EndPos;
|
|
EndPos:=Node.NextBrother.StartPos;
|
|
AddEmptyLine:=true;
|
|
if Trim(copy(Tool.Src,StartPos,EndPos-StartPos))='' then begin
|
|
// empty initialization => delete keyword as well
|
|
StartPos:=Node.StartPos;
|
|
AddEmptyLine:=false;
|
|
end else begin
|
|
// initialization section is not empty
|
|
// => keep the keyword, because it is needed by tools like 'Unused units'
|
|
end;
|
|
DeleteNode(Tool, Node, StartPos, EndPos, Changer, AddEmptyLine);
|
|
end;
|
|
Node:=Node.NextBrother;
|
|
end;
|
|
|
|
// apply changes and write changes to disk
|
|
CodeList:=TFPList.Create;
|
|
try
|
|
for i:=0 to Changer.BuffersToModifyCount-1 do
|
|
CodeList.Add(Changer.BuffersToModify[i]);
|
|
if not Changer.Apply then
|
|
E('unable to modify "'+UnitFilename+'"');
|
|
for i:=0 to CodeList.Count-1 do begin
|
|
Code:=TCodeBuffer(CodeList[i]);
|
|
if not Code.Save then
|
|
E('unable to write "'+Code.Filename+'"');
|
|
end;
|
|
finally
|
|
CodeList.Free;
|
|
end;
|
|
end;
|
|
|
|
constructor TSourceCloser.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
StopOnException:=True;
|
|
FSyntaxMode:=cmOBJFPC;
|
|
fDefines:=TStringToStringTree.Create(false);
|
|
FUndefines:=TStringToStringTree.Create(false);
|
|
FLPKFilenames:=TStringList.Create;
|
|
FUnitFilenames:=TStringList.Create;
|
|
FRemovePrivateSections:=true;
|
|
FClosedSrcError:=TStringList.Create;
|
|
FClosedSrcError.Add('This is a closed source unit. You cannot compile it, it was already compiled.');
|
|
FClosedSrcError.Add('Probably the IDE has cleaned up and you have to unpack the zip again.');
|
|
end;
|
|
|
|
destructor TSourceCloser.Destroy;
|
|
begin
|
|
FreeAndNil(FClosedSrcError);
|
|
FreeAndNil(FLPKFilenames);
|
|
FreeAndNil(FUnitFilenames);
|
|
FreeAndNil(FDefines);
|
|
FreeAndNil(FUndefines);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSourceCloser.WriteHelp;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
writeln('Usage:');
|
|
writeln(' ',ExeName,' -h');
|
|
writeln(' ',ExeName,' [options] [unit1.pas unit2.pp ...] [pkg1.lpk pk2.lpk ..]');
|
|
writeln;
|
|
writeln('Description:');
|
|
writeln(' This tool helps creating closed source Lazarus packages.');
|
|
writeln;
|
|
writeln(' If the file names contain * or ? it will be used as mask.');
|
|
writeln(' If you pass a lpk file, this tool will set "compile manually"');
|
|
writeln(' and appends "-Ur" to the compiler options.');
|
|
writeln(' You can pass multiple lpk files and they will be edited in this order.');
|
|
writeln(' If you pass a .pas or .pp file it will be treated as a pascal unit and');
|
|
writeln(' will remove the implementation, initialization, finalization sections.');
|
|
writeln;
|
|
writeln('Options:');
|
|
writeln(' -h, --help : This help messages.');
|
|
writeln(' -v, --verbose : be more verbose.');
|
|
writeln(' -q, --quiet : be more quiet.');
|
|
writeln('Package/lpk options:');
|
|
writeln(' --compileroptions=<compiler options>');
|
|
writeln(' Add custom compiler options to lpk.');
|
|
writeln(' -p, --disablecompile');
|
|
writeln(' Remove all compile commands from lpk.');
|
|
writeln('Unit options:');
|
|
writeln(' -M <SyntaxMode>, --mode=<SyntaxMode> : delphi|delphiunicode|objfpc|fpc|macpas, default: '+CompilerModeNames[SyntaxMode]);
|
|
writeln(' -d <MacroName>, --define=<MacroName> :');
|
|
writeln(' Define Free Pascal macro. Can be passed multiple times.');
|
|
writeln(' -u <MacroName>, --undefine=<MacroName> :');
|
|
writeln(' Undefine Free Pascal macro. Can be passed multiple times.');
|
|
writeln(' -i <path>, --includepath=<path> :');
|
|
writeln(' Append <path> to include search path. Can be passed multiple times.');
|
|
writeln(' -k, --keepprivate');
|
|
writeln(' Keep private sections in interface.');
|
|
writeln(' -e <errormessage>, --error=<error message>');
|
|
writeln(' Change the message of the error directive added to the units.');
|
|
writeln(' You can add this multiple times to add multiple directives.');
|
|
writeln(' The default error message is:');
|
|
for i:=0 to FClosedSrcError.Count-1 do
|
|
writeln(' ',FClosedSrcError[i]);
|
|
writeln;
|
|
writeln('Environment variables:');
|
|
writeln(' PP path to compiler,');
|
|
writeln(' e.g. C:\lazarus\fpc\2.6.2\bin\i386-win32\fpc.exe');
|
|
writeln(' The compiler is queried for the current defines for the');
|
|
writeln(' target platform.');
|
|
writeln(' FPCTARGET target os, e.g. win32');
|
|
writeln(' FPCTARGETCPU target cpu, e.g. i386');
|
|
end;
|
|
|
|
var
|
|
Application: TSourceCloser;
|
|
begin
|
|
Application:=TSourceCloser.Create(nil);
|
|
Application.Title:='SourceCloser';
|
|
Application.Run;
|
|
Application.Free;
|
|
end.
|
|
|