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 type Extension_Origin is (None, Extending_Simple, Extending_All);
55 -- Type of parameter From_Extended for procedures Parse_Single_Project and
56 -- Post_Parse_Context_Clause. Extending_All means that we are parsing the
57 -- tree rooted at an extending all project.
59 ------------------------------------
60 -- Local Packages and Subprograms --
61 ------------------------------------
63 type With_Id is new Nat;
64 No_With : constant With_Id := 0;
66 type With_Record is record
67 Path : File_Name_Type;
68 Location : Source_Ptr;
69 Limited_With : Boolean;
70 Node : Project_Node_Id;
73 -- Information about an imported project, to be put in table Withs below
75 package Withs is new Table.Table
76 (Table_Component_Type => With_Record,
77 Table_Index_Type => With_Id,
80 Table_Increment => 100,
81 Table_Name => "Prj.Part.Withs");
82 -- Table used to store temporarily paths and locations of imported
83 -- projects. These imported projects will be effectively parsed after the
84 -- name of the current project has been extablished.
86 type Names_And_Id is record
87 Path_Name : Path_Name_Type;
88 Canonical_Path_Name : Path_Name_Type;
91 -- Needs a comment ???
93 package Project_Stack is new Table.Table
94 (Table_Component_Type => Names_And_Id,
95 Table_Index_Type => Nat,
98 Table_Increment => 100,
99 Table_Name => "Prj.Part.Project_Stack");
100 -- This table is used to detect circular dependencies
101 -- for imported and extended projects and to get the project ids of
102 -- limited imported projects when there is a circularity with at least
103 -- one limited imported project file.
105 package Virtual_Hash is new System.HTable.Simple_HTable
106 (Header_Num => Header_Num,
107 Element => Project_Node_Id,
108 No_Element => Empty_Node,
109 Key => Project_Node_Id,
110 Hash => Prj.Tree.Hash,
112 -- Hash table to store the node id of the project for which a virtual
113 -- extending project need to be created.
115 package Processed_Hash is new System.HTable.Simple_HTable
116 (Header_Num => Header_Num,
119 Key => Project_Node_Id,
120 Hash => Prj.Tree.Hash,
122 -- Hash table to store the project process when looking for project that
123 -- need to have a virtual extending project, to avoid processing the same
126 procedure Create_Virtual_Extending_Project
127 (For_Project : Project_Node_Id;
128 Main_Project : Project_Node_Id;
129 In_Tree : Project_Node_Tree_Ref);
130 -- Create a virtual extending project of For_Project. Main_Project is
131 -- the extending all project.
133 -- The String_Value_Of is not set for the automatically added with
134 -- clause and keeps the default value of No_Name. This enables Prj.PP
135 -- to skip these automatically added with clauses to be processed.
137 procedure Look_For_Virtual_Projects_For
138 (Proj : Project_Node_Id;
139 In_Tree : Project_Node_Tree_Ref;
140 Potentially_Virtual : Boolean);
141 -- Look for projects that need to have a virtual extending project.
142 -- This procedure is recursive. If called with Potentially_Virtual set to
143 -- True, then Proj may need an virtual extending project; otherwise it
144 -- does not (because it is already extended), but other projects that it
145 -- imports may need to be virtually extended.
147 procedure Pre_Parse_Context_Clause
148 (In_Tree : Project_Node_Tree_Ref;
149 Context_Clause : out With_Id);
150 -- Parse the context clause of a project.
151 -- Store the paths and locations of the imported projects in table Withs.
152 -- Does nothing if there is no context clause (if the current
153 -- token is not "with" or "limited" followed by "with").
155 procedure Post_Parse_Context_Clause
156 (Context_Clause : With_Id;
157 In_Tree : Project_Node_Tree_Ref;
158 Imported_Projects : out Project_Node_Id;
159 Project_Directory : Path_Name_Type;
160 From_Extended : Extension_Origin;
161 In_Limited : Boolean;
162 Packages_To_Check : String_List_Access);
163 -- Parse the imported projects that have been stored in table Withs,
164 -- if any. From_Extended is used for the call to Parse_Single_Project
165 -- below. When In_Limited is True, the importing path includes at least
166 -- one "limited with".
168 procedure Parse_Single_Project
169 (In_Tree : Project_Node_Tree_Ref;
170 Project : out Project_Node_Id;
171 Extends_All : out Boolean;
174 From_Extended : Extension_Origin;
175 In_Limited : Boolean;
176 Packages_To_Check : String_List_Access);
177 -- Parse a project file.
178 -- Recursive procedure: it calls itself for imported and extended
179 -- projects. When From_Extended is not None, if the project has already
180 -- been parsed and is an extended project A, return the ultimate
181 -- (not extended) project that extends A. When In_Limited is True,
182 -- the importing path includes at least one "limited with".
184 function Project_Path_Name_Of
185 (Project_File_Name : String;
186 Directory : String) return String;
187 -- Returns the path name of a project file. Returns an empty string
188 -- if project file cannot be found.
190 function Immediate_Directory_Of
191 (Path_Name : Path_Name_Type) return Path_Name_Type;
192 -- Get the directory of the file with the specified path name.
193 -- This includes the directory separator as the last character.
194 -- Returns "./" if Path_Name contains no directory separator.
196 function Project_Name_From (Path_Name : String) return File_Name_Type;
197 -- Returns the name of the project that corresponds to its path name.
198 -- Returns No_Name if the path name is invalid, because the corresponding
199 -- project name does not have the syntax of an ada identifier.
201 --------------------------------------
202 -- Create_Virtual_Extending_Project --
203 --------------------------------------
205 procedure Create_Virtual_Extending_Project
206 (For_Project : Project_Node_Id;
207 Main_Project : Project_Node_Id;
208 In_Tree : Project_Node_Tree_Ref)
211 Virtual_Name : constant String :=
213 Get_Name_String (Name_Of (For_Project, In_Tree));
214 -- The name of the virtual extending project
216 Virtual_Name_Id : Name_Id;
217 -- Virtual extending project name id
219 Virtual_Path_Id : Path_Name_Type;
220 -- Fake path name of the virtual extending project. The directory is
221 -- the same directory as the extending all project.
223 Virtual_Dir_Id : constant Path_Name_Type :=
224 Immediate_Directory_Of (Path_Name_Of (Main_Project, In_Tree));
225 -- The directory of the extending all project
227 -- The source of the virtual extending project is something like:
229 -- project V$<project name> extends <project path> is
231 -- for Source_Dirs use ();
233 -- end V$<project name>;
235 -- The project directory cannot be specified during parsing; it will be
236 -- put directly in the virtual extending project data during processing.
238 -- Nodes that made up the virtual extending project
240 Virtual_Project : constant Project_Node_Id :=
242 (In_Tree, N_Project);
243 With_Clause : constant Project_Node_Id :=
245 (In_Tree, N_With_Clause);
246 Project_Declaration : constant Project_Node_Id :=
248 (In_Tree, N_Project_Declaration);
249 Source_Dirs_Declaration : constant Project_Node_Id :=
251 (In_Tree, N_Declarative_Item);
252 Source_Dirs_Attribute : constant Project_Node_Id :=
254 (In_Tree, N_Attribute_Declaration, List);
255 Source_Dirs_Expression : constant Project_Node_Id :=
257 (In_Tree, N_Expression, List);
258 Source_Dirs_Term : constant Project_Node_Id :=
260 (In_Tree, N_Term, List);
261 Source_Dirs_List : constant Project_Node_Id :=
263 (In_Tree, N_Literal_String_List, List);
266 -- Get the virtual name id
268 Name_Len := Virtual_Name'Length;
269 Name_Buffer (1 .. Name_Len) := Virtual_Name;
270 Virtual_Name_Id := Name_Find;
272 -- Get the virtual path name
274 Get_Name_String (Path_Name_Of (Main_Project, In_Tree));
277 and then Name_Buffer (Name_Len) /= Directory_Separator
278 and then Name_Buffer (Name_Len) /= '/'
280 Name_Len := Name_Len - 1;
283 Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) :=
285 Name_Len := Name_Len + Virtual_Name'Length;
286 Virtual_Path_Id := Name_Find;
290 Set_Name_Of (With_Clause, In_Tree, Virtual_Name_Id);
291 Set_Path_Name_Of (With_Clause, In_Tree, Virtual_Path_Id);
292 Set_Project_Node_Of (With_Clause, In_Tree, Virtual_Project);
293 Set_Next_With_Clause_Of
294 (With_Clause, In_Tree, First_With_Clause_Of (Main_Project, In_Tree));
295 Set_First_With_Clause_Of (Main_Project, In_Tree, With_Clause);
297 -- Virtual project node
299 Set_Name_Of (Virtual_Project, In_Tree, Virtual_Name_Id);
300 Set_Path_Name_Of (Virtual_Project, In_Tree, Virtual_Path_Id);
302 (Virtual_Project, In_Tree, Location_Of (Main_Project, In_Tree));
303 Set_Directory_Of (Virtual_Project, In_Tree, Virtual_Dir_Id);
304 Set_Project_Declaration_Of
305 (Virtual_Project, In_Tree, Project_Declaration);
306 Set_Extended_Project_Path_Of
307 (Virtual_Project, In_Tree, Path_Name_Of (For_Project, In_Tree));
309 -- Project declaration
311 Set_First_Declarative_Item_Of
312 (Project_Declaration, In_Tree, Source_Dirs_Declaration);
313 Set_Extended_Project_Of (Project_Declaration, In_Tree, For_Project);
315 -- Source_Dirs declaration
317 Set_Current_Item_Node
318 (Source_Dirs_Declaration, In_Tree, Source_Dirs_Attribute);
320 -- Source_Dirs attribute
322 Set_Name_Of (Source_Dirs_Attribute, In_Tree, Snames.Name_Source_Dirs);
324 (Source_Dirs_Attribute, In_Tree, Source_Dirs_Expression);
326 -- Source_Dirs expression
328 Set_First_Term (Source_Dirs_Expression, In_Tree, Source_Dirs_Term);
332 Set_Current_Term (Source_Dirs_Term, In_Tree, Source_Dirs_List);
334 -- Source_Dirs empty list: nothing to do
336 -- Put virtual project into Projects_Htable
338 Prj.Tree.Tree_Private_Part.Projects_Htable.Set
339 (T => In_Tree.Projects_HT,
340 K => Virtual_Name_Id,
341 E => (Name => Virtual_Name_Id,
342 Node => Virtual_Project,
343 Canonical_Path => No_Path,
345 end Create_Virtual_Extending_Project;
347 ----------------------------
348 -- Immediate_Directory_Of --
349 ----------------------------
351 function Immediate_Directory_Of
352 (Path_Name : Path_Name_Type) return Path_Name_Type
355 Get_Name_String (Path_Name);
357 for Index in reverse 1 .. Name_Len loop
358 if Name_Buffer (Index) = '/'
359 or else Name_Buffer (Index) = Dir_Sep
361 -- Remove all chars after last directory separator from name
364 Name_Len := Index - 1;
374 -- There is no directory separator in name. Return "./" or ".\"
377 Name_Buffer (1) := '.';
378 Name_Buffer (2) := Dir_Sep;
380 end Immediate_Directory_Of;
382 -----------------------------------
383 -- Look_For_Virtual_Projects_For --
384 -----------------------------------
386 procedure Look_For_Virtual_Projects_For
387 (Proj : Project_Node_Id;
388 In_Tree : Project_Node_Tree_Ref;
389 Potentially_Virtual : Boolean)
392 Declaration : Project_Node_Id := Empty_Node;
393 -- Node for the project declaration of Proj
395 With_Clause : Project_Node_Id := Empty_Node;
396 -- Node for a with clause of Proj
398 Imported : Project_Node_Id := Empty_Node;
399 -- Node for a project imported by Proj
401 Extended : Project_Node_Id := Empty_Node;
402 -- Node for the eventual project extended by Proj
405 -- Nothing to do if Proj is not defined or if it has already been
408 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
409 -- Make sure the project will not be processed again
411 Processed_Hash.Set (Proj, True);
413 Declaration := Project_Declaration_Of (Proj, In_Tree);
415 if Declaration /= Empty_Node then
416 Extended := Extended_Project_Of (Declaration, In_Tree);
419 -- If this is a project that may need a virtual extending project
420 -- and it is not itself an extending project, put it in the list.
422 if Potentially_Virtual and then Extended = Empty_Node then
423 Virtual_Hash.Set (Proj, Proj);
426 -- Now check the projects it imports
428 With_Clause := First_With_Clause_Of (Proj, In_Tree);
430 while With_Clause /= Empty_Node loop
431 Imported := Project_Node_Of (With_Clause, In_Tree);
433 if Imported /= Empty_Node then
434 Look_For_Virtual_Projects_For
435 (Imported, In_Tree, Potentially_Virtual => True);
438 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
441 -- Check also the eventual project extended by Proj. As this project
442 -- is already extended, call recursively with Potentially_Virtual
445 Look_For_Virtual_Projects_For
446 (Extended, In_Tree, Potentially_Virtual => False);
448 end Look_For_Virtual_Projects_For;
455 (In_Tree : Project_Node_Tree_Ref;
456 Project : out Project_Node_Id;
457 Project_File_Name : String;
458 Always_Errout_Finalize : Boolean;
459 Packages_To_Check : String_List_Access := All_Packages;
460 Store_Comments : Boolean := False)
462 Current_Directory : constant String := Get_Current_Dir;
465 Real_Project_File_Name : String_Access :=
466 Osint.To_Canonical_File_Spec
470 if Real_Project_File_Name = null then
471 Real_Project_File_Name := new String'(Project_File_Name);
474 Project := Empty_Node;
476 if Current_Verbosity >= Medium then
477 Write_Str ("ADA_PROJECT_PATH=""");
478 Write_Str (Project_Path);
483 Path_Name : constant String :=
484 Project_Path_Name_Of (Real_Project_File_Name.all,
485 Directory => Current_Directory);
488 Free (Real_Project_File_Name);
491 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
492 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
494 -- Parse the main project file
496 if Path_Name = "" then
498 ("project file """, Project_File_Name, """ not found");
499 Project := Empty_Node;
506 Extends_All => Dummy,
507 Path_Name => Path_Name,
509 From_Extended => None,
511 Packages_To_Check => Packages_To_Check);
513 -- If Project is an extending-all project, create the eventual
514 -- virtual extending projects and check that there are no illegally
515 -- imported projects.
517 if Project /= Empty_Node
518 and then Is_Extending_All (Project, In_Tree)
520 -- First look for projects that potentially need a virtual
521 -- extending project.
524 Processed_Hash.Reset;
526 -- Mark the extending all project as processed, to avoid checking
527 -- the imported projects in case of a "limited with" on this
528 -- extending all project.
530 Processed_Hash.Set (Project, True);
533 Declaration : constant Project_Node_Id :=
534 Project_Declaration_Of (Project, In_Tree);
536 Look_For_Virtual_Projects_For
537 (Extended_Project_Of (Declaration, In_Tree), In_Tree,
538 Potentially_Virtual => False);
541 -- Now, check the projects directly imported by the main project.
542 -- Remove from the potentially virtual any project extended by one
543 -- of these imported projects. For non extending imported
544 -- projects, check that they do not belong to the project tree of
545 -- the project being "extended-all" by the main project.
548 With_Clause : Project_Node_Id;
549 Imported : Project_Node_Id := Empty_Node;
550 Declaration : Project_Node_Id := Empty_Node;
553 With_Clause := First_With_Clause_Of (Project, In_Tree);
554 while With_Clause /= Empty_Node loop
555 Imported := Project_Node_Of (With_Clause, In_Tree);
557 if Imported /= Empty_Node then
558 Declaration := Project_Declaration_Of (Imported, In_Tree);
560 if Extended_Project_Of (Declaration, In_Tree) /=
565 Extended_Project_Of (Declaration, In_Tree);
566 exit when Imported = Empty_Node;
567 Virtual_Hash.Remove (Imported);
569 Project_Declaration_Of (Imported, In_Tree);
574 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
578 -- Now create all the virtual extending projects
581 Proj : Project_Node_Id := Virtual_Hash.Get_First;
583 while Proj /= Empty_Node loop
584 Create_Virtual_Extending_Project (Proj, Project, In_Tree);
585 Proj := Virtual_Hash.Get_Next;
590 -- If there were any kind of error during the parsing, serious
591 -- or not, then the parsing fails.
593 if Err_Vars.Total_Errors_Detected > 0 then
594 Project := Empty_Node;
597 if Project = Empty_Node or else Always_Errout_Finalize then
607 Write_Line (Exception_Information (X));
608 Write_Str ("Exception ");
609 Write_Str (Exception_Name (X));
610 Write_Line (" raised, while processing project file");
611 Project := Empty_Node;
614 ------------------------------
615 -- Pre_Parse_Context_Clause --
616 ------------------------------
618 procedure Pre_Parse_Context_Clause
619 (In_Tree : Project_Node_Tree_Ref;
620 Context_Clause : out With_Id)
622 Current_With_Clause : With_Id := No_With;
623 Limited_With : Boolean := False;
625 Current_With : With_Record;
627 Current_With_Node : Project_Node_Id := Empty_Node;
630 -- Assume no context clause
632 Context_Clause := No_With;
635 -- If Token is not WITH or LIMITED, there is no context clause, or we
636 -- have exhausted the with clauses.
638 while Token = Tok_With or else Token = Tok_Limited loop
640 Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree);
641 Limited_With := Token = Tok_Limited;
644 Scan (In_Tree); -- scan past LIMITED
645 Expect (Tok_With, "WITH");
646 exit With_Loop when Token /= Tok_With;
651 Scan (In_Tree); -- scan past WITH or ","
653 Expect (Tok_String_Literal, "literal string");
655 if Token /= Tok_String_Literal then
659 -- Store path and location in table Withs
662 (Path => File_Name_Type (Token_Name),
663 Location => Token_Ptr,
664 Limited_With => Limited_With,
665 Node => Current_With_Node,
668 Withs.Increment_Last;
669 Withs.Table (Withs.Last) := Current_With;
671 if Current_With_Clause = No_With then
672 Context_Clause := Withs.Last;
675 Withs.Table (Current_With_Clause).Next := Withs.Last;
678 Current_With_Clause := Withs.Last;
682 if Token = Tok_Semicolon then
683 Set_End_Of_Line (Current_With_Node);
684 Set_Previous_Line_Node (Current_With_Node);
686 -- End of (possibly multiple) with clause;
688 Scan (In_Tree); -- scan past the semicolon.
691 elsif Token = Tok_Comma then
692 Set_Is_Not_Last_In_List (Current_With_Node, In_Tree);
695 Error_Msg ("expected comma or semi colon", Token_Ptr);
701 (Of_Kind => N_With_Clause, In_Tree => In_Tree);
704 end Pre_Parse_Context_Clause;
706 -------------------------------
707 -- Post_Parse_Context_Clause --
708 -------------------------------
710 procedure Post_Parse_Context_Clause
711 (Context_Clause : With_Id;
712 In_Tree : Project_Node_Tree_Ref;
713 Imported_Projects : out Project_Node_Id;
714 Project_Directory : Path_Name_Type;
715 From_Extended : Extension_Origin;
716 In_Limited : Boolean;
717 Packages_To_Check : String_List_Access)
719 Current_With_Clause : With_Id;
721 Current_Project : Project_Node_Id := Empty_Node;
722 Previous_Project : Project_Node_Id := Empty_Node;
723 Next_Project : Project_Node_Id := Empty_Node;
725 Project_Directory_Path : constant String :=
726 Get_Name_String (Project_Directory);
728 Current_With : With_Record;
729 Limited_With : Boolean := False;
730 Extends_All : Boolean := False;
733 Imported_Projects := Empty_Node;
735 Current_With_Clause := Context_Clause;
736 while Current_With_Clause /= No_With loop
737 Current_With := Withs.Table (Current_With_Clause);
738 Current_With_Clause := Current_With.Next;
740 Limited_With := In_Limited or Current_With.Limited_With;
743 Original_Path : constant String :=
744 Get_Name_String (Current_With.Path);
746 Imported_Path_Name : constant String :=
748 (Original_Path, Project_Directory_Path);
750 Resolved_Path : constant String :=
753 Resolve_Links => True,
754 Case_Sensitive => True);
756 Withed_Project : Project_Node_Id := Empty_Node;
759 if Imported_Path_Name = "" then
761 -- The project file cannot be found
763 Error_Msg_File_1 := Current_With.Path;
764 Error_Msg ("unknown project file: {", Current_With.Location);
766 -- If this is not imported by the main project file,
767 -- display the import path.
769 if Project_Stack.Last > 1 then
770 for Index in reverse 1 .. Project_Stack.Last loop
772 File_Name_Type (Project_Stack.Table (Index).Path_Name);
773 Error_Msg ("\imported by {", Current_With.Location);
780 Previous_Project := Current_Project;
782 if Current_Project = Empty_Node then
784 -- First with clause of the context clause
786 Current_Project := Current_With.Node;
787 Imported_Projects := Current_Project;
790 Next_Project := Current_With.Node;
791 Set_Next_With_Clause_Of
792 (Current_Project, In_Tree, Next_Project);
793 Current_Project := Next_Project;
797 (Current_Project, In_Tree, Name_Id (Current_With.Path));
799 (Current_Project, In_Tree, Current_With.Location);
801 -- If this is a "limited with", check if we have a circularity.
802 -- If we have one, get the project id of the limited imported
803 -- project file, and do not parse it.
805 if Limited_With and then Project_Stack.Last > 1 then
807 Canonical_Path_Name : Path_Name_Type;
810 Name_Len := Resolved_Path'Length;
811 Name_Buffer (1 .. Name_Len) := Resolved_Path;
812 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
813 Canonical_Path_Name := Name_Find;
815 for Index in 1 .. Project_Stack.Last loop
816 if Project_Stack.Table (Index).Canonical_Path_Name =
819 -- We have found the limited imported project,
820 -- get its project id, and do not parse it.
822 Withed_Project := Project_Stack.Table (Index).Id;
829 -- Parse the imported project, if its project id is unknown
831 if Withed_Project = Empty_Node then
834 Project => Withed_Project,
835 Extends_All => Extends_All,
836 Path_Name => Imported_Path_Name,
838 From_Extended => From_Extended,
839 In_Limited => Limited_With,
840 Packages_To_Check => Packages_To_Check);
843 Extends_All := Is_Extending_All (Withed_Project, In_Tree);
846 if Withed_Project = Empty_Node then
847 -- If parsing was not successful, remove the
850 Current_Project := Previous_Project;
852 if Current_Project = Empty_Node then
853 Imported_Projects := Empty_Node;
856 Set_Next_With_Clause_Of
857 (Current_Project, In_Tree, Empty_Node);
860 -- If parsing was successful, record project name
861 -- and path name in with clause
864 (Node => Current_Project,
866 To => Withed_Project,
867 Limited_With => Current_With.Limited_With);
871 Name_Of (Withed_Project, In_Tree));
873 Name_Len := Resolved_Path'Length;
874 Name_Buffer (1 .. Name_Len) := Resolved_Path;
875 Set_Path_Name_Of (Current_Project, In_Tree, Name_Find);
878 Set_Is_Extending_All (Current_Project, In_Tree);
884 end Post_Parse_Context_Clause;
886 --------------------------
887 -- Parse_Single_Project --
888 --------------------------
890 procedure Parse_Single_Project
891 (In_Tree : Project_Node_Tree_Ref;
892 Project : out Project_Node_Id;
893 Extends_All : out Boolean;
896 From_Extended : Extension_Origin;
897 In_Limited : Boolean;
898 Packages_To_Check : String_List_Access)
900 Normed_Path_Name : Path_Name_Type;
901 Canonical_Path_Name : Path_Name_Type;
902 Project_Directory : Path_Name_Type;
903 Project_Scan_State : Saved_Project_Scan_State;
904 Source_Index : Source_File_Index;
906 Extending : Boolean := False;
908 Extended_Project : Project_Node_Id := Empty_Node;
910 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
911 Tree_Private_Part.Projects_Htable.Get_First
912 (In_Tree.Projects_HT);
914 Name_From_Path : constant File_Name_Type :=
915 Project_Name_From (Path_Name);
917 Name_Of_Project : Name_Id := No_Name;
919 First_With : With_Id;
921 use Tree_Private_Part;
923 Project_Comment_State : Tree.Comment_State;
926 Extends_All := False;
929 Normed_Path : constant String := Normalize_Pathname
930 (Path_Name, Resolve_Links => False,
931 Case_Sensitive => True);
932 Canonical_Path : constant String := Normalize_Pathname
933 (Normed_Path, Resolve_Links => True,
934 Case_Sensitive => False);
937 Name_Len := Normed_Path'Length;
938 Name_Buffer (1 .. Name_Len) := Normed_Path;
939 Normed_Path_Name := Name_Find;
940 Name_Len := Canonical_Path'Length;
941 Name_Buffer (1 .. Name_Len) := Canonical_Path;
942 Canonical_Path_Name := Name_Find;
945 -- Check for a circular dependency
947 for Index in 1 .. Project_Stack.Last loop
948 if Canonical_Path_Name =
949 Project_Stack.Table (Index).Canonical_Path_Name
951 Error_Msg ("circular dependency detected", Token_Ptr);
952 Error_Msg_File_1 := File_Name_Type (Normed_Path_Name);
953 Error_Msg ("\\ { is imported by", Token_Ptr);
955 for Current in reverse 1 .. Project_Stack.Last loop
957 File_Name_Type (Project_Stack.Table (Current).Path_Name);
959 if Project_Stack.Table (Current).Canonical_Path_Name /=
963 ("\\ { which itself is imported by", Token_Ptr);
966 Error_Msg ("\\ {", Token_Ptr);
971 Project := Empty_Node;
976 -- Put the new path name on the stack
978 Project_Stack.Increment_Last;
979 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
980 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
983 -- Check if the project file has already been parsed
986 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
988 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
991 if A_Project_Name_And_Node.Extended then
993 ("cannot extend the same project file several times",
997 ("cannot extend an already imported project file",
1001 elsif A_Project_Name_And_Node.Extended then
1003 Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree);
1005 -- If the imported project is an extended project A,
1006 -- and we are in an extended project, replace A with the
1007 -- ultimate project extending A.
1009 if From_Extended /= None then
1011 Decl : Project_Node_Id :=
1012 Project_Declaration_Of
1013 (A_Project_Name_And_Node.Node, In_Tree);
1015 Prj : Project_Node_Id :=
1016 Extending_Project_Of (Decl, In_Tree);
1020 Decl := Project_Declaration_Of (Prj, In_Tree);
1021 exit when Extending_Project_Of (Decl, In_Tree) =
1023 Prj := Extending_Project_Of (Decl, In_Tree);
1026 A_Project_Name_And_Node.Node := Prj;
1030 ("cannot import an already extended project file",
1035 Project := A_Project_Name_And_Node.Node;
1036 Project_Stack.Decrement_Last;
1040 A_Project_Name_And_Node :=
1041 Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT);
1044 -- We never encountered this project file
1045 -- Save the scan state, load the project file and start to scan it.
1047 Save_Project_Scan_State (Project_Scan_State);
1048 Source_Index := Load_Project_File (Path_Name);
1049 Tree.Save (Project_Comment_State);
1051 -- If we cannot find it, we stop
1053 if Source_Index = No_Source_File then
1054 Project := Empty_Node;
1055 Project_Stack.Decrement_Last;
1059 Prj.Err.Scanner.Initialize_Scanner (Source_Index);
1063 if Name_From_Path = No_File then
1065 -- The project file name is not correct (no or bad extension,
1066 -- or not following Ada identifier's syntax).
1068 Error_Msg_File_1 := File_Name_Type (Canonical_Path_Name);
1069 Error_Msg ("?{ is not a valid path name for a project file",
1073 if Current_Verbosity >= Medium then
1074 Write_Str ("Parsing """);
1075 Write_Str (Path_Name);
1080 -- Is there any imported project?
1082 Pre_Parse_Context_Clause (In_Tree, First_With);
1084 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1085 Project := Default_Project_Node
1086 (Of_Kind => N_Project, In_Tree => In_Tree);
1087 Project_Stack.Table (Project_Stack.Last).Id := Project;
1088 Set_Directory_Of (Project, In_Tree, Project_Directory);
1089 Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name);
1090 Set_Location_Of (Project, In_Tree, Token_Ptr);
1092 Expect (Tok_Project, "PROJECT");
1094 -- Mark location of PROJECT token if present
1096 if Token = Tok_Project then
1097 Scan (In_Tree); -- scan past PROJECT
1098 Set_Location_Of (Project, In_Tree, Token_Ptr);
1105 Expect (Tok_Identifier, "identifier");
1107 -- If the token is not an identifier, clear the buffer before
1108 -- exiting to indicate that the name of the project is ill-formed.
1110 if Token /= Tok_Identifier then
1115 -- Add the identifier name to the buffer
1117 Get_Name_String (Token_Name);
1118 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1120 -- Scan past the identifier
1124 -- If we have a dot, add a dot the the Buffer and look for the next
1127 exit when Token /= Tok_Dot;
1128 Add_To_Buffer (".", Buffer, Buffer_Last);
1130 -- Scan past the dot
1135 -- See if this is an extending project
1137 if Token = Tok_Extends then
1139 -- Make sure that gnatmake will use mapping files
1141 Create_Mapping_File := True;
1143 -- We are extending another project
1147 Scan (In_Tree); -- scan past EXTENDS
1149 if Token = Tok_All then
1150 Extends_All := True;
1151 Set_Is_Extending_All (Project, In_Tree);
1152 Scan (In_Tree); -- scan past ALL
1156 -- If the name is well formed, Buffer_Last is > 0
1158 if Buffer_Last > 0 then
1160 -- The Buffer contains the name of the project
1162 Name_Len := Buffer_Last;
1163 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1164 Name_Of_Project := Name_Find;
1165 Set_Name_Of (Project, In_Tree, Name_Of_Project);
1167 -- To get expected name of the project file, replace dots by dashes
1169 Name_Len := Buffer_Last;
1170 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1172 for Index in 1 .. Name_Len loop
1173 if Name_Buffer (Index) = '.' then
1174 Name_Buffer (Index) := '-';
1178 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1181 Expected_Name : constant File_Name_Type := Name_Find;
1184 -- Output a warning if the actual name is not the expected name
1186 if Name_From_Path /= No_File
1187 and then Expected_Name /= Name_From_Path
1189 Error_Msg_File_1 := Expected_Name;
1190 Error_Msg ("?file name does not match unit name, " &
1191 "should be `{" & Project_File_Extension & "`",
1197 Imported_Projects : Project_Node_Id := Empty_Node;
1198 From_Ext : Extension_Origin := None;
1201 -- Extending_All is always propagated
1203 if From_Extended = Extending_All or else Extends_All then
1204 From_Ext := Extending_All;
1206 -- Otherwise, From_Extended is set to Extending_Single if the
1207 -- current project is an extending project.
1210 From_Ext := Extending_Simple;
1213 Post_Parse_Context_Clause
1214 (In_Tree => In_Tree,
1215 Context_Clause => First_With,
1216 Imported_Projects => Imported_Projects,
1217 Project_Directory => Project_Directory,
1218 From_Extended => From_Ext,
1219 In_Limited => In_Limited,
1220 Packages_To_Check => Packages_To_Check);
1221 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1225 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1226 Tree_Private_Part.Projects_Htable.Get_First
1227 (In_Tree.Projects_HT);
1229 Project_Name : Name_Id := Name_And_Node.Name;
1232 -- Check if we already have a project with this name
1234 while Project_Name /= No_Name
1235 and then Project_Name /= Name_Of_Project
1238 Tree_Private_Part.Projects_Htable.Get_Next
1239 (In_Tree.Projects_HT);
1240 Project_Name := Name_And_Node.Name;
1243 -- Report an error if we already have a project with this name
1245 if Project_Name /= No_Name then
1246 Error_Msg_Name_1 := Project_Name;
1248 ("duplicate project name %%", Location_Of (Project, In_Tree));
1250 File_Name_Type (Path_Name_Of (Name_And_Node.Node, In_Tree));
1252 ("\already in {", Location_Of (Project, In_Tree));
1255 -- Otherwise, add the name of the project to the hash table, so
1256 -- that we can check that no other subsequent project will have
1259 Tree_Private_Part.Projects_Htable.Set
1260 (T => In_Tree.Projects_HT,
1261 K => Name_Of_Project,
1262 E => (Name => Name_Of_Project,
1264 Canonical_Path => Canonical_Path_Name,
1265 Extended => Extended));
1272 Expect (Tok_String_Literal, "literal string");
1274 if Token = Tok_String_Literal then
1275 Set_Extended_Project_Path_Of
1276 (Project, In_Tree, Path_Name_Type (Token_Name));
1279 Original_Path_Name : constant String :=
1280 Get_Name_String (Token_Name);
1282 Extended_Project_Path_Name : constant String :=
1283 Project_Path_Name_Of
1284 (Original_Path_Name,
1286 (Project_Directory));
1289 if Extended_Project_Path_Name = "" then
1291 -- We could not find the project file to extend
1293 Error_Msg_File_1 := File_Name_Type (Token_Name);
1294 Error_Msg ("unknown project file: {", Token_Ptr);
1296 -- If we are not in the main project file, display the
1299 if Project_Stack.Last > 1 then
1302 (Project_Stack.Table (Project_Stack.Last).Path_Name);
1303 Error_Msg ("\extended by {", Token_Ptr);
1305 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1308 (Project_Stack.Table (Index).Path_Name);
1309 Error_Msg ("\imported by {", Token_Ptr);
1315 From_Ext : Extension_Origin := None;
1318 if From_Extended = Extending_All or else Extends_All then
1319 From_Ext := Extending_All;
1322 Parse_Single_Project
1323 (In_Tree => In_Tree,
1324 Project => Extended_Project,
1325 Extends_All => Extends_All,
1326 Path_Name => Extended_Project_Path_Name,
1328 From_Extended => From_Ext,
1329 In_Limited => In_Limited,
1330 Packages_To_Check => Packages_To_Check);
1333 -- A project that extends an extending-all project is also
1334 -- an extending-all project.
1336 if Extended_Project /= Empty_Node
1337 and then Is_Extending_All (Extended_Project, In_Tree)
1339 Set_Is_Extending_All (Project, In_Tree);
1344 Scan (In_Tree); -- scan past the extended project path
1348 -- Check that a non extending-all project does not import an
1349 -- extending-all project.
1351 if not Is_Extending_All (Project, In_Tree) then
1353 With_Clause : Project_Node_Id :=
1354 First_With_Clause_Of (Project, In_Tree);
1355 Imported : Project_Node_Id := Empty_Node;
1359 while With_Clause /= Empty_Node loop
1360 Imported := Project_Node_Of (With_Clause, In_Tree);
1362 if Is_Extending_All (With_Clause, In_Tree) then
1364 File_Name_Type (Name_Of (Imported, In_Tree));
1365 Error_Msg ("cannot import extending-all project {",
1367 exit With_Clause_Loop;
1370 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1371 end loop With_Clause_Loop;
1375 -- Check that a project with a name including a dot either imports
1376 -- or extends the project whose name precedes the last dot.
1378 if Name_Of_Project /= No_Name then
1379 Get_Name_String (Name_Of_Project);
1385 -- Look for the last dot
1387 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1388 Name_Len := Name_Len - 1;
1391 -- If a dot was find, check if the parent project is imported
1394 if Name_Len > 0 then
1395 Name_Len := Name_Len - 1;
1398 Parent_Name : constant File_Name_Type := Name_Find;
1399 Parent_Found : Boolean := False;
1400 With_Clause : Project_Node_Id :=
1401 First_With_Clause_Of (Project, In_Tree);
1404 -- If there is an extended project, check its name
1406 if Extended_Project /= Empty_Node then
1408 Name_Of (Extended_Project, In_Tree) = Name_Id (Parent_Name);
1411 -- If the parent project is not the extended project,
1412 -- check each imported project until we find the parent project.
1414 while not Parent_Found and then With_Clause /= Empty_Node loop
1416 Name_Of (Project_Node_Of (With_Clause, In_Tree), In_Tree) =
1417 Name_Id (Parent_Name);
1418 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1421 -- If the parent project was not found, report an error
1423 if not Parent_Found then
1424 Error_Msg_Name_1 := Name_Of_Project;
1425 Error_Msg_File_1 := Parent_Name;
1426 Error_Msg ("project %% does not import or extend project {",
1427 Location_Of (Project, In_Tree));
1432 Expect (Tok_Is, "IS");
1433 Set_End_Of_Line (Project);
1434 Set_Previous_Line_Node (Project);
1435 Set_Next_End_Node (Project);
1438 Project_Declaration : Project_Node_Id := Empty_Node;
1441 -- No need to Scan past "is", Prj.Dect.Parse will do it
1444 (In_Tree => In_Tree,
1445 Declarations => Project_Declaration,
1446 Current_Project => Project,
1447 Extends => Extended_Project,
1448 Packages_To_Check => Packages_To_Check);
1449 Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration);
1451 if Extended_Project /= Empty_Node then
1452 Set_Extending_Project_Of
1453 (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree,
1458 Expect (Tok_End, "END");
1459 Remove_Next_End_Node;
1461 -- Skip "end" if present
1463 if Token = Tok_End then
1471 -- Store the name following "end" in the Buffer. The name may be made of
1472 -- several simple names.
1475 Expect (Tok_Identifier, "identifier");
1477 -- If we don't have an identifier, clear the buffer before exiting to
1478 -- avoid checking the name.
1480 if Token /= Tok_Identifier then
1485 -- Add the identifier to the Buffer
1486 Get_Name_String (Token_Name);
1487 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1489 -- Scan past the identifier
1492 exit when Token /= Tok_Dot;
1493 Add_To_Buffer (".", Buffer, Buffer_Last);
1497 -- If we have a valid name, check if it is the name of the project
1499 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1500 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1501 Get_Name_String (Name_Of (Project, In_Tree))
1503 -- Invalid name: report an error
1505 Error_Msg ("expected """ &
1506 Get_Name_String (Name_Of (Project, In_Tree)) & """",
1511 Expect (Tok_Semicolon, "`;`");
1513 -- Check that there is no more text following the end of the project
1516 if Token = Tok_Semicolon then
1517 Set_Previous_End_Node (Project);
1520 if Token /= Tok_EOF then
1522 ("unexpected text following end of project", Token_Ptr);
1526 -- Restore the scan state, in case we are not the main project
1528 Restore_Project_Scan_State (Project_Scan_State);
1530 -- And remove the project from the project stack
1532 Project_Stack.Decrement_Last;
1534 -- Indicate if there are unkept comments
1536 Tree.Set_Project_File_Includes_Unkept_Comments
1539 To => Tree.There_Are_Unkept_Comments);
1541 -- And restore the comment state that was saved
1543 Tree.Restore (Project_Comment_State);
1544 end Parse_Single_Project;
1546 -----------------------
1547 -- Project_Name_From --
1548 -----------------------
1550 function Project_Name_From (Path_Name : String) return File_Name_Type is
1551 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1552 First : Natural := Canonical'Last;
1553 Last : Natural := First;
1557 if Current_Verbosity = High then
1558 Write_Str ("Project_Name_From (""");
1559 Write_Str (Canonical);
1563 -- If the path name is empty, return No_Name to indicate failure
1569 Canonical_Case_File_Name (Canonical);
1571 -- Look for the last dot in the path name
1575 Canonical (First) /= '.'
1580 -- If we have a dot, check that it is followed by the correct extension
1582 if First > 0 and then Canonical (First) = '.' then
1583 if Canonical (First .. Last) = Project_File_Extension
1586 -- Look for the last directory separator, if any
1592 and then Canonical (First) /= '/'
1593 and then Canonical (First) /= Dir_Sep
1599 -- Not the correct extension, return No_Name to indicate failure
1604 -- If no dot in the path name, return No_Name to indicate failure
1612 -- If the extension is the file name, return No_Name to indicate failure
1614 if First > Last then
1618 -- Put the name in lower case into Name_Buffer
1620 Name_Len := Last - First + 1;
1621 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1625 -- Check if it is a well formed project name. Return No_Name if it is
1629 if not Is_Letter (Name_Buffer (Index)) then
1636 exit when Index >= Name_Len;
1638 if Name_Buffer (Index) = '_' then
1639 if Name_Buffer (Index + 1) = '_' then
1644 exit when Name_Buffer (Index) = '-';
1646 if Name_Buffer (Index) /= '_'
1647 and then not Is_Alphanumeric (Name_Buffer (Index))
1655 if Index >= Name_Len then
1656 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1658 -- All checks have succeeded. Return name in Name_Buffer
1666 elsif Name_Buffer (Index) = '-' then
1670 end Project_Name_From;
1672 --------------------------
1673 -- Project_Path_Name_Of --
1674 --------------------------
1676 function Project_Path_Name_Of
1677 (Project_File_Name : String;
1678 Directory : String) return String
1680 Result : String_Access;
1683 if Current_Verbosity = High then
1684 Write_Str ("Project_Path_Name_Of (""");
1685 Write_Str (Project_File_Name);
1686 Write_Str (""", """);
1687 Write_Str (Directory);
1688 Write_Line (""");");
1691 if not Is_Absolute_Path (Project_File_Name) then
1692 -- First we try <directory>/<file_name>.<extension>
1694 if Current_Verbosity = High then
1695 Write_Str (" Trying ");
1696 Write_Str (Directory);
1697 Write_Char (Directory_Separator);
1698 Write_Str (Project_File_Name);
1699 Write_Line (Project_File_Extension);
1704 (File_Name => Directory & Directory_Separator &
1705 Project_File_Name & Project_File_Extension,
1706 Path => Project_Path);
1708 -- Then we try <directory>/<file_name>
1710 if Result = null then
1711 if Current_Verbosity = High then
1712 Write_Str (" Trying ");
1713 Write_Str (Directory);
1714 Write_Char (Directory_Separator);
1715 Write_Line (Project_File_Name);
1720 (File_Name => Directory & Directory_Separator &
1722 Path => Project_Path);
1726 if Result = null then
1728 -- Then we try <file_name>.<extension>
1730 if Current_Verbosity = High then
1731 Write_Str (" Trying ");
1732 Write_Str (Project_File_Name);
1733 Write_Line (Project_File_Extension);
1738 (File_Name => Project_File_Name & Project_File_Extension,
1739 Path => Project_Path);
1742 if Result = null then
1744 -- Then we try <file_name>
1746 if Current_Verbosity = High then
1747 Write_Str (" Trying ");
1748 Write_Line (Project_File_Name);
1753 (File_Name => Project_File_Name,
1754 Path => Project_Path);
1757 -- If we cannot find the project file, we return an empty string
1759 if Result = null then
1764 Final_Result : constant String :=
1765 GNAT.OS_Lib.Normalize_Pathname
1767 Resolve_Links => False,
1768 Case_Sensitive => True);
1771 return Final_Result;
1774 end Project_Path_Name_Of;