1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2007, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
27 with Err_Vars; use Err_Vars;
29 with Osint; use Osint;
30 with Output; use Output;
31 with Prj.Com; use Prj.Com;
33 with Prj.Err; use Prj.Err;
34 with Prj.Ext; use Prj.Ext;
35 with Sinput; use Sinput;
36 with Sinput.P; use Sinput.P;
40 with Ada.Characters.Handling; use Ada.Characters.Handling;
41 with Ada.Exceptions; use Ada.Exceptions;
43 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
45 with System.HTable; use System.HTable;
47 package body Prj.Part is
49 Buffer : String_Access;
50 Buffer_Last : Natural := 0;
52 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
54 ------------------------------------
55 -- Local Packages and Subprograms --
56 ------------------------------------
58 type With_Id is new Nat;
59 No_With : constant With_Id := 0;
61 type With_Record is record
62 Path : Path_Name_Type;
63 Location : Source_Ptr;
64 Limited_With : Boolean;
65 Node : Project_Node_Id;
68 -- Information about an imported project, to be put in table Withs below
70 package Withs is new Table.Table
71 (Table_Component_Type => With_Record,
72 Table_Index_Type => With_Id,
75 Table_Increment => 100,
76 Table_Name => "Prj.Part.Withs");
77 -- Table used to store temporarily paths and locations of imported
78 -- projects. These imported projects will be effectively parsed after the
79 -- name of the current project has been extablished.
81 type Names_And_Id is record
82 Path_Name : Path_Name_Type;
83 Canonical_Path_Name : Path_Name_Type;
87 package Project_Stack is new Table.Table
88 (Table_Component_Type => Names_And_Id,
89 Table_Index_Type => Nat,
92 Table_Increment => 100,
93 Table_Name => "Prj.Part.Project_Stack");
94 -- This table is used to detect circular dependencies
95 -- for imported and extended projects and to get the project ids of
96 -- limited imported projects when there is a circularity with at least
97 -- one limited imported project file.
99 package Virtual_Hash is new System.HTable.Simple_HTable
100 (Header_Num => Header_Num,
101 Element => Project_Node_Id,
102 No_Element => Empty_Node,
103 Key => Project_Node_Id,
104 Hash => Prj.Tree.Hash,
106 -- Hash table to store the node id of the project for which a virtual
107 -- extending project need to be created.
109 package Processed_Hash is new System.HTable.Simple_HTable
110 (Header_Num => Header_Num,
113 Key => Project_Node_Id,
114 Hash => Prj.Tree.Hash,
116 -- Hash table to store the project process when looking for project that
117 -- need to have a virtual extending project, to avoid processing the same
120 procedure Create_Virtual_Extending_Project
121 (For_Project : Project_Node_Id;
122 Main_Project : Project_Node_Id;
123 In_Tree : Project_Node_Tree_Ref);
124 -- Create a virtual extending project of For_Project. Main_Project is
125 -- the extending all project.
127 -- The String_Value_Of is not set for the automatically added with
128 -- clause and keeps the default value of No_Name. This enables Prj.PP
129 -- to skip these automatically added with clauses to be processed.
131 procedure Look_For_Virtual_Projects_For
132 (Proj : Project_Node_Id;
133 In_Tree : Project_Node_Tree_Ref;
134 Potentially_Virtual : Boolean);
135 -- Look for projects that need to have a virtual extending project.
136 -- This procedure is recursive. If called with Potentially_Virtual set to
137 -- True, then Proj may need an virtual extending project; otherwise it
138 -- does not (because it is already extended), but other projects that it
139 -- imports may need to be virtually extended.
141 procedure Pre_Parse_Context_Clause
142 (In_Tree : Project_Node_Tree_Ref;
143 Context_Clause : out With_Id);
144 -- Parse the context clause of a project.
145 -- Store the paths and locations of the imported projects in table Withs.
146 -- Does nothing if there is no context clause (if the current
147 -- token is not "with" or "limited" followed by "with").
149 procedure Post_Parse_Context_Clause
150 (Context_Clause : With_Id;
151 In_Tree : Project_Node_Tree_Ref;
152 Imported_Projects : out Project_Node_Id;
153 Project_Directory : Path_Name_Type;
154 From_Extended : Extension_Origin;
155 In_Limited : Boolean;
156 Packages_To_Check : String_List_Access;
158 -- Parse the imported projects that have been stored in table Withs,
159 -- if any. From_Extended is used for the call to Parse_Single_Project
160 -- below. When In_Limited is True, the importing path includes at least
161 -- one "limited with".
163 function Project_Path_Name_Of
164 (Project_File_Name : String;
165 Directory : String) return String;
166 -- Returns the path name of a project file. Returns an empty string
167 -- if project file cannot be found.
169 function Immediate_Directory_Of
170 (Path_Name : Path_Name_Type) return Path_Name_Type;
171 -- Get the directory of the file with the specified path name.
172 -- This includes the directory separator as the last character.
173 -- Returns "./" if Path_Name contains no directory separator.
175 function Project_Name_From (Path_Name : String) return Name_Id;
176 -- Returns the name of the project that corresponds to its path name.
177 -- Returns No_Name if the path name is invalid, because the corresponding
178 -- project name does not have the syntax of an ada identifier.
180 --------------------------------------
181 -- Create_Virtual_Extending_Project --
182 --------------------------------------
184 procedure Create_Virtual_Extending_Project
185 (For_Project : Project_Node_Id;
186 Main_Project : Project_Node_Id;
187 In_Tree : Project_Node_Tree_Ref)
190 Virtual_Name : constant String :=
192 Get_Name_String (Name_Of (For_Project, In_Tree));
193 -- The name of the virtual extending project
195 Virtual_Name_Id : Name_Id;
196 -- Virtual extending project name id
198 Virtual_Path_Id : Path_Name_Type;
199 -- Fake path name of the virtual extending project. The directory is
200 -- the same directory as the extending all project.
202 Virtual_Dir_Id : constant Path_Name_Type :=
203 Immediate_Directory_Of (Path_Name_Of (Main_Project, In_Tree));
204 -- The directory of the extending all project
206 -- The source of the virtual extending project is something like:
208 -- project V$<project name> extends <project path> is
210 -- for Source_Dirs use ();
212 -- end V$<project name>;
214 -- The project directory cannot be specified during parsing; it will be
215 -- put directly in the virtual extending project data during processing.
217 -- Nodes that made up the virtual extending project
219 Virtual_Project : constant Project_Node_Id :=
221 (In_Tree, N_Project);
222 With_Clause : constant Project_Node_Id :=
224 (In_Tree, N_With_Clause);
225 Project_Declaration : constant Project_Node_Id :=
227 (In_Tree, N_Project_Declaration);
228 Source_Dirs_Declaration : constant Project_Node_Id :=
230 (In_Tree, N_Declarative_Item);
231 Source_Dirs_Attribute : constant Project_Node_Id :=
233 (In_Tree, N_Attribute_Declaration, List);
234 Source_Dirs_Expression : constant Project_Node_Id :=
236 (In_Tree, N_Expression, List);
237 Source_Dirs_Term : constant Project_Node_Id :=
239 (In_Tree, N_Term, List);
240 Source_Dirs_List : constant Project_Node_Id :=
242 (In_Tree, N_Literal_String_List, List);
245 -- Get the virtual name id
247 Name_Len := Virtual_Name'Length;
248 Name_Buffer (1 .. Name_Len) := Virtual_Name;
249 Virtual_Name_Id := Name_Find;
251 -- Get the virtual path name
253 Get_Name_String (Path_Name_Of (Main_Project, In_Tree));
256 and then Name_Buffer (Name_Len) /= Directory_Separator
257 and then Name_Buffer (Name_Len) /= '/'
259 Name_Len := Name_Len - 1;
262 Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) :=
264 Name_Len := Name_Len + Virtual_Name'Length;
265 Virtual_Path_Id := Name_Find;
269 Set_Name_Of (With_Clause, In_Tree, Virtual_Name_Id);
270 Set_Path_Name_Of (With_Clause, In_Tree, Virtual_Path_Id);
271 Set_Project_Node_Of (With_Clause, In_Tree, Virtual_Project);
272 Set_Next_With_Clause_Of
273 (With_Clause, In_Tree, First_With_Clause_Of (Main_Project, In_Tree));
274 Set_First_With_Clause_Of (Main_Project, In_Tree, With_Clause);
276 -- Virtual project node
278 Set_Name_Of (Virtual_Project, In_Tree, Virtual_Name_Id);
279 Set_Path_Name_Of (Virtual_Project, In_Tree, Virtual_Path_Id);
281 (Virtual_Project, In_Tree, Location_Of (Main_Project, In_Tree));
282 Set_Directory_Of (Virtual_Project, In_Tree, Virtual_Dir_Id);
283 Set_Project_Declaration_Of
284 (Virtual_Project, In_Tree, Project_Declaration);
285 Set_Extended_Project_Path_Of
286 (Virtual_Project, In_Tree, Path_Name_Of (For_Project, In_Tree));
288 -- Project declaration
290 Set_First_Declarative_Item_Of
291 (Project_Declaration, In_Tree, Source_Dirs_Declaration);
292 Set_Extended_Project_Of (Project_Declaration, In_Tree, For_Project);
294 -- Source_Dirs declaration
296 Set_Current_Item_Node
297 (Source_Dirs_Declaration, In_Tree, Source_Dirs_Attribute);
299 -- Source_Dirs attribute
301 Set_Name_Of (Source_Dirs_Attribute, In_Tree, Snames.Name_Source_Dirs);
303 (Source_Dirs_Attribute, In_Tree, Source_Dirs_Expression);
305 -- Source_Dirs expression
307 Set_First_Term (Source_Dirs_Expression, In_Tree, Source_Dirs_Term);
311 Set_Current_Term (Source_Dirs_Term, In_Tree, Source_Dirs_List);
313 -- Source_Dirs empty list: nothing to do
315 -- Put virtual project into Projects_Htable
317 Prj.Tree.Tree_Private_Part.Projects_Htable.Set
318 (T => In_Tree.Projects_HT,
319 K => Virtual_Name_Id,
320 E => (Name => Virtual_Name_Id,
321 Node => Virtual_Project,
322 Canonical_Path => No_Path,
324 end Create_Virtual_Extending_Project;
326 ----------------------------
327 -- Immediate_Directory_Of --
328 ----------------------------
330 function Immediate_Directory_Of
331 (Path_Name : Path_Name_Type)
332 return Path_Name_Type
335 Get_Name_String (Path_Name);
337 for Index in reverse 1 .. Name_Len loop
338 if Name_Buffer (Index) = '/'
339 or else Name_Buffer (Index) = Dir_Sep
341 -- Remove all chars after last directory separator from name
344 Name_Len := Index - 1;
354 -- There is no directory separator in name. Return "./" or ".\"
357 Name_Buffer (1) := '.';
358 Name_Buffer (2) := Dir_Sep;
360 end Immediate_Directory_Of;
362 -----------------------------------
363 -- Look_For_Virtual_Projects_For --
364 -----------------------------------
366 procedure Look_For_Virtual_Projects_For
367 (Proj : Project_Node_Id;
368 In_Tree : Project_Node_Tree_Ref;
369 Potentially_Virtual : Boolean)
372 Declaration : Project_Node_Id := Empty_Node;
373 -- Node for the project declaration of Proj
375 With_Clause : Project_Node_Id := Empty_Node;
376 -- Node for a with clause of Proj
378 Imported : Project_Node_Id := Empty_Node;
379 -- Node for a project imported by Proj
381 Extended : Project_Node_Id := Empty_Node;
382 -- Node for the eventual project extended by Proj
385 -- Nothing to do if Proj is not defined or if it has already been
388 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
389 -- Make sure the project will not be processed again
391 Processed_Hash.Set (Proj, True);
393 Declaration := Project_Declaration_Of (Proj, In_Tree);
395 if Declaration /= Empty_Node then
396 Extended := Extended_Project_Of (Declaration, In_Tree);
399 -- If this is a project that may need a virtual extending project
400 -- and it is not itself an extending project, put it in the list.
402 if Potentially_Virtual and then Extended = Empty_Node then
403 Virtual_Hash.Set (Proj, Proj);
406 -- Now check the projects it imports
408 With_Clause := First_With_Clause_Of (Proj, In_Tree);
410 while With_Clause /= Empty_Node loop
411 Imported := Project_Node_Of (With_Clause, In_Tree);
413 if Imported /= Empty_Node then
414 Look_For_Virtual_Projects_For
415 (Imported, In_Tree, Potentially_Virtual => True);
418 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
421 -- Check also the eventual project extended by Proj. As this project
422 -- is already extended, call recursively with Potentially_Virtual
425 Look_For_Virtual_Projects_For
426 (Extended, In_Tree, Potentially_Virtual => False);
428 end Look_For_Virtual_Projects_For;
435 (In_Tree : Project_Node_Tree_Ref;
436 Project : out Project_Node_Id;
437 Project_File_Name : String;
438 Always_Errout_Finalize : Boolean;
439 Packages_To_Check : String_List_Access := All_Packages;
440 Store_Comments : Boolean := False)
442 Current_Directory : constant String := Get_Current_Dir;
445 Real_Project_File_Name : String_Access :=
446 Osint.To_Canonical_File_Spec
450 if Real_Project_File_Name = null then
451 Real_Project_File_Name := new String'(Project_File_Name);
454 Project := Empty_Node;
456 if Current_Verbosity >= Medium then
457 Write_Str ("GPR_PROJECT_PATH=""");
458 Write_Str (Project_Path);
463 Path_Name : constant String :=
464 Project_Path_Name_Of (Real_Project_File_Name.all,
465 Directory => Current_Directory);
468 Free (Real_Project_File_Name);
471 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
472 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
474 -- Parse the main project file
476 if Path_Name = "" then
478 ("project file """, Project_File_Name, """ not found");
479 Project := Empty_Node;
486 Extends_All => Dummy,
487 Path_Name => Path_Name,
489 From_Extended => None,
491 Packages_To_Check => Packages_To_Check,
494 -- If Project is an extending-all project, create the eventual
495 -- virtual extending projects and check that there are no illegally
496 -- imported projects.
498 if Project /= Empty_Node
499 and then Is_Extending_All (Project, In_Tree)
501 -- First look for projects that potentially need a virtual
502 -- extending project.
505 Processed_Hash.Reset;
507 -- Mark the extending all project as processed, to avoid checking
508 -- the imported projects in case of a "limited with" on this
509 -- extending all project.
511 Processed_Hash.Set (Project, True);
514 Declaration : constant Project_Node_Id :=
515 Project_Declaration_Of (Project, In_Tree);
517 Look_For_Virtual_Projects_For
518 (Extended_Project_Of (Declaration, In_Tree), In_Tree,
519 Potentially_Virtual => False);
522 -- Now, check the projects directly imported by the main project.
523 -- Remove from the potentially virtual any project extended by one
524 -- of these imported projects. For non extending imported
525 -- projects, check that they do not belong to the project tree of
526 -- the project being "extended-all" by the main project.
529 With_Clause : Project_Node_Id;
530 Imported : Project_Node_Id := Empty_Node;
531 Declaration : Project_Node_Id := Empty_Node;
534 With_Clause := First_With_Clause_Of (Project, In_Tree);
535 while With_Clause /= Empty_Node loop
536 Imported := Project_Node_Of (With_Clause, In_Tree);
538 if Imported /= Empty_Node then
539 Declaration := Project_Declaration_Of (Imported, In_Tree);
541 if Extended_Project_Of (Declaration, In_Tree) /=
546 Extended_Project_Of (Declaration, In_Tree);
547 exit when Imported = Empty_Node;
548 Virtual_Hash.Remove (Imported);
550 Project_Declaration_Of (Imported, In_Tree);
555 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
559 -- Now create all the virtual extending projects
562 Proj : Project_Node_Id := Virtual_Hash.Get_First;
564 while Proj /= Empty_Node loop
565 Create_Virtual_Extending_Project (Proj, Project, In_Tree);
566 Proj := Virtual_Hash.Get_Next;
571 -- If there were any kind of error during the parsing, serious
572 -- or not, then the parsing fails.
574 if Err_Vars.Total_Errors_Detected > 0 then
575 Project := Empty_Node;
578 if Project = Empty_Node or else Always_Errout_Finalize then
588 Write_Line (Exception_Information (X));
589 Write_Str ("Exception ");
590 Write_Str (Exception_Name (X));
591 Write_Line (" raised, while processing project file");
592 Project := Empty_Node;
595 ------------------------------
596 -- Pre_Parse_Context_Clause --
597 ------------------------------
599 procedure Pre_Parse_Context_Clause
600 (In_Tree : Project_Node_Tree_Ref;
601 Context_Clause : out With_Id)
603 Current_With_Clause : With_Id := No_With;
604 Limited_With : Boolean := False;
606 Current_With : With_Record;
608 Current_With_Node : Project_Node_Id := Empty_Node;
611 -- Assume no context clause
613 Context_Clause := No_With;
616 -- If Token is not WITH or LIMITED, there is no context clause, or we
617 -- have exhausted the with clauses.
619 while Token = Tok_With or else Token = Tok_Limited loop
621 Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree);
622 Limited_With := Token = Tok_Limited;
624 if In_Configuration then
626 ("configuration project cannot import " &
627 "other configuration projects",
632 Scan (In_Tree); -- scan past LIMITED
633 Expect (Tok_With, "WITH");
634 exit With_Loop when Token /= Tok_With;
639 Scan (In_Tree); -- scan past WITH or ","
641 Expect (Tok_String_Literal, "literal string");
643 if Token /= Tok_String_Literal then
647 -- Store path and location in table Withs
650 (Path => Path_Name_Type (Token_Name),
651 Location => Token_Ptr,
652 Limited_With => Limited_With,
653 Node => Current_With_Node,
656 Withs.Increment_Last;
657 Withs.Table (Withs.Last) := Current_With;
659 if Current_With_Clause = No_With then
660 Context_Clause := Withs.Last;
663 Withs.Table (Current_With_Clause).Next := Withs.Last;
666 Current_With_Clause := Withs.Last;
670 if Token = Tok_Semicolon then
671 Set_End_Of_Line (Current_With_Node);
672 Set_Previous_Line_Node (Current_With_Node);
674 -- End of (possibly multiple) with clause;
676 Scan (In_Tree); -- scan past the semicolon.
679 elsif Token = Tok_Comma then
680 Set_Is_Not_Last_In_List (Current_With_Node, In_Tree);
683 Error_Msg ("expected comma or semi colon", Token_Ptr);
689 (Of_Kind => N_With_Clause, In_Tree => In_Tree);
692 end Pre_Parse_Context_Clause;
694 -------------------------------
695 -- Post_Parse_Context_Clause --
696 -------------------------------
698 procedure Post_Parse_Context_Clause
699 (Context_Clause : With_Id;
700 In_Tree : Project_Node_Tree_Ref;
701 Imported_Projects : out Project_Node_Id;
702 Project_Directory : Path_Name_Type;
703 From_Extended : Extension_Origin;
704 In_Limited : Boolean;
705 Packages_To_Check : String_List_Access;
708 Current_With_Clause : With_Id := Context_Clause;
710 Current_Project : Project_Node_Id := Empty_Node;
711 Previous_Project : Project_Node_Id := Empty_Node;
712 Next_Project : Project_Node_Id := Empty_Node;
714 Project_Directory_Path : constant String :=
715 Get_Name_String (Project_Directory);
717 Current_With : With_Record;
718 Limited_With : Boolean := False;
719 Extends_All : Boolean := False;
722 Imported_Projects := Empty_Node;
724 while Current_With_Clause /= No_With loop
725 Current_With := Withs.Table (Current_With_Clause);
726 Current_With_Clause := Current_With.Next;
728 Limited_With := In_Limited or Current_With.Limited_With;
731 Original_Path : constant String :=
732 Get_Name_String (Current_With.Path);
734 Imported_Path_Name : constant String :=
736 (Original_Path, Project_Directory_Path);
738 Resolved_Path : constant String :=
741 Resolve_Links => True,
742 Case_Sensitive => True);
744 Withed_Project : Project_Node_Id := Empty_Node;
747 if Imported_Path_Name = "" then
749 -- The project file cannot be found
751 Error_Msg_File_1 := File_Name_Type (Current_With.Path);
753 Error_Msg ("unknown project file: {", Current_With.Location);
755 -- If this is not imported by the main project file,
756 -- display the import path.
758 if Project_Stack.Last > 1 then
759 for Index in reverse 1 .. Project_Stack.Last loop
761 File_Name_Type (Project_Stack.Table (Index).Path_Name);
762 Error_Msg ("\imported by {", Current_With.Location);
769 Previous_Project := Current_Project;
771 if Current_Project = Empty_Node then
773 -- First with clause of the context clause
775 Current_Project := Current_With.Node;
776 Imported_Projects := Current_Project;
779 Next_Project := Current_With.Node;
780 Set_Next_With_Clause_Of
781 (Current_Project, In_Tree, Next_Project);
782 Current_Project := Next_Project;
786 (Current_Project, In_Tree, Name_Id (Current_With.Path));
788 (Current_Project, In_Tree, Current_With.Location);
790 -- If this is a "limited with", check if we have a circularity.
791 -- If we have one, get the project id of the limited imported
792 -- project file, and do not parse it.
794 if Limited_With and then Project_Stack.Last > 1 then
796 Canonical_Path_Name : Path_Name_Type;
799 Name_Len := Resolved_Path'Length;
800 Name_Buffer (1 .. Name_Len) := Resolved_Path;
801 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
802 Canonical_Path_Name := Name_Find;
804 for Index in 1 .. Project_Stack.Last loop
805 if Project_Stack.Table (Index).Canonical_Path_Name =
808 -- We have found the limited imported project,
809 -- get its project id, and do not parse it.
811 Withed_Project := Project_Stack.Table (Index).Id;
818 -- Parse the imported project, if its project id is unknown
820 if Withed_Project = Empty_Node then
823 Project => Withed_Project,
824 Extends_All => Extends_All,
825 Path_Name => Imported_Path_Name,
827 From_Extended => From_Extended,
828 In_Limited => Limited_With,
829 Packages_To_Check => Packages_To_Check,
833 Extends_All := Is_Extending_All (Withed_Project, In_Tree);
836 if Withed_Project = Empty_Node then
837 -- If parsing was not successful, remove the
840 Current_Project := Previous_Project;
842 if Current_Project = Empty_Node then
843 Imported_Projects := Empty_Node;
846 Set_Next_With_Clause_Of
847 (Current_Project, In_Tree, Empty_Node);
850 -- If parsing was successful, record project name
851 -- and path name in with clause
854 (Node => Current_Project,
856 To => Withed_Project,
857 Limited_With => Current_With.Limited_With);
861 Name_Of (Withed_Project, In_Tree));
863 Name_Len := Resolved_Path'Length;
864 Name_Buffer (1 .. Name_Len) := Resolved_Path;
865 Set_Path_Name_Of (Current_Project, In_Tree, Name_Find);
868 Set_Is_Extending_All (Current_Project, In_Tree);
874 end Post_Parse_Context_Clause;
876 --------------------------
877 -- Parse_Single_Project --
878 --------------------------
880 procedure Parse_Single_Project
881 (In_Tree : Project_Node_Tree_Ref;
882 Project : out Project_Node_Id;
883 Extends_All : out Boolean;
886 From_Extended : Extension_Origin;
887 In_Limited : Boolean;
888 Packages_To_Check : String_List_Access;
891 Normed_Path_Name : Path_Name_Type;
892 Canonical_Path_Name : Path_Name_Type;
893 Project_Directory : Path_Name_Type;
894 Project_Scan_State : Saved_Project_Scan_State;
895 Source_Index : Source_File_Index;
897 Extending : Boolean := False;
899 Extended_Project : Project_Node_Id := Empty_Node;
901 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
902 Tree_Private_Part.Projects_Htable.Get_First
903 (In_Tree.Projects_HT);
905 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
907 Name_Of_Project : Name_Id := No_Name;
909 First_With : With_Id;
911 use Tree_Private_Part;
913 Project_Comment_State : Tree.Comment_State;
916 Extends_All := False;
919 Normed_Path : constant String := Normalize_Pathname
920 (Path_Name, Resolve_Links => False,
921 Case_Sensitive => True);
922 Canonical_Path : constant String := Normalize_Pathname
923 (Normed_Path, Resolve_Links => True,
924 Case_Sensitive => False);
927 Name_Len := Normed_Path'Length;
928 Name_Buffer (1 .. Name_Len) := Normed_Path;
929 Normed_Path_Name := Name_Find;
930 Name_Len := Canonical_Path'Length;
931 Name_Buffer (1 .. Name_Len) := Canonical_Path;
932 Canonical_Path_Name := Name_Find;
935 -- Check for a circular dependency
937 for Index in 1 .. Project_Stack.Last loop
938 if Canonical_Path_Name =
939 Project_Stack.Table (Index).Canonical_Path_Name
941 Error_Msg ("circular dependency detected", Token_Ptr);
942 Error_Msg_Name_1 := Name_Id (Normed_Path_Name);
943 Error_Msg ("\ %% is imported by", Token_Ptr);
945 for Current in reverse 1 .. Project_Stack.Last loop
947 Name_Id (Project_Stack.Table (Current).Path_Name);
949 if Project_Stack.Table (Current).Canonical_Path_Name /=
953 ("\ %% which itself is imported by", Token_Ptr);
956 Error_Msg ("\ %%", Token_Ptr);
961 Project := Empty_Node;
966 -- Put the new path name on the stack
968 Project_Stack.Increment_Last;
969 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
970 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
973 -- Check if the project file has already been parsed
976 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
978 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
981 if A_Project_Name_And_Node.Extended then
983 ("cannot extend the same project file several times",
987 ("cannot extend an already imported project file",
991 elsif A_Project_Name_And_Node.Extended then
993 Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree);
995 -- If the imported project is an extended project A,
996 -- and we are in an extended project, replace A with the
997 -- ultimate project extending A.
999 if From_Extended /= None then
1001 Decl : Project_Node_Id :=
1002 Project_Declaration_Of
1003 (A_Project_Name_And_Node.Node, In_Tree);
1005 Prj : Project_Node_Id :=
1006 Extending_Project_Of (Decl, In_Tree);
1010 Decl := Project_Declaration_Of (Prj, In_Tree);
1011 exit when Extending_Project_Of (Decl, In_Tree) =
1013 Prj := Extending_Project_Of (Decl, In_Tree);
1016 A_Project_Name_And_Node.Node := Prj;
1020 ("cannot import an already extended project file",
1025 Project := A_Project_Name_And_Node.Node;
1026 Project_Stack.Decrement_Last;
1030 A_Project_Name_And_Node :=
1031 Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT);
1034 -- We never encountered this project file
1035 -- Save the scan state, load the project file and start to scan it.
1037 Save_Project_Scan_State (Project_Scan_State);
1038 Source_Index := Load_Project_File (Path_Name);
1039 Tree.Save (Project_Comment_State);
1041 -- If we cannot find it, we stop
1043 if Source_Index = No_Source_File then
1044 Project := Empty_Node;
1045 Project_Stack.Decrement_Last;
1049 Prj.Err.Scanner.Initialize_Scanner (Source_Index);
1053 if (not In_Configuration) and then (Name_From_Path = No_Name) then
1055 -- The project file name is not correct (no or bad extension,
1056 -- or not following Ada identifier's syntax).
1058 Error_Msg_File_1 := File_Name_Type (Canonical_Path_Name);
1060 if In_Configuration then
1061 Error_Msg ("{ is not a valid path name for a configuration " &
1066 Error_Msg ("?{ is not a valid path name for a project file",
1071 if Current_Verbosity >= Medium then
1072 Write_Str ("Parsing """);
1073 Write_Str (Path_Name);
1078 -- Is there any imported project?
1080 Pre_Parse_Context_Clause (In_Tree, First_With);
1082 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1083 Project := Default_Project_Node
1084 (Of_Kind => N_Project, In_Tree => In_Tree);
1085 Project_Stack.Table (Project_Stack.Last).Id := Project;
1086 Set_Directory_Of (Project, In_Tree, Project_Directory);
1087 Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name);
1088 Set_Location_Of (Project, In_Tree, Token_Ptr);
1090 Expect (Tok_Project, "PROJECT");
1092 -- Mark location of PROJECT token if present
1094 if Token = Tok_Project then
1095 Scan (In_Tree); -- scan past PROJECT
1096 Set_Location_Of (Project, In_Tree, Token_Ptr);
1103 Expect (Tok_Identifier, "identifier");
1105 -- If the token is not an identifier, clear the buffer before
1106 -- exiting to indicate that the name of the project is ill-formed.
1108 if Token /= Tok_Identifier then
1113 -- Add the identifier name to the buffer
1115 Get_Name_String (Token_Name);
1116 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1118 -- Scan past the identifier
1122 -- If we have a dot, add a dot to the Buffer and look for the next
1125 exit when Token /= Tok_Dot;
1126 Add_To_Buffer (".", Buffer, Buffer_Last);
1128 -- Scan past the dot
1133 -- See if this is an extending project
1135 if Token = Tok_Extends then
1137 if In_Configuration then
1139 ("extending configuration project not allowed", Token_Ptr);
1142 -- Make sure that gnatmake will use mapping files
1144 Create_Mapping_File := True;
1146 -- We are extending another project
1150 Scan (In_Tree); -- scan past EXTENDS
1152 if Token = Tok_All then
1153 Extends_All := True;
1154 Set_Is_Extending_All (Project, In_Tree);
1155 Scan (In_Tree); -- scan past ALL
1159 -- If the name is well formed, Buffer_Last is > 0
1161 if Buffer_Last > 0 then
1163 -- The Buffer contains the name of the project
1165 Name_Len := Buffer_Last;
1166 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1167 Name_Of_Project := Name_Find;
1168 Set_Name_Of (Project, In_Tree, Name_Of_Project);
1170 -- To get expected name of the project file, replace dots by dashes
1172 Name_Len := Buffer_Last;
1173 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1175 for Index in 1 .. Name_Len loop
1176 if Name_Buffer (Index) = '.' then
1177 Name_Buffer (Index) := '-';
1181 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1184 Expected_Name : constant Name_Id := Name_Find;
1185 Extension : String_Access;
1188 -- Output a warning if the actual name is not the expected name
1190 if (not In_Configuration)
1191 and then (Name_From_Path /= No_Name)
1192 and then Expected_Name /= Name_From_Path
1194 Error_Msg_Name_1 := Expected_Name;
1196 if In_Configuration then
1197 Extension := new String'(Config_Project_File_Extension);
1200 Extension := new String'(Project_File_Extension);
1203 Error_Msg ("?file name does not match project name, " &
1204 "should be `%%" & Extension.all & "`",
1210 Imported_Projects : Project_Node_Id := Empty_Node;
1211 From_Ext : Extension_Origin := None;
1214 -- Extending_All is always propagated
1216 if From_Extended = Extending_All or else Extends_All then
1217 From_Ext := Extending_All;
1219 -- Otherwise, From_Extended is set to Extending_Single if the
1220 -- current project is an extending project.
1223 From_Ext := Extending_Simple;
1226 Post_Parse_Context_Clause
1227 (In_Tree => In_Tree,
1228 Context_Clause => First_With,
1229 Imported_Projects => Imported_Projects,
1230 Project_Directory => Project_Directory,
1231 From_Extended => From_Ext,
1232 In_Limited => In_Limited,
1233 Packages_To_Check => Packages_To_Check,
1234 Depth => Depth + 1);
1235 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1239 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1240 Tree_Private_Part.Projects_Htable.Get_First
1241 (In_Tree.Projects_HT);
1242 Project_Name : Name_Id := Name_And_Node.Name;
1245 -- Check if we already have a project with this name
1247 while Project_Name /= No_Name
1248 and then Project_Name /= Name_Of_Project
1251 Tree_Private_Part.Projects_Htable.Get_Next
1252 (In_Tree.Projects_HT);
1253 Project_Name := Name_And_Node.Name;
1256 -- Report an error if we already have a project with this name
1258 if Project_Name /= No_Name then
1259 Error_Msg_Name_1 := Project_Name;
1261 ("duplicate project name %%", Location_Of (Project, In_Tree));
1263 Name_Id (Path_Name_Of (Name_And_Node.Node, In_Tree));
1265 ("\already in %%", Location_Of (Project, In_Tree));
1268 -- Otherwise, add the name of the project to the hash table, so
1269 -- that we can check that no other subsequent project will have
1272 Tree_Private_Part.Projects_Htable.Set
1273 (T => In_Tree.Projects_HT,
1274 K => Name_Of_Project,
1275 E => (Name => Name_Of_Project,
1277 Canonical_Path => Canonical_Path_Name,
1278 Extended => Extended));
1285 Expect (Tok_String_Literal, "literal string");
1287 if Token = Tok_String_Literal then
1288 Set_Extended_Project_Path_Of
1291 Path_Name_Type (Token_Name));
1294 Original_Path_Name : constant String :=
1295 Get_Name_String (Token_Name);
1297 Extended_Project_Path_Name : constant String :=
1298 Project_Path_Name_Of
1299 (Original_Path_Name,
1301 (Project_Directory));
1304 if Extended_Project_Path_Name = "" then
1306 -- We could not find the project file to extend
1308 Error_Msg_Name_1 := Token_Name;
1310 Error_Msg ("unknown project file: %%", Token_Ptr);
1312 -- If we are not in the main project file, display the
1315 if Project_Stack.Last > 1 then
1318 (Project_Stack.Table (Project_Stack.Last).Path_Name);
1319 Error_Msg ("\extended by %%", Token_Ptr);
1321 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1324 (Project_Stack.Table (Index).Path_Name);
1325 Error_Msg ("\imported by %%", Token_Ptr);
1331 From_Ext : Extension_Origin := None;
1334 if From_Extended = Extending_All or else Extends_All then
1335 From_Ext := Extending_All;
1338 Parse_Single_Project
1339 (In_Tree => In_Tree,
1340 Project => Extended_Project,
1341 Extends_All => Extends_All,
1342 Path_Name => Extended_Project_Path_Name,
1344 From_Extended => From_Ext,
1345 In_Limited => In_Limited,
1346 Packages_To_Check => Packages_To_Check,
1347 Depth => Depth + 1);
1350 -- A project that extends an extending-all project is also
1351 -- an extending-all project.
1353 if Extended_Project /= Empty_Node
1354 and then Is_Extending_All (Extended_Project, In_Tree)
1356 Set_Is_Extending_All (Project, In_Tree);
1361 Scan (In_Tree); -- scan past the extended project path
1365 -- Check that a non extending-all project does not import an
1366 -- extending-all project.
1368 if not Is_Extending_All (Project, In_Tree) then
1370 With_Clause : Project_Node_Id :=
1371 First_With_Clause_Of (Project, In_Tree);
1372 Imported : Project_Node_Id := Empty_Node;
1376 while With_Clause /= Empty_Node loop
1377 Imported := Project_Node_Of (With_Clause, In_Tree);
1379 if Is_Extending_All (With_Clause, In_Tree) then
1380 Error_Msg_Name_1 := Name_Of (Imported, In_Tree);
1381 Error_Msg ("cannot import extending-all project %%",
1383 exit With_Clause_Loop;
1386 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1387 end loop With_Clause_Loop;
1391 -- Check that a project with a name including a dot either imports
1392 -- or extends the project whose name precedes the last dot.
1394 if Name_Of_Project /= No_Name then
1395 Get_Name_String (Name_Of_Project);
1401 -- Look for the last dot
1403 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1404 Name_Len := Name_Len - 1;
1407 -- If a dot was find, check if the parent project is imported
1410 if Name_Len > 0 then
1411 Name_Len := Name_Len - 1;
1414 Parent_Name : constant Name_Id := Name_Find;
1415 Parent_Found : Boolean := False;
1416 With_Clause : Project_Node_Id :=
1417 First_With_Clause_Of (Project, In_Tree);
1420 -- If there is an extended project, check its name
1422 if Extended_Project /= Empty_Node then
1424 Name_Of (Extended_Project, In_Tree) = Parent_Name;
1427 -- If the parent project is not the extended project,
1428 -- check each imported project until we find the parent project.
1430 while not Parent_Found and then With_Clause /= Empty_Node loop
1432 Name_Of (Project_Node_Of (With_Clause, In_Tree), In_Tree) =
1434 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1437 -- If the parent project was not found, report an error
1439 if not Parent_Found then
1440 Error_Msg_Name_1 := Name_Of_Project;
1441 Error_Msg_Name_2 := Parent_Name;
1442 Error_Msg ("project %% does not import or extend project %%",
1443 Location_Of (Project, In_Tree));
1448 Expect (Tok_Is, "IS");
1449 Set_End_Of_Line (Project);
1450 Set_Previous_Line_Node (Project);
1451 Set_Next_End_Node (Project);
1454 Project_Declaration : Project_Node_Id := Empty_Node;
1457 -- No need to Scan past "is", Prj.Dect.Parse will do it
1460 (In_Tree => In_Tree,
1461 Declarations => Project_Declaration,
1462 Current_Project => Project,
1463 Extends => Extended_Project,
1464 Packages_To_Check => Packages_To_Check);
1465 Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration);
1467 if Extended_Project /= Empty_Node then
1468 Set_Extending_Project_Of
1469 (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree,
1474 Expect (Tok_End, "END");
1475 Remove_Next_End_Node;
1477 -- Skip "end" if present
1479 if Token = Tok_End then
1487 -- Store the name following "end" in the Buffer. The name may be made of
1488 -- several simple names.
1491 Expect (Tok_Identifier, "identifier");
1493 -- If we don't have an identifier, clear the buffer before exiting to
1494 -- avoid checking the name.
1496 if Token /= Tok_Identifier then
1501 -- Add the identifier to the Buffer
1502 Get_Name_String (Token_Name);
1503 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1505 -- Scan past the identifier
1508 exit when Token /= Tok_Dot;
1509 Add_To_Buffer (".", Buffer, Buffer_Last);
1513 -- If we have a valid name, check if it is the name of the project
1515 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1516 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1517 Get_Name_String (Name_Of (Project, In_Tree))
1519 -- Invalid name: report an error
1521 Error_Msg ("expected """ &
1522 Get_Name_String (Name_Of (Project, In_Tree)) & """",
1527 Expect (Tok_Semicolon, "`;`");
1529 -- Check that there is no more text following the end of the project
1532 if Token = Tok_Semicolon then
1533 Set_Previous_End_Node (Project);
1536 if Token /= Tok_EOF then
1538 ("unexpected text following end of project", Token_Ptr);
1542 -- Restore the scan state, in case we are not the main project
1544 Restore_Project_Scan_State (Project_Scan_State);
1546 -- And remove the project from the project stack
1548 Project_Stack.Decrement_Last;
1550 -- Indicate if there are unkept comments
1552 Tree.Set_Project_File_Includes_Unkept_Comments
1555 To => Tree.There_Are_Unkept_Comments);
1557 -- And restore the comment state that was saved
1559 Tree.Restore (Project_Comment_State);
1560 end Parse_Single_Project;
1562 -----------------------
1563 -- Project_Name_From --
1564 -----------------------
1566 function Project_Name_From (Path_Name : String) return Name_Id is
1567 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1568 First : Natural := Canonical'Last;
1569 Last : Natural := First;
1573 if Current_Verbosity = High then
1574 Write_Str ("Project_Name_From (""");
1575 Write_Str (Canonical);
1579 -- If the path name is empty, return No_Name to indicate failure
1585 Canonical_Case_File_Name (Canonical);
1587 -- Look for the last dot in the path name
1591 Canonical (First) /= '.'
1596 -- If we have a dot, check that it is followed by the correct extension
1598 if First > 0 and then Canonical (First) = '.' then
1599 if ((not In_Configuration) and then
1600 Canonical (First .. Last) = Project_File_Extension and then
1603 (In_Configuration and then
1604 Canonical (First .. Last) = Config_Project_File_Extension and then
1607 -- Look for the last directory separator, if any
1613 and then Canonical (First) /= '/'
1614 and then Canonical (First) /= Dir_Sep
1620 -- Not the correct extension, return No_Name to indicate failure
1625 -- If no dot in the path name, return No_Name to indicate failure
1633 -- If the extension is the file name, return No_Name to indicate failure
1635 if First > Last then
1639 -- Put the name in lower case into Name_Buffer
1641 Name_Len := Last - First + 1;
1642 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1646 -- Check if it is a well formed project name. Return No_Name if it is
1650 if not Is_Letter (Name_Buffer (Index)) then
1657 exit when Index >= Name_Len;
1659 if Name_Buffer (Index) = '_' then
1660 if Name_Buffer (Index + 1) = '_' then
1665 exit when Name_Buffer (Index) = '-';
1667 if Name_Buffer (Index) /= '_'
1668 and then not Is_Alphanumeric (Name_Buffer (Index))
1676 if Index >= Name_Len then
1677 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1679 -- All checks have succeeded. Return name in Name_Buffer
1687 elsif Name_Buffer (Index) = '-' then
1691 end Project_Name_From;
1693 --------------------------
1694 -- Project_Path_Name_Of --
1695 --------------------------
1697 function Project_Path_Name_Of
1698 (Project_File_Name : String;
1699 Directory : String) return String
1701 Result : String_Access;
1704 if Current_Verbosity = High then
1705 Write_Str ("Project_Path_Name_Of (""");
1706 Write_Str (Project_File_Name);
1707 Write_Str (""", """);
1708 Write_Str (Directory);
1709 Write_Line (""");");
1712 if not Is_Absolute_Path (Project_File_Name) then
1713 -- First we try <directory>/<file_name>.<extension>
1715 if Current_Verbosity = High then
1716 Write_Str (" Trying ");
1717 Write_Str (Directory);
1718 Write_Char (Directory_Separator);
1719 Write_Str (Project_File_Name);
1720 Write_Line (Project_File_Extension);
1725 (File_Name => Directory & Directory_Separator &
1726 Project_File_Name & Project_File_Extension,
1727 Path => Project_Path);
1729 -- Then we try <directory>/<file_name>
1731 if Result = null then
1732 if Current_Verbosity = High then
1733 Write_Str (" Trying ");
1734 Write_Str (Directory);
1735 Write_Char (Directory_Separator);
1736 Write_Line (Project_File_Name);
1741 (File_Name => Directory & Directory_Separator &
1743 Path => Project_Path);
1747 if Result = null then
1749 -- Then we try <file_name>.<extension>
1751 if Current_Verbosity = High then
1752 Write_Str (" Trying ");
1753 Write_Str (Project_File_Name);
1754 Write_Line (Project_File_Extension);
1759 (File_Name => Project_File_Name & Project_File_Extension,
1760 Path => Project_Path);
1763 if Result = null then
1765 -- Then we try <file_name>
1767 if Current_Verbosity = High then
1768 Write_Str (" Trying ");
1769 Write_Line (Project_File_Name);
1774 (File_Name => Project_File_Name,
1775 Path => Project_Path);
1778 -- If we cannot find the project file, we return an empty string
1780 if Result = null then
1785 Final_Result : constant String :=
1786 GNAT.OS_Lib.Normalize_Pathname
1788 Resolve_Links => False,
1789 Case_Sensitive => True);
1792 return Final_Result;
1795 end Project_Path_Name_Of;