File : clean.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                         GNAT COMPILER COMPONENTS                         --
   4 --                                                                          --
   5 --                                C L E A N                                 --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 2003-2015, 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 
  26 with ALI;      use ALI;
  27 with Csets;
  28 with Makeutl;  use Makeutl;
  29 with MLib.Tgt; use MLib.Tgt;
  30 with Namet;    use Namet;
  31 with Opt;      use Opt;
  32 with Osint;    use Osint;
  33 with Osint.M;  use Osint.M;
  34 with Prj;      use Prj;
  35 with Prj.Env;
  36 with Prj.Ext;
  37 with Prj.Pars;
  38 with Prj.Tree; use Prj.Tree;
  39 with Prj.Util; use Prj.Util;
  40 with Sdefault;
  41 with Snames;
  42 with Stringt;
  43 with Switch;   use Switch;
  44 with Table;
  45 with Targparm; use Targparm;
  46 with Types;    use Types;
  47 
  48 with Ada.Command_Line;          use Ada.Command_Line;
  49 
  50 with GNAT.Command_Line;         use GNAT.Command_Line;
  51 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
  52 with GNAT.IO;                   use GNAT.IO;
  53 with GNAT.OS_Lib;               use GNAT.OS_Lib;
  54 
  55 package body Clean is
  56 
  57    Initialized : Boolean := False;
  58    --  Set to True by the first call to Initialize to avoid reinitialization
  59    --  of some packages.
  60 
  61    --  Suffixes of various files
  62 
  63    Assembly_Suffix : constant String := ".s";
  64    ALI_Suffix      : constant String := ".ali";
  65    Tree_Suffix     : constant String := ".adt";
  66    Object_Suffix   : constant String := Get_Target_Object_Suffix.all;
  67    Debug_Suffix    : constant String := ".dg";
  68    Repinfo_Suffix  : constant String := ".rep";
  69    --  Suffix of representation info files
  70 
  71    B_Start : constant String := "b~";
  72    --  Prefix of binder generated file, and number of actual characters used
  73 
  74    Project_Tree : constant Project_Tree_Ref :=
  75      new Project_Tree_Data (Is_Root_Tree => True);
  76    --  The project tree
  77 
  78    Object_Directory_Path : String_Access := null;
  79    --  The path name of the object directory, set with switch -D
  80 
  81    Force_Deletions : Boolean := False;
  82    --  Set to True by switch -f. When True, attempts to delete non writable
  83    --  files will be done.
  84 
  85    Do_Nothing : Boolean := False;
  86    --  Set to True when switch -n is specified. When True, no file is deleted.
  87    --  gnatclean only lists the files that would have been deleted if the
  88    --  switch -n had not been specified.
  89 
  90    File_Deleted : Boolean := False;
  91    --  Set to True if at least one file has been deleted
  92 
  93    Copyright_Displayed : Boolean := False;
  94    Usage_Displayed     : Boolean := False;
  95 
  96    Project_File_Name : String_Access := null;
  97 
  98    Project_Node_Tree : Project_Node_Tree_Ref;
  99 
 100    Main_Project : Prj.Project_Id := Prj.No_Project;
 101 
 102    All_Projects : Boolean := False;
 103 
 104    --  Packages of project files where unknown attributes are errors
 105 
 106    Naming_String   : aliased String := "naming";
 107    Builder_String  : aliased String := "builder";
 108    Compiler_String : aliased String := "compiler";
 109    Binder_String   : aliased String := "binder";
 110    Linker_String   : aliased String := "linker";
 111 
 112    Gnatmake_Packages : aliased String_List :=
 113      (Naming_String   'Access,
 114       Builder_String  'Access,
 115       Compiler_String 'Access,
 116       Binder_String   'Access,
 117       Linker_String   'Access);
 118 
 119    Packages_To_Check_By_Gnatmake : constant String_List_Access :=
 120      Gnatmake_Packages'Access;
 121 
 122    package Processed_Projects is new Table.Table
 123      (Table_Component_Type => Project_Id,
 124       Table_Index_Type     => Natural,
 125       Table_Low_Bound      => 0,
 126       Table_Initial        => 10,
 127       Table_Increment      => 100,
 128       Table_Name           => "Clean.Processed_Projects");
 129    --  Table to keep track of what project files have been processed, when
 130    --  switch -r is specified.
 131 
 132    package Sources is new Table.Table
 133      (Table_Component_Type => File_Name_Type,
 134       Table_Index_Type     => Natural,
 135       Table_Low_Bound      => 0,
 136       Table_Initial        => 10,
 137       Table_Increment      => 100,
 138       Table_Name           => "Clean.Processed_Projects");
 139    --  Table to store all the source files of a library unit: spec, body and
 140    --  subunits, to detect .dg files and delete them.
 141 
 142    -----------------------------
 143    -- Other local subprograms --
 144    -----------------------------
 145 
 146    procedure Add_Source_Dir (N : String);
 147    --  Call Add_Src_Search_Dir and output one line when in verbose mode
 148 
 149    procedure Add_Source_Directories is
 150      new Prj.Env.For_All_Source_Dirs (Action => Add_Source_Dir);
 151 
 152    procedure Add_Object_Dir (N : String);
 153    --  Call Add_Lib_Search_Dir and output one line when in verbose mode
 154 
 155    procedure Add_Object_Directories is
 156      new Prj.Env.For_All_Object_Dirs (Action => Add_Object_Dir);
 157 
 158    function ALI_File_Name (Source : File_Name_Type) return String;
 159    --  Returns the name of the ALI file corresponding to Source
 160 
 161    function Assembly_File_Name (Source : File_Name_Type) return String;
 162    --  Returns the assembly file name corresponding to Source
 163 
 164    procedure Clean_Archive (Project : Project_Id; Global : Boolean);
 165    --  Delete a global archive or library project archive and the dependency
 166    --  file, if they exist.
 167 
 168    procedure Clean_Executables;
 169    --  Do the cleaning work when no project file is specified
 170 
 171    procedure Clean_Interface_Copy_Directory (Project : Project_Id);
 172    --  Delete files in an interface copy directory: any file that is a copy of
 173    --  a source of the project.
 174 
 175    procedure Clean_Library_Directory (Project : Project_Id);
 176    --  Delete the library file in a library directory and any ALI file of a
 177    --  source of the project in a library ALI directory.
 178 
 179    procedure Clean_Project (Project : Project_Id);
 180    --  Do the cleaning work when a project file is specified. This procedure
 181    --  calls itself recursively when there are several project files in the
 182    --  tree rooted at the main project file and switch -r has been specified.
 183 
 184    function Debug_File_Name (Source : File_Name_Type) return String;
 185    --  Name of the expanded source file corresponding to Source
 186 
 187    procedure Delete (In_Directory : String; File : String);
 188    --  Delete one file, or list the file name if switch -n is specified
 189 
 190    procedure Delete_Binder_Generated_Files
 191      (Dir    : String;
 192       Source : File_Name_Type);
 193    --  Delete the binder generated file in directory Dir for Source, if they
 194    --  exist: for Unix these are b~<source>.ads, b~<source>.adb,
 195    --  b~<source>.ali and b~<source>.o.
 196 
 197    procedure Display_Copyright;
 198    --  Display the Copyright notice. If called several times, display the
 199    --  Copyright notice only the first time.
 200 
 201    procedure Initialize;
 202    --  Call the necessary package initializations
 203 
 204    function Object_File_Name (Source : File_Name_Type) return String;
 205    --  Returns the object file name corresponding to Source
 206 
 207    procedure Parse_Cmd_Line;
 208    --  Parse the command line
 209 
 210    function Repinfo_File_Name (Source : File_Name_Type) return String;
 211    --  Returns the repinfo file name corresponding to Source
 212 
 213    function Tree_File_Name (Source : File_Name_Type) return String;
 214    --  Returns the tree file name corresponding to Source
 215 
 216    function In_Extension_Chain
 217      (Of_Project : Project_Id;
 218       Prj        : Project_Id) return Boolean;
 219    --  Returns True iff Prj is an extension of Of_Project or if Of_Project is
 220    --  an extension of Prj.
 221 
 222    procedure Usage;
 223    --  Display the usage. If called several times, the usage is displayed only
 224    --  the first time.
 225 
 226    --------------------
 227    -- Add_Object_Dir --
 228    --------------------
 229 
 230    procedure Add_Object_Dir (N : String) is
 231    begin
 232       Add_Lib_Search_Dir (N);
 233 
 234       if Opt.Verbose_Mode then
 235          Put ("Adding object directory """);
 236          Put (N);
 237          Put (""".");
 238          New_Line;
 239       end if;
 240    end Add_Object_Dir;
 241 
 242    --------------------
 243    -- Add_Source_Dir --
 244    --------------------
 245 
 246    procedure Add_Source_Dir (N : String) is
 247    begin
 248       Add_Src_Search_Dir (N);
 249 
 250       if Opt.Verbose_Mode then
 251          Put ("Adding source directory """);
 252          Put (N);
 253          Put (""".");
 254          New_Line;
 255       end if;
 256    end Add_Source_Dir;
 257 
 258    -------------------
 259    -- ALI_File_Name --
 260    -------------------
 261 
 262    function ALI_File_Name (Source : File_Name_Type) return String is
 263       Src : constant String := Get_Name_String (Source);
 264 
 265    begin
 266       --  If the source name has an extension, then replace it with
 267       --  the ALI suffix.
 268 
 269       for Index in reverse Src'First + 1 .. Src'Last loop
 270          if Src (Index) = '.' then
 271             return Src (Src'First .. Index - 1) & ALI_Suffix;
 272          end if;
 273       end loop;
 274 
 275       --  If there is no dot, or if it is the first character, just add the
 276       --  ALI suffix.
 277 
 278       return Src & ALI_Suffix;
 279    end ALI_File_Name;
 280 
 281    ------------------------
 282    -- Assembly_File_Name --
 283    ------------------------
 284 
 285    function Assembly_File_Name (Source : File_Name_Type) return String is
 286       Src : constant String := Get_Name_String (Source);
 287 
 288    begin
 289       --  If the source name has an extension, then replace it with
 290       --  the assembly suffix.
 291 
 292       for Index in reverse Src'First + 1 .. Src'Last loop
 293          if Src (Index) = '.' then
 294             return Src (Src'First .. Index - 1) & Assembly_Suffix;
 295          end if;
 296       end loop;
 297 
 298       --  If there is no dot, or if it is the first character, just add the
 299       --  assembly suffix.
 300 
 301       return Src & Assembly_Suffix;
 302    end Assembly_File_Name;
 303 
 304    -------------------
 305    -- Clean_Archive --
 306    -------------------
 307 
 308    procedure Clean_Archive (Project : Project_Id; Global : Boolean) is
 309       Current_Dir : constant Dir_Name_Str := Get_Current_Dir;
 310 
 311       Lib_Prefix : String_Access;
 312       Archive_Name : String_Access;
 313       --  The name of the archive file for this project
 314 
 315       Archive_Dep_Name : String_Access;
 316       --  The name of the archive dependency file for this project
 317 
 318       Obj_Dir : constant String :=
 319         Get_Name_String (Project.Object_Directory.Display_Name);
 320 
 321    begin
 322       Change_Dir (Obj_Dir);
 323 
 324       --  First, get the lib prefix, the archive file name and the archive
 325       --  dependency file name.
 326 
 327       if Global then
 328          Lib_Prefix :=
 329            new String'("lib" & Get_Name_String (Project.Display_Name));
 330       else
 331          Lib_Prefix :=
 332            new String'("lib" & Get_Name_String (Project.Library_Name));
 333       end if;
 334 
 335       Archive_Name := new String'(Lib_Prefix.all & '.' & Archive_Ext);
 336       Archive_Dep_Name := new String'(Lib_Prefix.all & ".deps");
 337 
 338       --  Delete the archive file and the archive dependency file, if they
 339       --  exist.
 340 
 341       if Is_Regular_File (Archive_Name.all) then
 342          Delete (Obj_Dir, Archive_Name.all);
 343       end if;
 344 
 345       if Is_Regular_File (Archive_Dep_Name.all) then
 346          Delete (Obj_Dir, Archive_Dep_Name.all);
 347       end if;
 348 
 349       Change_Dir (Current_Dir);
 350    end Clean_Archive;
 351 
 352    -----------------------
 353    -- Clean_Executables --
 354    -----------------------
 355 
 356    procedure Clean_Executables is
 357       Main_Source_File : File_Name_Type;
 358       --  Current main source
 359 
 360       Main_Lib_File : File_Name_Type;
 361       --  ALI file of the current main
 362 
 363       Lib_File : File_Name_Type;
 364       --  Current ALI file
 365 
 366       Full_Lib_File : File_Name_Type;
 367       --  Full name of the current ALI file
 368 
 369       Text    : Text_Buffer_Ptr;
 370       The_ALI : ALI_Id;
 371       Found   : Boolean;
 372       Source  : Queue.Source_Info;
 373 
 374    begin
 375       Queue.Initialize (Queue_Per_Obj_Dir => False);
 376 
 377       --  It does not really matter if there is or not an object file
 378       --  corresponding to an ALI file: if there is one, it will be deleted.
 379 
 380       Opt.Check_Object_Consistency := False;
 381 
 382       --  Proceed each executable one by one. Each source is marked as it is
 383       --  processed, so common sources between executables will not be
 384       --  processed several times.
 385 
 386       for N_File in 1 .. Osint.Number_Of_Files loop
 387          Main_Source_File := Next_Main_Source;
 388          Main_Lib_File :=
 389            Osint.Lib_File_Name (Main_Source_File, Current_File_Index);
 390 
 391          if Main_Lib_File /= No_File then
 392             Queue.Insert
 393               ((Format  => Format_Gnatmake,
 394                 File    => Main_Lib_File,
 395                 Unit    => No_Unit_Name,
 396                 Index   => 0,
 397                 Project => No_Project,
 398                 Sid     => No_Source));
 399          end if;
 400 
 401          while not Queue.Is_Empty loop
 402             Sources.Set_Last (0);
 403             Queue.Extract (Found, Source);
 404             pragma Assert (Found);
 405             pragma Assert (Source.File /= No_File);
 406             Lib_File := Source.File;
 407             Full_Lib_File := Osint.Full_Lib_File_Name (Lib_File);
 408 
 409             --  If we have existing ALI file that is not read-only, process it
 410 
 411             if Full_Lib_File /= No_File
 412               and then not Is_Readonly_Library (Full_Lib_File)
 413             then
 414                Text := Read_Library_Info (Lib_File);
 415 
 416                if Text /= null then
 417                   The_ALI :=
 418                     Scan_ALI (Lib_File, Text, Ignore_ED => False, Err => True);
 419                   Free (Text);
 420 
 421                   --  If no error was produced while loading this ALI file,
 422                   --  insert into the queue all the unmarked withed sources.
 423 
 424                   if The_ALI /= No_ALI_Id then
 425                      for J in ALIs.Table (The_ALI).First_Unit ..
 426                        ALIs.Table (The_ALI).Last_Unit
 427                      loop
 428                         Sources.Increment_Last;
 429                         Sources.Table (Sources.Last) :=
 430                           ALI.Units.Table (J).Sfile;
 431 
 432                         for K in ALI.Units.Table (J).First_With ..
 433                           ALI.Units.Table (J).Last_With
 434                         loop
 435                            if Withs.Table (K).Afile /= No_File then
 436                               Queue.Insert
 437                                 ((Format  => Format_Gnatmake,
 438                                   File    => Withs.Table (K).Afile,
 439                                   Unit    => No_Unit_Name,
 440                                   Index   => 0,
 441                                   Project => No_Project,
 442                                   Sid     => No_Source));
 443                            end if;
 444                         end loop;
 445                      end loop;
 446 
 447                      --  Look for subunits and put them in the Sources table
 448 
 449                      for J in ALIs.Table (The_ALI).First_Sdep ..
 450                        ALIs.Table (The_ALI).Last_Sdep
 451                      loop
 452                         if Sdep.Table (J).Subunit_Name /= No_Name then
 453                            Sources.Increment_Last;
 454                            Sources.Table (Sources.Last) :=
 455                              Sdep.Table (J).Sfile;
 456                         end if;
 457                      end loop;
 458                   end if;
 459                end if;
 460 
 461                --  Now delete all existing files corresponding to this ALI file
 462 
 463                declare
 464                   Obj_Dir : constant String :=
 465                     Dir_Name (Get_Name_String (Full_Lib_File));
 466                   Obj     : constant String := Object_File_Name (Lib_File);
 467                   Adt     : constant String := Tree_File_Name   (Lib_File);
 468                   Asm     : constant String := Assembly_File_Name (Lib_File);
 469 
 470                begin
 471                   Delete (Obj_Dir, Get_Name_String (Lib_File));
 472 
 473                   if Is_Regular_File (Obj_Dir & Dir_Separator & Obj) then
 474                      Delete (Obj_Dir, Obj);
 475                   end if;
 476 
 477                   if Is_Regular_File (Obj_Dir & Dir_Separator & Adt) then
 478                      Delete (Obj_Dir, Adt);
 479                   end if;
 480 
 481                   if Is_Regular_File (Obj_Dir & Dir_Separator & Asm) then
 482                      Delete (Obj_Dir, Asm);
 483                   end if;
 484 
 485                   --  Delete expanded source files (.dg) and/or repinfo files
 486                   --  (.rep) if any
 487 
 488                   for J in 1 .. Sources.Last loop
 489                      declare
 490                         Deb : constant String :=
 491                           Debug_File_Name (Sources.Table (J));
 492                         Rep : constant String :=
 493                           Repinfo_File_Name (Sources.Table (J));
 494 
 495                      begin
 496                         if Is_Regular_File (Obj_Dir & Dir_Separator & Deb) then
 497                            Delete (Obj_Dir, Deb);
 498                         end if;
 499 
 500                         if Is_Regular_File (Obj_Dir & Dir_Separator & Rep) then
 501                            Delete (Obj_Dir, Rep);
 502                         end if;
 503                      end;
 504                   end loop;
 505                end;
 506             end if;
 507          end loop;
 508 
 509          --  Delete the executable, if it exists, and the binder generated
 510          --  files, if any.
 511 
 512          if not Compile_Only then
 513             declare
 514                Source     : constant File_Name_Type :=
 515                  Strip_Suffix (Main_Lib_File);
 516                Executable : constant String :=
 517                  Get_Name_String (Executable_Name (Source));
 518             begin
 519                if Is_Regular_File (Executable) then
 520                   Delete ("", Executable);
 521                end if;
 522 
 523                Delete_Binder_Generated_Files (Get_Current_Dir, Source);
 524             end;
 525          end if;
 526       end loop;
 527    end Clean_Executables;
 528 
 529    ------------------------------------
 530    -- Clean_Interface_Copy_Directory --
 531    ------------------------------------
 532 
 533    procedure Clean_Interface_Copy_Directory (Project : Project_Id) is
 534       Current : constant String := Get_Current_Dir;
 535 
 536       Direc : Dir_Type;
 537 
 538       Name : String (1 .. 200);
 539       Last : Natural;
 540 
 541       Delete_File : Boolean;
 542       Unit        : Unit_Index;
 543 
 544    begin
 545       if Project.Library
 546         and then Project.Library_Src_Dir /= No_Path_Information
 547       then
 548          declare
 549             Directory : constant String :=
 550               Get_Name_String (Project.Library_Src_Dir.Display_Name);
 551 
 552          begin
 553             Change_Dir (Directory);
 554             Open (Direc, ".");
 555 
 556             --  For each regular file in the directory, if switch -n has not
 557             --  been specified, make it writable and delete the file if it is
 558             --  a copy of a source of the project.
 559 
 560             loop
 561                Read (Direc, Name, Last);
 562                exit when Last = 0;
 563 
 564                declare
 565                   Filename : constant String := Name (1 .. Last);
 566 
 567                begin
 568                   if Is_Regular_File (Filename) then
 569                      Canonical_Case_File_Name (Name (1 .. Last));
 570                      Delete_File := False;
 571 
 572                      Unit := Units_Htable.Get_First (Project_Tree.Units_HT);
 573 
 574                      --  Compare with source file names of the project
 575 
 576                      while Unit /= No_Unit_Index loop
 577                         if Unit.File_Names (Impl) /= null
 578                           and then Ultimate_Extending_Project_Of
 579                                      (Unit.File_Names (Impl).Project) = Project
 580                           and then
 581                             Get_Name_String (Unit.File_Names (Impl).File) =
 582                                                               Name (1 .. Last)
 583                         then
 584                            Delete_File := True;
 585                            exit;
 586                         end if;
 587 
 588                         if Unit.File_Names (Spec) /= null
 589                           and then Ultimate_Extending_Project_Of
 590                                      (Unit.File_Names (Spec).Project) = Project
 591                           and then
 592                             Get_Name_String
 593                               (Unit.File_Names (Spec).File) = Name (1 .. Last)
 594                         then
 595                            Delete_File := True;
 596                            exit;
 597                         end if;
 598 
 599                         Unit := Units_Htable.Get_Next (Project_Tree.Units_HT);
 600                      end loop;
 601 
 602                      if Delete_File then
 603                         if not Do_Nothing then
 604                            Set_Writable (Filename);
 605                         end if;
 606 
 607                         Delete (Directory, Filename);
 608                      end if;
 609                   end if;
 610                end;
 611             end loop;
 612 
 613             Close (Direc);
 614 
 615             --  Restore the initial working directory
 616 
 617             Change_Dir (Current);
 618          end;
 619       end if;
 620    end Clean_Interface_Copy_Directory;
 621 
 622    -----------------------------
 623    -- Clean_Library_Directory --
 624    -----------------------------
 625 
 626    Empty_String : aliased String := "";
 627 
 628    procedure Clean_Library_Directory (Project : Project_Id) is
 629       Current : constant String := Get_Current_Dir;
 630 
 631       Lib_Filename : constant String := Get_Name_String (Project.Library_Name);
 632       DLL_Name     : String :=
 633         DLL_Prefix & Lib_Filename & "." & DLL_Ext;
 634       Archive_Name : String :=
 635         "lib" & Lib_Filename & "." & Archive_Ext;
 636       Direc        : Dir_Type;
 637 
 638       Name : String (1 .. 200);
 639       Last : Natural;
 640 
 641       Delete_File : Boolean;
 642 
 643       Minor : String_Access := Empty_String'Access;
 644       Major : String_Access := Empty_String'Access;
 645 
 646    begin
 647       if Project.Library then
 648          if Project.Library_Kind /= Static
 649            and then MLib.Tgt.Library_Major_Minor_Id_Supported
 650            and then Project.Lib_Internal_Name /= No_Name
 651          then
 652             Minor := new String'(Get_Name_String (Project.Lib_Internal_Name));
 653             Major := new String'(MLib.Major_Id_Name (DLL_Name, Minor.all));
 654          end if;
 655 
 656          declare
 657             Lib_Directory     : constant String :=
 658               Get_Name_String (Project.Library_Dir.Display_Name);
 659             Lib_ALI_Directory : constant String :=
 660               Get_Name_String (Project.Library_ALI_Dir.Display_Name);
 661 
 662          begin
 663             Canonical_Case_File_Name (Archive_Name);
 664             Canonical_Case_File_Name (DLL_Name);
 665 
 666             if Is_Directory (Lib_Directory) then
 667                Change_Dir (Lib_Directory);
 668                Open (Direc, ".");
 669 
 670                --  For each regular file in the directory, if switch -n has not
 671                --  not been specified, make it writable and delete the file if
 672                --  it is the library file.
 673 
 674                loop
 675                   Read (Direc, Name, Last);
 676                   exit when Last = 0;
 677 
 678                   declare
 679                      Filename : constant String := Name (1 .. Last);
 680 
 681                   begin
 682                      if Is_Regular_File (Filename)
 683                        or else Is_Symbolic_Link (Filename)
 684                      then
 685                         Canonical_Case_File_Name (Name (1 .. Last));
 686                         Delete_File := False;
 687 
 688                         if (Project.Library_Kind = Static
 689                              and then Name (1 .. Last) = Archive_Name)
 690                           or else
 691                             ((Project.Library_Kind = Dynamic
 692                                 or else
 693                               Project.Library_Kind = Relocatable)
 694                              and then
 695                                (Name (1 .. Last) = DLL_Name
 696                                   or else
 697                                 Name (1 .. Last) = Minor.all
 698                                   or else
 699                                 Name (1 .. Last) = Major.all))
 700                         then
 701                            if not Do_Nothing then
 702                               Set_Writable (Filename);
 703                            end if;
 704 
 705                            Delete (Lib_Directory, Filename);
 706                         end if;
 707                      end if;
 708                   end;
 709                end loop;
 710 
 711                Close (Direc);
 712             end if;
 713 
 714             if not Is_Directory (Lib_ALI_Directory) then
 715                --  Nothing more to do, return now
 716                return;
 717             end if;
 718 
 719             Change_Dir (Lib_ALI_Directory);
 720             Open (Direc, ".");
 721 
 722             --  For each regular file in the directory, if switch -n has not
 723             --  been specified, make it writable and delete the file if it is
 724             --  any ALI file of a source of the project.
 725 
 726             loop
 727                Read (Direc, Name, Last);
 728                exit when Last = 0;
 729 
 730                declare
 731                   Filename : constant String := Name (1 .. Last);
 732                begin
 733                   if Is_Regular_File (Filename) then
 734                      Canonical_Case_File_Name (Name (1 .. Last));
 735                      Delete_File := False;
 736 
 737                      if Last > 4 and then Name (Last - 3 .. Last) = ".ali" then
 738                         declare
 739                            Unit : Unit_Index;
 740 
 741                         begin
 742                            --  Compare with ALI file names of the project
 743 
 744                            Unit :=
 745                              Units_Htable.Get_First (Project_Tree.Units_HT);
 746                            while Unit /= No_Unit_Index loop
 747                               if Unit.File_Names (Impl) /= null
 748                                 and then Unit.File_Names (Impl).Project /=
 749                                                                    No_Project
 750                               then
 751                                  if Ultimate_Extending_Project_Of
 752                                       (Unit.File_Names (Impl).Project) =
 753                                                                    Project
 754                                  then
 755                                     Get_Name_String
 756                                       (Unit.File_Names (Impl).File);
 757                                     Name_Len :=
 758                                       Name_Len -
 759                                         File_Extension
 760                                           (Name (1 .. Name_Len))'Length;
 761                                     if Name_Buffer (1 .. Name_Len) =
 762                                          Name (1 .. Last - 4)
 763                                     then
 764                                        Delete_File := True;
 765                                        exit;
 766                                     end if;
 767                                  end if;
 768 
 769                               elsif Unit.File_Names (Spec) /= null
 770                                 and then Ultimate_Extending_Project_Of
 771                                            (Unit.File_Names (Spec).Project) =
 772                                                                     Project
 773                               then
 774                                  Get_Name_String (Unit.File_Names (Spec).File);
 775                                  Name_Len :=
 776                                    Name_Len -
 777                                      File_Extension
 778                                        (Name (1 .. Name_Len))'Length;
 779 
 780                                  if Name_Buffer (1 .. Name_Len) =
 781                                       Name (1 .. Last - 4)
 782                                  then
 783                                     Delete_File := True;
 784                                     exit;
 785                                  end if;
 786                               end if;
 787 
 788                               Unit :=
 789                                 Units_Htable.Get_Next (Project_Tree.Units_HT);
 790                            end loop;
 791                         end;
 792                      end if;
 793 
 794                      if Delete_File then
 795                         if not Do_Nothing then
 796                            Set_Writable (Filename);
 797                         end if;
 798 
 799                         Delete (Lib_ALI_Directory, Filename);
 800                      end if;
 801                   end if;
 802                end;
 803             end loop;
 804 
 805             Close (Direc);
 806 
 807             --  Restore the initial working directory
 808 
 809             Change_Dir (Current);
 810          end;
 811       end if;
 812    end Clean_Library_Directory;
 813 
 814    -------------------
 815    -- Clean_Project --
 816    -------------------
 817 
 818    procedure Clean_Project (Project : Project_Id) is
 819       Main_Source_File : File_Name_Type;
 820       --  Name of executable on the command line without directory info
 821 
 822       Executable : File_Name_Type;
 823       --  Name of the executable file
 824 
 825       Current_Dir : constant Dir_Name_Str := Get_Current_Dir;
 826       Unit        : Unit_Index;
 827       File_Name1  : File_Name_Type;
 828       Index1      : Int;
 829       File_Name2  : File_Name_Type;
 830       Index2      : Int;
 831       Lib_File    : File_Name_Type;
 832 
 833       Global_Archive : Boolean := False;
 834 
 835    begin
 836       --  Check that we don't specify executable on the command line for
 837       --  a main library project.
 838 
 839       if Project = Main_Project
 840         and then Osint.Number_Of_Files /= 0
 841         and then Project.Library
 842       then
 843          Osint.Fail
 844            ("Cannot specify executable(s) for a Library Project File");
 845       end if;
 846 
 847       --  Nothing to clean in an externally built project
 848 
 849       if Project.Externally_Built then
 850          if Verbose_Mode then
 851             Put ("Nothing to do to clean externally built project """);
 852             Put (Get_Name_String (Project.Name));
 853             Put_Line ("""");
 854          end if;
 855 
 856       else
 857          if Verbose_Mode then
 858             Put ("Cleaning project """);
 859             Put (Get_Name_String (Project.Name));
 860             Put_Line ("""");
 861          end if;
 862 
 863          --  Add project to the list of processed projects
 864 
 865          Processed_Projects.Increment_Last;
 866          Processed_Projects.Table (Processed_Projects.Last) := Project;
 867 
 868          if Project.Object_Directory /= No_Path_Information
 869            and then Is_Directory
 870                       (Get_Name_String (Project.Object_Directory.Display_Name))
 871          then
 872             declare
 873                Obj_Dir : constant String :=
 874                  Get_Name_String (Project.Object_Directory.Display_Name);
 875 
 876             begin
 877                Change_Dir (Obj_Dir);
 878 
 879                --  First, deal with Ada
 880 
 881                --  Look through the units to find those that are either
 882                --  immediate sources or inherited sources of the project.
 883                --  Extending projects may have no language specified, if
 884                --  Source_Dirs or Source_Files is specified as an empty list,
 885                --  so always look for Ada units in extending projects.
 886 
 887                if Has_Ada_Sources (Project)
 888                  or else Project.Extends /= No_Project
 889                then
 890                   Unit := Units_Htable.Get_First (Project_Tree.Units_HT);
 891                   while Unit /= No_Unit_Index loop
 892                      File_Name1 := No_File;
 893                      File_Name2 := No_File;
 894 
 895                      --  If either the spec or the body is a source of the
 896                      --  project, check for the corresponding ALI file in the
 897                      --  object directory.
 898 
 899                      if (Unit.File_Names (Impl) /= null
 900                           and then
 901                             In_Extension_Chain
 902                               (Unit.File_Names (Impl).Project, Project))
 903                        or else
 904                          (Unit.File_Names (Spec) /= null
 905                            and then
 906                              In_Extension_Chain
 907                                (Unit.File_Names (Spec).Project, Project))
 908                      then
 909                         if Unit.File_Names (Impl) /= null then
 910                            File_Name1 := Unit.File_Names (Impl).File;
 911                            Index1     := Unit.File_Names (Impl).Index;
 912                         else
 913                            File_Name1 := No_File;
 914                            Index1     := 0;
 915                         end if;
 916 
 917                         if Unit.File_Names (Spec) /= null then
 918                            File_Name2 := Unit.File_Names (Spec).File;
 919                            Index2     := Unit.File_Names (Spec).Index;
 920                         else
 921                            File_Name2 := No_File;
 922                            Index2     := 0;
 923                         end if;
 924 
 925                         --  If there is no body file name, then there may be
 926                         --  only a spec.
 927 
 928                         if File_Name1 = No_File then
 929                            File_Name1 := File_Name2;
 930                            Index1     := Index2;
 931                            File_Name2 := No_File;
 932                            Index2     := 0;
 933                         end if;
 934                      end if;
 935 
 936                      --  If there is either a spec or a body, look for files
 937                      --  in the object directory.
 938 
 939                      if File_Name1 /= No_File then
 940                         Lib_File := Osint.Lib_File_Name (File_Name1, Index1);
 941 
 942                         declare
 943                            Asm : constant String :=
 944                                    Assembly_File_Name (Lib_File);
 945                            ALI : constant String :=
 946                                    ALI_File_Name      (Lib_File);
 947                            Obj : constant String :=
 948                                    Object_File_Name   (Lib_File);
 949                            Adt : constant String :=
 950                                    Tree_File_Name     (Lib_File);
 951                            Deb : constant String :=
 952                                    Debug_File_Name    (File_Name1);
 953                            Rep : constant String :=
 954                                    Repinfo_File_Name  (File_Name1);
 955                            Del : Boolean := True;
 956 
 957                         begin
 958                            --  If the ALI file exists and is read-only, no file
 959                            --  is deleted.
 960 
 961                            if Is_Regular_File (ALI) then
 962                               if Is_Writable_File (ALI) then
 963                                  Delete (Obj_Dir, ALI);
 964 
 965                               else
 966                                  Del := False;
 967 
 968                                  if Verbose_Mode then
 969                                     Put ('"');
 970                                     Put (Obj_Dir);
 971 
 972                                     if Obj_Dir (Obj_Dir'Last) /=
 973                                       Dir_Separator
 974                                     then
 975                                        Put (Dir_Separator);
 976                                     end if;
 977 
 978                                     Put (ALI);
 979                                     Put_Line (""" is read-only");
 980                                  end if;
 981                               end if;
 982                            end if;
 983 
 984                            if Del then
 985 
 986                               --  Object file
 987 
 988                               if Is_Regular_File (Obj) then
 989                                  Delete (Obj_Dir, Obj);
 990                               end if;
 991 
 992                               --  Assembly file
 993 
 994                               if Is_Regular_File (Asm) then
 995                                  Delete (Obj_Dir, Asm);
 996                               end if;
 997 
 998                               --  Tree file
 999 
1000                               if Is_Regular_File (Adt) then
1001                                  Delete (Obj_Dir, Adt);
1002                               end if;
1003 
1004                               --  First expanded source file
1005 
1006                               if Is_Regular_File (Deb) then
1007                                  Delete (Obj_Dir, Deb);
1008                               end if;
1009 
1010                               --  Repinfo file
1011 
1012                               if Is_Regular_File (Rep) then
1013                                  Delete (Obj_Dir, Rep);
1014                               end if;
1015 
1016                               --  Second expanded source file
1017 
1018                               if File_Name2 /= No_File then
1019                                  declare
1020                                     Deb : constant String :=
1021                                       Debug_File_Name (File_Name2);
1022                                     Rep : constant String :=
1023                                       Repinfo_File_Name (File_Name2);
1024 
1025                                  begin
1026                                     if Is_Regular_File (Deb) then
1027                                        Delete (Obj_Dir, Deb);
1028                                     end if;
1029 
1030                                     if Is_Regular_File (Rep) then
1031                                        Delete (Obj_Dir, Rep);
1032                                     end if;
1033                                  end;
1034                               end if;
1035                            end if;
1036                         end;
1037                      end if;
1038 
1039                      Unit := Units_Htable.Get_Next (Project_Tree.Units_HT);
1040                   end loop;
1041                end if;
1042 
1043                --  Check if a global archive and it dependency file could have
1044                --  been created and, if they exist, delete them.
1045 
1046                if Project = Main_Project and then not Project.Library then
1047                   Global_Archive := False;
1048 
1049                   declare
1050                      Proj : Project_List;
1051 
1052                   begin
1053                      Proj := Project_Tree.Projects;
1054                      while Proj /= null loop
1055 
1056                         --  For gnatmake, when the project specifies more than
1057                         --  just Ada as a language (even if course we could not
1058                         --  find any source file for the other languages), we
1059                         --  will take all the object files found in the object
1060                         --  directories. Since we know the project supports at
1061                         --  least Ada, we just have to test whether it has at
1062                         --  least two languages, and we do not care about the
1063                         --  sources.
1064 
1065                         if Proj.Project.Languages /= null
1066                           and then Proj.Project.Languages.Next /= null
1067                         then
1068                            Global_Archive := True;
1069                            exit;
1070                         end if;
1071 
1072                         Proj := Proj.Next;
1073                      end loop;
1074                   end;
1075 
1076                   if Global_Archive then
1077                      Clean_Archive (Project, Global => True);
1078                   end if;
1079                end if;
1080 
1081             end;
1082          end if;
1083 
1084          --  If this is a library project, clean the library directory, the
1085          --  interface copy dir and, for a Stand-Alone Library, the binder
1086          --  generated files of the library.
1087 
1088          --  The directories are cleaned only if switch -c is not specified
1089 
1090          if Project.Library then
1091             if not Compile_Only then
1092                Clean_Library_Directory (Project);
1093 
1094                if Project.Library_Src_Dir /= No_Path_Information then
1095                   Clean_Interface_Copy_Directory (Project);
1096                end if;
1097             end if;
1098 
1099             if Project.Standalone_Library /= No
1100               and then Project.Object_Directory /= No_Path_Information
1101             then
1102                Delete_Binder_Generated_Files
1103                  (Get_Name_String (Project.Object_Directory.Display_Name),
1104                   File_Name_Type (Project.Library_Name));
1105             end if;
1106          end if;
1107 
1108          if Verbose_Mode then
1109             New_Line;
1110          end if;
1111       end if;
1112 
1113       --  If switch -r is specified, call Clean_Project recursively for the
1114       --  imported projects and the project being extended.
1115 
1116       if All_Projects then
1117          declare
1118             Imported : Project_List;
1119             Process  : Boolean;
1120 
1121          begin
1122             --  For each imported project, call Clean_Project if the project
1123             --  has not been processed already.
1124 
1125             Imported := Project.Imported_Projects;
1126             while Imported /= null loop
1127                Process := True;
1128 
1129                for
1130                  J in Processed_Projects.First .. Processed_Projects.Last
1131                loop
1132                   if Imported.Project = Processed_Projects.Table (J) then
1133                      Process := False;
1134                      exit;
1135                   end if;
1136                end loop;
1137 
1138                if Process then
1139                   Clean_Project (Imported.Project);
1140                end if;
1141 
1142                Imported := Imported.Next;
1143             end loop;
1144 
1145             --  If this project extends another project, call Clean_Project for
1146             --  the project being extended. It is guaranteed that it has not
1147             --  called before, because no other project may import or extend
1148             --  this project.
1149 
1150             if Project.Extends /= No_Project then
1151                Clean_Project (Project.Extends);
1152             end if;
1153          end;
1154       end if;
1155 
1156          --  For the main project, delete the executables and the binder
1157          --  generated files.
1158 
1159          --  The executables are deleted only if switch -c is not specified
1160 
1161       if Project = Main_Project
1162         and then Project.Exec_Directory /= No_Path_Information
1163       then
1164          declare
1165             Exec_Dir : constant String :=
1166               Get_Name_String (Project.Exec_Directory.Display_Name);
1167 
1168          begin
1169             Change_Dir (Exec_Dir);
1170 
1171             for N_File in 1 .. Osint.Number_Of_Files loop
1172                Main_Source_File := Next_Main_Source;
1173 
1174                if not Compile_Only then
1175                   Executable :=
1176                     Executable_Of
1177                       (Main_Project,
1178                        Project_Tree.Shared,
1179                        Main_Source_File,
1180                        Current_File_Index);
1181 
1182                   declare
1183                      Exec_File_Name : constant String :=
1184                        Get_Name_String (Executable);
1185 
1186                   begin
1187                      if Is_Absolute_Path (Name => Exec_File_Name) then
1188                         if Is_Regular_File (Exec_File_Name) then
1189                            Delete ("", Exec_File_Name);
1190                         end if;
1191 
1192                      else
1193                         if Is_Regular_File (Exec_File_Name) then
1194                            Delete (Exec_Dir, Exec_File_Name);
1195                         end if;
1196                      end if;
1197                   end;
1198                end if;
1199 
1200                if Project.Object_Directory /= No_Path_Information
1201                  and then
1202                    Is_Directory
1203                      (Get_Name_String (Project.Object_Directory.Display_Name))
1204                then
1205                   Delete_Binder_Generated_Files
1206                     (Get_Name_String (Project.Object_Directory.Display_Name),
1207                      Strip_Suffix (Main_Source_File));
1208                end if;
1209             end loop;
1210          end;
1211       end if;
1212 
1213       --  Change back to previous directory
1214 
1215       Change_Dir (Current_Dir);
1216    end Clean_Project;
1217 
1218    ---------------------
1219    -- Debug_File_Name --
1220    ---------------------
1221 
1222    function Debug_File_Name (Source : File_Name_Type) return String is
1223    begin
1224       return Get_Name_String (Source) & Debug_Suffix;
1225    end Debug_File_Name;
1226 
1227    ------------
1228    -- Delete --
1229    ------------
1230 
1231    procedure Delete (In_Directory : String; File : String) is
1232       Full_Name : String (1 .. In_Directory'Length + File'Length + 1);
1233       Last      : Natural := 0;
1234       Success   : Boolean;
1235 
1236    begin
1237       --  Indicate that at least one file is deleted or is to be deleted
1238 
1239       File_Deleted := True;
1240 
1241       --  Build the path name of the file to delete
1242 
1243       Last := In_Directory'Length;
1244       Full_Name (1 .. Last) := In_Directory;
1245 
1246       if Last > 0 and then Full_Name (Last) /= Directory_Separator then
1247          Last := Last + 1;
1248          Full_Name (Last) := Directory_Separator;
1249       end if;
1250 
1251       Full_Name (Last + 1 .. Last + File'Length) := File;
1252       Last := Last + File'Length;
1253 
1254       --  If switch -n was used, simply output the path name
1255 
1256       if Do_Nothing then
1257          Put_Line (Full_Name (1 .. Last));
1258 
1259       --  Otherwise, delete the file if it is writable
1260 
1261       else
1262          if Force_Deletions
1263            or else Is_Writable_File (Full_Name (1 .. Last))
1264            or else Is_Symbolic_Link (Full_Name (1 .. Last))
1265          then
1266             Delete_File (Full_Name (1 .. Last), Success);
1267 
1268          --  Here if no deletion required
1269 
1270          else
1271             Success := False;
1272          end if;
1273 
1274          if Verbose_Mode or else not Quiet_Output then
1275             if not Success then
1276                Put ("Warning: """);
1277                Put (Full_Name (1 .. Last));
1278                Put_Line (""" could not be deleted");
1279 
1280             else
1281                Put ("""");
1282                Put (Full_Name (1 .. Last));
1283                Put_Line (""" has been deleted");
1284             end if;
1285          end if;
1286       end if;
1287    end Delete;
1288 
1289    -----------------------------------
1290    -- Delete_Binder_Generated_Files --
1291    -----------------------------------
1292 
1293    procedure Delete_Binder_Generated_Files
1294      (Dir    : String;
1295       Source : File_Name_Type)
1296    is
1297       Source_Name : constant String   := Get_Name_String (Source);
1298       Current     : constant String   := Get_Current_Dir;
1299       Last        : constant Positive := B_Start'Length + Source_Name'Length;
1300       File_Name   : String (1 .. Last + 4);
1301 
1302    begin
1303       Change_Dir (Dir);
1304 
1305       --  Build the file name (before the extension)
1306 
1307       File_Name (1 .. B_Start'Length) := B_Start;
1308       File_Name (B_Start'Length + 1 .. Last) := Source_Name;
1309 
1310       --  Spec
1311 
1312       File_Name (Last + 1 .. Last + 4) := ".ads";
1313 
1314       if Is_Regular_File (File_Name (1 .. Last + 4)) then
1315          Delete (Dir, File_Name (1 .. Last + 4));
1316       end if;
1317 
1318       --  Body
1319 
1320       File_Name (Last + 1 .. Last + 4) := ".adb";
1321 
1322       if Is_Regular_File (File_Name (1 .. Last + 4)) then
1323          Delete (Dir, File_Name (1 .. Last + 4));
1324       end if;
1325 
1326       --  ALI file
1327 
1328       File_Name (Last + 1 .. Last + 4) := ".ali";
1329 
1330       if Is_Regular_File (File_Name (1 .. Last + 4)) then
1331          Delete (Dir, File_Name (1 .. Last + 4));
1332       end if;
1333 
1334       --  Object file
1335 
1336       File_Name (Last + 1 .. Last + Object_Suffix'Length) := Object_Suffix;
1337 
1338       if Is_Regular_File (File_Name (1 .. Last + Object_Suffix'Length)) then
1339          Delete (Dir, File_Name (1 .. Last + Object_Suffix'Length));
1340       end if;
1341 
1342       --  Change back to previous directory
1343 
1344       Change_Dir (Current);
1345    end Delete_Binder_Generated_Files;
1346 
1347    -----------------------
1348    -- Display_Copyright --
1349    -----------------------
1350 
1351    procedure Display_Copyright is
1352    begin
1353       if not Copyright_Displayed then
1354          Copyright_Displayed := True;
1355          Display_Version ("GNATCLEAN", "2003");
1356       end if;
1357    end Display_Copyright;
1358 
1359    ---------------
1360    -- Gnatclean --
1361    ---------------
1362 
1363    procedure Gnatclean is
1364    begin
1365       --  Do the necessary initializations
1366 
1367       Clean.Initialize;
1368 
1369       --  Parse the command line, getting the switches and the executable names
1370 
1371       Parse_Cmd_Line;
1372 
1373       --  Add the default project search directories now, after the directories
1374       --  that have been specified by switches -aP<dir>.
1375 
1376       Prj.Env.Initialize_Default_Project_Path
1377         (Root_Environment.Project_Path,
1378          Target_Name => Sdefault.Target_Name.all);
1379 
1380       if Verbose_Mode then
1381          Display_Copyright;
1382       end if;
1383 
1384       if Project_File_Name /= null then
1385 
1386          --  Warn about 'gnatclean -P'
1387 
1388          if Project_File_Name /= null then
1389             Put_Line
1390               ("warning: gnatclean -P is obsolete and will not be available" &
1391                " in the next release; use gprclean instead.");
1392          end if;
1393 
1394          --  A project file was specified by a -P switch
1395 
1396          if Opt.Verbose_Mode then
1397             New_Line;
1398             Put ("Parsing Project File """);
1399             Put (Project_File_Name.all);
1400             Put_Line (""".");
1401             New_Line;
1402          end if;
1403 
1404          --  Set the project parsing verbosity to whatever was specified
1405          --  by a possible -vP switch.
1406 
1407          Prj.Pars.Set_Verbosity (To => Current_Verbosity);
1408 
1409          --  Parse the project file. If there is an error, Main_Project
1410          --  will still be No_Project.
1411 
1412          Prj.Pars.Parse
1413            (Project           => Main_Project,
1414             In_Tree           => Project_Tree,
1415             In_Node_Tree      => Project_Node_Tree,
1416             Project_File_Name => Project_File_Name.all,
1417             Env               => Root_Environment,
1418             Packages_To_Check => Packages_To_Check_By_Gnatmake);
1419 
1420          if Main_Project = No_Project then
1421             Fail ("""" & Project_File_Name.all & """ processing failed");
1422 
1423          elsif Main_Project.Qualifier = Aggregate then
1424             Fail ("aggregate projects are not supported");
1425 
1426          elsif Aggregate_Libraries_In (Project_Tree) then
1427             Fail ("aggregate library projects are not supported");
1428          end if;
1429 
1430          if Opt.Verbose_Mode then
1431             New_Line;
1432             Put ("Parsing of Project File """);
1433             Put (Project_File_Name.all);
1434             Put (""" is finished.");
1435             New_Line;
1436          end if;
1437 
1438          --  Add source directories and object directories to the search paths
1439 
1440          Add_Source_Directories (Main_Project, Project_Tree);
1441          Add_Object_Directories (Main_Project, Project_Tree);
1442       end if;
1443 
1444       Osint.Add_Default_Search_Dirs;
1445 
1446       --  If a project file was specified, but no executable name, put all
1447       --  the mains of the project file (if any) as if there were on the
1448       --  command line.
1449 
1450       if Main_Project /= No_Project and then Osint.Number_Of_Files = 0 then
1451          declare
1452             Main  : String_Element;
1453             Value : String_List_Id := Main_Project.Mains;
1454          begin
1455             while Value /= Prj.Nil_String loop
1456                Main := Project_Tree.Shared.String_Elements.Table (Value);
1457                Osint.Add_File
1458                  (File_Name => Get_Name_String (Main.Value),
1459                   Index     => Main.Index);
1460                Value := Main.Next;
1461             end loop;
1462          end;
1463       end if;
1464 
1465       --  If neither a project file nor an executable were specified, exit
1466       --  displaying the usage if there were no arguments on the command line.
1467 
1468       if Main_Project = No_Project and then Osint.Number_Of_Files = 0 then
1469          if Argument_Count = 0 then
1470             Usage;
1471          else
1472             Try_Help;
1473          end if;
1474 
1475          return;
1476       end if;
1477 
1478       if Verbose_Mode then
1479          New_Line;
1480       end if;
1481 
1482       if Main_Project /= No_Project then
1483 
1484          --  If a project file has been specified, call Clean_Project with the
1485          --  project id of this project file, after resetting the list of
1486          --  processed projects.
1487 
1488          Processed_Projects.Init;
1489          Clean_Project (Main_Project);
1490 
1491       else
1492          --  If no project file has been specified, the work is done in
1493          --  Clean_Executables.
1494 
1495          Clean_Executables;
1496       end if;
1497 
1498       --  In verbose mode, if Delete has not been called, indicate that no file
1499       --  needs to be deleted.
1500 
1501       if Verbose_Mode and (not File_Deleted) then
1502          New_Line;
1503 
1504          if Do_Nothing then
1505             Put_Line ("No file needs to be deleted");
1506          else
1507             Put_Line ("No file has been deleted");
1508          end if;
1509       end if;
1510    end Gnatclean;
1511 
1512    ------------------------
1513    -- In_Extension_Chain --
1514    ------------------------
1515 
1516    function In_Extension_Chain
1517      (Of_Project : Project_Id;
1518       Prj        : Project_Id) return Boolean
1519    is
1520       Proj : Project_Id;
1521 
1522    begin
1523       if Prj = No_Project or else Of_Project = No_Project then
1524          return False;
1525       end if;
1526 
1527       if Of_Project = Prj then
1528          return True;
1529       end if;
1530 
1531       Proj := Of_Project;
1532       while Proj.Extends /= No_Project loop
1533          if Proj.Extends = Prj then
1534             return True;
1535          end if;
1536 
1537          Proj := Proj.Extends;
1538       end loop;
1539 
1540       Proj := Prj;
1541       while Proj.Extends /= No_Project loop
1542          if Proj.Extends = Of_Project then
1543             return True;
1544          end if;
1545 
1546          Proj := Proj.Extends;
1547       end loop;
1548 
1549       return False;
1550    end In_Extension_Chain;
1551 
1552    ----------------
1553    -- Initialize --
1554    ----------------
1555 
1556    procedure Initialize is
1557    begin
1558       if not Initialized then
1559          Initialized := True;
1560 
1561          --  Get default search directories to locate system.ads when calling
1562          --  Targparm.Get_Target_Parameters.
1563 
1564          Osint.Add_Default_Search_Dirs;
1565 
1566          --  Initialize some packages
1567 
1568          Csets.Initialize;
1569          Snames.Initialize;
1570          Stringt.Initialize;
1571 
1572          Prj.Tree.Initialize (Root_Environment, Gnatmake_Flags);
1573 
1574          Project_Node_Tree := new Project_Node_Tree_Data;
1575          Prj.Tree.Initialize (Project_Node_Tree);
1576 
1577          Prj.Initialize (Project_Tree);
1578          Targparm.Get_Target_Parameters;
1579       end if;
1580 
1581       --  Reset global variables
1582 
1583       Free (Object_Directory_Path);
1584       Do_Nothing := False;
1585       File_Deleted := False;
1586       Copyright_Displayed := False;
1587       Usage_Displayed := False;
1588       Free (Project_File_Name);
1589       Main_Project := Prj.No_Project;
1590       All_Projects := False;
1591    end Initialize;
1592 
1593    ----------------------
1594    -- Object_File_Name --
1595    ----------------------
1596 
1597    function Object_File_Name (Source : File_Name_Type) return String is
1598       Src : constant String := Get_Name_String (Source);
1599 
1600    begin
1601       --  If the source name has an extension, then replace it with
1602       --  the Object suffix.
1603 
1604       for Index in reverse Src'First + 1 .. Src'Last loop
1605          if Src (Index) = '.' then
1606             return Src (Src'First .. Index - 1) & Object_Suffix;
1607          end if;
1608       end loop;
1609 
1610       --  If there is no dot, or if it is the first character, just add the
1611       --  ALI suffix.
1612 
1613       return Src & Object_Suffix;
1614    end Object_File_Name;
1615 
1616    --------------------
1617    -- Parse_Cmd_Line --
1618    --------------------
1619 
1620    procedure Parse_Cmd_Line is
1621       Last         : constant Natural := Argument_Count;
1622       Index        : Positive;
1623       Source_Index : Int := 0;
1624 
1625       procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
1626 
1627    begin
1628       --  First, check for --version and --help
1629 
1630       Check_Version_And_Help ("GNATCLEAN", "2003");
1631 
1632       --  First, check for switch -P and, if found and gprclean is available,
1633       --  silently invoke gprclean, with switch --target if not on a native
1634       --  platform.
1635 
1636       declare
1637          Arg_Len       : Positive      := Argument_Count;
1638          Call_Gprclean : Boolean       := False;
1639          Gprclean      : String_Access := null;
1640          Pos           : Natural       := 0;
1641          Success       : Boolean;
1642          Target        : String_Access := null;
1643 
1644       begin
1645          Find_Program_Name;
1646 
1647          if Name_Len >= 9
1648            and then Name_Buffer (Name_Len - 8 .. Name_Len) = "gnatclean"
1649          then
1650             if Name_Len > 9 then
1651                Target  := new String'(Name_Buffer (1 .. Name_Len - 10));
1652                Arg_Len := Arg_Len + 1;
1653             end if;
1654 
1655             for J in 1 .. Argument_Count loop
1656                declare
1657                   Arg : constant String := Argument (J);
1658                begin
1659                   if Arg'Length >= 2
1660                     and then Arg (Arg'First .. Arg'First + 1) = "-P"
1661                   then
1662                      Call_Gprclean := True;
1663                      exit;
1664                   end if;
1665                end;
1666             end loop;
1667 
1668             if Call_Gprclean then
1669                Gprclean := Locate_Exec_On_Path (Exec_Name => "gprclean");
1670 
1671                if Gprclean /= null then
1672                   declare
1673                      Args : Argument_List (1 .. Arg_Len);
1674                   begin
1675                      if Target /= null then
1676                         Args (1) := new String'("--target=" & Target.all);
1677                         Pos := 1;
1678                      end if;
1679 
1680                      for J in 1 .. Argument_Count loop
1681                         Pos := Pos + 1;
1682                         Args (Pos) := new String'(Argument (J));
1683                      end loop;
1684 
1685                      Spawn (Gprclean.all, Args, Success);
1686 
1687                      Free (Gprclean);
1688 
1689                      if Success then
1690                         Exit_Program (E_Success);
1691                      end if;
1692                   end;
1693                end if;
1694             end if;
1695          end if;
1696       end;
1697 
1698       Index := 1;
1699       while Index <= Last loop
1700          declare
1701             Arg : constant String := Argument (Index);
1702 
1703             procedure Bad_Argument;
1704             --  Signal bad argument
1705 
1706             ------------------
1707             -- Bad_Argument --
1708             ------------------
1709 
1710             procedure Bad_Argument is
1711             begin
1712                Fail ("invalid argument """ & Arg & """");
1713             end Bad_Argument;
1714 
1715          begin
1716             if Arg'Length /= 0 then
1717                if Arg (1) = '-' then
1718                   if Arg'Length = 1 then
1719                      Bad_Argument;
1720                   end if;
1721 
1722                   case Arg (2) is
1723                      when '-' =>
1724                         if Arg'Length > Subdirs_Option'Length
1725                           and then
1726                             Arg (1 .. Subdirs_Option'Length) = Subdirs_Option
1727                         then
1728                            Subdirs :=
1729                              new String'
1730                                (Arg (Subdirs_Option'Length + 1 .. Arg'Last));
1731 
1732                         elsif Arg = Makeutl.Unchecked_Shared_Lib_Imports then
1733                            Opt.Unchecked_Shared_Lib_Imports := True;
1734 
1735                         else
1736                            Bad_Argument;
1737                         end if;
1738 
1739                      when 'a' =>
1740                         if Arg'Length < 4 then
1741                            Bad_Argument;
1742                         end if;
1743 
1744                         if Arg (3) = 'O' then
1745                            Add_Lib_Search_Dir (Arg (4 .. Arg'Last));
1746 
1747                         elsif Arg (3) = 'P' then
1748                            Prj.Env.Add_Directories
1749                              (Root_Environment.Project_Path,
1750                               Arg (4 .. Arg'Last));
1751 
1752                         else
1753                            Bad_Argument;
1754                         end if;
1755 
1756                      when 'c' =>
1757                         Compile_Only := True;
1758 
1759                      when 'D' =>
1760                         if Object_Directory_Path /= null then
1761                            Fail ("duplicate -D switch");
1762 
1763                         elsif Project_File_Name /= null then
1764                            Fail ("-P and -D cannot be used simultaneously");
1765                         end if;
1766 
1767                         if Arg'Length > 2 then
1768                            declare
1769                               Dir : constant String := Arg (3 .. Arg'Last);
1770                            begin
1771                               if not Is_Directory (Dir) then
1772                                  Fail (Dir & " is not a directory");
1773                               else
1774                                  Add_Lib_Search_Dir (Dir);
1775                               end if;
1776                            end;
1777 
1778                         else
1779                            if Index = Last then
1780                               Fail ("no directory specified after -D");
1781                            end if;
1782 
1783                            Index := Index + 1;
1784 
1785                            declare
1786                               Dir : constant String := Argument (Index);
1787                            begin
1788                               if not Is_Directory (Dir) then
1789                                  Fail (Dir & " is not a directory");
1790                               else
1791                                  Add_Lib_Search_Dir (Dir);
1792                               end if;
1793                            end;
1794                         end if;
1795 
1796                      when 'e' =>
1797                         if Arg = "-eL" then
1798                            Follow_Links_For_Files := True;
1799                            Follow_Links_For_Dirs  := True;
1800 
1801                         else
1802                            Bad_Argument;
1803                         end if;
1804 
1805                      when 'f' =>
1806                         Force_Deletions := True;
1807                         Directories_Must_Exist_In_Projects := False;
1808 
1809                      when 'F' =>
1810                         Full_Path_Name_For_Brief_Errors := True;
1811 
1812                      when 'h' =>
1813                         Usage;
1814 
1815                      when 'i' =>
1816                         if Arg'Length = 2 then
1817                            Bad_Argument;
1818                         end if;
1819 
1820                         Source_Index := 0;
1821 
1822                         for J in 3 .. Arg'Last loop
1823                            if Arg (J) not in '0' .. '9' then
1824                               Bad_Argument;
1825                            end if;
1826 
1827                            Source_Index :=
1828                              (20 * Source_Index) +
1829                              (Character'Pos (Arg (J)) - Character'Pos ('0'));
1830                         end loop;
1831 
1832                      when 'I' =>
1833                         if Arg = "-I-" then
1834                            Opt.Look_In_Primary_Dir := False;
1835 
1836                         else
1837                            if Arg'Length = 2 then
1838                               Bad_Argument;
1839                            end if;
1840 
1841                            Add_Lib_Search_Dir (Arg (3 .. Arg'Last));
1842                         end if;
1843 
1844                      when 'n' =>
1845                         Do_Nothing := True;
1846 
1847                      when 'P' =>
1848                         if Project_File_Name /= null then
1849                            Fail ("multiple -P switches");
1850 
1851                         elsif Object_Directory_Path /= null then
1852                            Fail ("-D and -P cannot be used simultaneously");
1853 
1854                         end if;
1855 
1856                         if Arg'Length > 2 then
1857                            declare
1858                               Prj : constant String := Arg (3 .. Arg'Last);
1859                            begin
1860                               if Prj'Length > 1
1861                                  and then Prj (Prj'First) = '='
1862                               then
1863                                  Project_File_Name :=
1864                                    new String'
1865                                      (Prj (Prj'First + 1 ..  Prj'Last));
1866                               else
1867                                  Project_File_Name := new String'(Prj);
1868                               end if;
1869                            end;
1870 
1871                         else
1872                            if Index = Last then
1873                               Fail ("no project specified after -P");
1874                            end if;
1875 
1876                            Index := Index + 1;
1877                            Project_File_Name := new String'(Argument (Index));
1878                         end if;
1879 
1880                      when 'q' =>
1881                         Quiet_Output := True;
1882 
1883                      when 'r' =>
1884                         All_Projects := True;
1885 
1886                      when 'v' =>
1887                         if Arg = "-v" then
1888                            Verbose_Mode := True;
1889 
1890                         elsif Arg = "-vP0" then
1891                            Current_Verbosity := Prj.Default;
1892 
1893                         elsif Arg = "-vP1" then
1894                            Current_Verbosity := Prj.Medium;
1895 
1896                         elsif Arg = "-vP2" then
1897                            Current_Verbosity := Prj.High;
1898 
1899                         else
1900                            Bad_Argument;
1901                         end if;
1902 
1903                      when 'X' =>
1904                         if Arg'Length = 2 then
1905                            Bad_Argument;
1906                         end if;
1907 
1908                         declare
1909                            Ext_Asgn  : constant String := Arg (3 .. Arg'Last);
1910                            Start     : Positive := Ext_Asgn'First;
1911                            Stop      : Natural  := Ext_Asgn'Last;
1912                            OK        : Boolean  := True;
1913 
1914                         begin
1915                            if Ext_Asgn (Start) = '"' then
1916                               if Ext_Asgn (Stop) = '"' then
1917                                  Start := Start + 1;
1918                                  Stop  := Stop - 1;
1919 
1920                               else
1921                                  OK := False;
1922                               end if;
1923                            end if;
1924 
1925                            if not OK
1926                              or else not
1927                                Prj.Ext.Check (Root_Environment.External,
1928                                               Ext_Asgn (Start .. Stop))
1929                            then
1930                               Fail
1931                                 ("illegal external assignment '"
1932                                  & Ext_Asgn
1933                                  & "'");
1934                            end if;
1935                         end;
1936 
1937                      when others =>
1938                         Bad_Argument;
1939                   end case;
1940 
1941                else
1942                   Add_File (Arg, Source_Index);
1943                end if;
1944             end if;
1945          end;
1946 
1947          Index := Index + 1;
1948       end loop;
1949    end Parse_Cmd_Line;
1950 
1951    -----------------------
1952    -- Repinfo_File_Name --
1953    -----------------------
1954 
1955    function Repinfo_File_Name (Source : File_Name_Type) return String is
1956    begin
1957       return Get_Name_String (Source) & Repinfo_Suffix;
1958    end Repinfo_File_Name;
1959 
1960    --------------------
1961    -- Tree_File_Name --
1962    --------------------
1963 
1964    function Tree_File_Name (Source : File_Name_Type) return String is
1965       Src : constant String := Get_Name_String (Source);
1966 
1967    begin
1968       --  If source name has an extension, then replace it with the tree suffix
1969 
1970       for Index in reverse Src'First + 1 .. Src'Last loop
1971          if Src (Index) = '.' then
1972             return Src (Src'First .. Index - 1) & Tree_Suffix;
1973          end if;
1974       end loop;
1975 
1976       --  If there is no dot, or if it is the first character, just add the
1977       --  tree suffix.
1978 
1979       return Src & Tree_Suffix;
1980    end Tree_File_Name;
1981 
1982    -----------
1983    -- Usage --
1984    -----------
1985 
1986    procedure Usage is
1987    begin
1988       if not Usage_Displayed then
1989          Usage_Displayed := True;
1990          Display_Copyright;
1991          Put_Line ("Usage: gnatclean [switches] {[-innn] name}");
1992          New_Line;
1993 
1994          Display_Usage_Version_And_Help;
1995 
1996          Put_Line ("  names is one or more file names from which " &
1997                    "the .adb or .ads suffix may be omitted");
1998          Put_Line ("  names may be omitted if -P<project> is specified");
1999          New_Line;
2000 
2001          Put_Line ("  --subdirs=dir real obj/lib/exec dirs are subdirs");
2002          Put_Line ("  " & Makeutl.Unchecked_Shared_Lib_Imports);
2003          Put_Line ("       Allow shared libraries to import static libraries");
2004          New_Line;
2005 
2006          Put_Line ("  -c       Only delete compiler generated files");
2007          Put_Line ("  -D dir   Specify dir as the object library");
2008          Put_Line ("  -eL      Follow symbolic links when processing " &
2009                    "project files");
2010          Put_Line ("  -f       Force deletions of unwritable files");
2011          Put_Line ("  -F       Full project path name " &
2012                    "in brief error messages");
2013          Put_Line ("  -h       Display this message");
2014          Put_Line ("  -innn    Index of unit in source for following names");
2015          Put_Line ("  -n       Nothing to do: only list files to delete");
2016          Put_Line ("  -Pproj   Use GNAT Project File proj");
2017          Put_Line ("  -q       Be quiet/terse");
2018          Put_Line ("  -r       Clean all projects recursively");
2019          Put_Line ("  -v       Verbose mode");
2020          Put_Line ("  -vPx     Specify verbosity when parsing " &
2021                    "GNAT Project Files");
2022          Put_Line ("  -Xnm=val Specify an external reference " &
2023                    "for GNAT Project Files");
2024          New_Line;
2025 
2026          Put_Line ("  -aPdir   Add directory dir to project search path");
2027          New_Line;
2028 
2029          Put_Line ("  -aOdir   Specify ALI/object files search path");
2030          Put_Line ("  -Idir    Like -aOdir");
2031          Put_Line ("  -I-      Don't look for source/library files " &
2032                    "in the default directory");
2033          New_Line;
2034       end if;
2035    end Usage;
2036 
2037 end Clean;