ppumove.pp 15 KB

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