ppumove.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. {
  2. Copyright (c) 1999-2002 by the FPC Development Team
  3. Add multiple FPC units into a static/shared library
  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. {$ifndef TP}
  17. {$H+}
  18. {$endif}
  19. Program ppumove;
  20. uses
  21. sysutils,
  22. {$ifdef unix}
  23. Baseunix,Unix, UnixUtil,
  24. {$else unix}
  25. dos,
  26. {$endif unix}
  27. ppu,
  28. getopts;
  29. const
  30. Version = 'Version 2.0.2';
  31. Title = 'PPU-Mover';
  32. Copyright = 'Copyright (c) 1998-2006 by the Free Pascal Development Team';
  33. ShortOpts = 'o:e:d:i: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. OutputFileForPPU,
  60. OutputFile,
  61. OutputFileForLink, { the name of the output file needed when linking }
  62. InputPath,
  63. DestPath,
  64. PPLExt,
  65. LibExt : string;
  66. DoStrip,
  67. Batch,
  68. Quiet,
  69. MakeStatic : boolean;
  70. Buffer : Pointer;
  71. ObjFiles : PLinkOEnt;
  72. BatchFile : Text;
  73. Libs : ansistring;
  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:=unix.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:=FpStat(F,Info)=0;
  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. ext,
  217. s : string;
  218. begin
  219. DoPPU:=false;
  220. If Not Quiet then
  221. Write ('Processing ',PPUFn,'...');
  222. inppu:=tppufile.create(PPUFn);
  223. if not inppu.openfile then
  224. begin
  225. inppu.free;
  226. Error('Error: Could not open : '+PPUFn,false);
  227. Exit;
  228. end;
  229. { Check the ppufile }
  230. if not inppu.CheckPPUId then
  231. begin
  232. inppu.free;
  233. Error('Error: Not a PPU File : '+PPUFn,false);
  234. Exit;
  235. end;
  236. if inppu.GetPPUVersion<CurrentPPUVersion then
  237. begin
  238. inppu.free;
  239. Error('Error: Wrong PPU Version : '+PPUFn,false);
  240. Exit;
  241. end;
  242. { No .o file generated for this ppu, just skip }
  243. if (inppu.header.flags and uf_no_link)<>0 then
  244. begin
  245. inppu.free;
  246. If Not Quiet then
  247. Writeln (' No files.');
  248. DoPPU:=true;
  249. Exit;
  250. end;
  251. { Already a lib? }
  252. if (inppu.header.flags and uf_in_library)<>0 then
  253. begin
  254. inppu.free;
  255. Error('Error: PPU is already in a library : '+PPUFn,false);
  256. Exit;
  257. end;
  258. { We need a static linked unit }
  259. if (inppu.header.flags and uf_static_linked)=0 then
  260. begin
  261. inppu.free;
  262. Error('Error: PPU is not static linked : '+PPUFn,false);
  263. Exit;
  264. end;
  265. { Create the new ppu }
  266. if PPUFn=PPLFn then
  267. outppu:=tppufile.create('ppumove.$$$')
  268. else
  269. outppu:=tppufile.create(PPLFn);
  270. outppu.createfile;
  271. { Create new header, with the new flags }
  272. outppu.header:=inppu.header;
  273. outppu.header.flags:=outppu.header.flags or uf_in_library;
  274. if MakeStatic then
  275. outppu.header.flags:=outppu.header.flags or uf_static_linked
  276. else
  277. outppu.header.flags:=outppu.header.flags or uf_shared_linked;
  278. { read until the object files are found }
  279. untilb:=iblinkunitofiles;
  280. repeat
  281. b:=inppu.readentry;
  282. if b in [ibendinterface,ibend] then
  283. begin
  284. inppu.free;
  285. outppu.free;
  286. Error('Error: No files to be linked found : '+PPUFn,false);
  287. Exit;
  288. end;
  289. if b<>untilb then
  290. begin
  291. repeat
  292. inppu.getdatabuf(buffer^,bufsize,l);
  293. outppu.putdata(buffer^,l);
  294. until l<bufsize;
  295. outppu.writeentry(b);
  296. end;
  297. until (b=untilb);
  298. { we have now reached the section for the files which need to be added,
  299. now add them to the list }
  300. case b of
  301. iblinkunitofiles :
  302. begin
  303. { add all o files, and save the entry when not creating a static
  304. library to keep staticlinking possible }
  305. while not inppu.endofentry do
  306. begin
  307. s:=inppu.getstring;
  308. m:=inppu.getlongint;
  309. if not MakeStatic then
  310. begin
  311. outppu.putstring(s);
  312. outppu.putlongint(m);
  313. end;
  314. AddToLinkFiles(s);
  315. end;
  316. if not MakeStatic then
  317. outppu.writeentry(b);
  318. end;
  319. { iblinkunitstaticlibs :
  320. begin
  321. AddToLinkFiles(ExtractLib(inppu.getstring));
  322. if not inppu.endofentry then
  323. begin
  324. repeat
  325. inppu.getdatabuf(buffer^,bufsize,l);
  326. outppu.putdata(buffer^,l);
  327. until l<bufsize;
  328. outppu.writeentry(b);
  329. end;
  330. end; }
  331. end;
  332. { just add a new entry with the new lib }
  333. if MakeStatic then
  334. begin
  335. outppu.putstring(OutputfileForPPU);
  336. outppu.putlongint(link_static);
  337. outppu.writeentry(iblinkunitstaticlibs)
  338. end
  339. else
  340. begin
  341. outppu.putstring(OutputfileForPPU);
  342. outppu.putlongint(link_shared);
  343. outppu.writeentry(iblinkunitsharedlibs);
  344. end;
  345. { read all entries until the end and write them also to the new ppu }
  346. repeat
  347. b:=inppu.readentry;
  348. { don't write ibend, that's written automaticly }
  349. if b<>ibend then
  350. begin
  351. if b=iblinkothersharedlibs then
  352. begin
  353. while not inppu.endofentry do
  354. begin
  355. s:=inppu.getstring;
  356. m:=inppu.getlongint;
  357. outppu.putstring(s);
  358. { strip lib prefix }
  359. if copy(s,1,3)='lib' then
  360. delete(s,1,3);
  361. { strip lib prefix }
  362. if copy(s,1,3)='lib' then
  363. delete(s,1,3);
  364. ext:=ExtractFileExt(s);
  365. if ext<>'' then
  366. delete(s,length(s)-length(ext)+1,length(ext));
  367. libs:=libs+' -l'+s;
  368. outppu.putlongint(m);
  369. end;
  370. end
  371. else
  372. repeat
  373. inppu.getdatabuf(buffer^,bufsize,l);
  374. outppu.putdata(buffer^,l);
  375. until l<bufsize;
  376. outppu.writeentry(b);
  377. end;
  378. until b=ibend;
  379. { write the last stuff and close }
  380. outppu.flush;
  381. outppu.writeheader;
  382. outppu.free;
  383. inppu.free;
  384. { rename }
  385. if PPUFn=PPLFn then
  386. begin
  387. {$I-}
  388. assign(f,PPUFn);
  389. erase(f);
  390. assign(f,'ppumove.$$$');
  391. rename(f,PPUFn);
  392. {$I+}
  393. if ioresult<>0 then;
  394. end;
  395. { the end }
  396. If Not Quiet then
  397. Writeln (' Done.');
  398. DoPPU:=True;
  399. end;
  400. Function DoFile(const FileName:String):Boolean;
  401. {
  402. Process a file, mainly here for wildcard support under Dos
  403. }
  404. {$ifndef unix}
  405. var
  406. dir : searchrec;
  407. {$endif}
  408. begin
  409. {$ifdef unix}
  410. DoFile:=DoPPU(InputPath+FileName,InputPath+ForceExtension(FileName,PPLExt));
  411. {$else}
  412. DoFile:=false;
  413. findfirst(filename,$20,dir);
  414. while doserror=0 do
  415. begin
  416. if not DoPPU(InputPath+Dir.Name,InputPath+ForceExtension(Dir.Name,PPLExt)) then
  417. exit;
  418. findnext(dir);
  419. end;
  420. findclose(dir);
  421. DoFile:=true;
  422. {$endif}
  423. end;
  424. Procedure DoLink;
  425. {
  426. Link the object files together to form a (shared) library
  427. }
  428. Var
  429. Names : ansistring;
  430. f : file;
  431. Err : boolean;
  432. P : PLinkOEnt;
  433. begin
  434. if not Quiet then
  435. Write ('Linking ');
  436. P:=ObjFiles;
  437. names:='';
  438. While p<>nil do
  439. begin
  440. if Names<>'' then
  441. Names:=Names+' '+InputPath+P^.name
  442. else
  443. Names:=InputPath+p^.Name;
  444. p:=p^.next;
  445. end;
  446. if Names='' then
  447. begin
  448. If not Quiet then
  449. Writeln('Error: no files found to be linked');
  450. exit;
  451. end;
  452. If not Quiet then
  453. WriteLn(names+Libs);
  454. { Run ar or ld to create the lib }
  455. If MakeStatic then
  456. Err:=Shell(arbin+' rs '+outputfile+' '+names)<>0
  457. else
  458. begin
  459. Err:=Shell(ldbin+' -shared -E -o '+OutputFile+' '+names+' '+libs)<>0;
  460. if (not Err) and dostrip then
  461. Shell(stripbin+' --strip-unneeded '+OutputFile);
  462. end;
  463. If Err then
  464. Error('Fatal: Library building stage failed.',true);
  465. { fix permission to 644, so it's not 755 }
  466. {$ifdef unix}
  467. FPChmod(OutputFile,420);
  468. {$endif}
  469. { Rename to the destpath }
  470. if DestPath<>'' then
  471. begin
  472. Assign(F, OutputFile);
  473. Rename(F,DestPath+DirectorySeparator+OutputFile);
  474. end;
  475. end;
  476. Procedure usage;
  477. {
  478. Print usage and exit.
  479. }
  480. begin
  481. Writeln(paramstr(0),': [-qhwvbsS] [-e ext] [-o name] [-d path] file [file ...]');
  482. Halt(0);
  483. end;
  484. Procedure processopts;
  485. {
  486. Process command line opions, and checks if command line options OK.
  487. }
  488. var
  489. C : char;
  490. begin
  491. if paramcount=0 then
  492. usage;
  493. { Reset }
  494. ObjFiles:=Nil;
  495. Quiet:=False;
  496. Batch:=False;
  497. DoStrip:=False;
  498. OutputFile:='';
  499. PPLExt:='ppu';
  500. ArBin:='ar';
  501. LdBin:='ld';
  502. StripBin:='strip';
  503. repeat
  504. c:=Getopt (ShortOpts);
  505. Case C of
  506. EndOfOptions : break;
  507. 'S' : MakeStatic:=True;
  508. 'o' : OutputFile:=OptArg;
  509. 'd' : DestPath:=OptArg;
  510. 'i' : begin
  511. InputPath:=OptArg;
  512. if InputPath[length(InputPath)]<>DirectorySeparator then
  513. InputPath:=InputPath+DirectorySeparator;
  514. end;
  515. 'e' : PPLext:=OptArg;
  516. 'q' : Quiet:=True;
  517. 'w' : begin
  518. ArBin:='arw';
  519. LdBin:='ldw';
  520. end;
  521. 'b' : Batch:=true;
  522. 's' : DoStrip:=true;
  523. '?' : Usage;
  524. 'h' : Usage;
  525. end;
  526. until false;
  527. { Test filenames on the commandline }
  528. if (OptInd>Paramcount) then
  529. Error('Error: no input files',true);
  530. if (OptInd<ParamCount) and (OutputFile='') then
  531. Error('Error: when moving multiple units, specify an output name.',true);
  532. { alloc a buffer }
  533. GetMem (Buffer,Bufsize);
  534. If Buffer=Nil then
  535. Error('Error: could not allocate memory for buffer.',true);
  536. end;
  537. var
  538. i : longint;
  539. begin
  540. Libs:='';
  541. ProcessOpts;
  542. { Write Header }
  543. if not Quiet then
  544. begin
  545. Writeln(Title+' '+Version);
  546. Writeln(Copyright);
  547. Writeln;
  548. end;
  549. { Check if shared is allowed }
  550. {$ifndef unix}
  551. if arbin<>'arw' then
  552. begin
  553. Writeln('Warning: shared library not supported for Go32, switching to static library');
  554. MakeStatic:=true;
  555. end;
  556. {$endif}
  557. { fix the libext and outputfilename }
  558. if Makestatic then
  559. LibExt:=StaticLibExt
  560. else
  561. LibExt:=SharedLibExt;
  562. if OutputFile='' then
  563. OutputFile:=Paramstr(OptInd);
  564. OutputFileForPPU:=OutputFile;
  565. { fix filename }
  566. {$ifdef unix}
  567. if Copy(OutputFile,1,3)<>'lib' then
  568. OutputFile:='lib'+OutputFile;
  569. { For unix skip replacing the extension if a full .so.X.X if specified }
  570. i:=pos('.so.',Outputfile);
  571. if i<>0 then
  572. OutputFileForLink:=Copy(Outputfile,4,i-4)
  573. else
  574. begin
  575. OutputFile:=ForceExtension(OutputFile,LibExt);
  576. OutputFileForLink:=Copy(Outputfile,4,length(Outputfile)-length(LibExt)-4);
  577. end;
  578. {$else}
  579. OutputFile:=ForceExtension(OutputFile,LibExt);
  580. OutputFileForLink:=OutputFile;
  581. {$endif}
  582. { Open BatchFile }
  583. if Batch then
  584. begin
  585. Assign(BatchFile,'pmove'+BatchExt);
  586. Rewrite(BatchFile);
  587. end;
  588. { Process Files }
  589. i:=OptInd;
  590. While (i<=ParamCount) and Dofile(AddExtension(Paramstr(i),PPUExt)) do
  591. Inc(i);
  592. { Do Linking stage }
  593. DoLink;
  594. { Close BatchFile }
  595. if Batch then
  596. begin
  597. if Not Quiet then
  598. Writeln('Writing pmove'+BatchExt);
  599. Close(BatchFile);
  600. {$ifdef unix}
  601. FPChmod('pmove'+BatchExt,493);
  602. {$endif}
  603. end;
  604. { The End }
  605. if Not Quiet then
  606. Writeln('Done.');
  607. end.