1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2008, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
26 with Err_Vars; use Err_Vars;
28 with Osint; use Osint;
29 with Output; use Output;
30 with Prj.Com; use Prj.Com;
32 with Prj.Err; use Prj.Err;
33 with Prj.Ext; use Prj.Ext;
34 with Sinput; use Sinput;
35 with Sinput.P; use Sinput.P;
39 with Ada.Characters.Handling; use Ada.Characters.Handling;
40 with Ada.Exceptions; use Ada.Exceptions;
42 with System.HTable; use System.HTable;
44 package body Prj.Part is
46 Buffer : String_Access;
47 Buffer_Last : Natural := 0;
49 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
51 ------------------------------------
52 -- Local Packages and Subprograms --
53 ------------------------------------
55 type With_Id is new Nat;
56 No_With : constant With_Id := 0;
58 type With_Record is record
59 Path : Path_Name_Type;
60 Location : Source_Ptr;
61 Limited_With : Boolean;
62 Node : Project_Node_Id;
65 -- Information about an imported project, to be put in table Withs below
67 package Withs is new Table.Table
68 (Table_Component_Type => With_Record,
69 Table_Index_Type => With_Id,
72 Table_Increment => 100,
73 Table_Name => "Prj.Part.Withs");
74 -- Table used to store temporarily paths and locations of imported
75 -- projects. These imported projects will be effectively parsed later: just
76 -- before parsing the current project for the non limited withed projects,
77 -- after getting its name; after complete parsing of the current project
78 -- for the limited withed projects.
80 type Names_And_Id is record
81 Path_Name : Path_Name_Type;
82 Canonical_Path_Name : Path_Name_Type;
84 Limited_With : Boolean;
87 package Project_Stack is new Table.Table
88 (Table_Component_Type => Names_And_Id,
89 Table_Index_Type => Nat,
92 Table_Increment => 100,
93 Table_Name => "Prj.Part.Project_Stack");
94 -- This table is used to detect circular dependencies
95 -- for imported and extended projects and to get the project ids of
96 -- limited imported projects when there is a circularity with at least
97 -- one limited imported project file.
99 package Virtual_Hash is new System.HTable.Simple_HTable
100 (Header_Num => Header_Num,
101 Element => Project_Node_Id,
102 No_Element => Empty_Node,
103 Key => Project_Node_Id,
104 Hash => Prj.Tree.Hash,
106 -- Hash table to store the node id of the project for which a virtual
107 -- extending project need to be created.
109 package Processed_Hash is new System.HTable.Simple_HTable
110 (Header_Num => Header_Num,
113 Key => Project_Node_Id,
114 Hash => Prj.Tree.Hash,
116 -- Hash table to store the project process when looking for project that
117 -- need to have a virtual extending project, to avoid processing the same
120 package Projects_Paths is new System.HTable.Simple_HTable
121 (Header_Num => Header_Num,
122 Element => Path_Name_Type,
123 No_Element => No_Path,
127 -- Hash table to cache project path to avoid looking for them on the path
129 procedure Create_Virtual_Extending_Project
130 (For_Project : Project_Node_Id;
131 Main_Project : Project_Node_Id;
132 In_Tree : Project_Node_Tree_Ref);
133 -- Create a virtual extending project of For_Project. Main_Project is
134 -- the extending all project.
136 -- The String_Value_Of is not set for the automatically added with
137 -- clause and keeps the default value of No_Name. This enables Prj.PP
138 -- to skip these automatically added with clauses to be processed.
140 procedure Look_For_Virtual_Projects_For
141 (Proj : Project_Node_Id;
142 In_Tree : Project_Node_Tree_Ref;
143 Potentially_Virtual : Boolean);
144 -- Look for projects that need to have a virtual extending project.
145 -- This procedure is recursive. If called with Potentially_Virtual set to
146 -- True, then Proj may need an virtual extending project; otherwise it
147 -- does not (because it is already extended), but other projects that it
148 -- imports may need to be virtually extended.
150 procedure Pre_Parse_Context_Clause
151 (In_Tree : Project_Node_Tree_Ref;
152 Context_Clause : out With_Id);
153 -- Parse the context clause of a project. Store the paths and locations of
154 -- the imported projects in table Withs. Does nothing if there is no
155 -- context clause (if the current token is not "with" or "limited" followed
158 procedure Post_Parse_Context_Clause
159 (Context_Clause : With_Id;
160 In_Tree : Project_Node_Tree_Ref;
161 Limited_Withs : Boolean;
162 Imported_Projects : in out Project_Node_Id;
163 Project_Directory : Path_Name_Type;
164 From_Extended : Extension_Origin;
165 In_Limited : Boolean;
166 Packages_To_Check : String_List_Access;
168 Current_Dir : String);
169 -- Parse the imported projects that have been stored in table Withs, if
170 -- any. From_Extended is used for the call to Parse_Single_Project below.
171 -- When In_Limited is True, the importing path includes at least one
172 -- "limited with". When Limited_Withs is False, only non limited withed
173 -- projects are parsed. When Limited_Withs is True, only limited withed
174 -- projects are parsed.
176 function Project_Path_Name_Of
177 (Project_File_Name : String;
178 Directory : String) return String;
179 -- Returns the path name of a project file. Returns an empty string
180 -- if project file cannot be found.
182 function Immediate_Directory_Of
183 (Path_Name : Path_Name_Type) return Path_Name_Type;
184 -- Get the directory of the file with the specified path name.
185 -- This includes the directory separator as the last character.
186 -- Returns "./" if Path_Name contains no directory separator.
188 function Project_Name_From (Path_Name : String) return Name_Id;
189 -- Returns the name of the project that corresponds to its path name.
190 -- Returns No_Name if the path name is invalid, because the corresponding
191 -- project name does not have the syntax of an ada identifier.
193 --------------------------------------
194 -- Create_Virtual_Extending_Project --
195 --------------------------------------
197 procedure Create_Virtual_Extending_Project
198 (For_Project : Project_Node_Id;
199 Main_Project : Project_Node_Id;
200 In_Tree : Project_Node_Tree_Ref)
203 Virtual_Name : constant String :=
205 Get_Name_String (Name_Of (For_Project, In_Tree));
206 -- The name of the virtual extending project
208 Virtual_Name_Id : Name_Id;
209 -- Virtual extending project name id
211 Virtual_Path_Id : Path_Name_Type;
212 -- Fake path name of the virtual extending project. The directory is
213 -- the same directory as the extending all project.
215 Virtual_Dir_Id : constant Path_Name_Type :=
216 Immediate_Directory_Of (Path_Name_Of (Main_Project, In_Tree));
217 -- The directory of the extending all project
219 -- The source of the virtual extending project is something like:
221 -- project V$<project name> extends <project path> is
223 -- for Source_Dirs use ();
225 -- end V$<project name>;
227 -- The project directory cannot be specified during parsing; it will be
228 -- put directly in the virtual extending project data during processing.
230 -- Nodes that made up the virtual extending project
232 Virtual_Project : constant Project_Node_Id :=
234 (In_Tree, N_Project);
235 With_Clause : constant Project_Node_Id :=
237 (In_Tree, N_With_Clause);
238 Project_Declaration : constant Project_Node_Id :=
240 (In_Tree, N_Project_Declaration);
241 Source_Dirs_Declaration : constant Project_Node_Id :=
243 (In_Tree, N_Declarative_Item);
244 Source_Dirs_Attribute : constant Project_Node_Id :=
246 (In_Tree, N_Attribute_Declaration, List);
247 Source_Dirs_Expression : constant Project_Node_Id :=
249 (In_Tree, N_Expression, List);
250 Source_Dirs_Term : constant Project_Node_Id :=
252 (In_Tree, N_Term, List);
253 Source_Dirs_List : constant Project_Node_Id :=
255 (In_Tree, N_Literal_String_List, List);
258 -- Get the virtual name id
260 Name_Len := Virtual_Name'Length;
261 Name_Buffer (1 .. Name_Len) := Virtual_Name;
262 Virtual_Name_Id := Name_Find;
264 -- Get the virtual path name
266 Get_Name_String (Path_Name_Of (Main_Project, In_Tree));
269 and then Name_Buffer (Name_Len) /= Directory_Separator
270 and then Name_Buffer (Name_Len) /= '/'
272 Name_Len := Name_Len - 1;
275 Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) :=
277 Name_Len := Name_Len + Virtual_Name'Length;
278 Virtual_Path_Id := Name_Find;
282 Set_Name_Of (With_Clause, In_Tree, Virtual_Name_Id);
283 Set_Path_Name_Of (With_Clause, In_Tree, Virtual_Path_Id);
284 Set_Project_Node_Of (With_Clause, In_Tree, Virtual_Project);
285 Set_Next_With_Clause_Of
286 (With_Clause, In_Tree, First_With_Clause_Of (Main_Project, In_Tree));
287 Set_First_With_Clause_Of (Main_Project, In_Tree, With_Clause);
289 -- Virtual project node
291 Set_Name_Of (Virtual_Project, In_Tree, Virtual_Name_Id);
292 Set_Path_Name_Of (Virtual_Project, In_Tree, Virtual_Path_Id);
294 (Virtual_Project, In_Tree, Location_Of (Main_Project, In_Tree));
295 Set_Directory_Of (Virtual_Project, In_Tree, Virtual_Dir_Id);
296 Set_Project_Declaration_Of
297 (Virtual_Project, In_Tree, Project_Declaration);
298 Set_Extended_Project_Path_Of
299 (Virtual_Project, In_Tree, Path_Name_Of (For_Project, In_Tree));
301 -- Project declaration
303 Set_First_Declarative_Item_Of
304 (Project_Declaration, In_Tree, Source_Dirs_Declaration);
305 Set_Extended_Project_Of (Project_Declaration, In_Tree, For_Project);
307 -- Source_Dirs declaration
309 Set_Current_Item_Node
310 (Source_Dirs_Declaration, In_Tree, Source_Dirs_Attribute);
312 -- Source_Dirs attribute
314 Set_Name_Of (Source_Dirs_Attribute, In_Tree, Snames.Name_Source_Dirs);
316 (Source_Dirs_Attribute, In_Tree, Source_Dirs_Expression);
318 -- Source_Dirs expression
320 Set_First_Term (Source_Dirs_Expression, In_Tree, Source_Dirs_Term);
324 Set_Current_Term (Source_Dirs_Term, In_Tree, Source_Dirs_List);
326 -- Source_Dirs empty list: nothing to do
328 -- Put virtual project into Projects_Htable
330 Prj.Tree.Tree_Private_Part.Projects_Htable.Set
331 (T => In_Tree.Projects_HT,
332 K => Virtual_Name_Id,
333 E => (Name => Virtual_Name_Id,
334 Node => Virtual_Project,
335 Canonical_Path => No_Path,
337 end Create_Virtual_Extending_Project;
339 ----------------------------
340 -- Immediate_Directory_Of --
341 ----------------------------
343 function Immediate_Directory_Of
344 (Path_Name : Path_Name_Type) return Path_Name_Type
347 Get_Name_String (Path_Name);
349 for Index in reverse 1 .. Name_Len loop
350 if Name_Buffer (Index) = '/'
351 or else Name_Buffer (Index) = Dir_Sep
353 -- Remove all chars after last directory separator from name
356 Name_Len := Index - 1;
366 -- There is no directory separator in name. Return "./" or ".\"
369 Name_Buffer (1) := '.';
370 Name_Buffer (2) := Dir_Sep;
372 end Immediate_Directory_Of;
374 -----------------------------------
375 -- Look_For_Virtual_Projects_For --
376 -----------------------------------
378 procedure Look_For_Virtual_Projects_For
379 (Proj : Project_Node_Id;
380 In_Tree : Project_Node_Tree_Ref;
381 Potentially_Virtual : Boolean)
383 Declaration : Project_Node_Id := Empty_Node;
384 -- Node for the project declaration of Proj
386 With_Clause : Project_Node_Id := Empty_Node;
387 -- Node for a with clause of Proj
389 Imported : Project_Node_Id := Empty_Node;
390 -- Node for a project imported by Proj
392 Extended : Project_Node_Id := Empty_Node;
393 -- Node for the eventual project extended by Proj
396 -- Nothing to do if Proj is not defined or if it has already been
399 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
400 -- Make sure the project will not be processed again
402 Processed_Hash.Set (Proj, True);
404 Declaration := Project_Declaration_Of (Proj, In_Tree);
406 if Declaration /= Empty_Node then
407 Extended := Extended_Project_Of (Declaration, In_Tree);
410 -- If this is a project that may need a virtual extending project
411 -- and it is not itself an extending project, put it in the list.
413 if Potentially_Virtual and then Extended = Empty_Node then
414 Virtual_Hash.Set (Proj, Proj);
417 -- Now check the projects it imports
419 With_Clause := First_With_Clause_Of (Proj, In_Tree);
421 while With_Clause /= Empty_Node loop
422 Imported := Project_Node_Of (With_Clause, In_Tree);
424 if Imported /= Empty_Node then
425 Look_For_Virtual_Projects_For
426 (Imported, In_Tree, Potentially_Virtual => True);
429 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
432 -- Check also the eventual project extended by Proj. As this project
433 -- is already extended, call recursively with Potentially_Virtual
436 Look_For_Virtual_Projects_For
437 (Extended, In_Tree, Potentially_Virtual => False);
439 end Look_For_Virtual_Projects_For;
446 (In_Tree : Project_Node_Tree_Ref;
447 Project : out Project_Node_Id;
448 Project_File_Name : String;
449 Always_Errout_Finalize : Boolean;
450 Packages_To_Check : String_List_Access := All_Packages;
451 Store_Comments : Boolean := False;
452 Current_Directory : String := "")
455 pragma Warnings (Off, Dummy);
457 Real_Project_File_Name : String_Access :=
458 Osint.To_Canonical_File_Spec
462 if Real_Project_File_Name = null then
463 Real_Project_File_Name := new String'(Project_File_Name);
466 Project := Empty_Node;
468 Projects_Paths.Reset;
470 if Current_Verbosity >= Medium then
471 Write_Str ("GPR_PROJECT_PATH=""");
472 Write_Str (Project_Path);
477 Path_Name : constant String :=
478 Project_Path_Name_Of (Real_Project_File_Name.all,
479 Directory => Current_Directory);
482 Free (Real_Project_File_Name);
485 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
486 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
488 -- Parse the main project file
490 if Path_Name = "" then
494 """ not found in " & Project_Path);
495 Project := Empty_Node;
502 Extends_All => Dummy,
503 Path_Name => Path_Name,
505 From_Extended => None,
507 Packages_To_Check => Packages_To_Check,
509 Current_Dir => Current_Directory);
511 -- If Project is an extending-all project, create the eventual
512 -- virtual extending projects and check that there are no illegally
513 -- imported projects.
515 if Project /= Empty_Node
516 and then Is_Extending_All (Project, In_Tree)
518 -- First look for projects that potentially need a virtual
519 -- extending project.
522 Processed_Hash.Reset;
524 -- Mark the extending all project as processed, to avoid checking
525 -- the imported projects in case of a "limited with" on this
526 -- extending all project.
528 Processed_Hash.Set (Project, True);
531 Declaration : constant Project_Node_Id :=
532 Project_Declaration_Of (Project, In_Tree);
534 Look_For_Virtual_Projects_For
535 (Extended_Project_Of (Declaration, In_Tree), In_Tree,
536 Potentially_Virtual => False);
539 -- Now, check the projects directly imported by the main project.
540 -- Remove from the potentially virtual any project extended by one
541 -- of these imported projects. For non extending imported
542 -- projects, check that they do not belong to the project tree of
543 -- the project being "extended-all" by the main project.
546 With_Clause : Project_Node_Id;
547 Imported : Project_Node_Id := Empty_Node;
548 Declaration : Project_Node_Id := Empty_Node;
551 With_Clause := First_With_Clause_Of (Project, In_Tree);
552 while With_Clause /= Empty_Node loop
553 Imported := Project_Node_Of (With_Clause, In_Tree);
555 if Imported /= Empty_Node then
556 Declaration := Project_Declaration_Of (Imported, In_Tree);
558 if Extended_Project_Of (Declaration, In_Tree) /=
563 Extended_Project_Of (Declaration, In_Tree);
564 exit when Imported = Empty_Node;
565 Virtual_Hash.Remove (Imported);
567 Project_Declaration_Of (Imported, In_Tree);
572 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
576 -- Now create all the virtual extending projects
579 Proj : Project_Node_Id := Virtual_Hash.Get_First;
581 while Proj /= Empty_Node loop
582 Create_Virtual_Extending_Project (Proj, Project, In_Tree);
583 Proj := Virtual_Hash.Get_Next;
588 -- If there were any kind of error during the parsing, serious
589 -- or not, then the parsing fails.
591 if Err_Vars.Total_Errors_Detected > 0 then
592 Project := Empty_Node;
595 if Project = Empty_Node or else Always_Errout_Finalize then
605 Write_Line (Exception_Information (X));
606 Write_Str ("Exception ");
607 Write_Str (Exception_Name (X));
608 Write_Line (" raised, while processing project file");
609 Project := Empty_Node;
612 ------------------------------
613 -- Pre_Parse_Context_Clause --
614 ------------------------------
616 procedure Pre_Parse_Context_Clause
617 (In_Tree : Project_Node_Tree_Ref;
618 Context_Clause : out With_Id)
620 Current_With_Clause : With_Id := No_With;
621 Limited_With : Boolean := False;
622 Current_With : With_Record;
623 Current_With_Node : Project_Node_Id := Empty_Node;
626 -- Assume no context clause
628 Context_Clause := No_With;
631 -- If Token is not WITH or LIMITED, there is no context clause, or we
632 -- have exhausted the with clauses.
634 while Token = Tok_With or else Token = Tok_Limited loop
636 Default_Project_Node (Of_Kind => N_With_Clause, In_Tree => In_Tree);
637 Limited_With := Token = Tok_Limited;
639 if In_Configuration then
641 ("configuration project cannot import " &
642 "other configuration projects",
647 Scan (In_Tree); -- scan past LIMITED
648 Expect (Tok_With, "WITH");
649 exit With_Loop when Token /= Tok_With;
654 Scan (In_Tree); -- past WITH or ","
656 Expect (Tok_String_Literal, "literal string");
658 if Token /= Tok_String_Literal then
662 -- Store path and location in table Withs
665 (Path => Path_Name_Type (Token_Name),
666 Location => Token_Ptr,
667 Limited_With => Limited_With,
668 Node => Current_With_Node,
671 Withs.Increment_Last;
672 Withs.Table (Withs.Last) := Current_With;
674 if Current_With_Clause = No_With then
675 Context_Clause := Withs.Last;
678 Withs.Table (Current_With_Clause).Next := Withs.Last;
681 Current_With_Clause := Withs.Last;
685 if Token = Tok_Semicolon then
686 Set_End_Of_Line (Current_With_Node);
687 Set_Previous_Line_Node (Current_With_Node);
689 -- End of (possibly multiple) with clause;
691 Scan (In_Tree); -- past the semicolon
694 elsif Token = Tok_Comma then
695 Set_Is_Not_Last_In_List (Current_With_Node, In_Tree);
698 Error_Msg ("expected comma or semi colon", Token_Ptr);
704 (Of_Kind => N_With_Clause, In_Tree => In_Tree);
707 end Pre_Parse_Context_Clause;
709 -------------------------------
710 -- Post_Parse_Context_Clause --
711 -------------------------------
713 procedure Post_Parse_Context_Clause
714 (Context_Clause : With_Id;
715 In_Tree : Project_Node_Tree_Ref;
716 Limited_Withs : Boolean;
717 Imported_Projects : in out Project_Node_Id;
718 Project_Directory : Path_Name_Type;
719 From_Extended : Extension_Origin;
720 In_Limited : Boolean;
721 Packages_To_Check : String_List_Access;
723 Current_Dir : String)
725 Current_With_Clause : With_Id := Context_Clause;
727 Current_Project : Project_Node_Id := Imported_Projects;
728 Previous_Project : Project_Node_Id := Empty_Node;
729 Next_Project : Project_Node_Id := Empty_Node;
731 Project_Directory_Path : constant String :=
732 Get_Name_String (Project_Directory);
734 Current_With : With_Record;
735 Extends_All : Boolean := False;
738 -- Set Current_Project to the last project in the current list, if the
739 -- list is not empty.
741 if Current_Project /= Empty_Node then
743 Next_With_Clause_Of (Current_Project, In_Tree) /= Empty_Node
745 Current_Project := Next_With_Clause_Of (Current_Project, In_Tree);
749 while Current_With_Clause /= No_With loop
750 Current_With := Withs.Table (Current_With_Clause);
751 Current_With_Clause := Current_With.Next;
753 if Limited_Withs = Current_With.Limited_With then
755 Original_Path : constant String :=
756 Get_Name_String (Current_With.Path);
758 Imported_Path_Name : constant String :=
761 Project_Directory_Path);
763 Resolved_Path : constant String :=
766 Directory => Current_Dir,
768 Opt.Follow_Links_For_Files,
769 Case_Sensitive => True);
771 Withed_Project : Project_Node_Id := Empty_Node;
774 if Imported_Path_Name = "" then
776 -- The project file cannot be found
778 Error_Msg_File_1 := File_Name_Type (Current_With.Path);
780 ("unknown project file: {", Current_With.Location);
782 -- If this is not imported by the main project file, display
785 if Project_Stack.Last > 1 then
786 for Index in reverse 1 .. Project_Stack.Last loop
789 (Project_Stack.Table (Index).Path_Name);
791 ("\imported by {", Current_With.Location);
798 Previous_Project := Current_Project;
800 if Current_Project = Empty_Node then
802 -- First with clause of the context clause
804 Current_Project := Current_With.Node;
805 Imported_Projects := Current_Project;
808 Next_Project := Current_With.Node;
809 Set_Next_With_Clause_Of
810 (Current_Project, In_Tree, Next_Project);
811 Current_Project := Next_Project;
817 Name_Id (Current_With.Path));
819 (Current_Project, In_Tree, Current_With.Location);
821 -- If it is a limited with, check if we have a circularity.
822 -- If we have one, get the project id of the limited
823 -- imported project file, and do not parse it.
825 if Limited_Withs and then Project_Stack.Last > 1 then
827 Canonical_Path_Name : Path_Name_Type;
830 Name_Len := Resolved_Path'Length;
831 Name_Buffer (1 .. Name_Len) := Resolved_Path;
832 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
833 Canonical_Path_Name := Name_Find;
835 for Index in 1 .. Project_Stack.Last loop
836 if Project_Stack.Table (Index).Canonical_Path_Name =
839 -- We have found the limited imported project,
840 -- get its project id, and do not parse it.
842 Withed_Project := Project_Stack.Table (Index).Id;
849 -- Parse the imported project, if its project id is unknown
851 if Withed_Project = Empty_Node then
854 Project => Withed_Project,
855 Extends_All => Extends_All,
856 Path_Name => Imported_Path_Name,
858 From_Extended => From_Extended,
859 In_Limited => Limited_Withs,
860 Packages_To_Check => Packages_To_Check,
862 Current_Dir => Current_Dir);
865 Extends_All := Is_Extending_All (Withed_Project, In_Tree);
868 if Withed_Project = Empty_Node then
870 -- If parsing unsuccessful, remove the context clause
872 Current_Project := Previous_Project;
874 if Current_Project = Empty_Node then
875 Imported_Projects := Empty_Node;
878 Set_Next_With_Clause_Of
879 (Current_Project, In_Tree, Empty_Node);
882 -- If parsing was successful, record project name and
883 -- path name in with clause
886 (Node => Current_Project,
888 To => Withed_Project,
889 Limited_With => Current_With.Limited_With);
893 Name_Of (Withed_Project, In_Tree));
895 Name_Len := Resolved_Path'Length;
896 Name_Buffer (1 .. Name_Len) := Resolved_Path;
897 Set_Path_Name_Of (Current_Project, In_Tree, Name_Find);
900 Set_Is_Extending_All (Current_Project, In_Tree);
907 end Post_Parse_Context_Clause;
909 --------------------------
910 -- Parse_Single_Project --
911 --------------------------
913 procedure Parse_Single_Project
914 (In_Tree : Project_Node_Tree_Ref;
915 Project : out Project_Node_Id;
916 Extends_All : out Boolean;
919 From_Extended : Extension_Origin;
920 In_Limited : Boolean;
921 Packages_To_Check : String_List_Access;
923 Current_Dir : String)
925 Normed_Path_Name : Path_Name_Type;
926 Canonical_Path_Name : Path_Name_Type;
927 Project_Directory : Path_Name_Type;
928 Project_Scan_State : Saved_Project_Scan_State;
929 Source_Index : Source_File_Index;
931 Extending : Boolean := False;
933 Extended_Project : Project_Node_Id := Empty_Node;
935 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
936 Tree_Private_Part.Projects_Htable.Get_First
937 (In_Tree.Projects_HT);
939 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
940 Name_Of_Project : Name_Id := No_Name;
941 First_With : With_Id;
942 Imported_Projects : Project_Node_Id := Empty_Node;
944 use Tree_Private_Part;
946 Project_Comment_State : Tree.Comment_State;
948 Proj_Qualifier : Project_Qualifier := Unspecified;
949 Qualifier_Location : Source_Ptr;
952 Extends_All := False;
955 Normed_Path : constant String := Normalize_Pathname
957 Directory => Current_Dir,
958 Resolve_Links => False,
959 Case_Sensitive => True);
960 Canonical_Path : constant String := Normalize_Pathname
962 Directory => Current_Dir,
963 Resolve_Links => Opt.Follow_Links_For_Files,
964 Case_Sensitive => False);
966 Name_Len := Normed_Path'Length;
967 Name_Buffer (1 .. Name_Len) := Normed_Path;
968 Normed_Path_Name := Name_Find;
969 Name_Len := Canonical_Path'Length;
970 Name_Buffer (1 .. Name_Len) := Canonical_Path;
971 Canonical_Path_Name := Name_Find;
974 -- Check for a circular dependency
976 for Index in reverse 1 .. Project_Stack.Last loop
977 exit when Project_Stack.Table (Index).Limited_With;
979 if Canonical_Path_Name =
980 Project_Stack.Table (Index).Canonical_Path_Name
982 Error_Msg ("circular dependency detected", Token_Ptr);
983 Error_Msg_Name_1 := Name_Id (Normed_Path_Name);
984 Error_Msg ("\ %% is imported by", Token_Ptr);
986 for Current in reverse 1 .. Project_Stack.Last loop
988 Name_Id (Project_Stack.Table (Current).Path_Name);
990 if Project_Stack.Table (Current).Canonical_Path_Name /=
994 ("\ %% which itself is imported by", Token_Ptr);
997 Error_Msg ("\ %%", Token_Ptr);
1002 Project := Empty_Node;
1007 -- Put the new path name on the stack
1009 Project_Stack.Append
1010 ((Path_Name => Normed_Path_Name,
1011 Canonical_Path_Name => Canonical_Path_Name,
1013 Limited_With => In_Limited));
1015 -- Check if the project file has already been parsed
1018 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
1020 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
1023 if A_Project_Name_And_Node.Extended then
1025 ("cannot extend the same project file several times",
1029 ("cannot extend an already imported project file",
1033 elsif A_Project_Name_And_Node.Extended then
1035 Is_Extending_All (A_Project_Name_And_Node.Node, In_Tree);
1037 -- If the imported project is an extended project A, and we are
1038 -- in an extended project, replace A with the ultimate project
1041 if From_Extended /= None then
1043 Decl : Project_Node_Id :=
1044 Project_Declaration_Of
1045 (A_Project_Name_And_Node.Node, In_Tree);
1047 Prj : Project_Node_Id :=
1048 Extending_Project_Of (Decl, In_Tree);
1052 Decl := Project_Declaration_Of (Prj, In_Tree);
1053 exit when Extending_Project_Of (Decl, In_Tree) =
1055 Prj := Extending_Project_Of (Decl, In_Tree);
1058 A_Project_Name_And_Node.Node := Prj;
1062 ("cannot import an already extended project file",
1067 Project := A_Project_Name_And_Node.Node;
1068 Project_Stack.Decrement_Last;
1072 A_Project_Name_And_Node :=
1073 Tree_Private_Part.Projects_Htable.Get_Next (In_Tree.Projects_HT);
1076 -- We never encountered this project file. Save the scan state, load the
1077 -- project file and start to scan it.
1079 Save_Project_Scan_State (Project_Scan_State);
1080 Source_Index := Load_Project_File (Path_Name);
1081 Tree.Save (Project_Comment_State);
1083 -- If we cannot find it, we stop
1085 if Source_Index = No_Source_File then
1086 Project := Empty_Node;
1087 Project_Stack.Decrement_Last;
1091 Prj.Err.Scanner.Initialize_Scanner (Source_Index);
1095 if (not In_Configuration) and then (Name_From_Path = No_Name) then
1097 -- The project file name is not correct (no or bad extension, or not
1098 -- following Ada identifier's syntax).
1100 Error_Msg_File_1 := File_Name_Type (Canonical_Path_Name);
1101 Error_Msg ("?{ is not a valid path name for a project file",
1105 if Current_Verbosity >= Medium then
1106 Write_Str ("Parsing """);
1107 Write_Str (Path_Name);
1112 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1114 -- Is there any imported project?
1116 Pre_Parse_Context_Clause
1117 (In_Tree => In_Tree,
1118 Context_Clause => First_With);
1120 Project := Default_Project_Node
1121 (Of_Kind => N_Project, In_Tree => In_Tree);
1122 Project_Stack.Table (Project_Stack.Last).Id := Project;
1123 Set_Directory_Of (Project, In_Tree, Project_Directory);
1124 Set_Path_Name_Of (Project, In_Tree, Normed_Path_Name);
1125 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1127 -- Check if there is a qualifier before the reserved word "project"
1129 Qualifier_Location := Token_Ptr;
1131 if Token = Tok_Abstract then
1132 Proj_Qualifier := Dry;
1135 elsif Token = Tok_Identifier then
1137 when Snames.Name_Standard =>
1138 Proj_Qualifier := Standard;
1141 when Snames.Name_Aggregate =>
1142 Proj_Qualifier := Aggregate;
1145 if Token = Tok_Identifier and then
1146 Token_Name = Snames.Name_Library
1148 Proj_Qualifier := Aggregate_Library;
1152 when Snames.Name_Library =>
1153 Proj_Qualifier := Library;
1156 when Snames.Name_Configuration =>
1157 if not In_Configuration then
1158 Error_Msg ("configuration projects cannot belong to a user" &
1170 if Proj_Qualifier /= Unspecified then
1171 if In_Configuration then
1172 Error_Msg ("a configuration project cannot be qualified except " &
1173 "as configuration project",
1174 Qualifier_Location);
1177 Set_Project_Qualifier_Of (Project, In_Tree, Proj_Qualifier);
1180 Set_Location_Of (Project, In_Tree, Token_Ptr);
1182 Expect (Tok_Project, "PROJECT");
1184 -- Mark location of PROJECT token if present
1186 if Token = Tok_Project then
1187 Scan (In_Tree); -- past PROJECT
1188 Set_Location_Of (Project, In_Tree, Token_Ptr);
1195 Expect (Tok_Identifier, "identifier");
1197 -- If the token is not an identifier, clear the buffer before
1198 -- exiting to indicate that the name of the project is ill-formed.
1200 if Token /= Tok_Identifier then
1205 -- Add the identifier name to the buffer
1207 Get_Name_String (Token_Name);
1208 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1210 -- Scan past the identifier
1214 -- If we have a dot, add a dot to the Buffer and look for the next
1217 exit when Token /= Tok_Dot;
1218 Add_To_Buffer (".", Buffer, Buffer_Last);
1220 -- Scan past the dot
1225 -- See if this is an extending project
1227 if Token = Tok_Extends then
1229 if In_Configuration then
1231 ("extending configuration project not allowed", Token_Ptr);
1234 -- Make sure that gnatmake will use mapping files
1236 Create_Mapping_File := True;
1238 -- We are extending another project
1242 Scan (In_Tree); -- past EXTENDS
1244 if Token = Tok_All then
1245 Extends_All := True;
1246 Set_Is_Extending_All (Project, In_Tree);
1247 Scan (In_Tree); -- scan past ALL
1251 -- If the name is well formed, Buffer_Last is > 0
1253 if Buffer_Last > 0 then
1255 -- The Buffer contains the name of the project
1257 Name_Len := Buffer_Last;
1258 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1259 Name_Of_Project := Name_Find;
1260 Set_Name_Of (Project, In_Tree, Name_Of_Project);
1262 -- To get expected name of the project file, replace dots by dashes
1264 Name_Len := Buffer_Last;
1265 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1267 for Index in 1 .. Name_Len loop
1268 if Name_Buffer (Index) = '.' then
1269 Name_Buffer (Index) := '-';
1273 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1276 Expected_Name : constant Name_Id := Name_Find;
1277 Extension : String_Access;
1280 -- Output a warning if the actual name is not the expected name
1282 if (not In_Configuration)
1283 and then (Name_From_Path /= No_Name)
1284 and then Expected_Name /= Name_From_Path
1286 Error_Msg_Name_1 := Expected_Name;
1288 if In_Configuration then
1289 Extension := new String'(Config_Project_File_Extension);
1292 Extension := new String'(Project_File_Extension);
1295 Error_Msg ("?file name does not match project name, " &
1296 "should be `%%" & Extension.all & "`",
1302 From_Ext : Extension_Origin := None;
1305 -- Extending_All is always propagated
1307 if From_Extended = Extending_All or else Extends_All then
1308 From_Ext := Extending_All;
1310 -- Otherwise, From_Extended is set to Extending_Single if the
1311 -- current project is an extending project.
1314 From_Ext := Extending_Simple;
1317 Post_Parse_Context_Clause
1318 (In_Tree => In_Tree,
1319 Context_Clause => First_With,
1320 Limited_Withs => False,
1321 Imported_Projects => Imported_Projects,
1322 Project_Directory => Project_Directory,
1323 From_Extended => From_Ext,
1324 In_Limited => In_Limited,
1325 Packages_To_Check => Packages_To_Check,
1327 Current_Dir => Current_Dir);
1328 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1331 if not In_Configuration then
1333 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1334 Tree_Private_Part.Projects_Htable.Get_First
1335 (In_Tree.Projects_HT);
1336 Project_Name : Name_Id := Name_And_Node.Name;
1339 -- Check if we already have a project with this name
1341 while Project_Name /= No_Name
1342 and then Project_Name /= Name_Of_Project
1345 Tree_Private_Part.Projects_Htable.Get_Next
1346 (In_Tree.Projects_HT);
1347 Project_Name := Name_And_Node.Name;
1350 -- Report an error if we already have a project with this name
1352 if Project_Name /= No_Name then
1353 Error_Msg_Name_1 := Project_Name;
1355 ("duplicate project name %%",
1356 Location_Of (Project, In_Tree));
1358 Name_Id (Path_Name_Of (Name_And_Node.Node, In_Tree));
1360 ("\already in %%", Location_Of (Project, In_Tree));
1363 -- Otherwise, add the name of the project to the hash table,
1364 -- so that we can check that no other subsequent project
1365 -- will have the same name.
1367 Tree_Private_Part.Projects_Htable.Set
1368 (T => In_Tree.Projects_HT,
1369 K => Name_Of_Project,
1370 E => (Name => Name_Of_Project,
1372 Canonical_Path => Canonical_Path_Name,
1373 Extended => Extended));
1381 Expect (Tok_String_Literal, "literal string");
1383 if Token = Tok_String_Literal then
1384 Set_Extended_Project_Path_Of
1387 Path_Name_Type (Token_Name));
1390 Original_Path_Name : constant String :=
1391 Get_Name_String (Token_Name);
1393 Extended_Project_Path_Name : constant String :=
1394 Project_Path_Name_Of
1395 (Original_Path_Name,
1397 (Project_Directory));
1400 if Extended_Project_Path_Name = "" then
1402 -- We could not find the project file to extend
1404 Error_Msg_Name_1 := Token_Name;
1406 Error_Msg ("unknown project file: %%", Token_Ptr);
1408 -- If we are not in the main project file, display the
1411 if Project_Stack.Last > 1 then
1414 (Project_Stack.Table (Project_Stack.Last).Path_Name);
1415 Error_Msg ("\extended by %%", Token_Ptr);
1417 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1420 (Project_Stack.Table (Index).Path_Name);
1421 Error_Msg ("\imported by %%", Token_Ptr);
1427 From_Ext : Extension_Origin := None;
1430 if From_Extended = Extending_All or else Extends_All then
1431 From_Ext := Extending_All;
1434 Parse_Single_Project
1435 (In_Tree => In_Tree,
1436 Project => Extended_Project,
1437 Extends_All => Extends_All,
1438 Path_Name => Extended_Project_Path_Name,
1440 From_Extended => From_Ext,
1441 In_Limited => In_Limited,
1442 Packages_To_Check => Packages_To_Check,
1444 Current_Dir => Current_Dir);
1447 -- A project that extends an extending-all project is also
1448 -- an extending-all project.
1450 if Extended_Project /= Empty_Node
1451 and then Is_Extending_All (Extended_Project, In_Tree)
1453 Set_Is_Extending_All (Project, In_Tree);
1458 Scan (In_Tree); -- past the extended project path
1462 -- Check that a non extending-all project does not import an
1463 -- extending-all project.
1465 if not Is_Extending_All (Project, In_Tree) then
1467 With_Clause : Project_Node_Id :=
1468 First_With_Clause_Of (Project, In_Tree);
1469 Imported : Project_Node_Id := Empty_Node;
1473 while With_Clause /= Empty_Node loop
1474 Imported := Project_Node_Of (With_Clause, In_Tree);
1476 if Is_Extending_All (With_Clause, In_Tree) then
1477 Error_Msg_Name_1 := Name_Of (Imported, In_Tree);
1478 Error_Msg ("cannot import extending-all project %%",
1480 exit With_Clause_Loop;
1483 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1484 end loop With_Clause_Loop;
1488 -- Check that a project with a name including a dot either imports
1489 -- or extends the project whose name precedes the last dot.
1491 if Name_Of_Project /= No_Name then
1492 Get_Name_String (Name_Of_Project);
1498 -- Look for the last dot
1500 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1501 Name_Len := Name_Len - 1;
1504 -- If a dot was find, check if the parent project is imported
1507 if Name_Len > 0 then
1508 Name_Len := Name_Len - 1;
1511 Parent_Name : constant Name_Id := Name_Find;
1512 Parent_Found : Boolean := False;
1513 With_Clause : Project_Node_Id :=
1514 First_With_Clause_Of (Project, In_Tree);
1517 -- If there is an extended project, check its name
1519 if Extended_Project /= Empty_Node then
1521 Name_Of (Extended_Project, In_Tree) = Parent_Name;
1524 -- If the parent project is not the extended project,
1525 -- check each imported project until we find the parent project.
1527 while not Parent_Found and then With_Clause /= Empty_Node loop
1529 Name_Of (Project_Node_Of (With_Clause, In_Tree), In_Tree) =
1531 With_Clause := Next_With_Clause_Of (With_Clause, In_Tree);
1534 -- If the parent project was not found, report an error
1536 if not Parent_Found then
1537 Error_Msg_Name_1 := Name_Of_Project;
1538 Error_Msg_Name_2 := Parent_Name;
1539 Error_Msg ("project %% does not import or extend project %%",
1540 Location_Of (Project, In_Tree));
1545 Expect (Tok_Is, "IS");
1546 Set_End_Of_Line (Project);
1547 Set_Previous_Line_Node (Project);
1548 Set_Next_End_Node (Project);
1551 Project_Declaration : Project_Node_Id := Empty_Node;
1554 -- No need to Scan past "is", Prj.Dect.Parse will do it
1557 (In_Tree => In_Tree,
1558 Declarations => Project_Declaration,
1559 Current_Project => Project,
1560 Extends => Extended_Project,
1561 Packages_To_Check => Packages_To_Check);
1562 Set_Project_Declaration_Of (Project, In_Tree, Project_Declaration);
1564 if Extended_Project /= Empty_Node then
1565 Set_Extending_Project_Of
1566 (Project_Declaration_Of (Extended_Project, In_Tree), In_Tree,
1571 Expect (Tok_End, "END");
1572 Remove_Next_End_Node;
1574 -- Skip "end" if present
1576 if Token = Tok_End then
1584 -- Store the name following "end" in the Buffer. The name may be made of
1585 -- several simple names.
1588 Expect (Tok_Identifier, "identifier");
1590 -- If we don't have an identifier, clear the buffer before exiting to
1591 -- avoid checking the name.
1593 if Token /= Tok_Identifier then
1598 -- Add the identifier to the Buffer
1599 Get_Name_String (Token_Name);
1600 Add_To_Buffer (Name_Buffer (1 .. Name_Len), Buffer, Buffer_Last);
1602 -- Scan past the identifier
1605 exit when Token /= Tok_Dot;
1606 Add_To_Buffer (".", Buffer, Buffer_Last);
1610 -- If we have a valid name, check if it is the name of the project
1612 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1613 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1614 Get_Name_String (Name_Of (Project, In_Tree))
1616 -- Invalid name: report an error
1618 Error_Msg ("expected """ &
1619 Get_Name_String (Name_Of (Project, In_Tree)) & """",
1624 Expect (Tok_Semicolon, "`;`");
1626 -- Check that there is no more text following the end of the project
1629 if Token = Tok_Semicolon then
1630 Set_Previous_End_Node (Project);
1633 if Token /= Tok_EOF then
1635 ("unexpected text following end of project", Token_Ptr);
1640 From_Ext : Extension_Origin := None;
1643 -- Extending_All is always propagated
1645 if From_Extended = Extending_All or else Extends_All then
1646 From_Ext := Extending_All;
1648 -- Otherwise, From_Extended is set to Extending_Single if the
1649 -- current project is an extending project.
1652 From_Ext := Extending_Simple;
1655 Post_Parse_Context_Clause
1656 (In_Tree => In_Tree,
1657 Context_Clause => First_With,
1658 Limited_Withs => True,
1659 Imported_Projects => Imported_Projects,
1660 Project_Directory => Project_Directory,
1661 From_Extended => From_Ext,
1662 In_Limited => In_Limited,
1663 Packages_To_Check => Packages_To_Check,
1665 Current_Dir => Current_Dir);
1666 Set_First_With_Clause_Of (Project, In_Tree, Imported_Projects);
1669 -- Restore the scan state, in case we are not the main project
1671 Restore_Project_Scan_State (Project_Scan_State);
1673 -- And remove the project from the project stack
1675 Project_Stack.Decrement_Last;
1677 -- Indicate if there are unkept comments
1679 Tree.Set_Project_File_Includes_Unkept_Comments
1682 To => Tree.There_Are_Unkept_Comments);
1684 -- And restore the comment state that was saved
1686 Tree.Restore (Project_Comment_State);
1687 end Parse_Single_Project;
1689 -----------------------
1690 -- Project_Name_From --
1691 -----------------------
1693 function Project_Name_From (Path_Name : String) return Name_Id is
1694 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1695 First : Natural := Canonical'Last;
1696 Last : Natural := First;
1700 if Current_Verbosity = High then
1701 Write_Str ("Project_Name_From (""");
1702 Write_Str (Canonical);
1706 -- If the path name is empty, return No_Name to indicate failure
1712 Canonical_Case_File_Name (Canonical);
1714 -- Look for the last dot in the path name
1718 Canonical (First) /= '.'
1723 -- If we have a dot, check that it is followed by the correct extension
1725 if First > 0 and then Canonical (First) = '.' then
1726 if ((not In_Configuration) and then
1727 Canonical (First .. Last) = Project_File_Extension and then
1730 (In_Configuration and then
1731 Canonical (First .. Last) = Config_Project_File_Extension and then
1734 -- Look for the last directory separator, if any
1740 and then Canonical (First) /= '/'
1741 and then Canonical (First) /= Dir_Sep
1747 -- Not the correct extension, return No_Name to indicate failure
1752 -- If no dot in the path name, return No_Name to indicate failure
1760 -- If the extension is the file name, return No_Name to indicate failure
1762 if First > Last then
1766 -- Put the name in lower case into Name_Buffer
1768 Name_Len := Last - First + 1;
1769 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1773 -- Check if it is a well formed project name. Return No_Name if it is
1777 if not Is_Letter (Name_Buffer (Index)) then
1784 exit when Index >= Name_Len;
1786 if Name_Buffer (Index) = '_' then
1787 if Name_Buffer (Index + 1) = '_' then
1792 exit when Name_Buffer (Index) = '-';
1794 if Name_Buffer (Index) /= '_'
1795 and then not Is_Alphanumeric (Name_Buffer (Index))
1803 if Index >= Name_Len then
1804 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1806 -- All checks have succeeded. Return name in Name_Buffer
1814 elsif Name_Buffer (Index) = '-' then
1818 end Project_Name_From;
1820 --------------------------
1821 -- Project_Path_Name_Of --
1822 --------------------------
1824 function Project_Path_Name_Of
1825 (Project_File_Name : String;
1826 Directory : String) return String
1829 function Try_Path_Name (Path : String) return String_Access;
1830 pragma Inline (Try_Path_Name);
1831 -- Try the specified Path
1837 function Try_Path_Name (Path : String) return String_Access is
1839 if Current_Verbosity = High then
1840 Write_Str (" Trying ");
1844 return Locate_Regular_File
1846 Path => Project_Path);
1849 -- Local Declarations
1851 Result : String_Access;
1852 Result_Id : Path_Name_Type;
1853 Has_Dot : Boolean := False;
1856 -- Start of processing for Project_Path_Name_Of
1859 if Current_Verbosity = High then
1860 Write_Str ("Project_Path_Name_Of (""");
1861 Write_Str (Project_File_Name);
1862 Write_Str (""", """);
1863 Write_Str (Directory);
1864 Write_Line (""");");
1867 -- Check the project cache
1869 Name_Len := Project_File_Name'Length;
1870 Name_Buffer (1 .. Name_Len) := Project_File_Name;
1872 Result_Id := Projects_Paths.Get (Key);
1874 if Result_Id /= No_Path then
1875 return Get_Name_String (Result_Id);
1878 -- Check if Project_File_Name contains an extension (a dot before a
1879 -- directory separator). If it is the case we do not try project file
1880 -- with an added extension as it is not possible to have multiple dots
1881 -- on a project file name.
1883 Check_Dot : for K in reverse Project_File_Name'Range loop
1884 if Project_File_Name (K) = '.' then
1889 exit Check_Dot when Project_File_Name (K) = Directory_Separator
1890 or else Project_File_Name (K) = '/';
1893 if not Is_Absolute_Path (Project_File_Name) then
1895 -- First we try <directory>/<file_name>.<extension>
1898 Result := Try_Path_Name
1899 (Directory & Directory_Separator &
1900 Project_File_Name & Project_File_Extension);
1903 -- Then we try <directory>/<file_name>
1905 if Result = null then
1906 Result := Try_Path_Name
1907 (Directory & Directory_Separator & Project_File_Name);
1911 -- Then we try <file_name>.<extension>
1913 if Result = null and then not Has_Dot then
1914 Result := Try_Path_Name (Project_File_Name & Project_File_Extension);
1917 -- Then we try <file_name>
1919 if Result = null then
1920 Result := Try_Path_Name (Project_File_Name);
1923 -- If we cannot find the project file, we return an empty string
1925 if Result = null then
1930 Final_Result : constant String :=
1931 GNAT.OS_Lib.Normalize_Pathname
1933 Directory => Directory,
1934 Resolve_Links => False,
1935 Case_Sensitive => True);
1938 Name_Len := Final_Result'Length;
1939 Name_Buffer (1 .. Name_Len) := Final_Result;
1940 Result_Id := Name_Find;
1942 Projects_Paths.Set (Key, Result_Id);
1943 return Final_Result;
1946 end Project_Path_Name_Of;