| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- M A K E U T L -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2004-2015, 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 3, 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 COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with ALI; use ALI; |
| with Atree; use Atree; |
| with Debug; |
| with Err_Vars; use Err_Vars; |
| with Errutil; |
| with Fname; |
| with Osint; use Osint; |
| with Output; use Output; |
| with Opt; use Opt; |
| with Prj.Com; |
| with Prj.Err; |
| with Prj.Ext; |
| with Prj.Util; use Prj.Util; |
| with Sinput.P; |
| with Tempdir; |
| |
| with Ada.Command_Line; use Ada.Command_Line; |
| with Ada.Unchecked_Deallocation; |
| |
| with GNAT.Case_Util; use GNAT.Case_Util; |
| with GNAT.Directory_Operations; use GNAT.Directory_Operations; |
| with GNAT.HTable; |
| with GNAT.Regexp; use GNAT.Regexp; |
| |
| package body Makeutl is |
| |
| type Linker_Options_Data is record |
| Project : Project_Id; |
| Options : String_List_Id; |
| end record; |
| |
| Linker_Option_Initial_Count : constant := 20; |
| |
| Linker_Options_Buffer : String_List_Access := |
| new String_List (1 .. Linker_Option_Initial_Count); |
| |
| Last_Linker_Option : Natural := 0; |
| |
| package Linker_Opts is new Table.Table ( |
| Table_Component_Type => Linker_Options_Data, |
| Table_Index_Type => Integer, |
| Table_Low_Bound => 1, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Make.Linker_Opts"); |
| |
| procedure Add_Linker_Option (Option : String); |
| |
| --------- |
| -- Add -- |
| --------- |
| |
| procedure Add |
| (Option : String_Access; |
| To : in out String_List_Access; |
| Last : in out Natural) |
| is |
| begin |
| if Last = To'Last then |
| declare |
| New_Options : constant String_List_Access := |
| new String_List (1 .. To'Last * 2); |
| |
| begin |
| New_Options (To'Range) := To.all; |
| |
| -- Set all elements of the original options to null to avoid |
| -- deallocation of copies. |
| |
| To.all := (others => null); |
| |
| Free (To); |
| To := New_Options; |
| end; |
| end if; |
| |
| Last := Last + 1; |
| To (Last) := Option; |
| end Add; |
| |
| procedure Add |
| (Option : String; |
| To : in out String_List_Access; |
| Last : in out Natural) |
| is |
| begin |
| Add (Option => new String'(Option), To => To, Last => Last); |
| end Add; |
| |
| ----------------------- |
| -- Add_Linker_Option -- |
| ----------------------- |
| |
| procedure Add_Linker_Option (Option : String) is |
| begin |
| if Option'Length > 0 then |
| if Last_Linker_Option = Linker_Options_Buffer'Last then |
| declare |
| New_Buffer : constant String_List_Access := |
| new String_List |
| (1 .. Linker_Options_Buffer'Last + |
| Linker_Option_Initial_Count); |
| begin |
| New_Buffer (Linker_Options_Buffer'Range) := |
| Linker_Options_Buffer.all; |
| Linker_Options_Buffer.all := (others => null); |
| Free (Linker_Options_Buffer); |
| Linker_Options_Buffer := New_Buffer; |
| end; |
| end if; |
| |
| Last_Linker_Option := Last_Linker_Option + 1; |
| Linker_Options_Buffer (Last_Linker_Option) := new String'(Option); |
| end if; |
| end Add_Linker_Option; |
| |
| ------------------- |
| -- Absolute_Path -- |
| ------------------- |
| |
| function Absolute_Path |
| (Path : Path_Name_Type; |
| Project : Project_Id) return String |
| is |
| begin |
| Get_Name_String (Path); |
| |
| declare |
| Path_Name : constant String := Name_Buffer (1 .. Name_Len); |
| |
| begin |
| if Is_Absolute_Path (Path_Name) then |
| return Path_Name; |
| |
| else |
| declare |
| Parent_Directory : constant String := |
| Get_Name_String |
| (Project.Directory.Display_Name); |
| |
| begin |
| return Parent_Directory & Path_Name; |
| end; |
| end if; |
| end; |
| end Absolute_Path; |
| |
| ---------------------------- |
| -- Aggregate_Libraries_In -- |
| ---------------------------- |
| |
| function Aggregate_Libraries_In (Tree : Project_Tree_Ref) return Boolean is |
| List : Project_List; |
| |
| begin |
| List := Tree.Projects; |
| while List /= null loop |
| if List.Project.Qualifier = Aggregate_Library then |
| return True; |
| end if; |
| |
| List := List.Next; |
| end loop; |
| |
| return False; |
| end Aggregate_Libraries_In; |
| |
| ------------------------- |
| -- Base_Name_Index_For -- |
| ------------------------- |
| |
| function Base_Name_Index_For |
| (Main : String; |
| Main_Index : Int; |
| Index_Separator : Character) return File_Name_Type |
| is |
| Result : File_Name_Type; |
| |
| begin |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Base_Name (Main)); |
| |
| -- Remove the extension, if any, that is the last part of the base name |
| -- starting with a dot and following some characters. |
| |
| for J in reverse 2 .. Name_Len loop |
| if Name_Buffer (J) = '.' then |
| Name_Len := J - 1; |
| exit; |
| end if; |
| end loop; |
| |
| -- Add the index info, if index is different from 0 |
| |
| if Main_Index > 0 then |
| Add_Char_To_Name_Buffer (Index_Separator); |
| |
| declare |
| Img : constant String := Main_Index'Img; |
| begin |
| Add_Str_To_Name_Buffer (Img (2 .. Img'Last)); |
| end; |
| end if; |
| |
| Result := Name_Find; |
| return Result; |
| end Base_Name_Index_For; |
| |
| ------------------------------ |
| -- Check_Source_Info_In_ALI -- |
| ------------------------------ |
| |
| function Check_Source_Info_In_ALI |
| (The_ALI : ALI_Id; |
| Tree : Project_Tree_Ref) return Name_Id |
| is |
| Result : Name_Id := No_Name; |
| Unit_Name : Name_Id; |
| |
| begin |
| -- Loop through units |
| |
| for U in ALIs.Table (The_ALI).First_Unit .. |
| ALIs.Table (The_ALI).Last_Unit |
| loop |
| -- Check if the file name is one of the source of the unit |
| |
| Get_Name_String (Units.Table (U).Uname); |
| Name_Len := Name_Len - 2; |
| Unit_Name := Name_Find; |
| |
| if File_Not_A_Source_Of (Tree, Unit_Name, Units.Table (U).Sfile) then |
| return No_Name; |
| end if; |
| |
| if Result = No_Name then |
| Result := Unit_Name; |
| end if; |
| |
| -- Loop to do same check for each of the withed units |
| |
| for W in Units.Table (U).First_With .. Units.Table (U).Last_With loop |
| declare |
| WR : ALI.With_Record renames Withs.Table (W); |
| |
| begin |
| if WR.Sfile /= No_File then |
| Get_Name_String (WR.Uname); |
| Name_Len := Name_Len - 2; |
| Unit_Name := Name_Find; |
| |
| if File_Not_A_Source_Of (Tree, Unit_Name, WR.Sfile) then |
| return No_Name; |
| end if; |
| end if; |
| end; |
| end loop; |
| end loop; |
| |
| -- Loop to check subunits and replaced sources |
| |
| for D in ALIs.Table (The_ALI).First_Sdep .. |
| ALIs.Table (The_ALI).Last_Sdep |
| loop |
| declare |
| SD : Sdep_Record renames Sdep.Table (D); |
| |
| begin |
| Unit_Name := SD.Subunit_Name; |
| |
| if Unit_Name = No_Name then |
| |
| -- Check if this source file has been replaced by a source with |
| -- a different file name. |
| |
| if Tree /= null and then Tree.Replaced_Source_Number > 0 then |
| declare |
| Replacement : constant File_Name_Type := |
| Replaced_Source_HTable.Get |
| (Tree.Replaced_Sources, SD.Sfile); |
| |
| begin |
| if Replacement /= No_File then |
| if Verbose_Mode then |
| Write_Line |
| ("source file" |
| & Get_Name_String (SD.Sfile) |
| & " has been replaced by " |
| & Get_Name_String (Replacement)); |
| end if; |
| |
| return No_Name; |
| end if; |
| end; |
| end if; |
| |
| -- Check that a dependent source for a unit that is from a |
| -- project is indeed a source of this unit. |
| |
| Unit_Name := SD.Unit_Name; |
| |
| if Unit_Name /= No_Name |
| and then not Fname.Is_Internal_File_Name (SD.Sfile) |
| and then File_Not_A_Source_Of (Tree, Unit_Name, SD.Sfile) |
| then |
| return No_Name; |
| end if; |
| |
| else |
| -- For separates, the file is no longer associated with the |
| -- unit ("proc-sep.adb" is not associated with unit "proc.sep") |
| -- so we need to check whether the source file still exists in |
| -- the source tree: it will if it matches the naming scheme |
| -- (and then will be for the same unit). |
| |
| if Find_Source |
| (In_Tree => Tree, |
| Project => No_Project, |
| Base_Name => SD.Sfile) = No_Source |
| then |
| -- If this is not a runtime file or if, when gnatmake switch |
| -- -a is used, we are not able to find this subunit in the |
| -- source directories, then recompilation is needed. |
| |
| if not Fname.Is_Internal_File_Name (SD.Sfile) |
| or else |
| (Check_Readonly_Files |
| and then Full_Source_Name (SD.Sfile) = No_File) |
| then |
| if Verbose_Mode then |
| Write_Line |
| ("While parsing ALI file, file " |
| & Get_Name_String (SD.Sfile) |
| & " is indicated as containing subunit " |
| & Get_Name_String (Unit_Name) |
| & " but this does not match what was found while" |
| & " parsing the project. Will recompile"); |
| end if; |
| |
| return No_Name; |
| end if; |
| end if; |
| end if; |
| end; |
| end loop; |
| |
| return Result; |
| end Check_Source_Info_In_ALI; |
| |
| -------------------------------- |
| -- Create_Binder_Mapping_File -- |
| -------------------------------- |
| |
| function Create_Binder_Mapping_File |
| (Project_Tree : Project_Tree_Ref) return Path_Name_Type |
| is |
| Mapping_Path : Path_Name_Type := No_Path; |
| |
| Mapping_FD : File_Descriptor := Invalid_FD; |
| -- A File Descriptor for an eventual mapping file |
| |
| ALI_Unit : Unit_Name_Type := No_Unit_Name; |
| -- The unit name of an ALI file |
| |
| ALI_Name : File_Name_Type := No_File; |
| -- The file name of the ALI file |
| |
| ALI_Project : Project_Id := No_Project; |
| -- The project of the ALI file |
| |
| Bytes : Integer; |
| OK : Boolean := False; |
| Unit : Unit_Index; |
| |
| Status : Boolean; |
| -- For call to Close |
| |
| Iter : Source_Iterator := For_Each_Source |
| (In_Tree => Project_Tree, |
| Language => Name_Ada, |
| Encapsulated_Libs => False, |
| Locally_Removed => False); |
| |
| Source : Prj.Source_Id; |
| |
| begin |
| Tempdir.Create_Temp_File (Mapping_FD, Mapping_Path); |
| Record_Temp_File (Project_Tree.Shared, Mapping_Path); |
| |
| if Mapping_FD /= Invalid_FD then |
| OK := True; |
| |
| loop |
| Source := Element (Iter); |
| exit when Source = No_Source; |
| |
| Unit := Source.Unit; |
| |
| if Source.Replaced_By /= No_Source |
| or else Unit = No_Unit_Index |
| or else Unit.Name = No_Name |
| then |
| ALI_Name := No_File; |
| |
| -- If this is a body, put it in the mapping |
| |
| elsif Source.Kind = Impl |
| and then Unit.File_Names (Impl) /= No_Source |
| and then Unit.File_Names (Impl).Project /= No_Project |
| then |
| Get_Name_String (Unit.Name); |
| Add_Str_To_Name_Buffer ("%b"); |
| ALI_Unit := Name_Find; |
| ALI_Name := |
| Lib_File_Name (Unit.File_Names (Impl).Display_File); |
| ALI_Project := Unit.File_Names (Impl).Project; |
| |
| -- Otherwise, if this is a spec and there is no body, put it in |
| -- the mapping. |
| |
| elsif Source.Kind = Spec |
| and then Unit.File_Names (Impl) = No_Source |
| and then Unit.File_Names (Spec) /= No_Source |
| and then Unit.File_Names (Spec).Project /= No_Project |
| then |
| Get_Name_String (Unit.Name); |
| Add_Str_To_Name_Buffer ("%s"); |
| ALI_Unit := Name_Find; |
| ALI_Name := |
| Lib_File_Name (Unit.File_Names (Spec).Display_File); |
| ALI_Project := Unit.File_Names (Spec).Project; |
| |
| else |
| ALI_Name := No_File; |
| end if; |
| |
| -- If we have something to put in the mapping then do it now. If |
| -- the project is extended, look for the ALI file in the project, |
| -- then in the extending projects in order, and use the last one |
| -- found. |
| |
| if ALI_Name /= No_File then |
| |
| -- Look in the project and the projects that are extending it |
| -- to find the real ALI file. |
| |
| declare |
| ALI : constant String := Get_Name_String (ALI_Name); |
| ALI_Path : Name_Id := No_Name; |
| |
| begin |
| loop |
| -- For library projects, use the library ALI directory, |
| -- for other projects, use the object directory. |
| |
| if ALI_Project.Library then |
| Get_Name_String |
| (ALI_Project.Library_ALI_Dir.Display_Name); |
| else |
| Get_Name_String |
| (ALI_Project.Object_Directory.Display_Name); |
| end if; |
| |
| Add_Str_To_Name_Buffer (ALI); |
| |
| if Is_Regular_File (Name_Buffer (1 .. Name_Len)) then |
| ALI_Path := Name_Find; |
| end if; |
| |
| ALI_Project := ALI_Project.Extended_By; |
| exit when ALI_Project = No_Project; |
| end loop; |
| |
| if ALI_Path /= No_Name then |
| |
| -- First line is the unit name |
| |
| Get_Name_String (ALI_Unit); |
| Add_Char_To_Name_Buffer (ASCII.LF); |
| Bytes := |
| Write |
| (Mapping_FD, |
| Name_Buffer (1)'Address, |
| Name_Len); |
| OK := Bytes = Name_Len; |
| |
| exit when not OK; |
| |
| -- Second line is the ALI file name |
| |
| Get_Name_String (ALI_Name); |
| Add_Char_To_Name_Buffer (ASCII.LF); |
| Bytes := |
| Write |
| (Mapping_FD, |
| Name_Buffer (1)'Address, |
| Name_Len); |
| OK := (Bytes = Name_Len); |
| |
| exit when not OK; |
| |
| -- Third line is the ALI path name |
| |
| Get_Name_String (ALI_Path); |
| Add_Char_To_Name_Buffer (ASCII.LF); |
| Bytes := |
| Write |
| (Mapping_FD, |
| Name_Buffer (1)'Address, |
| Name_Len); |
| OK := (Bytes = Name_Len); |
| |
| -- If OK is False, it means we were unable to write a |
| -- line. No point in continuing with the other units. |
| |
| exit when not OK; |
| end if; |
| end; |
| end if; |
| |
| Next (Iter); |
| end loop; |
| |
| Close (Mapping_FD, Status); |
| |
| OK := OK and Status; |
| end if; |
| |
| -- If the creation of the mapping file was successful, we add the switch |
| -- to the arguments of gnatbind. |
| |
| if OK then |
| return Mapping_Path; |
| |
| else |
| return No_Path; |
| end if; |
| end Create_Binder_Mapping_File; |
| |
| ----------------- |
| -- Create_Name -- |
| ----------------- |
| |
| function Create_Name (Name : String) return File_Name_Type is |
| begin |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Name); |
| return Name_Find; |
| end Create_Name; |
| |
| function Create_Name (Name : String) return Name_Id is |
| begin |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Name); |
| return Name_Find; |
| end Create_Name; |
| |
| function Create_Name (Name : String) return Path_Name_Type is |
| begin |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Name); |
| return Name_Find; |
| end Create_Name; |
| |
| --------------------------- |
| -- Ensure_Absolute_Path -- |
| --------------------------- |
| |
| procedure Ensure_Absolute_Path |
| (Switch : in out String_Access; |
| Parent : String; |
| Do_Fail : Fail_Proc; |
| For_Gnatbind : Boolean := False; |
| Including_Non_Switch : Boolean := True; |
| Including_RTS : Boolean := False) |
| is |
| begin |
| if Switch /= null then |
| declare |
| Sw : String (1 .. Switch'Length); |
| Start : Positive; |
| |
| begin |
| Sw := Switch.all; |
| |
| if Sw (1) = '-' then |
| if Sw'Length >= 3 |
| and then (Sw (2) = 'I' |
| or else (not For_Gnatbind |
| and then (Sw (2) = 'L' |
| or else |
| Sw (2) = 'A'))) |
| then |
| Start := 3; |
| |
| if Sw = "-I-" then |
| return; |
| end if; |
| |
| elsif Sw'Length >= 4 |
| and then |
| (Sw (2 .. 3) = "aL" or else |
| Sw (2 .. 3) = "aO" or else |
| Sw (2 .. 3) = "aI" |
| or else (For_Gnatbind and then Sw (2 .. 3) = "A=")) |
| then |
| Start := 4; |
| |
| elsif Including_RTS |
| and then Sw'Length >= 7 |
| and then Sw (2 .. 6) = "-RTS=" |
| then |
| Start := 7; |
| |
| else |
| return; |
| end if; |
| |
| -- Because relative path arguments to --RTS= may be relative to |
| -- the search directory prefix, those relative path arguments |
| -- are converted only when they include directory information. |
| |
| if not Is_Absolute_Path (Sw (Start .. Sw'Last)) then |
| if Parent'Length = 0 then |
| Do_Fail |
| ("relative search path switches (""" |
| & Sw |
| & """) are not allowed"); |
| |
| elsif Including_RTS then |
| for J in Start .. Sw'Last loop |
| if Sw (J) = Directory_Separator then |
| Switch := |
| new String' |
| (Sw (1 .. Start - 1) |
| & Parent |
| & Directory_Separator |
| & Sw (Start .. Sw'Last)); |
| return; |
| end if; |
| end loop; |
| |
| else |
| Switch := |
| new String' |
| (Sw (1 .. Start - 1) |
| & Parent |
| & Directory_Separator |
| & Sw (Start .. Sw'Last)); |
| end if; |
| end if; |
| |
| elsif Including_Non_Switch then |
| if not Is_Absolute_Path (Sw) then |
| if Parent'Length = 0 then |
| Do_Fail |
| ("relative paths (""" & Sw & """) are not allowed"); |
| else |
| Switch := new String'(Parent & Directory_Separator & Sw); |
| end if; |
| end if; |
| end if; |
| end; |
| end if; |
| end Ensure_Absolute_Path; |
| |
| ---------------------------- |
| -- Executable_Prefix_Path -- |
| ---------------------------- |
| |
| function Executable_Prefix_Path return String is |
| Exec_Name : constant String := Command_Name; |
| |
| function Get_Install_Dir (S : String) return String; |
| -- S is the executable name preceded by the absolute or relative path, |
| -- e.g. "c:\usr\bin\gcc.exe". Returns the absolute directory where "bin" |
| -- lies (in the example "C:\usr"). If the executable is not in a "bin" |
| -- directory, return "". |
| |
| --------------------- |
| -- Get_Install_Dir -- |
| --------------------- |
| |
| function Get_Install_Dir (S : String) return String is |
| Exec : String := S; |
| Path_Last : Integer := 0; |
| |
| begin |
| for J in reverse Exec'Range loop |
| if Exec (J) = Directory_Separator then |
| Path_Last := J - 1; |
| exit; |
| end if; |
| end loop; |
| |
| if Path_Last >= Exec'First + 2 then |
| To_Lower (Exec (Path_Last - 2 .. Path_Last)); |
| end if; |
| |
| if Path_Last < Exec'First + 2 |
| or else Exec (Path_Last - 2 .. Path_Last) /= "bin" |
| or else (Path_Last - 3 >= Exec'First |
| and then Exec (Path_Last - 3) /= Directory_Separator) |
| then |
| return ""; |
| end if; |
| |
| return Normalize_Pathname |
| (Exec (Exec'First .. Path_Last - 4), |
| Resolve_Links => Opt.Follow_Links_For_Dirs) |
| & Directory_Separator; |
| end Get_Install_Dir; |
| |
| -- Beginning of Executable_Prefix_Path |
| |
| begin |
| -- First determine if a path prefix was placed in front of the |
| -- executable name. |
| |
| for J in reverse Exec_Name'Range loop |
| if Exec_Name (J) = Directory_Separator then |
| return Get_Install_Dir (Exec_Name); |
| end if; |
| end loop; |
| |
| -- If we get here, the user has typed the executable name with no |
| -- directory prefix. |
| |
| declare |
| Path : String_Access := Locate_Exec_On_Path (Exec_Name); |
| begin |
| if Path = null then |
| return ""; |
| else |
| declare |
| Dir : constant String := Get_Install_Dir (Path.all); |
| begin |
| Free (Path); |
| return Dir; |
| end; |
| end if; |
| end; |
| end Executable_Prefix_Path; |
| |
| ------------------ |
| -- Fail_Program -- |
| ------------------ |
| |
| procedure Fail_Program |
| (Project_Tree : Project_Tree_Ref; |
| S : String; |
| Flush_Messages : Boolean := True) |
| is |
| begin |
| if Flush_Messages and not No_Exit_Message then |
| if Total_Errors_Detected /= 0 or else Warnings_Detected /= 0 then |
| Errutil.Finalize; |
| end if; |
| end if; |
| |
| Finish_Program (Project_Tree, E_Fatal, S => S); |
| end Fail_Program; |
| |
| -------------------- |
| -- Finish_Program -- |
| -------------------- |
| |
| procedure Finish_Program |
| (Project_Tree : Project_Tree_Ref; |
| Exit_Code : Osint.Exit_Code_Type := Osint.E_Success; |
| S : String := "") |
| is |
| begin |
| if not Debug.Debug_Flag_N then |
| Delete_Temp_Config_Files (Project_Tree); |
| |
| if Project_Tree /= null then |
| Delete_All_Temp_Files (Project_Tree.Shared); |
| end if; |
| end if; |
| |
| if S'Length > 0 then |
| if Exit_Code /= E_Success then |
| if No_Exit_Message then |
| Osint.Exit_Program (E_Fatal); |
| else |
| Osint.Fail (S); |
| end if; |
| |
| elsif not No_Exit_Message then |
| Write_Str (S); |
| end if; |
| end if; |
| |
| -- Output Namet statistics |
| |
| Namet.Finalize; |
| |
| Exit_Program (Exit_Code); |
| end Finish_Program; |
| |
| -------------------------- |
| -- File_Not_A_Source_Of -- |
| -------------------------- |
| |
| function File_Not_A_Source_Of |
| (Project_Tree : Project_Tree_Ref; |
| Uname : Name_Id; |
| Sfile : File_Name_Type) return Boolean |
| is |
| Unit : constant Unit_Index := |
| Units_Htable.Get (Project_Tree.Units_HT, Uname); |
| |
| At_Least_One_File : Boolean := False; |
| |
| begin |
| if Unit /= No_Unit_Index then |
| for F in Unit.File_Names'Range loop |
| if Unit.File_Names (F) /= null then |
| At_Least_One_File := True; |
| if Unit.File_Names (F).File = Sfile then |
| return False; |
| end if; |
| end if; |
| end loop; |
| |
| if not At_Least_One_File then |
| |
| -- The unit was probably created initially for a separate unit |
| -- (which are initially created as IMPL when both suffixes are the |
| -- same). Later on, Override_Kind changed the type of the file, |
| -- and the unit is no longer valid in fact. |
| |
| return False; |
| end if; |
| |
| Verbose_Msg (Uname, "sources do not include ", Name_Id (Sfile)); |
| return True; |
| end if; |
| |
| return False; |
| end File_Not_A_Source_Of; |
| |
| --------------------- |
| -- Get_Directories -- |
| --------------------- |
| |
| procedure Get_Directories |
| (Project_Tree : Project_Tree_Ref; |
| For_Project : Project_Id; |
| Activity : Activity_Type; |
| Languages : Name_Ids) |
| is |
| |
| procedure Recursive_Add |
| (Project : Project_Id; |
| Tree : Project_Tree_Ref; |
| Extended : in out Boolean); |
| -- Add all the source directories of a project to the path only if |
| -- this project has not been visited. Calls itself recursively for |
| -- projects being extended, and imported projects. |
| |
| procedure Add_Dir (Value : Path_Name_Type); |
| -- Add directory Value in table Directories, if it is defined and not |
| -- already there. |
| |
| ------------- |
| -- Add_Dir -- |
| ------------- |
| |
| procedure Add_Dir (Value : Path_Name_Type) is |
| Add_It : Boolean := True; |
| |
| begin |
| if Value /= No_Path |
| and then Is_Directory (Get_Name_String (Value)) |
| then |
| for Index in 1 .. Directories.Last loop |
| if Directories.Table (Index) = Value then |
| Add_It := False; |
| exit; |
| end if; |
| end loop; |
| |
| if Add_It then |
| Directories.Increment_Last; |
| Directories.Table (Directories.Last) := Value; |
| end if; |
| end if; |
| end Add_Dir; |
| |
| ------------------- |
| -- Recursive_Add -- |
| ------------------- |
| |
| procedure Recursive_Add |
| (Project : Project_Id; |
| Tree : Project_Tree_Ref; |
| Extended : in out Boolean) |
| is |
| Current : String_List_Id; |
| Dir : String_Element; |
| OK : Boolean := False; |
| Lang_Proc : Language_Ptr := Project.Languages; |
| |
| begin |
| -- Add to path all directories of this project |
| |
| if Activity = Compilation then |
| Lang_Loop : |
| while Lang_Proc /= No_Language_Index loop |
| for J in Languages'Range loop |
| OK := Lang_Proc.Name = Languages (J); |
| exit Lang_Loop when OK; |
| end loop; |
| |
| Lang_Proc := Lang_Proc.Next; |
| end loop Lang_Loop; |
| |
| if OK then |
| Current := Project.Source_Dirs; |
| |
| while Current /= Nil_String loop |
| Dir := Tree.Shared.String_Elements.Table (Current); |
| Add_Dir (Path_Name_Type (Dir.Value)); |
| Current := Dir.Next; |
| end loop; |
| end if; |
| |
| elsif Project.Library then |
| if Activity = SAL_Binding and then Extended then |
| Add_Dir (Project.Object_Directory.Display_Name); |
| |
| else |
| Add_Dir (Project.Library_ALI_Dir.Display_Name); |
| end if; |
| |
| else |
| Add_Dir (Project.Object_Directory.Display_Name); |
| end if; |
| |
| if Project.Extends = No_Project then |
| Extended := False; |
| end if; |
| end Recursive_Add; |
| |
| procedure For_All_Projects is |
| new For_Every_Project_Imported (Boolean, Recursive_Add); |
| |
| Extended : Boolean := True; |
| |
| -- Start of processing for Get_Directories |
| |
| begin |
| Directories.Init; |
| For_All_Projects (For_Project, Project_Tree, Extended); |
| end Get_Directories; |
| |
| ------------------ |
| -- Get_Switches -- |
| ------------------ |
| |
| procedure Get_Switches |
| (Source : Prj.Source_Id; |
| Pkg_Name : Name_Id; |
| Project_Tree : Project_Tree_Ref; |
| Value : out Variable_Value; |
| Is_Default : out Boolean) |
| is |
| begin |
| Get_Switches |
| (Source_File => Source.File, |
| Source_Lang => Source.Language.Name, |
| Source_Prj => Source.Project, |
| Pkg_Name => Pkg_Name, |
| Project_Tree => Project_Tree, |
| Value => Value, |
| Is_Default => Is_Default); |
| end Get_Switches; |
| |
| ------------------ |
| -- Get_Switches -- |
| ------------------ |
| |
| procedure Get_Switches |
| (Source_File : File_Name_Type; |
| Source_Lang : Name_Id; |
| Source_Prj : Project_Id; |
| Pkg_Name : Name_Id; |
| Project_Tree : Project_Tree_Ref; |
| Value : out Variable_Value; |
| Is_Default : out Boolean; |
| Test_Without_Suffix : Boolean := False; |
| Check_ALI_Suffix : Boolean := False) |
| is |
| Project : constant Project_Id := |
| Ultimate_Extending_Project_Of (Source_Prj); |
| Pkg : constant Package_Id := |
| Prj.Util.Value_Of |
| (Name => Pkg_Name, |
| In_Packages => Project.Decl.Packages, |
| Shared => Project_Tree.Shared); |
| Lang : Language_Ptr; |
| |
| begin |
| Is_Default := False; |
| |
| if Source_File /= No_File then |
| Value := Prj.Util.Value_Of |
| (Name => Name_Id (Source_File), |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Pkg, |
| Shared => Project_Tree.Shared, |
| Allow_Wildcards => True); |
| end if; |
| |
| if Value = Nil_Variable_Value and then Test_Without_Suffix then |
| Lang := |
| Get_Language_From_Name (Project, Get_Name_String (Source_Lang)); |
| |
| if Lang /= null then |
| declare |
| Naming : Lang_Naming_Data renames Lang.Config.Naming_Data; |
| SF_Name : constant String := Get_Name_String (Source_File); |
| Last : Positive := SF_Name'Length; |
| Name : String (1 .. Last + 3); |
| Spec_Suffix : String := Get_Name_String (Naming.Spec_Suffix); |
| Body_Suffix : String := Get_Name_String (Naming.Body_Suffix); |
| Truncated : Boolean := False; |
| |
| begin |
| Canonical_Case_File_Name (Spec_Suffix); |
| Canonical_Case_File_Name (Body_Suffix); |
| Name (1 .. Last) := SF_Name; |
| |
| if Last > Body_Suffix'Length |
| and then |
| Name (Last - Body_Suffix'Length + 1 .. Last) = Body_Suffix |
| then |
| Truncated := True; |
| Last := Last - Body_Suffix'Length; |
| end if; |
| |
| if not Truncated |
| and then Last > Spec_Suffix'Length |
| and then |
| Name (Last - Spec_Suffix'Length + 1 .. Last) = Spec_Suffix |
| then |
| Truncated := True; |
| Last := Last - Spec_Suffix'Length; |
| end if; |
| |
| if Truncated then |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Name (1 .. Last)); |
| |
| Value := Prj.Util.Value_Of |
| (Name => Name_Find, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Pkg, |
| Shared => Project_Tree.Shared, |
| Allow_Wildcards => True); |
| end if; |
| |
| if Value = Nil_Variable_Value and then Check_ALI_Suffix then |
| Last := SF_Name'Length; |
| while Name (Last) /= '.' loop |
| Last := Last - 1; |
| end loop; |
| |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Name (1 .. Last)); |
| Add_Str_To_Name_Buffer ("ali"); |
| |
| Value := Prj.Util.Value_Of |
| (Name => Name_Find, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Pkg, |
| Shared => Project_Tree.Shared, |
| Allow_Wildcards => True); |
| end if; |
| end; |
| end if; |
| end if; |
| |
| if Value = Nil_Variable_Value then |
| Is_Default := True; |
| Value := |
| Prj.Util.Value_Of |
| (Name => Source_Lang, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Pkg, |
| Shared => Project_Tree.Shared, |
| Force_Lower_Case_Index => True); |
| end if; |
| |
| if Value = Nil_Variable_Value then |
| Value := |
| Prj.Util.Value_Of |
| (Name => All_Other_Names, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Pkg, |
| Shared => Project_Tree.Shared, |
| Force_Lower_Case_Index => True); |
| end if; |
| |
| if Value = Nil_Variable_Value then |
| Value := |
| Prj.Util.Value_Of |
| (Name => Source_Lang, |
| Attribute_Or_Array_Name => Name_Default_Switches, |
| In_Package => Pkg, |
| Shared => Project_Tree.Shared); |
| end if; |
| end Get_Switches; |
| |
| ------------ |
| -- Inform -- |
| ------------ |
| |
| procedure Inform (N : File_Name_Type; Msg : String) is |
| begin |
| Inform (Name_Id (N), Msg); |
| end Inform; |
| |
| procedure Inform (N : Name_Id := No_Name; Msg : String) is |
| begin |
| Osint.Write_Program_Name; |
| |
| Write_Str (": "); |
| |
| if N /= No_Name then |
| Write_Str (""""); |
| |
| declare |
| Name : constant String := Get_Name_String (N); |
| begin |
| if Debug.Debug_Flag_F and then Is_Absolute_Path (Name) then |
| Write_Str (File_Name (Name)); |
| else |
| Write_Str (Name); |
| end if; |
| end; |
| |
| Write_Str (""" "); |
| end if; |
| |
| Write_Str (Msg); |
| Write_Eol; |
| end Inform; |
| |
| ------------------------------ |
| -- Initialize_Source_Record -- |
| ------------------------------ |
| |
| procedure Initialize_Source_Record (Source : Prj.Source_Id) is |
| |
| procedure Set_Object_Project |
| (Obj_Dir : String; |
| Obj_Proj : Project_Id; |
| Obj_Path : Path_Name_Type; |
| Stamp : Time_Stamp_Type); |
| -- Update information about object file, switches file,... |
| |
| ------------------------ |
| -- Set_Object_Project -- |
| ------------------------ |
| |
| procedure Set_Object_Project |
| (Obj_Dir : String; |
| Obj_Proj : Project_Id; |
| Obj_Path : Path_Name_Type; |
| Stamp : Time_Stamp_Type) is |
| begin |
| Source.Object_Project := Obj_Proj; |
| Source.Object_Path := Obj_Path; |
| Source.Object_TS := Stamp; |
| |
| if Source.Language.Config.Dependency_Kind /= None then |
| declare |
| Dep_Path : constant String := |
| Normalize_Pathname |
| (Name => |
| Get_Name_String (Source.Dep_Name), |
| Resolve_Links => Opt.Follow_Links_For_Files, |
| Directory => Obj_Dir); |
| begin |
| Source.Dep_Path := Create_Name (Dep_Path); |
| Source.Dep_TS := Osint.Unknown_Attributes; |
| end; |
| end if; |
| |
| -- Get the path of the switches file, even if Opt.Check_Switches is |
| -- not set, as switch -s may be in the Builder switches that have not |
| -- been scanned yet. |
| |
| declare |
| Switches_Path : constant String := |
| Normalize_Pathname |
| (Name => |
| Get_Name_String (Source.Switches), |
| Resolve_Links => Opt.Follow_Links_For_Files, |
| Directory => Obj_Dir); |
| begin |
| Source.Switches_Path := Create_Name (Switches_Path); |
| |
| if Stamp /= Empty_Time_Stamp then |
| Source.Switches_TS := File_Stamp (Source.Switches_Path); |
| end if; |
| end; |
| end Set_Object_Project; |
| |
| Obj_Proj : Project_Id; |
| |
| begin |
| -- Nothing to do if source record has already been fully initialized |
| |
| if Source.Initialized then |
| return; |
| end if; |
| |
| -- Systematically recompute the time stamp |
| |
| Source.Source_TS := File_Stamp (Source.Path.Display_Name); |
| |
| -- Parse the source file to check whether we have a subunit |
| |
| if Source.Language.Config.Kind = Unit_Based |
| and then Source.Kind = Impl |
| and then Is_Subunit (Source) |
| then |
| Source.Kind := Sep; |
| end if; |
| |
| if Source.Language.Config.Object_Generated |
| and then Is_Compilable (Source) |
| then |
| -- First, get the correct object file name and dependency file name |
| -- if the source is in a multi-unit file. |
| |
| if Source.Index /= 0 then |
| Source.Object := |
| Object_Name |
| (Source_File_Name => Source.File, |
| Source_Index => Source.Index, |
| Index_Separator => |
| Source.Language.Config.Multi_Unit_Object_Separator, |
| Object_File_Suffix => |
| Source.Language.Config.Object_File_Suffix); |
| |
| Source.Dep_Name := |
| Dependency_Name |
| (Source.Object, Source.Language.Config.Dependency_Kind); |
| end if; |
| |
| -- Find the object file for that source. It could be either in the |
| -- current project or in an extended project (it might actually not |
| -- exist yet in the ultimate extending project, but if not found |
| -- elsewhere that's where we'll expect to find it). |
| |
| Obj_Proj := Source.Project; |
| |
| while Obj_Proj /= No_Project loop |
| if Obj_Proj.Object_Directory /= No_Path_Information then |
| declare |
| Dir : constant String := |
| Get_Name_String (Obj_Proj.Object_Directory.Display_Name); |
| |
| Object_Path : constant String := |
| Normalize_Pathname |
| (Name => Get_Name_String (Source.Object), |
| Resolve_Links => Opt.Follow_Links_For_Files, |
| Directory => Dir); |
| |
| Obj_Path : constant Path_Name_Type := |
| Create_Name (Object_Path); |
| |
| Stamp : Time_Stamp_Type := Empty_Time_Stamp; |
| |
| begin |
| -- For specs, we do not check object files if there is a |
| -- body. This saves a system call. On the other hand, we do |
| -- need to know the object_path, in case the user has passed |
| -- the .ads on the command line to compile the spec only. |
| |
| if Source.Kind /= Spec |
| or else Source.Unit = No_Unit_Index |
| or else Source.Unit.File_Names (Impl) = No_Source |
| then |
| Stamp := File_Stamp (Obj_Path); |
| end if; |
| |
| if Stamp /= Empty_Time_Stamp |
| or else (Obj_Proj.Extended_By = No_Project |
| and then Source.Object_Project = No_Project) |
| then |
| Set_Object_Project (Dir, Obj_Proj, Obj_Path, Stamp); |
| end if; |
| end; |
| end if; |
| |
| Obj_Proj := Obj_Proj.Extended_By; |
| end loop; |
| |
| elsif Source.Language.Config.Dependency_Kind = Makefile then |
| declare |
| Object_Dir : constant String := |
| Get_Name_String (Source.Project.Object_Directory.Display_Name); |
| Dep_Path : constant String := |
| Normalize_Pathname |
| (Name => Get_Name_String (Source.Dep_Name), |
| Resolve_Links => Opt.Follow_Links_For_Files, |
| Directory => Object_Dir); |
| begin |
| Source.Dep_Path := Create_Name (Dep_Path); |
| Source.Dep_TS := Osint.Unknown_Attributes; |
| end; |
| end if; |
| |
| Source.Initialized := True; |
| end Initialize_Source_Record; |
| |
| ---------------------------- |
| -- Is_External_Assignment -- |
| ---------------------------- |
| |
| function Is_External_Assignment |
| (Env : Prj.Tree.Environment; |
| Argv : String) return Boolean |
| is |
| Start : Positive := 3; |
| Finish : Natural := Argv'Last; |
| |
| pragma Assert (Argv'First = 1); |
| pragma Assert (Argv (1 .. 2) = "-X"); |
| |
| begin |
| if Argv'Last < 5 then |
| return False; |
| |
| elsif Argv (3) = '"' then |
| if Argv (Argv'Last) /= '"' or else Argv'Last < 7 then |
| return False; |
| else |
| Start := 4; |
| Finish := Argv'Last - 1; |
| end if; |
| end if; |
| |
| return Prj.Ext.Check |
| (Self => Env.External, |
| Declaration => Argv (Start .. Finish)); |
| end Is_External_Assignment; |
| |
| ---------------- |
| -- Is_Subunit -- |
| ---------------- |
| |
| function Is_Subunit (Source : Prj.Source_Id) return Boolean is |
| Src_Ind : Source_File_Index; |
| |
| begin |
| if Source.Kind = Sep then |
| return True; |
| |
| -- A Spec, a file based language source or a body with a spec cannot be |
| -- a subunit. |
| |
| elsif Source.Kind = Spec |
| or else Source.Unit = No_Unit_Index |
| or else Other_Part (Source) /= No_Source |
| then |
| return False; |
| end if; |
| |
| -- Here, we are assuming that the language is Ada, as it is the only |
| -- unit based language that we know. |
| |
| Src_Ind := |
| Sinput.P.Load_Project_File |
| (Get_Name_String (Source.Path.Display_Name)); |
| |
| return Sinput.P.Source_File_Is_Subunit (Src_Ind); |
| end Is_Subunit; |
| |
| ----------------------------- |
| -- Linker_Options_Switches -- |
| ----------------------------- |
| |
| function Linker_Options_Switches |
| (Project : Project_Id; |
| Do_Fail : Fail_Proc; |
| In_Tree : Project_Tree_Ref) return String_List |
| is |
| procedure Recursive_Add |
| (Proj : Project_Id; |
| In_Tree : Project_Tree_Ref; |
| Dummy : in out Boolean); |
| -- The recursive routine used to add linker options |
| |
| ------------------- |
| -- Recursive_Add -- |
| ------------------- |
| |
| procedure Recursive_Add |
| (Proj : Project_Id; |
| In_Tree : Project_Tree_Ref; |
| Dummy : in out Boolean) |
| is |
| Linker_Package : Package_Id; |
| Options : Variable_Value; |
| |
| begin |
| Linker_Package := |
| Prj.Util.Value_Of |
| (Name => Name_Linker, |
| In_Packages => Proj.Decl.Packages, |
| Shared => In_Tree.Shared); |
| |
| Options := |
| Prj.Util.Value_Of |
| (Name => Name_Ada, |
| Index => 0, |
| Attribute_Or_Array_Name => Name_Linker_Options, |
| In_Package => Linker_Package, |
| Shared => In_Tree.Shared); |
| |
| -- If attribute is present, add the project with the attribute to |
| -- table Linker_Opts. |
| |
| if Options /= Nil_Variable_Value then |
| Linker_Opts.Increment_Last; |
| Linker_Opts.Table (Linker_Opts.Last) := |
| (Project => Proj, Options => Options.Values); |
| end if; |
| end Recursive_Add; |
| |
| procedure For_All_Projects is |
| new For_Every_Project_Imported (Boolean, Recursive_Add); |
| |
| Dummy : Boolean := False; |
| |
| -- Start of processing for Linker_Options_Switches |
| |
| begin |
| Linker_Opts.Init; |
| |
| For_All_Projects (Project, In_Tree, Dummy, Imported_First => True); |
| |
| Last_Linker_Option := 0; |
| |
| for Index in reverse 1 .. Linker_Opts.Last loop |
| declare |
| Options : String_List_Id; |
| Proj : constant Project_Id := |
| Linker_Opts.Table (Index).Project; |
| Option : Name_Id; |
| Dir_Path : constant String := |
| Get_Name_String (Proj.Directory.Name); |
| |
| begin |
| Options := Linker_Opts.Table (Index).Options; |
| while Options /= Nil_String loop |
| Option := In_Tree.Shared.String_Elements.Table (Options).Value; |
| Get_Name_String (Option); |
| |
| -- Do not consider empty linker options |
| |
| if Name_Len /= 0 then |
| Add_Linker_Option (Name_Buffer (1 .. Name_Len)); |
| |
| -- Object files and -L switches specified with relative |
| -- paths must be converted to absolute paths. |
| |
| Ensure_Absolute_Path |
| (Switch => |
| Linker_Options_Buffer (Last_Linker_Option), |
| Parent => Dir_Path, |
| Do_Fail => Do_Fail, |
| For_Gnatbind => False); |
| end if; |
| |
| Options := In_Tree.Shared.String_Elements.Table (Options).Next; |
| end loop; |
| end; |
| end loop; |
| |
| return Linker_Options_Buffer (1 .. Last_Linker_Option); |
| end Linker_Options_Switches; |
| |
| ----------- |
| -- Mains -- |
| ----------- |
| |
| package body Mains is |
| |
| package Names is new Table.Table |
| (Table_Component_Type => Main_Info, |
| Table_Index_Type => Integer, |
| Table_Low_Bound => 1, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Makeutl.Mains.Names"); |
| -- The table that stores the mains |
| |
| Current : Natural := 0; |
| -- The index of the last main retrieved from the table |
| |
| Count_Of_Mains_With_No_Tree : Natural := 0; |
| -- Number of main units for which we do not know the project tree |
| |
| -------------- |
| -- Add_Main -- |
| -------------- |
| |
| procedure Add_Main |
| (Name : String; |
| Index : Int := 0; |
| Location : Source_Ptr := No_Location; |
| Project : Project_Id := No_Project; |
| Tree : Project_Tree_Ref := null) |
| is |
| begin |
| if Current_Verbosity = High then |
| Debug_Output ("Add_Main """ & Name & """ " & Index'Img |
| & " with_tree? " |
| & Boolean'Image (Tree /= null)); |
| end if; |
| |
| Name_Len := 0; |
| Add_Str_To_Name_Buffer (Name); |
| Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); |
| |
| Names.Increment_Last; |
| Names.Table (Names.Last) := |
| (Name_Find, Index, Location, No_Source, Project, Tree); |
| |
| if Tree /= null then |
| Builder_Data (Tree).Number_Of_Mains := |
| Builder_Data (Tree).Number_Of_Mains + 1; |
| |
| else |
| Mains.Count_Of_Mains_With_No_Tree := |
| Mains.Count_Of_Mains_With_No_Tree + 1; |
| end if; |
| end Add_Main; |
| |
| -------------------- |
| -- Complete_Mains -- |
| -------------------- |
| |
| procedure Complete_Mains |
| (Flags : Processing_Flags; |
| Root_Project : Project_Id; |
| Project_Tree : Project_Tree_Ref) |
| is |
| procedure Do_Complete (Project : Project_Id; Tree : Project_Tree_Ref); |
| -- Check the mains for this specific project |
| |
| procedure Complete_All is new For_Project_And_Aggregated |
| (Do_Complete); |
| |
| procedure Add_Multi_Unit_Sources |
| (Tree : Project_Tree_Ref; |
| Source : Prj.Source_Id); |
| -- Add all units from the same file as the multi-unit Source |
| |
| function Find_File_Add_Extension |
| (Tree : Project_Tree_Ref; |
| Base_Main : String) return Prj.Source_Id; |
| -- Search for Main in the project, adding body or spec extensions |
| |
| ---------------------------- |
| -- Add_Multi_Unit_Sources -- |
| ---------------------------- |
| |
| procedure Add_Multi_Unit_Sources |
| (Tree : Project_Tree_Ref; |
| Source : Prj.Source_Id) |
| is |
| Iter : Source_Iterator; |
| Src : Prj.Source_Id; |
| |
| begin |
| Debug_Output |
| ("found multi-unit source file in project", Source.Project.Name); |
| |
| Iter := For_Each_Source |
| (In_Tree => Tree, Project => Source.Project); |
| |
| while Element (Iter) /= No_Source loop |
| Src := Element (Iter); |
| |
| if Src.File = Source.File |
| and then Src.Index /= Source.Index |
| then |
| if Src.File = Source.File then |
| Debug_Output |
| ("add main in project, index=" & Src.Index'Img); |
| end if; |
| |
| Names.Increment_Last; |
| Names.Table (Names.Last) := |
| (File => Src.File, |
| Index => Src.Index, |
| Location => No_Location, |
| Source => Src, |
| Project => Src.Project, |
| Tree => Tree); |
| |
| Builder_Data (Tree).Number_Of_Mains := |
| Builder_Data (Tree).Number_Of_Mains + 1; |
| end if; |
| |
| Next (Iter); |
| end loop; |
| end Add_Multi_Unit_Sources; |
| |
| ----------------------------- |
| -- Find_File_Add_Extension -- |
| ----------------------------- |
| |
| function Find_File_Add_Extension |
| (Tree : Project_Tree_Ref; |
| Base_Main : String) return Prj.Source_Id |
| is |
| Spec_Source : Prj.Source_Id := No_Source; |
| Source : Prj.Source_Id; |
| Iter : Source_Iterator; |
| Suffix : File_Name_Type; |
| |
| begin |
| Source := No_Source; |
| Iter := For_Each_Source (Tree); -- In all projects |
| loop |
| Source := Prj.Element (Iter); |
| exit when Source = No_Source; |
| |
| if Source.Kind = Impl then |
| Get_Name_String (Source.File); |
| |
| if Name_Len > Base_Main'Length |
| and then Name_Buffer (1 .. Base_Main'Length) = Base_Main |
| then |
| Suffix := |
| Source.Language.Config.Naming_Data.Body_Suffix; |
| |
| if Suffix /= No_File then |
| declare |
| Suffix_Str : String := Get_Name_String (Suffix); |
| begin |
| Canonical_Case_File_Name (Suffix_Str); |
| exit when |
| Name_Buffer (Base_Main'Length + 1 .. Name_Len) = |
| Suffix_Str; |
| end; |
| end if; |
| end if; |
| |
| elsif Source.Kind = Spec |
| and then Source.Language.Config.Kind = Unit_Based |
| then |
| -- An Ada spec needs to be taken into account unless there |
| -- is also a body. So we delay the decision for them. |
| |
| Get_Name_String (Source.File); |
| |
| if Name_Len > Base_Main'Length |
| and then Name_Buffer (1 .. Base_Main'Length) = Base_Main |
| then |
| Suffix := Source.Language.Config.Naming_Data.Spec_Suffix; |
| |
| if Suffix /= No_File then |
| declare |
| Suffix_Str : String := Get_Name_String (Suffix); |
| |
| begin |
| Canonical_Case_File_Name (Suffix_Str); |
| |
| if Name_Buffer (Base_Main'Length + 1 .. Name_Len) = |
| Suffix_Str |
| then |
| Spec_Source := Source; |
| end if; |
| end; |
| end if; |
| end if; |
| end if; |
| |
| Next (Iter); |
| end loop; |
| |
| if Source = No_Source then |
| Source := Spec_Source; |
| end if; |
| |
| return Source; |
| end Find_File_Add_Extension; |
| |
| ----------------- |
| -- Do_Complete -- |
| ----------------- |
| |
| procedure Do_Complete |
| (Project : Project_Id; Tree : Project_Tree_Ref) |
| is |
| J : Integer; |
| |
| begin |
| if Mains.Number_Of_Mains (Tree) > 0 |
| or else Mains.Count_Of_Mains_With_No_Tree > 0 |
| then |
| -- Traverse in reverse order, since in the case of multi-unit |
| -- files we will be adding extra files at the end, and there's |
| -- no need to process them in turn. |
| |
| J := Names.Last; |
| Main_Loop : loop |
| declare |
| File : Main_Info := Names.Table (J); |
| Main_Id : File_Name_Type := File.File; |
| Main : constant String := |
| Get_Name_String (Main_Id); |
| Base : constant String := Base_Name (Main); |
| Source : Prj.Source_Id := No_Source; |
| Is_Absolute : Boolean := False; |
| |
| begin |
| if Base /= Main then |
| Is_Absolute := True; |
| |
| if Is_Absolute_Path (Main) then |
| Main_Id := Create_Name (Base); |
| |
| -- Not an absolute path |
| |
| else |
| -- Always resolve links here, so that users can be |
| -- specify any name on the command line. If the |
| -- project itself uses links, the user will be |
| -- using -eL anyway, and thus files are also stored |
| -- with resolved names. |
| |
| declare |
| Absolute : constant String := |
| Normalize_Pathname |
| (Name => Main, |
| Directory => "", |
| Resolve_Links => True, |
| Case_Sensitive => False); |
| begin |
| File.File := Create_Name (Absolute); |
| Main_Id := Create_Name (Base); |
| end; |
| end if; |
| end if; |
| |
| -- If no project or tree was specified for the main, it |
| -- came from the command line. |
| -- Note that the assignments below will not modify inside |
| -- the table itself. |
| |
| if File.Project = null then |
| File.Project := Project; |
| end if; |
| |
| if File.Tree = null then |
| File.Tree := Tree; |
| end if; |
| |
| if File.Source = null then |
| if Current_Verbosity = High then |
| Debug_Output |
| ("search for main """ & Main |
| & '"' & File.Index'Img & " in " |
| & Get_Name_String (Debug_Name (File.Tree)) |
| & ", project", Project.Name); |
| end if; |
| |
| -- First, look for the main as specified. We need to |
| -- search for the base name though, and if needed |
| -- check later that we found the correct file. |
| |
| declare |
| Sources : constant Source_Ids := |
| Find_All_Sources |
| (In_Tree => File.Tree, |
| Project => File.Project, |
| Base_Name => Main_Id, |
| Index => File.Index, |
| In_Imported_Only => True); |
| |
| begin |
| if Is_Absolute then |
| for J in Sources'Range loop |
| if File_Name_Type (Sources (J).Path.Name) = |
| File.File |
| then |
| Source := Sources (J); |
| exit; |
| end if; |
| end loop; |
| |
| elsif Sources'Length > 1 then |
| |
| -- This is only allowed if the units are from |
| -- the same multi-unit source file. |
| |
| Source := Sources (1); |
| |
| for J in 2 .. Sources'Last loop |
| if Sources (J).Path /= Source.Path |
| or else Sources (J).Index = Source.Index |
| then |
| Error_Msg_File_1 := Main_Id; |
| Prj.Err.Error_Msg |
| (Flags, "several main sources {", |
| No_Location, File.Project); |
| exit Main_Loop; |
| end if; |
| end loop; |
| |
| elsif Sources'Length = 1 then |
| Source := Sources (Sources'First); |
| end if; |
| end; |
| |
| if Source = No_Source then |
| Source := Find_File_Add_Extension |
| (File.Tree, Get_Name_String (Main_Id)); |
| end if; |
| |
| if Is_Absolute |
| and then Source /= No_Source |
| and then |
| File_Name_Type (Source.Path.Name) /= File.File |
| then |
| Debug_Output |
| ("Found a non-matching file", |
| Name_Id (Source.Path.Display_Name)); |
| Source := No_Source; |
| end if; |
| |
| if Source /= No_Source then |
| if not Is_Allowed_Language |
| (Source.Language.Name) |
| then |
| -- Remove any main that is not in the list of |
| -- restricted languages. |
| |
| Names.Table (J .. Names.Last - 1) := |
| Names.Table (J + 1 .. Names.Last); |
| Names.Set_Last (Names.Last - 1); |
| |
| else |
| -- If we have found a multi-unit source file but |
| -- did not specify an index initially, we'll |
| -- need to compile all the units from the same |
| -- source file. |
| |
| if Source.Index /= 0 and then File.Index = 0 then |
| Add_Multi_Unit_Sources (File.Tree, Source); |
| end if; |
| |
| -- Now update the original Main, otherwise it |
| -- will be reported as not found. |
| |
| Debug_Output |
| ("found main in project", Source.Project.Name); |
| Names.Table (J).File := Source.File; |
| Names.Table (J).Project := Source.Project; |
| |
| if Names.Table (J).Tree = null then |
| Names.Table (J).Tree := File.Tree; |
| |
| Builder_Data (File.Tree).Number_Of_Mains := |
| Builder_Data (File.Tree).Number_Of_Mains |
| + 1; |
| Mains.Count_Of_Mains_With_No_Tree := |
| Mains.Count_Of_Mains_With_No_Tree - 1; |
| end if; |
| |
| Names.Table (J).Source := Source; |
| Names.Table (J).Index := Source.Index; |
| end if; |
| |
| elsif File.Location /= No_Location then |
| |
| -- If the main is declared in package Builder of |
| -- the main project, report an error. If the main |
| -- is on the command line, it may be a main from |
| -- another project, so do nothing: if the main does |
| -- not exist in another project, an error will be |
| -- reported later. |
| |
| Error_Msg_File_1 := Main_Id; |
| Error_Msg_Name_1 := File.Project.Name; |
| Prj.Err.Error_Msg |
| (Flags, "{ is not a source of project %%", |
| File.Location, File.Project); |
| end if; |
| end if; |
| end; |
| |
| J := J - 1; |
| exit Main_Loop when J < Names.First; |
| end loop Main_Loop; |
| end if; |
| |
| if Total_Errors_Detected > 0 then |
| Fail_Program (Tree, "problems with main sources"); |
| end if; |
| end Do_Complete; |
| |
| -- Start of processing for Complete_Mains |
| |
| begin |
| Complete_All (Root_Project, Project_Tree); |
| |
| if Mains.Count_Of_Mains_With_No_Tree > 0 then |
| for J in Names.First .. Names.Last loop |
| if Names.Table (J).Source = No_Source then |
| Fail_Program |
| (Project_Tree, '"' & Get_Name_String (Names.Table (J).File) |
| & """ is not a source of any project"); |
| end if; |
| end loop; |
| end if; |
| end Complete_Mains; |
| |
| ------------ |
| -- Delete -- |
| ------------ |
| |
| procedure Delete is |
| begin |
| Names.Set_Last (0); |
| Mains.Reset; |
| end Delete; |
| |
| ----------------------- |
| -- Fill_From_Project -- |
| ----------------------- |
| |
| procedure Fill_From_Project |
| (Root_Project : Project_Id; |
| Project_Tree : Project_Tree_Ref) |
| is |
| procedure Add_Mains_From_Project |
| (Project : Project_Id; |
| Tree : Project_Tree_Ref); |
| -- Add the main units from this project into Mains. |
| -- This takes into account the aggregated projects |
| |
| ---------------------------- |
| -- Add_Mains_From_Project -- |
| ---------------------------- |
| |
| procedure Add_Mains_From_Project |
| (Project : Project_Id; |
| Tree : Project_Tree_Ref) |
| is |
| List : String_List_Id; |
| Element : String_Element; |
| |
| begin |
| if Number_Of_Mains (Tree) = 0 |
| and then Mains.Count_Of_Mains_With_No_Tree = 0 |
| then |
| Debug_Output ("Add_Mains_From_Project", Project.Name); |
| List := Project.Mains; |
| |
| if List /= Prj.Nil_String then |
| |
| -- The attribute Main is not an empty list. Get the mains in |
| -- the list. |
| |
| while List /= Prj.Nil_String loop |
| Element := Tree.Shared.String_Elements.Table (List); |
| Debug_Output ("Add_Main", Element.Value); |
| |
| if Project.Library then |
| Fail_Program |
| (Tree, |
| "cannot specify a main program " |
| & "for a library project file"); |
| end if; |
| |
| Add_Main (Name => Get_Name_String (Element.Value), |
| Index => Element.Index, |
| Location => Element.Location, |
| Project => Project, |
| Tree => Tree); |
| List := Element.Next; |
| end loop; |
| end if; |
| end if; |
| |
| if Total_Errors_Detected > 0 then |
| Fail_Program (Tree, "problems with main sources"); |
| end if; |
| end Add_Mains_From_Project; |
| |
| procedure Fill_All is new For_Project_And_Aggregated |
| (Add_Mains_From_Project); |
| |
| -- Start of processing for Fill_From_Project |
| |
| begin |
| Fill_All (Root_Project, Project_Tree); |
| end Fill_From_Project; |
| |
| --------------- |
| -- Next_Main -- |
| --------------- |
| |
| function Next_Main return String is |
| Info : constant Main_Info := Next_Main; |
| begin |
| if Info = No_Main_Info then |
| return ""; |
| else |
| return Get_Name_String (Info.File); |
| end if; |
| end Next_Main; |
| |
| function Next_Main return Main_Info is |
| begin |
| if Current >= Names.Last then |
| return No_Main_Info; |
| else |
| Current := Current + 1; |
| |
| -- If not using projects, and in the gnatmake case, the main file |
| -- may have not have the extension. Try ".adb" first then ".ads" |
| |
| if Names.Table (Current).Project = No_Project then |
| declare |
| Orig_Main : constant File_Name_Type := |
| Names.Table (Current).File; |
| Current_Main : File_Name_Type; |
| |
| begin |
| if Strip_Suffix (Orig_Main) = Orig_Main then |
| Get_Name_String (Orig_Main); |
| Add_Str_To_Name_Buffer (".adb"); |
| Current_Main := Name_Find; |
| |
| if Full_Source_Name (Current_Main) = No_File then |
| Get_Name_String (Orig_Main); |
| Add_Str_To_Name_Buffer (".ads"); |
| Current_Main := Name_Find; |
| |
| if Full_Source_Name (Current_Main) /= No_File then |
| Names.Table (Current).File := Current_Main; |
| end if; |
| |
| else |
| Names.Table (Current).File := Current_Main; |
| end if; |
| end if; |
| end; |
| end if; |
| |
| return Names.Table (Current); |
| end if; |
| end Next_Main; |
| |
| --------------------- |
| -- Number_Of_Mains -- |
| --------------------- |
| |
| function Number_Of_Mains (Tree : Project_Tree_Ref) return Natural is |
| begin |
| if Tree = null then |
| return Names.Last; |
| else |
| return Builder_Data (Tree).Number_Of_Mains; |
| end if; |
| end Number_Of_Mains; |
| |
| ----------- |
| -- Reset -- |
| ----------- |
| |
| procedure Reset is |
| begin |
| Current := 0; |
| end Reset; |
| |
| -------------------------- |
| -- Set_Multi_Unit_Index -- |
| -------------------------- |
| |
| procedure Set_Multi_Unit_Index |
| (Project_Tree : Project_Tree_Ref := null; |
| Index : Int := 0) |
| is |
| begin |
| if Index /= 0 then |
| if Names.Last = 0 then |
| Fail_Program |
| (Project_Tree, |
| "cannot specify a multi-unit index but no main " |
| & "on the command line"); |
| |
| elsif Names.Last > 1 then |
| Fail_Program |
| (Project_Tree, |
| "cannot specify several mains with a multi-unit index"); |
| |
| else |
| Names.Table (Names.Last).Index := Index; |
| end if; |
| end if; |
| end Set_Multi_Unit_Index; |
| |
| end Mains; |
| |
| ----------------------- |
| -- Path_Or_File_Name -- |
| ----------------------- |
| |
| function Path_Or_File_Name (Path : Path_Name_Type) return String is |
| Path_Name : constant String := Get_Name_String (Path); |
| begin |
| if Debug.Debug_Flag_F then |
| return File_Name (Path_Name); |
| else |
| return Path_Name; |
| end if; |
| end Path_Or_File_Name; |
| |
| ------------------- |
| -- Unit_Index_Of -- |
| ------------------- |
| |
| function Unit_Index_Of (ALI_File : File_Name_Type) return Int is |
| Start : Natural; |
| Finish : Natural; |
| Result : Int := 0; |
| |
| begin |
| Get_Name_String (ALI_File); |
| |
| -- First, find the last dot |
| |
| Finish := Name_Len; |
| |
| while Finish >= 1 and then Name_Buffer (Finish) /= '.' loop |
| Finish := Finish - 1; |
| end loop; |
| |
| if Finish = 1 then |
| return 0; |
| end if; |
| |
| -- Now check that the dot is preceded by digits |
| |
| Start := Finish; |
| Finish := Finish - 1; |
| while Start >= 1 and then Name_Buffer (Start - 1) in '0' .. '9' loop |
| Start := Start - 1; |
| end loop; |
| |
| -- If there are no digits, or if the digits are not preceded by the |
| -- character that precedes a unit index, this is not the ALI file of |
| -- a unit in a multi-unit source. |
| |
| if Start > Finish |
| or else Start = 1 |
| or else Name_Buffer (Start - 1) /= Multi_Unit_Index_Character |
| then |
| return 0; |
| end if; |
| |
| -- Build the index from the digit(s) |
| |
| while Start <= Finish loop |
| Result := Result * 10 + |
| Character'Pos (Name_Buffer (Start)) - Character'Pos ('0'); |
| Start := Start + 1; |
| end loop; |
| |
| return Result; |
| end Unit_Index_Of; |
| |
| ----------------- |
| -- Verbose_Msg -- |
| ----------------- |
| |
| procedure Verbose_Msg |
| (N1 : Name_Id; |
| S1 : String; |
| N2 : Name_Id := No_Name; |
| S2 : String := ""; |
| Prefix : String := " -> "; |
| Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low) |
| is |
| begin |
| if not Opt.Verbose_Mode |
| or else Minimum_Verbosity > Opt.Verbosity_Level |
| then |
| return; |
| end if; |
| |
| Write_Str (Prefix); |
| Write_Str (""""); |
| Write_Name (N1); |
| Write_Str (""" "); |
| Write_Str (S1); |
| |
| if N2 /= No_Name then |
| Write_Str (" """); |
| Write_Name (N2); |
| Write_Str (""" "); |
| end if; |
| |
| Write_Str (S2); |
| Write_Eol; |
| end Verbose_Msg; |
| |
| procedure Verbose_Msg |
| (N1 : File_Name_Type; |
| S1 : String; |
| N2 : File_Name_Type := No_File; |
| S2 : String := ""; |
| Prefix : String := " -> "; |
| Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low) |
| is |
| begin |
| Verbose_Msg |
| (Name_Id (N1), S1, Name_Id (N2), S2, Prefix, Minimum_Verbosity); |
| end Verbose_Msg; |
| |
| ----------- |
| -- Queue -- |
| ----------- |
| |
| package body Queue is |
| |
| type Q_Record is record |
| Info : Source_Info; |
| Processed : Boolean; |
| end record; |
| |
| package Q is new Table.Table |
| (Table_Component_Type => Q_Record, |
| Table_Index_Type => Natural, |
| Table_Low_Bound => 1, |
| Table_Initial => 1000, |
| Table_Increment => 100, |
| Table_Name => "Makeutl.Queue.Q"); |
| -- This is the actual Queue |
| |
| package Busy_Obj_Dirs is new GNAT.HTable.Simple_HTable |
| (Header_Num => Prj.Header_Num, |
| Element => Boolean, |
| No_Element => False, |
| Key => Path_Name_Type, |
| Hash => Hash, |
| Equal => "="); |
| |
| type Mark_Key is record |
| File : File_Name_Type; |
| Index : Int; |
| end record; |
| -- Identify either a mono-unit source (when Index = 0) or a specific |
| -- unit (index = 1's origin index of unit) in a multi-unit source. |
| |
| Max_Mask_Num : constant := 2048; |
| subtype Mark_Num is Union_Id range 0 .. Max_Mask_Num - 1; |
| |
| function Hash (Key : Mark_Key) return Mark_Num; |
| |
| package Marks is new GNAT.HTable.Simple_HTable |
| (Header_Num => Mark_Num, |
| Element => Boolean, |
| No_Element => False, |
| Key => Mark_Key, |
| Hash => Hash, |
| Equal => "="); |
| -- A hash table to keep tracks of the marked units. |
| -- These are the units that have already been processed, when using the |
| -- gnatmake format. When using the gprbuild format, we can directly |
| -- store in the source_id whether the file has already been processed. |
| |
| procedure Mark (Source_File : File_Name_Type; Index : Int := 0); |
| -- Mark a unit, identified by its source file and, when Index is not 0, |
| -- the index of the unit in the source file. Marking is used to signal |
| -- that the unit has already been inserted in the Q. |
| |
| function Is_Marked |
| (Source_File : File_Name_Type; |
| Index : Int := 0) return Boolean; |
| -- Returns True if the unit was previously marked |
| |
| Q_Processed : Natural := 0; |
| Q_Initialized : Boolean := False; |
| |
| Q_First : Natural := 1; |
| -- Points to the first valid element in the queue |
| |
| One_Queue_Per_Obj_Dir : Boolean := False; |
| -- See parameter to Initialize |
| |
| function Available_Obj_Dir (S : Source_Info) return Boolean; |
| -- Whether the object directory for S is available for a build |
| |
| procedure Debug_Display (S : Source_Info); |
| -- A debug display for S |
| |
| function Was_Processed (S : Source_Info) return Boolean; |
| -- Whether S has already been processed. This marks the source as |
| -- processed, if it hasn't already been processed. |
| |
| function Insert_No_Roots (Source : Source_Info) return Boolean; |
| -- Insert Source, but do not look for its roots (see doc for Insert) |
| |
| ------------------- |
| -- Was_Processed -- |
| ------------------- |
| |
| function Was_Processed (S : Source_Info) return Boolean is |
| begin |
| case S.Format is |
| when Format_Gprbuild => |
| if S.Id.In_The_Queue then |
| return True; |
| end if; |
| |
| S.Id.In_The_Queue := True; |
| |
| when Format_Gnatmake => |
| if Is_Marked (S.File, S.Index) then |
| return True; |
| end if; |
| |
| Mark (S.File, Index => S.Index); |
| end case; |
| |
| return False; |
| end Was_Processed; |
| |
| ----------------------- |
| -- Available_Obj_Dir -- |
| ----------------------- |
| |
| function Available_Obj_Dir (S : Source_Info) return Boolean is |
| begin |
| case S.Format is |
| when Format_Gprbuild => |
| return not Busy_Obj_Dirs.Get |
| (S.Id.Project.Object_Directory.Name); |
| |
| when Format_Gnatmake => |
| return S.Project = No_Project |
| or else |
| not Busy_Obj_Dirs.Get (S.Project.Object_Directory.Name); |
| end case; |
| end Available_Obj_Dir; |
| |
| ------------------- |
| -- Debug_Display -- |
| ------------------- |
| |
| procedure Debug_Display (S : Source_Info) is |
| begin |
| case S.Format is |
| when Format_Gprbuild => |
| Write_Name (S.Id.File); |
| |
| if S.Id.Index /= 0 then |
| Write_Str (", "); |
| Write_Int (S.Id.Index); |
| end if; |
| |
| when Format_Gnatmake => |
| Write_Name (S.File); |
| |
| if S.Index /= 0 then |
| Write_Str (", "); |
| Write_Int (S.Index); |
| end if; |
| end case; |
| end Debug_Display; |
| |
| ---------- |
| -- Hash -- |
| ---------- |
| |
| function Hash (Key : Mark_Key) return Mark_Num is |
| begin |
| return Union_Id (Key.File) mod Max_Mask_Num; |
| end Hash; |
| |
| --------------- |
| -- Is_Marked -- |
| --------------- |
| |
| function Is_Marked |
| (Source_File : File_Name_Type; |
| Index : Int := 0) return Boolean |
| is |
| begin |
| return Marks.Get (K => (File => Source_File, Index => Index)); |
| end Is_Marked; |
| |
| ---------- |
| -- Mark -- |
| ---------- |
| |
| procedure Mark (Source_File : File_Name_Type; Index : Int := 0) is |
| begin |
| Marks.Set (K => (File => Source_File, Index => Index), E => True); |
| end Mark; |
| |
| ------------- |
| -- Extract -- |
| ------------- |
| |
| procedure Extract |
| (Found : out Boolean; |
| Source : out Source_Info) |
| is |
| begin |
| Found := False; |
| |
| if One_Queue_Per_Obj_Dir then |
| for J in Q_First .. Q.Last loop |
| if not Q.Table (J).Processed |
| and then Available_Obj_Dir (Q.Table (J).Info) |
| then |
| Found := True; |
| Source := Q.Table (J).Info; |
| Q.Table (J).Processed := True; |
| |
| if J = Q_First then |
| while Q_First <= Q.Last |
| and then Q.Table (Q_First).Processed |
| loop |
| Q_First := Q_First + 1; |
| end loop; |
| end if; |
| |
| exit; |
| end if; |
| end loop; |
| |
| elsif Q_First <= Q.Last then |
| Source := Q.Table (Q_First).Info; |
| Q.Table (Q_First).Processed := True; |
| Q_First := Q_First + 1; |
| Found := True; |
| end if; |
| |
| if Found then |
| Q_Processed := Q_Processed + 1; |
| end if; |
| |
| if Found and then Debug.Debug_Flag_Q then |
| Write_Str (" Q := Q - [ "); |
| Debug_Display (Source); |
| Write_Str (" ]"); |
| Write_Eol; |
| |
| Write_Str (" Q_First ="); |
| Write_Int (Int (Q_First)); |
| Write_Eol; |
| |
| Write_Str (" Q.Last ="); |
| Write_Int (Int (Q.Last)); |
| Write_Eol; |
| end if; |
| end Extract; |
| |
| --------------- |
| -- Processed -- |
| --------------- |
| |
| function Processed return Natural is |
| begin |
| return Q_Processed; |
| end Processed; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize |
| (Queue_Per_Obj_Dir : Boolean; |
| Force : Boolean := False) |
| is |
| begin |
| if Force or else not Q_Initialized then |
| Q_Initialized := True; |
| |
| for J in 1 .. Q.Last loop |
| case Q.Table (J).Info.Format is |
| when Format_Gprbuild => |
| Q.Table (J).Info.Id.In_The_Queue := False; |
| when Format_Gnatmake => |
| null; |
| end case; |
| end loop; |
| |
| Q.Init; |
| Q_Processed := 0; |
| Q_First := 1; |
| One_Queue_Per_Obj_Dir := Queue_Per_Obj_Dir; |
| end if; |
| end Initialize; |
| |
| --------------------- |
| -- Insert_No_Roots -- |
| --------------------- |
| |
| function Insert_No_Roots (Source : Source_Info) return Boolean is |
| begin |
| pragma Assert |
| (Source.Format = Format_Gnatmake or else Source.Id /= No_Source); |
| |
| -- Only insert in the Q if it is not already done, to avoid |
| -- simultaneous compilations if -jnnn is used. |
| |
| if Was_Processed (Source) then |
| return False; |
| end if; |
| |
| -- For gprbuild, check if a source has already been inserted in the |
| -- queue from the same project in a different project tree. |
| |
| if Source.Format = Format_Gprbuild then |
| for J in 1 .. Q.Last loop |
| if Source.Id.Path.Name = Q.Table (J).Info.Id.Path.Name |
| and then Source.Id.Index = Q.Table (J).Info.Id.Index |
| and then |
| Ultimate_Extending_Project_Of (Source.Id.Project).Path.Name |
| = |
| Ultimate_Extending_Project_Of (Q.Table (J).Info.Id.Project). |
| Path.Name |
| then |
| -- No need to insert this source in the queue, but still |
| -- return True as we may need to insert its roots. |
| |
| return True; |
| end if; |
| end loop; |
| end if; |
| |
| if Current_Verbosity = High then |
| Write_Str ("Adding """); |
| Debug_Display (Source); |
| Write_Line (""" to the queue"); |
| end if; |
| |
| Q.Append (New_Val => (Info => Source, Processed => False)); |
| |
| if Debug.Debug_Flag_Q then |
| Write_Str (" Q := Q + [ "); |
| Debug_Display (Source); |
| Write_Str (" ] "); |
| Write_Eol; |
| |
| Write_Str (" Q_First ="); |
| Write_Int (Int (Q_First)); |
| Write_Eol; |
| |
| Write_Str (" Q.Last ="); |
| Write_Int (Int (Q.Last)); |
| Write_Eol; |
| end if; |
| |
| return True; |
| end Insert_No_Roots; |
| |
| ------------ |
| -- Insert -- |
| ------------ |
| |
| function Insert |
| (Source : Source_Info; |
| With_Roots : Boolean := False) return Boolean |
| is |
| Root_Arr : Array_Element_Id; |
| Roots : Variable_Value; |
| List : String_List_Id; |
| Elem : String_Element; |
| Unit_Name : Name_Id; |
| Pat_Root : Boolean; |
| Root_Pattern : Regexp; |
| Root_Found : Boolean; |
| Roots_Found : Boolean; |
| Root_Source : Prj.Source_Id; |
| Iter : Source_Iterator; |
| |
| Dummy : Boolean; |
| |
| begin |
| if not Insert_No_Roots (Source) then |
| |
| -- Was already in the queue |
| |
| return False; |
| end if; |
| |
| if With_Roots and then Source.Format = Format_Gprbuild then |
| Debug_Output ("looking for roots of", Name_Id (Source.Id.File)); |
| |
| Root_Arr := |
| Prj.Util.Value_Of |
| (Name => Name_Roots, |
| In_Arrays => Source.Id.Project.Decl.Arrays, |
| Shared => Source.Tree.Shared); |
| |
| Roots := |
| Prj.Util.Value_Of |
| (Index => Name_Id (Source.Id.File), |
| Src_Index => 0, |
| In_Array => Root_Arr, |
| Shared => Source.Tree.Shared); |
| |
| -- If there is no roots for the specific main, try the language |
| |
| if Roots = Nil_Variable_Value then |
| Roots := |
| Prj.Util.Value_Of |
| (Index => Source.Id.Language.Name, |
| Src_Index => 0, |
| In_Array => Root_Arr, |
| Shared => Source.Tree.Shared, |
| Force_Lower_Case_Index => True); |
| end if; |
| |
| -- Then try "*" |
| |
| if Roots = Nil_Variable_Value then |
| Name_Len := 1; |
| Name_Buffer (1) := '*'; |
| |
| Roots := |
| Prj.Util.Value_Of |
| (Index => Name_Find, |
| Src_Index => 0, |
| In_Array => Root_Arr, |
| Shared => Source.Tree.Shared, |
| Force_Lower_Case_Index => True); |
| end if; |
| |
| if Roots = Nil_Variable_Value then |
| Debug_Output (" -> no roots declared"); |
| |
| else |
| List := Roots.Values; |
| |
| Pattern_Loop : |
| while List /= Nil_String loop |
| Elem := Source.Tree.Shared.String_Elements.Table (List); |
| Get_Name_String (Elem.Value); |
| To_Lower (Name_Buffer (1 .. Name_Len)); |
| Unit_Name := Name_Find; |
| |
| -- Check if it is a unit name or a pattern |
| |
| Pat_Root := False; |
| |
| for J in 1 .. Name_Len loop |
| if Name_Buffer (J) not in 'a' .. 'z' and then |
| Name_Buffer (J) not in '0' .. '9' and then |
| Name_Buffer (J) /= '_' and then |
| Name_Buffer (J) /= '.' |
| then |
| Pat_Root := True; |
| exit; |
| end if; |
| end loop; |
| |
| if Pat_Root then |
| begin |
| Root_Pattern := |
| Compile |
| (Pattern => Name_Buffer (1 .. Name_Len), |
| Glob => True); |
| |
| exception |
| when Error_In_Regexp => |
| Err_Vars.Error_Msg_Name_1 := Unit_Name; |
| Errutil.Error_Msg |
| ("invalid pattern %", Roots.Location); |
| exit Pattern_Loop; |
| end; |
| end if; |
| |
| Roots_Found := False; |
| Iter := For_Each_Source (Source.Tree); |
| |
| Source_Loop : |
| loop |
| Root_Source := Prj.Element (Iter); |
| exit Source_Loop when Root_Source = No_Source; |
| |
| Root_Found := False; |
| if Pat_Root then |
| Root_Found := Root_Source.Unit /= No_Unit_Index |
| and then Match |
| (Get_Name_String (Root_Source.Unit.Name), |
| Root_Pattern); |
| |
| else |
| Root_Found := |
| Root_Source.Unit /= No_Unit_Index |
| and then Root_Source.Unit.Name = Unit_Name; |
| end if; |
| |
| if Root_Found then |
| case Root_Source.Kind is |
| when Impl => |
| null; |
| |
| when Spec => |
| Root_Found := Other_Part (Root_Source) = No_Source; |
| |
| when Sep => |
| Root_Found := False; |
| end case; |
| end if; |
| |
| if Root_Found then |
| Roots_Found := True; |
| Debug_Output |
| (" -> ", Name_Id (Root_Source.Display_File)); |
| Dummy := Queue.Insert_No_Roots |
| (Source => (Format => Format_Gprbuild, |
| Tree => Source.Tree, |
| Id => Root_Source, |
| Closure => False)); |
| |
| Initialize_Source_Record (Root_Source); |
| |
| if Other_Part (Root_Source) /= No_Source then |
| Initialize_Source_Record (Other_Part (Root_Source)); |
| end if; |
| |
| -- Save the root for the binder |
| |
| Source.Id.Roots := new Source_Roots' |
| (Root => Root_Source, |
| Next => Source.Id.Roots); |
| |
| exit Source_Loop when not Pat_Root; |
| end if; |
| |
| Next (Iter); |
| end loop Source_Loop; |
| |
| if not Roots_Found then |
| if Pat_Root then |
| if not Quiet_Output then |
| Error_Msg_Name_1 := Unit_Name; |
| Errutil.Error_Msg |
| ("?no unit matches pattern %", Roots.Location); |
| end if; |
| |
| else |
| Errutil.Error_Msg |
| ("Unit " & Get_Name_String (Unit_Name) |
| & " does not exist", Roots.Location); |
| end if; |
| end if; |
| |
| List := Elem.Next; |
| end loop Pattern_Loop; |
| end if; |
| end if; |
| |
| return True; |
| end Insert; |
| |
| ------------ |
| -- Insert -- |
| ------------ |
| |
| procedure Insert |
| (Source : Source_Info; |
| With_Roots : Boolean := False) |
| is |
| Discard : Boolean; |
| begin |
| Discard := Insert (Source, With_Roots); |
| end Insert; |
| |
| -------------- |
| -- Is_Empty -- |
| -------------- |
| |
| function Is_Empty return Boolean is |
| begin |
| return Q_Processed >= Q.Last; |
| end Is_Empty; |
| |
| ------------------------ |
| -- Is_Virtually_Empty -- |
| ------------------------ |
| |
| function Is_Virtually_Empty return Boolean is |
| begin |
| if One_Queue_Per_Obj_Dir then |
| for J in Q_First .. Q.Last loop |
| if not Q.Table (J).Processed |
| and then Available_Obj_Dir (Q.Table (J).Info) |
| then |
| return False; |
| end if; |
| end loop; |
| |
| return True; |
| |
| else |
| return Is_Empty; |
| end if; |
| end Is_Virtually_Empty; |
| |
| ---------------------- |
| -- Set_Obj_Dir_Busy -- |
| ---------------------- |
| |
| procedure Set_Obj_Dir_Busy (Obj_Dir : Path_Name_Type) is |
| begin |
| if One_Queue_Per_Obj_Dir then |
| Busy_Obj_Dirs.Set (Obj_Dir, True); |
| end if; |
| end Set_Obj_Dir_Busy; |
| |
| ---------------------- |
| -- Set_Obj_Dir_Free -- |
| ---------------------- |
| |
| procedure Set_Obj_Dir_Free (Obj_Dir : Path_Name_Type) is |
| begin |
| if One_Queue_Per_Obj_Dir then |
| Busy_Obj_Dirs.Set (Obj_Dir, False); |
| end if; |
| end Set_Obj_Dir_Free; |
| |
| ---------- |
| -- Size -- |
| ---------- |
| |
| function Size return Natural is |
| begin |
| return Q.Last; |
| end Size; |
| |
| ------------- |
| -- Element -- |
| ------------- |
| |
| function Element (Rank : Positive) return File_Name_Type is |
| begin |
| if Rank <= Q.Last then |
| case Q.Table (Rank).Info.Format is |
| when Format_Gprbuild => |
| return Q.Table (Rank).Info.Id.File; |
| when Format_Gnatmake => |
| return Q.Table (Rank).Info.File; |
| end case; |
| else |
| return No_File; |
| end if; |
| end Element; |
| |
| ------------------ |
| -- Remove_Marks -- |
| ------------------ |
| |
| procedure Remove_Marks is |
| begin |
| Marks.Reset; |
| end Remove_Marks; |
| |
| ---------------------------- |
| -- Insert_Project_Sources -- |
| ---------------------------- |
| |
| procedure Insert_Project_Sources |
| (Project : Project_Id; |
| Project_Tree : Project_Tree_Ref; |
| All_Projects : Boolean; |
| Unique_Compile : Boolean) |
| is |
| |
| procedure Do_Insert |
| (Project : Project_Id; |
| Tree : Project_Tree_Ref; |
| Context : Project_Context); |
| -- Local procedures must be commented ??? |
| |
| --------------- |
| -- Do_Insert -- |
| --------------- |
| |
| procedure Do_Insert |
| (Project : Project_Id; |
| Tree : Project_Tree_Ref; |
| Context : Project_Context) |
| is |
| Unit_Based : constant Boolean := |
| Unique_Compile |
| or else not Builder_Data (Tree).Closure_Needed; |
| -- When Unit_Based is True, we enqueue all compilable sources |
| -- including the unit based (Ada) one. When Unit_Based is False, |
| -- put the Ada sources only when they are in a library project. |
| |
| Iter : Source_Iterator; |
| Source : Prj.Source_Id; |
| OK : Boolean; |
| Closure : Boolean; |
| |
| begin |
| -- Nothing to do when "-u" was specified and some files were |
| -- specified on the command line |
| |
| if Unique_Compile and then Mains.Number_Of_Mains (Tree) > 0 then |
| return; |
| end if; |
| |
| Iter := For_Each_Source (Tree); |
| loop |
| Source := Prj.Element (Iter); |
| exit when Source = No_Source; |
| |
| if Is_Allowed_Language (Source.Language.Name) |
| and then Is_Compilable (Source) |
| and then (All_Projects |
| or else Is_Extending (Project, Source.Project)) |
| and then not Source.Locally_Removed |
| and then Source.Replaced_By = No_Source |
| and then (not Source.Project.Externally_Built |
| or else (Is_Extending (Project, Source.Project) |
| and then not Project.Externally_Built)) |
| and then Source.Kind /= Sep |
| and then Source.Path /= No_Path_Information |
| then |
| if Source.Kind = Impl |
| or else (Source.Unit /= No_Unit_Index |
| and then Source.Kind = Spec |
| and then (Other_Part (Source) = No_Source |
| or else |
| Other_Part (Source).Locally_Removed)) |
| then |
| if (Unit_Based |
| or else Source.Unit = No_Unit_Index |
| or else Source.Project.Library |
| or else Context.In_Aggregate_Lib |
| or else Project.Qualifier = Aggregate_Library) |
| and then not Is_Subunit (Source) |
| then |
| OK := True; |
| Closure := False; |
| |
| if Source.Unit /= No_Unit_Index |
| and then |
| (Source.Project.Library |
| or else Project.Qualifier = Aggregate_Library |
| or else Context.In_Aggregate_Lib) |
| and then Source.Project.Standalone_Library /= No |
| then |
| -- Check if the unit is in the interface |
| |
| OK := False; |
| |
| declare |
| List : String_List_Id; |
| Element : String_Element; |
| |
| begin |
| List := Source.Project.Lib_Interface_ALIs; |
| while List /= Nil_String loop |
| Element := |
| Project_Tree.Shared.String_Elements.Table |
| (List); |
| |
| if Element.Value = Name_Id (Source.Dep_Name) |
| then |
| OK := True; |
| Closure := True; |
| exit; |
| end if; |
| |
| List := Element.Next; |
| end loop; |
| end; |
| end if; |
| |
| if OK then |
| Queue.Insert |
| (Source => (Format => Format_Gprbuild, |
| Tree => Tree, |
| Id => Source, |
| Closure => Closure)); |
| end if; |
| end if; |
| end if; |
| end if; |
| |
| Next (Iter); |
| end loop; |
| end Do_Insert; |
| |
| procedure Insert_All is |
| new For_Project_And_Aggregated_Context (Do_Insert); |
| |
| begin |
| Insert_All (Project, Project_Tree); |
| end Insert_Project_Sources; |
| |
| ------------------------------- |
| -- Insert_Withed_Sources_For -- |
| ------------------------------- |
| |
| procedure Insert_Withed_Sources_For |
| (The_ALI : ALI.ALI_Id; |
| Project_Tree : Project_Tree_Ref; |
| Excluding_Shared_SALs : Boolean := False) |
| is |
| Sfile : File_Name_Type; |
| Afile : File_Name_Type; |
| Src_Id : Prj.Source_Id; |
| |
| begin |
| -- Insert in the queue the unmarked source files (i.e. those which |
| -- have never been inserted in the queue and hence never considered). |
| |
| for J in ALI.ALIs.Table (The_ALI).First_Unit .. |
| ALI.ALIs.Table (The_ALI).Last_Unit |
| loop |
| for K in ALI.Units.Table (J).First_With .. |
| ALI.Units.Table (J).Last_With |
| loop |
| Sfile := ALI.Withs.Table (K).Sfile; |
| |
| -- Skip generics |
| |
| if Sfile /= No_File then |
| Afile := ALI.Withs.Table (K).Afile; |
| |
| Src_Id := Source_Files_Htable.Get |
| (Project_Tree.Source_Files_HT, Sfile); |
| while Src_Id /= No_Source loop |
| Initialize_Source_Record (Src_Id); |
| |
| if Is_Compilable (Src_Id) |
| and then Src_Id.Dep_Name = Afile |
| then |
| case Src_Id.Kind is |
| when Spec => |
| declare |
| Bdy : constant Prj.Source_Id := |
| Other_Part (Src_Id); |
| begin |
| if Bdy /= No_Source |
| and then not Bdy.Locally_Removed |
| then |
| Src_Id := Other_Part (Src_Id); |
| end if; |
| end; |
| |
| when Impl => |
| if Is_Subunit (Src_Id) then |
| Src_Id := No_Source; |
| end if; |
| |
| when Sep => |
| Src_Id := No_Source; |
| end case; |
| |
| exit; |
| end if; |
| |
| Src_Id := Src_Id.Next_With_File_Name; |
| end loop; |
| |
| -- If Excluding_Shared_SALs is True, do not insert in the |
| -- queue the sources of a shared Stand-Alone Library. |
| |
| if Src_Id /= No_Source |
| and then (not Excluding_Shared_SALs |
| or else Src_Id.Project.Standalone_Library = No |
| or else Src_Id.Project.Library_Kind = Static) |
| then |
| Queue.Insert |
| (Source => (Format => Format_Gprbuild, |
| Tree => Project_Tree, |
| Id => Src_Id, |
| Closure => True)); |
| end if; |
| end if; |
| end loop; |
| end loop; |
| end Insert_Withed_Sources_For; |
| |
| end Queue; |
| |
| ---------- |
| -- Free -- |
| ---------- |
| |
| procedure Free (Data : in out Builder_Project_Tree_Data) is |
| procedure Unchecked_Free is new Ada.Unchecked_Deallocation |
| (Binding_Data_Record, Binding_Data); |
| |
| TmpB, Binding : Binding_Data := Data.Binding; |
| |
| begin |
| while Binding /= null loop |
| TmpB := Binding.Next; |
| Unchecked_Free (Binding); |
| Binding := TmpB; |
| end loop; |
| end Free; |
| |
| ------------------ |
| -- Builder_Data -- |
| ------------------ |
| |
| function Builder_Data |
| (Tree : Project_Tree_Ref) return Builder_Data_Access |
| is |
| begin |
| if Tree.Appdata = null then |
| Tree.Appdata := new Builder_Project_Tree_Data; |
| end if; |
| |
| return Builder_Data_Access (Tree.Appdata); |
| end Builder_Data; |
| |
| -------------------------------- |
| -- Compute_Compilation_Phases -- |
| -------------------------------- |
| |
| procedure Compute_Compilation_Phases |
| (Tree : Project_Tree_Ref; |
| Root_Project : Project_Id; |
| Option_Unique_Compile : Boolean := False; -- Was "-u" specified ? |
| Option_Compile_Only : Boolean := False; -- Was "-c" specified ? |
| Option_Bind_Only : Boolean := False; |
| Option_Link_Only : Boolean := False) |
| is |
| procedure Do_Compute (Project : Project_Id; Tree : Project_Tree_Ref); |
| |
| ---------------- |
| -- Do_Compute -- |
| ---------------- |
| |
| procedure Do_Compute (Project : Project_Id; Tree : Project_Tree_Ref) is |
| Data : constant Builder_Data_Access := Builder_Data (Tree); |
| All_Phases : constant Boolean := |
| not Option_Compile_Only |
| and then not Option_Bind_Only |
| and then not Option_Link_Only; |
| -- Whether the command line asked for all three phases. Depending on |
| -- the project settings, we might still disable some of the phases. |
| |
| Has_Mains : constant Boolean := Data.Number_Of_Mains > 0; |
| -- Whether there are some main units defined for this project tree |
| -- (either from one of the projects, or from the command line) |
| |
| begin |
| if Option_Unique_Compile then |
| |
| -- If -u or -U is specified on the command line, disregard any -c, |
| -- -b or -l switch: only perform compilation. |
| |
| Data.Closure_Needed := False; |
| Data.Need_Compilation := True; |
| Data.Need_Binding := False; |
| Data.Need_Linking := False; |
| |
| else |
| Data.Closure_Needed := |
| Has_Mains |
| or else (Root_Project.Library |
| and then Root_Project.Standalone_Library /= No); |
| Data.Need_Compilation := All_Phases or Option_Compile_Only; |
| Data.Need_Binding := All_Phases or Option_Bind_Only; |
| Data.Need_Linking := (All_Phases or Option_Link_Only) |
| and Has_Mains; |
| end if; |
| |
| if Current_Verbosity = High then |
| Debug_Output ("compilation phases: " |
| & " compile=" & Data.Need_Compilation'Img |
| & " bind=" & Data.Need_Binding'Img |
| & " link=" & Data.Need_Linking'Img |
| & " closure=" & Data.Closure_Needed'Img |
| & " mains=" & Data.Number_Of_Mains'Img, |
| Project.Name); |
| end if; |
| end Do_Compute; |
| |
| procedure Compute_All is new For_Project_And_Aggregated (Do_Compute); |
| |
| begin |
| Compute_All (Root_Project, Tree); |
| end Compute_Compilation_Phases; |
| |
| ------------------------------ |
| -- Compute_Builder_Switches -- |
| ------------------------------ |
| |
| procedure Compute_Builder_Switches |
| (Project_Tree : Project_Tree_Ref; |
| Env : in out Prj.Tree.Environment; |
| Main_Project : Project_Id; |
| Only_For_Lang : Name_Id := No_Name) |
| is |
| Builder_Package : constant Package_Id := |
| Value_Of (Name_Builder, Main_Project.Decl.Packages, |
| Project_Tree.Shared); |
| |
| Global_Compilation_Array : Array_Element_Id; |
| Global_Compilation_Elem : Array_Element; |
| Global_Compilation_Switches : Variable_Value; |
| |
| Default_Switches_Array : Array_Id; |
| |
| Builder_Switches_Lang : Name_Id := No_Name; |
| |
| List : String_List_Id; |
| Element : String_Element; |
| |
| Index : Name_Id; |
| Source : Prj.Source_Id; |
| |
| Lang : Name_Id := No_Name; -- language index for Switches |
| Switches_For_Lang : Variable_Value := Nil_Variable_Value; |
| -- Value of Builder'Default_Switches(lang) |
| |
| Name : Name_Id := No_Name; -- main file index for Switches |
| Switches_For_Main : Variable_Value := Nil_Variable_Value; |
| -- Switches for a specific main. When there are several mains, Name is |
| -- set to No_Name, and Switches_For_Main might be left with an actual |
| -- value (so that we can display a warning that it was ignored). |
| |
| Other_Switches : Variable_Value := Nil_Variable_Value; |
| -- Value of Builder'Switches(others) |
| |
| Defaults : Variable_Value := Nil_Variable_Value; |
| |
| Switches : Variable_Value := Nil_Variable_Value; |
| -- The computed builder switches |
| |
| Success : Boolean := False; |
| begin |
| if Builder_Package /= No_Package then |
| Mains.Reset; |
| |
| -- If there is no main, and there is only one compilable language, |
| -- use this language as the switches index. |
| |
| if Mains.Number_Of_Mains (Project_Tree) = 0 then |
| if Only_For_Lang = No_Name then |
| declare |
| Language : Language_Ptr := Main_Project.Languages; |
| |
| begin |
| while Language /= No_Language_Index loop |
| if Language.Config.Compiler_Driver /= No_File |
| and then Language.Config.Compiler_Driver /= Empty_File |
| then |
| if Lang /= No_Name then |
| Lang := No_Name; |
| exit; |
| else |
| Lang := Language.Name; |
| end if; |
| end if; |
| Language := Language.Next; |
| end loop; |
| end; |
| else |
| Lang := Only_For_Lang; |
| end if; |
| |
| else |
| for Index in 1 .. Mains.Number_Of_Mains (Project_Tree) loop |
| Source := Mains.Next_Main.Source; |
| |
| if Source /= No_Source then |
| if Switches_For_Main = Nil_Variable_Value then |
| Switches_For_Main := Value_Of |
| (Name => Name_Id (Source.File), |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Builder_Package, |
| Shared => Project_Tree.Shared, |
| Force_Lower_Case_Index => False, |
| Allow_Wildcards => True); |
| |
| -- If not found, try without extension. |
| -- That's because gnatmake accepts truncated file names |
| -- in Builder'Switches |
| |
| if Switches_For_Main = Nil_Variable_Value |
| and then Source.Unit /= null |
| then |
| Switches_For_Main := Value_Of |
| (Name => Source.Unit.Name, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Builder_Package, |
| Shared => Project_Tree.Shared, |
| Force_Lower_Case_Index => False, |
| Allow_Wildcards => True); |
| end if; |
| end if; |
| |
| if Index = 1 then |
| Lang := Source.Language.Name; |
| Name := Name_Id (Source.File); |
| else |
| Name := No_Name; -- Can't use main specific switches |
| |
| if Lang /= Source.Language.Name then |
| Lang := No_Name; |
| end if; |
| end if; |
| end if; |
| end loop; |
| end if; |
| |
| Global_Compilation_Array := Value_Of |
| (Name => Name_Global_Compilation_Switches, |
| In_Arrays => Project_Tree.Shared.Packages.Table |
| (Builder_Package).Decl.Arrays, |
| Shared => Project_Tree.Shared); |
| |
| Default_Switches_Array := |
| Project_Tree.Shared.Packages.Table (Builder_Package).Decl.Arrays; |
| |
| while Default_Switches_Array /= No_Array |
| and then |
| Project_Tree.Shared.Arrays.Table (Default_Switches_Array).Name /= |
| Name_Default_Switches |
| loop |
| Default_Switches_Array := |
| Project_Tree.Shared.Arrays.Table (Default_Switches_Array).Next; |
| end loop; |
| |
| if Global_Compilation_Array /= No_Array_Element |
| and then Default_Switches_Array /= No_Array |
| then |
| Prj.Err.Error_Msg |
| (Env.Flags, |
| "Default_Switches forbidden in presence of " |
| & "Global_Compilation_Switches. Use Switches instead.", |
| Project_Tree.Shared.Arrays.Table |
| (Default_Switches_Array).Location); |
| Fail_Program |
| (Project_Tree, "*** illegal combination of Builder attributes"); |
| end if; |
| |
| if Lang /= No_Name then |
| Switches_For_Lang := Prj.Util.Value_Of |
| (Name => Lang, |
| Index => 0, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Builder_Package, |
| Shared => Project_Tree.Shared, |
| Force_Lower_Case_Index => True); |
| |
| Defaults := Prj.Util.Value_Of |
| (Name => Lang, |
| Index => 0, |
| Attribute_Or_Array_Name => Name_Default_Switches, |
| In_Package => Builder_Package, |
| Shared => Project_Tree.Shared, |
| Force_Lower_Case_Index => True); |
| end if; |
| |
| Other_Switches := Prj.Util.Value_Of |
| (Name => All_Other_Names, |
| Index => 0, |
| Attribute_Or_Array_Name => Name_Switches, |
| In_Package => Builder_Package, |
| Shared => Project_Tree.Shared); |
| |
| if not Quiet_Output |
| and then Mains.Number_Of_Mains (Project_Tree) > 1 |
| and then Switches_For_Main /= Nil_Variable_Value |
| then |
| -- More than one main, but we had main-specific switches that |
| -- are ignored. |
| |
| if Switches_For_Lang /= Nil_Variable_Value then |
| Write_Line |
| ("Warning: using Builder'Switches(""" |
| & Get_Name_String (Lang) |
| & """), as there are several mains"); |
| |
| elsif Other_Switches /= Nil_Variable_Value then |
| Write_Line |
| ("Warning: using Builder'Switches(others), " |
| & "as there are several mains"); |
| |
| elsif Defaults /= Nil_Variable_Value then |
| Write_Line |
| ("Warning: using Builder'Default_Switches(""" |
| & Get_Name_String (Lang) |
| & """), as there are several mains"); |
| else |
| Write_Line |
| ("Warning: using no switches from package " |
| & "Builder, as there are several mains"); |
| end if; |
| end if; |
| |
| Builder_Switches_Lang := Lang; |
| |
| if Name /= No_Name then |
| -- Get the switches for the single main |
| Switches := Switches_For_Main; |
| end if; |
| |
| if Switches = Nil_Variable_Value or else Switches.Default then |
| -- Get the switches for the common language of the mains |
| Switches := Switches_For_Lang; |
| end if; |
| |
| if Switches = Nil_Variable_Value or else Switches.Default then |
| Switches := Other_Switches; |
| end if; |
| |
| -- For backward compatibility with gnatmake, if no Switches |
| -- are declared, check for Default_Switches (<language>). |
| |
| if Switches = Nil_Variable_Value or else Switches.Default then |
| Switches := Defaults; |
| end if; |
| |
| -- If switches have been found, scan them |
| |
| if Switches /= Nil_Variable_Value and then not Switches.Default then |
| List := Switches.Values; |
| |
| while List /= Nil_String loop |
| Element := Project_Tree.Shared.String_Elements.Table (List); |
| Get_Name_String (Element.Value); |
| |
| if Name_Len /= 0 then |
| declare |
| -- Add_Switch might itself be using the name_buffer, so |
| -- we make a temporary here. |
| Switch : constant String := Name_Buffer (1 .. Name_Len); |
| begin |
| Success := Add_Switch |
| (Switch => Switch, |
| For_Lang => Builder_Switches_Lang, |
| For_Builder => True, |
| Has_Global_Compilation_Switches => |
| Global_Compilation_Array /= No_Array_Element); |
| end; |
| |
| if not Success then |
| for J in reverse 1 .. Name_Len loop |
| Name_Buffer (J + J) := Name_Buffer (J); |
| Name_Buffer (J + J - 1) := '''; |
| end loop; |
| |
| Name_Len := Name_Len + Name_Len; |
| |
| Prj.Err.Error_Msg |
| (Env.Flags, |
| '"' & Name_Buffer (1 .. Name_Len) |
| & """ is not a builder switch. Consider moving " |
| & "it to Global_Compilation_Switches.", |
| Element.Location); |
| Fail_Program |
| (Project_Tree, |
| "*** illegal switch """ |
| & Get_Name_String (Element.Value) & '"'); |
| end if; |
| end if; |
| |
| List := Element.Next; |
| end loop; |
| end if; |
| |
| -- Reset the Builder Switches language |
| |
| Builder_Switches_Lang := No_Name; |
| |
| -- Take into account attributes Global_Compilation_Switches |
| |
| while Global_Compilation_Array /= No_Array_Element loop |
| Global_Compilation_Elem := |
| Project_Tree.Shared.Array_Elements.Table |
| (Global_Compilation_Array); |
| |
| Get_Name_String (Global_Compilation_Elem.Index); |
| To_Lower (Name_Buffer (1 .. Name_Len)); |
| Index := Name_Find; |
| |
| if Only_For_Lang = No_Name or else Index = Only_For_Lang then |
| Global_Compilation_Switches := Global_Compilation_Elem.Value; |
| |
| if Global_Compilation_Switches /= Nil_Variable_Value |
| and then not Global_Compilation_Switches.Default |
| then |
| -- We have found an attribute |
| -- Global_Compilation_Switches for a language: put the |
| -- switches in the appropriate table. |
| |
| List := Global_Compilation_Switches.Values; |
| while List /= Nil_String loop |
| Element := |
| Project_Tree.Shared.String_Elements.Table (List); |
| |
| if Element.Value /= No_Name then |
| Success := Add_Switch |
| (Switch => Get_Name_String (Element.Value), |
| For_Lang => Index, |
| For_Builder => False, |
| Has_Global_Compilation_Switches => |
| Global_Compilation_Array /= No_Array_Element); |
| end if; |
| |
| List := Element.Next; |
| end loop; |
| end if; |
| end if; |
| |
| Global_Compilation_Array := Global_Compilation_Elem.Next; |
| end loop; |
| end if; |
| end Compute_Builder_Switches; |
| |
| --------------------- |
| -- Write_Path_File -- |
| --------------------- |
| |
| procedure Write_Path_File (FD : File_Descriptor) is |
| Last : Natural; |
| Status : Boolean; |
| |
| begin |
| Name_Len := 0; |
| |
| for Index in Directories.First .. Directories.Last loop |
| Add_Str_To_Name_Buffer (Get_Name_String (Directories.Table (Index))); |
| Add_Char_To_Name_Buffer (ASCII.LF); |
| end loop; |
| |
| Last := Write (FD, Name_Buffer (1)'Address, Name_Len); |
| |
| if Last = Name_Len then |
| Close (FD, Status); |
| else |
| Status := False; |
| end if; |
| |
| if not Status then |
| Prj.Com.Fail ("could not write temporary file"); |
| end if; |
| end Write_Path_File; |
| |
| end Makeutl; |