------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- P R J . P A R T -- -- -- -- B o d y -- -- -- -- Copyright (C) 2001-2013, 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 Atree; use Atree; with Err_Vars; use Err_Vars; with Opt; use Opt; with Osint; use Osint; with Output; use Output; with Prj.Com; use Prj.Com; with Prj.Dect; with Prj.Env; use Prj.Env; with Prj.Err; use Prj.Err; with Sinput; use Sinput; with Sinput.P; use Sinput.P; with Snames; with Table; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Exceptions; use Ada.Exceptions; with GNAT.HTable; use GNAT.HTable; package body Prj.Part is Buffer : String_Access; Buffer_Last : Natural := 0; Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator; ------------------------------------ -- Local Packages and Subprograms -- ------------------------------------ type With_Id is new Nat; No_With : constant With_Id := 0; type With_Record is record Path : Path_Name_Type; Location : Source_Ptr; Limited_With : Boolean; Node : Project_Node_Id; Next : With_Id; end record; -- Information about an imported project, to be put in table Withs below package Withs is new Table.Table (Table_Component_Type => With_Record, Table_Index_Type => With_Id, Table_Low_Bound => 1, Table_Initial => 10, Table_Increment => 100, Table_Name => "Prj.Part.Withs"); -- Table used to store temporarily paths and locations of imported -- projects. These imported projects will be effectively parsed later: just -- before parsing the current project for the non limited withed projects, -- after getting its name; after complete parsing of the current project -- for the limited withed projects. type Names_And_Id is record Path_Name : Path_Name_Type; Canonical_Path_Name : Path_Name_Type; Id : Project_Node_Id; Limited_With : Boolean; end record; package Project_Stack is new Table.Table (Table_Component_Type => Names_And_Id, Table_Index_Type => Nat, Table_Low_Bound => 1, Table_Initial => 10, Table_Increment => 100, Table_Name => "Prj.Part.Project_Stack"); -- This table is used to detect circular dependencies -- for imported and extended projects and to get the project ids of -- limited imported projects when there is a circularity with at least -- one limited imported project file. package Virtual_Hash is new GNAT.HTable.Simple_HTable (Header_Num => Header_Num, Element => Project_Node_Id, No_Element => Project_Node_High_Bound, Key => Project_Node_Id, Hash => Prj.Tree.Hash, Equal => "="); -- Hash table to store the node ids of projects for which a virtual -- extending project need to be created. The corresponding value is the -- head of a list of WITH clauses corresponding to the context of the -- enclosing EXTEND ALL projects. Note: Default_Element is Project_Node_ -- High_Bound because we want Empty_Node to be a possible value. package Processed_Hash is new GNAT.HTable.Simple_HTable (Header_Num => Header_Num, Element => Boolean, No_Element => False, Key => Project_Node_Id, Hash => Prj.Tree.Hash, Equal => "="); -- Hash table to store the project process when looking for project that -- need to have a virtual extending project, to avoid processing the same -- project twice. function Has_Circular_Dependencies (Flags : Processing_Flags; Normed_Path_Name : Path_Name_Type; Canonical_Path_Name : Path_Name_Type) return Boolean; -- Check for a circular dependency in the loaded project. -- Generates an error message in such a case. procedure Read_Project_Qualifier (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Is_Config_File : Boolean; Qualifier_Location : out Source_Ptr; Project : Project_Node_Id); -- Check if there is a qualifier before the reserved word "project" -- Hash table to cache project path to avoid looking for them on the path procedure Check_Extending_All_Imports (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Project : Project_Node_Id); -- Check that a non extending-all project does not import an -- extending-all project. procedure Check_Aggregate_Imports (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Project : Project_Node_Id); -- Check that an aggregate project only imports abstract projects procedure Check_Import_Aggregate (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Project : Project_Node_Id); -- Check that a non aggregate project does not import an aggregate project procedure Create_Virtual_Extending_Project (For_Project : Project_Node_Id; Main_Project : Project_Node_Id; Extension_Withs : Project_Node_Id; In_Tree : Project_Node_Tree_Ref); -- Create a virtual extending project of For_Project. Main_Project is -- the extending all project. Extension_Withs is the head of a WITH clause -- list to be added to the created virtual project. -- -- The String_Value_Of is not set for the automatically added with -- clause and keeps the default value of No_Name. This enables Prj.PP -- to skip these automatically added with clauses to be processed. procedure Look_For_Virtual_Projects_For (Proj : Project_Node_Id; In_Tree : Project_Node_Tree_Ref; Potentially_Virtual : Boolean); -- Look for projects that need to have a virtual extending project. -- This procedure is recursive. If called with Potentially_Virtual set to -- True, then Proj may need an virtual extending project; otherwise it -- does not (because it is already extended), but other projects that it -- imports may need to be virtually extended. type Extension_Origin is (None, Extending_Simple, Extending_All); -- Type of parameter From_Extended for procedures Parse_Single_Project and -- Post_Parse_Context_Clause. Extending_All means that we are parsing the -- tree rooted at an extending all project. procedure Parse_Single_Project (In_Tree : Project_Node_Tree_Ref; Project : out Project_Node_Id; Extends_All : out Boolean; Path_Name_Id : Path_Name_Type; Extended : Boolean; From_Extended : Extension_Origin; In_Limited : Boolean; Packages_To_Check : String_List_Access; Depth : Natural; Current_Dir : String; Is_Config_File : Boolean; Env : in out Environment; Implicit_Project : Boolean := False); -- Parse a project file. This is a recursive procedure: it calls itself for -- imported and extended projects. When From_Extended is not None, if the -- project has already been parsed and is an extended project A, return the -- ultimate (not extended) project that extends A. When In_Limited is True, -- the importing path includes at least one "limited with". When parsing -- configuration projects, do not allow a depth > 1. -- -- Is_Config_File should be set to True if the project represents a config -- file (.cgpr) since some specific checks apply. -- -- If Implicit_Project is True, change the Directory of the project node -- to be the Current_Dir. Recursive calls to Parse_Single_Project are -- always done with the default False value for Implicit_Project. procedure Pre_Parse_Context_Clause (In_Tree : Project_Node_Tree_Ref; Context_Clause : out With_Id; Is_Config_File : Boolean; Flags : Processing_Flags); -- Parse the context clause of a project. Store the paths and locations of -- the imported projects in table Withs. Does nothing if there is no -- context clause (if the current token is not "with" or "limited" followed -- by "with"). -- Is_Config_File should be set to True if the project represents a config -- file (.cgpr) since some specific checks apply. procedure Post_Parse_Context_Clause (Context_Clause : With_Id; In_Tree : Project_Node_Tree_Ref; In_Limited : Boolean; Limited_Withs : Boolean; Imported_Projects : in out Project_Node_Id; Project_Directory : Path_Name_Type; From_Extended : Extension_Origin; Packages_To_Check : String_List_Access; Depth : Natural; Current_Dir : String; Is_Config_File : Boolean; Env : in out Environment); -- Parse the imported projects that have been stored in table Withs, if -- any. From_Extended is used for the call to Parse_Single_Project below. -- -- When In_Limited is True, the importing path includes at least one -- "limited with". When Limited_Withs is False, only non limited withed -- projects are parsed. When Limited_Withs is True, only limited withed -- projects are parsed. -- -- Is_Config_File should be set to True if the project represents a config -- file (.cgpr) since some specific checks apply. function Project_Name_From (Path_Name : String; Is_Config_File : Boolean) return Name_Id; -- Returns the name of the project that corresponds to its path name. -- Returns No_Name if the path name is invalid, because the corresponding -- project name does not have the syntax of an ada identifier. function Copy_With_Clause (With_Clause : Project_Node_Id; In_Tree : Project_Node_Tree_Ref; Next_Clause : Project_Node_Id) return Project_Node_Id; -- Return a copy of With_Clause in In_Tree, whose Next_With_Clause is the -- indicated one. ---------------------- -- Copy_With_Clause -- ---------------------- function Copy_With_Clause (With_Clause : Project_Node_Id; In_Tree : Project_Node_Tree_Ref; Next_Clause : Project_Node_Id) return Project_Node_Id is New_With_Clause : constant Project_Node_Id := Default_Project_Node (In_Tree, N_With_Clause); begin Set_Name_Of (New_With_Clause, In_Tree, Name_Of (With_Clause, In_Tree)); Set_Path_Name_Of (New_With_Clause, In_Tree, Path_Name_Of (With_Clause, In_Tree)); Set_Project_Node_Of (New_With_Clause, In_Tree, Project_Node_Of (With_Clause, In_Tree)); Set_Next_With_Clause_Of (New_With_Clause, In_Tree, Next_Clause); return New_With_Clause; end Copy_With_Clause; -------------------------------------- -- Create_Virtual_Extending_Project -- -------------------------------------- procedure Create_Virtual_Extending_Project (For_Project : Project_Node_Id; Main_Project : Project_Node_Id; Extension_Withs : Project_Node_Id; In_Tree : Project_Node_Tree_Ref) is Virtual_Name : constant String := Virtual_Prefix & Get_Name_String (Name_Of (For_Project, In_Tree)); -- The name of the virtual extending project Virtual_Name_Id : Name_Id; -- Virtual extending project name id Virtual_Path_Id : Path_Name_Type; -- Fake path name of the virtual extending project. The directory is -- the same directory as the extending all project. -- The source of the virtual extending project is something like: -- project V$ extends is -- for Source_Dirs use (); -- end V$; -- The project directory cannot be specified during parsing; it will be -- put directly in the virtual extending project data during processing. -- Nodes that made up the virtual extending project Virtual_Project : Project_Node_Id; With_Clause : constant Project_Node_Id := Default_Project_Node (In_Tree, N_With_Clause); Project_Declaration : Project_Node_Id; Source_Dirs_Declaration : constant Project_Node_Id := Default_Project_Node (In_Tree, N_Declarative_Item); Source_Dirs_Attribute : constant Project_Node_Id := Default_Project_Node (In_Tree, N_Attribute_Declaration, List); Source_Dirs_Expression : constant Project_Node_Id := Default_Project_Node (In_Tree, N_Expression, List); Source_Dirs_Term : constant Project_Node_Id := Default_Project_Node (In_Tree, N_Term, List); Source_Dirs_List : constant Project_Node_Id := Default_Project_Node (In_Tree, N_Literal_String_List, List); begin -- Get the virtual path name Get_Name_String (Path_Name_Of (Main_Project, In_Tree)); while Name_Len > 0 and then Name_Buffer (Name_Len) /= Directory_Separator and then Name_Buffer (Name_Len) /= '/' loop Name_Len := Name_Len - 1; end loop; Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) := Virtual_Name; Name_Len := Name_Len + Virtual_Name'Length; Virtual_Path_Id := Name_Find; -- Get the virtual name id Name_Len := Virtual_Name'Length; Name_Buffer (1 .. Name_Len) := Virtual_Name; Virtual_Name_Id := Name_Find; Virtual_Project := Create_Project (In_Tree => In_Tree, Name => Virtual_Name_Id, Full_Path => Virtual_Path_Id, Is_Config_File => False); Project_Declaration := Project_Declaration_Of (Virtual_Project, In_Tree); -- Add a WITH clause to the main project to import the newly created -- virtual extending project. Set_Name_Of (With_Clause, In_Tree, Virtual_Name_Id); Set_Path_Name_Of (With_Clause, In_Tree, Virtual_Path_Id); Set_Project_Node_Of (With_Clause, In_Tree, Virtual_Project); Set_Next_With_Clause_Of (With_Clause, In_Tree, First_With_Clause_Of (Main_Project, In_Tree)); Set_First_With_Clause_Of (Main_Project, In_Tree, With_Clause); -- Copy with clauses for projects imported by the extending-all project declare Org_With_Clause : Project_Node_Id := Extension_Withs; New_With_Clause : Project_Node_Id := Empty_Node; begin while Present (Org_With_Clause) loop New_With_Clause := Copy_With_Clause (Org_With_Clause, In_Tree, New_With_Clause); Org_With_Clause := Next_With_Clause_Of (Org_With_Clause, In_Tree); end loop; Set_First_With_Clause_Of (Virtual_Project, In_Tree, New_With_Clause); end; -- Virtual project node Set_Location_Of (Virtual_Project, In_Tree, Location_Of (Main_Project, In_Tree)); Set_Extended_Project_Path_Of (Virtual_Project, In_Tree, Path_Name_Of (For_Project, In_Tree)); -- Project declaration Set_First_Declarative_Item_Of (Project_Declaration, In_Tree, Source_Dirs_Declaration); Set_Extended_Project_Of (Project_Declaration, In_Tree, For_Project); -- Source_Dirs declaration Set_Current_Item_Node (Source_Dirs_Declaration, In_Tree, Source_Dirs_Attribute); -- Source_Dirs attribute Set_Name_Of (Source_Dirs_Attribute, In_Tree, Snames.Name_Source_Dirs); Set_Expression_Of (Source_Dirs_Attribute, In_Tree, Source_Dirs_Expression); -- Source_Dirs expression Set_First_Term (Source_Dirs_Expression, In_Tree, Source_Dirs_Term); -- Source_Dirs term Set_Current_Term (Source_Dirs_Term, In_Tree, Source_Dirs_List); -- Source_Dirs empty list: nothing to do end Create_Virtual_Extending_Project; ----------------------------------- -- Look_For_Virtual_Projects_For -- ----------------------------------- Extension_Withs : Project_Node_Id; -- Head of the current EXTENDS ALL imports list. When creating virtual -- projects for an EXTENDS ALL, we import in each virtual project all -- of the projects that appear in WITH clauses of the extending projects. -- This ensures that virtual projects share a consistent environment (in -- particular if a project imported by one of the extending projects -- replaces some runtime units). procedure Look_For_Virtual_Projects_For (Proj : Project_Node_Id; In_Tree : Project_Node_Tree_Ref; Potentially_Virtual : Boolean) is Declaration : Project_Node_Id := Empty_Node; -- Node for the project declaration of Proj With_Clause : Project_Node_Id := Empty_Node; -- Node for a with clause of Proj Imported : Project_Node_Id := Empty_Node; -- Node for a project imported by Proj Extended : Project_Node_Id := Empty_Node; -- Node for the eventual project extended by Proj Extends_All : Boolean := False; -- Set True if Proj is an EXTENDS ALL project Saved_Extension_Withs : constant Project_Node_Id := Extension_Withs; begin -- Nothing to do if Proj is undefined or has already been processed if Present (Proj) and then not Processed_Hash.Get (Proj) then -- Make sure the project will not be processed again Processed_Hash.Set (Proj, True); Declaration := Project_Declaration_Of (Proj, In_Tree); if Present (Declaration) then Extended := Extended_Project_Of (Declaration, In_Tree); Extends_All := Is_Extending_All (Proj, In_Tree); end if; -- If this is a project that may need a virtual extending project -- and it is not itself an extending project, put it in the list. if Potentially_Virtual and then No (Extended) then Virtual_Hash.Set (Proj, Extension_Withs); end if; -- Now check the projects it imports With_Clause := First_With_Clause_Of (Proj, In_Tree); while Present (With_Clause) loop Imported := Project_Node_Of (With_Clause, In_Tree); if Present (Imported) then Look_For_Virtual_Projects_For (Imported, In_Tree, Potentially_Virtual => True); end if; if Extends_All then -- This is an EXTENDS ALL project: prepend each of its WITH -- clauses to the currently active list of extension deps. Extension_Withs := Copy_With_Clause (With_Clause, In_Tree, Extension_Withs); end if; With_Clause := Next_With_Clause_Of (With_Clause, In_Tree); end loop; -- Check also the eventual project extended by Proj. As this project -- is already extended, call recursively with Potentially_Virtual -- being False. Look_For_Virtual_Projects_For (Extended, In_Tree, Potentially_Virtual => False); Extension_Withs := Saved_Extension_Withs; end if; end Look_For_Virtual_Projects_For; ----------- -- Parse -- ----------- procedure Parse (In_Tree : Project_Node_Tree_Ref; Project : out Project_Node_Id; Project_File_Name : String; Errout_Handling : Errout_Mode := Always_Finalize; Packages_To_Check : String_List_Access; Store_Comments : Boolean := False; Current_Directory : String := ""; Is_Config_File : Boolean; Env : in out Prj.Tree.Environment; Target_Name : String := ""; Implicit_Project : Boolean := False) is Dummy : Boolean; pragma Warnings (Off, Dummy); Real_Project_File_Name : String_Access := Osint.To_Canonical_File_Spec (Project_File_Name); Path_Name_Id : Path_Name_Type; begin In_Tree.Incomplete_With := False; if not Is_Initialized (Env.Project_Path) then Prj.Env.Initialize_Default_Project_Path (Env.Project_Path, Target_Name); end if; if Real_Project_File_Name = null then Real_Project_File_Name := new String'(Project_File_Name); end if; Project := Empty_Node; Find_Project (Env.Project_Path, Project_File_Name => Real_Project_File_Name.all, Directory => Current_Directory, Path => Path_Name_Id); Free (Real_Project_File_Name); if Errout_Handling /= Never_Finalize then Prj.Err.Initialize; end if; Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments); Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments); if Path_Name_Id = No_Path then declare P : String_Access; begin Get_Path (Env.Project_Path, Path => P); Prj.Com.Fail ("project file """ & Project_File_Name & """ not found in " & P.all); Project := Empty_Node; return; end; end if; -- Parse the main project file begin Parse_Single_Project (In_Tree => In_Tree, Project => Project, Extends_All => Dummy, Path_Name_Id => Path_Name_Id, Extended => False, From_Extended => None, In_Limited => False, Packages_To_Check => Packages_To_Check, Depth => 0, Current_Dir => Current_Directory, Is_Config_File => Is_Config_File, Env => Env, Implicit_Project => Implicit_Project); exception when Types.Unrecoverable_Error => -- Unrecoverable_Error is raised when a line is too long. -- A meaningful error message will be displayed later. Project := Empty_Node; end; -- If Project is an extending-all project, create the eventual -- virtual extending projects and check that there are no illegally -- imported projects. if Present (Project) and then Is_Extending_All (Project, In_Tree) then -- First look for projects that potentially need a virtual -- extending project. Virtual_Hash.Reset; Processed_Hash.Reset; -- Mark the extending all project as processed, to avoid checking -- the imported projects in case of a "limited with" on this -- extending all project. Processed_Hash.Set (Project, True); declare Declaration : constant Project_Node_Id := Project_Declaration_Of (Project, In_Tree); begin Extension_Withs := First_With_Clause_Of (Project, In_Tree); Look_For_Virtual_Projects_For (Extended_Project_Of (Declaration, In_Tree), In_Tree, Potentially_Virtual => False); end; -- Now, check the projects directly imported by the main project. -- Remove from the potentially virtual any project extended by one -- of these imported projects. declare With_Clause : Project_Node_Id; Imported : Project_Node_Id := Empty_Node; Declaration : Project_Node_Id := Empty_Node; begin With_Clause := First_With_Clause_Of (Project, In_Tree); while Present (With_Clause) loop Imported := Project_Node_Of (With_Clause, In_Tree); if Present (Imported) then Declaration := Project_Declaration_Of (Imported, In_Tree); if Extended_Project_Of (Declaration, In_Tree) /= Empty_Node then loop Imported := Extended_Project_Of (Declaration, In_Tree); exit when No (Imported); Virtual_Hash.Remove (Imported); Declaration := Project_Declaration_Of (Imported, In_Tree); end loop; end if; end if; With_Clause := Next_With_Clause_Of (With_Clause, In_Tree); end loop; end; -- Now create all the virtual extending projects declare Proj : Project_Node_Id := Empty_Node; Withs : Project_Node_Id; begin Virtual_Hash.Get_First (Proj, Withs); while Withs /= Project_Node_High_Bound loop Create_Virtual_Extending_Project (Proj, Project, Withs, In_Tree); Virtual_Hash.Get_Next (Proj, Withs); end loop; end; end if; -- If there were any kind of error during the parsing, serious -- or not, then the parsing fails. if Total_Errors_Detected > 0 then Project := Empty_Node; end if; case Errout_Handling is when Always_Finalize => Prj.Err.Finalize; -- Reinitialize to avoid duplicate warnings later on Prj.Err.Initialize; when Finalize_If_Error => if No (Project) then Prj.Err.Finalize; Prj.Err.Initialize; end if; when Never_Finalize => null; end case; exception when X : others => -- Internal error Write_Line (Exception_Information (X)); Write_Str ("Exception "); Write_Str (Exception_Name (X)); Write_Line (" raised, while processing project file"); Project := Empty_Node; end Parse; ------------------------------ -- Pre_Parse_Context_Clause -- ------------------------------ procedure Pre_Parse_Context_Clause (In_Tree : Project_Node_Tree_Ref; Context_Clause : out With_Id; Is_Config_File : Boolean; Flags : Processing_Flags) is Current_With_Clause : With_Id := No_With; Limited_With : Boolean := False; Current_With : With_Record; Current_With_Node : Project_Node_Id := Empty_Node; begin -- Assume no context clause Context_Clause := No_With; With_Loop : -- If Token is not WITH or LIMITED, there is no context clause, or we -- have exhausted the with clauses. while Token = Tok_With or else Token = Tok_Limited loop Current_With_Node := Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree); Limited_With := Token = Tok_Limited; if Is_Config_File then Error_Msg (Flags, "configuration project cannot import " & "other configuration projects", Token_Ptr); end if; if Limited_With then Scan (In_Tree); -- past LIMITED Expect (Tok_With, "WITH"); exit With_Loop when Token /= Tok_With; end if; Comma_Loop : loop Scan (In_Tree); -- past WITH or "," Expect (Tok_String_Literal, "literal string"); if Token /= Tok_String_Literal then return; end if; -- Store path and location in table Withs Current_With := (Path => Path_Name_Type (Token_Name), Location => Token_Ptr, Limited_With => Limited_With, Node => Current_With_Node, Next => No_With); Withs.Increment_Last; Withs.Table (Withs.Last) := Current_With; if Current_With_Clause = No_With then Context_Clause := Withs.Last; else Withs.Table (Current_With_Clause).Next := Withs.Last; end if; Current_With_Clause := Withs.Last; Scan (In_Tree); if Token = Tok_Semicolon then Set_End_Of_Line (Current_With_Node); Set_Previous_Line_Node (Current_With_Node); -- End of (possibly multiple) with clause; Scan (In_Tree); -- past semicolon exit Comma_Loop; elsif Token = Tok_Comma then Set_Is_Not_Last_In_List (Current_With_Node, In_Tree); else Error_Msg (Flags, "expected comma or semi colon", Token_Ptr); exit Comma_Loop; end if; Current_With_Node := Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree); end loop Comma_Loop; end loop With_Loop; end Pre_Parse_Context_Clause; ------------------------------- -- Post_Parse_Context_Clause -- ------------------------------- procedure Post_Parse_Context_Clause (Context_Clause : With_Id; In_Tree : Project_Node_Tree_Ref; In_Limited : Boolean; Limited_Withs : Boolean; Imported_Projects : in out Project_Node_Id; Project_Directory : Path_Name_Type; From_Extended : Extension_Origin; Packages_To_Check : String_List_Access; Depth : Natural; Current_Dir : String; Is_Config_File : Boolean; Env : in out Environment) is Current_With_Clause : With_Id := Context_Clause; Current_Project : Project_Node_Id := Imported_Projects; Previous_Project : Project_Node_Id := Empty_Node; Next_Project : Project_Node_Id := Empty_Node; Project_Directory_Path : constant String := Get_Name_String (Project_Directory); Current_With : With_Record; Extends_All : Boolean := False; Imported_Path_Name_Id : Path_Name_Type; begin -- Set Current_Project to the last project in the current list, if the -- list is not empty. if Present (Current_Project) then while Present (Next_With_Clause_Of (Current_Project, In_Tree)) loop Current_Project := Next_With_Clause_Of (Current_Project, In_Tree); end loop; end if; while Current_With_Clause /= No_With loop Current_With := Withs.Table (Current_With_Clause); Current_With_Clause := Current_With.Next; if Limited_Withs = Current_With.Limited_With then Find_Project (Env.Project_Path, Project_File_Name => Get_Name_String (Current_With.Path), Directory => Project_Directory_Path, Path => Imported_Path_Name_Id); if Imported_Path_Name_Id = No_Path then if Env.Flags.Ignore_Missing_With then In_Tree.Incomplete_With := True; else -- The project file cannot be found Error_Msg_File_1 := File_Name_Type (Current_With.Path); Error_Msg (Env.Flags, "unknown project file: {", Current_With.Location); -- If this is not imported by the main project file, display -- the import path. if Project_Stack.Last > 1 then for Index in reverse 1 .. Project_Stack.Last loop Error_Msg_File_1 := File_Name_Type (Project_Stack.Table (Index).Path_Name); Error_Msg (Env.Flags, "\imported by {", Current_With.Location); end loop; end if; end if; else -- New with clause declare Resolved_Path : constant String := Normalize_Pathname (Get_Name_String (Imported_Path_Name_Id), Directory => Current_Dir, Resolve_Links => Opt.Follow_Links_For_Files, Case_Sensitive => True); Withed_Project : Project_Node_Id := Empty_Node; begin Previous_Project := Current_Project; if No (Current_Project) then -- First with clause of the context clause Current_Project := Current_With.Node; Imported_Projects := Current_Project; else Next_Project := Current_With.Node; Set_Next_With_Clause_Of (Current_Project, In_Tree, Next_Project); Current_Project := Next_Project; end if; Set_String_Value_Of (Current_Project, In_Tree, Name_Id (Current_With.Path)); Set_Location_Of (Current_Project, In_Tree, Current_With.Location); -- If it is a limited with, check if we have a circularity. -- If we have one, get the project id of the limited -- imported project file, and do not parse it. if (In_Limited or Limited_Withs) and then Project_Stack.Last > 1 then declare Canonical_Path_Name : Path_Name_Type; begin Name_Len := Resolved_Path'Length; Name_Buffer (1 .. Name_Len) := Resolved_Path; Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Canonical_Path_Name := Name_Find; for Index in 1 .. Project_Stack.Last loop if Project_Stack.Table (Index).Canonical_Path_Name = Canonical_Path_Name then -- We have found the limited imported project, -- get its project id, and do not parse it. Withed_Project := Project_Stack.Table (Index).Id; exit; end if; end loop; end; end if; -- Parse the imported project if its project id is unknown if No (Withed_Project) then Parse_Single_Project (In_Tree => In_Tree, Project => Withed_Project, Extends_All => Extends_All, Path_Name_Id => Imported_Path_Name_Id, Extended => False, From_Extended => From_Extended, In_Limited => In_Limited or Limited_Withs, Packages_To_Check => Packages_To_Check, Depth => Depth, Current_Dir => Current_Dir, Is_Config_File => Is_Config_File, Env => Env); else Extends_All := Is_Extending_All (Withed_Project, In_Tree); end if; if No (Withed_Project) then -- If parsing unsuccessful, remove the context clause Current_Project := Previous_Project; if No (Current_Project) then Imported_Projects := Empty_Node; else Set_Next_With_Clause_Of (Current_Project, In_Tree, Empty_Node); end if; else -- If parsing was successful, record project name and -- path name in with clause Set_Project_Node_Of (Node => Current_Project, In_Tree => In_Tree, To => Withed_Project, Limited_With => Current_With.Limited_With); Set_Name_Of (Current_Project, In_Tree, Name_Of (Withed_Project, In_Tree)); Name_Len := Resolved_Path'Length; Name_Buffer (1 .. Name_Len) := Resolved_Path; Set_Path_Name_Of (Current_Project, In_Tree, Name_Find); if Extends_All then Set_Is_Extending_All (Current_Project, In_Tree); end if; end if; end; end if; end if; end loop; end Post_Parse_Context_Clause; --------------------------------- -- Check_Extending_All_Imports -- --------------------------------- procedure Check_Extending_All_Imports (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Project : Project_Node_Id) is With_Clause : Project_Node_Id; Imported : Project_Node_Id; begin if not Is_Extending_All (Project, In_Tree) then With_Clause := First_With_Clause_Of (Project, In_Tree); while Present (With_Clause) loop Imported := Project_Node_Of (With_Clause, In_Tree); if Is_Extending_All (With_Clause, In_Tree) then Error_Msg_Name_1 := Name_Of (Imported, In_Tree); Error_Msg (Flags, "cannot import extending-all project %%", Token_Ptr); exit; end if; With_Clause := Next_With_Clause_Of (With_Clause, In_Tree); end loop; end if; end Check_Extending_All_Imports; ----------------------------- -- Check_Aggregate_Imports -- ----------------------------- procedure Check_Aggregate_Imports (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Project : Project_Node_Id) is With_Clause, Imported : Project_Node_Id; begin if Project_Qualifier_Of (Project, In_Tree) = Aggregate then With_Clause := First_With_Clause_Of (Project, In_Tree); while Present (With_Clause) loop Imported := Project_Node_Of (With_Clause, In_Tree); if Project_Qualifier_Of (Imported, In_Tree) /= Dry then Error_Msg_Name_1 := Name_Id (Path_Name_Of (Imported, In_Tree)); Error_Msg (Flags, "can only import abstract projects, not %%", Token_Ptr); exit; end if; With_Clause := Next_With_Clause_Of (With_Clause, In_Tree); end loop; end if; end Check_Aggregate_Imports; ---------------------------- -- Check_Import_Aggregate -- ---------------------------- procedure Check_Import_Aggregate (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Project : Project_Node_Id) is With_Clause : Project_Node_Id; Imported : Project_Node_Id; begin if Project_Qualifier_Of (Project, In_Tree) /= Aggregate then With_Clause := First_With_Clause_Of (Project, In_Tree); while Present (With_Clause) loop Imported := Project_Node_Of (With_Clause, In_Tree); if Project_Qualifier_Of (Imported, In_Tree) = Aggregate then Error_Msg_Name_1 := Name_Id (Path_Name_Of (Imported, In_Tree)); Error_Msg (Flags, "cannot import aggregate project %%", Token_Ptr); exit; end if; With_Clause := Next_With_Clause_Of (With_Clause, In_Tree); end loop; end if; end Check_Import_Aggregate; ---------------------------- -- Read_Project_Qualifier -- ---------------------------- procedure Read_Project_Qualifier (Flags : Processing_Flags; In_Tree : Project_Node_Tree_Ref; Is_Config_File : Boolean; Qualifier_Location : out Source_Ptr; Project : Project_Node_Id) is Proj_Qualifier : Project_Qualifier := Unspecified; begin Qualifier_Location := Token_Ptr; if Token = Tok_Abstract then Proj_Qualifier := Dry; Scan (In_Tree); elsif Token = Tok_Identifier then case Token_Name is when Snames.Name_Standard => Proj_Qualifier := Standard; Scan (In_Tree); when Snames.Name_Aggregate => Proj_Qualifier := Aggregate; Scan (In_Tree); if Token = Tok_Identifier and then Token_Name = Snames.Name_Library then Proj_Qualifier := Aggregate_Library; Scan (In_Tree); end if; when Snames.Name_Library => Proj_Qualifier := Library; Scan (In_Tree); when Snames.Name_Configuration => if not Is_Config_File then Error_Msg (Flags, "configuration projects cannot belong to a user" & " project tree", Token_Ptr); end if; Proj_Qualifier := Configuration; Scan (In_Tree); when others => null; end case; end if; if Is_Config_File and then Proj_Qualifier = Unspecified then -- Set the qualifier to Configuration, even if the token doesn't -- exist in the source file itself, so that we can differentiate -- project files and configuration files later on. Proj_Qualifier := Configuration; end if; if Proj_Qualifier /= Unspecified then if Is_Config_File and then Proj_Qualifier /= Configuration then Error_Msg (Flags, "a configuration project cannot be qualified except " & "as configuration project", Qualifier_Location); end if; Set_Project_Qualifier_Of (Project, In_Tree, Proj_Qualifier); end if; end Read_Project_Qualifier; ------------------------------- -- Has_Circular_Dependencies -- ------------------------------- function Has_Circular_Dependencies (Flags : Processing_Flags; Normed_Path_Name : Path_Name_Type; Canonical_Path_Name : Path_Name_Type) return Boolean is begin for Index in reverse 1 .. Project_Stack.Last loop exit when Project_Stack.Table (Index).Limited_With; if Canonical_Path_Name = Project_Stack.Table (Index).Canonical_Path_Name then Error_Msg (Flags, "circular dependency detected", Token_Ptr); Error_Msg_Name_1 := Name_Id (Normed_Path_Name); Error_Msg (Flags, "\ %% is imported by", Token_Ptr); for Current in reverse 1 .. Project_Stack.Last loop Error_Msg_Name_1 := Name_Id (Project_Stack.Table (Current).Path_Name); if Project_Stack.Table (Current).Canonical_Path_Name /= Canonical_Path_Name then Error_Msg (Flags, "\ %% which itself is imported by", Token_Ptr); else Error_Msg (Flags, "\ %%", Token_Ptr); exit; end if; end loop; return True; end if; end loop; return False; end Has_Circular_Dependencies; -------------------------- -- Parse_Single_Project -- -------------------------- procedure Parse_Single_Project (In_Tree : Project_Node_Tree_Ref; Project : out Project_Node_Id; Extends_All : out Boolean; Path_Name_Id : Path_Name_Type; Extended : Boolean; From_Extended : Extension_Origin; In_Limited : Boolean; Packages_To_Check : String_List_Access; Depth : Natural; Current_Dir : String; Is_Config_File : Boolean; Env : in out Environment; Implicit_Project : Boolean := False) is Path_Name : constant String := Get_Name_String (Path_Name_Id); Normed_Path_Name : Path_Name_Type; Canonical_Path_Name : Path_Name_Type; Resolved_Path_Name : Path_Name_Type; Project_Directory : Path_Name_Type; Project_Scan_State : Saved_Project_Scan_State; Source_Index : Source_File_Index; Extending : Boolean := False; Extended_Project : Project_Node_Id := Empty_Node; A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node := Tree_Private_Part.Projects_Htable.Get_First (In_Tree.Projects_HT); Name_From_Path : constant Name_Id := Project_Name_From (Path_Name, Is_Config_File => Is_Config_File); Name_Of_Project : Name_Id := No_Name; Display_Name_Of_Project : Name_Id := No_Name; Duplicated : Boolean := False; First_With : With_Id; Imported_Projects : Project_Node_Id := Empty_Node; use Tree_Private_Part; Project_Comment_State : Tree.Comment_State; Qualifier_Location : Source_Ptr; begin Extends_All := False; declare Normed_Path : constant String := Normalize_Pathname (Path_Name, Directory => Current_Dir, Resolve_Links => False, Case_Sensitive => True); Canonical_Path : constant String := Normalize_Pathname (Normed_Path, Directory => Current_Dir, Resolve_Links => Opt.Follow_Links_For_Files, Case_Sensitive => False); begin Name_Len := Normed_Path'Length; Name_Buffer (1 .. Name_Len) := Normed_Path; Normed_Path_Name := Name_Find; Name_Len := Canonical_Path'Length; Name_Buffer (1 .. Name_Len) := Canonical_Path; Canonical_Path_Name := Name_Find; if Opt.Follow_Links_For_Files then Resolved_Path_Name := Canonical_Path_Name; else Name_Len := 0; Add_Str_To_Name_Buffer (Normalize_Pathname (Canonical_Path, Resolve_Links => True, Case_Sensitive => False)); Resolved_Path_Name := Name_Find; end if; end; if Has_Circular_Dependencies (Env.Flags, Normed_Path_Name, Canonical_Path_Name) then Project := Empty_Node; return; end if; -- Put the new path name on the stack Project_Stack.Append ((Path_Name => Normed_Path_Name, Canonical_Path_Name => Canonical_Path_Name, Id => Empty_Node, Limited_With => In_Limited)); -- Check if the project file has already been parsed while A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node loop if A_Project_Name_And_Node.Resolved_Path = Resolved_Path_Name then if Extended then if A_Project_Name_And_Node.Extended then if A_Project_Name_And_Node.Proj_Qualifier /= Dry then Error_Msg (Env.Flags, "cannot extend the same project file several times", Token_Ptr); end if; elsif not A_Project_Name_And_Node.From_Extended then Error_Msg (Env.Flags, "cannot extend an already imported project file", Token_Ptr); else -- Register this project as being extended A_Project_Name_And_Node.Extended := True; Tree_Private_Part.Projects_Htable.Set (In_Tree.Projects_HT, A_Project_Name_And_Node.Name, A_Project_Name_And_Node); end if; elsif A_Project_Name_And_Node.Extended then Extends_All := Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree); -- If the imported project is an extended project A, and we are -- in an extended project, replace A with the ultimate project -- extending A. if From_Extended /= None then declare Decl : Project_Node_Id := Project_Declaration_Of (A_Project_Name_And_Node.Node, In_Tree); Prj : Project_Node_Id := A_Project_Name_And_Node.Node; begin -- Loop through extending projects to find the ultimate -- extending project, that is the one that is not -- extended. For an abstract project, as it can be -- extended several times, there is no extending project -- registered, so the loop does not execute and the -- resulting project is the abstract project. while Extending_Project_Of (Decl, In_Tree) /= Empty_Node loop Prj := Extending_Project_Of (Decl, In_Tree); Decl := Project_Declaration_Of (Prj, In_Tree); end loop; A_Project_Name_And_Node.Node := Prj; end; else Error_Msg (Env.Flags, "cannot import an already extended project file", Token_Ptr); end if; elsif A_Project_Name_And_Node.From_Extended then -- This project is now imported from a non extending project. -- Indicate this in has table Projects.HT. A_Project_Name_And_Node.From_Extended := False; Tree_Private_Part.Projects_Htable.Set (In_Tree.Projects_HT, A_Project_Name_And_Node.Name, A_Project_Name_And_Node); end if; Project := A_Project_Name_And_Node.Node; Project_Stack.Decrement_Last; return; end if; A_Project_Name_And_Node := Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT); end loop; -- We never encountered this project file. Save the scan state, load the -- project file and start to scan it. Save_Project_Scan_State (Project_Scan_State); Source_Index := Load_Project_File (Path_Name); Tree.Save (Project_Comment_State); -- If we cannot find it, we stop if Source_Index = No_Source_File then Project := Empty_Node; Project_Stack.Decrement_Last; return; end if; Prj.Err.Scanner.Initialize_Scanner (Source_Index); Tree.Reset_State; Scan (In_Tree); if not Is_Config_File and then Name_From_Path = No_Name and then not Implicit_Project then -- The project file name is not correct (no or bad extension, or not -- following Ada identifier's syntax). Error_Msg_File_1 := File_Name_Type (Canonical_Path_Name); Error_Msg (Env.Flags, "?{ is not a valid path name for a project file", Token_Ptr); end if; if Current_Verbosity >= Medium then Debug_Increase_Indent ("Parsing """ & Path_Name & '"'); end if; Project_Directory := Path_Name_Type (Get_Directory (File_Name_Type (Normed_Path_Name))); -- Is there any imported project? Pre_Parse_Context_Clause (In_Tree => In_Tree, Is_Config_File => Is_Config_File, Context_Clause => First_With, Flags => Env.Flags); Project := Default_Project_Node (Of_Kind => N_Project, In_Tree => In_Tree); Project_Stack.Table (Project_Stack.Last).Id := Project; Set_Directory_Of (Project, In_Tree, Project_Directory); Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name); Read_Project_Qualifier (Env.Flags, In_Tree, Is_Config_File, Qualifier_Location, Project); Set_Location_Of (Project, In_Tree, Token_Ptr); Expect (Tok_Project, "PROJECT"); -- Mark location of PROJECT token if present if Token = Tok_Project then Scan (In_Tree); -- past PROJECT Set_Location_Of (Project, In_Tree, Token_Ptr); end if; -- Clear the Buffer Buffer_Last := 0; loop Expect (Tok_Identifier, "identifier"); -- If the token is not an identifier, clear the buffer before -- exiting to indicate that the name of the project is ill-formed. if Token /= Tok_Identifier then Buffer_Last := 0; exit; end if; -- Add the identifier name to the buffer Get_Name_String (Token_Name); Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last); -- Scan past the identifier Scan (In_Tree); -- If we have a dot, add a dot to the Buffer and look for the next -- identifier. exit when Token /= Tok_Dot; Add_To_Buffer (".", Buffer, Buffer_Last); -- Scan past the dot Scan (In_Tree); end loop; -- See if this is an extending project if Token = Tok_Extends then if Is_Config_File then Error_Msg (Env.Flags, "extending configuration project not allowed", Token_Ptr); end if; -- Make sure that gnatmake will use mapping files Opt.Create_Mapping_File := True; -- We are extending another project Extending := True; Scan (In_Tree); -- past EXTENDS if Token = Tok_All then Extends_All := True; Set_Is_Extending_All (Project, In_Tree); Scan (In_Tree); -- scan past ALL end if; end if; -- If the name is well formed, Buffer_Last is > 0 if Buffer_Last > 0 then -- The Buffer contains the name of the project Name_Len := Buffer_Last; Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last); Name_Of_Project := Name_Find; Set_Name_Of (Project, In_Tree, Name_Of_Project); -- To get expected name of the project file, replace dots by dashes for Index in 1 .. Name_Len loop if Name_Buffer (Index) = '.' then Name_Buffer (Index) := '-'; end if; end loop; Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); declare Expected_Name : constant Name_Id := Name_Find; Extension : String_Access; begin -- Output a warning if the actual name is not the expected name if not Is_Config_File and then (Name_From_Path /= No_Name) and then Expected_Name /= Name_From_Path then Error_Msg_Name_1 := Expected_Name; if Is_Config_File then Extension := new String'(Config_Project_File_Extension); else Extension := new String'(Project_File_Extension); end if; Error_Msg (Env.Flags, "?file name does not match project name, should be `%%" & Extension.all & "`", Token_Ptr); end if; end; -- Read the original casing of the project name declare Loc : Source_Ptr; begin Loc := Location_Of (Project, In_Tree); for J in 1 .. Name_Len loop Name_Buffer (J) := Sinput.Source (Loc); Loc := Loc + 1; end loop; Display_Name_Of_Project := Name_Find; end; declare From_Ext : Extension_Origin := None; begin -- Extending_All is always propagated if From_Extended = Extending_All or else Extends_All then From_Ext := Extending_All; -- Otherwise, From_Extended is set to Extending_Single if the -- current project is an extending project. elsif Extended then From_Ext := Extending_Simple; end if; Post_Parse_Context_Clause (In_Tree => In_Tree, Context_Clause => First_With, In_Limited => In_Limited, Limited_Withs => False, Imported_Projects => Imported_Projects, Project_Directory => Project_Directory, From_Extended => From_Ext, Packages_To_Check => Packages_To_Check, Depth => Depth + 1, Current_Dir => Current_Dir, Is_Config_File => Is_Config_File, Env => Env); Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects); end; if not Is_Config_File then declare Name_And_Node : Tree_Private_Part.Project_Name_And_Node := Tree_Private_Part.Projects_Htable.Get_First (In_Tree.Projects_HT); Project_Name : Name_Id := Name_And_Node.Name; begin -- Check if we already have a project with this name while Project_Name /= No_Name and then Project_Name /= Name_Of_Project loop Name_And_Node := Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT); Project_Name := Name_And_Node.Name; end loop; -- Report an error if we already have a project with this name if Project_Name /= No_Name then Duplicated := True; Error_Msg_Name_1 := Project_Name; Error_Msg (Env.Flags, "duplicate project name %%", Location_Of (Project, In_Tree)); Error_Msg_Name_1 := Name_Id (Path_Name_Of (Name_And_Node.Node, In_Tree)); Error_Msg (Env.Flags, "\already in %%", Location_Of (Project, In_Tree)); end if; end; end if; end if; if Extending then Expect (Tok_String_Literal, "literal string"); if Token = Tok_String_Literal then Set_Extended_Project_Path_Of (Project, In_Tree, Path_Name_Type (Token_Name)); declare Original_Path_Name : constant String := Get_Name_String (Token_Name); Extended_Project_Path_Name_Id : Path_Name_Type; begin Find_Project (Env.Project_Path, Project_File_Name => Original_Path_Name, Directory => Get_Name_String (Project_Directory), Path => Extended_Project_Path_Name_Id); if Extended_Project_Path_Name_Id = No_Path then -- We could not find the project file to extend Error_Msg_Name_1 := Token_Name; Error_Msg (Env.Flags, "unknown project file: %%", Token_Ptr); -- If not in the main project file, display the import path if Project_Stack.Last > 1 then Error_Msg_Name_1 := Name_Id (Project_Stack.Table (Project_Stack.Last).Path_Name); Error_Msg (Env.Flags, "\extended by %%", Token_Ptr); for Index in reverse 1 .. Project_Stack.Last - 1 loop Error_Msg_Name_1 := Name_Id (Project_Stack.Table (Index).Path_Name); Error_Msg (Env.Flags, "\imported by %%", Token_Ptr); end loop; end if; else declare From_Ext : Extension_Origin := None; begin if From_Extended = Extending_All or else Extends_All then From_Ext := Extending_All; end if; Parse_Single_Project (In_Tree => In_Tree, Project => Extended_Project, Extends_All => Extends_All, Path_Name_Id => Extended_Project_Path_Name_Id, Extended => True, From_Extended => From_Ext, In_Limited => In_Limited, Packages_To_Check => Packages_To_Check, Depth => Depth + 1, Current_Dir => Current_Dir, Is_Config_File => Is_Config_File, Env => Env); end; if Present (Extended_Project) then if Project_Qualifier_Of (Extended_Project, In_Tree) = Aggregate then Error_Msg_Name_1 := Name_Id (Path_Name_Of (Extended_Project, In_Tree)); Error_Msg (Env.Flags, "cannot extend aggregate project %%", Location_Of (Project, In_Tree)); end if; -- A project that extends an extending-all project is -- also an extending-all project. if Is_Extending_All (Extended_Project, In_Tree) then Set_Is_Extending_All (Project, In_Tree); end if; -- An abstract project can only extend an abstract -- project. Otherwise we may have an abstract project -- with sources if it inherits sources from the project -- it extends. if Project_Qualifier_Of (Project, In_Tree) = Dry and then Project_Qualifier_Of (Extended_Project, In_Tree) /= Dry then Error_Msg (Env.Flags, "an abstract project can only extend " & "another abstract project", Qualifier_Location); end if; end if; end if; end; Scan (In_Tree); -- past the extended project path end if; end if; Check_Extending_All_Imports (Env.Flags, In_Tree, Project); Check_Aggregate_Imports (Env.Flags, In_Tree, Project); Check_Import_Aggregate (Env.Flags, In_Tree, Project); -- Check that a project with a name including a dot either imports -- or extends the project whose name precedes the last dot. if Name_Of_Project /= No_Name then Get_Name_String (Name_Of_Project); else Name_Len := 0; end if; -- Look for the last dot while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop Name_Len := Name_Len - 1; end loop; -- If a dot was found, check if parent project is imported or extended if Name_Len > 0 then Name_Len := Name_Len - 1; declare Parent_Name : constant Name_Id := Name_Find; Parent_Found : Boolean := False; Parent_Node : Project_Node_Id := Empty_Node; With_Clause : Project_Node_Id := First_With_Clause_Of (Project, In_Tree); Imp_Proj_Name : Name_Id; begin -- If there is an extended project, check its name if Present (Extended_Project) then Parent_Node := Extended_Project; Parent_Found := Name_Of (Extended_Project, In_Tree) = Parent_Name; end if; -- If the parent project is not the extended project, -- check each imported project until we find the parent project. Imported_Loop : while not Parent_Found and then Present (With_Clause) loop Parent_Node := Project_Node_Of (With_Clause, In_Tree); Extension_Loop : while Present (Parent_Node) loop Imp_Proj_Name := Name_Of (Parent_Node, In_Tree); Parent_Found := Imp_Proj_Name = Parent_Name; exit Imported_Loop when Parent_Found; Parent_Node := Extended_Project_Of (Project_Declaration_Of (Parent_Node, In_Tree), In_Tree); end loop Extension_Loop; With_Clause := Next_With_Clause_Of (With_Clause, In_Tree); end loop Imported_Loop; if Parent_Found then Set_Parent_Project_Of (Project, In_Tree, To => Parent_Node); else -- If the parent project was not found, report an error Error_Msg_Name_1 := Name_Of_Project; Error_Msg_Name_2 := Parent_Name; Error_Msg (Env.Flags, "project %% does not import or extend project %%", Location_Of (Project, In_Tree)); end if; end; end if; Expect (Tok_Is, "IS"); Set_End_Of_Line (Project); Set_Previous_Line_Node (Project); Set_Next_End_Node (Project); declare Project_Declaration : Project_Node_Id := Empty_Node; begin -- No need to Scan past "is", Prj.Dect.Parse will do it Prj.Dect.Parse (In_Tree => In_Tree, Declarations => Project_Declaration, Current_Project => Project, Extends => Extended_Project, Packages_To_Check => Packages_To_Check, Is_Config_File => Is_Config_File, Flags => Env.Flags); Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration); if Present (Extended_Project) and then Project_Qualifier_Of (Extended_Project, In_Tree) /= Dry then Set_Extending_Project_Of (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree, To => Project); end if; end; Expect (Tok_End, "END"); Remove_Next_End_Node; -- Skip "end" if present if Token = Tok_End then Scan (In_Tree); end if; -- Clear the Buffer Buffer_Last := 0; -- Store the name following "end" in the Buffer. The name may be made of -- several simple names. loop Expect (Tok_Identifier, "identifier"); -- If we don't have an identifier, clear the buffer before exiting to -- avoid checking the name. if Token /= Tok_Identifier then Buffer_Last := 0; exit; end if; -- Add the identifier to the Buffer Get_Name_String (Token_Name); Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last); -- Scan past the identifier Scan (In_Tree); exit when Token /= Tok_Dot; Add_To_Buffer (".", Buffer, Buffer_Last); Scan (In_Tree); end loop; -- If we have a valid name, check if it is the name of the project if Name_Of_Project /= No_Name and then Buffer_Last > 0 then if To_Lower (Buffer (1 .. Buffer_Last)) /= Get_Name_String (Name_Of (Project, In_Tree)) then -- Invalid name: report an error Error_Msg (Env.Flags, "expected """ & Get_Name_String (Name_Of (Project, In_Tree)) & """", Token_Ptr); end if; end if; Expect (Tok_Semicolon, "`;`"); -- Check that there is no more text following the end of the project -- source. if Token = Tok_Semicolon then Set_Previous_End_Node (Project); Scan (In_Tree); if Token /= Tok_EOF then Error_Msg (Env.Flags, "unexpected text following end of project", Token_Ptr); end if; end if; if not Duplicated and then Name_Of_Project /= No_Name then -- Add the name of the project to the hash table, so that we can -- check that no other subsequent project will have the same name. Tree_Private_Part.Projects_Htable.Set (T => In_Tree.Projects_HT, K => Name_Of_Project, E => (Name => Name_Of_Project, Display_Name => Display_Name_Of_Project, Node => Project, Resolved_Path => Resolved_Path_Name, Extended => Extended, From_Extended => From_Extended /= None, Proj_Qualifier => Project_Qualifier_Of (Project, In_Tree))); end if; declare From_Ext : Extension_Origin := None; begin -- Extending_All is always propagated if From_Extended = Extending_All or else Extends_All then From_Ext := Extending_All; -- Otherwise, From_Extended is set to Extending_Single if the -- current project is an extending project. elsif Extended then From_Ext := Extending_Simple; end if; Post_Parse_Context_Clause (In_Tree => In_Tree, Context_Clause => First_With, In_Limited => In_Limited, Limited_Withs => True, Imported_Projects => Imported_Projects, Project_Directory => Project_Directory, From_Extended => From_Ext, Packages_To_Check => Packages_To_Check, Depth => Depth + 1, Current_Dir => Current_Dir, Is_Config_File => Is_Config_File, Env => Env); Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects); end; -- Restore the scan state, in case we are not the main project Restore_Project_Scan_State (Project_Scan_State); -- And remove the project from the project stack Project_Stack.Decrement_Last; -- Indicate if there are unkept comments Tree.Set_Project_File_Includes_Unkept_Comments (Node => Project, In_Tree => In_Tree, To => Tree.There_Are_Unkept_Comments); -- And restore the comment state that was saved Tree.Restore_And_Free (Project_Comment_State); Debug_Decrease_Indent; if Project /= Empty_Node and then Implicit_Project then Name_Len := 0; Add_Str_To_Name_Buffer (Current_Dir); Add_Char_To_Name_Buffer (Dir_Sep); In_Tree.Project_Nodes.Table (Project).Directory := Name_Find; end if; end Parse_Single_Project; ----------------------- -- Project_Name_From -- ----------------------- function Project_Name_From (Path_Name : String; Is_Config_File : Boolean) return Name_Id is Canonical : String (1 .. Path_Name'Length) := Path_Name; First : Natural := Canonical'Last; Last : Natural := First; Index : Positive; begin if Current_Verbosity = High then Debug_Output ("Project_Name_From (""" & Canonical & """)"); end if; -- If the path name is empty, return No_Name to indicate failure if First = 0 then return No_Name; end if; Canonical_Case_File_Name (Canonical); -- Look for the last dot in the path name while First > 0 and then Canonical (First) /= '.' loop First := First - 1; end loop; -- If we have a dot, check that it is followed by the correct extension if First > 0 and then Canonical (First) = '.' then if (not Is_Config_File and then Canonical (First .. Last) = Project_File_Extension and then First /= 1) or else (Is_Config_File and then Canonical (First .. Last) = Config_Project_File_Extension and then First /= 1) then -- Look for the last directory separator, if any First := First - 1; Last := First; while First > 0 and then Canonical (First) /= '/' and then Canonical (First) /= Dir_Sep loop First := First - 1; end loop; else -- Not the correct extension, return No_Name to indicate failure return No_Name; end if; -- If no dot in the path name, return No_Name to indicate failure else return No_Name; end if; First := First + 1; -- If the extension is the file name, return No_Name to indicate failure if First > Last then return No_Name; end if; -- Put the name in lower case into Name_Buffer Name_Len := Last - First + 1; Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last)); Index := 1; -- Check if it is a well formed project name. Return No_Name if it is -- ill formed. loop if not Is_Letter (Name_Buffer (Index)) then return No_Name; else loop Index := Index + 1; exit when Index >= Name_Len; if Name_Buffer (Index) = '_' then if Name_Buffer (Index + 1) = '_' then return No_Name; end if; end if; exit when Name_Buffer (Index) = '-'; if Name_Buffer (Index) /= '_' and then not Is_Alphanumeric (Name_Buffer (Index)) then return No_Name; end if; end loop; end if; if Index >= Name_Len then if Is_Alphanumeric (Name_Buffer (Name_Len)) then -- All checks have succeeded. Return name in Name_Buffer return Name_Find; else return No_Name; end if; elsif Name_Buffer (Index) = '-' then Index := Index + 1; end if; end loop; end Project_Name_From; end Prj.Part;