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-2018, 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------------------------------------------------------------------------------ 25 26with Ada.Characters.Handling; use Ada.Characters.Handling; 27with Ada.Command_Line; use Ada.Command_Line; 28with Ada.Text_IO; use Ada.Text_IO; 29 30with GNAT.Command_Line; use GNAT.Command_Line; 31with GNAT.Directory_Operations; use GNAT.Directory_Operations; 32with GNAT.Dynamic_Tables; 33with GNAT.OS_Lib; use GNAT.OS_Lib; 34 35with Make_Util; use Make_Util; 36with Namet; use Namet; 37with Opt; 38with Osint; use Osint; 39with Output; 40with Switch; use Switch; 41with Table; 42with Tempdir; 43with Types; use Types; 44 45with System.CRTL; 46with System.Regexp; use System.Regexp; 47 48procedure Gnatname is 49 50 pragma Warnings (Off); 51 type Matched_Type is (True, False, Excluded); 52 pragma Warnings (On); 53 54 Create_Project : Boolean := False; 55 56 Subdirs_Switch : constant String := "--subdirs="; 57 58 Usage_Output : Boolean := False; 59 -- Set to True when usage is output, to avoid multiple output 60 61 Usage_Needed : Boolean := False; 62 -- Set to True by -h switch 63 64 Version_Output : Boolean := False; 65 -- Set to True when version is output, to avoid multiple output 66 67 Very_Verbose : Boolean := False; 68 -- Set to True with -v -v 69 70 File_Path : String_Access := new String'("gnat.adc"); 71 -- Path name of the file specified by -c or -P switch 72 73 File_Set : Boolean := False; 74 -- Set to True by -c or -P switch. 75 -- Used to detect multiple -c/-P switches. 76 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. 80 81 Path_Name : String_Access; 82 83 Path_Last : Natural; 84 85 Directory_Last : Natural := 0; 86 87 function Dup (Fd : File_Descriptor) return File_Descriptor; 88 89 procedure Dup2 (Old_Fd, New_Fd : File_Descriptor); 90 91 Gcc : constant String := "gcc"; 92 Gcc_Path : String_Access := null; 93 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 101 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; 108 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 117 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. 127 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; 134 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. 144 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. 154 155 procedure Output_Version; 156 -- Print name and version 157 158 procedure Usage; 159 -- Print usage 160 161 procedure Scan_Args; 162 -- Scan the command line arguments 163 164 procedure Add_Source_Directory (S : String); 165 -- Add S in the Source_Directories table 166 167 procedure Get_Directories (From_File : String); 168 -- Read a source directory text file 169 170 procedure Write_Eol; 171 -- Output an empty line 172 173 procedure Write_A_String (S : String); 174 -- Write a String to Output_FD 175 176 procedure Initialize 177 (File_Path : String; 178 Preproc_Switches : Argument_List); 179 -- Start the creation of a configuration pragmas file 180 -- 181 -- File_Path is the name of the configuration pragmas file to create 182 -- 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. 185 186 type Regexp_List is array (Positive range <>) of Regexp; 187 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. 195 -- 196 -- Directories is the list of source directories where to look for sources. 197 -- 198 -- Name_Patterns is a potentially empty list of file name patterns to check 199 -- for Ada Sources. 200 -- 201 -- Excluded_Patterns is a potentially empty list of file name patterns that 202 -- should not be checked for Ada or non Ada sources. 203 -- 204 -- Foreign_Patterns is a potentially empty list of file name patterns to 205 -- check for non Ada sources. 206 -- 207 -- At least one of Name_Patterns and Foreign_Patterns is not empty 208 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. 212 213 -------------------------- 214 -- Add_Source_Directory -- 215 -------------------------- 216 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; 222 223 --------- 224 -- Dup -- 225 --------- 226 227 function Dup (Fd : File_Descriptor) return File_Descriptor is 228 begin 229 return File_Descriptor (System.CRTL.dup (Integer (Fd))); 230 end Dup; 231 232 ---------- 233 -- Dup2 -- 234 ---------- 235 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; 242 243 --------------------- 244 -- Get_Directories -- 245 --------------------- 246 247 procedure Get_Directories (From_File : String) is 248 File : Ada.Text_IO.File_Type; 249 Line : String (1 .. 2_000); 250 Last : Natural; 251 252 begin 253 Open (File, In_File, From_File); 254 255 while not End_Of_File (File) loop 256 Get_Line (File, Line, Last); 257 258 if Last /= 0 then 259 Add_Source_Directory (Line (1 .. Last)); 260 end if; 261 end loop; 262 263 Close (File); 264 265 exception 266 when Name_Error => 267 Fail ("cannot open source directory file """ & From_File & '"'); 268 end Get_Directories; 269 270 -------------- 271 -- Finalize -- 272 -------------- 273 274 procedure Finalize is 275 Discard : Boolean; 276 pragma Warnings (Off, Discard); 277 278 begin 279 -- Delete the file if it already exists 280 281 Delete_File 282 (Path_Name (Directory_Last + 1 .. Path_Last), 283 Success => Discard); 284 285 -- Create a new one 286 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; 292 293 Output_FD := Create_New_File 294 (Path_Name (Directory_Last + 1 .. Path_Last), 295 Fmode => Text); 296 297 -- Fails if file cannot be created 298 299 if Output_FD = Invalid_FD then 300 Fail_Program 301 ("cannot create new """ & Path_Name (1 .. Path_Last) & """"); 302 end if; 303 304 -- For each Ada source, write a pragma Source_File_Name to the 305 -- configuration pragmas file. 306 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; 316 317 if Sources.Table (Index).Spec then 318 Write_A_String (" Spec_File_Name => """); 319 320 else 321 Write_A_String (" Body_File_Name => """); 322 end if; 323 324 Write_A_String 325 (Get_Name_String (Sources.Table (Index).File_Name)); 326 327 Write_A_String (""""); 328 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; 333 334 Write_A_String (");"); 335 Write_Eol; 336 end if; 337 end loop; 338 339 Close (Output_FD); 340 end Finalize; 341 342 ---------------- 343 -- Initialize -- 344 ---------------- 345 346 procedure Initialize 347 (File_Path : String; 348 Preproc_Switches : Argument_List) 349 is 350 begin 351 Sources.Set_Last (0); 352 353 -- Initialize the compiler switches 354 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"); 362 363 -- Get the path and file names 364 365 Path_Name := new 366 String (1 .. File_Path'Length); 367 Path_Last := File_Path'Length; 368 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; 374 375 -- Get the end of directory information, if any 376 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; 383 384 -- Change the current directory to the directory of the project file, 385 -- if any directory information is specified. 386 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; 399 400 ------------- 401 -- Process -- 402 ------------- 403 404 procedure Process 405 (Directories : Argument_List; 406 Name_Patterns : Regexp_List; 407 Excluded_Patterns : Regexp_List; 408 Foreign_Patterns : Regexp_List) 409 is 410 procedure Process_Directory (Dir_Name : String); 411 -- Look for Ada and foreign sources in a directory, according to the 412 -- patterns. 413 414 ----------------------- 415 -- Process_Directory -- 416 ----------------------- 417 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; 425 426 Temp_File_Name : String_Access := null; 427 Save_Last_Source_Index : Natural := 0; 428 File_Name_Id : Name_Id := No_Name; 429 430 Current_Source : Source; 431 432 begin 433 -- Avoid processing the same directory more than once 434 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; 441 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; 448 449 Processed_Directories. Increment_Last; 450 Processed_Directories.Table (Processed_Directories.Last) := 451 new String'(Dir_Name); 452 453 -- Get the source file names from the directory. Fails if the 454 -- directory does not exist. 455 456 begin 457 Open (Dir, Dir_Name); 458 exception 459 when Directory_Error => 460 Fail_Program ("cannot open directory """ & Dir_Name & """"); 461 end; 462 463 -- Process each regular file in the directory 464 465 File_Loop : loop 466 Read (Dir, Str, Last); 467 exit File_Loop when Last = 0; 468 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. 472 473 Canon (1 .. Last) := Str (1 .. Last); 474 Canonical_Case_File_Name (Canon (1 .. Last)); 475 476 if Is_Regular_File 477 (Dir_Name & Directory_Separator & Str (1 .. Last)) 478 then 479 Matched := True; 480 481 Name_Len := Last; 482 Name_Buffer (1 .. Name_Len) := Str (1 .. Last); 483 File_Name_Id := Name_Find; 484 485 -- First, check if the file name matches at least one of 486 -- the excluded expressions; 487 488 for Index in Excluded_Patterns'Range loop 489 if 490 Match (Canon (1 .. Last), Excluded_Patterns (Index)) 491 then 492 Matched := Excluded; 493 exit; 494 end if; 495 end loop; 496 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. 500 501 if Matched = True then 502 Matched := False; 503 504 for Index in Name_Patterns'Range loop 505 if 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; 514 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; 522 523 -- If the file name matches one of the regular expressions, 524 -- parse it to get its unit name. 525 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; 533 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. 538 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; 548 549 if Gcc_Path = null then 550 Fail_Program ("could not locate " & Gcc); 551 end if; 552 end if; 553 554 -- Create the temporary file 555 556 Tempdir.Create_Temp_File (FD, Tmp_File); 557 558 if FD = Invalid_FD then 559 Fail_Program 560 ("could not create temporary file"); 561 562 else 563 Temp_File_Name := 564 new String'(Get_Name_String (Tmp_File)); 565 end if; 566 567 Args (Args'Last) := 568 new String' 569 (Dir_Name & Directory_Separator & Str (1 .. Last)); 570 571 -- Save the standard output and error 572 573 Saved_Output := Dup (Standout); 574 Saved_Error := Dup (Standerr); 575 576 -- Set standard output and error to the temporary file 577 578 Dup2 (FD, Standout); 579 Dup2 (FD, Standerr); 580 581 -- And spawn the compiler 582 583 Spawn (Gcc_Path.all, Args.all, Success); 584 585 -- Restore the standard output and error 586 587 Dup2 (Saved_Output, Standout); 588 Dup2 (Saved_Error, Standerr); 589 590 -- Close the temporary file 591 592 Close (FD); 593 594 -- And close the saved standard output and error to 595 -- avoid too many file descriptors. 596 597 Close (Saved_Output); 598 Close (Saved_Error); 599 600 -- Now that standard output is restored, check if 601 -- the compiler ran correctly. 602 603 -- Read the lines of the temporary file: 604 -- they should contain the kind and name of the unit. 605 606 declare 607 File : Ada.Text_IO.File_Type; 608 Text_Line : String (1 .. 1_000); 609 Text_Last : Natural; 610 611 begin 612 begin 613 Open (File, In_File, Temp_File_Name.all); 614 615 exception 616 when others => 617 Fail_Program 618 ("could not read temporary file " & 619 Temp_File_Name.all); 620 end; 621 622 Save_Last_Source_Index := Sources.Last; 623 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; 630 631 else 632 Line_Loop : while not End_Of_File (File) loop 633 Get_Line (File, Text_Line, Text_Last); 634 635 -- Find the first closing parenthesis 636 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 643 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)"); 653 654 Sources.Append (Current_Source); 655 end if; 656 657 exit Char_Loop; 658 end if; 659 end loop Char_Loop; 660 end loop Line_Loop; 661 end if; 662 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; 667 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; 679 680 for Index in Save_Last_Source_Index + 1 .. 681 Sources.Last 682 loop 683 Current_Source := Sources.Table (Index); 684 685 if Opt.Verbose_Mode then 686 if Current_Source.Spec then 687 Output.Write_Str (" spec of "); 688 689 else 690 Output.Write_Str (" body of "); 691 end if; 692 693 Output.Write_Line 694 (Get_Name_String 695 (Current_Source.Unit_Name)); 696 end if; 697 end loop; 698 end if; 699 700 Close (File); 701 702 Delete_File (Temp_File_Name.all, Success); 703 end; 704 end; 705 706 -- File name matches none of the regular expressions 707 708 else 709 -- If file is not excluded, see if this is foreign source 710 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; 721 722 if Very_Verbose then 723 case Matched is 724 when False => 725 Output.Write_Line ("no match"); 726 727 when Excluded => 728 Output.Write_Line ("excluded"); 729 730 when True => 731 Output.Write_Line ("foreign source"); 732 end case; 733 end if; 734 735 if Matched = True then 736 737 -- Add source file name without unit name 738 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; 750 751 Close (Dir); 752 end if; 753 754 end Process_Directory; 755 756 -- Start of processing for Process 757 758 begin 759 Processed_Directories.Set_Last (0); 760 761 -- Process each directory 762 763 for Index in Directories'Range loop 764 Process_Directory (Directories (Index).all); 765 end loop; 766 end Process; 767 768 -------------------- 769 -- Output_Version -- 770 -------------------- 771 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; 780 781 --------------- 782 -- Scan_Args -- 783 --------------- 784 785 procedure Scan_Args is 786 787 procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage); 788 789 Project_File_Name_Expected : Boolean; 790 791 Pragmas_File_Expected : Boolean; 792 793 Directory_Expected : Boolean; 794 795 Dir_File_Name_Expected : Boolean; 796 797 Foreign_Pattern_Expected : Boolean; 798 799 Excluded_Pattern_Expected : Boolean; 800 801 procedure Check_Regular_Expression (S : String); 802 -- Compile string S into a Regexp, fail if any error 803 804 ----------------------------- 805 -- Check_Regular_Expression-- 806 ----------------------------- 807 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; 817 818 -- Start of processing for Scan_Args 819 820 begin 821 -- First check for --version or --help 822 823 Check_Version_And_Help ("GNATNAME", "2001"); 824 825 -- Now scan the other switches 826 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; 833 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; 838 839 begin 840 if Arg'Length > 0 then 841 842 -- -P xxx 843 844 if Project_File_Name_Expected then 845 if Arg (1) = '-' then 846 Fail ("project file name missing"); 847 848 else 849 File_Set := True; 850 File_Path := new String'(Arg); 851 Project_File_Name_Expected := False; 852 end if; 853 854 -- -c file 855 856 elsif Pragmas_File_Expected then 857 File_Set := True; 858 File_Path := new String'(Arg); 859 Pragmas_File_Expected := False; 860 861 -- -d xxx 862 863 elsif Directory_Expected then 864 Add_Source_Directory (Arg); 865 Directory_Expected := False; 866 867 -- -D xxx 868 869 elsif Dir_File_Name_Expected then 870 Get_Directories (Arg); 871 Dir_File_Name_Expected := False; 872 873 -- -f xxx 874 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; 881 882 -- -x xxx 883 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; 890 891 -- There must be at least one Ada pattern or one foreign 892 -- pattern for the previous section. 893 894 -- --and 895 896 elsif Arg = "--and" then 897 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; 907 908 -- If no directory were specified for the previous section, 909 -- then the directory is the project directory. 910 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; 918 919 -- Add and initialize another component to Arguments table 920 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. 927 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 ??? 931 932 begin 933 Arguments.Append (New_Arguments); 934 end; 935 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); 952 953 -- Subdirectory switch 954 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 960 961 -- --no-backup 962 963 elsif Arg = "--no-backup" then 964 Opt.No_Backup := True; 965 966 -- -c 967 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; 972 973 if Arg'Length = 2 then 974 Pragmas_File_Expected := True; 975 976 if Next_Arg = Argument_Count then 977 Fail ("configuration pragmas file name missing"); 978 end if; 979 980 else 981 File_Set := True; 982 File_Path := new String'(Arg (3 .. Arg'Last)); 983 end if; 984 985 -- -d 986 987 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-d" then 988 if Arg'Length = 2 then 989 Directory_Expected := True; 990 991 if Next_Arg = Argument_Count then 992 Fail ("directory name missing"); 993 end if; 994 995 else 996 Add_Source_Directory (Arg (3 .. Arg'Last)); 997 end if; 998 999 -- -D 1000 1001 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-D" then 1002 if Arg'Length = 2 then 1003 Dir_File_Name_Expected := True; 1004 1005 if Next_Arg = Argument_Count then 1006 Fail ("directory list file name missing"); 1007 end if; 1008 1009 else 1010 Get_Directories (Arg (3 .. Arg'Last)); 1011 end if; 1012 1013 -- -eL 1014 1015 elsif Arg = "-eL" then 1016 Opt.Follow_Links_For_Files := True; 1017 Opt.Follow_Links_For_Dirs := True; 1018 1019 -- -f 1020 1021 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-f" then 1022 if Arg'Length = 2 then 1023 Foreign_Pattern_Expected := True; 1024 1025 if Next_Arg = Argument_Count then 1026 Fail ("foreign pattern missing"); 1027 end if; 1028 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; 1035 1036 -- -gnatep or -gnateD 1037 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)); 1042 1043 -- -h 1044 1045 elsif Arg = "-h" then 1046 Usage_Needed := True; 1047 1048 -- -P 1049 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; 1054 1055 if Arg'Length = 2 then 1056 if Next_Arg = Argument_Count then 1057 Fail ("project file name missing"); 1058 1059 else 1060 Project_File_Name_Expected := True; 1061 end if; 1062 1063 else 1064 File_Set := True; 1065 File_Path := new String'(Arg (3 .. Arg'Last)); 1066 end if; 1067 1068 Create_Project := True; 1069 1070 -- -v 1071 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; 1078 1079 -- -x 1080 1081 elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-x" then 1082 if Arg'Length = 2 then 1083 Excluded_Pattern_Expected := True; 1084 1085 if Next_Arg = Argument_Count then 1086 Fail ("excluded pattern missing"); 1087 end if; 1088 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; 1095 1096 -- Junk switch starting with minus 1097 1098 elsif Arg (1) = '-' then 1099 Fail ("wrong switch: " & Arg); 1100 1101 -- Not a recognized switch, assume file name 1102 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; 1114 1115 ----------- 1116 -- Usage -- 1117 ----------- 1118 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:"); 1131 1132 Display_Usage_Version_And_Help; 1133 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; 1139 1140 Output.Write_Line (" --and use different patterns"); 1141 Output.Write_Eol; 1142 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; 1163 1164 --------------- 1165 -- Write_Eol -- 1166 --------------- 1167 1168 procedure Write_Eol is 1169 begin 1170 Write_A_String ((1 => ASCII.LF)); 1171 end Write_Eol; 1172 1173 -------------------- 1174 -- Write_A_String -- 1175 -------------------- 1176 1177 procedure Write_A_String (S : String) is 1178 Str : String (1 .. S'Length); 1179 1180 begin 1181 if S'Length > 0 then 1182 Str := S; 1183 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; 1189 1190-- Start of processing for Gnatname 1191 1192begin 1193 -- Add the directory where gnatname is invoked in front of the 1194 -- path, if gnatname is invoked with directory information. 1195 1196 declare 1197 Command : constant String := Command_Name; 1198 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)); 1206 1207 PATH : constant String := 1208 Absolute_Dir & 1209 Path_Separator & 1210 Getenv ("PATH").all; 1211 1212 begin 1213 Setenv ("PATH", PATH); 1214 end; 1215 1216 exit; 1217 end if; 1218 end loop; 1219 end; 1220 1221 -- Initialize tables 1222 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; 1232 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); 1241 1242 Preprocessor_Switches.Set_Last (0); 1243 1244 -- Get the arguments 1245 1246 Scan_Args; 1247 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; 1262 1263 Find_Program_Name; 1264 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; 1271 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; 1279 1280 for J in 1 .. Argument_Count loop 1281 Pos := Pos + 1; 1282 Args (Pos) := new String'(Argument (J)); 1283 end loop; 1284 1285 Spawn (Gprname_Path.all, Args, Success); 1286 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; 1295 1296 if Opt.Verbose_Mode then 1297 Output_Version; 1298 end if; 1299 1300 if Usage_Needed then 1301 Usage; 1302 end if; 1303 1304 -- If no Ada or foreign pattern was specified, print the usage and return 1305 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; 1315 1316 return; 1317 end if; 1318 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. 1323 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; 1328 1329 -- Initialize 1330 1331 declare 1332 Prep_Switches : Argument_List 1333 (1 .. Integer (Preprocessor_Switches.Last)); 1334 1335 begin 1336 for Index in Prep_Switches'Range loop 1337 Prep_Switches (Index) := Preprocessor_Switches.Table (Index); 1338 end loop; 1339 1340 Initialize 1341 (File_Path => File_Path.all, 1342 Preproc_Switches => Prep_Switches); 1343 end; 1344 1345 -- Process each section successively 1346 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))); 1361 1362 begin 1363 -- Build the Directories and Patterns arguments 1364 1365 for Index in Directories'Range loop 1366 Directories (Index) := 1367 Arguments.Table (J).Directories.Table (Index); 1368 end loop; 1369 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; 1376 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; 1383 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; 1390 1391 -- Call Prj.Makr.Process where the real work is done 1392 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; 1400 1401 -- Finalize 1402 1403 Finalize; 1404 1405 if Opt.Verbose_Mode then 1406 Output.Write_Eol; 1407 end if; 1408end Gnatname; 1409