1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-2023, 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
;
35 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
36 with Ada
.Command_Line
; use Ada
.Command_Line
;
37 with Ada
.Text_IO
; use Ada
.Text_IO
;
39 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
42 Gprbuild
: constant String := "gprbuild";
43 Gprclean
: constant String := "gprclean";
44 Gprname
: constant String := "gprname";
45 Gprls
: constant String := "gprls";
47 Ada_Help_Switch
: constant String := "--help-ada";
48 -- Flag to display available build switches
50 Error_Exit
: exception;
51 -- Raise this exception if error detected
73 subtype Real_Command_Type
is Command_Type
range Bind
.. Test
;
74 -- All real command types (excludes only Undefined).
76 type Alternate_Command
is (Comp
, Ls
, Kr
, Pp
, Prep
);
77 -- Alternate command label
79 Corresponding_To
: constant array (Alternate_Command
) of Command_Type
:=
85 -- Mapping of alternate commands to commands
87 package First_Switches
is new Table
.Table
88 (Table_Component_Type
=> String_Access
,
89 Table_Index_Type
=> Integer,
92 Table_Increment
=> 100,
93 Table_Name
=> "Gnatcmd.First_Switches");
94 -- A table to keep the switches from the project file
96 package Last_Switches
is new Table
.Table
97 (Table_Component_Type
=> String_Access
,
98 Table_Index_Type
=> Integer,
101 Table_Increment
=> 100,
102 Table_Name
=> "Gnatcmd.Last_Switches");
104 ----------------------------------
105 -- Declarations for GNATCMD use --
106 ----------------------------------
108 The_Command
: Command_Type
;
109 -- The command specified in the invocation of the GNAT driver
111 Command_Arg
: Positive := 1;
112 -- The index of the command in the arguments of the GNAT driver
114 My_Exit_Status
: Exit_Status
:= Success
;
115 -- The exit status of the spawned tool
117 type Command_Entry
is record
118 Cname
: String_Access
;
119 -- Command name for GNAT xxx command
121 Unixcmd
: String_Access
;
122 -- Corresponding Unix command
124 Unixsws
: Argument_List_Access
;
125 -- List of switches to be used with the Unix command
128 Command_List
: constant array (Real_Command_Type
) of Command_Entry
:=
130 (Cname
=> new String'("BIND"),
131 Unixcmd => new String'("gnatbind"),
135 (Cname
=> new String'("CHOP"),
136 Unixcmd => new String'("gnatchop"),
140 (Cname
=> new String'("CLEAN"),
141 Unixcmd => new String'("gnatclean"),
145 (Cname
=> new String'("COMPILE"),
146 Unixcmd => new String'("gnatmake"),
147 Unixsws
=> new Argument_List
'(1 => new String'("-f"),
148 2 => new String'("-u"),
149 3 => new String'("-c"))),
152 (Cname
=> new String'("CHECK"),
153 Unixcmd => new String'("gnatcheck"),
157 (Cname
=> new String'("ELIM"),
158 Unixcmd => new String'("gnatelim"),
162 (Cname
=> new String'("KRUNCH"),
163 Unixcmd => new String'("gnatkr"),
167 (Cname
=> new String'("LINK"),
168 Unixcmd => new String'("gnatlink"),
172 (Cname
=> new String'("LIST"),
173 Unixcmd => new String'("gnatls"),
177 (Cname
=> new String'("MAKE"),
178 Unixcmd => new String'("gnatmake"),
182 (Cname
=> new String'("METRIC"),
183 Unixcmd => new String'("gnatmetric"),
187 (Cname
=> new String'("NAME"),
188 Unixcmd => new String'("gnatname"),
192 (Cname
=> new String'("PREPROCESS"),
193 Unixcmd => new String'("gnatprep"),
197 (Cname
=> new String'("PRETTY"),
198 Unixcmd => new String'("gnatpp"),
202 (Cname
=> new String'("STACK"),
203 Unixcmd => new String'("gnatstack"),
207 (Cname
=> new String'("STUB"),
208 Unixcmd => new String'("gnatstub"),
212 (Cname
=> new String'("TEST"),
213 Unixcmd => new String'("gnattest"),
217 -----------------------
218 -- Local Subprograms --
219 -----------------------
221 procedure Output_Version
;
222 -- Output the version of this program
224 procedure GNATCmd_Usage
;
231 procedure Output_Version
is
234 Put_Line
(Gnatvsn
.Gnat_Version_String
);
235 Put_Line
("Copyright 1996-" & Gnatvsn
.Current_Year
236 & ", Free Software Foundation, Inc.");
243 procedure GNATCmd_Usage
is
247 Put_Line
("To list Ada build switches use " & Ada_Help_Switch
);
249 Put_Line
("List of available commands");
252 for C
in Command_List
'Range loop
254 Put
(To_Lower
(Command_List
(C
).Cname
.all));
256 Put
(Program_Name
(Command_List
(C
).Unixcmd
.all, "gnat").all);
259 Sws
: Argument_List_Access
renames Command_List
(C
).Unixsws
;
262 for J
in Sws
'Range loop
275 procedure Check_Version_And_Help
276 is new Check_Version_And_Help_G
(GNATCmd_Usage
);
278 -- Start of processing for GNATCmd
281 -- All output from GNATCmd is debugging or error output: send to stderr
288 Last_Switches
.Set_Last
(0);
291 First_Switches
.Set_Last
(0);
293 -- Put the command line in environment variable GNAT_DRIVER_COMMAND_LINE,
294 -- so that the spawned tool may know the way the GNAT driver was invoked.
297 Add_Str_To_Name_Buffer
(Command_Name
);
299 for J
in 1 .. Argument_Count
loop
300 Add_Char_To_Name_Buffer
(' ');
301 Add_Str_To_Name_Buffer
(Argument
(J
));
304 Setenv
("GNAT_DRIVER_COMMAND_LINE", Name_Buffer
(1 .. Name_Len
));
306 -- Add the directory where the GNAT driver is invoked in front of the path,
307 -- if the GNAT driver is invoked with directory information.
310 Command
: constant String := Command_Name
;
313 for Index
in reverse Command
'Range loop
314 if Command
(Index
) = Directory_Separator
then
316 Absolute_Dir
: constant String :=
317 Normalize_Pathname
(Command
(Command
'First .. Index
));
318 PATH
: constant String :=
319 Absolute_Dir
& Path_Separator
& Getenv
("PATH").all;
321 Setenv
("PATH", PATH
);
329 -- Scan the command line
331 -- First, scan to detect --version and/or --help
333 Check_Version_And_Help
("GNAT", "1996");
337 if Command_Arg
<= Argument_Count
338 and then Argument
(Command_Arg
) = "-v"
340 Verbose_Mode
:= True;
341 Command_Arg
:= Command_Arg
+ 1;
343 elsif Command_Arg
<= Argument_Count
344 and then Argument
(Command_Arg
) = "-dn"
346 Keep_Temporary_Files
:= True;
347 Command_Arg
:= Command_Arg
+ 1;
349 elsif Command_Arg
<= Argument_Count
350 and then Argument
(Command_Arg
) = Ada_Help_Switch
353 Exit_Program
(E_Success
);
360 -- If there is no command, just output the usage
362 if Command_Arg
> Argument_Count
then
365 -- Add the following so that output is consistent with or without the
368 Write_Line
("Report bugs to report@adacore.com");
372 The_Command
:= Real_Command_Type
'Value (Argument
(Command_Arg
));
375 when Constraint_Error
=>
377 -- Check if it is an alternate command
380 Alternate
: Alternate_Command
;
383 Alternate
:= Alternate_Command
'Value (Argument
(Command_Arg
));
384 The_Command
:= Corresponding_To
(Alternate
);
387 when Constraint_Error
=>
389 Fail
("unknown command: " & Argument
(Command_Arg
));
393 -- Get the arguments from the command line and from the eventual
394 -- argument file(s) specified on the command line.
396 for Arg
in Command_Arg
+ 1 .. Argument_Count
loop
398 The_Arg
: constant String := Argument
(Arg
);
401 -- Check if an argument file is specified
403 if The_Arg
'Length > 0 and then The_Arg
(The_Arg
'First) = '@' then
405 Arg_File
: Ada
.Text_IO
.File_Type
;
406 Line
: String (1 .. 256);
410 -- Open the file and fail if the file cannot be found
413 Open
(Arg_File
, In_File
,
414 The_Arg
(The_Arg
'First + 1 .. The_Arg
'Last));
418 Put
(Standard_Error
, "Cannot open argument file """);
420 The_Arg
(The_Arg
'First + 1 .. The_Arg
'Last));
421 Put_Line
(Standard_Error
, """");
425 -- Read line by line and put the content of each non-
426 -- empty line in the Last_Switches table.
428 while not End_Of_File
(Arg_File
) loop
429 Get_Line
(Arg_File
, Line
, Last
);
432 Last_Switches
.Increment_Last
;
433 Last_Switches
.Table
(Last_Switches
.Last
) :=
434 new String'(Line (1 .. Last));
441 elsif The_Arg'Length > 0 then
442 -- It is not an argument file; just put the argument in
443 -- the Last_Switches table.
445 Last_Switches.Increment_Last;
446 Last_Switches.Table (Last_Switches.Last) := new String'(The_Arg
);
452 Program
: String_Access
;
453 Exec_Path
: String_Access
;
454 Get_Target
: Boolean := False;
457 if The_Command
= Stack
then
459 -- Never call gnatstack with a prefix
461 Program
:= new String'(Command_List (The_Command).Unixcmd.all);
463 elsif The_Command in Check | Test then
464 Program := new String'(Command_List
(The_Command
).Unixcmd
.all);
468 First_Switches
.Append
470 ("--target=" & Name_Buffer (1 .. Name_Len - 5)));
474 Program_Name (Command_List (The_Command).Unixcmd.all, "gnat");
476 -- If we want to invoke gnatmake/gnatclean with -P, then check if
477 -- gprbuild/gprclean is available; if it is, use gprbuild/gprclean
478 -- instead of gnatmake/gnatclean.
479 -- Ditto for gnatname -> gprname and gnatls -> gprls.
481 if The_Command in Make | Compile | Bind | Link | Clean | Name | List
484 Switch : String_Access;
485 Call_GPR_Tool : Boolean := False;
487 for J in 1 .. Last_Switches.Last loop
488 Switch := Last_Switches.Table (J);
490 if Switch'Length >= 2
491 and then Switch (Switch'First .. Switch'First + 1) = "-P"
493 Call_GPR_Tool := True;
498 if Call_GPR_Tool then
505 if Locate_Exec_On_Path (Gprbuild) /= null then
506 Program := new String'(Gprbuild
);
509 if The_Command
= Bind
then
510 First_Switches
.Append
(new String'("-b"));
511 elsif The_Command = Link then
512 First_Switches.Append (new String'("-l"));
515 elsif The_Command
= Bind
then
517 ("'gnat bind -P' is no longer supported;" &
518 " use 'gprbuild -b' instead.");
520 elsif The_Command
= Link
then
522 ("'gnat Link -P' is no longer supported;" &
523 " use 'gprbuild -l' instead.");
527 if Locate_Exec_On_Path
(Gprclean
) /= null then
528 Program
:= new String'(Gprclean);
533 if Locate_Exec_On_Path (Gprname) /= null then
534 Program := new String'(Gprname
);
539 if Locate_Exec_On_Path
(Gprls
) /= null then
540 Program
:= new String'(Gprls);
552 First_Switches.Append
554 ("--target=" & Name_Buffer
(1 .. Name_Len
- 5)));
562 -- Locate the executable for the command
564 Exec_Path
:= Locate_Exec_On_Path
(Program
.all);
566 if Exec_Path
= null then
567 Put_Line
(Standard_Error
, "could not locate " & Program
.all);
571 -- If there are switches for the executable, put them as first switches
573 if Command_List
(The_Command
).Unixsws
/= null then
574 for J
in Command_List
(The_Command
).Unixsws
'Range loop
575 First_Switches
.Increment_Last
;
576 First_Switches
.Table
(First_Switches
.Last
) :=
577 Command_List
(The_Command
).Unixsws
(J
);
581 -- Gather all the arguments and invoke the executable
584 The_Args
: Argument_List
585 (1 .. First_Switches
.Last
+ Last_Switches
.Last
);
586 Arg_Num
: Natural := 0;
589 for J
in 1 .. First_Switches
.Last
loop
590 Arg_Num
:= Arg_Num
+ 1;
591 The_Args
(Arg_Num
) := First_Switches
.Table
(J
);
594 for J
in 1 .. Last_Switches
.Last
loop
595 Arg_Num
:= Arg_Num
+ 1;
596 The_Args
(Arg_Num
) := Last_Switches
.Table
(J
);
602 for Arg
in The_Args
'Range loop
603 Put
(" " & The_Args
(Arg
).all);
609 My_Exit_Status
:= Exit_Status
(Spawn
(Exec_Path
.all, The_Args
));
611 Set_Exit_Status
(My_Exit_Status
);
617 Set_Exit_Status
(Failure
);