mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 05:38:05 +02:00
406 lines
9.7 KiB
ObjectPascal
406 lines
9.7 KiB
ObjectPascal
{
|
|
Copyright (c) 2024- by Michael Van Canneyt
|
|
|
|
This unit handles the compiler tasks.
|
|
|
|
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.
|
|
****************************************************************************
|
|
}
|
|
|
|
unit ctask;
|
|
|
|
{$mode ObjFPC}
|
|
|
|
{ $DEFINE DEBUG_CTASK}
|
|
|
|
interface
|
|
|
|
uses
|
|
fmodule, cclasses, globstat;
|
|
|
|
type
|
|
{ ttask_list }
|
|
|
|
ttask_list = class(tlinkedlistitem)
|
|
module : tmodule;
|
|
state : tglobalstate;
|
|
constructor create(_m : tmodule);
|
|
destructor destroy; override;
|
|
procedure SaveState;
|
|
Procedure RestoreState;
|
|
procedure DiscardState;
|
|
function nexttask : ttask_list; inline;
|
|
end;
|
|
|
|
ttasklinkedlist = class(tlinkedlist)
|
|
function firsttask : ttask_list; inline;
|
|
end;
|
|
|
|
{ ttask_handler }
|
|
|
|
|
|
ttask_handler = class
|
|
private
|
|
list : ttasklinkedlist;
|
|
hash : TFPHashList;
|
|
main : tmodule;
|
|
procedure rebuild_hash;
|
|
public
|
|
constructor create;
|
|
destructor destroy; override;
|
|
// Find the task for module m
|
|
function findtask(m : tmodule) : ttask_list;
|
|
// Can we continue processing this module ? If not, firstwaiting contains first module that m is waiting for.
|
|
function cancontinue(m : tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
|
|
// Overload of cancontinue, based on task.
|
|
function cancontinue(t: ttask_list; out firstwaiting: tmodule): boolean; inline;
|
|
// Continue processing this module. Return true if the module is done and can be removed.
|
|
function continue(t : ttask_list): Boolean;
|
|
// process the queue. Note that while processing the queue, elements will be added.
|
|
procedure processqueue;
|
|
// add a module to the queue. If a module is already in the queue, we do not add it again.
|
|
procedure addmodule(m : tmodule);
|
|
end;
|
|
|
|
|
|
var
|
|
task_handler : TTask_handler;
|
|
|
|
procedure InitTaskHandler;
|
|
procedure DoneTaskHandler;
|
|
|
|
implementation
|
|
|
|
uses verbose, fppu, finput, globtype, sysutils, scanner, parser, pmodules;
|
|
|
|
procedure InitTaskHandler;
|
|
begin
|
|
task_handler:=ttask_handler.create;
|
|
schedule_recompile_proc:=@task_handler.addmodule;
|
|
end;
|
|
|
|
procedure DoneTaskHandler;
|
|
begin
|
|
freeandnil(task_handler);
|
|
end;
|
|
|
|
{ ttasklinkedlist }
|
|
|
|
function ttasklinkedlist.firsttask: ttask_list;
|
|
begin
|
|
Result:=ttask_list(first);
|
|
end;
|
|
|
|
{ ttask_list }
|
|
|
|
constructor ttask_list.create(_m: tmodule);
|
|
begin
|
|
inherited create;
|
|
module:=_m;
|
|
state:=nil;
|
|
end;
|
|
|
|
|
|
destructor ttask_list.destroy;
|
|
begin
|
|
DiscardState;
|
|
Inherited;
|
|
end;
|
|
|
|
procedure ttask_list.DiscardState;
|
|
|
|
begin
|
|
FreeAndNil(state);
|
|
end;
|
|
|
|
function ttask_list.nexttask: ttask_list;
|
|
begin
|
|
Result:=ttask_list(next);
|
|
end;
|
|
|
|
procedure ttask_list.SaveState;
|
|
begin
|
|
if State=Nil then
|
|
State:=tglobalstate.Create(true)
|
|
else
|
|
State.save(true);
|
|
end;
|
|
|
|
procedure ttask_list.RestoreState;
|
|
begin
|
|
if not module.is_reset then
|
|
state.restore(true);
|
|
if assigned(current_scanner) and assigned(current_scanner.inputfile) then
|
|
if current_scanner.inputfile.closed then
|
|
begin
|
|
current_scanner.tempopeninputfile;
|
|
current_scanner.gettokenpos;
|
|
end;
|
|
end;
|
|
|
|
{ ttask_handler }
|
|
|
|
constructor ttask_handler.create;
|
|
begin
|
|
list:=ttasklinkedlist.Create;
|
|
hash:=TFPHashList.Create;
|
|
end;
|
|
|
|
destructor ttask_handler.destroy;
|
|
begin
|
|
hash.free;
|
|
List.Clear;
|
|
FreeAndNil(list);
|
|
inherited destroy;
|
|
end;
|
|
|
|
function ttask_handler.findtask(m: tmodule): ttask_list;
|
|
|
|
begin
|
|
result:=list.FirstTask;
|
|
while result<>nil do
|
|
begin
|
|
if result.module=m then
|
|
exit;
|
|
result:=result.nexttask;
|
|
end;
|
|
{$IFDEF DEBUG_CTASK}Writeln('No task found for '+m.ToString);{$ENDIF}
|
|
end;
|
|
|
|
function ttask_handler.cancontinue(m: tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
|
|
|
|
procedure CheckUsed(out acandidate : tmodule);
|
|
|
|
var
|
|
itm : TLinkedListItem;
|
|
iscandidate : boolean;
|
|
m2 : tmodule;
|
|
|
|
begin
|
|
acandidate:=nil;
|
|
itm:=m.used_units.First;
|
|
while (acandidate=Nil) and assigned(itm) do
|
|
begin
|
|
iscandidate:=Not (tused_unit(itm).u.state in [ms_processed,ms_compiled]);
|
|
if iscandidate then
|
|
begin
|
|
acandidate:=tused_unit(itm).u;
|
|
if not cancontinue(acandidate,false,m2) then
|
|
acandidate:=nil;
|
|
end;
|
|
itm:=itm.Next;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
m2 : tmodule;
|
|
|
|
begin
|
|
firstwaiting:=nil;
|
|
// We do not need to consider the program as long as there are units that need to be treated.
|
|
if (m.is_initial and not m.is_unit) and (list.count>1) then
|
|
exit(False);
|
|
case m.state of
|
|
ms_unknown : cancontinue:=true;
|
|
ms_registered : cancontinue:=true;
|
|
ms_compile : cancontinue:=true;
|
|
ms_compiling_waitimpl : cancontinue:=m.usedunitsloaded(false,firstwaiting);
|
|
ms_compiling_waitfinish : cancontinue:=m.nowaitingforunits(firstwaiting);
|
|
ms_compiling_waitintf : cancontinue:=m.usedunitsloaded(true,firstwaiting);
|
|
ms_compiling_wait : cancontinue:=m.usedunitsloaded(true,firstwaiting);
|
|
ms_load : cancontinue:=m.usedunitsloaded(true,firstwaiting);
|
|
ms_compiled : cancontinue:=true;
|
|
ms_processed : cancontinue:=true;
|
|
ms_moduleerror : cancontinue:=true;
|
|
{ else
|
|
InternalError(2024011802);}
|
|
end;
|
|
if (not cancontinue) and checksub then
|
|
begin
|
|
checkused(m2);
|
|
if m2<>nil then
|
|
firstwaiting:=m2;
|
|
end;
|
|
{$IFDEF DEBUG_CTASK}
|
|
Write(m.ToString,' state: ',m.state,', can continue: ',Result);
|
|
if result then
|
|
Writeln
|
|
else
|
|
begin
|
|
Write(' (First waiting: ');
|
|
If Assigned(FirstWaiting) then
|
|
Writeln(FirstWaiting.ToString,' )')
|
|
else
|
|
Writeln('<none>)');
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function ttask_handler.cancontinue(t : ttask_list; out firstwaiting : tmodule): boolean;
|
|
|
|
begin
|
|
Result:=cancontinue(t.module,true,firstwaiting);
|
|
end;
|
|
|
|
function ttask_handler.continue(t : ttask_list) : Boolean;
|
|
|
|
var
|
|
m : tmodule;
|
|
orgname : shortstring;
|
|
|
|
begin
|
|
m:=t.module;
|
|
orgname:=m.modulename^;
|
|
{$IFDEF DEBUG_CTASK}Writeln(m.ToString,' Continues. State: ',m.state);{$ENDIF}
|
|
if Assigned(t.state) then
|
|
t.RestoreState;
|
|
case m.state of
|
|
ms_registered : parser.compile_module(m);
|
|
ms_load : with tppumodule(m) do
|
|
loadppu(reload_from);
|
|
ms_compile : parser.compile_module(m);
|
|
ms_compiled : if (not m.is_initial) or m.is_unit then
|
|
(m as tppumodule).post_load_or_compile(m,m.compilecount>1);
|
|
ms_compiling_waitintf : pmodules.parse_unit_interface_declarations(m);
|
|
ms_compiling_waitimpl : pmodules.proc_unit_implementation(m);
|
|
ms_compiling_waitfinish : pmodules.finish_unit(m);
|
|
ms_compiling_wait : pmodules.proc_program_declarations(m,m.islibrary);
|
|
ms_processed : ;
|
|
else
|
|
InternalError(2024011801);
|
|
end;
|
|
if m.state=ms_compiled then
|
|
begin
|
|
parsing_done(m);
|
|
if m.is_initial and not m.is_unit then
|
|
m.state:=ms_processed;
|
|
end;
|
|
Result:=m.state=ms_processed;
|
|
{$IFDEF DEBUG_CTASK}
|
|
Write(m.ToString,' done: ',Result);
|
|
if Result then
|
|
Writeln
|
|
else
|
|
Writeln(', state is now: ',m.state);
|
|
{$ENDIF}
|
|
if not result then
|
|
// Not done, save state
|
|
t.SaveState;
|
|
{
|
|
the name can change as a result of processing, e.g. PROGRAM -> TB0406
|
|
Normally only for the initial module, but we'll do a generic check.
|
|
}
|
|
if m.modulename^<>orgname then
|
|
rebuild_hash;
|
|
end;
|
|
|
|
procedure ttask_handler.rebuild_hash;
|
|
|
|
var
|
|
t : ttask_list;
|
|
|
|
begin
|
|
Hash.Clear;
|
|
t:=list.firsttask;
|
|
While assigned(t) do
|
|
begin
|
|
Hash.Add(t.module.modulename^,t);
|
|
t:=t.nexttask;
|
|
end;
|
|
end;
|
|
|
|
procedure ttask_handler.processqueue;
|
|
|
|
var
|
|
t,t2 : ttask_list;
|
|
process : boolean;
|
|
dummy,firstwaiting : tmodule;
|
|
|
|
begin
|
|
t:=list.firsttask;
|
|
While t<>nil do
|
|
begin
|
|
process:=cancontinue(t,firstwaiting);
|
|
if process then
|
|
begin
|
|
if continue(t) then
|
|
begin
|
|
{$IFDEF DEBUG_CTASK}Writeln(t.module.ToString,' is finished, removing from task list');{$ENDIF}
|
|
hash.Remove(t.module);
|
|
list.Remove(t);
|
|
end;
|
|
// maybe the strategy can be improved.
|
|
t:=list.firsttask;
|
|
end
|
|
else if assigned(firstwaiting) and cancontinue(firstwaiting,true, dummy) then
|
|
begin
|
|
t2:=findtask(firstwaiting);
|
|
if t2=nil then
|
|
t2:=t.nexttask;
|
|
t:=t2;
|
|
end
|
|
else
|
|
begin
|
|
t:=t.nexttask;
|
|
end;
|
|
if t=nil then
|
|
t:=list.firsttask;
|
|
end;
|
|
end;
|
|
|
|
procedure ttask_handler.addmodule(m: tmodule);
|
|
|
|
var
|
|
n : TSymStr;
|
|
e, t : ttask_list;
|
|
|
|
begin
|
|
{$IFDEF DEBUG_CTASK}Writeln(m.ToString,' added to task scheduler. State: ',m.state);{$ENDIF}
|
|
n:=m.modulename^;
|
|
e:=ttask_list(Hash.Find(n));
|
|
if e=nil then
|
|
begin
|
|
// Clear reset flag.
|
|
// This can happen when during load, reset is done and unit is added to task list.
|
|
m.is_reset:=false;
|
|
t:=ttask_list.create(m);
|
|
list.insert(t);
|
|
hash.Add(n,t);
|
|
if list.count=1 then
|
|
main:=m;
|
|
end
|
|
else
|
|
begin
|
|
// We have a task, if it was reset, then clear the state and move the task to the start.
|
|
if m.is_reset then
|
|
begin
|
|
{$IFDEF DEBUG_CTASK}Writeln(m.ToString,' was reset, resetting flag. State: ',m.state);{$ENDIF}
|
|
m.is_reset:=false;
|
|
t:=findtask(m);
|
|
if assigned(t) then
|
|
begin
|
|
t.DiscardState;
|
|
list.Remove(t);
|
|
list.insertbefore(t,list.First);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|