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 ------------------------------------------------------------------------------
26 with Ada.Unchecked_Deallocation;
28 with GNAT.Case_Util; use GNAT.Case_Util;
30 with Osint; use Osint;
31 with Output; use Output;
33 with Snames; use Snames;
34 with Targparm; use Targparm;
36 package body Prj.Util is
38 procedure Free is new Ada.Unchecked_Deallocation
39 (Text_File_Data, Text_File);
45 procedure Close (File : in out Text_File) is
48 Prj.Com.Fail ("Close attempted on an invalid Text_File");
51 -- Close file, no need to test status, since this is a file that we
52 -- read, and the file was read successfully before we closed it.
63 (This : in out Name_List_Index;
64 In_Tree : Project_Tree_Ref)
66 Old_Current : Name_List_Index;
67 New_Current : Name_List_Index;
70 if This /= No_Name_List then
72 Name_List_Table.Increment_Last (In_Tree.Name_Lists);
73 New_Current := Name_List_Table.Last (In_Tree.Name_Lists);
75 In_Tree.Name_Lists.Table (New_Current) :=
76 (In_Tree.Name_Lists.Table (Old_Current).Name, No_Name_List);
79 Old_Current := In_Tree.Name_Lists.Table (Old_Current).Next;
80 exit when Old_Current = No_Name_List;
81 In_Tree.Name_Lists.Table (New_Current).Next := New_Current + 1;
82 Name_List_Table.Increment_Last (In_Tree.Name_Lists);
83 New_Current := New_Current + 1;
84 In_Tree.Name_Lists.Table (New_Current) :=
85 (In_Tree.Name_Lists.Table (Old_Current).Name, No_Name_List);
94 function End_Of_File (File : Text_File) return Boolean is
97 Prj.Com.Fail ("End_Of_File attempted on an invalid Text_File");
100 return File.End_Of_File_Reached;
107 function Executable_Of
108 (Project : Project_Id;
109 In_Tree : Project_Tree_Ref;
110 Main : File_Name_Type;
112 Ada_Main : Boolean := True;
113 Language : String := "") return File_Name_Type
115 pragma Assert (Project /= No_Project);
117 The_Packages : constant Package_Id := Project.Decl.Packages;
119 Builder_Package : constant Prj.Package_Id :=
121 (Name => Name_Builder,
122 In_Packages => The_Packages,
125 Executable : Variable_Value :=
127 (Name => Name_Id (Main),
129 Attribute_Or_Array_Name => Name_Executable,
130 In_Package => Builder_Package,
133 Executable_Suffix : Variable_Value := Nil_Variable_Value;
135 Executable_Suffix_Name : Name_Id := No_Name;
137 Naming : constant Naming_Data := Project.Naming;
139 Spec_Suffix : Name_Id := No_Name;
140 Body_Suffix : Name_Id := No_Name;
142 Spec_Suffix_Length : Natural := 0;
143 Body_Suffix_Length : Natural := 0;
145 procedure Get_Suffixes
148 -- Get the non empty suffixes in variables Spec_Suffix and Body_Suffix
154 procedure Get_Suffixes
159 if B_Suffix'Length > 0 then
160 Name_Len := B_Suffix'Length;
161 Name_Buffer (1 .. Name_Len) := B_Suffix;
162 Body_Suffix := Name_Find;
163 Body_Suffix_Length := B_Suffix'Length;
166 if S_Suffix'Length > 0 then
167 Name_Len := S_Suffix'Length;
168 Name_Buffer (1 .. Name_Len) := S_Suffix;
169 Spec_Suffix := Name_Find;
170 Spec_Suffix_Length := S_Suffix'Length;
174 -- Start of processing for Executable_Of
179 (B_Suffix => Body_Suffix_Of (In_Tree, "ada", Naming),
180 S_Suffix => Spec_Suffix_Of (In_Tree, "ada", Naming));
182 elsif Language /= "" then
184 (B_Suffix => Body_Suffix_Of (In_Tree, Language, Naming),
185 S_Suffix => Spec_Suffix_Of (In_Tree, Language, Naming));
188 if Builder_Package /= No_Package then
189 if Get_Mode = Multi_Language then
190 Executable_Suffix_Name := Project.Config.Executable_Suffix;
193 Executable_Suffix := Prj.Util.Value_Of
194 (Variable_Name => Name_Executable_Suffix,
195 In_Variables => In_Tree.Packages.Table
196 (Builder_Package).Decl.Attributes,
199 if Executable_Suffix /= Nil_Variable_Value
200 and then not Executable_Suffix.Default
202 Executable_Suffix_Name := Executable_Suffix.Value;
206 if Executable = Nil_Variable_Value and Ada_Main then
207 Get_Name_String (Main);
209 -- Try as index the name minus the implementation suffix or minus
210 -- the specification suffix.
213 Name : constant String (1 .. Name_Len) :=
214 Name_Buffer (1 .. Name_Len);
215 Last : Positive := Name_Len;
217 Truncated : Boolean := False;
220 if Last > Natural (Length_Of_Name (Body_Suffix))
221 and then Name (Last - Body_Suffix_Length + 1 .. Last) =
222 Get_Name_String (Body_Suffix)
225 Last := Last - Body_Suffix_Length;
229 and then Last > Spec_Suffix_Length
230 and then Name (Last - Spec_Suffix_Length + 1 .. Last) =
231 Get_Name_String (Spec_Suffix)
234 Last := Last - Spec_Suffix_Length;
239 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
244 Attribute_Or_Array_Name => Name_Executable,
245 In_Package => Builder_Package,
251 -- If we have found an Executable attribute, return its value,
252 -- possibly suffixed by the executable suffix.
254 if Executable /= Nil_Variable_Value
255 and then Executable.Value /= Empty_Name
257 -- Get the executable name. If Executable_Suffix is defined,
258 -- make sure that it will be the extension of the executable.
261 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
262 Result : File_Name_Type;
265 if Executable_Suffix_Name /= No_Name then
266 Executable_Extension_On_Target := Executable_Suffix_Name;
269 Result := Executable_Name (File_Name_Type (Executable.Value));
270 Executable_Extension_On_Target := Saved_EEOT;
276 Get_Name_String (Main);
278 -- If there is a body suffix or a spec suffix, remove this suffix,
279 -- otherwise remove any suffix ('.' followed by other characters), if
282 if Body_Suffix /= No_Name
283 and then Name_Len > Body_Suffix_Length
284 and then Name_Buffer (Name_Len - Body_Suffix_Length + 1 .. Name_Len) =
285 Get_Name_String (Body_Suffix)
287 -- Found the body termination, remove it
289 Name_Len := Name_Len - Body_Suffix_Length;
291 elsif Spec_Suffix /= No_Name
292 and then Name_Len > Spec_Suffix_Length
294 Name_Buffer (Name_Len - Spec_Suffix_Length + 1 .. Name_Len) =
295 Get_Name_String (Spec_Suffix)
297 -- Found the spec termination, remove it
299 Name_Len := Name_Len - Spec_Suffix_Length;
302 -- Remove any suffix, if there is one
304 Get_Name_String (Strip_Suffix (Main));
307 if Executable_Suffix /= Nil_Variable_Value
308 and then not Executable_Suffix.Default
310 -- If attribute Executable_Suffix is specified, add this suffix
313 Suffix : constant String :=
314 Get_Name_String (Executable_Suffix.Value);
316 Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix;
317 Name_Len := Name_Len + Suffix'Length;
322 -- Get the executable name. If Executable_Suffix is defined in the
323 -- configuration, make sure that it will be the extension of the
327 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
328 Result : File_Name_Type;
331 if Project.Config.Executable_Suffix /= No_Name then
332 Executable_Extension_On_Target :=
333 Project.Config.Executable_Suffix;
336 Result := Executable_Name (Name_Find);
337 Executable_Extension_On_Target := Saved_EEOT;
362 if File.Cursor = File.Buffer_Len then
366 A => File.Buffer'Address,
367 N => File.Buffer'Length);
369 if File.Buffer_Len = 0 then
370 File.End_Of_File_Reached := True;
377 File.Cursor := File.Cursor + 1;
381 -- Start of processing for Get_Line
385 Prj.Com.Fail ("Get_Line attempted on an invalid Text_File");
388 Last := Line'First - 1;
390 if not File.End_Of_File_Reached then
392 C := File.Buffer (File.Cursor);
393 exit when C = ASCII.CR or else C = ASCII.LF;
398 if File.End_Of_File_Reached then
402 exit when Last = Line'Last;
405 if C = ASCII.CR or else C = ASCII.LF then
408 if File.End_Of_File_Reached then
414 and then File.Buffer (File.Cursor) = ASCII.LF
425 function Is_Valid (File : Text_File) return Boolean is
434 procedure Open (File : out Text_File; Name : String) is
435 FD : File_Descriptor;
436 File_Name : String (1 .. Name'Length + 1);
439 File_Name (1 .. Name'Length) := Name;
440 File_Name (File_Name'Last) := ASCII.NUL;
441 FD := Open_Read (Name => File_Name'Address,
442 Fmode => GNAT.OS_Lib.Text);
444 if FD = Invalid_FD then
448 File := new Text_File_Data;
452 A => File.Buffer'Address,
453 N => File.Buffer'Length);
455 if File.Buffer_Len = 0 then
456 File.End_Of_File_Reached := True;
468 (Into_List : in out Name_List_Index;
469 From_List : String_List_Id;
470 In_Tree : Project_Tree_Ref;
471 Lower_Case : Boolean := False)
473 Current_Name : Name_List_Index;
474 List : String_List_Id;
475 Element : String_Element;
476 Last : Name_List_Index :=
477 Name_List_Table.Last (In_Tree.Name_Lists);
481 Current_Name := Into_List;
482 while Current_Name /= No_Name_List
483 and then In_Tree.Name_Lists.Table (Current_Name).Next /= No_Name_List
485 Current_Name := In_Tree.Name_Lists.Table (Current_Name).Next;
489 while List /= Nil_String loop
490 Element := In_Tree.String_Elements.Table (List);
491 Value := Element.Value;
494 Get_Name_String (Value);
495 To_Lower (Name_Buffer (1 .. Name_Len));
499 Name_List_Table.Append
500 (In_Tree.Name_Lists, (Name => Value, Next => No_Name_List));
504 if Current_Name = No_Name_List then
508 In_Tree.Name_Lists.Table (Current_Name).Next := Last;
511 Current_Name := Last;
513 List := Element.Next;
522 (Variable : Variable_Value;
523 Default : String) return String
526 if Variable.Kind /= Single
527 or else Variable.Default
528 or else Variable.Value = No_Name
532 return Get_Name_String (Variable.Value);
538 In_Array : Array_Element_Id;
539 In_Tree : Project_Tree_Ref) return Name_Id
541 Current : Array_Element_Id;
542 Element : Array_Element;
543 Real_Index : Name_Id := Index;
548 if Current = No_Array_Element then
552 Element := In_Tree.Array_Elements.Table (Current);
554 if not Element.Index_Case_Sensitive then
555 Get_Name_String (Index);
556 To_Lower (Name_Buffer (1 .. Name_Len));
557 Real_Index := Name_Find;
560 while Current /= No_Array_Element loop
561 Element := In_Tree.Array_Elements.Table (Current);
563 if Real_Index = Element.Index then
564 exit when Element.Value.Kind /= Single;
565 exit when Element.Value.Value = Empty_String;
566 return Element.Value.Value;
568 Current := Element.Next;
577 Src_Index : Int := 0;
578 In_Array : Array_Element_Id;
579 In_Tree : Project_Tree_Ref;
580 Force_Lower_Case_Index : Boolean := False) return Variable_Value
582 Current : Array_Element_Id;
583 Element : Array_Element;
584 Real_Index_1 : Name_Id;
585 Real_Index_2 : Name_Id;
590 if Current = No_Array_Element then
591 return Nil_Variable_Value;
594 Element := In_Tree.Array_Elements.Table (Current);
596 Real_Index_1 := Index;
598 if not Element.Index_Case_Sensitive or Force_Lower_Case_Index then
599 if Index /= All_Other_Names then
600 Get_Name_String (Index);
601 To_Lower (Name_Buffer (1 .. Name_Len));
602 Real_Index_1 := Name_Find;
606 while Current /= No_Array_Element loop
607 Element := In_Tree.Array_Elements.Table (Current);
608 Real_Index_2 := Element.Index;
610 if not Element.Index_Case_Sensitive or Force_Lower_Case_Index then
611 if Element.Index /= All_Other_Names then
612 Get_Name_String (Element.Index);
613 To_Lower (Name_Buffer (1 .. Name_Len));
614 Real_Index_2 := Name_Find;
618 if Real_Index_1 = Real_Index_2 and then
619 Src_Index = Element.Src_Index
621 return Element.Value;
623 Current := Element.Next;
627 return Nil_Variable_Value;
633 Attribute_Or_Array_Name : Name_Id;
634 In_Package : Package_Id;
635 In_Tree : Project_Tree_Ref;
636 Force_Lower_Case_Index : Boolean := False) return Variable_Value
638 The_Array : Array_Element_Id;
639 The_Attribute : Variable_Value := Nil_Variable_Value;
642 if In_Package /= No_Package then
644 -- First, look if there is an array element that fits
648 (Name => Attribute_Or_Array_Name,
649 In_Arrays => In_Tree.Packages.Table (In_Package).Decl.Arrays,
655 In_Array => The_Array,
657 Force_Lower_Case_Index => Force_Lower_Case_Index);
659 -- If there is no array element, look for a variable
661 if The_Attribute = Nil_Variable_Value then
664 (Variable_Name => Attribute_Or_Array_Name,
665 In_Variables => In_Tree.Packages.Table
666 (In_Package).Decl.Attributes,
671 return The_Attribute;
677 In_Arrays : Array_Id;
678 In_Tree : Project_Tree_Ref) return Name_Id
681 The_Array : Array_Data;
684 Current := In_Arrays;
685 while Current /= No_Array loop
686 The_Array := In_Tree.Arrays.Table (Current);
687 if The_Array.Name = In_Array then
689 (Index, In_Array => The_Array.Value, In_Tree => In_Tree);
691 Current := The_Array.Next;
700 In_Arrays : Array_Id;
701 In_Tree : Project_Tree_Ref) return Array_Element_Id
704 The_Array : Array_Data;
707 Current := In_Arrays;
708 while Current /= No_Array loop
709 The_Array := In_Tree.Arrays.Table (Current);
711 if The_Array.Name = Name then
712 return The_Array.Value;
714 Current := The_Array.Next;
718 return No_Array_Element;
723 In_Packages : Package_Id;
724 In_Tree : Project_Tree_Ref) return Package_Id
726 Current : Package_Id;
727 The_Package : Package_Element;
730 Current := In_Packages;
731 while Current /= No_Package loop
732 The_Package := In_Tree.Packages.Table (Current);
733 exit when The_Package.Name /= No_Name
734 and then The_Package.Name = Name;
735 Current := The_Package.Next;
742 (Variable_Name : Name_Id;
743 In_Variables : Variable_Id;
744 In_Tree : Project_Tree_Ref) return Variable_Value
746 Current : Variable_Id;
747 The_Variable : Variable;
750 Current := In_Variables;
751 while Current /= No_Variable loop
753 In_Tree.Variable_Elements.Table (Current);
755 if Variable_Name = The_Variable.Name then
756 return The_Variable.Value;
758 Current := The_Variable.Next;
762 return Nil_Variable_Value;
771 Max_Length : Positive;
772 Separator : Character)
774 First : Positive := S'First;
775 Last : Natural := S'Last;
778 -- Nothing to do for empty strings
782 -- Start on a new line if current line is already longer than
785 if Positive (Column) >= Max_Length then
789 -- If length of remainder is longer than Max_Length, we need to
790 -- cut the remainder in several lines.
792 while Positive (Column) + S'Last - First > Max_Length loop
794 -- Try the maximum length possible
796 Last := First + Max_Length - Positive (Column);
798 -- Look for last Separator in the line
800 while Last >= First and then S (Last) /= Separator loop
804 -- If we do not find a separator, we output the maximum length
808 Last := First + Max_Length - Positive (Column);
811 Write_Line (S (First .. Last));
813 -- Set the beginning of the new remainder
818 -- What is left goes to the buffer, without EOL
820 Write_Str (S (First .. S'Last));