1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2002-2003 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
43 with Osint; use Osint;
44 with Namet; use Namet;
46 with Ada.Characters.Handling; use Ada.Characters.Handling;
47 with Ada.Command_Line; use Ada.Command_Line;
48 with Ada.Text_IO; use Ada.Text_IO;
49 with GNAT.OS_Lib; use GNAT.OS_Lib;
50 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
51 with GNAT.Regpat; use GNAT.Regpat;
56 -- ??? comments are thin throughout this unit
59 procedure Cat (File : String);
60 -- Print the contents of file on standard output.
61 -- If the file cannot be read, exit the process with an error code.
63 procedure Check_Args (Condition : Boolean);
64 -- If Condition is false, print the usage, and exit the process.
66 procedure Deps (Objext : String; File : String; GCC : Boolean);
67 -- Process $(CC) dependency file. If GCC is True, add a rule so that make
68 -- will not complain when a file is removed/added. If GCC is False, add a
69 -- rule to recompute the dependency file when needed
71 procedure Extend (Dir : String);
72 -- If Dir ends with /**, Put all subdirs recursively on standard output,
76 -- Display the command line options and exit the process.
78 procedure Copy_Time_Stamp (From, To : String);
79 -- Copy file time stamp from file From to file To.
85 procedure Cat (File : String) is
87 Buffer : String_Access;
91 FD := Open_Read (File, Fmode => Binary);
93 if FD = Invalid_FD then
97 Length := Integer (File_Length (FD));
98 Buffer := new String (1 .. Length);
99 Length := Read (FD, Buffer.all'Address, Length);
109 procedure Check_Args (Condition : Boolean) is
111 if not Condition then
116 ---------------------
117 -- Copy_Time_Stamp --
118 ---------------------
120 procedure Copy_Time_Stamp (From, To : String) is
121 function Copy_Attributes
123 Mode : Integer) return Integer;
124 pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
125 -- Mode = 0 - copy only time stamps.
126 -- Mode = 1 - copy time stamps and read/write/execute attributes
128 FD : File_Descriptor;
131 if not Is_Regular_File (From) then
135 FD := Create_File (To, Fmode => Binary);
137 if FD = Invalid_FD then
143 if Copy_Attributes (From & ASCII.NUL, To & ASCII.NUL, 0) /= 0 then
152 procedure Deps (Objext : String; File : String; GCC : Boolean) is
153 Colon : constant String := ':' & ASCII.LF;
154 NL : constant String := (1 => ASCII.LF);
155 Base : constant String := ' ' & Base_Name (File) & ": ";
156 FD : File_Descriptor;
157 Buffer : String_Access;
159 Obj_Regexp : constant Pattern_Matcher :=
160 Compile ("^.*\" & Objext & ": ");
161 Matched : Match_Array (0 .. 0);
167 FD := Open_Read_Write (File, Fmode => Binary);
169 if FD = Invalid_FD then
173 Length := Integer (File_Length (FD));
174 Buffer := new String (1 .. Length);
175 Length := Read (FD, Buffer.all'Address, Length);
178 Lseek (FD, 0, Seek_End);
181 FD := Create_File (File, Fmode => Binary);
184 Start := Buffer'First;
186 while Start <= Buffer'Last loop
188 -- Parse Buffer line by line
190 while Start < Buffer'Last
191 and then (Buffer (Start) = ASCII.CR
192 or else Buffer (Start) = ASCII.LF)
199 while Last < Buffer'Last
200 and then Buffer (Last + 1) /= ASCII.CR
201 and then Buffer (Last + 1) /= ASCII.LF
206 Match (Obj_Regexp, Buffer (Start .. Last), Matched);
209 if Matched (0) = No_Match then
212 First := Matched (0).Last + 1;
215 Length := Write (FD, Buffer (First)'Address, Last - First + 1);
217 if Start = Last or else Buffer (Last) = '\' then
218 Length := Write (FD, NL (1)'Address, NL'Length);
220 Length := Write (FD, Colon (1)'Address, Colon'Length);
224 if Matched (0) = No_Match then
228 Write (FD, Buffer (Start)'Address,
229 Matched (0).Last - Start - 1);
230 Length := Write (FD, Base (Base'First)'Address, Base'Length);
231 First := Matched (0).Last + 1;
234 Length := Write (FD, Buffer (First)'Address, Last - First + 1);
235 Length := Write (FD, NL (1)'Address, NL'Length);
249 procedure Extend (Dir : String) is
251 procedure Recursive_Extend (D : String);
252 -- Recursively display all subdirectories of D.
254 ----------------------
255 -- Recursive_Extend --
256 ----------------------
258 procedure Recursive_Extend (D : String) is
260 Buffer : String (1 .. 8192);
267 Read (Iter, Buffer, Last);
271 if Buffer (1 .. Last) /= "."
272 and then Buffer (1 .. Last) /= ".."
275 Abs_Dir : constant String := D & Buffer (1 .. Last);
278 if Is_Directory (Abs_Dir)
279 and then not Is_Symbolic_Link (Abs_Dir)
282 Recursive_Extend (Abs_Dir & '/');
291 when Directory_Error =>
293 end Recursive_Extend;
295 -- Start of processing for Extend
299 or else (Dir (Dir'Last - 2) /= '/'
300 and then Dir (Dir'Last - 2) /= Directory_Separator)
301 or else Dir (Dir'Last - 1 .. Dir'Last) /= "**"
308 D : constant String := Dir (Dir'First .. Dir'Last - 2);
311 Recursive_Extend (D);
321 Put_Line (Standard_Error, "usage: gprcmd cmd [arguments]");
322 Put_Line (Standard_Error, "where cmd is one of the following commands:");
323 Put_Line (Standard_Error, " pwd " &
324 "display current directory");
325 Put_Line (Standard_Error, " to_lower " &
326 "display next argument in lower case");
327 Put_Line (Standard_Error, " to_absolute " &
328 "convert pathnames to absolute " &
329 "directories when needed");
330 Put_Line (Standard_Error, " cat " &
331 "dump contents of a given file");
332 Put_Line (Standard_Error, " extend " &
333 "handle recursive directories " &
334 "(""/**"" notation)");
335 Put_Line (Standard_Error, " deps " &
336 "post process dependency makefiles");
337 Put_Line (Standard_Error, " stamp " &
338 "copy file time stamp from file1 to file2");
342 -- Start of processing for Gprcmd
345 Check_Args (Argument_Count > 0);
348 Cmd : constant String := Argument (1);
353 -- Should this be on Standard_Error ???
355 Put (Standard_Error, "GPRCMD ");
356 Put (Standard_Error, Gnatvsn.Gnat_Version_String);
357 Put_Line (Standard_Error,
358 " Copyright 2002-2003, Free Software Fundation, Inc.");
361 elsif Cmd = "pwd" then
362 Put (Format_Pathname (Get_Current_Dir, UNIX));
364 elsif Cmd = "cat" then
365 Check_Args (Argument_Count = 2);
368 elsif Cmd = "to_lower" then
369 Check_Args (Argument_Count >= 2);
371 for J in 2 .. Argument_Count loop
372 Put (To_Lower (Argument (J)));
374 if J < Argument_Count then
379 elsif Cmd = "to_absolute" then
380 Check_Args (Argument_Count > 2);
383 Dir : constant String := Argument (2);
386 for J in 3 .. Argument_Count loop
387 if Is_Absolute_Path (Argument (J)) then
388 Put (Format_Pathname (Argument (J), UNIX));
390 Put (Format_Pathname (Normalize_Pathname (Argument (J), Dir),
394 if J < Argument_Count then
400 elsif Cmd = "extend" then
401 Check_Args (Argument_Count >= 2);
404 Dir : constant String := Argument (2);
407 for J in 3 .. Argument_Count loop
408 if Is_Absolute_Path (Argument (J)) then
409 Extend (Format_Pathname (Argument (J), UNIX));
412 (Format_Pathname (Normalize_Pathname (Argument (J), Dir),
416 if J < Argument_Count then
422 elsif Cmd = "deps" then
423 Check_Args (Argument_Count in 3 .. 4);
424 Deps (Argument (2), Argument (3), GCC => Argument_Count = 4);
426 elsif Cmd = "stamp" then
427 Check_Args (Argument_Count = 3);
428 Copy_Time_Stamp (Argument (2), Argument (3));
430 elsif Cmd = "prefix" then
432 -- Find the GNAT prefix. gprcmd is found in <prefix>/bin.
433 -- So we find the full path of gprcmd, verify that it is in a
434 -- subdirectory "bin", and return the <prefix> if it is the case.
435 -- Otherwise, nothing is returned.
440 Path : String_Access :=
441 Locate_Exec_On_Path (Name_Buffer (1 .. Name_Len));
448 while Index >= Path'First + 4 loop
449 exit when Path (Index) = Directory_Separator;
453 if Index > Path'First + 5
454 and then Path (Index - 3 .. Index - 1) = "bin"
455 and then Path (Index - 4) = Directory_Separator
457 -- We have found the <prefix>, return it.
459 Put (Path (Path'First .. Index - 5));