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 3, 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
26 with Err_Vars; use Err_Vars;
28 with Osint; use Osint;
29 with Output; use Output;
30 with Prj.Com; use Prj.Com;
32 with Prj.Err; use Prj.Err;
33 with Prj.Ext; use Prj.Ext;
34 with Sinput; use Sinput;
35 with Sinput.P; use Sinput.P;
39 with Ada.Characters.Handling; use Ada.Characters.Handling;
40 with Ada.Exceptions; use Ada.Exceptions;
42 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
44 with System.HTable; use System.HTable;
46 package body Prj.Part is
48 Buffer : String_Access;
49 Buffer_Last : Natural := 0;
51 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
53 ------------------------------------
54 -- Local Packages and Subprograms --
55 ------------------------------------
57 type With_Id is new Nat;
58 No_With : constant With_Id := 0;
60 type With_Record is record
61 Path : Path_Name_Type;
62 Location : Source_Ptr;
63 Limited_With : Boolean;
64 Node : Project_Node_Id;
67 -- Information about an imported project, to be put in table Withs below
69 package Withs is new Table.Table
70 (Table_Component_Type => With_Record,
71 Table_Index_Type => With_Id,
74 Table_Increment => 100,
75 Table_Name => "Prj.Part.Withs");
76 -- Table used to store temporarily paths and locations of imported
77 -- projects. These imported projects will be effectively parsed after the
78 -- name of the current project has been extablished.
80 type Names_And_Id is record
81 Path_Name : Path_Name_Type;
82 Canonical_Path_Name : Path_Name_Type;
86 package Project_Stack is new Table.Table
87 (Table_Component_Type => Names_And_Id,
88 Table_Index_Type => Nat,
91 Table_Increment => 100,
92 Table_Name => "Prj.Part.Project_Stack");
93 -- This table is used to detect circular dependencies
94 -- for imported and extended projects and to get the project ids of
95 -- limited imported projects when there is a circularity with at least
96 -- one limited imported project file.
98 package Virtual_Hash is new System.HTable.Simple_HTable
99 (Header_Num => Header_Num,
100 Element => Project_Node_Id,
101 No_Element => Empty_Node,
102 Key => Project_Node_Id,
103 Hash => Prj.Tree.Hash,
105 -- Hash table to store the node id of the project for which a virtual
106 -- extending project need to be created.
108 package Processed_Hash is new System.HTable.Simple_HTable
109 (Header_Num => Header_Num,
112 Key => Project_Node_Id,
113 Hash => Prj.Tree.Hash,
115 -- Hash table to store the project process when looking for project that
116 -- need to have a virtual extending project, to avoid processing the same
119 procedure Create_Virtual_Extending_Project
120 (For_Project : Project_Node_Id;
121 Main_Project : Project_Node_Id;
122 In_Tree : Project_Node_Tree_Ref);
123 -- Create a virtual extending project of For_Project. Main_Project is
124 -- the extending all project.
126 -- The String_Value_Of is not set for the automatically added with
127 -- clause and keeps the default value of No_Name. This enables Prj.PP
128 -- to skip these automatically added with clauses to be processed.
130 procedure Look_For_Virtual_Projects_For
131 (Proj : Project_Node_Id;
132 In_Tree : Project_Node_Tree_Ref;
133 Potentially_Virtual : Boolean);
134 -- Look for projects that need to have a virtual extending project.
135 -- This procedure is recursive. If called with Potentially_Virtual set to
136 -- True, then Proj may need an virtual extending project; otherwise it
137 -- does not (because it is already extended), but other projects that it
138 -- imports may need to be virtually extended.
140 procedure Pre_Parse_Context_Clause
141 (In_Tree : Project_Node_Tree_Ref;
142 Context_Clause : out With_Id);
143 -- Parse the context clause of a project.
144 -- Store the paths and locations of the imported projects in table Withs.
145 -- Does nothing if there is no context clause (if the current
146 -- token is not "with" or "limited" followed by "with").
148 procedure Post_Parse_Context_Clause
149 (Context_Clause : With_Id;
150 In_Tree : Project_Node_Tree_Ref;
151 Imported_Projects : out Project_Node_Id;
152 Project_Directory : Path_Name_Type;
153 From_Extended : Extension_Origin;
154 In_Limited : Boolean;
155 Packages_To_Check : String_List_Access;
157 -- Parse the imported projects that have been stored in table Withs,
158 -- if any. From_Extended is used for the call to Parse_Single_Project
159 -- below. When In_Limited is True, the importing path includes at least
160 -- one "limited with".
162 function Project_Path_Name_Of
163 (Project_File_Name : String;
164 Directory : String) return String;
165 -- Returns the path name of a project file. Returns an empty string
166 -- if project file cannot be found.
168 function Immediate_Directory_Of
169 (Path_Name : Path_Name_Type) return Path_Name_Type;
170 -- Get the directory of the file with the specified path name.
171 -- This includes the directory separator as the last character.
172 -- Returns "./" if Path_Name contains no directory separator.
174 function Project_Name_From (Path_Name : String) return Name_Id;
175 -- Returns the name of the project that corresponds to its path name.
176 -- Returns No_Name if the path name is invalid, because the corresponding
177 -- project name does not have the syntax of an ada identifier.
179 --------------------------------------
180 -- Create_Virtual_Extending_Project --
181 --------------------------------------
183 procedure Create_Virtual_Extending_Project
184 (For_Project : Project_Node_Id;
185 Main_Project : Project_Node_Id;
186 In_Tree : Project_Node_Tree_Ref)
189 Virtual_Name : constant String :=
191 Get_Name_String (Name_Of (For_Project, In_Tree));
192 -- The name of the virtual extending project
194 Virtual_Name_Id : Name_Id;
195 -- Virtual extending project name id
197 Virtual_Path_Id : Path_Name_Type;
198 -- Fake path name of the virtual extending project. The directory is
199 -- the same directory as the extending all project.
201 Virtual_Dir_Id : constant Path_Name_Type :=
202 Immediate_Directory_Of (Path_Name_Of (Main_Project, In_Tree));
203 -- The directory of the extending all project
205 -- The source of the virtual extending project is something like:
207 -- project V$<project name> extends <project path> is
209 -- for Source_Dirs use ();
211 -- end V$<project name>;
213 -- The project directory cannot be specified during parsing; it will be
214 -- put directly in the virtual extending project data during processing.
216 -- Nodes that made up the virtual extending project
218 Virtual_Project : constant Project_Node_Id :=
220 (In_Tree, N_Project);
221 With_Clause : constant Project_Node_Id :=
223 (In_Tree, N_With_Clause);
224 Project_Declaration : constant Project_Node_Id :=
226 (In_Tree, N_Project_Declaration);
227 Source_Dirs_Declaration : constant Project_Node_Id :=
229 (In_Tree, N_Declarative_Item);
230 Source_Dirs_Attribute : constant Project_Node_Id :=
232 (In_Tree, N_Attribute_Declaration, List);
233 Source_Dirs_Expression : constant Project_Node_Id :=
235 (In_Tree, N_Expression, List);
236 Source_Dirs_Term : constant Project_Node_Id :=
238 (In_Tree, N_Term, List);
239 Source_Dirs_List : constant Project_Node_Id :=
241 (In_Tree, N_Literal_String_List, List);
244 -- Get the virtual name id
246 Name_Len := Virtual_Name'Length;
247 Name_Buffer (1 .. Name_Len) := Virtual_Name;
248 Virtual_Name_Id := Name_Find;
250 -- Get the virtual path name
252 Get_Name_String (Path_Name_Of (Main_Project, In_Tree));
255 and then Name_Buffer (Name_Len) /= Directory_Separator
256 and then Name_Buffer (Name_Len) /= '/'
258 Name_Len := Name_Len - 1;
261 Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) :=
263 Name_Len := Name_Len + Virtual_Name'Length;
264 Virtual_Path_Id := Name_Find;
268 Set_Name_Of (With_Clause, In_Tree, Virtual_Name_Id);
269 Set_Path_Name_Of (With_Clause, In_Tree, Virtual_Path_Id);
270 Set_Project_Node_Of (With_Clause, In_Tree, Virtual_Project);
271 Set_Next_With_Clause_Of
272 (With_Clause, In_Tree, First_With_Clause_Of (Main_Project, In_Tree));
273 Set_First_With_Clause_Of (Main_Project, In_Tree, With_Clause);
275 -- Virtual project node
277 Set_Name_Of (Virtual_Project, In_Tree, Virtual_Name_Id);
278 Set_Path_Name_Of (Virtual_Project, In_Tree, Virtual_Path_Id);
280 (Virtual_Project, In_Tree, Location_Of (Main_Project, In_Tree));
281 Set_Directory_Of (Virtual_Project, In_Tree, Virtual_Dir_Id);
282 Set_Project_Declaration_Of
283 (Virtual_Project, In_Tree, Project_Declaration);
284 Set_Extended_Project_Path_Of
285 (Virtual_Project, In_Tree, Path_Name_Of (For_Project, In_Tree));
287 -- Project declaration
289 Set_First_Declarative_Item_Of
290 (Project_Declaration, In_Tree, Source_Dirs_Declaration);
291 Set_Extended_Project_Of (Project_Declaration, In_Tree, For_Project);
293 -- Source_Dirs declaration
295 Set_Current_Item_Node
296 (Source_Dirs_Declaration, In_Tree, Source_Dirs_Attribute);
298 -- Source_Dirs attribute
300 Set_Name_Of (Source_Dirs_Attribute, In_Tree, Snames.Name_Source_Dirs);
302 (Source_Dirs_Attribute, In_Tree, Source_Dirs_Expression);
304 -- Source_Dirs expression
306 Set_First_Term (Source_Dirs_Expression, In_Tree, Source_Dirs_Term);
310 Set_Current_Term (Source_Dirs_Term, In_Tree, Source_Dirs_List);
312 -- Source_Dirs empty list: nothing to do
314 -- Put virtual project into Projects_Htable
316 Prj.Tree.Tree_Private_Part.Projects_Htable.Set
317 (T => In_Tree.Projects_HT,
318 K => Virtual_Name_Id,
319 E => (Name => Virtual_Name_Id,
320 Node => Virtual_Project,
321 Canonical_Path => No_Path,
323 end Create_Virtual_Extending_Project;
325 ----------------------------
326 -- Immediate_Directory_Of --
327 ----------------------------
329 function Immediate_Directory_Of
330 (Path_Name : Path_Name_Type)
331 return Path_Name_Type
334 Get_Name_String (Path_Name);
336 for Index in reverse 1 .. Name_Len loop
337 if Name_Buffer (Index) = '/'
338 or else Name_Buffer (Index) = Dir_Sep
340 -- Remove all chars after last directory separator from name
343 Name_Len := Index - 1;
353 -- There is no directory separator in name. Return "./" or ".\"
356 Name_Buffer (1) := '.';
357 Name_Buffer (2) := Dir_Sep;
359 end Immediate_Directory_Of;
361 -----------------------------------
362 -- Look_For_Virtual_Projects_For --
363 -----------------------------------
365 procedure Look_For_Virtual_Projects_For
366 (Proj : Project_Node_Id;
367 In_Tree : Project_Node_Tree_Ref;
368 Potentially_Virtual : Boolean)
371 Declaration : Project_Node_Id := Empty_Node;
372 -- Node for the project declaration of Proj
374 With_Clause : Project_Node_Id := Empty_Node;
375 -- Node for a with clause of Proj
377 Imported : Project_Node_Id := Empty_Node;
378 -- Node for a project imported by Proj
380 Extended : Project_Node_Id := Empty_Node;
381 -- Node for the eventual project extended by Proj
384 -- Nothing to do if Proj is not defined or if it has already been
387 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
388 -- Make sure the project will not be processed again
390 Processed_Hash.Set (Proj, True);
392 Declaration := Project_Declaration_Of (Proj, In_Tree);
394 if Declaration /= Empty_Node then
395 Extended := Extended_Project_Of (Declaration, In_Tree);
398 -- If this is a project that may need a virtual extending project
399 -- and it is not itself an extending project, put it in the list.
401 if Potentially_Virtual and then Extended = Empty_Node then
402 Virtual_Hash.Set (Proj, Proj);
405 -- Now check the projects it imports
407 With_Clause := First_With_Clause_Of (Proj, In_Tree);
409 while With_Clause /= Empty_Node loop
410 Imported := Project_Node_Of (With_Clause, In_Tree);
412 if Imported /= Empty_Node then
413 Look_For_Virtual_Projects_For
414 (Imported, In_Tree, Potentially_Virtual => True);
417 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
420 -- Check also the eventual project extended by Proj. As this project
421 -- is already extended, call recursively with Potentially_Virtual
424 Look_For_Virtual_Projects_For
425 (Extended, In_Tree, Potentially_Virtual => False);
427 end Look_For_Virtual_Projects_For;
434 (In_Tree : Project_Node_Tree_Ref;
435 Project : out Project_Node_Id;
436 Project_File_Name : String;
437 Always_Errout_Finalize : Boolean;
438 Packages_To_Check : String_List_Access := All_Packages;
439 Store_Comments : Boolean := False)
441 Current_Directory : constant String := Get_Current_Dir;
444 pragma Warnings (Off, Dummy);
446 Real_Project_File_Name : String_Access :=
447 Osint.To_Canonical_File_Spec
451 if Real_Project_File_Name = null then
452 Real_Project_File_Name := new String'(Project_File_Name);
455 Project := Empty_Node;
457 if Current_Verbosity >= Medium then
458 Write_Str ("GPR_PROJECT_PATH=""");
459 Write_Str (Project_Path);
464 Path_Name : constant String :=
465 Project_Path_Name_Of (Real_Project_File_Name.all,
466 Directory => Current_Directory);
469 Free (Real_Project_File_Name);
472 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
473 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
475 -- Parse the main project file
477 if Path_Name = "" then
479 ("project file """, Project_File_Name, """ not found");
480 Project := Empty_Node;
487 Extends_All => Dummy,
488 Path_Name => Path_Name,
490 From_Extended => None,
492 Packages_To_Check => Packages_To_Check,
495 -- If Project is an extending-all project, create the eventual
496 -- virtual extending projects and check that there are no illegally
497 -- imported projects.
499 if Project /= Empty_Node
500 and then Is_Extending_All (Project, In_Tree)
502 -- First look for projects that potentially need a virtual
503 -- extending project.
506 Processed_Hash.Reset;
508 -- Mark the extending all project as processed, to avoid checking
509 -- the imported projects in case of a "limited with" on this
510 -- extending all project.
512 Processed_Hash.Set (Project, True);
515 Declaration : constant Project_Node_Id :=
516 Project_Declaration_Of (Project, In_Tree);
518 Look_For_Virtual_Projects_For
519 (Extended_Project_Of (Declaration, In_Tree), In_Tree,
520 Potentially_Virtual => False);
523 -- Now, check the projects directly imported by the main project.
524 -- Remove from the potentially virtual any project extended by one
525 -- of these imported projects. For non extending imported
526 -- projects, check that they do not belong to the project tree of
527 -- the project being "extended-all" by the main project.
530 With_Clause : Project_Node_Id;
531 Imported : Project_Node_Id := Empty_Node;
532 Declaration : Project_Node_Id := Empty_Node;
535 With_Clause := First_With_Clause_Of (Project, In_Tree);
536 while With_Clause /= Empty_Node loop
537 Imported := Project_Node_Of (With_Clause, In_Tree);
539 if Imported /= Empty_Node then
540 Declaration := Project_Declaration_Of (Imported, In_Tree);
542 if Extended_Project_Of (Declaration, In_Tree) /=
547 Extended_Project_Of (Declaration, In_Tree);
548 exit when Imported = Empty_Node;
549 Virtual_Hash.Remove (Imported);
551 Project_Declaration_Of (Imported, In_Tree);
556 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
560 -- Now create all the virtual extending projects
563 Proj : Project_Node_Id := Virtual_Hash.Get_First;
565 while Proj /= Empty_Node loop
566 Create_Virtual_Extending_Project (Proj, Project, In_Tree);
567 Proj := Virtual_Hash.Get_Next;
572 -- If there were any kind of error during the parsing, serious
573 -- or not, then the parsing fails.
575 if Err_Vars.Total_Errors_Detected > 0 then
576 Project := Empty_Node;
579 if Project = Empty_Node or else Always_Errout_Finalize then
589 Write_Line (Exception_Information (X));
590 Write_Str ("Exception ");
591 Write_Str (Exception_Name (X));
592 Write_Line (" raised, while processing project file");
593 Project := Empty_Node;
596 ------------------------------
597 -- Pre_Parse_Context_Clause --
598 ------------------------------
600 procedure Pre_Parse_Context_Clause
601 (In_Tree : Project_Node_Tree_Ref;
602 Context_Clause : out With_Id)
604 Current_With_Clause : With_Id := No_With;
605 Limited_With : Boolean := False;
607 Current_With : With_Record;
609 Current_With_Node : Project_Node_Id := Empty_Node;
612 -- Assume no context clause
614 Context_Clause := No_With;
617 -- If Token is not WITH or LIMITED, there is no context clause, or we
618 -- have exhausted the with clauses.
620 while Token = Tok_With or else Token = Tok_Limited loop
622 Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree);
623 Limited_With := Token = Tok_Limited;
625 if In_Configuration then
627 ("configuration project cannot import " &
628 "other configuration projects",
633 Scan (In_Tree); -- scan past LIMITED
634 Expect (Tok_With, "WITH");
635 exit With_Loop when Token /= Tok_With;
640 Scan (In_Tree); -- scan past WITH or ","
642 Expect (Tok_String_Literal, "literal string");
644 if Token /= Tok_String_Literal then
648 -- Store path and location in table Withs
651 (Path => Path_Name_Type (Token_Name),
652 Location => Token_Ptr,
653 Limited_With => Limited_With,
654 Node => Current_With_Node,
657 Withs.Increment_Last;
658 Withs.Table (Withs.Last) := Current_With;
660 if Current_With_Clause = No_With then
661 Context_Clause := Withs.Last;
664 Withs.Table (Current_With_Clause).Next := Withs.Last;
667 Current_With_Clause := Withs.Last;
671 if Token = Tok_Semicolon then
672 Set_End_Of_Line (Current_With_Node);
673 Set_Previous_Line_Node (Current_With_Node);
675 -- End of (possibly multiple) with clause;
677 Scan (In_Tree); -- scan past the semicolon.
680 elsif Token = Tok_Comma then
681 Set_Is_Not_Last_In_List (Current_With_Node, In_Tree);
684 Error_Msg ("expected comma or semi colon", Token_Ptr);
690 (Of_Kind => N_With_Clause, In_Tree => In_Tree);
693 end Pre_Parse_Context_Clause;
695 -------------------------------
696 -- Post_Parse_Context_Clause --
697 -------------------------------
699 procedure Post_Parse_Context_Clause
700 (Context_Clause : With_Id;
701 In_Tree : Project_Node_Tree_Ref;
702 Imported_Projects : out Project_Node_Id;
703 Project_Directory : Path_Name_Type;
704 From_Extended : Extension_Origin;
705 In_Limited : Boolean;
706 Packages_To_Check : String_List_Access;
709 Current_With_Clause : With_Id := Context_Clause;
711 Current_Project : Project_Node_Id := Empty_Node;
712 Previous_Project : Project_Node_Id := Empty_Node;
713 Next_Project : Project_Node_Id := Empty_Node;
715 Project_Directory_Path : constant String :=
716 Get_Name_String (Project_Directory);
718 Current_With : With_Record;
719 Limited_With : Boolean := False;
720 Extends_All : Boolean := False;
723 Imported_Projects := Empty_Node;
725 while Current_With_Clause /= No_With loop
726 Current_With := Withs.Table (Current_With_Clause);
727 Current_With_Clause := Current_With.Next;
729 Limited_With := In_Limited or Current_With.Limited_With;
732 Original_Path : constant String :=
733 Get_Name_String (Current_With.Path);
735 Imported_Path_Name : constant String :=
737 (Original_Path, Project_Directory_Path);
739 Resolved_Path : constant String :=
742 Resolve_Links => True,
743 Case_Sensitive => True);
745 Withed_Project : Project_Node_Id := Empty_Node;
748 if Imported_Path_Name = "" then
750 -- The project file cannot be found
752 Error_Msg_File_1 := File_Name_Type (Current_With.Path);
754 Error_Msg ("unknown project file: {", Current_With.Location);
756 -- If this is not imported by the main project file,
757 -- display the import path.
759 if Project_Stack.Last > 1 then
760 for Index in reverse 1 .. Project_Stack.Last loop
762 File_Name_Type (Project_Stack.Table (Index).Path_Name);
763 Error_Msg ("\imported by {", Current_With.Location);
770 Previous_Project := Current_Project;
772 if Current_Project = Empty_Node then
774 -- First with clause of the context clause
776 Current_Project := Current_With.Node;
777 Imported_Projects := Current_Project;
780 Next_Project := Current_With.Node;
781 Set_Next_With_Clause_Of
782 (Current_Project, In_Tree, Next_Project);
783 Current_Project := Next_Project;
787 (Current_Project, In_Tree, Name_Id (Current_With.Path));
789 (Current_Project, In_Tree, Current_With.Location);
791 -- If this is a "limited with", check if we have a circularity.
792 -- If we have one, get the project id of the limited imported
793 -- project file, and do not parse it.
795 if Limited_With and then Project_Stack.Last > 1 then
797 Canonical_Path_Name : Path_Name_Type;
800 Name_Len := Resolved_Path'Length;
801 Name_Buffer (1 .. Name_Len) := Resolved_Path;
802 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
803 Canonical_Path_Name := Name_Find;
805 for Index in 1 .. Project_Stack.Last loop
806 if Project_Stack.Table (Index).Canonical_Path_Name =
809 -- We have found the limited imported project,
810 -- get its project id, and do not parse it.
812 Withed_Project := Project_Stack.Table (Index).Id;
819 -- Parse the imported project, if its project id is unknown
821 if Withed_Project = Empty_Node then
824 Project => Withed_Project,
825 Extends_All => Extends_All,
826 Path_Name => Imported_Path_Name,
828 From_Extended => From_Extended,
829 In_Limited => Limited_With,
830 Packages_To_Check => Packages_To_Check,
834 Extends_All := Is_Extending_All (Withed_Project, In_Tree);
837 if Withed_Project = Empty_Node then
838 -- If parsing was not successful, remove the
841 Current_Project := Previous_Project;
843 if Current_Project = Empty_Node then
844 Imported_Projects := Empty_Node;
847 Set_Next_With_Clause_Of
848 (Current_Project, In_Tree, Empty_Node);
851 -- If parsing was successful, record project name
852 -- and path name in with clause
855 (Node => Current_Project,
857 To => Withed_Project,
858 Limited_With => Current_With.Limited_With);
862 Name_Of (Withed_Project, In_Tree));
864 Name_Len := Resolved_Path'Length;
865 Name_Buffer (1 .. Name_Len) := Resolved_Path;
866 Set_Path_Name_Of (Current_Project, In_Tree, Name_Find);
869 Set_Is_Extending_All (Current_Project, In_Tree);
875 end Post_Parse_Context_Clause;
877 --------------------------
878 -- Parse_Single_Project --
879 --------------------------
881 procedure Parse_Single_Project
882 (In_Tree : Project_Node_Tree_Ref;
883 Project : out Project_Node_Id;
884 Extends_All : out Boolean;
887 From_Extended : Extension_Origin;
888 In_Limited : Boolean;
889 Packages_To_Check : String_List_Access;
892 Normed_Path_Name : Path_Name_Type;
893 Canonical_Path_Name : Path_Name_Type;
894 Project_Directory : Path_Name_Type;
895 Project_Scan_State : Saved_Project_Scan_State;
896 Source_Index : Source_File_Index;
898 Extending : Boolean := False;
900 Extended_Project : Project_Node_Id := Empty_Node;
902 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
903 Tree_Private_Part.Projects_Htable.Get_First
904 (In_Tree.Projects_HT);
906 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
908 Name_Of_Project : Name_Id := No_Name;
910 First_With : With_Id;
912 use Tree_Private_Part;
914 Project_Comment_State : Tree.Comment_State;
917 Extends_All := False;
920 Normed_Path : constant String := Normalize_Pathname
921 (Path_Name, Resolve_Links => False,
922 Case_Sensitive => True);
923 Canonical_Path : constant String := Normalize_Pathname
924 (Normed_Path, Resolve_Links => True,
925 Case_Sensitive => False);
928 Name_Len := Normed_Path'Length;
929 Name_Buffer (1 .. Name_Len) := Normed_Path;
930 Normed_Path_Name := Name_Find;
931 Name_Len := Canonical_Path'Length;
932 Name_Buffer (1 .. Name_Len) := Canonical_Path;
933 Canonical_Path_Name := Name_Find;
936 -- Check for a circular dependency
938 for Index in 1 .. Project_Stack.Last loop
939 if Canonical_Path_Name =
940 Project_Stack.Table (Index).Canonical_Path_Name
942 Error_Msg ("circular dependency detected", Token_Ptr);
943 Error_Msg_Name_1 := Name_Id (Normed_Path_Name);
944 Error_Msg ("\ %% is imported by", Token_Ptr);
946 for Current in reverse 1 .. Project_Stack.Last loop
948 Name_Id (Project_Stack.Table (Current).Path_Name);
950 if Project_Stack.Table (Current).Canonical_Path_Name /=
954 ("\ %% which itself is imported by", Token_Ptr);
957 Error_Msg ("\ %%", Token_Ptr);
962 Project := Empty_Node;
967 -- Put the new path name on the stack
969 Project_Stack.Increment_Last;
970 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
971 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
974 -- Check if the project file has already been parsed
977 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
979 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
982 if A_Project_Name_And_Node.Extended then
984 ("cannot extend the same project file several times",
988 ("cannot extend an already imported project file",
992 elsif A_Project_Name_And_Node.Extended then
994 Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree);
996 -- If the imported project is an extended project A,
997 -- and we are in an extended project, replace A with the
998 -- ultimate project extending A.
1000 if From_Extended /= None then
1002 Decl : Project_Node_Id :=
1003 Project_Declaration_Of
1004 (A_Project_Name_And_Node.Node, In_Tree);
1006 Prj : Project_Node_Id :=
1007 Extending_Project_Of (Decl, In_Tree);
1011 Decl := Project_Declaration_Of (Prj, In_Tree);
1012 exit when Extending_Project_Of (Decl, In_Tree) =
1014 Prj := Extending_Project_Of (Decl, In_Tree);
1017 A_Project_Name_And_Node.Node := Prj;
1021 ("cannot import an already extended project file",
1026 Project := A_Project_Name_And_Node.Node;
1027 Project_Stack.Decrement_Last;
1031 A_Project_Name_And_Node :=
1032 Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT);
1035 -- We never encountered this project file
1036 -- Save the scan state, load the project file and start to scan it.
1038 Save_Project_Scan_State (Project_Scan_State);
1039 Source_Index := Load_Project_File (Path_Name);
1040 Tree.Save (Project_Comment_State);
1042 -- If we cannot find it, we stop
1044 if Source_Index = No_Source_File then
1045 Project := Empty_Node;
1046 Project_Stack.Decrement_Last;
1050 Prj.Err.Scanner.Initialize_Scanner (Source_Index);
1054 if (not In_Configuration) and then (Name_From_Path = No_Name) then
1056 -- The project file name is not correct (no or bad extension,
1057 -- or not following Ada identifier's syntax).
1059 Error_Msg_File_1 := File_Name_Type (Canonical_Path_Name);
1060 Error_Msg ("?{ is not a valid path name for a project file",
1064 if Current_Verbosity >= Medium then
1065 Write_Str ("Parsing """);
1066 Write_Str (Path_Name);
1071 -- Is there any imported project?
1073 Pre_Parse_Context_Clause (In_Tree, First_With);
1075 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1076 Project := Default_Project_Node
1077 (Of_Kind => N_Project, In_Tree => In_Tree);
1078 Project_Stack.Table (Project_Stack.Last).Id := Project;
1079 Set_Directory_Of (Project, In_Tree, Project_Directory);
1080 Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name);
1081 Set_Location_Of (Project, In_Tree, Token_Ptr);
1083 Expect (Tok_Project, "PROJECT");
1085 -- Mark location of PROJECT token if present
1087 if Token = Tok_Project then
1088 Scan (In_Tree); -- scan past PROJECT
1089 Set_Location_Of (Project, In_Tree, Token_Ptr);
1096 Expect (Tok_Identifier, "identifier");
1098 -- If the token is not an identifier, clear the buffer before
1099 -- exiting to indicate that the name of the project is ill-formed.
1101 if Token /= Tok_Identifier then
1106 -- Add the identifier name to the buffer
1108 Get_Name_String (Token_Name);
1109 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1111 -- Scan past the identifier
1115 -- If we have a dot, add a dot to the Buffer and look for the next
1118 exit when Token /= Tok_Dot;
1119 Add_To_Buffer (".", Buffer, Buffer_Last);
1121 -- Scan past the dot
1126 -- See if this is an extending project
1128 if Token = Tok_Extends then
1130 if In_Configuration then
1132 ("extending configuration project not allowed", Token_Ptr);
1135 -- Make sure that gnatmake will use mapping files
1137 Create_Mapping_File := True;
1139 -- We are extending another project
1143 Scan (In_Tree); -- scan past EXTENDS
1145 if Token = Tok_All then
1146 Extends_All := True;
1147 Set_Is_Extending_All (Project, In_Tree);
1148 Scan (In_Tree); -- scan past ALL
1152 -- If the name is well formed, Buffer_Last is > 0
1154 if Buffer_Last > 0 then
1156 -- The Buffer contains the name of the project
1158 Name_Len := Buffer_Last;
1159 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1160 Name_Of_Project := Name_Find;
1161 Set_Name_Of (Project, In_Tree, Name_Of_Project);
1163 -- To get expected name of the project file, replace dots by dashes
1165 Name_Len := Buffer_Last;
1166 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1168 for Index in 1 .. Name_Len loop
1169 if Name_Buffer (Index) = '.' then
1170 Name_Buffer (Index) := '-';
1174 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1177 Expected_Name : constant Name_Id := Name_Find;
1178 Extension : String_Access;
1181 -- Output a warning if the actual name is not the expected name
1183 if (not In_Configuration)
1184 and then (Name_From_Path /= No_Name)
1185 and then Expected_Name /= Name_From_Path
1187 Error_Msg_Name_1 := Expected_Name;
1189 if In_Configuration then
1190 Extension := new String'(Config_Project_File_Extension);
1193 Extension := new String'(Project_File_Extension);
1196 Error_Msg ("?file name does not match project name, " &
1197 "should be `%%" & Extension.all & "`",
1203 Imported_Projects : Project_Node_Id := Empty_Node;
1204 From_Ext : Extension_Origin := None;
1207 -- Extending_All is always propagated
1209 if From_Extended = Extending_All or else Extends_All then
1210 From_Ext := Extending_All;
1212 -- Otherwise, From_Extended is set to Extending_Single if the
1213 -- current project is an extending project.
1216 From_Ext := Extending_Simple;
1219 Post_Parse_Context_Clause
1220 (In_Tree => In_Tree,
1221 Context_Clause => First_With,
1222 Imported_Projects => Imported_Projects,
1223 Project_Directory => Project_Directory,
1224 From_Extended => From_Ext,
1225 In_Limited => In_Limited,
1226 Packages_To_Check => Packages_To_Check,
1227 Depth => Depth + 1);
1228 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1231 if not In_Configuration then
1233 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1234 Tree_Private_Part.Projects_Htable.Get_First
1235 (In_Tree.Projects_HT);
1236 Project_Name : Name_Id := Name_And_Node.Name;
1239 -- Check if we already have a project with this name
1241 while Project_Name /= No_Name
1242 and then Project_Name /= Name_Of_Project
1245 Tree_Private_Part.Projects_Htable.Get_Next
1246 (In_Tree.Projects_HT);
1247 Project_Name := Name_And_Node.Name;
1250 -- Report an error if we already have a project with this name
1252 if Project_Name /= No_Name then
1253 Error_Msg_Name_1 := Project_Name;
1255 ("duplicate project name %%",
1256 Location_Of (Project, In_Tree));
1258 Name_Id (Path_Name_Of (Name_And_Node.Node, In_Tree));
1260 ("\already in %%", Location_Of (Project, In_Tree));
1263 -- Otherwise, add the name of the project to the hash table,
1264 -- so that we can check that no other subsequent project
1265 -- will have the same name.
1267 Tree_Private_Part.Projects_Htable.Set
1268 (T => In_Tree.Projects_HT,
1269 K => Name_Of_Project,
1270 E => (Name => Name_Of_Project,
1272 Canonical_Path => Canonical_Path_Name,
1273 Extended => Extended));
1281 Expect (Tok_String_Literal, "literal string");
1283 if Token = Tok_String_Literal then
1284 Set_Extended_Project_Path_Of
1287 Path_Name_Type (Token_Name));
1290 Original_Path_Name : constant String :=
1291 Get_Name_String (Token_Name);
1293 Extended_Project_Path_Name : constant String :=
1294 Project_Path_Name_Of
1295 (Original_Path_Name,
1297 (Project_Directory));
1300 if Extended_Project_Path_Name = "" then
1302 -- We could not find the project file to extend
1304 Error_Msg_Name_1 := Token_Name;
1306 Error_Msg ("unknown project file: %%", Token_Ptr);
1308 -- If we are not in the main project file, display the
1311 if Project_Stack.Last > 1 then
1314 (Project_Stack.Table (Project_Stack.Last).Path_Name);
1315 Error_Msg ("\extended by %%", Token_Ptr);
1317 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1320 (Project_Stack.Table (Index).Path_Name);
1321 Error_Msg ("\imported by %%", Token_Ptr);
1327 From_Ext : Extension_Origin := None;
1330 if From_Extended = Extending_All or else Extends_All then
1331 From_Ext := Extending_All;
1334 Parse_Single_Project
1335 (In_Tree => In_Tree,
1336 Project => Extended_Project,
1337 Extends_All => Extends_All,
1338 Path_Name => Extended_Project_Path_Name,
1340 From_Extended => From_Ext,
1341 In_Limited => In_Limited,
1342 Packages_To_Check => Packages_To_Check,
1343 Depth => Depth + 1);
1346 -- A project that extends an extending-all project is also
1347 -- an extending-all project.
1349 if Extended_Project /= Empty_Node
1350 and then Is_Extending_All (Extended_Project, In_Tree)
1352 Set_Is_Extending_All (Project, In_Tree);
1357 Scan (In_Tree); -- scan past the extended project path
1361 -- Check that a non extending-all project does not import an
1362 -- extending-all project.
1364 if not Is_Extending_All (Project, In_Tree) then
1366 With_Clause : Project_Node_Id :=
1367 First_With_Clause_Of (Project, In_Tree);
1368 Imported : Project_Node_Id := Empty_Node;
1372 while With_Clause /= Empty_Node loop
1373 Imported := Project_Node_Of (With_Clause, In_Tree);
1375 if Is_Extending_All (With_Clause, In_Tree) then
1376 Error_Msg_Name_1 := Name_Of (Imported, In_Tree);
1377 Error_Msg ("cannot import extending-all project %%",
1379 exit With_Clause_Loop;
1382 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1383 end loop With_Clause_Loop;
1387 -- Check that a project with a name including a dot either imports
1388 -- or extends the project whose name precedes the last dot.
1390 if Name_Of_Project /= No_Name then
1391 Get_Name_String (Name_Of_Project);
1397 -- Look for the last dot
1399 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1400 Name_Len := Name_Len - 1;
1403 -- If a dot was find, check if the parent project is imported
1406 if Name_Len > 0 then
1407 Name_Len := Name_Len - 1;
1410 Parent_Name : constant Name_Id := Name_Find;
1411 Parent_Found : Boolean := False;
1412 With_Clause : Project_Node_Id :=
1413 First_With_Clause_Of (Project, In_Tree);
1416 -- If there is an extended project, check its name
1418 if Extended_Project /= Empty_Node then
1420 Name_Of (Extended_Project, In_Tree) = Parent_Name;
1423 -- If the parent project is not the extended project,
1424 -- check each imported project until we find the parent project.
1426 while not Parent_Found and then With_Clause /= Empty_Node loop
1428 Name_Of (Project_Node_Of (With_Clause, In_Tree), In_Tree) =
1430 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1433 -- If the parent project was not found, report an error
1435 if not Parent_Found then
1436 Error_Msg_Name_1 := Name_Of_Project;
1437 Error_Msg_Name_2 := Parent_Name;
1438 Error_Msg ("project %% does not import or extend project %%",
1439 Location_Of (Project, In_Tree));
1444 Expect (Tok_Is, "IS");
1445 Set_End_Of_Line (Project);
1446 Set_Previous_Line_Node (Project);
1447 Set_Next_End_Node (Project);
1450 Project_Declaration : Project_Node_Id := Empty_Node;
1453 -- No need to Scan past "is", Prj.Dect.Parse will do it
1456 (In_Tree => In_Tree,
1457 Declarations => Project_Declaration,
1458 Current_Project => Project,
1459 Extends => Extended_Project,
1460 Packages_To_Check => Packages_To_Check);
1461 Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration);
1463 if Extended_Project /= Empty_Node then
1464 Set_Extending_Project_Of
1465 (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree,
1470 Expect (Tok_End, "END");
1471 Remove_Next_End_Node;
1473 -- Skip "end" if present
1475 if Token = Tok_End then
1483 -- Store the name following "end" in the Buffer. The name may be made of
1484 -- several simple names.
1487 Expect (Tok_Identifier, "identifier");
1489 -- If we don't have an identifier, clear the buffer before exiting to
1490 -- avoid checking the name.
1492 if Token /= Tok_Identifier then
1497 -- Add the identifier to the Buffer
1498 Get_Name_String (Token_Name);
1499 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1501 -- Scan past the identifier
1504 exit when Token /= Tok_Dot;
1505 Add_To_Buffer (".", Buffer, Buffer_Last);
1509 -- If we have a valid name, check if it is the name of the project
1511 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1512 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1513 Get_Name_String (Name_Of (Project, In_Tree))
1515 -- Invalid name: report an error
1517 Error_Msg ("expected """ &
1518 Get_Name_String (Name_Of (Project, In_Tree)) & """",
1523 Expect (Tok_Semicolon, "`;`");
1525 -- Check that there is no more text following the end of the project
1528 if Token = Tok_Semicolon then
1529 Set_Previous_End_Node (Project);
1532 if Token /= Tok_EOF then
1534 ("unexpected text following end of project", Token_Ptr);
1538 -- Restore the scan state, in case we are not the main project
1540 Restore_Project_Scan_State (Project_Scan_State);
1542 -- And remove the project from the project stack
1544 Project_Stack.Decrement_Last;
1546 -- Indicate if there are unkept comments
1548 Tree.Set_Project_File_Includes_Unkept_Comments
1551 To => Tree.There_Are_Unkept_Comments);
1553 -- And restore the comment state that was saved
1555 Tree.Restore (Project_Comment_State);
1556 end Parse_Single_Project;
1558 -----------------------
1559 -- Project_Name_From --
1560 -----------------------
1562 function Project_Name_From (Path_Name : String) return Name_Id is
1563 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1564 First : Natural := Canonical'Last;
1565 Last : Natural := First;
1569 if Current_Verbosity = High then
1570 Write_Str ("Project_Name_From (""");
1571 Write_Str (Canonical);
1575 -- If the path name is empty, return No_Name to indicate failure
1581 Canonical_Case_File_Name (Canonical);
1583 -- Look for the last dot in the path name
1587 Canonical (First) /= '.'
1592 -- If we have a dot, check that it is followed by the correct extension
1594 if First > 0 and then Canonical (First) = '.' then
1595 if ((not In_Configuration) and then
1596 Canonical (First .. Last) = Project_File_Extension and then
1599 (In_Configuration and then
1600 Canonical (First .. Last) = Config_Project_File_Extension and then
1603 -- Look for the last directory separator, if any
1609 and then Canonical (First) /= '/'
1610 and then Canonical (First) /= Dir_Sep
1616 -- Not the correct extension, return No_Name to indicate failure
1621 -- If no dot in the path name, return No_Name to indicate failure
1629 -- If the extension is the file name, return No_Name to indicate failure
1631 if First > Last then
1635 -- Put the name in lower case into Name_Buffer
1637 Name_Len := Last - First + 1;
1638 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1642 -- Check if it is a well formed project name. Return No_Name if it is
1646 if not Is_Letter (Name_Buffer (Index)) then
1653 exit when Index >= Name_Len;
1655 if Name_Buffer (Index) = '_' then
1656 if Name_Buffer (Index + 1) = '_' then
1661 exit when Name_Buffer (Index) = '-';
1663 if Name_Buffer (Index) /= '_'
1664 and then not Is_Alphanumeric (Name_Buffer (Index))
1672 if Index >= Name_Len then
1673 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1675 -- All checks have succeeded. Return name in Name_Buffer
1683 elsif Name_Buffer (Index) = '-' then
1687 end Project_Name_From;
1689 --------------------------
1690 -- Project_Path_Name_Of --
1691 --------------------------
1693 function Project_Path_Name_Of
1694 (Project_File_Name : String;
1695 Directory : String) return String
1697 Result : String_Access;
1700 if Current_Verbosity = High then
1701 Write_Str ("Project_Path_Name_Of (""");
1702 Write_Str (Project_File_Name);
1703 Write_Str (""", """);
1704 Write_Str (Directory);
1705 Write_Line (""");");
1708 if not Is_Absolute_Path (Project_File_Name) then
1709 -- First we try <directory>/<file_name>.<extension>
1711 if Current_Verbosity = High then
1712 Write_Str (" Trying ");
1713 Write_Str (Directory);
1714 Write_Char (Directory_Separator);
1715 Write_Str (Project_File_Name);
1716 Write_Line (Project_File_Extension);
1721 (File_Name => Directory & Directory_Separator &
1722 Project_File_Name & Project_File_Extension,
1723 Path => Project_Path);
1725 -- Then we try <directory>/<file_name>
1727 if Result = null then
1728 if Current_Verbosity = High then
1729 Write_Str (" Trying ");
1730 Write_Str (Directory);
1731 Write_Char (Directory_Separator);
1732 Write_Line (Project_File_Name);
1737 (File_Name => Directory & Directory_Separator &
1739 Path => Project_Path);
1743 if Result = null then
1745 -- Then we try <file_name>.<extension>
1747 if Current_Verbosity = High then
1748 Write_Str (" Trying ");
1749 Write_Str (Project_File_Name);
1750 Write_Line (Project_File_Extension);
1755 (File_Name => Project_File_Name & Project_File_Extension,
1756 Path => Project_Path);
1759 if Result = null then
1761 -- Then we try <file_name>
1763 if Current_Verbosity = High then
1764 Write_Str (" Trying ");
1765 Write_Line (Project_File_Name);
1770 (File_Name => Project_File_Name,
1771 Path => Project_Path);
1774 -- If we cannot find the project file, we return an empty string
1776 if Result = null then
1781 Final_Result : constant String :=
1782 GNAT.OS_Lib.Normalize_Pathname
1784 Resolve_Links => False,
1785 Case_Sensitive => True);
1788 return Final_Result;
1791 end Project_Path_Name_Of;