ppumove.pp 15 KB

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