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);
305 if Buffer (1 .. Last) /= "."
306 and then Buffer (1 .. Last) /= ".."
309 Abs_Dir : constant String := D & Buffer (1 .. Last);
312 if Is_Directory (Abs_Dir)
313 and then not Is_Symbolic_Link (Abs_Dir)
316 Recursive_Extend (Abs_Dir & '/');
325 when Directory_Error =>
327 end Recursive_Extend;
329 -- Start of processing for Extend
333 or else (Dir (Dir'Last - 2) /= '/'
334 and then Dir (Dir'Last - 2) /= Directory_Separator)
335 or else Dir (Dir'Last - 1 .. Dir'Last) /= "**"
342 D : constant String := Dir (Dir'First .. Dir'Last - 2);
345 Recursive_Extend (D);
355 Put_Line (Standard_Error, "usage
: gprcmd cmd
[arguments
]");
356 Put_Line (Standard_Error, "where cmd
is one
of the following commands
:");
357 Put_Line (Standard_Error, " pwd
" &
358 "display current directory
");
359 Put_Line (Standard_Error, " to_lower
" &
360 "display next argument
in lower
case");
361 Put_Line (Standard_Error, " to_absolute
" &
362 "convert pathnames to absolute
" &
363 "directories
when needed
");
364 Put_Line (Standard_Error, " cat
" &
365 "dump contents
of a given file
");
366 Put_Line (Standard_Error, " extend
" &
367 "handle recursive directories
" &
368 "(""/**"" notation
)");
369 Put_Line (Standard_Error, " deps
" &
370 "post process dependency makefiles
");
371 Put_Line (Standard_Error, " stamp
" &
372 "copy file time stamp from file1 to file2
");
373 Put_Line (Standard_Error, " prefix
" &
374 "get the prefix
of the GNAT installation
");
375 Put_Line (Standard_Error, " path_sep
" &
376 "returns the path separator
");
377 Put_Line (Standard_Error, " linkopts
" &
378 "process attribute Linker
'Linker_Options");
379 Put_Line (Standard_Error, " ignore
" &
384 -- Start of processing for Gprcmd
391 Check_Args (Argument_Count > 0);
394 Cmd : constant String := Argument (1);
399 -- Output on standard error, because only returned values should
400 -- go to standard output.
402 Put (Standard_Error, "GPRCMD
");
403 Put (Standard_Error, Gnatvsn.Gnat_Version_String);
404 Put_Line (Standard_Error,
405 " Copyright
2002-2004, Free Software Fundation
, Inc
.");
408 elsif Cmd = "pwd
" then
409 Put (Format_Pathname (Get_Current_Dir, UNIX));
411 elsif Cmd = "cat
" then
412 Check_Args (Argument_Count = 2);
415 elsif Cmd = "to_lower
" then
416 Check_Args (Argument_Count >= 2);
418 for J in 2 .. Argument_Count loop
419 Put (To_Lower (Argument (J)));
421 if J < Argument_Count then
426 elsif Cmd = "to_absolute
" then
427 Check_Args (Argument_Count > 2);
430 Dir : constant String := Argument (2);
433 for J in 3 .. Argument_Count loop
434 if Is_Absolute_Path (Argument (J)) then
435 Put (Format_Pathname (Argument (J), UNIX));
439 (Format_Pathname (Argument (J)),
440 Format_Pathname (Dir)),
444 if J < Argument_Count then
450 elsif Cmd = "extend
" then
451 Check_Args (Argument_Count >= 2);
454 Dir : constant String := Argument (2);
457 -- Loop to remove quotes that may have been added around arguments
459 for J in 3 .. Argument_Count loop
461 Arg : constant String := Argument (J);
462 First : Natural := Arg'First;
463 Last : Natural := Arg'Last;
466 if Arg (First) = '"' and then Arg (Last) = '"' then
471 if Is_Absolute_Path (Arg (First .. Last)) then
472 Extend (Format_Pathname (Arg (First .. Last), UNIX));
477 (Format_Pathname (Arg (First .. Last)),
478 Format_Pathname (Dir)),
482 if J < Argument_Count then
489 elsif Cmd = "deps
" then
490 Check_Args (Argument_Count in 3 .. 4);
491 Deps (Argument (2), Argument (3), GCC => Argument_Count = 4);
493 elsif Cmd = "stamp
" then
494 Check_Args (Argument_Count = 3);
495 Copy_Time_Stamp (Argument (2), Argument (3));
497 elsif Cmd = "prefix
" then
499 -- Find the GNAT prefix. gprcmd is found in <prefix>/bin.
500 -- So we find the full path of gprcmd, verify that it is in a
501 -- subdirectory "bin
", and return the <prefix> if it is the case.
502 -- Otherwise, nothing is returned.
507 Path : constant String_Access :=
508 Locate_Exec_On_Path (Name_Buffer (1 .. Name_Len));
515 while Index >= Path'First + 4 loop
516 exit when Path (Index) = Directory_Separator;
520 if Index > Path'First + 5
521 and then Path (Index - 3 .. Index - 1) = "bin
"
522 and then Path (Index - 4) = Directory_Separator
524 -- We have found the <prefix>, return it
526 Put (Path (Path'First .. Index - 5));
531 -- For "path
" just add path separator after each directory argument
533 elsif Cmd = "path_sep
" then
534 Put (Path_Separator);
536 -- Check the linker options for relative paths. Insert the project
537 -- base dir before relative paths.
539 elsif Cmd = "linkopts
" then
540 Check_Args (Argument_Count >= 2);
542 -- First argument is the base directory of the project file
545 Base_Dir : constant String := Argument (2) & '/';
547 -- process the remainder of the arguments
549 for J in 3 .. Argument_Count loop
551 Arg : constant String := Argument (J);
553 -- If it is a switch other than a -L switch, just send back
556 if Arg (Arg'First) = '-' and then
557 (Arg'Length <= 2 or else Arg (Arg'First + 1) /= 'L')
562 -- If it is a file, check if its path is relative, and
563 -- if it is relative, add <project base dir>/ in front.
564 -- Otherwise just send back the argument.
567 or else Arg (Arg'First .. Arg'First + 1) /= "-L
"
569 if not Is_Absolute_Path (Arg) then
575 -- For -L switches, check if the path is relative and
576 -- proceed similarly.
582 not Is_Absolute_Path (Arg (Arg'First + 2 .. Arg'Last))
587 Put (Arg (Arg'First + 2 .. Arg'Last));
592 -- Insert a space between each processed argument
594 if J /= Argument_Count then
600 -- For "ignore
" do nothing
602 elsif Cmd = "ignore
" then