{
    $Id: compiler.pas,v 1.1.2.5 2001/08/04 10:24:46 peter Exp $
    Copyright (c) 1998-2000 by Florian Klaempfl

    This unit is the interface of the compiler which can be used by
     external programs to link in the compiler

    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.

 ****************************************************************************}

{
  possible compiler switches:
  -----------------------------------------------------------------
  TP                  to compile the compiler with Turbo or Borland Pascal
  I386                generate a compiler for the Intel i386+
  M68K                generate a compiler for the M68000
  GDB                 support of the GNU Debugger
  EXTDEBUG            some extra debug code is executed
  SUPPORT_MMX         only i386: releases the compiler switch
                      MMX which allows the compiler to generate
                      MMX instructions
  EXTERN_MSG          Don't compile the msgfiles in the compiler, always
                      use external messagefiles
  NOAG386INT          no Intel Assembler output
  NOAG386NSM          no NASM output
  -----------------------------------------------------------------
}

{$ifdef FPC}
   { One of Alpha, I386 or M68K must be defined }
   {$UNDEF CPUOK}

   {$ifdef I386}
   {$define CPUOK}
   {$endif}

   {$ifdef M68K}
   {$ifndef CPUOK}
   {$DEFINE CPUOK}
   {$else}
     {$fatal cannot define two CPU switches}
   {$endif}
   {$endif}

   {$ifdef alpha}
   {$ifndef CPUOK}
   {$DEFINE CPUOK}
   {$else}
     {$fatal cannot define two CPU switches}
   {$endif}
   {$endif}

   {$ifdef powerpc}
   {$ifndef CPUOK}
   {$DEFINE CPUOK}
   {$else}
     {$fatal cannot define two CPU switches}
   {$endif}
   {$endif}

   {$ifndef CPUOK}
   {$fatal One of the switches I386, Alpha, PowerPC or M68K must be defined}
   {$endif}

   {$ifdef support_mmx}
     {$ifndef i386}
       {$fatal I386 switch must be on for MMX support}
     {$endif i386}
   {$endif support_mmx}
{$endif}

unit compiler;
interface

{ Use exception catching so the compiler goes futher after a Stop }
{$ifndef NOUSEEXCEPT}
{$ifdef i386}
  {$define USEEXCEPT}
{$endif}

{$ifdef TP}
  {$ifdef DPMI}
    {$undef USEEXCEPT}
  {$endif}
{$endif}
{$endif ndef NOUSEEXCEPT}

uses
{$ifdef fpc}
  {$ifdef GO32V2}
    emu387,
{    dpmiexcp, }
  {$endif GO32V2}
{$endif}
{$ifdef USEEXCEPT}
  tpexcept,
{$endif USEEXCEPT}
{$ifdef BrowserLog}
  browlog,
{$endif BrowserLog}
{$ifdef Delphi}
  dmisc,
{$else Delphi}
  dos,
{$endif Delphi}
  verbose,comphook,systems,
  cobjects,files,globals,options,parser,symtable,link,import,export,tokens;

function Compile(const cmd:string):longint;


implementation

uses
  cpubase;

var
  CompilerInitedAfterArgs,
  CompilerInited : boolean;
  olddo_stop : tstopprocedure;

{$ifdef USEEXCEPT}

procedure RecoverStop;{$ifndef FPC}far;{$endif}
begin
  if recoverpospointer<>nil then
    LongJmp(recoverpospointer^,1)
  else
    Do_Halt(1);
end;
{$endif USEEXCEPT}

{$ifdef EXTDEBUG}
{$ifdef FPC}
  Var
    LostMemory : longint;
  Procedure CheckMemory(LostMemory : longint);
  begin
    if LostMemory<>0 then
      begin
        Writeln('Memory Lost = '+tostr(LostMemory));
{$ifdef DEBUG}
        def_gdb_stop(V_Warning);
{$endif DEBUG}
      end;
  end;
{$endif FPC}
{$endif EXTDEBUG}
{****************************************************************************
                                Compiler
****************************************************************************}

procedure DoneCompiler;
begin
  if not CompilerInited then
   exit;
{ Free compiler if args are read }
{$ifdef BrowserLog}
  DoneBrowserLog;
{$endif BrowserLog}
{$ifdef BrowserCol}
  do_doneSymbolInfo;
{$endif BrowserCol}
  if CompilerInitedAfterArgs then
   begin
     CompilerInitedAfterArgs:=false;
     doneparser;
     DoneImport;
     DoneExport;
     DoneLinker;
     DoneCpu;
   end;
{ Free memory for the others }
  CompilerInited:=false;
  DoneSymtable;
  DoneGlobals;
  donetokens;
{$ifdef USEEXCEPT}
  recoverpospointer:=nil;
  longjump_used:=false;
{$endif USEEXCEPT}
end;


