111
|
1 ------------------------------------------------------------------------------
|
|
2 -- --
|
|
3 -- GNAT COMPILER COMPONENTS --
|
|
4 -- --
|
|
5 -- M A K E _ U T I L --
|
|
6 -- --
|
|
7 -- B o d y --
|
|
8 -- --
|
|
9 -- Copyright (C) 2004-2017, 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 ------------------------------------------------------------------------------
|
|
25
|
|
26 with Atree; use Atree;
|
|
27 with Debug;
|
|
28 with Errutil;
|
|
29 with Osint; use Osint;
|
|
30 with Output; use Output;
|
|
31 with Opt; use Opt;
|
|
32 with Table;
|
|
33
|
|
34 with Ada.Command_Line; use Ada.Command_Line;
|
|
35
|
|
36 with GNAT.Case_Util; use GNAT.Case_Util;
|
|
37 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
|
|
38 with GNAT.HTable;
|
|
39
|
|
40 package body Make_Util is
|
|
41
|
|
42 ---------
|
|
43 -- Add --
|
|
44 ---------
|
|
45
|
|
46 procedure Add
|
|
47 (Option : String_Access;
|
|
48 To : in out String_List_Access;
|
|
49 Last : in out Natural)
|
|
50 is
|
|
51 begin
|
|
52 if Last = To'Last then
|
|
53 declare
|
|
54 New_Options : constant String_List_Access :=
|
|
55 new String_List (1 .. To'Last * 2);
|
|
56
|
|
57 begin
|
|
58 New_Options (To'Range) := To.all;
|
|
59
|
|
60 -- Set all elements of the original options to null to avoid
|
|
61 -- deallocation of copies.
|
|
62
|
|
63 To.all := (others => null);
|
|
64
|
|
65 Free (To);
|
|
66 To := New_Options;
|
|
67 end;
|
|
68 end if;
|
|
69
|
|
70 Last := Last + 1;
|
|
71 To (Last) := Option;
|
|
72 end Add;
|
|
73
|
|
74 procedure Add
|
|
75 (Option : String;
|
|
76 To : in out String_List_Access;
|
|
77 Last : in out Natural)
|
|
78 is
|
|
79 begin
|
|
80 Add (Option => new String'(Option), To => To, Last => Last);
|
|
81 end Add;
|
|
82
|
|
83 -------------------------
|
|
84 -- Base_Name_Index_For --
|
|
85 -------------------------
|
|
86
|
|
87 function Base_Name_Index_For
|
|
88 (Main : String;
|
|
89 Main_Index : Int;
|
|
90 Index_Separator : Character) return File_Name_Type
|
|
91 is
|
|
92 Result : File_Name_Type;
|
|
93
|
|
94 begin
|
|
95 Name_Len := 0;
|
|
96 Add_Str_To_Name_Buffer (Base_Name (Main));
|
|
97
|
|
98 -- Remove the extension, if any, that is the last part of the base name
|
|
99 -- starting with a dot and following some characters.
|
|
100
|
|
101 for J in reverse 2 .. Name_Len loop
|
|
102 if Name_Buffer (J) = '.' then
|
|
103 Name_Len := J - 1;
|
|
104 exit;
|
|
105 end if;
|
|
106 end loop;
|
|
107
|
|
108 -- Add the index info, if index is different from 0
|
|
109
|
|
110 if Main_Index > 0 then
|
|
111 Add_Char_To_Name_Buffer (Index_Separator);
|
|
112
|
|
113 declare
|
|
114 Img : constant String := Main_Index'Img;
|
|
115 begin
|
|
116 Add_Str_To_Name_Buffer (Img (2 .. Img'Last));
|
|
117 end;
|
|
118 end if;
|
|
119
|
|
120 Result := Name_Find;
|
|
121 return Result;
|
|
122 end Base_Name_Index_For;
|
|
123
|
|
124 -----------------
|
|
125 -- Create_Name --
|
|
126 -----------------
|
|
127
|
|
128 function Create_Name (Name : String) return File_Name_Type is
|
|
129 begin
|
|
130 Name_Len := 0;
|
|
131 Add_Str_To_Name_Buffer (Name);
|
|
132 return Name_Find;
|
|
133 end Create_Name;
|
|
134
|
|
135 function Create_Name (Name : String) return Name_Id is
|
|
136 begin
|
|
137 Name_Len := 0;
|
|
138 Add_Str_To_Name_Buffer (Name);
|
|
139 return Name_Find;
|
|
140 end Create_Name;
|
|
141
|
|
142 function Create_Name (Name : String) return Path_Name_Type is
|
|
143 begin
|
|
144 Name_Len := 0;
|
|
145 Add_Str_To_Name_Buffer (Name);
|
|
146 return Name_Find;
|
|
147 end Create_Name;
|
|
148
|
|
149 ---------------------------
|
|
150 -- Ensure_Absolute_Path --
|
|
151 ---------------------------
|
|
152
|
|
153 procedure Ensure_Absolute_Path
|
|
154 (Switch : in out String_Access;
|
|
155 Parent : String;
|
|
156 Do_Fail : Fail_Proc;
|
|
157 For_Gnatbind : Boolean := False;
|
|
158 Including_Non_Switch : Boolean := True;
|
|
159 Including_RTS : Boolean := False)
|
|
160 is
|
|
161 begin
|
|
162 if Switch /= null then
|
|
163 declare
|
|
164 Sw : String (1 .. Switch'Length);
|
|
165 Start : Positive;
|
|
166
|
|
167 begin
|
|
168 Sw := Switch.all;
|
|
169
|
|
170 if Sw (1) = '-' then
|
|
171 if Sw'Length >= 3
|
|
172 and then (Sw (2) = 'I'
|
|
173 or else (not For_Gnatbind
|
|
174 and then (Sw (2) = 'L'
|
|
175 or else
|
|
176 Sw (2) = 'A')))
|
|
177 then
|
|
178 Start := 3;
|
|
179
|
|
180 if Sw = "-I-" then
|
|
181 return;
|
|
182 end if;
|
|
183
|
|
184 elsif Sw'Length >= 4
|
|
185 and then
|
|
186 (Sw (2 .. 3) = "aL" or else
|
|
187 Sw (2 .. 3) = "aO" or else
|
|
188 Sw (2 .. 3) = "aI"
|
|
189 or else (For_Gnatbind and then Sw (2 .. 3) = "A="))
|
|
190 then
|
|
191 Start := 4;
|
|
192
|
|
193 elsif Including_RTS
|
|
194 and then Sw'Length >= 7
|
|
195 and then Sw (2 .. 6) = "-RTS="
|
|
196 then
|
|
197 Start := 7;
|
|
198
|
|
199 else
|
|
200 return;
|
|
201 end if;
|
|
202
|
|
203 -- Because relative path arguments to --RTS= may be relative to
|
|
204 -- the search directory prefix, those relative path arguments
|
|
205 -- are converted only when they include directory information.
|
|
206
|
|
207 if not Is_Absolute_Path (Sw (Start .. Sw'Last)) then
|
|
208 if Parent'Length = 0 then
|
|
209 Do_Fail
|
|
210 ("relative search path switches ("""
|
|
211 & Sw
|
|
212 & """) are not allowed");
|
|
213
|
|
214 elsif Including_RTS then
|
|
215 for J in Start .. Sw'Last loop
|
|
216 if Sw (J) = Directory_Separator then
|
|
217 Switch :=
|
|
218 new String'
|
|
219 (Sw (1 .. Start - 1)
|
|
220 & Parent
|
|
221 & Directory_Separator
|
|
222 & Sw (Start .. Sw'Last));
|
|
223 return;
|
|
224 end if;
|
|
225 end loop;
|
|
226
|
|
227 else
|
|
228 Switch :=
|
|
229 new String'
|
|
230 (Sw (1 .. Start - 1)
|
|
231 & Parent
|
|
232 & Directory_Separator
|
|
233 & Sw (Start .. Sw'Last));
|
|
234 end if;
|
|
235 end if;
|
|
236
|
|
237 elsif Including_Non_Switch then
|
|
238 if not Is_Absolute_Path (Sw) then
|
|
239 if Parent'Length = 0 then
|
|
240 Do_Fail
|
|
241 ("relative paths (""" & Sw & """) are not allowed");
|
|
242 else
|
|
243 Switch := new String'(Parent & Directory_Separator & Sw);
|
|
244 end if;
|
|
245 end if;
|
|
246 end if;
|
|
247 end;
|
|
248 end if;
|
|
249 end Ensure_Absolute_Path;
|
|
250
|
|
251 ----------------------------
|
|
252 -- Executable_Prefix_Path --
|
|
253 ----------------------------
|
|
254
|
|
255 function Executable_Prefix_Path return String is
|
|
256 Exec_Name : constant String := Command_Name;
|
|
257
|
|
258 function Get_Install_Dir (S : String) return String;
|
|
259 -- S is the executable name preceded by the absolute or relative path,
|
|
260 -- e.g. "c:\usr\bin\gcc.exe". Returns the absolute directory where "bin"
|
|
261 -- lies (in the example "C:\usr"). If the executable is not in a "bin"
|
|
262 -- directory, return "".
|
|
263
|
|
264 ---------------------
|
|
265 -- Get_Install_Dir --
|
|
266 ---------------------
|
|
267
|
|
268 function Get_Install_Dir (S : String) return String is
|
|
269 Exec : String := S;
|
|
270 Path_Last : Integer := 0;
|
|
271
|
|
272 begin
|
|
273 for J in reverse Exec'Range loop
|
|
274 if Exec (J) = Directory_Separator then
|
|
275 Path_Last := J - 1;
|
|
276 exit;
|
|
277 end if;
|
|
278 end loop;
|
|
279
|
|
280 if Path_Last >= Exec'First + 2 then
|
|
281 To_Lower (Exec (Path_Last - 2 .. Path_Last));
|
|
282 end if;
|
|
283
|
|
284 if Path_Last < Exec'First + 2
|
|
285 or else Exec (Path_Last - 2 .. Path_Last) /= "bin"
|
|
286 or else (Path_Last - 3 >= Exec'First
|
|
287 and then Exec (Path_Last - 3) /= Directory_Separator)
|
|
288 then
|
|
289 return "";
|
|
290 end if;
|
|
291
|
|
292 return Normalize_Pathname
|
|
293 (Exec (Exec'First .. Path_Last - 4),
|
|
294 Resolve_Links => Opt.Follow_Links_For_Dirs)
|
|
295 & Directory_Separator;
|
|
296 end Get_Install_Dir;
|
|
297
|
|
298 -- Beginning of Executable_Prefix_Path
|
|
299
|
|
300 begin
|
|
301 -- First determine if a path prefix was placed in front of the
|
|
302 -- executable name.
|
|
303
|
|
304 for J in reverse Exec_Name'Range loop
|
|
305 if Exec_Name (J) = Directory_Separator then
|
|
306 return Get_Install_Dir (Exec_Name);
|
|
307 end if;
|
|
308 end loop;
|
|
309
|
|
310 -- If we get here, the user has typed the executable name with no
|
|
311 -- directory prefix.
|
|
312
|
|
313 declare
|
|
314 Path : String_Access := Locate_Exec_On_Path (Exec_Name);
|
|
315 begin
|
|
316 if Path = null then
|
|
317 return "";
|
|
318 else
|
|
319 declare
|
|
320 Dir : constant String := Get_Install_Dir (Path.all);
|
|
321 begin
|
|
322 Free (Path);
|
|
323 return Dir;
|
|
324 end;
|
|
325 end if;
|
|
326 end;
|
|
327 end Executable_Prefix_Path;
|
|
328
|
|
329 ------------------
|
|
330 -- Fail_Program --
|
|
331 ------------------
|
|
332
|
|
333 procedure Fail_Program
|
|
334 (S : String;
|
|
335 Flush_Messages : Boolean := True)
|
|
336 is
|
|
337 begin
|
|
338 if Flush_Messages and not No_Exit_Message then
|
|
339 if Total_Errors_Detected /= 0 or else Warnings_Detected /= 0 then
|
|
340 Errutil.Finalize;
|
|
341 end if;
|
|
342 end if;
|
|
343
|
|
344 Finish_Program (E_Fatal, S => S);
|
|
345 end Fail_Program;
|
|
346
|
|
347 --------------------
|
|
348 -- Finish_Program --
|
|
349 --------------------
|
|
350
|
|
351 procedure Finish_Program
|
|
352 (Exit_Code : Osint.Exit_Code_Type := Osint.E_Success;
|
|
353 S : String := "")
|
|
354 is
|
|
355 begin
|
|
356 if S'Length > 0 then
|
|
357 if Exit_Code /= E_Success then
|
|
358 if No_Exit_Message then
|
|
359 Osint.Exit_Program (E_Fatal);
|
|
360 else
|
|
361 Osint.Fail (S);
|
|
362 end if;
|
|
363
|
|
364 elsif not No_Exit_Message then
|
|
365 Write_Str (S);
|
|
366 end if;
|
|
367 end if;
|
|
368
|
|
369 -- Output Namet statistics
|
|
370
|
|
371 Namet.Finalize;
|
|
372
|
|
373 Exit_Program (Exit_Code);
|
|
374 end Finish_Program;
|
|
375
|
|
376 ----------
|
|
377 -- Hash --
|
|
378 ----------
|
|
379
|
|
380 function Hash is new GNAT.HTable.Hash (Header_Num => Header_Num);
|
|
381 -- Used in implementation of other functions Hash below
|
|
382
|
|
383 ----------
|
|
384 -- Hash --
|
|
385 ----------
|
|
386
|
|
387 function Hash (Name : File_Name_Type) return Header_Num is
|
|
388 begin
|
|
389 return Hash (Get_Name_String (Name));
|
|
390 end Hash;
|
|
391
|
|
392 function Hash (Name : Name_Id) return Header_Num is
|
|
393 begin
|
|
394 return Hash (Get_Name_String (Name));
|
|
395 end Hash;
|
|
396
|
|
397 function Hash (Name : Path_Name_Type) return Header_Num is
|
|
398 begin
|
|
399 return Hash (Get_Name_String (Name));
|
|
400 end Hash;
|
|
401
|
|
402 ------------
|
|
403 -- Inform --
|
|
404 ------------
|
|
405
|
|
406 procedure Inform (N : File_Name_Type; Msg : String) is
|
|
407 begin
|
|
408 Inform (Name_Id (N), Msg);
|
|
409 end Inform;
|
|
410
|
|
411 procedure Inform (N : Name_Id := No_Name; Msg : String) is
|
|
412 begin
|
|
413 Osint.Write_Program_Name;
|
|
414
|
|
415 Write_Str (": ");
|
|
416
|
|
417 if N /= No_Name then
|
|
418 Write_Str ("""");
|
|
419
|
|
420 declare
|
|
421 Name : constant String := Get_Name_String (N);
|
|
422 begin
|
|
423 if Debug.Debug_Flag_F and then Is_Absolute_Path (Name) then
|
|
424 Write_Str (File_Name (Name));
|
|
425 else
|
|
426 Write_Str (Name);
|
|
427 end if;
|
|
428 end;
|
|
429
|
|
430 Write_Str (""" ");
|
|
431 end if;
|
|
432
|
|
433 Write_Str (Msg);
|
|
434 Write_Eol;
|
|
435 end Inform;
|
|
436
|
|
437 -----------
|
|
438 -- Mains --
|
|
439 -----------
|
|
440
|
|
441 package body Mains is
|
|
442
|
|
443 package Names is new Table.Table
|
|
444 (Table_Component_Type => Main_Info,
|
|
445 Table_Index_Type => Integer,
|
|
446 Table_Low_Bound => 1,
|
|
447 Table_Initial => 10,
|
|
448 Table_Increment => 100,
|
|
449 Table_Name => "Makeutl.Mains.Names");
|
|
450 -- The table that stores the mains
|
|
451
|
|
452 Current : Natural := 0;
|
|
453 -- The index of the last main retrieved from the table
|
|
454
|
|
455 Count_Of_Mains_With_No_Tree : Natural := 0;
|
|
456 -- Number of main units for which we do not know the project tree
|
|
457
|
|
458 --------------
|
|
459 -- Add_Main --
|
|
460 --------------
|
|
461
|
|
462 procedure Add_Main (Name : String; Index : Int := 0) is
|
|
463 begin
|
|
464 Name_Len := 0;
|
|
465 Add_Str_To_Name_Buffer (Name);
|
|
466 Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
|
|
467
|
|
468 Names.Increment_Last;
|
|
469 Names.Table (Names.Last) := (Name_Find, Index);
|
|
470
|
|
471 Mains.Count_Of_Mains_With_No_Tree :=
|
|
472 Mains.Count_Of_Mains_With_No_Tree + 1;
|
|
473 end Add_Main;
|
|
474
|
|
475 ------------
|
|
476 -- Delete --
|
|
477 ------------
|
|
478
|
|
479 procedure Delete is
|
|
480 begin
|
|
481 Names.Set_Last (0);
|
|
482 Mains.Reset;
|
|
483 end Delete;
|
|
484
|
|
485 ---------------
|
|
486 -- Next_Main --
|
|
487 ---------------
|
|
488
|
|
489 function Next_Main return String is
|
|
490 Info : constant Main_Info := Next_Main;
|
|
491 begin
|
|
492 if Info = No_Main_Info then
|
|
493 return "";
|
|
494 else
|
|
495 return Get_Name_String (Info.File);
|
|
496 end if;
|
|
497 end Next_Main;
|
|
498
|
|
499 function Next_Main return Main_Info is
|
|
500 begin
|
|
501 if Current >= Names.Last then
|
|
502 return No_Main_Info;
|
|
503 else
|
|
504 Current := Current + 1;
|
|
505
|
|
506 declare
|
|
507 Orig_Main : constant File_Name_Type :=
|
|
508 Names.Table (Current).File;
|
|
509 Current_Main : File_Name_Type;
|
|
510
|
|
511 begin
|
|
512 if Strip_Suffix (Orig_Main) = Orig_Main then
|
|
513 Get_Name_String (Orig_Main);
|
|
514 Add_Str_To_Name_Buffer (".adb");
|
|
515 Current_Main := Name_Find;
|
|
516
|
|
517 if Full_Source_Name (Current_Main) = No_File then
|
|
518 Get_Name_String (Orig_Main);
|
|
519 Add_Str_To_Name_Buffer (".ads");
|
|
520 Current_Main := Name_Find;
|
|
521
|
|
522 if Full_Source_Name (Current_Main) /= No_File then
|
|
523 Names.Table (Current).File := Current_Main;
|
|
524 end if;
|
|
525
|
|
526 else
|
|
527 Names.Table (Current).File := Current_Main;
|
|
528 end if;
|
|
529 end if;
|
|
530 end;
|
|
531
|
|
532 return Names.Table (Current);
|
|
533 end if;
|
|
534 end Next_Main;
|
|
535
|
|
536 ---------------------
|
|
537 -- Number_Of_Mains --
|
|
538 ---------------------
|
|
539
|
|
540 function Number_Of_Mains return Natural is
|
|
541 begin
|
|
542 return Names.Last;
|
|
543 end Number_Of_Mains;
|
|
544
|
|
545 -----------
|
|
546 -- Reset --
|
|
547 -----------
|
|
548
|
|
549 procedure Reset is
|
|
550 begin
|
|
551 Current := 0;
|
|
552 end Reset;
|
|
553
|
|
554 --------------------------
|
|
555 -- Set_Multi_Unit_Index --
|
|
556 --------------------------
|
|
557
|
|
558 procedure Set_Multi_Unit_Index
|
|
559 (Index : Int := 0)
|
|
560 is
|
|
561 begin
|
|
562 if Index /= 0 then
|
|
563 if Names.Last = 0 then
|
|
564 Fail_Program
|
|
565 ("cannot specify a multi-unit index but no main "
|
|
566 & "on the command line");
|
|
567
|
|
568 elsif Names.Last > 1 then
|
|
569 Fail_Program
|
|
570 ("cannot specify several mains with a multi-unit index");
|
|
571
|
|
572 else
|
|
573 Names.Table (Names.Last).Index := Index;
|
|
574 end if;
|
|
575 end if;
|
|
576 end Set_Multi_Unit_Index;
|
|
577
|
|
578 end Mains;
|
|
579
|
|
580 -----------------------
|
|
581 -- Path_Or_File_Name --
|
|
582 -----------------------
|
|
583
|
|
584 function Path_Or_File_Name (Path : Path_Name_Type) return String is
|
|
585 Path_Name : constant String := Get_Name_String (Path);
|
|
586 begin
|
|
587 if Debug.Debug_Flag_F then
|
|
588 return File_Name (Path_Name);
|
|
589 else
|
|
590 return Path_Name;
|
|
591 end if;
|
|
592 end Path_Or_File_Name;
|
|
593
|
|
594 -------------------
|
|
595 -- Unit_Index_Of --
|
|
596 -------------------
|
|
597
|
|
598 function Unit_Index_Of (ALI_File : File_Name_Type) return Int is
|
|
599 Start : Natural;
|
|
600 Finish : Natural;
|
|
601 Result : Int := 0;
|
|
602
|
|
603 begin
|
|
604 Get_Name_String (ALI_File);
|
|
605
|
|
606 -- First, find the last dot
|
|
607
|
|
608 Finish := Name_Len;
|
|
609
|
|
610 while Finish >= 1 and then Name_Buffer (Finish) /= '.' loop
|
|
611 Finish := Finish - 1;
|
|
612 end loop;
|
|
613
|
|
614 if Finish = 1 then
|
|
615 return 0;
|
|
616 end if;
|
|
617
|
|
618 -- Now check that the dot is preceded by digits
|
|
619
|
|
620 Start := Finish;
|
|
621 Finish := Finish - 1;
|
|
622 while Start >= 1 and then Name_Buffer (Start - 1) in '0' .. '9' loop
|
|
623 Start := Start - 1;
|
|
624 end loop;
|
|
625
|
|
626 -- If there are no digits, or if the digits are not preceded by the
|
|
627 -- character that precedes a unit index, this is not the ALI file of
|
|
628 -- a unit in a multi-unit source.
|
|
629
|
|
630 if Start > Finish
|
|
631 or else Start = 1
|
|
632 or else Name_Buffer (Start - 1) /= Multi_Unit_Index_Character
|
|
633 then
|
|
634 return 0;
|
|
635 end if;
|
|
636
|
|
637 -- Build the index from the digit(s)
|
|
638
|
|
639 while Start <= Finish loop
|
|
640 Result := Result * 10 +
|
|
641 Character'Pos (Name_Buffer (Start)) - Character'Pos ('0');
|
|
642 Start := Start + 1;
|
|
643 end loop;
|
|
644
|
|
645 return Result;
|
|
646 end Unit_Index_Of;
|
|
647
|
|
648 -----------------
|
|
649 -- Verbose_Msg --
|
|
650 -----------------
|
|
651
|
|
652 procedure Verbose_Msg
|
|
653 (N1 : Name_Id;
|
|
654 S1 : String;
|
|
655 N2 : Name_Id := No_Name;
|
|
656 S2 : String := "";
|
|
657 Prefix : String := " -> ";
|
|
658 Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low)
|
|
659 is
|
|
660 begin
|
|
661 if not Opt.Verbose_Mode
|
|
662 or else Minimum_Verbosity > Opt.Verbosity_Level
|
|
663 then
|
|
664 return;
|
|
665 end if;
|
|
666
|
|
667 Write_Str (Prefix);
|
|
668 Write_Str ("""");
|
|
669 Write_Name (N1);
|
|
670 Write_Str (""" ");
|
|
671 Write_Str (S1);
|
|
672
|
|
673 if N2 /= No_Name then
|
|
674 Write_Str (" """);
|
|
675 Write_Name (N2);
|
|
676 Write_Str (""" ");
|
|
677 end if;
|
|
678
|
|
679 Write_Str (S2);
|
|
680 Write_Eol;
|
|
681 end Verbose_Msg;
|
|
682
|
|
683 procedure Verbose_Msg
|
|
684 (N1 : File_Name_Type;
|
|
685 S1 : String;
|
|
686 N2 : File_Name_Type := No_File;
|
|
687 S2 : String := "";
|
|
688 Prefix : String := " -> ";
|
|
689 Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low)
|
|
690 is
|
|
691 begin
|
|
692 Verbose_Msg
|
|
693 (Name_Id (N1), S1, Name_Id (N2), S2, Prefix, Minimum_Verbosity);
|
|
694 end Verbose_Msg;
|
|
695
|
|
696 -----------
|
|
697 -- Queue --
|
|
698 -----------
|
|
699
|
|
700 package body Queue is
|
|
701
|
|
702 type Q_Record is record
|
|
703 Info : Source_Info;
|
|
704 Processed : Boolean;
|
|
705 end record;
|
|
706
|
|
707 package Q is new Table.Table
|
|
708 (Table_Component_Type => Q_Record,
|
|
709 Table_Index_Type => Natural,
|
|
710 Table_Low_Bound => 1,
|
|
711 Table_Initial => 1000,
|
|
712 Table_Increment => 100,
|
|
713 Table_Name => "Makeutl.Queue.Q");
|
|
714 -- This is the actual Queue
|
|
715
|
|
716 type Mark_Key is record
|
|
717 File : File_Name_Type;
|
|
718 Index : Int;
|
|
719 end record;
|
|
720 -- Identify either a mono-unit source (when Index = 0) or a specific
|
|
721 -- unit (index = 1's origin index of unit) in a multi-unit source.
|
|
722
|
|
723 Max_Mask_Num : constant := 2048;
|
|
724 subtype Mark_Num is Union_Id range 0 .. Max_Mask_Num - 1;
|
|
725
|
|
726 function Hash (Key : Mark_Key) return Mark_Num;
|
|
727
|
|
728 package Marks is new GNAT.HTable.Simple_HTable
|
|
729 (Header_Num => Mark_Num,
|
|
730 Element => Boolean,
|
|
731 No_Element => False,
|
|
732 Key => Mark_Key,
|
|
733 Hash => Hash,
|
|
734 Equal => "=");
|
|
735 -- A hash table to keep tracks of the marked units.
|
|
736 -- These are the units that have already been processed, when using the
|
|
737 -- gnatmake format. When using the gprbuild format, we can directly
|
|
738 -- store in the source_id whether the file has already been processed.
|
|
739
|
|
740 procedure Mark (Source_File : File_Name_Type; Index : Int := 0);
|
|
741 -- Mark a unit, identified by its source file and, when Index is not 0,
|
|
742 -- the index of the unit in the source file. Marking is used to signal
|
|
743 -- that the unit has already been inserted in the Q.
|
|
744
|
|
745 function Is_Marked
|
|
746 (Source_File : File_Name_Type;
|
|
747 Index : Int := 0) return Boolean;
|
|
748 -- Returns True if the unit was previously marked
|
|
749
|
|
750 Q_Processed : Natural := 0;
|
|
751 Q_Initialized : Boolean := False;
|
|
752
|
|
753 Q_First : Natural := 1;
|
|
754 -- Points to the first valid element in the queue
|
|
755
|
|
756 procedure Debug_Display (S : Source_Info);
|
|
757 -- A debug display for S
|
|
758
|
|
759 function Was_Processed (S : Source_Info) return Boolean;
|
|
760 -- Whether S has already been processed. This marks the source as
|
|
761 -- processed, if it hasn't already been processed.
|
|
762
|
|
763 -------------------
|
|
764 -- Was_Processed --
|
|
765 -------------------
|
|
766
|
|
767 function Was_Processed (S : Source_Info) return Boolean is
|
|
768 begin
|
|
769 if Is_Marked (S.File, S.Index) then
|
|
770 return True;
|
|
771 end if;
|
|
772
|
|
773 Mark (S.File, Index => S.Index);
|
|
774
|
|
775 return False;
|
|
776 end Was_Processed;
|
|
777
|
|
778 -------------------
|
|
779 -- Debug_Display --
|
|
780 -------------------
|
|
781
|
|
782 procedure Debug_Display (S : Source_Info) is
|
|
783 begin
|
|
784 Write_Name (S.File);
|
|
785
|
|
786 if S.Index /= 0 then
|
|
787 Write_Str (", ");
|
|
788 Write_Int (S.Index);
|
|
789 end if;
|
|
790 end Debug_Display;
|
|
791
|
|
792 ----------
|
|
793 -- Hash --
|
|
794 ----------
|
|
795
|
|
796 function Hash (Key : Mark_Key) return Mark_Num is
|
|
797 begin
|
|
798 return Union_Id (Key.File) mod Max_Mask_Num;
|
|
799 end Hash;
|
|
800
|
|
801 ---------------
|
|
802 -- Is_Marked --
|
|
803 ---------------
|
|
804
|
|
805 function Is_Marked
|
|
806 (Source_File : File_Name_Type;
|
|
807 Index : Int := 0) return Boolean
|
|
808 is
|
|
809 begin
|
|
810 return Marks.Get (K => (File => Source_File, Index => Index));
|
|
811 end Is_Marked;
|
|
812
|
|
813 ----------
|
|
814 -- Mark --
|
|
815 ----------
|
|
816
|
|
817 procedure Mark (Source_File : File_Name_Type; Index : Int := 0) is
|
|
818 begin
|
|
819 Marks.Set (K => (File => Source_File, Index => Index), E => True);
|
|
820 end Mark;
|
|
821
|
|
822 -------------
|
|
823 -- Extract --
|
|
824 -------------
|
|
825
|
|
826 procedure Extract
|
|
827 (Found : out Boolean;
|
|
828 Source : out Source_Info)
|
|
829 is
|
|
830 begin
|
|
831 Found := False;
|
|
832
|
|
833 if Q_First <= Q.Last then
|
|
834 Source := Q.Table (Q_First).Info;
|
|
835 Q.Table (Q_First).Processed := True;
|
|
836 Q_First := Q_First + 1;
|
|
837 Found := True;
|
|
838 end if;
|
|
839
|
|
840 if Found then
|
|
841 Q_Processed := Q_Processed + 1;
|
|
842 end if;
|
|
843
|
|
844 if Found and then Debug.Debug_Flag_Q then
|
|
845 Write_Str (" Q := Q - [ ");
|
|
846 Debug_Display (Source);
|
|
847 Write_Str (" ]");
|
|
848 Write_Eol;
|
|
849
|
|
850 Write_Str (" Q_First =");
|
|
851 Write_Int (Int (Q_First));
|
|
852 Write_Eol;
|
|
853
|
|
854 Write_Str (" Q.Last =");
|
|
855 Write_Int (Int (Q.Last));
|
|
856 Write_Eol;
|
|
857 end if;
|
|
858 end Extract;
|
|
859
|
|
860 ---------------
|
|
861 -- Processed --
|
|
862 ---------------
|
|
863
|
|
864 function Processed return Natural is
|
|
865 begin
|
|
866 return Q_Processed;
|
|
867 end Processed;
|
|
868
|
|
869 ----------------
|
|
870 -- Initialize --
|
|
871 ----------------
|
|
872
|
|
873 procedure Initialize (Force : Boolean := False) is
|
|
874 begin
|
|
875 if Force or else not Q_Initialized then
|
|
876 Q_Initialized := True;
|
|
877 Q.Init;
|
|
878 Q_Processed := 0;
|
|
879 Q_First := 1;
|
|
880 end if;
|
|
881 end Initialize;
|
|
882
|
|
883 ------------
|
|
884 -- Insert --
|
|
885 ------------
|
|
886
|
|
887 function Insert (Source : Source_Info) return Boolean is
|
|
888 begin
|
|
889 -- Only insert in the Q if it is not already done, to avoid
|
|
890 -- simultaneous compilations if -jnnn is used.
|
|
891
|
|
892 if Was_Processed (Source) then
|
|
893 return False;
|
|
894 end if;
|
|
895
|
|
896 Q.Append (New_Val => (Info => Source, Processed => False));
|
|
897
|
|
898 if Debug.Debug_Flag_Q then
|
|
899 Write_Str (" Q := Q + [ ");
|
|
900 Debug_Display (Source);
|
|
901 Write_Str (" ] ");
|
|
902 Write_Eol;
|
|
903
|
|
904 Write_Str (" Q_First =");
|
|
905 Write_Int (Int (Q_First));
|
|
906 Write_Eol;
|
|
907
|
|
908 Write_Str (" Q.Last =");
|
|
909 Write_Int (Int (Q.Last));
|
|
910 Write_Eol;
|
|
911 end if;
|
|
912
|
|
913 return True;
|
|
914 end Insert;
|
|
915
|
|
916 procedure Insert (Source : Source_Info) is
|
|
917 Discard : Boolean;
|
|
918 begin
|
|
919 Discard := Insert (Source);
|
|
920 end Insert;
|
|
921
|
|
922 --------------
|
|
923 -- Is_Empty --
|
|
924 --------------
|
|
925
|
|
926 function Is_Empty return Boolean is
|
|
927 begin
|
|
928 return Q_Processed >= Q.Last;
|
|
929 end Is_Empty;
|
|
930
|
|
931 ----------
|
|
932 -- Size --
|
|
933 ----------
|
|
934
|
|
935 function Size return Natural is
|
|
936 begin
|
|
937 return Q.Last;
|
|
938 end Size;
|
|
939
|
|
940 -------------
|
|
941 -- Element --
|
|
942 -------------
|
|
943
|
|
944 function Element (Rank : Positive) return File_Name_Type is
|
|
945 begin
|
|
946 if Rank <= Q.Last then
|
|
947 return Q.Table (Rank).Info.File;
|
|
948 else
|
|
949 return No_File;
|
|
950 end if;
|
|
951 end Element;
|
|
952
|
|
953 ------------------
|
|
954 -- Remove_Marks --
|
|
955 ------------------
|
|
956
|
|
957 procedure Remove_Marks is
|
|
958 begin
|
|
959 Marks.Reset;
|
|
960 end Remove_Marks;
|
|
961
|
|
962 end Queue;
|
|
963
|
|
964 end Make_Util;
|