{ $Id$ Copyright (c) 1999-2002 by the FPC Development Team Add multiple FPC units into a static/shared library This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************} {$ifndef TP} {$H+} {$endif} Program ppumove; uses {$ifdef unix} {$ifdef ver1_0} linux, {$else} Baseunix,Unix, UnixUtil, {$endif} {$else unix} dos, {$endif unix} ppu, getopts; const Version = 'Version 1.00'; Title = 'PPU-Mover'; Copyright = 'Copyright (c) 1998-2002 by the Free Pascal Development Team'; ShortOpts = 'o:e:d:qhsvbw'; BufSize = 4096; PPUExt = 'ppu'; ObjExt = 'o'; StaticLibExt ='a'; {$ifdef unix} SharedLibExt ='so'; BatchExt ='.sh'; {$else} SharedLibExt ='dll'; BatchExt ='.bat'; {$endif unix} { link options } link_none = $0; link_allways = $1; link_static = $2; link_smart = $4; link_shared = $8; Type PLinkOEnt = ^TLinkOEnt; TLinkOEnt = record Name : string; Next : PLinkOEnt; end; Var ArBin,LDBin,StripBin, OutputFile, OutputFileForLink, { the name of the output file needed when linking } DestPath, PPLExt, LibExt : string; Batch, Quiet, MakeStatic : boolean; Buffer : Pointer; ObjFiles : PLinkOEnt; BatchFile : Text; {***************************************************************************** Helpers *****************************************************************************} Procedure Error(const s:string;stop:boolean); { Write an error message to stderr } begin {$ifdef FPC} writeln(stderr,s); {$else} writeln(s); {$endif} if stop then halt(1); end; function Shell(const s:string):longint; { Run a shell commnad and return the exitcode } begin if Batch then begin Writeln(BatchFile,s); Shell:=0; exit; end; {$ifdef unix} Shell:={$ifdef ver1_0}linux{$else}unix{$endif}.shell(s); {$else} exec(getenv('COMSPEC'),'/C '+s); Shell:=DosExitCode; {$endif} end; Function FileExists (Const F : String) : Boolean; { Returns True if the file exists, False if not. } Var {$ifdef unix} info : Stat; {$else} info : searchrec; {$endif} begin {$ifdef unix} FileExists:={$ifdef VER1_0}FStat{$ELSE}FpStat{$endif} (F,Info){$ifndef VER1_0}=0{$endif}; {$else} FindFirst (F,anyfile,Info); FileExists:=DosError=0; {$endif} end; Function AddExtension(Const HStr,ext:String):String; { Return a filename which will have extension ext added if no extension is found } var j : longint; begin j:=length(Hstr); while (j>0) and (Hstr[j]<>'.') do dec(j); if j=0 then AddExtension:=Hstr+'.'+Ext else AddExtension:=HStr; end; Function ForceExtension(Const HStr,ext:String):String; { Return a filename which certainly has the extension ext } var j : longint; begin j:=length(Hstr); while (j>0) and (Hstr[j]<>'.') do dec(j); if j=0 then j:=255; ForceExtension:=Copy(Hstr,1,j-1)+'.'+Ext; end; Procedure AddToLinkFiles(const S : String); { Adds a filename to a list of object files to link to. No duplicates allowed. } Var P : PLinKOEnt; begin P:=ObjFiles; { Don't add files twice } While (P<>nil) and (p^.name<>s) do p:=p^.next; if p=nil then begin new(p); p^.next:=ObjFiles; p^.name:=s; ObjFiles:=P; end; end; Function ExtractLib(const libfn:string):string; { Extract a static library libfn and return the files with a wildcard } var n : namestr; d : dirstr; e : extstr; begin { create the temp dir first } fsplit(libfn,d,n,e); {$I-} mkdir(n+'.sl'); {$I+} if ioresult<>0 then; { Extract } if Shell(arbin+' x '+libfn)<>0 then Error('Fatal: Error running '+arbin,true); { Remove the lib file, it's extracted so it can be created with ease } if PPLExt=PPUExt then Shell('rm '+libfn); {$ifdef unix} ExtractLib:=n+'.sl/*'; {$else} ExtractLib:=n+'.sl\*'; {$endif} end; Function DoPPU(const PPUFn,PPLFn:String):Boolean; { Convert one file (in Filename) to library format. Return true if successful, false otherwise. } Var inppu, outppu : tppufile; b, untilb : byte; l,m : longint; f : file; s : string; begin DoPPU:=false; If Not Quiet then Write ('Processing ',PPUFn,'...'); inppu:=tppufile.create(PPUFn); if not inppu.openfile then begin inppu.free; Error('Error: Could not open : '+PPUFn,false); Exit; end; { Check the ppufile } if not inppu.CheckPPUId then begin inppu.free; Error('Error: Not a PPU File : '+PPUFn,false); Exit; end; if inppu.GetPPUVersion0 then begin inppu.free; If Not Quiet then Writeln (' No files.'); DoPPU:=true; Exit; end; { Already a lib? } if (inppu.header.flags and uf_in_library)<>0 then begin inppu.free; Error('Error: PPU is already in a library : '+PPUFn,false); Exit; end; { We need a static linked unit } if (inppu.header.flags and uf_static_linked)=0 then begin inppu.free; Error('Error: PPU is not static linked : '+PPUFn,false); Exit; end; { Create the new ppu } if PPUFn=PPLFn then outppu:=tppufile.create('ppumove.$$$') else outppu:=tppufile.create(PPLFn); outppu.createfile; { Create new header, with the new flags } outppu.header:=inppu.header; outppu.header.flags:=outppu.header.flags or uf_in_library; if MakeStatic then outppu.header.flags:=outppu.header.flags or uf_static_linked else outppu.header.flags:=outppu.header.flags or uf_shared_linked; { read until the object files are found } untilb:=iblinkunitofiles; repeat b:=inppu.readentry; if b in [ibendinterface,ibend] then begin inppu.free; outppu.free; Error('Error: No files to be linked found : '+PPUFn,false); Exit; end; if b<>untilb then begin repeat inppu.getdatabuf(buffer^,bufsize,l); outppu.putdata(buffer^,l); until libend then begin repeat inppu.getdatabuf(buffer^,bufsize,l); outppu.putdata(buffer^,l); until l0 then; end; { the end } If Not Quiet then Writeln (' Done.'); DoPPU:=True; end; Function DoFile(const FileName:String):Boolean; { Process a file, mainly here for wildcard support under Dos } {$ifndef unix} var dir : searchrec; {$endif} begin {$ifdef unix} DoFile:=DoPPU(FileName,ForceExtension(FileName,PPLExt)); {$else} DoFile:=false; findfirst(filename,$20,dir); while doserror=0 do begin if not DoPPU(Dir.Name,ForceExtension(Dir.Name,PPLExt)) then exit; findnext(dir); end; findclose(dir); DoFile:=true; {$endif} end; Procedure DoLink; { Link the object files together to form a (shared) library, the only problem here is the 255 char limit of Names } Var Names : String; f : file; Err : boolean; P : PLinkOEnt; begin if not Quiet then Write ('Linking '); P:=ObjFiles; names:=''; While p<>nil do begin if Names<>'' then Names:=Names+' '+P^.name else Names:=p^.Name; p:=p^.next; end; if Names='' then begin If not Quiet then Writeln('Error: no files found to be linked'); exit; end; If not Quiet then WriteLn(names); { Run ar or ld to create the lib } If MakeStatic then Err:=Shell(arbin+' rs '+outputfile+' '+names)<>0 else begin Err:=Shell(ldbin+' -shared -o '+OutputFile+' '+names)<>0; if not Err then Shell(stripbin+' --strip-unneeded '+OutputFile); end; If Err then Error('Fatal: Library building stage failed.',true); { fix permission to 644, so it's not 755 } {$ifdef unix} {$ifdef VER1_0}ChMod{$ELSE}FPChmod{$endif}(OutputFile,420); {$endif} { Rename to the destpath } if DestPath<>'' then begin Assign(F, OutputFile); Rename(F,DestPath+'/'+OutputFile); end; end; Procedure usage; { Print usage and exit. } begin Writeln(paramstr(0),': [-qhwvbs] [-e ext] [-o name] [-d path] file [file ...]'); Halt(0); end; Procedure processopts; { Process command line opions, and checks if command line options OK. } var C : char; begin if paramcount=0 then usage; { Reset } ObjFiles:=Nil; Quiet:=False; Batch:=False; OutputFile:=''; PPLExt:='ppu'; ArBin:='ar'; LdBin:='ld'; StripBin:='strip'; repeat c:=Getopt (ShortOpts); Case C of EndOfOptions : break; 's' : MakeStatic:=True; 'o' : OutputFile:=OptArg; 'd' : DestPath:=OptArg; 'e' : PPLext:=OptArg; 'q' : Quiet:=True; 'w' : begin ArBin:='arw'; LdBin:='ldw'; end; 'b' : Batch:=true; '?' : Usage; 'h' : Usage; end; until false; { Test filenames on the commandline } if (OptInd>Paramcount) then Error('Error: no input files',true); if (OptInd'arw' then begin Writeln('Warning: shared library not supported for Go32, switching to static library'); MakeStatic:=true; end; {$endif} { fix the libext and outputfilename } if Makestatic then LibExt:=StaticLibExt else LibExt:=SharedLibExt; if OutputFile='' then OutPutFile:=Paramstr(OptInd); { fix filename } {$ifdef unix} if Copy(OutputFile,1,3)<>'lib' then OutputFile:='lib'+OutputFile; { For unix skip replacing the extension if a full .so.X.X if specified } i:=pos('.so.',Outputfile); if i<>0 then OutputFileForLink:=Copy(Outputfile,4,i-4) else begin OutputFile:=ForceExtension(OutputFile,LibExt); OutputFileForLink:=Copy(Outputfile,4,length(Outputfile)-length(LibExt)-4); end; {$else} OutputFile:=ForceExtension(OutputFile,LibExt); OutputFileForLink:=OutputFile; {$endif} { Open BatchFile } if Batch then begin Assign(BatchFile,'pmove'+BatchExt); Rewrite(BatchFile); end; { Process Files } i:=OptInd; While (i<=ParamCount) and Dofile(AddExtension(Paramstr(i),PPUExt)) do Inc(i); { Do Linking stage } DoLink; { Close BatchFile } if Batch then begin if Not Quiet then Writeln('Writing pmove'+BatchExt); Close(BatchFile); {$ifdef unix} {$ifdef VER1_0}ChMod{$ELSE}FPChmod{$endif}('pmove'+BatchExt,493); {$endif} end; { The End } if Not Quiet then Writeln('Done.'); end. { $Log$ Revision 1.7 2003-09-18 15:48:22 marco * adapted to unixutil Revision 1.6 2003/09/14 20:26:18 marco * Unix reform Revision 1.5 2002/05/18 13:34:27 peter * readded missing revisions Revision 1.4 2002/05/16 19:46:54 carl + defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand + try to fix temp allocation (still in ifdef) + generic constructor calls + start of tassembler / tmodulebase class cleanup }