OSDN Git Service

2009-11-30 Robert Dewar <dewar@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / makeutl.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                              M A K E U T L                               --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 2004-2009, 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 ALI;      use ALI;
27 with Debug;
28 with Fname;
29 with Osint;    use Osint;
30 with Output;   use Output;
31 with Opt;      use Opt;
32 with Prj.Ext;
33 with Prj.Util;
34 with Snames;   use Snames;
35 with Table;
36
37 with Ada.Command_Line;  use Ada.Command_Line;
38
39 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
40
41 with System.Case_Util; use System.Case_Util;
42 with System.HTable;
43
44 package body Makeutl is
45
46    type Mark_Key is record
47       File  : File_Name_Type;
48       Index : Int;
49    end record;
50    --  Identify either a mono-unit source (when Index = 0) or a specific unit
51    --  (index = 1's origin index of unit) in a multi-unit source.
52
53    --  There follow many global undocumented declarations, comments needed ???
54
55    Max_Mask_Num : constant := 2048;
56
57    subtype Mark_Num is Union_Id range 0 .. Max_Mask_Num - 1;
58
59    function Hash (Key : Mark_Key) return Mark_Num;
60
61    package Marks is new System.HTable.Simple_HTable
62      (Header_Num => Mark_Num,
63       Element    => Boolean,
64       No_Element => False,
65       Key        => Mark_Key,
66       Hash       => Hash,
67       Equal      => "=");
68    --  A hash table to keep tracks of the marked units
69
70    type Linker_Options_Data is record
71       Project : Project_Id;
72       Options : String_List_Id;
73    end record;
74
75    Linker_Option_Initial_Count : constant := 20;
76
77    Linker_Options_Buffer : String_List_Access :=
78      new String_List (1 .. Linker_Option_Initial_Count);
79
80    Last_Linker_Option : Natural := 0;
81
82    package Linker_Opts is new Table.Table (
83      Table_Component_Type => Linker_Options_Data,
84      Table_Index_Type     => Integer,
85      Table_Low_Bound      => 1,
86      Table_Initial        => 10,
87      Table_Increment      => 100,
88      Table_Name           => "Make.Linker_Opts");
89
90    procedure Add_Linker_Option (Option : String);
91
92    ---------
93    -- Add --
94    ---------
95
96    procedure Add
97      (Option : String_Access;
98       To     : in out String_List_Access;
99       Last   : in out Natural)
100    is
101    begin
102       if Last = To'Last then
103          declare
104             New_Options : constant String_List_Access :=
105                             new String_List (1 .. To'Last * 2);
106
107          begin
108             New_Options (To'Range) := To.all;
109
110             --  Set all elements of the original options to null to avoid
111             --  deallocation of copies.
112
113             To.all := (others => null);
114
115             Free (To);
116             To := New_Options;
117          end;
118       end if;
119
120       Last := Last + 1;
121       To (Last) := Option;
122    end Add;
123
124    procedure Add
125      (Option : String;
126       To     : in out String_List_Access;
127       Last   : in out Natural)
128    is
129    begin
130       Add (Option => new String'(Option), To => To, Last => Last);
131    end Add;
132
133    -----------------------
134    -- Add_Linker_Option --
135    -----------------------
136
137    procedure Add_Linker_Option (Option : String) is
138    begin
139       if Option'Length > 0 then
140          if Last_Linker_Option = Linker_Options_Buffer'Last then
141             declare
142                New_Buffer : constant String_List_Access :=
143                               new String_List
144                                 (1 .. Linker_Options_Buffer'Last +
145                                         Linker_Option_Initial_Count);
146             begin
147                New_Buffer (Linker_Options_Buffer'Range) :=
148                  Linker_Options_Buffer.all;
149                Linker_Options_Buffer.all := (others => null);
150                Free (Linker_Options_Buffer);
151                Linker_Options_Buffer := New_Buffer;
152             end;
153          end if;
154
155          Last_Linker_Option := Last_Linker_Option + 1;
156          Linker_Options_Buffer (Last_Linker_Option) := new String'(Option);
157       end if;
158    end Add_Linker_Option;
159
160    ------------------------------
161    -- Check_Source_Info_In_ALI --
162    ------------------------------
163
164    function Check_Source_Info_In_ALI (The_ALI : ALI_Id) return Boolean is
165       Unit_Name : Name_Id;
166
167    begin
168       --  Loop through units
169
170       for U in ALIs.Table (The_ALI).First_Unit ..
171                ALIs.Table (The_ALI).Last_Unit
172       loop
173          --  Check if the file name is one of the source of the unit
174
175          Get_Name_String (Units.Table (U).Uname);
176          Name_Len  := Name_Len - 2;
177          Unit_Name := Name_Find;
178
179          if File_Not_A_Source_Of (Unit_Name, Units.Table (U).Sfile) then
180             return False;
181          end if;
182
183          --  Loop to do same check for each of the withed units
184
185          for W in Units.Table (U).First_With .. Units.Table (U).Last_With loop
186             declare
187                WR : ALI.With_Record renames Withs.Table (W);
188
189             begin
190                if WR.Sfile /= No_File then
191                   Get_Name_String (WR.Uname);
192                   Name_Len  := Name_Len - 2;
193                   Unit_Name := Name_Find;
194
195                   if File_Not_A_Source_Of (Unit_Name, WR.Sfile) then
196                      return False;
197                   end if;
198                end if;
199             end;
200          end loop;
201       end loop;
202
203       --  Loop to check subunits
204
205       for D in ALIs.Table (The_ALI).First_Sdep ..
206                ALIs.Table (The_ALI).Last_Sdep
207       loop
208          declare
209             SD : Sdep_Record renames Sdep.Table (D);
210
211          begin
212             Unit_Name := SD.Subunit_Name;
213
214             if Unit_Name /= No_Name then
215
216                --  For separates, the file is no longer associated with the
217                --  unit ("proc-sep.adb" is not associated with unit "proc.sep")
218                --  so we need to check whether the source file still exists in
219                --  the source tree: it will if it matches the naming scheme
220                --  (and then will be for the same unit).
221
222                if Find_Source
223                     (In_Tree   => Project_Tree,
224                      Project   => No_Project,
225                      Base_Name => SD.Sfile) = No_Source
226                then
227                   --  If this is not a runtime file or if, when gnatmake switch
228                   --  -a is used, we are not able to find this subunit in the
229                   --  source directories, then recompilation is needed.
230
231                   if not Fname.Is_Internal_File_Name (SD.Sfile)
232                     or else
233                       (Check_Readonly_Files
234                         and then Find_File (SD.Sfile, Osint.Source) = No_File)
235                   then
236                      if Verbose_Mode then
237                         Write_Line
238                           ("While parsing ALI file, file "
239                            & Get_Name_String (SD.Sfile)
240                            & " is indicated as containing subunit "
241                            & Get_Name_String (Unit_Name)
242                            & " but this does not match what was found while"
243                            & " parsing the project. Will recompile");
244                      end if;
245
246                      return False;
247                   end if;
248                end if;
249             end if;
250          end;
251       end loop;
252
253       return True;
254    end Check_Source_Info_In_ALI;
255
256    -----------------
257    -- Create_Name --
258    -----------------
259
260    function Create_Name (Name : String) return File_Name_Type is
261    begin
262       Name_Len := 0;
263       Add_Str_To_Name_Buffer (Name);
264       return Name_Find;
265    end Create_Name;
266
267    function Create_Name (Name : String) return Name_Id is
268    begin
269       Name_Len := 0;
270       Add_Str_To_Name_Buffer (Name);
271       return Name_Find;
272    end Create_Name;
273
274    function Create_Name (Name : String) return Path_Name_Type is
275    begin
276       Name_Len := 0;
277       Add_Str_To_Name_Buffer (Name);
278       return Name_Find;
279    end Create_Name;
280
281    ----------------------
282    -- Delete_All_Marks --
283    ----------------------
284
285    procedure Delete_All_Marks is
286    begin
287       Marks.Reset;
288    end Delete_All_Marks;
289
290    ----------------------------
291    -- Executable_Prefix_Path --
292    ----------------------------
293
294    function Executable_Prefix_Path return String is
295       Exec_Name : constant String := Command_Name;
296
297       function Get_Install_Dir (S : String) return String;
298       --  S is the executable name preceded by the absolute or relative path,
299       --  e.g. "c:\usr\bin\gcc.exe". Returns the absolute directory where "bin"
300       --  lies (in the example "C:\usr"). If the executable is not in a "bin"
301       --  directory, return "".
302
303       ---------------------
304       -- Get_Install_Dir --
305       ---------------------
306
307       function Get_Install_Dir (S : String) return String is
308          Exec      : String  := S;
309          Path_Last : Integer := 0;
310
311       begin
312          for J in reverse Exec'Range loop
313             if Exec (J) = Directory_Separator then
314                Path_Last := J - 1;
315                exit;
316             end if;
317          end loop;
318
319          if Path_Last >= Exec'First + 2 then
320             To_Lower (Exec (Path_Last - 2 .. Path_Last));
321          end if;
322
323          if Path_Last < Exec'First + 2
324            or else Exec (Path_Last - 2 .. Path_Last) /= "bin"
325            or else (Path_Last - 3 >= Exec'First
326                      and then Exec (Path_Last - 3) /= Directory_Separator)
327          then
328             return "";
329          end if;
330
331          return Normalize_Pathname
332                   (Exec (Exec'First .. Path_Last - 4),
333                    Resolve_Links => Opt.Follow_Links_For_Dirs)
334            & Directory_Separator;
335       end Get_Install_Dir;
336
337    --  Beginning of Executable_Prefix_Path
338
339    begin
340       --  First determine if a path prefix was placed in front of the
341       --  executable name.
342
343       for J in reverse Exec_Name'Range loop
344          if Exec_Name (J) = Directory_Separator then
345             return Get_Install_Dir (Exec_Name);
346          end if;
347       end loop;
348
349       --  If we get here, the user has typed the executable name with no
350       --  directory prefix.
351
352       declare
353          Path : String_Access := Locate_Exec_On_Path (Exec_Name);
354       begin
355          if Path = null then
356             return "";
357          else
358             declare
359                Dir : constant String := Get_Install_Dir (Path.all);
360             begin
361                Free (Path);
362                return Dir;
363             end;
364          end if;
365       end;
366    end Executable_Prefix_Path;
367
368    --------------------------
369    -- File_Not_A_Source_Of --
370    --------------------------
371
372    function File_Not_A_Source_Of
373      (Uname : Name_Id;
374       Sfile : File_Name_Type) return Boolean
375    is
376       Unit : constant Unit_Index :=
377                Units_Htable.Get (Project_Tree.Units_HT, Uname);
378
379       At_Least_One_File : Boolean := False;
380
381    begin
382       if Unit /= No_Unit_Index then
383          for F in Unit.File_Names'Range loop
384             if Unit.File_Names (F) /= null then
385                At_Least_One_File := True;
386                if Unit.File_Names (F).File = Sfile then
387                   return False;
388                end if;
389             end if;
390          end loop;
391
392          if not At_Least_One_File then
393
394             --  The unit was probably created initially for a separate unit
395             --  (which are initially created as IMPL when both suffixes are the
396             --  same). Later on, Override_Kind changed the type of the file,
397             --  and the unit is no longer valid in fact.
398
399             return False;
400          end if;
401
402          Verbose_Msg (Uname, "sources do not include ", Name_Id (Sfile));
403          return True;
404       end if;
405
406       return False;
407    end File_Not_A_Source_Of;
408
409    ----------
410    -- Hash --
411    ----------
412
413    function Hash (Key : Mark_Key) return Mark_Num is
414    begin
415       return Union_Id (Key.File) mod Max_Mask_Num;
416    end Hash;
417
418    ------------
419    -- Inform --
420    ------------
421
422    procedure Inform (N : File_Name_Type; Msg : String) is
423    begin
424       Inform (Name_Id (N), Msg);
425    end Inform;
426
427    procedure Inform (N : Name_Id := No_Name; Msg : String) is
428    begin
429       Osint.Write_Program_Name;
430
431       Write_Str (": ");
432
433       if N /= No_Name then
434          Write_Str ("""");
435
436          declare
437             Name : constant String := Get_Name_String (N);
438          begin
439             if Debug.Debug_Flag_F and then Is_Absolute_Path (Name) then
440                Write_Str (File_Name (Name));
441             else
442                Write_Str (Name);
443             end if;
444          end;
445
446          Write_Str (""" ");
447       end if;
448
449       Write_Str (Msg);
450       Write_Eol;
451    end Inform;
452
453    ----------------------------
454    -- Is_External_Assignment --
455    ----------------------------
456
457    function Is_External_Assignment
458      (Tree : Prj.Tree.Project_Node_Tree_Ref;
459       Argv : String) return Boolean
460    is
461       Start     : Positive := 3;
462       Finish    : Natural := Argv'Last;
463
464       pragma Assert (Argv'First = 1);
465       pragma Assert (Argv (1 .. 2) = "-X");
466
467    begin
468       if Argv'Last < 5 then
469          return False;
470
471       elsif Argv (3) = '"' then
472          if Argv (Argv'Last) /= '"' or else Argv'Last < 7 then
473             return False;
474          else
475             Start := 4;
476             Finish := Argv'Last - 1;
477          end if;
478       end if;
479
480       return Prj.Ext.Check
481         (Tree        => Tree,
482          Declaration => Argv (Start .. Finish));
483    end Is_External_Assignment;
484
485    ---------------
486    -- Is_Marked --
487    ---------------
488
489    function Is_Marked
490      (Source_File : File_Name_Type;
491       Index       : Int := 0) return Boolean
492    is
493    begin
494       return Marks.Get (K => (File => Source_File, Index => Index));
495    end Is_Marked;
496
497    -----------------------------
498    -- Linker_Options_Switches --
499    -----------------------------
500
501    function Linker_Options_Switches
502      (Project  : Project_Id;
503       In_Tree  : Project_Tree_Ref) return String_List
504    is
505       procedure Recursive_Add (Proj : Project_Id; Dummy : in out Boolean);
506       --  The recursive routine used to add linker options
507
508       -------------------
509       -- Recursive_Add --
510       -------------------
511
512       procedure Recursive_Add (Proj : Project_Id; Dummy : in out Boolean) is
513          pragma Unreferenced (Dummy);
514
515          Linker_Package : Package_Id;
516          Options        : Variable_Value;
517
518       begin
519          Linker_Package :=
520            Prj.Util.Value_Of
521              (Name        => Name_Linker,
522               In_Packages => Proj.Decl.Packages,
523               In_Tree     => In_Tree);
524
525          Options :=
526            Prj.Util.Value_Of
527              (Name                    => Name_Ada,
528               Index                   => 0,
529               Attribute_Or_Array_Name => Name_Linker_Options,
530               In_Package              => Linker_Package,
531               In_Tree                 => In_Tree);
532
533          --  If attribute is present, add the project with
534          --  the attribute to table Linker_Opts.
535
536          if Options /= Nil_Variable_Value then
537             Linker_Opts.Increment_Last;
538             Linker_Opts.Table (Linker_Opts.Last) :=
539               (Project => Proj, Options => Options.Values);
540          end if;
541       end Recursive_Add;
542
543       procedure For_All_Projects is
544         new For_Every_Project_Imported (Boolean, Recursive_Add);
545
546       Dummy : Boolean := False;
547
548    --  Start of processing for Linker_Options_Switches
549
550    begin
551       Linker_Opts.Init;
552
553       For_All_Projects (Project, Dummy, Imported_First => True);
554
555       Last_Linker_Option := 0;
556
557       for Index in reverse 1 .. Linker_Opts.Last loop
558          declare
559             Options : String_List_Id;
560             Proj    : constant Project_Id :=
561                         Linker_Opts.Table (Index).Project;
562             Option  : Name_Id;
563             Dir_Path : constant String :=
564                          Get_Name_String (Proj.Directory.Name);
565
566          begin
567             Options := Linker_Opts.Table (Index).Options;
568             while Options /= Nil_String loop
569                Option := In_Tree.String_Elements.Table (Options).Value;
570                Get_Name_String (Option);
571
572                --  Do not consider empty linker options
573
574                if Name_Len /= 0 then
575                   Add_Linker_Option (Name_Buffer (1 .. Name_Len));
576
577                   --  Object files and -L switches specified with relative
578                   --  paths must be converted to absolute paths.
579
580                   Test_If_Relative_Path
581                     (Switch => Linker_Options_Buffer (Last_Linker_Option),
582                      Parent => Dir_Path,
583                      Including_L_Switch => True);
584                end if;
585
586                Options := In_Tree.String_Elements.Table (Options).Next;
587             end loop;
588          end;
589       end loop;
590
591       return Linker_Options_Buffer (1 .. Last_Linker_Option);
592    end Linker_Options_Switches;
593
594    -----------
595    -- Mains --
596    -----------
597
598    package body Mains is
599
600       type File_And_Loc is record
601          File_Name : File_Name_Type;
602          Location  : Source_Ptr := No_Location;
603       end record;
604
605       package Names is new Table.Table
606         (Table_Component_Type => File_And_Loc,
607          Table_Index_Type     => Integer,
608          Table_Low_Bound      => 1,
609          Table_Initial        => 10,
610          Table_Increment      => 100,
611          Table_Name           => "Makeutl.Mains.Names");
612       --  The table that stores the mains
613
614       Current : Natural := 0;
615       --  The index of the last main retrieved from the table
616
617       --------------
618       -- Add_Main --
619       --------------
620
621       procedure Add_Main (Name : String) is
622       begin
623          Name_Len := 0;
624          Add_Str_To_Name_Buffer (Name);
625          Names.Increment_Last;
626          Names.Table (Names.Last) := (Name_Find, No_Location);
627       end Add_Main;
628
629       ------------
630       -- Delete --
631       ------------
632
633       procedure Delete is
634       begin
635          Names.Set_Last (0);
636          Mains.Reset;
637       end Delete;
638
639       ------------------
640       -- Get_Location --
641       ------------------
642
643       function Get_Location return Source_Ptr is
644       begin
645          if Current in Names.First .. Names.Last then
646             return Names.Table (Current).Location;
647          else
648             return No_Location;
649          end if;
650       end Get_Location;
651
652       ---------------
653       -- Next_Main --
654       ---------------
655
656       function Next_Main return String is
657       begin
658          if Current >= Names.Last then
659             return "";
660          else
661             Current := Current + 1;
662             return Get_Name_String (Names.Table (Current).File_Name);
663          end if;
664       end Next_Main;
665
666       ---------------------
667       -- Number_Of_Mains --
668       ---------------------
669
670       function Number_Of_Mains return Natural is
671       begin
672          return Names.Last;
673       end Number_Of_Mains;
674
675       -----------
676       -- Reset --
677       -----------
678
679       procedure Reset is
680       begin
681          Current := 0;
682       end Reset;
683
684       ------------------
685       -- Set_Location --
686       ------------------
687
688       procedure Set_Location (Location : Source_Ptr) is
689       begin
690          if Names.Last > 0 then
691             Names.Table (Names.Last).Location := Location;
692          end if;
693       end Set_Location;
694
695       -----------------
696       -- Update_Main --
697       -----------------
698
699       procedure Update_Main (Name : String) is
700       begin
701          if Current in Names.First .. Names.Last then
702             Name_Len := 0;
703             Add_Str_To_Name_Buffer (Name);
704             Names.Table (Current).File_Name := Name_Find;
705          end if;
706       end Update_Main;
707    end Mains;
708
709    ----------
710    -- Mark --
711    ----------
712
713    procedure Mark (Source_File : File_Name_Type; Index : Int := 0) is
714    begin
715       Marks.Set (K => (File => Source_File, Index => Index), E => True);
716    end Mark;
717
718    -----------------------
719    -- Path_Or_File_Name --
720    -----------------------
721
722    function Path_Or_File_Name (Path : Path_Name_Type) return String is
723       Path_Name : constant String := Get_Name_String (Path);
724    begin
725       if Debug.Debug_Flag_F then
726          return File_Name (Path_Name);
727       else
728          return Path_Name;
729       end if;
730    end Path_Or_File_Name;
731
732    ---------------------------
733    -- Test_If_Relative_Path --
734    ---------------------------
735
736    procedure Test_If_Relative_Path
737      (Switch               : in out String_Access;
738       Parent               : String;
739       Including_L_Switch   : Boolean := True;
740       Including_Non_Switch : Boolean := True;
741       Including_RTS        : Boolean := False)
742    is
743    begin
744       if Switch /= null then
745          declare
746             Sw    : String (1 .. Switch'Length);
747             Start : Positive;
748
749          begin
750             Sw := Switch.all;
751
752             if Sw (1) = '-' then
753                if Sw'Length >= 3
754                  and then (Sw (2) = 'A'
755                             or else Sw (2) = 'I'
756                             or else (Including_L_Switch and then Sw (2) = 'L'))
757                then
758                   Start := 3;
759
760                   if Sw = "-I-" then
761                      return;
762                   end if;
763
764                elsif Sw'Length >= 4
765                  and then (Sw (2 .. 3) = "aL"
766                             or else Sw (2 .. 3) = "aO"
767                             or else Sw (2 .. 3) = "aI")
768                then
769                   Start := 4;
770
771                elsif Including_RTS
772                  and then Sw'Length >= 7
773                  and then Sw (2 .. 6) = "-RTS="
774                then
775                   Start := 7;
776
777                else
778                   return;
779                end if;
780
781                --  Because relative path arguments to --RTS= may be relative
782                --  to the search directory prefix, those relative path
783                --  arguments are converted only when they include directory
784                --  information.
785
786                if not Is_Absolute_Path (Sw (Start .. Sw'Last)) then
787                   if Parent'Length = 0 then
788                      Do_Fail
789                        ("relative search path switches ("""
790                         & Sw
791                         & """) are not allowed");
792
793                   elsif Including_RTS then
794                      for J in Start .. Sw'Last loop
795                         if Sw (J) = Directory_Separator then
796                            Switch :=
797                              new String'
798                                (Sw (1 .. Start - 1) &
799                                 Parent &
800                                 Directory_Separator &
801                                 Sw (Start .. Sw'Last));
802                            return;
803                         end if;
804                      end loop;
805
806                   else
807                      Switch :=
808                        new String'
809                          (Sw (1 .. Start - 1) &
810                           Parent &
811                           Directory_Separator &
812                           Sw (Start .. Sw'Last));
813                   end if;
814                end if;
815
816             elsif Including_Non_Switch then
817                if not Is_Absolute_Path (Sw) then
818                   if Parent'Length = 0 then
819                      Do_Fail
820                        ("relative paths (""" & Sw & """) are not allowed");
821                   else
822                      Switch := new String'(Parent & Directory_Separator & Sw);
823                   end if;
824                end if;
825             end if;
826          end;
827       end if;
828    end Test_If_Relative_Path;
829
830    -------------------
831    -- Unit_Index_Of --
832    -------------------
833
834    function Unit_Index_Of (ALI_File : File_Name_Type) return Int is
835       Start  : Natural;
836       Finish : Natural;
837       Result : Int := 0;
838
839    begin
840       Get_Name_String (ALI_File);
841
842       --  First, find the last dot
843
844       Finish := Name_Len;
845
846       while Finish >= 1 and then Name_Buffer (Finish) /= '.' loop
847          Finish := Finish - 1;
848       end loop;
849
850       if Finish = 1 then
851          return 0;
852       end if;
853
854       --  Now check that the dot is preceded by digits
855
856       Start := Finish;
857       Finish := Finish - 1;
858
859       while Start >= 1 and then Name_Buffer (Start - 1) in '0' .. '9' loop
860          Start := Start - 1;
861       end loop;
862
863       --  If there are no digits, or if the digits are not preceded by the
864       --  character that precedes a unit index, this is not the ALI file of
865       --  a unit in a multi-unit source.
866
867       if Start > Finish
868         or else Start = 1
869         or else Name_Buffer (Start - 1) /= Multi_Unit_Index_Character
870       then
871          return 0;
872       end if;
873
874       --  Build the index from the digit(s)
875
876       while Start <= Finish loop
877          Result := Result * 10 +
878                      Character'Pos (Name_Buffer (Start)) - Character'Pos ('0');
879          Start := Start + 1;
880       end loop;
881
882       return Result;
883    end Unit_Index_Of;
884
885    -----------------
886    -- Verbose_Msg --
887    -----------------
888
889    procedure Verbose_Msg
890      (N1                : Name_Id;
891       S1                : String;
892       N2                : Name_Id := No_Name;
893       S2                : String  := "";
894       Prefix            : String := "  -> ";
895       Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low)
896    is
897    begin
898       if not Opt.Verbose_Mode
899         or else Minimum_Verbosity > Opt.Verbosity_Level
900       then
901          return;
902       end if;
903
904       Write_Str (Prefix);
905       Write_Str ("""");
906       Write_Name (N1);
907       Write_Str (""" ");
908       Write_Str (S1);
909
910       if N2 /= No_Name then
911          Write_Str (" """);
912          Write_Name (N2);
913          Write_Str (""" ");
914       end if;
915
916       Write_Str (S2);
917       Write_Eol;
918    end Verbose_Msg;
919
920    procedure Verbose_Msg
921      (N1                : File_Name_Type;
922       S1                : String;
923       N2                : File_Name_Type := No_File;
924       S2                : String  := "";
925       Prefix            : String := "  -> ";
926       Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low)
927    is
928    begin
929       Verbose_Msg
930         (Name_Id (N1), S1, Name_Id (N2), S2, Prefix, Minimum_Verbosity);
931    end Verbose_Msg;
932
933 end Makeutl;