(**********************************************************************

    :Program.    Make.mod
    :Contents.   m2make - automagical compiler + linker
    :Support.    Michael Frie▀ [mif]
    :Author.     Nicolas Benezan [bne]
    :Address.    Postwiesenstr. 2, D7000 Stuttgart 60
    :Phone.      711/333679
    :Copyright.  Shareware, see Doc-File for details
    :Language.   Modula-2
    :Translator. M2Amiga AMSoft V3.2d
    :Imports.    Queue1.2, AVL1.2 [mif]
    :Imports.    Environment1.2, PathFinder1.3, Interface1.0 [bne]
    :Imports.    ModuleInfos1.2, ErrorReq1.2, MemSystem1.3 [bne]
    :History.    V1.0b [bne] 4.Feb.1989
    :History.    V1.1a [bne] 9.Feb.1989 (+ ModQueue)
    :History.    V1.2b [bne] 17.Feb.1989 (bugs fixed, + Interface)
    :Remark.      V1.2 first release, alpha test version
    :History.    V1.3b [bne] 22.Feb.1989 (InitGlobal, cosmetics)
    :Update.     4.Mar.1989 [bne] adapted MemSystem V1.3
    :Remark.      V1.3 second release, beta test version
    :History.    V1.4 [bne] 4.Apr.1989 (gamma release, shareware)
    :History.    V1.5 [bne] 8.Apr.1989 (+"a"-Option)
    :History.    V1.6 [bne] 18.May.1989 (+ ExDos, bugs fixed)
    :History.    V1.7 [bne] 12.Aug.1989 (bug in InitDates() fixed)
    :History.    V1.8 [bne] 13.Aug.1989 (- AVL, + Trees)
    :History.    V1.9 [bne] 22.Aug.1989 (bugs in Arguments fixed)

**********************************************************************)

MODULE Make;

FROM Arts        IMPORT maxModName, ModKeys, ModName, ModType, ModuleId,
                        wbStarted;
FROM Dos         IMPORT Date;
FROM ExDos       IMPORT DupLock, FileLockPtr, Lock, SetCurrentDir,
                        sharedLock, UnLock;
FROM ErrorReq    IMPORT Assert;
FROM Environment IMPORT CloseInOut, GetArguments, InitInOut, MainDir,
                        OptionFlags, OptionFlagSet, WriteLn, WriteString;
FROM Interface   IMPORT AddCompilerArg, InitCompilerArgList, StartCompiler,
                        StartLinker, WaitUntilCompleted;
FROM MemSystem   IMPORT Deallocate, NoCareAllocate;
FROM ModuleInfos IMPORT EarlierDate, GetDate, ImportInfo, ImportInfoPtr,
                        InfoFlags, InfoFlagSet, LastFileDate, MatchKeys,
                        ModuleInfo, ModuleInfoPtr, ReadDefInfo,
                        ReadMainInfo, ReadModInfo, ResetLastFileDate;
FROM PathFinder  IMPORT BinDir, DirLock, ExtendFileName, FreeProjects,
                        GoToProject, InitGlobal, MainProject, ModNamePtr,
                        PathFlags, ProjectLock;
FROM SYSTEM      IMPORT ADDRESS, ADR;
FROM Trees       IMPORT DiscardTree, Get, InitTree, Add, Tree, TreeNode,
                        TreesAllocProc, TreesDeallocProc;
IMPORT ASCII, Queue, Trees;

CONST
  Copyright="M2Make (c) 1989 by Nicolas Benezan";

  (* Error messages: *)
  QueueInitFail   = "Queue.Init() failed";
  InitTreeFail    = "InitTree(ModuleHeap) failed";
  TwiceSameName   = "twice the same module name";
  EnqueueFail     = "Enqueue() failed";
  ModuleNotInTree = "Module not in the tree";

TYPE
  ModStatus=(noDef(*main module*),newDef,oldDef);
  ImplementInfo=RECORD
    name:ModName;
    status:ModStatus;
  END;

