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
41 with Ada.Characters.Handling; use Ada.Characters.Handling;
42 with Ada.Command_Line; use Ada.Command_Line;
43 with Ada.Text_IO; use Ada.Text_IO;
44 with GNAT.OS_Lib; use GNAT.OS_Lib;
45 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
46 with GNAT.Regpat; use GNAT.Regpat;
51 -- ??? comments are thin throughout this unit
53 Version : constant String :=
54 "GPRCMD " & Gnatvsn.Gnat_Version_String &
55 " Copyright 2002-2003, Ada Core Technologies Inc.";
57 procedure Cat (File : String);
58 -- Print the contents of file on standard output.
59 -- If the file cannot be read, exit the process with an error code.
61 procedure Check_Args (Condition : Boolean);
62 -- If Condition is false, print the usage, and exit the process.
64 procedure Deps (Objext : String; File : String; GCC : Boolean);
65 -- Process $(CC) dependency file. If GCC is True, add a rule so that make
66 -- will not complain when a file is removed/added. If GCC is False, add a
67 -- rule to recompute the dependency file when needed
69 procedure Extend (Dir : String);
70 -- If Dir ends with /**, Put all subdirs recursively on standard output,
74 -- Display the command line options and exit the process.
76 procedure Copy_Time_Stamp (From, To : String);
77 -- Copy file time stamp from file From to file To.
83 procedure Cat (File : String) is
85 Buffer : String_Access;
89 FD := Open_Read (File, Fmode => Binary);
91 if FD = Invalid_FD then
95 Length := Integer (File_Length (FD));
96 Buffer := new String (1 .. Length);
97 Length := Read (FD, Buffer.all'Address, Length);
107 procedure Check_Args (Condition : Boolean) is
109 if not Condition then
114 ---------------------
115 -- Copy_Time_Stamp --
116 ---------------------
118 procedure Copy_Time_Stamp (From, To : String) is
119 function Copy_Attributes
121 Mode : Integer) return Integer;
122 pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
123 -- Mode = 0 - copy only time stamps.
124 -- Mode = 1 - copy time stamps and read/write/execute attributes
126 FD : File_Descriptor;
129 if not Is_Regular_File (From) then
133 FD := Create_File (To, Fmode => Binary);
135 if FD = Invalid_FD then
141 if Copy_Attributes (From & ASCII.NUL, To & ASCII.NUL, 0) /= 0 then
150 procedure Deps (Objext : String; File : String; GCC : Boolean) is
151 Colon : constant String := ':' & ASCII.LF;
152 NL : constant String := (1 => ASCII.LF);
153 Base : constant String := ' ' & Base_Name (File) & ": ";
154 FD : File_Descriptor;
155 Buffer : String_Access;
157 Obj_Regexp : constant Pattern_Matcher :=
158 Compile ("^.*\" & Objext & ": ");
159 Matched : Match_Array (0 .. 0);
165 FD := Open_Read_Write (File, Fmode => Binary);
167 if FD = Invalid_FD then
171 Length := Integer (File_Length (FD));
172 Buffer := new String (1 .. Length);
173 Length := Read (FD, Buffer.all'Address, Length);
176 Lseek (FD, 0, Seek_End);
179 FD := Create_File (File, Fmode => Binary);
182 Start := Buffer'First;
184 while Start <= Buffer'Last loop
186 -- Parse Buffer line by line
188 while Start < Buffer'Last
189 and then (Buffer (Start) = ASCII.CR
190 or else Buffer (Start) = ASCII.LF)
197 while Last < Buffer'Last
198 and then Buffer (Last + 1) /= ASCII.CR
199 and then Buffer (Last + 1) /= ASCII.LF
204 Match (Obj_Regexp, Buffer (Start .. Last), Matched);
207 if Matched (0) = No_Match then
210 First := Matched (0).Last + 1;
213 Length := Write (FD, Buffer (First)'Address, Last - First + 1);
215 if Start = Last or else Buffer (Last) = '\' then
216 Length := Write (FD, NL (1)'Address, NL'Length);
218 Length := Write (FD, Colon (1)'Address, Colon'Length);
222 if Matched (0) = No_Match then
226 Write (FD, Buffer (Start)'Address,
227 Matched (0).Last - Start - 1);
228 Length := Write (FD, Base (Base'First)'Address, Base'Length);
229 First := Matched (0).Last + 1;
232 Length := Write (FD, Buffer (First)'Address, Last - First + 1);
233 Length := Write (FD, NL (1)'Address, NL'Length);
247 procedure Extend (Dir : String) is
249 procedure Recursive_Extend (D : String);
250 -- Recursively display all subdirectories of D.
252 ----------------------
253 -- Recursive_Extend --
254 ----------------------
256 procedure Recursive_Extend (D : String) is
258 Buffer : String (1 .. 8192);
265 Read (Iter, Buffer, Last);
269 if Buffer (1 .. Last) /= "."
270 and then Buffer (1 .. Last) /= ".."
273 Abs_Dir : constant String := D & Buffer (1 .. Last);
276 if Is_Directory (Abs_Dir)
277 and then not Is_Symbolic_Link (Abs_Dir)
280 Recursive_Extend (Abs_Dir & '/');
289 when Directory_Error =>
291 end Recursive_Extend;
293 -- Start of processing for Extend
297 or else (Dir (Dir'Last - 2) /= '/'
298 and then Dir (Dir'Last - 2) /= Directory_Separator)
299 or else Dir (Dir'Last - 1 .. Dir'Last) /= "**"
306 D : constant String := Dir (Dir'First .. Dir'Last - 2);
309 Recursive_Extend (D);
319 Put_Line (Standard_Error, "usage: gprcmd cmd [arguments]");
320 Put_Line (Standard_Error, "where cmd is one of the following commands:");
321 Put_Line (Standard_Error, " pwd " &
322 "display current directory");
323 Put_Line (Standard_Error, " to_lower " &
324 "display next argument in lower case");
325 Put_Line (Standard_Error, " to_absolute " &
326 "convert pathnames to absolute " &
327 "directories when needed");
328 Put_Line (Standard_Error, " cat " &
329 "dump contents of a given file");
330 Put_Line (Standard_Error, " extend " &
331 "handle recursive directories " &
332 "(""/**"" notation)");
333 Put_Line (Standard_Error, " deps " &
334 "post process dependency makefiles");
335 Put_Line (Standard_Error, " stamp " &
336 "copy file time stamp from file1 to file2");
340 -- Start of processing for Gprcmd
343 Check_Args (Argument_Count > 0);
346 Cmd : constant String := Argument (1);
350 Put_Line (Standard_Error, Version);
353 elsif Cmd = "pwd" then
354 Put (Format_Pathname (Get_Current_Dir, UNIX));
356 elsif Cmd = "cat" then
357 Check_Args (Argument_Count = 2);
360 elsif Cmd = "to_lower" then
361 Check_Args (Argument_Count >= 2);
363 for J in 2 .. Argument_Count loop
364 Put (To_Lower (Argument (J)));
366 if J < Argument_Count then
371 elsif Cmd = "to_absolute" then
372 Check_Args (Argument_Count > 2);
375 Dir : constant String := Argument (2);
378 for J in 3 .. Argument_Count loop
379 if Is_Absolute_Path (Argument (J)) then
380 Put (Format_Pathname (Argument (J), UNIX));
382 Put (Format_Pathname (Normalize_Pathname (Argument (J), Dir),
386 if J < Argument_Count then
392 elsif Cmd = "extend" then
393 Check_Args (Argument_Count >= 2);
396 Dir : constant String := Argument (2);
399 for J in 3 .. Argument_Count loop
400 if Is_Absolute_Path (Argument (J)) then
401 Extend (Format_Pathname (Argument (J), UNIX));
404 (Format_Pathname (Normalize_Pathname (Argument (J), Dir),
408 if J < Argument_Count then
414 elsif Cmd = "deps" then
415 Check_Args (Argument_Count in 3 .. 4);
416 Deps (Argument (2), Argument (3), GCC => Argument_Count = 4);
418 elsif Cmd = "stamp" then
419 Check_Args (Argument_Count = 3);
420 Copy_Time_Stamp (Argument (2), Argument (3));