-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset 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 distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

--------------------------------------------------------------------------------
--                                                                            --
--  Specification                                                             --
--                                                                            --
--  INPUT OPTIONS                                                             --
--  -------------                                                             --
--                                                                            --
--  Command Line                                                              --
--  1. Invoking SPARKMake with no arguments will output an error message      --
--  2. All switches can be cut down to their smallest unique value            --
--                                                                            --
--  /directory option                                                         --
--  1. The default is the current directory                                   --
--  2. Any other directories, specified by this option are also searched      --
--  3. This switch may be repeated and the union of the default and all       --
--     explicitly stated directories are searched                             --
--  4. An error is reported if the specified directory does not exist         --
--  5. An error will be reported if there is no argument                      --
--  6. A relative path may be used                                            --
--                                                                            --
--  /include option                                                           --
--  1. The default will find all *.ads and *.adb files (GNAT naming conv.)    --
--  2. A specified include option overrides the default                       --
--  3. This switch may be duplicated and the union will be taken              --
--  4. An error will be reported if there is no argument                      --
--  5. An error will be reported if the argument is not a regular expression  --
--  6. An error is output if the /inc option includes a file with an invalid  --
--     compilation unit.                                                      --
--                                                                            --
--  /exclude                                                                  --
--  1. The default excludes no files                                          --
--  2. When specified, files matching this expression are excluded            --
--  3. This switch may be duplicated and the union will be taken              --
--  4. An error will be reported if there is no argument                      --
--  5. An error will be reported if the argument is not a regular expression  --
--                                                                            --
--  Argument                                                                  --
--  1. The argument consitutes the root file                                  --
--  2. It may appear anywhere in the command line                             --
--  3. An error is reported if the root file cannot be found                  --
--  4. The root file is always included regardless of the /inc, /exc switches --
--  5. If no argument is provided then sparkmake will produce an index and    --
--     meta file for the analysis of all files in the current directory (and  --
--     any subdirectories).                                                   --
--                                                                            --
--  OUTPUT OPTIONS                                                            --
--  --------------                                                            --
--                                                                            --
--  /index                                                                    --
--  1. The default is the root file name (or spark.idx if no root file given) --
--  2. If the root file name has an extension it is replaced with idx         --
--  3. If the root file has no extension then idx is used                     --
--  4. When specified the argument constitues the index file                  --
--  5. If the file does not exist it is created                               --
--  6. If the file exists then it will be overwritten                         --
--  7. The index file contains entries for:                                   --
--    a. All files specified by the /include switch                           --
--    b. That are not excluded by the /exclude switch                         --
--    c. The root file                                                        --
--  8. The index file always specifies full pathnames                         --
--  9. The format of the index files matches that described in the Examiner   --
--       user manual.                                                         --
--  10. An error is output if the same unit is duplicated in more than one file--
--                                                                            --
--  /meta                                                                     --
--  1. The default is the root file name (or spark.smf if no root file given) --
--  2. If the root file name has an extension it is replaced with smf         --
--  3. If the root file has no extension then smf is used                     --
--  4. When specified the argument constitues the meta file                   --
--  5. If the file does not exist it is created                               --
--  6. If the file exists then it will be overwritten                         --
--  7. The meta file always specifies full path names                         --
--  8. The root of the make is the root file specified as an argument on the  --
--        command line                                                        --
--  9. The make process will attempt to make an entire closure for the root   --
--        file. i.e. it will make all the dependent units AND their bodies    --
--                   and separates.                                           --
--  10. All files involved in the make will have an entry in the index file   --
--  11. If a unit cannot be found it is ignored.                              --
--                                                                            --
--  /noindexfile                                                              --
--  1. Default is False.                                                      --
--  2. If True then the index file will not be generated.                     --
--                                                                            --
--  /nometafile                                                               --
--  1. Default is False.                                                      --
--  2. If True then the meta file will not be generated.                      --
--                                                                            --
--  /path                                                                     --
--  1. Default is "full"                                                      --
--  2. User can select "full" or "relative"                                   --
--                                                                            --
--  BEHAVIOUR OPTIONS                                                         --
--  -----------------                                                         --
--                                                                            --
--  /duplicates_are_errors                                                    --
--  1. Duplicate units are treated as an error                                --
--                                                                            --
--  /annotation_character                                                     --
--  1. The annotation character can be specified                              --
--  2. If it is not a character it will not be accepted                       --
--  3. If not specified '#' will be used                                      --
--------------------------------------------------------------------------------

