sourcepawn/batchtool/uFunc.pas
author Asher Baker <asherkin@gmail.com>
Fri May 17 21:46:43 2013 +0100 (2013-05-17)
changeset 3921 96e96debb70d
parent 1952 b7aa2b626807
permissions -rw-r--r--
Force rebuild.
     1 (* AMX Mod X
     2 *    compile.exe
     3 *
     4 * by the AMX Mod X Development Team
     5 *
     6 *
     7 *  This program is free software; you can redistribute it and/or modify it
     8 *  under the terms of the GNU General Public License as published by the
     9 *  Free Software Foundation; either version 2 of the License, or (at
    10 *  your option) any later version.
    11 *
    12 *  This program is distributed in the hope that it will be useful, but
    13 *  WITHOUT ANY WARRANTY; without even the implied warranty of
    14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    15 *  General Public License for more details.
    16 *
    17 *  You should have received a copy of the GNU General Public License
    18 *  along with this program; if not, write to the Free Software Foundation,
    19 *  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    20 *
    21 *  In addition, as a special exception, the author gives permission to
    22 *  link the code of this program with the Half-Life Game Engine ("HL
    23 *  Engine") and Modified Game Libraries ("MODs") developed by Valve,
    24 *  L.L.C ("Valve"). You must obey the GNU General Public License in all
    25 *  respects for all of the code used other than the HL Engine and MODs
    26 *  from Valve. If you modify this file, you may extend this exception
    27 *  to your version of the file, but you are not obligated to do so. If
    28 *  you do not wish to do so, delete this exception statement from your
    29 *  version.
    30 *)
    31 
    32 unit uFunc;
    33 
    34 interface
    35 
    36 uses
    37   Windows, SysUtils, Classes, Math, IniFiles;
    38 
    39 resourcestring
    40   COMPILER_EXE = 'spcomp.exe';
    41 
    42 procedure AppExit;
    43 procedure CompilePlugin(const Name, OutputDir: String);
    44 function GetAgeFromDat(const FileName: String): Integer;
    45 procedure SetAgeToDat(const FileName: String; const Age: Integer);
    46 function GetConsoleOutput(const Command: String; var Output: TStringList): Boolean;
    47 
    48 implementation
    49 
    50 procedure AppExit;
    51 begin
    52   WriteLn;
    53   Write('Press enter to exit ...');
    54   ReadLn;
    55   Halt;
    56 end;
    57 
    58 procedure CompilePlugin(const Name, OutputDir: String);
    59 var
    60   Output: TStringList;
    61   i: Word;
    62   cStart,cEnd: Longword;
    63   FileName, FilePath, Compiled: String;
    64 begin
    65   FileName := ExtractFileName(Name);
    66   FilePath := ExtractFilePath(Name);
    67   if (OutputDir = '') then
    68     Compiled := FilePath+'compiled\'+ChangeFileExt(Filename,'.smx')
    69   else
    70     Compiled := OutputDir+ChangeFileExt(Filename,'.smx');
    71 
    72   if (FilePath='') then
    73     FilePath := ExtractFilePath(ParamStr(0));
    74 
    75   WriteLn;
    76   WriteLn('//// '+ExtractFileName(FileName));
    77 
    78   if FileExists(Compiled) and ( GetAgeFromDat(Name)=FileAge(Name) ) then
    79   begin
    80     WriteLn('// Already compiled.');
    81     WriteLn('// ----------------------------------------');
    82     Exit;
    83   end;
    84 
    85   Output := TStringList.Create;
    86 
    87   try
    88     cStart := GetTickCount;
    89     if not GetConsoleOutput(ExtractFilePath(ParamStr(0))+COMPILER_EXE+' "'+Name+'" "-o'+Compiled+'"',Output) then
    90     begin
    91       WriteLn('// Internal error.');
    92       AppExit;
    93     end;
    94     cEnd := GetTickCount;
    95 
    96     for i := 3 to (Output.Count-1) do
    97     begin
    98       WriteLn('// '+Output.Strings[i]);
    99     end;
   100 
   101     WriteLn('//');
   102     WriteLn('// Compilation Time: '+FloatToStr(SimpleRoundTo((cEnd-cStart) / 1000,-2))+' sec');
   103     WriteLn('// ----------------------------------------');
   104     Output.Free;
   105   except
   106     WriteLn('// Internal error.');
   107     AppExit;
   108   end;
   109 
   110   SetAgeToDat(Name,FileAge(Name));
   111 end;
   112 
   113 function GetAgeFromDat(const FileName: String): Integer;
   114 var
   115   Ini: TIniFile;
   116 begin
   117   Ini := TIniFile.Create(ExtractFilePath(ParamStr(0))+'compile.dat');
   118   Result := Ini.ReadInteger(FileName,'Age',-1);
   119   Ini.Free;
   120 end;
   121 
   122 procedure SetAgeToDat(const FileName: String; const Age: Integer);
   123 var
   124   Ini: TIniFile;
   125 begin
   126   Ini := TIniFile.Create(ExtractFilePath(ParamStr(0))+'compile.dat');
   127   Ini.WriteInteger(FileName,'Age',Age);
   128   Ini.UpdateFile;
   129   Ini.Free;
   130   SetFileAttributes(PChar(ExtractFilePath(ParamStr(0))+'compile.dat'), faHidden);
   131 end;
   132 
   133 function GetConsoleOutput(const Command: String; var Output: TStringList): Boolean;
   134 var
   135   StartupInfo: TStartupInfo;
   136   ProcessInfo: TProcessInformation;
   137   SecurityAttr: TSecurityAttributes;
   138   PipeOutputRead: THandle;
   139   PipeOutputWrite: THandle;
   140   PipeErrorsRead: THandle;
   141   PipeErrorsWrite: THandle;
   142   Succeed: Boolean;
   143   Buffer: array [0..255] of Char;
   144   NumberOfBytesRead: DWORD;
   145   Stream: TMemoryStream;
   146   Errors: String;
   147 begin
   148   FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
   149   FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
   150 
   151   SecurityAttr.nLength := SizeOf(SecurityAttr);
   152   SecurityAttr.bInheritHandle := True;
   153   SecurityAttr.lpSecurityDescriptor := nil;
   154 
   155   CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
   156   CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);
   157 
   158   FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
   159   StartupInfo.cb:=SizeOf(StartupInfo);
   160   StartupInfo.hStdInput := 0;
   161   StartupInfo.hStdOutput := PipeOutputWrite;
   162   StartupInfo.hStdError := PipeErrorsWrite;
   163   StartupInfo.wShowWindow := SW_HIDE;
   164   StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
   165 
   166   if  CreateProcess(nil, PChar(command), nil, nil, true, CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then begin
   167     Result := True;
   168     CloseHandle(PipeOutputWrite);
   169     CloseHandle(PipeErrorsWrite);
   170 
   171     Stream := TMemoryStream.Create;
   172     try
   173       while True do begin
   174         Succeed := ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil);
   175         if not Succeed then Break;
   176         Stream.Write(Buffer, NumberOfBytesRead);
   177       end;
   178     finally
   179       // nothing
   180     end;
   181     Stream.Position := 0;
   182     Output.LoadFromStream(Stream);
   183     Stream.Free;
   184     CloseHandle(PipeOutputRead);
   185 
   186     Errors := '';
   187     try
   188       while True do
   189       begin
   190         Succeed := ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil);
   191         if not Succeed then Break;
   192         Errors := Errors + Copy(Buffer, 1, NumberOfBytesRead);
   193       end;
   194     finally
   195       // nothing
   196     end;
   197     CloseHandle(PipeErrorsRead);
   198 
   199     WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
   200     CloseHandle(ProcessInfo.hProcess);
   201   end
   202   else
   203   begin
   204     Result := False;
   205     CloseHandle(PipeOutputRead);
   206     CloseHandle(PipeOutputWrite);
   207     CloseHandle(PipeErrorsRead);
   208     CloseHandle(PipeErrorsWrite);
   209   end;
   210   // error management
   211   if (Errors <> '') then begin
   212     if (Output.Count > 1) then begin
   213       if (Trim(Output[Output.Count -2]) = '') then
   214         Output.Strings[Output.Count -2] := TrimRight(Errors)
   215       else
   216         Output.Insert(Output.Count -1, TrimRight(Errors));
   217     end
   218     else
   219       Output.Add(Errors);
   220     Output.Text := Output.Text; // pseudo-rearrangement
   221   end;
   222 end;
   223 
   224 end.