mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 22:08:11 +02:00
570 lines
16 KiB
ObjectPascal
570 lines
16 KiB
ObjectPascal
{
|
|
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.
|
|
|