procedure InitCompiler(const cmd:string);
begin
  if CompilerInited then
   DoneCompiler;
{ inits which need to be done before the arguments are parsed }
  InitSystems;
  InitVerbose;
{$ifdef BrowserLog}
  InitBrowserLog;
{$endif BrowserLog}
{$ifdef BrowserCol}
  do_initSymbolInfo;
{$endif BrowserCol}
  InitGlobals;
  inittokens;
  InitSymtable;
  CompilerInited:=true;
{ this is needed here for the IDE
  in case of compilation failure
  at the previous compile }
  current_module:=nil;
{ read the arguments }
  read_arguments(cmd);
{ inits which depend on arguments }
  initparser;
  InitImport;
  InitExport;
  InitLinker;
  InitCpu;
  CompilerInitedAfterArgs:=true;
end;

procedure minimal_stop;{$ifndef fpc}far;{$endif}
begin
  DoneCompiler;
  olddo_stop;
end;


function Compile(const cmd:string):longint;

{$ifdef fpc}
{$maxfpuregisters 0}
{$endif fpc}

  procedure writepathlist(w:longint;l:TSearchPathList);
  var
    hp : pstringqueueitem;
  begin
    hp:=l.first;
    while assigned(hp) do
     begin
       Message1(w,hp^.data^);
       hp:=hp^.next;
     end;
  end;

  function getrealtime : real;
  var
    h,m,s,s100 : word;
  begin
    gettime(h,m,s,s100);
    getrealtime:=h*3600.0+m*60.0+s+s100/100.0;
  end;

var
  starttime  : real;
{$ifdef USEEXCEPT}
  recoverpos : jmp_buf;
{$endif}
begin

  olddo_stop:=do_stop;
{$ifdef TP}
  do_stop:=minimal_stop;
{$else TP}
  do_stop:=@minimal_stop;
{$endif TP}
{ Initialize the compiler }
  InitCompiler(cmd);

{ show some info }
  Message1(general_t_compilername,FixFileName(paramstr(0)));
  Message1(general_d_sourceos,source_os.name);
  Message1(general_i_targetos,target_os.name);
  Message1(general_t_exepath,exepath);
  WritePathList(general_t_unitpath,unitsearchpath);
  WritePathList(general_t_includepath,includesearchpath);
  WritePathList(general_t_librarypath,librarysearchpath);
  WritePathList(general_t_objectpath,objectsearchpath);
{$ifdef TP}
{$ifndef Delphi}
  Comment(V_Info,'Memory: '+tostr(MemAvail)+' Bytes Free');
{$endif Delphi}
{$endif}

{$ifdef USEEXCEPT}
  if setjmp(recoverpos)=0 then
   begin
     recoverpospointer:=@recoverpos;
{$ifdef TP}
     do_stop:=recoverstop;
{$else TP}
     do_stop:=@recoverstop;
{$endif TP}
{$endif USEEXCEPT}
     starttime:=getrealtime;
     if parapreprocess then
      parser.preprocess(inputdir+inputfile+inputextension)
     else
      parser.compile(inputdir+inputfile+inputextension,false);
     if status.errorcount=0 then
      begin
        starttime:=getrealtime-starttime;
        if starttime<0 then
          starttime:=starttime+3600.0*24.0;
        Message2(general_i_abslines_compiled,tostr(status.compiledlines),tostr(trunc(starttime))+
          '.'+tostr(trunc(frac(starttime)*10)));
      end;
{$ifdef USEEXCEPT}
    end;
{$endif USEEXCEPT}

{ Stop is always called, so we come here when a program is compiled or not }
  do_stop:=olddo_stop;
{ Stop the compiler, frees also memory }
{ no message possible after this !!    }
  DoneCompiler;

