OSDN Git Service

* gcc-interface/Makefile.in (gnatlib-shared-default): Append
[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-2012, 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 Err_Vars; use Err_Vars;
29 with Errutil;
30 with Fname;
31 with Hostparm;
32 with Osint;    use Osint;
33 with Output;   use Output;
34 with Opt;      use Opt;
35 with Prj.Com;
36 with Prj.Err;
37 with Prj.Ext;
38 with Prj.Util; use Prj.Util;
39 with Sinput.P;
40 with Tempdir;
41
42 with Ada.Command_Line;           use Ada.Command_Line;
43 with Ada.Unchecked_Deallocation;
44
45 with GNAT.Case_Util;             use GNAT.Case_Util;
46 with GNAT.Directory_Operations;  use GNAT.Directory_Operations;
47 with GNAT.HTable;
48 with GNAT.Regexp;                use GNAT.Regexp;
49
50 package body Makeutl is
51
52    type Linker_Options_Data is record
53       Project : Project_Id;
54       Options : String_List_Id;
55    end record;
56
57    Linker_Option_Initial_Count : constant := 20;
58
59    Linker_Options_Buffer : String_List_Access :=
60      new String_List (1 .. Linker_Option_Initial_Count);
61
62    Last_Linker_Option : Natural := 0;
63
64    package Linker_Opts is new Table.Table (
65      Table_Component_Type => Linker_Options_Data,
66      Table_Index_Type     => Integer,
67      Table_Low_Bound      => 1,
68      Table_Initial        => 10,
69      Table_Increment      => 100,
70      Table_Name           => "Make.Linker_Opts");
71
72    procedure Add_Linker_Option (Option : String);
73
74    ---------
75    -- Add --
76    ---------
77
78    procedure Add
79      (Option : String_Access;
80       To     : in out String_List_Access;
81       Last   : in out Natural)
82    is
83    begin
84       if Last = To'Last then
85          declare
86             New_Options : constant String_List_Access :=
87                             new String_List (1 .. To'Last * 2);
88
89          begin
90             New_Options (To'Range) := To.all;
91
92             --  Set all elements of the original options to null to avoid
93             --  deallocation of copies.
94
95             To.all := (others => null);
96
97             Free (To);
98             To := New_Options;
99          end;
100       end if;
101
102       Last := Last + 1;
103       To (Last) := Option;
104    end Add;
105
106    procedure Add
107      (Option : String;
108       To     : in out String_List_Access;
109       Last   : in out Natural)
110    is
111    begin
112       Add (Option => new String'(Option), To => To, Last => Last);
113    end Add;
114
115    -----------------------
116    -- Add_Linker_Option --
117    -----------------------
118
119    procedure Add_Linker_Option (Option : String) is
120    begin
121       if Option'Length > 0 then
122          if Last_Linker_Option = Linker_Options_Buffer'Last then
123             declare
124                New_Buffer : constant String_List_Access :=
125                               new String_List
126                                 (1 .. Linker_Options_Buffer'Last +
127                                         Linker_Option_Initial_Count);
128             begin
129                New_Buffer (Linker_Options_Buffer'Range) :=
130                  Linker_Options_Buffer.all;
131                Linker_Options_Buffer.all := (others => null);
132                Free (Linker_Options_Buffer);
133                Linker_Options_Buffer := New_Buffer;
134             end;
135          end if;
136
137          Last_Linker_Option := Last_Linker_Option + 1;
138          Linker_Options_Buffer (Last_Linker_Option) := new String'(Option);
139       end if;
140    end Add_Linker_Option;
141
142    -------------------------
143    -- Base_Name_Index_For --
144    -------------------------
145
146    function Base_Name_Index_For
147      (Main            : String;
148       Main_Index      : Int;
149       Index_Separator : Character) return File_Name_Type
150    is
151       Result : File_Name_Type;
152
153    begin
154       Name_Len := 0;
155       Add_Str_To_Name_Buffer (Base_Name (Main));
156
157       --  Remove the extension, if any, that is the last part of the base name
158       --  starting with a dot and following some characters.
159
160       for J in reverse 2 .. Name_Len loop
161          if Name_Buffer (J) = '.' then
162             Name_Len := J - 1;
163             exit;
164          end if;
165       end loop;
166
167       --  Add the index info, if index is different from 0
168
169       if Main_Index > 0 then
170          Add_Char_To_Name_Buffer (Index_Separator);
171
172          declare
173             Img : constant String := Main_Index'Img;
174          begin
175             Add_Str_To_Name_Buffer (Img (2 .. Img'Last));
176          end;
177       end if;
178
179       Result := Name_Find;
180       return Result;
181    end Base_Name_Index_For;
182
183    ------------------------------
184    -- Check_Source_Info_In_ALI --
185    ------------------------------
186
187    function Check_Source_Info_In_ALI
188      (The_ALI : ALI_Id;
189       Tree    : Project_Tree_Ref) return Name_Id
190    is
191       Result    : Name_Id := No_Name;
192       Unit_Name : Name_Id;
193
194    begin
195       --  Loop through units
196
197       for U in ALIs.Table (The_ALI).First_Unit ..
198                ALIs.Table (The_ALI).Last_Unit
199       loop
200          --  Check if the file name is one of the source of the unit
201
202          Get_Name_String (Units.Table (U).Uname);
203          Name_Len  := Name_Len - 2;
204          Unit_Name := Name_Find;
205
206          if File_Not_A_Source_Of (Tree, Unit_Name, Units.Table (U).Sfile) then
207             return No_Name;
208          end if;
209
210          if Result = No_Name then
211             Result := Unit_Name;
212          end if;
213
214          --  Loop to do same check for each of the withed units
215
216          for W in Units.Table (U).First_With .. Units.Table (U).Last_With loop
217             declare
218                WR : ALI.With_Record renames Withs.Table (W);
219
220             begin
221                if WR.Sfile /= No_File then
222                   Get_Name_String (WR.Uname);
223                   Name_Len  := Name_Len - 2;
224                   Unit_Name := Name_Find;
225
226                   if File_Not_A_Source_Of (Tree, Unit_Name, WR.Sfile) then
227                      return No_Name;
228                   end if;
229                end if;
230             end;
231          end loop;
232       end loop;
233
234       --  Loop to check subunits and replaced sources
235
236       for D in ALIs.Table (The_ALI).First_Sdep ..
237                ALIs.Table (The_ALI).Last_Sdep
238       loop
239          declare
240             SD : Sdep_Record renames Sdep.Table (D);
241
242          begin
243             Unit_Name := SD.Subunit_Name;
244
245             if Unit_Name = No_Name then
246
247                --  Check if this source file has been replaced by a source with
248                --  a different file name.
249
250                if Tree /= null and then Tree.Replaced_Source_Number > 0 then
251                   declare
252                      Replacement : constant File_Name_Type :=
253                        Replaced_Source_HTable.Get
254                          (Tree.Replaced_Sources, SD.Sfile);
255
256                   begin
257                      if Replacement /= No_File then
258                         if Verbose_Mode then
259                            Write_Line
260                              ("source file" &
261                               Get_Name_String (SD.Sfile) &
262                               " has been replaced by " &
263                               Get_Name_String (Replacement));
264                         end if;
265
266                         return No_Name;
267                      end if;
268                   end;
269                end if;
270
271             else
272                --  For separates, the file is no longer associated with the
273                --  unit ("proc-sep.adb" is not associated with unit "proc.sep")
274                --  so we need to check whether the source file still exists in
275                --  the source tree: it will if it matches the naming scheme
276                --  (and then will be for the same unit).
277
278                if Find_Source
279                     (In_Tree   => Tree,
280                      Project   => No_Project,
281                      Base_Name => SD.Sfile) = No_Source
282                then
283                   --  If this is not a runtime file or if, when gnatmake switch
284                   --  -a is used, we are not able to find this subunit in the
285                   --  source directories, then recompilation is needed.
286
287                   if not Fname.Is_Internal_File_Name (SD.Sfile)
288                     or else
289                       (Check_Readonly_Files
290                         and then Full_Source_Name (SD.Sfile) = No_File)
291                   then
292                      if Verbose_Mode then
293                         Write_Line
294                           ("While parsing ALI file, file "
295                            & Get_Name_String (SD.Sfile)
296                            & " is indicated as containing subunit "
297                            & Get_Name_String (Unit_Name)
298                            & " but this does not match what was found while"
299                            & " parsing the project. Will recompile");
300                      end if;
301
302                      return No_Name;
303                   end if;
304                end if;
305             end if;
306          end;
307       end loop;
308
309       return Result;
310    end Check_Source_Info_In_ALI;
311
312    --------------------------------
313    -- Create_Binder_Mapping_File --
314    --------------------------------
315
316    function Create_Binder_Mapping_File
317      (Project_Tree : Project_Tree_Ref) return Path_Name_Type
318    is
319       Mapping_Path : Path_Name_Type := No_Path;
320
321       Mapping_FD : File_Descriptor := Invalid_FD;
322       --  A File Descriptor for an eventual mapping file
323
324       ALI_Unit : Unit_Name_Type := No_Unit_Name;
325       --  The unit name of an ALI file
326
327       ALI_Name : File_Name_Type := No_File;
328       --  The file name of the ALI file
329
330       ALI_Project : Project_Id := No_Project;
331       --  The project of the ALI file
332
333       Bytes : Integer;
334       OK    : Boolean := False;
335       Unit  : Unit_Index;
336
337       Status : Boolean;
338       --  For call to Close
339
340    begin
341       Tempdir.Create_Temp_File (Mapping_FD, Mapping_Path);
342       Record_Temp_File (Project_Tree.Shared, Mapping_Path);
343
344       if Mapping_FD /= Invalid_FD then
345          OK := True;
346
347          --  Traverse all units
348
349          Unit := Units_Htable.Get_First (Project_Tree.Units_HT);
350          while Unit /= No_Unit_Index loop
351             if Unit.Name /= No_Name then
352
353                --  If there is a body, put it in the mapping
354
355                if Unit.File_Names (Impl) /= No_Source
356                  and then Unit.File_Names (Impl).Project /= No_Project
357                then
358                   Get_Name_String (Unit.Name);
359                   Add_Str_To_Name_Buffer ("%b");
360                   ALI_Unit := Name_Find;
361                   ALI_Name :=
362                     Lib_File_Name (Unit.File_Names (Impl).Display_File);
363                   ALI_Project := Unit.File_Names (Impl).Project;
364
365                   --  Otherwise, if there is a spec, put it in the mapping
366
367                elsif Unit.File_Names (Spec) /= No_Source
368                  and then Unit.File_Names (Spec).Project /= No_Project
369                then
370                   Get_Name_String (Unit.Name);
371                   Add_Str_To_Name_Buffer ("%s");
372                   ALI_Unit := Name_Find;
373                   ALI_Name :=
374                     Lib_File_Name (Unit.File_Names (Spec).Display_File);
375                   ALI_Project := Unit.File_Names (Spec).Project;
376
377                else
378                   ALI_Name := No_File;
379                end if;
380
381                --  If we have something to put in the mapping then do it now.
382                --  However, if the project is extended, we don't put anything
383                --  in the mapping file, since we don't know where the ALI file
384                --  is: it might be in the extended project object directory as
385                --  well as in the extending project object directory.
386
387                if ALI_Name /= No_File
388                  and then ALI_Project.Extended_By = No_Project
389                  and then ALI_Project.Extends = No_Project
390                then
391                   --  First check if the ALI file exists. If it does not, do
392                   --  not put the unit in the mapping file.
393
394                   declare
395                      ALI : constant String := Get_Name_String (ALI_Name);
396
397                   begin
398                      --  For library projects, use the library ALI directory,
399                      --  for other projects, use the object directory.
400
401                      if ALI_Project.Library then
402                         Get_Name_String
403                           (ALI_Project.Library_ALI_Dir.Display_Name);
404                      else
405                         Get_Name_String
406                           (ALI_Project.Object_Directory.Display_Name);
407                      end if;
408
409                      Add_Str_To_Name_Buffer (ALI);
410                      Add_Char_To_Name_Buffer (ASCII.LF);
411
412                      declare
413                         ALI_Path_Name : constant String :=
414                                           Name_Buffer (1 .. Name_Len);
415
416                      begin
417                         if Is_Regular_File
418                              (ALI_Path_Name (1 .. ALI_Path_Name'Last - 1))
419                         then
420                            --  First line is the unit name
421
422                            Get_Name_String (ALI_Unit);
423                            Add_Char_To_Name_Buffer (ASCII.LF);
424                            Bytes :=
425                              Write
426                                (Mapping_FD,
427                                 Name_Buffer (1)'Address,
428                                 Name_Len);
429                            OK := Bytes = Name_Len;
430
431                            exit when not OK;
432
433                            --  Second line it the ALI file name
434
435                            Get_Name_String (ALI_Name);
436                            Add_Char_To_Name_Buffer (ASCII.LF);
437                            Bytes :=
438                              Write
439                                (Mapping_FD,
440                                 Name_Buffer (1)'Address,
441                                 Name_Len);
442                            OK := (Bytes = Name_Len);
443
444                            exit when not OK;
445
446                            --  Third line it the ALI path name
447
448                            Bytes :=
449                              Write
450                                (Mapping_FD,
451                                 ALI_Path_Name (1)'Address,
452                                 ALI_Path_Name'Length);
453                            OK := (Bytes = ALI_Path_Name'Length);
454
455                            --  If OK is False, it means we were unable to
456                            --  write a line. No point in continuing with the
457                            --  other units.
458
459                            exit when not OK;
460                         end if;
461                      end;
462                   end;
463                end if;
464             end if;
465
466             Unit := Units_Htable.Get_Next (Project_Tree.Units_HT);
467          end loop;
468
469          Close (Mapping_FD, Status);
470
471          OK := OK and Status;
472       end if;
473
474       --  If the creation of the mapping file was successful, we add the switch
475       --  to the arguments of gnatbind.
476
477       if OK then
478          return Mapping_Path;
479
480       else
481          return No_Path;
482       end if;
483    end Create_Binder_Mapping_File;
484
485    -----------------
486    -- Create_Name --
487    -----------------
488
489    function Create_Name (Name : String) return File_Name_Type is
490    begin
491       Name_Len := 0;
492       Add_Str_To_Name_Buffer (Name);
493       return Name_Find;
494    end Create_Name;
495
496    function Create_Name (Name : String) return Name_Id is
497    begin
498       Name_Len := 0;
499       Add_Str_To_Name_Buffer (Name);
500       return Name_Find;
501    end Create_Name;
502
503    function Create_Name (Name : String) return Path_Name_Type is
504    begin
505       Name_Len := 0;
506       Add_Str_To_Name_Buffer (Name);
507       return Name_Find;
508    end Create_Name;
509
510    ----------------------------
511    -- Executable_Prefix_Path --
512    ----------------------------
513
514    function Executable_Prefix_Path return String is
515       Exec_Name : constant String := Command_Name;
516
517       function Get_Install_Dir (S : String) return String;
518       --  S is the executable name preceded by the absolute or relative path,
519       --  e.g. "c:\usr\bin\gcc.exe". Returns the absolute directory where "bin"
520       --  lies (in the example "C:\usr"). If the executable is not in a "bin"
521       --  directory, return "".
522
523       ---------------------
524       -- Get_Install_Dir --
525       ---------------------
526
527       function Get_Install_Dir (S : String) return String is
528          Exec      : String  := S;
529          Path_Last : Integer := 0;
530
531       begin
532          for J in reverse Exec'Range loop
533             if Exec (J) = Directory_Separator then
534                Path_Last := J - 1;
535                exit;
536             end if;
537          end loop;
538
539          if Path_Last >= Exec'First + 2 then
540             To_Lower (Exec (Path_Last - 2 .. Path_Last));
541          end if;
542
543          if Path_Last < Exec'First + 2
544            or else Exec (Path_Last - 2 .. Path_Last) /= "bin"
545            or else (Path_Last - 3 >= Exec'First
546                      and then Exec (Path_Last - 3) /= Directory_Separator)
547          then
548             return "";
549          end if;
550
551          return Normalize_Pathname
552                   (Exec (Exec'First .. Path_Last - 4),
553                    Resolve_Links => Opt.Follow_Links_For_Dirs)
554            & Directory_Separator;
555       end Get_Install_Dir;
556
557    --  Beginning of Executable_Prefix_Path
558
559    begin
560       --  For VMS, the path returned is always /gnu/
561
562       if Hostparm.OpenVMS then
563          return "/gnu/";
564       end if;
565
566       --  First determine if a path prefix was placed in front of the
567       --  executable name.
568
569       for J in reverse Exec_Name'Range loop
570          if Exec_Name (J) = Directory_Separator then
571             return Get_Install_Dir (Exec_Name);
572          end if;
573       end loop;
574
575       --  If we get here, the user has typed the executable name with no
576       --  directory prefix.
577
578       declare
579          Path : String_Access := Locate_Exec_On_Path (Exec_Name);
580       begin
581          if Path = null then
582             return "";
583          else
584             declare
585                Dir : constant String := Get_Install_Dir (Path.all);
586             begin
587                Free (Path);
588                return Dir;
589             end;
590          end if;
591       end;
592    end Executable_Prefix_Path;
593
594    ------------------
595    -- Fail_Program --
596    ------------------
597
598    procedure Fail_Program
599      (Project_Tree   : Project_Tree_Ref;
600       S              : String;
601       Flush_Messages : Boolean := True)
602    is
603    begin
604       if Flush_Messages then
605          if Total_Errors_Detected /= 0 or else Warnings_Detected /= 0 then
606             Errutil.Finalize;
607          end if;
608       end if;
609
610       Finish_Program (Project_Tree, E_Fatal, S => S);
611    end Fail_Program;
612
613    --------------------
614    -- Finish_Program --
615    --------------------
616
617    procedure Finish_Program
618      (Project_Tree : Project_Tree_Ref;
619       Exit_Code    : Osint.Exit_Code_Type := Osint.E_Success;
620       S            : String := "")
621    is
622    begin
623       if not Debug.Debug_Flag_N then
624          Delete_Temp_Config_Files (Project_Tree);
625
626          if Project_Tree /= null then
627             Delete_All_Temp_Files (Project_Tree.Shared);
628          end if;
629       end if;
630
631       if S'Length > 0 then
632          if Exit_Code /= E_Success then
633             Osint.Fail (S);
634          else
635             Write_Str (S);
636          end if;
637       end if;
638
639       --  Output Namet statistics
640
641       Namet.Finalize;
642
643       Exit_Program (Exit_Code);
644    end Finish_Program;
645
646    --------------------------
647    -- File_Not_A_Source_Of --
648    --------------------------
649
650    function File_Not_A_Source_Of
651      (Project_Tree : Project_Tree_Ref;
652       Uname        : Name_Id;
653       Sfile        : File_Name_Type) return Boolean
654    is
655       Unit : constant Unit_Index :=
656                Units_Htable.Get (Project_Tree.Units_HT, Uname);
657
658       At_Least_One_File : Boolean := False;
659
660    begin
661       if Unit /= No_Unit_Index then
662          for F in Unit.File_Names'Range loop
663             if Unit.File_Names (F) /= null then
664                At_Least_One_File := True;
665                if Unit.File_Names (F).File = Sfile then
666                   return False;
667                end if;
668             end if;
669          end loop;
670
671          if not At_Least_One_File then
672
673             --  The unit was probably created initially for a separate unit
674             --  (which are initially created as IMPL when both suffixes are the
675             --  same). Later on, Override_Kind changed the type of the file,
676             --  and the unit is no longer valid in fact.
677
678             return False;
679          end if;
680
681          Verbose_Msg (Uname, "sources do not include ", Name_Id (Sfile));
682          return True;
683       end if;
684
685       return False;
686    end File_Not_A_Source_Of;
687
688    ---------------------
689    -- Get_Directories --
690    ---------------------
691
692    procedure Get_Directories
693      (Project_Tree : Project_Tree_Ref;
694       For_Project  : Project_Id;
695       Activity     : Activity_Type;
696       Languages    : Name_Ids)
697    is
698
699       procedure Recursive_Add
700         (Project  : Project_Id;
701          Tree     : Project_Tree_Ref;
702          Extended : in out Boolean);
703       --  Add all the source directories of a project to the path only if
704       --  this project has not been visited. Calls itself recursively for
705       --  projects being extended, and imported projects.
706
707       procedure Add_Dir (Value : Path_Name_Type);
708       --  Add directory Value in table Directories, if it is defined and not
709       --  already there.
710
711       -------------
712       -- Add_Dir --
713       -------------
714
715       procedure Add_Dir (Value : Path_Name_Type) is
716          Add_It : Boolean := True;
717
718       begin
719          if Value /= No_Path then
720             for Index in 1 .. Directories.Last loop
721                if Directories.Table (Index) = Value then
722                   Add_It := False;
723                   exit;
724                end if;
725             end loop;
726
727             if Add_It then
728                Directories.Increment_Last;
729                Directories.Table (Directories.Last) := Value;
730             end if;
731          end if;
732       end Add_Dir;
733
734       -------------------
735       -- Recursive_Add --
736       -------------------
737
738       procedure Recursive_Add
739         (Project  : Project_Id;
740          Tree     : Project_Tree_Ref;
741          Extended : in out Boolean)
742       is
743          Current   : String_List_Id;
744          Dir       : String_Element;
745          OK        : Boolean := False;
746          Lang_Proc : Language_Ptr := Project.Languages;
747
748       begin
749          --  Add to path all directories of this project
750
751          if Activity = Compilation then
752             Lang_Loop :
753             while Lang_Proc /= No_Language_Index loop
754                for J in Languages'Range loop
755                   OK := Lang_Proc.Name = Languages (J);
756                   exit Lang_Loop when OK;
757                end loop;
758
759                Lang_Proc := Lang_Proc.Next;
760             end loop Lang_Loop;
761
762             if OK then
763                Current := Project.Source_Dirs;
764
765                while Current /= Nil_String loop
766                   Dir := Tree.Shared.String_Elements.Table (Current);
767                   Add_Dir (Path_Name_Type (Dir.Value));
768                   Current := Dir.Next;
769                end loop;
770             end if;
771
772          elsif Project.Library then
773             if Activity = SAL_Binding and then Extended then
774                Add_Dir (Project.Object_Directory.Display_Name);
775
776             else
777                Add_Dir (Project.Library_ALI_Dir.Display_Name);
778             end if;
779
780          else
781             Add_Dir (Project.Object_Directory.Display_Name);
782          end if;
783
784          if Project.Extends = No_Project then
785             Extended := False;
786          end if;
787       end Recursive_Add;
788
789       procedure For_All_Projects is
790         new For_Every_Project_Imported (Boolean, Recursive_Add);
791
792       Extended : Boolean := True;
793
794       --  Start of processing for Get_Directories
795
796    begin
797       Directories.Init;
798       For_All_Projects (For_Project, Project_Tree, Extended);
799    end Get_Directories;
800
801    ------------------
802    -- Get_Switches --
803    ------------------
804
805    procedure Get_Switches
806      (Source       : Prj.Source_Id;
807       Pkg_Name     : Name_Id;
808       Project_Tree : Project_Tree_Ref;
809       Value        : out Variable_Value;
810       Is_Default   : out Boolean)
811    is
812    begin
813       Get_Switches
814         (Source_File  => Source.File,
815          Source_Lang  => Source.Language.Name,
816          Source_Prj   => Source.Project,
817          Pkg_Name     => Pkg_Name,
818          Project_Tree => Project_Tree,
819          Value        => Value,
820          Is_Default   => Is_Default);
821    end Get_Switches;
822
823    ------------------
824    -- Get_Switches --
825    ------------------
826
827    procedure Get_Switches
828      (Source_File         : File_Name_Type;
829       Source_Lang         : Name_Id;
830       Source_Prj          : Project_Id;
831       Pkg_Name            : Name_Id;
832       Project_Tree        : Project_Tree_Ref;
833       Value               : out Variable_Value;
834       Is_Default          : out Boolean;
835       Test_Without_Suffix : Boolean := False;
836       Check_ALI_Suffix    : Boolean := False)
837    is
838       Project : constant Project_Id :=
839                   Ultimate_Extending_Project_Of (Source_Prj);
840       Pkg     : constant Package_Id :=
841                   Prj.Util.Value_Of
842                     (Name        => Pkg_Name,
843                      In_Packages => Project.Decl.Packages,
844                      Shared      => Project_Tree.Shared);
845       Lang : Language_Ptr;
846
847    begin
848       Is_Default := False;
849
850       if Source_File /= No_File then
851          Value := Prj.Util.Value_Of
852            (Name                    => Name_Id (Source_File),
853             Attribute_Or_Array_Name => Name_Switches,
854             In_Package              => Pkg,
855             Shared                  => Project_Tree.Shared,
856             Allow_Wildcards         => True);
857       end if;
858
859       if Value = Nil_Variable_Value and then Test_Without_Suffix then
860          Lang :=
861            Get_Language_From_Name (Project, Get_Name_String (Source_Lang));
862
863          if Lang /= null then
864             declare
865                Naming      : Lang_Naming_Data renames Lang.Config.Naming_Data;
866                SF_Name     : constant String := Get_Name_String (Source_File);
867                Last        : Positive := SF_Name'Length;
868                Name        : String (1 .. Last + 3);
869                Spec_Suffix : String   := Get_Name_String (Naming.Spec_Suffix);
870                Body_Suffix : String   := Get_Name_String (Naming.Body_Suffix);
871                Truncated   : Boolean  := False;
872
873             begin
874                Canonical_Case_File_Name (Spec_Suffix);
875                Canonical_Case_File_Name (Body_Suffix);
876                Name (1 .. Last) := SF_Name;
877
878                if Last > Body_Suffix'Length
879                  and then
880                    Name (Last - Body_Suffix'Length + 1 .. Last) = Body_Suffix
881                then
882                   Truncated := True;
883                   Last := Last - Body_Suffix'Length;
884                end if;
885
886                if not Truncated
887                  and then Last > Spec_Suffix'Length
888                  and then
889                    Name (Last - Spec_Suffix'Length + 1 .. Last) = Spec_Suffix
890                then
891                   Truncated := True;
892                   Last := Last - Spec_Suffix'Length;
893                end if;
894
895                if Truncated then
896                   Name_Len := 0;
897                   Add_Str_To_Name_Buffer (Name (1 .. Last));
898
899                   Value := Prj.Util.Value_Of
900                     (Name                    => Name_Find,
901                      Attribute_Or_Array_Name => Name_Switches,
902                      In_Package              => Pkg,
903                      Shared                  => Project_Tree.Shared,
904                      Allow_Wildcards         => True);
905                end if;
906
907                if Value = Nil_Variable_Value and then Check_ALI_Suffix then
908                   Last := SF_Name'Length;
909                   while Name (Last) /= '.' loop
910                      Last := Last - 1;
911                   end loop;
912
913                   Name_Len := 0;
914                   Add_Str_To_Name_Buffer (Name (1 .. Last));
915                   Add_Str_To_Name_Buffer ("ali");
916
917                   Value := Prj.Util.Value_Of
918                     (Name                    => Name_Find,
919                      Attribute_Or_Array_Name => Name_Switches,
920                      In_Package              => Pkg,
921                      Shared                  => Project_Tree.Shared,
922                      Allow_Wildcards         => True);
923                end if;
924             end;
925          end if;
926       end if;
927
928       if Value = Nil_Variable_Value then
929          Is_Default := True;
930          Value :=
931            Prj.Util.Value_Of
932              (Name                    => Source_Lang,
933               Attribute_Or_Array_Name => Name_Switches,
934               In_Package              => Pkg,
935               Shared                  => Project_Tree.Shared,
936               Force_Lower_Case_Index  => True);
937       end if;
938
939       if Value = Nil_Variable_Value then
940          Value :=
941            Prj.Util.Value_Of
942              (Name                    => All_Other_Names,
943               Attribute_Or_Array_Name => Name_Switches,
944               In_Package              => Pkg,
945               Shared                  => Project_Tree.Shared,
946               Force_Lower_Case_Index  => True);
947       end if;
948
949       if Value = Nil_Variable_Value then
950          Value :=
951            Prj.Util.Value_Of
952              (Name                    => Source_Lang,
953               Attribute_Or_Array_Name => Name_Default_Switches,
954               In_Package              => Pkg,
955               Shared                  => Project_Tree.Shared);
956       end if;
957    end Get_Switches;
958
959    ------------
960    -- Inform --
961    ------------
962
963    procedure Inform (N : File_Name_Type; Msg : String) is
964    begin
965       Inform (Name_Id (N), Msg);
966    end Inform;
967
968    procedure Inform (N : Name_Id := No_Name; Msg : String) is
969    begin
970       Osint.Write_Program_Name;
971
972       Write_Str (": ");
973
974       if N /= No_Name then
975          Write_Str ("""");
976
977          declare
978             Name : constant String := Get_Name_String (N);
979          begin
980             if Debug.Debug_Flag_F and then Is_Absolute_Path (Name) then
981                Write_Str (File_Name (Name));
982             else
983                Write_Str (Name);
984             end if;
985          end;
986
987          Write_Str (""" ");
988       end if;
989
990       Write_Str (Msg);
991       Write_Eol;
992    end Inform;
993
994    ------------------------------
995    -- Initialize_Source_Record --
996    ------------------------------
997
998    procedure Initialize_Source_Record (Source : Prj.Source_Id) is
999
1000       procedure Set_Object_Project
1001         (Obj_Dir  : String;
1002          Obj_Proj : Project_Id;
1003          Obj_Path : Path_Name_Type;
1004          Stamp    : Time_Stamp_Type);
1005       --  Update information about object file, switches file,...
1006
1007       ------------------------
1008       -- Set_Object_Project --
1009       ------------------------
1010
1011       procedure Set_Object_Project
1012         (Obj_Dir  : String;
1013          Obj_Proj : Project_Id;
1014          Obj_Path : Path_Name_Type;
1015          Stamp    : Time_Stamp_Type) is
1016       begin
1017          Source.Object_Project := Obj_Proj;
1018          Source.Object_Path    := Obj_Path;
1019          Source.Object_TS      := Stamp;
1020
1021          if Source.Language.Config.Dependency_Kind /= None then
1022             declare
1023                Dep_Path : constant String :=
1024                             Normalize_Pathname
1025                               (Name          =>
1026                                  Get_Name_String (Source.Dep_Name),
1027                                Resolve_Links => Opt.Follow_Links_For_Files,
1028                                Directory     => Obj_Dir);
1029             begin
1030                Source.Dep_Path := Create_Name (Dep_Path);
1031                Source.Dep_TS   := Osint.Unknown_Attributes;
1032             end;
1033          end if;
1034
1035          --  Get the path of the switches file, even if Opt.Check_Switches is
1036          --  not set, as switch -s may be in the Builder switches that have not
1037          --  been scanned yet.
1038
1039          declare
1040             Switches_Path : constant String :=
1041                               Normalize_Pathname
1042                                 (Name          =>
1043                                    Get_Name_String (Source.Switches),
1044                                  Resolve_Links => Opt.Follow_Links_For_Files,
1045                                  Directory     => Obj_Dir);
1046          begin
1047             Source.Switches_Path := Create_Name (Switches_Path);
1048
1049             if Stamp /= Empty_Time_Stamp then
1050                Source.Switches_TS := File_Stamp (Source.Switches_Path);
1051             end if;
1052          end;
1053       end Set_Object_Project;
1054
1055       Obj_Proj : Project_Id;
1056
1057    begin
1058       --  Nothing to do if source record has already been fully initialized
1059
1060       if Source.Initialized then
1061          return;
1062       end if;
1063
1064       --  Systematically recompute the time stamp
1065
1066       Source.Source_TS := File_Stamp (Source.Path.Display_Name);
1067
1068       --  Parse the source file to check whether we have a subunit
1069
1070       if Source.Language.Config.Kind = Unit_Based
1071         and then Source.Kind = Impl
1072         and then Is_Subunit (Source)
1073       then
1074          Source.Kind := Sep;
1075       end if;
1076
1077       if Source.Language.Config.Object_Generated
1078         and then Is_Compilable (Source)
1079       then
1080          --  First, get the correct object file name and dependency file name
1081          --  if the source is in a multi-unit file.
1082
1083          if Source.Index /= 0 then
1084             Source.Object :=
1085               Object_Name
1086                 (Source_File_Name   => Source.File,
1087                  Source_Index       => Source.Index,
1088                  Index_Separator    =>
1089                    Source.Language.Config.Multi_Unit_Object_Separator,
1090                  Object_File_Suffix =>
1091                    Source.Language.Config.Object_File_Suffix);
1092
1093             Source.Dep_Name :=
1094               Dependency_Name
1095                 (Source.Object, Source.Language.Config.Dependency_Kind);
1096          end if;
1097
1098          --  Find the object file for that source. It could be either in the
1099          --  current project or in an extended project (it might actually not
1100          --  exist yet in the ultimate extending project, but if not found
1101          --  elsewhere that's where we'll expect to find it).
1102
1103          Obj_Proj := Source.Project;
1104
1105          while Obj_Proj /= No_Project loop
1106             declare
1107                Dir  : constant String :=
1108                         Get_Name_String
1109                           (Obj_Proj.Object_Directory.Display_Name);
1110
1111                Object_Path : constant String :=
1112                                Normalize_Pathname
1113                                  (Name          =>
1114                                     Get_Name_String (Source.Object),
1115                                   Resolve_Links => Opt.Follow_Links_For_Files,
1116                                   Directory     => Dir);
1117
1118                Obj_Path : constant Path_Name_Type := Create_Name (Object_Path);
1119                Stamp    : Time_Stamp_Type := Empty_Time_Stamp;
1120
1121             begin
1122                --  For specs, we do not check object files if there is a body.
1123                --  This saves a system call. On the other hand, we do need to
1124                --  know the object_path, in case the user has passed the .ads
1125                --  on the command line to compile the spec only.
1126
1127                if Source.Kind /= Spec
1128                  or else Source.Unit = No_Unit_Index
1129                  or else Source.Unit.File_Names (Impl) = No_Source
1130                then
1131                   Stamp := File_Stamp (Obj_Path);
1132                end if;
1133
1134                if Stamp /= Empty_Time_Stamp
1135                  or else (Obj_Proj.Extended_By = No_Project
1136                           and then Source.Object_Project = No_Project)
1137                then
1138                   Set_Object_Project (Dir, Obj_Proj, Obj_Path, Stamp);
1139                end if;
1140
1141                Obj_Proj := Obj_Proj.Extended_By;
1142             end;
1143          end loop;
1144
1145       elsif Source.Language.Config.Dependency_Kind = Makefile then
1146          declare
1147             Object_Dir : constant String :=
1148                            Get_Name_String
1149                              (Source.Project.Object_Directory.Display_Name);
1150             Dep_Path   : constant String :=
1151                            Normalize_Pathname
1152                              (Name        => Get_Name_String (Source.Dep_Name),
1153                               Resolve_Links =>
1154                                 Opt.Follow_Links_For_Files,
1155                               Directory     => Object_Dir);
1156          begin
1157             Source.Dep_Path := Create_Name (Dep_Path);
1158             Source.Dep_TS   := Osint.Unknown_Attributes;
1159          end;
1160       end if;
1161
1162       Source.Initialized := True;
1163    end Initialize_Source_Record;
1164
1165    ----------------------------
1166    -- Is_External_Assignment --
1167    ----------------------------
1168
1169    function Is_External_Assignment
1170      (Env  : Prj.Tree.Environment;
1171       Argv : String) return Boolean
1172    is
1173       Start     : Positive := 3;
1174       Finish    : Natural := Argv'Last;
1175
1176       pragma Assert (Argv'First = 1);
1177       pragma Assert (Argv (1 .. 2) = "-X");
1178
1179    begin
1180       if Argv'Last < 5 then
1181          return False;
1182
1183       elsif Argv (3) = '"' then
1184          if Argv (Argv'Last) /= '"' or else Argv'Last < 7 then
1185             return False;
1186          else
1187             Start := 4;
1188             Finish := Argv'Last - 1;
1189          end if;
1190       end if;
1191
1192       return Prj.Ext.Check
1193         (Self        => Env.External,
1194          Declaration => Argv (Start .. Finish));
1195    end Is_External_Assignment;
1196
1197    ----------------
1198    -- Is_Subunit --
1199    ----------------
1200
1201    function Is_Subunit (Source : Prj.Source_Id) return Boolean is
1202       Src_Ind : Source_File_Index;
1203
1204    begin
1205       if Source.Kind = Sep then
1206          return True;
1207
1208       --  A Spec, a file based language source or a body with a spec cannot be
1209       --  a subunit.
1210
1211       elsif Source.Kind = Spec
1212         or else Source.Unit = No_Unit_Index
1213         or else Other_Part (Source) /= No_Source
1214       then
1215          return False;
1216       end if;
1217
1218       --  Here, we are assuming that the language is Ada, as it is the only
1219       --  unit based language that we know.
1220
1221       Src_Ind :=
1222         Sinput.P.Load_Project_File
1223           (Get_Name_String (Source.Path.Display_Name));
1224
1225       return Sinput.P.Source_File_Is_Subunit (Src_Ind);
1226    end Is_Subunit;
1227
1228    -----------------------------
1229    -- Linker_Options_Switches --
1230    -----------------------------
1231
1232    function Linker_Options_Switches
1233      (Project  : Project_Id;
1234       Do_Fail  : Fail_Proc;
1235       In_Tree  : Project_Tree_Ref) return String_List
1236    is
1237       procedure Recursive_Add
1238         (Proj    : Project_Id;
1239          In_Tree : Project_Tree_Ref;
1240          Dummy   : in out Boolean);
1241       --  The recursive routine used to add linker options
1242
1243       -------------------
1244       -- Recursive_Add --
1245       -------------------
1246
1247       procedure Recursive_Add
1248         (Proj    : Project_Id;
1249          In_Tree : Project_Tree_Ref;
1250          Dummy   : in out Boolean)
1251       is
1252          pragma Unreferenced (Dummy);
1253
1254          Linker_Package : Package_Id;
1255          Options        : Variable_Value;
1256
1257       begin
1258          Linker_Package :=
1259            Prj.Util.Value_Of
1260              (Name        => Name_Linker,
1261               In_Packages => Proj.Decl.Packages,
1262               Shared      => In_Tree.Shared);
1263
1264          Options :=
1265            Prj.Util.Value_Of
1266              (Name                    => Name_Ada,
1267               Index                   => 0,
1268               Attribute_Or_Array_Name => Name_Linker_Options,
1269               In_Package              => Linker_Package,
1270               Shared                  => In_Tree.Shared);
1271
1272          --  If attribute is present, add the project with the attribute to
1273          --  table Linker_Opts.
1274
1275          if Options /= Nil_Variable_Value then
1276             Linker_Opts.Increment_Last;
1277             Linker_Opts.Table (Linker_Opts.Last) :=
1278               (Project => Proj, Options => Options.Values);
1279          end if;
1280       end Recursive_Add;
1281
1282       procedure For_All_Projects is
1283         new For_Every_Project_Imported (Boolean, Recursive_Add);
1284
1285       Dummy : Boolean := False;
1286
1287    --  Start of processing for Linker_Options_Switches
1288
1289    begin
1290       Linker_Opts.Init;
1291
1292       For_All_Projects (Project, In_Tree, Dummy, Imported_First => True);
1293
1294       Last_Linker_Option := 0;
1295
1296       for Index in reverse 1 .. Linker_Opts.Last loop
1297          declare
1298             Options  : String_List_Id;
1299             Proj     : constant Project_Id :=
1300                          Linker_Opts.Table (Index).Project;
1301             Option   : Name_Id;
1302             Dir_Path : constant String :=
1303                          Get_Name_String (Proj.Directory.Name);
1304
1305          begin
1306             Options := Linker_Opts.Table (Index).Options;
1307             while Options /= Nil_String loop
1308                Option := In_Tree.Shared.String_Elements.Table (Options).Value;
1309                Get_Name_String (Option);
1310
1311                --  Do not consider empty linker options
1312
1313                if Name_Len /= 0 then
1314                   Add_Linker_Option (Name_Buffer (1 .. Name_Len));
1315
1316                   --  Object files and -L switches specified with relative
1317                   --  paths must be converted to absolute paths.
1318
1319                   Test_If_Relative_Path
1320                     (Switch  => Linker_Options_Buffer (Last_Linker_Option),
1321                      Parent  => Dir_Path,
1322                      Do_Fail => Do_Fail,
1323                      Including_L_Switch => True);
1324                end if;
1325
1326                Options := In_Tree.Shared.String_Elements.Table (Options).Next;
1327             end loop;
1328          end;
1329       end loop;
1330
1331       return Linker_Options_Buffer (1 .. Last_Linker_Option);
1332    end Linker_Options_Switches;
1333
1334    -----------
1335    -- Mains --
1336    -----------
1337
1338    package body Mains is
1339
1340       package Names is new Table.Table
1341         (Table_Component_Type => Main_Info,
1342          Table_Index_Type     => Integer,
1343          Table_Low_Bound      => 1,
1344          Table_Initial        => 10,
1345          Table_Increment      => 100,
1346          Table_Name           => "Makeutl.Mains.Names");
1347       --  The table that stores the mains
1348
1349       Current : Natural := 0;
1350       --  The index of the last main retrieved from the table
1351
1352       Count_Of_Mains_With_No_Tree : Natural := 0;
1353       --  Number of main units for which we do not know the project tree
1354
1355       --------------
1356       -- Add_Main --
1357       --------------
1358
1359       procedure Add_Main
1360         (Name     : String;
1361          Index    : Int := 0;
1362          Location : Source_Ptr := No_Location;
1363          Project  : Project_Id := No_Project;
1364          Tree     : Project_Tree_Ref := null)
1365       is
1366       begin
1367          if Current_Verbosity = High then
1368             Debug_Output ("Add_Main """ & Name & """ " & Index'Img
1369                           & " with_tree? "
1370                           & Boolean'Image (Tree /= null));
1371          end if;
1372
1373          Name_Len := 0;
1374          Add_Str_To_Name_Buffer (Name);
1375          Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1376
1377          Names.Increment_Last;
1378          Names.Table (Names.Last) :=
1379            (Name_Find, Index, Location, No_Source, Project, Tree);
1380
1381          if Tree /= null then
1382             Builder_Data (Tree).Number_Of_Mains :=
1383               Builder_Data (Tree).Number_Of_Mains + 1;
1384
1385          else
1386             Mains.Count_Of_Mains_With_No_Tree :=
1387               Mains.Count_Of_Mains_With_No_Tree + 1;
1388          end if;
1389       end Add_Main;
1390
1391       --------------------
1392       -- Complete_Mains --
1393       --------------------
1394
1395       procedure Complete_Mains
1396         (Flags        : Processing_Flags;
1397          Root_Project : Project_Id;
1398          Project_Tree : Project_Tree_Ref)
1399       is
1400          procedure Do_Complete (Project : Project_Id; Tree : Project_Tree_Ref);
1401          --  Check the mains for this specific project
1402
1403          procedure Complete_All is new For_Project_And_Aggregated
1404            (Do_Complete);
1405
1406          procedure Add_Multi_Unit_Sources
1407            (Tree   : Project_Tree_Ref;
1408             Source : Prj.Source_Id);
1409          --  Add all units from the same file as the multi-unit Source
1410
1411          function Find_File_Add_Extension
1412            (Tree      : Project_Tree_Ref;
1413             Base_Main : String) return Prj.Source_Id;
1414          --  Search for Main in the project, adding body or spec extensions
1415
1416          ----------------------------
1417          -- Add_Multi_Unit_Sources --
1418          ----------------------------
1419
1420          procedure Add_Multi_Unit_Sources
1421            (Tree   : Project_Tree_Ref;
1422             Source : Prj.Source_Id)
1423          is
1424             Iter : Source_Iterator;
1425             Src  : Prj.Source_Id;
1426
1427          begin
1428             Debug_Output
1429               ("found multi-unit source file in project", Source.Project.Name);
1430
1431             Iter := For_Each_Source
1432               (In_Tree => Tree, Project => Source.Project);
1433
1434             while Element (Iter) /= No_Source loop
1435                Src := Element (Iter);
1436
1437                if Src.File = Source.File
1438                  and then Src.Index /= Source.Index
1439                then
1440                   if Src.File = Source.File then
1441                      Debug_Output
1442                        ("add main in project, index=" & Src.Index'Img);
1443                   end if;
1444
1445                   Names.Increment_Last;
1446                   Names.Table (Names.Last) :=
1447                     (File     => Src.File,
1448                      Index    => Src.Index,
1449                      Location => No_Location,
1450                      Source   => Src,
1451                      Project  => Src.Project,
1452                      Tree     => Tree);
1453
1454                   Builder_Data (Tree).Number_Of_Mains :=
1455                     Builder_Data (Tree).Number_Of_Mains + 1;
1456                end if;
1457
1458                Next (Iter);
1459             end loop;
1460          end Add_Multi_Unit_Sources;
1461
1462          -----------------------------
1463          -- Find_File_Add_Extension --
1464          -----------------------------
1465
1466          function Find_File_Add_Extension
1467            (Tree      : Project_Tree_Ref;
1468             Base_Main : String) return Prj.Source_Id
1469          is
1470             Spec_Source : Prj.Source_Id := No_Source;
1471             Source      : Prj.Source_Id;
1472             Iter        : Source_Iterator;
1473             Suffix      : File_Name_Type;
1474
1475          begin
1476             Source := No_Source;
1477             Iter := For_Each_Source (Tree);  --  In all projects
1478             loop
1479                Source := Prj.Element (Iter);
1480                exit when Source = No_Source;
1481
1482                if Source.Kind = Impl then
1483                   Get_Name_String (Source.File);
1484
1485                   if Name_Len > Base_Main'Length
1486                     and then Name_Buffer (1 .. Base_Main'Length) = Base_Main
1487                   then
1488                      Suffix :=
1489                        Source.Language.Config.Naming_Data.Body_Suffix;
1490
1491                      if Suffix /= No_File then
1492                         declare
1493                            Suffix_Str : String := Get_Name_String (Suffix);
1494                         begin
1495                            Canonical_Case_File_Name (Suffix_Str);
1496                            exit when
1497                              Name_Buffer (Base_Main'Length + 1 .. Name_Len) =
1498                              Suffix_Str;
1499                         end;
1500                      end if;
1501                   end if;
1502
1503                elsif Source.Kind = Spec then
1504                   --  A spec needs to be taken into account unless there is
1505                   --  also a body. So we delay the decision for them.
1506
1507                   Get_Name_String (Source.File);
1508
1509                   if Name_Len > Base_Main'Length
1510                     and then Name_Buffer (1 .. Base_Main'Length) = Base_Main
1511                   then
1512                      Suffix := Source.Language.Config.Naming_Data.Spec_Suffix;
1513
1514                      if Suffix /= No_File then
1515                         declare
1516                            Suffix_Str : String := Get_Name_String (Suffix);
1517
1518                         begin
1519                            Canonical_Case_File_Name (Suffix_Str);
1520
1521                            if Name_Buffer (Base_Main'Length + 1 .. Name_Len) =
1522                              Suffix_Str
1523                            then
1524                               Spec_Source := Source;
1525                            end if;
1526                         end;
1527                      end if;
1528                   end if;
1529                end if;
1530
1531                Next (Iter);
1532             end loop;
1533
1534             if Source = No_Source then
1535                Source := Spec_Source;
1536             end if;
1537
1538             return Source;
1539          end Find_File_Add_Extension;
1540
1541          -----------------
1542          -- Do_Complete --
1543          -----------------
1544
1545          procedure Do_Complete
1546            (Project : Project_Id; Tree : Project_Tree_Ref)
1547          is
1548             J : Integer;
1549
1550          begin
1551             if Mains.Number_Of_Mains (Tree) > 0
1552               or else Mains.Count_Of_Mains_With_No_Tree > 0
1553             then
1554                --  Traverse in reverse order, since in the case of multi-unit
1555                --  files we will be adding extra files at the end, and there's
1556                --  no need to process them in turn.
1557
1558                J := Names.Last;
1559                loop
1560                   declare
1561                      File        : Main_Info       := Names.Table (J);
1562                      Main_Id     : File_Name_Type  := File.File;
1563                      Main        : constant String :=
1564                                      Get_Name_String (Main_Id);
1565                      Base        : constant String := Base_Name (Main);
1566                      Source      : Prj.Source_Id   := No_Source;
1567                      Is_Absolute : Boolean         := False;
1568
1569                   begin
1570                      if Base /= Main then
1571                         Is_Absolute := True;
1572
1573                         if Is_Absolute_Path (Main) then
1574                            Main_Id := Create_Name (Base);
1575
1576                         --  Not an absolute path
1577
1578                         else
1579                            --  Always resolve links here, so that users can be
1580                            --  specify any name on the command line. If the
1581                            --  project itself uses links, the user will be
1582                            --  using -eL anyway, and thus files are also stored
1583                            --  with resolved names.
1584
1585                            declare
1586                               Absolute : constant String :=
1587                                            Normalize_Pathname
1588                                              (Name           => Main,
1589                                               Directory      => "",
1590                                               Resolve_Links  => True,
1591                                               Case_Sensitive => False);
1592                            begin
1593                               File.File := Create_Name (Absolute);
1594                               Main_Id := Create_Name (Base);
1595                            end;
1596                         end if;
1597                      end if;
1598
1599                      --  If no project or tree was specified for the main, it
1600                      --  came from the command line.
1601                      --  Note that the assignments below will not modify inside
1602                      --  the table itself.
1603
1604                      if File.Project = null then
1605                         File.Project := Project;
1606                      end if;
1607
1608                      if File.Tree = null then
1609                         File.Tree := Tree;
1610                      end if;
1611
1612                      if File.Source = null then
1613                         if Current_Verbosity = High then
1614                            Debug_Output
1615                              ("search for main """ & Main
1616                               & '"' & File.Index'Img & " in "
1617                               & Get_Name_String (Debug_Name (File.Tree))
1618                               & ", project", Project.Name);
1619                         end if;
1620
1621                         --  First, look for the main as specified. We need to
1622                         --  search for the base name though, and if needed
1623                         --  check later that we found the correct file.
1624
1625                         Source := Find_Source
1626                           (In_Tree          => File.Tree,
1627                            Project          => File.Project,
1628                            Base_Name        => Main_Id,
1629                            Index            => File.Index,
1630                            In_Imported_Only => True);
1631
1632                         if Source = No_Source then
1633                            Source := Find_File_Add_Extension
1634                              (Tree, Get_Name_String (Main_Id));
1635                         end if;
1636
1637                         if Is_Absolute
1638                           and then Source /= No_Source
1639                           and then
1640                             File_Name_Type (Source.Path.Name) /= File.File
1641                         then
1642                            Debug_Output
1643                              ("Found a non-matching file",
1644                               Name_Id (Source.Path.Display_Name));
1645                            Source := No_Source;
1646                         end if;
1647
1648                         if Source /= No_Source then
1649                            if not Is_Allowed_Language
1650                                     (Source.Language.Name)
1651                            then
1652                               --  Remove any main that is not in the list of
1653                               --  restricted languages.
1654
1655                               Names.Table (J .. Names.Last - 1) :=
1656                                 Names.Table (J + 1 .. Names.Last);
1657                               Names.Set_Last (Names.Last - 1);
1658
1659                            else
1660                               --  If we have found a multi-unit source file but
1661                               --  did not specify an index initially, we'll
1662                               --  need to compile all the units from the same
1663                               --  source file.
1664
1665                               if Source.Index /= 0 and then File.Index = 0 then
1666                                  Add_Multi_Unit_Sources (File.Tree, Source);
1667                               end if;
1668
1669                               --  Now update the original Main, otherwise it
1670                               --  will be reported as not found.
1671
1672                               Debug_Output
1673                                 ("found main in project", Source.Project.Name);
1674                               Names.Table (J).File    := Source.File;
1675                               Names.Table (J).Project := Source.Project;
1676
1677                               if Names.Table (J).Tree = null then
1678                                  Names.Table (J).Tree := File.Tree;
1679
1680                                  Builder_Data (File.Tree).Number_Of_Mains :=
1681                                    Builder_Data (File.Tree).Number_Of_Mains
1682                                                                          + 1;
1683                                  Mains.Count_Of_Mains_With_No_Tree :=
1684                                    Mains.Count_Of_Mains_With_No_Tree - 1;
1685                               end if;
1686
1687                               Names.Table (J).Source  := Source;
1688                               Names.Table (J).Index   := Source.Index;
1689                            end if;
1690
1691                         elsif File.Location /= No_Location then
1692
1693                            --  If the main is declared in package Builder of
1694                            --  the main project, report an error. If the main
1695                            --  is on the command line, it may be a main from
1696                            --  another project, so do nothing: if the main does
1697                            --  not exist in another project, an error will be
1698                            --  reported later.
1699
1700                            Error_Msg_File_1 := Main_Id;
1701                            Error_Msg_Name_1 := Root_Project.Name;
1702                            Prj.Err.Error_Msg
1703                              (Flags, "{ is not a source of project %%",
1704                               File.Location, Project);
1705                         end if;
1706                      end if;
1707                   end;
1708
1709                   J := J - 1;
1710                   exit when J < Names.First;
1711                end loop;
1712             end if;
1713
1714             if Total_Errors_Detected > 0 then
1715                Fail_Program (Tree, "problems with main sources");
1716             end if;
1717          end Do_Complete;
1718
1719       --  Start of processing for Complete_Mains
1720
1721       begin
1722          Complete_All (Root_Project, Project_Tree);
1723
1724          if Mains.Count_Of_Mains_With_No_Tree > 0 then
1725             for J in Names.First .. Names.Last loop
1726                if Names.Table (J).Source = No_Source then
1727                   Fail_Program
1728                     (Project_Tree, '"' & Get_Name_String (Names.Table (J).File)
1729                      & """ is not a source of any project");
1730                end if;
1731             end loop;
1732          end if;
1733       end Complete_Mains;
1734
1735       ------------
1736       -- Delete --
1737       ------------
1738
1739       procedure Delete is
1740       begin
1741          Names.Set_Last (0);
1742          Mains.Reset;
1743       end Delete;
1744
1745       -----------------------
1746       -- Fill_From_Project --
1747       -----------------------
1748
1749       procedure Fill_From_Project
1750         (Root_Project : Project_Id;
1751          Project_Tree : Project_Tree_Ref)
1752       is
1753          procedure Add_Mains_From_Project
1754            (Project : Project_Id;
1755             Tree    : Project_Tree_Ref);
1756          --  Add the main units from this project into Mains.
1757          --  This takes into account the aggregated projects
1758
1759          ----------------------------
1760          -- Add_Mains_From_Project --
1761          ----------------------------
1762
1763          procedure Add_Mains_From_Project
1764            (Project : Project_Id;
1765             Tree    : Project_Tree_Ref)
1766          is
1767             List    : String_List_Id;
1768             Element : String_Element;
1769
1770          begin
1771             if Number_Of_Mains (Tree) = 0
1772               and then Mains.Count_Of_Mains_With_No_Tree = 0
1773             then
1774                Debug_Output ("Add_Mains_From_Project", Project.Name);
1775                List := Project.Mains;
1776
1777                if List /= Prj.Nil_String then
1778
1779                   --  The attribute Main is not an empty list. Get the mains in
1780                   --  the list.
1781
1782                   while List /= Prj.Nil_String loop
1783                      Element := Tree.Shared.String_Elements.Table (List);
1784                      Debug_Output ("Add_Main", Element.Value);
1785
1786                      if Project.Library then
1787                         Fail_Program
1788                           (Tree,
1789                            "cannot specify a main program " &
1790                            "for a library project file");
1791                      end if;
1792
1793                      Add_Main (Name     => Get_Name_String (Element.Value),
1794                                Index    => Element.Index,
1795                                Location => Element.Location,
1796                                Project  => Project,
1797                                Tree     => Tree);
1798                      List := Element.Next;
1799                   end loop;
1800                end if;
1801             end if;
1802
1803             if Total_Errors_Detected > 0 then
1804                Fail_Program (Tree, "problems with main sources");
1805             end if;
1806          end Add_Mains_From_Project;
1807
1808          procedure Fill_All is new For_Project_And_Aggregated
1809            (Add_Mains_From_Project);
1810
1811       --  Start of processing for Fill_From_Project
1812
1813       begin
1814          Fill_All (Root_Project, Project_Tree);
1815       end Fill_From_Project;
1816
1817       ---------------
1818       -- Next_Main --
1819       ---------------
1820
1821       function Next_Main return String is
1822          Info : constant Main_Info := Next_Main;
1823       begin
1824          if Info = No_Main_Info then
1825             return "";
1826          else
1827             return Get_Name_String (Info.File);
1828          end if;
1829       end Next_Main;
1830
1831       function Next_Main return Main_Info is
1832       begin
1833          if Current >= Names.Last then
1834             return No_Main_Info;
1835          else
1836             Current := Current + 1;
1837
1838             --  If not using projects, and in the gnatmake case, the main file
1839             --  may have not have the extension. Try ".adb" first then ".ads"
1840
1841             if Names.Table (Current).Project = No_Project then
1842                declare
1843                   Orig_Main : constant File_Name_Type :=
1844                     Names.Table (Current).File;
1845                   Current_Main : File_Name_Type;
1846
1847                begin
1848                   if Strip_Suffix (Orig_Main) = Orig_Main then
1849                      Get_Name_String (Orig_Main);
1850                      Add_Str_To_Name_Buffer (".adb");
1851                      Current_Main := Name_Find;
1852
1853                      if Full_Source_Name (Current_Main) = No_File then
1854                         Get_Name_String (Orig_Main);
1855                         Add_Str_To_Name_Buffer (".ads");
1856                         Current_Main := Name_Find;
1857
1858                         if Full_Source_Name (Current_Main) /= No_File then
1859                            Names.Table (Current).File := Current_Main;
1860                         end if;
1861
1862                      else
1863                         Names.Table (Current).File := Current_Main;
1864                      end if;
1865                   end if;
1866                end;
1867             end if;
1868
1869             return Names.Table (Current);
1870          end if;
1871       end Next_Main;
1872
1873       ---------------------
1874       -- Number_Of_Mains --
1875       ---------------------
1876
1877       function Number_Of_Mains (Tree : Project_Tree_Ref) return Natural is
1878       begin
1879          if Tree = null then
1880             return Names.Last;
1881          else
1882             return Builder_Data (Tree).Number_Of_Mains;
1883          end if;
1884       end Number_Of_Mains;
1885
1886       -----------
1887       -- Reset --
1888       -----------
1889
1890       procedure Reset is
1891       begin
1892          Current := 0;
1893       end Reset;
1894
1895       --------------------------
1896       -- Set_Multi_Unit_Index --
1897       --------------------------
1898
1899       procedure Set_Multi_Unit_Index
1900         (Project_Tree : Project_Tree_Ref := null;
1901          Index        : Int := 0)
1902       is
1903       begin
1904          if Index /= 0 then
1905             if Names.Last = 0 then
1906                Fail_Program
1907                  (Project_Tree,
1908                   "cannot specify a multi-unit index but no main " &
1909                   "on the command line");
1910
1911             elsif Names.Last > 1 then
1912                Fail_Program
1913                  (Project_Tree,
1914                   "cannot specify several mains with a multi-unit index");
1915
1916             else
1917                Names.Table (Names.Last).Index := Index;
1918             end if;
1919          end if;
1920       end Set_Multi_Unit_Index;
1921
1922    end Mains;
1923
1924    -----------------------
1925    -- Path_Or_File_Name --
1926    -----------------------
1927
1928    function Path_Or_File_Name (Path : Path_Name_Type) return String is
1929       Path_Name : constant String := Get_Name_String (Path);
1930    begin
1931       if Debug.Debug_Flag_F then
1932          return File_Name (Path_Name);
1933       else
1934          return Path_Name;
1935       end if;
1936    end Path_Or_File_Name;
1937
1938    ---------------------------
1939    -- Test_If_Relative_Path --
1940    ---------------------------
1941
1942    procedure Test_If_Relative_Path
1943      (Switch               : in out String_Access;
1944       Parent               : String;
1945       Do_Fail              : Fail_Proc;
1946       Including_L_Switch   : Boolean := True;
1947       Including_Non_Switch : Boolean := True;
1948       Including_RTS        : Boolean := False)
1949    is
1950    begin
1951       if Switch /= null then
1952          declare
1953             Sw    : String (1 .. Switch'Length);
1954             Start : Positive;
1955
1956          begin
1957             Sw := Switch.all;
1958
1959             if Sw (1) = '-' then
1960                if Sw'Length >= 3
1961                  and then (Sw (2) = 'A'
1962                             or else Sw (2) = 'I'
1963                             or else (Including_L_Switch and then Sw (2) = 'L'))
1964                then
1965                   Start := 3;
1966
1967                   if Sw = "-I-" then
1968                      return;
1969                   end if;
1970
1971                elsif Sw'Length >= 4
1972                  and then (Sw (2 .. 3) = "aL"
1973                              or else
1974                            Sw (2 .. 3) = "aO"
1975                              or else
1976                            Sw (2 .. 3) = "aI")
1977                then
1978                   Start := 4;
1979
1980                elsif Including_RTS
1981                  and then Sw'Length >= 7
1982                  and then Sw (2 .. 6) = "-RTS="
1983                then
1984                   Start := 7;
1985
1986                else
1987                   return;
1988                end if;
1989
1990                --  Because relative path arguments to --RTS= may be relative to
1991                --  the search directory prefix, those relative path arguments
1992                --  are converted only when they include directory information.
1993
1994                if not Is_Absolute_Path (Sw (Start .. Sw'Last)) then
1995                   if Parent'Length = 0 then
1996                      Do_Fail
1997                        ("relative search path switches ("""
1998                         & Sw
1999                         & """) are not allowed");
2000
2001                   elsif Including_RTS then
2002                      for J in Start .. Sw'Last loop
2003                         if Sw (J) = Directory_Separator then
2004                            Switch :=
2005                              new String'
2006                                (Sw (1 .. Start - 1) &
2007                                 Parent &
2008                                 Directory_Separator &
2009                                 Sw (Start .. Sw'Last));
2010                            return;
2011                         end if;
2012                      end loop;
2013
2014                   else
2015                      Switch :=
2016                        new String'
2017                          (Sw (1 .. Start - 1) &
2018                           Parent &
2019                           Directory_Separator &
2020                           Sw (Start .. Sw'Last));
2021                   end if;
2022                end if;
2023
2024             elsif Including_Non_Switch then
2025                if not Is_Absolute_Path (Sw) then
2026                   if Parent'Length = 0 then
2027                      Do_Fail
2028                        ("relative paths (""" & Sw & """) are not allowed");
2029                   else
2030                      Switch := new String'(Parent & Directory_Separator & Sw);
2031                   end if;
2032                end if;
2033             end if;
2034          end;
2035       end if;
2036    end Test_If_Relative_Path;
2037
2038    -------------------
2039    -- Unit_Index_Of --
2040    -------------------
2041
2042    function Unit_Index_Of (ALI_File : File_Name_Type) return Int is
2043       Start  : Natural;
2044       Finish : Natural;
2045       Result : Int := 0;
2046
2047    begin
2048       Get_Name_String (ALI_File);
2049
2050       --  First, find the last dot
2051
2052       Finish := Name_Len;
2053
2054       while Finish >= 1 and then Name_Buffer (Finish) /= '.' loop
2055          Finish := Finish - 1;
2056       end loop;
2057
2058       if Finish = 1 then
2059          return 0;
2060       end if;
2061
2062       --  Now check that the dot is preceded by digits
2063
2064       Start := Finish;
2065       Finish := Finish - 1;
2066       while Start >= 1 and then Name_Buffer (Start - 1) in '0' .. '9' loop
2067          Start := Start - 1;
2068       end loop;
2069
2070       --  If there are no digits, or if the digits are not preceded by the
2071       --  character that precedes a unit index, this is not the ALI file of
2072       --  a unit in a multi-unit source.
2073
2074       if Start > Finish
2075         or else Start = 1
2076         or else Name_Buffer (Start - 1) /= Multi_Unit_Index_Character
2077       then
2078          return 0;
2079       end if;
2080
2081       --  Build the index from the digit(s)
2082
2083       while Start <= Finish loop
2084          Result := Result * 10 +
2085                      Character'Pos (Name_Buffer (Start)) - Character'Pos ('0');
2086          Start := Start + 1;
2087       end loop;
2088
2089       return Result;
2090    end Unit_Index_Of;
2091
2092    -----------------
2093    -- Verbose_Msg --
2094    -----------------
2095
2096    procedure Verbose_Msg
2097      (N1                : Name_Id;
2098       S1                : String;
2099       N2                : Name_Id := No_Name;
2100       S2                : String  := "";
2101       Prefix            : String := "  -> ";
2102       Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low)
2103    is
2104    begin
2105       if not Opt.Verbose_Mode
2106         or else Minimum_Verbosity > Opt.Verbosity_Level
2107       then
2108          return;
2109       end if;
2110
2111       Write_Str (Prefix);
2112       Write_Str ("""");
2113       Write_Name (N1);
2114       Write_Str (""" ");
2115       Write_Str (S1);
2116
2117       if N2 /= No_Name then
2118          Write_Str (" """);
2119          Write_Name (N2);
2120          Write_Str (""" ");
2121       end if;
2122
2123       Write_Str (S2);
2124       Write_Eol;
2125    end Verbose_Msg;
2126
2127    procedure Verbose_Msg
2128      (N1                : File_Name_Type;
2129       S1                : String;
2130       N2                : File_Name_Type := No_File;
2131       S2                : String  := "";
2132       Prefix            : String := "  -> ";
2133       Minimum_Verbosity : Opt.Verbosity_Level_Type := Opt.Low)
2134    is
2135    begin
2136       Verbose_Msg
2137         (Name_Id (N1), S1, Name_Id (N2), S2, Prefix, Minimum_Verbosity);
2138    end Verbose_Msg;
2139
2140    -----------
2141    -- Queue --
2142    -----------
2143
2144    package body Queue is
2145
2146       type Q_Record is record
2147          Info      : Source_Info;
2148          Processed : Boolean;
2149       end record;
2150
2151       package Q is new Table.Table
2152         (Table_Component_Type => Q_Record,
2153          Table_Index_Type     => Natural,
2154          Table_Low_Bound      => 1,
2155          Table_Initial        => 1000,
2156          Table_Increment      => 100,
2157          Table_Name           => "Makeutl.Queue.Q");
2158       --  This is the actual Queue
2159
2160       package Busy_Obj_Dirs is new GNAT.HTable.Simple_HTable
2161         (Header_Num => Prj.Header_Num,
2162          Element    => Boolean,
2163          No_Element => False,
2164          Key        => Path_Name_Type,
2165          Hash       => Hash,
2166          Equal      => "=");
2167
2168       type Mark_Key is record
2169          File  : File_Name_Type;
2170          Index : Int;
2171       end record;
2172       --  Identify either a mono-unit source (when Index = 0) or a specific
2173       --  unit (index = 1's origin index of unit) in a multi-unit source.
2174
2175       Max_Mask_Num : constant := 2048;
2176       subtype Mark_Num is Union_Id range 0 .. Max_Mask_Num - 1;
2177
2178       function Hash (Key : Mark_Key) return Mark_Num;
2179
2180       package Marks is new GNAT.HTable.Simple_HTable
2181         (Header_Num => Mark_Num,
2182          Element    => Boolean,
2183          No_Element => False,
2184          Key        => Mark_Key,
2185          Hash       => Hash,
2186          Equal      => "=");
2187       --  A hash table to keep tracks of the marked units.
2188       --  These are the units that have already been processed, when using the
2189       --  gnatmake format. When using the gprbuild format, we can directly
2190       --  store in the source_id whether the file has already been processed.
2191
2192       procedure Mark (Source_File : File_Name_Type; Index : Int := 0);
2193       --  Mark a unit, identified by its source file and, when Index is not 0,
2194       --  the index of the unit in the source file. Marking is used to signal
2195       --  that the unit has already been inserted in the Q.
2196
2197       function Is_Marked
2198         (Source_File : File_Name_Type;
2199          Index       : Int := 0) return Boolean;
2200       --  Returns True if the unit was previously marked
2201
2202       Q_Processed   : Natural := 0;
2203       Q_Initialized : Boolean := False;
2204
2205       Q_First : Natural := 1;
2206       --  Points to the first valid element in the queue
2207
2208       One_Queue_Per_Obj_Dir : Boolean := False;
2209       --  See parameter to Initialize
2210
2211       function Available_Obj_Dir (S : Source_Info) return Boolean;
2212       --  Whether the object directory for S is available for a build
2213
2214       procedure Debug_Display (S : Source_Info);
2215       --  A debug display for S
2216
2217       function Was_Processed (S : Source_Info) return Boolean;
2218       --  Whether S has already been processed. This marks the source as
2219       --  processed, if it hasn't already been processed.
2220
2221       function Insert_No_Roots (Source  : Source_Info) return Boolean;
2222       --  Insert Source, but do not look for its roots (see doc for Insert)
2223
2224       -------------------
2225       -- Was_Processed --
2226       -------------------
2227
2228       function Was_Processed (S : Source_Info) return Boolean is
2229       begin
2230          case S.Format is
2231             when Format_Gprbuild =>
2232                if S.Id.In_The_Queue then
2233                   return True;
2234                end if;
2235
2236                S.Id.In_The_Queue := True;
2237
2238             when Format_Gnatmake =>
2239                if Is_Marked (S.File, S.Index) then
2240                   return True;
2241                end if;
2242
2243                Mark (S.File, Index => S.Index);
2244          end case;
2245
2246          return False;
2247       end Was_Processed;
2248
2249       -----------------------
2250       -- Available_Obj_Dir --
2251       -----------------------
2252
2253       function Available_Obj_Dir (S : Source_Info) return Boolean is
2254       begin
2255          case S.Format is
2256             when Format_Gprbuild =>
2257                return not Busy_Obj_Dirs.Get
2258                  (S.Id.Project.Object_Directory.Name);
2259
2260             when Format_Gnatmake =>
2261                return S.Project = No_Project
2262                  or else
2263                    not Busy_Obj_Dirs.Get (S.Project.Object_Directory.Name);
2264          end case;
2265       end Available_Obj_Dir;
2266
2267       -------------------
2268       -- Debug_Display --
2269       -------------------
2270
2271       procedure Debug_Display (S : Source_Info) is
2272       begin
2273          case S.Format is
2274             when Format_Gprbuild =>
2275                Write_Name (S.Id.File);
2276
2277                if S.Id.Index /= 0 then
2278                   Write_Str (", ");
2279                   Write_Int (S.Id.Index);
2280                end if;
2281
2282             when Format_Gnatmake =>
2283                Write_Name (S.File);
2284
2285                if S.Index /= 0 then
2286                   Write_Str (", ");
2287                   Write_Int (S.Index);
2288                end if;
2289          end case;
2290       end Debug_Display;
2291
2292       ----------
2293       -- Hash --
2294       ----------
2295
2296       function Hash (Key : Mark_Key) return Mark_Num is
2297       begin
2298          return Union_Id (Key.File) mod Max_Mask_Num;
2299       end Hash;
2300
2301       ---------------
2302       -- Is_Marked --
2303       ---------------
2304
2305       function Is_Marked
2306         (Source_File : File_Name_Type;
2307          Index       : Int := 0) return Boolean
2308       is
2309       begin
2310          return Marks.Get (K => (File => Source_File, Index => Index));
2311       end Is_Marked;
2312
2313       ----------
2314       -- Mark --
2315       ----------
2316
2317       procedure Mark (Source_File : File_Name_Type; Index : Int := 0) is
2318       begin
2319          Marks.Set (K => (File => Source_File, Index => Index), E => True);
2320       end Mark;
2321
2322       -------------
2323       -- Extract --
2324       -------------
2325
2326       procedure Extract
2327         (Found  : out Boolean;
2328          Source : out Source_Info)
2329       is
2330       begin
2331          Found := False;
2332
2333          if One_Queue_Per_Obj_Dir then
2334             for J in Q_First .. Q.Last loop
2335                if not Q.Table (J).Processed
2336                  and then Available_Obj_Dir (Q.Table (J).Info)
2337                then
2338                   Found := True;
2339                   Source := Q.Table (J).Info;
2340                   Q.Table (J).Processed := True;
2341
2342                   if J = Q_First then
2343                      while Q_First <= Q.Last
2344                        and then Q.Table (Q_First).Processed
2345                      loop
2346                         Q_First := Q_First + 1;
2347                      end loop;
2348                   end if;
2349
2350                   exit;
2351                end if;
2352             end loop;
2353
2354          elsif Q_First <= Q.Last then
2355             Source := Q.Table (Q_First).Info;
2356             Q.Table (Q_First).Processed := True;
2357             Q_First := Q_First + 1;
2358             Found := True;
2359          end if;
2360
2361          if Found then
2362             Q_Processed := Q_Processed + 1;
2363          end if;
2364
2365          if Found and then Debug.Debug_Flag_Q then
2366             Write_Str ("   Q := Q - [ ");
2367             Debug_Display (Source);
2368             Write_Str (" ]");
2369             Write_Eol;
2370
2371             Write_Str ("   Q_First =");
2372             Write_Int (Int (Q_First));
2373             Write_Eol;
2374
2375             Write_Str ("   Q.Last =");
2376             Write_Int (Int (Q.Last));
2377             Write_Eol;
2378          end if;
2379       end Extract;
2380
2381       ---------------
2382       -- Processed --
2383       ---------------
2384
2385       function Processed return Natural is
2386       begin
2387          return Q_Processed;
2388       end Processed;
2389
2390       ----------------
2391       -- Initialize --
2392       ----------------
2393
2394       procedure Initialize
2395         (Queue_Per_Obj_Dir : Boolean;
2396          Force             : Boolean := False)
2397       is
2398       begin
2399          if Force or else not Q_Initialized then
2400             Q_Initialized := True;
2401
2402             for J in 1 .. Q.Last loop
2403                case Q.Table (J).Info.Format is
2404                when Format_Gprbuild =>
2405                   Q.Table (J).Info.Id.In_The_Queue := False;
2406                when Format_Gnatmake =>
2407                   null;
2408                end case;
2409             end loop;
2410
2411             Q.Init;
2412             Q_Processed := 0;
2413             Q_First     := 1;
2414             One_Queue_Per_Obj_Dir := Queue_Per_Obj_Dir;
2415          end if;
2416       end Initialize;
2417
2418       ---------------------
2419       -- Insert_No_Roots --
2420       ---------------------
2421
2422       function Insert_No_Roots (Source  : Source_Info) return Boolean is
2423       begin
2424          pragma Assert
2425            (Source.Format = Format_Gnatmake or else Source.Id /= No_Source);
2426
2427          --  Only insert in the Q if it is not already done, to avoid
2428          --  simultaneous compilations if -jnnn is used.
2429
2430          if Was_Processed (Source) then
2431             return False;
2432          end if;
2433
2434          if Current_Verbosity = High then
2435             Write_Str ("Adding """);
2436             Debug_Display (Source);
2437             Write_Line (""" to the queue");
2438          end if;
2439
2440          Q.Append (New_Val => (Info => Source, Processed => False));
2441
2442          if Debug.Debug_Flag_Q then
2443             Write_Str ("   Q := Q + [ ");
2444             Debug_Display (Source);
2445             Write_Str (" ] ");
2446             Write_Eol;
2447
2448             Write_Str ("   Q_First =");
2449             Write_Int (Int (Q_First));
2450             Write_Eol;
2451
2452             Write_Str ("   Q.Last =");
2453             Write_Int (Int (Q.Last));
2454             Write_Eol;
2455          end if;
2456
2457          return True;
2458       end Insert_No_Roots;
2459
2460       ------------
2461       -- Insert --
2462       ------------
2463
2464       function Insert
2465         (Source     : Source_Info;
2466          With_Roots : Boolean := False) return Boolean
2467       is
2468          Root_Arr     : Array_Element_Id;
2469          Roots        : Variable_Value;
2470          List         : String_List_Id;
2471          Elem         : String_Element;
2472          Unit_Name    : Name_Id;
2473          Pat_Root     : Boolean;
2474          Root_Pattern : Regexp;
2475          Root_Found   : Boolean;
2476          Roots_Found  : Boolean;
2477          Root_Source  : Prj.Source_Id;
2478          Iter         : Source_Iterator;
2479
2480          Dummy : Boolean;
2481          pragma Unreferenced (Dummy);
2482
2483       begin
2484          if not Insert_No_Roots (Source) then
2485
2486             --  Was already in the queue
2487
2488             return False;
2489          end if;
2490
2491          if With_Roots and then Source.Format = Format_Gprbuild then
2492             Debug_Output ("looking for roots of", Name_Id (Source.Id.File));
2493
2494             Root_Arr :=
2495               Prj.Util.Value_Of
2496                 (Name      => Name_Roots,
2497                  In_Arrays => Source.Id.Project.Decl.Arrays,
2498                  Shared    => Source.Tree.Shared);
2499
2500             Roots :=
2501               Prj.Util.Value_Of
2502                 (Index     => Name_Id (Source.Id.File),
2503                  Src_Index => 0,
2504                  In_Array  => Root_Arr,
2505                  Shared    => Source.Tree.Shared);
2506
2507             --  If there is no roots for the specific main, try the language
2508
2509             if Roots = Nil_Variable_Value then
2510                Roots :=
2511                  Prj.Util.Value_Of
2512                    (Index                  => Source.Id.Language.Name,
2513                     Src_Index              => 0,
2514                     In_Array               => Root_Arr,
2515                     Shared                 => Source.Tree.Shared,
2516                     Force_Lower_Case_Index => True);
2517             end if;
2518
2519             --  Then try "*"
2520
2521             if Roots = Nil_Variable_Value then
2522                Name_Len := 1;
2523                Name_Buffer (1) := '*';
2524
2525                Roots :=
2526                  Prj.Util.Value_Of
2527                    (Index                  => Name_Find,
2528                     Src_Index              => 0,
2529                     In_Array               => Root_Arr,
2530                     Shared                 => Source.Tree.Shared,
2531                     Force_Lower_Case_Index => True);
2532             end if;
2533
2534             if Roots = Nil_Variable_Value then
2535                Debug_Output ("   -> no roots declared");
2536
2537             else
2538                List := Roots.Values;
2539
2540                Pattern_Loop :
2541                while List /= Nil_String loop
2542                   Elem := Source.Tree.Shared.String_Elements.Table (List);
2543                   Get_Name_String (Elem.Value);
2544                   To_Lower (Name_Buffer (1 .. Name_Len));
2545                   Unit_Name := Name_Find;
2546
2547                   --  Check if it is a unit name or a pattern
2548
2549                   Pat_Root := False;
2550
2551                   for J in 1 .. Name_Len loop
2552                      if Name_Buffer (J) not in 'a' .. 'z' and then
2553                         Name_Buffer (J) not in '0' .. '9' and then
2554                         Name_Buffer (J) /= '_'            and then
2555                         Name_Buffer (J) /= '.'
2556                      then
2557                         Pat_Root := True;
2558                         exit;
2559                      end if;
2560                   end loop;
2561
2562                   if Pat_Root then
2563                      begin
2564                         Root_Pattern :=
2565                           Compile
2566                             (Pattern => Name_Buffer (1 .. Name_Len),
2567                              Glob    => True);
2568
2569                      exception
2570                         when Error_In_Regexp =>
2571                            Err_Vars.Error_Msg_Name_1 := Unit_Name;
2572                            Errutil.Error_Msg
2573                              ("invalid pattern %", Roots.Location);
2574                            exit Pattern_Loop;
2575                      end;
2576                   end if;
2577
2578                   Roots_Found := False;
2579                   Iter        := For_Each_Source (Source.Tree);
2580
2581                   Source_Loop :
2582                   loop
2583                      Root_Source := Prj.Element (Iter);
2584                      exit Source_Loop when Root_Source = No_Source;
2585
2586                      Root_Found := False;
2587                      if Pat_Root then
2588                         Root_Found := Root_Source.Unit /= No_Unit_Index
2589                           and then Match
2590                             (Get_Name_String (Root_Source.Unit.Name),
2591                              Root_Pattern);
2592
2593                      else
2594                         Root_Found :=
2595                           Root_Source.Unit /= No_Unit_Index
2596                             and then Root_Source.Unit.Name = Unit_Name;
2597                      end if;
2598
2599                      if Root_Found then
2600                         case Root_Source.Kind is
2601                         when Impl =>
2602                            null;
2603
2604                         when Spec =>
2605                            Root_Found := Other_Part (Root_Source) = No_Source;
2606
2607                         when Sep =>
2608                            Root_Found := False;
2609                         end case;
2610                      end if;
2611
2612                      if Root_Found then
2613                         Roots_Found := True;
2614                         Debug_Output
2615                           ("   -> ", Name_Id (Root_Source.Display_File));
2616                         Dummy := Queue.Insert_No_Roots
2617                           (Source => (Format => Format_Gprbuild,
2618                                       Tree   => Source.Tree,
2619                                       Id     => Root_Source));
2620
2621                         Initialize_Source_Record (Root_Source);
2622
2623                         if Other_Part (Root_Source) /= No_Source then
2624                            Initialize_Source_Record (Other_Part (Root_Source));
2625                         end if;
2626
2627                         --  Save the root for the binder
2628
2629                         Source.Id.Roots := new Source_Roots'
2630                           (Root => Root_Source,
2631                            Next => Source.Id.Roots);
2632
2633                         exit Source_Loop when not Pat_Root;
2634                      end if;
2635
2636                      Next (Iter);
2637                   end loop Source_Loop;
2638
2639                   if not Roots_Found then
2640                      if Pat_Root then
2641                         if not Quiet_Output then
2642                            Error_Msg_Name_1 := Unit_Name;
2643                            Errutil.Error_Msg
2644                              ("?no unit matches pattern %", Roots.Location);
2645                         end if;
2646
2647                      else
2648                         Errutil.Error_Msg
2649                           ("Unit " & Get_Name_String (Unit_Name)
2650                            & " does not exist", Roots.Location);
2651                      end if;
2652                   end if;
2653
2654                   List := Elem.Next;
2655                end loop Pattern_Loop;
2656             end if;
2657          end if;
2658
2659          return True;
2660       end Insert;
2661
2662       ------------
2663       -- Insert --
2664       ------------
2665
2666       procedure Insert
2667         (Source     : Source_Info;
2668          With_Roots : Boolean := False)
2669       is
2670          Discard : Boolean;
2671          pragma Unreferenced (Discard);
2672       begin
2673          Discard := Insert (Source, With_Roots);
2674       end Insert;
2675
2676       --------------
2677       -- Is_Empty --
2678       --------------
2679
2680       function Is_Empty return Boolean is
2681       begin
2682          return Q_Processed >= Q.Last;
2683       end Is_Empty;
2684
2685       ------------------------
2686       -- Is_Virtually_Empty --
2687       ------------------------
2688
2689       function Is_Virtually_Empty return Boolean is
2690       begin
2691          if One_Queue_Per_Obj_Dir then
2692             for J in Q_First .. Q.Last loop
2693                if not Q.Table (J).Processed
2694                  and then Available_Obj_Dir (Q.Table (J).Info)
2695                then
2696                   return False;
2697                end if;
2698             end loop;
2699
2700             return True;
2701
2702          else
2703             return Is_Empty;
2704          end if;
2705       end Is_Virtually_Empty;
2706
2707       ----------------------
2708       -- Set_Obj_Dir_Busy --
2709       ----------------------
2710
2711       procedure Set_Obj_Dir_Busy (Obj_Dir : Path_Name_Type) is
2712       begin
2713          if One_Queue_Per_Obj_Dir then
2714             Busy_Obj_Dirs.Set (Obj_Dir, True);
2715          end if;
2716       end Set_Obj_Dir_Busy;
2717
2718       ----------------------
2719       -- Set_Obj_Dir_Free --
2720       ----------------------
2721
2722       procedure Set_Obj_Dir_Free (Obj_Dir : Path_Name_Type) is
2723       begin
2724          if One_Queue_Per_Obj_Dir then
2725             Busy_Obj_Dirs.Set (Obj_Dir, False);
2726          end if;
2727       end Set_Obj_Dir_Free;
2728
2729       ----------
2730       -- Size --
2731       ----------
2732
2733       function Size return Natural is
2734       begin
2735          return Q.Last;
2736       end Size;
2737
2738       -------------
2739       -- Element --
2740       -------------
2741
2742       function Element (Rank : Positive) return File_Name_Type is
2743       begin
2744          if Rank <= Q.Last then
2745             case Q.Table (Rank).Info.Format is
2746                when Format_Gprbuild =>
2747                   return Q.Table (Rank).Info.Id.File;
2748                when Format_Gnatmake =>
2749                   return Q.Table (Rank).Info.File;
2750             end case;
2751          else
2752             return No_File;
2753          end if;
2754       end Element;
2755
2756       ------------------
2757       -- Remove_Marks --
2758       ------------------
2759
2760       procedure Remove_Marks is
2761       begin
2762          Marks.Reset;
2763       end Remove_Marks;
2764
2765       ----------------------------
2766       -- Insert_Project_Sources --
2767       ----------------------------
2768
2769       procedure Insert_Project_Sources
2770         (Project        : Project_Id;
2771          Project_Tree   : Project_Tree_Ref;
2772          All_Projects   : Boolean;
2773          Unique_Compile : Boolean)
2774       is
2775          procedure Do_Insert (Project : Project_Id; Tree : Project_Tree_Ref);
2776
2777          ---------------
2778          -- Do_Insert --
2779          ---------------
2780
2781          procedure Do_Insert (Project : Project_Id; Tree : Project_Tree_Ref) is
2782             Unit_Based : constant Boolean :=
2783                            Unique_Compile
2784                              or else not Builder_Data (Tree).Closure_Needed;
2785             --  When Unit_Based is True, put in the queue all compilable
2786             --  sources including the unit based (Ada) one. When Unit_Based is
2787             --  False, put the Ada sources only when they are in a library
2788             --  project.
2789
2790             Iter   : Source_Iterator;
2791             Source : Prj.Source_Id;
2792
2793          begin
2794             --  Nothing to do when "-u" was specified and some files were
2795             --  specified on the command line
2796
2797             if Unique_Compile
2798               and then Mains.Number_Of_Mains (Tree) > 0
2799             then
2800                return;
2801             end if;
2802
2803             Iter := For_Each_Source (Tree);
2804             loop
2805                Source := Prj.Element (Iter);
2806                exit when Source = No_Source;
2807
2808                if Is_Allowed_Language (Source.Language.Name)
2809                  and then Is_Compilable (Source)
2810                  and then
2811                    (All_Projects
2812                      or else Is_Extending (Project, Source.Project))
2813                  and then not Source.Locally_Removed
2814                  and then Source.Replaced_By = No_Source
2815                  and then
2816                    (not Source.Project.Externally_Built
2817                      or else
2818                        (Is_Extending (Project, Source.Project)
2819                          and then not Project.Externally_Built))
2820                  and then Source.Kind /= Sep
2821                  and then Source.Path /= No_Path_Information
2822                then
2823                   if Source.Kind = Impl
2824                     or else (Source.Unit /= No_Unit_Index
2825                               and then Source.Kind = Spec
2826                               and then (Other_Part (Source) = No_Source
2827                                           or else
2828                                         Other_Part (Source).Locally_Removed))
2829                   then
2830                      if (Unit_Based
2831                           or else Source.Unit = No_Unit_Index
2832                           or else Source.Project.Library)
2833                        and then not Is_Subunit (Source)
2834                      then
2835                         Queue.Insert
2836                           (Source => (Format => Format_Gprbuild,
2837                                       Tree   => Tree,
2838                                       Id     => Source));
2839                      end if;
2840                   end if;
2841                end if;
2842
2843                Next (Iter);
2844             end loop;
2845          end Do_Insert;
2846
2847          procedure Insert_All is new For_Project_And_Aggregated (Do_Insert);
2848
2849       begin
2850          Insert_All (Project, Project_Tree);
2851       end Insert_Project_Sources;
2852
2853       -------------------------------
2854       -- Insert_Withed_Sources_For --
2855       -------------------------------
2856
2857       procedure Insert_Withed_Sources_For
2858         (The_ALI               : ALI.ALI_Id;
2859          Project_Tree          : Project_Tree_Ref;
2860          Excluding_Shared_SALs : Boolean := False)
2861       is
2862          Sfile  : File_Name_Type;
2863          Afile  : File_Name_Type;
2864          Src_Id : Prj.Source_Id;
2865
2866       begin
2867          --  Insert in the queue the unmarked source files (i.e. those which
2868          --  have never been inserted in the queue and hence never considered).
2869
2870          for J in ALI.ALIs.Table (The_ALI).First_Unit ..
2871            ALI.ALIs.Table (The_ALI).Last_Unit
2872          loop
2873             for K in ALI.Units.Table (J).First_With ..
2874               ALI.Units.Table (J).Last_With
2875             loop
2876                Sfile := ALI.Withs.Table (K).Sfile;
2877
2878                --  Skip generics
2879
2880                if Sfile /= No_File then
2881                   Afile := ALI.Withs.Table (K).Afile;
2882
2883                   Src_Id := Source_Files_Htable.Get
2884                               (Project_Tree.Source_Files_HT, Sfile);
2885                   while Src_Id /= No_Source loop
2886                      Initialize_Source_Record (Src_Id);
2887
2888                      if Is_Compilable (Src_Id)
2889                        and then Src_Id.Dep_Name = Afile
2890                      then
2891                         case Src_Id.Kind is
2892                            when Spec =>
2893                               declare
2894                                  Bdy : constant Prj.Source_Id :=
2895                                          Other_Part (Src_Id);
2896                               begin
2897                                  if Bdy /= No_Source
2898                                    and then not Bdy.Locally_Removed
2899                                  then
2900                                     Src_Id := Other_Part (Src_Id);
2901                                  end if;
2902                               end;
2903
2904                            when Impl =>
2905                               if Is_Subunit (Src_Id) then
2906                                  Src_Id := No_Source;
2907                               end if;
2908
2909                            when Sep =>
2910                               Src_Id := No_Source;
2911                         end case;
2912
2913                         exit;
2914                      end if;
2915
2916                      Src_Id := Src_Id.Next_With_File_Name;
2917                   end loop;
2918
2919                   --  If Excluding_Shared_SALs is True, do not insert in the
2920                   --  queue the sources of a shared Stand-Alone Library.
2921
2922                   if Src_Id /= No_Source
2923                     and then (not Excluding_Shared_SALs
2924                                or else Src_Id.Project.Standalone_Library = No
2925                                or else Src_Id.Project.Library_Kind = Static)
2926                   then
2927                      Queue.Insert
2928                        (Source => (Format => Format_Gprbuild,
2929                                    Tree   => Project_Tree,
2930                                    Id     => Src_Id));
2931                   end if;
2932                end if;
2933             end loop;
2934          end loop;
2935       end Insert_Withed_Sources_For;
2936
2937    end Queue;
2938
2939    ----------
2940    -- Free --
2941    ----------
2942
2943    procedure Free (Data : in out Builder_Project_Tree_Data) is
2944       procedure Unchecked_Free is new Ada.Unchecked_Deallocation
2945         (Binding_Data_Record, Binding_Data);
2946
2947       TmpB, Binding : Binding_Data := Data.Binding;
2948
2949    begin
2950       while Binding /= null loop
2951          TmpB := Binding.Next;
2952          Unchecked_Free (Binding);
2953          Binding := TmpB;
2954       end loop;
2955    end Free;
2956
2957    ------------------
2958    -- Builder_Data --
2959    ------------------
2960
2961    function Builder_Data
2962      (Tree : Project_Tree_Ref) return Builder_Data_Access
2963    is
2964    begin
2965       if Tree.Appdata = null then
2966          Tree.Appdata := new Builder_Project_Tree_Data;
2967       end if;
2968
2969       return Builder_Data_Access (Tree.Appdata);
2970    end Builder_Data;
2971
2972    --------------------------------
2973    -- Compute_Compilation_Phases --
2974    --------------------------------
2975
2976    procedure Compute_Compilation_Phases
2977      (Tree                  : Project_Tree_Ref;
2978       Root_Project          : Project_Id;
2979       Option_Unique_Compile : Boolean := False;   --  Was "-u" specified ?
2980       Option_Compile_Only   : Boolean := False;   --  Was "-c" specified ?
2981       Option_Bind_Only      : Boolean := False;
2982       Option_Link_Only      : Boolean := False)
2983    is
2984       procedure Do_Compute (Project : Project_Id; Tree : Project_Tree_Ref);
2985
2986       ----------------
2987       -- Do_Compute --
2988       ----------------
2989
2990       procedure Do_Compute (Project : Project_Id; Tree : Project_Tree_Ref) is
2991          Data       : constant Builder_Data_Access := Builder_Data (Tree);
2992          All_Phases : constant Boolean :=
2993                         not Option_Compile_Only
2994                         and then not Option_Bind_Only
2995                         and then not Option_Link_Only;
2996          --  Whether the command line asked for all three phases. Depending on
2997          --  the project settings, we might still disable some of the phases.
2998
2999          Has_Mains : constant Boolean := Data.Number_Of_Mains > 0;
3000          --  Whether there are some main units defined for this project tree
3001          --  (either from one of the projects, or from the command line)
3002
3003       begin
3004          if Option_Unique_Compile then
3005
3006             --  If -u or -U is specified on the command line, disregard any -c,
3007             --  -b or -l switch: only perform compilation.
3008
3009             Data.Closure_Needed   := False;
3010             Data.Need_Compilation := True;
3011             Data.Need_Binding     := False;
3012             Data.Need_Linking     := False;
3013
3014          else
3015             Data.Closure_Needed   := Has_Mains;
3016             Data.Need_Compilation := All_Phases or Option_Compile_Only;
3017             Data.Need_Binding     := All_Phases or Option_Bind_Only;
3018             Data.Need_Linking     := (All_Phases or Option_Link_Only)
3019                                        and Has_Mains;
3020          end if;
3021
3022          if Current_Verbosity = High then
3023             Debug_Output ("compilation phases: "
3024                           & " compile=" & Data.Need_Compilation'Img
3025                           & " bind=" & Data.Need_Binding'Img
3026                           & " link=" & Data.Need_Linking'Img
3027                           & " closure=" & Data.Closure_Needed'Img
3028                           & " mains=" & Data.Number_Of_Mains'Img,
3029                           Project.Name);
3030          end if;
3031       end Do_Compute;
3032
3033       procedure Compute_All is new For_Project_And_Aggregated (Do_Compute);
3034
3035    begin
3036       Compute_All (Root_Project, Tree);
3037    end Compute_Compilation_Phases;
3038
3039    ------------------------------
3040    -- Compute_Builder_Switches --
3041    ------------------------------
3042
3043    procedure Compute_Builder_Switches
3044      (Project_Tree        : Project_Tree_Ref;
3045       Root_Environment    : in out Prj.Tree.Environment;
3046       Main_Project        : Project_Id;
3047       Only_For_Lang       : Name_Id := No_Name)
3048    is
3049       Builder_Package  : constant Package_Id :=
3050                            Value_Of (Name_Builder, Main_Project.Decl.Packages,
3051                                      Project_Tree.Shared);
3052
3053       Global_Compilation_Array    : Array_Element_Id;
3054       Global_Compilation_Elem     : Array_Element;
3055       Global_Compilation_Switches : Variable_Value;
3056
3057       Default_Switches_Array : Array_Id;
3058
3059       Builder_Switches_Lang : Name_Id := No_Name;
3060
3061       List             : String_List_Id;
3062       Element          : String_Element;
3063
3064       Index            : Name_Id;
3065       Source           : Prj.Source_Id;
3066
3067       Lang              : Name_Id := No_Name;  --  language index for Switches
3068       Switches_For_Lang : Variable_Value := Nil_Variable_Value;
3069       --  Value of Builder'Default_Switches(lang)
3070
3071       Name              : Name_Id := No_Name;  --  main file index for Switches
3072       Switches_For_Main : Variable_Value := Nil_Variable_Value;
3073       --  Switches for a specific main. When there are several mains, Name is
3074       --  set to No_Name, and Switches_For_Main might be left with an actual
3075       --  value (so that we can display a warning that it was ignored).
3076
3077       Other_Switches : Variable_Value := Nil_Variable_Value;
3078       --  Value of Builder'Switches(others)
3079
3080       Defaults : Variable_Value := Nil_Variable_Value;
3081
3082       Switches : Variable_Value := Nil_Variable_Value;
3083       --  The computed builder switches
3084
3085       Success          : Boolean := False;
3086    begin
3087       if Builder_Package /= No_Package then
3088          Mains.Reset;
3089
3090          --  If there is no main, and there is only one compilable language,
3091          --  use this language as the switches index.
3092
3093          if Mains.Number_Of_Mains (Project_Tree) = 0 then
3094             if Only_For_Lang = No_Name then
3095                declare
3096                   Language : Language_Ptr := Main_Project.Languages;
3097
3098                begin
3099                   while Language /= No_Language_Index loop
3100                      if Language.Config.Compiler_Driver /= No_File
3101                        and then Language.Config.Compiler_Driver /= Empty_File
3102                      then
3103                         if Lang /= No_Name then
3104                            Lang := No_Name;
3105                            exit;
3106                         else
3107                            Lang := Language.Name;
3108                         end if;
3109                      end if;
3110                      Language := Language.Next;
3111                   end loop;
3112                end;
3113             else
3114                Lang := Only_For_Lang;
3115             end if;
3116
3117          else
3118             for Index in 1 .. Mains.Number_Of_Mains (Project_Tree) loop
3119                Source := Mains.Next_Main.Source;
3120
3121                if Source /= No_Source then
3122                   if Switches_For_Main = Nil_Variable_Value then
3123                      Switches_For_Main := Value_Of
3124                        (Name                    => Name_Id (Source.File),
3125                         Attribute_Or_Array_Name => Name_Switches,
3126                         In_Package              => Builder_Package,
3127                         Shared                  => Project_Tree.Shared,
3128                         Force_Lower_Case_Index  => False,
3129                         Allow_Wildcards         => True);
3130
3131                      --  If not found, try without extension.
3132                      --  That's because gnatmake accepts truncated file names
3133                      --  in Builder'Switches
3134
3135                      if Switches_For_Main = Nil_Variable_Value
3136                        and then Source.Unit /= null
3137                      then
3138                         Switches_For_Main := Value_Of
3139                           (Name                    => Source.Unit.Name,
3140                            Attribute_Or_Array_Name => Name_Switches,
3141                            In_Package              => Builder_Package,
3142                            Shared                  => Project_Tree.Shared,
3143                            Force_Lower_Case_Index  => False,
3144                            Allow_Wildcards         => True);
3145                      end if;
3146                   end if;
3147
3148                   if Index = 1 then
3149                      Lang := Source.Language.Name;
3150                      Name := Name_Id (Source.File);
3151                   else
3152                      Name := No_Name;  --  Can't use main specific switches
3153
3154                      if Lang /= Source.Language.Name then
3155                         Lang := No_Name;
3156                      end if;
3157                   end if;
3158                end if;
3159             end loop;
3160          end if;
3161
3162          Global_Compilation_Array := Value_Of
3163            (Name      => Name_Global_Compilation_Switches,
3164             In_Arrays => Project_Tree.Shared.Packages.Table
3165               (Builder_Package).Decl.Arrays,
3166             Shared    => Project_Tree.Shared);
3167
3168          Default_Switches_Array :=
3169            Project_Tree.Shared.Packages.Table (Builder_Package).Decl.Arrays;
3170
3171          while Default_Switches_Array /= No_Array
3172            and then
3173              Project_Tree.Shared.Arrays.Table (Default_Switches_Array).Name /=
3174                Name_Default_Switches
3175          loop
3176             Default_Switches_Array :=
3177               Project_Tree.Shared.Arrays.Table (Default_Switches_Array).Next;
3178          end loop;
3179
3180          if Global_Compilation_Array /= No_Array_Element
3181            and then Default_Switches_Array /= No_Array
3182          then
3183             Prj.Err.Error_Msg
3184               (Root_Environment.Flags,
3185                "Default_Switches forbidden in presence of " &
3186                "Global_Compilation_Switches. Use Switches instead.",
3187                Project_Tree.Shared.Arrays.Table
3188                  (Default_Switches_Array).Location);
3189             Fail_Program
3190               (Project_Tree,
3191                "*** illegal combination of Builder attributes");
3192          end if;
3193
3194          if Lang /= No_Name then
3195             Switches_For_Lang := Prj.Util.Value_Of
3196               (Name                    => Lang,
3197                Index                   => 0,
3198                Attribute_Or_Array_Name => Name_Switches,
3199                In_Package              => Builder_Package,
3200                Shared                  => Project_Tree.Shared,
3201                Force_Lower_Case_Index  => True);
3202
3203             Defaults := Prj.Util.Value_Of
3204               (Name                    => Lang,
3205                Index                   => 0,
3206                Attribute_Or_Array_Name => Name_Default_Switches,
3207                In_Package              => Builder_Package,
3208                Shared                  => Project_Tree.Shared,
3209                Force_Lower_Case_Index  => True);
3210          end if;
3211
3212          Other_Switches := Prj.Util.Value_Of
3213            (Name                    => All_Other_Names,
3214             Index                   => 0,
3215             Attribute_Or_Array_Name => Name_Switches,
3216             In_Package              => Builder_Package,
3217             Shared                  => Project_Tree.Shared);
3218
3219          if not Quiet_Output
3220            and then Mains.Number_Of_Mains (Project_Tree) > 1
3221            and then Switches_For_Main /= Nil_Variable_Value
3222          then
3223             --  More than one main, but we had main-specific switches that
3224             --  are ignored.
3225
3226             if Switches_For_Lang /= Nil_Variable_Value then
3227                Write_Line
3228                  ("Warning: using Builder'Switches("""
3229                   & Get_Name_String (Lang)
3230                   & """), as there are several mains");
3231
3232             elsif Other_Switches /= Nil_Variable_Value then
3233                Write_Line
3234                  ("Warning: using Builder'Switches(others), "
3235                   & "as there are several mains");
3236
3237             elsif Defaults /= Nil_Variable_Value then
3238                Write_Line
3239                  ("Warning: using Builder'Default_Switches("""
3240                   & Get_Name_String (Lang)
3241                   & """), as there are several mains");
3242             else
3243                Write_Line
3244                  ("Warning: using no switches from package "
3245                   & "Builder, as there are several mains");
3246             end if;
3247          end if;
3248
3249          Builder_Switches_Lang := Lang;
3250
3251          if Name /= No_Name then
3252             --  Get the switches for the single main
3253             Switches := Switches_For_Main;
3254          end if;
3255
3256          if Switches = Nil_Variable_Value or else Switches.Default then
3257             --  Get the switches for the common language of the mains
3258             Switches := Switches_For_Lang;
3259          end if;
3260
3261          if Switches = Nil_Variable_Value or else Switches.Default then
3262             Switches := Other_Switches;
3263          end if;
3264
3265          --  For backward compatibility with gnatmake, if no Switches
3266          --  are declared, check for Default_Switches (<language>).
3267
3268          if Switches = Nil_Variable_Value or else Switches.Default then
3269             Switches := Defaults;
3270          end if;
3271
3272          --  If switches have been found, scan them
3273
3274          if Switches /= Nil_Variable_Value and then not Switches.Default then
3275             List := Switches.Values;
3276
3277             while List /= Nil_String loop
3278                Element := Project_Tree.Shared.String_Elements.Table (List);
3279                Get_Name_String (Element.Value);
3280
3281                if Name_Len /= 0 then
3282                   declare
3283                      --  Add_Switch might itself be using the name_buffer, so
3284                      --  we make a temporary here.
3285                      Switch : constant String := Name_Buffer (1 .. Name_Len);
3286                   begin
3287                      Success := Add_Switch
3288                        (Switch      => Switch,
3289                         For_Lang    => Builder_Switches_Lang,
3290                         For_Builder => True,
3291                         Has_Global_Compilation_Switches =>
3292                           Global_Compilation_Array /= No_Array_Element);
3293                   end;
3294
3295                   if not Success then
3296                      for J in reverse 1 .. Name_Len loop
3297                         Name_Buffer (J + J) := Name_Buffer (J);
3298                         Name_Buffer (J + J - 1) := ''';
3299                      end loop;
3300
3301                      Name_Len := Name_Len + Name_Len;
3302
3303                      Prj.Err.Error_Msg
3304                        (Root_Environment.Flags,
3305                         '"' & Name_Buffer (1 .. Name_Len) &
3306                         """ is not a builder switch. Consider moving " &
3307                         "it to Global_Compilation_Switches.",
3308                         Element.Location);
3309                      Fail_Program
3310                        (Project_Tree,
3311                         "*** illegal switch """ &
3312                         Get_Name_String (Element.Value) & '"');
3313                   end if;
3314                end if;
3315
3316                List := Element.Next;
3317             end loop;
3318          end if;
3319
3320          --  Reset the Builder Switches language
3321
3322          Builder_Switches_Lang := No_Name;
3323
3324          --  Take into account attributes Global_Compilation_Switches
3325
3326          while Global_Compilation_Array /= No_Array_Element loop
3327             Global_Compilation_Elem :=
3328               Project_Tree.Shared.Array_Elements.Table
3329                 (Global_Compilation_Array);
3330
3331             Get_Name_String (Global_Compilation_Elem.Index);
3332             To_Lower (Name_Buffer (1 .. Name_Len));
3333             Index := Name_Find;
3334
3335             if Only_For_Lang = No_Name or else Index = Only_For_Lang then
3336                Global_Compilation_Switches := Global_Compilation_Elem.Value;
3337
3338                if Global_Compilation_Switches /= Nil_Variable_Value
3339                  and then not Global_Compilation_Switches.Default
3340                then
3341                   --  We have found an attribute
3342                   --  Global_Compilation_Switches for a language: put the
3343                   --  switches in the appropriate table.
3344
3345                   List := Global_Compilation_Switches.Values;
3346                   while List /= Nil_String loop
3347                      Element :=
3348                        Project_Tree.Shared.String_Elements.Table (List);
3349
3350                      if Element.Value /= No_Name then
3351                         Success := Add_Switch
3352                           (Switch      => Get_Name_String (Element.Value),
3353                            For_Lang    => Index,
3354                            For_Builder => False,
3355                            Has_Global_Compilation_Switches =>
3356                              Global_Compilation_Array /= No_Array_Element);
3357                      end if;
3358
3359                      List := Element.Next;
3360                   end loop;
3361                end if;
3362             end if;
3363
3364             Global_Compilation_Array := Global_Compilation_Elem.Next;
3365          end loop;
3366       end if;
3367    end Compute_Builder_Switches;
3368
3369    ---------------------
3370    -- Write_Path_File --
3371    ---------------------
3372
3373    procedure Write_Path_File (FD : File_Descriptor) is
3374       Last   : Natural;
3375       Status : Boolean;
3376
3377    begin
3378       Name_Len := 0;
3379
3380       for Index in Directories.First .. Directories.Last loop
3381          Add_Str_To_Name_Buffer (Get_Name_String (Directories.Table (Index)));
3382          Add_Char_To_Name_Buffer (ASCII.LF);
3383       end loop;
3384
3385       Last := Write (FD, Name_Buffer (1)'Address, Name_Len);
3386
3387       if Last = Name_Len then
3388          Close (FD, Status);
3389       else
3390          Status := False;
3391       end if;
3392
3393       if not Status then
3394          Prj.Com.Fail ("could not write temporary file");
3395       end if;
3396    end Write_Path_File;
3397
3398 end Makeutl;