10cf3458ebfa669988bf99da2d0cb07de7bdbe76
[platform/upstream/gcc.git] / gcc / ada / gnatcmd.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                              G N A T C M D                               --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1996-2009, Free Software Foundation, Inc.         --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license.          --
20 --                                                                          --
21 -- GNAT was originally developed  by the GNAT team at  New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
23 --                                                                          --
24 ------------------------------------------------------------------------------
25
26 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
27
28 with Csets;
29 with Makeutl;  use Makeutl;
30 with MLib.Tgt; use MLib.Tgt;
31 with MLib.Utl;
32 with MLib.Fil;
33 with Namet;    use Namet;
34 with Opt;      use Opt;
35 with Osint;    use Osint;
36 with Output;
37 with Prj;      use Prj;
38 with Prj.Env;
39 with Prj.Ext;  use Prj.Ext;
40 with Prj.Pars;
41 with Prj.Tree; use Prj.Tree;
42 with Prj.Util; use Prj.Util;
43 with Sinput.P;
44 with Snames;   use Snames;
45 with Table;
46 with Targparm;
47 with Tempdir;
48 with Types;    use Types;
49 with Hostparm; use Hostparm;
50 --  Used to determine if we are in VMS or not for error message purposes
51
52 with Ada.Characters.Handling; use Ada.Characters.Handling;
53 with Ada.Command_Line;        use Ada.Command_Line;
54 with Ada.Text_IO;             use Ada.Text_IO;
55
56 with GNAT.OS_Lib;             use GNAT.OS_Lib;
57
58 with VMS_Conv;                use VMS_Conv;
59
60 procedure GNATCmd is
61    Project_Node_Tree : Project_Node_Tree_Ref;
62    Project_File      : String_Access;
63    Project           : Prj.Project_Id;
64    Current_Verbosity : Prj.Verbosity := Prj.Default;
65    Tool_Package_Name : Name_Id       := No_Name;
66
67    B_Start : String_Ptr    := new String'("b~");
68    --  Prefix of binder generated file, changed to b__ for VMS
69
70    Old_Project_File_Used : Boolean := False;
71    --  This flag indicates a switch -p (for gnatxref and gnatfind) for
72    --  an old fashioned project file. -p cannot be used in conjunction
73    --  with -P.
74
75    Temp_File_Name : Path_Name_Type := No_Path;
76    --  The name of the temporary text file to put a list of source/object
77    --  files to pass to a tool.
78
79    ASIS_Main : String_Access := null;
80    --  Main for commands Check, Metric and Pretty, when -U is used
81
82    package First_Switches is new Table.Table
83      (Table_Component_Type => String_Access,
84       Table_Index_Type     => Integer,
85       Table_Low_Bound      => 1,
86       Table_Initial        => 20,
87       Table_Increment      => 100,
88       Table_Name           => "Gnatcmd.First_Switches");
89    --  A table to keep the switches from the project file
90
91    package Carg_Switches is new Table.Table
92      (Table_Component_Type => String_Access,
93       Table_Index_Type     => Integer,
94       Table_Low_Bound      => 1,
95       Table_Initial        => 20,
96       Table_Increment      => 100,
97       Table_Name           => "Gnatcmd.Carg_Switches");
98    --  A table to keep the switches following -cargs for ASIS tools
99
100    package Rules_Switches is new Table.Table
101      (Table_Component_Type => String_Access,
102       Table_Index_Type     => Integer,
103       Table_Low_Bound      => 1,
104       Table_Initial        => 20,
105       Table_Increment      => 100,
106       Table_Name           => "Gnatcmd.Rules_Switches");
107    --  A table to keep the switches following -rules for gnatcheck
108
109    package Library_Paths is new Table.Table (
110      Table_Component_Type => String_Access,
111      Table_Index_Type     => Integer,
112      Table_Low_Bound      => 1,
113      Table_Initial        => 20,
114      Table_Increment      => 100,
115      Table_Name           => "Make.Library_Path");
116
117    --  Packages of project files to pass to Prj.Pars.Parse, depending on the
118    --  tool. We allocate objects because we cannot declare aliased objects
119    --  as we are in a procedure, not a library level package.
120
121    subtype SA is String_Access;
122
123    Naming_String      : constant SA := new String'("naming");
124    Binder_String      : constant SA := new String'("binder");
125    Compiler_String    : constant SA := new String'("compiler");
126    Check_String       : constant SA := new String'("check");
127    Synchronize_String : constant SA := new String'("synchronize");
128    Eliminate_String   : constant SA := new String'("eliminate");
129    Finder_String      : constant SA := new String'("finder");
130    Linker_String      : constant SA := new String'("linker");
131    Gnatls_String      : constant SA := new String'("gnatls");
132    Pretty_String      : constant SA := new String'("pretty_printer");
133    Stack_String       : constant SA := new String'("stack");
134    Gnatstub_String    : constant SA := new String'("gnatstub");
135    Metric_String      : constant SA := new String'("metrics");
136    Xref_String        : constant SA := new String'("cross_reference");
137
138    Packages_To_Check_By_Binder   : constant String_List_Access :=
139      new String_List'((Naming_String, Binder_String));
140
141    Packages_To_Check_By_Check : constant String_List_Access :=
142      new String_List'((Naming_String, Check_String, Compiler_String));
143
144    Packages_To_Check_By_Sync : constant String_List_Access :=
145      new String_List'((Naming_String, Synchronize_String, Compiler_String));
146
147    Packages_To_Check_By_Eliminate : constant String_List_Access :=
148      new String_List'((Naming_String, Eliminate_String, Compiler_String));
149
150    Packages_To_Check_By_Finder    : constant String_List_Access :=
151      new String_List'((Naming_String, Finder_String));
152
153    Packages_To_Check_By_Linker    : constant String_List_Access :=
154      new String_List'((Naming_String, Linker_String));
155
156    Packages_To_Check_By_Gnatls    : constant String_List_Access :=
157      new String_List'((Naming_String, Gnatls_String));
158
159    Packages_To_Check_By_Pretty    : constant String_List_Access :=
160      new String_List'((Naming_String, Pretty_String, Compiler_String));
161
162    Packages_To_Check_By_Stack     : constant String_List_Access :=
163      new String_List'((Naming_String, Stack_String));
164
165    Packages_To_Check_By_Gnatstub  : constant String_List_Access :=
166      new String_List'((Naming_String, Gnatstub_String, Compiler_String));
167
168    Packages_To_Check_By_Metric  : constant String_List_Access :=
169      new String_List'((Naming_String, Metric_String, Compiler_String));
170
171    Packages_To_Check_By_Xref      : constant String_List_Access :=
172      new String_List'((Naming_String, Xref_String));
173
174    Packages_To_Check : String_List_Access := Prj.All_Packages;
175
176    ----------------------------------
177    -- Declarations for GNATCMD use --
178    ----------------------------------
179
180    The_Command : Command_Type;
181    --  The command specified in the invocation of the GNAT driver
182
183    Command_Arg : Positive := 1;
184    --  The index of the command in the arguments of the GNAT driver
185
186    My_Exit_Status : Exit_Status := Success;
187    --  The exit status of the spawned tool. Used to set the correct VMS
188    --  exit status.
189
190    Current_Work_Dir : constant String := Get_Current_Dir;
191    --  The path of the working directory
192
193    All_Projects : Boolean := False;
194    --  Flag used for GNAT CHECK, GNAT PRETTY, GNAT METRIC, and GNAT STACK to
195    --  indicate that the underlying tool (gnatcheck, gnatpp or gnatmetric)
196    --  should be invoked for all sources of all projects.
197
198    -----------------------
199    -- Local Subprograms --
200    -----------------------
201
202    procedure Add_To_Carg_Switches (Switch : String_Access);
203    --  Add a switch to the Carg_Switches table. If it is the first one, put the
204    --  switch "-cargs" at the beginning of the table.
205
206    procedure Add_To_Rules_Switches (Switch : String_Access);
207    --  Add a switch to the Rules_Switches table. If it is the first one, put
208    --  the switch "-crules" at the beginning of the table.
209
210    procedure Check_Files;
211    --  For GNAT LIST, GNAT PRETTY, GNAT METRIC, and GNAT STACK, check if a
212    --  project file is specified, without any file arguments and without a
213    --  switch -files=. If it is the case, invoke the GNAT tool with the proper
214    --  list of files, derived from the sources of the project.
215
216    function Check_Project
217      (Project      : Project_Id;
218       Root_Project : Project_Id) return Boolean;
219    --  Returns True if Project = Root_Project or if we want to consider all
220    --  sources of all projects. For GNAT METRIC, also returns True if Project
221    --  is extended by Root_Project.
222
223    procedure Check_Relative_Executable (Name : in out String_Access);
224    --  Check if an executable is specified as a relative path. If it is, and
225    --  the path contains directory information, fail. Otherwise, prepend the
226    --  exec directory. This procedure is only used for GNAT LINK when a project
227    --  file is specified.
228
229    function Configuration_Pragmas_File return Path_Name_Type;
230    --  Return an argument, if there is a configuration pragmas file to be
231    --  specified for Project, otherwise return No_Name. Used for gnatstub (GNAT
232    --  STUB), gnatpp (GNAT PRETTY), gnatelim (GNAT ELIM), and gnatmetric (GNAT
233    --  METRIC).
234
235    function Mapping_File return Path_Name_Type;
236    --  Create and return the path name of a mapping file. Used for gnatstub
237    --  (GNAT STUB), gnatpp (GNAT PRETTY), gnatelim (GNAT ELIM), and gnatmetric
238    --  (GNAT METRIC).
239
240    procedure Delete_Temp_Config_Files;
241    --  Delete all temporary config files. The caller is responsible for
242    --  ensuring that Keep_Temporary_Files is False.
243
244    procedure Get_Closure;
245    --  Get the sources in the closure of the ASIS_Main and add them to the
246    --  list of arguments.
247
248    function Index (Char : Character; Str : String) return Natural;
249    --  Returns first occurrence of Char in Str, returns 0 if Char not in Str
250
251    procedure Non_VMS_Usage;
252    --  Display usage for platforms other than VMS
253
254    procedure Process_Link;
255    --  Process GNAT LINK, when there is a project file specified
256
257    procedure Set_Library_For
258      (Project           : Project_Id;
259       Libraries_Present : in out Boolean);
260    --  If Project is a library project, add the correct -L and -l switches to
261    --  the linker invocation.
262
263    procedure Set_Libraries is
264       new For_Every_Project_Imported (Boolean, Set_Library_For);
265    --  Add the -L and -l switches to the linker for all of the library
266    --  projects.
267
268    procedure Test_If_Relative_Path
269      (Switch : in out String_Access;
270       Parent : String);
271    --  Test if Switch is a relative search path switch. If it is and it
272    --  includes directory information, prepend the path with Parent. This
273    --  subprogram is only called when using project files.
274
275    --------------------------
276    -- Add_To_Carg_Switches --
277    --------------------------
278
279    procedure Add_To_Carg_Switches (Switch : String_Access) is
280    begin
281       --  If the Carg_Switches table is empty, put "-cargs" at the beginning
282
283       if Carg_Switches.Last = 0 then
284          Carg_Switches.Increment_Last;
285          Carg_Switches.Table (Carg_Switches.Last) := new String'("-cargs");
286       end if;
287
288       Carg_Switches.Increment_Last;
289       Carg_Switches.Table (Carg_Switches.Last) := Switch;
290    end Add_To_Carg_Switches;
291
292    ---------------------------
293    -- Add_To_Rules_Switches --
294    ---------------------------
295
296    procedure Add_To_Rules_Switches (Switch : String_Access) is
297    begin
298       --  If the Rules_Switches table is empty, put "-rules" at the beginning
299
300       if Rules_Switches.Last = 0 then
301          Rules_Switches.Increment_Last;
302          Rules_Switches.Table (Rules_Switches.Last) := new String'("-rules");
303       end if;
304
305       Rules_Switches.Increment_Last;
306       Rules_Switches.Table (Rules_Switches.Last) := Switch;
307    end Add_To_Rules_Switches;
308
309    -----------------
310    -- Check_Files --
311    -----------------
312
313    procedure Check_Files is
314       Add_Sources : Boolean := True;
315       Unit        : Prj.Unit_Index;
316       Subunit     : Boolean := False;
317       FD          : File_Descriptor := Invalid_FD;
318       Status      : Integer;
319       Success     : Boolean;
320
321    begin
322       --  Check if there is at least one argument that is not a switch or if
323       --  there is a -files= switch.
324
325       for Index in 1 .. Last_Switches.Last loop
326          if Last_Switches.Table (Index).all'Length > 7
327            and then Last_Switches.Table (Index) (1 .. 7) = "-files="
328          then
329             Add_Sources := False;
330             exit;
331
332          elsif Last_Switches.Table (Index) (1) /= '-' then
333             if Index = 1
334               or else
335                 (The_Command = Check
336                    and then
337                      Last_Switches.Table (Index - 1).all /= "-o")
338               or else
339                 (The_Command = Pretty
340                    and then
341                      Last_Switches.Table (Index - 1).all /= "-o"  and then
342                      Last_Switches.Table (Index - 1).all /= "-of")
343               or else
344                 (The_Command = Metric
345                    and then
346                      Last_Switches.Table (Index - 1).all /= "-o"  and then
347                      Last_Switches.Table (Index - 1).all /= "-og" and then
348                      Last_Switches.Table (Index - 1).all /= "-ox" and then
349                      Last_Switches.Table (Index - 1).all /= "-d")
350               or else
351                 (The_Command /= Check  and then
352                  The_Command /= Pretty and then
353                  The_Command /= Metric)
354             then
355                Add_Sources := False;
356                exit;
357             end if;
358          end if;
359       end loop;
360
361       --  If all arguments are switches and there is no switch -files=, add
362       --  the path names of all the sources of the main project.
363
364       if Add_Sources then
365
366          --  For gnatcheck, gnatpp and gnatmetric , create a temporary file
367          --  and put the list of sources in it.
368
369          if The_Command = Check  or else
370             The_Command = Pretty or else
371             The_Command = Metric
372          then
373             Tempdir.Create_Temp_File (FD, Temp_File_Name);
374             Last_Switches.Increment_Last;
375             Last_Switches.Table (Last_Switches.Last) :=
376               new String'("-files=" & Get_Name_String (Temp_File_Name));
377          end if;
378
379          declare
380             Proj : Project_List;
381
382          begin
383             --  Gnatstack needs to add the .ci file for the binder generated
384             --  files corresponding to all of the library projects and main
385             --  units belonging to the application.
386
387             if The_Command = Stack then
388                Proj := Project_Tree.Projects;
389                while Proj /= null loop
390                   if Check_Project (Proj.Project, Project) then
391                      declare
392                         Main : String_List_Id;
393                         File : String_Access;
394
395                      begin
396                         --  Include binder generated files for main programs
397
398                         Main := Proj.Project.Mains;
399                         while Main /= Nil_String loop
400                            File :=
401                              new String'
402                                (Get_Name_String
403                                  (Proj.Project.Object_Directory.Name)        &
404                                 B_Start.all                                  &
405                                 MLib.Fil.Ext_To
406                                   (Get_Name_String
407                                      (Project_Tree.String_Elements.Table
408                                         (Main).Value),
409                                    "ci"));
410
411                            if Is_Regular_File (File.all) then
412                               Last_Switches.Increment_Last;
413                               Last_Switches.Table (Last_Switches.Last) := File;
414                            end if;
415
416                            Main :=
417                              Project_Tree.String_Elements.Table (Main).Next;
418                         end loop;
419
420                         if Proj.Project.Library then
421
422                            --  Include the .ci file for the binder generated
423                            --  files that contains the initialization and
424                            --  finalization of the library.
425
426                            File :=
427                              new String'
428                                (Get_Name_String
429                                  (Proj.Project.Object_Directory.Name)        &
430                                 B_Start.all                                  &
431                                 Get_Name_String (Proj.Project.Library_Name)  &
432                                 ".ci");
433
434                            if Is_Regular_File (File.all) then
435                               Last_Switches.Increment_Last;
436                               Last_Switches.Table (Last_Switches.Last) := File;
437                            end if;
438                         end if;
439                      end;
440                   end if;
441
442                   Proj := Proj.Next;
443                end loop;
444             end if;
445
446             Unit := Units_Htable.Get_First (Project_Tree.Units_HT);
447             while Unit /= No_Unit_Index loop
448
449                --  For gnatls, we only need to put the library units, body or
450                --  spec, but not the subunits.
451
452                if The_Command = List then
453                   if Unit.File_Names (Impl) /= null
454                     and then not Unit.File_Names (Impl).Locally_Removed
455                   then
456                      --  There is a body, check if it is for this project
457
458                      if All_Projects or else
459                         Unit.File_Names (Impl).Project = Project
460                      then
461                         Subunit := False;
462
463                         if Unit.File_Names (Spec) = null
464                           or else Unit.File_Names (Spec).Locally_Removed
465                         then
466                            --  We have a body with no spec: we need to check if
467                            --  this is a subunit, because gnatls will complain
468                            --  about subunits.
469
470                            declare
471                               Src_Ind : constant Source_File_Index :=
472                                           Sinput.P.Load_Project_File
473                                             (Get_Name_String
474                                               (Unit.File_Names
475                                                 (Impl).Path.Name));
476                            begin
477                               Subunit :=
478                                 Sinput.P.Source_File_Is_Subunit (Src_Ind);
479                            end;
480                         end if;
481
482                         if not Subunit then
483                            Last_Switches.Increment_Last;
484                            Last_Switches.Table (Last_Switches.Last) :=
485                              new String'
486                                (Get_Name_String
487                                     (Unit.File_Names
488                                          (Impl).Display_File));
489                         end if;
490                      end if;
491
492                   elsif Unit.File_Names (Spec) /= null
493                     and then not Unit.File_Names (Spec).Locally_Removed
494                   then
495                      --  We have a spec with no body. Check if it is for this
496                      --  project.
497
498                      if All_Projects or else
499                         Unit.File_Names (Spec).Project = Project
500                      then
501                         Last_Switches.Increment_Last;
502                         Last_Switches.Table (Last_Switches.Last) :=
503                           new String'(Get_Name_String
504                                        (Unit.File_Names (Spec).Display_File));
505                      end if;
506                   end if;
507
508                --  For gnatstack, we put the .ci files corresponding to the
509                --  different units, including the binder generated files. We
510                --  only need to do that for the library units, body or spec,
511                --  but not the subunits.
512
513                elsif The_Command = Stack then
514                   if Unit.File_Names (Impl) /= null
515                     and then not Unit.File_Names (Impl).Locally_Removed
516                   then
517                      --  There is a body. Check if .ci files for this project
518                      --  must be added.
519
520                      if Check_Project
521                           (Unit.File_Names (Impl).Project, Project)
522                      then
523                         Subunit := False;
524
525                         if Unit.File_Names (Spec) = null
526                           or else Unit.File_Names (Spec).Locally_Removed
527                         then
528                            --  We have a body with no spec: we need to check
529                            --  if this is a subunit, because .ci files are not
530                            --  generated for subunits.
531
532                            declare
533                               Src_Ind : constant Source_File_Index :=
534                                           Sinput.P.Load_Project_File
535                                             (Get_Name_String
536                                               (Unit.File_Names
537                                                 (Impl).Path.Name));
538                            begin
539                               Subunit :=
540                                 Sinput.P.Source_File_Is_Subunit (Src_Ind);
541                            end;
542                         end if;
543
544                         if not Subunit then
545                            Last_Switches.Increment_Last;
546                            Last_Switches.Table (Last_Switches.Last) :=
547                              new String'
548                                (Get_Name_String
549                                  (Unit.File_Names
550                                    (Impl).Project. Object_Directory.Name)  &
551                                 MLib.Fil.Ext_To
552                                   (Get_Name_String
553                                      (Unit.File_Names (Impl).Display_File),
554                                    "ci"));
555                         end if;
556                      end if;
557
558                   elsif Unit.File_Names (Spec) /= null
559                     and then not Unit.File_Names (Spec).Locally_Removed
560                   then
561                      --  Spec with no body, check if it is for this project
562
563                      if Check_Project
564                           (Unit.File_Names (Spec).Project, Project)
565                      then
566                         Last_Switches.Increment_Last;
567                         Last_Switches.Table (Last_Switches.Last) :=
568                           new String'
569                             (Get_Name_String
570                               (Unit.File_Names
571                                 (Spec).Project. Object_Directory.Name)     &
572                              Dir_Separator                                 &
573                              MLib.Fil.Ext_To
574                                (Get_Name_String (Unit.File_Names (Spec).File),
575                                 "ci"));
576                      end if;
577                   end if;
578
579                else
580                   --  For gnatcheck, gnatsync, gnatpp and gnatmetric, put all
581                   --  sources of the project, or of all projects if -U was
582                   --  specified.
583
584                   for Kind in Spec_Or_Body loop
585                      if Unit.File_Names (Kind) /= null
586                        and then Check_Project
587                                   (Unit.File_Names (Kind).Project, Project)
588                        and then not Unit.File_Names (Kind).Locally_Removed
589                      then
590                         Name_Len := 0;
591                         Add_Char_To_Name_Buffer ('"');
592                         Add_Str_To_Name_Buffer
593                           (Get_Name_String
594                             (Unit.File_Names (Kind).Path.Display_Name));
595                         Add_Char_To_Name_Buffer ('"');
596
597                         if FD /= Invalid_FD then
598                            Name_Len := Name_Len + 1;
599                            Name_Buffer (Name_Len) := ASCII.LF;
600                            Status :=
601                              Write (FD, Name_Buffer (1)'Address, Name_Len);
602
603                            if Status /= Name_Len then
604                               Osint.Fail ("disk full");
605                            end if;
606
607                         else
608                            Last_Switches.Increment_Last;
609                            Last_Switches.Table (Last_Switches.Last) :=
610                              new String'(Get_Name_String
611                                           (Unit.File_Names
612                                             (Kind).Path.Display_Name));
613                         end if;
614                      end if;
615                   end loop;
616                end if;
617
618                Unit := Units_Htable.Get_Next (Project_Tree.Units_HT);
619             end loop;
620          end;
621
622          if FD /= Invalid_FD then
623             Close (FD, Success);
624
625             if not Success then
626                Osint.Fail ("disk full");
627             end if;
628          end if;
629       end if;
630    end Check_Files;
631
632    -------------------
633    -- Check_Project --
634    -------------------
635
636    function Check_Project
637      (Project      : Project_Id;
638       Root_Project : Project_Id) return Boolean
639    is
640       Proj : Project_Id;
641
642    begin
643       if Project = No_Project then
644          return False;
645
646       elsif All_Projects or else Project = Root_Project then
647          return True;
648
649       elsif The_Command = Metric then
650          Proj := Root_Project;
651          while Proj.Extends /= No_Project loop
652             if Project = Proj.Extends then
653                return True;
654             end if;
655
656             Proj := Proj.Extends;
657          end loop;
658       end if;
659
660       return False;
661    end Check_Project;
662
663    -------------------------------
664    -- Check_Relative_Executable --
665    -------------------------------
666
667    procedure Check_Relative_Executable (Name : in out String_Access) is
668       Exec_File_Name : constant String := Name.all;
669
670    begin
671       if not Is_Absolute_Path (Exec_File_Name) then
672          for Index in Exec_File_Name'Range loop
673             if Exec_File_Name (Index) = Directory_Separator then
674                Fail ("relative executable (""" &
675                        Exec_File_Name &
676                        """) with directory part not allowed " &
677                        "when using project files");
678             end if;
679          end loop;
680
681          Get_Name_String (Project.Exec_Directory.Name);
682
683          if Name_Buffer (Name_Len) /= Directory_Separator then
684             Name_Len := Name_Len + 1;
685             Name_Buffer (Name_Len) := Directory_Separator;
686          end if;
687
688          Name_Buffer (Name_Len + 1 ..
689                         Name_Len + Exec_File_Name'Length) :=
690            Exec_File_Name;
691          Name_Len := Name_Len + Exec_File_Name'Length;
692          Name := new String'(Name_Buffer (1 .. Name_Len));
693       end if;
694    end Check_Relative_Executable;
695
696    --------------------------------
697    -- Configuration_Pragmas_File --
698    --------------------------------
699
700    function Configuration_Pragmas_File return Path_Name_Type is
701    begin
702       Prj.Env.Create_Config_Pragmas_File (Project, Project_Tree);
703       return Project.Config_File_Name;
704    end Configuration_Pragmas_File;
705
706    ------------------------------
707    -- Delete_Temp_Config_Files --
708    ------------------------------
709
710    procedure Delete_Temp_Config_Files is
711       Success : Boolean;
712       Proj    : Project_List;
713       pragma Warnings (Off, Success);
714
715    begin
716       --  This should only be called if Keep_Temporary_Files is False
717
718       pragma Assert (not Keep_Temporary_Files);
719
720       if Project /= No_Project then
721          Proj := Project_Tree.Projects;
722          while Proj /= null loop
723             if Proj.Project.Config_File_Temp then
724                Delete_Temporary_File
725                  (Project_Tree, Proj.Project.Config_File_Name);
726             end if;
727
728             Proj := Proj.Next;
729          end loop;
730       end if;
731
732       --  If a temporary text file that contains a list of files for a tool
733       --  has been created, delete this temporary file.
734
735       if Temp_File_Name /= No_Path then
736          Delete_Temporary_File (Project_Tree, Temp_File_Name);
737       end if;
738    end Delete_Temp_Config_Files;
739
740    -----------------
741    -- Get_Closure --
742    -----------------
743
744    procedure Get_Closure is
745       Args : constant Argument_List :=
746                (1 => new String'("-q"),
747                 2 => new String'("-b"),
748                 3 => new String'("-P"),
749                 4 => Project_File,
750                 5 => ASIS_Main,
751                 6 => new String'("-bargs"),
752                 7 => new String'("-R"),
753                 8 => new String'("-Z"));
754       --  Arguments for the invocation of gnatmake which are added to the
755       --  Last_Arguments list by this procedure.
756
757       FD : File_Descriptor;
758       --  File descriptor for the temp file that will get the output of the
759       --  invocation of gnatmake.
760
761       Name : Path_Name_Type;
762       --  Path of the file FD
763
764       GN_Name : constant String := Program_Name ("gnatmake", "gnat").all;
765       --  Name for gnatmake
766
767       GN_Path : constant String_Access := Locate_Exec_On_Path (GN_Name);
768       --  Path of gnatmake
769
770       Return_Code : Integer;
771
772       Unused : Boolean;
773       pragma Warnings (Off, Unused);
774
775       File : Ada.Text_IO.File_Type;
776       Line : String (1 .. 250);
777       Last : Natural;
778       --  Used to read file if there is an error, it is good enough to display
779       --  just 250 characters if the first line of the file is very long.
780
781       Unit  : Unit_Index;
782       Path  : Path_Name_Type;
783
784    begin
785       if GN_Path = null then
786          Put_Line (Standard_Error, "could not locate " & GN_Name);
787          raise Error_Exit;
788       end if;
789
790       --  Create the temp file
791
792       Tempdir.Create_Temp_File (FD, Name);
793
794       --  And close it, because on VMS Spawn with a file descriptor created
795       --  with Create_Temp_File does not redirect output.
796
797       Close (FD);
798
799       --  Spawn "gnatmake -q -b -P <project> <main> -bargs -R -Z"
800
801       Spawn
802         (Program_Name => GN_Path.all,
803          Args         => Args,
804          Output_File  => Get_Name_String (Name),
805          Success      => Unused,
806          Return_Code  => Return_Code,
807          Err_To_Out   => True);
808
809       Close (FD);
810
811       --  Read the output of the invocation of gnatmake
812
813       Open (File, In_File, Get_Name_String (Name));
814
815       --  If it was unsuccessful, display the first line in the file and exit
816       --  with error.
817
818       if Return_Code /= 0 then
819          Get_Line (File, Line, Last);
820
821          if not Keep_Temporary_Files then
822             Delete (File);
823          else
824             Close (File);
825          end if;
826
827          Put_Line (Standard_Error, Line (1 .. Last));
828          Put_Line
829            (Standard_Error, "could not get closure of " & ASIS_Main.all);
830          raise Error_Exit;
831
832       else
833          --  Get each file name in the file, find its path and add it the
834          --  list of arguments.
835
836          while not End_Of_File (File) loop
837             Get_Line (File, Line, Last);
838             Path := No_Path;
839
840             Unit := Units_Htable.Get_First (Project_Tree.Units_HT);
841             while Unit /= No_Unit_Index loop
842                if Unit.File_Names (Spec) /= null
843                  and then
844                    Get_Name_String (Unit.File_Names (Spec).File) =
845                       Line (1 .. Last)
846                then
847                   Path := Unit.File_Names (Spec).Path.Name;
848                   exit;
849
850                elsif Unit.File_Names (Impl) /= null
851                  and then
852                    Get_Name_String (Unit.File_Names (Impl).File) =
853                      Line (1 .. Last)
854                then
855                   Path := Unit.File_Names (Impl).Path.Name;
856                   exit;
857                end if;
858
859                Unit := Units_Htable.Get_Next (Project_Tree.Units_HT);
860             end loop;
861
862             Last_Switches.Increment_Last;
863
864             if Path /= No_Path then
865                Last_Switches.Table (Last_Switches.Last) :=
866                   new String'(Get_Name_String (Path));
867
868             else
869                Last_Switches.Table (Last_Switches.Last) :=
870                  new String'(Line (1 .. Last));
871             end if;
872          end loop;
873
874          if not Keep_Temporary_Files then
875             Delete (File);
876          else
877             Close (File);
878          end if;
879       end if;
880    end Get_Closure;
881
882    -----------
883    -- Index --
884    -----------
885
886    function Index (Char : Character; Str : String) return Natural is
887    begin
888       for Index in Str'Range loop
889          if Str (Index) = Char then
890             return Index;
891          end if;
892       end loop;
893
894       return 0;
895    end Index;
896
897    ------------------
898    -- Mapping_File --
899    ------------------
900
901    function Mapping_File return Path_Name_Type is
902       Result : Path_Name_Type;
903
904    begin
905       Prj.Env.Create_Mapping_File
906         (Project  => Project,
907          Language => Name_Ada,
908          In_Tree  => Project_Tree,
909          Name     => Result);
910       return Result;
911    end Mapping_File;
912
913    ------------------
914    -- Process_Link --
915    ------------------
916
917    procedure Process_Link is
918       Look_For_Executable : Boolean := True;
919       Libraries_Present   : Boolean := False;
920       Path_Option         : constant String_Access :=
921                               MLib.Linker_Library_Path_Option;
922       Prj                 : Project_Id := Project;
923       Arg                 : String_Access;
924       Last                : Natural := 0;
925       Skip_Executable     : Boolean := False;
926
927    begin
928       --  Add the default search directories, to be able to find
929       --  libgnat in call to MLib.Utl.Lib_Directory.
930
931       Add_Default_Search_Dirs;
932
933       Library_Paths.Set_Last (0);
934
935       --  Check if there are library project files
936
937       if MLib.Tgt.Support_For_Libraries /= None then
938          Set_Libraries (Project, Libraries_Present);
939       end if;
940
941       --  If there are, add the necessary additional switches
942
943       if Libraries_Present then
944
945          --  Add -L<lib_dir> -lgnarl -lgnat -Wl,-rpath,<lib_dir>
946
947          Last_Switches.Increment_Last;
948          Last_Switches.Table (Last_Switches.Last) :=
949            new String'("-L" & MLib.Utl.Lib_Directory);
950          Last_Switches.Increment_Last;
951          Last_Switches.Table (Last_Switches.Last) :=
952            new String'("-lgnarl");
953          Last_Switches.Increment_Last;
954          Last_Switches.Table (Last_Switches.Last) :=
955            new String'("-lgnat");
956
957          --  If Path_Option is not null, create the switch ("-Wl,-rpath," or
958          --  equivalent) with all the library dirs plus the standard GNAT
959          --  library dir.
960
961          if Path_Option /= null then
962             declare
963                Option  : String_Access;
964                Length  : Natural := Path_Option'Length;
965                Current : Natural;
966
967             begin
968                if MLib.Separate_Run_Path_Options then
969
970                   --  We are going to create one switch of the form
971                   --  "-Wl,-rpath,dir_N" for each directory to consider.
972
973                   --  One switch for each library directory
974
975                   for Index in
976                     Library_Paths.First .. Library_Paths.Last
977                   loop
978                      Last_Switches.Increment_Last;
979                      Last_Switches.Table
980                        (Last_Switches.Last) := new String'
981                        (Path_Option.all &
982                         Last_Switches.Table (Index).all);
983                   end loop;
984
985                   --  One switch for the standard GNAT library dir
986
987                   Last_Switches.Increment_Last;
988                   Last_Switches.Table
989                     (Last_Switches.Last) := new String'
990                     (Path_Option.all & MLib.Utl.Lib_Directory);
991
992                else
993                   --  First, compute the exact length for the switch
994
995                   for Index in
996                     Library_Paths.First .. Library_Paths.Last
997                   loop
998                      --  Add the length of the library dir plus one for the
999                      --  directory separator.
1000
1001                      Length :=
1002                        Length +
1003                          Library_Paths.Table (Index)'Length + 1;
1004                   end loop;
1005
1006                   --  Finally, add the length of the standard GNAT library dir
1007
1008                   Length := Length + MLib.Utl.Lib_Directory'Length;
1009                   Option := new String (1 .. Length);
1010                   Option (1 .. Path_Option'Length) := Path_Option.all;
1011                   Current := Path_Option'Length;
1012
1013                   --  Put each library dir followed by a dir separator
1014
1015                   for Index in
1016                     Library_Paths.First .. Library_Paths.Last
1017                   loop
1018                      Option
1019                        (Current + 1 ..
1020                           Current +
1021                             Library_Paths.Table (Index)'Length) :=
1022                        Library_Paths.Table (Index).all;
1023                      Current :=
1024                        Current +
1025                          Library_Paths.Table (Index)'Length + 1;
1026                      Option (Current) := Path_Separator;
1027                   end loop;
1028
1029                   --  Finally put the standard GNAT library dir
1030
1031                   Option
1032                     (Current + 1 ..
1033                        Current + MLib.Utl.Lib_Directory'Length) :=
1034                       MLib.Utl.Lib_Directory;
1035
1036                   --  And add the switch to the last switches
1037
1038                   Last_Switches.Increment_Last;
1039                   Last_Switches.Table (Last_Switches.Last) :=
1040                     Option;
1041                end if;
1042             end;
1043          end if;
1044       end if;
1045
1046       --  Check if the first ALI file specified can be found, either in the
1047       --  object directory of the main project or in an object directory of a
1048       --  project file extended by the main project. If the ALI file can be
1049       --  found, replace its name with its absolute path.
1050
1051       Skip_Executable := False;
1052
1053       Switch_Loop : for J in 1 .. Last_Switches.Last loop
1054
1055          --  If we have an executable just reset the flag
1056
1057          if Skip_Executable then
1058             Skip_Executable := False;
1059
1060          --  If -o, set flag so that next switch is not processed
1061
1062          elsif Last_Switches.Table (J).all = "-o" then
1063             Skip_Executable := True;
1064
1065          --  Normal case
1066
1067          else
1068             declare
1069                Switch    : constant String :=
1070                              Last_Switches.Table (J).all;
1071                ALI_File  : constant String (1 .. Switch'Length + 4) :=
1072                              Switch & ".ali";
1073
1074                Test_Existence : Boolean := False;
1075
1076             begin
1077                Last := Switch'Length;
1078
1079                --  Skip real switches
1080
1081                if Switch'Length /= 0
1082                  and then Switch (Switch'First) /= '-'
1083                then
1084                   --  Append ".ali" if file name does not end with it
1085
1086                   if Switch'Length <= 4
1087                     or else Switch (Switch'Last - 3 .. Switch'Last)
1088                     /= ".ali"
1089                   then
1090                      Last := ALI_File'Last;
1091                   end if;
1092
1093                   --  If file name includes directory information, stop if ALI
1094                   --  file exists.
1095
1096                   if Is_Absolute_Path (ALI_File (1 .. Last)) then
1097                      Test_Existence := True;
1098
1099                   else
1100                      for K in Switch'Range loop
1101                         if Switch (K) = '/' or else
1102                           Switch (K) = Directory_Separator
1103                         then
1104                            Test_Existence := True;
1105                            exit;
1106                         end if;
1107                      end loop;
1108                   end if;
1109
1110                   if Test_Existence then
1111                      if Is_Regular_File (ALI_File (1 .. Last)) then
1112                         exit Switch_Loop;
1113                      end if;
1114
1115                   --  Look in object directories if ALI file exists
1116
1117                   else
1118                      Project_Loop : loop
1119                         declare
1120                            Dir : constant String :=
1121                                    Get_Name_String (Prj.Object_Directory.Name);
1122                         begin
1123                            if Is_Regular_File
1124                                 (Dir &
1125                                  ALI_File (1 .. Last))
1126                            then
1127                               --  We have found the correct project, so we
1128                               --  replace the file with the absolute path.
1129
1130                               Last_Switches.Table (J) :=
1131                                 new String'(Dir & ALI_File (1 .. Last));
1132
1133                               --  And we are done
1134
1135                               exit Switch_Loop;
1136                            end if;
1137                         end;
1138
1139                         --  Go to the project being extended, if any
1140
1141                         Prj := Prj.Extends;
1142                         exit Project_Loop when Prj = No_Project;
1143                      end loop Project_Loop;
1144                   end if;
1145                end if;
1146             end;
1147          end if;
1148       end loop Switch_Loop;
1149
1150       --  If a relative path output file has been specified, we add the exec
1151       --  directory.
1152
1153       for J in reverse 1 .. Last_Switches.Last - 1 loop
1154          if Last_Switches.Table (J).all = "-o" then
1155             Check_Relative_Executable
1156               (Name => Last_Switches.Table (J + 1));
1157             Look_For_Executable := False;
1158             exit;
1159          end if;
1160       end loop;
1161
1162       if Look_For_Executable then
1163          for J in reverse 1 .. First_Switches.Last - 1 loop
1164             if First_Switches.Table (J).all = "-o" then
1165                Look_For_Executable := False;
1166                Check_Relative_Executable
1167                  (Name => First_Switches.Table (J + 1));
1168                exit;
1169             end if;
1170          end loop;
1171       end if;
1172
1173       --  If no executable is specified, then find the name of the first ALI
1174       --  file on the command line and issue a -o switch with the absolute path
1175       --  of the executable in the exec directory.
1176
1177       if Look_For_Executable then
1178          for J in 1 .. Last_Switches.Last loop
1179             Arg  := Last_Switches.Table (J);
1180             Last := 0;
1181
1182             if Arg'Length /= 0 and then Arg (Arg'First) /= '-' then
1183                if Arg'Length > 4
1184                  and then Arg (Arg'Last - 3 .. Arg'Last) = ".ali"
1185                then
1186                   Last := Arg'Last - 4;
1187
1188                elsif Is_Regular_File (Arg.all & ".ali") then
1189                   Last := Arg'Last;
1190                end if;
1191
1192                if Last /= 0 then
1193                   Last_Switches.Increment_Last;
1194                   Last_Switches.Table (Last_Switches.Last) :=
1195                     new String'("-o");
1196                   Get_Name_String (Project.Exec_Directory.Name);
1197                   Last_Switches.Increment_Last;
1198                   Last_Switches.Table (Last_Switches.Last) :=
1199                     new String'(Name_Buffer (1 .. Name_Len) &
1200                                 Executable_Name
1201                                   (Base_Name (Arg (Arg'First .. Last))));
1202                   exit;
1203                end if;
1204             end if;
1205          end loop;
1206       end if;
1207    end Process_Link;
1208
1209    ---------------------
1210    -- Set_Library_For --
1211    ---------------------
1212
1213    procedure Set_Library_For
1214      (Project           : Project_Id;
1215       Libraries_Present : in out Boolean)
1216    is
1217       Path_Option : constant String_Access :=
1218                       MLib.Linker_Library_Path_Option;
1219
1220    begin
1221       --  Case of library project
1222
1223       if Project.Library then
1224          Libraries_Present := True;
1225
1226          --  Add the -L switch
1227
1228          Last_Switches.Increment_Last;
1229          Last_Switches.Table (Last_Switches.Last) :=
1230            new String'("-L" & Get_Name_String (Project.Library_Dir.Name));
1231
1232          --  Add the -l switch
1233
1234          Last_Switches.Increment_Last;
1235          Last_Switches.Table (Last_Switches.Last) :=
1236            new String'("-l" & Get_Name_String (Project.Library_Name));
1237
1238          --  Add the directory to table Library_Paths, to be processed later
1239          --  if library is not static and if Path_Option is not null.
1240
1241          if Project.Library_Kind /= Static
1242            and then Path_Option /= null
1243          then
1244             Library_Paths.Increment_Last;
1245             Library_Paths.Table (Library_Paths.Last) :=
1246               new String'(Get_Name_String (Project.Library_Dir.Name));
1247          end if;
1248       end if;
1249    end Set_Library_For;
1250
1251    ---------------------------
1252    -- Test_If_Relative_Path --
1253    ---------------------------
1254
1255    procedure Test_If_Relative_Path
1256      (Switch : in out String_Access;
1257       Parent : String)
1258    is
1259    begin
1260       Makeutl.Test_If_Relative_Path
1261         (Switch, Parent, Including_Non_Switch => False, Including_RTS => True);
1262    end Test_If_Relative_Path;
1263
1264    -------------------
1265    -- Non_VMS_Usage --
1266    -------------------
1267
1268    procedure Non_VMS_Usage is
1269    begin
1270       Output_Version;
1271       New_Line;
1272       Put_Line ("List of available commands");
1273       New_Line;
1274
1275       for C in Command_List'Range loop
1276          if not Command_List (C).VMS_Only then
1277             if Targparm.AAMP_On_Target then
1278                Put ("gnaampcmd ");
1279             else
1280                Put ("gnat ");
1281             end if;
1282
1283             Put (To_Lower (Command_List (C).Cname.all));
1284             Set_Col (25);
1285
1286             --  Never call gnatstack with a prefix
1287
1288             if C = Stack then
1289                Put (Command_List (C).Unixcmd.all);
1290             else
1291                Put (Program_Name (Command_List (C).Unixcmd.all, "gnat").all);
1292             end if;
1293
1294             declare
1295                Sws : Argument_List_Access renames Command_List (C).Unixsws;
1296             begin
1297                if Sws /= null then
1298                   for J in Sws'Range loop
1299                      Put (' ');
1300                      Put (Sws (J).all);
1301                   end loop;
1302                end if;
1303             end;
1304
1305             New_Line;
1306          end if;
1307       end loop;
1308
1309       New_Line;
1310       Put_Line ("Commands find, list, metric, pretty, stack, stub and xref " &
1311                 "accept project file switches -vPx, -Pprj and -Xnam=val");
1312       New_Line;
1313    end Non_VMS_Usage;
1314
1315    -------------------------------------
1316    -- Start of processing for GNATCmd --
1317    -------------------------------------
1318
1319 begin
1320    --  Initializations
1321
1322    Namet.Initialize;
1323    Csets.Initialize;
1324
1325    Snames.Initialize;
1326
1327    Project_Node_Tree := new Project_Node_Tree_Data;
1328    Prj.Tree.Initialize (Project_Node_Tree);
1329
1330    Prj.Initialize (Project_Tree);
1331
1332    Last_Switches.Init;
1333    Last_Switches.Set_Last (0);
1334
1335    First_Switches.Init;
1336    First_Switches.Set_Last (0);
1337    Carg_Switches.Init;
1338    Carg_Switches.Set_Last (0);
1339    Rules_Switches.Init;
1340    Rules_Switches.Set_Last (0);
1341
1342    VMS_Conv.Initialize;
1343
1344    --  Add the default search directories, to be able to find system.ads in the
1345    --  subsequent call to Targparm.Get_Target_Parameters.
1346
1347    Add_Default_Search_Dirs;
1348
1349    --  Get target parameters so that AAMP_On_Target will be set, for testing in
1350    --  Osint.Program_Name to handle the mapping of GNAAMP tool names.
1351
1352    Targparm.Get_Target_Parameters;
1353
1354    --  Add the directory where the GNAT driver is invoked in front of the path,
1355    --  if the GNAT driver is invoked with directory information. Do not do this
1356    --  for VMS, where the notion of path does not really exist.
1357
1358    if not OpenVMS then
1359       declare
1360          Command : constant String := Command_Name;
1361
1362       begin
1363          for Index in reverse Command'Range loop
1364             if Command (Index) = Directory_Separator then
1365                declare
1366                   Absolute_Dir : constant String :=
1367                                    Normalize_Pathname
1368                                      (Command (Command'First .. Index));
1369
1370                   PATH : constant String :=
1371                            Absolute_Dir & Path_Separator & Getenv ("PATH").all;
1372
1373                begin
1374                   Setenv ("PATH", PATH);
1375                end;
1376
1377                exit;
1378             end if;
1379          end loop;
1380       end;
1381    end if;
1382
1383    --  If on VMS, or if VMS emulation is on, convert VMS style /qualifiers,
1384    --  filenames and pathnames to Unix style.
1385
1386    if Hostparm.OpenVMS
1387      or else To_Lower (Getenv ("EMULATE_VMS").all) = "true"
1388    then
1389       VMS_Conversion (The_Command);
1390
1391       B_Start := new String'("b__");
1392
1393    --  If not on VMS, scan the command line directly
1394
1395    else
1396       if Argument_Count = 0 then
1397          Non_VMS_Usage;
1398          return;
1399       else
1400          begin
1401             loop
1402                if Argument_Count > Command_Arg
1403                  and then Argument (Command_Arg) = "-v"
1404                then
1405                   Verbose_Mode := True;
1406                   Command_Arg := Command_Arg + 1;
1407
1408                elsif Argument_Count > Command_Arg
1409                  and then Argument (Command_Arg) = "-dn"
1410                then
1411                   Keep_Temporary_Files := True;
1412                   Command_Arg := Command_Arg + 1;
1413
1414                else
1415                   exit;
1416                end if;
1417             end loop;
1418
1419             The_Command := Real_Command_Type'Value (Argument (Command_Arg));
1420
1421             if Command_List (The_Command).VMS_Only then
1422                Non_VMS_Usage;
1423                Fail
1424                  ("Command """
1425                   & Command_List (The_Command).Cname.all
1426                   & """ can only be used on VMS");
1427             end if;
1428
1429          exception
1430             when Constraint_Error =>
1431
1432                --  Check if it is an alternate command
1433
1434                declare
1435                   Alternate : Alternate_Command;
1436
1437                begin
1438                   Alternate := Alternate_Command'Value
1439                                               (Argument (Command_Arg));
1440                   The_Command := Corresponding_To (Alternate);
1441
1442                exception
1443                   when Constraint_Error =>
1444                      Non_VMS_Usage;
1445                      Fail ("Unknown command: " & Argument (Command_Arg));
1446                end;
1447          end;
1448
1449          --  Get the arguments from the command line and from the eventual
1450          --  argument file(s) specified on the command line.
1451
1452          for Arg in Command_Arg + 1 .. Argument_Count loop
1453             declare
1454                The_Arg : constant String := Argument (Arg);
1455
1456             begin
1457                --  Check if an argument file is specified
1458
1459                if The_Arg (The_Arg'First) = '@' then
1460                   declare
1461                      Arg_File : Ada.Text_IO.File_Type;
1462                      Line     : String (1 .. 256);
1463                      Last     : Natural;
1464
1465                   begin
1466                      --  Open the file and fail if the file cannot be found
1467
1468                      begin
1469                         Open
1470                           (Arg_File, In_File,
1471                            The_Arg (The_Arg'First + 1 .. The_Arg'Last));
1472
1473                      exception
1474                         when others =>
1475                            Put
1476                              (Standard_Error, "Cannot open argument file """);
1477                            Put
1478                              (Standard_Error,
1479                               The_Arg (The_Arg'First + 1 .. The_Arg'Last));
1480
1481                            Put_Line (Standard_Error, """");
1482                            raise Error_Exit;
1483                      end;
1484
1485                      --  Read line by line and put the content of each non-
1486                      --  empty line in the Last_Switches table.
1487
1488                      while not End_Of_File (Arg_File) loop
1489                         Get_Line (Arg_File, Line, Last);
1490
1491                         if Last /= 0 then
1492                            Last_Switches.Increment_Last;
1493                            Last_Switches.Table (Last_Switches.Last) :=
1494                              new String'(Line (1 .. Last));
1495                         end if;
1496                      end loop;
1497
1498                      Close (Arg_File);
1499                   end;
1500
1501                else
1502                   --  It is not an argument file; just put the argument in
1503                   --  the Last_Switches table.
1504
1505                   Last_Switches.Increment_Last;
1506                   Last_Switches.Table (Last_Switches.Last) :=
1507                     new String'(The_Arg);
1508                end if;
1509             end;
1510          end loop;
1511       end if;
1512    end if;
1513
1514    declare
1515       Program   : String_Access;
1516       Exec_Path : String_Access;
1517
1518    begin
1519       if The_Command = Stack then
1520
1521          --  Never call gnatstack with a prefix
1522
1523          Program := new String'(Command_List (The_Command).Unixcmd.all);
1524
1525       else
1526          Program :=
1527            Program_Name (Command_List (The_Command).Unixcmd.all, "gnat");
1528       end if;
1529
1530       --  Locate the executable for the command
1531
1532       Exec_Path := Locate_Exec_On_Path (Program.all);
1533
1534       if Exec_Path = null then
1535          Put_Line (Standard_Error, "could not locate " & Program.all);
1536          raise Error_Exit;
1537       end if;
1538
1539       --  If there are switches for the executable, put them as first switches
1540
1541       if Command_List (The_Command).Unixsws /= null then
1542          for J in Command_List (The_Command).Unixsws'Range loop
1543             First_Switches.Increment_Last;
1544             First_Switches.Table (First_Switches.Last) :=
1545               Command_List (The_Command).Unixsws (J);
1546          end loop;
1547       end if;
1548
1549       --  For BIND, CHECK, ELIM, FIND, LINK, LIST, METRIC, PRETTY, STACK, STUB,
1550       --  SYNC and XREF, look for project file related switches.
1551
1552       case The_Command is
1553          when Bind =>
1554             Tool_Package_Name := Name_Binder;
1555             Packages_To_Check := Packages_To_Check_By_Binder;
1556          when Check =>
1557             Tool_Package_Name := Name_Check;
1558             Packages_To_Check := Packages_To_Check_By_Check;
1559          when Elim =>
1560             Tool_Package_Name := Name_Eliminate;
1561             Packages_To_Check := Packages_To_Check_By_Eliminate;
1562          when Find =>
1563             Tool_Package_Name := Name_Finder;
1564             Packages_To_Check := Packages_To_Check_By_Finder;
1565          when Link =>
1566             Tool_Package_Name := Name_Linker;
1567             Packages_To_Check := Packages_To_Check_By_Linker;
1568          when List =>
1569             Tool_Package_Name := Name_Gnatls;
1570             Packages_To_Check := Packages_To_Check_By_Gnatls;
1571          when Metric =>
1572             Tool_Package_Name := Name_Metrics;
1573             Packages_To_Check := Packages_To_Check_By_Metric;
1574          when Pretty =>
1575             Tool_Package_Name := Name_Pretty_Printer;
1576             Packages_To_Check := Packages_To_Check_By_Pretty;
1577          when Stack =>
1578             Tool_Package_Name := Name_Stack;
1579             Packages_To_Check := Packages_To_Check_By_Stack;
1580          when Stub =>
1581             Tool_Package_Name := Name_Gnatstub;
1582             Packages_To_Check := Packages_To_Check_By_Gnatstub;
1583          when Sync =>
1584             Tool_Package_Name := Name_Synchronize;
1585             Packages_To_Check := Packages_To_Check_By_Sync;
1586          when Xref =>
1587             Tool_Package_Name := Name_Cross_Reference;
1588             Packages_To_Check := Packages_To_Check_By_Xref;
1589          when others =>
1590             Tool_Package_Name := No_Name;
1591       end case;
1592
1593       if Tool_Package_Name /= No_Name then
1594
1595          --  Check that the switches are consistent. Detect project file
1596          --  related switches.
1597
1598          Inspect_Switches : declare
1599             Arg_Num : Positive := 1;
1600             Argv    : String_Access;
1601
1602             procedure Remove_Switch (Num : Positive);
1603             --  Remove a project related switch from table Last_Switches
1604
1605             -------------------
1606             -- Remove_Switch --
1607             -------------------
1608
1609             procedure Remove_Switch (Num : Positive) is
1610             begin
1611                Last_Switches.Table (Num .. Last_Switches.Last - 1) :=
1612                  Last_Switches.Table (Num + 1 .. Last_Switches.Last);
1613                Last_Switches.Decrement_Last;
1614             end Remove_Switch;
1615
1616          --  Start of processing for Inspect_Switches
1617
1618          begin
1619             while Arg_Num <= Last_Switches.Last loop
1620                Argv := Last_Switches.Table (Arg_Num);
1621
1622                if Argv (Argv'First) = '-' then
1623                   if Argv'Length = 1 then
1624                      Fail
1625                        ("switch character cannot be followed by a blank");
1626                   end if;
1627
1628                   --  The two style project files (-p and -P) cannot be used
1629                   --  together
1630
1631                   if (The_Command = Find or else The_Command = Xref)
1632                     and then Argv (2) = 'p'
1633                   then
1634                      Old_Project_File_Used := True;
1635                      if Project_File /= null then
1636                         Fail ("-P and -p cannot be used together");
1637                      end if;
1638                   end if;
1639
1640                   --  --subdirs=... Specify Subdirs
1641
1642                   if Argv'Length > Makeutl.Subdirs_Option'Length and then
1643                     Argv
1644                      (Argv'First ..
1645                       Argv'First + Makeutl.Subdirs_Option'Length - 1) =
1646                         Makeutl.Subdirs_Option
1647                   then
1648                      Subdirs :=
1649                        new String'
1650                          (Argv
1651                            (Argv'First + Makeutl.Subdirs_Option'Length ..
1652                             Argv'Last));
1653
1654                      Remove_Switch (Arg_Num);
1655
1656                   --  -aPdir  Add dir to the project search path
1657
1658                   elsif Argv'Length > 3
1659                     and then Argv (Argv'First + 1 .. Argv'First + 2) = "aP"
1660                   then
1661                      Add_Search_Project_Directory
1662                        (Project_Node_Tree, Argv (Argv'First + 3 .. Argv'Last));
1663
1664                      Remove_Switch (Arg_Num);
1665
1666                   --  -eL  Follow links for files
1667
1668                   elsif Argv.all = "-eL" then
1669                      Follow_Links_For_Files := True;
1670                      Follow_Links_For_Dirs  := True;
1671
1672                      Remove_Switch (Arg_Num);
1673
1674                   --  -vPx  Specify verbosity while parsing project files
1675
1676                   elsif Argv'Length = 4
1677                     and then Argv (Argv'First + 1 .. Argv'First + 2) = "vP"
1678                   then
1679                      case Argv (Argv'Last) is
1680                         when '0' =>
1681                            Current_Verbosity := Prj.Default;
1682                         when '1' =>
1683                            Current_Verbosity := Prj.Medium;
1684                         when '2' =>
1685                            Current_Verbosity := Prj.High;
1686                         when others =>
1687                            Fail ("Invalid switch: " & Argv.all);
1688                      end case;
1689
1690                      Remove_Switch (Arg_Num);
1691
1692                   --  -Pproject_file  Specify project file to be used
1693
1694                   elsif Argv (Argv'First + 1) = 'P' then
1695
1696                      --  Only one -P switch can be used
1697
1698                      if Project_File /= null then
1699                         Fail
1700                           (Argv.all
1701                            & ": second project file forbidden (first is """
1702                            & Project_File.all
1703                            & """)");
1704
1705                      --  The two style project files (-p and -P) cannot be
1706                      --  used together.
1707
1708                      elsif Old_Project_File_Used then
1709                         Fail ("-p and -P cannot be used together");
1710
1711                      elsif Argv'Length = 2 then
1712
1713                         --  There is space between -P and the project file
1714                         --  name. -P cannot be the last option.
1715
1716                         if Arg_Num = Last_Switches.Last then
1717                            Fail ("project file name missing after -P");
1718
1719                         else
1720                            Remove_Switch (Arg_Num);
1721                            Argv := Last_Switches.Table (Arg_Num);
1722
1723                            --  After -P, there must be a project file name,
1724                            --  not another switch.
1725
1726                            if Argv (Argv'First) = '-' then
1727                               Fail ("project file name missing after -P");
1728
1729                            else
1730                               Project_File := new String'(Argv.all);
1731                            end if;
1732                         end if;
1733
1734                      else
1735                         --  No space between -P and project file name
1736
1737                         Project_File :=
1738                           new String'(Argv (Argv'First + 2 .. Argv'Last));
1739                      end if;
1740
1741                      Remove_Switch (Arg_Num);
1742
1743                   --  -Xexternal=value Specify an external reference to be
1744                   --                   used in project files
1745
1746                   elsif Argv'Length >= 5
1747                     and then Argv (Argv'First + 1) = 'X'
1748                   then
1749                      declare
1750                         Equal_Pos : constant Natural :=
1751                                       Index
1752                                         ('=',
1753                                          Argv (Argv'First + 2 .. Argv'Last));
1754                      begin
1755                         if Equal_Pos >= Argv'First + 3 and then
1756                           Equal_Pos /= Argv'Last then
1757                            Add (Project_Node_Tree,
1758                                 External_Name =>
1759                                   Argv (Argv'First + 2 .. Equal_Pos - 1),
1760                                 Value => Argv (Equal_Pos + 1 .. Argv'Last));
1761                         else
1762                            Fail
1763                              (Argv.all
1764                               & " is not a valid external assignment.");
1765                         end if;
1766                      end;
1767
1768                      Remove_Switch (Arg_Num);
1769
1770                   elsif
1771                     (The_Command = Check  or else
1772                      The_Command = Sync   or else
1773                      The_Command = Pretty or else
1774                      The_Command = Metric or else
1775                      The_Command = Stack  or else
1776                      The_Command = List)
1777                     and then Argv'Length = 2
1778                     and then Argv (2) = 'U'
1779                   then
1780                      All_Projects := True;
1781                      Remove_Switch (Arg_Num);
1782
1783                   else
1784                      Arg_Num := Arg_Num + 1;
1785                   end if;
1786
1787                elsif ((The_Command = Check and then Argv (Argv'First) /= '+')
1788                         or else The_Command = Sync
1789                         or else The_Command = Metric
1790                         or else The_Command = Pretty)
1791                  and then Project_File /= null
1792                  and then All_Projects
1793                then
1794                   if ASIS_Main /= null then
1795                      Fail ("cannot specify more than one main after -U");
1796                   else
1797                      ASIS_Main := Argv;
1798                      Remove_Switch (Arg_Num);
1799                   end if;
1800
1801                else
1802                   Arg_Num := Arg_Num + 1;
1803                end if;
1804             end loop;
1805          end Inspect_Switches;
1806       end if;
1807
1808       --  If there is a project file specified, parse it, get the switches
1809       --  for the tool and setup PATH environment variables.
1810
1811       if Project_File /= null then
1812          Prj.Pars.Set_Verbosity (To => Current_Verbosity);
1813
1814          Prj.Pars.Parse
1815            (Project           => Project,
1816             In_Tree           => Project_Tree,
1817             In_Node_Tree      => Project_Node_Tree,
1818             Project_File_Name => Project_File.all,
1819             Flags             => Gnatmake_Flags,
1820             Packages_To_Check => Packages_To_Check);
1821
1822          if Project = Prj.No_Project then
1823             Fail ("""" & Project_File.all & """ processing failed");
1824          end if;
1825
1826          --  Check if a package with the name of the tool is in the project
1827          --  file and if there is one, get the switches, if any, and scan them.
1828
1829          declare
1830             Pkg : constant Prj.Package_Id :=
1831                     Prj.Util.Value_Of
1832                       (Name        => Tool_Package_Name,
1833                        In_Packages => Project.Decl.Packages,
1834                        In_Tree     => Project_Tree);
1835
1836             Element : Package_Element;
1837
1838             Switches_Array : Array_Element_Id;
1839
1840             The_Switches : Prj.Variable_Value;
1841             Current      : Prj.String_List_Id;
1842             The_String   : String_Element;
1843
1844             Main : String_Access := null;
1845
1846          begin
1847             if Pkg /= No_Package then
1848                Element := Project_Tree.Packages.Table (Pkg);
1849
1850                --  Packages Gnatls and Gnatstack have a single attribute
1851                --  Switches, that is not an associative array.
1852
1853                if The_Command = List or else The_Command = Stack then
1854                   The_Switches :=
1855                     Prj.Util.Value_Of
1856                     (Variable_Name => Snames.Name_Switches,
1857                      In_Variables  => Element.Decl.Attributes,
1858                      In_Tree       => Project_Tree);
1859
1860                --  Packages Binder (for gnatbind), Cross_Reference (for
1861                --  gnatxref), Linker (for gnatlink), Finder (for gnatfind),
1862                --  Pretty_Printer (for gnatpp), Eliminate (for gnatelim), Check
1863                --  (for gnatcheck), and Metric (for gnatmetric) have an
1864                --  attributed Switches, an associative array, indexed by the
1865                --  name of the file.
1866
1867                --  They also have an attribute Default_Switches, indexed by the
1868                --  name of the programming language.
1869
1870                else
1871                   --  First check if there is a single main
1872
1873                   for J in 1 .. Last_Switches.Last loop
1874                      if Last_Switches.Table (J) (1) /= '-' then
1875                         if Main = null then
1876                            Main := Last_Switches.Table (J);
1877
1878                         else
1879                            Main := null;
1880                            exit;
1881                         end if;
1882                      end if;
1883                   end loop;
1884
1885                   if Main /= null then
1886                      Switches_Array :=
1887                        Prj.Util.Value_Of
1888                          (Name      => Name_Switches,
1889                           In_Arrays => Element.Decl.Arrays,
1890                           In_Tree   => Project_Tree);
1891                      Name_Len := 0;
1892                      Add_Str_To_Name_Buffer (Main.all);
1893                      The_Switches := Prj.Util.Value_Of
1894                        (Index     => Name_Find,
1895                         Src_Index => 0,
1896                         In_Array  => Switches_Array,
1897                         In_Tree   => Project_Tree);
1898                   end if;
1899
1900                   if The_Switches.Kind = Prj.Undefined then
1901                      Switches_Array :=
1902                        Prj.Util.Value_Of
1903                          (Name      => Name_Default_Switches,
1904                           In_Arrays => Element.Decl.Arrays,
1905                           In_Tree   => Project_Tree);
1906                      The_Switches := Prj.Util.Value_Of
1907                        (Index     => Name_Ada,
1908                         Src_Index => 0,
1909                         In_Array  => Switches_Array,
1910                         In_Tree   => Project_Tree);
1911                   end if;
1912                end if;
1913
1914                --  If there are switches specified in the package of the
1915                --  project file corresponding to the tool, scan them.
1916
1917                case The_Switches.Kind is
1918                   when Prj.Undefined =>
1919                      null;
1920
1921                   when Prj.Single =>
1922                      declare
1923                         Switch : constant String :=
1924                                    Get_Name_String (The_Switches.Value);
1925
1926                      begin
1927                         if Switch'Length > 0 then
1928                            First_Switches.Increment_Last;
1929                            First_Switches.Table (First_Switches.Last) :=
1930                              new String'(Switch);
1931                         end if;
1932                      end;
1933
1934                   when Prj.List =>
1935                      Current := The_Switches.Values;
1936                      while Current /= Prj.Nil_String loop
1937                         The_String := Project_Tree.String_Elements.
1938                                         Table (Current);
1939
1940                         declare
1941                            Switch : constant String :=
1942                              Get_Name_String (The_String.Value);
1943
1944                         begin
1945                            if Switch'Length > 0 then
1946                               First_Switches.Increment_Last;
1947                               First_Switches.Table (First_Switches.Last) :=
1948                                 new String'(Switch);
1949                            end if;
1950                         end;
1951
1952                         Current := The_String.Next;
1953                      end loop;
1954                end case;
1955             end if;
1956          end;
1957
1958          if The_Command = Bind
1959            or else The_Command = Link
1960            or else The_Command = Elim
1961          then
1962             Change_Dir (Get_Name_String (Project.Object_Directory.Name));
1963          end if;
1964
1965          --  Set up the env vars for project path files
1966
1967          Prj.Env.Set_Ada_Paths
1968            (Project, Project_Tree, Including_Libraries => False);
1969
1970          --  For gnatcheck, gnatstub, gnatmetric, gnatpp and gnatelim, create
1971          --  a configuration pragmas file, if necessary.
1972
1973          if The_Command = Pretty
1974            or else The_Command = Metric
1975            or else The_Command = Stub
1976            or else The_Command = Elim
1977            or else The_Command = Check
1978            or else The_Command = Sync
1979          then
1980             --  If there are switches in package Compiler, put them in the
1981             --  Carg_Switches table.
1982
1983             declare
1984                Pkg  : constant Prj.Package_Id :=
1985                         Prj.Util.Value_Of
1986                           (Name        => Name_Compiler,
1987                            In_Packages => Project.Decl.Packages,
1988                            In_Tree     => Project_Tree);
1989
1990                Element : Package_Element;
1991
1992                Switches_Array : Array_Element_Id;
1993
1994                The_Switches : Prj.Variable_Value;
1995                Current      : Prj.String_List_Id;
1996                The_String   : String_Element;
1997
1998                Main    : String_Access := null;
1999                Main_Id : Name_Id;
2000
2001             begin
2002                if Pkg /= No_Package then
2003
2004                   --  First, check if there is a single main specified.
2005
2006                   for J in 1  .. Last_Switches.Last loop
2007                      if Last_Switches.Table (J) (1) /= '-' then
2008                         if Main = null then
2009                            Main := Last_Switches.Table (J);
2010
2011                         else
2012                            Main := null;
2013                            exit;
2014                         end if;
2015                      end if;
2016                   end loop;
2017
2018                   Element := Project_Tree.Packages.Table (Pkg);
2019
2020                   --  If there is a single main and there is compilation
2021                   --  switches specified in the project file, use them.
2022
2023                   if Main /= null and then not All_Projects then
2024                      Name_Len := Main'Length;
2025                      Name_Buffer (1 .. Name_Len) := Main.all;
2026                      Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
2027                      Main_Id := Name_Find;
2028
2029                      Switches_Array :=
2030                        Prj.Util.Value_Of
2031                          (Name      => Name_Switches,
2032                           In_Arrays => Element.Decl.Arrays,
2033                           In_Tree   => Project_Tree);
2034                      The_Switches := Prj.Util.Value_Of
2035                        (Index     => Main_Id,
2036                         Src_Index => 0,
2037                         In_Array  => Switches_Array,
2038                         In_Tree   => Project_Tree);
2039                   end if;
2040
2041                   --  Otherwise, get the Default_Switches ("Ada")
2042
2043                   if The_Switches.Kind = Undefined then
2044                      Switches_Array :=
2045                        Prj.Util.Value_Of
2046                          (Name      => Name_Default_Switches,
2047                           In_Arrays => Element.Decl.Arrays,
2048                           In_Tree   => Project_Tree);
2049                      The_Switches := Prj.Util.Value_Of
2050                        (Index     => Name_Ada,
2051                         Src_Index => 0,
2052                         In_Array  => Switches_Array,
2053                         In_Tree   => Project_Tree);
2054                   end if;
2055
2056                   --  If there are switches specified, put them in the
2057                   --  Carg_Switches table.
2058
2059                   case The_Switches.Kind is
2060                      when Prj.Undefined =>
2061                         null;
2062
2063                      when Prj.Single =>
2064                         declare
2065                            Switch : constant String :=
2066                                       Get_Name_String (The_Switches.Value);
2067                         begin
2068                            if Switch'Length > 0 then
2069                               Add_To_Carg_Switches (new String'(Switch));
2070                            end if;
2071                         end;
2072
2073                      when Prj.List =>
2074                         Current := The_Switches.Values;
2075                         while Current /= Prj.Nil_String loop
2076                            The_String :=
2077                              Project_Tree.String_Elements.Table (Current);
2078
2079                            declare
2080                               Switch : constant String :=
2081                                          Get_Name_String (The_String.Value);
2082                            begin
2083                               if Switch'Length > 0 then
2084                                  Add_To_Carg_Switches (new String'(Switch));
2085                               end if;
2086                            end;
2087
2088                            Current := The_String.Next;
2089                         end loop;
2090                   end case;
2091                end if;
2092             end;
2093
2094             --  If -cargs is one of the switches, move the following switches
2095             --  to the Carg_Switches table.
2096
2097             for J in 1 .. First_Switches.Last loop
2098                if First_Switches.Table (J).all = "-cargs" then
2099                   declare
2100                      K    : Positive;
2101                      Last : Natural;
2102
2103                   begin
2104                      --  Move the switches that are before -rules when the
2105                      --  command is CHECK.
2106
2107                      K := J + 1;
2108                      while K <= First_Switches.Last
2109                        and then
2110                         (The_Command /= Check
2111                            or else First_Switches.Table (K).all /= "-rules")
2112                      loop
2113                         Add_To_Carg_Switches (First_Switches.Table (K));
2114                         K := K + 1;
2115                      end loop;
2116
2117                      if K > First_Switches.Last then
2118                         First_Switches.Set_Last (J - 1);
2119
2120                      else
2121                         Last := J - 1;
2122                         while K <= First_Switches.Last loop
2123                            Last := Last + 1;
2124                            First_Switches.Table (Last) :=
2125                              First_Switches.Table (K);
2126                            K := K + 1;
2127                         end loop;
2128
2129                         First_Switches.Set_Last (Last);
2130                      end if;
2131                   end;
2132
2133                   exit;
2134                end if;
2135             end loop;
2136
2137             for J in 1 .. Last_Switches.Last loop
2138                if Last_Switches.Table (J).all = "-cargs" then
2139                   declare
2140                      K    : Positive;
2141                      Last : Natural;
2142
2143                   begin
2144                      --  Move the switches that are before -rules when the
2145                      --  command is CHECK.
2146
2147                      K := J + 1;
2148                      while K <= Last_Switches.Last
2149                        and then
2150                         (The_Command /= Check
2151                          or else
2152                          Last_Switches.Table (K).all /= "-rules")
2153                      loop
2154                         Add_To_Carg_Switches (Last_Switches.Table (K));
2155                         K := K + 1;
2156                      end loop;
2157
2158                      if K > Last_Switches.Last then
2159                         Last_Switches.Set_Last (J - 1);
2160
2161                      else
2162                         Last := J - 1;
2163                         while K <= Last_Switches.Last loop
2164                            Last := Last + 1;
2165                            Last_Switches.Table (Last) :=
2166                              Last_Switches.Table (K);
2167                            K := K + 1;
2168                         end loop;
2169
2170                         Last_Switches.Set_Last (Last);
2171                      end if;
2172                   end;
2173
2174                   exit;
2175                end if;
2176             end loop;
2177
2178             declare
2179                CP_File : constant Path_Name_Type := Configuration_Pragmas_File;
2180                M_File  : constant Path_Name_Type := Mapping_File;
2181
2182             begin
2183                if CP_File /= No_Path then
2184                   if The_Command = Elim then
2185                      First_Switches.Increment_Last;
2186                      First_Switches.Table (First_Switches.Last)  :=
2187                        new String'("-C" & Get_Name_String (CP_File));
2188
2189                   else
2190                      Add_To_Carg_Switches
2191                        (new String'("-gnatec=" & Get_Name_String (CP_File)));
2192                   end if;
2193                end if;
2194
2195                if M_File /= No_Path then
2196                   Add_To_Carg_Switches
2197                     (new String'("-gnatem=" & Get_Name_String (M_File)));
2198                end if;
2199             end;
2200          end if;
2201
2202          if The_Command = Link then
2203             Process_Link;
2204          end if;
2205
2206          if The_Command = Link or else The_Command = Bind then
2207
2208             --  For files that are specified as relative paths with directory
2209             --  information, we convert them to absolute paths, with parent
2210             --  being the current working directory if specified on the command
2211             --  line and the project directory if specified in the project
2212             --  file. This is what gnatmake is doing for linker and binder
2213             --  arguments.
2214
2215             for J in 1 .. Last_Switches.Last loop
2216                GNATCmd.Test_If_Relative_Path
2217                  (Last_Switches.Table (J), Current_Work_Dir);
2218             end loop;
2219
2220             Get_Name_String (Project.Directory.Name);
2221
2222             declare
2223                Project_Dir : constant String := Name_Buffer (1 .. Name_Len);
2224             begin
2225                for J in 1 .. First_Switches.Last loop
2226                   GNATCmd.Test_If_Relative_Path
2227                     (First_Switches.Table (J), Project_Dir);
2228                end loop;
2229             end;
2230
2231          elsif The_Command = Stub then
2232             declare
2233                File_Index : Integer := 0;
2234                Dir_Index  : Integer := 0;
2235                Last       : constant Integer := Last_Switches.Last;
2236                Lang       : constant Language_Ptr :=
2237                               Get_Language_From_Name (Project, "ada");
2238
2239             begin
2240                for Index in 1 .. Last loop
2241                   if Last_Switches.Table (Index)
2242                     (Last_Switches.Table (Index)'First) /= '-'
2243                   then
2244                      File_Index := Index;
2245                      exit;
2246                   end if;
2247                end loop;
2248
2249                --  If the project file naming scheme is not standard, and if
2250                --  the file name ends with the spec suffix, then indicate to
2251                --  gnatstub the name of the body file with a -o switch.
2252
2253                if not Is_Standard_GNAT_Naming (Lang.Config.Naming_Data) then
2254                   if File_Index /= 0 then
2255                      declare
2256                         Spec : constant String :=
2257                                  Base_Name
2258                                    (Last_Switches.Table (File_Index).all);
2259                         Last : Natural := Spec'Last;
2260
2261                      begin
2262                         Get_Name_String (Lang.Config.Naming_Data.Spec_Suffix);
2263
2264                         if Spec'Length > Name_Len
2265                           and then Spec (Last - Name_Len + 1 .. Last) =
2266                                                   Name_Buffer (1 .. Name_Len)
2267                         then
2268                            Last := Last - Name_Len;
2269                            Get_Name_String
2270                              (Lang.Config.Naming_Data.Body_Suffix);
2271                            Last_Switches.Increment_Last;
2272                            Last_Switches.Table (Last_Switches.Last) :=
2273                              new String'("-o");
2274                            Last_Switches.Increment_Last;
2275                            Last_Switches.Table (Last_Switches.Last) :=
2276                              new String'(Spec (Spec'First .. Last) &
2277                                            Name_Buffer (1 .. Name_Len));
2278                         end if;
2279                      end;
2280                   end if;
2281                end if;
2282
2283                --  Add the directory of the spec as the destination directory
2284                --  of the body, if there is no destination directory already
2285                --  specified.
2286
2287                if File_Index /= 0 then
2288                   for Index in File_Index + 1 .. Last loop
2289                      if Last_Switches.Table (Index)
2290                          (Last_Switches.Table (Index)'First) /= '-'
2291                      then
2292                         Dir_Index := Index;
2293                         exit;
2294                      end if;
2295                   end loop;
2296
2297                   if Dir_Index = 0 then
2298                      Last_Switches.Increment_Last;
2299                      Last_Switches.Table (Last_Switches.Last) :=
2300                        new String'
2301                              (Dir_Name (Last_Switches.Table (File_Index).all));
2302                   end if;
2303                end if;
2304             end;
2305          end if;
2306
2307          --  For gnatmetric, the generated files should be put in the object
2308          --  directory. This must be the first switch, because it may be
2309          --  overridden by a switch in package Metrics in the project file or
2310          --  by a command line option. Note that we don't add the -d= switch
2311          --  if there is no object directory available.
2312
2313          if The_Command = Metric
2314            and then Project.Object_Directory /= No_Path_Information
2315          then
2316             First_Switches.Increment_Last;
2317             First_Switches.Table (2 .. First_Switches.Last) :=
2318               First_Switches.Table (1 .. First_Switches.Last - 1);
2319             First_Switches.Table (1) :=
2320               new String'("-d=" &
2321                           Get_Name_String (Project.Object_Directory.Name));
2322          end if;
2323
2324          --  For gnat check, -rules and the following switches need to be the
2325          --  last options, so move all these switches to table Rules_Switches.
2326
2327          if The_Command = Check then
2328             declare
2329                New_Last : Natural;
2330                --  Set to rank of options preceding "-rules"
2331
2332                In_Rules_Switches : Boolean;
2333                --  Set to True when options "-rules" is found
2334
2335             begin
2336                New_Last := First_Switches.Last;
2337                In_Rules_Switches := False;
2338
2339                for J in 1 .. First_Switches.Last loop
2340                   if In_Rules_Switches then
2341                      Add_To_Rules_Switches (First_Switches.Table (J));
2342
2343                   elsif First_Switches.Table (J).all = "-rules" then
2344                      New_Last := J - 1;
2345                      In_Rules_Switches := True;
2346                   end if;
2347                end loop;
2348
2349                if In_Rules_Switches then
2350                   First_Switches.Set_Last (New_Last);
2351                end if;
2352
2353                New_Last := Last_Switches.Last;
2354                In_Rules_Switches := False;
2355
2356                for J in 1 .. Last_Switches.Last loop
2357                   if In_Rules_Switches then
2358                      Add_To_Rules_Switches (Last_Switches.Table (J));
2359
2360                   elsif Last_Switches.Table (J).all = "-rules" then
2361                      New_Last := J - 1;
2362                      In_Rules_Switches := True;
2363                   end if;
2364                end loop;
2365
2366                if In_Rules_Switches then
2367                   Last_Switches.Set_Last (New_Last);
2368                end if;
2369             end;
2370          end if;
2371
2372          --  For gnat check, sync, metric or pretty with -U + a main, get the
2373          --  list of sources from the closure and add them to the arguments.
2374
2375          if ASIS_Main /= null then
2376             Get_Closure;
2377
2378             --  On VMS, set up the env var again for source dirs file. This is
2379             --  because the call to gnatmake has set this env var to another
2380             --  file that has now been deleted.
2381
2382             if Hostparm.OpenVMS then
2383
2384                --  First make sure that the recorded file names are empty
2385
2386                Prj.Env.Initialize (Project_Tree);
2387
2388                Prj.Env.Set_Ada_Paths
2389                  (Project, Project_Tree, Including_Libraries => False);
2390             end if;
2391
2392          --  For gnat check, gnat sync, gnat pretty, gnat metric, gnat list,
2393          --  and gnat stack, if no file has been put on the command line, call
2394          --  tool with all the sources of the main project.
2395
2396          elsif The_Command = Check  or else
2397                The_Command = Sync   or else
2398                The_Command = Pretty or else
2399                The_Command = Metric or else
2400                The_Command = List   or else
2401                The_Command = Stack
2402          then
2403             Check_Files;
2404          end if;
2405       end if;
2406
2407       --  Gather all the arguments and invoke the executable
2408
2409       declare
2410          The_Args : Argument_List
2411                       (1 .. First_Switches.Last +
2412                             Last_Switches.Last +
2413                             Carg_Switches.Last +
2414                             Rules_Switches.Last);
2415          Arg_Num  : Natural := 0;
2416
2417       begin
2418          for J in 1 .. First_Switches.Last loop
2419             Arg_Num := Arg_Num + 1;
2420             The_Args (Arg_Num) := First_Switches.Table (J);
2421          end loop;
2422
2423          for J in 1 .. Last_Switches.Last loop
2424             Arg_Num := Arg_Num + 1;
2425             The_Args (Arg_Num) := Last_Switches.Table (J);
2426          end loop;
2427
2428          for J in 1 .. Carg_Switches.Last loop
2429             Arg_Num := Arg_Num + 1;
2430             The_Args (Arg_Num) := Carg_Switches.Table (J);
2431          end loop;
2432
2433          for J in 1 .. Rules_Switches.Last loop
2434             Arg_Num := Arg_Num + 1;
2435             The_Args (Arg_Num) := Rules_Switches.Table (J);
2436          end loop;
2437
2438          --  If Display_Command is on, only display the generated command
2439
2440          if Display_Command then
2441             Put (Standard_Error, "generated command -->");
2442             Put (Standard_Error, Exec_Path.all);
2443
2444             for Arg in The_Args'Range loop
2445                Put (Standard_Error, " ");
2446                Put (Standard_Error, The_Args (Arg).all);
2447             end loop;
2448
2449             Put (Standard_Error, "<--");
2450             New_Line (Standard_Error);
2451             raise Normal_Exit;
2452          end if;
2453
2454          if Verbose_Mode then
2455             Output.Write_Str (Exec_Path.all);
2456
2457             for Arg in The_Args'Range loop
2458                Output.Write_Char (' ');
2459                Output.Write_Str (The_Args (Arg).all);
2460             end loop;
2461
2462             Output.Write_Eol;
2463          end if;
2464
2465          My_Exit_Status :=
2466            Exit_Status (Spawn (Exec_Path.all, The_Args));
2467          raise Normal_Exit;
2468       end;
2469    end;
2470
2471 exception
2472    when Error_Exit =>
2473       if not Keep_Temporary_Files then
2474          Prj.Delete_All_Temp_Files (Project_Tree);
2475          Delete_Temp_Config_Files;
2476       end if;
2477
2478       Set_Exit_Status (Failure);
2479
2480    when Normal_Exit =>
2481       if not Keep_Temporary_Files then
2482          Prj.Delete_All_Temp_Files (Project_Tree);
2483          Delete_Temp_Config_Files;
2484       end if;
2485
2486       --  Since GNATCmd is normally called from DCL (the VMS shell), it must
2487       --  return an understandable VMS exit status. However the exit status
2488       --  returned *to* GNATCmd is a Posix style code, so we test it and return
2489       --  just a simple success or failure on VMS.
2490
2491       if Hostparm.OpenVMS and then My_Exit_Status /= Success then
2492          Set_Exit_Status (Failure);
2493       else
2494          Set_Exit_Status (My_Exit_Status);
2495       end if;
2496 end GNATCmd;