You've already forked lazarus-ccr
iphonelazext: adding pbx file-xcode project sources
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3713 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
752
components/iphonelazext/pbx/pbxcontainer.pas
Normal file
752
components/iphonelazext/pbx/pbxcontainer.pas
Normal file
@ -0,0 +1,752 @@
|
|||||||
|
unit pbxcontainer;
|
||||||
|
(*-------------------------------------------------------------------------------
|
||||||
|
* by Dmitry Boyarintsev - Oct 2014 *
|
||||||
|
* *
|
||||||
|
* license: free for use, but please leave a note to the origin of the library *
|
||||||
|
* *
|
||||||
|
* PBXcontainer unit is a library to read/write the pbx formatter file as a *
|
||||||
|
* whole The file structure is made to keep the reference in a complex objects *
|
||||||
|
* struture. Hierarchial trees, lists, cycles and so on. *
|
||||||
|
* *
|
||||||
|
* It's achieved by giving a full list of objects. With each object having an *
|
||||||
|
* "id" assigned. Later in the description, the reference is specifeid by the *
|
||||||
|
* object's id. *
|
||||||
|
* *
|
||||||
|
* Currently PBXContainer would read and try to build a structure based of *
|
||||||
|
* Object Pascal RTTI. (Not sure if Delphi compatible) *
|
||||||
|
* Following rules are used *
|
||||||
|
* read/write objects are going to join the list of objects (for the futher *
|
||||||
|
* reference) *
|
||||||
|
* read-only objects should be allocated in the constructor of the parent *
|
||||||
|
* they're "inlined" in the file *
|
||||||
|
* for array of objects TPBXObjectsList must be used *
|
||||||
|
* for array of strings TPBXStringArray must be used *
|
||||||
|
* for key-value set use TPBXKeyValue class *
|
||||||
|
* string and integer properties are supported... anything else? *
|
||||||
|
* *
|
||||||
|
* todo: add more documentions *
|
||||||
|
* *
|
||||||
|
* todo: memoty allocation and release. ObjC is using ref-counted structure. *
|
||||||
|
* do the same? similar? *
|
||||||
|
-------------------------------------------------------------------------------*)
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
{$ifdef fpc}{$mode delphi}{$endif}
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, typinfo, pbxfile, contnrs;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TPBXObject }
|
||||||
|
|
||||||
|
{ PBXObject }
|
||||||
|
|
||||||
|
PBXObject = class(TObject)
|
||||||
|
private
|
||||||
|
_id : string;
|
||||||
|
_fheaderComment : string;
|
||||||
|
protected
|
||||||
|
// collects the name of string properties that should be written out
|
||||||
|
// even if their values is an empty string.
|
||||||
|
// if property value is an empty string, it would not be written to the file
|
||||||
|
class procedure _WriteEmpty(propnames: TStrings); virtual;
|
||||||
|
public
|
||||||
|
property __id: string read _id;
|
||||||
|
property _headerComment: string read _fheaderComment write _fheaderComment;
|
||||||
|
constructor Create; virtual;
|
||||||
|
|
||||||
|
end;
|
||||||
|
PBXObjectClass = class of PBXObject;
|
||||||
|
|
||||||
|
TPBXObjectsList = class(TObjectList);
|
||||||
|
TPBXStringArray = class(TStringList);
|
||||||
|
|
||||||
|
TPBXKeyValue = class;
|
||||||
|
|
||||||
|
TPBXValueType = (vtString, vtArrayOfStr, vtKeyVal);
|
||||||
|
|
||||||
|
{ TPBXValue }
|
||||||
|
|
||||||
|
TPBXValue = class(TObject)
|
||||||
|
public
|
||||||
|
valType : TPBXValueType;
|
||||||
|
str : string;
|
||||||
|
arr : TPBXStringArray;
|
||||||
|
keyval : TPBXKeyValue;
|
||||||
|
destructor Destroy; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXKeyValue }
|
||||||
|
|
||||||
|
TPBXKeyValue = class(TFPHashObjectList)
|
||||||
|
protected
|
||||||
|
function AddVal(const name: string; atype: TPBXValueType): TPBXValue;
|
||||||
|
public
|
||||||
|
function AddStr(const name: string; const avalue: string = ''): TPBXValue;
|
||||||
|
function AddStrArray(const name: string): TPBXValue;
|
||||||
|
function AddKeyVal(const name: string): TPBXValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TPBXFileInfo = record
|
||||||
|
archiveVersion : string;
|
||||||
|
objectVersion : string;
|
||||||
|
rootObject : PBXObject;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXReref }
|
||||||
|
|
||||||
|
TPBXReref = class(TObject)
|
||||||
|
instance : TObject;
|
||||||
|
propname : string;
|
||||||
|
_id : string;
|
||||||
|
constructor Create(ainstance: TObject; const apropname, aref: string);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXContainer }
|
||||||
|
|
||||||
|
TObjHashList = TFPHashObjectList;
|
||||||
|
|
||||||
|
TPBXContainer = class(TObject)
|
||||||
|
protected
|
||||||
|
procedure ReadObjects(p: TPBXParser; objs: TObjHashList);
|
||||||
|
function AllocObject(const nm: string): PBXObject;
|
||||||
|
public
|
||||||
|
function ReadFile(s: TStream; var AFileInfo: TPBXFileInfo): Boolean;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestContainer(const buf: string);
|
||||||
|
|
||||||
|
procedure PBXRegisterClass(aclass: PBXObjectClass);
|
||||||
|
function PBXFindClass(const aclassname: string): PBXObjectClass;
|
||||||
|
|
||||||
|
function PBXReadObjectsListRef(p: TPBXParser; obj: PBXObject; propName: string; refs: TList): Boolean;
|
||||||
|
function PBXReadStringArray(p: TPBXParser; arr: TPBXStringArray): Boolean;
|
||||||
|
function PBXReadKeyValue(p: TPBXParser; kv: TPBXKeyValue): Boolean;
|
||||||
|
function PBXReadClass(p: TPBXParser; obj: PBXObject; refs: TList): Boolean;
|
||||||
|
procedure PBXReref(objs: TObjHashList; refs: TList);
|
||||||
|
|
||||||
|
function PBXWriteContainer(const FileInfo: TPBXFileInfo; AssignRef: Boolean = true): string;
|
||||||
|
procedure PBXWriteObjArray( w: TPBXWriter; list: TPBXObjectsList );
|
||||||
|
procedure PBXWriteStrArray( w: TPBXWriter; list: TPBXStringArray );
|
||||||
|
procedure PBXWriteKeyValue( w: TPBXWriter; kv: TPBXKeyValue );
|
||||||
|
procedure PBXWriteObj(pbx: PBXObject; w: TPBXWriter; WriteEmpty: TStrings);
|
||||||
|
|
||||||
|
procedure PBXAssignRef(list: TList);
|
||||||
|
|
||||||
|
procedure PBXGatherObjects(obj: TObject; srz: TList);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
var
|
||||||
|
pbxClassList : TStringList;
|
||||||
|
|
||||||
|
function PBXReadKeyValue(p: TPBXParser; kv: TPBXKeyValue): Boolean;
|
||||||
|
var
|
||||||
|
et : TPBXEntity;
|
||||||
|
v : TPBXValue;
|
||||||
|
begin
|
||||||
|
et:=p.FetchNextEntity;
|
||||||
|
while et<>etCloseObject do begin
|
||||||
|
case et of
|
||||||
|
etValue: kv.AddStr(p.Name, p.Value);
|
||||||
|
etOpenArray: begin
|
||||||
|
v:=kv.AddStrArray(p.Name);
|
||||||
|
PBXReadStringArray(p, v.arr);
|
||||||
|
end;
|
||||||
|
etOpenObject: begin
|
||||||
|
v:=kv.AddKeyVal(p.Name);
|
||||||
|
PBXReadKeyValue(p, v.keyval);
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
Result:=false;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
et:=p.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
Result:=True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestContainer(const buf: string);
|
||||||
|
var
|
||||||
|
c : TPBXContainer;
|
||||||
|
st : TStringStream;
|
||||||
|
info : TPBXFileInfo;
|
||||||
|
begin
|
||||||
|
c:= TPBXContainer.Create;
|
||||||
|
st := TStringStream.Create(buf);
|
||||||
|
try
|
||||||
|
c.ReadFile(st, info);
|
||||||
|
writeln('arch ver: ',info.archiveVersion);
|
||||||
|
writeln(' obj ver: ',info.objectVersion);
|
||||||
|
writeln('root obj: ', PtrUInt( info.rootObject ));
|
||||||
|
finally
|
||||||
|
st.Free;
|
||||||
|
c.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PBXRegisterClass(aclass: PBXObjectClass);
|
||||||
|
begin
|
||||||
|
pbxClassList.AddObject(aclass.ClassName, TObject(aclass));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXFindClass(const aclassname: string): PBXObjectClass;
|
||||||
|
var
|
||||||
|
i : integer;
|
||||||
|
begin
|
||||||
|
i:=pbxClassList.IndexOf(aclassname);
|
||||||
|
if i<0 then Result:=nil
|
||||||
|
else Result:=PBXObjectClass(pbxClassList.Objects[i]);
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXValue }
|
||||||
|
|
||||||
|
destructor TPBXValue.Destroy;
|
||||||
|
begin
|
||||||
|
arr.Free;
|
||||||
|
keyval.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXKeyValue }
|
||||||
|
|
||||||
|
function TPBXKeyValue.AddVal(const name: string; atype: TPBXValueType): TPBXValue;
|
||||||
|
begin
|
||||||
|
Result:=TPBXValue.Create;
|
||||||
|
Result.valType:=atype;
|
||||||
|
case atype of
|
||||||
|
vtKeyVal: Result.keyval:=TPBXKeyValue.Create(true);
|
||||||
|
vtArrayOfStr: Result.arr:=TPBXStringArray.Create;
|
||||||
|
end;
|
||||||
|
Add(name, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXKeyValue.AddStr(const name: string; const avalue: string): TPBXValue;
|
||||||
|
begin
|
||||||
|
Result:=AddVal(name, vtString);
|
||||||
|
Result.str:=avalue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXKeyValue.AddStrArray(const name: string): TPBXValue;
|
||||||
|
begin
|
||||||
|
Result:=AddVal(name, vtArrayOfStr);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXKeyValue.AddKeyVal(const name: string): TPBXValue;
|
||||||
|
begin
|
||||||
|
Result:=AddVal(name, vtKeyVal);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXReref }
|
||||||
|
|
||||||
|
constructor TPBXReref.Create(ainstance: TObject; const apropname, aref: string);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
instance := ainstance;
|
||||||
|
propname := apropname;
|
||||||
|
_id := aref;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXObject }
|
||||||
|
|
||||||
|
class procedure PBXObject._WriteEmpty(propnames: TStrings);
|
||||||
|
begin
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor PBXObject.Create;
|
||||||
|
begin
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXContainer }
|
||||||
|
|
||||||
|
procedure PBXReref(objs: TObjHashList; refs: TList);
|
||||||
|
var
|
||||||
|
i : integer;
|
||||||
|
refobj : TObject;
|
||||||
|
r : TPBXReref;
|
||||||
|
prp : PPropInfo;
|
||||||
|
pcls : TObject;
|
||||||
|
begin
|
||||||
|
for i:=0 to refs.Count-1 do begin
|
||||||
|
r := TPBXReref(refs[i]);
|
||||||
|
refobj:=objs.Find(r._id);
|
||||||
|
if Assigned(refobj) then begin
|
||||||
|
prp:=GetPropInfo(r.instance, r.propname);
|
||||||
|
if prp^.PropType^.Kind=tkClass then begin
|
||||||
|
pcls:=GetObjectProp(r.instance, r.propname);
|
||||||
|
if pcls is TPBXObjectsList then begin
|
||||||
|
TPBXObjectsList(pcls).Add(refobj);
|
||||||
|
end else begin
|
||||||
|
//writeln('setting prop: ', r.propname,' ');
|
||||||
|
SetObjectProp(r.instance, r.propname, refobj);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//else writeln('no object found! ', r._id);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXContainer.ReadObjects(p: TPBXParser; objs: TObjHashList);
|
||||||
|
var
|
||||||
|
tk : TPBXEntity;
|
||||||
|
id : string;
|
||||||
|
cls : string;
|
||||||
|
obj : PBXObject;
|
||||||
|
i : Integer;
|
||||||
|
refs : TList;
|
||||||
|
cmt : string;
|
||||||
|
begin
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
refs:=TList.Create;
|
||||||
|
try
|
||||||
|
while tk<>etCloseObject do begin
|
||||||
|
if tk=etOpenObject then begin
|
||||||
|
id:=p.Name;
|
||||||
|
cmt:=p.LastComment;
|
||||||
|
cls:='';
|
||||||
|
p.FetchNextEntity;
|
||||||
|
if (p.CurEntity = etValue) and (p.Name = 'isa') then begin
|
||||||
|
cls:=p.Value;
|
||||||
|
obj:=AllocObject(cls);
|
||||||
|
if Assigned(obj) then begin
|
||||||
|
obj._headerComment:=cmt;
|
||||||
|
obj._id:=id;
|
||||||
|
PBXReadClass(p, obj, refs);
|
||||||
|
objs.Add(id, obj);
|
||||||
|
end else
|
||||||
|
PBXParserSkipLevel(p);
|
||||||
|
|
||||||
|
end else
|
||||||
|
PBXParserSkipLevel(p);
|
||||||
|
end;
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
|
||||||
|
PBXReref(objs, refs);
|
||||||
|
|
||||||
|
finally
|
||||||
|
for i:=0 to refs.Count-1 do TObject(refs[i]).Free;
|
||||||
|
refs.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXContainer.AllocObject(const nm: string): PBXObject;
|
||||||
|
var
|
||||||
|
cls : PBXObjectClass;
|
||||||
|
begin
|
||||||
|
cls:=PBXFindClass(nm);
|
||||||
|
if not Assigned(cls) then Result:=nil
|
||||||
|
else Result:=cls.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXContainer.ReadFile(s: TStream; var AFileInfo: TPBXFileInfo): Boolean;
|
||||||
|
var
|
||||||
|
p : TPBXParser;
|
||||||
|
buf : string;
|
||||||
|
tk : TPBXEntity;
|
||||||
|
root : string;
|
||||||
|
objs : TObjHashList;
|
||||||
|
rt : TObject;
|
||||||
|
begin
|
||||||
|
Result:=false;
|
||||||
|
AFileInfo.archiveVersion:='';
|
||||||
|
AFileInfo.objectVersion:='';
|
||||||
|
AFileInfo.rootObject:=nil;
|
||||||
|
|
||||||
|
if not Assigned(s) then Exit;
|
||||||
|
SetLength(buf, s.Size);
|
||||||
|
s.Read(buf[1], length(buf));
|
||||||
|
|
||||||
|
objs:=TObjHashList.Create(False);
|
||||||
|
p:=TPBXParser.Create;
|
||||||
|
try
|
||||||
|
p.scanner.SetBuf(buf);
|
||||||
|
if p.FetchNextEntity <> etOpenObject then Exit;
|
||||||
|
|
||||||
|
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
while tk <> etEOF do begin
|
||||||
|
if tk = etValue then begin
|
||||||
|
if p.Name='archiveVersion' then AFileInfo.archiveVersion:=p.Value
|
||||||
|
else if p.Name='objectVersion' then AFileInfo.objectVersion:=p.Value
|
||||||
|
else if p.Name='rootObject' then root:=p.Value;
|
||||||
|
end else if (tk=etOpenObject) and (p.Name = 'objects') then begin
|
||||||
|
ReadObjects(p, objs);
|
||||||
|
end;
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
|
||||||
|
rt:=objs.Find(root);
|
||||||
|
|
||||||
|
if Assigned(rt) and (rt is PBXObject) then
|
||||||
|
AFileInfo.rootObject:=PBXObject(rt);
|
||||||
|
Result:=true;
|
||||||
|
finally
|
||||||
|
objs.Free;
|
||||||
|
p.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXReadObjectsListRef(p: TPBXParser; obj: PBXObject; propName: string; refs: TList): Boolean;
|
||||||
|
begin
|
||||||
|
Result:=true;
|
||||||
|
p.FetchNextEntity;
|
||||||
|
while not (p.CurEntity in [etCloseArray, etEOF, etError]) do begin
|
||||||
|
if p.CurEntity <> etValue then begin
|
||||||
|
Result:=false;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
if p.Value<>'' then
|
||||||
|
refs.Add ( TPBXReref.Create( obj, propName, p.Value ));
|
||||||
|
p.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXReadStringArray(p: TPBXParser; arr: TPBXStringArray): Boolean;
|
||||||
|
begin
|
||||||
|
Result:=true;
|
||||||
|
p.FetchNextEntity;
|
||||||
|
while not (p.CurEntity in [etCloseArray, etEOF, etError]) do begin
|
||||||
|
if p.CurEntity <> etValue then begin
|
||||||
|
Result:=false;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
arr.Add(p.Value);
|
||||||
|
p.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXReadClass(p: TPBXParser; obj: PBXObject; refs: TList): Boolean;
|
||||||
|
var
|
||||||
|
tk : TPBXEntity;
|
||||||
|
lvl : Integer;
|
||||||
|
prp : PPropInfo;
|
||||||
|
pobj : TObject;
|
||||||
|
pk : TTypeKind;
|
||||||
|
begin
|
||||||
|
lvl:=p.Level;
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
while p.Level>=lvl {tk<>tkCurlyBraceClose} do begin
|
||||||
|
prp:=GetPropInfo(obj, p.Name);
|
||||||
|
if Assigned(prp) then begin
|
||||||
|
pk:=prp^.PropType^.Kind;
|
||||||
|
if pk=tkClass then
|
||||||
|
pobj:=GetObjectProp(obj, prp)
|
||||||
|
else
|
||||||
|
pobj:=nil;
|
||||||
|
|
||||||
|
if tk=etValue then begin
|
||||||
|
|
||||||
|
case pk of
|
||||||
|
tkClass: begin
|
||||||
|
//writeln('ref for: ',p.Name,' to ', p.Value);
|
||||||
|
refs.Add( TPBXReref.Create(obj, p.Name, p.Value))
|
||||||
|
end;
|
||||||
|
tkInteger, tkInt64, tkQWord: begin
|
||||||
|
SetInt64Prop(obj, p.Name, StrToIntDef(p.Value, GetInt64Prop(obj, p.Name)) );
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
SetStrProp(obj, p.Name, p.Value);
|
||||||
|
end;
|
||||||
|
end else begin
|
||||||
|
{write( p.CurEntity,' ',p.Name,' ',PtrUInt(pobj));
|
||||||
|
if Assigned(pobj) then write(' ', pobj.ClassName);
|
||||||
|
writeln;}
|
||||||
|
if (pobj is TPBXObjectsList) and (tk = etOpenArray) then begin
|
||||||
|
Result:=PBXReadObjectsListRef(p, obj, p.Name, refs);
|
||||||
|
if not Result then Exit;
|
||||||
|
end else if (pobj is TPBXStringArray) and (tk = etOpenArray) then begin
|
||||||
|
Result:=PBXReadStringArray(p, TPBXStringArray(pobj) );
|
||||||
|
if not Result then Exit;
|
||||||
|
end else if (pobj is TPBXKeyValue) and (tk = etOpenObject) then begin
|
||||||
|
Result:=PBXReadKeyValue(p, TPBXKeyValue(pobj) );
|
||||||
|
if not Result then Exit;
|
||||||
|
end else
|
||||||
|
// array of object
|
||||||
|
PBXParserSkipLevel(p);
|
||||||
|
end;
|
||||||
|
end else begin
|
||||||
|
writeln(obj.ClassName, ': property not found: ', p.Name);
|
||||||
|
if tk <> etValue then
|
||||||
|
PBXParserSkipLevel(p);
|
||||||
|
end;
|
||||||
|
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
Result:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PBXGatherObjects(obj: TObject; srz: TList);
|
||||||
|
var
|
||||||
|
plist : PPropList;
|
||||||
|
cnt : Integer;
|
||||||
|
i : Integer;
|
||||||
|
j : Integer;
|
||||||
|
k : Integer;
|
||||||
|
arr : TPBXObjectsList;
|
||||||
|
ch : TObject;
|
||||||
|
ach : TObject;
|
||||||
|
kind : TTypeKind;
|
||||||
|
const
|
||||||
|
FlagGet = 3; // 1 + 2 //ptField = 0;
|
||||||
|
FlagSet = 12; // 4 + 8 , 16 + 32 //ptStatic = 1;
|
||||||
|
FlagSP = 16 + 32; //ptVirtual = 2;
|
||||||
|
FlagIdx = 64; //ptConst = 3; }
|
||||||
|
begin
|
||||||
|
if (not Assigned(obj)) or (not Assigned(srz)) then Exit;
|
||||||
|
|
||||||
|
srz.Add(obj);
|
||||||
|
j:=0;
|
||||||
|
while j<srz.Count do begin
|
||||||
|
obj:=TObject(srz[j]);
|
||||||
|
|
||||||
|
plist:=nil;
|
||||||
|
cnt:=GetPropList(obj, plist);
|
||||||
|
if Assigned(plist) then begin
|
||||||
|
for i:=0 to cnt-1 do begin
|
||||||
|
kind := plist^[i]^.PropType^.Kind;
|
||||||
|
if (kind<>tkClass) then Continue;
|
||||||
|
|
||||||
|
ch:=GetObjectProp(obj, plist^[i] );
|
||||||
|
if not Assigned(ch) then Continue;
|
||||||
|
|
||||||
|
if (plist^[i]^.PropProcs and FlagSet <> FlagSet) then begin
|
||||||
|
if srz.IndexOf(ch)<0 then
|
||||||
|
srz.Add ( ch );
|
||||||
|
end else if ch is TPBXObjectsList then begin
|
||||||
|
|
||||||
|
arr:=TPBXObjectsList(ch);
|
||||||
|
for k:=0 to arr.Count-1 do begin
|
||||||
|
ach:=arr[k];
|
||||||
|
if srz.IndexOf(ach)<0 then srz.Add(ach);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Freemem(plist);
|
||||||
|
end;
|
||||||
|
inc(j);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PBXAssignRef(list: TList);
|
||||||
|
var
|
||||||
|
i : Integer;
|
||||||
|
p : PBXObject;
|
||||||
|
id: Int64;
|
||||||
|
begin
|
||||||
|
if not Assigned(list) then Exit;
|
||||||
|
id:=2; // root! :)
|
||||||
|
for i:=0 to list.Count-1 do begin
|
||||||
|
p:=PBXObject(list[i]);
|
||||||
|
if not Assigned(p) then Continue;
|
||||||
|
if (p._id='') then begin
|
||||||
|
p._id:=IntToHex(id, 24);
|
||||||
|
inc(id);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// 0AFA6EA519F60EFD004C8FD9
|
||||||
|
// 123456789012345678901234
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PBXWriteStrArray( w: TPBXWriter; list: TPBXStringArray );
|
||||||
|
var
|
||||||
|
i : Integer;
|
||||||
|
begin
|
||||||
|
w.OpenBlock('(');
|
||||||
|
for i:=0 to list.Count-1 do
|
||||||
|
w.WriteArrValue(list.Strings[i]);
|
||||||
|
w.CloseBlock(')');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure PBXWriteObjArray( w: TPBXWriter; list: TPBXObjectsList );
|
||||||
|
var
|
||||||
|
i : Integer;
|
||||||
|
pbx : PBXObject;
|
||||||
|
begin
|
||||||
|
for i:=0 to list.Count-1 do begin
|
||||||
|
pbx:=PBXObject(list[i]);
|
||||||
|
w.WriteArrValue(pbx._id, pbx._headerComment);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PBXWriteKeyValue( w: TPBXWriter; kv: TPBXKeyValue );
|
||||||
|
var
|
||||||
|
i : Integer;
|
||||||
|
v : TPBXValue;
|
||||||
|
nm : string;
|
||||||
|
begin
|
||||||
|
w.OpenBlock( '{' );
|
||||||
|
for i:=0 to kv.Count-1 do begin
|
||||||
|
v:=TPBXValue(kv.Items[i]);
|
||||||
|
nm:=kv.NameOfIndex(i);
|
||||||
|
w.WriteName(nm);
|
||||||
|
case v.valType of
|
||||||
|
vtString: w.WriteValue(v.str);
|
||||||
|
vtArrayOfStr: PBXWriteStrArray(w, v.arr);
|
||||||
|
vtKeyVal: PBXWriteKeyValue(w, v.keyval);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
w.CloseBlock( '}' );
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure PBXWriteObj(pbx: PBXObject; w: TPBXWriter; WriteEmpty: TStrings);
|
||||||
|
var
|
||||||
|
p : PPropList;
|
||||||
|
cnt : Integer;
|
||||||
|
i,j : Integer;
|
||||||
|
isMan : Boolean;
|
||||||
|
vl : string;
|
||||||
|
sobj : TObject;
|
||||||
|
nm : string;
|
||||||
|
vcmt : string;
|
||||||
|
isstr : Boolean;
|
||||||
|
|
||||||
|
// used for sorting. todo: find a better way for sort by names!
|
||||||
|
names : TStringList;
|
||||||
|
begin
|
||||||
|
|
||||||
|
w.WriteName(pbx._id, pbx._headerComment);
|
||||||
|
|
||||||
|
isMan:=(pbx.ClassName='PBXFileReference') or (pbx.ClassName='PBXBuildFile');
|
||||||
|
if isMan then w.ManualLineBreak:=true;
|
||||||
|
|
||||||
|
w.OpenBlock('{');
|
||||||
|
w.WriteNamedValue('isa', pbx.ClassName);
|
||||||
|
|
||||||
|
p:=nil;
|
||||||
|
cnt:=GetPropList(pbx, p);
|
||||||
|
|
||||||
|
//todo: I don't like this soritng at all!
|
||||||
|
// but it appears to be the most common available
|
||||||
|
names:=TStringList.Create;
|
||||||
|
try
|
||||||
|
for i:=0 to cnt-1 do names.AddObject(p^[i].Name, TObject(PtrUInt(i)));
|
||||||
|
names.Sort;
|
||||||
|
|
||||||
|
for j:=0 to names.Count-1 do begin
|
||||||
|
i:=Integer(PtrUInt(names.Objects[j]));
|
||||||
|
|
||||||
|
vl:='';
|
||||||
|
vcmt:='';
|
||||||
|
isstr:=false;
|
||||||
|
|
||||||
|
nm:=p^[i].Name;
|
||||||
|
if p^[i].PropType.Kind=tkClass then begin
|
||||||
|
sobj:=GetObjectProp(pbx, p^[i]);
|
||||||
|
if sobj is PBXObject then begin
|
||||||
|
vl:=PBXObject(sobj)._id;
|
||||||
|
vcmt:=PBXObject(sobj)._headerComment;
|
||||||
|
isstr:=vl<>'';
|
||||||
|
end else if sobj is TPBXObjectsList then begin
|
||||||
|
w.WriteName(nm); w.OpenBlock('(');
|
||||||
|
PBXWriteObjArray( w, TPBXObjectsList(sobj) );
|
||||||
|
w.CloseBlock(')');
|
||||||
|
end else if sobj is TPBXStringArray then begin
|
||||||
|
w.WriteName(nm);
|
||||||
|
PBXWriteStrArray( w, TPBXStringArray(sobj) );
|
||||||
|
end else if sobj is TPBXKeyValue then begin
|
||||||
|
w.WriteName(nm);
|
||||||
|
PBXWriteKeyValue(w, TPBXKeyValue(sobj));
|
||||||
|
end;
|
||||||
|
end else if p^[i].PropType.Kind in [tkAString, tkString] then begin
|
||||||
|
vl:=GetStrProp(pbx,p^[i]);
|
||||||
|
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
|
||||||
|
end else if p^[i].PropType.Kind in [tkInteger, tkInt64, tkQWord] then begin
|
||||||
|
vl:=IntToStr(GetInt64Prop(pbx, p^[i]));
|
||||||
|
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if isstr then begin
|
||||||
|
w.WriteName(nm);
|
||||||
|
w.WriteValue(vl,vcmt);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
if isMan then w.ManualLineBreak:=false;
|
||||||
|
w.CloseBlock('}');
|
||||||
|
finally
|
||||||
|
names.Free;
|
||||||
|
if Assigned(p) then Freemem(p);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXWriteContainer(const FileInfo: TPBXFileInfo; AssignRef: Boolean = true): string;
|
||||||
|
var
|
||||||
|
lst : TList;
|
||||||
|
st : TStringList;
|
||||||
|
i : Integer;
|
||||||
|
w : TPBXWriter;
|
||||||
|
sc : string;
|
||||||
|
pbx : PBXObject;
|
||||||
|
emp : TStringList;
|
||||||
|
begin
|
||||||
|
lst:=TList.Create;
|
||||||
|
st:=TStringList.Create;
|
||||||
|
emp:=TStringList.Create;
|
||||||
|
try
|
||||||
|
PBXGatherObjects(fileInfo.rootObject, lst);
|
||||||
|
if AssignRef then PBXAssignRef(lst);
|
||||||
|
|
||||||
|
for i:=0 to lst.Count-1 do begin
|
||||||
|
st.AddObject( PBXObject(lst[i]).ClassName+' '+PBXObject(lst[i])._id, PBXObject(lst[i]));
|
||||||
|
end;
|
||||||
|
st.Sort;
|
||||||
|
|
||||||
|
w:=TPBXWriter.Create;
|
||||||
|
try
|
||||||
|
sc:='';
|
||||||
|
w.WriteRaw('// !$*UTF8*$!');
|
||||||
|
w.WriteLineBreak;
|
||||||
|
w.OpenBlock('{');
|
||||||
|
w.WriteNamedValue('archiveVersion', FileInfo.archiveVersion);
|
||||||
|
w.WriteName('classes'); w.OpenBlock('{'); w.CloseBlock('}');
|
||||||
|
w.WriteNamedValue('objectVersion', FileInfo.objectVersion);
|
||||||
|
w.WriteName('objects'); w.OpenBlock('{');
|
||||||
|
for i:=0 to st.Count-1 do begin
|
||||||
|
pbx:=PBXObject(st.Objects[i]);
|
||||||
|
if sc<>pbx.ClassName then begin
|
||||||
|
if sc<>'' then begin
|
||||||
|
w.WriteLineComment('End '+sc+' section');
|
||||||
|
end;
|
||||||
|
sc:=pbx.ClassName;
|
||||||
|
w.WriteLineBreak();
|
||||||
|
w.WriteLineComment('Begin '+sc+' section');
|
||||||
|
emp.Clear;
|
||||||
|
pbx._WriteEmpty(emp);
|
||||||
|
end;
|
||||||
|
PBXWriteObj(pbx, w, emp);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if sc<>'' then w.WriteLineComment('End '+sc+' section');
|
||||||
|
w.CloseBlock('}');
|
||||||
|
|
||||||
|
w.WriteNamedValue('rootObject', FileInfo.rootObject._id, FileInfo.rootObject._headerComment);
|
||||||
|
w.CloseBlock('}');
|
||||||
|
Result:=w.Buffer;
|
||||||
|
finally
|
||||||
|
w.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
finally
|
||||||
|
st.Free;
|
||||||
|
lst.Free;
|
||||||
|
emp.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
initialization
|
||||||
|
pbxClassList := TStringList.Create;
|
||||||
|
|
||||||
|
finalization
|
||||||
|
pbxClassList.Free;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end.
|
715
components/iphonelazext/pbx/pbxfile.pas
Normal file
715
components/iphonelazext/pbx/pbxfile.pas
Normal file
@ -0,0 +1,715 @@
|
|||||||
|
unit pbxfile;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
(*-------------------------------------------------------------------------------
|
||||||
|
* by Dmitry Boyarintsev - Oct 2014 *
|
||||||
|
* *
|
||||||
|
* license: free for use, but please leave a note to the origin of the library *
|
||||||
|
* *
|
||||||
|
* pbxfile is JSON file format by Apple. Unlike JSON, it allows to add comments. *
|
||||||
|
* (was it introduced in NextStep system?) *
|
||||||
|
* other differences *
|
||||||
|
* no explicit type in values/identifiers *
|
||||||
|
* . and / are valud value/identifier character *
|
||||||
|
* ; - is a separator in values *
|
||||||
|
* () - is an array. the last element can (should?) end up with comma *
|
||||||
|
* {} - is an object (just like json) *
|
||||||
|
* escaping characters with C-style escaping: *
|
||||||
|
* * quotes (") *
|
||||||
|
* * line breaks (note OSX is typically using \n, unline Unix \r) *
|
||||||
|
* *
|
||||||
|
* PBXScanner - scans through the file *
|
||||||
|
* PBXParser - parses the file, returning a higher level entities of the file: *
|
||||||
|
* values, open/close of object/array *
|
||||||
|
* The parser doesn't produce any kind of structure. Instead it only allows to *
|
||||||
|
* build one. i.e. PBXContainer *
|
||||||
|
-------------------------------------------------------------------------------*)
|
||||||
|
|
||||||
|
{$ifdef fpc}{$mode delphi}{$endif}
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, StrUtils;
|
||||||
|
|
||||||
|
type
|
||||||
|
TPBXToken = (
|
||||||
|
tkEOF,
|
||||||
|
tkComma, // ','
|
||||||
|
tkSemiColon, // ';'
|
||||||
|
tkEqual, // '='
|
||||||
|
tkCurlyBraceOpen, // '{'
|
||||||
|
tkCurlyBraceClose, // '}'
|
||||||
|
tkRoundBraceOpen, // '('
|
||||||
|
tkRoundBraceClose, // ')'
|
||||||
|
tkIdentifier,
|
||||||
|
tkUnknown
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
TCommentEvent = procedure (Sender: TObject; const cmtText: string) of object;
|
||||||
|
{ TPBXScanner }
|
||||||
|
|
||||||
|
TPBXScanner = class(TObject)
|
||||||
|
private
|
||||||
|
buf : string;
|
||||||
|
idx : Integer;
|
||||||
|
FCurLine : string;
|
||||||
|
FCurRow : Integer;
|
||||||
|
FCurToken : TPBXToken;
|
||||||
|
FCurTokenString: string;
|
||||||
|
function GetCurColumn: Integer;
|
||||||
|
protected
|
||||||
|
procedure DoComment(const cmt: string);
|
||||||
|
procedure SkipComment(const EndOfLine: Boolean);
|
||||||
|
function DoFetchToken: TPBXToken;
|
||||||
|
public
|
||||||
|
OnComment: TCommentEvent;
|
||||||
|
procedure SetBuf(const abuf: string);
|
||||||
|
function FetchToken: TPBXToken;
|
||||||
|
|
||||||
|
property CurLine: string read FCurLine;
|
||||||
|
property CurRow: Integer read FCurRow;
|
||||||
|
property CurColumn: Integer read GetCurColumn;
|
||||||
|
|
||||||
|
property CurToken: TPBXToken read FCurToken;
|
||||||
|
property CurTokenString: string read FCurTokenString;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TPBXEntity = (
|
||||||
|
etOpenArray, etCloseArray
|
||||||
|
, etOpenObject, etCloseObject
|
||||||
|
, etValue
|
||||||
|
, etEOF
|
||||||
|
, etError
|
||||||
|
);
|
||||||
|
TPBXParserState = (stInit, stObject, stObjectNext, stArray, stArrayNext, stError);
|
||||||
|
|
||||||
|
{ TPBXParser }
|
||||||
|
|
||||||
|
TPBXParser = class(TObject)
|
||||||
|
private
|
||||||
|
fState : TPBXParserState;
|
||||||
|
fStStack : array of TPBXParserState;
|
||||||
|
fStCount : Integer;
|
||||||
|
|
||||||
|
fFetchComment: TCommentEvent;
|
||||||
|
procedure PushState(AState: TPBXParserState);
|
||||||
|
function PopState: TPBXParserState;
|
||||||
|
|
||||||
|
function DefaultFetch(tk: TPBXToken): TPBXEntity;
|
||||||
|
procedure DoScanComment(sender: TObject; const acomment: string);
|
||||||
|
public
|
||||||
|
scanner : TPBXScanner;
|
||||||
|
Name : string;
|
||||||
|
Value : string;
|
||||||
|
CurEntity : TPBXEntity;
|
||||||
|
LastComment : string;
|
||||||
|
procedure Reset;
|
||||||
|
function FetchNextEntity: TPBXEntity;
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
property Level: Integer read fStCount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXWriter }
|
||||||
|
|
||||||
|
TPBXWriter = class(TObject)
|
||||||
|
private
|
||||||
|
fbuf : string;
|
||||||
|
idx : Integer;
|
||||||
|
protected
|
||||||
|
fprefix : Integer;
|
||||||
|
fManualLineBreak : Boolean;
|
||||||
|
fisNewLine : Boolean;
|
||||||
|
//fstack : array of fstack;
|
||||||
|
procedure IncPrefix;
|
||||||
|
procedure DecPrefix;
|
||||||
|
function GetBuf: string;
|
||||||
|
procedure DoWriteRaw(const s: string);
|
||||||
|
procedure DoWrite(const s: string);
|
||||||
|
procedure DoLineBreak;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
procedure OpenBlock(const openchar: string);
|
||||||
|
procedure CloseBlock(const closechar: string);
|
||||||
|
procedure WriteRaw(const s: string);
|
||||||
|
procedure WriteLineBreak;
|
||||||
|
procedure WriteLineComment(const s: string);
|
||||||
|
procedure WriteName(const nm: string; const cmt: string = '');
|
||||||
|
procedure WriteValue(const v: string; const cmt: string = '');
|
||||||
|
procedure WriteArrValue(const v: string; const cmt: string = '');
|
||||||
|
procedure WriteNamedValue(const nm, v: string; const cmt: string = '');
|
||||||
|
property Buffer: string read GetBuf;
|
||||||
|
property ManualLineBreak: Boolean read fManualLineBreak write fManualLineBreak;
|
||||||
|
end;
|
||||||
|
|
||||||
|
const
|
||||||
|
CharOffset = #$09;
|
||||||
|
CharLineBreak = #$0A;
|
||||||
|
CharSeparator = ';';
|
||||||
|
CharArrSeparator = ',';
|
||||||
|
CharSpace = #$20;
|
||||||
|
|
||||||
|
procedure ScanAString(const test: string);
|
||||||
|
procedure ParseAString(const test: string);
|
||||||
|
|
||||||
|
function PBXParserSkipLevel(p: TPBXParser): Boolean;
|
||||||
|
function PBXRawWriteValue(const v: string): string;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
type
|
||||||
|
TCharSet = set of char;
|
||||||
|
|
||||||
|
const
|
||||||
|
LineBreaks = [#10, #13];
|
||||||
|
WhiteSpace = [#9,#8,#32];
|
||||||
|
WhiteSpaceBreaks = LineBreaks+WhiteSpace;
|
||||||
|
Alpha = ['a'..'z','A'..'Z'];
|
||||||
|
Numeric = ['0'..'9'];
|
||||||
|
AlphaNumeric = Alpha+Numeric;
|
||||||
|
IdentName = AlphaNumeric+['_','.','/']; // . and / are allowed in values
|
||||||
|
ToEscape = ['"',#13,#9,#10,'\'];
|
||||||
|
// commas are not
|
||||||
|
|
||||||
|
function PBXRawWriteValue(const v: string): string;
|
||||||
|
var
|
||||||
|
i : Integer;
|
||||||
|
k : Integer;
|
||||||
|
begin
|
||||||
|
k:=0;
|
||||||
|
for i:=1 to length(v) do begin
|
||||||
|
if not (v[i] in IdentName) then begin
|
||||||
|
if Result='' then begin
|
||||||
|
SetLength(Result, length(v)*2+2);
|
||||||
|
Result[1]:='"';
|
||||||
|
Move(v[1], Result[2], i);
|
||||||
|
k:=i+1;
|
||||||
|
end;
|
||||||
|
if (v[i] in ToEscape) then begin
|
||||||
|
Result[k]:='\';
|
||||||
|
inc(k);
|
||||||
|
case v[i] of
|
||||||
|
'"': Result[k]:='"';
|
||||||
|
#13: Result[k]:='n';
|
||||||
|
#10: Result[k]:='r';
|
||||||
|
#9: Result[k]:='t';
|
||||||
|
'\': Result[k]:='\';
|
||||||
|
end;
|
||||||
|
inc(k);
|
||||||
|
end else begin
|
||||||
|
Result[k]:=v[i];
|
||||||
|
inc(k);
|
||||||
|
end;
|
||||||
|
end else if k>0 then begin
|
||||||
|
Result[k]:=v[i];
|
||||||
|
inc(k);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if k=0 then
|
||||||
|
Result:=v
|
||||||
|
else begin
|
||||||
|
Result[k]:='"';
|
||||||
|
SetLength(Result,k);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ScanTo(const s: string; var idx: Integer; ToChars: TCharSet): string;
|
||||||
|
var
|
||||||
|
i : integer;
|
||||||
|
begin
|
||||||
|
i:=idx;
|
||||||
|
while (idx<=length(s)) and not (s[idx] in ToChars) do inc(idx);
|
||||||
|
Result:=Copy(s, i, idx-i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ScanWhile(const s: string; var idx: Integer; WhileChars: TCharSet): string;
|
||||||
|
var
|
||||||
|
i : integer;
|
||||||
|
begin
|
||||||
|
i:=idx;
|
||||||
|
while (idx<=length(s)) and (s[idx] in WhileChars) do
|
||||||
|
inc(idx);
|
||||||
|
Result:=Copy(s, i, idx-i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXWriter }
|
||||||
|
|
||||||
|
procedure TPBXWriter.IncPrefix;
|
||||||
|
begin
|
||||||
|
inc(fprefix);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.DecPrefix;
|
||||||
|
begin
|
||||||
|
dec(fprefix);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXWriter.GetBuf: string;
|
||||||
|
begin
|
||||||
|
Result:=fbuf;
|
||||||
|
SetLength(Result, idx-1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.DoWriteRaw(const s: string);
|
||||||
|
var
|
||||||
|
sz : Integer;
|
||||||
|
bufsz : Integer;
|
||||||
|
begin
|
||||||
|
if s ='' then Exit;
|
||||||
|
sz:=length(s)+idx-1;
|
||||||
|
bufsz:=length(fbuf);
|
||||||
|
while bufsz<sz do begin
|
||||||
|
if bufsz=0 then bufsz:=1024
|
||||||
|
else bufsz:=bufsz*2;
|
||||||
|
end;
|
||||||
|
SetLength(fbuf, bufsz);
|
||||||
|
Move(s[1], fbuf[idx], length(s));
|
||||||
|
inc(idx, length(s));
|
||||||
|
fisNewLine:=false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.DoWrite(const s: string);
|
||||||
|
var
|
||||||
|
pfx : string;
|
||||||
|
begin
|
||||||
|
if fisNewLine and (fprefix>0) then begin
|
||||||
|
SetLength(pfx, fprefix);
|
||||||
|
FillChar(pfx[1], fprefix, CharOffset);
|
||||||
|
DoWriteRaw(pfx);
|
||||||
|
end;
|
||||||
|
DoWriteRaw(s);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.DoLineBreak;
|
||||||
|
begin
|
||||||
|
DoWriteRaw(CharLineBreak);
|
||||||
|
fisNewLine:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TPBXWriter.Create;
|
||||||
|
begin
|
||||||
|
idx:=1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.OpenBlock(const openchar: string);
|
||||||
|
begin
|
||||||
|
DoWrite(openchar);
|
||||||
|
IncPrefix;
|
||||||
|
if not fManualLineBreak then DoLineBreak;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.CloseBlock(const closechar: string);
|
||||||
|
begin
|
||||||
|
DecPrefix;
|
||||||
|
DoWrite(closechar);
|
||||||
|
if fprefix>0 then DoWriteRaw(CharSeparator);
|
||||||
|
if not fManualLineBreak then DoLineBreak;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteRaw(const s: string);
|
||||||
|
begin
|
||||||
|
DoWriteRaw(s);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteLineBreak;
|
||||||
|
begin
|
||||||
|
DoLineBreak;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteLineComment(const s: string);
|
||||||
|
begin
|
||||||
|
DoWriteRaw('/* ');
|
||||||
|
DoWriteRaw(s);
|
||||||
|
DoWriteRaw(' */');
|
||||||
|
DoLineBreak;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteName(const nm: string; const cmt: string = '');
|
||||||
|
begin
|
||||||
|
if nm='' then Exit;
|
||||||
|
DoWrite(PBXRawWriteValue(nm));
|
||||||
|
if cmt<>'' then begin
|
||||||
|
DoWriteRaw(' /* ');
|
||||||
|
DoWriteRaw(cmt);
|
||||||
|
DoWriteRaw(' */');
|
||||||
|
end;
|
||||||
|
DoWriteRaw(' = ');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteValue(const v: string; const cmt: string = '');
|
||||||
|
begin
|
||||||
|
if v ='' then DoWriteRaw('""')
|
||||||
|
else DoWriteRaw(PBXRawWriteValue(v));
|
||||||
|
if cmt<>'' then begin
|
||||||
|
DoWriteRaw(' /* ');
|
||||||
|
DoWriteRaw(cmt);
|
||||||
|
DoWriteRaw(' */');
|
||||||
|
end;
|
||||||
|
DoWriteRaw(CharSeparator);
|
||||||
|
if not fManualLineBreak then DoLineBreak
|
||||||
|
else DoWriteRaw(CharSpace);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteArrValue(const v: string; const cmt: string);
|
||||||
|
begin
|
||||||
|
DoWrite(PBXRawWriteValue(v));
|
||||||
|
if cmt<>'' then begin
|
||||||
|
DoWriteRaw(' /* ');
|
||||||
|
DoWriteRaw(cmt);
|
||||||
|
DoWriteRaw(' */');
|
||||||
|
end;
|
||||||
|
DoWriteRaw(CharArrSeparator);
|
||||||
|
if not fManualLineBreak then DoLineBreak
|
||||||
|
else DoWriteRaw(CharSpace);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXWriter.WriteNamedValue(const nm, v: string; const cmt: string);
|
||||||
|
begin
|
||||||
|
WriteName(nm);
|
||||||
|
WriteValue(v, cmt);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPBXParser }
|
||||||
|
|
||||||
|
procedure TPBXParser.PushState(AState: TPBXParserState);
|
||||||
|
begin
|
||||||
|
if fStCount=length(fStStack) then begin
|
||||||
|
if fStCount=0 then SetLength(fStStack, 4)
|
||||||
|
else SetLength(fStStack, fStCount*2);
|
||||||
|
end;
|
||||||
|
fStStack[fStcount]:=AState;
|
||||||
|
inc(fStcount);
|
||||||
|
fState:=AState;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXParser.PopState: TPBXParserState;
|
||||||
|
begin
|
||||||
|
dec(fStCount);
|
||||||
|
if fStCount>0 then begin
|
||||||
|
fState:=fStStack[fStCount-1];
|
||||||
|
if fState = stObject then fState:=stObjectNext
|
||||||
|
else if fState = stArray then fState:=stArrayNext;
|
||||||
|
end else
|
||||||
|
fState:=stInit;
|
||||||
|
Result:=fState;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXParser.DefaultFetch(tk: TPBXToken): TPBXEntity;
|
||||||
|
begin
|
||||||
|
case tk of
|
||||||
|
tkIdentifier: begin
|
||||||
|
Value:=scanner.CurTokenString;
|
||||||
|
Result:=etValue;
|
||||||
|
end;
|
||||||
|
tkCurlyBraceOpen: begin
|
||||||
|
Result:=etOpenObject;
|
||||||
|
PushState(stObject)
|
||||||
|
end;
|
||||||
|
tkRoundBraceOpen: begin
|
||||||
|
Result:=etOpenArray;
|
||||||
|
PushState(stArray)
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
Result:=etError;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXParser.DoScanComment(sender: TObject; const acomment: string);
|
||||||
|
begin
|
||||||
|
LastComment:=acomment;
|
||||||
|
if Assigned(fFetchComment) then
|
||||||
|
fFetchComment(sender, acomment);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXParser.Reset;
|
||||||
|
begin
|
||||||
|
fState:=stInit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXParser.FetchNextEntity: TPBXEntity;
|
||||||
|
var
|
||||||
|
tk : TPBXToken;
|
||||||
|
done : Boolean;
|
||||||
|
begin
|
||||||
|
LastComment:='';
|
||||||
|
Name:='';
|
||||||
|
Value:='';
|
||||||
|
case fState of
|
||||||
|
stInit :
|
||||||
|
case scanner.FetchToken of
|
||||||
|
tkCurlyBraceOpen:
|
||||||
|
begin
|
||||||
|
PushState(stObject);
|
||||||
|
Result:=etOpenObject;
|
||||||
|
end;
|
||||||
|
tkEOF:
|
||||||
|
Result:=etEOF;
|
||||||
|
else
|
||||||
|
Result:=etError;
|
||||||
|
end;
|
||||||
|
stObject, stObjectNext:
|
||||||
|
repeat
|
||||||
|
done:=true;
|
||||||
|
case scanner.FetchToken of
|
||||||
|
tkSemiColon: begin
|
||||||
|
if fState = stObjectNext then begin
|
||||||
|
done:=false;
|
||||||
|
fState:=stObject;
|
||||||
|
end else
|
||||||
|
Result:=etError;
|
||||||
|
end;
|
||||||
|
tkCurlyBraceClose: begin
|
||||||
|
PopState;
|
||||||
|
Result:=etCloseObject;
|
||||||
|
end;
|
||||||
|
tkIdentifier:
|
||||||
|
begin
|
||||||
|
Name:=scanner.CurTokenString;
|
||||||
|
LastComment:='';
|
||||||
|
if scanner.FetchToken <> tkEqual then begin
|
||||||
|
Result:=etError;
|
||||||
|
end else begin
|
||||||
|
tk:=scanner.FetchToken;
|
||||||
|
Result:=DefaultFetch(tk);
|
||||||
|
if Result=etValue then fState:=stObjectNext;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
until done;
|
||||||
|
stArray, stArrayNext: begin
|
||||||
|
repeat
|
||||||
|
done:=true;
|
||||||
|
tk:=scanner.FetchToken;
|
||||||
|
case tk of
|
||||||
|
tkComma: begin
|
||||||
|
if fState = stArrayNext then begin
|
||||||
|
fState:=stArray;
|
||||||
|
done:=false;
|
||||||
|
end else
|
||||||
|
Result:=etError; // unexpected comma
|
||||||
|
end;
|
||||||
|
tkRoundBraceClose: begin
|
||||||
|
PopState;
|
||||||
|
Result:=etCloseArray;
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
Result:=DefaultFetch(tk);
|
||||||
|
if Result=etValue then fState:=stArrayNext;
|
||||||
|
end;
|
||||||
|
until done;
|
||||||
|
end;
|
||||||
|
stError:
|
||||||
|
Result:=etError;
|
||||||
|
end;
|
||||||
|
if Result=etError then
|
||||||
|
fState:=stError;
|
||||||
|
CurEntity:=Result;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TPBXParser.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
scanner:=TPBXScanner.Create;
|
||||||
|
scanner.OnComment:=DoScanComment;
|
||||||
|
Reset;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TPBXParser.Destroy;
|
||||||
|
begin
|
||||||
|
scanner.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TPBXScanner }
|
||||||
|
|
||||||
|
function TPBXScanner.GetCurColumn: Integer;
|
||||||
|
begin
|
||||||
|
Result:=0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXScanner.DoComment(const cmt: string);
|
||||||
|
begin
|
||||||
|
if Assigned(OnComment) then OnComment(Self, cmt);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXScanner.SkipComment(const EndOfLine: Boolean);
|
||||||
|
var
|
||||||
|
cmt : string;
|
||||||
|
i : integer;
|
||||||
|
cnt : string;
|
||||||
|
begin
|
||||||
|
if EndOfLine then begin
|
||||||
|
cmt:=ScanTo(buf, idx, LineBreaks);
|
||||||
|
cnt:=trim(cmt);
|
||||||
|
end else begin
|
||||||
|
i:=PosEx('*/', buf, idx+2);
|
||||||
|
cnt:=trim(Copy(buf, idx+2, i-idx-2));
|
||||||
|
if i>0 then inc(i,2);
|
||||||
|
cmt:=Copy(buf, idx, i-idx);
|
||||||
|
inc(idx, length(cmt));
|
||||||
|
end;
|
||||||
|
DoComment(cnt);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXScanner.DoFetchToken: TPBXToken;
|
||||||
|
begin
|
||||||
|
if idx>length(buf) then begin
|
||||||
|
Result:=tkEOF;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// skipping comments
|
||||||
|
while true do begin
|
||||||
|
ScanWhile(buf, idx, WhiteSpaceBreaks);
|
||||||
|
if (idx<length(buf)) and (buf[idx]='/') then begin
|
||||||
|
if (buf[idx+1]='*') then
|
||||||
|
SkipComment(false)
|
||||||
|
else if buf[idx+1]='/' then begin
|
||||||
|
SkipComment(true);
|
||||||
|
end else begin
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
if idx>length(buf) then begin
|
||||||
|
Result:=tkEOF;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if buf[idx] in IdentName then begin
|
||||||
|
Result:=tkIdentifier;
|
||||||
|
FCurTokenString:=ScanWhile(buf, idx, IdentName);
|
||||||
|
end else
|
||||||
|
case buf[idx] of
|
||||||
|
'"': begin
|
||||||
|
inc(idx);
|
||||||
|
Result:=tkIdentifier;
|
||||||
|
FCurTokenString:=ScanTo(buf, idx, ['"']);
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
'=': begin
|
||||||
|
Result:= tkEqual;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
'{': begin
|
||||||
|
Result:=tkCurlyBraceOpen;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
'}': begin
|
||||||
|
Result:=tkCurlyBraceClose;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
')': begin
|
||||||
|
Result:=tkRoundBraceClose;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
'(': begin
|
||||||
|
Result:=tkRoundBraceOpen;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
';': begin
|
||||||
|
Result:=tkSemiColon;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
',': begin
|
||||||
|
Result:=tkComma;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
Result:= tkUnknown;
|
||||||
|
FCurTokenString:=buf[idx];
|
||||||
|
inc(idx);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPBXScanner.SetBuf(const abuf: string);
|
||||||
|
begin
|
||||||
|
buf:=abuf;
|
||||||
|
idx:=1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPBXScanner.FetchToken: TPBXToken;
|
||||||
|
begin
|
||||||
|
Result:=DoFetchToken;
|
||||||
|
FCurToken:=Result;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure ScanAString(const test: string);
|
||||||
|
var
|
||||||
|
sc : TPBXScanner;
|
||||||
|
begin
|
||||||
|
sc := TPBXScanner.Create;
|
||||||
|
try
|
||||||
|
sc.SetBuf(test);
|
||||||
|
while sc.FetchToken<>tkEOF do begin
|
||||||
|
if sc.CurToken=tkUnknown then begin
|
||||||
|
writeln(sc.CurToken:20,' ', IntToHex( byte(sc.CurTokenString[1]), 2 ) );
|
||||||
|
writeln('idx = ', sc.idx);
|
||||||
|
end else
|
||||||
|
;
|
||||||
|
//writeln(sc.CurToken:20,' ', sc.CurTokenString);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
sc.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure ParseAString(const test: string);
|
||||||
|
var
|
||||||
|
pr : TPBXParser;
|
||||||
|
et : TPBXEntity;
|
||||||
|
begin
|
||||||
|
pr := TPBXParser.Create;
|
||||||
|
try
|
||||||
|
pr.scanner.SetBuf(test);
|
||||||
|
et:=pr.FetchNextEntity;
|
||||||
|
while et <> etEOF do begin
|
||||||
|
if pr.Name<>'' then write('"',pr.Name,'":');
|
||||||
|
|
||||||
|
case et of
|
||||||
|
etValue: writeln('"',pr.Value,'",');
|
||||||
|
etCloseObject: writeln('},');
|
||||||
|
etOpenObject: writeln('{');
|
||||||
|
etOpenArray: writeln('[');
|
||||||
|
etCloseArray: writeln('],');
|
||||||
|
else
|
||||||
|
writeln(et);
|
||||||
|
end;
|
||||||
|
if et = etError then Break;
|
||||||
|
//writeln(pr.fState);
|
||||||
|
et:=pr.FetchNextEntity;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
pr.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXParserSkipLevel(p: TPBXParser): Boolean;
|
||||||
|
var
|
||||||
|
lvl : Integer;
|
||||||
|
tk : TPBXEntity;
|
||||||
|
begin
|
||||||
|
if not Assigned(p) then Exit;
|
||||||
|
lvl:=p.Level;
|
||||||
|
while (p.Level>=lvl) do begin
|
||||||
|
tk:=p.FetchNextEntity;
|
||||||
|
if tk=etError then begin
|
||||||
|
Result:=false;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
74
components/iphonelazext/pbx/test/pbxreader.lpi
Normal file
74
components/iphonelazext/pbx/test/pbxreader.lpi
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CONFIG>
|
||||||
|
<ProjectOptions>
|
||||||
|
<Version Value="9"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<General>
|
||||||
|
<Flags>
|
||||||
|
<MainUnitHasCreateFormStatements Value="False"/>
|
||||||
|
<MainUnitHasTitleStatement Value="False"/>
|
||||||
|
</Flags>
|
||||||
|
<SessionStorage Value="InProjectDir"/>
|
||||||
|
<MainUnit Value="0"/>
|
||||||
|
<Title Value="pbxreader"/>
|
||||||
|
<UseAppBundle Value="False"/>
|
||||||
|
<ResourceType Value="res"/>
|
||||||
|
</General>
|
||||||
|
<i18n>
|
||||||
|
<EnableI18N LFM="False"/>
|
||||||
|
</i18n>
|
||||||
|
<VersionInfo>
|
||||||
|
<StringTable ProductVersion=""/>
|
||||||
|
</VersionInfo>
|
||||||
|
<BuildModes Count="1">
|
||||||
|
<Item1 Name="Default" Default="True"/>
|
||||||
|
</BuildModes>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
</PublishOptions>
|
||||||
|
<RunParams>
|
||||||
|
<local>
|
||||||
|
<FormatVersion Value="1"/>
|
||||||
|
</local>
|
||||||
|
</RunParams>
|
||||||
|
<Units Count="1">
|
||||||
|
<Unit0>
|
||||||
|
<Filename Value="pbxreader.lpr"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit0>
|
||||||
|
</Units>
|
||||||
|
</ProjectOptions>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<Target>
|
||||||
|
<Filename Value="pbxreader"/>
|
||||||
|
</Target>
|
||||||
|
<SearchPaths>
|
||||||
|
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||||
|
<OtherUnitFiles Value=".."/>
|
||||||
|
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
|
||||||
|
</SearchPaths>
|
||||||
|
<CodeGeneration>
|
||||||
|
<Checks>
|
||||||
|
<RangeChecks Value="True"/>
|
||||||
|
<OverflowChecks Value="True"/>
|
||||||
|
<StackChecks Value="True"/>
|
||||||
|
</Checks>
|
||||||
|
<TargetCPU Value="i386"/>
|
||||||
|
</CodeGeneration>
|
||||||
|
</CompilerOptions>
|
||||||
|
<Debugging>
|
||||||
|
<Exceptions Count="3">
|
||||||
|
<Item1>
|
||||||
|
<Name Value="EAbort"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<Name Value="ECodetoolError"/>
|
||||||
|
</Item2>
|
||||||
|
<Item3>
|
||||||
|
<Name Value="EFOpenError"/>
|
||||||
|
</Item3>
|
||||||
|
</Exceptions>
|
||||||
|
</Debugging>
|
||||||
|
</CONFIG>
|
273
components/iphonelazext/pbx/test/pbxreader.lpr
Normal file
273
components/iphonelazext/pbx/test/pbxreader.lpr
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
program pbxreader;
|
||||||
|
|
||||||
|
{$mode delphi}{$H+}
|
||||||
|
|
||||||
|
uses
|
||||||
|
{$IFDEF UNIX}{$IFDEF UseCThreads}
|
||||||
|
cthreads,
|
||||||
|
{$ENDIF}{$ENDIF}
|
||||||
|
heaptrc,
|
||||||
|
Classes, SysUtils, pbxfile, pbxcontainer, xcodeproj
|
||||||
|
{ you can add units after this };
|
||||||
|
|
||||||
|
function ReadFileToString(const fn: string): string;
|
||||||
|
var
|
||||||
|
fs : TFileStream;
|
||||||
|
begin
|
||||||
|
fs:=TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
|
||||||
|
try
|
||||||
|
SetLength(Result, fs.Size);
|
||||||
|
fs.Read(Result[1], fs.Size);
|
||||||
|
finally
|
||||||
|
fs.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure WriteStringToFile(const s, fn: string);
|
||||||
|
var
|
||||||
|
fs : TFileStream;
|
||||||
|
begin
|
||||||
|
fs:=TFileStream.Create(fn, fmCreate);
|
||||||
|
try
|
||||||
|
if length(s)>0 then begin
|
||||||
|
fs.Write(s[1], length(s));
|
||||||
|
fs.Size:=length(s);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
fs.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestProject(const buf: string);
|
||||||
|
var
|
||||||
|
c : TPBXContainer;
|
||||||
|
st : TStringStream;
|
||||||
|
info : TPBXFileInfo;
|
||||||
|
prj : PBXProject;
|
||||||
|
i : Integer;
|
||||||
|
begin
|
||||||
|
c:= TPBXContainer.Create;
|
||||||
|
st := TStringStream.Create(buf);
|
||||||
|
try
|
||||||
|
c.ReadFile(st, info);
|
||||||
|
writeln('arch ver: ',info.archiveVersion);
|
||||||
|
writeln(' obj ver: ',info.objectVersion);
|
||||||
|
writeln('root obj: ', PtrUInt( info.rootObject ));
|
||||||
|
|
||||||
|
if info.rootObject is PBXProject then begin
|
||||||
|
writeln('project!');
|
||||||
|
prj:=PBXProject(info.rootObject);
|
||||||
|
writeln(prj.knownRegions.Text);
|
||||||
|
writeln('targets: ', prj.targets.Count );
|
||||||
|
for i:=0 to prj.targets.Count-1 do begin
|
||||||
|
writeln(prj.targets.Items[i].ClassName);
|
||||||
|
writeln(PBXNativeTarget(prj.targets.Items[i]).name);
|
||||||
|
end;
|
||||||
|
|
||||||
|
writeln(PtrUInt(prj.buildConfigurationList));
|
||||||
|
writeln('build configuration:');
|
||||||
|
for i:=0 to prj.buildConfigurationList.buildConfigurations.Count-1 do begin
|
||||||
|
writeln(' ',XCBuildConfiguration(prj.buildConfigurationList.buildConfigurations[i]).name);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
st.Free;
|
||||||
|
c.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ MyClass }
|
||||||
|
|
||||||
|
MyClass = class(TObject)
|
||||||
|
private
|
||||||
|
fInt: Integer;
|
||||||
|
fInt2: Integer;
|
||||||
|
fInt3: Integer;
|
||||||
|
fRO: TList;
|
||||||
|
fRW: TList;
|
||||||
|
protected
|
||||||
|
function GetInt3: Integer;
|
||||||
|
procedure SetInt3(AValue: Integer);
|
||||||
|
function GetInt2: Integer;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
published
|
||||||
|
property Int: Integer read fInt write fInt;
|
||||||
|
property Int2: Integer read GetInt2 write fInt2;
|
||||||
|
property Int3: Integer read GetInt3 write SetInt3;
|
||||||
|
property RO: TList read fRO;
|
||||||
|
property RW: TList read fRW write fRW;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
lst : TList;
|
||||||
|
m : MyClass;
|
||||||
|
|
||||||
|
{ MyClass }
|
||||||
|
|
||||||
|
function MyClass.GetInt3: Integer;
|
||||||
|
begin
|
||||||
|
Result:=fInt3;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure MyClass.SetInt3(AValue: Integer);
|
||||||
|
begin
|
||||||
|
fInt3:=AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function MyClass.GetInt2: Integer;
|
||||||
|
begin
|
||||||
|
Result:=fInt2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor MyClass.Create;
|
||||||
|
begin
|
||||||
|
fRO:=TList.Create;
|
||||||
|
fRW:=TList.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestRTTI;
|
||||||
|
var
|
||||||
|
lst : TList;
|
||||||
|
m : MyClass;
|
||||||
|
begin
|
||||||
|
lst:=TList.Create;
|
||||||
|
m:=MyClass.Create;
|
||||||
|
PBXGatherObjects(m, lst);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestWriter;
|
||||||
|
var
|
||||||
|
w : TPBXWriter;
|
||||||
|
begin
|
||||||
|
w := TPBXWriter.Create;
|
||||||
|
try
|
||||||
|
w.OpenBlock('{');
|
||||||
|
w.WriteName('archiveVersion');
|
||||||
|
w.WriteValue('1');
|
||||||
|
w.WriteName('classes');
|
||||||
|
w.OpenBlock('{');
|
||||||
|
w.CloseBlock('}');
|
||||||
|
w.WriteName('objectVersion');
|
||||||
|
w.WriteValue('46');
|
||||||
|
w.WriteName('objects');
|
||||||
|
w.OpenBlock('{');
|
||||||
|
w.CloseBlock('}');
|
||||||
|
w.WriteName('rootObject');
|
||||||
|
w.WriteValue('aaaa','Project object');
|
||||||
|
w.CloseBlock('}');
|
||||||
|
write(w.Buffer);
|
||||||
|
finally
|
||||||
|
w.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TestReadThenWrite;
|
||||||
|
var
|
||||||
|
prj : PBXProject;
|
||||||
|
list : TList;
|
||||||
|
i : Integer;
|
||||||
|
st : TStringList;
|
||||||
|
begin
|
||||||
|
if ParamCount=0 then begin
|
||||||
|
writeln('please provide pbx file');
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
//ScanAString( ReadFileToString(ParamStr(1)));
|
||||||
|
//ParseAString( ReadFileToString(ParamStr(1)));
|
||||||
|
//TestProject( ReadFileToString(ParamStr(1)));
|
||||||
|
if LoadProjectFromFile(ParamStr(1), prj) then begin
|
||||||
|
//list:=TList.Create;
|
||||||
|
//PBXGatherObjects(prj, list);
|
||||||
|
Write(ProjectWrite(prj));
|
||||||
|
|
||||||
|
prj.Free;
|
||||||
|
{st:=TStringList.Create;
|
||||||
|
try
|
||||||
|
for i:=0 to list.Count-1 do begin
|
||||||
|
st.AddObject( PBXObject(list[i]).ClassName, PBXObject(list[i]));
|
||||||
|
end;
|
||||||
|
st.Sort;
|
||||||
|
for i:=0 to st.Count-1 do begin
|
||||||
|
writeln(PBXObject(st.Objects[i]).__id,' : ',PBXObject(st.Objects[i]).ClassName);
|
||||||
|
end;
|
||||||
|
|
||||||
|
finally
|
||||||
|
st.Free;
|
||||||
|
end;}
|
||||||
|
|
||||||
|
//list.Free;
|
||||||
|
end else
|
||||||
|
writeln('not a project');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestWriteAProject;
|
||||||
|
var
|
||||||
|
p : PBXProject;
|
||||||
|
s : string;
|
||||||
|
t : PBXNativeTarget;
|
||||||
|
prd : PBXGroup;
|
||||||
|
//cfg : XCBuildConfiguration;
|
||||||
|
ph : PBXShellScriptBuildPhase;
|
||||||
|
begin
|
||||||
|
p:=CreateMinProject;
|
||||||
|
p.buildConfigurationList._headerComment:=p.buildConfigurationList._headerComment+' for PBXProject "test"';
|
||||||
|
p.attributes.AddStr('LastUpgradeCheck','0610');
|
||||||
|
t:=ProjectAddTarget(p,'targetto');
|
||||||
|
ph:=TargetAddRunScript(t);
|
||||||
|
ph.shellScript:='echo "hello world"';
|
||||||
|
//ph.buildActionMask:='0';
|
||||||
|
|
||||||
|
ph.runOnlyForDeploymentPostprocessing:='0';
|
||||||
|
t.productReference:=CreateFileRef('targetto', FILETYPE_EXEC);
|
||||||
|
PBXFileReference(t.productReference).sourceTree:='BUILT_PRODUCTS_DIR';
|
||||||
|
t.productName:='targetto';
|
||||||
|
t.productType:=PRODTYPE_TOOL;
|
||||||
|
|
||||||
|
// at least one configuration is added !
|
||||||
|
//todo: a target should automatically copy project's building settings
|
||||||
|
t.buildConfigurationList:=XCConfigurationList.Create;
|
||||||
|
t.buildConfigurationList._headerComment:='Build configuration list for PBXNativeTarget "targetto"';
|
||||||
|
t.buildConfigurationList.addConfig('Default').buildSettings.AddStr('PRODUCT_NAME','targetto');
|
||||||
|
t.buildConfigurationList.addConfig('Release').buildSettings.AddStr('PRODUCT_NAME','targetto');
|
||||||
|
t.buildConfigurationList.defaultConfigurationIsVisible:='0';
|
||||||
|
t.buildConfigurationList.defaultConfigurationName:='Release';
|
||||||
|
|
||||||
|
|
||||||
|
{ cfg:=XCBuildConfiguration(p.buildConfigurationList.buildConfigurations[0]);
|
||||||
|
cfg.buildSettings.AddStr('COPY_PHASE_STRIP', 'NO');
|
||||||
|
cfg.buildSettings.AddStr('GCC_DYNAMIC_NO_PIC', 'NO');
|
||||||
|
cfg.buildSettings.AddStr('GCC_OPTIMIZATION_LEVEL', '0');
|
||||||
|
cfg.buildSettings.AddStr('PRODUCT_NAME', 'targetto'); }
|
||||||
|
|
||||||
|
|
||||||
|
p.mainGroup:=CreateRootGroup('/Users/dmitry/pbx/utils/test.xcodeproj');
|
||||||
|
// requirements ?
|
||||||
|
prd:=p.mainGroup.addSubGroup('Products');
|
||||||
|
prd.children.Add(t.productReference);
|
||||||
|
|
||||||
|
p.productRefGroup:=prd;
|
||||||
|
|
||||||
|
p.compatibilityVersion:='Xcode 3.2';
|
||||||
|
|
||||||
|
s:=ProjectWrite(p);
|
||||||
|
WriteStringToFile(s, 'test.xcodeproj/project.pbxproj');
|
||||||
|
p.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if FileExists('leaks.txt') then DeleteFile('leaks.txt');
|
||||||
|
SetHeapTraceOutput('leaks.txt');
|
||||||
|
try
|
||||||
|
TestReadThenWrite;
|
||||||
|
except
|
||||||
|
on e: exception do
|
||||||
|
writeln(e.Message);
|
||||||
|
end;
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
577
components/iphonelazext/pbx/xcodeproj.pas
Normal file
577
components/iphonelazext/pbx/xcodeproj.pas
Normal file
@ -0,0 +1,577 @@
|
|||||||
|
unit xcodeproj;
|
||||||
|
{--------------------------------------------------------------------------------
|
||||||
|
* by Dmitry Boyarintsev - Oct 2014 *
|
||||||
|
* *
|
||||||
|
* license: free for use, but please leave a note to the origin of the library *
|
||||||
|
* *
|
||||||
|
* PBXcontainer unit is a library to read/write the pbx formatter file as a *
|
||||||
|
* whole The file structure is made to keep the reference in a complex objects *
|
||||||
|
* *
|
||||||
|
* Memory Management. *
|
||||||
|
* Cocoa is reference - counted library, thus the usage of an object *
|
||||||
|
* controlled natively by ref counting. *
|
||||||
|
* A bit trickier for pascal *
|
||||||
|
* Following rules are applied *
|
||||||
|
* * read-only property objects are freed by the host (obviously) *
|
||||||
|
* * other "freeing" operations are noted at "mmgr" comments *
|
||||||
|
* * any objects within key-value tables are freed *
|
||||||
|
* Alternative solution - implement ref counting! *
|
||||||
|
* *
|
||||||
|
--------------------------------------------------------------------------------}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils,
|
||||||
|
typinfo, pbxcontainer;
|
||||||
|
|
||||||
|
type
|
||||||
|
{ XCBuildConfiguration }
|
||||||
|
|
||||||
|
XCBuildConfiguration = class(PBXObject)
|
||||||
|
private
|
||||||
|
fname : string;
|
||||||
|
fbuildSettings: TPBXKeyValue;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
published
|
||||||
|
property buildSettings : TPBXKeyValue read fbuildSettings;
|
||||||
|
property name: string read fname write fname;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ XCConfigurationList }
|
||||||
|
|
||||||
|
// mmgr: XCConfigurationList frees
|
||||||
|
// * content of buildConfigurations
|
||||||
|
XCConfigurationList = class(PBXObject)
|
||||||
|
private
|
||||||
|
fdefaultConfigurationIsVisible: string;
|
||||||
|
fdefaultConfigurationName: string;
|
||||||
|
fbuildConfigurations: TPBXObjectsList;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
function addConfig(const aname: string): XCBuildConfiguration;
|
||||||
|
published
|
||||||
|
property buildConfigurations: TPBXObjectsList read fbuildConfigurations;
|
||||||
|
property defaultConfigurationIsVisible: string read fdefaultConfigurationIsVisible write fdefaultConfigurationIsVisible;
|
||||||
|
property defaultConfigurationName: string read fdefaultConfigurationName write fdefaultConfigurationName;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXContainerItemProxy }
|
||||||
|
|
||||||
|
PBXContainerItemProxy = class(PBXObject)
|
||||||
|
private
|
||||||
|
FcontainerPortal : PBXObject;
|
||||||
|
fproxyType : string;
|
||||||
|
fremoteGlobalIDString: string;
|
||||||
|
fremoteInfo: string;
|
||||||
|
published
|
||||||
|
property containerPortal: PBXObject read fcontainerPortal write fcontainerPortal; // Object = 0AFA6EA519F60EFD004C8FD9 /* Project object */;
|
||||||
|
property proxyType: string read FproxyType write fproxyType;
|
||||||
|
property remoteGlobalIDString: string read fremoteGlobalIDString write fremoteGlobalIDString; //object = 0AFA6EAC19F60EFD004C8FD9;
|
||||||
|
property remoteInfo : string read fremoteInfo write fremoteInfo; // ttestGame;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXFileReference }
|
||||||
|
|
||||||
|
PBXFileReference = class(PBXObject)
|
||||||
|
private
|
||||||
|
FexplicitFileType: string;
|
||||||
|
FincludeInIndex: string;
|
||||||
|
FlastKnownFileType: string;
|
||||||
|
Fname: string;
|
||||||
|
Fpath: string;
|
||||||
|
FsourceTree: string;
|
||||||
|
published
|
||||||
|
property explicitFileType: string read FexplicitFileType write FexplicitFileType;
|
||||||
|
property includeInIndex: string read FincludeInIndex write FincludeInIndex;
|
||||||
|
property lastKnownFileType: string read flastKnownFileType write flastKnownFileType;
|
||||||
|
property name: string read Fname write Fname;
|
||||||
|
property path: string read Fpath write Fpath;
|
||||||
|
property sourceTree: string read FsourceTree write FsourceTree;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXBuildFile }
|
||||||
|
|
||||||
|
PBXBuildFile = class(PBXObject)
|
||||||
|
private
|
||||||
|
fFileRef : PBXFileReference;
|
||||||
|
published
|
||||||
|
property fileRef : PBXFileReference read ffileRef write ffileRef; // obj
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXBuildPhase }
|
||||||
|
|
||||||
|
// mmgr: on free
|
||||||
|
// * content of files
|
||||||
|
PBXBuildPhase = class(PBXObject)
|
||||||
|
private
|
||||||
|
fbuildActionMask : Integer;
|
||||||
|
ffiles: TPBXObjectsList;
|
||||||
|
frunOnlyForDeploymentPostprocessing: string;
|
||||||
|
fname: string;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
published
|
||||||
|
property buildActionMask: Integer read fbuildActionMask write fbuildActionMask;
|
||||||
|
property files: TPBXObjectsList read ffiles;
|
||||||
|
property name: string read fname write fname;
|
||||||
|
property runOnlyForDeploymentPostprocessing: string read frunOnlyForDeploymentPostprocessing write frunOnlyForDeploymentPostprocessing;
|
||||||
|
end;
|
||||||
|
{ PBXFrameworksBuildPhase }
|
||||||
|
|
||||||
|
PBXFrameworksBuildPhase = class(PBXBuildPhase);
|
||||||
|
PBXResourcesBuildPhase = class(PBXBuildPhase);
|
||||||
|
PBXSourcesBuildPhase = class(PBXBuildPhase);
|
||||||
|
|
||||||
|
{ PBXShellScriptBuildPhase }
|
||||||
|
|
||||||
|
PBXShellScriptBuildPhase = class(PBXBuildPhase)
|
||||||
|
private
|
||||||
|
finputPaths: TPBXStringArray;
|
||||||
|
foutputPaths: TPBXStringArray;
|
||||||
|
fshellpath: string;
|
||||||
|
fshellScript: string;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
published
|
||||||
|
property inputPaths: TPBXStringArray read finputPaths;
|
||||||
|
property outputPaths: TPBXStringArray read foutputPaths;
|
||||||
|
property shellPath: string read fshellpath write fshellPath;
|
||||||
|
property shellScript: string read fshellScript write fshellScript;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXGroup }
|
||||||
|
|
||||||
|
// mmgt: PBXGroup owns children object (PBXGroup and PBXFileRefernece)
|
||||||
|
// and would free then on release;
|
||||||
|
// note, that PBXFileReference objects might be used in other places
|
||||||
|
PBXGroup = class(PBXObject)
|
||||||
|
private
|
||||||
|
fsourceTree : string;
|
||||||
|
fchildren: TPBXObjectsList;
|
||||||
|
fname: string;
|
||||||
|
fpath: string;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
function addSubGroup(const aname: string): PBXGroup;
|
||||||
|
published
|
||||||
|
property children: TPBXObjectsList read fchildren;
|
||||||
|
property name: string read fname write fname;
|
||||||
|
property path: string read fpath write fpath;
|
||||||
|
property sourceTree: string read fsourceTree write fsourceTree;
|
||||||
|
end;
|
||||||
|
|
||||||
|
PBXVariantGroup = class(PBXGroup);
|
||||||
|
|
||||||
|
{ PBXNativeTarget }
|
||||||
|
|
||||||
|
// mmgr: PBXNativeTarget
|
||||||
|
// * buildConfigurationList
|
||||||
|
// * contents of buildPhases
|
||||||
|
// * contents of buildRules
|
||||||
|
// * content of dependencies
|
||||||
|
PBXNativeTarget = class(PBXObject)
|
||||||
|
private
|
||||||
|
fbuildConfigurationList: XCConfigurationList;
|
||||||
|
fname : string;
|
||||||
|
fproductName : string;
|
||||||
|
fproductReference : PBXObject;
|
||||||
|
fproductType : string;
|
||||||
|
fbuildPhases : TPBXObjectsList;
|
||||||
|
fbuildRules : TPBXObjectsList;
|
||||||
|
fdependencies : TPBXObjectsList;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
published
|
||||||
|
property buildConfigurationList : XCConfigurationList read fbuildConfigurationList write fbuildConfigurationList; //= 0AFA6ED419F60F01004C8FD9 /* Build configuration list for PBXNativeTarget "ttestGame" */;
|
||||||
|
property buildPhases: TPBXObjectsList read fbuildPhases;
|
||||||
|
property buildRules: TPBXObjectsList read fbuildRules;
|
||||||
|
property dependencies: TPBXObjectsList read fdependencies;
|
||||||
|
property name: string read fname write fname;
|
||||||
|
property productName: string read fproductName write fproductName; // = ttestGame;
|
||||||
|
property productReference: PBXObject read fproductReference write fproductReference; // = 0AFA6EAD19F60EFE004C8FD9 /* ttestGame.app */;
|
||||||
|
property productType: string read fproductType write fproductType; // = "com.apple.product-type.application";
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXTargetDependency }
|
||||||
|
|
||||||
|
// mmgt:
|
||||||
|
// targetProxy - is freed
|
||||||
|
PBXTargetDependency = class(PBXObject)
|
||||||
|
private
|
||||||
|
ftargetProxy: PBXContainerItemProxy;
|
||||||
|
ftarget: PBXNativeTarget;
|
||||||
|
public
|
||||||
|
destructor Destroy; override;
|
||||||
|
published
|
||||||
|
property target : PBXNativeTarget read ftarget write ftarget;
|
||||||
|
property targetProxy: PBXContainerItemProxy read ftargetProxy write ftargetProxy; {* PBXContainerItemProxy *}
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXProject }
|
||||||
|
|
||||||
|
// mmgt: PBXProject frees the following property objects, if assigned:
|
||||||
|
// * mainGroup
|
||||||
|
// * buildConfigurationList
|
||||||
|
// * contents of targets
|
||||||
|
PBXProject = class(PBXObject)
|
||||||
|
private
|
||||||
|
fattributes : TPBXKeyValue;
|
||||||
|
fcompatibilityVersion : string;
|
||||||
|
fdevelopmentRegion : string;
|
||||||
|
fhasScannedForEncodings: string;
|
||||||
|
fmainGroup: PBXGroup;
|
||||||
|
fknownRegions: TPBXStringArray;
|
||||||
|
fproductRefGroup: PBXGroup;
|
||||||
|
fprojectDirPath: string;
|
||||||
|
fprojectRoot : string;
|
||||||
|
ftargets: TPBXObjectsList;
|
||||||
|
fbuildConfigurationList: XCConfigurationList;
|
||||||
|
protected
|
||||||
|
class procedure _WriteEmpty(propnames: TStrings); override;
|
||||||
|
public
|
||||||
|
constructor Create; override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
function addTarget(const aname: string): PBXNativeTarget;
|
||||||
|
published
|
||||||
|
property attributes: TPBXKeyValue read fattributes;
|
||||||
|
property buildConfigurationList: XCConfigurationList read fbuildConfigurationList write fbuildConfigurationList;
|
||||||
|
property compatibilityVersion: string read fcompatibilityVersion write fcompatibilityVersion;
|
||||||
|
property developmentRegion: string read fdevelopmentRegion write fdevelopmentRegion;
|
||||||
|
property hasScannedForEncodings: string read fhasScannedForEncodings write fhasScannedForEncodings;
|
||||||
|
property knownRegions: TPBXStringArray read fknownRegions;
|
||||||
|
property mainGroup: PBXGroup read fmainGroup write fmainGroup;
|
||||||
|
property productRefGroup: PBXGroup read fproductRefGroup write fproductRefGroup;
|
||||||
|
property projectDirPath: string read fprojectDirPath write fprojectDirPath;
|
||||||
|
property projectRoot: string read fprojectRoot write fprojectRoot;
|
||||||
|
property targets: TPBXObjectsList read ftargets;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function LoadProjectFromStream(st: TStream; var prj: PBXProject): Boolean;
|
||||||
|
function LoadProjectFromFile(const fn: string; var prj: PBXProject): Boolean;
|
||||||
|
|
||||||
|
function ProjectWrite(prj: PBXProject): string;
|
||||||
|
|
||||||
|
function CreateMinProject: PBXProject;
|
||||||
|
function ProjectAddTarget(prj: PBXProject; const ATargetName: string): PBXNativeTarget;
|
||||||
|
|
||||||
|
const
|
||||||
|
SCRIPT_RUNPATH = '/bin/sh';
|
||||||
|
SCRIPT_DEFAULT = '';
|
||||||
|
SCRIPT_DEFNAME = 'Run Script';
|
||||||
|
|
||||||
|
function TargetAddRunScript(atarget: PBXNativeTarget): PBXShellScriptBuildPhase;
|
||||||
|
|
||||||
|
const
|
||||||
|
//FILETYPE_SCRIPT = 'text.script.sh';
|
||||||
|
FILETYPE_EXEC = 'compiled.mach-o.executable';
|
||||||
|
FILETYPE_MACHO = FILETYPE_EXEC;
|
||||||
|
|
||||||
|
function CreateFileRef(const afilename: string; const filetype: string = ''): PBXFileReference;
|
||||||
|
|
||||||
|
const
|
||||||
|
GROUPSRC_ABSOLUTE = '<absolute>';
|
||||||
|
GROUPSRC_GROUP = '<group>';
|
||||||
|
|
||||||
|
function CreateGroup(const aname: string; const srcTree: string = GROUPSRC_GROUP): PBXGroup;
|
||||||
|
//todo: need a rountine to update the path whenever the project is saved
|
||||||
|
function CreateRootGroup(const projectfolder: string): PBXGroup;
|
||||||
|
|
||||||
|
const
|
||||||
|
PRODTYPE_TOOL = 'com.apple.product-type.tool';
|
||||||
|
|
||||||
|
//
|
||||||
|
// PBXSourcesBuildPhase (sources) - is part of a PBXNativeTarget
|
||||||
|
// PBXNativeTarget - is part of Target
|
||||||
|
// PBXNativeTarget
|
||||||
|
//buildPhases = (
|
||||||
|
//0AA67B651A04929900CF0DD7 /* Sources */,
|
||||||
|
//0AA67B661A04929900CF0DD7 /* Frameworks */,
|
||||||
|
//0AA67B671A04929900CF0DD7 /* CopyFiles */,
|
||||||
|
//);
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ PBXTargetDependency }
|
||||||
|
|
||||||
|
destructor PBXTargetDependency.Destroy;
|
||||||
|
begin
|
||||||
|
ftargetProxy.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXShellScriptBuildPhase }
|
||||||
|
|
||||||
|
constructor PBXShellScriptBuildPhase.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
finputPaths:=TPBXStringArray.Create;
|
||||||
|
foutputPaths:=TPBXStringArray.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor PBXShellScriptBuildPhase.Destroy;
|
||||||
|
begin
|
||||||
|
finputPaths.Free;
|
||||||
|
foutputPaths.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXNativeTarget }
|
||||||
|
|
||||||
|
constructor PBXNativeTarget.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
fbuildPhases := TPBXObjectsList.Create(true);
|
||||||
|
fdependencies := TPBXObjectsList.Create(true);
|
||||||
|
fbuildRules := TPBXObjectsList.Create(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor PBXNativeTarget.Destroy;
|
||||||
|
begin
|
||||||
|
fbuildConfigurationList.Free;
|
||||||
|
fbuildRules.Free;
|
||||||
|
fbuildPhases.Free;
|
||||||
|
fdependencies.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXProject }
|
||||||
|
|
||||||
|
class procedure PBXProject._WriteEmpty(propnames: TStrings);
|
||||||
|
begin
|
||||||
|
propnames.Add('projectDirPath');
|
||||||
|
propnames.Add('projectRoot');
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor PBXProject.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
ftargets:=TPBXObjectsList.create(true);
|
||||||
|
fknownRegions:=TPBXStringArray.Create;
|
||||||
|
fattributes:=TPBXKeyValue.Create(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor PBXProject.Destroy;
|
||||||
|
begin
|
||||||
|
fattributes.Free;
|
||||||
|
fknownRegions.Free;
|
||||||
|
ftargets.Free;
|
||||||
|
|
||||||
|
fmainGroup.Free;
|
||||||
|
fbuildConfigurationList.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXProject.addTarget(const aname: string): PBXNativeTarget;
|
||||||
|
begin
|
||||||
|
Result:=PBXNativeTarget.Create;
|
||||||
|
targets.Add(Result);
|
||||||
|
Result._headerComment:=aname;
|
||||||
|
Result.name:=aname;
|
||||||
|
// productName?
|
||||||
|
// productReference - is a resulting file
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ XCConfigurationList }
|
||||||
|
|
||||||
|
constructor XCConfigurationList.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
fbuildConfigurations:=TPBXObjectsList.Create(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor XCConfigurationList.Destroy;
|
||||||
|
begin
|
||||||
|
fbuildConfigurations.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function XCConfigurationList.AddConfig(const aname: string): XCBuildConfiguration;
|
||||||
|
begin
|
||||||
|
Result:=XCBuildConfiguration.Create;
|
||||||
|
Result.name:=aname;
|
||||||
|
Result._headerComment:=aname;
|
||||||
|
fbuildConfigurations.Add(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ XCBuildConfiguration }
|
||||||
|
|
||||||
|
constructor XCBuildConfiguration.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
fbuildSettings:=TPBXKeyValue.Create(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor XCBuildConfiguration.Destroy;
|
||||||
|
begin
|
||||||
|
fbuildSettings.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXGroup }
|
||||||
|
|
||||||
|
constructor PBXGroup.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
fchildren:=TPBXObjectsList.Create(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor PBXGroup.Destroy;
|
||||||
|
begin
|
||||||
|
fchildren.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PBXGroup.addSubGroup(const aname: string): PBXGroup;
|
||||||
|
begin
|
||||||
|
Result:=PBXGroup.Create;
|
||||||
|
fchildren.Add(Result);
|
||||||
|
Result.name:=aname;
|
||||||
|
Result._headerComment:=aname;
|
||||||
|
Result.sourceTree:=GROUPSRC_GROUP;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ PBXBuildPhase }
|
||||||
|
|
||||||
|
constructor PBXBuildPhase.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
ffiles:=TPBXObjectsList.Create(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor PBXBuildPhase.Destroy;
|
||||||
|
begin
|
||||||
|
ffiles.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function LoadProjectFromStream(st: TStream; var prj: PBXProject): Boolean;
|
||||||
|
var
|
||||||
|
c : TPBXContainer;
|
||||||
|
info : TPBXFileInfo;
|
||||||
|
begin
|
||||||
|
prj:=nil;
|
||||||
|
c:= TPBXContainer.Create;
|
||||||
|
try
|
||||||
|
Result:=c.ReadFile(st, info);
|
||||||
|
if Result then begin
|
||||||
|
if not (info.rootObject is PBXProject) then begin
|
||||||
|
info.rootObject.Free;
|
||||||
|
end else begin
|
||||||
|
prj:=PBXProject(info.rootObject);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
finally
|
||||||
|
c.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function LoadProjectFromFile(const fn: string; var prj: PBXProject): Boolean;
|
||||||
|
var
|
||||||
|
fs :TFileStream;
|
||||||
|
begin
|
||||||
|
fs:=TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
|
||||||
|
try
|
||||||
|
Result:=LoadProjectFromStream(fs, prj);
|
||||||
|
finally
|
||||||
|
fs.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ProjectWrite(prj: PBXProject): string;
|
||||||
|
var
|
||||||
|
info : TPBXFileInfo;
|
||||||
|
begin
|
||||||
|
info.archiveVersion:='1';
|
||||||
|
info.objectVersion:='46';
|
||||||
|
info.rootObject:=prj;
|
||||||
|
Result:=PBXWriteContainer(info);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CreateMinProject: PBXProject;
|
||||||
|
var
|
||||||
|
p : PBXProject;
|
||||||
|
cfg : XCBuildConfiguration;
|
||||||
|
begin
|
||||||
|
// requirements:
|
||||||
|
// * at least one build configuration
|
||||||
|
p := PBXProject.Create;
|
||||||
|
p._headerComment:='Project object';
|
||||||
|
|
||||||
|
p.buildConfigurationList:=XCConfigurationList.Create;
|
||||||
|
p.buildConfigurationList._headerComment:='Build configuration list';
|
||||||
|
p.buildConfigurationList.defaultConfigurationIsVisible:='0';
|
||||||
|
|
||||||
|
cfg:=p.buildConfigurationList.addConfig('Default');
|
||||||
|
cfg:=p.buildConfigurationList.addConfig('Release');
|
||||||
|
// default name must be present
|
||||||
|
p.buildConfigurationList.defaultConfigurationName:='Release';
|
||||||
|
Result:=p;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ProjectAddTarget(prj: PBXProject; const ATargetName: string): PBXNativeTarget;
|
||||||
|
begin
|
||||||
|
Result:=nil;
|
||||||
|
if not Assigned(prj) then Exit;
|
||||||
|
Result:=prj.addTarget(ATargetName);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TargetAddRunScript(atarget: PBXNativeTarget): PBXShellScriptBuildPhase;
|
||||||
|
begin
|
||||||
|
Result:=PBXShellScriptBuildPhase.Create;
|
||||||
|
Result.name:=SCRIPT_DEFNAME;
|
||||||
|
Result._headerComment:=SCRIPT_DEFNAME;
|
||||||
|
Result.shellScript:=SCRIPT_DEFAULT;
|
||||||
|
Result.shellPath:=SCRIPT_RUNPATH;
|
||||||
|
atarget.buildPhases.Add(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CreateFileRef(const afilename: string; const filetype: string ): PBXFileReference;
|
||||||
|
begin
|
||||||
|
Result:=PBXFileReference.Create;
|
||||||
|
Result.path:=afilename;
|
||||||
|
Result._headerComment:=afilename;
|
||||||
|
Result.explicitFileType:=FILETYPE_EXEC;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CreateGroup(const aname, srcTree: string): PBXGroup;
|
||||||
|
begin
|
||||||
|
Result:=PBXGroup.Create;
|
||||||
|
Result.name:=aname;
|
||||||
|
Result.sourceTree:=srcTree;
|
||||||
|
Result._headerComment:=aname;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CreateRootGroup(const projectfolder: string): PBXGroup;
|
||||||
|
begin
|
||||||
|
Result:=CreateGroup('', GROUPSRC_ABSOLUTE);
|
||||||
|
Result.path:=projectfolder;
|
||||||
|
Result._headerComment:=projectfolder;
|
||||||
|
end;
|
||||||
|
|
||||||
|
initialization
|
||||||
|
PBXRegisterClass(PBXBuildFile);
|
||||||
|
PBXRegisterClass(PBXContainerItemProxy);
|
||||||
|
PBXRegisterClass(PBXFileReference);
|
||||||
|
PBXRegisterClass(PBXFrameworksBuildPhase);
|
||||||
|
PBXRegisterClass(PBXGroup);
|
||||||
|
PBXRegisterClass(PBXNativeTarget);
|
||||||
|
PBXRegisterClass(PBXProject);
|
||||||
|
PBXRegisterClass(PBXResourcesBuildPhase);
|
||||||
|
PBXRegisterClass(PBXSourcesBuildPhase);
|
||||||
|
PBXRegisterClass(PBXTargetDependency);
|
||||||
|
PBXRegisterClass(PBXVariantGroup);
|
||||||
|
PBXRegisterClass(XCBuildConfiguration);
|
||||||
|
PBXRegisterClass(XCConfigurationList);
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user