PR rtl-optimization/82913
[official-gcc.git] / gcc / ada / gnatname.adb
blob4a9973f5dfdd4eae2ba1b1ebb9bcfca915142ddc
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T N A M E --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2001-2017, Free Software Foundation, Inc. --
10 -- --
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 3, 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Ada.Characters.Handling; use Ada.Characters.Handling;
27 with Ada.Command_Line; use Ada.Command_Line;
28 with Ada.Text_IO; use Ada.Text_IO;
30 with GNAT.Command_Line; use GNAT.Command_Line;
31 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
32 with GNAT.Dynamic_Tables;
33 with GNAT.OS_Lib; use GNAT.OS_Lib;
35 with Make_Util; use Make_Util;
36 with Namet; use Namet;
37 with Opt;
38 with Osint; use Osint;
39 with Output;
40 with Switch; use Switch;
41 with Table;
42 with Tempdir;
43 with Types; use Types;
45 with System.CRTL;
46 with System.Regexp; use System.Regexp;
48 procedure Gnatname is
50 pragma Warnings (Off);
51 type Matched_Type is (True, False, Excluded);
52 pragma Warnings (On);
54 Create_Project : Boolean := False;
56 Subdirs_Switch : constant String := "--subdirs=";
58 Usage_Output : Boolean := False;
59 -- Set to True when usage is output, to avoid multiple output
61 Usage_Needed : Boolean := False;
62 -- Set to True by -h switch
64 Version_Output : Boolean := False;
65 -- Set to True when version is output, to avoid multiple output
67 Very_Verbose : Boolean := False;
68 -- Set to True with -v -v
70 File_Path : String_Access := new String'("gnat.adc");
71 -- Path name of the file specified by -c or -P switch
73 File_Set : Boolean := False;
74 -- Set to True by -c or -P switch.
75 -- Used to detect multiple -c/-P switches.
77 Args : Argument_List_Access;
78 -- The list of arguments for calls to the compiler to get the unit names
79 -- and kinds (spec or body) in the Ada sources.
81 Path_Name : String_Access;
83 Path_Last : Natural;
85 Directory_Last : Natural := 0;
87 function Dup (Fd : File_Descriptor) return File_Descriptor;
89 procedure Dup2 (Old_Fd, New_Fd : File_Descriptor);
91 Gcc : constant String := "gcc";
92 Gcc_Path : String_Access := null;
94 package Patterns is new GNAT.Dynamic_Tables
95 (Table_Component_Type => String_Access,
96 Table_Index_Type => Natural,
97 Table_Low_Bound => 0,
98 Table_Initial => 10,
99 Table_Increment => 100);
100 -- Table to accumulate the patterns
102 type Argument_Data is record
103 Directories : Patterns.Instance;
104 Name_Patterns : Patterns.Instance;
105 Excluded_Patterns : Patterns.Instance;
106 Foreign_Patterns : Patterns.Instance;
107 end record;
109 package Arguments is new Table.Table
110 (Table_Component_Type => Argument_Data,
111 Table_Index_Type => Natural,
112 Table_Low_Bound => 0,
113 Table_Initial => 10,
114 Table_Increment => 100,
115 Table_Name => "Gnatname.Arguments");
116 -- Table to accumulate directories and patterns
118 package Preprocessor_Switches is new Table.Table
119 (Table_Component_Type => String_Access,
120 Table_Index_Type => Natural,
121 Table_Low_Bound => 0,
122 Table_Initial => 10,
123 Table_Increment => 100,
124 Table_Name => "Gnatname.Preprocessor_Switches");
125 -- Table to store the preprocessor switches to be used in the call
126 -- to the compiler.
128 type Source is record
129 File_Name : Name_Id;
130 Unit_Name : Name_Id;
131 Index : Int := 0;
132 Spec : Boolean;
133 end record;
135 package Processed_Directories is new Table.Table
136 (Table_Component_Type => String_Access,
137 Table_Index_Type => Natural,
138 Table_Low_Bound => 0,
139 Table_Initial => 10,
140 Table_Increment => 100,
141 Table_Name => "Prj.Makr.Processed_Directories");
142 -- The list of already processed directories for each section, to avoid
143 -- processing several times the same directory in the same section.
145 package Sources is new Table.Table
146 (Table_Component_Type => Source,
147 Table_Index_Type => Natural,
148 Table_Low_Bound => 0,
149 Table_Initial => 10,
150 Table_Increment => 100,
151 Table_Name => "Gnatname.Sources");
152 -- The list of Ada sources found, with their unit name and kind, to be put
153 -- in the pragmas Source_File_Name in the configuration pragmas file.
155 procedure Output_Version;
156 -- Print name and version
158 procedure Usage;
159 -- Print usage
161 procedure Scan_Args;
162 -- Scan the command line arguments
164 procedure Add_Source_Directory (S : String);
165 -- Add S in the Source_Directories table
167 procedure Get_Directories (From_File : String);
168 -- Read a source directory text file
170 procedure Write_Eol;
171 -- Output an empty line
173 procedure Write_A_String (S : String);
174 -- Write a String to Output_FD
176 procedure Initialize
177 (File_Path : String;
178 Preproc_Switches : Argument_List);
179 -- Start the creation of a configuration pragmas file
181 -- File_Path is the name of the configuration pragmas file to create
183 -- Preproc_Switches is a list of switches to be used when invoking the
184 -- compiler to get the name and kind of unit of a source file.
186 type Regexp_List is array (Positive range <>) of Regexp;
188 procedure Process
189 (Directories : Argument_List;
190 Name_Patterns : Regexp_List;
191 Excluded_Patterns : Regexp_List;
192 Foreign_Patterns : Regexp_List);
193 -- Look for source files in the specified directories, with the specified
194 -- patterns.
196 -- Directories is the list of source directories where to look for sources.
198 -- Name_Patterns is a potentially empty list of file name patterns to check
199 -- for Ada Sources.
201 -- Excluded_Patterns is a potentially empty list of file name patterns that
202 -- should not be checked for Ada or non Ada sources.
204 -- Foreign_Patterns is a potentially empty list of file name patterns to
205 -- check for non Ada sources.
207 -- At least one of Name_Patterns and Foreign_Patterns is not empty
209 procedure Finalize;
210 -- Write the configuration pragmas file indicated in a call to procedure
211 -- Initialize, after one or several calls to procedure Process.
213 --------------------------
214 -- Add_Source_Directory --
215 --------------------------
217 procedure Add_Source_Directory (S : String) is
218 begin
219 Patterns.Append
220 (Arguments.Table (Arguments.Last).Directories, new String'(S));
221 end Add_Source_Directory;
223 ---------
224 -- Dup --
225 ---------
227 function Dup (Fd : File_Descriptor) return File_Descriptor is
228 begin
229 return File_Descriptor (System.CRTL.dup (Integer (Fd)));
230 end Dup;
232 ----------
233 -- Dup2 --
234 ----------
236 procedure Dup2 (Old_Fd, New_Fd : File_Descriptor) is
237 Fd : Integer;
238 pragma Warnings (Off, Fd);
239 begin
240 Fd := System.CRTL.dup2 (Integer (Old_Fd), Integer (New_Fd));
241 end Dup2;
243 ---------------------
244 -- Get_Directories --
245 ---------------------
247 procedure Get_Directories (From_File : String) is
248 File : Ada.Text_IO.File_Type;
249 Line : String (1 .. 2_000);
250 Last : Natural;
252 begin
253 Open (File, In_File, From_File);
255 while not End_Of_File (File) loop
256 Get_Line (File, Line, Last);
258 if Last /= 0 then
259 Add_Source_Directory (Line (1 .. Last));
260 end if;
261 end loop;
263 Close (File);
265 exception
266 when Name_Error =>
267 Fail ("cannot open source directory file """ & From_File & '"');
268 end Get_Directories;
270 --------------
271 -- Finalize --
272 --------------
274 procedure Finalize is
275 Discard : Boolean;
276 pragma Warnings (Off, Discard);
278 begin
279 -- Delete the file if it already exists
281 Delete_File
282 (Path_Name (Directory_Last + 1 .. Path_Last),
283 Success => Discard);
285 -- Create a new one
287 if Opt.Verbose_Mode then
288 Output.Write_Str ("Creating new file """);
289 Output.Write_Str (Path_Name (Directory_Last + 1 .. Path_Last));
290 Output.Write_Line ("""");
291 end if;
293 Output_FD := Create_New_File
294 (Path_Name (Directory_Last + 1 .. Path_Last),
295 Fmode => Text);
297 -- Fails if file cannot be created
299 if Output_FD = Invalid_FD then
300 Fail_Program
301 ("cannot create new """ & Path_Name (1 .. Path_Last) & """");
302 end if;
304 -- For each Ada source, write a pragma Source_File_Name to the
305 -- configuration pragmas file.
307 for Index in 1 .. Sources.Last loop
308 if Sources.Table (Index).Unit_Name /= No_Name then
309 Write_A_String ("pragma Source_File_Name");
310 Write_Eol;
311 Write_A_String (" (");
312 Write_A_String
313 (Get_Name_String (Sources.Table (Index).Unit_Name));
314 Write_A_String (",");
315 Write_Eol;
317 if Sources.Table (Index).Spec then
318 Write_A_String (" Spec_File_Name => """);
320 else
321 Write_A_String (" Body_File_Name => """);
322 end if;
324 Write_A_String
325 (Get_Name_String (Sources.Table (Index).File_Name));
327 Write_A_String ("""");
329 if Sources.Table (Index).Index /= 0 then
330 Write_A_String (", Index =>");
331 Write_A_String (Sources.Table (Index).Index'Img);
332 end if;
334 Write_A_String (");");
335 Write_Eol;
336 end if;
337 end loop;
339 Close (Output_FD);
340 end Finalize;
342 ----------------
343 -- Initialize --
344 ----------------
346 procedure Initialize
347 (File_Path : String;
348 Preproc_Switches : Argument_List)
350 begin
351 Sources.Set_Last (0);
353 -- Initialize the compiler switches
355 Args := new Argument_List (1 .. Preproc_Switches'Length + 6);
356 Args (1) := new String'("-c");
357 Args (2) := new String'("-gnats");
358 Args (3) := new String'("-gnatu");
359 Args (4 .. 3 + Preproc_Switches'Length) := Preproc_Switches;
360 Args (4 + Preproc_Switches'Length) := new String'("-x");
361 Args (5 + Preproc_Switches'Length) := new String'("ada");
363 -- Get the path and file names
365 Path_Name := new
366 String (1 .. File_Path'Length);
367 Path_Last := File_Path'Length;
369 if File_Names_Case_Sensitive then
370 Path_Name (1 .. Path_Last) := File_Path;
371 else
372 Path_Name (1 .. Path_Last) := To_Lower (File_Path);
373 end if;
375 -- Get the end of directory information, if any
377 for Index in reverse 1 .. Path_Last loop
378 if Path_Name (Index) = Directory_Separator then
379 Directory_Last := Index;
380 exit;
381 end if;
382 end loop;
384 -- Change the current directory to the directory of the project file,
385 -- if any directory information is specified.
387 if Directory_Last /= 0 then
388 begin
389 Change_Dir (Path_Name (1 .. Directory_Last));
390 exception
391 when Directory_Error =>
392 Fail_Program
393 ("unknown directory """
394 & Path_Name (1 .. Directory_Last)
395 & """");
396 end;
397 end if;
398 end Initialize;
400 -------------
401 -- Process --
402 -------------
404 procedure Process
405 (Directories : Argument_List;
406 Name_Patterns : Regexp_List;
407 Excluded_Patterns : Regexp_List;
408 Foreign_Patterns : Regexp_List)
410 procedure Process_Directory (Dir_Name : String);
411 -- Look for Ada and foreign sources in a directory, according to the
412 -- patterns.
414 -----------------------
415 -- Process_Directory --
416 -----------------------
418 procedure Process_Directory (Dir_Name : String) is
419 Matched : Matched_Type := False;
420 Str : String (1 .. 2_000);
421 Canon : String (1 .. 2_000);
422 Last : Natural;
423 Dir : Dir_Type;
424 Do_Process : Boolean := True;
426 Temp_File_Name : String_Access := null;
427 Save_Last_Source_Index : Natural := 0;
428 File_Name_Id : Name_Id := No_Name;
430 Current_Source : Source;
432 begin
433 -- Avoid processing the same directory more than once
435 for Index in 1 .. Processed_Directories.Last loop
436 if Processed_Directories.Table (Index).all = Dir_Name then
437 Do_Process := False;
438 exit;
439 end if;
440 end loop;
442 if Do_Process then
443 if Opt.Verbose_Mode then
444 Output.Write_Str ("Processing directory """);
445 Output.Write_Str (Dir_Name);
446 Output.Write_Line ("""");
447 end if;
449 Processed_Directories. Increment_Last;
450 Processed_Directories.Table (Processed_Directories.Last) :=
451 new String'(Dir_Name);
453 -- Get the source file names from the directory. Fails if the
454 -- directory does not exist.
456 begin
457 Open (Dir, Dir_Name);
458 exception
459 when Directory_Error =>
460 Fail_Program ("cannot open directory """ & Dir_Name & """");
461 end;
463 -- Process each regular file in the directory
465 File_Loop : loop
466 Read (Dir, Str, Last);
467 exit File_Loop when Last = 0;
469 -- Copy the file name and put it in canonical case to match
470 -- against the patterns that have themselves already been put
471 -- in canonical case.
473 Canon (1 .. Last) := Str (1 .. Last);
474 Canonical_Case_File_Name (Canon (1 .. Last));
476 if Is_Regular_File
477 (Dir_Name & Directory_Separator & Str (1 .. Last))
478 then
479 Matched := True;
481 Name_Len := Last;
482 Name_Buffer (1 .. Name_Len) := Str (1 .. Last);
483 File_Name_Id := Name_Find;
485 -- First, check if the file name matches at least one of
486 -- the excluded expressions;
488 for Index in Excluded_Patterns'Range loop
490 Match (Canon (1 .. Last), Excluded_Patterns (Index))
491 then
492 Matched := Excluded;
493 exit;
494 end if;
495 end loop;
497 -- If it does not match any of the excluded expressions,
498 -- check if the file name matches at least one of the
499 -- regular expressions.
501 if Matched = True then
502 Matched := False;
504 for Index in Name_Patterns'Range loop
506 Match
507 (Canon (1 .. Last), Name_Patterns (Index))
508 then
509 Matched := True;
510 exit;
511 end if;
512 end loop;
513 end if;
515 if Very_Verbose
516 or else (Matched = True and then Opt.Verbose_Mode)
517 then
518 Output.Write_Str (" Checking """);
519 Output.Write_Str (Str (1 .. Last));
520 Output.Write_Line (""": ");
521 end if;
523 -- If the file name matches one of the regular expressions,
524 -- parse it to get its unit name.
526 if Matched = True then
527 declare
528 FD : File_Descriptor;
529 Success : Boolean;
530 Saved_Output : File_Descriptor;
531 Saved_Error : File_Descriptor;
532 Tmp_File : Path_Name_Type;
534 begin
535 -- If we don't have the path of the compiler yet,
536 -- get it now. The compiler name may have a prefix,
537 -- so we get the potentially prefixed name.
539 if Gcc_Path = null then
540 declare
541 Prefix_Gcc : String_Access :=
542 Program_Name (Gcc, "gnatname");
543 begin
544 Gcc_Path :=
545 Locate_Exec_On_Path (Prefix_Gcc.all);
546 Free (Prefix_Gcc);
547 end;
549 if Gcc_Path = null then
550 Fail_Program ("could not locate " & Gcc);
551 end if;
552 end if;
554 -- Create the temporary file
556 Tempdir.Create_Temp_File (FD, Tmp_File);
558 if FD = Invalid_FD then
559 Fail_Program
560 ("could not create temporary file");
562 else
563 Temp_File_Name :=
564 new String'(Get_Name_String (Tmp_File));
565 end if;
567 Args (Args'Last) :=
568 new String'
569 (Dir_Name & Directory_Separator & Str (1 .. Last));
571 -- Save the standard output and error
573 Saved_Output := Dup (Standout);
574 Saved_Error := Dup (Standerr);
576 -- Set standard output and error to the temporary file
578 Dup2 (FD, Standout);
579 Dup2 (FD, Standerr);
581 -- And spawn the compiler
583 Spawn (Gcc_Path.all, Args.all, Success);
585 -- Restore the standard output and error
587 Dup2 (Saved_Output, Standout);
588 Dup2 (Saved_Error, Standerr);
590 -- Close the temporary file
592 Close (FD);
594 -- And close the saved standard output and error to
595 -- avoid too many file descriptors.
597 Close (Saved_Output);
598 Close (Saved_Error);
600 -- Now that standard output is restored, check if
601 -- the compiler ran correctly.
603 -- Read the lines of the temporary file:
604 -- they should contain the kind and name of the unit.
606 declare
607 File : Ada.Text_IO.File_Type;
608 Text_Line : String (1 .. 1_000);
609 Text_Last : Natural;
611 begin
612 begin
613 Open (File, In_File, Temp_File_Name.all);
615 exception
616 when others =>
617 Fail_Program
618 ("could not read temporary file " &
619 Temp_File_Name.all);
620 end;
622 Save_Last_Source_Index := Sources.Last;
624 if End_Of_File (File) then
625 if Opt.Verbose_Mode then
626 if not Success then
627 Output.Write_Str (" (process died) ");
628 end if;
629 end if;
631 else
632 Line_Loop : while not End_Of_File (File) loop
633 Get_Line (File, Text_Line, Text_Last);
635 -- Find the first closing parenthesis
637 Char_Loop : for J in 1 .. Text_Last loop
638 if Text_Line (J) = ')' then
639 if J >= 13 and then
640 Text_Line (1 .. 4) = "Unit"
641 then
642 -- Add entry to Sources table
644 Name_Len := J - 12;
645 Name_Buffer (1 .. Name_Len) :=
646 Text_Line (6 .. J - 7);
647 Current_Source :=
648 (Unit_Name => Name_Find,
649 File_Name => File_Name_Id,
650 Index => 0,
651 Spec => Text_Line (J - 5 .. J) =
652 "(spec)");
654 Sources.Append (Current_Source);
655 end if;
657 exit Char_Loop;
658 end if;
659 end loop Char_Loop;
660 end loop Line_Loop;
661 end if;
663 if Save_Last_Source_Index = Sources.Last then
664 if Opt.Verbose_Mode then
665 Output.Write_Line (" not a unit");
666 end if;
668 else
669 if Sources.Last >
670 Save_Last_Source_Index + 1
671 then
672 for Index in Save_Last_Source_Index + 1 ..
673 Sources.Last
674 loop
675 Sources.Table (Index).Index :=
676 Int (Index - Save_Last_Source_Index);
677 end loop;
678 end if;
680 for Index in Save_Last_Source_Index + 1 ..
681 Sources.Last
682 loop
683 Current_Source := Sources.Table (Index);
685 if Opt.Verbose_Mode then
686 if Current_Source.Spec then
687 Output.Write_Str (" spec of ");
689 else
690 Output.Write_Str (" body of ");
691 end if;
693 Output.Write_Line
694 (Get_Name_String
695 (Current_Source.Unit_Name));
696 end if;
697 end loop;
698 end if;
700 Close (File);
702 Delete_File (Temp_File_Name.all, Success);
703 end;
704 end;
706 -- File name matches none of the regular expressions
708 else
709 -- If file is not excluded, see if this is foreign source
711 if Matched /= Excluded then
712 for Index in Foreign_Patterns'Range loop
713 if Match (Canon (1 .. Last),
714 Foreign_Patterns (Index))
715 then
716 Matched := True;
717 exit;
718 end if;
719 end loop;
720 end if;
722 if Very_Verbose then
723 case Matched is
724 when False =>
725 Output.Write_Line ("no match");
727 when Excluded =>
728 Output.Write_Line ("excluded");
730 when True =>
731 Output.Write_Line ("foreign source");
732 end case;
733 end if;
735 if Matched = True then
737 -- Add source file name without unit name
739 Name_Len := 0;
740 Add_Str_To_Name_Buffer (Canon (1 .. Last));
741 Sources.Append
742 ((File_Name => Name_Find,
743 Unit_Name => No_Name,
744 Index => 0,
745 Spec => False));
746 end if;
747 end if;
748 end if;
749 end loop File_Loop;
751 Close (Dir);
752 end if;
754 end Process_Directory;
756 -- Start of processing for Process
758 begin
759 Processed_Directories.Set_Last (0);
761 -- Process each directory
763 for Index in Directories'Range loop
764 Process_Directory (Directories (Index).all);
765 end loop;
766 end Process;
768 --------------------
769 -- Output_Version --
770 --------------------
772 procedure Output_Version is
773 begin
774 if not Version_Output then
775 Version_Output := True;
776 Output.Write_Eol;
777 Display_Version ("GNATNAME", "2001");
778 end if;
779 end Output_Version;
781 ---------------
782 -- Scan_Args --
783 ---------------
785 procedure Scan_Args is
787 procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
789 Project_File_Name_Expected : Boolean;
791 Pragmas_File_Expected : Boolean;
793 Directory_Expected : Boolean;
795 Dir_File_Name_Expected : Boolean;
797 Foreign_Pattern_Expected : Boolean;
799 Excluded_Pattern_Expected : Boolean;
801 procedure Check_Regular_Expression (S : String);
802 -- Compile string S into a Regexp, fail if any error
804 -----------------------------
805 -- Check_Regular_Expression--
806 -----------------------------
808 procedure Check_Regular_Expression (S : String) is
809 Dummy : Regexp;
810 pragma Warnings (Off, Dummy);
811 begin
812 Dummy := Compile (S, Glob => True);
813 exception
814 when Error_In_Regexp =>
815 Fail ("invalid regular expression """ & S & """");
816 end Check_Regular_Expression;
818 -- Start of processing for Scan_Args
820 begin
821 -- First check for --version or --help
823 Check_Version_And_Help ("GNATNAME", "2001");
825 -- Now scan the other switches
827 Project_File_Name_Expected := False;
828 Pragmas_File_Expected := False;
829 Directory_Expected := False;
830 Dir_File_Name_Expected := False;
831 Foreign_Pattern_Expected := False;
832 Excluded_Pattern_Expected := False;
834 for Next_Arg in 1 .. Argument_Count loop
835 declare
836 Next_Argv : constant String := Argument (Next_Arg);
837 Arg : String (1 .. Next_Argv'Length) := Next_Argv;
839 begin
840 if Arg'Length > 0 then
842 -- -P xxx
844 if Project_File_Name_Expected then
845 if Arg (1) = '-' then
846 Fail ("project file name missing");
848 else
849 File_Set := True;
850 File_Path := new String'(Arg);
851 Project_File_Name_Expected := False;
852 end if;
854 -- -c file
856 elsif Pragmas_File_Expected then
857 File_Set := True;
858 File_Path := new String'(Arg);
859 Pragmas_File_Expected := False;
861 -- -d xxx
863 elsif Directory_Expected then
864 Add_Source_Directory (Arg);
865 Directory_Expected := False;
867 -- -D xxx
869 elsif Dir_File_Name_Expected then
870 Get_Directories (Arg);
871 Dir_File_Name_Expected := False;
873 -- -f xxx
875 elsif Foreign_Pattern_Expected then
876 Patterns.Append
877 (Arguments.Table (Arguments.Last).Foreign_Patterns,
878 new String'(Arg));
879 Check_Regular_Expression (Arg);
880 Foreign_Pattern_Expected := False;
882 -- -x xxx
884 elsif Excluded_Pattern_Expected then
885 Patterns.Append
886 (Arguments.Table (Arguments.Last).Excluded_Patterns,
887 new String'(Arg));
888 Check_Regular_Expression (Arg);
889 Excluded_Pattern_Expected := False;
891 -- There must be at least one Ada pattern or one foreign
892 -- pattern for the previous section.
894 -- --and
896 elsif Arg = "--and" then
898 if Patterns.Last
899 (Arguments.Table (Arguments.Last).Name_Patterns) = 0
900 and then
901 Patterns.Last
902 (Arguments.Table (Arguments.Last).Foreign_Patterns) = 0
903 then
904 Try_Help;
905 return;
906 end if;
908 -- If no directory were specified for the previous section,
909 -- then the directory is the project directory.
911 if Patterns.Last
912 (Arguments.Table (Arguments.Last).Directories) = 0
913 then
914 Patterns.Append
915 (Arguments.Table (Arguments.Last).Directories,
916 new String'("."));
917 end if;
919 -- Add and initialize another component to Arguments table
921 declare
922 New_Arguments : Argument_Data;
923 pragma Warnings (Off, New_Arguments);
924 -- Declaring this defaulted initialized object ensures
925 -- that the new allocated component of table Arguments
926 -- is correctly initialized.
928 -- This is VERY ugly, Table should never be used with
929 -- data requiring default initialization. We should
930 -- find a way to avoid violating this rule ???
932 begin
933 Arguments.Append (New_Arguments);
934 end;
936 Patterns.Init
937 (Arguments.Table (Arguments.Last).Directories);
938 Patterns.Set_Last
939 (Arguments.Table (Arguments.Last).Directories, 0);
940 Patterns.Init
941 (Arguments.Table (Arguments.Last).Name_Patterns);
942 Patterns.Set_Last
943 (Arguments.Table (Arguments.Last).Name_Patterns, 0);
944 Patterns.Init
945 (Arguments.Table (Arguments.Last).Excluded_Patterns);
946 Patterns.Set_Last
947 (Arguments.Table (Arguments.Last).Excluded_Patterns, 0);
948 Patterns.Init
949 (Arguments.Table (Arguments.Last).Foreign_Patterns);
950 Patterns.Set_Last
951 (Arguments.Table (Arguments.Last).Foreign_Patterns, 0);
953 -- Subdirectory switch
955 elsif Arg'Length > Subdirs_Switch'Length
956 and then Arg (1 .. Subdirs_Switch'Length) = Subdirs_Switch
957 then
958 null;
959 -- Subdirs are only used in gprname
961 -- --no-backup
963 elsif Arg = "--no-backup" then
964 Opt.No_Backup := True;
966 -- -c
968 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-c" then
969 if File_Set then
970 Fail ("only one -P or -c switch may be specified");
971 end if;
973 if Arg'Length = 2 then
974 Pragmas_File_Expected := True;
976 if Next_Arg = Argument_Count then
977 Fail ("configuration pragmas file name missing");
978 end if;
980 else
981 File_Set := True;
982 File_Path := new String'(Arg (3 .. Arg'Last));
983 end if;
985 -- -d
987 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-d" then
988 if Arg'Length = 2 then
989 Directory_Expected := True;
991 if Next_Arg = Argument_Count then
992 Fail ("directory name missing");
993 end if;
995 else
996 Add_Source_Directory (Arg (3 .. Arg'Last));
997 end if;
999 -- -D
1001 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-D" then
1002 if Arg'Length = 2 then
1003 Dir_File_Name_Expected := True;
1005 if Next_Arg = Argument_Count then
1006 Fail ("directory list file name missing");
1007 end if;
1009 else
1010 Get_Directories (Arg (3 .. Arg'Last));
1011 end if;
1013 -- -eL
1015 elsif Arg = "-eL" then
1016 Opt.Follow_Links_For_Files := True;
1017 Opt.Follow_Links_For_Dirs := True;
1019 -- -f
1021 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-f" then
1022 if Arg'Length = 2 then
1023 Foreign_Pattern_Expected := True;
1025 if Next_Arg = Argument_Count then
1026 Fail ("foreign pattern missing");
1027 end if;
1029 else
1030 Patterns.Append
1031 (Arguments.Table (Arguments.Last).Foreign_Patterns,
1032 new String'(Arg (3 .. Arg'Last)));
1033 Check_Regular_Expression (Arg (3 .. Arg'Last));
1034 end if;
1036 -- -gnatep or -gnateD
1038 elsif Arg'Length > 7 and then
1039 (Arg (1 .. 7) = "-gnatep" or else Arg (1 .. 7) = "-gnateD")
1040 then
1041 Preprocessor_Switches.Append (new String'(Arg));
1043 -- -h
1045 elsif Arg = "-h" then
1046 Usage_Needed := True;
1048 -- -P
1050 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-P" then
1051 if File_Set then
1052 Fail ("only one -c or -P switch may be specified");
1053 end if;
1055 if Arg'Length = 2 then
1056 if Next_Arg = Argument_Count then
1057 Fail ("project file name missing");
1059 else
1060 Project_File_Name_Expected := True;
1061 end if;
1063 else
1064 File_Set := True;
1065 File_Path := new String'(Arg (3 .. Arg'Last));
1066 end if;
1068 Create_Project := True;
1070 -- -v
1072 elsif Arg = "-v" then
1073 if Opt.Verbose_Mode then
1074 Very_Verbose := True;
1075 else
1076 Opt.Verbose_Mode := True;
1077 end if;
1079 -- -x
1081 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-x" then
1082 if Arg'Length = 2 then
1083 Excluded_Pattern_Expected := True;
1085 if Next_Arg = Argument_Count then
1086 Fail ("excluded pattern missing");
1087 end if;
1089 else
1090 Patterns.Append
1091 (Arguments.Table (Arguments.Last).Excluded_Patterns,
1092 new String'(Arg (3 .. Arg'Last)));
1093 Check_Regular_Expression (Arg (3 .. Arg'Last));
1094 end if;
1096 -- Junk switch starting with minus
1098 elsif Arg (1) = '-' then
1099 Fail ("wrong switch: " & Arg);
1101 -- Not a recognized switch, assume file name
1103 else
1104 Canonical_Case_File_Name (Arg);
1105 Patterns.Append
1106 (Arguments.Table (Arguments.Last).Name_Patterns,
1107 new String'(Arg));
1108 Check_Regular_Expression (Arg);
1109 end if;
1110 end if;
1111 end;
1112 end loop;
1113 end Scan_Args;
1115 -----------
1116 -- Usage --
1117 -----------
1119 procedure Usage is
1120 begin
1121 if not Usage_Output then
1122 Usage_Needed := False;
1123 Usage_Output := True;
1124 Output.Write_Str ("Usage: ");
1125 Osint.Write_Program_Name;
1126 Output.Write_Line (" [switches] naming-pattern [naming-patterns]");
1127 Output.Write_Line
1128 (" {--and [switches] naming-pattern [naming-patterns]}");
1129 Output.Write_Eol;
1130 Output.Write_Line ("switches:");
1132 Display_Usage_Version_And_Help;
1134 Output.Write_Line
1135 (" --subdirs=dir real obj/lib/exec dirs are subdirs");
1136 Output.Write_Line
1137 (" --no-backup do not create backup of project file");
1138 Output.Write_Eol;
1140 Output.Write_Line (" --and use different patterns");
1141 Output.Write_Eol;
1143 Output.Write_Line
1144 (" -cfile create configuration pragmas file");
1145 Output.Write_Line (" -ddir use dir as one of the source " &
1146 "directories");
1147 Output.Write_Line (" -Dfile get source directories from file");
1148 Output.Write_Line
1149 (" -eL follow symbolic links when processing " &
1150 "project files");
1151 Output.Write_Line (" -fpat foreign pattern");
1152 Output.Write_Line
1153 (" -gnateDsym=v preprocess with symbol definition");
1154 Output.Write_Line (" -gnatep=data preprocess files with data file");
1155 Output.Write_Line (" -h output this help message");
1156 Output.Write_Line
1157 (" -Pproj update or create project file proj");
1158 Output.Write_Line (" -v verbose output");
1159 Output.Write_Line (" -v -v very verbose output");
1160 Output.Write_Line (" -xpat exclude pattern pat");
1161 end if;
1162 end Usage;
1164 ---------------
1165 -- Write_Eol --
1166 ---------------
1168 procedure Write_Eol is
1169 begin
1170 Write_A_String ((1 => ASCII.LF));
1171 end Write_Eol;
1173 --------------------
1174 -- Write_A_String --
1175 --------------------
1177 procedure Write_A_String (S : String) is
1178 Str : String (1 .. S'Length);
1180 begin
1181 if S'Length > 0 then
1182 Str := S;
1184 if Write (Output_FD, Str (1)'Address, Str'Length) /= Str'Length then
1185 Fail_Program ("disk full");
1186 end if;
1187 end if;
1188 end Write_A_String;
1190 -- Start of processing for Gnatname
1192 begin
1193 -- Add the directory where gnatname is invoked in front of the
1194 -- path, if gnatname is invoked with directory information.
1196 declare
1197 Command : constant String := Command_Name;
1199 begin
1200 for Index in reverse Command'Range loop
1201 if Command (Index) = Directory_Separator then
1202 declare
1203 Absolute_Dir : constant String :=
1204 Normalize_Pathname
1205 (Command (Command'First .. Index));
1207 PATH : constant String :=
1208 Absolute_Dir &
1209 Path_Separator &
1210 Getenv ("PATH").all;
1212 begin
1213 Setenv ("PATH", PATH);
1214 end;
1216 exit;
1217 end if;
1218 end loop;
1219 end;
1221 -- Initialize tables
1223 Arguments.Set_Last (0);
1224 declare
1225 New_Arguments : Argument_Data;
1226 pragma Warnings (Off, New_Arguments);
1227 -- Declaring this defaulted initialized object ensures that the new
1228 -- allocated component of table Arguments is correctly initialized.
1229 begin
1230 Arguments.Append (New_Arguments);
1231 end;
1233 Patterns.Init (Arguments.Table (1).Directories);
1234 Patterns.Set_Last (Arguments.Table (1).Directories, 0);
1235 Patterns.Init (Arguments.Table (1).Name_Patterns);
1236 Patterns.Set_Last (Arguments.Table (1).Name_Patterns, 0);
1237 Patterns.Init (Arguments.Table (1).Excluded_Patterns);
1238 Patterns.Set_Last (Arguments.Table (1).Excluded_Patterns, 0);
1239 Patterns.Init (Arguments.Table (1).Foreign_Patterns);
1240 Patterns.Set_Last (Arguments.Table (1).Foreign_Patterns, 0);
1242 Preprocessor_Switches.Set_Last (0);
1244 -- Get the arguments
1246 Scan_Args;
1248 if Create_Project then
1249 declare
1250 Gprname_Path : constant String_Access :=
1251 Locate_Exec_On_Path ("gprname");
1252 Arg_Len : Natural := Argument_Count;
1253 Pos : Natural := 0;
1254 Target : String_Access := null;
1255 Success : Boolean := False;
1256 begin
1257 if Gprname_Path = null then
1258 Fail_Program
1259 ("project files are no longer supported by gnatname;" &
1260 " use gprname instead");
1261 end if;
1263 Find_Program_Name;
1265 if Name_Len > 9
1266 and then Name_Buffer (Name_Len - 7 .. Name_Len) = "gnatname"
1267 then
1268 Target := new String'(Name_Buffer (1 .. Name_Len - 9));
1269 Arg_Len := Arg_Len + 1;
1270 end if;
1272 declare
1273 Args : Argument_List (1 .. Arg_Len);
1274 begin
1275 if Target /= null then
1276 Args (1) := new String'("--target=" & Target.all);
1277 Pos := 1;
1278 end if;
1280 for J in 1 .. Argument_Count loop
1281 Pos := Pos + 1;
1282 Args (Pos) := new String'(Argument (J));
1283 end loop;
1285 Spawn (Gprname_Path.all, Args, Success);
1287 if Success then
1288 Exit_Program (E_Success);
1289 else
1290 Exit_Program (E_Errors);
1291 end if;
1292 end;
1293 end;
1294 end if;
1296 if Opt.Verbose_Mode then
1297 Output_Version;
1298 end if;
1300 if Usage_Needed then
1301 Usage;
1302 end if;
1304 -- If no Ada or foreign pattern was specified, print the usage and return
1306 if Patterns.Last (Arguments.Table (Arguments.Last).Name_Patterns) = 0
1307 and then
1308 Patterns.Last (Arguments.Table (Arguments.Last).Foreign_Patterns) = 0
1309 then
1310 if Argument_Count = 0 then
1311 Usage;
1312 elsif not Usage_Output then
1313 Try_Help;
1314 end if;
1316 return;
1317 end if;
1319 -- If no source directory was specified, use the current directory as the
1320 -- unique directory. Note that if a file was specified with directory
1321 -- information, the current directory is the directory of the specified
1322 -- file.
1324 if Patterns.Last (Arguments.Table (Arguments.Last).Directories) = 0 then
1325 Patterns.Append
1326 (Arguments.Table (Arguments.Last).Directories, new String'("."));
1327 end if;
1329 -- Initialize
1331 declare
1332 Prep_Switches : Argument_List
1333 (1 .. Integer (Preprocessor_Switches.Last));
1335 begin
1336 for Index in Prep_Switches'Range loop
1337 Prep_Switches (Index) := Preprocessor_Switches.Table (Index);
1338 end loop;
1340 Initialize
1341 (File_Path => File_Path.all,
1342 Preproc_Switches => Prep_Switches);
1343 end;
1345 -- Process each section successively
1347 for J in 1 .. Arguments.Last loop
1348 declare
1349 Directories : Argument_List
1350 (1 .. Integer
1351 (Patterns.Last (Arguments.Table (J).Directories)));
1352 Name_Patterns : Regexp_List
1353 (1 .. Integer
1354 (Patterns.Last (Arguments.Table (J).Name_Patterns)));
1355 Excl_Patterns : Regexp_List
1356 (1 .. Integer
1357 (Patterns.Last (Arguments.Table (J).Excluded_Patterns)));
1358 Frgn_Patterns : Regexp_List
1359 (1 .. Integer
1360 (Patterns.Last (Arguments.Table (J).Foreign_Patterns)));
1362 begin
1363 -- Build the Directories and Patterns arguments
1365 for Index in Directories'Range loop
1366 Directories (Index) :=
1367 Arguments.Table (J).Directories.Table (Index);
1368 end loop;
1370 for Index in Name_Patterns'Range loop
1371 Name_Patterns (Index) :=
1372 Compile
1373 (Arguments.Table (J).Name_Patterns.Table (Index).all,
1374 Glob => True);
1375 end loop;
1377 for Index in Excl_Patterns'Range loop
1378 Excl_Patterns (Index) :=
1379 Compile
1380 (Arguments.Table (J).Excluded_Patterns.Table (Index).all,
1381 Glob => True);
1382 end loop;
1384 for Index in Frgn_Patterns'Range loop
1385 Frgn_Patterns (Index) :=
1386 Compile
1387 (Arguments.Table (J).Foreign_Patterns.Table (Index).all,
1388 Glob => True);
1389 end loop;
1391 -- Call Prj.Makr.Process where the real work is done
1393 Process
1394 (Directories => Directories,
1395 Name_Patterns => Name_Patterns,
1396 Excluded_Patterns => Excl_Patterns,
1397 Foreign_Patterns => Frgn_Patterns);
1398 end;
1399 end loop;
1401 -- Finalize
1403 Finalize;
1405 if Opt.Verbose_Mode then
1406 Output.Write_Eol;
1407 end if;
1408 end Gnatname;