* c-decl.c (duplicate_decls): Conditionalize DECL_SAVED_TREE copy.
[official-gcc.git] / gcc / ada / prj-part.adb
blob8100ad49e95a41236774d41e8eeb7f852ad99529
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- P R J . P A R T --
6 -- --
7 -- B o d y --
8 -- --
9 -- $Revision: 1.10 $
10 -- --
11 -- Copyright (C) 2001 Free Software Foundation, Inc. --
12 -- --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNAT; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
23 -- --
24 -- GNAT was originally developed by the GNAT team at New York University. --
25 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
26 -- --
27 ------------------------------------------------------------------------------
29 with Ada.Characters.Handling; use Ada.Characters.Handling;
30 with Ada.Exceptions; use Ada.Exceptions;
31 with Errout; use Errout;
32 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
33 with GNAT.OS_Lib; use GNAT.OS_Lib;
34 with Namet; use Namet;
35 with Osint; use Osint;
36 with Output; use Output;
37 with Prj.Com; use Prj.Com;
38 with Prj.Dect;
39 with Scans; use Scans;
40 with Scn; use Scn;
41 with Sinfo; use Sinfo;
42 with Sinput; use Sinput;
43 with Sinput.P; use Sinput.P;
44 with Stringt; use Stringt;
45 with Table;
46 with Types; use Types;
48 pragma Elaborate_All (GNAT.OS_Lib);
50 package body Prj.Part is
52 Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator;
54 Project_File_Extension : String := ".gpr";
56 Project_Path : String_Access;
57 -- The project path; initialized during package elaboration.
59 Ada_Project_Path : constant String := "ADA_PROJECT_PATH";
60 Prj_Path : constant String_Access := Getenv (Ada_Project_Path);
62 ------------------------------------
63 -- Local Packages and Subprograms --
64 ------------------------------------
66 package Project_Stack is new Table.Table
67 (Table_Component_Type => Name_Id,
68 Table_Index_Type => Nat,
69 Table_Low_Bound => 1,
70 Table_Initial => 10,
71 Table_Increment => 10,
72 Table_Name => "Prj.Part.Project_Stack");
73 -- This table is used to detect circular dependencies
74 -- for imported and modified projects.
76 procedure Parse_Context_Clause
77 (Context_Clause : out Project_Node_Id;
78 Project_Directory : Name_Id);
79 -- Parse the context clause of a project
80 -- Does nothing if there is b\no context clause (if the current
81 -- token is not "with").
83 procedure Parse_Single_Project
84 (Project : out Project_Node_Id;
85 Path_Name : String;
86 Modified : Boolean);
87 -- Parse a project file.
88 -- Recursive procedure: it calls itself for imported and
89 -- modified projects.
91 function Path_Name_Of
92 (File_Name : String;
93 Directory : String)
94 return String;
95 -- Returns the path name of a (non project) file.
96 -- Returns an empty string if file cannot be found.
98 function Project_Path_Name_Of
99 (Project_File_Name : String;
100 Directory : String)
101 return String;
102 -- Returns the path name of a project file.
103 -- Returns an empty string if project file cannot be found.
105 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id;
106 -- Get the directory of the file with the specified path name.
107 -- This includes the directory separator as the last character.
108 -- Returns "./" if Path_Name contains no directory separator.
110 function Simple_File_Name_Of (Path_Name : Name_Id) return Name_Id;
111 -- Returns the name of a file with the specified path name
112 -- with no directory information.
114 function Project_Name_From (Path_Name : String) return Name_Id;
115 -- Returns the name of the project that corresponds to its path name.
116 -- Returns No_Name if the path name is invalid, because the corresponding
117 -- project name does not have the syntax of an ada identifier.
119 ----------------------------
120 -- Immediate_Directory_Of --
121 ----------------------------
123 function Immediate_Directory_Of (Path_Name : Name_Id) return Name_Id is
124 begin
125 Get_Name_String (Path_Name);
127 for Index in reverse 1 .. Name_Len loop
128 if Name_Buffer (Index) = '/'
129 or else Name_Buffer (Index) = Dir_Sep
130 then
131 -- Remove from name all characters after the last
132 -- directory separator.
134 Name_Len := Index;
135 return Name_Find;
136 end if;
137 end loop;
139 -- There is no directory separator in name. Return "./" or ".\".
140 Name_Len := 2;
141 Name_Buffer (1) := '.';
142 Name_Buffer (2) := Dir_Sep;
143 return Name_Find;
144 end Immediate_Directory_Of;
146 -----------
147 -- Parse --
148 -----------
150 procedure Parse
151 (Project : out Project_Node_Id;
152 Project_File_Name : String;
153 Always_Errout_Finalize : Boolean)
155 Current_Directory : constant String := Get_Current_Dir;
157 begin
158 Project := Empty_Node;
160 if Current_Verbosity >= Medium then
161 Write_Str ("ADA_PROJECT_PATH=""");
162 Write_Str (Project_Path.all);
163 Write_Line ("""");
164 end if;
166 declare
167 Path_Name : constant String :=
168 Project_Path_Name_Of (Project_File_Name,
169 Directory => Current_Directory);
171 begin
172 -- Initialize the tables
174 Tree_Private_Part.Project_Nodes.Set_Last (Empty_Node);
175 Tree_Private_Part.Projects_Htable.Reset;
177 Errout.Initialize;
179 -- And parse the main project file
181 if Path_Name = "" then
182 Fail ("project file """ & Project_File_Name & """ not found");
183 end if;
185 Parse_Single_Project
186 (Project => Project,
187 Path_Name => Path_Name,
188 Modified => False);
190 if Errout.Errors_Detected > 0 then
191 Project := Empty_Node;
192 end if;
194 if Project = Empty_Node or else Always_Errout_Finalize then
195 Errout.Finalize;
196 end if;
197 end;
199 exception
200 when X : others =>
202 -- Internal error
204 Write_Line (Exception_Information (X));
205 Write_Str ("Exception ");
206 Write_Str (Exception_Name (X));
207 Write_Line (" raised, while processing project file");
208 Project := Empty_Node;
209 end Parse;
211 --------------------------
212 -- Parse_Context_Clause --
213 --------------------------
215 procedure Parse_Context_Clause
216 (Context_Clause : out Project_Node_Id;
217 Project_Directory : Name_Id)
219 Project_Directory_Path : constant String :=
220 Get_Name_String (Project_Directory);
221 Current_With_Clause : Project_Node_Id := Empty_Node;
222 Next_With_Clause : Project_Node_Id := Empty_Node;
224 begin
225 -- Assume no context clause
227 Context_Clause := Empty_Node;
228 With_Loop :
230 -- If Token is not "with", there is no context clause,
231 -- or we have exhausted the with clauses.
233 while Token = Tok_With loop
234 Comma_Loop :
235 loop
236 -- Scan past "with" or ","
238 Scan;
239 Expect (Tok_String_Literal, "literal string");
241 if Token /= Tok_String_Literal then
242 return;
243 end if;
245 -- New with clause
247 if Current_With_Clause = Empty_Node then
249 -- First with clause of the context clause
251 Current_With_Clause := Default_Project_Node
252 (Of_Kind => N_With_Clause);
253 Context_Clause := Current_With_Clause;
255 else
256 Next_With_Clause := Default_Project_Node
257 (Of_Kind => N_With_Clause);
258 Set_Next_With_Clause_Of (Current_With_Clause, Next_With_Clause);
259 Current_With_Clause := Next_With_Clause;
260 end if;
262 Set_String_Value_Of (Current_With_Clause, Strval (Token_Node));
263 Set_Location_Of (Current_With_Clause, Token_Ptr);
264 String_To_Name_Buffer (String_Value_Of (Current_With_Clause));
266 declare
267 Original_Path : constant String :=
268 Name_Buffer (1 .. Name_Len);
270 Imported_Path_Name : constant String :=
271 Project_Path_Name_Of
272 (Original_Path,
273 Project_Directory_Path);
275 Withed_Project : Project_Node_Id := Empty_Node;
277 begin
278 if Imported_Path_Name = "" then
280 -- The project file cannot be found
282 Name_Len := Original_Path'Length;
283 Name_Buffer (1 .. Name_Len) := Original_Path;
284 Error_Msg_Name_1 := Name_Find;
286 Error_Msg ("unknown project file: {", Token_Ptr);
288 else
289 -- Parse the imported project
291 Parse_Single_Project
292 (Project => Withed_Project,
293 Path_Name => Imported_Path_Name,
294 Modified => False);
296 if Withed_Project /= Empty_Node then
298 -- If parsing was successful, record project name
299 -- and path name in with clause
301 Set_Project_Node_Of (Current_With_Clause, Withed_Project);
302 Set_Name_Of (Current_With_Clause,
303 Name_Of (Withed_Project));
304 Name_Len := Imported_Path_Name'Length;
305 Name_Buffer (1 .. Name_Len) := Imported_Path_Name;
306 Set_Path_Name_Of (Current_With_Clause, Name_Find);
307 end if;
308 end if;
309 end;
311 Scan;
312 if Token = Tok_Semicolon then
314 -- End of (possibly multiple) with clause;
315 -- Scan past the semicolon.
317 Scan;
318 exit Comma_Loop;
320 elsif Token /= Tok_Comma then
321 Error_Msg ("expected comma or semi colon", Token_Ptr);
322 exit Comma_Loop;
323 end if;
324 end loop Comma_Loop;
325 end loop With_Loop;
327 end Parse_Context_Clause;
329 --------------------------
330 -- Parse_Single_Project --
331 --------------------------
333 procedure Parse_Single_Project
334 (Project : out Project_Node_Id;
335 Path_Name : String;
336 Modified : Boolean)
338 Canonical_Path_Name : Name_Id;
339 Project_Directory : Name_Id;
340 Project_Scan_State : Saved_Project_Scan_State;
341 Source_Index : Source_File_Index;
343 Modified_Project : Project_Node_Id := Empty_Node;
345 A_Project_Name_And_Node : Tree_Private_Part.Project_Name_And_Node :=
346 Tree_Private_Part.Projects_Htable.Get_First;
348 Name_From_Path : constant Name_Id := Project_Name_From (Path_Name);
350 use Tree_Private_Part;
352 begin
353 Name_Len := Path_Name'Length;
354 Name_Buffer (1 .. Name_Len) := Path_Name;
355 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
356 Canonical_Path_Name := Name_Find;
358 -- Check for a circular dependency
360 for Index in 1 .. Project_Stack.Last loop
361 if Canonical_Path_Name = Project_Stack.Table (Index) then
362 Error_Msg ("circular dependency detected", Token_Ptr);
363 Error_Msg_Name_1 := Canonical_Path_Name;
364 Error_Msg ("\ { is imported by", Token_Ptr);
366 for Current in reverse 1 .. Project_Stack.Last loop
367 Error_Msg_Name_1 := Project_Stack.Table (Current);
369 if Error_Msg_Name_1 /= Canonical_Path_Name then
370 Error_Msg
371 ("\ { which itself is imported by", Token_Ptr);
373 else
374 Error_Msg ("\ {", Token_Ptr);
375 exit;
376 end if;
377 end loop;
379 Project := Empty_Node;
380 return;
381 end if;
382 end loop;
384 Project_Stack.Increment_Last;
385 Project_Stack.Table (Project_Stack.Last) := Canonical_Path_Name;
387 -- Check if the project file has already been parsed.
389 while
390 A_Project_Name_And_Node /= Tree_Private_Part.No_Project_Name_And_Node
391 loop
393 Path_Name_Of (A_Project_Name_And_Node.Node) = Canonical_Path_Name
394 then
395 if Modified then
397 if A_Project_Name_And_Node.Modified then
398 Error_Msg
399 ("cannot modify several times the same project file",
400 Token_Ptr);
402 else
403 Error_Msg
404 ("cannot modify an imported project file",
405 Token_Ptr);
406 end if;
408 elsif A_Project_Name_And_Node.Modified then
409 Error_Msg
410 ("cannot imported a modified project file",
411 Token_Ptr);
412 end if;
414 Project := A_Project_Name_And_Node.Node;
415 Project_Stack.Decrement_Last;
416 return;
417 end if;
419 A_Project_Name_And_Node := Tree_Private_Part.Projects_Htable.Get_Next;
420 end loop;
422 -- We never encountered this project file
423 -- Save the scan state, load the project file and start to scan it.
425 Save_Project_Scan_State (Project_Scan_State);
426 Source_Index := Load_Project_File (Path_Name);
428 -- if we cannot find it, we stop
430 if Source_Index = No_Source_File then
431 Project := Empty_Node;
432 Project_Stack.Decrement_Last;
433 return;
434 end if;
436 Initialize_Scanner (Types.No_Unit, Source_Index);
438 if Name_From_Path = No_Name then
440 -- The project file name is not correct (no or bad extension,
441 -- or not following Ada identifier's syntax).
443 Error_Msg_Name_1 := Canonical_Path_Name;
444 Error_Msg ("?{ is not a valid path name for a project file",
445 Token_Ptr);
446 end if;
448 if Current_Verbosity >= Medium then
449 Write_Str ("Parsing """);
450 Write_Str (Path_Name);
451 Write_Char ('"');
452 Write_Eol;
453 end if;
455 Project_Directory := Immediate_Directory_Of (Canonical_Path_Name);
456 Project := Default_Project_Node (Of_Kind => N_Project);
457 Set_Directory_Of (Project, Project_Directory);
458 Set_Name_Of (Project, Simple_File_Name_Of (Canonical_Path_Name));
459 Set_Path_Name_Of (Project, Canonical_Path_Name);
460 Set_Location_Of (Project, Token_Ptr);
462 -- Is there any imported project?
464 declare
465 First_With_Clause : Project_Node_Id := Empty_Node;
467 begin
468 Parse_Context_Clause (Context_Clause => First_With_Clause,
469 Project_Directory => Project_Directory);
470 Set_First_With_Clause_Of (Project, First_With_Clause);
471 end;
473 Expect (Tok_Project, "project");
475 -- Scan past "project"
477 if Token = Tok_Project then
478 Set_Location_Of (Project, Token_Ptr);
479 Scan;
480 end if;
482 Expect (Tok_Identifier, "identifier");
484 if Token = Tok_Identifier then
485 Set_Name_Of (Project, Token_Name);
487 Get_Name_String (Token_Name);
488 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
490 declare
491 Expected_Name : constant Name_Id := Name_Find;
493 begin
494 if Name_From_Path /= No_Name
495 and then Expected_Name /= Name_From_Path
496 then
497 -- The project name is not the one that was expected from
498 -- the file name. Report a warning.
500 Error_Msg_Name_1 := Expected_Name;
501 Error_Msg ("?file name does not match unit name, " &
502 "should be `{" & Project_File_Extension & "`",
503 Token_Ptr);
504 end if;
505 end;
507 declare
508 Project_Name : Name_Id :=
509 Tree_Private_Part.Projects_Htable.Get_First.Name;
511 begin
512 -- Check if we already have a project with this name
514 while Project_Name /= No_Name
515 and then Project_Name /= Token_Name
516 loop
517 Project_Name := Tree_Private_Part.Projects_Htable.Get_Next.Name;
518 end loop;
520 if Project_Name /= No_Name then
521 Error_Msg ("duplicate project name", Token_Ptr);
523 else
524 Tree_Private_Part.Projects_Htable.Set
525 (K => Token_Name,
526 E => (Name => Token_Name,
527 Node => Project,
528 Modified => Modified));
529 end if;
530 end;
532 -- Scan past the project name
534 Scan;
536 end if;
538 if Token = Tok_Modifying then
540 -- We are modifying another project
542 -- Scan past "modifying"
544 Scan;
546 Expect (Tok_String_Literal, "literal string");
548 if Token = Tok_String_Literal then
549 Set_Modified_Project_Path_Of (Project, Strval (Token_Node));
550 String_To_Name_Buffer (Modified_Project_Path_Of (Project));
552 declare
553 Original_Path_Name : constant String :=
554 Name_Buffer (1 .. Name_Len);
556 Modified_Project_Path_Name : constant String :=
557 Project_Path_Name_Of
558 (Original_Path_Name,
559 Get_Name_String
560 (Project_Directory));
562 begin
563 if Modified_Project_Path_Name = "" then
565 -- We could not find the project file to modify
567 Name_Len := Original_Path_Name'Length;
568 Name_Buffer (1 .. Name_Len) := Original_Path_Name;
569 Error_Msg_Name_1 := Name_Find;
571 Error_Msg ("unknown project file: {", Token_Ptr);
573 else
574 Parse_Single_Project
575 (Project => Modified_Project,
576 Path_Name => Modified_Project_Path_Name,
577 Modified => True);
578 end if;
579 end;
581 -- Scan past the modified project path
583 Scan;
584 end if;
585 end if;
587 Expect (Tok_Is, "is");
589 declare
590 Project_Declaration : Project_Node_Id := Empty_Node;
592 begin
593 -- No need to Scan past "is", Prj.Dect.Parse will do it.
595 Prj.Dect.Parse
596 (Declarations => Project_Declaration,
597 Current_Project => Project,
598 Modifying => Modified_Project);
599 Set_Project_Declaration_Of (Project, Project_Declaration);
600 end;
602 Expect (Tok_End, "end");
604 -- Scan past "end"
606 if Token = Tok_End then
607 Scan;
608 end if;
610 Expect (Tok_Identifier, "identifier");
612 if Token = Tok_Identifier then
614 -- We check if this is the project name
616 if To_Lower (Get_Name_String (Token_Name)) /=
617 Get_Name_String (Name_Of (Project))
618 then
619 Error_Msg ("Expected """ &
620 Get_Name_String (Name_Of (Project)) & """",
621 Token_Ptr);
622 end if;
623 end if;
625 if Token /= Tok_Semicolon then
626 Scan;
627 end if;
629 Expect (Tok_Semicolon, ";");
631 -- Restore the scan state, in case we are not the main project
633 Restore_Project_Scan_State (Project_Scan_State);
635 Project_Stack.Decrement_Last;
636 end Parse_Single_Project;
638 ------------------
639 -- Path_Name_Of --
640 ------------------
642 function Path_Name_Of
643 (File_Name : String;
644 Directory : String)
645 return String
647 Result : String_Access;
649 begin
650 Result := Locate_Regular_File (File_Name => File_Name,
651 Path => Directory);
653 if Result = null then
654 return "";
656 else
657 Canonical_Case_File_Name (Result.all);
658 return Result.all;
659 end if;
660 end Path_Name_Of;
662 -----------------------
663 -- Project_Name_From --
664 -----------------------
666 function Project_Name_From (Path_Name : String) return Name_Id is
667 Canonical : String (1 .. Path_Name'Length) := Path_Name;
668 First : Natural := Canonical'Last;
669 Last : Positive := First;
671 begin
672 if First = 0 then
673 return No_Name;
674 end if;
676 Canonical_Case_File_Name (Canonical);
678 while First > 0
679 and then
680 Canonical (First) /= '.'
681 loop
682 First := First - 1;
683 end loop;
685 if Canonical (First) = '.' then
686 if Canonical (First .. Last) = Project_File_Extension
687 and then First /= 1
688 then
689 First := First - 1;
690 Last := First;
692 while First > 0
693 and then Canonical (First) /= '/'
694 and then Canonical (First) /= Dir_Sep
695 loop
696 First := First - 1;
697 end loop;
699 else
700 return No_Name;
701 end if;
703 else
704 return No_Name;
705 end if;
707 if Canonical (First) = '/'
708 or else Canonical (First) = Dir_Sep
709 then
710 First := First + 1;
711 end if;
713 Name_Len := Last - First + 1;
714 Name_Buffer (1 .. Name_Len) := To_Lower (Canonical (First .. Last));
716 if not Is_Letter (Name_Buffer (1)) then
717 return No_Name;
719 else
720 for Index in 2 .. Name_Len - 1 loop
721 if Name_Buffer (Index) = '_' then
722 if Name_Buffer (Index + 1) = '_' then
723 return No_Name;
724 end if;
726 elsif not Is_Alphanumeric (Name_Buffer (Index)) then
727 return No_Name;
728 end if;
730 end loop;
732 if not Is_Alphanumeric (Name_Buffer (Name_Len)) then
733 return No_Name;
735 else
736 return Name_Find;
737 end if;
739 end if;
740 end Project_Name_From;
742 --------------------------
743 -- Project_Path_Name_Of --
744 --------------------------
746 function Project_Path_Name_Of
747 (Project_File_Name : String;
748 Directory : String)
749 return String
751 Result : String_Access;
753 begin
754 -- First we try <file_name>.<extension>
756 if Current_Verbosity = High then
757 Write_Str ("Project_Path_Name_Of (""");
758 Write_Str (Project_File_Name);
759 Write_Str (""", """);
760 Write_Str (Directory);
761 Write_Line (""");");
762 Write_Str (" Trying ");
763 Write_Str (Project_File_Name);
764 Write_Line (Project_File_Extension);
765 end if;
767 Result :=
768 Locate_Regular_File
769 (File_Name => Project_File_Name & Project_File_Extension,
770 Path => Project_Path.all);
772 -- Then we try <file_name>
774 if Result = null then
775 if Current_Verbosity = High then
776 Write_Str (" Trying ");
777 Write_Line (Project_File_Name);
778 end if;
780 Result :=
781 Locate_Regular_File
782 (File_Name => Project_File_Name,
783 Path => Project_Path.all);
785 -- The we try <directory>/<file_name>.<extension>
787 if Result = null then
788 if Current_Verbosity = High then
789 Write_Str (" Trying ");
790 Write_Str (Directory);
791 Write_Str (Project_File_Name);
792 Write_Line (Project_File_Extension);
793 end if;
795 Result :=
796 Locate_Regular_File
797 (File_Name => Directory & Project_File_Name &
798 Project_File_Extension,
799 Path => Project_Path.all);
801 -- Then we try <directory>/<file_name>
803 if Result = null then
804 if Current_Verbosity = High then
805 Write_Str (" Trying ");
806 Write_Str (Directory);
807 Write_Line (Project_File_Name);
808 end if;
810 Result :=
811 Locate_Regular_File
812 (File_Name => Directory & Project_File_Name,
813 Path => Project_Path.all);
814 end if;
815 end if;
816 end if;
818 -- If we cannot find the project file, we return an empty string
820 if Result = null then
821 return "";
823 else
824 declare
825 Final_Result : String
826 := GNAT.OS_Lib.Normalize_Pathname (Result.all);
827 begin
828 Free (Result);
829 Canonical_Case_File_Name (Final_Result);
830 return Final_Result;
831 end;
833 end if;
835 end Project_Path_Name_Of;
837 -------------------------
838 -- Simple_File_Name_Of --
839 -------------------------
841 function Simple_File_Name_Of (Path_Name : Name_Id) return Name_Id is
842 begin
843 Get_Name_String (Path_Name);
845 for Index in reverse 1 .. Name_Len loop
846 if Name_Buffer (Index) = '/'
847 or else Name_Buffer (Index) = Dir_Sep
848 then
849 exit when Index = Name_Len;
850 Name_Buffer (1 .. Name_Len - Index) :=
851 Name_Buffer (Index + 1 .. Name_Len);
852 Name_Len := Name_Len - Index;
853 return Name_Find;
854 end if;
855 end loop;
857 return No_Name;
859 end Simple_File_Name_Of;
861 begin
862 Canonical_Case_File_Name (Project_File_Extension);
864 if Prj_Path.all = "" then
865 Project_Path := new String'(".");
867 else
868 Project_Path := new String'("." & Path_Separator & Prj_Path.all);
869 end if;
871 end Prj.Part;