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