VAR
  MainModule: ModName;
  MainInfo: ModuleInfoPtr;
  Options: OptionFlagSet;
  AnyChanges: BOOLEAN;
  Import: ImportInfoPtr;
  ModuleHeap: Tree;
  ModQueue: Queue.queue;
  Status: ModStatus;
  Implementation: ModName;
  ExeFileDate: Date;
  MainObjDir: FileLockPtr;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* key compare procedure for tree handling                              *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE CompareNames(Name1,Name2:ADDRESS):INTEGER;
  VAR
    String1,String2:POINTER TO CHAR;
  BEGIN
    String1:=Name1;
    String2:=Name2;
    WHILE (String1^=String2^) AND (String1^#ASCII.nul) DO
      INC(String1);
      INC(String2);
    END;
    RETURN ORD(String1^)-ORD(String2^);
  END CompareNames;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Add an entry to the compile list                                     *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE Compile(Project:ProjectLock;Name:ModName;Type:PathFlags);
  BEGIN
    ExtendFileName(Name,Type);
    WriteString(" * ");WriteString(Name);WriteString(" to be compiled");
    WriteLn;
    AddCompilerArg(Name,DirLock(Project,Type));
    AnyChanges:=TRUE;
  END Compile;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Start the linker                                                     *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE Link(Name:ModName;ObjDir:FileLockPtr);
  BEGIN
    ExtendFileName(Name,obj);
    WriteString("linking ");WriteString(Name);
    WriteLn;
    StartLinker(Name,ObjDir);
    WaitUntilCompleted;
  END Link;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* install memory allocation procedures                                 *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE InitMemSystem;
  BEGIN
    Queue.QueueAllocProc:=NoCareAllocate;
    Queue.QueueDeallocProc:=Deallocate;
    TreesAllocProc:=NoCareAllocate;
    TreesDeallocProc:=Deallocate;
  END InitMemSystem;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Read module information from the tree                                *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE GetProjectInfo(    Name: ModNamePtr;
                         VAR ModInfo: ModuleInfoPtr): BOOLEAN;
  BEGIN
    ModInfo:=Get(ModuleHeap, Name);
    IF ModInfo#NIL THEN
      DEC(ModInfo, SIZE(TreeNode));
      RETURN TRUE
    END;
    RETURN FALSE;
  END GetProjectInfo;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Write module information to the AVL.tree                             *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE AddProjectInfo(ModInfo:ModuleInfoPtr);
  BEGIN
    Assert(Add(ModuleHeap, ModInfo), ADR(TwiceSameName));
  END AddProjectInfo;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Enqueue iplementation module for later processing                    *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE EnqueueMod(Name:ModNamePtr;Status:ModStatus);
  VAR
    Module:ImplementInfo;
  BEGIN
    Module.name:=Name^;
    Module.status:=Status;
    Assert(Queue.Write(ModQueue,Module),ADR(EnqueueFail));
  END EnqueueMod;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Dequeue implementation module                                        *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE DequeueMod(VAR Name:ModName;VAR Status:ModStatus);
  VAR
    Module:ImplementInfo;
  BEGIN
    Queue.Read(ModQueue,Module);
    Name:=Module.name;
    Status:=Module.status;
  END DequeueMod;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Examine DEFINITION MODULE - Compile() if necessary                   *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE ExamineDef(Project: ProjectLock; (* info for PathFinder *)
                     Module: ModNamePtr;
                     ExpectedKey: ModKeys): BOOLEAN (* new version ? *);
  VAR
    NewVersion: BOOLEAN;
    ModInfo: ModuleInfoPtr;
    ImportList: Queue.queue; (* queue instead of list for simplification *)
  (*Import: ImportInfoPtr is global for efficiency reasons *)
  BEGIN
    IF GetProjectInfo(Module, ModInfo) THEN       (* if already examined *)
      WITH ModInfo^ DO
        RETURN compiled OR NOT MatchKeys(ExpectedKey, key);
      END;
    ELSE
      WriteString(" - "); WriteString(Module^);
      WriteLn;
      Assert(Queue.Init(ImportList), ADR(QueueInitFail));
      ReadDefInfo(Project, Module, ImportList, ModInfo);
      NewVersion:=FALSE;
      WHILE NOT Queue.Empty(ImportList) DO
        (* before compiling this we Examine() all imported Modules *)
        Queue.Get(ImportList, Import);
        NewVersion:=(ExamineDef(ModInfo^.project, ADR(Import^.name),
                                Import^.key) OR NewVersion);
        Queue.Remove(ImportList);
      END;
      Queue.Discard(ImportList);
      NewVersion:=NewVersion OR
                  (((all IN Options) OR
                    NOT (symValid IN ModInfo^.flags))
                   AND (defExist IN ModInfo^.flags));
      IF NewVersion THEN
        Compile(ModInfo^.project, Module^, def);
        ModInfo^.compiled:=TRUE;
        Status:=newDef;
      ELSE
        Status:=oldDef;
      END;
      IF ModInfo^.type=mod THEN
        EnqueueMod(Module, Status); (* Implementation Module *)
      END;
      NewVersion:=NewVersion OR NOT MatchKeys(ExpectedKey, ModInfo^.key);
      AddProjectInfo(ModInfo);
        (* prevent Module from being compiled twice *)
      RETURN NewVersion;
    END;
  END ExamineDef;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Examine IMPLEMENTATION MODULE - Compile() if neccessary              *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE ExamineMod(Module: ModNamePtr;
                     Status: ModStatus);
  VAR
    NewMod: BOOLEAN;
    ImportList: Queue.queue; (* queue instead of list for simplification *)
    ModInfo: ModuleInfoPtr;
    ObjKey: ModKeys;
  (*Import: ImportInfoPtr is global for efficiency reasons *)
  BEGIN
    NewMod:=FALSE;
    Assert(GetProjectInfo(Module, ModInfo),ADR(ModuleNotInTree));
    Assert(Queue.Init(ImportList), ADR(QueueInitFail));
    ReadModInfo(ImportList, ModInfo, ObjKey);
    WHILE NOT Queue.Empty(ImportList) DO
      Queue.Get(ImportList, Import);
      NewMod:=(ExamineDef(ModInfo^.project, ADR(Import^.name), Import^.key)
               OR NewMod);
      Queue.Remove(ImportList);
    END;
    Queue.Discard(ImportList);
    IF NOT NewMod AND (Status#newDef) AND (objValid IN ModInfo^.flags)
       AND NOT ((all IN Options) AND (modExist IN ModInfo^.flags))
       AND ((Status=noDef) OR MatchKeys(ModInfo^.key,ObjKey)) THEN
      RETURN
    END;
    Compile(ModInfo^.project, Module^,imp);
  END ExamineMod;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Get date of executable (if exists) and reset LatestFileDate          *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE InitDates;
  VAR
    BinDirLock,ExeLock:FileLockPtr;
  BEGIN
    GoToProject(MainProject(),proj);
    BinDirLock:=Lock(BinDir,sharedLock);
    IF BinDirLock#NIL THEN
      SetCurrentDir(BinDirLock);
    END;
    ExeLock:=Lock(MainModule,sharedLock);
    IF ExeLock#NIL THEN
      GetDate(ExeLock,ExeFileDate);
      ResetLastFileDate;
      UnLock(ExeLock);
    ELSE
      ResetLastFileDate;
      ExeFileDate:=LastFileDate; (* null *)
    END;
    IF BinDirLock#NIL THEN
      SetCurrentDir(MainDir);
      UnLock(BinDirLock);
    END;
  END InitDates;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* Make Main                                                            *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
BEGIN
  InitMemSystem;
  GetArguments(MainModule,Options);
  InitInOut;
  InitGlobal;
  WriteString("Amiga Modula-2 Make, V1.9 22.Aug.89, й AMOK [bne]");
  WriteLn;
  WriteString("This is SHAREWARE, not in the Public Domain !");
  WriteLn;
  WriteString("making ");WriteString(MainModule);
  WriteLn;
  AnyChanges:=FALSE;
  Assert(InitTree(ModuleHeap, FALSE, CompareNames), ADR(InitTreeFail));
  Assert(Queue.Init(ModQueue),ADR(QueueInitFail));
  InitCompilerArgList;
  ReadMainInfo(MainProject(),ADR(MainModule),MainInfo);
  AddProjectInfo(MainInfo);
  InitDates;
  ExamineMod(ADR(MainModule),noDef);
  WHILE NOT Queue.Empty(ModQueue) DO
    DequeueMod(Implementation,Status);
    ExamineMod(ADR(Implementation),Status);
  END;
  Queue.Discard(ModQueue);
  DiscardTree(ModuleHeap);
  MainObjDir:=DupLock(DirLock(MainProject(),obj));
  FreeProjects;
  IF AnyChanges THEN
    StartCompiler;
    WaitUntilCompleted;
  END;
  IF AnyChanges OR EarlierDate(ExeFileDate,LastFileDate) THEN
    Link(MainModule,MainObjDir);
  END;
  CloseInOut;
END Make.

