111
|
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-2017, 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 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;
|
|
33
|
|
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;
|
|
37
|
|
38 with GNAT.OS_Lib; use GNAT.OS_Lib;
|
|
39
|
|
40 procedure GNATCmd is
|
|
41 Gprbuild : constant String := "gprbuild";
|
|
42 Gprclean : constant String := "gprclean";
|
|
43 Gprname : constant String := "gprname";
|
|
44 Gprls : constant String := "gprls";
|
|
45
|
|
46 Error_Exit : exception;
|
|
47 -- Raise this exception if error detected
|
|
48
|
|
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);
|
|
70
|
|
71 subtype Real_Command_Type is Command_Type range Bind .. Xref;
|
|
72 -- All real command types (excludes only Undefined).
|
|
73
|
|
74 type Alternate_Command is (Comp, Ls, Kr, Pp, Prep);
|
|
75 -- Alternate command label
|
|
76
|
|
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
|
|
84
|
|
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
|
|
93
|
|
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");
|
|
101
|
|
102 ----------------------------------
|
|
103 -- Declarations for GNATCMD use --
|
|
104 ----------------------------------
|
|
105
|
|
106 The_Command : Command_Type;
|
|
107 -- The command specified in the invocation of the GNAT driver
|
|
108
|
|
109 Command_Arg : Positive := 1;
|
|
110 -- The index of the command in the arguments of the GNAT driver
|
|
111
|
|
112 My_Exit_Status : Exit_Status := Success;
|
|
113 -- The exit status of the spawned tool
|
|
114
|
|
115 type Command_Entry is record
|
|
116 Cname : String_Access;
|
|
117 -- Command name for GNAT xxx command
|
|
118
|
|
119 Unixcmd : String_Access;
|
|
120 -- Corresponding Unix command
|
|
121
|
|
122 Unixsws : Argument_List_Access;
|
|
123 -- List of switches to be used with the Unix command
|
|
124 end record;
|
|
125
|
|
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),
|
|
131
|
|
132 Chop =>
|
|
133 (Cname => new String'("CHOP"),
|
|
134 Unixcmd => new String'("gnatchop"),
|
|
135 Unixsws => null),
|
|
136
|
|
137 Clean =>
|
|
138 (Cname => new String'("CLEAN"),
|
|
139 Unixcmd => new String'("gnatclean"),
|
|
140 Unixsws => null),
|
|
141
|
|
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"))),
|
|
148
|
|
149 Check =>
|
|
150 (Cname => new String'("CHECK"),
|
|
151 Unixcmd => new String'("gnatcheck"),
|
|
152 Unixsws => null),
|
|
153
|
|
154 Elim =>
|
|
155 (Cname => new String'("ELIM"),
|
|
156 Unixcmd => new String'("gnatelim"),
|
|
157 Unixsws => null),
|
|
158
|
|
159 Find =>
|
|
160 (Cname => new String'("FIND"),
|
|
161 Unixcmd => new String'("gnatfind"),
|
|
162 Unixsws => null),
|
|
163
|
|
164 Krunch =>
|
|
165 (Cname => new String'("KRUNCH"),
|
|
166 Unixcmd => new String'("gnatkr"),
|
|
167 Unixsws => null),
|
|
168
|
|
169 Link =>
|
|
170 (Cname => new String'("LINK"),
|
|
171 Unixcmd => new String'("gnatlink"),
|
|
172 Unixsws => null),
|
|
173
|
|
174 List =>
|
|
175 (Cname => new String'("LIST"),
|
|
176 Unixcmd => new String'("gnatls"),
|
|
177 Unixsws => null),
|
|
178
|
|
179 Make =>
|
|
180 (Cname => new String'("MAKE"),
|
|
181 Unixcmd => new String'("gnatmake"),
|
|
182 Unixsws => null),
|
|
183
|
|
184 Metric =>
|
|
185 (Cname => new String'("METRIC"),
|
|
186 Unixcmd => new String'("gnatmetric"),
|
|
187 Unixsws => null),
|
|
188
|
|
189 Name =>
|
|
190 (Cname => new String'("NAME"),
|
|
191 Unixcmd => new String'("gnatname"),
|
|
192 Unixsws => null),
|
|
193
|
|
194 Preprocess =>
|
|
195 (Cname => new String'("PREPROCESS"),
|
|
196 Unixcmd => new String'("gnatprep"),
|
|
197 Unixsws => null),
|
|
198
|
|
199 Pretty =>
|
|
200 (Cname => new String'("PRETTY"),
|
|
201 Unixcmd => new String'("gnatpp"),
|
|
202 Unixsws => null),
|
|
203
|
|
204 Stack =>
|
|
205 (Cname => new String'("STACK"),
|
|
206 Unixcmd => new String'("gnatstack"),
|
|
207 Unixsws => null),
|
|
208
|
|
209 Stub =>
|
|
210 (Cname => new String'("STUB"),
|
|
211 Unixcmd => new String'("gnatstub"),
|
|
212 Unixsws => null),
|
|
213
|
|
214 Test =>
|
|
215 (Cname => new String'("TEST"),
|
|
216 Unixcmd => new String'("gnattest"),
|
|
217 Unixsws => null),
|
|
218
|
|
219 Xref =>
|
|
220 (Cname => new String'("XREF"),
|
|
221 Unixcmd => new String'("gnatxref"),
|
|
222 Unixsws => null)
|
|
223 );
|
|
224
|
|
225 -----------------------
|
|
226 -- Local Subprograms --
|
|
227 -----------------------
|
|
228
|
|
229 procedure Output_Version;
|
|
230 -- Output the version of this program
|
|
231
|
|
232 procedure Usage;
|
|
233 -- Display usage
|
|
234
|
|
235 --------------------
|
|
236 -- Output_Version --
|
|
237 --------------------
|
|
238
|
|
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;
|
|
246
|
|
247 -----------
|
|
248 -- Usage --
|
|
249 -----------
|
|
250
|
|
251 procedure Usage is
|
|
252 begin
|
|
253 Output_Version;
|
|
254 New_Line;
|
|
255 Put_Line ("List of available commands");
|
|
256 New_Line;
|
|
257
|
|
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);
|
|
263
|
|
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;
|
|
274
|
|
275 New_Line;
|
|
276 end loop;
|
|
277
|
|
278 New_Line;
|
|
279 end Usage;
|
|
280
|
|
281 procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
|
|
282
|
|
283 -- Start of processing for GNATCmd
|
|
284
|
|
285 begin
|
|
286 -- All output from GNATCmd is debugging or error output: send to stderr
|
|
287
|
|
288 Set_Standard_Error;
|
|
289
|
|
290 -- Initializations
|
|
291
|
|
292 Last_Switches.Init;
|
|
293 Last_Switches.Set_Last (0);
|
|
294
|
|
295 First_Switches.Init;
|
|
296 First_Switches.Set_Last (0);
|
|
297
|
|
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.
|
|
300
|
|
301 Name_Len := 0;
|
|
302 Add_Str_To_Name_Buffer (Command_Name);
|
|
303
|
|
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;
|
|
308
|
|
309 Setenv ("GNAT_DRIVER_COMMAND_LINE", Name_Buffer (1 .. Name_Len));
|
|
310
|
|
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.
|
|
313
|
|
314 declare
|
|
315 Command : constant String := Command_Name;
|
|
316
|
|
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;
|
|
328
|
|
329 exit;
|
|
330 end if;
|
|
331 end loop;
|
|
332 end;
|
|
333
|
|
334 -- Scan the command line
|
|
335
|
|
336 -- First, scan to detect --version and/or --help
|
|
337
|
|
338 Check_Version_And_Help ("GNAT", "1996");
|
|
339
|
|
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;
|
|
347
|
|
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;
|
|
353
|
|
354 else
|
|
355 exit;
|
|
356 end if;
|
|
357 end loop;
|
|
358
|
|
359 -- If there is no command, just output the usage
|
|
360
|
|
361 if Command_Arg > Argument_Count then
|
|
362 Usage;
|
|
363 return;
|
|
364 end if;
|
|
365
|
|
366 The_Command := Real_Command_Type'Value (Argument (Command_Arg));
|
|
367
|
|
368 exception
|
|
369 when Constraint_Error =>
|
|
370
|
|
371 -- Check if it is an alternate command
|
|
372
|
|
373 declare
|
|
374 Alternate : Alternate_Command;
|
|
375
|
|
376 begin
|
|
377 Alternate := Alternate_Command'Value (Argument (Command_Arg));
|
|
378 The_Command := Corresponding_To (Alternate);
|
|
379
|
|
380 exception
|
|
381 when Constraint_Error =>
|
|
382 Usage;
|
|
383 Fail ("unknown command: " & Argument (Command_Arg));
|
|
384 end;
|
|
385 end;
|
|
386
|
|
387 -- Get the arguments from the command line and from the eventual
|
|
388 -- argument file(s) specified on the command line.
|
|
389
|
|
390 for Arg in Command_Arg + 1 .. Argument_Count loop
|
|
391 declare
|
|
392 The_Arg : constant String := Argument (Arg);
|
|
393
|
|
394 begin
|
|
395 -- Check if an argument file is specified
|
|
396
|
|
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;
|
|
402
|
|
403 begin
|
|
404 -- Open the file and fail if the file cannot be found
|
|
405
|
|
406 begin
|
|
407 Open (Arg_File, In_File,
|
|
408 The_Arg (The_Arg'First + 1 .. The_Arg'Last));
|
|
409
|
|
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;
|
|
418
|
|
419 -- Read line by line and put the content of each non-
|
|
420 -- empty line in the Last_Switches table.
|
|
421
|
|
422 while not End_Of_File (Arg_File) loop
|
|
423 Get_Line (Arg_File, Line, Last);
|
|
424
|
|
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;
|
|
431
|
|
432 Close (Arg_File);
|
|
433 end;
|
|
434
|
|
435 elsif The_Arg'Length > 0 then
|
|
436 -- It is not an argument file; just put the argument in
|
|
437 -- the Last_Switches table.
|
|
438
|
|
439 Last_Switches.Increment_Last;
|
|
440 Last_Switches.Table (Last_Switches.Last) := new String'(The_Arg);
|
|
441 end if;
|
|
442 end;
|
|
443 end loop;
|
|
444
|
|
445 declare
|
|
446 Program : String_Access;
|
|
447 Exec_Path : String_Access;
|
|
448 Get_Target : Boolean := False;
|
|
449
|
|
450 begin
|
|
451 if The_Command = Stack then
|
|
452
|
|
453 -- Never call gnatstack with a prefix
|
|
454
|
|
455 Program := new String'(Command_List (The_Command).Unixcmd.all);
|
|
456
|
|
457 else
|
|
458 Program :=
|
|
459 Program_Name (Command_List (The_Command).Unixcmd.all, "gnat");
|
|
460
|
|
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.
|
|
465
|
|
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);
|
|
480
|
|
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;
|
|
488
|
|
489 if Call_GPR_Tool then
|
|
490 case The_Command is
|
|
491 when Bind
|
|
492 | Compile
|
|
493 | Link
|
|
494 | Make
|
|
495 =>
|
|
496 if Locate_Exec_On_Path (Gprbuild) /= null then
|
|
497 Program := new String'(Gprbuild);
|
|
498 Get_Target := True;
|
|
499
|
|
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;
|
|
505
|
|
506 elsif The_Command = Bind then
|
|
507 Fail
|
|
508 ("'gnat bind -P' is no longer supported;" &
|
|
509 " use 'gprbuild -b' instead.");
|
|
510
|
|
511 elsif The_Command = Link then
|
|
512 Fail
|
|
513 ("'gnat Link -P' is no longer supported;" &
|
|
514 " use 'gprbuild -l' instead.");
|
|
515 end if;
|
|
516
|
|
517 when Clean =>
|
|
518 if Locate_Exec_On_Path (Gprclean) /= null then
|
|
519 Program := new String'(Gprclean);
|
|
520 Get_Target := True;
|
|
521 end if;
|
|
522
|
|
523 when Name =>
|
|
524 if Locate_Exec_On_Path (Gprname) /= null then
|
|
525 Program := new String'(Gprname);
|
|
526 Get_Target := True;
|
|
527 end if;
|
|
528
|
|
529 when List =>
|
|
530 if Locate_Exec_On_Path (Gprls) /= null then
|
|
531 Program := new String'(Gprls);
|
|
532 Get_Target := True;
|
|
533 end if;
|
|
534
|
|
535 when others =>
|
|
536 null;
|
|
537 end case;
|
|
538
|
|
539 if Get_Target then
|
|
540 Find_Program_Name;
|
|
541
|
|
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;
|
|
552
|
|
553 -- Locate the executable for the command
|
|
554
|
|
555 Exec_Path := Locate_Exec_On_Path (Program.all);
|
|
556
|
|
557 if Exec_Path = null then
|
|
558 Put_Line (Standard_Error, "could not locate " & Program.all);
|
|
559 raise Error_Exit;
|
|
560 end if;
|
|
561
|
|
562 -- If there are switches for the executable, put them as first switches
|
|
563
|
|
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;
|
|
571
|
|
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.
|
|
575
|
|
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);
|
|
582
|
|
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;
|
|
595
|
|
596 -- Gather all the arguments and invoke the executable
|
|
597
|
|
598 declare
|
|
599 The_Args : Argument_List
|
|
600 (1 .. First_Switches.Last + Last_Switches.Last);
|
|
601 Arg_Num : Natural := 0;
|
|
602
|
|
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;
|
|
608
|
|
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;
|
|
613
|
|
614 if Verbose_Mode then
|
|
615 Put (Exec_Path.all);
|
|
616
|
|
617 for Arg in The_Args'Range loop
|
|
618 Put (" " & The_Args (Arg).all);
|
|
619 end loop;
|
|
620
|
|
621 New_Line;
|
|
622 end if;
|
|
623
|
|
624 My_Exit_Status := Exit_Status (Spawn (Exec_Path.all, The_Args));
|
|
625
|
|
626 Set_Exit_Status (My_Exit_Status);
|
|
627 end;
|
|
628 end;
|
|
629
|
|
630 exception
|
|
631 when Error_Exit =>
|
|
632 Set_Exit_Status (Failure);
|
|
633 end GNATCmd;
|