1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2009, 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 ------------------------------------------------------------------------------
28 with Osint; use Osint;
29 with Output; use Output;
30 with Prj.Com; use Prj.Com;
33 package body Prj.Env is
35 Default_Naming : constant Naming_Id := Naming_Table.First;
37 -----------------------
38 -- Local Subprograms --
39 -----------------------
42 (Source_Dirs : String_List_Id;
43 In_Tree : Project_Tree_Ref);
44 -- Add to Ada_Path_Buffer all the source directories in string list
45 -- Source_Dirs, if any. Increment Ada_Path_Length.
47 procedure Add_To_Path (Dir : String; In_Tree : Project_Tree_Ref);
48 -- If Dir is not already in the global variable Ada_Path_Buffer, add it.
49 -- Increment Ada_Path_Length.
50 -- If Ada_Path_Length /= 0, prepend a Path_Separator character to
53 procedure Add_To_Source_Path
54 (Source_Dirs : String_List_Id; In_Tree : Project_Tree_Ref);
55 -- Add to Ada_Path_B all the source directories in string list
56 -- Source_Dirs, if any. Increment Ada_Path_Length.
58 procedure Add_To_Object_Path
59 (Object_Dir : Path_Name_Type;
60 In_Tree : Project_Tree_Ref);
61 -- Add Object_Dir to object path table. Make sure it is not duplicate
62 -- and it is the last one in the current table.
64 procedure Set_Path_File_Var (Name : String; Value : String);
65 -- Call Setenv, after calling To_Host_File_Spec
67 function Ultimate_Extension_Of
68 (Project : Project_Id;
69 In_Tree : Project_Tree_Ref) return Project_Id;
70 -- Return a project that is either Project or an extended ancestor of
71 -- Project that itself is not extended.
73 ----------------------
74 -- Ada_Include_Path --
75 ----------------------
77 function Ada_Include_Path
78 (Project : Project_Id;
79 In_Tree : Project_Tree_Ref) return String_Access
81 procedure Add (Project : Project_Id; Dummy : in out Boolean);
82 -- Add source dirs of Project to the path
88 procedure Add (Project : Project_Id; Dummy : in out Boolean) is
89 pragma Unreferenced (Dummy);
91 Add_To_Path (In_Tree.Projects.Table (Project).Source_Dirs, In_Tree);
94 procedure For_All_Projects is
95 new For_Every_Project_Imported (Boolean, Add);
96 Dummy : Boolean := False;
98 -- Start of processing for Ada_Include_Path
101 -- If it is the first time we call this function for
102 -- this project, compute the source path
104 if In_Tree.Projects.Table (Project).Ada_Include_Path = null then
105 In_Tree.Private_Part.Ada_Path_Length := 0;
106 For_All_Projects (Project, In_Tree, Dummy);
108 In_Tree.Projects.Table (Project).Ada_Include_Path :=
110 (In_Tree.Private_Part.Ada_Path_Buffer
111 (1 .. In_Tree.Private_Part.Ada_Path_Length));
114 return In_Tree.Projects.Table (Project).Ada_Include_Path;
115 end Ada_Include_Path;
117 ----------------------
118 -- Ada_Include_Path --
119 ----------------------
121 function Ada_Include_Path
122 (Project : Project_Id;
123 In_Tree : Project_Tree_Ref;
124 Recursive : Boolean) return String
128 return Ada_Include_Path (Project, In_Tree).all;
130 In_Tree.Private_Part.Ada_Path_Length := 0;
132 (In_Tree.Projects.Table (Project).Source_Dirs, In_Tree);
134 In_Tree.Private_Part.Ada_Path_Buffer
135 (1 .. In_Tree.Private_Part.Ada_Path_Length);
137 end Ada_Include_Path;
139 ----------------------
140 -- Ada_Objects_Path --
141 ----------------------
143 function Ada_Objects_Path
144 (Project : Project_Id;
145 In_Tree : Project_Tree_Ref;
146 Including_Libraries : Boolean := True) return String_Access
148 procedure Add (Project : Project_Id; Dummy : in out Boolean);
149 -- Add all the object directories of a project to the path
155 procedure Add (Project : Project_Id; Dummy : in out Boolean) is
156 pragma Unreferenced (Dummy);
157 Path : constant Path_Name_Type :=
160 Including_Libraries => Including_Libraries,
161 Only_If_Ada => False);
163 if Path /= No_Path then
164 Add_To_Path (Get_Name_String (Path), In_Tree);
168 procedure For_All_Projects is
169 new For_Every_Project_Imported (Boolean, Add);
170 Dummy : Boolean := False;
172 -- Start of processing for Ada_Objects_Path
175 -- If it is the first time we call this function for
176 -- this project, compute the objects path
178 if In_Tree.Projects.Table (Project).Ada_Objects_Path = null then
179 In_Tree.Private_Part.Ada_Path_Length := 0;
180 For_All_Projects (Project, In_Tree, Dummy);
182 In_Tree.Projects.Table (Project).Ada_Objects_Path :=
184 (In_Tree.Private_Part.Ada_Path_Buffer
185 (1 .. In_Tree.Private_Part.Ada_Path_Length));
188 return In_Tree.Projects.Table (Project).Ada_Objects_Path;
189 end Ada_Objects_Path;
191 ------------------------
192 -- Add_To_Object_Path --
193 ------------------------
195 procedure Add_To_Object_Path
196 (Object_Dir : Path_Name_Type; In_Tree : Project_Tree_Ref)
199 -- Check if the directory is already in the table
201 for Index in Object_Path_Table.First ..
202 Object_Path_Table.Last (In_Tree.Private_Part.Object_Paths)
205 -- If it is, remove it, and add it as the last one
207 if In_Tree.Private_Part.Object_Paths.Table (Index) = Object_Dir then
208 for Index2 in Index + 1 ..
209 Object_Path_Table.Last
210 (In_Tree.Private_Part.Object_Paths)
212 In_Tree.Private_Part.Object_Paths.Table (Index2 - 1) :=
213 In_Tree.Private_Part.Object_Paths.Table (Index2);
216 In_Tree.Private_Part.Object_Paths.Table
217 (Object_Path_Table.Last (In_Tree.Private_Part.Object_Paths)) :=
223 -- The directory is not already in the table, add it
225 Object_Path_Table.Increment_Last (In_Tree.Private_Part.Object_Paths);
226 In_Tree.Private_Part.Object_Paths.Table
227 (Object_Path_Table.Last (In_Tree.Private_Part.Object_Paths)) :=
229 end Add_To_Object_Path;
235 procedure Add_To_Path
236 (Source_Dirs : String_List_Id;
237 In_Tree : Project_Tree_Ref)
239 Current : String_List_Id := Source_Dirs;
240 Source_Dir : String_Element;
242 while Current /= Nil_String loop
243 Source_Dir := In_Tree.String_Elements.Table (Current);
244 Add_To_Path (Get_Name_String (Source_Dir.Display_Value), In_Tree);
245 Current := Source_Dir.Next;
249 procedure Add_To_Path (Dir : String; In_Tree : Project_Tree_Ref) is
251 New_Buffer : String_Access;
254 function Is_Present (Path : String; Dir : String) return Boolean;
255 -- Return True if Dir is part of Path
261 function Is_Present (Path : String; Dir : String) return Boolean is
262 Last : constant Integer := Path'Last - Dir'Length + 1;
265 for J in Path'First .. Last loop
267 -- Note: the order of the conditions below is important, since
268 -- it ensures a minimal number of string comparisons.
271 or else Path (J - 1) = Path_Separator)
273 (J + Dir'Length > Path'Last
274 or else Path (J + Dir'Length) = Path_Separator)
275 and then Dir = Path (J .. J + Dir'Length - 1)
284 -- Start of processing for Add_To_Path
287 if Is_Present (In_Tree.Private_Part.Ada_Path_Buffer
288 (1 .. In_Tree.Private_Part.Ada_Path_Length),
292 -- Dir is already in the path, nothing to do
297 Min_Len := In_Tree.Private_Part.Ada_Path_Length + Dir'Length;
299 if In_Tree.Private_Part.Ada_Path_Length > 0 then
301 -- Add 1 for the Path_Separator character
303 Min_Len := Min_Len + 1;
306 -- If Ada_Path_Buffer is too small, increase it
308 Len := In_Tree.Private_Part.Ada_Path_Buffer'Last;
310 if Len < Min_Len then
313 exit when Len >= Min_Len;
316 New_Buffer := new String (1 .. Len);
317 New_Buffer (1 .. In_Tree.Private_Part.Ada_Path_Length) :=
318 In_Tree.Private_Part.Ada_Path_Buffer
319 (1 .. In_Tree.Private_Part.Ada_Path_Length);
320 Free (In_Tree.Private_Part.Ada_Path_Buffer);
321 In_Tree.Private_Part.Ada_Path_Buffer := New_Buffer;
324 if In_Tree.Private_Part.Ada_Path_Length > 0 then
325 In_Tree.Private_Part.Ada_Path_Length :=
326 In_Tree.Private_Part.Ada_Path_Length + 1;
327 In_Tree.Private_Part.Ada_Path_Buffer
328 (In_Tree.Private_Part.Ada_Path_Length) := Path_Separator;
331 In_Tree.Private_Part.Ada_Path_Buffer
332 (In_Tree.Private_Part.Ada_Path_Length + 1 ..
333 In_Tree.Private_Part.Ada_Path_Length + Dir'Length) := Dir;
334 In_Tree.Private_Part.Ada_Path_Length :=
335 In_Tree.Private_Part.Ada_Path_Length + Dir'Length;
338 ------------------------
339 -- Add_To_Source_Path --
340 ------------------------
342 procedure Add_To_Source_Path
343 (Source_Dirs : String_List_Id; In_Tree : Project_Tree_Ref)
345 Current : String_List_Id := Source_Dirs;
346 Source_Dir : String_Element;
350 -- Add each source directory
352 while Current /= Nil_String loop
353 Source_Dir := In_Tree.String_Elements.Table (Current);
356 -- Check if the source directory is already in the table
358 for Index in Source_Path_Table.First ..
359 Source_Path_Table.Last
360 (In_Tree.Private_Part.Source_Paths)
362 -- If it is already, no need to add it
364 if In_Tree.Private_Part.Source_Paths.Table (Index) =
373 Source_Path_Table.Increment_Last
374 (In_Tree.Private_Part.Source_Paths);
375 In_Tree.Private_Part.Source_Paths.Table
376 (Source_Path_Table.Last (In_Tree.Private_Part.Source_Paths)) :=
380 -- Next source directory
382 Current := Source_Dir.Next;
384 end Add_To_Source_Path;
386 --------------------------------
387 -- Create_Config_Pragmas_File --
388 --------------------------------
390 procedure Create_Config_Pragmas_File
391 (For_Project : Project_Id;
392 Main_Project : Project_Id;
393 In_Tree : Project_Tree_Ref;
394 Include_Config_Files : Boolean := True)
396 pragma Unreferenced (Main_Project);
397 pragma Unreferenced (Include_Config_Files);
399 File_Name : Path_Name_Type := No_Path;
400 File : File_Descriptor := Invalid_FD;
402 Current_Unit : Unit_Index := Unit_Table.First;
404 First_Project : Project_List := Empty_Project_List;
406 Current_Project : Project_List;
407 Current_Naming : Naming_Id;
412 procedure Check (Project : Project_Id);
413 -- Recursive procedure that put in the config pragmas file any non
414 -- standard naming schemes, if it is not already in the file, then call
415 -- itself for any imported project.
417 procedure Check_Temp_File;
418 -- Check that a temporary file has been opened.
419 -- If not, create one, and put its name in the project data,
420 -- with the indication that it is a temporary file.
423 (Unit_Name : Name_Id;
424 File_Name : File_Name_Type;
425 Unit_Kind : Spec_Or_Body;
427 -- Put an SFN pragma in the temporary file
429 procedure Put (File : File_Descriptor; S : String);
430 procedure Put_Line (File : File_Descriptor; S : String);
431 -- Output procedures, analogous to normal Text_IO procs of same name
437 procedure Check (Project : Project_Id) is
438 Data : constant Project_Data :=
439 In_Tree.Projects.Table (Project);
442 if Current_Verbosity = High then
443 Write_Str ("Checking project file """);
444 Write_Str (Namet.Get_Name_String (Data.Name));
449 -- Is this project in the list of the visited project?
451 Current_Project := First_Project;
452 while Current_Project /= Empty_Project_List
453 and then In_Tree.Project_Lists.Table
454 (Current_Project).Project /= Project
457 In_Tree.Project_Lists.Table (Current_Project).Next;
460 -- If it is not, put it in the list, and visit it
462 if Current_Project = Empty_Project_List then
463 Project_List_Table.Increment_Last
464 (In_Tree.Project_Lists);
465 In_Tree.Project_Lists.Table
466 (Project_List_Table.Last (In_Tree.Project_Lists)) :=
467 (Project => Project, Next => First_Project);
469 Project_List_Table.Last (In_Tree.Project_Lists);
471 -- Is the naming scheme of this project one that we know?
473 Current_Naming := Default_Naming;
474 while Current_Naming <=
475 Naming_Table.Last (In_Tree.Private_Part.Namings)
476 and then not Same_Naming_Scheme
477 (Left => In_Tree.Private_Part.Namings.Table (Current_Naming),
478 Right => Data.Naming) loop
479 Current_Naming := Current_Naming + 1;
482 -- If we don't know it, add it
485 Naming_Table.Last (In_Tree.Private_Part.Namings)
487 Naming_Table.Increment_Last (In_Tree.Private_Part.Namings);
488 In_Tree.Private_Part.Namings.Table
489 (Naming_Table.Last (In_Tree.Private_Part.Namings)) :=
492 -- We need a temporary file to be created
496 -- Put the SFN pragmas for the naming scheme
501 (File, "pragma Source_File_Name_Project");
503 (File, " (Spec_File_Name => ""*" &
504 Spec_Suffix_Of (In_Tree, "ada", Data.Naming) &
507 (File, " Casing => " &
508 Image (Data.Naming.Casing) & ",");
510 (File, " Dot_Replacement => """ &
511 Namet.Get_Name_String (Data.Naming.Dot_Replacement) &
517 (File, "pragma Source_File_Name_Project");
519 (File, " (Body_File_Name => ""*" &
520 Body_Suffix_Of (In_Tree, "ada", Data.Naming) &
523 (File, " Casing => " &
524 Image (Data.Naming.Casing) & ",");
526 (File, " Dot_Replacement => """ &
527 Namet.Get_Name_String (Data.Naming.Dot_Replacement) &
530 -- and maybe separate
532 if Body_Suffix_Of (In_Tree, "ada", Data.Naming) /=
533 Get_Name_String (Data.Naming.Separate_Suffix)
536 (File, "pragma Source_File_Name_Project");
538 (File, " (Subunit_File_Name => ""*" &
539 Namet.Get_Name_String (Data.Naming.Separate_Suffix) &
542 (File, " Casing => " &
543 Image (Data.Naming.Casing) &
546 (File, " Dot_Replacement => """ &
547 Namet.Get_Name_String (Data.Naming.Dot_Replacement) &
552 if Data.Extends /= No_Project then
553 Check (Data.Extends);
557 Current : Project_List := Data.Imported_Projects;
560 while Current /= Empty_Project_List loop
562 (In_Tree.Project_Lists.Table
564 Current := In_Tree.Project_Lists.Table
571 ---------------------
572 -- Check_Temp_File --
573 ---------------------
575 procedure Check_Temp_File is
577 if File = Invalid_FD then
578 Tempdir.Create_Temp_File (File, Name => File_Name);
580 if File = Invalid_FD then
582 ("unable to create temporary configuration pragmas file");
585 Record_Temp_File (File_Name);
587 if Opt.Verbose_Mode then
588 Write_Str ("Creating temp file """);
589 Write_Str (Get_Name_String (File_Name));
601 (Unit_Name : Name_Id;
602 File_Name : File_Name_Type;
603 Unit_Kind : Spec_Or_Body;
607 -- A temporary file needs to be open
611 -- Put the pragma SFN for the unit kind (spec or body)
613 Put (File, "pragma Source_File_Name_Project (");
614 Put (File, Namet.Get_Name_String (Unit_Name));
616 if Unit_Kind = Specification then
617 Put (File, ", Spec_File_Name => """);
619 Put (File, ", Body_File_Name => """);
622 Put (File, Namet.Get_Name_String (File_Name));
626 Put (File, ", Index =>");
627 Put (File, Index'Img);
630 Put_Line (File, ");");
633 procedure Put (File : File_Descriptor; S : String) is
637 Last := Write (File, S (S'First)'Address, S'Length);
639 if Last /= S'Length then
640 Prj.Com.Fail ("Disk full");
643 if Current_Verbosity = High then
652 procedure Put_Line (File : File_Descriptor; S : String) is
653 S0 : String (1 .. S'Length + 1);
657 -- Add an ASCII.LF to the string. As this config file is supposed to
658 -- be used only by the compiler, we don't care about the characters
659 -- for the end of line. In fact we could have put a space, but
660 -- it is more convenient to be able to read gnat.adc during
661 -- development, for which the ASCII.LF is fine.
663 S0 (1 .. S'Length) := S;
664 S0 (S0'Last) := ASCII.LF;
665 Last := Write (File, S0'Address, S0'Length);
667 if Last /= S'Length + 1 then
668 Prj.Com.Fail ("Disk full");
671 if Current_Verbosity = High then
676 -- Start of processing for Create_Config_Pragmas_File
680 In_Tree.Projects.Table (For_Project).Config_Checked
683 -- Remove any memory of processed naming schemes, if any
685 Naming_Table.Set_Last (In_Tree.Private_Part.Namings, Default_Naming);
687 -- Check the naming schemes
691 -- Visit all the units and process those that need an SFN pragma
694 Current_Unit <= Unit_Table.Last (In_Tree.Units)
697 Unit : constant Unit_Data :=
698 In_Tree.Units.Table (Current_Unit);
701 if Unit.File_Names (Specification).Needs_Pragma then
703 Unit.File_Names (Specification).Name,
705 Unit.File_Names (Specification).Index);
708 if Unit.File_Names (Body_Part).Needs_Pragma then
710 Unit.File_Names (Body_Part).Name,
712 Unit.File_Names (Body_Part).Index);
715 Current_Unit := Current_Unit + 1;
719 -- If there are no non standard naming scheme, issue the GNAT
720 -- standard naming scheme. This will tell the compiler that
721 -- a project file is used and will forbid any pragma SFN.
723 if File = Invalid_FD then
726 Put_Line (File, "pragma Source_File_Name_Project");
727 Put_Line (File, " (Spec_File_Name => ""*.ads"",");
728 Put_Line (File, " Dot_Replacement => ""-"",");
729 Put_Line (File, " Casing => lowercase);");
731 Put_Line (File, "pragma Source_File_Name_Project");
732 Put_Line (File, " (Body_File_Name => ""*.adb"",");
733 Put_Line (File, " Dot_Replacement => ""-"",");
734 Put_Line (File, " Casing => lowercase);");
737 -- Close the temporary file
739 GNAT.OS_Lib.Close (File, Status);
742 Prj.Com.Fail ("disk full");
745 if Opt.Verbose_Mode then
746 Write_Str ("Closing configuration file """);
747 Write_Str (Get_Name_String (File_Name));
751 In_Tree.Projects.Table (For_Project).Config_File_Name :=
753 In_Tree.Projects.Table (For_Project).Config_File_Temp :=
756 In_Tree.Projects.Table (For_Project).Config_Checked :=
759 end Create_Config_Pragmas_File;
765 procedure Create_Mapping (In_Tree : Project_Tree_Ref) is
766 The_Unit_Data : Unit_Data;
767 Data : File_Name_Data;
772 for Unit in 1 .. Unit_Table.Last (In_Tree.Units) loop
773 The_Unit_Data := In_Tree.Units.Table (Unit);
775 -- Process only if the unit has a valid name
777 if The_Unit_Data.Name /= No_Name then
778 Data := The_Unit_Data.File_Names (Specification);
780 -- If there is a spec, put it in the mapping
782 if Data.Name /= No_File then
783 if Data.Path.Name = Slash then
784 Fmap.Add_Forbidden_File_Name (Data.Name);
787 (Unit_Name => Unit_Name_Type (The_Unit_Data.Name),
788 File_Name => Data.Name,
789 Path_Name => File_Name_Type (Data.Path.Name));
793 Data := The_Unit_Data.File_Names (Body_Part);
795 -- If there is a body (or subunit) put it in the mapping
797 if Data.Name /= No_File then
798 if Data.Path.Name = Slash then
799 Fmap.Add_Forbidden_File_Name (Data.Name);
802 (Unit_Name => Unit_Name_Type (The_Unit_Data.Name),
803 File_Name => Data.Name,
804 Path_Name => File_Name_Type (Data.Path.Name));
811 -------------------------
812 -- Create_Mapping_File --
813 -------------------------
815 procedure Create_Mapping_File
816 (Project : Project_Id;
817 Language : Name_Id := No_Name;
818 In_Tree : Project_Tree_Ref;
819 Name : out Path_Name_Type)
821 File : File_Descriptor := Invalid_FD;
824 Present : array (No_Project .. Project_Table.Last (In_Tree.Projects))
825 of Boolean := (others => False);
826 -- For each project in the closure of Project, the corresponding flag
827 -- will be set to True.
830 Suffix : File_Name_Type;
831 The_Unit_Data : Unit_Data;
832 Data : File_Name_Data;
833 Iter : Source_Iterator;
835 procedure Put_Name_Buffer;
836 -- Put the line contained in the Name_Buffer in the mapping file
838 procedure Put_Data (Spec : Boolean);
839 -- Put the mapping of the spec or body contained in Data in the file
842 procedure Recursive_Flag (Prj : Project_Id);
843 -- Set the flags corresponding to Prj, the projects it imports
844 -- (directly or indirectly) or extends to True. Call itself recursively.
850 procedure Put_Name_Buffer is
854 Name_Len := Name_Len + 1;
855 Name_Buffer (Name_Len) := ASCII.LF;
856 Last := Write (File, Name_Buffer (1)'Address, Name_Len);
858 if Last /= Name_Len then
859 Prj.Com.Fail ("Disk full, cannot write mapping file");
867 procedure Put_Data (Spec : Boolean) is
869 -- Line with the unit name
871 Get_Name_String (The_Unit_Data.Name);
872 Name_Len := Name_Len + 1;
873 Name_Buffer (Name_Len) := '%';
874 Name_Len := Name_Len + 1;
877 Name_Buffer (Name_Len) := 's';
879 Name_Buffer (Name_Len) := 'b';
884 -- Line with the file name
886 Get_Name_String (Data.Name);
889 -- Line with the path name
891 Get_Name_String (Data.Path.Name);
899 procedure Recursive_Flag (Prj : Project_Id) is
900 Imported : Project_List;
904 -- Nothing to do for non existent project or project that has already
907 if Prj /= No_Project and then not Present (Prj) then
908 Present (Prj) := True;
910 Imported := In_Tree.Projects.Table (Prj).Imported_Projects;
911 while Imported /= Empty_Project_List loop
912 Proj := In_Tree.Project_Lists.Table (Imported).Project;
913 Imported := In_Tree.Project_Lists.Table (Imported).Next;
914 Recursive_Flag (Proj);
917 Recursive_Flag (In_Tree.Projects.Table (Prj).Extends);
921 -- Start of processing for Create_Mapping_File
924 -- Flag the necessary projects
926 Recursive_Flag (Project);
928 -- Create the temporary file
930 Tempdir.Create_Temp_File (File, Name => Name);
932 if File = Invalid_FD then
933 Prj.Com.Fail ("unable to create temporary mapping file");
936 Record_Temp_File (Name);
938 if Opt.Verbose_Mode then
939 Write_Str ("Creating temp mapping file """);
940 Write_Str (Get_Name_String (Name));
945 if Language = No_Name then
946 if In_Tree.Private_Part.Fill_Mapping_File then
947 for Unit in 1 .. Unit_Table.Last (In_Tree.Units) loop
948 The_Unit_Data := In_Tree.Units.Table (Unit);
950 -- Case of unit has a valid name
952 if The_Unit_Data.Name /= No_Name then
953 Data := The_Unit_Data.File_Names (Specification);
955 -- If there is a spec, put it mapping in the file if it is
956 -- from a project in the closure of Project.
958 if Data.Name /= No_File and then Present (Data.Project) then
959 Put_Data (Spec => True);
962 Data := The_Unit_Data.File_Names (Body_Part);
964 -- If there is a body (or subunit) put its mapping in the
965 -- file if it is from a project in the closure of Project.
967 if Data.Name /= No_File and then Present (Data.Project) then
968 Put_Data (Spec => False);
974 -- If language is defined
976 -- For all source of the Language of all projects in the closure
978 for Proj in Present'Range loop
979 if Present (Proj) then
981 Iter := For_Each_Source (In_Tree, Proj);
983 Source := Prj.Element (Iter);
984 exit when Source = No_Source;
986 if Source.Language.Name = Language
987 and then not Source.Locally_Removed
988 and then Source.Replaced_By = No_Source
989 and then Source.Path.Name /= No_Path
991 if Source.Unit /= No_Name then
992 Get_Name_String (Source.Unit);
994 if Source.Kind = Spec then
996 Source.Language.Config.Mapping_Spec_Suffix;
999 Source.Language.Config.Mapping_Body_Suffix;
1002 if Suffix /= No_File then
1003 Add_Str_To_Name_Buffer (Get_Name_String (Suffix));
1009 Get_Name_String (Source.File);
1012 Get_Name_String (Source.Path.Name);
1022 GNAT.OS_Lib.Close (File, Status);
1026 -- We were able to create the temporary file, so there is no problem
1027 -- of protection. However, we are not able to close it, so there must
1028 -- be a capacity problem that we express using "disk full".
1030 Prj.Com.Fail ("disk full, could not write mapping file");
1032 end Create_Mapping_File;
1034 --------------------------
1035 -- Create_New_Path_File --
1036 --------------------------
1038 procedure Create_New_Path_File
1039 (In_Tree : Project_Tree_Ref;
1040 Path_FD : out File_Descriptor;
1041 Path_Name : out Path_Name_Type)
1044 Tempdir.Create_Temp_File (Path_FD, Path_Name);
1046 if Path_Name /= No_Path then
1047 Record_Temp_File (Path_Name);
1049 -- Record the name, so that the temp path file will be deleted at the
1050 -- end of the program.
1052 Path_File_Table.Increment_Last (In_Tree.Private_Part.Path_Files);
1053 In_Tree.Private_Part.Path_Files.Table
1054 (Path_File_Table.Last (In_Tree.Private_Part.Path_Files)) :=
1057 end Create_New_Path_File;
1059 ---------------------------
1060 -- Delete_All_Path_Files --
1061 ---------------------------
1063 procedure Delete_All_Path_Files (In_Tree : Project_Tree_Ref) is
1064 Disregard : Boolean := True;
1065 pragma Warnings (Off, Disregard);
1068 for Index in Path_File_Table.First ..
1069 Path_File_Table.Last (In_Tree.Private_Part.Path_Files)
1071 if In_Tree.Private_Part.Path_Files.Table (Index) /= No_Path then
1074 (In_Tree.Private_Part.Path_Files.Table (Index)),
1079 -- If any of the environment variables ADA_PRJ_INCLUDE_FILE or
1080 -- ADA_PRJ_OBJECTS_FILE has been set, then reset their value to
1081 -- the empty string. On VMS, this has the effect of deassigning
1082 -- the logical names.
1084 if In_Tree.Private_Part.Ada_Prj_Include_File_Set then
1085 Setenv (Project_Include_Path_File, "");
1086 In_Tree.Private_Part.Ada_Prj_Include_File_Set := False;
1089 if In_Tree.Private_Part.Ada_Prj_Objects_File_Set then
1090 Setenv (Project_Objects_Path_File, "");
1091 In_Tree.Private_Part.Ada_Prj_Objects_File_Set := False;
1093 end Delete_All_Path_Files;
1095 ------------------------------------
1096 -- File_Name_Of_Library_Unit_Body --
1097 ------------------------------------
1099 function File_Name_Of_Library_Unit_Body
1101 Project : Project_Id;
1102 In_Tree : Project_Tree_Ref;
1103 Main_Project_Only : Boolean := True;
1104 Full_Path : Boolean := False) return String
1106 The_Project : Project_Id := Project;
1107 Data : Project_Data :=
1108 In_Tree.Projects.Table (Project);
1109 Original_Name : String := Name;
1111 Extended_Spec_Name : String :=
1113 Spec_Suffix_Of (In_Tree, "ada", Data.Naming);
1114 Extended_Body_Name : String :=
1116 Body_Suffix_Of (In_Tree, "ada", Data.Naming);
1120 The_Original_Name : Name_Id;
1121 The_Spec_Name : Name_Id;
1122 The_Body_Name : Name_Id;
1125 Canonical_Case_File_Name (Original_Name);
1126 Name_Len := Original_Name'Length;
1127 Name_Buffer (1 .. Name_Len) := Original_Name;
1128 The_Original_Name := Name_Find;
1130 Canonical_Case_File_Name (Extended_Spec_Name);
1131 Name_Len := Extended_Spec_Name'Length;
1132 Name_Buffer (1 .. Name_Len) := Extended_Spec_Name;
1133 The_Spec_Name := Name_Find;
1135 Canonical_Case_File_Name (Extended_Body_Name);
1136 Name_Len := Extended_Body_Name'Length;
1137 Name_Buffer (1 .. Name_Len) := Extended_Body_Name;
1138 The_Body_Name := Name_Find;
1140 if Current_Verbosity = High then
1141 Write_Str ("Looking for file name of """);
1145 Write_Str (" Extended Spec Name = """);
1146 Write_Str (Extended_Spec_Name);
1149 Write_Str (" Extended Body Name = """);
1150 Write_Str (Extended_Body_Name);
1155 -- For extending project, search in the extended project if the source
1156 -- is not found. For non extending projects, this loop will be run only
1160 -- Loop through units
1161 -- Should have comment explaining reverse ???
1163 for Current in reverse Unit_Table.First ..
1164 Unit_Table.Last (In_Tree.Units)
1166 Unit := In_Tree.Units.Table (Current);
1170 if not Main_Project_Only
1171 or else Unit.File_Names (Body_Part).Project = The_Project
1174 Current_Name : constant File_Name_Type :=
1175 Unit.File_Names (Body_Part).Name;
1178 -- Case of a body present
1180 if Current_Name /= No_File then
1181 if Current_Verbosity = High then
1182 Write_Str (" Comparing with """);
1183 Write_Str (Get_Name_String (Current_Name));
1188 -- If it has the name of the original name, return the
1191 if Unit.Name = The_Original_Name
1193 Current_Name = File_Name_Type (The_Original_Name)
1195 if Current_Verbosity = High then
1200 return Get_Name_String
1201 (Unit.File_Names (Body_Part).Path.Name);
1204 return Get_Name_String (Current_Name);
1207 -- If it has the name of the extended body name,
1208 -- return the extended body name
1210 elsif Current_Name = File_Name_Type (The_Body_Name) then
1211 if Current_Verbosity = High then
1216 return Get_Name_String
1217 (Unit.File_Names (Body_Part).Path.Name);
1220 return Extended_Body_Name;
1224 if Current_Verbosity = High then
1225 Write_Line (" not good");
1234 if not Main_Project_Only
1235 or else Unit.File_Names (Specification).Project = The_Project
1238 Current_Name : constant File_Name_Type :=
1239 Unit.File_Names (Specification).Name;
1242 -- Case of spec present
1244 if Current_Name /= No_File then
1245 if Current_Verbosity = High then
1246 Write_Str (" Comparing with """);
1247 Write_Str (Get_Name_String (Current_Name));
1252 -- If name same as original name, return original name
1254 if Unit.Name = The_Original_Name
1256 Current_Name = File_Name_Type (The_Original_Name)
1258 if Current_Verbosity = High then
1263 return Get_Name_String
1264 (Unit.File_Names (Specification).Path.Name);
1266 return Get_Name_String (Current_Name);
1269 -- If it has the same name as the extended spec name,
1270 -- return the extended spec name.
1272 elsif Current_Name = File_Name_Type (The_Spec_Name) then
1273 if Current_Verbosity = High then
1278 return Get_Name_String
1279 (Unit.File_Names (Specification).Path.Name);
1281 return Extended_Spec_Name;
1285 if Current_Verbosity = High then
1286 Write_Line (" not good");
1294 -- If we are not in an extending project, give up
1296 exit when (not Main_Project_Only) or else Data.Extends = No_Project;
1298 -- Otherwise, look in the project we are extending
1300 The_Project := Data.Extends;
1301 Data := In_Tree.Projects.Table (The_Project);
1304 -- We don't know this file name, return an empty string
1307 end File_Name_Of_Library_Unit_Body;
1309 -------------------------
1310 -- For_All_Object_Dirs --
1311 -------------------------
1313 procedure For_All_Object_Dirs
1314 (Project : Project_Id;
1315 In_Tree : Project_Tree_Ref)
1317 procedure For_Project (Prj : Project_Id; Dummy : in out Integer);
1318 -- Get all object directories of Prj
1324 procedure For_Project (Prj : Project_Id; Dummy : in out Integer) is
1325 pragma Unreferenced (Dummy);
1327 Data : Project_Data renames In_Tree.Projects.Table (Prj);
1330 -- ??? Set_Ada_Paths has a different behavior for library project
1331 -- files, should we have the same ?
1333 if Data.Object_Directory /= No_Path_Information then
1334 Get_Name_String (Data.Object_Directory.Display_Name);
1335 Action (Name_Buffer (1 .. Name_Len));
1339 procedure Get_Object_Dirs is
1340 new For_Every_Project_Imported (Integer, For_Project);
1341 Dummy : Integer := 1;
1343 -- Start of processing for For_All_Object_Dirs
1346 Get_Object_Dirs (Project, In_Tree, Dummy);
1347 end For_All_Object_Dirs;
1349 -------------------------
1350 -- For_All_Source_Dirs --
1351 -------------------------
1353 procedure For_All_Source_Dirs
1354 (Project : Project_Id;
1355 In_Tree : Project_Tree_Ref)
1357 procedure For_Project (Prj : Project_Id; Dummy : in out Integer);
1358 -- Get all object directories of Prj
1364 procedure For_Project (Prj : Project_Id; Dummy : in out Integer) is
1365 pragma Unreferenced (Dummy);
1367 Data : Project_Data renames In_Tree.Projects.Table (Prj);
1368 Current : String_List_Id := Data.Source_Dirs;
1369 The_String : String_Element;
1372 -- If there are Ada sources, call action with the name of every
1373 -- source directory.
1375 if Has_Ada_Sources (In_Tree.Projects.Table (Project)) then
1376 while Current /= Nil_String loop
1377 The_String := In_Tree.String_Elements.Table (Current);
1378 Action (Get_Name_String (The_String.Display_Value));
1379 Current := The_String.Next;
1384 procedure Get_Source_Dirs is
1385 new For_Every_Project_Imported (Integer, For_Project);
1386 Dummy : Integer := 1;
1388 -- Start of processing for For_All_Source_Dirs
1391 Get_Source_Dirs (Project, In_Tree, Dummy);
1392 end For_All_Source_Dirs;
1398 procedure Get_Reference
1399 (Source_File_Name : String;
1400 In_Tree : Project_Tree_Ref;
1401 Project : out Project_Id;
1402 Path : out Path_Name_Type)
1405 -- Body below could use some comments ???
1407 if Current_Verbosity > Default then
1408 Write_Str ("Getting Reference_Of (""");
1409 Write_Str (Source_File_Name);
1410 Write_Str (""") ... ");
1414 Original_Name : String := Source_File_Name;
1418 Canonical_Case_File_Name (Original_Name);
1420 for Id in Unit_Table.First ..
1421 Unit_Table.Last (In_Tree.Units)
1423 Unit := In_Tree.Units.Table (Id);
1425 if (Unit.File_Names (Specification).Name /= No_File
1427 Namet.Get_Name_String
1428 (Unit.File_Names (Specification).Name) = Original_Name)
1429 or else (Unit.File_Names (Specification).Path /=
1432 Namet.Get_Name_String
1433 (Unit.File_Names (Specification).Path.Name) =
1436 Project := Ultimate_Extension_Of
1437 (Project => Unit.File_Names (Specification).Project,
1438 In_Tree => In_Tree);
1439 Path := Unit.File_Names (Specification).Path.Display_Name;
1441 if Current_Verbosity > Default then
1442 Write_Str ("Done: Specification.");
1448 elsif (Unit.File_Names (Body_Part).Name /= No_File
1450 Namet.Get_Name_String
1451 (Unit.File_Names (Body_Part).Name) = Original_Name)
1452 or else (Unit.File_Names (Body_Part).Path /= No_Path_Information
1453 and then Namet.Get_Name_String
1454 (Unit.File_Names (Body_Part).Path.Name) =
1457 Project := Ultimate_Extension_Of
1458 (Project => Unit.File_Names (Body_Part).Project,
1459 In_Tree => In_Tree);
1460 Path := Unit.File_Names (Body_Part).Path.Display_Name;
1462 if Current_Verbosity > Default then
1463 Write_Str ("Done: Body.");
1472 Project := No_Project;
1475 if Current_Verbosity > Default then
1476 Write_Str ("Cannot be found.");
1485 procedure Initialize (In_Tree : Project_Tree_Ref) is
1487 In_Tree.Private_Part.Fill_Mapping_File := True;
1488 In_Tree.Private_Part.Current_Source_Path_File := No_Path;
1489 In_Tree.Private_Part.Current_Object_Path_File := No_Path;
1496 -- Could use some comments in this body ???
1498 procedure Print_Sources (In_Tree : Project_Tree_Ref) is
1502 Write_Line ("List of Sources:");
1504 for Id in Unit_Table.First ..
1505 Unit_Table.Last (In_Tree.Units)
1507 Unit := In_Tree.Units.Table (Id);
1509 Write_Line (Namet.Get_Name_String (Unit.Name));
1511 if Unit.File_Names (Specification).Name /= No_File then
1512 if Unit.File_Names (Specification).Project = No_Project then
1513 Write_Line (" No project");
1516 Write_Str (" Project: ");
1518 (In_Tree.Projects.Table
1519 (Unit.File_Names (Specification).Project).Path.Name);
1520 Write_Line (Name_Buffer (1 .. Name_Len));
1523 Write_Str (" spec: ");
1525 (Namet.Get_Name_String
1526 (Unit.File_Names (Specification).Name));
1529 if Unit.File_Names (Body_Part).Name /= No_File then
1530 if Unit.File_Names (Body_Part).Project = No_Project then
1531 Write_Line (" No project");
1534 Write_Str (" Project: ");
1536 (In_Tree.Projects.Table
1537 (Unit.File_Names (Body_Part).Project).Path.Name);
1538 Write_Line (Name_Buffer (1 .. Name_Len));
1541 Write_Str (" body: ");
1543 (Namet.Get_Name_String
1544 (Unit.File_Names (Body_Part).Name));
1548 Write_Line ("end of List of Sources.");
1557 Main_Project : Project_Id;
1558 In_Tree : Project_Tree_Ref) return Project_Id
1560 Result : Project_Id := No_Project;
1562 Original_Name : String := Name;
1564 Data : constant Project_Data :=
1565 In_Tree.Projects.Table (Main_Project);
1567 Extended_Spec_Name : String :=
1569 Spec_Suffix_Of (In_Tree, "ada", Data.Naming);
1570 Extended_Body_Name : String :=
1572 Body_Suffix_Of (In_Tree, "ada", Data.Naming);
1576 Current_Name : File_Name_Type;
1577 The_Original_Name : File_Name_Type;
1578 The_Spec_Name : File_Name_Type;
1579 The_Body_Name : File_Name_Type;
1582 Canonical_Case_File_Name (Original_Name);
1583 Name_Len := Original_Name'Length;
1584 Name_Buffer (1 .. Name_Len) := Original_Name;
1585 The_Original_Name := Name_Find;
1587 Canonical_Case_File_Name (Extended_Spec_Name);
1588 Name_Len := Extended_Spec_Name'Length;
1589 Name_Buffer (1 .. Name_Len) := Extended_Spec_Name;
1590 The_Spec_Name := Name_Find;
1592 Canonical_Case_File_Name (Extended_Body_Name);
1593 Name_Len := Extended_Body_Name'Length;
1594 Name_Buffer (1 .. Name_Len) := Extended_Body_Name;
1595 The_Body_Name := Name_Find;
1597 for Current in reverse Unit_Table.First ..
1598 Unit_Table.Last (In_Tree.Units)
1600 Unit := In_Tree.Units.Table (Current);
1604 Current_Name := Unit.File_Names (Body_Part).Name;
1606 -- Case of a body present
1608 if Current_Name /= No_File then
1610 -- If it has the name of the original name or the body name,
1611 -- we have found the project.
1613 if Unit.Name = Name_Id (The_Original_Name)
1614 or else Current_Name = The_Original_Name
1615 or else Current_Name = The_Body_Name
1617 Result := Unit.File_Names (Body_Part).Project;
1624 Current_Name := Unit.File_Names (Specification).Name;
1626 if Current_Name /= No_File then
1628 -- If name same as the original name, or the spec name, we have
1629 -- found the project.
1631 if Unit.Name = Name_Id (The_Original_Name)
1632 or else Current_Name = The_Original_Name
1633 or else Current_Name = The_Spec_Name
1635 Result := Unit.File_Names (Specification).Project;
1641 -- Get the ultimate extending project
1643 if Result /= No_Project then
1644 while In_Tree.Projects.Table (Result).Extended_By /=
1647 Result := In_Tree.Projects.Table (Result).Extended_By;
1658 procedure Set_Ada_Paths
1659 (Project : Project_Id;
1660 In_Tree : Project_Tree_Ref;
1661 Including_Libraries : Boolean)
1664 Source_FD : File_Descriptor := Invalid_FD;
1665 Object_FD : File_Descriptor := Invalid_FD;
1667 Process_Source_Dirs : Boolean := False;
1668 Process_Object_Dirs : Boolean := False;
1671 -- For calls to Close
1675 procedure Recursive_Add (Project : Project_Id; Dummy : in out Boolean);
1676 -- Recursive procedure to add the source/object paths of extended/
1677 -- imported projects.
1683 procedure Recursive_Add (Project : Project_Id; Dummy : in out Boolean) is
1684 pragma Unreferenced (Dummy);
1686 Data : constant Project_Data := In_Tree.Projects.Table (Project);
1687 Path : Path_Name_Type;
1690 -- ??? This is almost the equivalent of For_All_Source_Dirs
1692 if Process_Source_Dirs then
1694 -- Add to path all source directories of this project if there are
1697 if Has_Ada_Sources (In_Tree.Projects.Table (Project)) then
1698 Add_To_Source_Path (Data.Source_Dirs, In_Tree);
1702 if Process_Object_Dirs then
1703 Path := Get_Object_Directory
1705 Including_Libraries => Including_Libraries,
1706 Only_If_Ada => True);
1708 if Path /= No_Path then
1709 Add_To_Object_Path (Path, In_Tree);
1714 procedure For_All_Projects is
1715 new For_Every_Project_Imported (Boolean, Recursive_Add);
1716 Dummy : Boolean := False;
1718 -- Start of processing for Set_Ada_Paths
1721 -- If it is the first time we call this procedure for this project,
1722 -- compute the source path and/or the object path.
1724 if In_Tree.Projects.Table (Project).Include_Path_File = No_Path then
1725 Process_Source_Dirs := True;
1726 Create_New_Path_File
1727 (In_Tree, Source_FD,
1728 In_Tree.Projects.Table (Project).Include_Path_File);
1731 -- For the object path, we make a distinction depending on
1732 -- Including_Libraries.
1734 if Including_Libraries then
1735 if In_Tree.Projects.Table
1736 (Project).Objects_Path_File_With_Libs = No_Path
1738 Process_Object_Dirs := True;
1739 Create_New_Path_File
1740 (In_Tree, Object_FD, In_Tree.Projects.Table (Project).
1741 Objects_Path_File_With_Libs);
1745 if In_Tree.Projects.Table
1746 (Project).Objects_Path_File_Without_Libs = No_Path
1748 Process_Object_Dirs := True;
1749 Create_New_Path_File
1750 (In_Tree, Object_FD, In_Tree.Projects.Table (Project).
1751 Objects_Path_File_Without_Libs);
1755 -- If there is something to do, set Seen to False for all projects,
1756 -- then call the recursive procedure Add for Project.
1758 if Process_Source_Dirs or Process_Object_Dirs then
1759 Source_Path_Table.Set_Last (In_Tree.Private_Part.Source_Paths, 0);
1760 Object_Path_Table.Set_Last (In_Tree.Private_Part.Object_Paths, 0);
1761 For_All_Projects (Project, In_Tree, Dummy);
1764 -- Write and close any file that has been created
1766 if Source_FD /= Invalid_FD then
1767 for Index in Source_Path_Table.First ..
1768 Source_Path_Table.Last
1769 (In_Tree.Private_Part.Source_Paths)
1771 Get_Name_String (In_Tree.Private_Part.Source_Paths.Table (Index));
1772 Name_Len := Name_Len + 1;
1773 Name_Buffer (Name_Len) := ASCII.LF;
1774 Len := Write (Source_FD, Name_Buffer (1)'Address, Name_Len);
1776 if Len /= Name_Len then
1777 Prj.Com.Fail ("disk full");
1781 Close (Source_FD, Status);
1784 Prj.Com.Fail ("disk full");
1788 if Object_FD /= Invalid_FD then
1789 for Index in Object_Path_Table.First ..
1790 Object_Path_Table.Last
1791 (In_Tree.Private_Part.Object_Paths)
1793 Get_Name_String (In_Tree.Private_Part.Object_Paths.Table (Index));
1794 Name_Len := Name_Len + 1;
1795 Name_Buffer (Name_Len) := ASCII.LF;
1796 Len := Write (Object_FD, Name_Buffer (1)'Address, Name_Len);
1798 if Len /= Name_Len then
1799 Prj.Com.Fail ("disk full");
1803 Close (Object_FD, Status);
1806 Prj.Com.Fail ("disk full");
1810 -- Set the env vars, if they need to be changed, and set the
1811 -- corresponding flags.
1813 if In_Tree.Private_Part.Current_Source_Path_File /=
1814 In_Tree.Projects.Table (Project).Include_Path_File
1816 In_Tree.Private_Part.Current_Source_Path_File :=
1817 In_Tree.Projects.Table (Project).Include_Path_File;
1819 (Project_Include_Path_File,
1820 Get_Name_String (In_Tree.Private_Part.Current_Source_Path_File));
1821 In_Tree.Private_Part.Ada_Prj_Include_File_Set := True;
1824 if Including_Libraries then
1825 if In_Tree.Private_Part.Current_Object_Path_File /=
1826 In_Tree.Projects.Table (Project).Objects_Path_File_With_Libs
1828 In_Tree.Private_Part.Current_Object_Path_File :=
1829 In_Tree.Projects.Table
1830 (Project).Objects_Path_File_With_Libs;
1832 (Project_Objects_Path_File,
1834 (In_Tree.Private_Part.Current_Object_Path_File));
1835 In_Tree.Private_Part.Ada_Prj_Objects_File_Set := True;
1839 if In_Tree.Private_Part.Current_Object_Path_File /=
1840 In_Tree.Projects.Table (Project).Objects_Path_File_Without_Libs
1842 In_Tree.Private_Part.Current_Object_Path_File :=
1843 In_Tree.Projects.Table
1844 (Project).Objects_Path_File_Without_Libs;
1846 (Project_Objects_Path_File,
1848 (In_Tree.Private_Part.Current_Object_Path_File));
1849 In_Tree.Private_Part.Ada_Prj_Objects_File_Set := True;
1854 ---------------------------------------------
1855 -- Set_Mapping_File_Initial_State_To_Empty --
1856 ---------------------------------------------
1858 procedure Set_Mapping_File_Initial_State_To_Empty
1859 (In_Tree : Project_Tree_Ref)
1862 In_Tree.Private_Part.Fill_Mapping_File := False;
1863 end Set_Mapping_File_Initial_State_To_Empty;
1865 -----------------------
1866 -- Set_Path_File_Var --
1867 -----------------------
1869 procedure Set_Path_File_Var (Name : String; Value : String) is
1870 Host_Spec : String_Access := To_Host_File_Spec (Value);
1872 if Host_Spec = null then
1874 ("could not convert file name """ & Value & """ to host spec");
1876 Setenv (Name, Host_Spec.all);
1879 end Set_Path_File_Var;
1881 ---------------------------
1882 -- Ultimate_Extension_Of --
1883 ---------------------------
1885 function Ultimate_Extension_Of
1886 (Project : Project_Id;
1887 In_Tree : Project_Tree_Ref) return Project_Id
1889 Result : Project_Id := Project;
1892 while In_Tree.Projects.Table (Result).Extended_By /= No_Project loop
1893 Result := In_Tree.Projects.Table (Result).Extended_By;
1897 end Ultimate_Extension_Of;