ppumove.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. {
  2. $Id$
  3. Copyright (c) 1999-2002 by the FPC Development Team
  4. Add multiple FPC units into a static/shared library
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************}
  17. {$ifndef TP}
  18. {$H+}
  19. {$endif}
  20. Program ppumove;
  21. uses
  22. {$ifdef unix}
  23. {$ifdef ver1_0}
  24. linux,
  25. {$else}
  26. Baseunix,Unix, UnixUtil,
  27. {$endif}
  28. {$else unix}
  29. dos,
  30. {$endif unix}
  31. ppu,
  32. getopts;
  33. const
  34. Version = 'Version 1.00';
  35. Title = 'PPU-Mover';
  36. Copyright = 'Copyright (c) 1998-2002 by the Free Pascal Development Team';
  37. ShortOpts = 'o:e:d:qhsvbw';
  38. BufSize = 4096;
  39. PPUExt = 'ppu';
  40. ObjExt = 'o';
  41. StaticLibExt ='a';
  42. {$ifdef unix}
  43. SharedLibExt ='so';
  44. BatchExt ='.sh';
  45. {$else}
  46. SharedLibExt ='dll';
  47. BatchExt ='.bat';
  48. {$endif unix}
  49. { link options }
  50. link_none = $0;
  51. link_allways = $1;
  52. link_static = $2;
  53. link_smart = $4;
  54. link_shared = $8;
  55. Type
  56. PLinkOEnt = ^TLinkOEnt;
  57. TLinkOEnt = record
  58. Name : string;
  59. Next : PLinkOEnt;
  60. end;
  61. Var
  62. ArBin,LDBin,StripBin,
  63. OutputFile,
  64. OutputFileForLink, { the name of the output file needed when linking }
  65. DestPath,
  66. PPLExt,
  67. LibExt : string;
  68. Batch,
  69. Quiet,
  70. MakeStatic : boolean;
  71. Buffer : Pointer;
  72. ObjFiles : PLinkOEnt;
  73. BatchFile : Text;
  74. {*****************************************************************************
  75. Helpers
  76. *****************************************************************************}
  77. Procedure Error(const s:string;stop:boolean);
  78. {
  79. Write an error message to stderr
  80. }
  81. begin
  82. {$ifdef FPC}
  83. writeln(stderr,s);
  84. {$else}
  85. writeln(s);
  86. {$endif}
  87. if stop then
  88. halt(1);
  89. end;
  90. function Shell(const s:string):longint;
  91. {
  92. Run a shell commnad and return the exitcode
  93. }
  94. begin
  95. if Batch then
  96. begin
  97. Writeln(BatchFile,s);
  98. Shell:=0;
  99. exit;
  100. end;
  101. {$ifdef unix}
  102. Shell:={$ifdef ver1_0}linux{$else}unix{$endif}.shell(s);
  103. {$else}
  104. exec(getenv('COMSPEC'),'/C '+s);
  105. Shell:=DosExitCode;
  106. {$endif}
  107. end;
  108. Function FileExists (Const F : String) : Boolean;
  109. {
  110. Returns True if the file exists, False if not.
  111. }
  112. Var
  113. {$ifdef unix}
  114. info : Stat;
  115. {$else}
  116. info : searchrec;
  117. {$endif}
  118. begin
  119. {$ifdef unix}
  120. FileExists:={$ifdef VER1_0}FStat{$ELSE}FpStat{$endif} (F,Info){$ifndef VER1_0}=0{$endif};
  121. {$else}
  122. FindFirst (F,anyfile,Info);
  123. FileExists:=DosError=0;
  124. {$endif}
  125. end;
  126. Function AddExtension(Const HStr,ext:String):String;
  127. {
  128. Return a filename which will have extension ext added if no
  129. extension is found
  130. }
  131. var
  132. j : longint;
  133. begin
  134. j:=length(Hstr);
  135. while (j>0) and (Hstr[j]<>'.') do
  136. dec(j);
  137. if j=0 then
  138. AddExtension:=Hstr+'.'+Ext
  139. else
  140. AddExtension:=HStr;
  141. end;
  142. Function ForceExtension(Const HStr,ext:String):String;
  143. {
  144. Return a filename which certainly has the extension ext
  145. }
  146. var
  147. j : longint;
  148. begin
  149. j:=length(Hstr);
  150. while (j>0) and (Hstr[j]<>'.') do
  151. dec(j);
  152. if j=0 then
  153. j:=255;
  154. ForceExtension:=Copy(Hstr,1,j-1)+'.'+Ext;
  155. end;
  156. Procedure AddToLinkFiles(const S : String);
  157. {
  158. Adds a filename to a list of object files to link to.
  159. No duplicates allowed.
  160. }
  161. Var
  162. P : PLinKOEnt;
  163. begin
  164. P:=ObjFiles;
  165. { Don't add files twice }
  166. While (P<>nil) and (p^.name<>s) do
  167. p:=p^.next;
  168. if p=nil then
  169. begin
  170. new(p);
  171. p^.next:=ObjFiles;
  172. p^.name:=s;
  173. ObjFiles:=P;
  174. end;
  175. end;
  176. Function ExtractLib(const libfn:string):string;
  177. {
  178. Extract a static library libfn and return the files with a
  179. wildcard
  180. }
  181. var
  182. n : namestr;
  183. d : dirstr;
  184. e : extstr;
  185. begin
  186. { create the temp dir first }
  187. fsplit(libfn,d,n,e);
  188. {$I-}
  189. mkdir(n+'.sl');
  190. {$I+}
  191. if ioresult<>0 then;
  192. { Extract }
  193. if Shell(arbin+' x '+libfn)<>0 then
  194. Error('Fatal: Error running '+arbin,true);
  195. { Remove the lib file, it's extracted so it can be created with ease }
  196. if PPLExt=PPUExt then
  197. Shell('rm '+libfn);
  198. {$ifdef unix}
  199. ExtractLib:=n+'.sl/*';
  200. {$else}
  201. ExtractLib:=n+'.sl\*';
  202. {$endif}
  203. end;
  204. Function DoPPU(const PPUFn,PPLFn:String):Boolean;
  205. {
  206. Convert one file (in Filename) to library format.
  207. Return true if successful, false otherwise.
  208. }
  209. Var
  210. inppu,
  211. outppu : tppufile;
  212. b,
  213. untilb : byte;
  214. l,m : longint;
  215. f : file;
  216. s : string;
  217. begin
  218. DoPPU:=false;
  219. If Not Quiet then
  220. Write ('Processing ',PPUFn,'...');
  221. inppu:=tppufile.create(PPUFn);
  222. if not inppu.openfile then
  223. begin
  224. inppu.free;
  225. Error('Error: Could not open : '+PPUFn,false);
  226. Exit;
  227. end;
  228. { Check the ppufile }
  229. if not inppu.CheckPPUId then
  230. begin
  231. inppu.free;
  232. Error('Error: Not a PPU File : '+PPUFn,false);
  233. Exit;
  234. end;
  235. if inppu.GetPPUVersion<CurrentPPUVersion then
  236. begin
  237. inppu.free;
  238. Error('Error: Wrong PPU Version : '+PPUFn,false);
  239. Exit;
  240. end;
  241. { No .o file generated for this ppu, just skip }
  242. if (inppu.header.flags and uf_no_link)<>0 then
  243. begin
  244. inppu.free;
  245. If Not Quiet then
  246. Writeln (' No files.');
  247. DoPPU:=true;
  248. Exit;
  249. end;
  250. { Already a lib? }
  251. if (inppu.header.flags and uf_in_library)<>0 then
  252. begin
  253. inppu.free;
  254. Error('Error: PPU is already in a library : '+PPUFn,false);
  255. Exit;
  256. end;
  257. { We need a static linked unit }
  258. if (inppu.header.flags and uf_static_linked)=0 then
  259. begin
  260. inppu.free;
  261. Error('Error: PPU is not static linked : '+PPUFn,false);
  262. Exit;
  263. end;
  264. { Create the new ppu }
  265. if PPUFn=PPLFn then
  266. outppu:=tppufile.create('ppumove.$$$')
  267. else
  268. outppu:=tppufile.create(PPLFn);
  269. outppu.createfile;
  270. { Create new header, with the new flags }
  271. outppu.header:=inppu.header;
  272. outppu.header.flags:=outppu.header.flags or uf_in_library;
  273. if MakeStatic then
  274. outppu.header.flags:=outppu.header.flags or uf_static_linked
  275. else
  276. outppu.header.flags:=outppu.header.flags or uf_shared_linked;
  277. { read until the object files are found }
  278. untilb:=iblinkunitofiles;
  279. repeat
  280. b:=inppu.readentry;
  281. if b in [ibendinterface,ibend] then
  282. begin
  283. inppu.free;
  284. outppu.free;
  285. Error('Error: No files to be linked found : '+PPUFn,false);
  286. Exit;
  287. end;
  288. if b<>untilb then
  289. begin
  290. repeat
  291. inppu.getdatabuf(buffer^,bufsize,l);
  292. outppu.putdata(buffer^,l);
  293. until l<bufsize;
  294. outppu.writeentry(b);
  295. end;
  296. until (b=untilb);
  297. { we have now reached the section for the files which need to be added,
  298. now add them to the list }
  299. case b of
  300. iblinkunitofiles :
  301. begin
  302. { add all o files, and save the entry when not creating a static
  303. library to keep staticlinking possible }
  304. while not inppu.endofentry do
  305. begin
  306. s:=inppu.getstring;
  307. m:=inppu.getlongint;
  308. if not MakeStatic then
  309. begin
  310. outppu.putstring(s);
  311. outppu.putlongint(m);
  312. end;
  313. AddToLinkFiles(s);
  314. end;
  315. if not MakeStatic then
  316. outppu.writeentry(b);
  317. end;
  318. { iblinkunitstaticlibs :
  319. begin
  320. AddToLinkFiles(ExtractLib(inppu.getstring));
  321. if not inppu.endofentry then
  322. begin
  323. repeat
  324. inppu.getdatabuf(buffer^,bufsize,l);
  325. outppu.putdata(buffer^,l);
  326. until l<bufsize;
  327. outppu.writeentry(b);
  328. end;
  329. end; }
  330. end;
  331. { just add a new entry with the new lib }
  332. if MakeStatic then
  333. begin
  334. outppu.putstring(outputfileforlink);
  335. outppu.putlongint(link_static);
  336. outppu.writeentry(iblinkunitstaticlibs)
  337. end
  338. else
  339. begin
  340. outppu.putstring(outputfileforlink);
  341. outppu.putlongint(link_shared);
  342. outppu.writeentry(iblinkunitsharedlibs);
  343. end;
  344. { read all entries until the end and write them also to the new ppu }
  345. repeat
  346. b:=inppu.readentry;
  347. { don't write ibend, that's written automaticly }
  348. if b<>ibend then
  349. begin
  350. repeat
  351. inppu.getdatabuf(buffer^,bufsize,l);
  352. outppu.putdata(buffer^,l);
  353. until l<bufsize;
  354. outppu.writeentry(b);
  355. end;
  356. until b=ibend;
  357. { write the last stuff and close }
  358. outppu.flush;
  359. outppu.writeheader;
  360. outppu.free;
  361. inppu.free;
  362. { rename }
  363. if PPUFn=PPLFn then
  364. begin
  365. {$I-}
  366. assign(f,PPUFn);
  367. erase(f);
  368. assign(f,'ppumove.$$$');
  369. rename(f,PPUFn);
  370. {$I+}
  371. if ioresult<>0 then;
  372. end;
  373. { the end }
  374. If Not Quiet then
  375. Writeln (' Done.');
  376. DoPPU:=True;
  377. end;
  378. Function DoFile(const FileName:String):Boolean;
  379. {
  380. Process a file, mainly here for wildcard support under Dos
  381. }
  382. {$ifndef unix}
  383. var
  384. dir : searchrec;
  385. {$endif}
  386. begin
  387. {$ifdef unix}
  388. DoFile:=DoPPU(FileName,ForceExtension(FileName,PPLExt));
  389. {$else}
  390. DoFile:=false;
  391. findfirst(filename,$20,dir);
  392. while doserror=0 do
  393. begin
  394. if not DoPPU(Dir.Name,ForceExtension(Dir.Name,PPLExt)) then
  395. exit;
  396. findnext(dir);
  397. end;
  398. findclose(dir);
  399. DoFile:=true;
  400. {$endif}
  401. end;
  402. Procedure DoLink;
  403. {
  404. Link the object files together to form a (shared) library, the only
  405. problem here is the 255 char limit of Names
  406. }
  407. Var
  408. Names : String;
  409. f : file;
  410. Err : boolean;
  411. P : PLinkOEnt;
  412. begin
  413. if not Quiet then
  414. Write ('Linking ');
  415. P:=ObjFiles;
  416. names:='';
  417. While p<>nil do
  418. begin
  419. if Names<>'' then
  420. Names:=Names+' '+P^.name
  421. else
  422. Names:=p^.Name;
  423. p:=p^.next;
  424. end;
  425. if Names='' then
  426. begin
  427. If not Quiet then
  428. Writeln('Error: no files found to be linked');
  429. exit;
  430. end;
  431. If not Quiet then
  432. WriteLn(names);
  433. { Run ar or ld to create the lib }
  434. If MakeStatic then
  435. Err:=Shell(arbin+' rs '+outputfile+' '+names)<>0
  436. else
  437. begin
  438. Err:=Shell(ldbin+' -shared -o '+OutputFile+' '+names)<>0;
  439. if not Err then
  440. Shell(stripbin+' --strip-unneeded '+OutputFile);
  441. end;
  442. If Err then
  443. Error('Fatal: Library building stage failed.',true);
  444. { fix permission to 644, so it's not 755 }
  445. {$ifdef unix}
  446. {$ifdef VER1_0}ChMod{$ELSE}FPChmod{$endif}(OutputFile,420);
  447. {$endif}
  448. { Rename to the destpath }
  449. if DestPath<>'' then
  450. begin
  451. Assign(F, OutputFile);
  452. Rename(F,DestPath+'/'+OutputFile);
  453. end;
  454. end;
  455. Procedure usage;
  456. {
  457. Print usage and exit.
  458. }
  459. begin
  460. Writeln(paramstr(0),': [-qhwvbs] [-e ext] [-o name] [-d path] file [file ...]');
  461. Halt(0);
  462. end;
  463. Procedure processopts;
  464. {
  465. Process command line opions, and checks if command line options OK.
  466. }
  467. var
  468. C : char;
  469. begin
  470. if paramcount=0 then
  471. usage;
  472. { Reset }
  473. ObjFiles:=Nil;
  474. Quiet:=False;
  475. Batch:=False;
  476. OutputFile:='';
  477. PPLExt:='ppu';
  478. ArBin:='ar';
  479. LdBin:='ld';
  480. StripBin:='strip';
  481. repeat
  482. c:=Getopt (ShortOpts);
  483. Case C of
  484. EndOfOptions : break;
  485. 's' : MakeStatic:=True;
  486. 'o' : OutputFile:=OptArg;
  487. 'd' : DestPath:=OptArg;
  488. 'e' : PPLext:=OptArg;
  489. 'q' : Quiet:=True;
  490. 'w' : begin
  491. ArBin:='arw';
  492. LdBin:='ldw';
  493. end;
  494. 'b' : Batch:=true;
  495. '?' : Usage;
  496. 'h' : Usage;
  497. end;
  498. until false;
  499. { Test filenames on the commandline }
  500. if (OptInd>Paramcount) then
  501. Error('Error: no input files',true);
  502. if (OptInd<ParamCount) and (OutputFile='') then
  503. Error('Error: when moving multiple units, specify an output name.',true);
  504. { alloc a buffer }
  505. GetMem (Buffer,Bufsize);
  506. If Buffer=Nil then
  507. Error('Error: could not allocate memory for buffer.',true);
  508. end;
  509. var
  510. i : longint;
  511. begin
  512. ProcessOpts;
  513. { Write Header }
  514. if not Quiet then
  515. begin
  516. Writeln(Title+' '+Version);
  517. Writeln(Copyright);
  518. Writeln;
  519. end;
  520. { Check if shared is allowed }
  521. {$ifndef unix}
  522. if arbin<>'arw' then
  523. begin
  524. Writeln('Warning: shared library not supported for Go32, switching to static library');
  525. MakeStatic:=true;
  526. end;
  527. {$endif}
  528. { fix the libext and outputfilename }
  529. if Makestatic then
  530. LibExt:=StaticLibExt
  531. else
  532. LibExt:=SharedLibExt;
  533. if OutputFile='' then
  534. OutPutFile:=Paramstr(OptInd);
  535. { fix filename }
  536. {$ifdef unix}
  537. if Copy(OutputFile,1,3)<>'lib' then
  538. OutputFile:='lib'+OutputFile;
  539. { For unix skip replacing the extension if a full .so.X.X if specified }
  540. i:=pos('.so.',Outputfile);
  541. if i<>0 then
  542. OutputFileForLink:=Copy(Outputfile,4,i-4)
  543. else
  544. begin
  545. OutputFile:=ForceExtension(OutputFile,LibExt);
  546. OutputFileForLink:=Copy(Outputfile,4,length(Outputfile)-length(LibExt)-4);
  547. end;
  548. {$else}
  549. OutputFile:=ForceExtension(OutputFile,LibExt);
  550. OutputFileForLink:=OutputFile;
  551. {$endif}
  552. { Open BatchFile }
  553. if Batch then
  554. begin
  555. Assign(BatchFile,'pmove'+BatchExt);
  556. Rewrite(BatchFile);
  557. end;
  558. { Process Files }
  559. i:=OptInd;
  560. While (i<=ParamCount) and Dofile(AddExtension(Paramstr(i),PPUExt)) do
  561. Inc(i);
  562. { Do Linking stage }
  563. DoLink;
  564. { Close BatchFile }
  565. if Batch then
  566. begin
  567. if Not Quiet then
  568. Writeln('Writing pmove'+BatchExt);
  569. Close(BatchFile);
  570. {$ifdef unix}
  571. {$ifdef VER1_0}ChMod{$ELSE}FPChmod{$endif}('pmove'+BatchExt,493);
  572. {$endif}
  573. end;
  574. { The End }
  575. if Not Quiet then
  576. Writeln('Done.');
  577. end.
  578. {
  579. $Log$
  580. Revision 1.7 2003-09-18 15:48:22 marco
  581. * adapted to unixutil
  582. Revision 1.6 2003/09/14 20:26:18 marco
  583. * Unix reform
  584. Revision 1.5 2002/05/18 13:34:27 peter
  585. * readded missing revisions
  586. Revision 1.4 2002/05/16 19:46:54 carl
  587. + defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand
  588. + try to fix temp allocation (still in ifdef)
  589. + generic constructor calls
  590. + start of tassembler / tmodulebase class cleanup
  591. }