ppumove.pp 15 KB

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