ppumove.pp 14 KB

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