------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- M A K E G P R --
-- --
-- B o d y --
-- --
-- Copyright (C) 2004-2007, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 2, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 GNAT; see file COPYING. If not, write --
-- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
-- Boston, MA 02110-1301, USA. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Csets;
with Gnatvsn;
with Hostparm; use Hostparm;
with Makeutl; use Makeutl;
with MLib.Tgt; use MLib.Tgt;
with Namet; use Namet;
with Output; use Output;
with Opt; use Opt;
with Osint; use Osint;
with Prj; use Prj;
with Prj.Ext; use Prj.Ext;
with Prj.Pars;
with Prj.Util; use Prj.Util;
with Snames; use Snames;
with Table;
with Types; use Types;
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Deallocation;
with GNAT.Directory_Operations; use GNAT.Directory_Operations;
with GNAT.Dynamic_Tables;
with GNAT.Expect; use GNAT.Expect;
with GNAT.HTable;
with GNAT.OS_Lib; use GNAT.OS_Lib;
with GNAT.Regpat; use GNAT.Regpat;
with System;
with System.Case_Util; use System.Case_Util;
package body Makegpr is
On_Windows : constant Boolean := Directory_Separator = '\';
-- True when on Windows. Used in Check_Compilation_Needed when processing
-- C/C++ dependency files for backslash handling.
Max_In_Archives : constant := 50;
-- The maximum number of arguments for a single invocation of the
-- Archive Indexer (ar).
No_Argument : aliased Argument_List := (1 .. 0 => null);
-- Null argument list representing case of no arguments
FD : Process_Descriptor;
-- The process descriptor used when invoking a non GNU compiler with -M
-- and getting the output with GNAT.Expect.
Line_Matcher : constant Pattern_Matcher := Compile ("^.*?\n", Single_Line);
-- Pattern for GNAT.Expect for the invocation of a non GNU compiler with -M
Name_Ide : Name_Id;
Name_Compiler_Command : Name_Id;
-- Names of package IDE and its attribute Compiler_Command.
-- Set up by Initialize.
Unique_Compile : Boolean := False;
-- True when switch -u is used on the command line
type Source_Index_Rec is record
Project : Project_Id;
Id : Other_Source_Id;
Found : Boolean := False;
end record;
-- Used as Source_Indexes component to check if archive needs to be rebuilt
type Source_Index_Array is array (Positive range <>) of Source_Index_Rec;
type Source_Indexes_Ref is access Source_Index_Array;
procedure Free is new Ada.Unchecked_Deallocation
(Source_Index_Array, Source_Indexes_Ref);
Initial_Source_Index_Count : constant Positive := 20;
Source_Indexes : Source_Indexes_Ref :=
new Source_Index_Array (1 .. Initial_Source_Index_Count);
-- A list of the Other_Source_Ids of a project file, with an indication
-- that they have been found in the archive dependency file.
Last_Source : Natural := 0;
-- The index of the last valid component of Source_Indexes
Compiler_Names : array (First_Language_Indexes) of String_Access;
-- The names of the compilers to be used. Set up by Get_Compiler.
-- Used to display the commands spawned.
Gnatmake_String : constant String_Access := new String'("gnatmake");
GCC_String : constant String_Access := new String'("gcc");
G_Plus_Plus_String : constant String_Access := new String'("g++");
Default_Compiler_Names : constant array
(First_Language_Indexes range
Ada_Language_Index .. C_Plus_Plus_Language_Index)
of String_Access :=
(Ada_Language_Index => Gnatmake_String,
C_Language_Index => GCC_String,
C_Plus_Plus_Language_Index => G_Plus_Plus_String);
Compiler_Paths : array (First_Language_Indexes) of String_Access;
-- The path names of the compiler to be used. Set up by Get_Compiler.
-- Used to spawn compiling/linking processes.
Compiler_Is_Gcc : array (First_Language_Indexes) of Boolean;
-- An indication that a compiler is a GCC compiler, to be able to use
-- specific GCC switches.
Archive_Builder_Path : String_Access := null;
-- The path name of the archive builder (ar). To be used when spawning
-- ar commands.
Archive_Indexer_Path : String_Access := null;
-- The path name of the archive indexer (ranlib), if it exists
Copyright_Output : Boolean := False;
Usage_Output : Boolean := False;
-- Flags to avoid multiple displays of Copyright notice and of Usage
Output_File_Name : String_Access := null;
-- The name given after a switch -o
Output_File_Name_Expected : Boolean := False;
-- True when last switch was -o
Project_File_Name : String_Access := null;
-- The name of the project file specified with switch -P
Project_File_Name_Expected : Boolean := False;
-- True when last switch was -P
Naming_String : aliased String := "naming";
Builder_String : aliased String := "builder";
Compiler_String : aliased String := "compiler";
Binder_String : aliased String := "binder";
Linker_String : aliased String := "linker";
-- Name of packages to be checked when parsing/processing project files
List_Of_Packages : aliased String_List :=
(Naming_String 'Access,
Builder_String 'Access,
Compiler_String 'Access,
Binder_String 'Access,
Linker_String 'Access);
Packages_To_Check : constant String_List_Access := List_Of_Packages'Access;
-- List of the packages to be checked when parsing/processing project files
Project_Tree : constant Project_Tree_Ref := new Project_Tree_Data;
Main_Project : Project_Id;
-- The project id of the main project
type Processor is (None, Linker, Compiler);
Current_Processor : Processor := None;
-- This variable changes when switches -*args are used
Current_Language : Language_Index := Ada_Language_Index;
-- The compiler language to consider when Processor is Compiler
package Comp_Opts is new GNAT.Dynamic_Tables
(Table_Component_Type => String_Access,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 20,
Table_Increment => 100);
Options : array (First_Language_Indexes) of Comp_Opts.Instance;
-- Tables to store compiling options for the different compilers
package Linker_Options is new Table.Table
(Table_Component_Type => String_Access,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 20,
Table_Increment => 100,
Table_Name => "Makegpr.Linker_Options");
-- Table to store the linking options
package Library_Opts is new Table.Table
(Table_Component_Type => String_Access,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 20,
Table_Increment => 100,
Table_Name => "Makegpr.Library_Opts");
-- Table to store the linking options
package Ada_Mains is new Table.Table
(Table_Component_Type => String_Access,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 20,
Table_Increment => 100,
Table_Name => "Makegpr.Ada_Mains");
-- Table to store the Ada mains, either specified on the command line
-- or found in attribute Main of the main project file.
package Other_Mains is new Table.Table
(Table_Component_Type => Other_Source,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 20,
Table_Increment => 100,
Table_Name => "Makegpr.Other_Mains");
-- Table to store the mains of languages other than Ada, either specified
-- on the command line or found in attribute Main of the main project file.
package Sources_Compiled is new GNAT.HTable.Simple_HTable
(Header_Num => Header_Num,
Element => Boolean,
No_Element => False,
Key => File_Name_Type,
Hash => Hash,
Equal => "=");
package Saved_Switches is new Table.Table
(Table_Component_Type => String_Access,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 10,
Table_Increment => 100,
Table_Name => "Makegpr.Saved_Switches");
-- Table to store the switches to be passed to gnatmake
Initial_Argument_Count : constant Positive := 20;
type Boolean_Array is array (Positive range <>) of Boolean;
type Booleans is access Boolean_Array;
procedure Free is new Ada.Unchecked_Deallocation (Boolean_Array, Booleans);
Arguments : Argument_List_Access :=
new Argument_List (1 .. Initial_Argument_Count);
-- Used to store lists of arguments to be used when spawning a process
Arguments_Displayed : Booleans :=
new Boolean_Array (1 .. Initial_Argument_Count);
-- For each argument in Arguments, indicate if the argument should be
-- displayed when procedure Display_Command is called.
Last_Argument : Natural := 0;
-- Index of the last valid argument in Arguments
package Cache_Args is new Table.Table
(Table_Component_Type => String_Access,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 200,
Table_Increment => 100,
Table_Name => "Makegpr.Cache_Args");
-- A table to cache arguments, to avoid multiple allocation of the same
-- strings. It is not possible to use a hash table, because String is
-- an unconstrained type.
-- Various switches used when spawning processes:
Dash_B_String : aliased String := "-B";
Dash_B : constant String_Access := Dash_B_String'Access;
Dash_c_String : aliased String := "-c";
Dash_c : constant String_Access := Dash_c_String'Access;
Dash_cargs_String : aliased String := "-cargs";
Dash_cargs : constant String_Access := Dash_cargs_String'Access;
Dash_d_String : aliased String := "-d";
Dash_d : constant String_Access := Dash_d_String'Access;
Dash_f_String : aliased String := "-f";
Dash_f : constant String_Access := Dash_f_String'Access;
Dash_k_String : aliased String := "-k";
Dash_k : constant String_Access := Dash_k_String'Access;
Dash_largs_String : aliased String := "-largs";
Dash_largs : constant String_Access := Dash_largs_String'Access;
Dash_M_String : aliased String := "-M";
Dash_M : constant String_Access := Dash_M_String'Access;
Dash_margs_String : aliased String := "-margs";
Dash_margs : constant String_Access := Dash_margs_String'Access;
Dash_o_String : aliased String := "-o";
Dash_o : constant String_Access := Dash_o_String'Access;
Dash_P_String : aliased String := "-P";
Dash_P : constant String_Access := Dash_P_String'Access;
Dash_q_String : aliased String := "-q";
Dash_q : constant String_Access := Dash_q_String'Access;
Dash_u_String : aliased String := "-u";
Dash_u : constant String_Access := Dash_u_String'Access;
Dash_v_String : aliased String := "-v";
Dash_v : constant String_Access := Dash_v_String'Access;
Dash_vP1_String : aliased String := "-vP1";
Dash_vP1 : constant String_Access := Dash_vP1_String'Access;
Dash_vP2_String : aliased String := "-vP2";
Dash_vP2 : constant String_Access := Dash_vP2_String'Access;
Dash_x_String : aliased String := "-x";
Dash_x : constant String_Access := Dash_x_String'Access;
r_String : aliased String := "r";
r : constant String_Access := r_String'Access;
CPATH : constant String := "CPATH";
-- The environment variable to set when compiler is a GCC compiler
-- to indicate the include directory path.
Current_Include_Paths : array (First_Language_Indexes) of String_Access;
-- A cache for the paths of included directories, to avoid setting
-- env var CPATH unnecessarily.
C_Plus_Plus_Is_Used : Boolean := False;
-- True when there are sources in C++
Link_Options_Switches : Argument_List_Access := null;
-- The link options coming from the attributes Linker'Linker_Options in
-- project files imported, directly or indirectly, by the main project.
Total_Number_Of_Errors : Natural := 0;
-- Used when Keep_Going is True (switch -k) to keep the total number
-- of compilation/linking errors, to report at the end of execution.
Need_To_Rebuild_Global_Archive : Boolean := False;
Error_Header : constant String := "*** ERROR: ";
-- The beginning of error message, when Keep_Going is True
Need_To_Relink : Boolean := False;
-- True when an executable of a language other than Ada need to be linked
Global_Archive_Exists : Boolean := False;
-- True if there is a non empty global archive, to prevent creation
-- of such archives.
Path_Option : String_Access;
-- The path option switch, when supported
Project_Of_Current_Object_Directory : Project_Id := No_Project;
-- The object directory of the project for the last compilation. Avoid
-- calling Change_Dir if the current working directory is already this
-- directory.
package Lib_Path is new Table.Table
(Table_Component_Type => Character,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 200,
Table_Increment => 100,
Table_Name => "Makegpr.Lib_Path");
-- A table to compute the path to put in the path option switch, when it
-- is supported.
procedure Add_Archives (For_Gnatmake : Boolean);
-- Add to Arguments the list of archives for linking an executable
procedure Add_Argument (Arg : String_Access; Display : Boolean);
procedure Add_Argument (Arg : String; Display : Boolean);
-- Add an argument to Arguments. Reallocate if necessary
procedure Add_Arguments (Args : Argument_List; Display : Boolean);
-- Add a list of arguments to Arguments. Reallocate if necessary
procedure Add_Option (Arg : String);
-- Add a switch for the Ada, C or C++ compiler, or for the linker.
-- The table where this option is stored depends on the values of
-- Current_Processor and Current_Language.
procedure Add_Search_Directories
(Data : Project_Data;
Language : First_Language_Indexes);
-- Either add to the Arguments the necessary -I switches needed to
-- compile, or, when compiler is gcc/g++, set up the C*INCLUDE_PATH
-- environment variable, if necessary.
procedure Add_Source_Id (Project : Project_Id; Id : Other_Source_Id);
-- Add a source id to Source_Indexes, with Found set to False
procedure Add_Switches
(Data : Project_Data;
Proc : Processor;
Language : Language_Index;
File_Name : File_Name_Type);
-- Add to Arguments the switches, if any, for a source (attribute Switches)
-- or language (attribute Default_Switches), coming from package Compiler
-- or Linker (depending on Proc) of a specified project file.
procedure Build_Global_Archive;
-- Build the archive for the main project
procedure Build_Library (Project : Project_Id; Unconditionally : Boolean);
-- Build the library for a library project. If Unconditionally is
-- False, first check if the library is up to date, and build it only
-- if it is not.
procedure Check (Option : String);
-- Check that a switch coming from a project file is not the concatenation
-- of several valid switch, for example "-g -v". If it is, issue a warning.
procedure Check_Archive_Builder;
-- Check if the archive builder (ar) is there
procedure Check_Compilation_Needed
(Source : Other_Source;
Need_To_Compile : out Boolean);
-- Check if a source of a language other than Ada needs to be compiled or
-- recompiled.
procedure Check_For_C_Plus_Plus;
-- Check if C++ is used in at least one project
procedure Compile
(Source_Id : Other_Source_Id;
Data : Project_Data;
Local_Errors : in out Boolean);
-- Compile one non-Ada source
procedure Compile_Individual_Sources;
-- Compile the sources specified on the command line, when in
-- Unique_Compile mode.
procedure Compile_Link_With_Gnatmake (Mains_Specified : Boolean);
-- Compile/Link with gnatmake when there are Ada sources in the main
-- project. Arguments may already contain options to be used by
-- gnatmake. Used for both Ada mains and mains of other languages.
-- When Compile_Only is True, do not use the linking options
procedure Compile_Sources;
-- Compile the sources of languages other than Ada, if necessary
procedure Copyright;
-- Output the Copyright notice
procedure Create_Archive_Dependency_File
(Name : String;
First_Source : Other_Source_Id);
-- Create the archive dependency file for a library project
procedure Create_Global_Archive_Dependency_File (Name : String);
-- Create the archive depenency file for the main project
procedure Display_Command
(Name : String;
Path : String_Access;
CPATH : String_Access := null;
Ellipse : Boolean := False);
-- Display the command for a spawned process, if in Verbose_Mode or not in
-- Quiet_Output. In non verbose mode, when Ellipse is True, display "..."
-- in place of the first argument that has Display set to False.
procedure Get_Compiler (For_Language : First_Language_Indexes);
-- Find the compiler name and path name for a specified programming
-- language, if not already done. Results are in the corresponding elements
-- of arrays Compiler_Names and Compiler_Paths. Name of compiler is found
-- in package IDE of the main project, or defaulted. Fail if compiler
-- cannot be found on the path. For the Ada language, gnatmake, rather than
-- the Ada compiler is returned.
procedure Get_Imported_Directories
(Project : Project_Id;
Data : in out Project_Data);
-- Find the necessary switches -I to be used when compiling sources of
-- languages other than Ada, in a specified project file. Cache the result
-- in component Imported_Directories_Switches of the project data. For
-- gcc/g++ compilers, get the value of the C*_INCLUDE_PATH, instead.
procedure Initialize;
-- Do the necessary package initialization and process the command line
-- arguments.
function Is_Included_In_Global_Archive
(Object_Name : File_Name_Type;
Project : Project_Id) return Boolean;
-- Return True if the object Object_Name is not overridden by a source
-- in a project extending project Project.
procedure Link_Executables;
-- Link executables
procedure Report_Error (S1 : String; S2 : String := ""; S3 : String := "");
-- Report an error. If Keep_Going is False, just call Osint.Fail. If
-- Keep_Going is True, display the error and increase the total number of
-- errors.
procedure Report_Total_Errors (Kind : String);
-- If Total_Number_Of_Errors is not zero, report it, and fail
procedure Scan_Arg (Arg : String);
-- Process one command line argument
function Strip_CR_LF (Text : String) return String;
-- Remove characters ASCII.CR and ASCII.LF from a String
procedure Usage;
-- Display the usage
------------------
-- Add_Archives --
------------------
procedure Add_Archives (For_Gnatmake : Boolean) is
Last_Arg : constant Natural := Last_Argument;
-- The position of the last argument before adding the archives. Used to
-- reverse the order of the arguments added when processing the
-- archives.
procedure Recursive_Add_Archives (Project : Project_Id);
-- Recursive procedure to add the archive of a project file, if any,
-- then call itself for the project imported.
----------------------------
-- Recursive_Add_Archives --
----------------------------
procedure Recursive_Add_Archives (Project : Project_Id) is
Data : Project_Data;
Imported : Project_List;
Prj : Project_Id;
procedure Add_Archive_Path;
-- For a library project or the main project, add the archive
-- path to the arguments.
----------------------
-- Add_Archive_Path --
----------------------
procedure Add_Archive_Path is
Increment : Positive;
Prev_Last : Positive;
begin
if Data.Library then
-- If it is a library project file, nothing to do if gnatmake
-- will be invoked, because gnatmake will take care of it, even
-- if the library is not an Ada library.
if not For_Gnatmake then
if Data.Library_Kind = Static then
Add_Argument
(Get_Name_String (Data.Display_Library_Dir) &
Directory_Separator &
"lib" & Get_Name_String (Data.Library_Name) &
'.' & Archive_Ext,
Verbose_Mode);
else
-- As we first insert in the reverse order,
-- -L
is put after -l
Add_Argument
("-l" & Get_Name_String (Data.Library_Name),
Verbose_Mode);
Get_Name_String (Data.Display_Library_Dir);
Add_Argument
("-L" & Name_Buffer (1 .. Name_Len),
Verbose_Mode);
-- If there is a run path option, prepend this directory
-- to the library path. It is probable that the order of
-- the directories in the path option is not important,
-- but just in case put the directories in the same order
-- as the libraries.
if Path_Option /= null then
-- If it is not the first directory, make room at the
-- beginning of the table, including for a path
-- separator.
if Lib_Path.Last > 0 then
Increment := Name_Len + 1;
Prev_Last := Lib_Path.Last;
Lib_Path.Set_Last (Prev_Last + Increment);
for Index in reverse 1 .. Prev_Last loop
Lib_Path.Table (Index + Increment) :=
Lib_Path.Table (Index);
end loop;
Lib_Path.Table (Increment) := Path_Separator;
else
-- If it is the first directory, just set
-- Last to the length of the directory.
Lib_Path.Set_Last (Name_Len);
end if;
-- Put the directory at the beginning of the
-- table.
for Index in 1 .. Name_Len loop
Lib_Path.Table (Index) := Name_Buffer (Index);
end loop;
end if;
end if;
end if;
-- For a non-library project, the only archive needed is the one
-- for the main project, if there is one.
elsif Project = Main_Project and then Global_Archive_Exists then
Add_Argument
(Get_Name_String (Data.Display_Object_Dir) &
Directory_Separator &
"lib" & Get_Name_String (Data.Display_Name)
& '.' & Archive_Ext,
Verbose_Mode);
end if;
end Add_Archive_Path;
begin
-- Nothing to do when there is no project specified
if Project /= No_Project then
Data := Project_Tree.Projects.Table (Project);
-- Nothing to do if the project has already been processed
if not Data.Seen then
-- Mark the project as processed, to avoid processing it again
Project_Tree.Projects.Table (Project).Seen := True;
Recursive_Add_Archives (Data.Extends);
Imported := Data.Imported_Projects;
-- Call itself recursively for all imported projects
while Imported /= Empty_Project_List loop
Prj := Project_Tree.Project_Lists.Table
(Imported).Project;
if Prj /= No_Project then
while Project_Tree.Projects.Table
(Prj).Extended_By /= No_Project
loop
Prj := Project_Tree.Projects.Table
(Prj).Extended_By;
end loop;
Recursive_Add_Archives (Prj);
end if;
Imported := Project_Tree.Project_Lists.Table
(Imported).Next;
end loop;
-- If there is sources of language other than Ada in this
-- project, add the path of the archive to Arguments.
if Project = Main_Project
or else Data.Other_Sources_Present
then
Add_Archive_Path;
end if;
end if;
end if;
end Recursive_Add_Archives;
-- Start of processing for Add_Archives
begin
-- First, mark all projects as not processed
for Project in Project_Table.First ..
Project_Table.Last (Project_Tree.Projects)
loop
Project_Tree.Projects.Table (Project).Seen := False;
end loop;
-- Take care of the run path option
if Path_Option = null then
Path_Option := MLib.Linker_Library_Path_Option;
end if;
Lib_Path.Set_Last (0);
-- Add archives in the reverse order
Recursive_Add_Archives (Main_Project);
-- And reverse the order
declare
First : Positive;
Last : Natural;
Temp : String_Access;
begin
First := Last_Arg + 1;
Last := Last_Argument;
while First < Last loop
Temp := Arguments (First);
Arguments (First) := Arguments (Last);
Arguments (Last) := Temp;
First := First + 1;
Last := Last - 1;
end loop;
end;
end Add_Archives;
------------------
-- Add_Argument --
------------------
procedure Add_Argument (Arg : String_Access; Display : Boolean) is
begin
-- Nothing to do if no argument is specified or if argument is empty
if Arg /= null or else Arg'Length = 0 then
-- Reallocate arrays if necessary
if Last_Argument = Arguments'Last then
declare
New_Arguments : constant Argument_List_Access :=
new Argument_List
(1 .. Last_Argument +
Initial_Argument_Count);
New_Arguments_Displayed : constant Booleans :=
new Boolean_Array
(1 .. Last_Argument +
Initial_Argument_Count);
begin
New_Arguments (Arguments'Range) := Arguments.all;
-- To avoid deallocating the strings, nullify all components
-- of Arguments before calling Free.
Arguments.all := (others => null);
Free (Arguments);
Arguments := New_Arguments;
New_Arguments_Displayed (Arguments_Displayed'Range) :=
Arguments_Displayed.all;
Free (Arguments_Displayed);
Arguments_Displayed := New_Arguments_Displayed;
end;
end if;
-- Add the argument and its display indication
Last_Argument := Last_Argument + 1;
Arguments (Last_Argument) := Arg;
Arguments_Displayed (Last_Argument) := Display;
end if;
end Add_Argument;
procedure Add_Argument (Arg : String; Display : Boolean) is
Argument : String_Access := null;
begin
-- Nothing to do if argument is empty
if Arg'Length > 0 then
-- Check if the argument is already in the Cache_Args table.
-- If it is already there, reuse the allocated value.
for Index in 1 .. Cache_Args.Last loop
if Cache_Args.Table (Index).all = Arg then
Argument := Cache_Args.Table (Index);
exit;
end if;
end loop;
-- If the argument is not in the cache, create a new entry in the
-- cache.
if Argument = null then
Argument := new String'(Arg);
Cache_Args.Increment_Last;
Cache_Args.Table (Cache_Args.Last) := Argument;
end if;
-- And add the argument
Add_Argument (Argument, Display);
end if;
end Add_Argument;
-------------------
-- Add_Arguments --
-------------------
procedure Add_Arguments (Args : Argument_List; Display : Boolean) is
begin
-- Reallocate the arrays, if necessary
if Last_Argument + Args'Length > Arguments'Last then
declare
New_Arguments : constant Argument_List_Access :=
new Argument_List
(1 .. Last_Argument + Args'Length +
Initial_Argument_Count);
New_Arguments_Displayed : constant Booleans :=
new Boolean_Array
(1 .. Last_Argument +
Args'Length +
Initial_Argument_Count);
begin
New_Arguments (1 .. Last_Argument) :=
Arguments (1 .. Last_Argument);
-- To avoid deallocating the strings, nullify all components
-- of Arguments before calling Free.
Arguments.all := (others => null);
Free (Arguments);
Arguments := New_Arguments;
New_Arguments_Displayed (1 .. Last_Argument) :=
Arguments_Displayed (1 .. Last_Argument);
Free (Arguments_Displayed);
Arguments_Displayed := New_Arguments_Displayed;
end;
end if;
-- Add the new arguments and the display indications
Arguments (Last_Argument + 1 .. Last_Argument + Args'Length) := Args;
Arguments_Displayed (Last_Argument + 1 .. Last_Argument + Args'Length) :=
(others => Display);
Last_Argument := Last_Argument + Args'Length;
end Add_Arguments;
----------------
-- Add_Option --
----------------
procedure Add_Option (Arg : String) is
Option : constant String_Access := new String'(Arg);
begin
case Current_Processor is
when None =>
null;
when Linker =>
-- Add option to the linker table
Linker_Options.Increment_Last;
Linker_Options.Table (Linker_Options.Last) := Option;
when Compiler =>
-- Add option to the compiler option table, depending on the
-- value of Current_Language.
Comp_Opts.Increment_Last (Options (Current_Language));
Options (Current_Language).Table
(Comp_Opts.Last (Options (Current_Language))) := Option;
end case;
end Add_Option;
-------------------
-- Add_Source_Id --
-------------------
procedure Add_Source_Id (Project : Project_Id; Id : Other_Source_Id) is
begin
-- Reallocate the array, if necessary
if Last_Source = Source_Indexes'Last then
declare
New_Indexes : constant Source_Indexes_Ref :=
new Source_Index_Array
(1 .. Source_Indexes'Last +
Initial_Source_Index_Count);
begin
New_Indexes (Source_Indexes'Range) := Source_Indexes.all;
Free (Source_Indexes);
Source_Indexes := New_Indexes;
end;
end if;
Last_Source := Last_Source + 1;
Source_Indexes (Last_Source) := (Project, Id, False);
end Add_Source_Id;
----------------------------
-- Add_Search_Directories --
----------------------------
procedure Add_Search_Directories
(Data : Project_Data;
Language : First_Language_Indexes)
is
begin
-- If a GNU compiler is used, set the CPATH environment variable,
-- if it does not already has the correct value.
if Compiler_Is_Gcc (Language) then
if Current_Include_Paths (Language) /= Data.Include_Path then
Current_Include_Paths (Language) := Data.Include_Path;
Setenv (CPATH, Data.Include_Path.all);
end if;
else
Add_Arguments (Data.Imported_Directories_Switches.all, Verbose_Mode);
end if;
end Add_Search_Directories;
------------------
-- Add_Switches --
------------------
procedure Add_Switches
(Data : Project_Data;
Proc : Processor;
Language : Language_Index;
File_Name : File_Name_Type)
is
Switches : Variable_Value;
-- The switches, if any, for the file/language
Pkg : Package_Id;
-- The id of the package where to look for the switches
Defaults : Array_Element_Id;
-- The Default_Switches associative array
Switches_Array : Array_Element_Id;
-- The Switches associative array
Element_Id : String_List_Id;
Element : String_Element;
begin
-- First, choose the proper package
case Proc is
when None =>
raise Program_Error;
when Linker =>
Pkg := Value_Of (Name_Linker, Data.Decl.Packages, Project_Tree);
when Compiler =>
Pkg := Value_Of (Name_Compiler, Data.Decl.Packages, Project_Tree);
end case;
if Pkg /= No_Package then
-- Get the Switches ("file name"), if they exist
Switches_Array := Prj.Util.Value_Of
(Name => Name_Switches,
In_Arrays => Project_Tree.Packages.Table
(Pkg).Decl.Arrays,
In_Tree => Project_Tree);
Switches :=
Prj.Util.Value_Of
(Index => Name_Id (File_Name),
Src_Index => 0,
In_Array => Switches_Array,
In_Tree => Project_Tree);
-- Otherwise, get the Default_Switches ("language"), if they exist
if Switches = Nil_Variable_Value then
Defaults := Prj.Util.Value_Of
(Name => Name_Default_Switches,
In_Arrays => Project_Tree.Packages.Table
(Pkg).Decl.Arrays,
In_Tree => Project_Tree);
Switches := Prj.Util.Value_Of
(Index => Language_Names.Table (Language),
Src_Index => 0,
In_Array => Defaults,
In_Tree => Project_Tree);
end if;
-- If there are switches, add them to Arguments
if Switches /= Nil_Variable_Value then
Element_Id := Switches.Values;
while Element_Id /= Nil_String loop
Element := Project_Tree.String_Elements.Table
(Element_Id);
if Element.Value /= No_Name then
Get_Name_String (Element.Value);
if not Quiet_Output then
-- When not in quiet output (no -q), check that the
-- switch is not the concatenation of several valid
-- switches, such as "-g -v". If it is, issue a warning.
Check (Option => Name_Buffer (1 .. Name_Len));
end if;
Add_Argument (Name_Buffer (1 .. Name_Len), True);
end if;
Element_Id := Element.Next;
end loop;
end if;
end if;
end Add_Switches;
--------------------------
-- Build_Global_Archive --
--------------------------
procedure Build_Global_Archive is
Data : Project_Data := Project_Tree.Projects.Table (Main_Project);
Source_Id : Other_Source_Id;
S_Id : Other_Source_Id;
Source : Other_Source;
Success : Boolean;
Archive_Name : constant String :=
"lib"
& Get_Name_String (Data.Display_Name)
& '.'
& Archive_Ext;
-- The name of the archive file for this project
Archive_Dep_Name : constant String :=
"lib"
& Get_Name_String (Data.Display_Name)
& ".deps";
-- The name of the archive dependency file for this project
Need_To_Rebuild : Boolean := Need_To_Rebuild_Global_Archive;
-- When True, archive will be rebuilt
File : Prj.Util.Text_File;
Object_Path : Path_Name_Type;
Time_Stamp : Time_Stamp_Type;
Saved_Last_Argument : Natural;
First_Object : Natural;
Discard : Boolean;
begin
Check_Archive_Builder;
if Project_Of_Current_Object_Directory /= Main_Project then
Project_Of_Current_Object_Directory := Main_Project;
Change_Dir (Get_Name_String (Data.Object_Directory));
if Verbose_Mode then
Write_Str ("Changing to object directory of """);
Write_Name (Data.Display_Name);
Write_Str (""": """);
Write_Name (Data.Display_Object_Dir);
Write_Line ("""");
end if;
end if;
if not Need_To_Rebuild then
if Verbose_Mode then
Write_Str (" Checking ");
Write_Line (Archive_Name);
end if;
-- If the archive does not exist, of course it needs to be built
if not Is_Regular_File (Archive_Name) then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Line (" -> archive does not exist");
end if;
-- Archive does exist
else
-- Check the archive dependency file
Open (File, Archive_Dep_Name);
-- If the archive dependency file does not exist, we need to
-- rebuild the archive and to create its dependency file.
if not Is_Valid (File) then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> archive dependency file ");
Write_Str (Archive_Dep_Name);
Write_Line (" does not exist");
end if;
else
-- Put all sources of language other than Ada in Source_Indexes
declare
Local_Data : Project_Data;
begin
Last_Source := 0;
for Proj in Project_Table.First ..
Project_Table.Last (Project_Tree.Projects)
loop
Local_Data := Project_Tree.Projects.Table (Proj);
if not Local_Data.Library then
Source_Id := Local_Data.First_Other_Source;
while Source_Id /= No_Other_Source loop
Add_Source_Id (Proj, Source_Id);
Source_Id := Project_Tree.Other_Sources.Table
(Source_Id).Next;
end loop;
end if;
end loop;
end;
-- Read the dependency file, line by line
while not End_Of_File (File) loop
Get_Line (File, Name_Buffer, Name_Len);
-- First line is the path of the object file
Object_Path := Name_Find;
Source_Id := No_Other_Source;
-- Check if this object file is for a source of this project
for S in 1 .. Last_Source loop
S_Id := Source_Indexes (S).Id;
Source := Project_Tree.Other_Sources.Table (S_Id);
if (not Source_Indexes (S).Found)
and then Source.Object_Path = Object_Path
then
-- We have found the object file: get the source data,
-- and mark it as found.
Source_Id := S_Id;
Source_Indexes (S).Found := True;
exit;
end if;
end loop;
-- If it is not for a source of this project, then the
-- archive needs to be rebuilt.
if Source_Id = No_Other_Source then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> ");
Write_Str (Get_Name_String (Object_Path));
Write_Line (" is not an object of any project");
end if;
exit;
end if;
-- The second line is the time stamp of the object file. If
-- there is no next line, then the dependency file is
-- truncated, and the archive need to be rebuilt.
if End_Of_File (File) then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> archive dependency file ");
Write_Line (" is truncated");
end if;
exit;
end if;
Get_Line (File, Name_Buffer, Name_Len);
-- If the line has the wrong number of characters, then
-- the dependency file is incorrectly formatted, and the
-- archive needs to be rebuilt.
if Name_Len /= Time_Stamp_Length then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> archive dependency file ");
Write_Line (" is incorrectly formatted (time stamp)");
end if;
exit;
end if;
Time_Stamp := Time_Stamp_Type (Name_Buffer (1 .. Name_Len));
-- If the time stamp in the dependency file is different
-- from the time stamp of the object file, then the archive
-- needs to be rebuilt.
if Time_Stamp /= Source.Object_TS then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> time stamp of ");
Write_Str (Get_Name_String (Object_Path));
Write_Str (" is incorrect in the archive");
Write_Line (" dependency file");
end if;
exit;
end if;
end loop;
Close (File);
end if;
end if;
end if;
if not Need_To_Rebuild then
if Verbose_Mode then
Write_Line (" -> up to date");
end if;
-- No need to create a global archive, if there is no object
-- file to put into.
Global_Archive_Exists := Last_Source /= 0;
-- Archive needs to be rebuilt
else
-- If archive already exists, first delete it
-- Comment needed on why we discard result???
if Is_Regular_File (Archive_Name) then
Delete_File (Archive_Name, Discard);
end if;
Last_Argument := 0;
-- Start with the options found in MLib.Tgt (usually just "rc")
Add_Arguments (Archive_Builder_Options.all, True);
-- Followed by the archive name
Add_Argument (Archive_Name, True);
First_Object := Last_Argument;
-- Followed by all the object files of the non library projects
for Proj in Project_Table.First ..
Project_Table.Last (Project_Tree.Projects)
loop
Data := Project_Tree.Projects.Table (Proj);
if not Data.Library then
Source_Id := Data.First_Other_Source;
while Source_Id /= No_Other_Source loop
Source :=
Project_Tree.Other_Sources.Table (Source_Id);
-- Only include object file name that have not been
-- overriden in extending projects.
if Is_Included_In_Global_Archive
(Source.Object_Name, Proj)
then
Add_Argument
(Get_Name_String (Source.Object_Path),
Verbose_Mode or (First_Object = Last_Argument));
end if;
Source_Id := Source.Next;
end loop;
end if;
end loop;
-- No need to create a global archive, if there is no object
-- file to put into.
Global_Archive_Exists := Last_Argument > First_Object;
if Global_Archive_Exists then
-- If the archive is built, then linking will need to occur
-- unconditionally.
Need_To_Relink := True;
-- Spawn the archive builder (ar)
Saved_Last_Argument := Last_Argument;
Last_Argument := First_Object + Max_In_Archives;
loop
if Last_Argument > Saved_Last_Argument then
Last_Argument := Saved_Last_Argument;
end if;
Display_Command
(Archive_Builder,
Archive_Builder_Path,
Ellipse => True);
Spawn
(Archive_Builder_Path.all,
Arguments (1 .. Last_Argument),
Success);
exit when not Success
or else Last_Argument = Saved_Last_Argument;
Arguments (1) := r;
Arguments (3 .. Saved_Last_Argument - Last_Argument + 2) :=
Arguments (Last_Argument + 1 .. Saved_Last_Argument);
Saved_Last_Argument := Saved_Last_Argument - Last_Argument + 2;
end loop;
-- If the archive was built, run the archive indexer (ranlib)
-- if there is one.
if Success then
if Archive_Indexer_Path /= null then
Last_Argument := 0;
Add_Argument (Archive_Name, True);
Display_Command (Archive_Indexer, Archive_Indexer_Path);
Spawn
(Archive_Indexer_Path.all, Arguments (1 .. 1), Success);
if not Success then
-- Running ranlib failed, delete the dependency file,
-- if it exists.
if Is_Regular_File (Archive_Dep_Name) then
Delete_File (Archive_Dep_Name, Success);
end if;
-- And report the error
Report_Error
("running" & Archive_Indexer & " for project """,
Get_Name_String (Data.Display_Name),
""" failed");
return;
end if;
end if;
-- The archive was correctly built, create its dependency file
Create_Global_Archive_Dependency_File (Archive_Dep_Name);
-- Building the archive failed, delete dependency file if one
-- exists.
else
if Is_Regular_File (Archive_Dep_Name) then
Delete_File (Archive_Dep_Name, Success);
end if;
-- And report the error
Report_Error
("building archive for project """,
Get_Name_String (Data.Display_Name),
""" failed");
end if;
end if;
end if;
end Build_Global_Archive;
-------------------
-- Build_Library --
-------------------
procedure Build_Library (Project : Project_Id; Unconditionally : Boolean) is
Data : constant Project_Data :=
Project_Tree.Projects.Table (Project);
Source_Id : Other_Source_Id;
Source : Other_Source;
Archive_Name : constant String :=
"lib" & Get_Name_String (Data.Display_Name)
& '.' & Archive_Ext;
-- The name of the archive file for this project
Archive_Dep_Name : constant String :=
"lib" & Get_Name_String (Data.Display_Name)
& ".deps";
-- The name of the archive dependency file for this project
Need_To_Rebuild : Boolean := Unconditionally;
-- When True, archive will be rebuilt
File : Prj.Util.Text_File;
Object_Name : File_Name_Type;
Time_Stamp : Time_Stamp_Type;
Driver_Name : Name_Id := No_Name;
Lib_Opts : Argument_List_Access := No_Argument'Access;
begin
Check_Archive_Builder;
-- If Unconditionally is False, check if the archive need to be built
if not Need_To_Rebuild then
if Verbose_Mode then
Write_Str (" Checking ");
Write_Line (Archive_Name);
end if;
-- If the archive does not exist, of course it needs to be built
if not Is_Regular_File (Archive_Name) then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Line (" -> archive does not exist");
end if;
-- Archive does exist
else
-- Check the archive dependency file
Open (File, Archive_Dep_Name);
-- If the archive dependency file does not exist, we need to
-- rebuild the archive and to create its dependency file.
if not Is_Valid (File) then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> archive dependency file ");
Write_Str (Archive_Dep_Name);
Write_Line (" does not exist");
end if;
else
-- Put all sources of language other than Ada in Source_Indexes
Last_Source := 0;
Source_Id := Data.First_Other_Source;
while Source_Id /= No_Other_Source loop
Add_Source_Id (Project, Source_Id);
Source_Id :=
Project_Tree.Other_Sources.Table (Source_Id).Next;
end loop;
-- Read the dependency file, line by line
while not End_Of_File (File) loop
Get_Line (File, Name_Buffer, Name_Len);
-- First line is the name of an object file
Object_Name := Name_Find;
Source_Id := No_Other_Source;
-- Check if this object file is for a source of this project
for S in 1 .. Last_Source loop
if (not Source_Indexes (S).Found)
and then
Project_Tree.Other_Sources.Table
(Source_Indexes (S).Id).Object_Name = Object_Name
then
-- We have found the object file: get the source
-- data, and mark it as found.
Source_Id := Source_Indexes (S).Id;
Source := Project_Tree.Other_Sources.Table
(Source_Id);
Source_Indexes (S).Found := True;
exit;
end if;
end loop;
-- If it is not for a source of this project, then the
-- archive needs to be rebuilt.
if Source_Id = No_Other_Source then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> ");
Write_Str (Get_Name_String (Object_Name));
Write_Line (" is not an object of the project");
end if;
exit;
end if;
-- The second line is the time stamp of the object file.
-- If there is no next line, then the dependency file is
-- truncated, and the archive need to be rebuilt.
if End_Of_File (File) then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> archive dependency file ");
Write_Line (" is truncated");
end if;
exit;
end if;
Get_Line (File, Name_Buffer, Name_Len);
-- If the line has the wrong number of character, then
-- the dependency file is incorrectly formatted, and the
-- archive needs to be rebuilt.
if Name_Len /= Time_Stamp_Length then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> archive dependency file ");
Write_Line (" is incorrectly formatted (time stamp)");
end if;
exit;
end if;
Time_Stamp := Time_Stamp_Type (Name_Buffer (1 .. Name_Len));
-- If the time stamp in the dependency file is different
-- from the time stamp of the object file, then the archive
-- needs to be rebuilt.
if Time_Stamp /= Source.Object_TS then
Need_To_Rebuild := True;
if Verbose_Mode then
Write_Str (" -> time stamp of ");
Write_Str (Get_Name_String (Object_Name));
Write_Str (" is incorrect in the archive");
Write_Line (" dependency file");
end if;
exit;
end if;
end loop;
Close (File);
if not Need_To_Rebuild then
-- Now, check if all object files of the project have been
-- accounted for. If any of them is not in the dependency
-- file, the archive needs to be rebuilt.
for Index in 1 .. Last_Source loop
if not Source_Indexes (Index).Found then
Need_To_Rebuild := True;
if Verbose_Mode then
Source_Id := Source_Indexes (Index).Id;
Source := Project_Tree.Other_Sources.Table
(Source_Id);
Write_Str (" -> ");
Write_Str (Get_Name_String (Source.Object_Name));
Write_Str (" is not in the archive ");
Write_Line ("dependency file");
end if;
exit;
end if;
end loop;
end if;
if (not Need_To_Rebuild) and Verbose_Mode then
Write_Line (" -> up to date");
end if;
end if;
end if;
end if;
-- Build the library if necessary
if Need_To_Rebuild then
-- If a library is built, then linking will need to occur
-- unconditionally.
Need_To_Relink := True;
Last_Argument := 0;
-- If there are sources in Ada, then gnatmake will build the library,
-- so nothing to do.
if not Data.Languages (Ada_Language_Index) then
-- Get all the object files of the project
Source_Id := Data.First_Other_Source;
while Source_Id /= No_Other_Source loop
Source := Project_Tree.Other_Sources.Table (Source_Id);
Add_Argument
(Get_Name_String (Source.Object_Name), Verbose_Mode);
Source_Id := Source.Next;
end loop;
-- If it is a library, it need to be built it the same way Ada
-- libraries are built.
if Data.Library_Kind = Static then
MLib.Build_Library
(Ofiles => Arguments (1 .. Last_Argument),
Afiles => No_Argument,
Output_File => Get_Name_String (Data.Library_Name),
Output_Dir => Get_Name_String (Data.Display_Library_Dir));
else
-- Link with g++ if C++ is one of the languages, otherwise
-- building the library may fail with unresolved symbols.
if C_Plus_Plus_Is_Used then
if Compiler_Names (C_Plus_Plus_Language_Index) = null then
Get_Compiler (C_Plus_Plus_Language_Index);
end if;
if Compiler_Is_Gcc (C_Plus_Plus_Language_Index) then
Name_Len := 0;
Add_Str_To_Name_Buffer
(Compiler_Names (C_Plus_Plus_Language_Index).all);
Driver_Name := Name_Find;
end if;
end if;
-- If Library_Options is specified, add these options
declare
Library_Options : constant Variable_Value :=
Value_Of
(Name_Library_Options,
Data.Decl.Attributes,
Project_Tree);
begin
if not Library_Options.Default then
declare
Current : String_List_Id;
Element : String_Element;
begin
Current := Library_Options.Values;
while Current /= Nil_String loop
Element :=
Project_Tree.String_Elements.Table (Current);
Get_Name_String (Element.Value);
if Name_Len /= 0 then
Library_Opts.Increment_Last;
Library_Opts.Table (Library_Opts.Last) :=
new String'(Name_Buffer (1 .. Name_Len));
end if;
Current := Element.Next;
end loop;
end;
end if;
Lib_Opts :=
new Argument_List'(Argument_List
(Library_Opts.Table (1 .. Library_Opts.Last)));
end;
MLib.Tgt.Build_Dynamic_Library
(Ofiles => Arguments (1 .. Last_Argument),
Foreign => Arguments (1 .. Last_Argument),
Afiles => No_Argument,
Options => No_Argument,
Options_2 => Lib_Opts.all,
Interfaces => No_Argument,
Lib_Filename => Get_Name_String (Data.Library_Name),
Lib_Dir => Get_Name_String (Data.Library_Dir),
Symbol_Data => No_Symbols,
Driver_Name => Driver_Name,
Lib_Version => "",
Auto_Init => False);
end if;
end if;
-- Create fake empty archive, so we can check its time stamp later
declare
Archive : Ada.Text_IO.File_Type;
begin
Create (Archive, Out_File, Archive_Name);
Close (Archive);
end;
Create_Archive_Dependency_File
(Archive_Dep_Name, Data.First_Other_Source);
end if;
end Build_Library;
-----------
-- Check --
-----------
procedure Check (Option : String) is
First : Positive := Option'First;
Last : Natural;
begin
for Index in Option'First + 1 .. Option'Last - 1 loop
if Option (Index) = ' ' and then Option (Index + 1) = '-' then
Write_Str ("warning: switch """);
Write_Str (Option);
Write_Str (""" is suspicious; consider using ");
Last := First;
while Last <= Option'Last loop
if Option (Last) = ' ' then
if First /= Option'First then
Write_Str (", ");
end if;
Write_Char ('"');
Write_Str (Option (First .. Last - 1));
Write_Char ('"');
while Last <= Option'Last and then Option (Last) = ' ' loop
Last := Last + 1;
end loop;
First := Last;
else
if Last = Option'Last then
if First /= Option'First then
Write_Str (", ");
end if;
Write_Char ('"');
Write_Str (Option (First .. Last));
Write_Char ('"');
end if;
Last := Last + 1;
end if;
end loop;
Write_Line (" instead");
exit;
end if;
end loop;
end Check;
---------------------------
-- Check_Archive_Builder --
---------------------------
procedure Check_Archive_Builder is
begin
-- First, make sure that the archive builder (ar) is on the path
if Archive_Builder_Path = null then
Archive_Builder_Path := Locate_Exec_On_Path (Archive_Builder);
if Archive_Builder_Path = null then
Osint.Fail
("unable to locate archive builder """,
Archive_Builder,
"""");
end if;
-- If there is an archive indexer (ranlib), try to locate it on the
-- path. Don't fail if it is not found.
if Archive_Indexer /= "" then
Archive_Indexer_Path := Locate_Exec_On_Path (Archive_Indexer);
end if;
end if;
end Check_Archive_Builder;
------------------------------
-- Check_Compilation_Needed --
------------------------------
procedure Check_Compilation_Needed
(Source : Other_Source;
Need_To_Compile : out Boolean)
is
Source_Name : constant String := Get_Name_String (Source.File_Name);
Source_Path : constant String := Get_Name_String (Source.Path_Name);
Object_Name : constant String := Get_Name_String (Source.Object_Name);
Dep_Name : constant String := Get_Name_String (Source.Dep_Name);
C_Source_Path : String := Source_Path;
Source_In_Dependencies : Boolean := False;
-- Set True if source was found in dependency file of its object file
Dep_File : Prj.Util.Text_File;
Start : Natural;
Finish : Natural;
Looping : Boolean := False;
-- Set to True at the end of the first Big_Loop
begin
Canonical_Case_File_Name (C_Source_Path);
-- Assume the worst, so that statement "return;" may be used if there
-- is any problem.
Need_To_Compile := True;
if Verbose_Mode then
Write_Str (" Checking ");
Write_Str (Source_Name);
Write_Line (" ... ");
end if;
-- If object file does not exist, of course source need to be compiled
if Source.Object_TS = Empty_Time_Stamp then
if Verbose_Mode then
Write_Str (" -> object file ");
Write_Str (Object_Name);
Write_Line (" does not exist");
end if;
return;
end if;
-- If the object file has been created before the last modification
-- of the source, the source need to be recompiled.
if Source.Object_TS < Source.Source_TS then
if Verbose_Mode then
Write_Str (" -> object file ");
Write_Str (Object_Name);
Write_Line (" has time stamp earlier than source");
end if;
return;
end if;
-- If there is no dependency file, then the source needs to be
-- recompiled and the dependency file need to be created.
if Source.Dep_TS = Empty_Time_Stamp then
if Verbose_Mode then
Write_Str (" -> dependency file ");
Write_Str (Dep_Name);
Write_Line (" does not exist");
end if;
return;
end if;
-- The source needs to be recompiled if the source has been modified
-- after the dependency file has been created.
if Source.Dep_TS < Source.Source_TS then
if Verbose_Mode then
Write_Str (" -> dependency file ");
Write_Str (Dep_Name);
Write_Line (" has time stamp earlier than source");
end if;
return;
end if;
-- Look for all dependencies
Open (Dep_File, Dep_Name);
-- If dependency file cannot be open, we need to recompile the source
if not Is_Valid (Dep_File) then
if Verbose_Mode then
Write_Str (" -> could not open dependency file ");
Write_Line (Dep_Name);
end if;
return;
end if;
-- Loop Big_Loop is executed several times only when the dependency file
-- contains several times
--