ppumove.pp 13 KB

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