845b546ee8baaa6301c08d5e48ab6af04bf3186f
[gcc.git] / gcc / ada / prj-util.adb
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-2006, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
21 -- --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 -- --
25 ------------------------------------------------------------------------------
26
27 with Ada.Unchecked_Deallocation;
28
29 with GNAT.Case_Util; use GNAT.Case_Util;
30
31 with Namet; use Namet;
32 with Osint; use Osint;
33 with Output; use Output;
34 with Prj.Com;
35 with Snames; use Snames;
36 with Targparm; use Targparm;
37
38 package body Prj.Util is
39
40 procedure Free is new Ada.Unchecked_Deallocation
41 (Text_File_Data, Text_File);
42
43 -----------
44 -- Close --
45 -----------
46
47 procedure Close (File : in out Text_File) is
48 begin
49 if File = null then
50 Prj.Com.Fail ("Close attempted on an invalid Text_File");
51 end if;
52
53 -- Close file, no need to test status, since this is a file that we
54 -- read, and the file was read successfully before we closed it.
55
56 Close (File.FD);
57 Free (File);
58 end Close;
59
60 -----------------
61 -- End_Of_File --
62 -----------------
63
64 function End_Of_File (File : Text_File) return Boolean is
65 begin
66 if File = null then
67 Prj.Com.Fail ("End_Of_File attempted on an invalid Text_File");
68 end if;
69
70 return File.End_Of_File_Reached;
71 end End_Of_File;
72
73 -------------------
74 -- Executable_Of --
75 -------------------
76
77 function Executable_Of
78 (Project : Project_Id;
79 In_Tree : Project_Tree_Ref;
80 Main : Name_Id;
81 Index : Int;
82 Ada_Main : Boolean := True) return Name_Id
83 is
84 pragma Assert (Project /= No_Project);
85
86 The_Packages : constant Package_Id :=
87 In_Tree.Projects.Table (Project).Decl.Packages;
88
89 Builder_Package : constant Prj.Package_Id :=
90 Prj.Util.Value_Of
91 (Name => Name_Builder,
92 In_Packages => The_Packages,
93 In_Tree => In_Tree);
94
95 Executable : Variable_Value :=
96 Prj.Util.Value_Of
97 (Name => Main,
98 Index => Index,
99 Attribute_Or_Array_Name => Name_Executable,
100 In_Package => Builder_Package,
101 In_Tree => In_Tree);
102
103 Executable_Suffix : Variable_Value := Nil_Variable_Value;
104
105 Body_Append : constant String := Get_Name_String
106 (In_Tree.Projects.Table
107 (Project).
108 Naming.Ada_Body_Suffix);
109
110 Spec_Append : constant String := Get_Name_String
111 (In_Tree.Projects.Table
112 (Project).
113 Naming.Ada_Spec_Suffix);
114
115 begin
116 if Builder_Package /= No_Package then
117 Executable_Suffix := Prj.Util.Value_Of
118 (Variable_Name => Name_Executable_Suffix,
119 In_Variables => In_Tree.Packages.Table
120 (Builder_Package).Decl.Attributes,
121 In_Tree => In_Tree);
122
123 if Executable = Nil_Variable_Value and Ada_Main then
124 Get_Name_String (Main);
125
126 -- Try as index the name minus the implementation suffix or minus
127 -- the specification suffix.
128
129 declare
130 Name : constant String (1 .. Name_Len) :=
131 Name_Buffer (1 .. Name_Len);
132 Last : Positive := Name_Len;
133
134 Naming : constant Naming_Data :=
135 In_Tree.Projects.Table (Project).Naming;
136
137 Spec_Suffix : constant String :=
138 Get_Name_String (Naming.Ada_Spec_Suffix);
139 Body_Suffix : constant String :=
140 Get_Name_String (Naming.Ada_Body_Suffix);
141
142 Truncated : Boolean := False;
143
144 begin
145 if Last > Body_Suffix'Length
146 and then Name (Last - Body_Suffix'Length + 1 .. Last) =
147 Body_Suffix
148 then
149 Truncated := True;
150 Last := Last - Body_Suffix'Length;
151 end if;
152
153 if not Truncated
154 and then Last > Spec_Suffix'Length
155 and then Name (Last - Spec_Suffix'Length + 1 .. Last) =
156 Spec_Suffix
157 then
158 Truncated := True;
159 Last := Last - Spec_Suffix'Length;
160 end if;
161
162 if Truncated then
163 Name_Len := Last;
164 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
165 Executable :=
166 Prj.Util.Value_Of
167 (Name => Name_Find,
168 Index => 0,
169 Attribute_Or_Array_Name => Name_Executable,
170 In_Package => Builder_Package,
171 In_Tree => In_Tree);
172 end if;
173 end;
174 end if;
175
176 -- If we have found an Executable attribute, return its value,
177 -- possibly suffixed by the executable suffix.
178
179 if Executable /= Nil_Variable_Value
180 and then Executable.Value /= Empty_Name
181 then
182 -- Get the executable name. If Executable_Suffix is defined,
183 -- make sure that it will be the extension of the executable.
184
185 declare
186 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
187 Result : Name_Id;
188
189 begin
190 if Executable_Suffix /= Nil_Variable_Value
191 and then not Executable_Suffix.Default
192 then
193 Executable_Extension_On_Target := Executable_Suffix.Value;
194 end if;
195
196 Result := Executable_Name (Executable.Value);
197 Executable_Extension_On_Target := Saved_EEOT;
198 return Result;
199 end;
200 end if;
201 end if;
202
203 Get_Name_String (Main);
204
205 -- If there is a body suffix or a spec suffix, remove this suffix,
206 -- otherwise remove any suffix ('.' followed by other characters), if
207 -- there is one.
208
209 if Ada_Main and then Name_Len > Body_Append'Length
210 and then Name_Buffer (Name_Len - Body_Append'Length + 1 .. Name_Len) =
211 Body_Append
212 then
213 -- Found the body termination, remove it
214
215 Name_Len := Name_Len - Body_Append'Length;
216
217 elsif Ada_Main and then Name_Len > Spec_Append'Length
218 and then Name_Buffer (Name_Len - Spec_Append'Length + 1 .. Name_Len) =
219 Spec_Append
220 then
221 -- Found the spec termination, remove it
222
223 Name_Len := Name_Len - Spec_Append'Length;
224
225 else
226 -- Remove any suffix, if there is one
227
228 Get_Name_String (Strip_Suffix (Main));
229 end if;
230
231 if Executable_Suffix /= Nil_Variable_Value
232 and then not Executable_Suffix.Default
233 then
234 -- If attribute Executable_Suffix is specified, add this suffix
235
236 declare
237 Suffix : constant String :=
238 Get_Name_String (Executable_Suffix.Value);
239 begin
240 Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix;
241 Name_Len := Name_Len + Suffix'Length;
242 return Name_Find;
243 end;
244
245 else
246 -- Otherwise, add the standard suffix for the platform, if any
247
248 return Executable_Name (Name_Find);
249 end if;
250 end Executable_Of;
251
252 --------------
253 -- Get_Line --
254 --------------
255
256 procedure Get_Line
257 (File : Text_File;
258 Line : out String;
259 Last : out Natural)
260 is
261 C : Character;
262
263 procedure Advance;
264
265 -------------
266 -- Advance --
267 -------------
268
269 procedure Advance is
270 begin
271 if File.Cursor = File.Buffer_Len then
272 File.Buffer_Len :=
273 Read
274 (FD => File.FD,
275 A => File.Buffer'Address,
276 N => File.Buffer'Length);
277
278 if File.Buffer_Len = 0 then
279 File.End_Of_File_Reached := True;
280 return;
281 else
282 File.Cursor := 1;
283 end if;
284
285 else
286 File.Cursor := File.Cursor + 1;
287 end if;
288 end Advance;
289
290 -- Start of processing for Get_Line
291
292 begin
293 if File = null then
294 Prj.Com.Fail ("Get_Line attempted on an invalid Text_File");
295 end if;
296
297 Last := Line'First - 1;
298
299 if not File.End_Of_File_Reached then
300 loop
301 C := File.Buffer (File.Cursor);
302 exit when C = ASCII.CR or else C = ASCII.LF;
303 Last := Last + 1;
304 Line (Last) := C;
305 Advance;
306
307 if File.End_Of_File_Reached then
308 return;
309 end if;
310
311 exit when Last = Line'Last;
312 end loop;
313
314 if C = ASCII.CR or else C = ASCII.LF then
315 Advance;
316
317 if File.End_Of_File_Reached then
318 return;
319 end if;
320 end if;
321
322 if C = ASCII.CR
323 and then File.Buffer (File.Cursor) = ASCII.LF
324 then
325 Advance;
326 end if;
327 end if;
328 end Get_Line;
329
330 --------------
331 -- Is_Valid --
332 --------------
333
334 function Is_Valid (File : Text_File) return Boolean is
335 begin
336 return File /= null;
337 end Is_Valid;
338
339 ----------
340 -- Open --
341 ----------
342
343 procedure Open (File : out Text_File; Name : String) is
344 FD : File_Descriptor;
345 File_Name : String (1 .. Name'Length + 1);
346
347 begin
348 File_Name (1 .. Name'Length) := Name;
349 File_Name (File_Name'Last) := ASCII.NUL;
350 FD := Open_Read (Name => File_Name'Address,
351 Fmode => GNAT.OS_Lib.Text);
352 if FD = Invalid_FD then
353 File := null;
354 else
355 File := new Text_File_Data;
356 File.FD := FD;
357 File.Buffer_Len :=
358 Read (FD => FD,
359 A => File.Buffer'Address,
360 N => File.Buffer'Length);
361
362 if File.Buffer_Len = 0 then
363 File.End_Of_File_Reached := True;
364 else
365 File.Cursor := 1;
366 end if;
367 end if;
368 end Open;
369
370 --------------
371 -- Value_Of --
372 --------------
373
374 function Value_Of
375 (Variable : Variable_Value;
376 Default : String) return String
377 is
378 begin
379 if Variable.Kind /= Single
380 or else Variable.Default
381 or else Variable.Value = No_Name
382 then
383 return Default;
384 else
385 return Get_Name_String (Variable.Value);
386 end if;
387 end Value_Of;
388
389 function Value_Of
390 (Index : Name_Id;
391 In_Array : Array_Element_Id;
392 In_Tree : Project_Tree_Ref) return Name_Id
393 is
394 Current : Array_Element_Id := In_Array;
395 Element : Array_Element;
396 Real_Index : Name_Id := Index;
397
398 begin
399 if Current = No_Array_Element then
400 return No_Name;
401 end if;
402
403 Element := In_Tree.Array_Elements.Table (Current);
404
405 if not Element.Index_Case_Sensitive then
406 Get_Name_String (Index);
407 To_Lower (Name_Buffer (1 .. Name_Len));
408 Real_Index := Name_Find;
409 end if;
410
411 while Current /= No_Array_Element loop
412 Element := In_Tree.Array_Elements.Table (Current);
413
414 if Real_Index = Element.Index then
415 exit when Element.Value.Kind /= Single;
416 exit when Element.Value.Value = Empty_String;
417 return Element.Value.Value;
418 else
419 Current := Element.Next;
420 end if;
421 end loop;
422
423 return No_Name;
424 end Value_Of;
425
426 function Value_Of
427 (Index : Name_Id;
428 Src_Index : Int := 0;
429 In_Array : Array_Element_Id;
430 In_Tree : Project_Tree_Ref) return Variable_Value
431 is
432 Current : Array_Element_Id := In_Array;
433 Element : Array_Element;
434 Real_Index : Name_Id := Index;
435
436 begin
437 if Current = No_Array_Element then
438 return Nil_Variable_Value;
439 end if;
440
441 Element := In_Tree.Array_Elements.Table (Current);
442
443 if not Element.Index_Case_Sensitive then
444 Get_Name_String (Index);
445 To_Lower (Name_Buffer (1 .. Name_Len));
446 Real_Index := Name_Find;
447 end if;
448
449 while Current /= No_Array_Element loop
450 Element := In_Tree.Array_Elements.Table (Current);
451
452 if Real_Index = Element.Index and then
453 Src_Index = Element.Src_Index
454 then
455 return Element.Value;
456 else
457 Current := Element.Next;
458 end if;
459 end loop;
460
461 return Nil_Variable_Value;
462 end Value_Of;
463
464 function Value_Of
465 (Name : Name_Id;
466 Index : Int := 0;
467 Attribute_Or_Array_Name : Name_Id;
468 In_Package : Package_Id;
469 In_Tree : Project_Tree_Ref) return Variable_Value
470 is
471 The_Array : Array_Element_Id;
472 The_Attribute : Variable_Value := Nil_Variable_Value;
473
474 begin
475 if In_Package /= No_Package then
476
477 -- First, look if there is an array element that fits
478
479 The_Array :=
480 Value_Of
481 (Name => Attribute_Or_Array_Name,
482 In_Arrays => In_Tree.Packages.Table (In_Package).Decl.Arrays,
483 In_Tree => In_Tree);
484 The_Attribute :=
485 Value_Of
486 (Index => Name,
487 Src_Index => Index,
488 In_Array => The_Array,
489 In_Tree => In_Tree);
490
491 -- If there is no array element, look for a variable
492
493 if The_Attribute = Nil_Variable_Value then
494 The_Attribute :=
495 Value_Of
496 (Variable_Name => Attribute_Or_Array_Name,
497 In_Variables => In_Tree.Packages.Table
498 (In_Package).Decl.Attributes,
499 In_Tree => In_Tree);
500 end if;
501 end if;
502
503 return The_Attribute;
504 end Value_Of;
505
506 function Value_Of
507 (Index : Name_Id;
508 In_Array : Name_Id;
509 In_Arrays : Array_Id;
510 In_Tree : Project_Tree_Ref) return Name_Id
511 is
512 Current : Array_Id := In_Arrays;
513 The_Array : Array_Data;
514
515 begin
516 while Current /= No_Array loop
517 The_Array := In_Tree.Arrays.Table (Current);
518 if The_Array.Name = In_Array then
519 return Value_Of
520 (Index, In_Array => The_Array.Value, In_Tree => In_Tree);
521 else
522 Current := The_Array.Next;
523 end if;
524 end loop;
525
526 return No_Name;
527 end Value_Of;
528
529 function Value_Of
530 (Name : Name_Id;
531 In_Arrays : Array_Id;
532 In_Tree : Project_Tree_Ref) return Array_Element_Id
533 is
534 Current : Array_Id := In_Arrays;
535 The_Array : Array_Data;
536
537 begin
538 while Current /= No_Array loop
539 The_Array := In_Tree.Arrays.Table (Current);
540
541 if The_Array.Name = Name then
542 return The_Array.Value;
543 else
544 Current := The_Array.Next;
545 end if;
546 end loop;
547
548 return No_Array_Element;
549 end Value_Of;
550
551 function Value_Of
552 (Name : Name_Id;
553 In_Packages : Package_Id;
554 In_Tree : Project_Tree_Ref) return Package_Id
555 is
556 Current : Package_Id := In_Packages;
557 The_Package : Package_Element;
558
559 begin
560 while Current /= No_Package loop
561 The_Package := In_Tree.Packages.Table (Current);
562 exit when The_Package.Name /= No_Name
563 and then The_Package.Name = Name;
564 Current := The_Package.Next;
565 end loop;
566
567 return Current;
568 end Value_Of;
569
570 function Value_Of
571 (Variable_Name : Name_Id;
572 In_Variables : Variable_Id;
573 In_Tree : Project_Tree_Ref) return Variable_Value
574 is
575 Current : Variable_Id := In_Variables;
576 The_Variable : Variable;
577
578 begin
579 while Current /= No_Variable loop
580 The_Variable :=
581 In_Tree.Variable_Elements.Table (Current);
582
583 if Variable_Name = The_Variable.Name then
584 return The_Variable.Value;
585 else
586 Current := The_Variable.Next;
587 end if;
588 end loop;
589
590 return Nil_Variable_Value;
591 end Value_Of;
592
593 ---------------
594 -- Write_Str --
595 ---------------
596
597 procedure Write_Str
598 (S : String;
599 Max_Length : Positive;
600 Separator : Character)
601 is
602 First : Positive := S'First;
603 Last : Natural := S'Last;
604
605 begin
606 -- Nothing to do for empty strings
607
608 if S'Length > 0 then
609
610 -- Start on a new line if current line is already longer than
611 -- Max_Length.
612
613 if Positive (Column) >= Max_Length then
614 Write_Eol;
615 end if;
616
617 -- If length of remainder is longer than Max_Length, we need to
618 -- cut the remainder in several lines.
619
620 while Positive (Column) + S'Last - First > Max_Length loop
621
622 -- Try the maximum length possible
623
624 Last := First + Max_Length - Positive (Column);
625
626 -- Look for last Separator in the line
627
628 while Last >= First and then S (Last) /= Separator loop
629 Last := Last - 1;
630 end loop;
631
632 -- If we do not find a separator, we output the maximum length
633 -- possible.
634
635 if Last < First then
636 Last := First + Max_Length - Positive (Column);
637 end if;
638
639 Write_Line (S (First .. Last));
640
641 -- Set the beginning of the new remainder
642
643 First := Last + 1;
644 end loop;
645
646 -- What is left goes to the buffer, without EOL
647
648 Write_Str (S (First .. S'Last));
649 end if;
650 end Write_Str;
651 end Prj.Util;