1 {
2     This file is part of the Free Pascal Integrated Development Environment
3     Copyright (c) 1998 by Berczi Gabor
4 
5     Compiler call routines for the IDE
6 
7     See the file COPYING.FPC, included in this distribution,
8     for details about the copyright.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 
14  **********************************************************************}
15 {$i globdir.inc}
16 unit FPCompil;
17 
18 {2.0 compatibility}
19 {$ifdef VER2_0}
20   {$macro on}
21   {$define resourcestring := const}
22 {$endif}
23 
24 interface
25 
26 { don't redir under linux, because all stdout (also from the ide!) will
27   then be redired (PFV) }
28 { this should work now correctly because
29   RedirDisableAll and RedirEnableAll function are added in fpredir (PM) }
30 
31 { $define VERBOSETXT}
32 
33 {$mode objfpc}
34 
35 uses
36   { We need to include the exceptions from SysUtils, but the types from
37     Objects need to be used. Keep the order SysUtils,Objects }
38   SysUtils,
39   Objects,
40   FInput,
41   Drivers,Views,Dialogs,
42   WUtils,WViews,WCEdit,
43   FPSymbol,
44   FPViews;
45 
46 type
47   TCompileMode = (cBuild,cMake,cCompile,cRun);
48 
49 type
50     PCompilerMessage = ^TCompilerMessage;
51     TCompilerMessage = object(TMessageItem)
GetTextnull52       function GetText(MaxLen: Sw_Integer): String; virtual;
53     end;
54 
55     PCompilerMessageListBox = ^TCompilerMessageListBox;
56     TCompilerMessageListBox = object(TMessageListBox)
GetPalettenull57       function  GetPalette: PPalette; virtual;
58       procedure SelectFirstError;
59     end;
60 
61     PCompilerMessageWindow = ^TCompilerMessageWindow;
62     TCompilerMessageWindow = object(TFPWindow)
63       constructor Init;
64       procedure   HandleEvent(var Event: TEvent); virtual;
GetPalettenull65       function    GetPalette: PPalette; virtual;
66       procedure   Close;virtual;
67       destructor  Done; virtual;
68       procedure   SizeLimits(var Min, Max: TPoint); virtual;
69       procedure   AddMessage(AClass: longint;const Msg, Module: string; Line, Column: longint);
70       procedure   ClearMessages;
71       constructor Load(var S: TStream);
72       procedure   Store(var S: TStream);
73       procedure   SetState(AState: Word; Enable: Boolean); virtual;
74       procedure   UpdateCommands; virtual;
75     private
76       {CompileShowed : boolean;}
77       {Mode   : TCompileMode;}
78       MsgLB  : PCompilerMessageListBox;
79       {CurrST,
80       InfoST : PColorStaticText;}
81     end;
82 
83     PCompilerStatusDialog = ^TCompilerStatusDialog;
84     TCompilerStatusDialog = object(TCenterDialog)
85       ST    : PAdvancedStaticText;
86       KeyST : PColorStaticText;
87       starttime : real;
88       constructor Init;
89       destructor Done;virtual;
90       procedure   Update;
91       procedure SetStartTime(r : real);
92     end;
93 
94     TFPInputFile = class(tinputfile)
95       constructor Create(AEditor: PFileEditor);
96     protected
fileopennull97       function fileopen(const filename: ansistring): boolean; override;
fileseeknull98       function fileseek(pos: longint): boolean; override;
filereadnull99       function fileread(var databuf; maxsize: longint): longint; override;
fileeofnull100       function fileeof: boolean; override;
fileclosenull101       function fileclose: boolean; override;
102       procedure filegettime; override;
103     private
104       Editor: PFileEditor;
105       S: PStream;
106     end;
107 
108 const
109     CompilerMessageWindow : PCompilerMessageWindow  = nil;
110     CompilerStatusDialog  : PCompilerStatusDialog = nil;
111     CompileStamp          : longint = 0;
112     RestartingDebugger    : boolean = false;
113 
114 procedure DoCompile(Mode: TCompileMode);
NeedRecompilenull115 function  NeedRecompile(Mode :TCompileMode; verbose : boolean): boolean;
116 procedure ParseUserScreen;
117 
118 procedure RegisterFPCompile;
119 
120 const
121   CompilingHiddenFile : PSourceWindow = nil;
122 
123 implementation
124 
125 uses
126 {$ifdef Unix}
127   Unix, BaseUnix,
128 {$endif}
129 {$ifdef go32v2}
130   dpmiexcp,
131 {$endif}
132 {$ifdef Windows}
133   {$ifdef HasSignal}
134     signals,
135   {$endif}
136 {$endif}
137 { $ifdef HasSignal}
138   fpcatch,
139 { $endif HasSignal}
140   Dos,
141   Video,
142   globals,
143   StdDlg,App,tokens,
144   FVConsts,
145   CompHook, Compiler, systems, browcol,
146   WEditor,
147   FPRedir,FPDesk,
148   FPUsrScr,FPHelp,
149 {$ifndef NODEBUG}FPDebug,{$endif}
150   FPConst,FPVars,FPUtils,
151   FPCodCmp,FPIntf,FPSwitch;
152 
153 {$ifndef NOOBJREG}
154 const
155   RCompilerMessageListBox: TStreamRec = (
156      ObjType: 1211;
157      VmtLink: Ofs(TypeOf(TCompilerMessageListBox)^);
158      Load:    @TCompilerMessageListBox.Load;
159      Store:   @TCompilerMessageListBox.Store
160   );
161   RCompilerMessageWindow: TStreamRec = (
162      ObjType: 1212;
163      VmtLink: Ofs(TypeOf(TCompilerMessageWindow)^);
164      Load:    @TCompilerMessageWindow.Load;
165      Store:   @TCompilerMessageWindow.Store
166   );
167 {$endif}
168 {$ifdef useresstrings}
169 resourcestring
170 {$else}
171 const
172 {$endif}
173                 dialog_compilermessages = 'Compiler Messages';
174                 dialog_compilingwithmode = 'Compiling  (%s mode)';
175 
176                 { Compiler message classes }
177                 msg_class_normal   = '';
178                 msg_class_fatal    = 'Fatal';
179                 msg_class_error    = 'Error';
180                 msg_class_warning  = 'Warning';
181                 msg_class_note     = 'Note';
182                 msg_class_hint     = 'Hint';
183                 msg_class_macro    = 'Macro';
184                 msg_class_procedure= 'Procedure';
185                 msg_class_conditional = 'Conditional';
186                 msg_class_info     = 'Info';
187                 msg_class_status   = 'Status';
188                 msg_class_used     = 'Used';
189                 msg_class_tried    = 'Tried';
190                 msg_class_debug    = 'Debug';
191 
192                 { Compile status dialog texts }
193                 msg_compilingfile      = 'Compiling %s';
194                 msg_loadingunit        = 'Loading %s unit';
195                 msg_linkingfile        = 'Linking %s';
196                 msg_compiledone        = 'Done.';
197                 msg_failedtocompile    = 'Failed to compile...';
198                 msg_compilationaborted = 'Compilation aborted...';
199 
200                 msg_nothingtocompile = 'Oooops, nothing to compile.';
201                 msg_cantcompileunsavedfile = 'Can''t compile unsaved file.';
202 
203                 msg_couldnotcreatefile = 'could not create %s';
204                 msg_therearemoreerrorsinfile = 'There are more errors in file %s';
205                 msg_firstcompilationof = 'First compilation of %s';
206                 msg_recompilingbecauseof = 'Recompiling because of %s';
207 
208                 msg_errorinexternalcompilation = 'Error in external compilation';
209                 msg_iostatusis = 'IOStatus = %d';
210                 msg_executeresultis = 'ExecuteResult = %d';
211 
212                 { Status hints during compilation }
213                 msg_hint_pressesctocancel = 'Press ESC to cancel';
214                 msg_hint_compilesuccessfulpressenter = 'Compile successful: ~Press any key~';
215                 msg_hint_compilefailed = 'Compile failed';
216                 msg_hint_compileaborted = 'Compile aborted';
217                 msg_hint_pleasewait = 'Please wait...';
218 
219                 msg_cantopenfile = 'Can''t open %s';
220 
221 procedure ParseUserScreen;
222 var
223   Y,YMax : longint;
224   LEvent : TEvent;
225   Text,Attr : String;
226   DisplayCompilerWindow : boolean;
227   cc: integer;
228 
229     procedure SearchBackTrace;
230       var AText,ModuleName,st : String;
231           row : longint;
232       begin
233         if pos('  $',Text)=1 then
234           begin
235             AText:=Text;
236             Delete(Text,1,11);
237             While pos(' ',Text)=1 do
238               Delete(Text,1,1);
239             if pos('of ',Text)>0 then
240               begin
241                 ModuleName:=Copy(Text,pos('of ',Text)+3,255);
242                 While ModuleName[Length(ModuleName)]=' ' do
243                   Delete(ModuleName,Length(ModuleName),1);
244               end
245             else
246               ModuleName:='';
247             if pos('line ',Text)>0 then
248               begin
249                 Text:=Copy(Text,Pos('line ',Text)+5,255);
250                 st:=Copy(Text,1,Pos(' ',Text)-1);
251                 Val(st,row,cc);
252               end
253             else
254               row:=0;
255             CompilerMessageWindow^.AddMessage(V_Fatal or v_lineinfo,AText
256                   ,ModuleName,row,1);
257             DisplayCompilerWindow:=true;
258           end;
259       end;
260 
261     procedure InsertInMessages(Const TypeStr : String;_Type : longint;EnableDisplay : boolean);
262       var p,p2,col,row : longint;
263           St,ModuleName : string;
264 
265       begin
266         p:=pos(TypeStr,Text);
267         p2:=Pos('(',Text);
268         if (p>0)  and (p2>0) and (p2<p) then
269           begin
270             ModuleName:=Copy(Text,1,p2-1);
271             st:=Copy(Text,p2+1,255);
272             Val(Copy(st,1,pos(',',st)-1),row,cc);
273             st:=Copy(st,Pos(',',st)+1,255);
274             Val(Copy(st,1,pos(')',st)-1),col,cc);
275             CompilerMessageWindow^.AddMessage(_type,Copy(Text,pos(':',Text)+1,255)
276               ,ModuleName,row,col);
277             If EnableDisplay then
278               DisplayCompilerWindow:=true;
279           end;
280       end;
281 
282 begin
283   if not assigned(UserScreen) then
284     exit;
285   DisplayCompilerWindow:=false;
286   YMax:=UserScreen^.GetHeight;
287   PushStatus('Parsing User Screen');
288   CompilerMessageWindow^.Lock;
289   for Y:=0 to YMax do
290     begin
291       UserScreen^.GetLine(Y,Text,Attr);
292       if (y mod 10) = 0 then
293         begin
294           CompilerMessageWindow^.Unlock;
295           SetStatus('Parsing User Screen line '+IntToStr(y)+'/'+IntToStr(YMax));
296           CompilerMessageWindow^.Lock;
297         end;
298       GetKeyEvent(LEvent);
299       if (LEvent.What=evKeyDown) and (LEvent.KeyCode=kbEsc) then
300         break;
301       SearchBackTrace;
302       InsertInMessages(' Fatal:',v_Fatal or v_lineinfo,true);
303       InsertInMessages(' Error:',v_Error or v_lineinfo,true);
304       InsertInMessages(' Warning:',v_Warning or v_lineinfo,false);
305       InsertInMessages(' Note:',v_Note or v_lineinfo,false);
306       InsertInMessages(' Info:',v_Info or v_lineinfo,false);
307       InsertInMessages(' Hint:',v_Hint or v_lineinfo,false);
308     end;
309   if DisplayCompilerWindow then
310     begin
311       if not CompilerMessageWindow^.GetState(sfVisible) then
312         CompilerMessageWindow^.Show;
313       CompilerMessageWindow^.MakeFirst;
314       CompilerMessageWindow^.MsgLB^.SelectFirstError;
315     end;
316   CompilerMessageWindow^.UnLock;
317   PopStatus;
318 end;
319 
320 {*****************************************************************************
321                                TCompilerMessage
322 *****************************************************************************}
323 
TCompilerMessage.GetTextnull324 function TCompilerMessage.GetText(MaxLen: Sw_Integer): String;
325 var
326   ClassS: string[20];
327   S: string;
328 begin
329   case TClass and V_LevelMask of
330     V_Fatal   : ClassS:=msg_class_Fatal;
331     V_Error   : ClassS:=msg_class_Error;
332     V_Normal  : ClassS:=msg_class_Normal;
333     V_Warning : ClassS:=msg_class_Warning;
334     V_Note    : ClassS:=msg_class_Note;
335     V_Hint    : ClassS:=msg_class_Hint;
336 {$ifdef VERBOSETXT}
337     V_Conditional : ClassS:=msg_class_conditional;
338     V_Info    : ClassS:=msg_class_info;
339     V_Status  : ClassS:=msg_class_status;
340     V_Used    : ClassS:=msg_class_used;
341     V_Tried   : ClassS:=msg_class_tried;
342     V_Debug   : ClassS:=msg_class_debug;
343     else
344       ClassS:='???';
345 {$endif}
346     else
347       ClassS:='';
348   end;
349   if ClassS<>'' then
350    ClassS:=RExpand(ClassS,0)+': ';
351   if assigned(Module) and
352      ((TClass and V_LineInfo)=V_LineInfo) then
353     begin
354       if Row>0 then
355        begin
356          if Col>0 then
357           S:=NameAndExtOf(Module^)+'('+IntToStr(Row)+','+IntToStr(Col)+') '+ClassS
358          else
359           S:=NameAndExtOf(Module^)+'('+IntToStr(Row)+') '+ClassS;
360        end
361       else
362        S:=NameAndExtOf(Module^)+'('+IntToStr(Row)+') '+ClassS
363     end
364   else
365     S:=ClassS;
366   if assigned(Text) then
367     S:=S+Text^;
368   if length(S)>MaxLen then
369     S:=copy(S,1,MaxLen-2)+'..';
370   GetText:=S;
371 end;
372 
373 
374 {*****************************************************************************
375                              TCompilerMessageListBox
376 *****************************************************************************}
377 
GetPalettenull378 function TCompilerMessageListBox.GetPalette: PPalette;
379 const
380   P: string[length(CBrowserListBox)] = CBrowserListBox;
381 begin
382   GetPalette:=PPalette(@P);
383 end;
384 
385 procedure TCompilerMessageListBox.SelectFirstError;
IsErrornull386   function IsError(P : PCompilerMessage) : boolean;
387     begin
388       IsError:=(P^.TClass and (V_Fatal or V_Error))<>0;
389     end;
390   var
391     P : PCompilerMessage;
392 begin
393   P:=List^.FirstThat(@IsError);
394   If Assigned(P) then
395     Begin
396       FocusItem(List^.IndexOf(P));
397       DrawView;
398     End;
399 end;
400 
401 
402 {*****************************************************************************
403                                 TCompilerMessageWindow
404 *****************************************************************************}
405 
406 constructor TCompilerMessageWindow.Init;
407 var R: TRect;
408     HSB,VSB: PScrollBar;
409 begin
410   Desktop^.GetExtent(R);
411   R.A.Y:=R.B.Y-7;
412   inherited Init(R,dialog_compilermessages,{SearchFreeWindowNo}wnNoNumber);
413   HelpCtx:=hcCompilerMessagesWindow;
414 
415   AutoNumber:=true;
416 
417   HSB:=StandardScrollBar(sbHorizontal+sbHandleKeyboard);
418   HSB^.GrowMode:=gfGrowLoY+gfGrowHiX+gfGrowHiY;
419   Insert(HSB);
420   VSB:=StandardScrollBar(sbVertical+sbHandleKeyboard);
421   VSB^.GrowMode:=gfGrowLoX+gfGrowHiX+gfGrowHiY;
422   Insert(VSB);
423 
424   GetExtent(R);
425   R.Grow(-1,-1);
426   New(MsgLB, Init(R, HSB, VSB));
427 
428   MsgLB^.GrowMode:=gfGrowHiX+gfGrowHiY;
429   Insert(MsgLB);
430   CompilerMessageWindow:=@self;
431 end;
432 
433 
434 procedure TCompilerMessageWindow.AddMessage(AClass: longint;const Msg, Module: string; Line, Column: longint);
435 begin
436   if (AClass and V_LineInfo)<>V_LineInfo then
437     Line:=0;
438   MsgLB^.AddItem(New(PCompilerMessage,Init(AClass, Msg, MsgLB^.AddModuleName(Module), Line, Column)));
439   if (@Self=CompilerMessageWindow) and ((AClass = V_fatal) or (AClass = V_Error)) then
440     begin
441       if not GetState(sfVisible) then
442         Show;
443       if Desktop^.First<>PView(CompilerMessageWindow) then
444         MakeFirst;
445     end;
446 end;
447 
448 
449 procedure TCompilerMessageWindow.ClearMessages;
450 begin
451   MsgLB^.Clear;
452   ReDraw;
453 end;
454 
455 
456 {procedure TCompilerMessageWindow.Updateinfo;
457 begin
458   if CompileShowed then
459    begin
460      InfoST^.SetText(
461        RExpand(' Main file : '#1#$7f+Copy(SmartPath(MainFile),1,39),40)+#2+
462          'Total lines  : '#1#$7e+IntToStr(Status.CompiledLines)+#2#13+
463        RExpand(' Target    : '#1#$7f+KillTilde(TargetSwitches^.ItemName(TargetSwitches^.GetCurrSel)),40)+#2+
464          'Total errors : '#1#$7e+IntToStr(Status.ErrorCount)
465      );
466      if status.currentline>0 then
467       CurrST^.SetText(' Status: '#1#$7e+status.currentsource+'('+IntToStr(status.currentline)+')'#2)
468      else
469       CurrST^.SetText(' Status: '#1#$7e+status.currentsource+#2);
470    end;
471   ReDraw;
472 end;}
473 
474 
475 procedure TCompilerMessageWindow.HandleEvent(var Event: TEvent);
476 begin
477   case Event.What of
478     evBroadcast :
479       case Event.Command of
480         cmListFocusChanged :
481           if Event.InfoPtr=MsgLB then
482             Message(Application,evBroadcast,cmClearLineHighlights,@Self);
483       end;
484   end;
485   inherited HandleEvent(Event);
486 end;
487 
488 
489 procedure TCompilerMessageWindow.SizeLimits(var Min, Max: TPoint);
490 begin
491   inherited SizeLimits(Min,Max);
492   Min.X:=20;
493   Min.Y:=4;
494 end;
495 
496 
497 procedure TCompilerMessageWindow.Close;
498 begin
499   Hide;
500 end;
501 
502 
GetPalettenull503 function TCompilerMessageWindow.GetPalette: PPalette;
504 const
505   S : string[length(CBrowserWindow)] = CBrowserWindow;
506 begin
507   GetPalette:=PPalette(@S);
508 end;
509 
510 
511 constructor TCompilerMessageWindow.Load(var S: TStream);
512 begin
513   inherited Load(S);
514   GetSubViewPtr(S,MsgLB);
515 end;
516 
517 
518 procedure TCompilerMessageWindow.Store(var S: TStream);
519 begin
520   if MsgLB^.List=nil then
521     MsgLB^.NewList(New(PCollection, Init(100,100)));
522   inherited Store(S);
523   PutSubViewPtr(S,MsgLB);
524 end;
525 
526 procedure TCompilerMessageWindow.UpdateCommands;
527 var Active: boolean;
528 begin
529   Active:=GetState(sfActive);
530   SetCmdState(CompileCmds,Active);
531   Message(Application,evBroadcast,cmCommandSetChanged,nil);
532 end;
533 
534 procedure TCompilerMessageWindow.SetState(AState: Word; Enable: Boolean);
535 var OldState: word;
536 begin
537   OldState:=State;
538   inherited SetState(AState,Enable);
539   if ((AState and sfActive)<>0) and (((OldState xor State) and sfActive)<>0) then
540     UpdateCommands;
541 end;
542 
543 destructor TCompilerMessageWindow.Done;
544 begin
545   CompilerMessageWindow:=nil;
546   inherited Done;
547 end;
548 
549 
550 {****************************************************************************
551                           CompilerStatusDialog
552 ****************************************************************************}
553 
getrealtimenull554 function getrealtime : real;
555 var
556 {$IFDEF USE_SYSUTILS}
557   h,m,s,s1000 : word;
558 {$ELSE USE_SYSUTILS}
559   h,m,s,s100 : word;
560 {$ENDIF USE_SYSUTILS}
561 begin
562 {$IFDEF USE_SYSUTILS}
563   DecodeTime(Time,h,m,s,s1000);
564   getrealtime:=h*3600.0+m*60.0+s+s1000/1000.0;
565 {$ELSE USE_SYSUTILS}
566   gettime(h,m,s,s100);
567   getrealtime:=h*3600.0+m*60.0+s+s100/100.0;
568 {$ENDIF USE_SYSUTILS}
569 end;
570 
571 constructor TCompilerStatusDialog.Init;
572 var R: TRect;
573 begin
574   R.Assign(0,0,56,11);
575   ClearFormatParams; AddFormatParamStr(KillTilde(SwitchesModeName[SwitchesMode]));
576   inherited Init(R, FormatStrF(dialog_compilingwithmode, FormatParams));
577   starttime:=getrealtime;
578   GetExtent(R); R.B.Y:=11;
579   R.Grow(-3,-2);
580   New(ST, Init(R, ''));
581   Insert(ST);
582   GetExtent(R); R.B.Y:=11;
583   R.Grow(-1,-1); R.A.Y:=R.B.Y-1;
584   New(KeyST, Init(R, '', Blue*16+White+longint($80+Blue*16+White)*256,true));
585   Insert(KeyST);
586   { Reset Status infos see bug 1585 }
587   Fillchar(Status,SizeOf(Status),#0);
588 end;
589 
590 destructor TCompilerStatusDialog.Done;
591 begin
592   if @Self=CompilerStatusDialog then
593     CompilerStatusDialog:=nil;
594   Inherited Done;
595 end;
596 
597 procedure TCompilerStatusDialog.SetStartTime(r : real);
598   begin
599     starttime:=r;
600   end;
601 
602 procedure TCompilerStatusDialog.Update;
603 var
604   StatusS,KeyS: string;
605   hstatus : TFPCHeapStatus;
606   r : real;
607 const
608   MaxFileNameSize = 46;
609 begin
610   case CompilationPhase of
611     cpCompiling :
612       begin
613         ClearFormatParams;
614         if Upcase(Status.currentmodulestate)='COMPILE' then
615           begin
616             AddFormatParamStr(ShrinkPath(SmartPath(Status.Currentsourcepath+Status.CurrentSource),
617               MaxFileNameSize - Length(msg_compilingfile)));
618             StatusS:=FormatStrF(msg_compilingfile,FormatParams);
619           end
620         else
621           begin
622             if Status.CurrentSource='' then
623               StatusS:='      '
624             else
625               begin
626                 StatusS:=ShrinkPath(SmartPath(DirAndNameOf(Status.Currentsourcepath+Status.CurrentSource)),
627                   MaxFileNameSize-Length(msg_loadingunit));
628                 AddFormatParamStr(StatusS);
629                 StatusS:=FormatStrF(msg_loadingunit,FormatParams);
630               end;
631           end;
632         KeyS:=msg_hint_pressesctocancel;
633       end;
634     cpLinking   :
635       begin
636         ClearFormatParams;
637         AddFormatParamStr(ShrinkPath(ExeFile,
638           MaxFileNameSize-Length(msg_linkingfile)));
639         StatusS:=FormatStrF(msg_linkingfile,FormatParams);
640         KeyS:=msg_hint_pleasewait;
641       end;
642     cpDone      :
643       begin
644         StatusS:=msg_compiledone;
645         KeyS:=msg_hint_compilesuccessfulpressenter;
646       end;
647     cpFailed    :
648       begin
649         StatusS:=msg_failedtocompile;
650         KeyS:=msg_hint_compilefailed;
651       end;
652     cpAborted    :
653       begin
654         StatusS:=msg_compilationaborted;
655         KeyS:=msg_hint_compileaborted;
656       end;
657   end;
658   ClearFormatParams;
659   AddFormatParamStr(ShrinkPath(SmartPath(MainFile),
660     MaxFileNameSize-Length('Main file: %s')));
661   AddFormatParamStr(StatusS);
662   AddFormatParamStr(KillTilde(TargetSwitches^.ItemName(TargetSwitches^.GetCurrSel)));
663   AddFormatParamInt(Status.CurrentLine);
664   AddFormatParamInt(Status.CompiledLines);
665   hstatus:=GetFPCHeapStatus;
666   AddFormatParamInt(hstatus.CurrHeapUsed div 1024);
667   AddFormatParamInt(hstatus.CurrHeapSize div 1024);
668   AddFormatParamInt(Status.ErrorCount);
669   r:=getrealtime;
670   AddFormatParamInt(trunc(r-starttime));
671   AddFormatParamInt(trunc(frac(r-starttime)*10));
672   ST^.SetText(
673    FormatStrF(
674     'Main file: %s'#13+
675     '%s'+#13#13+
676     'Target: %s'#13+
677     'Line number: %6d     '+'Total lines:      %6d'+#13+
678     'Used memory: %6dK    '+'Allocated memory: %6dK'#13+
679     'Total errors:%6d     '+'Compile time: %8d.%1ds',
680    FormatParams)
681   );
682   KeyST^.SetText(^C+KeyS);
683 end;
684 
685 
686 {****************************************************************************
687                                Compiler Hooks
688 ****************************************************************************}
689 
690 const
691   lasttime  : real = 0;
692 
CompilerStatusnull693 function CompilerStatus: boolean;
694   var
695      event : tevent;
696 
697 begin
698   GetKeyEvent(Event);
699   if (Event.What=evKeyDown) and (Event.KeyCode=kbEsc) then
700     begin
701        CompilationPhase:=cpAborted;
702        { update info messages }
703        if assigned(CompilerStatusDialog) then
704         begin
705 {$ifdef redircompiler}
706           RedirDisableAll;
707 {$endif}
708           CompilerStatusDialog^.Update;
709 {$ifdef redircompiler}
710           RedirEnableAll;
711 {$endif}
712         end;
713        CompilerStatus:=true;
714        exit;
715     end;
716 { only display line info every 100 lines, ofcourse all other messages
717   will be displayed directly }
718   if (getrealtime-lasttime>=CompilerStatusUpdateDelay) or (status.compiledlines=1) then
719    begin
720      lasttime:=getrealtime;
721      { update info messages }
722 {$ifdef redircompiler}
723           RedirDisableAll;
724 {$endif}
725      if assigned(CompilerStatusDialog) then
726       CompilerStatusDialog^.Update;
727 {$ifdef redircompiler}
728           RedirEnableAll;
729 {$endif}
730      { update memory usage }
731      { HeapView^.Update; }
732    end;
733   CompilerStatus:=false;
734 end;
735 
CompilerGetNamedFileTimenull736 Function  CompilerGetNamedFileTime(const filename : ansistring) : Longint;
737 var t: longint;
738     W: PSourceWindow;
739 begin
740   W:=EditorWindowFile(FExpand(filename));
741   if Assigned(W) and (W^.Editor^.GetModified) then
742     t:=Now
743   else
744     t:=def_getnamedfiletime(filename);
745   CompilerGetNamedFileTime:=t;
746 end;
747 
CompilerOpenInputFilenull748 function CompilerOpenInputFile(const filename: ansistring): tinputfile;
749 var f: tinputfile;
750     W: PSourceWindow;
751 begin
752   if assigned(CompilingHiddenFile) and
753      (NameandExtof(filename)=CompilingHiddenFile^.Editor^.Filename) then
754     W:=CompilingHiddenFile
755   else
756     W:=EditorWindowFile(FExpand(filename));
757   if Assigned(W) and (W^.Editor^.GetModified) then
758     f:=TFPInputFile.Create(W^.Editor)
759   else
760     f:=def_openinputfile(filename);
761   if assigned(W) then
762     W^.Editor^.CompileStamp:=CompileStamp;
763   CompilerOpenInputFile:=f;
764 end;
765 
CompilerCommentnull766 function CompilerComment(Level:Longint; const s:ansistring):boolean;
767 begin
768   CompilerComment:=false;
769   if (status.verbosity and Level)<>0 then
770    begin
771 {$ifdef redircompiler}
772      RedirDisableAll;
773 {$endif}
774 
775      if not CompilerMessageWindow^.GetState(sfVisible) then
776        CompilerMessageWindow^.Show;
777      if Desktop^.First<>PView(CompilerMessageWindow) then
778        CompilerMessageWindow^.MakeFirst;
779      CompilerMessageWindow^.AddMessage(Level,S,status.currentsourcepath+status.currentsource,
780        status.currentline,status.currentcolumn);
781      { update info messages }
782      if assigned(CompilerStatusDialog) then
783       CompilerStatusDialog^.Update;
784 {$ifdef redircompiler}
785       RedirEnableAll;
786 {$endif}
787      { update memory usage }
788      { HeapView^.Update; }
789    end;
790 end;
791 
792 
793 {****************************************************************************
794                                  DoCompile
795 ****************************************************************************}
796 
797 { This function must return '' if
798   "Options|Directories|Exe and PPU directory" is empty }
GetExePathnull799 function GetExePath: string;
800 var Path: string;
801     I: Sw_integer;
802 begin
803   Path:='';
804   if DirectorySwitches<>nil then
805     with DirectorySwitches^ do
806     for I:=0 to ItemCount-1 do
807       begin
808         if ItemParam(I)='-FE' then
809           begin
810             Path:=GetStringItem(I);
811             Break;
812           end;
813       end;
814   if Path<>'' then
815     GetExePath:=CompleteDir(FExpand(Path))
816   else
817     GetExePath:='';
818 end;
819 
GetMainFilenull820 function GetMainFile(Mode: TCompileMode): string;
821 var FileName: string;
822     P : PSourceWindow;
823 begin
824   if assigned(CompilingHiddenFile) then
825     P:=CompilingHiddenFile
826   else
827     P:=Message(Desktop,evBroadcast,cmSearchWindow,nil);
828   if (PrimaryFileMain='') and (P=nil) then
829     FileName:='' { nothing to compile }
830   else
831     begin
832       if (PrimaryFileMain<>'') and (Mode<>cCompile) then
833         FileName:=PrimaryFileMain
834       else if assigned(P) then
835         begin
836           FileName:=P^.Editor^.FileName;
837           if FileName='' then
838             begin
839               P^.Editor^.SaveAsk(true);
840               FileName:=P^.Editor^.FileName;
841             end;
842         end
843       else
844         FileName:='';
845     end;
846   {$ifdef Unix}
847   If (FileName<>'') then
848     FileName:=FExpand(FileName);
849   {$else}
850   If (FileName<>'') then
851     FileName:=FixFileName(FExpand(FileName));
852   {$endif}
853   GetMainFile:=FileName;
854 end;
855 
856 procedure ResetErrorMessages;
857   procedure ResetErrorLine(P: PView);
858   begin
859     if assigned(P) and
860        (TypeOf(P^)=TypeOf(TSourceWindow)) then
861        PSourceWindow(P)^.Editor^.SetErrorMessage('');
862   end;
863 begin
864   Desktop^.ForEach(@ResetErrorLine);
865 end;
866 
867 
868 procedure DoCompile(Mode: TCompileMode);
869 
IsExitEventnull870   function IsExitEvent(E: TEvent): boolean;
871   begin
872     { following suggestion by Harsha Senanayake }
873     IsExitEvent:=(E.What=evKeyDown);
874   end;
GetTargetExeExtnull875   function GetTargetExeExt : string;
876     begin
877         GetTargetExeExt:=target_info.exeext;
878      end;
879 var
880   s,FileName: string;
881   ErrFile : Text;
882   MustRestartDebugger : boolean;
883   Error,LinkErrorCount : longint;
884   E : TEvent;
885   DummyView: PView;
886   PPasFile : string[64];
887 begin
888   AskRecompileIfModifiedFlag:=true;
889 { Get FileName }
890   FileName:=GetMainFile(Mode);
891   if FileName='' then
892     begin
893       ErrorBox(msg_nothingtocompile,nil);
894       Exit;
895     end else
896   { THis is not longer necessary as unsaved files are loaded from a memorystream,
897     and with the file as primaryfile set it is already incompatible with itself
898    if FileName='*' then
899     begin
900       ErrorBox(msg_cantcompileunsavedfile,nil);
901       Exit;
902     end; }
903   PushStatus('Beginning compilation...');
904 { Show Compiler Messages Window }
905 {  if not CompilerMessageWindow^.GetState(sfVisible) then
906    CompilerMessageWindow^.Show;
907   CompilerMessageWindow^.MakeFirst;}
908   CompilerMessageWindow^.ClearMessages;
909   { Tell why we compile }
910   NeedRecompile(Mode,true);
911 
912   MainFile:=FileName;
913   SetStatus('Writing switches to file...');
914   WriteSwitches(SwitchesPath);
915   { leaving open browsers leads to crashes !! (PM) }
916   SetStatus('Preparing symbol info...');
917   CloseAllBrowsers;
918   if ((DesktopFileFlags and dfSymbolInformation)<>0) then
919     WriteSymbolsFile(BrowserName);
920 {  MainFile:=FixFileName(FExpand(FileName));}
921   SetStatus('Preparing to compile...'+NameOf(MainFile));
922 { Reset }
923   CtrlBreakHit:=false;
924 { Create Compiler Status Dialog }
925   CompilationPhase:=cpCompiling;
926   if not assigned(CompilingHiddenFile) then
927     begin
928       New(CompilerStatusDialog, Init);
929       CompilerStatusDialog^.SetStartTime(getrealtime);
930       CompilerStatusDialog^.SetState(sfModal,true);
931       { disable window closing }
932       CompilerStatusDialog^.Flags:=CompilerStatusDialog^.Flags and not wfclose;
933       Application^.Insert(CompilerStatusDialog);
934       CompilerStatusDialog^.Update;
935     end;
936   { Restore dir that could be changed during debugging }
937   {$I-}
938    ChDir(StartUpDir);
939   {$I+}
940   EatIO;
941 { hook compiler output }
942   do_status:=@CompilerStatus;
943   do_comment:=@CompilerComment;
944   do_openinputfile:=@CompilerOpenInputFile;
945   do_getnamedfiletime:=@CompilerGetNamedFileTime;
946   do_initsymbolinfo:=@InitBrowserCol;
947   do_donesymbolinfo:=@DoneBrowserCol;
948   do_extractsymbolinfo:=@CreateBrowserCol;
949 { Compile ! }
950 {$ifdef redircompiler}
951   ChangeRedirOut(FPOutFileName,false);
952   ChangeRedirError(FPErrFileName,false);
953 {$endif}
954   { insert "" around name so that spaces are allowed }
955   { only supported in compiler after 2000/01/14 PM   }
956   if pos(' ',FileName)>0 then
957     FileName:='"'+FileName+'"';
958   if mode=cBuild then
959     FileName:='-B '+FileName;
960   { tokens are created and distroed by compiler.compile !! PM }
961   DoneTokens;
962   PPasFile:='ppas'+source_info.scriptext;
963   WUtils.DeleteFile(GetExePath+PpasFile);
964   SetStatus('Compiling...');
965   inc(CompileStamp);
966   ResetErrorMessages;
967   {$ifndef NODEBUG}
968   MustRestartDebugger:=false;
969   if assigned(Debugger) then
970   if Debugger^.HasExe then
971     begin
972       Debugger^.Reset;
973       MustRestartDebugger:=true;
974     end;
975   {$endif NODEBUG}
976   try
977     FpIntF.Compile(FileName,SwitchesPath);
978   except
979     on ECompilerAbort do
980       CompilerMessageWindow^.AddMessage(V_error,'Error during compilation','',0,0);
981     on E:Exception do
982       CompilerMessageWindow^.AddMessage(V_error,E.Message+' during compilation','',0,0);
983   end;
984   SetStatus('Finished compiling...');
985 
986   { Retrieve created exefile }
987   If GetEXEPath<>'' then
988     EXEFile:=FixFileName(GetEXEPath+NameOf(MainFile)+GetTargetExeExt)
989   else
990     EXEFile:=DirOf(MainFile)+NameOf(MainFile)+GetTargetExeExt;
991   DefaultReplacements(ExeFile);
992   { tokens are created and distroyed by compiler.compile !! PM }
993   InitTokens;
994   if LinkAfter and
995      ExistsFile(GetExePath+PpasFile) and
996      (CompilationPhase<>cpAborted) and
997      (status.errorCount=0) then
998     begin
999        CompilationPhase:=cpLinking;
1000        if assigned(CompilerStatusDialog) then
1001          CompilerStatusDialog^.Update;
1002        SetStatus('Assembling and/or linking...');
1003 {$ifndef redircompiler}
1004        { At least here we want to catch output
1005         of batch file PM }
1006        ChangeRedirOut(FPOutFileName,false);
1007        ChangeRedirError(FPErrFileName,false);
1008 {$endif}
1009 {$ifdef Unix}
1010        error:=0;
1011        If fpsystem(GetExePath+PpasFile)=-1 Then
1012         Error:=fpgeterrno;
1013 {$else}
1014        DosExecute(GetEnv('COMSPEC'),'/C '+GetExePath+PpasFile);
1015        Error:=DosError;
1016 {$endif}
1017        SetStatus('Finished linking...');
1018        RestoreRedirOut;
1019        RestoreRedirError;
1020        if Error<>0 then
1021          Inc(status.errorCount);
1022        if Status.IsExe and not Status.IsLibrary and not ExistsFile(EXEFile) then
1023          begin
1024            Inc(status.errorCount);
1025            ClearFormatParams; AddFormatParamStr(ExeFile);
1026            CompilerMessageWindow^.AddMessage(V_error,FormatStrF(msg_couldnotcreatefile,FormatParams),'',0,0);
1027          {$I-}
1028            Assign(ErrFile,FPErrFileName);
1029            Reset(ErrFile);
1030            if EatIO<>0 then
1031              ErrorBox(FormatStrStr(msg_cantopenfile,FPErrFileName),nil)
1032            else
1033            begin
1034              LinkErrorCount:=0;
1035              While not eof(ErrFile) and (LinkErrorCount<25) do
1036                begin
1037                  readln(ErrFile,s);
1038                  CompilerMessageWindow^.AddMessage(V_error,s,'',0,0);
1039                  inc(LinkErrorCount);
1040                end;
1041              if not eof(ErrFile) then
1042              begin
1043                ClearFormatParams; AddFormatParamStr(FPErrFileName);
1044                CompilerMessageWindow^.AddMessage(V_error,
1045                  FormatStrF(msg_therearemoreerrorsinfile,FormatParams),'',0,0);
1046              end;
1047 
1048              Close(ErrFile);
1049            end;
1050            EatIO;
1051          {$I+}
1052          end
1053        else if error=0 then
1054          WUtils.DeleteFile(GetExePath+PpasFile);
1055     end;
1056 {$ifdef redircompiler}
1057   RestoreRedirOut;
1058   RestoreRedirError;
1059 {$endif}
1060   PopStatus;
1061 { Set end status }
1062   if not (CompilationPhase in [cpAborted,cpFailed]) then
1063     if (status.errorCount=0) then
1064       begin
1065         CompilationPhase:=cpDone;
1066         LastCompileTime := cardinal(Now);
1067       end
1068     else
1069       CompilationPhase:=cpFailed;
1070 { Show end status }
1071   { reenable window closing }
1072   if assigned(CompilerStatusDialog) then
1073     begin
1074       CompilerStatusDialog^.Flags:=CompilerStatusDialog^.Flags or wfclose;
1075       CompilerStatusDialog^.Update;
1076       CompilerStatusDialog^.ReDraw;
1077       CompilerStatusDialog^.SetState(sfModal,false);
1078       if ((CompilationPhase in [cpAborted,cpDone,cpFailed]) or (ShowStatusOnError))
1079         and ((Mode<>cRun) or (CompilationPhase<>cpDone)) then
1080        repeat
1081          CompilerStatusDialog^.GetEvent(E);
1082          if IsExitEvent(E)=false then
1083           CompilerStatusDialog^.HandleEvent(E);
1084        until IsExitEvent(E) or not assigned(CompilerStatusDialog);
1085        {if IsExitEvent(E) then
1086          Application^.PutEvent(E);}
1087       if assigned(CompilerStatusDialog) then
1088         begin
1089           Application^.Delete(CompilerStatusDialog);
1090           Dispose(CompilerStatusDialog, Done);
1091         end;
1092     end;
1093   CompilerStatusDialog:=nil;
1094 { end compilation returns true if the messagewindow should be removed }
1095   if CompilationPhase=cpDone then
1096    begin
1097      CompilerMessageWindow^.Hide;
1098      { This is the last compiled main file }
1099      PrevMainFile:=MainFile;
1100      MainHasDebugInfo:=DebugInfoSwitches^.GetCurrSelParam<>'-';
1101    end;
1102 { Update the app }
1103   Message(Application,evCommand,cmUpdate,nil);
1104   DummyView:=Desktop^.First;
1105   while (DummyView<>nil) and (DummyView^.GetState(sfVisible)=false) do
1106   begin
1107     DummyView:=DummyView^.NextView;
1108   end;
1109   with DummyView^ do
1110    if GetState(sfVisible) then
1111     begin
1112       SetState(sfSelected,false);
1113       SetState(sfSelected,true);
1114     end;
1115   if Assigned(CompilerMessageWindow) then
1116     with CompilerMessageWindow^ do
1117       begin
1118         if GetState(sfVisible) then
1119           begin
1120             SetState(sfSelected,false);
1121             SetState(sfSelected,true);
1122           end;
1123         if (status.errorCount>0) then
1124           MsgLB^.SelectFirstError;
1125       end;
1126   { ^^^ we need this trick to reactivate the desktop }
1127   EditorModified:=false;
1128 {$ifndef NODEBUG}
1129   if MustRestartDebugger then
1130     InitDebugger;
1131 {$endif NODEBUG}
1132   { In case we have something that the compiler touched }
1133   AskToReloadAllModifiedFiles;
1134   { Try to read Browser info in again if compilation failure !! }
1135   if Not Assigned(Modules) and (CompilationPhase<>cpDone) and
1136      ((DesktopFileFlags and dfSymbolInformation)<>0) then
1137     ReadSymbolsFile(BrowserName);
1138   if UseAllUnitsInCodeComplete and not assigned(CompilingHiddenFile) then
1139     AddAvailableUnitsToCodeComplete(false);
1140 end;
1141 
NeedRecompilenull1142 function NeedRecompile(Mode :TCompileMode; verbose : boolean): boolean;
1143 var Need: boolean;
1144     I: sw_integer;
1145     SF: PSourceFile;
1146     SourceTime,PPUTime,ObjTime: longint;
1147     W: PSourceWindow;
1148 begin
1149   if Assigned(SourceFiles)=false then
1150      Need:={(EditorModified=true)}true
1151   else
1152     begin
1153       Need:=(PrevMainFile<>GetMainFile(Mode)) and (PrevMainFile<>'');
1154       if Need then
1155         begin
1156           if verbose then
1157           begin
1158             ClearFormatParams; AddFormatParamStr(GetMainFile(Mode));
1159             CompilerMessageWindow^.AddMessage(V_info,
1160               FormatStrF(msg_firstcompilationof,FormatParams),
1161               '',0,0);
1162           end;
1163         end
1164       else
1165         for I:=0 to SourceFiles^.Count-1 do
1166           begin
1167             SF:=SourceFiles^.At(I);
1168             SourceTime:=wutils.GetFileTime(SF^.GetSourceFileName);
1169             PPUTime:=wutils.GetFileTime(SF^.GetPPUFileName);
1170             ObjTime:=wutils.GetFileTime(SF^.GetObjFileName);
1171 {            writeln('S: ',SF^.GetSourceFileName,' - ',SourceTime);
1172             writeln('P: ',SF^.GetPPUFileName,' - ',PPUTime);
1173             writeln('O: ',SF^.GetObjFileName,' - ',ObjTime);
1174             writeln('------');}
1175             { some units don't generate object files }
1176             W:=EditorWindowFile(SF^.GetSourceFileName);
1177             if (SourceTime<>-1) then
1178               if ((SourceTime>PPUTime) or
1179                  ((SourceTime>ObjTime) and
1180                  (ObjTime<>-1))) or
1181                  (assigned(W) and (W^.Editor^.CompileStamp<0)) then
1182                 begin
1183                   Need:=true;
1184                   if verbose then
1185                   begin
1186                     ClearFormatParams; AddFormatParamStr(SF^.GetSourceFileName);
1187                     CompilerMessageWindow^.AddMessage(V_info,
1188                       FormatStrF(msg_recompilingbecauseof,FormatParams),
1189                       SF^.GetSourceFileName,1,1);
1190                   end;
1191                   Break;
1192                 end;
1193           end;
1194 {      writeln('Need?', Need); system.readln;}
1195     end;
1196 
1197   NeedRecompile:=Need;
1198 end;
1199 
1200 
1201 constructor TFPInputFile.Create(AEditor: PFileEditor);
1202 begin
1203   if not Assigned(AEditor) then Fail;
1204   if inherited Create(AEditor^.FileName)=nil then
1205     Fail;
1206   Editor:=AEditor;
1207 end;
1208 
1209 
fileopennull1210 function TFPInputFile.fileopen(const filename: ansistring): boolean;
1211 var OK: boolean;
1212 begin
1213   S:=New(PMemoryStream, Init(0,0));
1214   OK:=Assigned(S) and (S^.Status=stOK);
1215   if OK then OK:=Editor^.SaveToStream(S);
1216   if OK then
1217     S^.Seek(0)
1218   else
1219     begin
1220       if Assigned(S) then Dispose(S, Done);
1221       S:=nil;
1222     end;
1223   fileopen:=OK;
1224 end;
1225 
fileseeknull1226 function TFPInputFile.fileseek(pos: longint): boolean;
1227 var OK: boolean;
1228 begin
1229   OK:=assigned(S);
1230   if OK then
1231   begin
1232     S^.Reset;
1233     S^.Seek(pos);
1234     OK:=(S^.Status=stOK);
1235   end;
1236   fileseek:=OK;
1237 end;
1238 
filereadnull1239 function TFPInputFile.fileread(var databuf; maxsize: longint): longint;
1240 var
1241     size: longint;
1242 begin
1243   if not assigned(S) then size:=0 else
1244   begin
1245     size:=min(maxsize,(S^.GetSize-S^.GetPos));
1246     S^.Read(databuf,size);
1247     if S^.Status<>stOK then size:=0;
1248   end;
1249   fileread:=size;
1250 end;
1251 
TFPInputFile.fileeofnull1252 function TFPInputFile.fileeof: boolean;
1253 var EOF: boolean;
1254 begin
1255   EOF:=not assigned(S);
1256   if not EOF then
1257     EOF:=(S^.Status<>stOK) or (S^.GetPos=S^.GetSize);
1258   fileeof:=EOF;
1259 end;
1260 
fileclosenull1261 function TFPInputFile.fileclose: boolean;
1262 var OK: boolean;
1263 begin
1264   OK:=assigned(S);
1265   if OK then
1266   begin
1267     S^.Reset;
1268     Dispose(S, Done);
1269     S:=nil;
1270     OK:=true;
1271   end;
1272   fileclose:=OK;
1273 end;
1274 
1275 procedure tfpinputfile.filegettime;
1276 var
1277   dt : datetime;
1278   hsec,wday : word;
1279 begin
1280   { current time }
1281   dos.getdate(dt.year,dt.month,dt.day,wday);
1282   dos.gettime(dt.hour,dt.min,dt.sec,hsec);
1283   packtime(dt,filetime);
1284 end;
1285 
1286 procedure RegisterFPCompile;
1287 begin
1288 {$ifndef NOOBJREG}
1289   RegisterType(RCompilerMessageListBox);
1290   RegisterType(RCompilerMessageWindow);
1291 {$endif}
1292 end;
1293 
1294 
1295 end.
1296