1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-2018, Free Software Foundation, Inc. --
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. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
27 with Namet
; use Namet
;
29 with Osint
; use Osint
;
30 with Output
; use Output
;
31 with Switch
; use Switch
;
34 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
35 with Ada
.Command_Line
; use Ada
.Command_Line
;
36 with Ada
.Text_IO
; use Ada
.Text_IO
;
38 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
41 Gprbuild
: constant String := "gprbuild";
42 Gprclean
: constant String := "gprclean";
43 Gprname
: constant String := "gprname";
44 Gprls
: constant String := "gprls";
46 Error_Exit
: exception;
47 -- Raise this exception if error detected
71 subtype Real_Command_Type
is Command_Type
range Bind
.. Xref
;
72 -- All real command types (excludes only Undefined).
74 type Alternate_Command
is (Comp
, Ls
, Kr
, Pp
, Prep
);
75 -- Alternate command label
77 Corresponding_To
: constant array (Alternate_Command
) of Command_Type
:=
83 -- Mapping of alternate commands to commands
85 package First_Switches
is new Table
.Table
86 (Table_Component_Type
=> String_Access
,
87 Table_Index_Type
=> Integer,
90 Table_Increment
=> 100,
91 Table_Name
=> "Gnatcmd.First_Switches");
92 -- A table to keep the switches from the project file
94 package Last_Switches
is new Table
.Table
95 (Table_Component_Type
=> String_Access
,
96 Table_Index_Type
=> Integer,
99 Table_Increment
=> 100,
100 Table_Name
=> "Gnatcmd.Last_Switches");
102 ----------------------------------
103 -- Declarations for GNATCMD use --
104 ----------------------------------
106 The_Command
: Command_Type
;
107 -- The command specified in the invocation of the GNAT driver
109 Command_Arg
: Positive := 1;
110 -- The index of the command in the arguments of the GNAT driver
112 My_Exit_Status
: Exit_Status
:= Success
;
113 -- The exit status of the spawned tool
115 type Command_Entry
is record
116 Cname
: String_Access
;
117 -- Command name for GNAT xxx command
119 Unixcmd
: String_Access
;
120 -- Corresponding Unix command
122 Unixsws
: Argument_List_Access
;
123 -- List of switches to be used with the Unix command
126 Command_List
: constant array (Real_Command_Type
) of Command_Entry
:=
128 (Cname
=> new String'("BIND"),
129 Unixcmd => new String'("gnatbind"),
133 (Cname
=> new String'("CHOP"),
134 Unixcmd => new String'("gnatchop"),
138 (Cname
=> new String'("CLEAN"),
139 Unixcmd => new String'("gnatclean"),
143 (Cname
=> new String'("COMPILE"),
144 Unixcmd => new String'("gnatmake"),
145 Unixsws
=> new Argument_List
'(1 => new String'("-f"),
146 2 => new String'("-u"),
147 3 => new String'("-c"))),
150 (Cname
=> new String'("CHECK"),
151 Unixcmd => new String'("gnatcheck"),
155 (Cname
=> new String'("ELIM"),
156 Unixcmd => new String'("gnatelim"),
160 (Cname
=> new String'("FIND"),
161 Unixcmd => new String'("gnatfind"),
165 (Cname
=> new String'("KRUNCH"),
166 Unixcmd => new String'("gnatkr"),
170 (Cname
=> new String'("LINK"),
171 Unixcmd => new String'("gnatlink"),
175 (Cname
=> new String'("LIST"),
176 Unixcmd => new String'("gnatls"),
180 (Cname
=> new String'("MAKE"),
181 Unixcmd => new String'("gnatmake"),
185 (Cname
=> new String'("METRIC"),
186 Unixcmd => new String'("gnatmetric"),
190 (Cname
=> new String'("NAME"),
191 Unixcmd => new String'("gnatname"),
195 (Cname
=> new String'("PREPROCESS"),
196 Unixcmd => new String'("gnatprep"),
200 (Cname
=> new String'("PRETTY"),
201 Unixcmd => new String'("gnatpp"),
205 (Cname
=> new String'("STACK"),
206 Unixcmd => new String'("gnatstack"),
210 (Cname
=> new String'("STUB"),
211 Unixcmd => new String'("gnatstub"),
215 (Cname
=> new String'("TEST"),
216 Unixcmd => new String'("gnattest"),
220 (Cname
=> new String'("XREF"),
221 Unixcmd => new String'("gnatxref"),
225 -----------------------
226 -- Local Subprograms --
227 -----------------------
229 procedure Output_Version
;
230 -- Output the version of this program
239 procedure Output_Version
is
242 Put_Line
(Gnatvsn
.Gnat_Version_String
);
243 Put_Line
("Copyright 1996-" & Gnatvsn
.Current_Year
244 & ", Free Software Foundation, Inc.");
255 Put_Line
("List of available commands");
258 for C
in Command_List
'Range loop
260 Put
(To_Lower
(Command_List
(C
).Cname
.all));
262 Put
(Program_Name
(Command_List
(C
).Unixcmd
.all, "gnat").all);
265 Sws
: Argument_List_Access
renames Command_List
(C
).Unixsws
;
268 for J
in Sws
'Range loop
281 procedure Check_Version_And_Help
is new Check_Version_And_Help_G
(Usage
);
283 -- Start of processing for GNATCmd
286 -- All output from GNATCmd is debugging or error output: send to stderr
293 Last_Switches
.Set_Last
(0);
296 First_Switches
.Set_Last
(0);
298 -- Put the command line in environment variable GNAT_DRIVER_COMMAND_LINE,
299 -- so that the spawned tool may know the way the GNAT driver was invoked.
302 Add_Str_To_Name_Buffer
(Command_Name
);
304 for J
in 1 .. Argument_Count
loop
305 Add_Char_To_Name_Buffer
(' ');
306 Add_Str_To_Name_Buffer
(Argument
(J
));
309 Setenv
("GNAT_DRIVER_COMMAND_LINE", Name_Buffer
(1 .. Name_Len
));
311 -- Add the directory where the GNAT driver is invoked in front of the path,
312 -- if the GNAT driver is invoked with directory information.
315 Command
: constant String := Command_Name
;
318 for Index
in reverse Command
'Range loop
319 if Command
(Index
) = Directory_Separator
then
321 Absolute_Dir
: constant String :=
322 Normalize_Pathname
(Command
(Command
'First .. Index
));
323 PATH
: constant String :=
324 Absolute_Dir
& Path_Separator
& Getenv
("PATH").all;
326 Setenv
("PATH", PATH
);
334 -- Scan the command line
336 -- First, scan to detect --version and/or --help
338 Check_Version_And_Help
("GNAT", "1996");
342 if Command_Arg
<= Argument_Count
343 and then Argument
(Command_Arg
) = "-v"
345 Verbose_Mode
:= True;
346 Command_Arg
:= Command_Arg
+ 1;
348 elsif Command_Arg
<= Argument_Count
349 and then Argument
(Command_Arg
) = "-dn"
351 Keep_Temporary_Files
:= True;
352 Command_Arg
:= Command_Arg
+ 1;
359 -- If there is no command, just output the usage
361 if Command_Arg
> Argument_Count
then
366 The_Command
:= Real_Command_Type
'Value (Argument
(Command_Arg
));
369 when Constraint_Error
=>
371 -- Check if it is an alternate command
374 Alternate
: Alternate_Command
;
377 Alternate
:= Alternate_Command
'Value (Argument
(Command_Arg
));
378 The_Command
:= Corresponding_To
(Alternate
);
381 when Constraint_Error
=>
383 Fail
("unknown command: " & Argument
(Command_Arg
));
387 -- Get the arguments from the command line and from the eventual
388 -- argument file(s) specified on the command line.
390 for Arg
in Command_Arg
+ 1 .. Argument_Count
loop
392 The_Arg
: constant String := Argument
(Arg
);
395 -- Check if an argument file is specified
397 if The_Arg
'Length > 0 and then The_Arg
(The_Arg
'First) = '@' then
399 Arg_File
: Ada
.Text_IO
.File_Type
;
400 Line
: String (1 .. 256);
404 -- Open the file and fail if the file cannot be found
407 Open
(Arg_File
, In_File
,
408 The_Arg
(The_Arg
'First + 1 .. The_Arg
'Last));
412 Put
(Standard_Error
, "Cannot open argument file """);
414 The_Arg
(The_Arg
'First + 1 .. The_Arg
'Last));
415 Put_Line
(Standard_Error
, """");
419 -- Read line by line and put the content of each non-
420 -- empty line in the Last_Switches table.
422 while not End_Of_File
(Arg_File
) loop
423 Get_Line
(Arg_File
, Line
, Last
);
426 Last_Switches
.Increment_Last
;
427 Last_Switches
.Table
(Last_Switches
.Last
) :=
428 new String'(Line (1 .. Last));
435 elsif The_Arg'Length > 0 then
436 -- It is not an argument file; just put the argument in
437 -- the Last_Switches table.
439 Last_Switches.Increment_Last;
440 Last_Switches.Table (Last_Switches.Last) := new String'(The_Arg
);
446 Program
: String_Access
;
447 Exec_Path
: String_Access
;
448 Get_Target
: Boolean := False;
451 if The_Command
= Stack
then
453 -- Never call gnatstack with a prefix
455 Program
:= new String'(Command_List (The_Command).Unixcmd.all);
459 Program_Name (Command_List (The_Command).Unixcmd.all, "gnat");
461 -- If we want to invoke gnatmake/gnatclean with -P, then check if
462 -- gprbuild/gprclean is available; if it is, use gprbuild/gprclean
463 -- instead of gnatmake/gnatclean.
464 -- Ditto for gnatname -> gprname and gnatls -> gprls.
466 if The_Command = Make
467 or else The_Command = Compile
468 or else The_Command = Bind
469 or else The_Command = Link
470 or else The_Command = Clean
471 or else The_Command = Name
472 or else The_Command = List
475 Switch : String_Access;
476 Call_GPR_Tool : Boolean := False;
478 for J in 1 .. Last_Switches.Last loop
479 Switch := Last_Switches.Table (J);
481 if Switch'Length >= 2
482 and then Switch (Switch'First .. Switch'First + 1) = "-P"
484 Call_GPR_Tool := True;
489 if Call_GPR_Tool then
496 if Locate_Exec_On_Path (Gprbuild) /= null then
497 Program := new String'(Gprbuild
);
500 if The_Command
= Bind
then
501 First_Switches
.Append
(new String'("-b"));
502 elsif The_Command = Link then
503 First_Switches.Append (new String'("-l"));
506 elsif The_Command
= Bind
then
508 ("'gnat bind -P' is no longer supported;" &
509 " use 'gprbuild -b' instead.");
511 elsif The_Command
= Link
then
513 ("'gnat Link -P' is no longer supported;" &
514 " use 'gprbuild -l' instead.");
518 if Locate_Exec_On_Path
(Gprclean
) /= null then
519 Program
:= new String'(Gprclean);
524 if Locate_Exec_On_Path (Gprname) /= null then
525 Program := new String'(Gprname
);
530 if Locate_Exec_On_Path
(Gprls
) /= null then
531 Program
:= new String'(Gprls);
543 First_Switches.Append
545 ("--target=" & Name_Buffer
(1 .. Name_Len
- 5)));
553 -- Locate the executable for the command
555 Exec_Path
:= Locate_Exec_On_Path
(Program
.all);
557 if Exec_Path
= null then
558 Put_Line
(Standard_Error
, "could not locate " & Program
.all);
562 -- If there are switches for the executable, put them as first switches
564 if Command_List
(The_Command
).Unixsws
/= null then
565 for J
in Command_List
(The_Command
).Unixsws
'Range loop
566 First_Switches
.Increment_Last
;
567 First_Switches
.Table
(First_Switches
.Last
) :=
568 Command_List
(The_Command
).Unixsws
(J
);
572 -- For FIND and XREF, look for switch -P. If it is specified, then
573 -- report an error indicating that the command is no longer supporting
576 if The_Command
= Find
or else The_Command
= Xref
then
578 Argv
: String_Access
;
580 for Arg_Num
in 1 .. Last_Switches
.Last
loop
581 Argv
:= Last_Switches
.Table
(Arg_Num
);
583 if Argv
'Length >= 2 and then
584 Argv
(Argv
'First .. Argv
'First + 1) = "-P"
586 if The_Command
= Find
then
587 Fail
("'gnat find -P' is no longer supported;");
589 Fail
("'gnat xref -P' is no longer supported;");
596 -- Gather all the arguments and invoke the executable
599 The_Args
: Argument_List
600 (1 .. First_Switches
.Last
+ Last_Switches
.Last
);
601 Arg_Num
: Natural := 0;
604 for J
in 1 .. First_Switches
.Last
loop
605 Arg_Num
:= Arg_Num
+ 1;
606 The_Args
(Arg_Num
) := First_Switches
.Table
(J
);
609 for J
in 1 .. Last_Switches
.Last
loop
610 Arg_Num
:= Arg_Num
+ 1;
611 The_Args
(Arg_Num
) := Last_Switches
.Table
(J
);
617 for Arg
in The_Args
'Range loop
618 Put
(" " & The_Args
(Arg
).all);
624 My_Exit_Status
:= Exit_Status
(Spawn
(Exec_Path
.all, The_Args
));
626 Set_Exit_Status
(My_Exit_Status
);
632 Set_Exit_Status
(Failure
);