1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2002-2004 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 2, 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 COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
27 -- A utility used by Makefile.generic to handle multi-language builds.
28 -- gprcmd provides a set of commands so that the makefiles do not need
29 -- to depend on unix utilities not available on all targets.
31 -- The list of commands recognized by gprcmd are:
33 -- pwd display current directory
34 -- to_lower display next argument in lower case
35 -- to_absolute convert pathnames to absolute directories when needed
36 -- cat dump contents of a given file
37 -- extend handle recursive directories ("/**" notation)
38 -- deps post process dependency makefiles
39 -- stamp copy file time stamp from file1 to file2
40 -- prefix get the prefix of the GNAT installation
41 -- path convert a list of directories to a path list, inserting a
42 -- path separator after each directory, including the last one
46 with Osint
; use Osint
;
47 with Namet
; use Namet
;
49 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
50 with Ada
.Command_Line
; use Ada
.Command_Line
;
51 with Ada
.Text_IO
; use Ada
.Text_IO
;
52 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
53 with GNAT
.Directory_Operations
; use GNAT
.Directory_Operations
;
54 with GNAT
.Regpat
; use GNAT
.Regpat
;
59 -- ??? comments are thin throughout this unit
61 Gprdebug
: constant String := To_Lower
(Getenv
("GPRDEBUG").all);
62 Debug
: constant Boolean := Gprdebug
= "true";
63 -- When Debug is True, gprcmd displays its arguments to Standard_Error.
64 -- This is to help to debug.
66 procedure Cat
(File
: String);
67 -- Print the contents of file on standard output.
68 -- If the file cannot be read, exit the process with an error code.
70 procedure Check_Args
(Condition
: Boolean);
71 -- If Condition is false, print command invoked, then the usage,
72 -- and exit the process.
74 procedure Deps
(Objext
: String; File
: String; GCC
: Boolean);
75 -- Process $(CC) dependency file. If GCC is True, add a rule so that make
76 -- will not complain when a file is removed/added. If GCC is False, add a
77 -- rule to recompute the dependency file when needed
79 procedure Extend
(Dir
: String);
80 -- If Dir ends with /**, Put all subdirs recursively on standard output,
84 -- Display the command line options and exit the process.
86 procedure Copy_Time_Stamp
(From
, To
: String);
87 -- Copy file time stamp from file From to file To.
89 procedure Display_Command
;
90 -- Display the invoked command to Standard_Error
96 procedure Cat
(File
: String) is
98 Buffer
: String_Access
;
102 FD
:= Open_Read
(File
, Fmode
=> Binary
);
104 if FD
= Invalid_FD
then
108 Length
:= Integer (File_Length
(FD
));
109 Buffer
:= new String (1 .. Length
);
110 Length
:= Read
(FD
, Buffer
.all'Address, Length
);
120 procedure Check_Args
(Condition
: Boolean) is
122 if not Condition
then
125 "bad call to gprcmd with" & Argument_Count
'Img & " arguments.");
127 for J
in 0 .. Argument_Count
loop
128 Put
(Standard_Error
, Argument
(J
) & " ");
131 New_Line
(Standard_Error
);
137 ---------------------
138 -- Copy_Time_Stamp --
139 ---------------------
141 procedure Copy_Time_Stamp
(From
, To
: String) is
142 function Copy_Attributes
144 Mode
: Integer) return Integer;
145 pragma Import
(C
, Copy_Attributes
, "__gnat_copy_attribs");
146 -- Mode = 0 - copy only time stamps.
147 -- Mode = 1 - copy time stamps and read/write/execute attributes
149 FD
: File_Descriptor
;
152 if not Is_Regular_File
(From
) then
156 FD
:= Create_File
(To
, Fmode
=> Binary
);
158 if FD
= Invalid_FD
then
164 if Copy_Attributes
(From
& ASCII
.NUL
, To
& ASCII
.NUL
, 0) /= 0 then
173 procedure Deps
(Objext
: String; File
: String; GCC
: Boolean) is
174 Colon
: constant String := ':' & ASCII
.LF
;
175 NL
: constant String := (1 => ASCII
.LF
);
176 Base
: constant String := ' ' & Base_Name
(File
) & ": ";
177 FD
: File_Descriptor
;
178 Buffer
: String_Access
;
180 Obj_Regexp
: constant Pattern_Matcher
:=
181 Compile
("^.*\" & Objext & ": ");
182 Matched : Match_Array (0 .. 0);
188 FD := Open_Read_Write (File, Fmode => Binary);
190 if FD = Invalid_FD then
194 Length := Integer (File_Length (FD));
195 Buffer := new String (1 .. Length);
196 Length := Read (FD, Buffer.all'Address, Length);
199 Lseek (FD, 0, Seek_End);
202 FD := Create_File (File, Fmode => Binary);
205 Start := Buffer'First;
207 while Start <= Buffer'Last loop
209 -- Parse Buffer line by line
211 while Start < Buffer'Last
212 and then (Buffer (Start) = ASCII.CR
213 or else Buffer (Start) = ASCII.LF)
220 while Last < Buffer'Last
221 and then Buffer (Last + 1) /= ASCII.CR
222 and then Buffer (Last + 1) /= ASCII.LF
227 Match (Obj_Regexp, Buffer (Start .. Last), Matched);
230 if Matched (0) = No_Match then
233 First := Matched (0).Last + 1;
236 Length := Write (FD, Buffer (First)'Address, Last - First + 1);
238 if Start = Last or else Buffer (Last) = '\' then
239 Length := Write (FD, NL (1)'Address, NL'Length);
241 Length := Write (FD, Colon (1)'Address, Colon'Length);
245 if Matched (0) = No_Match then
249 Write (FD, Buffer (Start)'Address,
250 Matched (0).Last - Start - 1);
251 Length := Write (FD, Base (Base'First)'Address, Base'Length);
252 First := Matched (0).Last + 1;
255 Length := Write (FD, Buffer (First)'Address, Last - First + 1);
256 Length := Write (FD, NL (1)'Address, NL'Length);
266 ---------------------
267 -- Display_Command --
268 ---------------------
270 procedure Display_Command is
272 for J in 0 .. Argument_Count loop
273 Put (Standard_Error, Argument (J) & ' ');
276 New_Line (Standard_Error);
283 procedure Extend (Dir : String) is
285 procedure Recursive_Extend (D : String);
286 -- Recursively display all subdirectories of D
288 ----------------------
289 -- Recursive_Extend --
290 ----------------------
292 procedure Recursive_Extend (D : String) is
294 Buffer : String (1 .. 8192);
301 Read (Iter, Buffer, Last);
304 if Buffer (1 .. Last) /= "."
305 and then Buffer (1 .. Last) /= ".."
308 Abs_Dir : constant String := D & "/" & Buffer (1 .. Last);
310 if Is_Directory (Abs_Dir)
311 and then not Is_Symbolic_Link (Abs_Dir)
314 Recursive_Extend (Abs_Dir);
323 when Directory_Error =>
325 end Recursive_Extend;
327 -- Start of processing for Extend
331 or else (Dir (Dir'Last - 2) /= '/'
332 and then Dir (Dir'Last - 2) /= Directory_Separator)
333 or else Dir (Dir'Last - 1 .. Dir'Last) /= "**"
340 D : constant String := Dir (Dir'First .. Dir'Last - 3);
343 Recursive_Extend (D);
353 Put_Line (Standard_Error, "usage
: gprcmd cmd
[arguments
]");
354 Put_Line (Standard_Error, "where cmd
is one
of the following commands
:");
355 Put_Line (Standard_Error, " pwd
" &
356 "display current directory
");
357 Put_Line (Standard_Error, " to_lower
" &
358 "display next argument
in lower
case");
359 Put_Line (Standard_Error, " to_absolute
" &
360 "convert pathnames to absolute
" &
361 "directories
when needed
");
362 Put_Line (Standard_Error, " cat
" &
363 "dump contents
of a given file
");
364 Put_Line (Standard_Error, " extend
" &
365 "handle recursive directories
" &
366 "(""/**"" notation
)");
367 Put_Line (Standard_Error, " deps
" &
368 "post process dependency makefiles
");
369 Put_Line (Standard_Error, " stamp
" &
370 "copy file time stamp from file1 to file2
");
371 Put_Line (Standard_Error, " prefix
" &
372 "get the prefix
of the GNAT installation
");
373 Put_Line (Standard_Error, " path_sep
" &
374 "returns the path separator
");
375 Put_Line (Standard_Error, " linkopts
" &
376 "process attribute Linker
'Linker_Options");
377 Put_Line (Standard_Error, " ignore
" &
382 -- Start of processing for Gprcmd
389 Check_Args (Argument_Count > 0);
392 Cmd : constant String := Argument (1);
397 -- Output on standard error, because only returned values should
398 -- go to standard output.
400 Put (Standard_Error, "GPRCMD
");
401 Put (Standard_Error, Gnatvsn.Gnat_Version_String);
402 Put_Line (Standard_Error,
403 " Copyright
2002-2004, Free Software Fundation
, Inc
.");
406 elsif Cmd = "pwd
" then
408 CD : constant String := Get_Current_Dir;
410 Put (Format_Pathname (CD (CD'First .. CD'Last - 1), UNIX));
413 elsif Cmd = "cat
" then
414 Check_Args (Argument_Count = 2);
417 elsif Cmd = "to_lower
" then
418 Check_Args (Argument_Count >= 2);
420 for J in 2 .. Argument_Count loop
421 Put (To_Lower (Argument (J)));
423 if J < Argument_Count then
428 elsif Cmd = "to_absolute
" then
429 Check_Args (Argument_Count > 2);
432 Dir : constant String := Argument (2);
435 for J in 3 .. Argument_Count loop
436 if Is_Absolute_Path (Argument (J)) then
437 Put (Format_Pathname (Argument (J), UNIX));
441 (Format_Pathname (Argument (J)),
442 Format_Pathname (Dir)),
446 if J < Argument_Count then
452 elsif Cmd = "extend
" then
453 Check_Args (Argument_Count >= 2);
456 Dir : constant String := Argument (2);
459 -- Loop to remove quotes that may have been added around arguments
461 for J in 3 .. Argument_Count loop
463 Arg : constant String := Argument (J);
464 First : Natural := Arg'First;
465 Last : Natural := Arg'Last;
468 if Arg (First) = '"' and then Arg (Last) = '"' then
473 if Is_Absolute_Path (Arg (First .. Last)) then
474 Extend (Format_Pathname (Arg (First .. Last), UNIX));
479 (Format_Pathname (Arg (First .. Last)),
480 Format_Pathname (Dir)),
484 if J < Argument_Count then
491 elsif Cmd = "deps
" then
492 Check_Args (Argument_Count in 3 .. 4);
493 Deps (Argument (2), Argument (3), GCC => Argument_Count = 4);
495 elsif Cmd = "stamp
" then
496 Check_Args (Argument_Count = 3);
497 Copy_Time_Stamp (Argument (2), Argument (3));
499 elsif Cmd = "prefix
" then
501 -- Find the GNAT prefix. gprcmd is found in <prefix>/bin.
502 -- So we find the full path of gprcmd, verify that it is in a
503 -- subdirectory "bin
", and return the <prefix> if it is the case.
504 -- Otherwise, nothing is returned.
509 Path : constant String_Access :=
510 Locate_Exec_On_Path (Name_Buffer (1 .. Name_Len));
517 while Index >= Path'First + 4 loop
518 exit when Path (Index) = Directory_Separator;
522 if Index > Path'First + 5
523 and then Path (Index - 3 .. Index - 1) = "bin
"
524 and then Path (Index - 4) = Directory_Separator
526 -- We have found the <prefix>, return it
528 Put (Path (Path'First .. Index - 5));
533 -- For "path
" just add path separator after each directory argument
535 elsif Cmd = "path_sep
" then
536 Put (Path_Separator);
538 -- Check the linker options for relative paths. Insert the project
539 -- base dir before relative paths.
541 elsif Cmd = "linkopts
" then
542 Check_Args (Argument_Count >= 2);
544 -- First argument is the base directory of the project file
547 Base_Dir : constant String := Argument (2) & '/';
549 -- process the remainder of the arguments
551 for J in 3 .. Argument_Count loop
553 Arg : constant String := Argument (J);
555 -- If it is a switch other than a -L switch, just send back
558 if Arg (Arg'First) = '-' and then
559 (Arg'Length <= 2 or else Arg (Arg'First + 1) /= 'L')
564 -- If it is a file, check if its path is relative, and
565 -- if it is relative, add <project base dir>/ in front.
566 -- Otherwise just send back the argument.
569 or else Arg (Arg'First .. Arg'First + 1) /= "-L
"
571 if not Is_Absolute_Path (Arg) then
577 -- For -L switches, check if the path is relative and
578 -- proceed similarly.
584 not Is_Absolute_Path (Arg (Arg'First + 2 .. Arg'Last))
589 Put (Arg (Arg'First + 2 .. Arg'Last));
594 -- Insert a space between each processed argument
596 if J /= Argument_Count then
602 -- For "ignore
" do nothing
604 elsif Cmd = "ignore
" then