{ Copyright (c) 2013-2016 by Free Pascal development team This unit implements the loading and searching of package files This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit fpcp; {$i fpcdefs.inc} interface uses cclasses,cstreams, globtype, pcp,finput,fpkg; type tpcppackage=class(tpackage) private loaded : boolean; pcpfile : tpcpfile; private function openpcp:boolean; function search_package(ashortname:boolean):boolean; function search_package_file:boolean; procedure setfilename(const fn:string;allowoutput:boolean); procedure writecontainernames; procedure writecontainedunits; procedure writerequiredpackages; procedure writepputable; procedure writeppudata; procedure readcontainernames; procedure readcontainedunits; procedure readrequiredpackages; procedure readpputable; public constructor create(const pn:string); destructor destroy; override; procedure loadpcp; procedure savepcp; function getmodulestream(module:tmodulebase):tcstream; procedure initmoduleinfo(module:tmodulebase); procedure addunit(module:tmodulebase); procedure add_required_package(pkg:tpackage); end; implementation uses sysutils, cfileutl,cutils, systems,globals,version, verbose, ppu, entfile,pkgutil; { tpcppackage } function tpcppackage.openpcp: boolean; var pcpfiletime : longint; begin result:=false; Message1(package_t_pcp_loading,pcpfilename); { Get pcpfile time (also check if the file exists) } pcpfiletime:=getnamedfiletime(pcpfilename); if pcpfiletime=-1 then exit; { Open the pcpfile } Message1(package_u_pcp_name,pcpfilename); pcpfile:=tpcpfile.create(pcpfilename); if not pcpfile.openfile then begin pcpfile.free; pcpfile:=nil; Message(package_u_pcp_file_too_short); exit; end; { check for a valid PPU file } if not pcpfile.checkpcpid then begin pcpfile.free; pcpfile:=nil; Message(package_u_pcp_invalid_header); exit; end; { check for allowed PCP versions } if not (pcpfile.getversion=CurrentPCPVersion) then begin Message1(package_u_pcp_invalid_version,tostr(pcpfile.getversion)); pcpfile.free; pcpfile:=nil; exit; end; { check the target processor } if tsystemcpu(pcpfile.header.common.cpu)<>target_cpu then begin pcpfile.free; pcpfile:=nil; Message(package_u_pcp_invalid_processor); exit; end; { check target } if tsystem(pcpfile.header.common.target)<>target_info.system then begin pcpfile.free; pcpfile:=nil; Message(package_u_pcp_invalid_target); exit; end; {$ifdef cpufpemu} { check if floating point emulation is on? fpu emulation isn't unit levelwise because it affects calling convention } if ((uf_fpu_emulation and pcpfile.header.common.flags)<>0) <> (cs_fp_emulation in current_settings.moduleswitches) then begin pcpfile.free; pcpfile:=nil; Message(package_u_pcp_invalid_fpumode); exit; end; {$endif cpufpemu} { Show Debug info } Message1(package_u_pcp_time,filetimestring(pcpfiletime)); Message1(package_u_pcp_flags,tostr(pcpfile.header.common.flags{flags})); Message1(package_u_pcp_crc,hexstr(pcpfile.header.checksum,8)); (*Message1(package_u_pcp_crc,hexstr(ppufile.header.interface_checksum,8)+' (intfc)'); Message1(package_u_pcp_crc,hexstr(ppufile.header.indirect_checksum,8)+' (indc)'); Comment(V_used,'Number of definitions: '+tostr(ppufile.header.deflistsize)); Comment(V_used,'Number of symbols: '+tostr(ppufile.header.symlistsize)); do_compile:=false;*) result:=true; end; function tpcppackage.search_package(ashortname:boolean):boolean; var singlepathstring, filename : TCmdStr; function package_exists(const ext:string;var foundfile:TCmdStr):boolean; begin if CheckVerbosity(V_Tried) then Message1(package_t_packagesearch,Singlepathstring+filename+ext); result:=FindFile(filename+ext,singlepathstring,true,foundfile); end; function package_search_path(const s:TCmdStr):boolean; var found : boolean; hs : TCmdStr; begin found:=false; singlepathstring:=FixPath(s,false); { Check for package file } { TODO } found:=package_exists({target_info.pkginfoext}'.pcp',hs); if found then begin setfilename(hs,false); found:=openpcp; end; result:=found; end; function search_path_list(list:TSearchPathList):boolean; var hp : TCmdStrListItem; found : boolean; begin found:=false; hp:=TCmdStrListItem(list.First); while assigned(hp) do begin found:=package_search_path(hp.Str); if found then break; hp:=TCmdStrListItem(hp.next); end; result:=found; end; begin filename:=realpackagename^; result:=search_path_list(packagesearchpath); end; function tpcppackage.search_package_file: boolean; var found : boolean; begin found:=false; if search_package(false) then found:=true; if not found and (length(packagename^)>8) and search_package(true) then found:=true; result:=found; end; procedure tpcppackage.setfilename(const fn:string;allowoutput:boolean); var p,n : tpathstr; begin p:=FixPath(ExtractFilePath(fn),false); n:=FixFileName(ChangeFileExt(ExtractFileName(fn),'')); { pcp name } if allowoutput then if (OutputUnitDir<>'') then p:=OutputUnitDir else if (OutputExeDir<>'') then p:=OutputExeDir; pcpfilename:=p+n+{target_info.pkginfoext}'.pcp'; end; procedure tpcppackage.writecontainernames; begin pcpfile.putstring(pplfilename); //pcpfile.putstring(ppafilename); pcpfile.writeentry(ibpackagefiles); end; procedure tpcppackage.writecontainedunits; var p : pcontainedunit; i : longint; begin pcpfile.putlongint(containedmodules.count); pcpfile.writeentry(ibstartcontained); { for now we write the unit name and the ppu file name } for i:=0 to containedmodules.count-1 do begin p:=pcontainedunit(containedmodules.items[i]); pcpfile.putstring(p^.module.modulename^); pcpfile.putstring(p^.ppufile); end; pcpfile.writeentry(ibendcontained); end; procedure tpcppackage.writerequiredpackages; var i : longint; begin pcpfile.putlongint(requiredpackages.count); pcpfile.writeentry(ibstartrequireds); for i:=0 to requiredpackages.count-1 do begin pcpfile.putstring(requiredpackages.NameOfIndex(i)); end; pcpfile.writeentry(ibendrequireds); end; procedure tpcppackage.writepputable; var module : pcontainedunit; i : longint; begin { no need to write the count again; it's the same as for the contained units } for i:=0 to containedmodules.count-1 do begin module:=pcontainedunit(containedmodules[i]); pcpfile.putlongint(module^.offset); pcpfile.putlongint(module^.size); end; pcpfile.writeentry(ibpputable); end; procedure tpcppackage.writeppudata; const align: array[0..15] of byte = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); var i, pos, rem : longint; module : pcontainedunit; stream : TCStream; begin pcpfile.flush; for i:=0 to containedmodules.count-1 do begin module:=pcontainedunit(containedmodules[i]); pos:=pcpfile.position; { align to 16 byte so that it can be nicely viewed in hex editors; maybe we could also use 512 byte alignment instead } rem:=$f-(pos and $f); pcpfile.stream.write(align[0],rem+1); pcpfile.flush; module^.offset:=pcpfile.position; { retrieve substream for the current position } stream:=pcpfile.substream(module^.offset,-1); rewriteppu(module^.module.ppufilename,stream); module^.size:=stream.position; stream.free; end; pos:=pcpfile.position; { align to 16 byte so that it can be nicely viewed in hex editors; maybe we could also use 512 byte alignment instead } rem:=$f-(pos and $f); pcpfile.stream.write(align[0],rem+1); end; procedure tpcppackage.readcontainernames; begin if pcpfile.readentry<>ibpackagefiles then begin message(package_f_pcp_read_error); internalerror(2020100818); end; pplfilename:=pcpfile.getstring; message1(package_u_ppl_filename,pplfilename); end; procedure tpcppackage.readcontainedunits; var cnt,i : longint; name,path : string; p : pcontainedunit; begin if pcpfile.readentry<>ibstartcontained then begin message(package_f_pcp_read_error); internalerror(2020100819); end; cnt:=pcpfile.getlongint; if pcpfile.readentry<>ibendcontained then begin message(package_f_pcp_read_error); internalerror(2020100820); end; for i:=0 to cnt-1 do begin name:=pcpfile.getstring; path:=pcpfile.getstring; new(p); p^.module:=nil; p^.ppufile:=path; p^.offset:=0; p^.size:=0; containedmodules.add(name,p); message1(package_u_contained_unit,name); end; end; procedure tpcppackage.readrequiredpackages; var cnt,i : longint; name : string; begin if pcpfile.readentry<>ibstartrequireds then begin message(package_f_pcp_read_error); internalerror(2014110901); end; cnt:=pcpfile.getlongint; if pcpfile.readentry<>ibendrequireds then begin message(package_f_pcp_read_error); internalerror(2014110902); end; for i:=0 to cnt-1 do begin name:=pcpfile.getstring; requiredpackages.add(name,nil); message1(package_u_required_package,name); end; end; procedure tpcppackage.readpputable; var module : pcontainedunit; i : longint; begin if pcpfile.readentry<>ibpputable then begin message(package_f_pcp_read_error); internalerror(2015103001); end; for i:=0 to containedmodules.count-1 do begin module:=pcontainedunit(containedmodules[i]); module^.offset:=pcpfile.getlongint; module^.size:=pcpfile.getlongint; end; end; constructor tpcppackage.create(const pn: string); begin inherited create(pn); setfilename(pn+'.ppk',true); end; destructor tpcppackage.destroy; begin pcpfile.free; inherited destroy; end; procedure tpcppackage.loadpcp; var newpackagename : string; begin if loaded then exit; if not search_package_file then begin Message1(package_f_cant_find_pcp,realpackagename^); exit; end else Message1(package_u_pcp_found,realpackagename^); if not assigned(pcpfile) then internalerror(2013053101); if pcpfile.readentry<>ibpackagename then Message1(package_f_cant_read_pcp,realpackagename^); newpackagename:=pcpfile.getstring; if upper(newpackagename)<>packagename^ then Comment(V_Error,'Package was renamed: '+realpackagename^); readcontainernames; readrequiredpackages; readcontainedunits; readpputable; end; procedure tpcppackage.savepcp; var tablepos, oldpos : longint; begin { create new ppufile } pcpfile:=tpcpfile.create(pcpfilename); if not pcpfile.createfile then Message2(package_f_cant_create_pcp,realpackagename^,pcpfilename); pcpfile.putstring(realpackagename^); pcpfile.writeentry(ibpackagename); writecontainernames; writerequiredpackages; writecontainedunits; { the offsets and the contents of the ppus are not crc'd } pcpfile.do_crc:=false; pcpfile.flush; tablepos:=pcpfile.position; { this will write a table with empty entries } writepputable; pcpfile.do_crc:=true; { the last entry ibend is written automatically } { flush to be sure } pcpfile.flush; { create and write header } pcpfile.header.common.size:=pcpfile.size; pcpfile.header.checksum:=pcpfile.crc; pcpfile.header.common.compiler:=wordversion; pcpfile.header.common.cpu:=word(target_cpu); pcpfile.header.common.target:=word(target_info.system); //pcpfile.header.flags:=flags; pcpfile.header.ppulistsize:=containedmodules.count; pcpfile.header.requiredlistsize:=requiredpackages.count; pcpfile.writeheader; { write the ppu table which will also fill the offsets/sizes } writeppudata; pcpfile.flush; oldpos:=pcpfile.position; { now write the filled PPU table at the previously stored position } pcpfile.position:=tablepos; writepputable; pcpfile.position:=oldpos; { save crc in current module also } //crc:=pcpfile.crc; pcpfile.closefile; pcpfile.free; pcpfile:=nil; end; function tpcppackage.getmodulestream(module:tmodulebase):tcstream; var i : longint; contained : pcontainedunit; begin for i:=0 to containedmodules.count-1 do begin contained:=pcontainedunit(containedmodules[i]); if contained^.module=module then begin result:=pcpfile.substream(contained^.offset,contained^.size); exit; end; end; result:=nil; end; procedure tpcppackage.initmoduleinfo(module: tmodulebase); begin pplfilename:=extractfilename(module.sharedlibfilename); end; procedure tpcppackage.addunit(module: tmodulebase); var containedunit : pcontainedunit; begin new(containedunit); containedunit^.module:=module; containedunit^.ppufile:=extractfilename(module.ppufilename); containedunit^.offset:=0; containedunit^.size:=0; containedmodules.add(module.modulename^,containedunit); end; procedure tpcppackage.add_required_package(pkg:tpackage); var p : tpackage; begin p:=tpackage(requiredpackages.find(pkg.packagename^)); if not assigned(p) then requiredpackages.Add(pkg.packagename^,pkg) else if p<>pkg then internalerror(2015112302); end; end.