1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2003 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 Name_And_Id
is record
102 Id
: Project_Node_Id
;
105 package Project_Stack
is new Table
.Table
106 (Table_Component_Type
=> Name_And_Id
,
107 Table_Index_Type
=> Nat
,
108 Table_Low_Bound
=> 1,
110 Table_Increment
=> 50,
111 Table_Name
=> "Prj.Part.Project_Stack");
112 -- This table is used to detect circular dependencies
113 -- for imported and extended projects and to get the project ids of
114 -- limited imported projects when there is a circularity with at least
115 -- one limited imported project file.
117 package Virtual_Hash
is new Simple_HTable
118 (Header_Num
=> Header_Num
,
119 Element
=> Project_Node_Id
,
120 No_Element
=> Empty_Node
,
121 Key
=> Project_Node_Id
,
122 Hash
=> Prj
.Tree
.Hash
,
124 -- Hash table to store the node id of the project for which a virtual
125 -- extending project need to be created.
127 package Processed_Hash
is new Simple_HTable
128 (Header_Num
=> Header_Num
,
131 Key
=> Project_Node_Id
,
132 Hash
=> Prj
.Tree
.Hash
,
134 -- Hash table to store the project process when looking for project that
135 -- need to have a virtual extending project, to avoid processing the same
138 procedure Create_Virtual_Extending_Project
139 (For_Project
: Project_Node_Id
;
140 Main_Project
: Project_Node_Id
);
141 -- Create a virtual extending project of For_Project. Main_Project is
142 -- the extending all project.
144 procedure Look_For_Virtual_Projects_For
145 (Proj
: Project_Node_Id
;
146 Potentially_Virtual
: Boolean);
147 -- Look for projects that need to have a virtual extending project.
148 -- This procedure is recursive. If called with Potentially_Virtual set to
149 -- True, then Proj may need an virtual extending project; otherwise it
150 -- does not (because it is already extended), but other projects that it
151 -- imports may need to be virtually extended.
153 procedure Pre_Parse_Context_Clause
(Context_Clause
: out With_Id
);
154 -- Parse the context clause of a project.
155 -- Store the paths and locations of the imported projects in table Withs.
156 -- Does nothing if there is no context clause (if the current
157 -- token is not "with" or "limited" followed by "with").
159 procedure Post_Parse_Context_Clause
160 (Context_Clause
: With_Id
;
161 Imported_Projects
: out Project_Node_Id
;
162 Project_Directory
: Name_Id
;
163 From_Extended
: Extension_Origin
);
164 -- Parse the imported projects that have been stored in table Withs,
165 -- if any. From_Extended is used for the call to Parse_Single_Project
168 procedure Parse_Single_Project
169 (Project
: out Project_Node_Id
;
172 From_Extended
: Extension_Origin
);
173 -- Parse a project file.
174 -- Recursive procedure: it calls itself for imported and extended
175 -- projects. When From_Extended is not None, if the project has already
176 -- been parsed and is an extended project A, return the ultimate
177 -- (not extended) project that extends A.
179 function Project_Path_Name_Of
180 (Project_File_Name
: String;
183 -- Returns the path name of a project file. Returns an empty string
184 -- if project file cannot be found.
186 function Immediate_Directory_Of
(Path_Name
: Name_Id
) return Name_Id
;
187 -- Get the directory of the file with the specified path name.
188 -- This includes the directory separator as the last character.
189 -- Returns "./" if Path_Name contains no directory separator.
191 function Project_Name_From
(Path_Name
: String) return Name_Id
;
192 -- Returns the name of the project that corresponds to its path name.
193 -- Returns No_Name if the path name is invalid, because the corresponding
194 -- project name does not have the syntax of an ada identifier.
196 --------------------------------------
197 -- Create_Virtual_Extending_Project --
198 --------------------------------------
200 procedure Create_Virtual_Extending_Project
201 (For_Project
: Project_Node_Id
;
202 Main_Project
: Project_Node_Id
)
205 Virtual_Name
: constant String :=
207 Get_Name_String
(Name_Of
(For_Project
));
208 -- The name of the virtual extending project
210 Virtual_Name_Id
: Name_Id
;
211 -- Virtual extending project name id
213 Virtual_Path_Id
: Name_Id
;
214 -- Fake path name of the virtual extending project. The directory is
215 -- the same directory as the extending all project.
217 Virtual_Dir_Id
: constant Name_Id
:=
218 Immediate_Directory_Of
(Path_Name_Of
(Main_Project
));
219 -- The directory of the extending all project
221 -- The source of the virtual extending project is something like:
223 -- project V$<project name> extends <project path> is
225 -- for Source_Dirs use ();
227 -- end V$<project name>;
229 -- The project directory cannot be specified during parsing; it will be
230 -- put directly in the virtual extending project data during processing.
232 -- Nodes that made up the virtual extending project
234 Virtual_Project
: constant Project_Node_Id
:=
235 Default_Project_Node
(N_Project
);
236 With_Clause
: constant Project_Node_Id
:=
237 Default_Project_Node
(N_With_Clause
);
238 Project_Declaration
: constant Project_Node_Id
:=
239 Default_Project_Node
(N_Project_Declaration
);
240 Source_Dirs_Declaration
: constant Project_Node_Id
:=
241 Default_Project_Node
(N_Declarative_Item
);
242 Source_Dirs_Attribute
: constant Project_Node_Id
:=
244 (N_Attribute_Declaration
, List
);
245 Source_Dirs_Expression
: constant Project_Node_Id
:=
246 Default_Project_Node
(N_Expression
, List
);
247 Source_Dirs_Term
: constant Project_Node_Id
:=
248 Default_Project_Node
(N_Term
, List
);
249 Source_Dirs_List
: constant Project_Node_Id
:=
251 (N_Literal_String_List
, List
);
254 -- Get the virtual name id
256 Name_Len
:= Virtual_Name
'Length;
257 Name_Buffer
(1 .. Name_Len
) := Virtual_Name
;
258 Virtual_Name_Id
:= Name_Find
;
260 -- Get the virtual path name
262 Get_Name_String
(Path_Name_Of
(Main_Project
));
265 and then Name_Buffer
(Name_Len
) /= Directory_Separator
266 and then Name_Buffer
(Name_Len
) /= '/'
268 Name_Len
:= Name_Len
- 1;
271 Name_Buffer
(Name_Len
+ 1 .. Name_Len
+ Virtual_Name
'Length) :=
273 Name_Len
:= Name_Len
+ Virtual_Name
'Length;
274 Virtual_Path_Id
:= Name_Find
;
278 Set_Name_Of
(With_Clause
, Virtual_Name_Id
);
279 Set_Path_Name_Of
(With_Clause
, Virtual_Path_Id
);
280 Set_Project_Node_Of
(With_Clause
, Virtual_Project
);
281 Set_Next_With_Clause_Of
282 (With_Clause
, First_With_Clause_Of
(Main_Project
));
283 Set_First_With_Clause_Of
(Main_Project
, With_Clause
);
285 -- Virtual project node
287 Set_Name_Of
(Virtual_Project
, Virtual_Name_Id
);
288 Set_Path_Name_Of
(Virtual_Project
, Virtual_Path_Id
);
289 Set_Location_Of
(Virtual_Project
, Location_Of
(Main_Project
));
290 Set_Directory_Of
(Virtual_Project
, Virtual_Dir_Id
);
291 Set_Project_Declaration_Of
(Virtual_Project
, Project_Declaration
);
292 Set_Extended_Project_Path_Of
293 (Virtual_Project
, Path_Name_Of
(For_Project
));
295 -- Project declaration
297 Set_First_Declarative_Item_Of
298 (Project_Declaration
, Source_Dirs_Declaration
);
299 Set_Extended_Project_Of
(Project_Declaration
, For_Project
);
301 -- Source_Dirs declaration
303 Set_Current_Item_Node
(Source_Dirs_Declaration
, Source_Dirs_Attribute
);
305 -- Source_Dirs attribute
307 Set_Name_Of
(Source_Dirs_Attribute
, Snames
.Name_Source_Dirs
);
308 Set_Expression_Of
(Source_Dirs_Attribute
, Source_Dirs_Expression
);
310 -- Source_Dirs expression
312 Set_First_Term
(Source_Dirs_Expression
, Source_Dirs_Term
);
316 Set_Current_Term
(Source_Dirs_Term
, Source_Dirs_List
);
318 -- Source_Dirs empty list: nothing to do
320 end Create_Virtual_Extending_Project
;
322 ----------------------------
323 -- Immediate_Directory_Of --
324 ----------------------------
326 function Immediate_Directory_Of
(Path_Name
: Name_Id
) return Name_Id
is
328 Get_Name_String
(Path_Name
);
330 for Index
in reverse 1 .. Name_Len
loop
331 if Name_Buffer
(Index
) = '/'
332 or else Name_Buffer
(Index
) = Dir_Sep
334 -- Remove all chars after last directory separator from name
337 Name_Len
:= Index
- 1;
347 -- There is no directory separator in name. Return "./" or ".\"
350 Name_Buffer
(1) := '.';
351 Name_Buffer
(2) := Dir_Sep
;
353 end Immediate_Directory_Of
;
355 -----------------------------------
356 -- Look_For_Virtual_Projects_For --
357 -----------------------------------
359 procedure Look_For_Virtual_Projects_For
360 (Proj
: Project_Node_Id
;
361 Potentially_Virtual
: Boolean)
364 Declaration
: Project_Node_Id
:= Empty_Node
;
365 -- Node for the project declaration of Proj
367 With_Clause
: Project_Node_Id
:= Empty_Node
;
368 -- Node for a with clause of Proj
370 Imported
: Project_Node_Id
:= Empty_Node
;
371 -- Node for a project imported by Proj
373 Extended
: Project_Node_Id
:= Empty_Node
;
374 -- Node for the eventual project extended by Proj
377 -- Nothing to do if Proj is not defined or if it has already been
380 if Proj
/= Empty_Node
and then not Processed_Hash
.Get
(Proj
) then
381 -- Make sure the project will not be processed again
383 Processed_Hash
.Set
(Proj
, True);
385 Declaration
:= Project_Declaration_Of
(Proj
);
387 if Declaration
/= Empty_Node
then
388 Extended
:= Extended_Project_Of
(Declaration
);
391 -- If this is a project that may need a virtual extending project
392 -- and it is not itself an extending project, put it in the list.
394 if Potentially_Virtual
and then Extended
= Empty_Node
then
395 Virtual_Hash
.Set
(Proj
, Proj
);
398 -- Now check the projects it imports
400 With_Clause
:= First_With_Clause_Of
(Proj
);
402 while With_Clause
/= Empty_Node
loop
403 Imported
:= Project_Node_Of
(With_Clause
);
405 if Imported
/= Empty_Node
then
406 Look_For_Virtual_Projects_For
407 (Imported
, Potentially_Virtual
=> True);
410 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
413 -- Check also the eventual project extended by Proj. As this project
414 -- is already extended, call recursively with Potentially_Virtual
417 Look_For_Virtual_Projects_For
418 (Extended
, Potentially_Virtual
=> False);
420 end Look_For_Virtual_Projects_For
;
427 (Project
: out Project_Node_Id
;
428 Project_File_Name
: String;
429 Always_Errout_Finalize
: Boolean;
430 Packages_To_Check
: String_List_Access
:= All_Packages
;
431 Store_Comments
: Boolean := False)
433 Current_Directory
: constant String := Get_Current_Dir
;
436 -- Save the Packages_To_Check in Prj, so that it is visible from
439 Current_Packages_To_Check
:= Packages_To_Check
;
441 Project
:= Empty_Node
;
443 if Current_Verbosity
>= Medium
then
444 Write_Str
("ADA_PROJECT_PATH=""");
445 Write_Str
(Project_Path
.all);
450 Path_Name
: constant String :=
451 Project_Path_Name_Of
(Project_File_Name
,
452 Directory
=> Current_Directory
);
456 Prj
.Err
.Scanner
.Set_Comment_As_Token
(Store_Comments
);
457 Prj
.Err
.Scanner
.Set_End_Of_Line_As_Token
(Store_Comments
);
459 -- Parse the main project file
461 if Path_Name
= "" then
463 ("project file """, Project_File_Name
, """ not found");
464 Project
:= Empty_Node
;
470 Path_Name
=> Path_Name
,
472 From_Extended
=> None
);
474 -- If Project is an extending-all project, create the eventual
475 -- virtual extending projects and check that there are no illegally
476 -- imported projects.
478 if Project
/= Empty_Node
and then Is_Extending_All
(Project
) then
479 -- First look for projects that potentially need a virtual
480 -- extending project.
483 Processed_Hash
.Reset
;
485 -- Mark the extending all project as processed, to avoid checking
486 -- the imported projects in case of a "limited with" on this
487 -- extending all project.
489 Processed_Hash
.Set
(Project
, True);
492 Declaration
: constant Project_Node_Id
:=
493 Project_Declaration_Of
(Project
);
495 Look_For_Virtual_Projects_For
496 (Extended_Project_Of
(Declaration
),
497 Potentially_Virtual
=> False);
500 -- Now, check the projects directly imported by the main project.
501 -- Remove from the potentially virtual any project extended by one
502 -- of these imported projects. For non extending imported
503 -- projects, check that they do not belong to the project tree of
504 -- the project being "extended-all" by the main project.
507 With_Clause
: Project_Node_Id
:=
508 First_With_Clause_Of
(Project
);
509 Imported
: Project_Node_Id
:= Empty_Node
;
510 Declaration
: Project_Node_Id
:= Empty_Node
;
513 while With_Clause
/= Empty_Node
loop
514 Imported
:= Project_Node_Of
(With_Clause
);
516 if Imported
/= Empty_Node
then
517 Declaration
:= Project_Declaration_Of
(Imported
);
519 if Extended_Project_Of
(Declaration
) /= Empty_Node
then
521 Imported
:= Extended_Project_Of
(Declaration
);
522 exit when Imported
= Empty_Node
;
523 Virtual_Hash
.Remove
(Imported
);
524 Declaration
:= Project_Declaration_Of
(Imported
);
527 elsif Virtual_Hash
.Get
(Imported
) /= Empty_Node
then
529 ("this project cannot be imported directly",
530 Location_Of
(With_Clause
));
535 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
539 -- Now create all the virtual extending projects
542 Proj
: Project_Node_Id
:= Virtual_Hash
.Get_First
;
544 while Proj
/= Empty_Node
loop
545 Create_Virtual_Extending_Project
(Proj
, Project
);
546 Proj
:= Virtual_Hash
.Get_Next
;
551 -- If there were any kind of error during the parsing, serious
552 -- or not, then the parsing fails.
554 if Err_Vars
.Total_Errors_Detected
> 0 then
555 Project
:= Empty_Node
;
558 if Project
= Empty_Node
or else Always_Errout_Finalize
then
568 Write_Line
(Exception_Information
(X
));
569 Write_Str
("Exception ");
570 Write_Str
(Exception_Name
(X
));
571 Write_Line
(" raised, while processing project file");
572 Project
:= Empty_Node
;
575 ------------------------------
576 -- Pre_Parse_Context_Clause --
577 ------------------------------
579 procedure Pre_Parse_Context_Clause
(Context_Clause
: out With_Id
) is
580 Current_With_Clause
: With_Id
:= No_With
;
581 Limited_With
: Boolean := False;
583 Current_With
: With_Record
;
585 Current_With_Node
: Project_Node_Id
:= Empty_Node
;
588 -- Assume no context clause
590 Context_Clause
:= No_With
;
593 -- If Token is not WITH or LIMITED, there is no context clause,
594 -- or we have exhausted the with clauses.
596 while Token
= Tok_With
or else Token
= Tok_Limited
loop
597 Current_With_Node
:= Default_Project_Node
(Of_Kind
=> N_With_Clause
);
598 Limited_With
:= Token
= Tok_Limited
;
601 Scan
; -- scan past LIMITED
602 Expect
(Tok_With
, "WITH");
603 exit With_Loop
when Token
/= Tok_With
;
608 Scan
; -- scan past WITH or ","
610 Expect
(Tok_String_Literal
, "literal string");
612 if Token
/= Tok_String_Literal
then
616 -- Store path and location in table Withs
620 Location
=> Token_Ptr
,
621 Limited_With
=> Limited_With
,
622 Node
=> Current_With_Node
,
625 Withs
.Increment_Last
;
626 Withs
.Table
(Withs
.Last
) := Current_With
;
628 if Current_With_Clause
= No_With
then
629 Context_Clause
:= Withs
.Last
;
632 Withs
.Table
(Current_With_Clause
).Next
:= Withs
.Last
;
635 Current_With_Clause
:= Withs
.Last
;
639 if Token
= Tok_Semicolon
then
640 Set_End_Of_Line
(Current_With_Node
);
641 Set_Previous_Line_Node
(Current_With_Node
);
643 -- End of (possibly multiple) with clause;
645 Scan
; -- scan past the semicolon.
648 elsif Token
/= Tok_Comma
then
649 Error_Msg
("expected comma or semi colon", Token_Ptr
);
654 Default_Project_Node
(Of_Kind
=> N_With_Clause
);
657 end Pre_Parse_Context_Clause
;
660 -------------------------------
661 -- Post_Parse_Context_Clause --
662 -------------------------------
664 procedure Post_Parse_Context_Clause
665 (Context_Clause
: With_Id
;
666 Imported_Projects
: out Project_Node_Id
;
667 Project_Directory
: Name_Id
;
668 From_Extended
: Extension_Origin
)
670 Current_With_Clause
: With_Id
:= Context_Clause
;
672 Current_Project
: Project_Node_Id
:= Empty_Node
;
673 Previous_Project
: Project_Node_Id
:= Empty_Node
;
674 Next_Project
: Project_Node_Id
:= Empty_Node
;
676 Project_Directory_Path
: constant String :=
677 Get_Name_String
(Project_Directory
);
679 Current_With
: With_Record
;
680 Limited_With
: Boolean := False;
683 Imported_Projects
:= Empty_Node
;
685 while Current_With_Clause
/= No_With
loop
686 Current_With
:= Withs
.Table
(Current_With_Clause
);
687 Current_With_Clause
:= Current_With
.Next
;
689 Limited_With
:= Current_With
.Limited_With
;
692 Original_Path
: constant String :=
693 Get_Name_String
(Current_With
.Path
);
695 Imported_Path_Name
: constant String :=
698 Project_Directory_Path
);
700 Withed_Project
: Project_Node_Id
:= Empty_Node
;
703 if Imported_Path_Name
= "" then
705 -- The project file cannot be found
707 Error_Msg_Name_1
:= Current_With
.Path
;
709 Error_Msg
("unknown project file: {", Current_With
.Location
);
711 -- If this is not imported by the main project file,
712 -- display the import path.
714 if Project_Stack
.Last
> 1 then
715 for Index
in reverse 1 .. Project_Stack
.Last
loop
716 Error_Msg_Name_1
:= Project_Stack
.Table
(Index
).Name
;
717 Error_Msg
("\imported by {", Current_With
.Location
);
724 Previous_Project
:= Current_Project
;
726 if Current_Project
= Empty_Node
then
728 -- First with clause of the context clause
730 Current_Project
:= Current_With
.Node
;
731 Imported_Projects
:= Current_Project
;
734 Next_Project
:= Current_With
.Node
;
735 Set_Next_With_Clause_Of
(Current_Project
, Next_Project
);
736 Current_Project
:= Next_Project
;
740 (Current_Project
, Current_With
.Path
);
741 Set_Location_Of
(Current_Project
, Current_With
.Location
);
743 -- If this is a "limited with", check if we have
744 -- a circularity; if we have one, get the project id
745 -- of the limited imported project file, and don't
748 if Limited_With
and then Project_Stack
.Last
> 1 then
750 Normed
: constant String :=
751 Normalize_Pathname
(Imported_Path_Name
);
752 Canonical_Path_Name
: Name_Id
;
755 Name_Len
:= Normed
'Length;
756 Name_Buffer
(1 .. Name_Len
) := Normed
;
757 Canonical_Path_Name
:= Name_Find
;
759 for Index
in 1 .. Project_Stack
.Last
loop
760 if Project_Stack
.Table
(Index
).Name
=
763 -- We have found the limited imported project,
764 -- get its project id, and don't parse it.
766 Withed_Project
:= Project_Stack
.Table
(Index
).Id
;
773 -- Parse the imported project, if its project id is unknown
775 if Withed_Project
= Empty_Node
then
777 (Project
=> Withed_Project
,
778 Path_Name
=> Imported_Path_Name
,
780 From_Extended
=> From_Extended
);
783 if Withed_Project
= Empty_Node
then
784 -- If parsing was not successful, remove the
787 Current_Project
:= Previous_Project
;
789 if Current_Project
= Empty_Node
then
790 Imported_Projects
:= Empty_Node
;
793 Set_Next_With_Clause_Of
794 (Current_Project
, Empty_Node
);
797 -- If parsing was successful, record project name
798 -- and path name in with clause
801 (Node
=> Current_Project
,
802 To
=> Withed_Project
,
803 Limited_With
=> Limited_With
);
804 Set_Name_Of
(Current_Project
, Name_Of
(Withed_Project
));
805 Name_Len
:= Imported_Path_Name
'Length;
806 Name_Buffer
(1 .. Name_Len
) := Imported_Path_Name
;
807 Set_Path_Name_Of
(Current_Project
, Name_Find
);
812 end Post_Parse_Context_Clause
;
814 --------------------------
815 -- Parse_Single_Project --
816 --------------------------
818 procedure Parse_Single_Project
819 (Project
: out Project_Node_Id
;
822 From_Extended
: Extension_Origin
)
824 Normed_Path_Name
: Name_Id
;
825 Canonical_Path_Name
: Name_Id
;
826 Project_Directory
: Name_Id
;
827 Project_Scan_State
: Saved_Project_Scan_State
;
828 Source_Index
: Source_File_Index
;
830 Extended_Project
: Project_Node_Id
:= Empty_Node
;
832 A_Project_Name_And_Node
: Tree_Private_Part
.Project_Name_And_Node
:=
833 Tree_Private_Part
.Projects_Htable
.Get_First
;
835 Name_From_Path
: constant Name_Id
:= Project_Name_From
(Path_Name
);
837 Name_Of_Project
: Name_Id
:= No_Name
;
839 First_With
: With_Id
;
841 use Tree_Private_Part
;
843 Project_Comment_State
: Tree
.Comment_State
;
847 Normed
: String := Normalize_Pathname
(Path_Name
);
849 Name_Len
:= Normed
'Length;
850 Name_Buffer
(1 .. Name_Len
) := Normed
;
851 Normed_Path_Name
:= Name_Find
;
852 Canonical_Case_File_Name
(Normed
);
853 Name_Len
:= Normed
'Length;
854 Name_Buffer
(1 .. Name_Len
) := Normed
;
855 Canonical_Path_Name
:= Name_Find
;
858 -- Check for a circular dependency
860 for Index
in 1 .. Project_Stack
.Last
loop
861 if Canonical_Path_Name
= Project_Stack
.Table
(Index
).Name
then
862 Error_Msg
("circular dependency detected", Token_Ptr
);
863 Error_Msg_Name_1
:= Normed_Path_Name
;
864 Error_Msg
("\ { is imported by", Token_Ptr
);
866 for Current
in reverse 1 .. Project_Stack
.Last
loop
867 Error_Msg_Name_1
:= Project_Stack
.Table
(Current
).Name
;
869 if Error_Msg_Name_1
/= Canonical_Path_Name
then
871 ("\ { which itself is imported by", Token_Ptr
);
874 Error_Msg
("\ {", Token_Ptr
);
879 Project
:= Empty_Node
;
884 -- Put the new path name on the stack
886 Project_Stack
.Increment_Last
;
887 Project_Stack
.Table
(Project_Stack
.Last
).Name
:= Canonical_Path_Name
;
889 -- Check if the project file has already been parsed.
892 A_Project_Name_And_Node
/= Tree_Private_Part
.No_Project_Name_And_Node
895 Path_Name_Of
(A_Project_Name_And_Node
.Node
) = Canonical_Path_Name
899 if A_Project_Name_And_Node
.Extended
then
901 ("cannot extend the same project file several times",
906 ("cannot extend an already imported project file",
910 elsif A_Project_Name_And_Node
.Extended
then
911 -- If the imported project is an extended project A, and we are
912 -- in an extended project, replace A with the ultimate project
915 if From_Extended
/= None
then
917 Decl
: Project_Node_Id
:=
918 Project_Declaration_Of
919 (A_Project_Name_And_Node
.Node
);
920 Prj
: Project_Node_Id
:=
921 Extending_Project_Of
(Decl
);
924 Decl
:= Project_Declaration_Of
(Prj
);
925 exit when Extending_Project_Of
(Decl
) = Empty_Node
;
926 Prj
:= Extending_Project_Of
(Decl
);
929 A_Project_Name_And_Node
.Node
:= Prj
;
933 ("cannot import an already extended project file",
938 Project
:= A_Project_Name_And_Node
.Node
;
939 Project_Stack
.Decrement_Last
;
943 A_Project_Name_And_Node
:= Tree_Private_Part
.Projects_Htable
.Get_Next
;
946 -- We never encountered this project file
947 -- Save the scan state, load the project file and start to scan it.
949 Save_Project_Scan_State
(Project_Scan_State
);
950 Source_Index
:= Load_Project_File
(Path_Name
);
951 Tree
.Save
(Project_Comment_State
);
953 -- if we cannot find it, we stop
955 if Source_Index
= No_Source_File
then
956 Project
:= Empty_Node
;
957 Project_Stack
.Decrement_Last
;
961 Prj
.Err
.Scanner
.Initialize_Scanner
(Types
.No_Unit
, Source_Index
);
965 if Name_From_Path
= No_Name
then
967 -- The project file name is not correct (no or bad extension,
968 -- or not following Ada identifier's syntax).
970 Error_Msg_Name_1
:= Canonical_Path_Name
;
971 Error_Msg
("?{ is not a valid path name for a project file",
975 if Current_Verbosity
>= Medium
then
976 Write_Str
("Parsing """);
977 Write_Str
(Path_Name
);
982 -- Is there any imported project?
984 Pre_Parse_Context_Clause
(First_With
);
986 Project_Directory
:= Immediate_Directory_Of
(Normed_Path_Name
);
987 Project
:= Default_Project_Node
(Of_Kind
=> N_Project
);
988 Project_Stack
.Table
(Project_Stack
.Last
).Id
:= Project
;
989 Set_Directory_Of
(Project
, Project_Directory
);
990 Set_Path_Name_Of
(Project
, Normed_Path_Name
);
991 Set_Location_Of
(Project
, Token_Ptr
);
993 Expect
(Tok_Project
, "PROJECT");
995 -- Mark location of PROJECT token if present
997 if Token
= Tok_Project
then
998 Set_Location_Of
(Project
, Token_Ptr
);
999 Scan
; -- scan past project
1007 Expect
(Tok_Identifier
, "identifier");
1009 -- If the token is not an identifier, clear the buffer before
1010 -- exiting to indicate that the name of the project is ill-formed.
1012 if Token
/= Tok_Identifier
then
1017 -- Add the identifier name to the buffer
1019 Get_Name_String
(Token_Name
);
1020 Add_To_Buffer
(Name_Buffer
(1 .. Name_Len
));
1022 -- Scan past the identifier
1026 -- If we have a dot, add a dot the the Buffer and look for the next
1029 exit when Token
/= Tok_Dot
;
1030 Add_To_Buffer
(".");
1032 -- Scan past the dot
1037 -- If the name is well formed, Buffer_Last is > 0
1039 if Buffer_Last
> 0 then
1041 -- The Buffer contains the name of the project
1043 Name_Len
:= Buffer_Last
;
1044 Name_Buffer
(1 .. Name_Len
) := Buffer
(1 .. Buffer_Last
);
1045 Name_Of_Project
:= Name_Find
;
1046 Set_Name_Of
(Project
, Name_Of_Project
);
1048 -- To get expected name of the project file, replace dots by dashes
1050 Name_Len
:= Buffer_Last
;
1051 Name_Buffer
(1 .. Name_Len
) := Buffer
(1 .. Buffer_Last
);
1053 for Index
in 1 .. Name_Len
loop
1054 if Name_Buffer
(Index
) = '.' then
1055 Name_Buffer
(Index
) := '-';
1059 Canonical_Case_File_Name
(Name_Buffer
(1 .. Name_Len
));
1062 Expected_Name
: constant Name_Id
:= Name_Find
;
1065 -- Output a warning if the actual name is not the expected name
1067 if Name_From_Path
/= No_Name
1068 and then Expected_Name
/= Name_From_Path
1070 Error_Msg_Name_1
:= Expected_Name
;
1071 Error_Msg
("?file name does not match unit name, " &
1072 "should be `{" & Project_File_Extension
& "`",
1078 Imported_Projects
: Project_Node_Id
:= Empty_Node
;
1079 From_Ext
: Extension_Origin
:= None
;
1082 -- Extending_All is always propagated
1084 if From_Extended
= Extending_All
then
1085 From_Ext
:= Extending_All
;
1087 -- Otherwise, From_Extended is set to Extending_Single if the
1088 -- current project is an extending project.
1091 From_Ext
:= Extending_Simple
;
1094 Post_Parse_Context_Clause
1095 (Context_Clause
=> First_With
,
1096 Imported_Projects
=> Imported_Projects
,
1097 Project_Directory
=> Project_Directory
,
1098 From_Extended
=> From_Ext
);
1099 Set_First_With_Clause_Of
(Project
, Imported_Projects
);
1103 Project_Name
: Name_Id
:=
1104 Tree_Private_Part
.Projects_Htable
.Get_First
.Name
;
1107 -- Check if we already have a project with this name
1109 while Project_Name
/= No_Name
1110 and then Project_Name
/= Name_Of_Project
1112 Project_Name
:= Tree_Private_Part
.Projects_Htable
.Get_Next
.Name
;
1115 -- Report an error if we already have a project with this name
1117 if Project_Name
/= No_Name
then
1118 Error_Msg
("duplicate project name", Token_Ptr
);
1121 -- Otherwise, add the name of the project to the hash table, so
1122 -- that we can check that no other subsequent project will have
1125 Tree_Private_Part
.Projects_Htable
.Set
1126 (K
=> Name_Of_Project
,
1127 E
=> (Name
=> Name_Of_Project
,
1129 Extended
=> Extended
));
1135 if Token
= Tok_Extends
then
1137 -- Make sure that gnatmake will use mapping files
1139 Opt
.Create_Mapping_File
:= True;
1141 -- We are extending another project
1143 Scan
; -- scan past EXTENDS
1145 if Token
= Tok_All
then
1146 Set_Is_Extending_All
(Project
);
1147 Scan
; -- scan past ALL
1150 Expect
(Tok_String_Literal
, "literal string");
1152 if Token
= Tok_String_Literal
then
1153 Set_Extended_Project_Path_Of
(Project
, Token_Name
);
1156 Original_Path_Name
: constant String :=
1157 Get_Name_String
(Token_Name
);
1159 Extended_Project_Path_Name
: constant String :=
1160 Project_Path_Name_Of
1161 (Original_Path_Name
,
1163 (Project_Directory
));
1166 if Extended_Project_Path_Name
= "" then
1168 -- We could not find the project file to extend
1170 Error_Msg_Name_1
:= Token_Name
;
1172 Error_Msg
("unknown project file: {", Token_Ptr
);
1174 -- If we are not in the main project file, display the
1177 if Project_Stack
.Last
> 1 then
1179 Project_Stack
.Table
(Project_Stack
.Last
).Name
;
1180 Error_Msg
("\extended by {", Token_Ptr
);
1182 for Index
in reverse 1 .. Project_Stack
.Last
- 1 loop
1183 Error_Msg_Name_1
:= Project_Stack
.Table
(Index
).Name
;
1184 Error_Msg
("\imported by {", Token_Ptr
);
1190 From_Extended
: Extension_Origin
:= None
;
1193 if Is_Extending_All
(Project
) then
1194 From_Extended
:= Extending_All
;
1197 Parse_Single_Project
1198 (Project
=> Extended_Project
,
1199 Path_Name
=> Extended_Project_Path_Name
,
1201 From_Extended
=> From_Extended
);
1204 -- A project that extends an extending-all project is also
1205 -- an extending-all project.
1207 if Is_Extending_All
(Extended_Project
) then
1208 Set_Is_Extending_All
(Project
);
1213 Scan
; -- scan past the extended project path
1217 -- Check that a non extending-all project does not import an
1218 -- extending-all project.
1220 if not Is_Extending_All
(Project
) then
1222 With_Clause
: Project_Node_Id
:= First_With_Clause_Of
(Project
);
1223 Imported
: Project_Node_Id
:= Empty_Node
;
1227 while With_Clause
/= Empty_Node
loop
1228 Imported
:= Project_Node_Of
(With_Clause
);
1229 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
1231 if Is_Extending_All
(Imported
) then
1232 Error_Msg_Name_1
:= Name_Of
(Imported
);
1233 Error_Msg
("cannot import extending-all project {",
1235 exit With_Clause_Loop
;
1237 end loop With_Clause_Loop
;
1241 -- Check that a project with a name including a dot either imports
1242 -- or extends the project whose name precedes the last dot.
1244 if Name_Of_Project
/= No_Name
then
1245 Get_Name_String
(Name_Of_Project
);
1251 -- Look for the last dot
1253 while Name_Len
> 0 and then Name_Buffer
(Name_Len
) /= '.' loop
1254 Name_Len
:= Name_Len
- 1;
1257 -- If a dot was find, check if the parent project is imported
1260 if Name_Len
> 0 then
1261 Name_Len
:= Name_Len
- 1;
1264 Parent_Name
: constant Name_Id
:= Name_Find
;
1265 Parent_Found
: Boolean := False;
1266 With_Clause
: Project_Node_Id
:= First_With_Clause_Of
(Project
);
1269 -- If there is an extended project, check its name
1271 if Extended_Project
/= Empty_Node
then
1272 Parent_Found
:= Name_Of
(Extended_Project
) = Parent_Name
;
1275 -- If the parent project is not the extended project,
1276 -- check each imported project until we find the parent project.
1278 while not Parent_Found
and then With_Clause
/= Empty_Node
loop
1279 Parent_Found
:= Name_Of
(Project_Node_Of
(With_Clause
))
1281 With_Clause
:= Next_With_Clause_Of
(With_Clause
);
1284 -- If the parent project was not found, report an error
1286 if not Parent_Found
then
1287 Error_Msg_Name_1
:= Name_Of_Project
;
1288 Error_Msg_Name_2
:= Parent_Name
;
1289 Error_Msg
("project { does not import or extend project {",
1290 Location_Of
(Project
));
1295 Expect
(Tok_Is
, "IS");
1296 Set_End_Of_Line
(Project
);
1297 Set_Previous_Line_Node
(Project
);
1298 Set_Next_End_Node
(Project
);
1301 Project_Declaration
: Project_Node_Id
:= Empty_Node
;
1304 -- No need to Scan past "is", Prj.Dect.Parse will do it.
1307 (Declarations
=> Project_Declaration
,
1308 Current_Project
=> Project
,
1309 Extends
=> Extended_Project
);
1310 Set_Project_Declaration_Of
(Project
, Project_Declaration
);
1312 if Extended_Project
/= Empty_Node
then
1313 Set_Extending_Project_Of
1314 (Project_Declaration_Of
(Extended_Project
), To
=> Project
);
1318 Expect
(Tok_End
, "END");
1319 Remove_Next_End_Node
;
1321 -- Skip "end" if present
1323 if Token
= Tok_End
then
1331 -- Store the name following "end" in the Buffer. The name may be made of
1332 -- several simple names.
1335 Expect
(Tok_Identifier
, "identifier");
1337 -- If we don't have an identifier, clear the buffer before exiting to
1338 -- avoid checking the name.
1340 if Token
/= Tok_Identifier
then
1345 -- Add the identifier to the Buffer
1346 Get_Name_String
(Token_Name
);
1347 Add_To_Buffer
(Name_Buffer
(1 .. Name_Len
));
1349 -- Scan past the identifier
1352 exit when Token
/= Tok_Dot
;
1353 Add_To_Buffer
(".");
1357 -- If we have a valid name, check if it is the name of the project
1359 if Name_Of_Project
/= No_Name
and then Buffer_Last
> 0 then
1360 if To_Lower
(Buffer
(1 .. Buffer_Last
)) /=
1361 Get_Name_String
(Name_Of
(Project
))
1363 -- Invalid name: report an error
1365 Error_Msg
("Expected """ &
1366 Get_Name_String
(Name_Of
(Project
)) & """",
1371 Expect
(Tok_Semicolon
, "`;`");
1373 -- Check that there is no more text following the end of the project
1376 if Token
= Tok_Semicolon
then
1377 Set_Previous_End_Node
(Project
);
1380 if Token
/= Tok_EOF
then
1382 ("Unexpected text following end of project", Token_Ptr
);
1386 -- Restore the scan state, in case we are not the main project
1388 Restore_Project_Scan_State
(Project_Scan_State
);
1390 -- And remove the project from the project stack
1392 Project_Stack
.Decrement_Last
;
1394 -- Indicate if there are unkept comments
1396 Tree
.Set_Project_File_Includes_Unkept_Comments
1397 (Node
=> Project
, To
=> Tree
.There_Are_Unkept_Comments
);
1399 -- And restore the comment state that was saved
1401 Tree
.Restore
(Project_Comment_State
);
1402 end Parse_Single_Project
;
1404 -----------------------
1405 -- Project_Name_From --
1406 -----------------------
1408 function Project_Name_From
(Path_Name
: String) return Name_Id
is
1409 Canonical
: String (1 .. Path_Name
'Length) := Path_Name
;
1410 First
: Natural := Canonical
'Last;
1411 Last
: Natural := First
;
1415 if Current_Verbosity
= High
then
1416 Write_Str
("Project_Name_From (""");
1417 Write_Str
(Canonical
);
1421 -- If the path name is empty, return No_Name to indicate failure
1427 Canonical_Case_File_Name
(Canonical
);
1429 -- Look for the last dot in the path name
1433 Canonical
(First
) /= '.'
1438 -- If we have a dot, check that it is followed by the correct extension
1440 if First
> 0 and then Canonical
(First
) = '.' then
1441 if Canonical
(First
.. Last
) = Project_File_Extension
1444 -- Look for the last directory separator, if any
1450 and then Canonical
(First
) /= '/'
1451 and then Canonical
(First
) /= Dir_Sep
1457 -- Not the correct extension, return No_Name to indicate failure
1462 -- If no dot in the path name, return No_Name to indicate failure
1470 -- If the extension is the file name, return No_Name to indicate failure
1472 if First
> Last
then
1476 -- Put the name in lower case into Name_Buffer
1478 Name_Len
:= Last
- First
+ 1;
1479 Name_Buffer
(1 .. Name_Len
) := To_Lower
(Canonical
(First
.. Last
));
1483 -- Check if it is a well formed project name. Return No_Name if it is
1487 if not Is_Letter
(Name_Buffer
(Index
)) then
1494 exit when Index
>= Name_Len
;
1496 if Name_Buffer
(Index
) = '_' then
1497 if Name_Buffer
(Index
+ 1) = '_' then
1502 exit when Name_Buffer
(Index
) = '-';
1504 if Name_Buffer
(Index
) /= '_'
1505 and then not Is_Alphanumeric
(Name_Buffer
(Index
))
1513 if Index
>= Name_Len
then
1514 if Is_Alphanumeric
(Name_Buffer
(Name_Len
)) then
1516 -- All checks have succeeded. Return name in Name_Buffer
1524 elsif Name_Buffer
(Index
) = '-' then
1528 end Project_Name_From
;
1530 --------------------------
1531 -- Project_Path_Name_Of --
1532 --------------------------
1534 function Project_Path_Name_Of
1535 (Project_File_Name
: String;
1539 Result
: String_Access
;
1542 if Current_Verbosity
= High
then
1543 Write_Str
("Project_Path_Name_Of (""");
1544 Write_Str
(Project_File_Name
);
1545 Write_Str
(""", """);
1546 Write_Str
(Directory
);
1547 Write_Line
(""");");
1550 if not Is_Absolute_Path
(Project_File_Name
) then
1551 -- First we try <directory>/<file_name>.<extension>
1553 if Current_Verbosity
= High
then
1554 Write_Str
(" Trying ");
1555 Write_Str
(Directory
);
1556 Write_Char
(Directory_Separator
);
1557 Write_Str
(Project_File_Name
);
1558 Write_Line
(Project_File_Extension
);
1563 (File_Name
=> Directory
& Directory_Separator
&
1564 Project_File_Name
& Project_File_Extension
,
1565 Path
=> Project_Path
.all);
1567 -- Then we try <directory>/<file_name>
1569 if Result
= null then
1570 if Current_Verbosity
= High
then
1571 Write_Str
(" Trying ");
1572 Write_Str
(Directory
);
1573 Write_Char
(Directory_Separator
);
1574 Write_Line
(Project_File_Name
);
1579 (File_Name
=> Directory
& Directory_Separator
&
1581 Path
=> Project_Path
.all);
1585 if Result
= null then
1587 -- Then we try <file_name>.<extension>
1589 if Current_Verbosity
= High
then
1590 Write_Str
(" Trying ");
1591 Write_Str
(Project_File_Name
);
1592 Write_Line
(Project_File_Extension
);
1597 (File_Name
=> Project_File_Name
& Project_File_Extension
,
1598 Path
=> Project_Path
.all);
1601 if Result
= null then
1603 -- Then we try <file_name>
1605 if Current_Verbosity
= High
then
1606 Write_Str
(" Trying ");
1607 Write_Line
(Project_File_Name
);
1612 (File_Name
=> Project_File_Name
,
1613 Path
=> Project_Path
.all);
1616 -- If we cannot find the project file, we return an empty string
1618 if Result
= null then
1623 Final_Result
: String :=
1624 GNAT
.OS_Lib
.Normalize_Pathname
(Result
.all);
1627 Canonical_Case_File_Name
(Final_Result
);
1628 return Final_Result
;
1631 end Project_Path_Name_Of
;
1634 -- Initialize Project_Path during package elaboration
1636 if Prj_Path
.all = "" then
1637 Project_Path
:= new String'(".");
1639 Project_Path := new String'("." & Path_Separator
& Prj_Path
.all);