with Ada.Exceptions;
with GNAT.Traceback.Symbolic;
with CommandLineData;
with E_Strings;
with ScreenEcho;
with SPARK_IO;
with ErrorHandler;
with LexTokenManager;
with Dictionary;
with Unit;
with Units;
with UnitManager;
with SparkLex;
with SparkMakeCommandLine;
with SparkMakeErrors;
with SparkMakeDebug;
with Directory_Operations;

use type Unit.Kind;
use type Unit.Object;
use type Unit.Id;
use type SPARK_IO.File_Status;
use type SPARK_IO.File_Type;
use type SparkMakeCommandLine.Path_Type;

--# inherit CommandLine,
--#         CommandLineData,
--#         Dictionary,
--#         Directory_Operations,
--#         ErrorHandler,
--#         E_Strings,
--#         LexTokenManager,
--#         SparkLex,
--#         SparkMakeCommandLine,
--#         SparkMakeDebug,
--#         SparkMakeErrors,
--#         SPARK_IO,
--#         Unit,
--#         UnitManager,
--#         Units;

--# main_program

procedure Sparkmake
--# global in     CommandLine.State;
--#        in out ErrorHandler.Error_Context;
--#        in out LexTokenManager.State;
--#        in out SPARK_IO.File_Sys;
--#        in out UnitManager.State;
--#           out CommandLineData.Content;
--#           out Dictionary.Dict;
--#           out SparkLex.Curr_Line;
--#           out SparkMakeCommandLine.State;
--# derives CommandLineData.Content,
--#         SparkMakeCommandLine.State from CommandLine.State &
--#         Dictionary.Dict            from LexTokenManager.State,
--#                                         SPARK_IO.File_Sys &
--#         ErrorHandler.Error_Context,
--#         LexTokenManager.State,
--#         SparkLex.Curr_Line,
--#         SPARK_IO.File_Sys,
--#         UnitManager.State          from CommandLine.State,
--#                                         ErrorHandler.Error_Context,
--#                                         LexTokenManager.State,
--#                                         SPARK_IO.File_Sys,
--#                                         UnitManager.State;
is

   -- The entry type signifiers must all be the same length
   Main_Program_Sig  : constant String := "main_program  is in ";
   Specification_Sig : constant String := "specification is in ";
   Body_Sig          : constant String := "body          is in ";
   Subunit_Sig       : constant String := "subunit       is in ";
   Component_Sig     : constant String := "components    are   ";
   -- This must equal the length of the above signifiers
   Sig_Length : constant E_Strings.Lengths := 20;

   -- The files output by this program
   The_Meta_File : SPARK_IO.File_Type;

   Success           : Boolean;
   Help_Or_Ver_Found : Boolean;

   --------------------------------------------------------------------------

   procedure Open_Or_Create_File
     (File    : in     E_Strings.T;
      Mode    : in     SPARK_IO.File_Mode;
      Id      : in out SPARK_IO.File_Type;
      Success :    out Boolean)
   --# global in out SPARK_IO.File_Sys;
   --# derives Id,
   --#         SPARK_IO.File_Sys,
   --#         Success           from File,
   --#                                Id,
   --#                                Mode,
   --#                                SPARK_IO.File_Sys;
   is
      Status : SPARK_IO.File_Status;
   begin
      E_Strings.Open (File         => Id,
                      Mode_Of_File => Mode,
                      Name_Of_File => File,
                      Form_Of_File => "",
                      Status       => Status);

      if Status = SPARK_IO.Name_Error then
         E_Strings.Create (File         => Id,
                           Name_Of_File => File,
                           Form_Of_File => "",
                           Status       => Status);
      end if;
      Success := Status = SPARK_IO.Ok;
      if not Success then
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Cannot_Open_File,
            E_Str1    => File,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Open_Or_Create_File;

   --------------------------------------------------------------------------

   procedure Close_File (File    : in     E_Strings.T;
                         File_Id : in out SPARK_IO.File_Type)
   --# global in out SPARK_IO.File_Sys;
   --# derives File_Id           from * &
   --#         SPARK_IO.File_Sys from *,
   --#                                File,
   --#                                File_Id;
   is
      Status : SPARK_IO.File_Status;
   begin
      if File_Id /= SPARK_IO.Null_File then
         SPARK_IO.Close (File   => File_Id,
                         Status => Status);
         if Status /= SPARK_IO.Ok then
            SparkMakeErrors.Report
              (The_Fault => SparkMakeErrors.Cannot_Close_File,
               E_Str1    => File,
               E_Str2    => E_Strings.Empty_String,
               E_Str3    => E_Strings.Empty_String);
         end if;
      end if;
   end Close_File;

   --------------------------------------------------------------------------

   procedure Output_Debug (The_File : in E_Strings.T;
                           Indent   : in Integer)
   --# derives null from Indent,
   --#                   The_File;
   is
      E_Str : E_Strings.T;
   begin
      E_Str := E_Strings.Empty_String;
      for I in Integer range 1 .. Indent loop
         E_Strings.Append_String (E_Str => E_Str,
                                  Str   => " ");
      end loop;
      E_Strings.Append_Examiner_String (E_Str1 => E_Str,
                                        E_Str2 => The_File);
      SparkMakeDebug.Report_Text_E_Text (Text   => "Make: ",
                                         E_Text => E_Str);
   end Output_Debug;

   --------------------------------------------------------------------------

   function Quote_If_Needed (F : in E_Strings.T) return E_Strings.T is
      Result : E_Strings.T;

      function Contains_Space return Boolean
      --# global in F;
      is
         Result : Boolean := False;
      begin
         for I in E_Strings.Positions range 1 .. E_Strings.Get_Length (E_Str => F) loop
            if E_Strings.Get_Element (E_Str => F,
                                      Pos   => I) = ' ' then
               Result := True;
               exit;
            end if;
         end loop;
         return Result;
      end Contains_Space;

   begin
      if Contains_Space then
         Result := E_Strings.Empty_String;
         E_Strings.Append_String (E_Str => Result,
                                  Str   => """");
         E_Strings.Append_Examiner_String (E_Str1 => Result,
                                           E_Str2 => F);
         E_Strings.Append_String (E_Str => Result,
                                  Str   => """");
      else
         Result := F;
      end if;
      return Result;
   end Quote_If_Needed;

   --------------------------------------------------------------------------

   procedure Make (The_File : in E_Strings.T)
   --# global in     SparkMakeCommandLine.State;
   --#        in     The_Meta_File;
   --#        in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *,
   --#                                SparkMakeCommandLine.State,
   --#                                The_File,
   --#                                The_Meta_File;
   --#
   is

      --# hide Make;

      The_Unit : Unit.Id;
      Found    : Boolean;

      Main_Program_Found : Boolean     := False;
      Main_Program_Name  : E_Strings.T := E_Strings.Empty_String;

      -- Stores which units have been placed in the meta file.
      The_Made_Stack : Units.Stack;

      -- Root units for the make
      Root_Stack    : Units.Stack;
      The_Root_Unit : Unit.Id;

      -- For debug purposes
      Indent : Integer := 0;

      --------------------------------------------------------------------------

      -- Forward declaration.
      -- Make_Unit and Make_Units are mutually recursive.

      procedure Make_Units (The_Units : in Units.Stack);

      --------------------------------------------------------------------------

      procedure Make_Unit (The_Unit : in Unit.Id) is
         Found     : Boolean;
         The_Units : Units.Stack;
         The_File  : E_Strings.T;
      begin

         -- don't make something twice or we'll recurse forever!
         --
         if not Units.InStack (TheUnit  => The_Unit,
                               TheStack => The_Made_Stack) then

            -- Check we know about this unit
            UnitManager.Get_File (For_Unit => The_Unit,
                                  The_File => The_File,
                                  Found    => Found);

            if Found then
               -- Record the fact we've made this unit.
               --
               Units.Push (The_Made_Stack, The_Unit);

               if SparkMakeCommandLine.Debug_On then
                  Output_Debug (The_File => The_File,
                                Indent   => Indent);
               end if;

               -- Get the required units
               --
               The_Units := UnitManager.Required_Units (For_Unit => The_Unit);

               if not Units.IsEmpty (The_Units) then
                  Indent := Indent + 3;

                  -- Make the required units first
                  --
                  Make_Units (The_Units => The_Units);

                  Indent := Indent - 3;
               end if;

               -- Write this filename for this unit to the meta file.
               --
               if SparkMakeCommandLine.Path_Required = SparkMakeCommandLine.Full then
                  E_Strings.Put_Line (File  => The_Meta_File,
                                      E_Str => Quote_If_Needed (F => The_File));
               else
                  E_Strings.Put_Line
                    (File  => The_Meta_File,
                     E_Str => Quote_If_Needed
                       (F => Directory_Operations.Relative_Name
                          (Of_This_File_Or_Dir => The_File,
                           To_This_Dir         => Directory_Operations.Current_Directory)));
               end if;

               -- If we find multiple main programs in the make then warn the user
               -- because the resulting meta file will not Examiner cleanly.
               if The_Unit.The_Kind = Unit.Main_Program_Unit then
                  if Main_Program_Found then -- Don't warn for the first one we find!
                     SparkMakeErrors.Report
                       (The_Fault => SparkMakeErrors.Multiple_Main_Programs,
                        E_Str1    => Main_Program_Name,
                        E_Str2    => The_File,
                        E_Str3    => E_Strings.Empty_String);
                  else
                     Main_Program_Name := The_File; -- only update this the first time round
                  end if;
                  Main_Program_Found := True;
               end if;

               if The_Unit.The_Kind in Unit.Specification_Unit then
                  -- This was a spec so make the body
                  Make_Unit (The_Unit => UnitManager.Package_Body (For_Unit => The_Unit));
               else
                  -- This was a body so make any separates
                  Make_Units (The_Units => UnitManager.Separate_Units (For_Unit => The_Unit));
               end if;
            end if;
         end if;
      end Make_Unit;

      --------------------------------------------------------------------------

      procedure Make_Units (The_Units : in Units.Stack) is
         The_Unit    : Unit.Id;
         Local_Units : Units.Stack;
      begin
         Local_Units := The_Units;
         while not Units.IsEmpty (Local_Units) loop

            Units.Pop (TheStack => Local_Units,
                       TheUnit  => The_Unit);

            Make_Unit (The_Unit => The_Unit);

         end loop;
      end Make_Units;

      --------------------------------------------------------------------------

      procedure Warn_Of_Units_Not_In_Make
      --# global The_Made_Stack;
      is
         All_Units              : Units.Stack;
         The_Unit               : Unit.Id;
         Found                  : Boolean;
         The_File               : E_Strings.T;
         Printed_Warning_Header : Boolean := False;
      begin

         All_Units := UnitManager.Get_All_Units;

         -- If any unit is not in the made stack then it wasn't written to the
         -- meta file. Warn the user that this is the case.

         while not Units.IsEmpty (All_Units) loop

            Units.Pop (TheStack => All_Units,
                       TheUnit  => The_Unit);

            if not Units.InStack (TheUnit  => The_Unit,
                                  TheStack => The_Made_Stack) then

               -- Check we know about this unit
               UnitManager.Get_File (For_Unit => The_Unit,
                                     The_File => The_File,
                                     Found    => Found);

               if Found then

                  if not Printed_Warning_Header then
                     SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "The following units were not included in the meta file:", 0);
                     Printed_Warning_Header := True;
                  end if;

                  E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                      E_Str => The_File);
               end if;

            end if;

         end loop;

      end Warn_Of_Units_Not_In_Make;

      --------------------------------------------------------------------------

   begin

      -- If no root filename specified then do it for the whole directory
      if E_Strings.Is_Empty (E_Str => The_File) then

         Root_Stack := UnitManager.Find_Roots; -- Get all the root units for this make

         -- Nothing made so far.
         The_Made_Stack := Units.NullStack;

         -- Iterate through all the roots, making the metafile entries
         -- for each one in turn.
         while not Units.IsEmpty (Root_Stack) loop
            Units.Pop (TheStack => Root_Stack,
                       TheUnit  => The_Root_Unit);

            -- kick off the recursive make for this root.
            Make_Unit (The_Unit => The_Root_Unit);

         end loop;

      else
         -- A filename was supplied so use that as the root for the make

         -- Nothing made so far.
         The_Made_Stack := Units.NullStack;

         -- Get the unit in this file
         UnitManager.Get_Unit (In_File  => The_File,
                               The_Unit => The_Unit,
                               Found    => Found);

         if Found then
            -- kick off the recursive make
            Make_Unit (The_Unit => The_Unit);
         else
            SparkMakeErrors.Report
              (The_Fault => SparkMakeErrors.Cannot_Find_File,
               E_Str1    => The_File,
               E_Str2    => E_Strings.Empty_String,
               E_Str3    => E_Strings.Empty_String);
         end if;
      end if;

      -- The user may be expecting *all* files to be included in the make
      -- so warn about any that were not put in the meta file (ie those
      -- that are present in All_Units but not in The_Made_Stack).
      Warn_Of_Units_Not_In_Make;

   end Make;

   --------------------------------------------------------------------------

   function Max_Unit_Name_Length (The_Stack : in Units.Stack) return E_Strings.Lengths is
      Iterator  : Units.Iterator;
      Next_Unit : Unit.Id;
      Max_Id    : E_Strings.Lengths;
   begin
      Units.Init_Iterator (The_Stack, Iterator);
      Max_Id := 0;

      while not Units.Iterated (Iterator) loop
         Units.Iterate (Iterator, Next_Unit);
         if E_Strings.Get_Length (E_Str => Next_Unit.The_Name) > Max_Id then
            Max_Id := E_Strings.Get_Length (E_Str => Next_Unit.The_Name);
         end if;
      end loop;

      return Max_Id;
   end Max_Unit_Name_Length;

   --------------------------------------------------------------------------

   procedure Tab_To_Position (E_Str        : in out E_Strings.T;
                              Tab_Position : in     E_Strings.Lengths)
   --# derives E_Str from *,
   --#                    Tab_Position;
   is
   begin
      for I in E_Strings.Lengths range E_Strings.Get_Length (E_Str => E_Str) .. Tab_Position loop
         E_Strings.Append_Char (E_Str => E_Str,
                                Ch    => ' ');
      end loop;
   end Tab_To_Position;

   --------------------------------------------------------------------------

   function Line (For_Unit     : Unit.Object;
                  Tab_Position : E_Strings.Lengths) return E_Strings.T
   --# global in SparkMakeCommandLine.State;
   --
   -- Returns <unit name> <specification|body> is in <file name>
   is
      Temp : E_Strings.T;
   begin
      Temp := E_Strings.Empty_String;
      E_Strings.Append_Examiner_String (E_Str1 => Temp,
                                        E_Str2 => For_Unit.The_Id.The_Name);

      Tab_To_Position (E_Str        => Temp,
                       Tab_Position => Tab_Position);

      case For_Unit.The_Id.The_Kind is

         when Unit.Main_Program_Unit =>
            E_Strings.Append_String (E_Str => Temp,
                                     Str   => Main_Program_Sig);

         when Unit.Specification_Unit =>
            E_Strings.Append_String (E_Str => Temp,
                                     Str   => Specification_Sig);

         when Unit.Package_Body_Unit =>
            E_Strings.Append_String (E_Str => Temp,
                                     Str   => Body_Sig);

         when Unit.Separate_Body_Unit =>
            E_Strings.Append_String (E_Str => Temp,
                                     Str   => Subunit_Sig);

      end case;

      if SparkMakeCommandLine.Path_Required = SparkMakeCommandLine.Full then
         E_Strings.Append_Examiner_String (E_Str1 => Temp,
                                           E_Str2 => Quote_If_Needed (F => For_Unit.The_File));
      else
         E_Strings.Append_Examiner_String
           (E_Str1 => Temp,
            E_Str2 => Quote_If_Needed
              (F => Directory_Operations.Relative_Name
                 (Of_This_File_Or_Dir => For_Unit.The_File,
                  To_This_Dir         => Directory_Operations.Current_Directory)));
      end if;

      return Temp;
   end Line;

   --------------------------------------------------------------------------

   procedure Build_Index_File (The_Name : in     E_Strings.T;
                               Success  :    out Boolean)
   --# global in     SparkMakeCommandLine.State;
   --#        in     UnitManager.State;
   --#        in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *,
   --#                                SparkMakeCommandLine.State,
   --#                                The_Name,
   --#                                UnitManager.State &
   --#         Success           from SPARK_IO.File_Sys,
   --#                                The_Name;

   is
      Current_Component : Unit.Id;
      The_Components    : Units.Stack;
      All_Units         : Units.Stack;
      Current_Unit      : Unit.Object;
      Id                : Unit.Id;
      First_Component   : Boolean;
      The_Index_File    : SPARK_IO.File_Type := SPARK_IO.Null_File;
      Tab_Position      : E_Strings.Lengths;
      Component_Line    : E_Strings.T;
   begin
      SPARK_IO.Put_String (SPARK_IO.Standard_Output, "Building index file ", 0);

      E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                          E_Str => The_Name);

      Open_Or_Create_File (File    => The_Name,
                           Mode    => SPARK_IO.Out_File,
                           Id      => The_Index_File,
                           Success => Success);

      if Success then

         All_Units := UnitManager.Get_All_Units;
         Units.Sort (All_Units);

         Tab_Position := Max_Unit_Name_Length (The_Stack => All_Units);

         while not Units.IsEmpty (All_Units) loop

            Units.Pop (TheStack => All_Units,
                       TheUnit  => Id);

            Current_Unit := UnitManager.Get (The_Unit => Id);

            Unit.Output_Object (The_Unit => Current_Unit);

            -- Write the file -> unit mapping to the index file
            E_Strings.Put_Line (File  => The_Index_File,
                                E_Str => Line (For_Unit     => Current_Unit,
                                               Tab_Position => Tab_Position));

            -- if this is a specification
            -- Write any component information to the index file
            if Current_Unit.The_Id.The_Kind in Unit.Specification_Unit then
               The_Components := UnitManager.Components (For_Unit => Current_Unit.The_Id);

               First_Component := True;
               Component_Line  := Current_Unit.The_Id.The_Name;

               while not Units.IsEmpty (The_Components) loop

                  Units.Pop (TheStack => The_Components,
                             TheUnit  => Current_Component);

                  if First_Component then
                     -- Create the <Unit_Name> components are
                     First_Component := False;
                     Tab_To_Position (E_Str        => Component_Line,
                                      Tab_Position => Tab_Position);
                     E_Strings.Append_String (E_Str => Component_Line,
                                              Str   => Component_Sig);
                  else
                     -- Add comma to continue list on new line
                     E_Strings.Append_String (E_Str => Component_Line,
                                              Str   => ",");
                     E_Strings.Put_Line (File  => The_Index_File,
                                         E_Str => Component_Line);

                     -- Align component names
                     Component_Line := E_Strings.Empty_String;
                     Tab_To_Position (E_Str        => Component_Line,
                                      Tab_Position => Tab_Position + Sig_Length);
                  end if;

                  -- Append a component name
                  E_Strings.Append_Examiner_String (E_Str1 => Component_Line,
                                                    E_Str2 => Current_Component.The_Name);

               end loop;
               if not First_Component then
                  E_Strings.Put_Line (File  => The_Index_File,
                                      E_Str => Component_Line);
               end if;
            end if;
         end loop;
      end if;
      --# accept Flow, 10, The_Index_File, "Ineffective assignment here OK";
      Close_File (File    => The_Name,
                  File_Id => The_Index_File);
   end Build_Index_File;

   ----------------------------------------------------------------------------------

   procedure Build_Meta_File (The_Name  : in E_Strings.T;
                              From_Root : in E_Strings.T)
   --# global in     SparkMakeCommandLine.State;
   --#        in out SPARK_IO.File_Sys;
   --#           out The_Meta_File;
   --# derives SPARK_IO.File_Sys from *,
   --#                                From_Root,
   --#                                SparkMakeCommandLine.State,
   --#                                The_Name &
   --#         The_Meta_File     from SPARK_IO.File_Sys,
   --#                                The_Name;
   is
      Success : Boolean;
   begin
      The_Meta_File := SPARK_IO.Null_File;
      SPARK_IO.Put_String (SPARK_IO.Standard_Output, "Building meta file ", 0);

      E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                          E_Str => The_Name);

      Open_Or_Create_File (File    => The_Name,
                           Mode    => SPARK_IO.Out_File,
                           Id      => The_Meta_File,
                           Success => Success);

      if Success then
         Make (The_File => From_Root);
         Close_File (File    => The_Name,
                     File_Id => The_Meta_File);
      end if;
   end Build_Meta_File;