{ Set the return value if an error has occurred }
  if status.errorcount=0 then
   Compile:=0
  else
   Compile:=1;

  DoneVerbose;
{$ifdef EXTDEBUG}
{$ifdef FPC}
  LostMemory:=system.HeapSize-MemAvail-EntryMemUsed;
  CheckMemory(LostMemory);
{$endif FPC}
{$ifndef newcg}
  Writeln('Repetitive firstpass = '+tostr(firstpass_several)+'/'+tostr(total_of_firstpass));
{$endif newcg}
{$endif EXTDEBUG}
{$ifdef MEMDEBUG}
  Writeln('Memory used: ',system.Heapsize);
{$endif}
{$ifdef fixLeaksOnError}
 {$ifdef tp}
  do_stop;
 {$else tp}
  do_stop();
 {$endif tp}
{$endif fixLeaksOnError}
end;


end.
{
  $Log: compiler.pas,v $
  Revision 1.1.2.5  2001/08/04 10:24:46  peter
    * move is_exe and is_library to status record

  Revision 1.1.2.4  2000/11/19 00:18:06  pierre
   + IsLibrary boolean for IDE

  Revision 1.1.2.3  2000/10/06 23:51:58  pierre
   * avoid IDE crash after compilation failure

  Revision 1.1.2.2  2000/08/21 09:09:08  jonas
    - removed catch unit from uses clause for Linux (clashed with fpcatch
     from IDE and is already in pp.pas for command line compiler)

  Revision 1.1.2.1  2000/08/02 19:36:33  peter
    * memdebug additions

  Revision 1.1  2000/07/13 06:29:48  michael
  + Initial import

  Revision 1.51  2000/06/30 20:23:33  peter
    * new message files layout with msg numbers (but still no code to
      show the number on the screen)

  Revision 1.50  2000/05/29 10:04:40  pierre
    * New bunch of Gabor changes

  Revision 1.49  2000/05/03 16:31:22  pierre
   + easier debug when memory is lost

  Revision 1.48  2000/04/05 21:18:04  pierre
   * set NOUSEEXCEPT to remove use of setjump/longjump

  Revision 1.47  2000/03/18 15:05:33  jonas
    + added $maxfpuregisters 0 for compile() procedure

  Revision 1.46  2000/02/09 13:22:50  peter
    * log truncated

  Revision 1.45  2000/01/11 17:16:04  jonas
    * removed a lot of memory leaks when an error is encountered (caused by
      procinfo and pstringcontainers). There are still plenty left though :)

  Revision 1.44  2000/01/11 16:56:22  jonas
    - removed call to do_stop at the end of compile() since it obviously breaks the
      automatic compiling of units. Make cycle worked though! 8)

  Revision 1.43  2000/01/11 16:53:24  jonas
    + call do_stop at the end of compile()

  Revision 1.42  2000/01/07 01:14:23  peter
    * updated copyright to 2000

  Revision 1.41  1999/12/02 17:34:34  peter
    * preprocessor support. But it fails on the caret in type blocks

  Revision 1.40  1999/11/18 13:43:48  pierre
   + IsExe global var needed for IDE

  Revision 1.39  1999/11/12 11:03:50  peter
    * searchpaths changed to stringqueue object

  Revision 1.38  1999/11/09 23:47:53  pierre
   + minimal_stop to avoid memory loss with -iTO switch

  Revision 1.37  1999/11/06 14:34:20  peter
    * truncated log to 20 revs

  Revision 1.36  1999/10/12 21:20:41  florian
    * new codegenerator compiles again

  Revision 1.35  1999/09/28 19:48:45  florian
    * bug 617 fixed

  Revision 1.34  1999/09/16 23:05:52  florian
    * m68k compiler is again compilable (only gas writer, no assembler reader)

  Revision 1.33  1999/09/07 15:10:04  pierre
   * use do_halt instead of halt

  Revision 1.32  1999/09/02 18:47:44  daniel
    * Could not compile with TP, some arrays moved to heap
    * NOAG386BIN default for TP
    * AG386* files were not compatible with TP, fixed.

  Revision 1.31  1999/08/20 10:17:01  michael
  + Patch from pierre

  Revision 1.30  1999/08/11 17:26:31  peter
    * tlinker object is now inherited for win32 and dos
    * postprocessexecutable is now a method of tlinker

  Revision 1.29  1999/08/09 22:13:43  peter
    * fixed writing of lost memory which should be after donecompiler

  Revision 1.28  1999/08/04 13:02:40  jonas
    * all tokens now start with an underscore
    * PowerPC compiles!!

  Revision 1.27  1999/08/02 21:28:56  florian
    * the main branch psub.pas is now used for
      newcg compiler

  Revision 1.26  1999/08/02 20:46:57  michael
  * Alpha aware switch detection

}
