Fix GNU coding style for G_.
[official-gcc.git] / gcc / ada / gnatcmd.adb
blob72a9be1c2f9df3d24e762b0302d968ed2461c0c1
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-2018, 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 Gnatvsn;
27 with Namet; use Namet;
28 with Opt; use Opt;
29 with Osint; use Osint;
30 with Output; use Output;
31 with Switch; use Switch;
32 with Table;
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;
40 procedure GNATCmd is
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
49 type Command_Type is
50 (Bind,
51 Chop,
52 Clean,
53 Compile,
54 Check,
55 Elim,
56 Find,
57 Krunch,
58 Link,
59 List,
60 Make,
61 Metric,
62 Name,
63 Preprocess,
64 Pretty,
65 Stack,
66 Stub,
67 Test,
68 Xref,
69 Undefined);
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 :=
78 (Comp => Compile,
79 Ls => List,
80 Kr => Krunch,
81 Prep => Preprocess,
82 Pp => Pretty);
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,
88 Table_Low_Bound => 1,
89 Table_Initial => 20,
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,
97 Table_Low_Bound => 1,
98 Table_Initial => 20,
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
124 end record;
126 Command_List : constant array (Real_Command_Type) of Command_Entry :=
127 (Bind =>
128 (Cname => new String'("BIND"),
129 Unixcmd => new String'("gnatbind"),
130 Unixsws => null),
132 Chop =>
133 (Cname => new String'("CHOP"),
134 Unixcmd => new String'("gnatchop"),
135 Unixsws => null),
137 Clean =>
138 (Cname => new String'("CLEAN"),
139 Unixcmd => new String'("gnatclean"),
140 Unixsws => null),
142 Compile =>
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"))),
149 Check =>
150 (Cname => new String'("CHECK"),
151 Unixcmd => new String'("gnatcheck"),
152 Unixsws => null),
154 Elim =>
155 (Cname => new String'("ELIM"),
156 Unixcmd => new String'("gnatelim"),
157 Unixsws => null),
159 Find =>
160 (Cname => new String'("FIND"),
161 Unixcmd => new String'("gnatfind"),
162 Unixsws => null),
164 Krunch =>
165 (Cname => new String'("KRUNCH"),
166 Unixcmd => new String'("gnatkr"),
167 Unixsws => null),
169 Link =>
170 (Cname => new String'("LINK"),
171 Unixcmd => new String'("gnatlink"),
172 Unixsws => null),
174 List =>
175 (Cname => new String'("LIST"),
176 Unixcmd => new String'("gnatls"),
177 Unixsws => null),
179 Make =>
180 (Cname => new String'("MAKE"),
181 Unixcmd => new String'("gnatmake"),
182 Unixsws => null),
184 Metric =>
185 (Cname => new String'("METRIC"),
186 Unixcmd => new String'("gnatmetric"),
187 Unixsws => null),
189 Name =>
190 (Cname => new String'("NAME"),
191 Unixcmd => new String'("gnatname"),
192 Unixsws => null),
194 Preprocess =>
195 (Cname => new String'("PREPROCESS"),
196 Unixcmd => new String'("gnatprep"),
197 Unixsws => null),
199 Pretty =>
200 (Cname => new String'("PRETTY"),
201 Unixcmd => new String'("gnatpp"),
202 Unixsws => null),
204 Stack =>
205 (Cname => new String'("STACK"),
206 Unixcmd => new String'("gnatstack"),
207 Unixsws => null),
209 Stub =>
210 (Cname => new String'("STUB"),
211 Unixcmd => new String'("gnatstub"),
212 Unixsws => null),
214 Test =>
215 (Cname => new String'("TEST"),
216 Unixcmd => new String'("gnattest"),
217 Unixsws => null),
219 Xref =>
220 (Cname => new String'("XREF"),
221 Unixcmd => new String'("gnatxref"),
222 Unixsws => null)
225 -----------------------
226 -- Local Subprograms --
227 -----------------------
229 procedure Output_Version;
230 -- Output the version of this program
232 procedure Usage;
233 -- Display usage
235 --------------------
236 -- Output_Version --
237 --------------------
239 procedure Output_Version is
240 begin
241 Put ("GNAT ");
242 Put_Line (Gnatvsn.Gnat_Version_String);
243 Put_Line ("Copyright 1996-" & Gnatvsn.Current_Year
244 & ", Free Software Foundation, Inc.");
245 end Output_Version;
247 -----------
248 -- Usage --
249 -----------
251 procedure Usage is
252 begin
253 Output_Version;
254 New_Line;
255 Put_Line ("List of available commands");
256 New_Line;
258 for C in Command_List'Range loop
259 Put ("gnat ");
260 Put (To_Lower (Command_List (C).Cname.all));
261 Set_Col (25);
262 Put (Program_Name (Command_List (C).Unixcmd.all, "gnat").all);
264 declare
265 Sws : Argument_List_Access renames Command_List (C).Unixsws;
266 begin
267 if Sws /= null then
268 for J in Sws'Range loop
269 Put (' ');
270 Put (Sws (J).all);
271 end loop;
272 end if;
273 end;
275 New_Line;
276 end loop;
278 New_Line;
279 end Usage;
281 procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
283 -- Start of processing for GNATCmd
285 begin
286 -- All output from GNATCmd is debugging or error output: send to stderr
288 Set_Standard_Error;
290 -- Initializations
292 Last_Switches.Init;
293 Last_Switches.Set_Last (0);
295 First_Switches.Init;
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.
301 Name_Len := 0;
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));
307 end loop;
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.
314 declare
315 Command : constant String := Command_Name;
317 begin
318 for Index in reverse Command'Range loop
319 if Command (Index) = Directory_Separator then
320 declare
321 Absolute_Dir : constant String :=
322 Normalize_Pathname (Command (Command'First .. Index));
323 PATH : constant String :=
324 Absolute_Dir & Path_Separator & Getenv ("PATH").all;
325 begin
326 Setenv ("PATH", PATH);
327 end;
329 exit;
330 end if;
331 end loop;
332 end;
334 -- Scan the command line
336 -- First, scan to detect --version and/or --help
338 Check_Version_And_Help ("GNAT", "1996");
340 begin
341 loop
342 if Command_Arg <= Argument_Count
343 and then Argument (Command_Arg) = "-v"
344 then
345 Verbose_Mode := True;
346 Command_Arg := Command_Arg + 1;
348 elsif Command_Arg <= Argument_Count
349 and then Argument (Command_Arg) = "-dn"
350 then
351 Keep_Temporary_Files := True;
352 Command_Arg := Command_Arg + 1;
354 else
355 exit;
356 end if;
357 end loop;
359 -- If there is no command, just output the usage
361 if Command_Arg > Argument_Count then
362 Usage;
363 return;
364 end if;
366 The_Command := Real_Command_Type'Value (Argument (Command_Arg));
368 exception
369 when Constraint_Error =>
371 -- Check if it is an alternate command
373 declare
374 Alternate : Alternate_Command;
376 begin
377 Alternate := Alternate_Command'Value (Argument (Command_Arg));
378 The_Command := Corresponding_To (Alternate);
380 exception
381 when Constraint_Error =>
382 Usage;
383 Fail ("unknown command: " & Argument (Command_Arg));
384 end;
385 end;
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
391 declare
392 The_Arg : constant String := Argument (Arg);
394 begin
395 -- Check if an argument file is specified
397 if The_Arg'Length > 0 and then The_Arg (The_Arg'First) = '@' then
398 declare
399 Arg_File : Ada.Text_IO.File_Type;
400 Line : String (1 .. 256);
401 Last : Natural;
403 begin
404 -- Open the file and fail if the file cannot be found
406 begin
407 Open (Arg_File, In_File,
408 The_Arg (The_Arg'First + 1 .. The_Arg'Last));
410 exception
411 when others =>
412 Put (Standard_Error, "Cannot open argument file """);
413 Put (Standard_Error,
414 The_Arg (The_Arg'First + 1 .. The_Arg'Last));
415 Put_Line (Standard_Error, """");
416 raise Error_Exit;
417 end;
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);
425 if Last /= 0 then
426 Last_Switches.Increment_Last;
427 Last_Switches.Table (Last_Switches.Last) :=
428 new String'(Line (1 .. Last));
429 end if;
430 end loop;
432 Close (Arg_File);
433 end;
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);
441 end if;
442 end;
443 end loop;
445 declare
446 Program : String_Access;
447 Exec_Path : String_Access;
448 Get_Target : Boolean := False;
450 begin
451 if The_Command = Stack then
453 -- Never call gnatstack with a prefix
455 Program := new String'(Command_List (The_Command).Unixcmd.all);
457 else
458 Program :=
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
473 then
474 declare
475 Switch : String_Access;
476 Call_GPR_Tool : Boolean := False;
477 begin
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"
483 then
484 Call_GPR_Tool := True;
485 exit;
486 end if;
487 end loop;
489 if Call_GPR_Tool then
490 case The_Command is
491 when Bind
492 | Compile
493 | Link
494 | Make
496 if Locate_Exec_On_Path (Gprbuild) /= null then
497 Program := new String'(Gprbuild);
498 Get_Target := True;
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"));
504 end if;
506 elsif The_Command = Bind then
507 Fail
508 ("'gnat bind -P' is no longer supported;" &
509 " use 'gprbuild -b' instead.");
511 elsif The_Command = Link then
512 Fail
513 ("'gnat Link -P' is no longer supported;" &
514 " use 'gprbuild -l' instead.");
515 end if;
517 when Clean =>
518 if Locate_Exec_On_Path (Gprclean) /= null then
519 Program := new String'(Gprclean);
520 Get_Target := True;
521 end if;
523 when Name =>
524 if Locate_Exec_On_Path (Gprname) /= null then
525 Program := new String'(Gprname);
526 Get_Target := True;
527 end if;
529 when List =>
530 if Locate_Exec_On_Path (Gprls) /= null then
531 Program := new String'(Gprls);
532 Get_Target := True;
533 end if;
535 when others =>
536 null;
537 end case;
539 if Get_Target then
540 Find_Program_Name;
542 if Name_Len > 5 then
543 First_Switches.Append
544 (new String'
545 ("--target=" & Name_Buffer (1 .. Name_Len - 5)));
546 end if;
547 end if;
548 end if;
549 end;
550 end if;
551 end if;
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);
559 raise Error_Exit;
560 end if;
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);
569 end loop;
570 end if;
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
574 -- project files.
576 if The_Command = Find or else The_Command = Xref then
577 declare
578 Argv : String_Access;
579 begin
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"
585 then
586 if The_Command = Find then
587 Fail ("'gnat find -P' is no longer supported;");
588 else
589 Fail ("'gnat xref -P' is no longer supported;");
590 end if;
591 end if;
592 end loop;
593 end;
594 end if;
596 -- Gather all the arguments and invoke the executable
598 declare
599 The_Args : Argument_List
600 (1 .. First_Switches.Last + Last_Switches.Last);
601 Arg_Num : Natural := 0;
603 begin
604 for J in 1 .. First_Switches.Last loop
605 Arg_Num := Arg_Num + 1;
606 The_Args (Arg_Num) := First_Switches.Table (J);
607 end loop;
609 for J in 1 .. Last_Switches.Last loop
610 Arg_Num := Arg_Num + 1;
611 The_Args (Arg_Num) := Last_Switches.Table (J);
612 end loop;
614 if Verbose_Mode then
615 Put (Exec_Path.all);
617 for Arg in The_Args'Range loop
618 Put (" " & The_Args (Arg).all);
619 end loop;
621 New_Line;
622 end if;
624 My_Exit_Status := Exit_Status (Spawn (Exec_Path.all, The_Args));
626 Set_Exit_Status (My_Exit_Status);
627 end;
628 end;
630 exception
631 when Error_Exit =>
632 Set_Exit_Status (Failure);
633 end GNATCmd;