------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- M A K E U T L -- -- -- -- B o d y -- -- -- -- Copyright (C) 2004-2016, 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 (). 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;