1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2004 Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
27 with Err_Vars
; use Err_Vars
;
28 with Namet
; use Namet
;
30 with Osint
; use Osint
;
31 with Output
; use Output
;
32 with Prj
.Com
; use Prj
.Com
;
34 with Prj
.Err
; use Prj
.Err
;
35 with Scans
; use Scans
;
36 with Sinput
; use Sinput
;
37 with Sinput
.P
; use Sinput
.P
;
40 with Types
; use Types
;
42 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
43 with Ada
.Exceptions
; use Ada
.Exceptions
;
45 with GNAT
.Directory_Operations
; use GNAT
.Directory_Operations
;
46 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
48 with System
.HTable
; use System
.HTable
;
50 pragma Elaborate_All
(GNAT
.OS_Lib
);
52 package body Prj
.Part
is
54 Dir_Sep
: Character renames GNAT
.OS_Lib
.Directory_Separator
;
56 Project_Path
: String_Access
;
57 -- The project path; initialized during package elaboration.
58 -- Contains at least the current working directory.
60 Ada_Project_Path
: constant String := "ADA_PROJECT_PATH";
61 -- Name of the env. variable that contains path name(s) of directories
62 -- where project files may reside.
64 Prj_Path
: constant String_Access
:= Getenv
(Ada_Project_Path
);
65 -- The path name(s) of directories where project files may reside.
68 type Extension_Origin
is (None
, Extending_Simple
, Extending_All
);
69 -- Type of parameter From_Extended for procedures Parse_Single_Project and
70 -- Post_Parse_Context_Clause. Extending_All means that we are parsing the
71 -- tree rooted at an extending all project.
73 ------------------------------------
74 -- Local Packages and Subprograms --
75 ------------------------------------
77 type With_Id
is new Nat
;
78 No_With
: constant With_Id
:= 0;
80 type With_Record
is record
82 Location
: Source_Ptr
;
83 Limited_With
: Boolean;
84 Node
: Project_Node_Id
;
87 -- Information about an imported project, to be put in table Withs below
89 package Withs
is new Table
.Table
90 (Table_Component_Type
=> With_Record
,
91 Table_Index_Type
=> With_Id
,
94 Table_Increment
=> 50,
95 Table_Name
=> "Prj.Part.Withs");
96 -- Table used to store temporarily paths and locations of imported
97 -- projects. These imported projects will be effectively parsed after the
98 -- name of the current project has been extablished.
100 type Names_And_Id
is record
102 Canonical_Path_Name
: Name_Id
;
103 Id
: Project_Node_Id
;
106 package Project_Stack
is new Table
.Table
107 (Table_Component_Type
=> Names_And_Id
,
108 Table_Index_Type
=> Nat
,
109 Table_Low_Bound
=> 1,
111 Table_Increment
=> 50,
112 Table_Name
=> "Prj.Part.Project_Stack");
113 -- This table is used to detect circular dependencies
114 -- for imported and extended projects and to get the project ids of
115 -- limited imported projects when there is a circularity with at least
116 -- one limited imported project file.
118 package Virtual_Hash
is new Simple_HTable
119 (Header_Num
=> Header_Num
,
120 Element
=> Project_Node_Id
,
121 No_Element
=> Empty_Node
,
122 Key
=> Project_Node_Id
,
123 Hash
=> Prj
.Tree
.Hash
,
125 -- Hash table to store the node id of the project for which a virtual
126 -- extending project need to be created.
128 package Processed_Hash
is new Simple_HTable
129 (Header_Num
=> Header_Num
,
132 Key
=> Project_Node_Id
,
133 Hash
=> Prj
.Tree
.Hash
,
135 -- Hash table to store the project process when looking for project that
136 -- need to have a virtual extending project, to avoid processing the same
139 procedure Create_Virtual_Extending_Project
140 (For_Project
: Project_Node_Id
;
141 Main_Project
: Project_Node_Id
);
142 -- Create a virtual extending project of For_Project. Main_Project is
143 -- the extending all project.
145 procedure Look_For_Virtual_Projects_For
146 (Proj
: Project_Node_Id
;
147 Potentially_Virtual
: Boolean);
148 -- Look for projects that need to have a virtual extending project.
149 -- This procedure is recursive. If called with Potentially_Virtual set to
150 -- True, then Proj may need an virtual extending project; otherwise it
151 -- does not (because it is already extended), but other projects that it
152 -- imports may need to be virtually extended.
154 procedure Pre_Parse_Context_Clause
(Context_Clause
: out With_Id
);
155 -- Parse the context clause of a project.
156 -- Store the paths and locations of the imported projects in table Withs.
157 -- Does nothing if there is no context clause (if the current
158 -- token is not "with" or "limited" followed by "with").
160 procedure Post_Parse_Context_Clause
161 (Context_Clause
: With_Id
;
162 Imported_Projects
: out Project_Node_Id
;
163 Project_Directory
: Name_Id
;
164 From_Extended
: Extension_Origin
);
165 -- Parse the imported projects that have been stored in table Withs,
166 -- if any. From_Extended is used for the call to Parse_Single_Project
169 procedure Parse_Single_Project
170 (Project
: out Project_Node_Id
;
171 Extends_All
: out Boolean;
174 From_Extended
: Extension_Origin
);
175 -- Parse a project file.
176 -- Recursive procedure: it calls itself for imported and extended
177 -- projects. When From_Extended is not None, if the project has already
178 -- been parsed and is an extended project A, return the ultimate
179 -- (not extended) project that extends A.
181 function Project_Path_Name_Of
182 (Project_File_Name
: String;
183 Directory
: String) return String;
184 -- Returns the path name of a project file. Returns an empty string
185 -- if project file cannot be found.
187 function Immediate_Directory_Of
(Path_Name
: Name_Id
) return Name_Id
;
188 -- Get the directory of the file with the specified path name.
189 -- This includes the directory separator as the last character.
190 -- Returns "./" if Path_Name contains no directory separator.
192 function Project_Name_From
(Path_Name
: String) return Name_Id
;
193 -- Returns the name of the project that corresponds to its path name.
194 -- Returns No_Name if the path name is invalid, because the corresponding
195 -- project name does not have the syntax of an ada identifier.
197 --------------------------------------
198 -- Create_Virtual_Extending_Project --
199 --------------------------------------
201 procedure Create_Virtual_Extending_Project
202 (For_Project
: Project_Node_Id
;
203 Main_Project
: Project_Node_Id
)
206 Virtual_Name
: constant String :=
208 Get_Name_String
(Name_Of
(For_Project
));
209 -- The name of the virtual extending project
211 Virtual_Name_Id
: Name_Id
;
212 -- Virtual extending project name id
214 Virtual_Path_Id
: Name_Id
;
215 -- Fake path name of the virtual extending project. The directory is
216 -- the same directory as the extending all project.
218 Virtual_Dir_Id
: constant Name_Id
:=
219 Immediate_Directory_Of
(Path_Name_Of
(Main_Project
));
220 -- The directory of the extending all project
222 -- The source of the virtual extending project is something like:
224 -- project V$<project name> extends <project path> is
226 -- for Source_Dirs use ();
228 -- end V$<project name>;
230 -- The project directory cannot be specified during parsing; it will be
231 -- put directly in the virtual extending project data during processing.
233 -- Nodes that made up the virtual extending project
235 Virtual_Project
: constant Project_Node_Id
:=
236 Default_Project_Node
(N_Project
);
237 With_Clause
: constant Project_Node_Id
:=
238 Default_Project_Node
(N_With_Clause
);
239 Project_Declaration
: constant Project_Node_Id
:=
240 Default_Project_Node
(N_Project_Declaration
);
241 Source_Dirs_Declaration
: constant Project_Node_Id
:=
242 Default_Project_Node
(N_Declarative_Item
);
243 Source_Dirs_Attribute
: constant Project_Node_Id
:=
245 (N_Attribute_Declaration
, List
);
246 Source_Dirs_Expression
: constant Project_Node_Id
:=
247 Default_Project_Node
(N_Expression
, List
);
248 Source_Dirs_Term
: constant Project_Node_Id
:=
249 Default_Project_Node
(N_Term
, List
);
250 Source_Dirs_List
: constant Project_Node_Id
:=
252 (N_Literal_String_List
, List
);
255 -- Get the virtual name id
257 Name_Len
:= Virtual_Name
'Length;
258 Name_Buffer
(1 .. Name_Len
) := Virtual_Name
;
259 Virtual_Name_Id
:= Name_Find
;
261 -- Get the virtual path name
263 Get_Name_String
(Path_Name_Of
(Main_Project
));
266 and then Name_Buffer
(Name_Len
) /= Directory_Separator
267 and then Name_Buffer
(Name_Len
) /= '/'
269 Name_Len
:= Name_Len
- 1;
272 Name_Buffer
(Name_Len
+ 1 .. Name_Len
+ Virtual_Name
'Length) :=
274 Name_Len
:= Name_Len
+ Virtual_Name
'Length;
275 Virtual_Path_Id
:= Name_Find
;
279 Set_Name_Of
(With_Clause
, Virtual_Name_Id
);
280 Set_Path_Name_Of
(With_Clause
, Virtual_Path_Id
);
281 Set_Project_Node_Of
(With_Clause
, Virtual_Project
);
282 Set_Next_With_Clause_Of
283 (With_Clause
, First_With_Clause_Of
(Main_Project
));
284 Set_First_With_Clause_Of
(Main_Project
, With_Clause
);
286 -- Virtual project node
288 Set_Name_Of
(Virtual_Project
, Virtual_Name_Id
);
289 Set_Path_Name_Of
(Virtual_Project
, Virtual_Path_Id
);
290 Set_Location_Of
(Virtual_Project
, Location_Of
(Main_Project
));
291 Set_Directory_Of
(Virtual_Project
, Virtual_Dir_Id
);
292 Set_Project_Declaration_Of
(Virtual_Project
, Project_Declaration
);
293 Set_Extended_Project_Path_Of
294 (Virtual_Project
, Path_Name_Of
(For_Project
));
296 -- Project declaration
298 Set_First_Declarative_Item_Of
299 (Project_Declaration
, Source_Dirs_Declaration
);
300 Set_Extended_Project_Of
(Project_Declaration
, For_Project
);
302 -- Source_Dirs declaration
304 Set_Current_Item_Node
(Source_Dirs_Declaration
, Source_Dirs_Attribute
);
306 -- Source_Dirs attribute
308 Set_Name_Of
(Source_Dirs_Attribute
, Snames
.Name_Source_Dirs
);
309 Set_Expression_Of
(Source_Dirs_Attribute
, Source_Dirs_Expression
);
311 -- Source_Dirs expression
313 Set_First_Term
(Source_Dirs_Expression
, Source_Dirs_Term
);
317 Set_Current_Term
(Source_Dirs_Term
, Source_Dirs_List
);
319 -- Source_Dirs empty list: nothing to do
321 end Create_Virtual_Extending_Project
;
323 ----------------------------
324 -- Immediate_Directory_Of --
325 ----------------------------
327 function Immediate_Directory_Of
(Path_Name
: Name_Id
) return Name_Id
is
329 Get_Name_String
(Path_Name
);
331 for Index
in reverse 1 .. Name_Len
loop
332 if Name_Buffer
(Index
) = '/'
333 or else Name_Buffer
(Index
) = Dir_Sep
335 -- Remove all chars after last directory separator from name
338 Name_Len
:= Index
- 1;
348 -- There is no directory separator in name. Return "./" or ".\"
351 Name_Buffer
(1) := '.';
352 Name_Buffer
(2) := Dir_Sep
;
354 end Immediate_Directory_Of
;
356 -----------------------------------
357 -- Look_For_Virtual_Projects_For --
358 -----------------------------------
360 procedure Look_For_Virtual_Projects_For
361 (Proj
: Project_Node_Id
;
362 Potentially_Virtual
: Boolean)
365 Declaration
: Project_Node_Id
:= Empty_Node
;
366 -- Node for the project declaration of Proj
368 With_Clause
: Project_Node_Id
:= Empty_Node
;
369 -- Node for a with clause of Proj
371 Imported
: Project_Node_Id
:= Empty_Node
;
372 -- Node for a project imported by Proj
374 Extended
: Project_Node_Id
:= Empty_Node
;
375 -- Node for the eventual project extended by Proj
378 -- Nothing to do if Proj is not defined or if it has already been
381 if Proj
/= Empty_Node
and then not Processed_Hash
.Get
(Proj
) then
382 -- Make sure the project will not be processed again
384 Processed_Hash
.Set
(Proj
, True);
386 Declaration
:= Project_Declaration_Of
(Proj
);
388 if Declaration
/= Empty_Node
then
389 Extended
:= Extended_Project_Of
(Declaration
);
392 -- If this is a project that may need a virtual extending project
393 -- and it is not itself an extending project, put it in the list.
395 if Potentially_Virtual
and then Extended
= Empty_Node
then
396 Virtual_Hash
.Set
(Proj
, Proj
);
399 -- Now check the projects it imports
401 With_Clause
:= First_With_Clause_Of
(Proj
);
403 while With_Clause
/= Empty_Node
loop
404 Imported
:= Project_Node_Of
(With_Clause
);
406 if Imported
/= Empty_Node
then
407 Look_For_Virtual_Projects_For
408 (Imported
, Potentially_Virtual
=> True);
411 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
414 -- Check also the eventual project extended by Proj. As this project
415 -- is already extended, call recursively with Potentially_Virtual
418 Look_For_Virtual_Projects_For
419 (Extended
, Potentially_Virtual
=> False);
421 end Look_For_Virtual_Projects_For
;
428 (Project
: out Project_Node_Id
;
429 Project_File_Name
: String;
430 Always_Errout_Finalize
: Boolean;
431 Packages_To_Check
: String_List_Access
:= All_Packages
;
432 Store_Comments
: Boolean := False)
434 Current_Directory
: constant String := Get_Current_Dir
;
438 -- Save the Packages_To_Check in Prj, so that it is visible from
441 Current_Packages_To_Check
:= Packages_To_Check
;
443 Project
:= Empty_Node
;
445 if Current_Verbosity
>= Medium
then
446 Write_Str
("ADA_PROJECT_PATH=""");
447 Write_Str
(Project_Path
.all);
452 Path_Name
: constant String :=
453 Project_Path_Name_Of
(Project_File_Name
,
454 Directory
=> Current_Directory
);
458 Prj
.Err
.Scanner
.Set_Comment_As_Token
(Store_Comments
);
459 Prj
.Err
.Scanner
.Set_End_Of_Line_As_Token
(Store_Comments
);
461 -- Parse the main project file
463 if Path_Name
= "" then
465 ("project file """, Project_File_Name
, """ not found");
466 Project
:= Empty_Node
;
472 Extends_All
=> Dummy
,
473 Path_Name
=> Path_Name
,
475 From_Extended
=> None
);
477 -- If Project is an extending-all project, create the eventual
478 -- virtual extending projects and check that there are no illegally
479 -- imported projects.
481 if Project
/= Empty_Node
and then Is_Extending_All
(Project
) then
482 -- First look for projects that potentially need a virtual
483 -- extending project.
486 Processed_Hash
.Reset
;
488 -- Mark the extending all project as processed, to avoid checking
489 -- the imported projects in case of a "limited with" on this
490 -- extending all project.
492 Processed_Hash
.Set
(Project
, True);
495 Declaration
: constant Project_Node_Id
:=
496 Project_Declaration_Of
(Project
);
498 Look_For_Virtual_Projects_For
499 (Extended_Project_Of
(Declaration
),
500 Potentially_Virtual
=> False);
503 -- Now, check the projects directly imported by the main project.
504 -- Remove from the potentially virtual any project extended by one
505 -- of these imported projects. For non extending imported
506 -- projects, check that they do not belong to the project tree of
507 -- the project being "extended-all" by the main project.
510 With_Clause
: Project_Node_Id
:=
511 First_With_Clause_Of
(Project
);
512 Imported
: Project_Node_Id
:= Empty_Node
;
513 Declaration
: Project_Node_Id
:= Empty_Node
;
516 while With_Clause
/= Empty_Node
loop
517 Imported
:= Project_Node_Of
(With_Clause
);
519 if Imported
/= Empty_Node
then
520 Declaration
:= Project_Declaration_Of
(Imported
);
522 if Extended_Project_Of
(Declaration
) /= Empty_Node
then
524 Imported
:= Extended_Project_Of
(Declaration
);
525 exit when Imported
= Empty_Node
;
526 Virtual_Hash
.Remove
(Imported
);
527 Declaration
:= Project_Declaration_Of
(Imported
);
530 elsif Virtual_Hash
.Get
(Imported
) /= Empty_Node
then
532 ("this project cannot be imported directly",
533 Location_Of
(With_Clause
));
538 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
542 -- Now create all the virtual extending projects
545 Proj
: Project_Node_Id
:= Virtual_Hash
.Get_First
;
547 while Proj
/= Empty_Node
loop
548 Create_Virtual_Extending_Project
(Proj
, Project
);
549 Proj
:= Virtual_Hash
.Get_Next
;
554 -- If there were any kind of error during the parsing, serious
555 -- or not, then the parsing fails.
557 if Err_Vars
.Total_Errors_Detected
> 0 then
558 Project
:= Empty_Node
;
561 if Project
= Empty_Node
or else Always_Errout_Finalize
then
571 Write_Line
(Exception_Information
(X
));
572 Write_Str
("Exception ");
573 Write_Str
(Exception_Name
(X
));
574 Write_Line
(" raised, while processing project file");
575 Project
:= Empty_Node
;
578 ------------------------------
579 -- Pre_Parse_Context_Clause --
580 ------------------------------
582 procedure Pre_Parse_Context_Clause
(Context_Clause
: out With_Id
) is
583 Current_With_Clause
: With_Id
:= No_With
;
584 Limited_With
: Boolean := False;
586 Current_With
: With_Record
;
588 Current_With_Node
: Project_Node_Id
:= Empty_Node
;
591 -- Assume no context clause
593 Context_Clause
:= No_With
;
596 -- If Token is not WITH or LIMITED, there is no context clause,
597 -- or we have exhausted the with clauses.
599 while Token
= Tok_With
or else Token
= Tok_Limited
loop
600 Current_With_Node
:= Default_Project_Node
(Of_Kind
=> N_With_Clause
);
601 Limited_With
:= Token
= Tok_Limited
;
604 Scan
; -- scan past LIMITED
605 Expect
(Tok_With
, "WITH");
606 exit With_Loop
when Token
/= Tok_With
;
611 Scan
; -- scan past WITH or ","
613 Expect
(Tok_String_Literal
, "literal string");
615 if Token
/= Tok_String_Literal
then
619 -- Store path and location in table Withs
623 Location
=> Token_Ptr
,
624 Limited_With
=> Limited_With
,
625 Node
=> Current_With_Node
,
628 Withs
.Increment_Last
;
629 Withs
.Table
(Withs
.Last
) := Current_With
;
631 if Current_With_Clause
= No_With
then
632 Context_Clause
:= Withs
.Last
;
635 Withs
.Table
(Current_With_Clause
).Next
:= Withs
.Last
;
638 Current_With_Clause
:= Withs
.Last
;
642 if Token
= Tok_Semicolon
then
643 Set_End_Of_Line
(Current_With_Node
);
644 Set_Previous_Line_Node
(Current_With_Node
);
646 -- End of (possibly multiple) with clause;
648 Scan
; -- scan past the semicolon.
651 elsif Token
/= Tok_Comma
then
652 Error_Msg
("expected comma or semi colon", Token_Ptr
);
657 Default_Project_Node
(Of_Kind
=> N_With_Clause
);
660 end Pre_Parse_Context_Clause
;
663 -------------------------------
664 -- Post_Parse_Context_Clause --
665 -------------------------------
667 procedure Post_Parse_Context_Clause
668 (Context_Clause
: With_Id
;
669 Imported_Projects
: out Project_Node_Id
;
670 Project_Directory
: Name_Id
;
671 From_Extended
: Extension_Origin
)
673 Current_With_Clause
: With_Id
:= Context_Clause
;
675 Current_Project
: Project_Node_Id
:= Empty_Node
;
676 Previous_Project
: Project_Node_Id
:= Empty_Node
;
677 Next_Project
: Project_Node_Id
:= Empty_Node
;
679 Project_Directory_Path
: constant String :=
680 Get_Name_String
(Project_Directory
);
682 Current_With
: With_Record
;
683 Limited_With
: Boolean := False;
684 Extends_All
: Boolean := False;
687 Imported_Projects
:= Empty_Node
;
689 while Current_With_Clause
/= No_With
loop
690 Current_With
:= Withs
.Table
(Current_With_Clause
);
691 Current_With_Clause
:= Current_With
.Next
;
693 Limited_With
:= Current_With
.Limited_With
;
696 Original_Path
: constant String :=
697 Get_Name_String
(Current_With
.Path
);
699 Imported_Path_Name
: constant String :=
702 Project_Directory_Path
);
704 Withed_Project
: Project_Node_Id
:= Empty_Node
;
707 if Imported_Path_Name
= "" then
709 -- The project file cannot be found
711 Error_Msg_Name_1
:= Current_With
.Path
;
713 Error_Msg
("unknown project file: {", Current_With
.Location
);
715 -- If this is not imported by the main project file,
716 -- display the import path.
718 if Project_Stack
.Last
> 1 then
719 for Index
in reverse 1 .. Project_Stack
.Last
loop
720 Error_Msg_Name_1
:= Project_Stack
.Table
(Index
).Path_Name
;
721 Error_Msg
("\imported by {", Current_With
.Location
);
728 Previous_Project
:= Current_Project
;
730 if Current_Project
= Empty_Node
then
732 -- First with clause of the context clause
734 Current_Project
:= Current_With
.Node
;
735 Imported_Projects
:= Current_Project
;
738 Next_Project
:= Current_With
.Node
;
739 Set_Next_With_Clause_Of
(Current_Project
, Next_Project
);
740 Current_Project
:= Next_Project
;
744 (Current_Project
, Current_With
.Path
);
745 Set_Location_Of
(Current_Project
, Current_With
.Location
);
747 -- If this is a "limited with", check if we have
748 -- a circularity; if we have one, get the project id
749 -- of the limited imported project file, and don't
752 if Limited_With
and then Project_Stack
.Last
> 1 then
754 Normed
: constant String :=
755 Normalize_Pathname
(Imported_Path_Name
);
756 Canonical_Path_Name
: Name_Id
;
759 Name_Len
:= Normed
'Length;
760 Name_Buffer
(1 .. Name_Len
) := Normed
;
761 Canonical_Case_File_Name
(Name_Buffer
(1 .. Name_Len
));
762 Canonical_Path_Name
:= Name_Find
;
764 for Index
in 1 .. Project_Stack
.Last
loop
765 if Project_Stack
.Table
(Index
).Canonical_Path_Name
=
768 -- We have found the limited imported project,
769 -- get its project id, and do not parse it.
771 Withed_Project
:= Project_Stack
.Table
(Index
).Id
;
778 -- Parse the imported project, if its project id is unknown
780 if Withed_Project
= Empty_Node
then
782 (Project
=> Withed_Project
,
783 Extends_All
=> Extends_All
,
784 Path_Name
=> Imported_Path_Name
,
786 From_Extended
=> From_Extended
);
789 Extends_All
:= Is_Extending_All
(Withed_Project
);
792 if Withed_Project
= Empty_Node
then
793 -- If parsing was not successful, remove the
796 Current_Project
:= Previous_Project
;
798 if Current_Project
= Empty_Node
then
799 Imported_Projects
:= Empty_Node
;
802 Set_Next_With_Clause_Of
803 (Current_Project
, Empty_Node
);
806 -- If parsing was successful, record project name
807 -- and path name in with clause
810 (Node
=> Current_Project
,
811 To
=> Withed_Project
,
812 Limited_With
=> Limited_With
);
813 Set_Name_Of
(Current_Project
, Name_Of
(Withed_Project
));
814 Name_Len
:= Imported_Path_Name
'Length;
815 Name_Buffer
(1 .. Name_Len
) := Imported_Path_Name
;
816 Set_Path_Name_Of
(Current_Project
, Name_Find
);
819 Set_Is_Extending_All
(Current_Project
);
825 end Post_Parse_Context_Clause
;
827 --------------------------
828 -- Parse_Single_Project --
829 --------------------------
831 procedure Parse_Single_Project
832 (Project
: out Project_Node_Id
;
833 Extends_All
: out Boolean;
836 From_Extended
: Extension_Origin
)
838 Normed_Path_Name
: Name_Id
;
839 Canonical_Path_Name
: Name_Id
;
840 Project_Directory
: Name_Id
;
841 Project_Scan_State
: Saved_Project_Scan_State
;
842 Source_Index
: Source_File_Index
;
844 Extending
: Boolean := False;
846 Extended_Project
: Project_Node_Id
:= Empty_Node
;
848 A_Project_Name_And_Node
: Tree_Private_Part
.Project_Name_And_Node
:=
849 Tree_Private_Part
.Projects_Htable
.Get_First
;
851 Name_From_Path
: constant Name_Id
:= Project_Name_From
(Path_Name
);
853 Name_Of_Project
: Name_Id
:= No_Name
;
855 First_With
: With_Id
;
857 use Tree_Private_Part
;
859 Project_Comment_State
: Tree
.Comment_State
;
862 Extends_All
:= False;
865 Normed_Path
: constant String := Normalize_Pathname
866 (Path_Name
, Resolve_Links
=> False,
867 Case_Sensitive
=> True);
868 Canonical_Path
: constant String := Normalize_Pathname
869 (Normed_Path
, Resolve_Links
=> True,
870 Case_Sensitive
=> False);
873 Name_Len
:= Normed_Path
'Length;
874 Name_Buffer
(1 .. Name_Len
) := Normed_Path
;
875 Normed_Path_Name
:= Name_Find
;
876 Name_Len
:= Canonical_Path
'Length;
877 Name_Buffer
(1 .. Name_Len
) := Canonical_Path
;
878 Canonical_Path_Name
:= Name_Find
;
881 -- Check for a circular dependency
883 for Index
in 1 .. Project_Stack
.Last
loop
884 if Canonical_Path_Name
=
885 Project_Stack
.Table
(Index
).Canonical_Path_Name
887 Error_Msg
("circular dependency detected", Token_Ptr
);
888 Error_Msg_Name_1
:= Normed_Path_Name
;
889 Error_Msg
("\ { is imported by", Token_Ptr
);
891 for Current
in reverse 1 .. Project_Stack
.Last
loop
892 Error_Msg_Name_1
:= Project_Stack
.Table
(Current
).Path_Name
;
894 if Project_Stack
.Table
(Current
).Canonical_Path_Name
/=
898 ("\ { which itself is imported by", Token_Ptr
);
901 Error_Msg
("\ {", Token_Ptr
);
906 Project
:= Empty_Node
;
911 -- Put the new path name on the stack
913 Project_Stack
.Increment_Last
;
914 Project_Stack
.Table
(Project_Stack
.Last
).Path_Name
:= Normed_Path_Name
;
915 Project_Stack
.Table
(Project_Stack
.Last
).Canonical_Path_Name
:=
918 -- Check if the project file has already been parsed.
921 A_Project_Name_And_Node
/= Tree_Private_Part
.No_Project_Name_And_Node
924 Path_Id
: Name_Id
:= Path_Name_Of
(A_Project_Name_And_Node
.Node
);
927 if Path_Id
/= No_Name
then
928 Get_Name_String
(Path_Id
);
929 Canonical_Case_File_Name
(Name_Buffer
(1 .. Name_Len
));
930 Path_Id
:= Name_Find
;
933 if Path_Id
= Canonical_Path_Name
then
936 if A_Project_Name_And_Node
.Extended
then
938 ("cannot extend the same project file several times",
943 ("cannot extend an already imported project file",
947 elsif A_Project_Name_And_Node
.Extended
then
949 Is_Extending_All
(A_Project_Name_And_Node
.Node
);
951 -- If the imported project is an extended project A,
952 -- and we are in an extended project, replace A with the
953 -- ultimate project extending A.
955 if From_Extended
/= None
then
957 Decl
: Project_Node_Id
:=
958 Project_Declaration_Of
959 (A_Project_Name_And_Node
.Node
);
961 Prj
: Project_Node_Id
:=
962 Extending_Project_Of
(Decl
);
966 Decl
:= Project_Declaration_Of
(Prj
);
967 exit when Extending_Project_Of
(Decl
) = Empty_Node
;
968 Prj
:= Extending_Project_Of
(Decl
);
971 A_Project_Name_And_Node
.Node
:= Prj
;
975 ("cannot import an already extended project file",
980 Project
:= A_Project_Name_And_Node
.Node
;
981 Project_Stack
.Decrement_Last
;
986 A_Project_Name_And_Node
:= Tree_Private_Part
.Projects_Htable
.Get_Next
;
989 -- We never encountered this project file
990 -- Save the scan state, load the project file and start to scan it.
992 Save_Project_Scan_State
(Project_Scan_State
);
993 Source_Index
:= Load_Project_File
(Path_Name
);
994 Tree
.Save
(Project_Comment_State
);
996 -- If we cannot find it, we stop
998 if Source_Index
= No_Source_File
then
999 Project
:= Empty_Node
;
1000 Project_Stack
.Decrement_Last
;
1004 Prj
.Err
.Scanner
.Initialize_Scanner
(Types
.No_Unit
, Source_Index
);
1008 if Name_From_Path
= No_Name
then
1010 -- The project file name is not correct (no or bad extension,
1011 -- or not following Ada identifier's syntax).
1013 Error_Msg_Name_1
:= Canonical_Path_Name
;
1014 Error_Msg
("?{ is not a valid path name for a project file",
1018 if Current_Verbosity
>= Medium
then
1019 Write_Str
("Parsing """);
1020 Write_Str
(Path_Name
);
1025 -- Is there any imported project?
1027 Pre_Parse_Context_Clause
(First_With
);
1029 Project_Directory
:= Immediate_Directory_Of
(Normed_Path_Name
);
1030 Project
:= Default_Project_Node
(Of_Kind
=> N_Project
);
1031 Project_Stack
.Table
(Project_Stack
.Last
).Id
:= Project
;
1032 Set_Directory_Of
(Project
, Project_Directory
);
1033 Set_Path_Name_Of
(Project
, Normed_Path_Name
);
1034 Set_Location_Of
(Project
, Token_Ptr
);
1036 Expect
(Tok_Project
, "PROJECT");
1038 -- Mark location of PROJECT token if present
1040 if Token
= Tok_Project
then
1041 Set_Location_Of
(Project
, Token_Ptr
);
1042 Scan
; -- scan past project
1050 Expect
(Tok_Identifier
, "identifier");
1052 -- If the token is not an identifier, clear the buffer before
1053 -- exiting to indicate that the name of the project is ill-formed.
1055 if Token
/= Tok_Identifier
then
1060 -- Add the identifier name to the buffer
1062 Get_Name_String
(Token_Name
);
1063 Add_To_Buffer
(Name_Buffer
(1 .. Name_Len
));
1065 -- Scan past the identifier
1069 -- If we have a dot, add a dot the the Buffer and look for the next
1072 exit when Token
/= Tok_Dot
;
1073 Add_To_Buffer
(".");
1075 -- Scan past the dot
1080 -- See if this is an extending project
1082 if Token
= Tok_Extends
then
1084 -- Make sure that gnatmake will use mapping files
1086 Create_Mapping_File
:= True;
1088 -- We are extending another project
1092 Scan
; -- scan past EXTENDS
1094 if Token
= Tok_All
then
1095 Extends_All
:= True;
1096 Set_Is_Extending_All
(Project
);
1097 Scan
; -- scan past ALL
1101 -- If the name is well formed, Buffer_Last is > 0
1103 if Buffer_Last
> 0 then
1105 -- The Buffer contains the name of the project
1107 Name_Len
:= Buffer_Last
;
1108 Name_Buffer
(1 .. Name_Len
) := Buffer
(1 .. Buffer_Last
);
1109 Name_Of_Project
:= Name_Find
;
1110 Set_Name_Of
(Project
, Name_Of_Project
);
1112 -- To get expected name of the project file, replace dots by dashes
1114 Name_Len
:= Buffer_Last
;
1115 Name_Buffer
(1 .. Name_Len
) := Buffer
(1 .. Buffer_Last
);
1117 for Index
in 1 .. Name_Len
loop
1118 if Name_Buffer
(Index
) = '.' then
1119 Name_Buffer
(Index
) := '-';
1123 Canonical_Case_File_Name
(Name_Buffer
(1 .. Name_Len
));
1126 Expected_Name
: constant Name_Id
:= Name_Find
;
1129 -- Output a warning if the actual name is not the expected name
1131 if Name_From_Path
/= No_Name
1132 and then Expected_Name
/= Name_From_Path
1134 Error_Msg_Name_1
:= Expected_Name
;
1135 Error_Msg
("?file name does not match unit name, " &
1136 "should be `{" & Project_File_Extension
& "`",
1142 Imported_Projects
: Project_Node_Id
:= Empty_Node
;
1143 From_Ext
: Extension_Origin
:= None
;
1146 -- Extending_All is always propagated
1148 if From_Extended
= Extending_All
or else Extends_All
then
1149 From_Ext
:= Extending_All
;
1151 -- Otherwise, From_Extended is set to Extending_Single if the
1152 -- current project is an extending project.
1155 From_Ext
:= Extending_Simple
;
1158 Post_Parse_Context_Clause
1159 (Context_Clause
=> First_With
,
1160 Imported_Projects
=> Imported_Projects
,
1161 Project_Directory
=> Project_Directory
,
1162 From_Extended
=> From_Ext
);
1163 Set_First_With_Clause_Of
(Project
, Imported_Projects
);
1167 Project_Name
: Name_Id
:=
1168 Tree_Private_Part
.Projects_Htable
.Get_First
.Name
;
1171 -- Check if we already have a project with this name
1173 while Project_Name
/= No_Name
1174 and then Project_Name
/= Name_Of_Project
1176 Project_Name
:= Tree_Private_Part
.Projects_Htable
.Get_Next
.Name
;
1179 -- Report an error if we already have a project with this name
1181 if Project_Name
/= No_Name
then
1182 Error_Msg
("duplicate project name", Token_Ptr
);
1185 -- Otherwise, add the name of the project to the hash table, so
1186 -- that we can check that no other subsequent project will have
1189 Tree_Private_Part
.Projects_Htable
.Set
1190 (K
=> Name_Of_Project
,
1191 E
=> (Name
=> Name_Of_Project
,
1193 Extended
=> Extended
));
1200 Expect
(Tok_String_Literal
, "literal string");
1202 if Token
= Tok_String_Literal
then
1203 Set_Extended_Project_Path_Of
(Project
, Token_Name
);
1206 Original_Path_Name
: constant String :=
1207 Get_Name_String
(Token_Name
);
1209 Extended_Project_Path_Name
: constant String :=
1210 Project_Path_Name_Of
1211 (Original_Path_Name
,
1213 (Project_Directory
));
1216 if Extended_Project_Path_Name
= "" then
1218 -- We could not find the project file to extend
1220 Error_Msg_Name_1
:= Token_Name
;
1222 Error_Msg
("unknown project file: {", Token_Ptr
);
1224 -- If we are not in the main project file, display the
1227 if Project_Stack
.Last
> 1 then
1229 Project_Stack
.Table
(Project_Stack
.Last
).Path_Name
;
1230 Error_Msg
("\extended by {", Token_Ptr
);
1232 for Index
in reverse 1 .. Project_Stack
.Last
- 1 loop
1234 Project_Stack
.Table
(Index
).Path_Name
;
1235 Error_Msg
("\imported by {", Token_Ptr
);
1241 From_Ext
: Extension_Origin
:= None
;
1244 if From_Extended
= Extending_All
or else Extends_All
then
1245 From_Ext
:= Extending_All
;
1248 Parse_Single_Project
1249 (Project
=> Extended_Project
,
1250 Extends_All
=> Extends_All
,
1251 Path_Name
=> Extended_Project_Path_Name
,
1253 From_Extended
=> From_Ext
);
1256 -- A project that extends an extending-all project is also
1257 -- an extending-all project.
1259 if Is_Extending_All
(Extended_Project
) then
1260 Set_Is_Extending_All
(Project
);
1265 Scan
; -- scan past the extended project path
1269 -- Check that a non extending-all project does not import an
1270 -- extending-all project.
1272 if not Is_Extending_All
(Project
) then
1274 With_Clause
: Project_Node_Id
:= First_With_Clause_Of
(Project
);
1275 Imported
: Project_Node_Id
:= Empty_Node
;
1279 while With_Clause
/= Empty_Node
loop
1280 Imported
:= Project_Node_Of
(With_Clause
);
1282 if Is_Extending_All
(With_Clause
) then
1283 Error_Msg_Name_1
:= Name_Of
(Imported
);
1284 Error_Msg
("cannot import extending-all project {",
1286 exit With_Clause_Loop
;
1289 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
1290 end loop With_Clause_Loop
;
1294 -- Check that a project with a name including a dot either imports
1295 -- or extends the project whose name precedes the last dot.
1297 if Name_Of_Project
/= No_Name
then
1298 Get_Name_String
(Name_Of_Project
);
1304 -- Look for the last dot
1306 while Name_Len
> 0 and then Name_Buffer
(Name_Len
) /= '.' loop
1307 Name_Len
:= Name_Len
- 1;
1310 -- If a dot was find, check if the parent project is imported
1313 if Name_Len
> 0 then
1314 Name_Len
:= Name_Len
- 1;
1317 Parent_Name
: constant Name_Id
:= Name_Find
;
1318 Parent_Found
: Boolean := False;
1319 With_Clause
: Project_Node_Id
:= First_With_Clause_Of
(Project
);
1322 -- If there is an extended project, check its name
1324 if Extended_Project
/= Empty_Node
then
1325 Parent_Found
:= Name_Of
(Extended_Project
) = Parent_Name
;
1328 -- If the parent project is not the extended project,
1329 -- check each imported project until we find the parent project.
1331 while not Parent_Found
and then With_Clause
/= Empty_Node
loop
1332 Parent_Found
:= Name_Of
(Project_Node_Of
(With_Clause
))
1334 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
1337 -- If the parent project was not found, report an error
1339 if not Parent_Found
then
1340 Error_Msg_Name_1
:= Name_Of_Project
;
1341 Error_Msg_Name_2
:= Parent_Name
;
1342 Error_Msg
("project { does not import or extend project {",
1343 Location_Of
(Project
));
1348 Expect
(Tok_Is
, "IS");
1349 Set_End_Of_Line
(Project
);
1350 Set_Previous_Line_Node
(Project
);
1351 Set_Next_End_Node
(Project
);
1354 Project_Declaration
: Project_Node_Id
:= Empty_Node
;
1357 -- No need to Scan past "is", Prj.Dect.Parse will do it.
1360 (Declarations
=> Project_Declaration
,
1361 Current_Project
=> Project
,
1362 Extends
=> Extended_Project
);
1363 Set_Project_Declaration_Of
(Project
, Project_Declaration
);
1365 if Extended_Project
/= Empty_Node
then
1366 Set_Extending_Project_Of
1367 (Project_Declaration_Of
(Extended_Project
), To
=> Project
);
1371 Expect
(Tok_End
, "END");
1372 Remove_Next_End_Node
;
1374 -- Skip "end" if present
1376 if Token
= Tok_End
then
1384 -- Store the name following "end" in the Buffer. The name may be made of
1385 -- several simple names.
1388 Expect
(Tok_Identifier
, "identifier");
1390 -- If we don't have an identifier, clear the buffer before exiting to
1391 -- avoid checking the name.
1393 if Token
/= Tok_Identifier
then
1398 -- Add the identifier to the Buffer
1399 Get_Name_String
(Token_Name
);
1400 Add_To_Buffer
(Name_Buffer
(1 .. Name_Len
));
1402 -- Scan past the identifier
1405 exit when Token
/= Tok_Dot
;
1406 Add_To_Buffer
(".");
1410 -- If we have a valid name, check if it is the name of the project
1412 if Name_Of_Project
/= No_Name
and then Buffer_Last
> 0 then
1413 if To_Lower
(Buffer
(1 .. Buffer_Last
)) /=
1414 Get_Name_String
(Name_Of
(Project
))
1416 -- Invalid name: report an error
1418 Error_Msg
("Expected """ &
1419 Get_Name_String
(Name_Of
(Project
)) & """",
1424 Expect
(Tok_Semicolon
, "`;`");
1426 -- Check that there is no more text following the end of the project
1429 if Token
= Tok_Semicolon
then
1430 Set_Previous_End_Node
(Project
);
1433 if Token
/= Tok_EOF
then
1435 ("Unexpected text following end of project", Token_Ptr
);
1439 -- Restore the scan state, in case we are not the main project
1441 Restore_Project_Scan_State
(Project_Scan_State
);
1443 -- And remove the project from the project stack
1445 Project_Stack
.Decrement_Last
;
1447 -- Indicate if there are unkept comments
1449 Tree
.Set_Project_File_Includes_Unkept_Comments
1450 (Node
=> Project
, To
=> Tree
.There_Are_Unkept_Comments
);
1452 -- And restore the comment state that was saved
1454 Tree
.Restore
(Project_Comment_State
);
1455 end Parse_Single_Project
;
1457 -----------------------
1458 -- Project_Name_From --
1459 -----------------------
1461 function Project_Name_From
(Path_Name
: String) return Name_Id
is
1462 Canonical
: String (1 .. Path_Name
'Length) := Path_Name
;
1463 First
: Natural := Canonical
'Last;
1464 Last
: Natural := First
;
1468 if Current_Verbosity
= High
then
1469 Write_Str
("Project_Name_From (""");
1470 Write_Str
(Canonical
);
1474 -- If the path name is empty, return No_Name to indicate failure
1480 Canonical_Case_File_Name
(Canonical
);
1482 -- Look for the last dot in the path name
1486 Canonical
(First
) /= '.'
1491 -- If we have a dot, check that it is followed by the correct extension
1493 if First
> 0 and then Canonical
(First
) = '.' then
1494 if Canonical
(First
.. Last
) = Project_File_Extension
1497 -- Look for the last directory separator, if any
1503 and then Canonical
(First
) /= '/'
1504 and then Canonical
(First
) /= Dir_Sep
1510 -- Not the correct extension, return No_Name to indicate failure
1515 -- If no dot in the path name, return No_Name to indicate failure
1523 -- If the extension is the file name, return No_Name to indicate failure
1525 if First
> Last
then
1529 -- Put the name in lower case into Name_Buffer
1531 Name_Len
:= Last
- First
+ 1;
1532 Name_Buffer
(1 .. Name_Len
) := To_Lower
(Canonical
(First
.. Last
));
1536 -- Check if it is a well formed project name. Return No_Name if it is
1540 if not Is_Letter
(Name_Buffer
(Index
)) then
1547 exit when Index
>= Name_Len
;
1549 if Name_Buffer
(Index
) = '_' then
1550 if Name_Buffer
(Index
+ 1) = '_' then
1555 exit when Name_Buffer
(Index
) = '-';
1557 if Name_Buffer
(Index
) /= '_'
1558 and then not Is_Alphanumeric
(Name_Buffer
(Index
))
1566 if Index
>= Name_Len
then
1567 if Is_Alphanumeric
(Name_Buffer
(Name_Len
)) then
1569 -- All checks have succeeded. Return name in Name_Buffer
1577 elsif Name_Buffer
(Index
) = '-' then
1581 end Project_Name_From
;
1583 --------------------------
1584 -- Project_Path_Name_Of --
1585 --------------------------
1587 function Project_Path_Name_Of
1588 (Project_File_Name
: String;
1589 Directory
: String) return String
1591 Result
: String_Access
;
1594 if Current_Verbosity
= High
then
1595 Write_Str
("Project_Path_Name_Of (""");
1596 Write_Str
(Project_File_Name
);
1597 Write_Str
(""", """);
1598 Write_Str
(Directory
);
1599 Write_Line
(""");");
1602 if not Is_Absolute_Path
(Project_File_Name
) then
1603 -- First we try <directory>/<file_name>.<extension>
1605 if Current_Verbosity
= High
then
1606 Write_Str
(" Trying ");
1607 Write_Str
(Directory
);
1608 Write_Char
(Directory_Separator
);
1609 Write_Str
(Project_File_Name
);
1610 Write_Line
(Project_File_Extension
);
1615 (File_Name
=> Directory
& Directory_Separator
&
1616 Project_File_Name
& Project_File_Extension
,
1617 Path
=> Project_Path
.all);
1619 -- Then we try <directory>/<file_name>
1621 if Result
= null then
1622 if Current_Verbosity
= High
then
1623 Write_Str
(" Trying ");
1624 Write_Str
(Directory
);
1625 Write_Char
(Directory_Separator
);
1626 Write_Line
(Project_File_Name
);
1631 (File_Name
=> Directory
& Directory_Separator
&
1633 Path
=> Project_Path
.all);
1637 if Result
= null then
1639 -- Then we try <file_name>.<extension>
1641 if Current_Verbosity
= High
then
1642 Write_Str
(" Trying ");
1643 Write_Str
(Project_File_Name
);
1644 Write_Line
(Project_File_Extension
);
1649 (File_Name
=> Project_File_Name
& Project_File_Extension
,
1650 Path
=> Project_Path
.all);
1653 if Result
= null then
1655 -- Then we try <file_name>
1657 if Current_Verbosity
= High
then
1658 Write_Str
(" Trying ");
1659 Write_Line
(Project_File_Name
);
1664 (File_Name
=> Project_File_Name
,
1665 Path
=> Project_Path
.all);
1668 -- If we cannot find the project file, we return an empty string
1670 if Result
= null then
1675 Final_Result
: constant String :=
1676 GNAT
.OS_Lib
.Normalize_Pathname
1678 Resolve_Links
=> False,
1679 Case_Sensitive
=> True);
1682 return Final_Result
;
1685 end Project_Path_Name_Of
;
1688 -- Initialize Project_Path during package elaboration
1690 if Prj_Path
.all = "" then
1691 Project_Path
:= new String'(".");
1693 Project_Path := new String'("." & Path_Separator
& Prj_Path
.all);