1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2006, 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 Sinput
; use Sinput
;
37 with Sinput
.P
; use Sinput
.P
;
41 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
42 with Ada
.Exceptions
; use Ada
.Exceptions
;
44 with GNAT
.Directory_Operations
; use GNAT
.Directory_Operations
;
46 with System
.HTable
; use System
.HTable
;
48 package body Prj
.Part
is
50 Buffer
: String_Access
;
51 Buffer_Last
: Natural := 0;
53 Dir_Sep
: Character renames GNAT
.OS_Lib
.Directory_Separator
;
55 type Extension_Origin
is (None
, Extending_Simple
, Extending_All
);
56 -- Type of parameter From_Extended for procedures Parse_Single_Project and
57 -- Post_Parse_Context_Clause. Extending_All means that we are parsing the
58 -- tree rooted at an extending all project.
60 ------------------------------------
61 -- Local Packages and Subprograms --
62 ------------------------------------
64 type With_Id
is new Nat
;
65 No_With
: constant With_Id
:= 0;
67 type With_Record
is record
69 Location
: Source_Ptr
;
70 Limited_With
: Boolean;
71 Node
: Project_Node_Id
;
74 -- Information about an imported project, to be put in table Withs below
76 package Withs
is new Table
.Table
77 (Table_Component_Type
=> With_Record
,
78 Table_Index_Type
=> With_Id
,
81 Table_Increment
=> 100,
82 Table_Name
=> "Prj.Part.Withs");
83 -- Table used to store temporarily paths and locations of imported
84 -- projects. These imported projects will be effectively parsed after the
85 -- name of the current project has been extablished.
87 type Names_And_Id
is record
89 Canonical_Path_Name
: Name_Id
;
93 package Project_Stack
is new Table
.Table
94 (Table_Component_Type
=> Names_And_Id
,
95 Table_Index_Type
=> Nat
,
98 Table_Increment
=> 100,
99 Table_Name
=> "Prj.Part.Project_Stack");
100 -- This table is used to detect circular dependencies
101 -- for imported and extended projects and to get the project ids of
102 -- limited imported projects when there is a circularity with at least
103 -- one limited imported project file.
105 package Virtual_Hash
is new System
.HTable
.Simple_HTable
106 (Header_Num
=> Header_Num
,
107 Element
=> Project_Node_Id
,
108 No_Element
=> Empty_Node
,
109 Key
=> Project_Node_Id
,
110 Hash
=> Prj
.Tree
.Hash
,
112 -- Hash table to store the node id of the project for which a virtual
113 -- extending project need to be created.
115 package Processed_Hash
is new System
.HTable
.Simple_HTable
116 (Header_Num
=> Header_Num
,
119 Key
=> Project_Node_Id
,
120 Hash
=> Prj
.Tree
.Hash
,
122 -- Hash table to store the project process when looking for project that
123 -- need to have a virtual extending project, to avoid processing the same
126 procedure Create_Virtual_Extending_Project
127 (For_Project
: Project_Node_Id
;
128 Main_Project
: Project_Node_Id
;
129 In_Tree
: Project_Node_Tree_Ref
);
130 -- Create a virtual extending project of For_Project. Main_Project is
131 -- the extending all project.
133 -- The String_Value_Of is not set for the automatically added with
134 -- clause and keeps the default value of No_Name. This enables Prj.PP
135 -- to skip these automatically added with clauses to be processed.
137 procedure Look_For_Virtual_Projects_For
138 (Proj
: Project_Node_Id
;
139 In_Tree
: Project_Node_Tree_Ref
;
140 Potentially_Virtual
: Boolean);
141 -- Look for projects that need to have a virtual extending project.
142 -- This procedure is recursive. If called with Potentially_Virtual set to
143 -- True, then Proj may need an virtual extending project; otherwise it
144 -- does not (because it is already extended), but other projects that it
145 -- imports may need to be virtually extended.
147 procedure Pre_Parse_Context_Clause
148 (In_Tree
: Project_Node_Tree_Ref
;
149 Context_Clause
: out With_Id
);
150 -- Parse the context clause of a project.
151 -- Store the paths and locations of the imported projects in table Withs.
152 -- Does nothing if there is no context clause (if the current
153 -- token is not "with" or "limited" followed by "with").
155 procedure Post_Parse_Context_Clause
156 (Context_Clause
: With_Id
;
157 In_Tree
: Project_Node_Tree_Ref
;
158 Imported_Projects
: out Project_Node_Id
;
159 Project_Directory
: Name_Id
;
160 From_Extended
: Extension_Origin
;
161 In_Limited
: Boolean;
162 Packages_To_Check
: String_List_Access
);
163 -- Parse the imported projects that have been stored in table Withs,
164 -- if any. From_Extended is used for the call to Parse_Single_Project
165 -- below. When In_Limited is True, the importing path includes at least
166 -- one "limited with".
168 procedure Parse_Single_Project
169 (In_Tree
: Project_Node_Tree_Ref
;
170 Project
: out Project_Node_Id
;
171 Extends_All
: out Boolean;
174 From_Extended
: Extension_Origin
;
175 In_Limited
: Boolean;
176 Packages_To_Check
: String_List_Access
);
177 -- Parse a project file.
178 -- Recursive procedure: it calls itself for imported and extended
179 -- projects. When From_Extended is not None, if the project has already
180 -- been parsed and is an extended project A, return the ultimate
181 -- (not extended) project that extends A. When In_Limited is True,
182 -- the importing path includes at least one "limited with".
184 function Project_Path_Name_Of
185 (Project_File_Name
: String;
186 Directory
: String) return String;
187 -- Returns the path name of a project file. Returns an empty string
188 -- if project file cannot be found.
190 function Immediate_Directory_Of
(Path_Name
: Name_Id
) return Name_Id
;
191 -- Get the directory of the file with the specified path name.
192 -- This includes the directory separator as the last character.
193 -- Returns "./" if Path_Name contains no directory separator.
195 function Project_Name_From
(Path_Name
: String) return Name_Id
;
196 -- Returns the name of the project that corresponds to its path name.
197 -- Returns No_Name if the path name is invalid, because the corresponding
198 -- project name does not have the syntax of an ada identifier.
200 --------------------------------------
201 -- Create_Virtual_Extending_Project --
202 --------------------------------------
204 procedure Create_Virtual_Extending_Project
205 (For_Project
: Project_Node_Id
;
206 Main_Project
: Project_Node_Id
;
207 In_Tree
: Project_Node_Tree_Ref
)
210 Virtual_Name
: constant String :=
212 Get_Name_String
(Name_Of
(For_Project
, In_Tree
));
213 -- The name of the virtual extending project
215 Virtual_Name_Id
: Name_Id
;
216 -- Virtual extending project name id
218 Virtual_Path_Id
: Name_Id
;
219 -- Fake path name of the virtual extending project. The directory is
220 -- the same directory as the extending all project.
222 Virtual_Dir_Id
: constant Name_Id
:=
223 Immediate_Directory_Of
(Path_Name_Of
(Main_Project
, In_Tree
));
224 -- The directory of the extending all project
226 -- The source of the virtual extending project is something like:
228 -- project V$<project name> extends <project path> is
230 -- for Source_Dirs use ();
232 -- end V$<project name>;
234 -- The project directory cannot be specified during parsing; it will be
235 -- put directly in the virtual extending project data during processing.
237 -- Nodes that made up the virtual extending project
239 Virtual_Project
: constant Project_Node_Id
:=
241 (In_Tree
, N_Project
);
242 With_Clause
: constant Project_Node_Id
:=
244 (In_Tree
, N_With_Clause
);
245 Project_Declaration
: constant Project_Node_Id
:=
247 (In_Tree
, N_Project_Declaration
);
248 Source_Dirs_Declaration
: constant Project_Node_Id
:=
250 (In_Tree
, N_Declarative_Item
);
251 Source_Dirs_Attribute
: constant Project_Node_Id
:=
253 (In_Tree
, N_Attribute_Declaration
, List
);
254 Source_Dirs_Expression
: constant Project_Node_Id
:=
256 (In_Tree
, N_Expression
, List
);
257 Source_Dirs_Term
: constant Project_Node_Id
:=
259 (In_Tree
, N_Term
, List
);
260 Source_Dirs_List
: constant Project_Node_Id
:=
262 (In_Tree
, N_Literal_String_List
, List
);
265 -- Get the virtual name id
267 Name_Len
:= Virtual_Name
'Length;
268 Name_Buffer
(1 .. Name_Len
) := Virtual_Name
;
269 Virtual_Name_Id
:= Name_Find
;
271 -- Get the virtual path name
273 Get_Name_String
(Path_Name_Of
(Main_Project
, In_Tree
));
276 and then Name_Buffer
(Name_Len
) /= Directory_Separator
277 and then Name_Buffer
(Name_Len
) /= '/'
279 Name_Len
:= Name_Len
- 1;
282 Name_Buffer
(Name_Len
+ 1 .. Name_Len
+ Virtual_Name
'Length) :=
284 Name_Len
:= Name_Len
+ Virtual_Name
'Length;
285 Virtual_Path_Id
:= Name_Find
;
289 Set_Name_Of
(With_Clause
, In_Tree
, Virtual_Name_Id
);
290 Set_Path_Name_Of
(With_Clause
, In_Tree
, Virtual_Path_Id
);
291 Set_Project_Node_Of
(With_Clause
, In_Tree
, Virtual_Project
);
292 Set_Next_With_Clause_Of
293 (With_Clause
, In_Tree
, First_With_Clause_Of
(Main_Project
, In_Tree
));
294 Set_First_With_Clause_Of
(Main_Project
, In_Tree
, With_Clause
);
296 -- Virtual project node
298 Set_Name_Of
(Virtual_Project
, In_Tree
, Virtual_Name_Id
);
299 Set_Path_Name_Of
(Virtual_Project
, In_Tree
, Virtual_Path_Id
);
301 (Virtual_Project
, In_Tree
, Location_Of
(Main_Project
, In_Tree
));
302 Set_Directory_Of
(Virtual_Project
, In_Tree
, Virtual_Dir_Id
);
303 Set_Project_Declaration_Of
304 (Virtual_Project
, In_Tree
, Project_Declaration
);
305 Set_Extended_Project_Path_Of
306 (Virtual_Project
, In_Tree
, Path_Name_Of
(For_Project
, In_Tree
));
308 -- Project declaration
310 Set_First_Declarative_Item_Of
311 (Project_Declaration
, In_Tree
, Source_Dirs_Declaration
);
312 Set_Extended_Project_Of
(Project_Declaration
, In_Tree
, For_Project
);
314 -- Source_Dirs declaration
316 Set_Current_Item_Node
317 (Source_Dirs_Declaration
, In_Tree
, Source_Dirs_Attribute
);
319 -- Source_Dirs attribute
321 Set_Name_Of
(Source_Dirs_Attribute
, In_Tree
, Snames
.Name_Source_Dirs
);
323 (Source_Dirs_Attribute
, In_Tree
, Source_Dirs_Expression
);
325 -- Source_Dirs expression
327 Set_First_Term
(Source_Dirs_Expression
, In_Tree
, Source_Dirs_Term
);
331 Set_Current_Term
(Source_Dirs_Term
, In_Tree
, Source_Dirs_List
);
333 -- Source_Dirs empty list: nothing to do
335 -- Put virtual project into Projects_Htable
337 Prj
.Tree
.Tree_Private_Part
.Projects_Htable
.Set
338 (T
=> In_Tree
.Projects_HT
,
339 K
=> Virtual_Name_Id
,
340 E
=> (Name
=> Virtual_Name_Id
,
341 Node
=> Virtual_Project
,
342 Canonical_Path
=> No_Name
,
344 end Create_Virtual_Extending_Project
;
346 ----------------------------
347 -- Immediate_Directory_Of --
348 ----------------------------
350 function Immediate_Directory_Of
(Path_Name
: Name_Id
) return Name_Id
is
352 Get_Name_String
(Path_Name
);
354 for Index
in reverse 1 .. Name_Len
loop
355 if Name_Buffer
(Index
) = '/'
356 or else Name_Buffer
(Index
) = Dir_Sep
358 -- Remove all chars after last directory separator from name
361 Name_Len
:= Index
- 1;
371 -- There is no directory separator in name. Return "./" or ".\"
374 Name_Buffer
(1) := '.';
375 Name_Buffer
(2) := Dir_Sep
;
377 end Immediate_Directory_Of
;
379 -----------------------------------
380 -- Look_For_Virtual_Projects_For --
381 -----------------------------------
383 procedure Look_For_Virtual_Projects_For
384 (Proj
: Project_Node_Id
;
385 In_Tree
: Project_Node_Tree_Ref
;
386 Potentially_Virtual
: Boolean)
389 Declaration
: Project_Node_Id
:= Empty_Node
;
390 -- Node for the project declaration of Proj
392 With_Clause
: Project_Node_Id
:= Empty_Node
;
393 -- Node for a with clause of Proj
395 Imported
: Project_Node_Id
:= Empty_Node
;
396 -- Node for a project imported by Proj
398 Extended
: Project_Node_Id
:= Empty_Node
;
399 -- Node for the eventual project extended by Proj
402 -- Nothing to do if Proj is not defined or if it has already been
405 if Proj
/= Empty_Node
and then not Processed_Hash
.Get
(Proj
) then
406 -- Make sure the project will not be processed again
408 Processed_Hash
.Set
(Proj
, True);
410 Declaration
:= Project_Declaration_Of
(Proj
, In_Tree
);
412 if Declaration
/= Empty_Node
then
413 Extended
:= Extended_Project_Of
(Declaration
, In_Tree
);
416 -- If this is a project that may need a virtual extending project
417 -- and it is not itself an extending project, put it in the list.
419 if Potentially_Virtual
and then Extended
= Empty_Node
then
420 Virtual_Hash
.Set
(Proj
, Proj
);
423 -- Now check the projects it imports
425 With_Clause
:= First_With_Clause_Of
(Proj
, In_Tree
);
427 while With_Clause
/= Empty_Node
loop
428 Imported
:= Project_Node_Of
(With_Clause
, In_Tree
);
430 if Imported
/= Empty_Node
then
431 Look_For_Virtual_Projects_For
432 (Imported
, In_Tree
, Potentially_Virtual
=> True);
435 With_Clause
:= Next_With_Clause_Of
(With_Clause
, In_Tree
);
438 -- Check also the eventual project extended by Proj. As this project
439 -- is already extended, call recursively with Potentially_Virtual
442 Look_For_Virtual_Projects_For
443 (Extended
, In_Tree
, Potentially_Virtual
=> False);
445 end Look_For_Virtual_Projects_For
;
452 (In_Tree
: Project_Node_Tree_Ref
;
453 Project
: out Project_Node_Id
;
454 Project_File_Name
: String;
455 Always_Errout_Finalize
: Boolean;
456 Packages_To_Check
: String_List_Access
:= All_Packages
;
457 Store_Comments
: Boolean := False)
459 Current_Directory
: constant String := Get_Current_Dir
;
462 Real_Project_File_Name
: String_Access
:=
463 Osint
.To_Canonical_File_Spec
467 if Real_Project_File_Name
= null then
468 Real_Project_File_Name
:= new String'(Project_File_Name);
471 Project := Empty_Node;
473 if Current_Verbosity >= Medium then
474 Write_Str ("ADA_PROJECT_PATH=""");
475 Write_Str (Project_Path);
480 Path_Name : constant String :=
481 Project_Path_Name_Of (Real_Project_File_Name.all,
482 Directory => Current_Directory);
485 Free (Real_Project_File_Name);
488 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
489 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
491 -- Parse the main project file
493 if Path_Name = "" then
495 ("project file """, Project_File_Name, """ not found");
496 Project := Empty_Node;
503 Extends_All => Dummy,
504 Path_Name => Path_Name,
506 From_Extended => None,
508 Packages_To_Check => Packages_To_Check);
510 -- If Project is an extending-all project, create the eventual
511 -- virtual extending projects and check that there are no illegally
512 -- imported projects.
514 if Project /= Empty_Node
515 and then Is_Extending_All (Project, In_Tree)
517 -- First look for projects that potentially need a virtual
518 -- extending project.
521 Processed_Hash.Reset;
523 -- Mark the extending all project as processed, to avoid checking
524 -- the imported projects in case of a "limited with" on this
525 -- extending all project.
527 Processed_Hash.Set (Project, True);
530 Declaration : constant Project_Node_Id :=
531 Project_Declaration_Of (Project, In_Tree);
533 Look_For_Virtual_Projects_For
534 (Extended_Project_Of (Declaration, In_Tree), In_Tree,
535 Potentially_Virtual => False);
538 -- Now, check the projects directly imported by the main project.
539 -- Remove from the potentially virtual any project extended by one
540 -- of these imported projects. For non extending imported
541 -- projects, check that they do not belong to the project tree of
542 -- the project being "extended-all" by the main project.
545 With_Clause : Project_Node_Id;
546 Imported : Project_Node_Id := Empty_Node;
547 Declaration : Project_Node_Id := Empty_Node;
550 With_Clause := First_With_Clause_Of (Project, In_Tree);
551 while With_Clause /= Empty_Node loop
552 Imported := Project_Node_Of (With_Clause, In_Tree);
554 if Imported /= Empty_Node then
555 Declaration := Project_Declaration_Of (Imported, In_Tree);
557 if Extended_Project_Of (Declaration, In_Tree) /=
562 Extended_Project_Of (Declaration, In_Tree);
563 exit when Imported = Empty_Node;
564 Virtual_Hash.Remove (Imported);
566 Project_Declaration_Of (Imported, In_Tree);
571 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
575 -- Now create all the virtual extending projects
578 Proj : Project_Node_Id := Virtual_Hash.Get_First;
580 while Proj /= Empty_Node loop
581 Create_Virtual_Extending_Project (Proj, Project, In_Tree);
582 Proj := Virtual_Hash.Get_Next;
587 -- If there were any kind of error during the parsing, serious
588 -- or not, then the parsing fails.
590 if Err_Vars.Total_Errors_Detected > 0 then
591 Project := Empty_Node;
594 if Project = Empty_Node or else Always_Errout_Finalize then
604 Write_Line (Exception_Information (X));
605 Write_Str ("Exception ");
606 Write_Str (Exception_Name (X));
607 Write_Line (" raised, while processing project file");
608 Project := Empty_Node;
611 ------------------------------
612 -- Pre_Parse_Context_Clause --
613 ------------------------------
615 procedure Pre_Parse_Context_Clause
616 (In_Tree : Project_Node_Tree_Ref;
617 Context_Clause : out With_Id)
619 Current_With_Clause : With_Id := No_With;
620 Limited_With : Boolean := False;
622 Current_With : With_Record;
624 Current_With_Node : Project_Node_Id := Empty_Node;
627 -- Assume no context clause
629 Context_Clause := No_With;
632 -- If Token is not WITH or LIMITED, there is no context clause, or we
633 -- have exhausted the with clauses.
635 while Token = Tok_With or else Token = Tok_Limited loop
637 Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree);
638 Limited_With := Token = Tok_Limited;
641 Scan (In_Tree); -- scan past LIMITED
642 Expect (Tok_With, "WITH");
643 exit With_Loop when Token /= Tok_With;
648 Scan (In_Tree); -- scan past WITH or ","
650 Expect (Tok_String_Literal, "literal string");
652 if Token /= Tok_String_Literal then
656 -- Store path and location in table Withs
660 Location => Token_Ptr,
661 Limited_With => Limited_With,
662 Node => Current_With_Node,
665 Withs.Increment_Last;
666 Withs.Table (Withs.Last) := Current_With;
668 if Current_With_Clause = No_With then
669 Context_Clause := Withs.Last;
672 Withs.Table (Current_With_Clause).Next := Withs.Last;
675 Current_With_Clause := Withs.Last;
679 if Token = Tok_Semicolon then
680 Set_End_Of_Line (Current_With_Node);
681 Set_Previous_Line_Node (Current_With_Node);
683 -- End of (possibly multiple) with clause;
685 Scan (In_Tree); -- scan past the semicolon.
688 elsif Token = Tok_Comma then
689 Set_Is_Not_Last_In_List (Current_With_Node, In_Tree);
692 Error_Msg ("expected comma or semi colon", Token_Ptr);
698 (Of_Kind => N_With_Clause, In_Tree => In_Tree);
701 end Pre_Parse_Context_Clause;
703 -------------------------------
704 -- Post_Parse_Context_Clause --
705 -------------------------------
707 procedure Post_Parse_Context_Clause
708 (Context_Clause : With_Id;
709 In_Tree : Project_Node_Tree_Ref;
710 Imported_Projects : out Project_Node_Id;
711 Project_Directory : Name_Id;
712 From_Extended : Extension_Origin;
713 In_Limited : Boolean;
714 Packages_To_Check : String_List_Access)
716 Current_With_Clause : With_Id := Context_Clause;
718 Current_Project : Project_Node_Id := Empty_Node;
719 Previous_Project : Project_Node_Id := Empty_Node;
720 Next_Project : Project_Node_Id := Empty_Node;
722 Project_Directory_Path : constant String :=
723 Get_Name_String (Project_Directory);
725 Current_With : With_Record;
726 Limited_With : Boolean := False;
727 Extends_All : Boolean := False;
730 Imported_Projects := Empty_Node;
732 while Current_With_Clause /= No_With loop
733 Current_With := Withs.Table (Current_With_Clause);
734 Current_With_Clause := Current_With.Next;
736 Limited_With := In_Limited or Current_With.Limited_With;
739 Original_Path : constant String :=
740 Get_Name_String (Current_With.Path);
742 Imported_Path_Name : constant String :=
744 (Original_Path, Project_Directory_Path);
746 Resolved_Path : constant String :=
749 Resolve_Links => True,
750 Case_Sensitive => True);
752 Withed_Project : Project_Node_Id := Empty_Node;
755 if Imported_Path_Name = "" then
757 -- The project file cannot be found
759 Error_Msg_Name_1 := Current_With.Path;
761 Error_Msg ("unknown project file: {", Current_With.Location);
763 -- If this is not imported by the main project file,
764 -- display the import path.
766 if Project_Stack.Last > 1 then
767 for Index in reverse 1 .. Project_Stack.Last loop
768 Error_Msg_Name_1 := Project_Stack.Table (Index).Path_Name;
769 Error_Msg ("\imported by {", Current_With.Location);
776 Previous_Project := Current_Project;
778 if Current_Project = Empty_Node then
780 -- First with clause of the context clause
782 Current_Project := Current_With.Node;
783 Imported_Projects := Current_Project;
786 Next_Project := Current_With.Node;
787 Set_Next_With_Clause_Of
788 (Current_Project, In_Tree, Next_Project);
789 Current_Project := Next_Project;
793 (Current_Project, In_Tree, Current_With.Path);
795 (Current_Project, In_Tree, Current_With.Location);
797 -- If this is a "limited with", check if we have a circularity.
798 -- If we have one, get the project id of the limited imported
799 -- project file, and do not parse it.
801 if Limited_With and then Project_Stack.Last > 1 then
803 Canonical_Path_Name : Name_Id;
806 Name_Len := Resolved_Path'Length;
807 Name_Buffer (1 .. Name_Len) := Resolved_Path;
808 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
809 Canonical_Path_Name := Name_Find;
811 for Index in 1 .. Project_Stack.Last loop
812 if Project_Stack.Table (Index).Canonical_Path_Name =
815 -- We have found the limited imported project,
816 -- get its project id, and do not parse it.
818 Withed_Project := Project_Stack.Table (Index).Id;
825 -- Parse the imported project, if its project id is unknown
827 if Withed_Project = Empty_Node then
830 Project => Withed_Project,
831 Extends_All => Extends_All,
832 Path_Name => Imported_Path_Name,
834 From_Extended => From_Extended,
835 In_Limited => Limited_With,
836 Packages_To_Check => Packages_To_Check);
839 Extends_All := Is_Extending_All (Withed_Project, In_Tree);
842 if Withed_Project = Empty_Node then
843 -- If parsing was not successful, remove the
846 Current_Project := Previous_Project;
848 if Current_Project = Empty_Node then
849 Imported_Projects := Empty_Node;
852 Set_Next_With_Clause_Of
853 (Current_Project, In_Tree, Empty_Node);
856 -- If parsing was successful, record project name
857 -- and path name in with clause
860 (Node => Current_Project,
862 To => Withed_Project,
863 Limited_With => Current_With.Limited_With);
867 Name_Of (Withed_Project, In_Tree));
869 Name_Len := Resolved_Path'Length;
870 Name_Buffer (1 .. Name_Len) := Resolved_Path;
871 Set_Path_Name_Of (Current_Project, In_Tree, Name_Find);
874 Set_Is_Extending_All (Current_Project, In_Tree);
880 end Post_Parse_Context_Clause;
882 --------------------------
883 -- Parse_Single_Project --
884 --------------------------
886 procedure Parse_Single_Project
887 (In_Tree : Project_Node_Tree_Ref;
888 Project : out Project_Node_Id;
889 Extends_All : out Boolean;
892 From_Extended : Extension_Origin;
893 In_Limited : Boolean;
894 Packages_To_Check : String_List_Access)
896 Normed_Path_Name : Name_Id;
897 Canonical_Path_Name : Name_Id;
898 Project_Directory : Name_Id;
899 Project_Scan_State : Saved_Project_Scan_State;
900 Source_Index : Source_File_Index;
902 Extending : Boolean := False;
904 Extended_Project : Project_Node_Id := Empty_Node;
906 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
907 Tree_Private_Part.Projects_Htable.Get_First
908 (In_Tree.Projects_HT);
910 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
912 Name_Of_Project : Name_Id := No_Name;
914 First_With : With_Id;
916 use Tree_Private_Part;
918 Project_Comment_State : Tree.Comment_State;
921 Extends_All := False;
924 Normed_Path : constant String := Normalize_Pathname
925 (Path_Name, Resolve_Links => False,
926 Case_Sensitive => True);
927 Canonical_Path : constant String := Normalize_Pathname
928 (Normed_Path, Resolve_Links => True,
929 Case_Sensitive => False);
932 Name_Len := Normed_Path'Length;
933 Name_Buffer (1 .. Name_Len) := Normed_Path;
934 Normed_Path_Name := Name_Find;
935 Name_Len := Canonical_Path'Length;
936 Name_Buffer (1 .. Name_Len) := Canonical_Path;
937 Canonical_Path_Name := Name_Find;
940 -- Check for a circular dependency
942 for Index in 1 .. Project_Stack.Last loop
943 if Canonical_Path_Name =
944 Project_Stack.Table (Index).Canonical_Path_Name
946 Error_Msg ("circular dependency detected", Token_Ptr);
947 Error_Msg_Name_1 := Normed_Path_Name;
948 Error_Msg ("\ { is imported by", Token_Ptr);
950 for Current in reverse 1 .. Project_Stack.Last loop
951 Error_Msg_Name_1 := Project_Stack.Table (Current).Path_Name;
953 if Project_Stack.Table (Current).Canonical_Path_Name /=
957 ("\ { which itself is imported by", Token_Ptr);
960 Error_Msg ("\ {", Token_Ptr);
965 Project := Empty_Node;
970 -- Put the new path name on the stack
972 Project_Stack.Increment_Last;
973 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
974 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
977 -- Check if the project file has already been parsed
980 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
982 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
985 if A_Project_Name_And_Node.Extended then
987 ("cannot extend the same project file several times",
991 ("cannot extend an already imported project file",
995 elsif A_Project_Name_And_Node.Extended then
997 Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree);
999 -- If the imported project is an extended project A,
1000 -- and we are in an extended project, replace A with the
1001 -- ultimate project extending A.
1003 if From_Extended /= None then
1005 Decl : Project_Node_Id :=
1006 Project_Declaration_Of
1007 (A_Project_Name_And_Node.Node, In_Tree);
1009 Prj : Project_Node_Id :=
1010 Extending_Project_Of (Decl, In_Tree);
1014 Decl := Project_Declaration_Of (Prj, In_Tree);
1015 exit when Extending_Project_Of (Decl, In_Tree) =
1017 Prj := Extending_Project_Of (Decl, In_Tree);
1020 A_Project_Name_And_Node.Node := Prj;
1024 ("cannot import an already extended project file",
1029 Project := A_Project_Name_And_Node.Node;
1030 Project_Stack.Decrement_Last;
1034 A_Project_Name_And_Node :=
1035 Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT);
1038 -- We never encountered this project file
1039 -- Save the scan state, load the project file and start to scan it.
1041 Save_Project_Scan_State (Project_Scan_State);
1042 Source_Index := Load_Project_File (Path_Name);
1043 Tree.Save (Project_Comment_State);
1045 -- If we cannot find it, we stop
1047 if Source_Index = No_Source_File then
1048 Project := Empty_Node;
1049 Project_Stack.Decrement_Last;
1053 Prj.Err.Scanner.Initialize_Scanner (Source_Index);
1057 if Name_From_Path = No_Name then
1059 -- The project file name is not correct (no or bad extension,
1060 -- or not following Ada identifier's syntax).
1062 Error_Msg_Name_1 := Canonical_Path_Name;
1063 Error_Msg ("?{ is not a valid path name for a project file",
1067 if Current_Verbosity >= Medium then
1068 Write_Str ("Parsing """);
1069 Write_Str (Path_Name);
1074 -- Is there any imported project?
1076 Pre_Parse_Context_Clause (In_Tree, First_With);
1078 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1079 Project := Default_Project_Node
1080 (Of_Kind => N_Project, In_Tree => In_Tree);
1081 Project_Stack.Table (Project_Stack.Last).Id := Project;
1082 Set_Directory_Of (Project, In_Tree, Project_Directory);
1083 Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name);
1084 Set_Location_Of (Project, In_Tree, Token_Ptr);
1086 Expect (Tok_Project, "PROJECT
");
1088 -- Mark location of PROJECT token if present
1090 if Token = Tok_Project then
1091 Scan (In_Tree); -- scan past PROJECT
1092 Set_Location_Of (Project, In_Tree, Token_Ptr);
1099 Expect (Tok_Identifier, "identifier
");
1101 -- If the token is not an identifier, clear the buffer before
1102 -- exiting to indicate that the name of the project is ill-formed.
1104 if Token /= Tok_Identifier then
1109 -- Add the identifier name to the buffer
1111 Get_Name_String (Token_Name);
1112 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1114 -- Scan past the identifier
1118 -- If we have a dot, add a dot the the Buffer and look for the next
1121 exit when Token /= Tok_Dot;
1122 Add_To_Buffer (".", Buffer, Buffer_Last);
1124 -- Scan past the dot
1129 -- See if this is an extending project
1131 if Token = Tok_Extends then
1133 -- Make sure that gnatmake will use mapping files
1135 Create_Mapping_File := True;
1137 -- We are extending another project
1141 Scan (In_Tree); -- scan past EXTENDS
1143 if Token = Tok_All then
1144 Extends_All := True;
1145 Set_Is_Extending_All (Project, In_Tree);
1146 Scan (In_Tree); -- scan past ALL
1150 -- If the name is well formed, Buffer_Last is > 0
1152 if Buffer_Last > 0 then
1154 -- The Buffer contains the name of the project
1156 Name_Len := Buffer_Last;
1157 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1158 Name_Of_Project := Name_Find;
1159 Set_Name_Of (Project, In_Tree, Name_Of_Project);
1161 -- To get expected name of the project file, replace dots by dashes
1163 Name_Len := Buffer_Last;
1164 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1166 for Index in 1 .. Name_Len loop
1167 if Name_Buffer (Index) = '.' then
1168 Name_Buffer (Index) := '-';
1172 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1175 Expected_Name : constant Name_Id := Name_Find;
1178 -- Output a warning if the actual name is not the expected name
1180 if Name_From_Path /= No_Name
1181 and then Expected_Name /= Name_From_Path
1183 Error_Msg_Name_1 := Expected_Name;
1184 Error_Msg ("?file name does
not match unit name
, " &
1185 "should be `
{" & Project_File_Extension & "`
",
1191 Imported_Projects : Project_Node_Id := Empty_Node;
1192 From_Ext : Extension_Origin := None;
1195 -- Extending_All is always propagated
1197 if From_Extended = Extending_All or else Extends_All then
1198 From_Ext := Extending_All;
1200 -- Otherwise, From_Extended is set to Extending_Single if the
1201 -- current project is an extending project.
1204 From_Ext := Extending_Simple;
1207 Post_Parse_Context_Clause
1208 (In_Tree => In_Tree,
1209 Context_Clause => First_With,
1210 Imported_Projects => Imported_Projects,
1211 Project_Directory => Project_Directory,
1212 From_Extended => From_Ext,
1213 In_Limited => In_Limited,
1214 Packages_To_Check => Packages_To_Check);
1215 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1219 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1220 Tree_Private_Part.Projects_Htable.Get_First
1221 (In_Tree.Projects_HT);
1222 Project_Name : Name_Id := Name_And_Node.Name;
1225 -- Check if we already have a project with this name
1227 while Project_Name /= No_Name
1228 and then Project_Name /= Name_Of_Project
1231 Tree_Private_Part.Projects_Htable.Get_Next
1232 (In_Tree.Projects_HT);
1233 Project_Name := Name_And_Node.Name;
1236 -- Report an error if we already have a project with this name
1238 if Project_Name /= No_Name then
1239 Error_Msg_Name_1 := Project_Name;
1241 ("duplicate project name
{", Location_Of (Project, In_Tree));
1243 Path_Name_Of (Name_And_Node.Node, In_Tree);
1245 ("\already
in {", Location_Of (Project, In_Tree));
1248 -- Otherwise, add the name of the project to the hash table, so
1249 -- that we can check that no other subsequent project will have
1252 Tree_Private_Part.Projects_Htable.Set
1253 (T => In_Tree.Projects_HT,
1254 K => Name_Of_Project,
1255 E => (Name => Name_Of_Project,
1257 Canonical_Path => Canonical_Path_Name,
1258 Extended => Extended));
1265 Expect (Tok_String_Literal, "literal
string");
1267 if Token = Tok_String_Literal then
1268 Set_Extended_Project_Path_Of (Project, In_Tree, Token_Name);
1271 Original_Path_Name : constant String :=
1272 Get_Name_String (Token_Name);
1274 Extended_Project_Path_Name : constant String :=
1275 Project_Path_Name_Of
1276 (Original_Path_Name,
1278 (Project_Directory));
1281 if Extended_Project_Path_Name = "" then
1283 -- We could not find the project file to extend
1285 Error_Msg_Name_1 := Token_Name;
1287 Error_Msg ("unknown project file
: {", Token_Ptr);
1289 -- If we are not in the main project file, display the
1292 if Project_Stack.Last > 1 then
1294 Project_Stack.Table (Project_Stack.Last).Path_Name;
1295 Error_Msg ("\extended by
{", Token_Ptr);
1297 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1299 Project_Stack.Table (Index).Path_Name;
1300 Error_Msg ("\imported by
{", Token_Ptr);
1306 From_Ext : Extension_Origin := None;
1309 if From_Extended = Extending_All or else Extends_All then
1310 From_Ext := Extending_All;
1313 Parse_Single_Project
1314 (In_Tree => In_Tree,
1315 Project => Extended_Project,
1316 Extends_All => Extends_All,
1317 Path_Name => Extended_Project_Path_Name,
1319 From_Extended => From_Ext,
1320 In_Limited => In_Limited,
1321 Packages_To_Check => Packages_To_Check);
1324 -- A project that extends an extending-all project is also
1325 -- an extending-all project.
1327 if Extended_Project /= Empty_Node
1328 and then Is_Extending_All (Extended_Project, In_Tree)
1330 Set_Is_Extending_All (Project, In_Tree);
1335 Scan (In_Tree); -- scan past the extended project path
1339 -- Check that a non extending-all project does not import an
1340 -- extending-all project.
1342 if not Is_Extending_All (Project, In_Tree) then
1344 With_Clause : Project_Node_Id :=
1345 First_With_Clause_Of (Project, In_Tree);
1346 Imported : Project_Node_Id := Empty_Node;
1350 while With_Clause /= Empty_Node loop
1351 Imported := Project_Node_Of (With_Clause, In_Tree);
1353 if Is_Extending_All (With_Clause, In_Tree) then
1354 Error_Msg_Name_1 := Name_Of (Imported, In_Tree);
1355 Error_Msg ("cannot import extending
-all project
{",
1357 exit With_Clause_Loop;
1360 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1361 end loop With_Clause_Loop;
1365 -- Check that a project with a name including a dot either imports
1366 -- or extends the project whose name precedes the last dot.
1368 if Name_Of_Project /= No_Name then
1369 Get_Name_String (Name_Of_Project);
1375 -- Look for the last dot
1377 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1378 Name_Len := Name_Len - 1;
1381 -- If a dot was find, check if the parent project is imported
1384 if Name_Len > 0 then
1385 Name_Len := Name_Len - 1;
1388 Parent_Name : constant Name_Id := Name_Find;
1389 Parent_Found : Boolean := False;
1390 With_Clause : Project_Node_Id :=
1391 First_With_Clause_Of (Project, In_Tree);
1394 -- If there is an extended project, check its name
1396 if Extended_Project /= Empty_Node then
1398 Name_Of (Extended_Project, In_Tree) = Parent_Name;
1401 -- If the parent project is not the extended project,
1402 -- check each imported project until we find the parent project.
1404 while not Parent_Found and then With_Clause /= Empty_Node loop
1406 Name_Of (Project_Node_Of (With_Clause, In_Tree), In_Tree) =
1408 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1411 -- If the parent project was not found, report an error
1413 if not Parent_Found then
1414 Error_Msg_Name_1 := Name_Of_Project;
1415 Error_Msg_Name_2 := Parent_Name;
1416 Error_Msg ("project
{ does
not import
or extend project
{",
1417 Location_Of (Project, In_Tree));
1422 Expect (Tok_Is, "IS");
1423 Set_End_Of_Line (Project);
1424 Set_Previous_Line_Node (Project);
1425 Set_Next_End_Node (Project);
1428 Project_Declaration : Project_Node_Id := Empty_Node;
1431 -- No need to Scan past "is", Prj.Dect.Parse will do it
1434 (In_Tree => In_Tree,
1435 Declarations => Project_Declaration,
1436 Current_Project => Project,
1437 Extends => Extended_Project,
1438 Packages_To_Check => Packages_To_Check);
1439 Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration);
1441 if Extended_Project /= Empty_Node then
1442 Set_Extending_Project_Of
1443 (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree,
1448 Expect (Tok_End, "END");
1449 Remove_Next_End_Node;
1451 -- Skip "end" if present
1453 if Token = Tok_End then
1461 -- Store the name following "end" in the Buffer. The name may be made of
1462 -- several simple names.
1465 Expect (Tok_Identifier, "identifier
");
1467 -- If we don't have an identifier, clear the buffer before exiting to
1468 -- avoid checking the name.
1470 if Token /= Tok_Identifier then
1475 -- Add the identifier to the Buffer
1476 Get_Name_String (Token_Name);
1477 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1479 -- Scan past the identifier
1482 exit when Token /= Tok_Dot;
1483 Add_To_Buffer (".", Buffer, Buffer_Last);
1487 -- If we have a valid name, check if it is the name of the project
1489 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1490 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1491 Get_Name_String (Name_Of (Project, In_Tree))
1493 -- Invalid name: report an error
1495 Error_Msg ("expected
""" &
1496 Get_Name_String (Name_Of (Project, In_Tree)) & """",
1501 Expect (Tok_Semicolon, "`
;`
");
1503 -- Check that there is no more text following the end of the project
1506 if Token = Tok_Semicolon then
1507 Set_Previous_End_Node (Project);
1510 if Token /= Tok_EOF then
1512 ("unexpected text following
end of project
", Token_Ptr);
1516 -- Restore the scan state, in case we are not the main project
1518 Restore_Project_Scan_State (Project_Scan_State);
1520 -- And remove the project from the project stack
1522 Project_Stack.Decrement_Last;
1524 -- Indicate if there are unkept comments
1526 Tree.Set_Project_File_Includes_Unkept_Comments
1529 To => Tree.There_Are_Unkept_Comments);
1531 -- And restore the comment state that was saved
1533 Tree.Restore (Project_Comment_State);
1534 end Parse_Single_Project;
1536 -----------------------
1537 -- Project_Name_From --
1538 -----------------------
1540 function Project_Name_From (Path_Name : String) return Name_Id is
1541 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1542 First : Natural := Canonical'Last;
1543 Last : Natural := First;
1547 if Current_Verbosity = High then
1548 Write_Str ("Project_Name_From
(""");
1549 Write_Str (Canonical);
1553 -- If the path name is empty, return No_Name to indicate failure
1559 Canonical_Case_File_Name (Canonical);
1561 -- Look for the last dot in the path name
1565 Canonical (First) /= '.'
1570 -- If we have a dot, check that it is followed by the correct extension
1572 if First > 0 and then Canonical (First) = '.' then
1573 if Canonical (First .. Last) = Project_File_Extension
1576 -- Look for the last directory separator, if any
1582 and then Canonical (First) /= '/'
1583 and then Canonical (First) /= Dir_Sep
1589 -- Not the correct extension, return No_Name to indicate failure
1594 -- If no dot in the path name, return No_Name to indicate failure
1602 -- If the extension is the file name, return No_Name to indicate failure
1604 if First > Last then
1608 -- Put the name in lower case into Name_Buffer
1610 Name_Len := Last - First + 1;
1611 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1615 -- Check if it is a well formed project name. Return No_Name if it is
1619 if not Is_Letter (Name_Buffer (Index)) then
1626 exit when Index >= Name_Len;
1628 if Name_Buffer (Index) = '_' then
1629 if Name_Buffer (Index + 1) = '_' then
1634 exit when Name_Buffer (Index) = '-';
1636 if Name_Buffer (Index) /= '_'
1637 and then not Is_Alphanumeric (Name_Buffer (Index))
1645 if Index >= Name_Len then
1646 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1648 -- All checks have succeeded. Return name in Name_Buffer
1656 elsif Name_Buffer (Index) = '-' then
1660 end Project_Name_From;
1662 --------------------------
1663 -- Project_Path_Name_Of --
1664 --------------------------
1666 function Project_Path_Name_Of
1667 (Project_File_Name : String;
1668 Directory : String) return String
1670 Result : String_Access;
1673 if Current_Verbosity = High then
1674 Write_Str ("Project_Path_Name_Of
(""");
1675 Write_Str (Project_File_Name);
1676 Write_Str (""", """);
1677 Write_Str (Directory);
1678 Write_Line (""");");
1681 if not Is_Absolute_Path (Project_File_Name) then
1682 -- First we try <directory>/<file_name>.<extension>
1684 if Current_Verbosity = High then
1685 Write_Str (" Trying
");
1686 Write_Str (Directory);
1687 Write_Char (Directory_Separator);
1688 Write_Str (Project_File_Name);
1689 Write_Line (Project_File_Extension);
1694 (File_Name => Directory & Directory_Separator &
1695 Project_File_Name & Project_File_Extension,
1696 Path => Project_Path);
1698 -- Then we try <directory>/<file_name>
1700 if Result = null then
1701 if Current_Verbosity = High then
1702 Write_Str (" Trying
");
1703 Write_Str (Directory);
1704 Write_Char (Directory_Separator);
1705 Write_Line (Project_File_Name);
1710 (File_Name => Directory & Directory_Separator &
1712 Path => Project_Path);
1716 if Result = null then
1718 -- Then we try <file_name>.<extension>
1720 if Current_Verbosity = High then
1721 Write_Str (" Trying
");
1722 Write_Str (Project_File_Name);
1723 Write_Line (Project_File_Extension);
1728 (File_Name => Project_File_Name & Project_File_Extension,
1729 Path => Project_Path);
1732 if Result = null then
1734 -- Then we try <file_name>
1736 if Current_Verbosity = High then
1737 Write_Str (" Trying
");
1738 Write_Line (Project_File_Name);
1743 (File_Name => Project_File_Name,
1744 Path => Project_Path);
1747 -- If we cannot find the project file, we return an empty string
1749 if Result = null then
1754 Final_Result : constant String :=
1755 GNAT.OS_Lib.Normalize_Pathname
1757 Resolve_Links => False,
1758 Case_Sensitive => True);
1761 return Final_Result;
1764 end Project_Path_Name_Of;