Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / gcc / ada / prj-part.adb
blob291fc23eb2accf8fa8f36c38e0192e9452f68e50
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- P R J . P A R T --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2001-2004 Free Software Foundation, Inc. --
10 -- --
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. --
21 -- --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 -- --
25 ------------------------------------------------------------------------------
27 with Err_Vars; use Err_Vars;
28 with Namet; use Namet;
29 with Opt; use Opt;
30 with Osint; use Osint;
31 with Output; use Output;
32 with Prj.Com; use Prj.Com;
33 with Prj.Dect;
34 with Prj.Err; use Prj.Err;
35 with Prj.Ext; use Prj.Ext;
36 with Scans; use Scans;
37 with Sinput; use Sinput;
38 with Sinput.P; use Sinput.P;
39 with Snames;
40 with Table;
41 with Types; use Types;
43 with Ada.Characters.Handling; use Ada.Characters.Handling;
44 with Ada.Exceptions; use Ada.Exceptions;
46 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
47 with GNAT.OS_Lib; use GNAT.OS_Lib;
49 with System.HTable; use System.HTable;
51 pragma Elaborate_All (GNAT.OS_Lib);
53 package body Prj.Part is
55 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
57 type Extension_Origin is (None, Extending_Simple, Extending_All);
58 -- Type of parameter From_Extended for procedures Parse_Single_Project and
59 -- Post_Parse_Context_Clause. Extending_All means that we are parsing the
60 -- tree rooted at an extending all project.
62 ------------------------------------
63 -- Local Packages and Subprograms --
64 ------------------------------------
66 type With_Id is new Nat;
67 No_With : constant With_Id := 0;
69 type With_Record is record
70 Path : Name_Id;
71 Location : Source_Ptr;
72 Limited_With : Boolean;
73 Node : Project_Node_Id;
74 Next : With_Id;
75 end record;
76 -- Information about an imported project, to be put in table Withs below
78 package Withs is new Table.Table
79 (Table_Component_Type => With_Record,
80 Table_Index_Type => With_Id,
81 Table_Low_Bound => 1,
82 Table_Initial => 10,
83 Table_Increment => 50,
84 Table_Name => "Prj.Part.Withs");
85 -- Table used to store temporarily paths and locations of imported
86 -- projects. These imported projects will be effectively parsed after the
87 -- name of the current project has been extablished.
89 type Names_And_Id is record
90 Path_Name : Name_Id;
91 Canonical_Path_Name : Name_Id;
92 Id : Project_Node_Id;
93 end record;
95 package Project_Stack is new Table.Table
96 (Table_Component_Type => Names_And_Id,
97 Table_Index_Type => Nat,
98 Table_Low_Bound => 1,
99 Table_Initial => 10,
100 Table_Increment => 50,
101 Table_Name => "Prj.Part.Project_Stack");
102 -- This table is used to detect circular dependencies
103 -- for imported and extended projects and to get the project ids of
104 -- limited imported projects when there is a circularity with at least
105 -- one limited imported project file.
107 package Virtual_Hash is new Simple_HTable
108 (Header_Num => Header_Num,
109 Element => Project_Node_Id,
110 No_Element => Empty_Node,
111 Key => Project_Node_Id,
112 Hash => Prj.Tree.Hash,
113 Equal => "=");
114 -- Hash table to store the node id of the project for which a virtual
115 -- extending project need to be created.
117 package Processed_Hash is new Simple_HTable
118 (Header_Num => Header_Num,
119 Element => Boolean,
120 No_Element => False,
121 Key => Project_Node_Id,
122 Hash => Prj.Tree.Hash,
123 Equal => "=");
124 -- Hash table to store the project process when looking for project that
125 -- need to have a virtual extending project, to avoid processing the same
126 -- project twice.
128 procedure Create_Virtual_Extending_Project
129 (For_Project : Project_Node_Id;
130 Main_Project : Project_Node_Id);
131 -- Create a virtual extending project of For_Project. Main_Project is
132 -- the extending all project.
134 procedure Look_For_Virtual_Projects_For
135 (Proj : Project_Node_Id;
136 Potentially_Virtual : Boolean);
137 -- Look for projects that need to have a virtual extending project.
138 -- This procedure is recursive. If called with Potentially_Virtual set to
139 -- True, then Proj may need an virtual extending project; otherwise it
140 -- does not (because it is already extended), but other projects that it
141 -- imports may need to be virtually extended.
143 procedure Pre_Parse_Context_Clause (Context_Clause : out With_Id);
144 -- Parse the context clause of a project.
145 -- Store the paths and locations of the imported projects in table Withs.
146 -- Does nothing if there is no context clause (if the current
147 -- token is not "with" or "limited" followed by "with").
149 procedure Post_Parse_Context_Clause
150 (Context_Clause : With_Id;
151 Imported_Projects : out Project_Node_Id;
152 Project_Directory : Name_Id;
153 From_Extended : Extension_Origin;
154 In_Limited : Boolean);
155 -- Parse the imported projects that have been stored in table Withs,
156 -- if any. From_Extended is used for the call to Parse_Single_Project
157 -- below. When In_Limited is True, the importing path includes at least
158 -- one "limited with".
160 procedure Parse_Single_Project
161 (Project : out Project_Node_Id;
162 Extends_All : out Boolean;
163 Path_Name : String;
164 Extended : Boolean;
165 From_Extended : Extension_Origin;
166 In_Limited : Boolean);
167 -- Parse a project file.
168 -- Recursive procedure: it calls itself for imported and extended
169 -- projects. When From_Extended is not None, if the project has already
170 -- been parsed and is an extended project A, return the ultimate
171 -- (not extended) project that extends A. When In_Limited is True,
172 -- the importing path includes at least one "limited with".
174 function Project_Path_Name_Of
175 (Project_File_Name : String;
176 Directory : String) return String;
177 -- Returns the path name of a project file. Returns an empty string
178 -- if project file cannot be found.
180 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id;
181 -- Get the directory of the file with the specified path name.
182 -- This includes the directory separator as the last character.
183 -- Returns "./" if Path_Name contains no directory separator.
185 function Project_Name_From (Path_Name : String) return Name_Id;
186 -- Returns the name of the project that corresponds to its path name.
187 -- Returns No_Name if the path name is invalid, because the corresponding
188 -- project name does not have the syntax of an ada identifier.
190 --------------------------------------
191 -- Create_Virtual_Extending_Project --
192 --------------------------------------
194 procedure Create_Virtual_Extending_Project
195 (For_Project : Project_Node_Id;
196 Main_Project : Project_Node_Id)
199 Virtual_Name : constant String :=
200 Virtual_Prefix &
201 Get_Name_String (Name_Of (For_Project));
202 -- The name of the virtual extending project
204 Virtual_Name_Id : Name_Id;
205 -- Virtual extending project name id
207 Virtual_Path_Id : Name_Id;
208 -- Fake path name of the virtual extending project. The directory is
209 -- the same directory as the extending all project.
211 Virtual_Dir_Id : constant Name_Id :=
212 Immediate_Directory_Of (Path_Name_Of (Main_Project));
213 -- The directory of the extending all project
215 -- The source of the virtual extending project is something like:
217 -- project V$<project name> extends <project path> is
219 -- for Source_Dirs use ();
221 -- end V$<project name>;
223 -- The project directory cannot be specified during parsing; it will be
224 -- put directly in the virtual extending project data during processing.
226 -- Nodes that made up the virtual extending project
228 Virtual_Project : constant Project_Node_Id :=
229 Default_Project_Node (N_Project);
230 With_Clause : constant Project_Node_Id :=
231 Default_Project_Node (N_With_Clause);
232 Project_Declaration : constant Project_Node_Id :=
233 Default_Project_Node (N_Project_Declaration);
234 Source_Dirs_Declaration : constant Project_Node_Id :=
235 Default_Project_Node (N_Declarative_Item);
236 Source_Dirs_Attribute : constant Project_Node_Id :=
237 Default_Project_Node
238 (N_Attribute_Declaration, List);
239 Source_Dirs_Expression : constant Project_Node_Id :=
240 Default_Project_Node (N_Expression, List);
241 Source_Dirs_Term : constant Project_Node_Id :=
242 Default_Project_Node (N_Term, List);
243 Source_Dirs_List : constant Project_Node_Id :=
244 Default_Project_Node
245 (N_Literal_String_List, List);
247 begin
248 -- Get the virtual name id
250 Name_Len := Virtual_Name'Length;
251 Name_Buffer (1 .. Name_Len) := Virtual_Name;
252 Virtual_Name_Id := Name_Find;
254 -- Get the virtual path name
256 Get_Name_String (Path_Name_Of (Main_Project));
258 while Name_Len > 0
259 and then Name_Buffer (Name_Len) /= Directory_Separator
260 and then Name_Buffer (Name_Len) /= '/'
261 loop
262 Name_Len := Name_Len - 1;
263 end loop;
265 Name_Buffer (Name_Len + 1 .. Name_Len + Virtual_Name'Length) :=
266 Virtual_Name;
267 Name_Len := Name_Len + Virtual_Name'Length;
268 Virtual_Path_Id := Name_Find;
270 -- With clause
272 Set_Name_Of (With_Clause, Virtual_Name_Id);
273 Set_Path_Name_Of (With_Clause, Virtual_Path_Id);
274 Set_Project_Node_Of (With_Clause, Virtual_Project);
275 Set_Next_With_Clause_Of
276 (With_Clause, First_With_Clause_Of (Main_Project));
277 Set_First_With_Clause_Of (Main_Project, With_Clause);
279 -- Virtual project node
281 Set_Name_Of (Virtual_Project, Virtual_Name_Id);
282 Set_Path_Name_Of (Virtual_Project, Virtual_Path_Id);
283 Set_Location_Of (Virtual_Project, Location_Of (Main_Project));
284 Set_Directory_Of (Virtual_Project, Virtual_Dir_Id);
285 Set_Project_Declaration_Of (Virtual_Project, Project_Declaration);
286 Set_Extended_Project_Path_Of
287 (Virtual_Project, Path_Name_Of (For_Project));
289 -- Project declaration
291 Set_First_Declarative_Item_Of
292 (Project_Declaration, Source_Dirs_Declaration);
293 Set_Extended_Project_Of (Project_Declaration, For_Project);
295 -- Source_Dirs declaration
297 Set_Current_Item_Node (Source_Dirs_Declaration, Source_Dirs_Attribute);
299 -- Source_Dirs attribute
301 Set_Name_Of (Source_Dirs_Attribute, Snames.Name_Source_Dirs);
302 Set_Expression_Of (Source_Dirs_Attribute, Source_Dirs_Expression);
304 -- Source_Dirs expression
306 Set_First_Term (Source_Dirs_Expression, Source_Dirs_Term);
308 -- Source_Dirs term
310 Set_Current_Term (Source_Dirs_Term, Source_Dirs_List);
312 -- Source_Dirs empty list: nothing to do
314 end Create_Virtual_Extending_Project;
316 ----------------------------
317 -- Immediate_Directory_Of --
318 ----------------------------
320 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id is
321 begin
322 Get_Name_String (Path_Name);
324 for Index in reverse 1 .. Name_Len loop
325 if Name_Buffer (Index) = '/'
326 or else Name_Buffer (Index) = Dir_Sep
327 then
328 -- Remove all chars after last directory separator from name
330 if Index > 1 then
331 Name_Len := Index - 1;
333 else
334 Name_Len := Index;
335 end if;
337 return Name_Find;
338 end if;
339 end loop;
341 -- There is no directory separator in name. Return "./" or ".\"
343 Name_Len := 2;
344 Name_Buffer (1) := '.';
345 Name_Buffer (2) := Dir_Sep;
346 return Name_Find;
347 end Immediate_Directory_Of;
349 -----------------------------------
350 -- Look_For_Virtual_Projects_For --
351 -----------------------------------
353 procedure Look_For_Virtual_Projects_For
354 (Proj : Project_Node_Id;
355 Potentially_Virtual : Boolean)
358 Declaration : Project_Node_Id := Empty_Node;
359 -- Node for the project declaration of Proj
361 With_Clause : Project_Node_Id := Empty_Node;
362 -- Node for a with clause of Proj
364 Imported : Project_Node_Id := Empty_Node;
365 -- Node for a project imported by Proj
367 Extended : Project_Node_Id := Empty_Node;
368 -- Node for the eventual project extended by Proj
370 begin
371 -- Nothing to do if Proj is not defined or if it has already been
372 -- processed.
374 if Proj /= Empty_Node and then not Processed_Hash.Get (Proj) then
375 -- Make sure the project will not be processed again
377 Processed_Hash.Set (Proj, True);
379 Declaration := Project_Declaration_Of (Proj);
381 if Declaration /= Empty_Node then
382 Extended := Extended_Project_Of (Declaration);
383 end if;
385 -- If this is a project that may need a virtual extending project
386 -- and it is not itself an extending project, put it in the list.
388 if Potentially_Virtual and then Extended = Empty_Node then
389 Virtual_Hash.Set (Proj, Proj);
390 end if;
392 -- Now check the projects it imports
394 With_Clause := First_With_Clause_Of (Proj);
396 while With_Clause /= Empty_Node loop
397 Imported := Project_Node_Of (With_Clause);
399 if Imported /= Empty_Node then
400 Look_For_Virtual_Projects_For
401 (Imported, Potentially_Virtual => True);
402 end if;
404 With_Clause := Next_With_Clause_Of (With_Clause);
405 end loop;
407 -- Check also the eventual project extended by Proj. As this project
408 -- is already extended, call recursively with Potentially_Virtual
409 -- being False.
411 Look_For_Virtual_Projects_For
412 (Extended, Potentially_Virtual => False);
413 end if;
414 end Look_For_Virtual_Projects_For;
416 -----------
417 -- Parse --
418 -----------
420 procedure Parse
421 (Project : out Project_Node_Id;
422 Project_File_Name : String;
423 Always_Errout_Finalize : Boolean;
424 Packages_To_Check : String_List_Access := All_Packages;
425 Store_Comments : Boolean := False)
427 Current_Directory : constant String := Get_Current_Dir;
428 Dummy : Boolean;
430 begin
431 -- Save the Packages_To_Check in Prj, so that it is visible from
432 -- Prj.Dect.
434 Current_Packages_To_Check := Packages_To_Check;
436 Project := Empty_Node;
438 if Current_Verbosity >= Medium then
439 Write_Str ("ADA_PROJECT_PATH=""");
440 Write_Str (Project_Path);
441 Write_Line ("""");
442 end if;
444 declare
445 Path_Name : constant String :=
446 Project_Path_Name_Of (Project_File_Name,
447 Directory => Current_Directory);
449 begin
450 Prj.Err.Initialize;
451 Prj.Err.Scanner.Set_Comment_As_Token (Store_Comments);
452 Prj.Err.Scanner.Set_End_Of_Line_As_Token (Store_Comments);
454 -- Parse the main project file
456 if Path_Name = "" then
457 Prj.Com.Fail
458 ("project file """, Project_File_Name, """ not found");
459 Project := Empty_Node;
460 return;
461 end if;
463 Parse_Single_Project
464 (Project => Project,
465 Extends_All => Dummy,
466 Path_Name => Path_Name,
467 Extended => False,
468 From_Extended => None,
469 In_Limited => False);
471 -- If Project is an extending-all project, create the eventual
472 -- virtual extending projects and check that there are no illegally
473 -- imported projects.
475 if Project /= Empty_Node and then Is_Extending_All (Project) then
476 -- First look for projects that potentially need a virtual
477 -- extending project.
479 Virtual_Hash.Reset;
480 Processed_Hash.Reset;
482 -- Mark the extending all project as processed, to avoid checking
483 -- the imported projects in case of a "limited with" on this
484 -- extending all project.
486 Processed_Hash.Set (Project, True);
488 declare
489 Declaration : constant Project_Node_Id :=
490 Project_Declaration_Of (Project);
491 begin
492 Look_For_Virtual_Projects_For
493 (Extended_Project_Of (Declaration),
494 Potentially_Virtual => False);
495 end;
497 -- Now, check the projects directly imported by the main project.
498 -- Remove from the potentially virtual any project extended by one
499 -- of these imported projects. For non extending imported
500 -- projects, check that they do not belong to the project tree of
501 -- the project being "extended-all" by the main project.
503 declare
504 With_Clause : Project_Node_Id :=
505 First_With_Clause_Of (Project);
506 Imported : Project_Node_Id := Empty_Node;
507 Declaration : Project_Node_Id := Empty_Node;
509 begin
510 while With_Clause /= Empty_Node loop
511 Imported := Project_Node_Of (With_Clause);
513 if Imported /= Empty_Node then
514 Declaration := Project_Declaration_Of (Imported);
516 if Extended_Project_Of (Declaration) /= Empty_Node then
517 loop
518 Imported := Extended_Project_Of (Declaration);
519 exit when Imported = Empty_Node;
520 Virtual_Hash.Remove (Imported);
521 Declaration := Project_Declaration_Of (Imported);
522 end loop;
523 end if;
525 end if;
527 With_Clause := Next_With_Clause_Of (With_Clause);
528 end loop;
529 end;
531 -- Now create all the virtual extending projects
533 declare
534 Proj : Project_Node_Id := Virtual_Hash.Get_First;
535 begin
536 while Proj /= Empty_Node loop
537 Create_Virtual_Extending_Project (Proj, Project);
538 Proj := Virtual_Hash.Get_Next;
539 end loop;
540 end;
541 end if;
543 -- If there were any kind of error during the parsing, serious
544 -- or not, then the parsing fails.
546 if Err_Vars.Total_Errors_Detected > 0 then
547 Project := Empty_Node;
548 end if;
550 if Project = Empty_Node or else Always_Errout_Finalize then
551 Prj.Err.Finalize;
552 end if;
553 end;
555 exception
556 when X : others =>
558 -- Internal error
560 Write_Line (Exception_Information (X));
561 Write_Str ("Exception ");
562 Write_Str (Exception_Name (X));
563 Write_Line (" raised, while processing project file");
564 Project := Empty_Node;
565 end Parse;
567 ------------------------------
568 -- Pre_Parse_Context_Clause --
569 ------------------------------
571 procedure Pre_Parse_Context_Clause (Context_Clause : out With_Id) is
572 Current_With_Clause : With_Id := No_With;
573 Limited_With : Boolean := False;
575 Current_With : With_Record;
577 Current_With_Node : Project_Node_Id := Empty_Node;
579 begin
580 -- Assume no context clause
582 Context_Clause := No_With;
583 With_Loop :
585 -- If Token is not WITH or LIMITED, there is no context clause,
586 -- or we have exhausted the with clauses.
588 while Token = Tok_With or else Token = Tok_Limited loop
589 Current_With_Node := Default_Project_Node (Of_Kind => N_With_Clause);
590 Limited_With := Token = Tok_Limited;
592 if Limited_With then
593 Scan; -- scan past LIMITED
594 Expect (Tok_With, "WITH");
595 exit With_Loop when Token /= Tok_With;
596 end if;
598 Comma_Loop :
599 loop
600 Scan; -- scan past WITH or ","
602 Expect (Tok_String_Literal, "literal string");
604 if Token /= Tok_String_Literal then
605 return;
606 end if;
608 -- Store path and location in table Withs
610 Current_With :=
611 (Path => Token_Name,
612 Location => Token_Ptr,
613 Limited_With => Limited_With,
614 Node => Current_With_Node,
615 Next => No_With);
617 Withs.Increment_Last;
618 Withs.Table (Withs.Last) := Current_With;
620 if Current_With_Clause = No_With then
621 Context_Clause := Withs.Last;
623 else
624 Withs.Table (Current_With_Clause).Next := Withs.Last;
625 end if;
627 Current_With_Clause := Withs.Last;
629 Scan;
631 if Token = Tok_Semicolon then
632 Set_End_Of_Line (Current_With_Node);
633 Set_Previous_Line_Node (Current_With_Node);
635 -- End of (possibly multiple) with clause;
637 Scan; -- scan past the semicolon.
638 exit Comma_Loop;
640 elsif Token /= Tok_Comma then
641 Error_Msg ("expected comma or semi colon", Token_Ptr);
642 exit Comma_Loop;
643 end if;
645 Current_With_Node :=
646 Default_Project_Node (Of_Kind => N_With_Clause);
647 end loop Comma_Loop;
648 end loop With_Loop;
649 end Pre_Parse_Context_Clause;
652 -------------------------------
653 -- Post_Parse_Context_Clause --
654 -------------------------------
656 procedure Post_Parse_Context_Clause
657 (Context_Clause : With_Id;
658 Imported_Projects : out Project_Node_Id;
659 Project_Directory : Name_Id;
660 From_Extended : Extension_Origin;
661 In_Limited : Boolean)
663 Current_With_Clause : With_Id := Context_Clause;
665 Current_Project : Project_Node_Id := Empty_Node;
666 Previous_Project : Project_Node_Id := Empty_Node;
667 Next_Project : Project_Node_Id := Empty_Node;
669 Project_Directory_Path : constant String :=
670 Get_Name_String (Project_Directory);
672 Current_With : With_Record;
673 Limited_With : Boolean := False;
674 Extends_All : Boolean := False;
676 begin
677 Imported_Projects := Empty_Node;
679 while Current_With_Clause /= No_With loop
680 Current_With := Withs.Table (Current_With_Clause);
681 Current_With_Clause := Current_With.Next;
683 Limited_With := In_Limited or Current_With.Limited_With;
685 declare
686 Original_Path : constant String :=
687 Get_Name_String (Current_With.Path);
689 Imported_Path_Name : constant String :=
690 Project_Path_Name_Of
691 (Original_Path,
692 Project_Directory_Path);
694 Resolved_Path : constant String :=
695 Normalize_Pathname
696 (Imported_Path_Name,
697 Resolve_Links => True,
698 Case_Sensitive => True);
700 Withed_Project : Project_Node_Id := Empty_Node;
702 begin
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).Path_Name;
717 Error_Msg ("\imported by {", Current_With.Location);
718 end loop;
719 end if;
721 else
722 -- New with clause
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;
733 else
734 Next_Project := Current_With.Node;
735 Set_Next_With_Clause_Of (Current_Project, Next_Project);
736 Current_Project := Next_Project;
737 end if;
739 Set_String_Value_Of
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 a circularity.
744 -- If we have one, get the project id of the limited imported
745 -- project file, and do not parse it.
747 if Limited_With and then Project_Stack.Last > 1 then
748 declare
749 Canonical_Path_Name : Name_Id;
751 begin
752 Name_Len := Resolved_Path'Length;
753 Name_Buffer (1 .. Name_Len) := Resolved_Path;
754 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
755 Canonical_Path_Name := Name_Find;
757 for Index in 1 .. Project_Stack.Last loop
758 if Project_Stack.Table (Index).Canonical_Path_Name =
759 Canonical_Path_Name
760 then
761 -- We have found the limited imported project,
762 -- get its project id, and do not parse it.
764 Withed_Project := Project_Stack.Table (Index).Id;
765 exit;
766 end if;
767 end loop;
768 end;
769 end if;
771 -- Parse the imported project, if its project id is unknown
773 if Withed_Project = Empty_Node then
774 Parse_Single_Project
775 (Project => Withed_Project,
776 Extends_All => Extends_All,
777 Path_Name => Imported_Path_Name,
778 Extended => False,
779 From_Extended => From_Extended,
780 In_Limited => Limited_With);
782 else
783 Extends_All := Is_Extending_All (Withed_Project);
784 end if;
786 if Withed_Project = Empty_Node then
787 -- If parsing was not successful, remove the
788 -- context clause.
790 Current_Project := Previous_Project;
792 if Current_Project = Empty_Node then
793 Imported_Projects := Empty_Node;
795 else
796 Set_Next_With_Clause_Of
797 (Current_Project, Empty_Node);
798 end if;
799 else
800 -- If parsing was successful, record project name
801 -- and path name in with clause
803 Set_Project_Node_Of
804 (Node => Current_Project,
805 To => Withed_Project,
806 Limited_With => Limited_With);
807 Set_Name_Of (Current_Project, Name_Of (Withed_Project));
809 Name_Len := Resolved_Path'Length;
810 Name_Buffer (1 .. Name_Len) := Resolved_Path;
811 Set_Path_Name_Of (Current_Project, Name_Find);
813 if Extends_All then
814 Set_Is_Extending_All (Current_Project);
815 end if;
816 end if;
817 end if;
818 end;
819 end loop;
820 end Post_Parse_Context_Clause;
822 --------------------------
823 -- Parse_Single_Project --
824 --------------------------
826 procedure Parse_Single_Project
827 (Project : out Project_Node_Id;
828 Extends_All : out Boolean;
829 Path_Name : String;
830 Extended : Boolean;
831 From_Extended : Extension_Origin;
832 In_Limited : Boolean)
834 Normed_Path_Name : Name_Id;
835 Canonical_Path_Name : Name_Id;
836 Project_Directory : Name_Id;
837 Project_Scan_State : Saved_Project_Scan_State;
838 Source_Index : Source_File_Index;
840 Extending : Boolean := False;
842 Extended_Project : Project_Node_Id := Empty_Node;
844 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
845 Tree_Private_Part.Projects_Htable.Get_First;
847 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
849 Name_Of_Project : Name_Id := No_Name;
851 First_With : With_Id;
853 use Tree_Private_Part;
855 Project_Comment_State : Tree.Comment_State;
857 begin
858 Extends_All := False;
860 declare
861 Normed_Path : constant String := Normalize_Pathname
862 (Path_Name, Resolve_Links => False,
863 Case_Sensitive => True);
864 Canonical_Path : constant String := Normalize_Pathname
865 (Normed_Path, Resolve_Links => True,
866 Case_Sensitive => False);
868 begin
869 Name_Len := Normed_Path'Length;
870 Name_Buffer (1 .. Name_Len) := Normed_Path;
871 Normed_Path_Name := Name_Find;
872 Name_Len := Canonical_Path'Length;
873 Name_Buffer (1 .. Name_Len) := Canonical_Path;
874 Canonical_Path_Name := Name_Find;
875 end;
877 -- Check for a circular dependency
879 for Index in 1 .. Project_Stack.Last loop
880 if Canonical_Path_Name =
881 Project_Stack.Table (Index).Canonical_Path_Name
882 then
883 Error_Msg ("circular dependency detected", Token_Ptr);
884 Error_Msg_Name_1 := Normed_Path_Name;
885 Error_Msg ("\ { is imported by", Token_Ptr);
887 for Current in reverse 1 .. Project_Stack.Last loop
888 Error_Msg_Name_1 := Project_Stack.Table (Current).Path_Name;
890 if Project_Stack.Table (Current).Canonical_Path_Name /=
891 Canonical_Path_Name
892 then
893 Error_Msg
894 ("\ { which itself is imported by", Token_Ptr);
896 else
897 Error_Msg ("\ {", Token_Ptr);
898 exit;
899 end if;
900 end loop;
902 Project := Empty_Node;
903 return;
904 end if;
905 end loop;
907 -- Put the new path name on the stack
909 Project_Stack.Increment_Last;
910 Project_Stack.Table (Project_Stack.Last).Path_Name := Normed_Path_Name;
911 Project_Stack.Table (Project_Stack.Last).Canonical_Path_Name :=
912 Canonical_Path_Name;
914 -- Check if the project file has already been parsed
916 while
917 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
918 loop
919 if A_Project_Name_And_Node.Canonical_Path = Canonical_Path_Name then
920 if Extended then
922 if A_Project_Name_And_Node.Extended then
923 Error_Msg
924 ("cannot extend the same project file several times",
925 Token_Ptr);
926 else
927 Error_Msg
928 ("cannot extend an already imported project file",
929 Token_Ptr);
930 end if;
932 elsif A_Project_Name_And_Node.Extended then
933 Extends_All :=
934 Is_Extending_All (A_Project_Name_And_Node.Node);
936 -- If the imported project is an extended project A,
937 -- and we are in an extended project, replace A with the
938 -- ultimate project extending A.
940 if From_Extended /= None then
941 declare
942 Decl : Project_Node_Id :=
943 Project_Declaration_Of
944 (A_Project_Name_And_Node.Node);
946 Prj : Project_Node_Id := Extending_Project_Of (Decl);
948 begin
949 loop
950 Decl := Project_Declaration_Of (Prj);
951 exit when Extending_Project_Of (Decl) = Empty_Node;
952 Prj := Extending_Project_Of (Decl);
953 end loop;
955 A_Project_Name_And_Node.Node := Prj;
956 end;
957 else
958 Error_Msg
959 ("cannot import an already extended project file",
960 Token_Ptr);
961 end if;
962 end if;
964 Project := A_Project_Name_And_Node.Node;
965 Project_Stack.Decrement_Last;
966 return;
967 end if;
969 A_Project_Name_And_Node := Tree_Private_Part.Projects_Htable.Get_Next;
970 end loop;
972 -- We never encountered this project file
973 -- Save the scan state, load the project file and start to scan it.
975 Save_Project_Scan_State (Project_Scan_State);
976 Source_Index := Load_Project_File (Path_Name);
977 Tree.Save (Project_Comment_State);
979 -- If we cannot find it, we stop
981 if Source_Index = No_Source_File then
982 Project := Empty_Node;
983 Project_Stack.Decrement_Last;
984 return;
985 end if;
987 Prj.Err.Scanner.Initialize_Scanner (Types.No_Unit, Source_Index);
988 Tree.Reset_State;
989 Scan;
991 if Name_From_Path = No_Name then
993 -- The project file name is not correct (no or bad extension,
994 -- or not following Ada identifier's syntax).
996 Error_Msg_Name_1 := Canonical_Path_Name;
997 Error_Msg ("?{ is not a valid path name for a project file",
998 Token_Ptr);
999 end if;
1001 if Current_Verbosity >= Medium then
1002 Write_Str ("Parsing """);
1003 Write_Str (Path_Name);
1004 Write_Char ('"');
1005 Write_Eol;
1006 end if;
1008 -- Is there any imported project?
1010 Pre_Parse_Context_Clause (First_With);
1012 Project_Directory := Immediate_Directory_Of (Normed_Path_Name);
1013 Project := Default_Project_Node (Of_Kind => N_Project);
1014 Project_Stack.Table (Project_Stack.Last).Id := Project;
1015 Set_Directory_Of (Project, Project_Directory);
1016 Set_Path_Name_Of (Project, Normed_Path_Name);
1017 Set_Location_Of (Project, Token_Ptr);
1019 Expect (Tok_Project, "PROJECT");
1021 -- Mark location of PROJECT token if present
1023 if Token = Tok_Project then
1024 Set_Location_Of (Project, Token_Ptr);
1025 Scan; -- scan past project
1026 end if;
1028 -- Clear the Buffer
1030 Buffer_Last := 0;
1031 loop
1032 Expect (Tok_Identifier, "identifier");
1034 -- If the token is not an identifier, clear the buffer before
1035 -- exiting to indicate that the name of the project is ill-formed.
1037 if Token /= Tok_Identifier then
1038 Buffer_Last := 0;
1039 exit;
1040 end if;
1042 -- Add the identifier name to the buffer
1044 Get_Name_String (Token_Name);
1045 Add_To_Buffer (Name_Buffer (1 .. Name_Len));
1047 -- Scan past the identifier
1049 Scan;
1051 -- If we have a dot, add a dot the the Buffer and look for the next
1052 -- identifier.
1054 exit when Token /= Tok_Dot;
1055 Add_To_Buffer (".");
1057 -- Scan past the dot
1059 Scan;
1060 end loop;
1062 -- See if this is an extending project
1064 if Token = Tok_Extends then
1066 -- Make sure that gnatmake will use mapping files
1068 Create_Mapping_File := True;
1070 -- We are extending another project
1072 Extending := True;
1074 Scan; -- scan past EXTENDS
1076 if Token = Tok_All then
1077 Extends_All := True;
1078 Set_Is_Extending_All (Project);
1079 Scan; -- scan past ALL
1080 end if;
1081 end if;
1083 -- If the name is well formed, Buffer_Last is > 0
1085 if Buffer_Last > 0 then
1087 -- The Buffer contains the name of the project
1089 Name_Len := Buffer_Last;
1090 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1091 Name_Of_Project := Name_Find;
1092 Set_Name_Of (Project, Name_Of_Project);
1094 -- To get expected name of the project file, replace dots by dashes
1096 Name_Len := Buffer_Last;
1097 Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last);
1099 for Index in 1 .. Name_Len loop
1100 if Name_Buffer (Index) = '.' then
1101 Name_Buffer (Index) := '-';
1102 end if;
1103 end loop;
1105 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1107 declare
1108 Expected_Name : constant Name_Id := Name_Find;
1110 begin
1111 -- Output a warning if the actual name is not the expected name
1113 if Name_From_Path /= No_Name
1114 and then Expected_Name /= Name_From_Path
1115 then
1116 Error_Msg_Name_1 := Expected_Name;
1117 Error_Msg ("?file name does not match unit name, " &
1118 "should be `{" & Project_File_Extension & "`",
1119 Token_Ptr);
1120 end if;
1121 end;
1123 declare
1124 Imported_Projects : Project_Node_Id := Empty_Node;
1125 From_Ext : Extension_Origin := None;
1127 begin
1128 -- Extending_All is always propagated
1130 if From_Extended = Extending_All or else Extends_All then
1131 From_Ext := Extending_All;
1133 -- Otherwise, From_Extended is set to Extending_Single if the
1134 -- current project is an extending project.
1136 elsif Extended then
1137 From_Ext := Extending_Simple;
1138 end if;
1140 Post_Parse_Context_Clause
1141 (Context_Clause => First_With,
1142 Imported_Projects => Imported_Projects,
1143 Project_Directory => Project_Directory,
1144 From_Extended => From_Ext,
1145 In_Limited => In_Limited);
1146 Set_First_With_Clause_Of (Project, Imported_Projects);
1147 end;
1149 declare
1150 Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
1151 Tree_Private_Part.Projects_Htable.Get_First;
1152 Project_Name : Name_Id := Name_And_Node.Name;
1154 begin
1155 -- Check if we already have a project with this name
1157 while Project_Name /= No_Name
1158 and then Project_Name /= Name_Of_Project
1159 loop
1160 Name_And_Node := Tree_Private_Part.Projects_Htable.Get_Next;
1161 Project_Name := Name_And_Node.Name;
1162 end loop;
1164 -- Report an error if we already have a project with this name
1166 if Project_Name /= No_Name then
1167 Error_Msg_Name_1 := Project_Name;
1168 Error_Msg ("duplicate project name {", Location_Of (Project));
1169 Error_Msg_Name_1 := Path_Name_Of (Name_And_Node.Node);
1170 Error_Msg ("\already in {", Location_Of (Project));
1172 else
1173 -- Otherwise, add the name of the project to the hash table, so
1174 -- that we can check that no other subsequent project will have
1175 -- the same name.
1177 Tree_Private_Part.Projects_Htable.Set
1178 (K => Name_Of_Project,
1179 E => (Name => Name_Of_Project,
1180 Node => Project,
1181 Canonical_Path => Canonical_Path_Name,
1182 Extended => Extended));
1183 end if;
1184 end;
1186 end if;
1188 if Extending then
1189 Expect (Tok_String_Literal, "literal string");
1191 if Token = Tok_String_Literal then
1192 Set_Extended_Project_Path_Of (Project, Token_Name);
1194 declare
1195 Original_Path_Name : constant String :=
1196 Get_Name_String (Token_Name);
1198 Extended_Project_Path_Name : constant String :=
1199 Project_Path_Name_Of
1200 (Original_Path_Name,
1201 Get_Name_String
1202 (Project_Directory));
1204 begin
1205 if Extended_Project_Path_Name = "" then
1207 -- We could not find the project file to extend
1209 Error_Msg_Name_1 := Token_Name;
1211 Error_Msg ("unknown project file: {", Token_Ptr);
1213 -- If we are not in the main project file, display the
1214 -- import path.
1216 if Project_Stack.Last > 1 then
1217 Error_Msg_Name_1 :=
1218 Project_Stack.Table (Project_Stack.Last).Path_Name;
1219 Error_Msg ("\extended by {", Token_Ptr);
1221 for Index in reverse 1 .. Project_Stack.Last - 1 loop
1222 Error_Msg_Name_1 :=
1223 Project_Stack.Table (Index).Path_Name;
1224 Error_Msg ("\imported by {", Token_Ptr);
1225 end loop;
1226 end if;
1228 else
1229 declare
1230 From_Ext : Extension_Origin := None;
1232 begin
1233 if From_Extended = Extending_All or else Extends_All then
1234 From_Ext := Extending_All;
1235 end if;
1237 Parse_Single_Project
1238 (Project => Extended_Project,
1239 Extends_All => Extends_All,
1240 Path_Name => Extended_Project_Path_Name,
1241 Extended => True,
1242 From_Extended => From_Ext,
1243 In_Limited => In_Limited);
1244 end;
1246 -- A project that extends an extending-all project is also
1247 -- an extending-all project.
1249 if Extended_Project /= Empty_Node
1250 and then Is_Extending_All (Extended_Project)
1251 then
1252 Set_Is_Extending_All (Project);
1253 end if;
1254 end if;
1255 end;
1257 Scan; -- scan past the extended project path
1258 end if;
1259 end if;
1261 -- Check that a non extending-all project does not import an
1262 -- extending-all project.
1264 if not Is_Extending_All (Project) then
1265 declare
1266 With_Clause : Project_Node_Id := First_With_Clause_Of (Project);
1267 Imported : Project_Node_Id := Empty_Node;
1269 begin
1270 With_Clause_Loop :
1271 while With_Clause /= Empty_Node loop
1272 Imported := Project_Node_Of (With_Clause);
1274 if Is_Extending_All (With_Clause) then
1275 Error_Msg_Name_1 := Name_Of (Imported);
1276 Error_Msg ("cannot import extending-all project {",
1277 Token_Ptr);
1278 exit With_Clause_Loop;
1279 end if;
1281 With_Clause := Next_With_Clause_Of (With_Clause);
1282 end loop With_Clause_Loop;
1283 end;
1284 end if;
1286 -- Check that a project with a name including a dot either imports
1287 -- or extends the project whose name precedes the last dot.
1289 if Name_Of_Project /= No_Name then
1290 Get_Name_String (Name_Of_Project);
1292 else
1293 Name_Len := 0;
1294 end if;
1296 -- Look for the last dot
1298 while Name_Len > 0 and then Name_Buffer (Name_Len) /= '.' loop
1299 Name_Len := Name_Len - 1;
1300 end loop;
1302 -- If a dot was find, check if the parent project is imported
1303 -- or extended.
1305 if Name_Len > 0 then
1306 Name_Len := Name_Len - 1;
1308 declare
1309 Parent_Name : constant Name_Id := Name_Find;
1310 Parent_Found : Boolean := False;
1311 With_Clause : Project_Node_Id := First_With_Clause_Of (Project);
1313 begin
1314 -- If there is an extended project, check its name
1316 if Extended_Project /= Empty_Node then
1317 Parent_Found := Name_Of (Extended_Project) = Parent_Name;
1318 end if;
1320 -- If the parent project is not the extended project,
1321 -- check each imported project until we find the parent project.
1323 while not Parent_Found and then With_Clause /= Empty_Node loop
1324 Parent_Found := Name_Of (Project_Node_Of (With_Clause))
1325 = Parent_Name;
1326 With_Clause := Next_With_Clause_Of (With_Clause);
1327 end loop;
1329 -- If the parent project was not found, report an error
1331 if not Parent_Found then
1332 Error_Msg_Name_1 := Name_Of_Project;
1333 Error_Msg_Name_2 := Parent_Name;
1334 Error_Msg ("project { does not import or extend project {",
1335 Location_Of (Project));
1336 end if;
1337 end;
1338 end if;
1340 Expect (Tok_Is, "IS");
1341 Set_End_Of_Line (Project);
1342 Set_Previous_Line_Node (Project);
1343 Set_Next_End_Node (Project);
1345 declare
1346 Project_Declaration : Project_Node_Id := Empty_Node;
1348 begin
1349 -- No need to Scan past "is", Prj.Dect.Parse will do it
1351 Prj.Dect.Parse
1352 (Declarations => Project_Declaration,
1353 Current_Project => Project,
1354 Extends => Extended_Project);
1355 Set_Project_Declaration_Of (Project, Project_Declaration);
1357 if Extended_Project /= Empty_Node then
1358 Set_Extending_Project_Of
1359 (Project_Declaration_Of (Extended_Project), To => Project);
1360 end if;
1361 end;
1363 Expect (Tok_End, "END");
1364 Remove_Next_End_Node;
1366 -- Skip "end" if present
1368 if Token = Tok_End then
1369 Scan;
1370 end if;
1372 -- Clear the Buffer
1374 Buffer_Last := 0;
1376 -- Store the name following "end" in the Buffer. The name may be made of
1377 -- several simple names.
1379 loop
1380 Expect (Tok_Identifier, "identifier");
1382 -- If we don't have an identifier, clear the buffer before exiting to
1383 -- avoid checking the name.
1385 if Token /= Tok_Identifier then
1386 Buffer_Last := 0;
1387 exit;
1388 end if;
1390 -- Add the identifier to the Buffer
1391 Get_Name_String (Token_Name);
1392 Add_To_Buffer (Name_Buffer (1 .. Name_Len));
1394 -- Scan past the identifier
1396 Scan;
1397 exit when Token /= Tok_Dot;
1398 Add_To_Buffer (".");
1399 Scan;
1400 end loop;
1402 -- If we have a valid name, check if it is the name of the project
1404 if Name_Of_Project /= No_Name and then Buffer_Last > 0 then
1405 if To_Lower (Buffer (1 .. Buffer_Last)) /=
1406 Get_Name_String (Name_Of (Project))
1407 then
1408 -- Invalid name: report an error
1410 Error_Msg ("Expected """ &
1411 Get_Name_String (Name_Of (Project)) & """",
1412 Token_Ptr);
1413 end if;
1414 end if;
1416 Expect (Tok_Semicolon, "`;`");
1418 -- Check that there is no more text following the end of the project
1419 -- source.
1421 if Token = Tok_Semicolon then
1422 Set_Previous_End_Node (Project);
1423 Scan;
1425 if Token /= Tok_EOF then
1426 Error_Msg
1427 ("Unexpected text following end of project", Token_Ptr);
1428 end if;
1429 end if;
1431 -- Restore the scan state, in case we are not the main project
1433 Restore_Project_Scan_State (Project_Scan_State);
1435 -- And remove the project from the project stack
1437 Project_Stack.Decrement_Last;
1439 -- Indicate if there are unkept comments
1441 Tree.Set_Project_File_Includes_Unkept_Comments
1442 (Node => Project, To => Tree.There_Are_Unkept_Comments);
1444 -- And restore the comment state that was saved
1446 Tree.Restore (Project_Comment_State);
1447 end Parse_Single_Project;
1449 -----------------------
1450 -- Project_Name_From --
1451 -----------------------
1453 function Project_Name_From (Path_Name : String) return Name_Id is
1454 Canonical : String (1 .. Path_Name'Length) := Path_Name;
1455 First : Natural := Canonical'Last;
1456 Last : Natural := First;
1457 Index : Positive;
1459 begin
1460 if Current_Verbosity = High then
1461 Write_Str ("Project_Name_From (""");
1462 Write_Str (Canonical);
1463 Write_Line (""")");
1464 end if;
1466 -- If the path name is empty, return No_Name to indicate failure
1468 if First = 0 then
1469 return No_Name;
1470 end if;
1472 Canonical_Case_File_Name (Canonical);
1474 -- Look for the last dot in the path name
1476 while First > 0
1477 and then
1478 Canonical (First) /= '.'
1479 loop
1480 First := First - 1;
1481 end loop;
1483 -- If we have a dot, check that it is followed by the correct extension
1485 if First > 0 and then Canonical (First) = '.' then
1486 if Canonical (First .. Last) = Project_File_Extension
1487 and then First /= 1
1488 then
1489 -- Look for the last directory separator, if any
1491 First := First - 1;
1492 Last := First;
1494 while First > 0
1495 and then Canonical (First) /= '/'
1496 and then Canonical (First) /= Dir_Sep
1497 loop
1498 First := First - 1;
1499 end loop;
1501 else
1502 -- Not the correct extension, return No_Name to indicate failure
1504 return No_Name;
1505 end if;
1507 -- If no dot in the path name, return No_Name to indicate failure
1509 else
1510 return No_Name;
1511 end if;
1513 First := First + 1;
1515 -- If the extension is the file name, return No_Name to indicate failure
1517 if First > Last then
1518 return No_Name;
1519 end if;
1521 -- Put the name in lower case into Name_Buffer
1523 Name_Len := Last - First + 1;
1524 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
1526 Index := 1;
1528 -- Check if it is a well formed project name. Return No_Name if it is
1529 -- ill formed.
1531 loop
1532 if not Is_Letter (Name_Buffer (Index)) then
1533 return No_Name;
1535 else
1536 loop
1537 Index := Index + 1;
1539 exit when Index >= Name_Len;
1541 if Name_Buffer (Index) = '_' then
1542 if Name_Buffer (Index + 1) = '_' then
1543 return No_Name;
1544 end if;
1545 end if;
1547 exit when Name_Buffer (Index) = '-';
1549 if Name_Buffer (Index) /= '_'
1550 and then not Is_Alphanumeric (Name_Buffer (Index))
1551 then
1552 return No_Name;
1553 end if;
1555 end loop;
1556 end if;
1558 if Index >= Name_Len then
1559 if Is_Alphanumeric (Name_Buffer (Name_Len)) then
1561 -- All checks have succeeded. Return name in Name_Buffer
1563 return Name_Find;
1565 else
1566 return No_Name;
1567 end if;
1569 elsif Name_Buffer (Index) = '-' then
1570 Index := Index + 1;
1571 end if;
1572 end loop;
1573 end Project_Name_From;
1575 --------------------------
1576 -- Project_Path_Name_Of --
1577 --------------------------
1579 function Project_Path_Name_Of
1580 (Project_File_Name : String;
1581 Directory : String) return String
1583 Result : String_Access;
1585 begin
1586 if Current_Verbosity = High then
1587 Write_Str ("Project_Path_Name_Of (""");
1588 Write_Str (Project_File_Name);
1589 Write_Str (""", """);
1590 Write_Str (Directory);
1591 Write_Line (""");");
1592 end if;
1594 if not Is_Absolute_Path (Project_File_Name) then
1595 -- First we try <directory>/<file_name>.<extension>
1597 if Current_Verbosity = High then
1598 Write_Str (" Trying ");
1599 Write_Str (Directory);
1600 Write_Char (Directory_Separator);
1601 Write_Str (Project_File_Name);
1602 Write_Line (Project_File_Extension);
1603 end if;
1605 Result :=
1606 Locate_Regular_File
1607 (File_Name => Directory & Directory_Separator &
1608 Project_File_Name & Project_File_Extension,
1609 Path => Project_Path);
1611 -- Then we try <directory>/<file_name>
1613 if Result = null then
1614 if Current_Verbosity = High then
1615 Write_Str (" Trying ");
1616 Write_Str (Directory);
1617 Write_Char (Directory_Separator);
1618 Write_Line (Project_File_Name);
1619 end if;
1621 Result :=
1622 Locate_Regular_File
1623 (File_Name => Directory & Directory_Separator &
1624 Project_File_Name,
1625 Path => Project_Path);
1626 end if;
1627 end if;
1629 if Result = null then
1631 -- Then we try <file_name>.<extension>
1633 if Current_Verbosity = High then
1634 Write_Str (" Trying ");
1635 Write_Str (Project_File_Name);
1636 Write_Line (Project_File_Extension);
1637 end if;
1639 Result :=
1640 Locate_Regular_File
1641 (File_Name => Project_File_Name & Project_File_Extension,
1642 Path => Project_Path);
1643 end if;
1645 if Result = null then
1647 -- Then we try <file_name>
1649 if Current_Verbosity = High then
1650 Write_Str (" Trying ");
1651 Write_Line (Project_File_Name);
1652 end if;
1654 Result :=
1655 Locate_Regular_File
1656 (File_Name => Project_File_Name,
1657 Path => Project_Path);
1658 end if;
1660 -- If we cannot find the project file, we return an empty string
1662 if Result = null then
1663 return "";
1665 else
1666 declare
1667 Final_Result : constant String :=
1668 GNAT.OS_Lib.Normalize_Pathname
1669 (Result.all,
1670 Resolve_Links => False,
1671 Case_Sensitive => True);
1672 begin
1673 Free (Result);
1674 return Final_Result;
1675 end;
1676 end if;
1677 end Project_Path_Name_Of;
1679 end Prj.Part;