begin -- Set Up
   CommandLineData.Initialize;

   LexTokenManager.Initialise_String_Table;
   Dictionary.Initialize (False);
   ErrorHandler.Spark_Make_Init;
   SparkLex.Clear_Line_Context;

   -- Read the command line
   SparkMakeCommandLine.Process (Success, Help_Or_Ver_Found);

   if Success and not Help_Or_Ver_Found then
      UnitManager.Initialise
        (The_Directories => SparkMakeCommandLine.The_Directory_Names,
         Include         => SparkMakeCommandLine.The_Inc_File_Reg_Exps,
         Exclude         => SparkMakeCommandLine.The_Exc_File_Reg_Exps,
         Root_File       => SparkMakeCommandLine.Root_Filename,
         Duplicates      => SparkMakeCommandLine.Duplicates_Error,
         Success         => Success);

      if Success then
         if not SparkMakeCommandLine.No_Index_File then
            Build_Index_File (The_Name => SparkMakeCommandLine.Index_Filename,
                              Success  => Success);
         end if;

         if Success then
            if not SparkMakeCommandLine.No_Meta_File then
               --# accept Flow, 10, The_Meta_File, "Ineffective assignment here OK";
               Build_Meta_File (The_Name  => SparkMakeCommandLine.Meta_Filename,
                                From_Root => SparkMakeCommandLine.Root_Filename);
               --# end accept;
            end if;
         end if;
      end if;
   end if;
   --# accept Flow, 33, The_Meta_File, "Metafile not referenced here";
exception
   --# hide Sparkmake;
   when E : others =>
      ScreenEcho.New_Line (1);
      ScreenEcho.Put_Line ("Unhandled Exception in SPARKMake");
      ScreenEcho.Put_Line ("Exception information:");
      ScreenEcho.Put_Line (Ada.Exceptions.Exception_Information (E));
      ScreenEcho.Put_Line ("Traceback:");
      ScreenEcho.Put_Line (GNAT.Traceback.Symbolic.Symbolic_Traceback (E));
end Sparkmake;
