1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2004 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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, 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;
28 with Namet; use Namet;
30 with Osint; use Osint;
31 with Output; use Output;
32 with Prj.Com; use Prj.Com;
34 with Prj.Err; use Prj.Err;
35 with Scans; use Scans;
36 with Sinput; use Sinput;
37 with Sinput.P; use Sinput.P;
40 with Types; use Types;
42 with Ada.Characters.Handling; use Ada.Characters.Handling;
43 with Ada.Exceptions; use Ada.Exceptions;
45 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
46 with GNAT.OS_Lib; use GNAT.OS_Lib;
48 with System.HTable; use System.HTable;
50 pragma Elaborate_All (GNAT.OS_Lib);
52 package body Prj.Part is
54 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
56 Project_Path : String_Access;
57 -- The project path; initialized during package elaboration.
58 -- Contains at least the current working directory.
60 Ada_Project_Path : constant String := "ADA_PROJECT_PATH";
61 -- Name of the env. variable that contains path name(s) of directories
62 -- where project files may reside.
64 Prj_Path : constant String_Access := Getenv (Ada_Project_Path);
65 -- The path name(s) of directories where project files may reside.
68 type Extension_Origin is (None, Extending_Simple, Extending_All);
69 -- Type of parameter From_Extended for procedures Parse_Single_Project and
70 -- Post_Parse_Context_Clause. Extending_All means that we are parsing the
71 -- tree rooted at an extending all project.
73 ------------------------------------
74 -- Local Packages and Subprograms --
75 ------------------------------------
77 type With_Id is new Nat;
78 No_With : constant With_Id := 0;
80 type With_Record is record
82 Location : Source_Ptr;
83 Limited_With : Boolean;
84 Node : Project_Node_Id;
87 -- Information about an imported project, to be put in table Withs below
89 package Withs is new Table.Table
90 (Table_Component_Type => With_Record,
91 Table_Index_Type => With_Id,
94 Table_Increment => 50,
95 Table_Name => "Prj.Part.Withs");
96 -- Table used to store temporarily paths and locations of imported
97 -- projects. These imported projects will be effectively parsed after the
98 -- name of the current project has been extablished.
100 type Names_And_Id is record
102 Canonical_Path_Name : Name_Id;
103 Id : Project_Node_Id;
106 package Project_Stack is new Table.Table
107 (Table_Component_Type => Names_And_Id,
108 Table_Index_Type => Nat,
109 Table_Low_Bound => 1,
111 Table_Increment => 50,
112 Table_Name => "Prj.Part.Project_Stack");
113 -- This table is used to detect circular dependencies
114 -- for imported and extended projects and to get the project ids of
115 -- limited imported projects when there is a circularity with at least
116 -- one limited imported project file.
118 package Virtual_Hash is new Simple_HTable
119 (Header_Num => Header_Num,
120 Element => Project_Node_Id,
121 No_Element => Empty_Node,
122 Key => Project_Node_Id,
123 Hash => Prj.Tree.Hash,
125 -- Hash table to store the node id of the project for which a virtual
126 -- extending project need to be created.
128 package Processed_Hash is new Simple_HTable
129 (Header_Num => Header_Num,
132 Key => Project_Node_Id,
133 Hash => Prj.Tree.Hash,
135 -- Hash table to store the project process when looking for project that
136 -- need to have a virtual extending project, to avoid processing the same
139 procedure Create_Virtual_Extending_Project
140 (For_Project : Project_Node_Id;
141 Main_Project : Project_Node_Id);
142 -- Create a virtual extending project of For_Project. Main_Project is
143 -- the extending all project.
145 procedure Look_For_Virtual_Projects_For
146 (Proj : Project_Node_Id;
147 Potentially_Virtual : Boolean);
148 -- Look for projects that need to have a virtual extending project.
149 -- This procedure is recursive. If called with Potentially_Virtual set to
150 -- True, then Proj may need an virtual extending project; otherwise it
151 -- does not (because it is already extended), but other projects that it
152 -- imports may need to be virtually extended.
154 procedure Pre_Parse_Context_Clause (Context_Clause : out With_Id);
155 -- Parse the context clause of a project.
156 -- Store the paths and locations of the imported projects in table Withs.
157 -- Does nothing if there is no context clause (if the current
158 -- token is not "with" or "limited" followed by "with").
160 procedure Post_Parse_Context_Clause
161 (Context_Clause : With_Id;
162 Imported_Projects : out Project_Node_Id;
163 Project_Directory : Name_Id;
164 From_Extended : Extension_Origin);
165 -- Parse the imported projects that have been stored in table Withs,
166 -- if any. From_Extended is used for the call to Parse_Single_Project
169 procedure Parse_Single_Project
170 (Project : out Project_Node_Id;
171 Extends_All : out Boolean;
174 From_Extended : Extension_Origin);
175 -- Parse a project file.
176 -- Recursive procedure: it calls itself for imported and extended
177 -- projects. When From_Extended is not None, if the project has already
178 -- been parsed and is an extended project A, return the ultimate
179 -- (not extended) project that extends A.
181 function Project_Path_Name_Of
182 (Project_File_Name : String;
185 -- Returns the path name of a project file. Returns an empty string
186 -- if project file cannot be found.
188 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id;
189 -- Get the directory of the file with the specified path name.
190 -- This includes the directory separator as the last character.
191 -- Returns "./" if Path_Name contains no directory separator.
193 function Project_Name_From (Path_Name : String) return Name_Id;
194 -- Returns the name of the project that corresponds to its path name.
195 -- Returns No_Name if the path name is invalid, because the corresponding
196 -- project name does not have the syntax of an ada identifier.
198 --------------------------------------
199 -- Create_Virtual_Extending_Project --
200 --------------------------------------
202 procedure Create_Virtual_Extending_Project
203 (For_Project : Project_Node_Id;
204 Main_Project : Project_Node_Id)
207 Virtual_Name : constant String :=
209 Get_Name_String (Name_Of (For_Project));
210 -- The name of the virtual extending project
212 Virtual_Name_Id : Name_Id;
213 -- Virtual extending project name id
215 Virtual_Path_Id : Name_Id;
216 -- Fake path name of the virtual extending project. The directory is
217 -- the same directory as the extending all project.
219 Virtual_Dir_Id : constant Name_Id :=
220 Immediate_Directory_Of (Path_Name_Of (Main_Project));
221 -- The directory of the extending all project
223 -- The source of the virtual extending project is something like:
225 -- project V$<project name> extends <project path> is
227 -- for Source_Dirs use ();
229 -- end V$<project name>;
231 -- The project directory cannot be specified during parsing; it will be
232 -- put directly in the virtual extending project data during processing.
234 -- Nodes that made up the virtual extending project
236 Virtual_Project : constant Project_Node_Id :=
237 Default_Project_Node (N_Project);
238 With_Clause : constant Project_Node_Id :=
239 Default_Project_Node (N_With_Clause);
240 Project_Declaration : constant Project_Node_Id :=
241 Default_Project_Node (N_Project_Declaration);
242 Source_Dirs_Declaration : constant Project_Node_Id :=
243 Default_Project_Node (N_Declarative_Item);
244 Source_Dirs_Attribute : constant Project_Node_Id :=
246 (N_Attribute_Declaration, List);
247 Source_Dirs_Expression : constant Project_Node_Id :=
248 Default_Project_Node (N_Expression, List);
249 Source_Dirs_Term : constant Project_Node_Id :=
250 Default_Project_Node (N_Term, List);
251 Source_Dirs_List : constant Project_Node_Id :=
253 (N_Literal_String_List, List);
256 -- Get the virtual name id
258 Name_Len := Virtual_Name'Length;
259 Name_Buffer (1 .. Name_Len) := Virtual_Name;
260 Virtual_Name_Id := Name_Find;
262 -- Get the virtual path name
264 Get_Name_String (Path_Name_Of (Main_Project));
267 and then Name_Buffer (Name_Len) /= Directory_Separator
268 and then Name_Buffer (Name_Len) /= '/'
270 Name_Len := Name_Len - 1;
273 Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) :=
275 Name_Len := Name_Len + Virtual_Name'Length;
276 Virtual_Path_Id := Name_Find;
280 Set_Name_Of (With_Clause, Virtual_Name_Id);
281 Set_Path_Name_Of (With_Clause, Virtual_Path_Id);
282 Set_Project_Node_Of (With_Clause, Virtual_Project);
283 Set_Next_With_Clause_Of
284 (With_Clause, First_With_Clause_Of (Main_Project));
285 Set_First_With_Clause_Of (Main_Project, With_Clause);
287 -- Virtual project node
289 Set_Name_Of (Virtual_Project, Virtual_Name_Id);
290 Set_Path_Name_Of (Virtual_Project, Virtual_Path_Id);
291 Set_Location_Of (Virtual_Project, Location_Of (Main_Project));
292 Set_Directory_Of (Virtual_Project, Virtual_Dir_Id);
293 Set_Project_Declaration_Of (Virtual_Project, Project_Declaration);
294 Set_Extended_Project_Path_Of
295 (Virtual_Project, Path_Name_Of (For_Project));
297 -- Project declaration
299 Set_First_Declarative_Item_Of
300 (Project_Declaration, Source_Dirs_Declaration);
301 Set_Extended_Project_Of (Project_Declaration, For_Project);
303 -- Source_Dirs declaration
305 Set_Current_Item_Node (Source_Dirs_Declaration, Source_Dirs_Attribute);
307 -- Source_Dirs attribute
309 Set_Name_Of (Source_Dirs_Attribute, Snames.Name_Source_Dirs);
310 Set_Expression_Of (Source_Dirs_Attribute, Source_Dirs_Expression);
312 -- Source_Dirs expression
314 Set_First_Term (Source_Dirs_Expression, Source_Dirs_Term);
318 Set_Current_Term (Source_Dirs_Term, Source_Dirs_List);
320 -- Source_Dirs empty list: nothing to do
322 end Create_Virtual_Extending_Project;
324 ----------------------------
325 -- Immediate_Directory_Of --
326 ----------------------------
328 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id is
330 Get_Name_String (Path_Name);
332 for Index in reverse 1 .. Name_Len loop
333 if Name_Buffer (Index) = '/'
334 or else Name_Buffer (Index) = Dir_Sep
336 -- Remove all chars after last directory separator from name
339 Name_Len := Index - 1;
349 -- There is no directory separator in name. Return "./" or ".\"
352 Name_Buffer (1) := '.';
353 Name_Buffer (2) := Dir_Sep;
355 end Immediate_Directory_Of;
357 -----------------------------------
358 -- Look_For_Virtual_Projects_For --
359 -----------------------------------
361 procedure Look_For_Virtual_Projects_For
362 (Proj : Project_Node_Id;
363 Potentially_Virtual : Boolean)
366 Declaration : Project_Node_Id := Empty_Node;
367 -- Node for the project declaration of Proj
369 With_Clause : Project_Node_Id := Empty_Node;
370 -- Node for a with clause of Proj
372 Imported : Project_Node_Id := Empty_Node;
373 -- Node for a project imported by Proj
375 Extended : Project_Node_Id := Empty_Node;
376 -- Node for the eventual project extended by Proj
379 -- Nothing to do if Proj is not defined or if it has already been
382 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
383 -- Make sure the project will not be processed again
385 Processed_Hash.Set (Proj, True);
387 Declaration := Project_Declaration_Of (Proj);
389 if Declaration /= Empty_Node then
390 Extended := Extended_Project_Of (Declaration);
393 -- If this is a project that may need a virtual extending project
394 -- and it is not itself an extending project, put it in the list.
396 if Potentially_Virtual and then Extended = Empty_Node then
397 Virtual_Hash.Set (Proj, Proj);
400 -- Now check the projects it imports
402 With_Clause := First_With_Clause_Of (Proj);
404 while With_Clause /= Empty_Node loop
405 Imported := Project_Node_Of (With_Clause);
407 if Imported /= Empty_Node then
408 Look_For_Virtual_Projects_For
409 (Imported, Potentially_Virtual => True);
412 With_Clause := Next_With_Clause_Of (With_Clause);
415 -- Check also the eventual project extended by Proj. As this project
416 -- is already extended, call recursively with Potentially_Virtual
419 Look_For_Virtual_Projects_For
420 (Extended, Potentially_Virtual => False);
422 end Look_For_Virtual_Projects_For;
429 (Project : out Project_Node_Id;
430 Project_File_Name : String;
431 Always_Errout_Finalize : Boolean;
432 Packages_To_Check : String_List_Access := All_Packages;
433 Store_Comments : Boolean := False)
435 Current_Directory : constant String := Get_Current_Dir;
439 -- Save the Packages_To_Check in Prj, so that it is visible from
442 Current_Packages_To_Check := Packages_To_Check;
444 Project := Empty_Node;
446 if Current_Verbosity >= Medium then
447 Write_Str ("ADA_PROJECT_PATH=""");
448 Write_Str (Project_Path.all);
453 Path_Name : constant String :=
454 Project_Path_Name_Of (Project_File_Name,
455 Directory => Current_Directory);
459 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
460 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
462 -- Parse the main project file
464 if Path_Name = "" then
466 ("project file """, Project_File_Name, """ not found");
467 Project := Empty_Node;
473 Extends_All => Dummy,
474 Path_Name => Path_Name,
476 From_Extended => None);
478 -- If Project is an extending-all project, create the eventual
479 -- virtual extending projects and check that there are no illegally
480 -- imported projects.
482 if Project /= Empty_Node and then Is_Extending_All (Project) then
483 -- First look for projects that potentially need a virtual
484 -- extending project.
487 Processed_Hash.Reset;
489 -- Mark the extending all project as processed, to avoid checking
490 -- the imported projects in case of a "limited with" on this
491 -- extending all project.
493 Processed_Hash.Set (Project, True);
496 Declaration : constant Project_Node_Id :=
497 Project_Declaration_Of (Project);
499 Look_For_Virtual_Projects_For
500 (Extended_Project_Of (Declaration),
501 Potentially_Virtual => False);
504 -- Now, check the projects directly imported by the main project.
505 -- Remove from the potentially virtual any project extended by one
506 -- of these imported projects. For non extending imported
507 -- projects, check that they do not belong to the project tree of
508 -- the project being "extended-all" by the main project.
511 With_Clause : Project_Node_Id :=
512 First_With_Clause_Of (Project);
513 Imported : Project_Node_Id := Empty_Node;
514 Declaration : Project_Node_Id := Empty_Node;
517 while With_Clause /= Empty_Node loop
518 Imported := Project_Node_Of (With_Clause);
520 if Imported /= Empty_Node then
521 Declaration := Project_Declaration_Of (Imported);
523 if Extended_Project_Of (Declaration) /= Empty_Node then
525 Imported := Extended_Project_Of (Declaration);
526 exit when Imported = Empty_Node;
527 Virtual_Hash.Remove (Imported);
528 Declaration := Project_Declaration_Of (Imported);
531 elsif Virtual_Hash.Get (Imported) /= Empty_Node then
533 ("this project cannot be imported directly",
534 Location_Of (With_Clause));
539 With_Clause := Next_With_Clause_Of (With_Clause);
543 -- Now create all the virtual extending projects
546 Proj : Project_Node_Id := Virtual_Hash.Get_First;
548 while Proj /= Empty_Node loop
549 Create_Virtual_Extending_Project (Proj, Project);
550 Proj := Virtual_Hash.Get_Next;
555 -- If there were any kind of error during the parsing, serious
556 -- or not, then the parsing fails.
558 if Err_Vars.Total_Errors_Detected > 0 then
559 Project := Empty_Node;
562 if Project = Empty_Node or else Always_Errout_Finalize then
572 Write_Line (Exception_Information (X));
573 Write_Str ("Exception ");
574 Write_Str (Exception_Name (X));
575 Write_Line (" raised, while processing project file");
576 Project := Empty_Node;
579 ------------------------------
580 -- Pre_Parse_Context_Clause --
581 ------------------------------
583 procedure Pre_Parse_Context_Clause (Context_Clause : out With_Id) is
584 Current_With_Clause : With_Id := No_With;
585 Limited_With : Boolean := False;
587 Current_With : With_Record;
589 Current_With_Node : Project_Node_Id := Empty_Node;
592 -- Assume no context clause
594 Context_Clause := No_With;
597 -- If Token is not WITH or LIMITED, there is no context clause,
598 -- or we have exhausted the with clauses.
600 while Token = Tok_With or else Token = Tok_Limited loop
601 Current_With_Node := Default_Project_Node (Of_Kind => N_With_Clause);
602 Limited_With := Token = Tok_Limited;
605 Scan; -- scan past LIMITED
606 Expect (Tok_With, "WITH");
607 exit With_Loop when Token /= Tok_With;
612 Scan; -- scan past WITH or ","
614 Expect (Tok_String_Literal, "literal string");
616 if Token /= Tok_String_Literal then
620 -- Store path and location in table Withs
624 Location => Token_Ptr,
625 Limited_With => Limited_With,
626 Node => Current_With_Node,
629 Withs.Increment_Last;
630 Withs.Table (Withs.Last) := Current_With;
632 if Current_With_Clause = No_With then
633 Context_Clause := Withs.Last;
636 Withs.Table (Current_With_Clause).Next := Withs.Last;
639 Current_With_Clause := Withs.Last;
643 if Token = Tok_Semicolon then
644 Set_End_Of_Line (Current_With_Node);
645 Set_Previous_Line_Node (Current_With_Node);
647 -- End of (possibly multiple) with clause;
649 Scan; -- scan past the semicolon.
652 elsif Token /= Tok_Comma then
653 Error_Msg ("expected comma or semi colon", Token_Ptr);
658 Default_Project_Node (Of_Kind => N_With_Clause);
661 end Pre_Parse_Context_Clause;
664 -------------------------------
665 -- Post_Parse_Context_Clause --
666 -------------------------------
668 procedure Post_Parse_Context_Clause
669 (Context_Clause : With_Id;
670 Imported_Projects : out Project_Node_Id;
671 Project_Directory : Name_Id;
672 From_Extended : Extension_Origin)
674 Current_With_Clause : With_Id := Context_Clause;
676 Current_Project : Project_Node_Id := Empty_Node;
677 Previous_Project : Project_Node_Id := Empty_Node;
678 Next_Project : Project_Node_Id := Empty_Node;
680 Project_Directory_Path : constant String :=
681 Get_Name_String (Project_Directory);
683 Current_With : With_Record;
684 Limited_With : Boolean := False;
685 Extends_All : Boolean := False;
688 Imported_Projects := Empty_Node;
690 while Current_With_Clause /= No_With loop
691 Current_With := Withs.Table (Current_With_Clause);
692 Current_With_Clause := Current_With.Next;
694 Limited_With := Current_With.Limited_With;
697 Original_Path : constant String :=
698 Get_Name_String (Current_With.Path);
700 Imported_Path_Name : constant String :=
703 Project_Directory_Path);
705 Withed_Project : Project_Node_Id := Empty_Node;
708 if Imported_Path_Name = "" then
710 -- The project file cannot be found
712 Error_Msg_Name_1 := Current_With.Path;
714 Error_Msg ("unknown project file: {", Current_With.Location);
716 -- If this is not imported by the main project file,
717 -- display the import path.
719 if Project_Stack.Last > 1 then
720 for Index in reverse 1 .. Project_Stack.Last loop
721 Error_Msg_Name_1 := Project_Stack.Table (Index).Path_Name;
722 Error_Msg ("\imported by {", Current_With.Location);
729 Previous_Project := Current_Project;
731 if Current_Project = Empty_Node then
733 -- First with clause of the context clause
735 Current_Project := Current_With.Node;
736 Imported_Projects := Current_Project;
739 Next_Project := Current_With.Node;
740 Set_Next_With_Clause_Of (Current_Project, Next_Project);
741 Current_Project := Next_Project;
745 (Current_Project, Current_With.Path);
746 Set_Location_Of (Current_Project, Current_With.Location);
748 -- If this is a "limited with", check if we have
749 -- a circularity; if we have one, get the project id
750 -- of the limited imported project file, and don't
753 if Limited_With and then Project_Stack.Last > 1 then
755 Normed : constant String :=
756 Normalize_Pathname (Imported_Path_Name);
757 Canonical_Path_Name : Name_Id;
760 Name_Len := Normed'Length;
761 Name_Buffer (1 .. Name_Len) := Normed;
762 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
763 Canonical_Path_Name := Name_Find;
765 for Index in 1 .. Project_Stack.Last loop
766 if Project_Stack.Table (Index).Canonical_Path_Name =
769 -- We have found the limited imported project,
770 -- get its project id, and do not parse it.
772 Withed_Project := Project_Stack.Table (Index).Id;
779 -- Parse the imported project, if its project id is unknown
781 if Withed_Project = Empty_Node then
783 (Project => Withed_Project,
784 Extends_All => Extends_All,
785 Path_Name => Imported_Path_Name,
787 From_Extended => From_Extended);
790 Extends_All := Is_Extending_All (Withed_Project);
793 if Withed_Project = Empty_Node then
794 -- If parsing was not successful, remove the
797 Current_Project := Previous_Project;
799 if Current_Project = Empty_Node then
800 Imported_Projects := Empty_Node;
803 Set_Next_With_Clause_Of
804 (Current_Project, Empty_Node);
807 -- If parsing was successful, record project name
808 -- and path name in with clause
811 (Node => Current_Project,
812 To => Withed_Project,
813 Limited_With => Limited_With);
814 Set_Name_Of (Current_Project, Name_Of (Withed_Project));
815 Name_Len := Imported_Path_Name'Length;
816 Name_Buffer (1 .. Name_Len) := Imported_Path_Name;
817 Set_Path_Name_Of (Current_Project, Name_Find);
820 Set_Is_Extending_All (Current_Project);
826 end Post_Parse_Context_Clause;
828 --------------------------
829 -- Parse_Single_Project --
830 --------------------------
832 procedure Parse_Single_Project
833 (Project : out Project_Node_Id;
834 Extends_All : out Boolean;
837 From_Extended : Extension_Origin)
839 Normed_Path_Name : Name_Id;
840 Canonical_Path_Name : Name_Id;
841 Project_Directory : Name_Id;
842 Project_Scan_State : Saved_Project_Scan_State;
843 Source_Index : Source_File_Index;
845 Extending : Boolean := False;
847 Extended_Project : Project_Node_Id := Empty_Node;
849 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
850 Tree_Private_Part.Projects_Htable.Get_First;
852 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
854 Name_Of_Project : Name_Id := No_Name;
856 First_With : With_Id;
858 use Tree_Private_Part;
860 Project_Comment_State : Tree.Comment_State;
863 Extends_All := False;
866 Normed : String := Normalize_Pathname (Path_Name);
868 Name_Len := Normed'Length;
869 Name_Buffer (1 .. Name_Len) := Normed;
870 Normed_Path_Name := Name_Find;
871 Canonical_Case_File_Name (Normed);
872 Name_Len := Normed'Length;
873 Name_Buffer (1 .. Name_Len) := Normed;
874 Canonical_Path_Name := Name_Find;
877 -- Check for a circular dependency
879 for Index in 1 .. Project_Stack.Last loop
880 if Canonical_Path_Name =
881 Project_Stack.Table (Index).Canonical_Path_Name
883 Error_Msg ("circular dependency detected", Token_Ptr);
884 Error_Msg_Name_1 := Normed_Path_Name;
885 Error_Msg ("\ { is imported by", Token_Ptr);
887 for Current in reverse 1 .. Project_Stack.Last loop
888 Error_Msg_Name_1 := Project_Stack.Table (Current).Path_Name;
890 if Project_Stack.Table (Current).Canonical_Path_Name /=
894 ("\ { which itself is imported by", Token_Ptr);
897 Error_Msg ("\ {", Token_Ptr);
902 Project := Empty_Node;
907 -- Put the new path name on the stack
909 Project_Stack.Increment_Last;
910 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
911 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
914 -- Check if the project file has already been parsed.
917 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
920 Path_Id : Name_Id := Path_Name_Of (A_Project_Name_And_Node.Node);
923 if Path_Id /= No_Name then
924 Get_Name_String (Path_Id);
925 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
926 Path_Id := Name_Find;
929 if Path_Id = Canonical_Path_Name then
932 if A_Project_Name_And_Node.Extended then
934 ("cannot extend the same project file several times",
939 ("cannot extend an already imported project file",
943 elsif A_Project_Name_And_Node.Extended then
945 Is_Extending_All (A_Project_Name_And_Node.Node);
947 -- If the imported project is an extended project A,
948 -- and we are in an extended project, replace A with the
949 -- ultimate project extending A.
951 if From_Extended /= None then
953 Decl : Project_Node_Id :=
954 Project_Declaration_Of
955 (A_Project_Name_And_Node.Node);
957 Prj : Project_Node_Id :=
958 Extending_Project_Of (Decl);
962 Decl := Project_Declaration_Of (Prj);
963 exit when Extending_Project_Of (Decl) = Empty_Node;
964 Prj := Extending_Project_Of (Decl);
967 A_Project_Name_And_Node.Node := Prj;
971 ("cannot import an already extended project file",
976 Project := A_Project_Name_And_Node.Node;
977 Project_Stack.Decrement_Last;
982 A_Project_Name_And_Node := Tree_Private_Part.Projects_Htable.Get_Next;
985 -- We never encountered this project file
986 -- Save the scan state, load the project file and start to scan it.
988 Save_Project_Scan_State (Project_Scan_State);
989 Source_Index := Load_Project_File (Path_Name);
990 Tree.Save (Project_Comment_State);
992 -- If we cannot find it, we stop
994 if Source_Index = No_Source_File then
995 Project := Empty_Node;
996 Project_Stack.Decrement_Last;
1000 Prj.Err.Scanner.Initialize_Scanner (Types.No_Unit, Source_Index);
1004 if Name_From_Path = No_Name then
1006 -- The project file name is not correct (no or bad extension,
1007 -- or not following Ada identifier's syntax).
1009 Error_Msg_Name_1 := Canonical_Path_Name;
1010 Error_Msg ("?{ is not a valid path name for a project file",
1014 if Current_Verbosity >= Medium then
1015 Write_Str ("Parsing """);
1016 Write_Str (Path_Name);
1021 -- Is there any imported project?
1023 Pre_Parse_Context_Clause (First_With);
1025 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1026 Project := Default_Project_Node (Of_Kind => N_Project);
1027 Project_Stack.Table (Project_Stack.Last).Id := Project;
1028 Set_Directory_Of (Project, Project_Directory);
1029 Set_Path_Name_Of (Project, Normed_Path_Name);
1030 Set_Location_Of (Project, Token_Ptr);
1032 Expect (Tok_Project, "PROJECT");
1034 -- Mark location of PROJECT token if present
1036 if Token = Tok_Project then
1037 Set_Location_Of (Project, Token_Ptr);
1038 Scan; -- scan past project
1046 Expect (Tok_Identifier, "identifier");
1048 -- If the token is not an identifier, clear the buffer before
1049 -- exiting to indicate that the name of the project is ill-formed.
1051 if Token /= Tok_Identifier then
1056 -- Add the identifier name to the buffer
1058 Get_Name_String (Token_Name);
1059 Add_To_Buffer (Name_Buffer (1 .. Name_Len));
1061 -- Scan past the identifier
1065 -- If we have a dot, add a dot the the Buffer and look for the next
1068 exit when Token /= Tok_Dot;
1069 Add_To_Buffer (".");
1071 -- Scan past the dot
1076 -- See if this is an extending project
1078 if Token = Tok_Extends then
1080 -- Make sure that gnatmake will use mapping files
1082 Create_Mapping_File := True;
1084 -- We are extending another project
1088 Scan; -- scan past EXTENDS
1090 if Token = Tok_All then
1091 Extends_All := True;
1092 Set_Is_Extending_All (Project);
1093 Scan; -- scan past ALL
1097 -- If the name is well formed, Buffer_Last is > 0
1099 if Buffer_Last > 0 then
1101 -- The Buffer contains the name of the project
1103 Name_Len := Buffer_Last;
1104 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1105 Name_Of_Project := Name_Find;
1106 Set_Name_Of (Project, Name_Of_Project);
1108 -- To get expected name of the project file, replace dots by dashes
1110 Name_Len := Buffer_Last;
1111 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1113 for Index in 1 .. Name_Len loop
1114 if Name_Buffer (Index) = '.' then
1115 Name_Buffer (Index) := '-';
1119 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1122 Expected_Name : constant Name_Id := Name_Find;
1125 -- Output a warning if the actual name is not the expected name
1127 if Name_From_Path /= No_Name
1128 and then Expected_Name /= Name_From_Path
1130 Error_Msg_Name_1 := Expected_Name;
1131 Error_Msg ("?file name does not match unit name, " &
1132 "should be `{" & Project_File_Extension & "`",
1138 Imported_Projects : Project_Node_Id := Empty_Node;
1139 From_Ext : Extension_Origin := None;
1142 -- Extending_All is always propagated
1144 if From_Extended = Extending_All or else Extends_All then
1145 From_Ext := Extending_All;
1147 -- Otherwise, From_Extended is set to Extending_Single if the
1148 -- current project is an extending project.
1151 From_Ext := Extending_Simple;
1154 Post_Parse_Context_Clause
1155 (Context_Clause => First_With,
1156 Imported_Projects => Imported_Projects,
1157 Project_Directory => Project_Directory,
1158 From_Extended => From_Ext);
1159 Set_First_With_Clause_Of (Project, Imported_Projects);
1163 Project_Name : Name_Id :=
1164 Tree_Private_Part.Projects_Htable.Get_First.Name;
1167 -- Check if we already have a project with this name
1169 while Project_Name /= No_Name
1170 and then Project_Name /= Name_Of_Project
1172 Project_Name := Tree_Private_Part.Projects_Htable.Get_Next.Name;
1175 -- Report an error if we already have a project with this name
1177 if Project_Name /= No_Name then
1178 Error_Msg ("duplicate project name", Token_Ptr);
1181 -- Otherwise, add the name of the project to the hash table, so
1182 -- that we can check that no other subsequent project will have
1185 Tree_Private_Part.Projects_Htable.Set
1186 (K => Name_Of_Project,
1187 E => (Name => Name_Of_Project,
1189 Extended => Extended));
1196 Expect (Tok_String_Literal, "literal string");
1198 if Token = Tok_String_Literal then
1199 Set_Extended_Project_Path_Of (Project, Token_Name);
1202 Original_Path_Name : constant String :=
1203 Get_Name_String (Token_Name);
1205 Extended_Project_Path_Name : constant String :=
1206 Project_Path_Name_Of
1207 (Original_Path_Name,
1209 (Project_Directory));
1212 if Extended_Project_Path_Name = "" then
1214 -- We could not find the project file to extend
1216 Error_Msg_Name_1 := Token_Name;
1218 Error_Msg ("unknown project file: {", Token_Ptr);
1220 -- If we are not in the main project file, display the
1223 if Project_Stack.Last > 1 then
1225 Project_Stack.Table (Project_Stack.Last).Path_Name;
1226 Error_Msg ("\extended by {", Token_Ptr);
1228 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1230 Project_Stack.Table (Index).Path_Name;
1231 Error_Msg ("\imported by {", Token_Ptr);
1237 From_Ext : Extension_Origin := None;
1240 if From_Extended = Extending_All or else Extends_All then
1241 From_Ext := Extending_All;
1244 Parse_Single_Project
1245 (Project => Extended_Project,
1246 Extends_All => Extends_All,
1247 Path_Name => Extended_Project_Path_Name,
1249 From_Extended => From_Ext);
1252 -- A project that extends an extending-all project is also
1253 -- an extending-all project.
1255 if Is_Extending_All (Extended_Project) then
1256 Set_Is_Extending_All (Project);
1261 Scan; -- scan past the extended project path
1265 -- Check that a non extending-all project does not import an
1266 -- extending-all project.
1268 if not Is_Extending_All (Project) then
1270 With_Clause : Project_Node_Id := First_With_Clause_Of (Project);
1271 Imported : Project_Node_Id := Empty_Node;
1275 while With_Clause /= Empty_Node loop
1276 Imported := Project_Node_Of (With_Clause);
1278 if Is_Extending_All (With_Clause) then
1279 Error_Msg_Name_1 := Name_Of (Imported);
1280 Error_Msg ("cannot import extending-all project {",
1282 exit With_Clause_Loop;
1285 With_Clause := Next_With_Clause_Of (With_Clause);
1286 end loop With_Clause_Loop;
1290 -- Check that a project with a name including a dot either imports
1291 -- or extends the project whose name precedes the last dot.
1293 if Name_Of_Project /= No_Name then
1294 Get_Name_String (Name_Of_Project);
1300 -- Look for the last dot
1302 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1303 Name_Len := Name_Len - 1;
1306 -- If a dot was find, check if the parent project is imported
1309 if Name_Len > 0 then
1310 Name_Len := Name_Len - 1;
1313 Parent_Name : constant Name_Id := Name_Find;
1314 Parent_Found : Boolean := False;
1315 With_Clause : Project_Node_Id := First_With_Clause_Of (Project);
1318 -- If there is an extended project, check its name
1320 if Extended_Project /= Empty_Node then
1321 Parent_Found := Name_Of (Extended_Project) = Parent_Name;
1324 -- If the parent project is not the extended project,
1325 -- check each imported project until we find the parent project.
1327 while not Parent_Found and then With_Clause /= Empty_Node loop
1328 Parent_Found := Name_Of (Project_Node_Of (With_Clause))
1330 With_Clause := Next_With_Clause_Of (With_Clause);
1333 -- If the parent project was not found, report an error
1335 if not Parent_Found then
1336 Error_Msg_Name_1 := Name_Of_Project;
1337 Error_Msg_Name_2 := Parent_Name;
1338 Error_Msg ("project { does not import or extend project {",
1339 Location_Of (Project));
1344 Expect (Tok_Is, "IS");
1345 Set_End_Of_Line (Project);
1346 Set_Previous_Line_Node (Project);
1347 Set_Next_End_Node (Project);
1350 Project_Declaration : Project_Node_Id := Empty_Node;
1353 -- No need to Scan past "is", Prj.Dect.Parse will do it.
1356 (Declarations => Project_Declaration,
1357 Current_Project => Project,
1358 Extends => Extended_Project);
1359 Set_Project_Declaration_Of (Project, Project_Declaration);
1361 if Extended_Project /= Empty_Node then
1362 Set_Extending_Project_Of
1363 (Project_Declaration_Of (Extended_Project), To => Project);
1367 Expect (Tok_End, "END");
1368 Remove_Next_End_Node;
1370 -- Skip "end" if present
1372 if Token = Tok_End then
1380 -- Store the name following "end" in the Buffer. The name may be made of
1381 -- several simple names.
1384 Expect (Tok_Identifier, "identifier");
1386 -- If we don't have an identifier, clear the buffer before exiting to
1387 -- avoid checking the name.
1389 if Token /= Tok_Identifier then
1394 -- Add the identifier to the Buffer
1395 Get_Name_String (Token_Name);
1396 Add_To_Buffer (Name_Buffer (1 .. Name_Len));
1398 -- Scan past the identifier
1401 exit when Token /= Tok_Dot;
1402 Add_To_Buffer (".");
1406 -- If we have a valid name, check if it is the name of the project
1408 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1409 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1410 Get_Name_String (Name_Of (Project))
1412 -- Invalid name: report an error
1414 Error_Msg ("Expected """ &
1415 Get_Name_String (Name_Of (Project)) & """",
1420 Expect (Tok_Semicolon, "`;`");
1422 -- Check that there is no more text following the end of the project
1425 if Token = Tok_Semicolon then
1426 Set_Previous_End_Node (Project);
1429 if Token /= Tok_EOF then
1431 ("Unexpected text following end of project", Token_Ptr);
1435 -- Restore the scan state, in case we are not the main project
1437 Restore_Project_Scan_State (Project_Scan_State);
1439 -- And remove the project from the project stack
1441 Project_Stack.Decrement_Last;
1443 -- Indicate if there are unkept comments
1445 Tree.Set_Project_File_Includes_Unkept_Comments
1446 (Node => Project, To => Tree.There_Are_Unkept_Comments);
1448 -- And restore the comment state that was saved
1450 Tree.Restore (Project_Comment_State);
1451 end Parse_Single_Project;
1453 -----------------------
1454 -- Project_Name_From --
1455 -----------------------
1457 function Project_Name_From (Path_Name : String) return Name_Id is
1458 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1459 First : Natural := Canonical'Last;
1460 Last : Natural := First;
1464 if Current_Verbosity = High then
1465 Write_Str ("Project_Name_From (""");
1466 Write_Str (Canonical);
1470 -- If the path name is empty, return No_Name to indicate failure
1476 Canonical_Case_File_Name (Canonical);
1478 -- Look for the last dot in the path name
1482 Canonical (First) /= '.'
1487 -- If we have a dot, check that it is followed by the correct extension
1489 if First > 0 and then Canonical (First) = '.' then
1490 if Canonical (First .. Last) = Project_File_Extension
1493 -- Look for the last directory separator, if any
1499 and then Canonical (First) /= '/'
1500 and then Canonical (First) /= Dir_Sep
1506 -- Not the correct extension, return No_Name to indicate failure
1511 -- If no dot in the path name, return No_Name to indicate failure
1519 -- If the extension is the file name, return No_Name to indicate failure
1521 if First > Last then
1525 -- Put the name in lower case into Name_Buffer
1527 Name_Len := Last - First + 1;
1528 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1532 -- Check if it is a well formed project name. Return No_Name if it is
1536 if not Is_Letter (Name_Buffer (Index)) then
1543 exit when Index >= Name_Len;
1545 if Name_Buffer (Index) = '_' then
1546 if Name_Buffer (Index + 1) = '_' then
1551 exit when Name_Buffer (Index) = '-';
1553 if Name_Buffer (Index) /= '_'
1554 and then not Is_Alphanumeric (Name_Buffer (Index))
1562 if Index >= Name_Len then
1563 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1565 -- All checks have succeeded. Return name in Name_Buffer
1573 elsif Name_Buffer (Index) = '-' then
1577 end Project_Name_From;
1579 --------------------------
1580 -- Project_Path_Name_Of --
1581 --------------------------
1583 function Project_Path_Name_Of
1584 (Project_File_Name : String;
1588 Result : String_Access;
1591 if Current_Verbosity = High then
1592 Write_Str ("Project_Path_Name_Of (""");
1593 Write_Str (Project_File_Name);
1594 Write_Str (""", """);
1595 Write_Str (Directory);
1596 Write_Line (""");");
1599 if not Is_Absolute_Path (Project_File_Name) then
1600 -- First we try <directory>/<file_name>.<extension>
1602 if Current_Verbosity = High then
1603 Write_Str (" Trying ");
1604 Write_Str (Directory);
1605 Write_Char (Directory_Separator);
1606 Write_Str (Project_File_Name);
1607 Write_Line (Project_File_Extension);
1612 (File_Name => Directory & Directory_Separator &
1613 Project_File_Name & Project_File_Extension,
1614 Path => Project_Path.all);
1616 -- Then we try <directory>/<file_name>
1618 if Result = null then
1619 if Current_Verbosity = High then
1620 Write_Str (" Trying ");
1621 Write_Str (Directory);
1622 Write_Char (Directory_Separator);
1623 Write_Line (Project_File_Name);
1628 (File_Name => Directory & Directory_Separator &
1630 Path => Project_Path.all);
1634 if Result = null then
1636 -- Then we try <file_name>.<extension>
1638 if Current_Verbosity = High then
1639 Write_Str (" Trying ");
1640 Write_Str (Project_File_Name);
1641 Write_Line (Project_File_Extension);
1646 (File_Name => Project_File_Name & Project_File_Extension,
1647 Path => Project_Path.all);
1650 if Result = null then
1652 -- Then we try <file_name>
1654 if Current_Verbosity = High then
1655 Write_Str (" Trying ");
1656 Write_Line (Project_File_Name);
1661 (File_Name => Project_File_Name,
1662 Path => Project_Path.all);
1665 -- If we cannot find the project file, we return an empty string
1667 if Result = null then
1672 Final_Result : constant String :=
1673 GNAT.OS_Lib.Normalize_Pathname (Result.all);
1676 return Final_Result;
1679 end Project_Path_Name_Of;
1682 -- Initialize Project_Path during package elaboration
1684 if Prj_Path.all = "" then
1685 Project_Path := new String'(".");
1687 Project_Path := new String'("." & Path_Separator & Prj_Path.all);