fpcp.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. {
  2. Copyright (c) 2013-2014 by Free Pascal development team
  3. This unit implements the loading and searching of package files
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit fpcp;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,cstreams,
  22. globtype,
  23. pcp,finput,fpkg;
  24. type
  25. tpcppackage=class(tpackage)
  26. private
  27. loaded : boolean;
  28. pcpfile : tpcpfile;
  29. private
  30. function openpcp:boolean;
  31. function search_package(ashortname:boolean):boolean;
  32. function search_package_file:boolean;
  33. procedure setfilename(const fn:string;allowoutput:boolean);
  34. procedure writecontainernames;
  35. procedure writecontainedunits;
  36. procedure writerequiredpackages;
  37. procedure writepputable;
  38. procedure writeppudata;
  39. procedure readcontainernames;
  40. procedure readcontainedunits;
  41. procedure readrequiredpackages;
  42. procedure readpputable;
  43. public
  44. constructor create(const pn:string);
  45. destructor destroy; override;
  46. procedure loadpcp;
  47. procedure savepcp;
  48. function getmodulestream(module:tmodulebase):tcstream;
  49. procedure initmoduleinfo(module:tmodulebase);
  50. procedure addunit(module:tmodulebase);
  51. procedure add_required_package(pkg:tpackage);
  52. end;
  53. implementation
  54. uses
  55. sysutils,
  56. cfileutl,cutils,
  57. systems,globals,version,
  58. verbose,
  59. entfile,fppu,pkgutil;
  60. { tpcppackage }
  61. function tpcppackage.openpcp: boolean;
  62. var
  63. pcpfiletime : longint;
  64. begin
  65. result:=false;
  66. Message1(package_t_pcp_loading,pcpfilename);
  67. { Get pcpfile time (also check if the file exists) }
  68. pcpfiletime:=getnamedfiletime(pcpfilename);
  69. if pcpfiletime=-1 then
  70. exit;
  71. { Open the pcpfile }
  72. Message1(package_u_pcp_name,pcpfilename);
  73. pcpfile:=tpcpfile.create(pcpfilename);
  74. if not pcpfile.openfile then
  75. begin
  76. pcpfile.free;
  77. pcpfile:=nil;
  78. Message(package_u_pcp_file_too_short);
  79. exit;
  80. end;
  81. { check for a valid PPU file }
  82. if not pcpfile.checkpcpid then
  83. begin
  84. pcpfile.free;
  85. pcpfile:=nil;
  86. Message(package_u_pcp_invalid_header);
  87. exit;
  88. end;
  89. { check for allowed PCP versions }
  90. if not (pcpfile.getversion=CurrentPCPVersion) then
  91. begin
  92. Message1(package_u_pcp_invalid_version,tostr(pcpfile.getversion));
  93. pcpfile.free;
  94. pcpfile:=nil;
  95. exit;
  96. end;
  97. { check the target processor }
  98. if tsystemcpu(pcpfile.header.common.cpu)<>target_cpu then
  99. begin
  100. pcpfile.free;
  101. pcpfile:=nil;
  102. Message(package_u_pcp_invalid_processor);
  103. exit;
  104. end;
  105. { check target }
  106. if tsystem(pcpfile.header.common.target)<>target_info.system then
  107. begin
  108. pcpfile.free;
  109. pcpfile:=nil;
  110. Message(package_u_pcp_invalid_target);
  111. exit;
  112. end;
  113. {$ifdef cpufpemu}
  114. { check if floating point emulation is on?
  115. fpu emulation isn't unit levelwise because it affects calling convention }
  116. if ((pcpfile.header.common.flags and uf_fpu_emulation)<>0) xor
  117. (cs_fp_emulation in current_settings.moduleswitches) then
  118. begin
  119. pcpfile.free;
  120. pcpfile:=nil;
  121. Message(package_u_pcp_invalid_fpumode);
  122. exit;
  123. end;
  124. {$endif cpufpemu}
  125. { Load values to be access easier }
  126. //flags:=pcpfile.header.common.flags;
  127. //crc:=pcpfile.header.checksum;
  128. { Show Debug info }
  129. Message1(package_u_pcp_time,filetimestring(pcpfiletime));
  130. Message1(package_u_pcp_flags,tostr(pcpfile.header.common.flags{flags}));
  131. Message1(package_u_pcp_crc,hexstr(pcpfile.header.checksum,8));
  132. (*Message1(package_u_pcp_crc,hexstr(ppufile.header.interface_checksum,8)+' (intfc)');
  133. Message1(package_u_pcp_crc,hexstr(ppufile.header.indirect_checksum,8)+' (indc)');
  134. Comment(V_used,'Number of definitions: '+tostr(ppufile.header.deflistsize));
  135. Comment(V_used,'Number of symbols: '+tostr(ppufile.header.symlistsize));
  136. do_compile:=false;*)
  137. result:=true;
  138. end;
  139. function tpcppackage.search_package(ashortname:boolean):boolean;
  140. var
  141. singlepathstring,
  142. filename : TCmdStr;
  143. function package_exists(const ext:string;var foundfile:TCmdStr):boolean;
  144. begin
  145. if CheckVerbosity(V_Tried) then
  146. Message1(package_t_packagesearch,Singlepathstring+filename+ext);
  147. result:=FindFile(filename+ext,singlepathstring,true,foundfile);
  148. end;
  149. function package_search_path(const s:TCmdStr):boolean;
  150. var
  151. found : boolean;
  152. hs : TCmdStr;
  153. begin
  154. found:=false;
  155. singlepathstring:=FixPath(s,false);
  156. { Check for package file }
  157. { TODO }
  158. found:=package_exists({target_info.pkginfoext}'.pcp',hs);
  159. if found then
  160. begin
  161. setfilename(hs,false);
  162. found:=openpcp;
  163. end;
  164. result:=found;
  165. end;
  166. function search_path_list(list:TSearchPathList):boolean;
  167. var
  168. hp : TCmdStrListItem;
  169. found : boolean;
  170. begin
  171. found:=false;
  172. hp:=TCmdStrListItem(list.First);
  173. while assigned(hp) do
  174. begin
  175. found:=package_search_path(hp.Str);
  176. if found then
  177. break;
  178. hp:=TCmdStrListItem(hp.next);
  179. end;
  180. result:=found;
  181. end;
  182. begin
  183. filename:=packagename^;
  184. result:=search_path_list(packagesearchpath);
  185. end;
  186. function tpcppackage.search_package_file: boolean;
  187. var
  188. found : boolean;
  189. begin
  190. found:=false;
  191. if search_package(false) then
  192. found:=true;
  193. if not found and
  194. (length(packagename^)>8) and
  195. search_package(true) then
  196. found:=true;
  197. result:=found;
  198. end;
  199. procedure tpcppackage.setfilename(const fn:string;allowoutput:boolean);
  200. var
  201. p,n : tpathstr;
  202. begin
  203. p:=FixPath(ExtractFilePath(fn),false);
  204. n:=FixFileName(ChangeFileExt(ExtractFileName(fn),''));
  205. { pcp name }
  206. if allowoutput then
  207. if (OutputUnitDir<>'') then
  208. p:=OutputUnitDir
  209. else
  210. if (OutputExeDir<>'') then
  211. p:=OutputExeDir;
  212. pcpfilename:=p+n+{target_info.pkginfoext}'.pcp';
  213. end;
  214. procedure tpcppackage.writecontainernames;
  215. begin
  216. pcpfile.putstring(pplfilename);
  217. //pcpfile.putstring(ppafilename);
  218. pcpfile.writeentry(ibpackagefiles);
  219. end;
  220. procedure tpcppackage.writecontainedunits;
  221. var
  222. p : pcontainedunit;
  223. i : longint;
  224. begin
  225. pcpfile.putlongint(containedmodules.count);
  226. pcpfile.writeentry(ibstartcontained);
  227. { for now we write the unit name and the ppu file name }
  228. for i:=0 to containedmodules.count-1 do
  229. begin
  230. p:=pcontainedunit(containedmodules.items[i]);
  231. pcpfile.putstring(p^.module.modulename^);
  232. pcpfile.putstring(p^.ppufile);
  233. end;
  234. pcpfile.writeentry(ibendcontained);
  235. end;
  236. procedure tpcppackage.writerequiredpackages;
  237. var
  238. i : longint;
  239. begin
  240. pcpfile.putlongint(requiredpackages.count);
  241. pcpfile.writeentry(ibstartrequireds);
  242. for i:=0 to requiredpackages.count-1 do
  243. begin
  244. pcpfile.putstring(requiredpackages.NameOfIndex(i));
  245. end;
  246. pcpfile.writeentry(ibendrequireds);
  247. end;
  248. procedure tpcppackage.writepputable;
  249. var
  250. module : pcontainedunit;
  251. i : longint;
  252. begin
  253. { no need to write the count again; it's the same as for the contained units }
  254. for i:=0 to containedmodules.count-1 do
  255. begin
  256. module:=pcontainedunit(containedmodules[i]);
  257. pcpfile.putlongint(module^.offset);
  258. pcpfile.putlongint(module^.size);
  259. end;
  260. pcpfile.writeentry(ibpputable);
  261. end;
  262. procedure tpcppackage.writeppudata;
  263. const
  264. align: array[0..15] of byte = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
  265. var
  266. i,j,
  267. pos,
  268. rem : longint;
  269. module : pcontainedunit;
  270. stream : TCStream;
  271. begin
  272. pcpfile.flush;
  273. for i:=0 to containedmodules.count-1 do
  274. begin
  275. module:=pcontainedunit(containedmodules[i]);
  276. pos:=pcpfile.position;
  277. { align to 16 byte so that it can be nicely viewed in hex editors;
  278. maybe we could also use 512 byte alignment instead }
  279. rem:=$f-(pos and $f);
  280. pcpfile.stream.write(align[0],rem+1);
  281. pcpfile.flush;
  282. module^.offset:=pcpfile.position;
  283. { retrieve substream for the current position }
  284. stream:=pcpfile.substream(module^.offset,-1);
  285. rewriteppu(module^.module.ppufilename,stream);
  286. module^.size:=stream.position;
  287. stream.free;
  288. end;
  289. pos:=pcpfile.position;
  290. { align to 16 byte so that it can be nicely viewed in hex editors;
  291. maybe we could also use 512 byte alignment instead }
  292. rem:=$f-(pos and $f);
  293. pcpfile.stream.write(align[0],rem+1);
  294. end;
  295. procedure tpcppackage.readcontainernames;
  296. begin
  297. if pcpfile.readentry<>ibpackagefiles then
  298. begin
  299. message(package_f_pcp_read_error);
  300. internalerror(424242);
  301. end;
  302. pplfilename:=pcpfile.getstring;
  303. writeln('PPL filename: ',pplfilename);
  304. end;
  305. procedure tpcppackage.readcontainedunits;
  306. var
  307. cnt,i : longint;
  308. name,path : string;
  309. p : pcontainedunit;
  310. begin
  311. if pcpfile.readentry<>ibstartcontained then
  312. begin
  313. message(package_f_pcp_read_error);
  314. internalerror(424242);
  315. end;
  316. cnt:=pcpfile.getlongint;
  317. if pcpfile.readentry<>ibendcontained then
  318. begin
  319. message(package_f_pcp_read_error);
  320. internalerror(424242);
  321. end;
  322. for i:=0 to cnt-1 do
  323. begin
  324. name:=pcpfile.getstring;
  325. path:=pcpfile.getstring;
  326. new(p);
  327. p^.module:=nil;
  328. p^.ppufile:=path;
  329. p^.offset:=0;
  330. p^.size:=0;
  331. containedmodules.add(name,p);
  332. message1(package_u_contained_unit,name);
  333. end;
  334. end;
  335. procedure tpcppackage.readrequiredpackages;
  336. var
  337. cnt,i : longint;
  338. name : string;
  339. begin
  340. if pcpfile.readentry<>ibstartrequireds then
  341. begin
  342. message(package_f_pcp_read_error);
  343. internalerror(2014110901);
  344. end;
  345. cnt:=pcpfile.getlongint;
  346. if pcpfile.readentry<>ibendrequireds then
  347. begin
  348. message(package_f_pcp_read_error);
  349. internalerror(2014110902);
  350. end;
  351. for i:=0 to cnt-1 do
  352. begin
  353. name:=pcpfile.getstring;
  354. requiredpackages.add(name,nil);
  355. message1(package_u_required_package,name);
  356. end;
  357. end;
  358. procedure tpcppackage.readpputable;
  359. var
  360. module : pcontainedunit;
  361. i : longint;
  362. begin
  363. if pcpfile.readentry<>ibpputable then
  364. begin
  365. message(package_f_pcp_read_error);
  366. internalerror(2015103001);
  367. end;
  368. for i:=0 to containedmodules.count-1 do
  369. begin
  370. module:=pcontainedunit(containedmodules[i]);
  371. module^.offset:=pcpfile.getlongint;
  372. module^.size:=pcpfile.getlongint;
  373. end;
  374. end;
  375. constructor tpcppackage.create(const pn: string);
  376. begin
  377. inherited create(pn);
  378. setfilename(pn,true);
  379. end;
  380. destructor tpcppackage.destroy;
  381. begin
  382. pcpfile.free;
  383. inherited destroy;
  384. end;
  385. procedure tpcppackage.loadpcp;
  386. var
  387. newpackagename : string;
  388. begin
  389. if loaded then
  390. exit;
  391. if not search_package_file then
  392. begin
  393. Message1(package_f_cant_find_pcp,realpackagename^);
  394. exit;
  395. end
  396. else
  397. Message1(package_u_pcp_found,realpackagename^);
  398. if not assigned(pcpfile) then
  399. internalerror(2013053101);
  400. if pcpfile.readentry<>ibpackagename then
  401. Message1(package_f_cant_read_pcp,realpackagename^);
  402. newpackagename:=pcpfile.getstring;
  403. if upper(newpackagename)<>packagename^ then
  404. Comment(V_Error,'Package was renamed: '+realpackagename^);
  405. readcontainernames;
  406. readrequiredpackages;
  407. readcontainedunits;
  408. readpputable;
  409. end;
  410. procedure tpcppackage.savepcp;
  411. var
  412. tablepos,
  413. oldpos : longint;
  414. begin
  415. { create new ppufile }
  416. pcpfile:=tpcpfile.create(pcpfilename);
  417. if not pcpfile.createfile then
  418. Message2(package_f_cant_create_pcp,realpackagename^,pcpfilename);
  419. pcpfile.putstring(realpackagename^);
  420. pcpfile.writeentry(ibpackagename);
  421. writecontainernames;
  422. writerequiredpackages;
  423. writecontainedunits;
  424. { the offsets and the contents of the ppus are not crc'd }
  425. pcpfile.do_crc:=false;
  426. pcpfile.flush;
  427. tablepos:=pcpfile.position;
  428. { this will write a table with empty entries }
  429. writepputable;
  430. pcpfile.do_crc:=true;
  431. { the last entry ibend is written automatically }
  432. { flush to be sure }
  433. pcpfile.flush;
  434. { create and write header }
  435. pcpfile.header.common.size:=pcpfile.size;
  436. pcpfile.header.checksum:=pcpfile.crc;
  437. pcpfile.header.common.compiler:=wordversion;
  438. pcpfile.header.common.cpu:=word(target_cpu);
  439. pcpfile.header.common.target:=word(target_info.system);
  440. //pcpfile.header.flags:=flags;
  441. pcpfile.header.ppulistsize:=containedmodules.count;
  442. pcpfile.header.requiredlistsize:=requiredpackages.count;
  443. pcpfile.writeheader;
  444. { write the ppu table which will also fill the offsets/sizes }
  445. writeppudata;
  446. pcpfile.flush;
  447. oldpos:=pcpfile.position;
  448. { now write the filled PPU table at the previously stored position }
  449. pcpfile.position:=tablepos;
  450. writepputable;
  451. pcpfile.position:=oldpos;
  452. { save crc in current module also }
  453. //crc:=pcpfile.crc;
  454. pcpfile.closefile;
  455. pcpfile.free;
  456. pcpfile:=nil;
  457. end;
  458. function tpcppackage.getmodulestream(module:tmodulebase):tcstream;
  459. var
  460. i : longint;
  461. contained : pcontainedunit;
  462. begin
  463. for i:=0 to containedmodules.count-1 do
  464. begin
  465. contained:=pcontainedunit(containedmodules[i]);
  466. if contained^.module=module then
  467. begin
  468. result:=pcpfile.substream(contained^.offset,contained^.size);
  469. exit;
  470. end;
  471. end;
  472. result:=nil;
  473. end;
  474. procedure tpcppackage.initmoduleinfo(module: tmodulebase);
  475. begin
  476. pplfilename:=extractfilename(module.sharedlibfilename);
  477. end;
  478. procedure tpcppackage.addunit(module: tmodulebase);
  479. var
  480. containedunit : pcontainedunit;
  481. begin
  482. new(containedunit);
  483. containedunit^.module:=module;
  484. containedunit^.ppufile:=extractfilename(module.ppufilename);
  485. containedunit^.offset:=0;
  486. containedunit^.size:=0;
  487. containedmodules.add(module.modulename^,containedunit);
  488. end;
  489. procedure tpcppackage.add_required_package(pkg:tpackage);
  490. var
  491. p : tpackage;
  492. begin
  493. p:=tpackage(requiredpackages.find(pkg.packagename^));
  494. if not assigned(p) then
  495. requiredpackages.Add(pkg.packagename^,pkg)
  496. else
  497. if p<>pkg then
  498. internalerror(2015112302);
  499. end;
  500. end.