2015-09-28 Paul Thomas <pault@gcc.gnu.org>
[official-gcc.git] / gcc / ada / prj-util.adb
blob3f3b358311e6c32c212fcef84615d73ea52e05fa
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- P R J . U T I L --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2001-2015, 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 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. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Ada.Containers.Indefinite_Ordered_Sets;
27 with Ada.Directories;
28 with Ada.Strings.Fixed; use Ada.Strings.Fixed;
29 with Ada.Strings.Maps; use Ada.Strings.Maps;
30 with Ada.Unchecked_Deallocation;
32 with GNAT.Case_Util; use GNAT.Case_Util;
33 with GNAT.Regexp; use GNAT.Regexp;
35 with ALI; use ALI;
36 with Osint; use Osint;
37 with Output; use Output;
38 with Opt;
39 with Prj.Com;
40 with Snames; use Snames;
41 with Table;
42 with Targparm; use Targparm;
44 with GNAT.HTable;
46 package body Prj.Util is
48 package Source_Info_Table is new Table.Table
49 (Table_Component_Type => Source_Info_Iterator,
50 Table_Index_Type => Natural,
51 Table_Low_Bound => 1,
52 Table_Initial => 10,
53 Table_Increment => 100,
54 Table_Name => "Makeutl.Source_Info_Table");
56 package Source_Info_Project_HTable is new GNAT.HTable.Simple_HTable
57 (Header_Num => Prj.Header_Num,
58 Element => Natural,
59 No_Element => 0,
60 Key => Name_Id,
61 Hash => Prj.Hash,
62 Equal => "=");
64 procedure Free is new Ada.Unchecked_Deallocation
65 (Text_File_Data, Text_File);
67 -----------
68 -- Close --
69 -----------
71 procedure Close (File : in out Text_File) is
72 Len : Integer;
73 Status : Boolean;
75 begin
76 if File = null then
77 Prj.Com.Fail ("Close attempted on an invalid Text_File");
78 end if;
80 if File.Out_File then
81 if File.Buffer_Len > 0 then
82 Len := Write (File.FD, File.Buffer'Address, File.Buffer_Len);
84 if Len /= File.Buffer_Len then
85 Prj.Com.Fail ("Unable to write to an out Text_File");
86 end if;
87 end if;
89 Close (File.FD, Status);
91 if not Status then
92 Prj.Com.Fail ("Unable to close an out Text_File");
93 end if;
95 else
97 -- Close in file, no need to test status, since this is a file that
98 -- we read, and the file was read successfully before we closed it.
100 Close (File.FD);
101 end if;
103 Free (File);
104 end Close;
106 ------------
107 -- Create --
108 ------------
110 procedure Create (File : out Text_File; Name : String) is
111 FD : File_Descriptor;
112 File_Name : String (1 .. Name'Length + 1);
114 begin
115 File_Name (1 .. Name'Length) := Name;
116 File_Name (File_Name'Last) := ASCII.NUL;
117 FD := Create_File (Name => File_Name'Address,
118 Fmode => GNAT.OS_Lib.Text);
120 if FD = Invalid_FD then
121 File := null;
123 else
124 File := new Text_File_Data;
125 File.FD := FD;
126 File.Out_File := True;
127 File.End_Of_File_Reached := True;
128 end if;
129 end Create;
131 ---------------
132 -- Duplicate --
133 ---------------
135 procedure Duplicate
136 (This : in out Name_List_Index;
137 Shared : Shared_Project_Tree_Data_Access)
139 Old_Current : Name_List_Index;
140 New_Current : Name_List_Index;
142 begin
143 if This /= No_Name_List then
144 Old_Current := This;
145 Name_List_Table.Increment_Last (Shared.Name_Lists);
146 New_Current := Name_List_Table.Last (Shared.Name_Lists);
147 This := New_Current;
148 Shared.Name_Lists.Table (New_Current) :=
149 (Shared.Name_Lists.Table (Old_Current).Name, No_Name_List);
151 loop
152 Old_Current := Shared.Name_Lists.Table (Old_Current).Next;
153 exit when Old_Current = No_Name_List;
154 Shared.Name_Lists.Table (New_Current).Next := New_Current + 1;
155 Name_List_Table.Increment_Last (Shared.Name_Lists);
156 New_Current := New_Current + 1;
157 Shared.Name_Lists.Table (New_Current) :=
158 (Shared.Name_Lists.Table (Old_Current).Name, No_Name_List);
159 end loop;
160 end if;
161 end Duplicate;
163 -----------------
164 -- End_Of_File --
165 -----------------
167 function End_Of_File (File : Text_File) return Boolean is
168 begin
169 if File = null then
170 Prj.Com.Fail ("End_Of_File attempted on an invalid Text_File");
171 end if;
173 return File.End_Of_File_Reached;
174 end End_Of_File;
176 -------------------
177 -- Executable_Of --
178 -------------------
180 function Executable_Of
181 (Project : Project_Id;
182 Shared : Shared_Project_Tree_Data_Access;
183 Main : File_Name_Type;
184 Index : Int;
185 Ada_Main : Boolean := True;
186 Language : String := "";
187 Include_Suffix : Boolean := True) return File_Name_Type
189 pragma Assert (Project /= No_Project);
191 The_Packages : constant Package_Id := Project.Decl.Packages;
193 Builder_Package : constant Prj.Package_Id :=
194 Prj.Util.Value_Of
195 (Name => Name_Builder,
196 In_Packages => The_Packages,
197 Shared => Shared);
199 Executable : Variable_Value :=
200 Prj.Util.Value_Of
201 (Name => Name_Id (Main),
202 Index => Index,
203 Attribute_Or_Array_Name => Name_Executable,
204 In_Package => Builder_Package,
205 Shared => Shared);
207 Lang : Language_Ptr;
209 Spec_Suffix : Name_Id := No_Name;
210 Body_Suffix : Name_Id := No_Name;
212 Spec_Suffix_Length : Natural := 0;
213 Body_Suffix_Length : Natural := 0;
215 procedure Get_Suffixes
216 (B_Suffix : File_Name_Type;
217 S_Suffix : File_Name_Type);
218 -- Get the non empty suffixes in variables Spec_Suffix and Body_Suffix
220 function Add_Suffix (File : File_Name_Type) return File_Name_Type;
221 -- Return the name of the executable, based on File, and adding the
222 -- executable suffix if needed
224 ------------------
225 -- Get_Suffixes --
226 ------------------
228 procedure Get_Suffixes
229 (B_Suffix : File_Name_Type;
230 S_Suffix : File_Name_Type)
232 begin
233 if B_Suffix /= No_File then
234 Body_Suffix := Name_Id (B_Suffix);
235 Body_Suffix_Length := Natural (Length_Of_Name (Body_Suffix));
236 end if;
238 if S_Suffix /= No_File then
239 Spec_Suffix := Name_Id (S_Suffix);
240 Spec_Suffix_Length := Natural (Length_Of_Name (Spec_Suffix));
241 end if;
242 end Get_Suffixes;
244 ----------------
245 -- Add_Suffix --
246 ----------------
248 function Add_Suffix (File : File_Name_Type) return File_Name_Type is
249 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
250 Result : File_Name_Type;
251 Suffix_From_Project : Variable_Value;
252 begin
253 if Include_Suffix then
254 if Project.Config.Executable_Suffix /= No_Name then
255 Executable_Extension_On_Target :=
256 Project.Config.Executable_Suffix;
257 end if;
259 Result := Executable_Name (File);
260 Executable_Extension_On_Target := Saved_EEOT;
261 return Result;
263 elsif Builder_Package /= No_Package then
265 -- If the suffix is specified in the project itself, as opposed to
266 -- the config file, it needs to be taken into account. However,
267 -- when the project was processed, in both cases the suffix was
268 -- stored in Project.Config, so get it from the project again.
270 Suffix_From_Project :=
271 Prj.Util.Value_Of
272 (Variable_Name => Name_Executable_Suffix,
273 In_Variables =>
274 Shared.Packages.Table (Builder_Package).Decl.Attributes,
275 Shared => Shared);
277 if Suffix_From_Project /= Nil_Variable_Value
278 and then Suffix_From_Project.Value /= No_Name
279 then
280 Executable_Extension_On_Target := Suffix_From_Project.Value;
281 Result := Executable_Name (File);
282 Executable_Extension_On_Target := Saved_EEOT;
283 return Result;
284 end if;
285 end if;
287 return File;
288 end Add_Suffix;
290 -- Start of processing for Executable_Of
292 begin
293 if Ada_Main then
294 Lang := Get_Language_From_Name (Project, "ada");
295 elsif Language /= "" then
296 Lang := Get_Language_From_Name (Project, Language);
297 end if;
299 if Lang /= null then
300 Get_Suffixes
301 (B_Suffix => Lang.Config.Naming_Data.Body_Suffix,
302 S_Suffix => Lang.Config.Naming_Data.Spec_Suffix);
303 end if;
305 if Builder_Package /= No_Package then
306 if Executable = Nil_Variable_Value and then Ada_Main then
307 Get_Name_String (Main);
309 -- Try as index the name minus the implementation suffix or minus
310 -- the specification suffix.
312 declare
313 Name : constant String (1 .. Name_Len) :=
314 Name_Buffer (1 .. Name_Len);
315 Last : Positive := Name_Len;
317 Truncated : Boolean := False;
319 begin
320 if Body_Suffix /= No_Name
321 and then Last > Natural (Length_Of_Name (Body_Suffix))
322 and then Name (Last - Body_Suffix_Length + 1 .. Last) =
323 Get_Name_String (Body_Suffix)
324 then
325 Truncated := True;
326 Last := Last - Body_Suffix_Length;
327 end if;
329 if Spec_Suffix /= No_Name
330 and then not Truncated
331 and then Last > Spec_Suffix_Length
332 and then Name (Last - Spec_Suffix_Length + 1 .. Last) =
333 Get_Name_String (Spec_Suffix)
334 then
335 Truncated := True;
336 Last := Last - Spec_Suffix_Length;
337 end if;
339 if Truncated then
340 Name_Len := Last;
341 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
342 Executable :=
343 Prj.Util.Value_Of
344 (Name => Name_Find,
345 Index => 0,
346 Attribute_Or_Array_Name => Name_Executable,
347 In_Package => Builder_Package,
348 Shared => Shared);
349 end if;
350 end;
351 end if;
353 -- If we have found an Executable attribute, return its value,
354 -- possibly suffixed by the executable suffix.
356 if Executable /= Nil_Variable_Value
357 and then Executable.Value /= No_Name
358 and then Length_Of_Name (Executable.Value) /= 0
359 then
360 return Add_Suffix (File_Name_Type (Executable.Value));
361 end if;
362 end if;
364 Get_Name_String (Main);
366 -- If there is a body suffix or a spec suffix, remove this suffix,
367 -- otherwise remove any suffix ('.' followed by other characters), if
368 -- there is one.
370 if Body_Suffix /= No_Name
371 and then Name_Len > Body_Suffix_Length
372 and then Name_Buffer (Name_Len - Body_Suffix_Length + 1 .. Name_Len) =
373 Get_Name_String (Body_Suffix)
374 then
375 -- Found the body termination, remove it
377 Name_Len := Name_Len - Body_Suffix_Length;
379 elsif Spec_Suffix /= No_Name
380 and then Name_Len > Spec_Suffix_Length
381 and then
382 Name_Buffer (Name_Len - Spec_Suffix_Length + 1 .. Name_Len) =
383 Get_Name_String (Spec_Suffix)
384 then
385 -- Found the spec termination, remove it
387 Name_Len := Name_Len - Spec_Suffix_Length;
389 else
390 -- Remove any suffix, if there is one
392 Get_Name_String (Strip_Suffix (Main));
393 end if;
395 return Add_Suffix (Name_Find);
396 end Executable_Of;
398 ---------------------------
399 -- For_Interface_Sources --
400 ---------------------------
402 procedure For_Interface_Sources
403 (Tree : Project_Tree_Ref;
404 Project : Project_Id)
406 use Ada;
407 use type Ada.Containers.Count_Type;
409 package Dep_Names is new Containers.Indefinite_Ordered_Sets (String);
411 function Load_ALI (Filename : String) return ALI_Id;
412 -- Load an ALI file and return its id
414 --------------
415 -- Load_ALI --
416 --------------
418 function Load_ALI (Filename : String) return ALI_Id is
419 Result : ALI_Id := No_ALI_Id;
420 Text : Text_Buffer_Ptr;
421 Lib_File : File_Name_Type;
423 begin
424 if Directories.Exists (Filename) then
425 Name_Len := 0;
426 Add_Str_To_Name_Buffer (Filename);
427 Lib_File := Name_Find;
428 Text := Osint.Read_Library_Info (Lib_File);
429 Result :=
430 ALI.Scan_ALI
431 (Lib_File,
432 Text,
433 Ignore_ED => False,
434 Err => True,
435 Read_Lines => "UD");
436 Free (Text);
437 end if;
439 return Result;
440 end Load_ALI;
442 -- Local declarations
444 Iter : Source_Iterator;
445 Sid : Source_Id;
446 ALI : ALI_Id;
448 First_Unit : Unit_Id;
449 Second_Unit : Unit_Id;
450 Body_Needed : Boolean;
451 Deps : Dep_Names.Set;
453 -- Start of processing for For_Interface_Sources
455 begin
456 if Project.Qualifier = Aggregate_Library then
457 Iter := For_Each_Source (Tree);
458 else
459 Iter := For_Each_Source (Tree, Project);
460 end if;
462 -- First look at each spec, check if the body is needed
464 loop
465 Sid := Element (Iter);
466 exit when Sid = No_Source;
468 -- Skip sources that are removed/excluded and sources not part of
469 -- the interface for standalone libraries.
471 if Sid.Kind = Spec
472 and then (not Sid.Project.Externally_Built
473 or else Sid.Project = Project)
474 and then not Sid.Locally_Removed
475 and then (Project.Standalone_Library = No
476 or else Sid.Declared_In_Interfaces)
478 -- Handle case of non-compilable languages
480 and then Sid.Dep_Name /= No_File
481 then
482 Action (Sid);
484 -- Check ALI for dependencies on body and sep
486 ALI :=
487 Load_ALI
488 (Get_Name_String (Get_Object_Directory (Sid.Project, True))
489 & Get_Name_String (Sid.Dep_Name));
491 if ALI /= No_ALI_Id then
492 First_Unit := ALIs.Table (ALI).First_Unit;
493 Second_Unit := No_Unit_Id;
494 Body_Needed := True;
496 -- If there is both a spec and a body, check if both needed
498 if Units.Table (First_Unit).Utype = Is_Body then
499 Second_Unit := ALIs.Table (ALI).Last_Unit;
501 -- If the body is not needed, then reset First_Unit
503 if not Units.Table (Second_Unit).Body_Needed_For_SAL then
504 Body_Needed := False;
505 end if;
507 elsif Units.Table (First_Unit).Utype = Is_Spec_Only then
508 Body_Needed := False;
509 end if;
511 -- Handle all the separates, if any
513 if Body_Needed then
514 if Other_Part (Sid) /= null then
515 Deps.Include (Get_Name_String (Other_Part (Sid).File));
516 end if;
518 for Dep in ALIs.Table (ALI).First_Sdep ..
519 ALIs.Table (ALI).Last_Sdep
520 loop
521 if Sdep.Table (Dep).Subunit_Name /= No_Name then
522 Deps.Include
523 (Get_Name_String (Sdep.Table (Dep).Sfile));
524 end if;
525 end loop;
526 end if;
527 end if;
528 end if;
530 Next (Iter);
531 end loop;
533 -- Now handle the bodies and separates if needed
535 if Deps.Length /= 0 then
536 if Project.Qualifier = Aggregate_Library then
537 Iter := For_Each_Source (Tree);
538 else
539 Iter := For_Each_Source (Tree, Project);
540 end if;
542 loop
543 Sid := Element (Iter);
544 exit when Sid = No_Source;
546 if Sid.Kind /= Spec
547 and then Deps.Contains (Get_Name_String (Sid.File))
548 then
549 Action (Sid);
550 end if;
552 Next (Iter);
553 end loop;
554 end if;
555 end For_Interface_Sources;
557 --------------
558 -- Get_Line --
559 --------------
561 procedure Get_Line
562 (File : Text_File;
563 Line : out String;
564 Last : out Natural)
566 C : Character;
568 procedure Advance;
570 -------------
571 -- Advance --
572 -------------
574 procedure Advance is
575 begin
576 if File.Cursor = File.Buffer_Len then
577 File.Buffer_Len :=
578 Read
579 (FD => File.FD,
580 A => File.Buffer'Address,
581 N => File.Buffer'Length);
583 if File.Buffer_Len = 0 then
584 File.End_Of_File_Reached := True;
585 return;
586 else
587 File.Cursor := 1;
588 end if;
590 else
591 File.Cursor := File.Cursor + 1;
592 end if;
593 end Advance;
595 -- Start of processing for Get_Line
597 begin
598 if File = null then
599 Prj.Com.Fail ("Get_Line attempted on an invalid Text_File");
601 elsif File.Out_File then
602 Prj.Com.Fail ("Get_Line attempted on an out file");
603 end if;
605 Last := Line'First - 1;
607 if not File.End_Of_File_Reached then
608 loop
609 C := File.Buffer (File.Cursor);
610 exit when C = ASCII.CR or else C = ASCII.LF;
611 Last := Last + 1;
612 Line (Last) := C;
613 Advance;
615 if File.End_Of_File_Reached then
616 return;
617 end if;
619 exit when Last = Line'Last;
620 end loop;
622 if C = ASCII.CR or else C = ASCII.LF then
623 Advance;
625 if File.End_Of_File_Reached then
626 return;
627 end if;
628 end if;
630 if C = ASCII.CR
631 and then File.Buffer (File.Cursor) = ASCII.LF
632 then
633 Advance;
634 end if;
635 end if;
636 end Get_Line;
638 ----------------
639 -- Initialize --
640 ----------------
642 procedure Initialize
643 (Iter : out Source_Info_Iterator;
644 For_Project : Name_Id)
646 Ind : constant Natural := Source_Info_Project_HTable.Get (For_Project);
647 begin
648 if Ind = 0 then
649 Iter := (No_Source_Info, 0);
650 else
651 Iter := Source_Info_Table.Table (Ind);
652 end if;
653 end Initialize;
655 --------------
656 -- Is_Valid --
657 --------------
659 function Is_Valid (File : Text_File) return Boolean is
660 begin
661 return File /= null;
662 end Is_Valid;
664 ----------
665 -- Next --
666 ----------
668 procedure Next (Iter : in out Source_Info_Iterator) is
669 begin
670 if Iter.Next = 0 then
671 Iter.Info := No_Source_Info;
673 else
674 Iter := Source_Info_Table.Table (Iter.Next);
675 end if;
676 end Next;
678 ----------
679 -- Open --
680 ----------
682 procedure Open (File : out Text_File; Name : String) is
683 FD : File_Descriptor;
684 File_Name : String (1 .. Name'Length + 1);
686 begin
687 File_Name (1 .. Name'Length) := Name;
688 File_Name (File_Name'Last) := ASCII.NUL;
689 FD := Open_Read (Name => File_Name'Address,
690 Fmode => GNAT.OS_Lib.Text);
692 if FD = Invalid_FD then
693 File := null;
695 else
696 File := new Text_File_Data;
697 File.FD := FD;
698 File.Buffer_Len :=
699 Read (FD => FD,
700 A => File.Buffer'Address,
701 N => File.Buffer'Length);
703 if File.Buffer_Len = 0 then
704 File.End_Of_File_Reached := True;
705 else
706 File.Cursor := 1;
707 end if;
708 end if;
709 end Open;
711 ---------
712 -- Put --
713 ---------
715 procedure Put
716 (Into_List : in out Name_List_Index;
717 From_List : String_List_Id;
718 In_Tree : Project_Tree_Ref;
719 Lower_Case : Boolean := False)
721 Shared : constant Shared_Project_Tree_Data_Access := In_Tree.Shared;
723 Current_Name : Name_List_Index;
724 List : String_List_Id;
725 Element : String_Element;
726 Last : Name_List_Index :=
727 Name_List_Table.Last (Shared.Name_Lists);
728 Value : Name_Id;
730 begin
731 Current_Name := Into_List;
732 while Current_Name /= No_Name_List
733 and then Shared.Name_Lists.Table (Current_Name).Next /= No_Name_List
734 loop
735 Current_Name := Shared.Name_Lists.Table (Current_Name).Next;
736 end loop;
738 List := From_List;
739 while List /= Nil_String loop
740 Element := Shared.String_Elements.Table (List);
741 Value := Element.Value;
743 if Lower_Case then
744 Get_Name_String (Value);
745 To_Lower (Name_Buffer (1 .. Name_Len));
746 Value := Name_Find;
747 end if;
749 Name_List_Table.Append
750 (Shared.Name_Lists, (Name => Value, Next => No_Name_List));
752 Last := Last + 1;
754 if Current_Name = No_Name_List then
755 Into_List := Last;
756 else
757 Shared.Name_Lists.Table (Current_Name).Next := Last;
758 end if;
760 Current_Name := Last;
762 List := Element.Next;
763 end loop;
764 end Put;
766 procedure Put (File : Text_File; S : String) is
767 Len : Integer;
768 begin
769 if File = null then
770 Prj.Com.Fail ("Attempted to write on an invalid Text_File");
772 elsif not File.Out_File then
773 Prj.Com.Fail ("Attempted to write an in Text_File");
774 end if;
776 if File.Buffer_Len + S'Length > File.Buffer'Last then
777 -- Write buffer
778 Len := Write (File.FD, File.Buffer'Address, File.Buffer_Len);
780 if Len /= File.Buffer_Len then
781 Prj.Com.Fail ("Failed to write to an out Text_File");
782 end if;
784 File.Buffer_Len := 0;
785 end if;
787 File.Buffer (File.Buffer_Len + 1 .. File.Buffer_Len + S'Length) := S;
788 File.Buffer_Len := File.Buffer_Len + S'Length;
789 end Put;
791 --------------
792 -- Put_Line --
793 --------------
795 procedure Put_Line (File : Text_File; Line : String) is
796 L : String (1 .. Line'Length + 1);
797 begin
798 L (1 .. Line'Length) := Line;
799 L (L'Last) := ASCII.LF;
800 Put (File, L);
801 end Put_Line;
803 -------------------
804 -- Relative_Path --
805 -------------------
807 function Relative_Path (Pathname : String; To : String) return String is
808 function Ensure_Directory (Path : String) return String;
809 -- Returns Path with an added directory separator if needed
811 ----------------------
812 -- Ensure_Directory --
813 ----------------------
815 function Ensure_Directory (Path : String) return String is
816 begin
817 if Path'Length = 0
818 or else Path (Path'Last) = Directory_Separator
819 or else Path (Path'Last) = '/' -- on Windows check also for /
820 then
821 return Path;
822 else
823 return Path & Directory_Separator;
824 end if;
825 end Ensure_Directory;
827 -- Local variables
829 Dir_Sep_Map : constant Character_Mapping := To_Mapping ("\", "/");
831 P : String (1 .. Pathname'Length) := Pathname;
832 T : String (1 .. To'Length) := To;
834 Pi : Natural; -- common prefix ending
835 N : Natural := 0;
837 -- Start of processing for Relative_Path
839 begin
840 pragma Assert (Is_Absolute_Path (Pathname));
841 pragma Assert (Is_Absolute_Path (To));
843 -- Use canonical directory separator
845 Translate (Source => P, Mapping => Dir_Sep_Map);
846 Translate (Source => T, Mapping => Dir_Sep_Map);
848 -- First check for common prefix
850 Pi := 1;
851 while Pi < P'Last and then Pi < T'Last and then P (Pi) = T (Pi) loop
852 Pi := Pi + 1;
853 end loop;
855 -- Cut common prefix at a directory separator
857 while Pi > P'First and then P (Pi) /= '/' loop
858 Pi := Pi - 1;
859 end loop;
861 -- Count directory under prefix in P, these will be replaced by the
862 -- corresponding number of "..".
864 N := Count (T (Pi + 1 .. T'Last), "/");
866 if T (T'Last) /= '/' then
867 N := N + 1;
868 end if;
870 return N * "../" & Ensure_Directory (P (Pi + 1 .. P'Last));
871 end Relative_Path;
873 ---------------------------
874 -- Read_Source_Info_File --
875 ---------------------------
877 procedure Read_Source_Info_File (Tree : Project_Tree_Ref) is
878 File : Text_File;
879 Info : Source_Info_Iterator;
880 Proj : Name_Id;
882 procedure Report_Error;
884 ------------------
885 -- Report_Error --
886 ------------------
888 procedure Report_Error is
889 begin
890 Write_Line ("errors in source info file """ &
891 Tree.Source_Info_File_Name.all & '"');
892 Tree.Source_Info_File_Exists := False;
893 end Report_Error;
895 begin
896 Source_Info_Project_HTable.Reset;
897 Source_Info_Table.Init;
899 if Tree.Source_Info_File_Name = null then
900 Tree.Source_Info_File_Exists := False;
901 return;
902 end if;
904 Open (File, Tree.Source_Info_File_Name.all);
906 if not Is_Valid (File) then
907 if Opt.Verbose_Mode then
908 Write_Line ("source info file " & Tree.Source_Info_File_Name.all &
909 " does not exist");
910 end if;
912 Tree.Source_Info_File_Exists := False;
913 return;
914 end if;
916 Tree.Source_Info_File_Exists := True;
918 if Opt.Verbose_Mode then
919 Write_Line ("Reading source info file " &
920 Tree.Source_Info_File_Name.all);
921 end if;
923 Source_Loop :
924 while not End_Of_File (File) loop
925 Info := (new Source_Info_Data, 0);
926 Source_Info_Table.Increment_Last;
928 -- project name
929 Get_Line (File, Name_Buffer, Name_Len);
930 Proj := Name_Find;
931 Info.Info.Project := Proj;
932 Info.Next := Source_Info_Project_HTable.Get (Proj);
933 Source_Info_Project_HTable.Set (Proj, Source_Info_Table.Last);
935 if End_Of_File (File) then
936 Report_Error;
937 exit Source_Loop;
938 end if;
940 -- language name
941 Get_Line (File, Name_Buffer, Name_Len);
942 Info.Info.Language := Name_Find;
944 if End_Of_File (File) then
945 Report_Error;
946 exit Source_Loop;
947 end if;
949 -- kind
950 Get_Line (File, Name_Buffer, Name_Len);
951 Info.Info.Kind := Source_Kind'Value (Name_Buffer (1 .. Name_Len));
953 if End_Of_File (File) then
954 Report_Error;
955 exit Source_Loop;
956 end if;
958 -- display path name
959 Get_Line (File, Name_Buffer, Name_Len);
960 Info.Info.Display_Path_Name := Name_Find;
961 Info.Info.Path_Name := Info.Info.Display_Path_Name;
963 if End_Of_File (File) then
964 Report_Error;
965 exit Source_Loop;
966 end if;
968 -- optional fields
969 Option_Loop :
970 loop
971 Get_Line (File, Name_Buffer, Name_Len);
972 exit Option_Loop when Name_Len = 0;
974 if Name_Len <= 2 then
975 Report_Error;
976 exit Source_Loop;
978 else
979 if Name_Buffer (1 .. 2) = "P=" then
980 Name_Buffer (1 .. Name_Len - 2) :=
981 Name_Buffer (3 .. Name_Len);
982 Name_Len := Name_Len - 2;
983 Info.Info.Path_Name := Name_Find;
985 elsif Name_Buffer (1 .. 2) = "U=" then
986 Name_Buffer (1 .. Name_Len - 2) :=
987 Name_Buffer (3 .. Name_Len);
988 Name_Len := Name_Len - 2;
989 Info.Info.Unit_Name := Name_Find;
991 elsif Name_Buffer (1 .. 2) = "I=" then
992 Info.Info.Index := Int'Value (Name_Buffer (3 .. Name_Len));
994 elsif Name_Buffer (1 .. Name_Len) = "N=Y" then
995 Info.Info.Naming_Exception := Yes;
997 elsif Name_Buffer (1 .. Name_Len) = "N=I" then
998 Info.Info.Naming_Exception := Inherited;
1000 else
1001 Report_Error;
1002 exit Source_Loop;
1003 end if;
1004 end if;
1005 end loop Option_Loop;
1007 Source_Info_Table.Table (Source_Info_Table.Last) := Info;
1008 end loop Source_Loop;
1010 Close (File);
1012 exception
1013 when others =>
1014 Close (File);
1015 Report_Error;
1016 end Read_Source_Info_File;
1018 --------------------
1019 -- Source_Info_Of --
1020 --------------------
1022 function Source_Info_Of (Iter : Source_Info_Iterator) return Source_Info is
1023 begin
1024 return Iter.Info;
1025 end Source_Info_Of;
1027 --------------
1028 -- Value_Of --
1029 --------------
1031 function Value_Of
1032 (Variable : Variable_Value;
1033 Default : String) return String
1035 begin
1036 if Variable.Kind /= Single
1037 or else Variable.Default
1038 or else Variable.Value = No_Name
1039 then
1040 return Default;
1041 else
1042 return Get_Name_String (Variable.Value);
1043 end if;
1044 end Value_Of;
1046 function Value_Of
1047 (Index : Name_Id;
1048 In_Array : Array_Element_Id;
1049 Shared : Shared_Project_Tree_Data_Access) return Name_Id
1052 Current : Array_Element_Id;
1053 Element : Array_Element;
1054 Real_Index : Name_Id := Index;
1056 begin
1057 Current := In_Array;
1059 if Current = No_Array_Element then
1060 return No_Name;
1061 end if;
1063 Element := Shared.Array_Elements.Table (Current);
1065 if not Element.Index_Case_Sensitive then
1066 Get_Name_String (Index);
1067 To_Lower (Name_Buffer (1 .. Name_Len));
1068 Real_Index := Name_Find;
1069 end if;
1071 while Current /= No_Array_Element loop
1072 Element := Shared.Array_Elements.Table (Current);
1074 if Real_Index = Element.Index then
1075 exit when Element.Value.Kind /= Single;
1076 exit when Element.Value.Value = Empty_String;
1077 return Element.Value.Value;
1078 else
1079 Current := Element.Next;
1080 end if;
1081 end loop;
1083 return No_Name;
1084 end Value_Of;
1086 function Value_Of
1087 (Index : Name_Id;
1088 Src_Index : Int := 0;
1089 In_Array : Array_Element_Id;
1090 Shared : Shared_Project_Tree_Data_Access;
1091 Force_Lower_Case_Index : Boolean := False;
1092 Allow_Wildcards : Boolean := False) return Variable_Value
1094 Current : Array_Element_Id;
1095 Element : Array_Element;
1096 Real_Index_1 : Name_Id;
1097 Real_Index_2 : Name_Id;
1099 begin
1100 Current := In_Array;
1102 if Current = No_Array_Element then
1103 return Nil_Variable_Value;
1104 end if;
1106 Element := Shared.Array_Elements.Table (Current);
1108 Real_Index_1 := Index;
1110 if not Element.Index_Case_Sensitive or else Force_Lower_Case_Index then
1111 if Index /= All_Other_Names then
1112 Get_Name_String (Index);
1113 To_Lower (Name_Buffer (1 .. Name_Len));
1114 Real_Index_1 := Name_Find;
1115 end if;
1116 end if;
1118 while Current /= No_Array_Element loop
1119 Element := Shared.Array_Elements.Table (Current);
1120 Real_Index_2 := Element.Index;
1122 if not Element.Index_Case_Sensitive
1123 or else Force_Lower_Case_Index
1124 then
1125 if Element.Index /= All_Other_Names then
1126 Get_Name_String (Element.Index);
1127 To_Lower (Name_Buffer (1 .. Name_Len));
1128 Real_Index_2 := Name_Find;
1129 end if;
1130 end if;
1132 if Src_Index = Element.Src_Index and then
1133 (Real_Index_1 = Real_Index_2 or else
1134 (Real_Index_2 /= All_Other_Names and then
1135 Allow_Wildcards and then
1136 Match (Get_Name_String (Real_Index_1),
1137 Compile (Get_Name_String (Real_Index_2),
1138 Glob => True))))
1139 then
1140 return Element.Value;
1141 else
1142 Current := Element.Next;
1143 end if;
1144 end loop;
1146 return Nil_Variable_Value;
1147 end Value_Of;
1149 function Value_Of
1150 (Name : Name_Id;
1151 Index : Int := 0;
1152 Attribute_Or_Array_Name : Name_Id;
1153 In_Package : Package_Id;
1154 Shared : Shared_Project_Tree_Data_Access;
1155 Force_Lower_Case_Index : Boolean := False;
1156 Allow_Wildcards : Boolean := False) return Variable_Value
1158 The_Array : Array_Element_Id;
1159 The_Attribute : Variable_Value := Nil_Variable_Value;
1161 begin
1162 if In_Package /= No_Package then
1164 -- First, look if there is an array element that fits
1166 The_Array :=
1167 Value_Of
1168 (Name => Attribute_Or_Array_Name,
1169 In_Arrays => Shared.Packages.Table (In_Package).Decl.Arrays,
1170 Shared => Shared);
1171 The_Attribute :=
1172 Value_Of
1173 (Index => Name,
1174 Src_Index => Index,
1175 In_Array => The_Array,
1176 Shared => Shared,
1177 Force_Lower_Case_Index => Force_Lower_Case_Index,
1178 Allow_Wildcards => Allow_Wildcards);
1180 -- If there is no array element, look for a variable
1182 if The_Attribute = Nil_Variable_Value then
1183 The_Attribute :=
1184 Value_Of
1185 (Variable_Name => Attribute_Or_Array_Name,
1186 In_Variables => Shared.Packages.Table
1187 (In_Package).Decl.Attributes,
1188 Shared => Shared);
1189 end if;
1190 end if;
1192 return The_Attribute;
1193 end Value_Of;
1195 function Value_Of
1196 (Index : Name_Id;
1197 In_Array : Name_Id;
1198 In_Arrays : Array_Id;
1199 Shared : Shared_Project_Tree_Data_Access) return Name_Id
1201 Current : Array_Id;
1202 The_Array : Array_Data;
1204 begin
1205 Current := In_Arrays;
1206 while Current /= No_Array loop
1207 The_Array := Shared.Arrays.Table (Current);
1208 if The_Array.Name = In_Array then
1209 return Value_Of
1210 (Index, In_Array => The_Array.Value, Shared => Shared);
1211 else
1212 Current := The_Array.Next;
1213 end if;
1214 end loop;
1216 return No_Name;
1217 end Value_Of;
1219 function Value_Of
1220 (Name : Name_Id;
1221 In_Arrays : Array_Id;
1222 Shared : Shared_Project_Tree_Data_Access) return Array_Element_Id
1224 Current : Array_Id;
1225 The_Array : Array_Data;
1227 begin
1228 Current := In_Arrays;
1229 while Current /= No_Array loop
1230 The_Array := Shared.Arrays.Table (Current);
1232 if The_Array.Name = Name then
1233 return The_Array.Value;
1234 else
1235 Current := The_Array.Next;
1236 end if;
1237 end loop;
1239 return No_Array_Element;
1240 end Value_Of;
1242 function Value_Of
1243 (Name : Name_Id;
1244 In_Packages : Package_Id;
1245 Shared : Shared_Project_Tree_Data_Access) return Package_Id
1247 Current : Package_Id;
1248 The_Package : Package_Element;
1250 begin
1251 Current := In_Packages;
1252 while Current /= No_Package loop
1253 The_Package := Shared.Packages.Table (Current);
1254 exit when The_Package.Name /= No_Name
1255 and then The_Package.Name = Name;
1256 Current := The_Package.Next;
1257 end loop;
1259 return Current;
1260 end Value_Of;
1262 function Value_Of
1263 (Variable_Name : Name_Id;
1264 In_Variables : Variable_Id;
1265 Shared : Shared_Project_Tree_Data_Access) return Variable_Value
1267 Current : Variable_Id;
1268 The_Variable : Variable;
1270 begin
1271 Current := In_Variables;
1272 while Current /= No_Variable loop
1273 The_Variable := Shared.Variable_Elements.Table (Current);
1275 if Variable_Name = The_Variable.Name then
1276 return The_Variable.Value;
1277 else
1278 Current := The_Variable.Next;
1279 end if;
1280 end loop;
1282 return Nil_Variable_Value;
1283 end Value_Of;
1285 ----------------------------
1286 -- Write_Source_Info_File --
1287 ----------------------------
1289 procedure Write_Source_Info_File (Tree : Project_Tree_Ref) is
1290 Iter : Source_Iterator := For_Each_Source (Tree);
1291 Source : Prj.Source_Id;
1292 File : Text_File;
1294 begin
1295 if Opt.Verbose_Mode then
1296 Write_Line ("Writing new source info file " &
1297 Tree.Source_Info_File_Name.all);
1298 end if;
1300 Create (File, Tree.Source_Info_File_Name.all);
1302 if not Is_Valid (File) then
1303 Write_Line ("warning: unable to create source info file """ &
1304 Tree.Source_Info_File_Name.all & '"');
1305 return;
1306 end if;
1308 loop
1309 Source := Element (Iter);
1310 exit when Source = No_Source;
1312 if not Source.Locally_Removed and then
1313 Source.Replaced_By = No_Source
1314 then
1315 -- Project name
1317 Put_Line (File, Get_Name_String (Source.Project.Name));
1319 -- Language name
1321 Put_Line (File, Get_Name_String (Source.Language.Name));
1323 -- Kind
1325 Put_Line (File, Source.Kind'Img);
1327 -- Display path name
1329 Put_Line (File, Get_Name_String (Source.Path.Display_Name));
1331 -- Optional lines:
1333 -- Path name (P=)
1335 if Source.Path.Name /= Source.Path.Display_Name then
1336 Put (File, "P=");
1337 Put_Line (File, Get_Name_String (Source.Path.Name));
1338 end if;
1340 -- Unit name (U=)
1342 if Source.Unit /= No_Unit_Index then
1343 Put (File, "U=");
1344 Put_Line (File, Get_Name_String (Source.Unit.Name));
1345 end if;
1347 -- Multi-source index (I=)
1349 if Source.Index /= 0 then
1350 Put (File, "I=");
1351 Put_Line (File, Source.Index'Img);
1352 end if;
1354 -- Naming exception ("N=T");
1356 if Source.Naming_Exception = Yes then
1357 Put_Line (File, "N=Y");
1359 elsif Source.Naming_Exception = Inherited then
1360 Put_Line (File, "N=I");
1361 end if;
1363 -- Empty line to indicate end of info on this source
1365 Put_Line (File, "");
1366 end if;
1368 Next (Iter);
1369 end loop;
1371 Close (File);
1372 end Write_Source_Info_File;
1374 ---------------
1375 -- Write_Str --
1376 ---------------
1378 procedure Write_Str
1379 (S : String;
1380 Max_Length : Positive;
1381 Separator : Character)
1383 First : Positive := S'First;
1384 Last : Natural := S'Last;
1386 begin
1387 -- Nothing to do for empty strings
1389 if S'Length > 0 then
1391 -- Start on a new line if current line is already longer than
1392 -- Max_Length.
1394 if Positive (Column) >= Max_Length then
1395 Write_Eol;
1396 end if;
1398 -- If length of remainder is longer than Max_Length, we need to
1399 -- cut the remainder in several lines.
1401 while Positive (Column) + S'Last - First > Max_Length loop
1403 -- Try the maximum length possible
1405 Last := First + Max_Length - Positive (Column);
1407 -- Look for last Separator in the line
1409 while Last >= First and then S (Last) /= Separator loop
1410 Last := Last - 1;
1411 end loop;
1413 -- If we do not find a separator, output maximum length possible
1415 if Last < First then
1416 Last := First + Max_Length - Positive (Column);
1417 end if;
1419 Write_Line (S (First .. Last));
1421 -- Set the beginning of the new remainder
1423 First := Last + 1;
1424 end loop;
1426 -- What is left goes to the buffer, without EOL
1428 Write_Str (S (First .. S'Last));
1429 end if;
1430 end Write_Str;
1432 end Prj.Util;