gcc/ChangeLog:
[official-gcc.git] / gcc / ada / gnatcmd.adb
blobce880ca4cc68a23d08e3252822c98a21a2b832ed
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-2016, 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 ------------------------------------------------------------------------------
26 with Csets;
27 with Gnatvsn;
28 with Makeutl; use Makeutl;
29 with Namet; use Namet;
30 with Opt; use Opt;
31 with Osint; use Osint;
32 with Output; use Output;
33 with Prj; use Prj;
34 with Prj.Env;
35 with Prj.Ext; use Prj.Ext;
36 with Prj.Pars;
37 with Prj.Tree; use Prj.Tree;
38 with Prj.Util; use Prj.Util;
39 with Sdefault;
40 with Sinput.P;
41 with Snames; use Snames;
42 with Stringt;
43 with Switch; use Switch;
44 with Table;
45 with Tempdir;
46 with Types; use Types;
48 with Ada.Characters.Handling; use Ada.Characters.Handling;
49 with Ada.Command_Line; use Ada.Command_Line;
50 with Ada.Text_IO; use Ada.Text_IO;
52 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
53 with GNAT.OS_Lib; use GNAT.OS_Lib;
55 procedure GNATCmd is
56 Gprbuild : constant String := "gprbuild";
57 Gprclean : constant String := "gprclean";
58 Gprname : constant String := "gprname";
59 Gprls : constant String := "gprls";
61 Error_Exit : exception;
62 -- Raise this exception if error detected
64 type Command_Type is
65 (Bind,
66 Chop,
67 Clean,
68 Compile,
69 Check,
70 Elim,
71 Find,
72 Krunch,
73 Link,
74 List,
75 Make,
76 Metric,
77 Name,
78 Preprocess,
79 Pretty,
80 Stack,
81 Stub,
82 Test,
83 Xref,
84 Undefined);
86 subtype Real_Command_Type is Command_Type range Bind .. Xref;
87 -- All real command types (excludes only Undefined).
89 type Alternate_Command is (Comp, Ls, Kr, Pp, Prep);
90 -- Alternate command label
92 Corresponding_To : constant array (Alternate_Command) of Command_Type :=
93 (Comp => Compile,
94 Ls => List,
95 Kr => Krunch,
96 Prep => Preprocess,
97 Pp => Pretty);
98 -- Mapping of alternate commands to commands
100 Call_GPR_Tool : Boolean := False;
101 -- True when a GPR tool should be called, if available
103 Project_Node_Tree : Project_Node_Tree_Ref;
104 Project_File : String_Access;
105 Project : Prj.Project_Id;
106 Current_Verbosity : Prj.Verbosity := Prj.Default;
107 Tool_Package_Name : Name_Id := No_Name;
109 Project_Tree : constant Project_Tree_Ref :=
110 new Project_Tree_Data (Is_Root_Tree => True);
111 -- The project tree
113 All_Projects : Boolean := False;
115 Temp_File_Name : Path_Name_Type := No_Path;
116 -- The name of the temporary text file to put a list of source/object
117 -- files to pass to a tool.
119 package First_Switches is new Table.Table
120 (Table_Component_Type => String_Access,
121 Table_Index_Type => Integer,
122 Table_Low_Bound => 1,
123 Table_Initial => 20,
124 Table_Increment => 100,
125 Table_Name => "Gnatcmd.First_Switches");
126 -- A table to keep the switches from the project file
128 package Last_Switches is new Table.Table
129 (Table_Component_Type => String_Access,
130 Table_Index_Type => Integer,
131 Table_Low_Bound => 1,
132 Table_Initial => 20,
133 Table_Increment => 100,
134 Table_Name => "Gnatcmd.Last_Switches");
136 ----------------------------------
137 -- Declarations for GNATCMD use --
138 ----------------------------------
140 The_Command : Command_Type;
141 -- The command specified in the invocation of the GNAT driver
143 Command_Arg : Positive := 1;
144 -- The index of the command in the arguments of the GNAT driver
146 My_Exit_Status : Exit_Status := Success;
147 -- The exit status of the spawned tool
149 type Command_Entry is record
150 Cname : String_Access;
151 -- Command name for GNAT xxx command
153 Unixcmd : String_Access;
154 -- Corresponding Unix command
156 Unixsws : Argument_List_Access;
157 -- List of switches to be used with the Unix command
158 end record;
160 Command_List : constant array (Real_Command_Type) of Command_Entry :=
161 (Bind =>
162 (Cname => new String'("BIND"),
163 Unixcmd => new String'("gnatbind"),
164 Unixsws => null),
166 Chop =>
167 (Cname => new String'("CHOP"),
168 Unixcmd => new String'("gnatchop"),
169 Unixsws => null),
171 Clean =>
172 (Cname => new String'("CLEAN"),
173 Unixcmd => new String'("gnatclean"),
174 Unixsws => null),
176 Compile =>
177 (Cname => new String'("COMPILE"),
178 Unixcmd => new String'("gnatmake"),
179 Unixsws => new Argument_List'(1 => new String'("-f"),
180 2 => new String'("-u"),
181 3 => new String'("-c"))),
183 Check =>
184 (Cname => new String'("CHECK"),
185 Unixcmd => new String'("gnatcheck"),
186 Unixsws => null),
188 Elim =>
189 (Cname => new String'("ELIM"),
190 Unixcmd => new String'("gnatelim"),
191 Unixsws => null),
193 Find =>
194 (Cname => new String'("FIND"),
195 Unixcmd => new String'("gnatfind"),
196 Unixsws => null),
198 Krunch =>
199 (Cname => new String'("KRUNCH"),
200 Unixcmd => new String'("gnatkr"),
201 Unixsws => null),
203 Link =>
204 (Cname => new String'("LINK"),
205 Unixcmd => new String'("gnatlink"),
206 Unixsws => null),
208 List =>
209 (Cname => new String'("LIST"),
210 Unixcmd => new String'("gnatls"),
211 Unixsws => null),
213 Make =>
214 (Cname => new String'("MAKE"),
215 Unixcmd => new String'("gnatmake"),
216 Unixsws => null),
218 Metric =>
219 (Cname => new String'("METRIC"),
220 Unixcmd => new String'("gnatmetric"),
221 Unixsws => null),
223 Name =>
224 (Cname => new String'("NAME"),
225 Unixcmd => new String'("gnatname"),
226 Unixsws => null),
228 Preprocess =>
229 (Cname => new String'("PREPROCESS"),
230 Unixcmd => new String'("gnatprep"),
231 Unixsws => null),
233 Pretty =>
234 (Cname => new String'("PRETTY"),
235 Unixcmd => new String'("gnatpp"),
236 Unixsws => null),
238 Stack =>
239 (Cname => new String'("STACK"),
240 Unixcmd => new String'("gnatstack"),
241 Unixsws => null),
243 Stub =>
244 (Cname => new String'("STUB"),
245 Unixcmd => new String'("gnatstub"),
246 Unixsws => null),
248 Test =>
249 (Cname => new String'("TEST"),
250 Unixcmd => new String'("gnattest"),
251 Unixsws => null),
253 Xref =>
254 (Cname => new String'("XREF"),
255 Unixcmd => new String'("gnatxref"),
256 Unixsws => null)
259 subtype SA is String_Access;
261 Naming_String : constant SA := new String'("naming");
262 Gnatls_String : constant SA := new String'("gnatls");
264 Packages_To_Check_By_Gnatls : constant String_List_Access :=
265 new String_List'((Naming_String, Gnatls_String));
267 Packages_To_Check : String_List_Access := Prj.All_Packages;
269 -----------------------
270 -- Local Subprograms --
271 -----------------------
273 procedure Check_Files;
274 -- For GNAT LIST -V, check if a project file is specified, without any file
275 -- arguments and without a switch -files=. If it is the case, invoke the
276 -- GNAT tool with the proper list of files, derived from the sources of
277 -- the project.
279 procedure Output_Version;
280 -- Output the version of this program
282 procedure Usage;
283 -- Display usage
285 -----------------
286 -- Check_Files --
287 -----------------
289 procedure Check_Files is
290 Add_Sources : Boolean := True;
291 Unit : Prj.Unit_Index;
292 Subunit : Boolean := False;
293 FD : File_Descriptor := Invalid_FD;
294 Status : Integer;
295 Success : Boolean;
297 procedure Add_To_Response_File
298 (File_Name : String;
299 Check_File : Boolean := True);
300 -- Include the file name passed as parameter in the response file for
301 -- the tool being called. If the response file can not be written then
302 -- the file name is passed in the parameter list of the tool. If the
303 -- Check_File parameter is True then the procedure verifies the
304 -- existence of the file before adding it to the response file.
306 --------------------------
307 -- Add_To_Response_File --
308 --------------------------
310 procedure Add_To_Response_File
311 (File_Name : String;
312 Check_File : Boolean := True)
314 begin
315 Name_Len := 0;
317 Add_Str_To_Name_Buffer (File_Name);
319 if not Check_File or else
320 Is_Regular_File (Name_Buffer (1 .. Name_Len))
321 then
322 if FD /= Invalid_FD then
323 Name_Len := Name_Len + 1;
324 Name_Buffer (Name_Len) := ASCII.LF;
326 Status := Write (FD, Name_Buffer (1)'Address, Name_Len);
328 if Status /= Name_Len then
329 Osint.Fail ("disk full");
330 end if;
331 else
332 Last_Switches.Increment_Last;
333 Last_Switches.Table (Last_Switches.Last) :=
334 new String'(File_Name);
335 end if;
336 end if;
337 end Add_To_Response_File;
339 -- Start of processing for Check_Files
341 begin
342 -- Check if there is at least one argument that is not a switch
344 for Index in 1 .. Last_Switches.Last loop
345 if Last_Switches.Table (Index) (1) /= '-'
346 or else (Last_Switches.Table (Index).all'Length > 7
347 and then Last_Switches.Table (Index) (1 .. 7) = "-files=")
348 then
349 Add_Sources := False;
350 exit;
351 end if;
352 end loop;
354 -- If all arguments are switches and there is no switch -files=, add the
355 -- path names of all the sources of the main project.
357 if Add_Sources then
358 Tempdir.Create_Temp_File (FD, Temp_File_Name);
359 Last_Switches.Increment_Last;
360 Last_Switches.Table (Last_Switches.Last) :=
361 new String'("-files=" & Get_Name_String (Temp_File_Name));
363 Unit := Units_Htable.Get_First (Project_Tree.Units_HT);
364 while Unit /= No_Unit_Index loop
366 -- We only need to put the library units, body or spec, but not
367 -- the subunits.
369 if Unit.File_Names (Impl) /= null
370 and then not Unit.File_Names (Impl).Locally_Removed
371 then
372 -- There is a body, check if it is for this project
374 if All_Projects
375 or else Unit.File_Names (Impl).Project = Project
376 then
377 Subunit := False;
379 if Unit.File_Names (Spec) = null
380 or else Unit.File_Names (Spec).Locally_Removed
381 then
382 -- We have a body with no spec: we need to check if
383 -- this is a subunit, because gnatls will complain
384 -- about subunits.
386 declare
387 Src_Ind : constant Source_File_Index :=
388 Sinput.P.Load_Project_File
389 (Get_Name_String
390 (Unit.File_Names (Impl).Path.Name));
391 begin
392 Subunit := Sinput.P.Source_File_Is_Subunit (Src_Ind);
393 end;
394 end if;
396 if not Subunit then
397 Add_To_Response_File
398 (Get_Name_String (Unit.File_Names (Impl).Display_File),
399 Check_File => False);
400 end if;
401 end if;
403 elsif Unit.File_Names (Spec) /= null
404 and then not Unit.File_Names (Spec).Locally_Removed
405 then
406 -- We have a spec with no body. Check if it is for this project
408 if All_Projects
409 or else Unit.File_Names (Spec).Project = Project
410 then
411 Add_To_Response_File
412 (Get_Name_String (Unit.File_Names (Spec).Display_File),
413 Check_File => False);
414 end if;
415 end if;
417 Unit := Units_Htable.Get_Next (Project_Tree.Units_HT);
418 end loop;
420 if FD /= Invalid_FD then
421 Close (FD, Success);
423 if not Success then
424 Osint.Fail ("disk full");
425 end if;
426 end if;
427 end if;
428 end Check_Files;
430 --------------------
431 -- Output_Version --
432 --------------------
434 procedure Output_Version is
435 begin
436 Put ("GNAT ");
437 Put_Line (Gnatvsn.Gnat_Version_String);
438 Put_Line ("Copyright 1996-" & Gnatvsn.Current_Year
439 & ", Free Software Foundation, Inc.");
440 end Output_Version;
442 -----------
443 -- Usage --
444 -----------
446 procedure Usage is
447 begin
448 Output_Version;
449 New_Line;
450 Put_Line ("List of available commands");
451 New_Line;
453 for C in Command_List'Range loop
454 Put ("gnat ");
455 Put (To_Lower (Command_List (C).Cname.all));
456 Set_Col (25);
457 Put (Program_Name (Command_List (C).Unixcmd.all, "gnat").all);
459 declare
460 Sws : Argument_List_Access renames Command_List (C).Unixsws;
461 begin
462 if Sws /= null then
463 for J in Sws'Range loop
464 Put (' ');
465 Put (Sws (J).all);
466 end loop;
467 end if;
468 end;
470 New_Line;
471 end loop;
473 New_Line;
474 end Usage;
476 procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
478 -- Start of processing for GNATCmd
480 begin
481 -- All output from GNATCmd is debugging or error output: send to stderr
483 Set_Standard_Error;
485 -- Initializations
487 Csets.Initialize;
488 Snames.Initialize;
489 Stringt.Initialize;
491 Prj.Tree.Initialize (Root_Environment, Gnatmake_Flags);
493 Project_Node_Tree := new Project_Node_Tree_Data;
494 Prj.Tree.Initialize (Project_Node_Tree);
496 Prj.Initialize (Project_Tree);
498 Last_Switches.Init;
499 Last_Switches.Set_Last (0);
501 First_Switches.Init;
502 First_Switches.Set_Last (0);
504 -- Put the command line in environment variable GNAT_DRIVER_COMMAND_LINE,
505 -- so that the spawned tool may know the way the GNAT driver was invoked.
507 Name_Len := 0;
508 Add_Str_To_Name_Buffer (Command_Name);
510 for J in 1 .. Argument_Count loop
511 Add_Char_To_Name_Buffer (' ');
512 Add_Str_To_Name_Buffer (Argument (J));
513 end loop;
515 Setenv ("GNAT_DRIVER_COMMAND_LINE", Name_Buffer (1 .. Name_Len));
517 -- Add the directory where the GNAT driver is invoked in front of the path,
518 -- if the GNAT driver is invoked with directory information.
520 declare
521 Command : constant String := Command_Name;
523 begin
524 for Index in reverse Command'Range loop
525 if Command (Index) = Directory_Separator then
526 declare
527 Absolute_Dir : constant String :=
528 Normalize_Pathname (Command (Command'First .. Index));
529 PATH : constant String :=
530 Absolute_Dir & Path_Separator & Getenv ("PATH").all;
531 begin
532 Setenv ("PATH", PATH);
533 end;
535 exit;
536 end if;
537 end loop;
538 end;
540 -- Scan the command line
542 -- First, scan to detect --version and/or --help
544 Check_Version_And_Help ("GNAT", "1996");
546 begin
547 loop
548 if Command_Arg <= Argument_Count
549 and then Argument (Command_Arg) = "-v"
550 then
551 Verbose_Mode := True;
552 Command_Arg := Command_Arg + 1;
554 elsif Command_Arg <= Argument_Count
555 and then Argument (Command_Arg) = "-dn"
556 then
557 Keep_Temporary_Files := True;
558 Command_Arg := Command_Arg + 1;
560 else
561 exit;
562 end if;
563 end loop;
565 -- If there is no command, just output the usage
567 if Command_Arg > Argument_Count then
568 Usage;
569 return;
570 end if;
572 The_Command := Real_Command_Type'Value (Argument (Command_Arg));
574 exception
575 when Constraint_Error =>
577 -- Check if it is an alternate command
579 declare
580 Alternate : Alternate_Command;
582 begin
583 Alternate := Alternate_Command'Value (Argument (Command_Arg));
584 The_Command := Corresponding_To (Alternate);
586 exception
587 when Constraint_Error =>
588 Usage;
589 Fail ("unknown command: " & Argument (Command_Arg));
590 end;
591 end;
593 -- Get the arguments from the command line and from the eventual
594 -- argument file(s) specified on the command line.
596 for Arg in Command_Arg + 1 .. Argument_Count loop
597 declare
598 The_Arg : constant String := Argument (Arg);
600 begin
601 -- Check if an argument file is specified
603 if The_Arg (The_Arg'First) = '@' then
604 declare
605 Arg_File : Ada.Text_IO.File_Type;
606 Line : String (1 .. 256);
607 Last : Natural;
609 begin
610 -- Open the file and fail if the file cannot be found
612 begin
613 Open (Arg_File, In_File,
614 The_Arg (The_Arg'First + 1 .. The_Arg'Last));
616 exception
617 when others =>
618 Put (Standard_Error, "Cannot open argument file """);
619 Put (Standard_Error,
620 The_Arg (The_Arg'First + 1 .. The_Arg'Last));
621 Put_Line (Standard_Error, """");
622 raise Error_Exit;
623 end;
625 -- Read line by line and put the content of each non-
626 -- empty line in the Last_Switches table.
628 while not End_Of_File (Arg_File) loop
629 Get_Line (Arg_File, Line, Last);
631 if Last /= 0 then
632 Last_Switches.Increment_Last;
633 Last_Switches.Table (Last_Switches.Last) :=
634 new String'(Line (1 .. Last));
635 end if;
636 end loop;
638 Close (Arg_File);
639 end;
641 else
642 -- It is not an argument file; just put the argument in
643 -- the Last_Switches table.
645 Last_Switches.Increment_Last;
646 Last_Switches.Table (Last_Switches.Last) := new String'(The_Arg);
647 end if;
648 end;
649 end loop;
651 declare
652 Program : String_Access;
653 Exec_Path : String_Access;
654 Get_Target : Boolean := False;
656 begin
657 if The_Command = Stack then
659 -- Never call gnatstack with a prefix
661 Program := new String'(Command_List (The_Command).Unixcmd.all);
663 else
664 Program :=
665 Program_Name (Command_List (The_Command).Unixcmd.all, "gnat");
667 -- If we want to invoke gnatmake/gnatclean with -P, then check if
668 -- gprbuild/gprclean is available; if it is, use gprbuild/gprclean
669 -- instead of gnatmake/gnatclean.
670 -- Ditto for gnatname -> gprname and gnatls -> gprls.
672 if The_Command = Make
673 or else The_Command = Compile
674 or else The_Command = Bind
675 or else The_Command = Link
676 or else The_Command = Clean
677 or else The_Command = Name
678 or else The_Command = List
679 then
680 declare
681 Switch : String_Access;
682 Dash_V_Switch : constant String := "-V";
684 begin
685 for J in 1 .. Last_Switches.Last loop
686 Switch := Last_Switches.Table (J);
688 if The_Command = List and then Switch.all = Dash_V_Switch
689 then
690 Call_GPR_Tool := False;
691 exit;
692 end if;
694 if Switch'Length >= 2
695 and then Switch (Switch'First .. Switch'First + 1) = "-P"
696 then
697 Call_GPR_Tool := True;
698 end if;
699 end loop;
701 if Call_GPR_Tool then
702 case The_Command is
703 when Make | Compile | Bind | Link =>
704 if Locate_Exec_On_Path (Gprbuild) /= null then
705 Program := new String'(Gprbuild);
706 Get_Target := True;
708 if The_Command = Bind then
709 First_Switches.Append (new String'("-b"));
710 elsif The_Command = Link then
711 First_Switches.Append (new String'("-l"));
712 end if;
714 elsif The_Command = Bind then
715 Fail
716 ("'gnat bind -P' is no longer supported;" &
717 " use 'gprbuild -b' instead.");
719 elsif The_Command = Link then
720 Fail
721 ("'gnat Link -P' is no longer supported;" &
722 " use 'gprbuild -l' instead.");
723 end if;
725 when Clean =>
726 if Locate_Exec_On_Path (Gprclean) /= null then
727 Program := new String'(Gprclean);
728 Get_Target := True;
729 end if;
731 when Name =>
732 if Locate_Exec_On_Path (Gprname) /= null then
733 Program := new String'(Gprname);
734 Get_Target := True;
735 end if;
737 when List =>
738 if Locate_Exec_On_Path (Gprls) /= null then
739 Program := new String'(Gprls);
740 Get_Target := True;
741 end if;
743 when others =>
744 null;
745 end case;
747 if Get_Target then
748 Find_Program_Name;
750 if Name_Len > 5 then
751 First_Switches.Append
752 (new String'
753 ("--target=" & Name_Buffer (1 .. Name_Len - 5)));
754 end if;
755 end if;
756 end if;
757 end;
758 end if;
759 end if;
761 -- Locate the executable for the command
763 Exec_Path := Locate_Exec_On_Path (Program.all);
765 if Exec_Path = null then
766 Put_Line (Standard_Error, "could not locate " & Program.all);
767 raise Error_Exit;
768 end if;
770 -- If there are switches for the executable, put them as first switches
772 if Command_List (The_Command).Unixsws /= null then
773 for J in Command_List (The_Command).Unixsws'Range loop
774 First_Switches.Increment_Last;
775 First_Switches.Table (First_Switches.Last) :=
776 Command_List (The_Command).Unixsws (J);
777 end loop;
778 end if;
780 -- For FIND and XREF, look for switch -P. If it is specified, then
781 -- report an error indicating that the command is no longer supporting
782 -- project files.
784 if The_Command = Find or else The_Command = Xref then
785 declare
786 Argv : String_Access;
787 begin
788 for Arg_Num in 1 .. Last_Switches.Last loop
789 Argv := Last_Switches.Table (Arg_Num);
791 if Argv'Length >= 2 and then
792 Argv (Argv'First .. Argv'First + 1) = "-P"
793 then
794 if The_Command = Find then
795 Fail ("'gnat find -P' is no longer supported;");
796 else
797 Fail ("'gnat xref -P' is no longer supported;");
798 end if;
799 end if;
800 end loop;
801 end;
802 end if;
804 if The_Command = List and then not Call_GPR_Tool then
805 Tool_Package_Name := Name_Gnatls;
806 Packages_To_Check := Packages_To_Check_By_Gnatls;
808 -- Check that the switches are consistent. Detect project file
809 -- related switches.
811 Inspect_Switches : declare
812 Arg_Num : Positive := 1;
813 Argv : String_Access;
815 procedure Remove_Switch (Num : Positive);
816 -- Remove a project related switch from table Last_Switches
818 -------------------
819 -- Remove_Switch --
820 -------------------
822 procedure Remove_Switch (Num : Positive) is
823 begin
824 Last_Switches.Table (Num .. Last_Switches.Last - 1) :=
825 Last_Switches.Table (Num + 1 .. Last_Switches.Last);
826 Last_Switches.Decrement_Last;
827 end Remove_Switch;
829 -- Start of processing for Inspect_Switches
831 begin
832 while Arg_Num <= Last_Switches.Last loop
833 Argv := Last_Switches.Table (Arg_Num);
835 if Argv (Argv'First) = '-' then
836 if Argv'Length = 1 then
837 Fail ("switch character cannot be followed by a blank");
838 end if;
840 -- --subdirs=... Specify Subdirs
842 if Argv'Length > Makeutl.Subdirs_Option'Length
843 and then
844 Argv
845 (Argv'First ..
846 Argv'First + Makeutl.Subdirs_Option'Length - 1) =
847 Makeutl.Subdirs_Option
848 then
849 Subdirs :=
850 new String'
851 (Argv (Argv'First + Makeutl.Subdirs_Option'Length ..
852 Argv'Last));
854 Remove_Switch (Arg_Num);
856 -- -aPdir Add dir to the project search path
858 elsif Argv'Length > 3
859 and then Argv (Argv'First + 1 .. Argv'First + 2) = "aP"
860 then
861 Prj.Env.Add_Directories
862 (Root_Environment.Project_Path,
863 Argv (Argv'First + 3 .. Argv'Last));
865 -- Pass -aPdir to gnatls, but not to other tools
867 if The_Command = List then
868 Arg_Num := Arg_Num + 1;
869 else
870 Remove_Switch (Arg_Num);
871 end if;
873 -- -eL Follow links for files
875 elsif Argv.all = "-eL" then
876 Follow_Links_For_Files := True;
877 Follow_Links_For_Dirs := True;
879 Remove_Switch (Arg_Num);
881 -- -vPx Specify verbosity while parsing project files
883 elsif Argv'Length >= 3
884 and then Argv (Argv'First + 1 .. Argv'First + 2) = "vP"
885 then
886 if Argv'Length = 4
887 and then Argv (Argv'Last) in '0' .. '2'
888 then
889 case Argv (Argv'Last) is
890 when '0' =>
891 Current_Verbosity := Prj.Default;
892 when '1' =>
893 Current_Verbosity := Prj.Medium;
894 when '2' =>
895 Current_Verbosity := Prj.High;
896 when others =>
898 -- Cannot happen
900 raise Program_Error;
901 end case;
902 else
903 Fail ("invalid verbosity level: "
904 & Argv (Argv'First + 3 .. Argv'Last));
905 end if;
907 Remove_Switch (Arg_Num);
909 -- -Pproject_file Specify project file to be used
911 elsif Argv (Argv'First + 1) = 'P' then
913 -- Only one -P switch can be used
915 if Project_File /= null then
916 Fail
917 (Argv.all
918 & ": second project file forbidden (first is """
919 & Project_File.all & """)");
921 elsif Argv'Length = 2 then
923 -- There is space between -P and the project file
924 -- name. -P cannot be the last option.
926 if Arg_Num = Last_Switches.Last then
927 Fail ("project file name missing after -P");
929 else
930 Remove_Switch (Arg_Num);
931 Argv := Last_Switches.Table (Arg_Num);
933 -- After -P, there must be a project file name,
934 -- not another switch.
936 if Argv (Argv'First) = '-' then
937 Fail ("project file name missing after -P");
939 else
940 Project_File := new String'(Argv.all);
941 end if;
942 end if;
944 else
945 -- No space between -P and project file name
947 Project_File :=
948 new String'(Argv (Argv'First + 2 .. Argv'Last));
949 end if;
951 Remove_Switch (Arg_Num);
953 -- -Xexternal=value Specify an external reference to be
954 -- used in project files
956 elsif Argv'Length >= 5
957 and then Argv (Argv'First + 1) = 'X'
958 then
959 if not Check (Root_Environment.External,
960 Argv (Argv'First + 2 .. Argv'Last))
961 then
962 Fail
963 (Argv.all & " is not a valid external assignment.");
964 end if;
966 Remove_Switch (Arg_Num);
968 -- --unchecked-shared-lib-imports
970 elsif Argv.all = "--unchecked-shared-lib-imports" then
971 Opt.Unchecked_Shared_Lib_Imports := True;
972 Remove_Switch (Arg_Num);
974 -- gnat list -U
976 elsif
977 The_Command = List
978 and then Argv'Length = 2
979 and then Argv (2) = 'U'
980 then
981 All_Projects := True;
982 Remove_Switch (Arg_Num);
984 else
985 Arg_Num := Arg_Num + 1;
986 end if;
988 else
989 Arg_Num := Arg_Num + 1;
990 end if;
991 end loop;
992 end Inspect_Switches;
993 end if;
995 -- Add the default project search directories now, after the directories
996 -- that have been specified by switches -aP<dir>.
998 Prj.Env.Initialize_Default_Project_Path
999 (Root_Environment.Project_Path,
1000 Target_Name => Sdefault.Target_Name.all);
1002 -- If there is a project file specified, parse it, get the switches
1003 -- for the tool and setup PATH environment variables.
1005 if Project_File /= null then
1006 Prj.Pars.Set_Verbosity (To => Current_Verbosity);
1008 Prj.Pars.Parse
1009 (Project => Project,
1010 In_Tree => Project_Tree,
1011 In_Node_Tree => Project_Node_Tree,
1012 Project_File_Name => Project_File.all,
1013 Env => Root_Environment,
1014 Packages_To_Check => Packages_To_Check);
1016 -- Prj.Pars.Parse calls Set_Standard_Output, reset to stderr
1018 Set_Standard_Error;
1020 if Project = Prj.No_Project then
1021 Fail ("""" & Project_File.all & """ processing failed");
1023 elsif Project.Qualifier = Aggregate then
1024 Fail ("aggregate projects are not supported");
1026 elsif Aggregate_Libraries_In (Project_Tree) then
1027 Fail ("aggregate library projects are not supported");
1028 end if;
1030 -- Check if a package with the name of the tool is in the project
1031 -- file and if there is one, get the switches, if any, and scan them.
1033 declare
1034 Pkg : constant Prj.Package_Id :=
1035 Prj.Util.Value_Of
1036 (Name => Tool_Package_Name,
1037 In_Packages => Project.Decl.Packages,
1038 Shared => Project_Tree.Shared);
1040 Element : Package_Element;
1042 Switches_Array : Array_Element_Id;
1044 The_Switches : Prj.Variable_Value;
1045 Current : Prj.String_List_Id;
1046 The_String : String_Element;
1048 Main : String_Access := null;
1050 begin
1051 if Pkg /= No_Package then
1052 Element := Project_Tree.Shared.Packages.Table (Pkg);
1054 -- Package Gnatls has a single attribute Switches, that is not
1055 -- an associative array.
1057 if The_Command = List then
1058 The_Switches :=
1059 Prj.Util.Value_Of
1060 (Variable_Name => Snames.Name_Switches,
1061 In_Variables => Element.Decl.Attributes,
1062 Shared => Project_Tree.Shared);
1064 -- Packages Binder (for gnatbind), Cross_Reference (for
1065 -- gnatxref), Linker (for gnatlink), Finder (for gnatfind),
1066 -- have an attributed Switches, an associative array, indexed
1067 -- by the name of the file.
1069 -- They also have an attribute Default_Switches, indexed by the
1070 -- name of the programming language.
1072 else
1073 -- First check if there is a single main
1075 for J in 1 .. Last_Switches.Last loop
1076 if Last_Switches.Table (J) (1) /= '-' then
1077 if Main = null then
1078 Main := Last_Switches.Table (J);
1079 else
1080 Main := null;
1081 exit;
1082 end if;
1083 end if;
1084 end loop;
1086 if Main /= null then
1087 Switches_Array :=
1088 Prj.Util.Value_Of
1089 (Name => Name_Switches,
1090 In_Arrays => Element.Decl.Arrays,
1091 Shared => Project_Tree.Shared);
1092 Name_Len := 0;
1094 -- If the single main has been specified as an absolute
1095 -- path, use only the simple file name. If the absolute
1096 -- path is incorrect, an error will be reported by the
1097 -- underlying tool and it does not make a difference
1098 -- what switches are used.
1100 if Is_Absolute_Path (Main.all) then
1101 Add_Str_To_Name_Buffer (File_Name (Main.all));
1102 else
1103 Add_Str_To_Name_Buffer (Main.all);
1104 end if;
1106 The_Switches := Prj.Util.Value_Of
1107 (Index => Name_Find,
1108 Src_Index => 0,
1109 In_Array => Switches_Array,
1110 Shared => Project_Tree.Shared);
1111 end if;
1113 if The_Switches.Kind = Prj.Undefined then
1114 Switches_Array :=
1115 Prj.Util.Value_Of
1116 (Name => Name_Default_Switches,
1117 In_Arrays => Element.Decl.Arrays,
1118 Shared => Project_Tree.Shared);
1119 The_Switches := Prj.Util.Value_Of
1120 (Index => Name_Ada,
1121 Src_Index => 0,
1122 In_Array => Switches_Array,
1123 Shared => Project_Tree.Shared);
1124 end if;
1125 end if;
1127 -- If there are switches specified in the package of the
1128 -- project file corresponding to the tool, scan them.
1130 case The_Switches.Kind is
1131 when Prj.Undefined =>
1132 null;
1134 when Prj.Single =>
1135 declare
1136 Switch : constant String :=
1137 Get_Name_String (The_Switches.Value);
1138 begin
1139 if Switch'Length > 0 then
1140 First_Switches.Increment_Last;
1141 First_Switches.Table (First_Switches.Last) :=
1142 new String'(Switch);
1143 end if;
1144 end;
1146 when Prj.List =>
1147 Current := The_Switches.Values;
1148 while Current /= Prj.Nil_String loop
1149 The_String := Project_Tree.Shared.String_Elements.
1150 Table (Current);
1152 declare
1153 Switch : constant String :=
1154 Get_Name_String (The_String.Value);
1155 begin
1156 if Switch'Length > 0 then
1157 First_Switches.Increment_Last;
1158 First_Switches.Table (First_Switches.Last) :=
1159 new String'(Switch);
1160 end if;
1161 end;
1163 Current := The_String.Next;
1164 end loop;
1165 end case;
1166 end if;
1167 end;
1169 if The_Command = Bind or else The_Command = Link then
1170 if Project.Object_Directory.Name = No_Path then
1171 Fail ("project " & Get_Name_String (Project.Display_Name)
1172 & " has no object directory");
1173 end if;
1175 Change_Dir (Get_Name_String (Project.Object_Directory.Name));
1176 end if;
1178 -- Set up the env vars for project path files
1180 Prj.Env.Set_Ada_Paths
1181 (Project, Project_Tree, Including_Libraries => True);
1183 if The_Command = List then
1184 Check_Files;
1185 end if;
1186 end if;
1188 -- Gather all the arguments and invoke the executable
1190 declare
1191 The_Args : Argument_List
1192 (1 .. First_Switches.Last + Last_Switches.Last);
1193 Arg_Num : Natural := 0;
1195 begin
1196 for J in 1 .. First_Switches.Last loop
1197 Arg_Num := Arg_Num + 1;
1198 The_Args (Arg_Num) := First_Switches.Table (J);
1199 end loop;
1201 for J in 1 .. Last_Switches.Last loop
1202 Arg_Num := Arg_Num + 1;
1203 The_Args (Arg_Num) := Last_Switches.Table (J);
1204 end loop;
1206 if Verbose_Mode then
1207 Put (Exec_Path.all);
1209 for Arg in The_Args'Range loop
1210 Put (" " & The_Args (Arg).all);
1211 end loop;
1213 New_Line;
1214 end if;
1216 My_Exit_Status := Exit_Status (Spawn (Exec_Path.all, The_Args));
1217 Set_Exit_Status (My_Exit_Status);
1218 end;
1219 end;
1221 exception
1222 when Error_Exit =>
1223 Set_Exit_Status (Failure);
1224 end GNATCmd;