1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2005 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;
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 Prj.Ext; use Prj.Ext;
36 with Scans; use Scans;
37 with Sinput; use Sinput;
38 with Sinput.P; use Sinput.P;
41 with Types; use Types;
43 with Ada.Characters.Handling; use Ada.Characters.Handling;
44 with Ada.Exceptions; use Ada.Exceptions;
46 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
47 with GNAT.OS_Lib; use GNAT.OS_Lib;
49 with System.HTable; use System.HTable;
51 pragma Elaborate_All (GNAT.OS_Lib);
53 package body Prj.Part is
55 Buffer : String_Access;
56 Buffer_Last : Natural := 0;
58 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
60 type Extension_Origin is (None, Extending_Simple, Extending_All);
61 -- Type of parameter From_Extended for procedures Parse_Single_Project and
62 -- Post_Parse_Context_Clause. Extending_All means that we are parsing the
63 -- tree rooted at an extending all project.
65 ------------------------------------
66 -- Local Packages and Subprograms --
67 ------------------------------------
69 type With_Id is new Nat;
70 No_With : constant With_Id := 0;
72 type With_Record is record
74 Location : Source_Ptr;
75 Limited_With : Boolean;
76 Node : Project_Node_Id;
79 -- Information about an imported project, to be put in table Withs below
81 package Withs is new Table.Table
82 (Table_Component_Type => With_Record,
83 Table_Index_Type => With_Id,
86 Table_Increment => 50,
87 Table_Name => "Prj.Part.Withs");
88 -- Table used to store temporarily paths and locations of imported
89 -- projects. These imported projects will be effectively parsed after the
90 -- name of the current project has been extablished.
92 type Names_And_Id is record
94 Canonical_Path_Name : Name_Id;
98 package Project_Stack is new Table.Table
99 (Table_Component_Type => Names_And_Id,
100 Table_Index_Type => Nat,
101 Table_Low_Bound => 1,
103 Table_Increment => 50,
104 Table_Name => "Prj.Part.Project_Stack");
105 -- This table is used to detect circular dependencies
106 -- for imported and extended projects and to get the project ids of
107 -- limited imported projects when there is a circularity with at least
108 -- one limited imported project file.
110 package Virtual_Hash is new System.HTable.Simple_HTable
111 (Header_Num => Header_Num,
112 Element => Project_Node_Id,
113 No_Element => Empty_Node,
114 Key => Project_Node_Id,
115 Hash => Prj.Tree.Hash,
117 -- Hash table to store the node id of the project for which a virtual
118 -- extending project need to be created.
120 package Processed_Hash is new System.HTable.Simple_HTable
121 (Header_Num => Header_Num,
124 Key => Project_Node_Id,
125 Hash => Prj.Tree.Hash,
127 -- Hash table to store the project process when looking for project that
128 -- need to have a virtual extending project, to avoid processing the same
131 procedure Create_Virtual_Extending_Project
132 (For_Project : Project_Node_Id;
133 Main_Project : Project_Node_Id;
134 In_Tree : Project_Node_Tree_Ref);
135 -- Create a virtual extending project of For_Project. Main_Project is
136 -- the extending all project.
138 procedure Look_For_Virtual_Projects_For
139 (Proj : Project_Node_Id;
140 In_Tree : Project_Node_Tree_Ref;
141 Potentially_Virtual : Boolean);
142 -- Look for projects that need to have a virtual extending project.
143 -- This procedure is recursive. If called with Potentially_Virtual set to
144 -- True, then Proj may need an virtual extending project; otherwise it
145 -- does not (because it is already extended), but other projects that it
146 -- imports may need to be virtually extended.
148 procedure Pre_Parse_Context_Clause
149 (In_Tree : Project_Node_Tree_Ref;
150 Context_Clause : out With_Id);
151 -- Parse the context clause of a project.
152 -- Store the paths and locations of the imported projects in table Withs.
153 -- Does nothing if there is no context clause (if the current
154 -- token is not "with" or "limited" followed by "with").
156 procedure Post_Parse_Context_Clause
157 (Context_Clause : With_Id;
158 In_Tree : Project_Node_Tree_Ref;
159 Imported_Projects : out Project_Node_Id;
160 Project_Directory : Name_Id;
161 From_Extended : Extension_Origin;
162 In_Limited : Boolean;
163 Packages_To_Check : String_List_Access);
164 -- Parse the imported projects that have been stored in table Withs,
165 -- if any. From_Extended is used for the call to Parse_Single_Project
166 -- below. When In_Limited is True, the importing path includes at least
167 -- one "limited with".
169 procedure Parse_Single_Project
170 (In_Tree : Project_Node_Tree_Ref;
171 Project : out Project_Node_Id;
172 Extends_All : out Boolean;
175 From_Extended : Extension_Origin;
176 In_Limited : Boolean;
177 Packages_To_Check : String_List_Access);
178 -- Parse a project file.
179 -- Recursive procedure: it calls itself for imported and extended
180 -- projects. When From_Extended is not None, if the project has already
181 -- been parsed and is an extended project A, return the ultimate
182 -- (not extended) project that extends A. When In_Limited is True,
183 -- the importing path includes at least one "limited with".
185 function Project_Path_Name_Of
186 (Project_File_Name : String;
187 Directory : String) return String;
188 -- Returns the path name of a project file. Returns an empty string
189 -- if project file cannot be found.
191 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id;
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 Name_Id;
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 : Name_Id;
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 Name_Id :=
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 end Create_Virtual_Extending_Project;
338 ----------------------------
339 -- Immediate_Directory_Of --
340 ----------------------------
342 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id is
344 Get_Name_String (Path_Name);
346 for Index in reverse 1 .. Name_Len loop
347 if Name_Buffer (Index) = '/'
348 or else Name_Buffer (Index) = Dir_Sep
350 -- Remove all chars after last directory separator from name
353 Name_Len := Index - 1;
363 -- There is no directory separator in name. Return "./" or ".\"
366 Name_Buffer (1) := '.';
367 Name_Buffer (2) := Dir_Sep;
369 end Immediate_Directory_Of;
371 -----------------------------------
372 -- Look_For_Virtual_Projects_For --
373 -----------------------------------
375 procedure Look_For_Virtual_Projects_For
376 (Proj : Project_Node_Id;
377 In_Tree : Project_Node_Tree_Ref;
378 Potentially_Virtual : Boolean)
381 Declaration : Project_Node_Id := Empty_Node;
382 -- Node for the project declaration of Proj
384 With_Clause : Project_Node_Id := Empty_Node;
385 -- Node for a with clause of Proj
387 Imported : Project_Node_Id := Empty_Node;
388 -- Node for a project imported by Proj
390 Extended : Project_Node_Id := Empty_Node;
391 -- Node for the eventual project extended by Proj
394 -- Nothing to do if Proj is not defined or if it has already been
397 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
398 -- Make sure the project will not be processed again
400 Processed_Hash.Set (Proj, True);
402 Declaration := Project_Declaration_Of (Proj, In_Tree);
404 if Declaration /= Empty_Node then
405 Extended := Extended_Project_Of (Declaration, In_Tree);
408 -- If this is a project that may need a virtual extending project
409 -- and it is not itself an extending project, put it in the list.
411 if Potentially_Virtual and then Extended = Empty_Node then
412 Virtual_Hash.Set (Proj, Proj);
415 -- Now check the projects it imports
417 With_Clause := First_With_Clause_Of (Proj, In_Tree);
419 while With_Clause /= Empty_Node loop
420 Imported := Project_Node_Of (With_Clause, In_Tree);
422 if Imported /= Empty_Node then
423 Look_For_Virtual_Projects_For
424 (Imported, In_Tree, Potentially_Virtual => True);
427 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
430 -- Check also the eventual project extended by Proj. As this project
431 -- is already extended, call recursively with Potentially_Virtual
434 Look_For_Virtual_Projects_For
435 (Extended, In_Tree, Potentially_Virtual => False);
437 end Look_For_Virtual_Projects_For;
444 (In_Tree : Project_Node_Tree_Ref;
445 Project : out Project_Node_Id;
446 Project_File_Name : String;
447 Always_Errout_Finalize : Boolean;
448 Packages_To_Check : String_List_Access := All_Packages;
449 Store_Comments : Boolean := False)
451 Current_Directory : constant String := Get_Current_Dir;
455 Project := Empty_Node;
457 if Current_Verbosity >= Medium then
458 Write_Str ("ADA_PROJECT_PATH=""");
459 Write_Str (Project_Path);
464 Path_Name : constant String :=
465 Project_Path_Name_Of (Project_File_Name,
466 Directory => Current_Directory);
470 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
471 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
473 -- Parse the main project file
475 if Path_Name = "" then
477 ("project file """, Project_File_Name, """ not found");
478 Project := Empty_Node;
485 Extends_All => Dummy,
486 Path_Name => Path_Name,
488 From_Extended => None,
490 Packages_To_Check => Packages_To_Check);
492 -- If Project is an extending-all project, create the eventual
493 -- virtual extending projects and check that there are no illegally
494 -- imported projects.
496 if Project /= Empty_Node
497 and then Is_Extending_All (Project, In_Tree)
499 -- First look for projects that potentially need a virtual
500 -- extending project.
503 Processed_Hash.Reset;
505 -- Mark the extending all project as processed, to avoid checking
506 -- the imported projects in case of a "limited with" on this
507 -- extending all project.
509 Processed_Hash.Set (Project, True);
512 Declaration : constant Project_Node_Id :=
513 Project_Declaration_Of (Project, In_Tree);
515 Look_For_Virtual_Projects_For
516 (Extended_Project_Of (Declaration, In_Tree), In_Tree,
517 Potentially_Virtual => False);
520 -- Now, check the projects directly imported by the main project.
521 -- Remove from the potentially virtual any project extended by one
522 -- of these imported projects. For non extending imported
523 -- projects, check that they do not belong to the project tree of
524 -- the project being "extended-all" by the main project.
527 With_Clause : Project_Node_Id;
528 Imported : Project_Node_Id := Empty_Node;
529 Declaration : Project_Node_Id := Empty_Node;
532 With_Clause := First_With_Clause_Of (Project, In_Tree);
533 while With_Clause /= Empty_Node loop
534 Imported := Project_Node_Of (With_Clause, In_Tree);
536 if Imported /= Empty_Node then
537 Declaration := Project_Declaration_Of (Imported, In_Tree);
539 if Extended_Project_Of (Declaration, In_Tree) /=
544 Extended_Project_Of (Declaration, In_Tree);
545 exit when Imported = Empty_Node;
546 Virtual_Hash.Remove (Imported);
548 Project_Declaration_Of (Imported, In_Tree);
553 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
557 -- Now create all the virtual extending projects
560 Proj : Project_Node_Id := Virtual_Hash.Get_First;
562 while Proj /= Empty_Node loop
563 Create_Virtual_Extending_Project (Proj, Project, In_Tree);
564 Proj := Virtual_Hash.Get_Next;
569 -- If there were any kind of error during the parsing, serious
570 -- or not, then the parsing fails.
572 if Err_Vars.Total_Errors_Detected > 0 then
573 Project := Empty_Node;
576 if Project = Empty_Node or else Always_Errout_Finalize then
586 Write_Line (Exception_Information (X));
587 Write_Str ("Exception ");
588 Write_Str (Exception_Name (X));
589 Write_Line (" raised, while processing project file");
590 Project := Empty_Node;
593 ------------------------------
594 -- Pre_Parse_Context_Clause --
595 ------------------------------
597 procedure Pre_Parse_Context_Clause
598 (In_Tree : Project_Node_Tree_Ref;
599 Context_Clause : out With_Id)
601 Current_With_Clause : With_Id := No_With;
602 Limited_With : Boolean := False;
604 Current_With : With_Record;
606 Current_With_Node : Project_Node_Id := Empty_Node;
609 -- Assume no context clause
611 Context_Clause := No_With;
614 -- If Token is not WITH or LIMITED, there is no context clause, or we
615 -- have exhausted the with clauses.
617 while Token = Tok_With or else Token = Tok_Limited loop
619 Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree);
620 Limited_With := Token = Tok_Limited;
623 Scan (In_Tree); -- scan past LIMITED
624 Expect (Tok_With, "WITH");
625 exit With_Loop when Token /= Tok_With;
630 Scan (In_Tree); -- scan past WITH or ","
632 Expect (Tok_String_Literal, "literal string");
634 if Token /= Tok_String_Literal then
638 -- Store path and location in table Withs
642 Location => Token_Ptr,
643 Limited_With => Limited_With,
644 Node => Current_With_Node,
647 Withs.Increment_Last;
648 Withs.Table (Withs.Last) := Current_With;
650 if Current_With_Clause = No_With then
651 Context_Clause := Withs.Last;
654 Withs.Table (Current_With_Clause).Next := Withs.Last;
657 Current_With_Clause := Withs.Last;
661 if Token = Tok_Semicolon then
662 Set_End_Of_Line (Current_With_Node);
663 Set_Previous_Line_Node (Current_With_Node);
665 -- End of (possibly multiple) with clause;
667 Scan (In_Tree); -- scan past the semicolon.
670 elsif Token = Tok_Comma then
671 Set_Is_Not_Last_In_List (Current_With_Node, In_Tree);
674 Error_Msg ("expected comma or semi colon", Token_Ptr);
680 (Of_Kind => N_With_Clause, In_Tree => In_Tree);
683 end Pre_Parse_Context_Clause;
685 -------------------------------
686 -- Post_Parse_Context_Clause --
687 -------------------------------
689 procedure Post_Parse_Context_Clause
690 (Context_Clause : With_Id;
691 In_Tree : Project_Node_Tree_Ref;
692 Imported_Projects : out Project_Node_Id;
693 Project_Directory : Name_Id;
694 From_Extended : Extension_Origin;
695 In_Limited : Boolean;
696 Packages_To_Check : String_List_Access)
698 Current_With_Clause : With_Id := Context_Clause;
700 Current_Project : Project_Node_Id := Empty_Node;
701 Previous_Project : Project_Node_Id := Empty_Node;
702 Next_Project : Project_Node_Id := Empty_Node;
704 Project_Directory_Path : constant String :=
705 Get_Name_String (Project_Directory);
707 Current_With : With_Record;
708 Limited_With : Boolean := False;
709 Extends_All : Boolean := False;
712 Imported_Projects := Empty_Node;
714 while Current_With_Clause /= No_With loop
715 Current_With := Withs.Table (Current_With_Clause);
716 Current_With_Clause := Current_With.Next;
718 Limited_With := In_Limited or Current_With.Limited_With;
721 Original_Path : constant String :=
722 Get_Name_String (Current_With.Path);
724 Imported_Path_Name : constant String :=
726 (Original_Path, Project_Directory_Path);
728 Resolved_Path : constant String :=
731 Resolve_Links => True,
732 Case_Sensitive => True);
734 Withed_Project : Project_Node_Id := Empty_Node;
737 if Imported_Path_Name = "" then
739 -- The project file cannot be found
741 Error_Msg_Name_1 := Current_With.Path;
743 Error_Msg ("unknown project file: {", Current_With.Location);
745 -- If this is not imported by the main project file,
746 -- display the import path.
748 if Project_Stack.Last > 1 then
749 for Index in reverse 1 .. Project_Stack.Last loop
750 Error_Msg_Name_1 := Project_Stack.Table (Index).Path_Name;
751 Error_Msg ("\imported by {", Current_With.Location);
758 Previous_Project := Current_Project;
760 if Current_Project = Empty_Node then
762 -- First with clause of the context clause
764 Current_Project := Current_With.Node;
765 Imported_Projects := Current_Project;
768 Next_Project := Current_With.Node;
769 Set_Next_With_Clause_Of
770 (Current_Project, In_Tree, Next_Project);
771 Current_Project := Next_Project;
775 (Current_Project, In_Tree, Current_With.Path);
777 (Current_Project, In_Tree, Current_With.Location);
779 -- If this is a "limited with", check if we have a circularity.
780 -- If we have one, get the project id of the limited imported
781 -- project file, and do not parse it.
783 if Limited_With and then Project_Stack.Last > 1 then
785 Canonical_Path_Name : Name_Id;
788 Name_Len := Resolved_Path'Length;
789 Name_Buffer (1 .. Name_Len) := Resolved_Path;
790 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
791 Canonical_Path_Name := Name_Find;
793 for Index in 1 .. Project_Stack.Last loop
794 if Project_Stack.Table (Index).Canonical_Path_Name =
797 -- We have found the limited imported project,
798 -- get its project id, and do not parse it.
800 Withed_Project := Project_Stack.Table (Index).Id;
807 -- Parse the imported project, if its project id is unknown
809 if Withed_Project = Empty_Node then
812 Project => Withed_Project,
813 Extends_All => Extends_All,
814 Path_Name => Imported_Path_Name,
816 From_Extended => From_Extended,
817 In_Limited => Limited_With,
818 Packages_To_Check => Packages_To_Check);
821 Extends_All := Is_Extending_All (Withed_Project, In_Tree);
824 if Withed_Project = Empty_Node then
825 -- If parsing was not successful, remove the
828 Current_Project := Previous_Project;
830 if Current_Project = Empty_Node then
831 Imported_Projects := Empty_Node;
834 Set_Next_With_Clause_Of
835 (Current_Project, In_Tree, Empty_Node);
838 -- If parsing was successful, record project name
839 -- and path name in with clause
842 (Node => Current_Project,
844 To => Withed_Project,
845 Limited_With => Current_With.Limited_With);
849 Name_Of (Withed_Project, In_Tree));
851 Name_Len := Resolved_Path'Length;
852 Name_Buffer (1 .. Name_Len) := Resolved_Path;
853 Set_Path_Name_Of (Current_Project, In_Tree, Name_Find);
856 Set_Is_Extending_All (Current_Project, In_Tree);
862 end Post_Parse_Context_Clause;
864 --------------------------
865 -- Parse_Single_Project --
866 --------------------------
868 procedure Parse_Single_Project
869 (In_Tree : Project_Node_Tree_Ref;
870 Project : out Project_Node_Id;
871 Extends_All : out Boolean;
874 From_Extended : Extension_Origin;
875 In_Limited : Boolean;
876 Packages_To_Check : String_List_Access)
878 Normed_Path_Name : Name_Id;
879 Canonical_Path_Name : Name_Id;
880 Project_Directory : Name_Id;
881 Project_Scan_State : Saved_Project_Scan_State;
882 Source_Index : Source_File_Index;
884 Extending : Boolean := False;
886 Extended_Project : Project_Node_Id := Empty_Node;
888 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
889 Tree_Private_Part.Projects_Htable.Get_First
890 (In_Tree.Projects_HT);
892 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
894 Name_Of_Project : Name_Id := No_Name;
896 First_With : With_Id;
898 use Tree_Private_Part;
900 Project_Comment_State : Tree.Comment_State;
903 Extends_All := False;
906 Normed_Path : constant String := Normalize_Pathname
907 (Path_Name, Resolve_Links => False,
908 Case_Sensitive => True);
909 Canonical_Path : constant String := Normalize_Pathname
910 (Normed_Path, Resolve_Links => True,
911 Case_Sensitive => False);
914 Name_Len := Normed_Path'Length;
915 Name_Buffer (1 .. Name_Len) := Normed_Path;
916 Normed_Path_Name := Name_Find;
917 Name_Len := Canonical_Path'Length;
918 Name_Buffer (1 .. Name_Len) := Canonical_Path;
919 Canonical_Path_Name := Name_Find;
922 -- Check for a circular dependency
924 for Index in 1 .. Project_Stack.Last loop
925 if Canonical_Path_Name =
926 Project_Stack.Table (Index).Canonical_Path_Name
928 Error_Msg ("circular dependency detected", Token_Ptr);
929 Error_Msg_Name_1 := Normed_Path_Name;
930 Error_Msg ("\ { is imported by", Token_Ptr);
932 for Current in reverse 1 .. Project_Stack.Last loop
933 Error_Msg_Name_1 := Project_Stack.Table (Current).Path_Name;
935 if Project_Stack.Table (Current).Canonical_Path_Name /=
939 ("\ { which itself is imported by", Token_Ptr);
942 Error_Msg ("\ {", Token_Ptr);
947 Project := Empty_Node;
952 -- Put the new path name on the stack
954 Project_Stack.Increment_Last;
955 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
956 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
959 -- Check if the project file has already been parsed
962 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
964 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
967 if A_Project_Name_And_Node.Extended then
969 ("cannot extend the same project file several times",
973 ("cannot extend an already imported project file",
977 elsif A_Project_Name_And_Node.Extended then
979 Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree);
981 -- If the imported project is an extended project A,
982 -- and we are in an extended project, replace A with the
983 -- ultimate project extending A.
985 if From_Extended /= None then
987 Decl : Project_Node_Id :=
988 Project_Declaration_Of
989 (A_Project_Name_And_Node.Node, In_Tree);
991 Prj : Project_Node_Id :=
992 Extending_Project_Of (Decl, In_Tree);
996 Decl := Project_Declaration_Of (Prj, In_Tree);
997 exit when Extending_Project_Of (Decl, In_Tree) =
999 Prj := Extending_Project_Of (Decl, In_Tree);
1002 A_Project_Name_And_Node.Node := Prj;
1006 ("cannot import an already extended project file",
1011 Project := A_Project_Name_And_Node.Node;
1012 Project_Stack.Decrement_Last;
1016 A_Project_Name_And_Node :=
1017 Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT);
1020 -- We never encountered this project file
1021 -- Save the scan state, load the project file and start to scan it.
1023 Save_Project_Scan_State (Project_Scan_State);
1024 Source_Index := Load_Project_File (Path_Name);
1025 Tree.Save (Project_Comment_State);
1027 -- If we cannot find it, we stop
1029 if Source_Index = No_Source_File then
1030 Project := Empty_Node;
1031 Project_Stack.Decrement_Last;
1035 Prj.Err.Scanner.Initialize_Scanner (Types.No_Unit, Source_Index);
1039 if Name_From_Path = No_Name then
1041 -- The project file name is not correct (no or bad extension,
1042 -- or not following Ada identifier's syntax).
1044 Error_Msg_Name_1 := Canonical_Path_Name;
1045 Error_Msg ("?{ is not a valid path name for a project file",
1049 if Current_Verbosity >= Medium then
1050 Write_Str ("Parsing """);
1051 Write_Str (Path_Name);
1056 -- Is there any imported project?
1058 Pre_Parse_Context_Clause (In_Tree, First_With);
1060 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1061 Project := Default_Project_Node
1062 (Of_Kind => N_Project, In_Tree => In_Tree);
1063 Project_Stack.Table (Project_Stack.Last).Id := Project;
1064 Set_Directory_Of (Project, In_Tree, Project_Directory);
1065 Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name);
1066 Set_Location_Of (Project, In_Tree, Token_Ptr);
1068 Expect (Tok_Project, "PROJECT");
1070 -- Mark location of PROJECT token if present
1072 if Token = Tok_Project then
1073 Scan (In_Tree); -- scan past PROJECT
1074 Set_Location_Of (Project, In_Tree, Token_Ptr);
1081 Expect (Tok_Identifier, "identifier");
1083 -- If the token is not an identifier, clear the buffer before
1084 -- exiting to indicate that the name of the project is ill-formed.
1086 if Token /= Tok_Identifier then
1091 -- Add the identifier name to the buffer
1093 Get_Name_String (Token_Name);
1094 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1096 -- Scan past the identifier
1100 -- If we have a dot, add a dot the the Buffer and look for the next
1103 exit when Token /= Tok_Dot;
1104 Add_To_Buffer (".", Buffer, Buffer_Last);
1106 -- Scan past the dot
1111 -- See if this is an extending project
1113 if Token = Tok_Extends then
1115 -- Make sure that gnatmake will use mapping files
1117 Create_Mapping_File := True;
1119 -- We are extending another project
1123 Scan (In_Tree); -- scan past EXTENDS
1125 if Token = Tok_All then
1126 Extends_All := True;
1127 Set_Is_Extending_All (Project, In_Tree);
1128 Scan (In_Tree); -- scan past ALL
1132 -- If the name is well formed, Buffer_Last is > 0
1134 if Buffer_Last > 0 then
1136 -- The Buffer contains the name of the project
1138 Name_Len := Buffer_Last;
1139 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1140 Name_Of_Project := Name_Find;
1141 Set_Name_Of (Project, In_Tree, Name_Of_Project);
1143 -- To get expected name of the project file, replace dots by dashes
1145 Name_Len := Buffer_Last;
1146 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1148 for Index in 1 .. Name_Len loop
1149 if Name_Buffer (Index) = '.' then
1150 Name_Buffer (Index) := '-';
1154 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1157 Expected_Name : constant Name_Id := Name_Find;
1160 -- Output a warning if the actual name is not the expected name
1162 if Name_From_Path /= No_Name
1163 and then Expected_Name /= Name_From_Path
1165 Error_Msg_Name_1 := Expected_Name;
1166 Error_Msg ("?file name does not match unit name, " &
1167 "should be `{" & Project_File_Extension & "`",
1173 Imported_Projects : Project_Node_Id := Empty_Node;
1174 From_Ext : Extension_Origin := None;
1177 -- Extending_All is always propagated
1179 if From_Extended = Extending_All or else Extends_All then
1180 From_Ext := Extending_All;
1182 -- Otherwise, From_Extended is set to Extending_Single if the
1183 -- current project is an extending project.
1186 From_Ext := Extending_Simple;
1189 Post_Parse_Context_Clause
1190 (In_Tree => In_Tree,
1191 Context_Clause => First_With,
1192 Imported_Projects => Imported_Projects,
1193 Project_Directory => Project_Directory,
1194 From_Extended => From_Ext,
1195 In_Limited => In_Limited,
1196 Packages_To_Check => Packages_To_Check);
1197 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1201 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1202 Tree_Private_Part.Projects_Htable.Get_First
1203 (In_Tree.Projects_HT);
1204 Project_Name : Name_Id := Name_And_Node.Name;
1207 -- Check if we already have a project with this name
1209 while Project_Name /= No_Name
1210 and then Project_Name /= Name_Of_Project
1213 Tree_Private_Part.Projects_Htable.Get_Next
1214 (In_Tree.Projects_HT);
1215 Project_Name := Name_And_Node.Name;
1218 -- Report an error if we already have a project with this name
1220 if Project_Name /= No_Name then
1221 Error_Msg_Name_1 := Project_Name;
1223 ("duplicate project name {", Location_Of (Project, In_Tree));
1225 Path_Name_Of (Name_And_Node.Node, In_Tree);
1227 ("\already in {", Location_Of (Project, In_Tree));
1230 -- Otherwise, add the name of the project to the hash table, so
1231 -- that we can check that no other subsequent project will have
1234 Tree_Private_Part.Projects_Htable.Set
1235 (T => In_Tree.Projects_HT,
1236 K => Name_Of_Project,
1237 E => (Name => Name_Of_Project,
1239 Canonical_Path => Canonical_Path_Name,
1240 Extended => Extended));
1247 Expect (Tok_String_Literal, "literal string");
1249 if Token = Tok_String_Literal then
1250 Set_Extended_Project_Path_Of (Project, In_Tree, Token_Name);
1253 Original_Path_Name : constant String :=
1254 Get_Name_String (Token_Name);
1256 Extended_Project_Path_Name : constant String :=
1257 Project_Path_Name_Of
1258 (Original_Path_Name,
1260 (Project_Directory));
1263 if Extended_Project_Path_Name = "" then
1265 -- We could not find the project file to extend
1267 Error_Msg_Name_1 := Token_Name;
1269 Error_Msg ("unknown project file: {", Token_Ptr);
1271 -- If we are not in the main project file, display the
1274 if Project_Stack.Last > 1 then
1276 Project_Stack.Table (Project_Stack.Last).Path_Name;
1277 Error_Msg ("\extended by {", Token_Ptr);
1279 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1281 Project_Stack.Table (Index).Path_Name;
1282 Error_Msg ("\imported by {", Token_Ptr);
1288 From_Ext : Extension_Origin := None;
1291 if From_Extended = Extending_All or else Extends_All then
1292 From_Ext := Extending_All;
1295 Parse_Single_Project
1296 (In_Tree => In_Tree,
1297 Project => Extended_Project,
1298 Extends_All => Extends_All,
1299 Path_Name => Extended_Project_Path_Name,
1301 From_Extended => From_Ext,
1302 In_Limited => In_Limited,
1303 Packages_To_Check => Packages_To_Check);
1306 -- A project that extends an extending-all project is also
1307 -- an extending-all project.
1309 if Extended_Project /= Empty_Node
1310 and then Is_Extending_All (Extended_Project, In_Tree)
1312 Set_Is_Extending_All (Project, In_Tree);
1317 Scan (In_Tree); -- scan past the extended project path
1321 -- Check that a non extending-all project does not import an
1322 -- extending-all project.
1324 if not Is_Extending_All (Project, In_Tree) then
1326 With_Clause : Project_Node_Id :=
1327 First_With_Clause_Of (Project, In_Tree);
1328 Imported : Project_Node_Id := Empty_Node;
1332 while With_Clause /= Empty_Node loop
1333 Imported := Project_Node_Of (With_Clause, In_Tree);
1335 if Is_Extending_All (With_Clause, In_Tree) then
1336 Error_Msg_Name_1 := Name_Of (Imported, In_Tree);
1337 Error_Msg ("cannot import extending-all project {",
1339 exit With_Clause_Loop;
1342 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1343 end loop With_Clause_Loop;
1347 -- Check that a project with a name including a dot either imports
1348 -- or extends the project whose name precedes the last dot.
1350 if Name_Of_Project /= No_Name then
1351 Get_Name_String (Name_Of_Project);
1357 -- Look for the last dot
1359 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1360 Name_Len := Name_Len - 1;
1363 -- If a dot was find, check if the parent project is imported
1366 if Name_Len > 0 then
1367 Name_Len := Name_Len - 1;
1370 Parent_Name : constant Name_Id := Name_Find;
1371 Parent_Found : Boolean := False;
1372 With_Clause : Project_Node_Id :=
1373 First_With_Clause_Of (Project, In_Tree);
1376 -- If there is an extended project, check its name
1378 if Extended_Project /= Empty_Node then
1380 Name_Of (Extended_Project, In_Tree) = Parent_Name;
1383 -- If the parent project is not the extended project,
1384 -- check each imported project until we find the parent project.
1386 while not Parent_Found and then With_Clause /= Empty_Node loop
1388 Name_Of (Project_Node_Of (With_Clause, In_Tree), In_Tree) =
1390 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1393 -- If the parent project was not found, report an error
1395 if not Parent_Found then
1396 Error_Msg_Name_1 := Name_Of_Project;
1397 Error_Msg_Name_2 := Parent_Name;
1398 Error_Msg ("project { does not import or extend project {",
1399 Location_Of (Project, In_Tree));
1404 Expect (Tok_Is, "IS");
1405 Set_End_Of_Line (Project);
1406 Set_Previous_Line_Node (Project);
1407 Set_Next_End_Node (Project);
1410 Project_Declaration : Project_Node_Id := Empty_Node;
1413 -- No need to Scan past "is", Prj.Dect.Parse will do it
1416 (In_Tree => In_Tree,
1417 Declarations => Project_Declaration,
1418 Current_Project => Project,
1419 Extends => Extended_Project,
1420 Packages_To_Check => Packages_To_Check);
1421 Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration);
1423 if Extended_Project /= Empty_Node then
1424 Set_Extending_Project_Of
1425 (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree,
1430 Expect (Tok_End, "END");
1431 Remove_Next_End_Node;
1433 -- Skip "end" if present
1435 if Token = Tok_End then
1443 -- Store the name following "end" in the Buffer. The name may be made of
1444 -- several simple names.
1447 Expect (Tok_Identifier, "identifier");
1449 -- If we don't have an identifier, clear the buffer before exiting to
1450 -- avoid checking the name.
1452 if Token /= Tok_Identifier then
1457 -- Add the identifier to the Buffer
1458 Get_Name_String (Token_Name);
1459 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1461 -- Scan past the identifier
1464 exit when Token /= Tok_Dot;
1465 Add_To_Buffer (".", Buffer, Buffer_Last);
1469 -- If we have a valid name, check if it is the name of the project
1471 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1472 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1473 Get_Name_String (Name_Of (Project, In_Tree))
1475 -- Invalid name: report an error
1477 Error_Msg ("expected """ &
1478 Get_Name_String (Name_Of (Project, In_Tree)) & """",
1483 Expect (Tok_Semicolon, "`;`");
1485 -- Check that there is no more text following the end of the project
1488 if Token = Tok_Semicolon then
1489 Set_Previous_End_Node (Project);
1492 if Token /= Tok_EOF then
1494 ("unexpected text following end of project", Token_Ptr);
1498 -- Restore the scan state, in case we are not the main project
1500 Restore_Project_Scan_State (Project_Scan_State);
1502 -- And remove the project from the project stack
1504 Project_Stack.Decrement_Last;
1506 -- Indicate if there are unkept comments
1508 Tree.Set_Project_File_Includes_Unkept_Comments
1511 To => Tree.There_Are_Unkept_Comments);
1513 -- And restore the comment state that was saved
1515 Tree.Restore (Project_Comment_State);
1516 end Parse_Single_Project;
1518 -----------------------
1519 -- Project_Name_From --
1520 -----------------------
1522 function Project_Name_From (Path_Name : String) return Name_Id is
1523 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1524 First : Natural := Canonical'Last;
1525 Last : Natural := First;
1529 if Current_Verbosity = High then
1530 Write_Str ("Project_Name_From (""");
1531 Write_Str (Canonical);
1535 -- If the path name is empty, return No_Name to indicate failure
1541 Canonical_Case_File_Name (Canonical);
1543 -- Look for the last dot in the path name
1547 Canonical (First) /= '.'
1552 -- If we have a dot, check that it is followed by the correct extension
1554 if First > 0 and then Canonical (First) = '.' then
1555 if Canonical (First .. Last) = Project_File_Extension
1558 -- Look for the last directory separator, if any
1564 and then Canonical (First) /= '/'
1565 and then Canonical (First) /= Dir_Sep
1571 -- Not the correct extension, return No_Name to indicate failure
1576 -- If no dot in the path name, return No_Name to indicate failure
1584 -- If the extension is the file name, return No_Name to indicate failure
1586 if First > Last then
1590 -- Put the name in lower case into Name_Buffer
1592 Name_Len := Last - First + 1;
1593 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1597 -- Check if it is a well formed project name. Return No_Name if it is
1601 if not Is_Letter (Name_Buffer (Index)) then
1608 exit when Index >= Name_Len;
1610 if Name_Buffer (Index) = '_' then
1611 if Name_Buffer (Index + 1) = '_' then
1616 exit when Name_Buffer (Index) = '-';
1618 if Name_Buffer (Index) /= '_'
1619 and then not Is_Alphanumeric (Name_Buffer (Index))
1627 if Index >= Name_Len then
1628 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1630 -- All checks have succeeded. Return name in Name_Buffer
1638 elsif Name_Buffer (Index) = '-' then
1642 end Project_Name_From;
1644 --------------------------
1645 -- Project_Path_Name_Of --
1646 --------------------------
1648 function Project_Path_Name_Of
1649 (Project_File_Name : String;
1650 Directory : String) return String
1652 Result : String_Access;
1655 if Current_Verbosity = High then
1656 Write_Str ("Project_Path_Name_Of (""");
1657 Write_Str (Project_File_Name);
1658 Write_Str (""", """);
1659 Write_Str (Directory);
1660 Write_Line (""");");
1663 if not Is_Absolute_Path (Project_File_Name) then
1664 -- First we try <directory>/<file_name>.<extension>
1666 if Current_Verbosity = High then
1667 Write_Str (" Trying ");
1668 Write_Str (Directory);
1669 Write_Char (Directory_Separator);
1670 Write_Str (Project_File_Name);
1671 Write_Line (Project_File_Extension);
1676 (File_Name => Directory & Directory_Separator &
1677 Project_File_Name & Project_File_Extension,
1678 Path => Project_Path);
1680 -- Then we try <directory>/<file_name>
1682 if Result = null then
1683 if Current_Verbosity = High then
1684 Write_Str (" Trying ");
1685 Write_Str (Directory);
1686 Write_Char (Directory_Separator);
1687 Write_Line (Project_File_Name);
1692 (File_Name => Directory & Directory_Separator &
1694 Path => Project_Path);
1698 if Result = null then
1700 -- Then we try <file_name>.<extension>
1702 if Current_Verbosity = High then
1703 Write_Str (" Trying ");
1704 Write_Str (Project_File_Name);
1705 Write_Line (Project_File_Extension);
1710 (File_Name => Project_File_Name & Project_File_Extension,
1711 Path => Project_Path);
1714 if Result = null then
1716 -- Then we try <file_name>
1718 if Current_Verbosity = High then
1719 Write_Str (" Trying ");
1720 Write_Line (Project_File_Name);
1725 (File_Name => Project_File_Name,
1726 Path => Project_Path);
1729 -- If we cannot find the project file, we return an empty string
1731 if Result = null then
1736 Final_Result : constant String :=
1737 GNAT.OS_Lib.Normalize_Pathname
1739 Resolve_Links => False,
1740 Case_Sensitive => True);
1743 return Final_Result;
1746 end Project_Path_Name_Of;