ppumove.pp 13 KB

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