ppumove.pp 14 KB

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