libgpr2_24.0.0_eda3c693/tools/src/gprls-process.adb

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
------------------------------------------------------------------------------
--                                                                          --
--                           GPR2 PROJECT MANAGER                           --
--                                                                          --
--                     Copyright (C) 2019-2023, AdaCore                     --
--                                                                          --
-- This is  free  software;  you can redistribute it and/or modify it under --
-- terms of the  GNU  General Public License as published by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  This software is distributed in the hope  that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License for more details.  You should have received  a copy of the  GNU  --
-- General Public License distributed with GNAT; see file  COPYING. If not, --
-- see <http://www.gnu.org/licenses/>.                                      --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Calendar;
with Ada.Command_Line;
with Ada.Containers.Indefinite_Ordered_Sets;
with Ada.Directories;
with Ada.Text_IO;

with GPR2.Unit;
with GPR2.Containers;
with GPR2.Log;
with GPR2.Message;
with GPR2.Path_Name;
with GPR2.Path_Name.Set;
with GPR2.Project.Source.Artifact;
with GPR2.Project.Source.Part_Set;
with GPR2.Project.Source.Set;
with GPR2.Project.Tree;
with GPR2.Project.Unit_Info;
with GPR2.Project.View;
with GPR2.Source_Info.Parser.Registry;
with GPR2.Version;

with GPRtools.Options;
with GPRtools.Util;

with GPRls.Common;
with GPRls.Gnatdist;
with GPRls.Options;

function GPRls.Process
  (Opt : in out GPRls.Options.Object) return Ada.Command_Line.Exit_Status
is

   use Ada;

   use GPR2;
   use GPR2.Project.Source.Set;
   use all type GPR2.Unit.Library_Unit_Type;

   use GPRls.Common;
   use GPRls.Options;

   use GPRtools;
   use GPRtools.Util;

   Tree : Project.Tree.Object renames Opt.Tree.all;

   procedure Display_Paths;

   procedure Put (Str : String; Lvl : Verbosity_Level);
   pragma Unreferenced (Put);
   --  Call Ada.Text_IO.Put (Str) if Opt.Verbosity is at least Lvl

   procedure Put_Line (Str : String; Lvl : Verbosity_Level);
   --  Call Ada.Text_IO.Put_Line (Str) if Opt.Verbosity is at least Lvl

   procedure Show_Tree_Load_Errors;
   --  Print errors/warnings following a project tree load

   -------------------
   -- Display_Paths --
   -------------------

   procedure Display_Paths is
      Curr_Dir : constant String := Directories.Current_Directory;
      Obj_Path : Path_Name.Set.Object;

      function Mask_Current (Dir : String) return String is
        (if Dir (Dir'First .. Dir'Last - 1) = Curr_Dir
         then "<Current_Directory>" else Dir);

   begin
      Text_IO.New_Line;
      Version.Display ("GPRLS", "2018", Version_String => Version.Long_Value);

      --  Source search path

      Text_IO.New_Line;
      Text_IO.Put_Line ("Source Search Path:");

      for V of Tree loop
         if V.Kind in With_Source_Dirs_Kind then
            for Src of V.Source_Directories loop
               Text_IO.Put_Line ("   " & String (Src.Dir_Name));
            end loop;
         end if;
      end loop;

      if Tree.Has_Runtime_Project then
         for Src of Tree.Runtime_Project.Source_Directories loop
            Text_IO.Put_Line ("   " & String (Src.Dir_Name));
         end loop;
      end if;

      --  Object search path

      for V of Tree loop
         case V.Kind is
            when K_Standard =>
               Obj_Path.Append (V.Object_Directory);
            when K_Library | K_Aggregate_Library =>
               Obj_Path.Append (V.Library_Ali_Directory);
            when others =>
               null;
         end case;
      end loop;

      if Tree.Has_Runtime_Project then
         Obj_Path.Append (Tree.Runtime_Project.Object_Directory);
      end if;

      Text_IO.New_Line;
      Text_IO.Put_Line ("Object Search Path:");

      for P of Obj_Path loop
         Text_IO.Put_Line ("   " & P.Dir_Name);
      end loop;

      --  Project search path

      Text_IO.New_Line;
      Text_IO.Put_Line ("Project Search Path:");

      for P of Tree.Project_Search_Paths loop
         Text_IO.Put_Line ("   " & Mask_Current (P.Dir_Name));
      end loop;

      Text_IO.New_Line;
   end Display_Paths;

   ---------
   -- Put --
   ---------

   procedure Put (Str : String; Lvl : Verbosity_Level) is
   begin
      if Opt.Verbosity >= Lvl then
         Text_IO.Put (Str);
      end if;
   end Put;

   --------------
   -- Put_Line --
   --------------

   procedure Put_Line (Str : String; Lvl : Verbosity_Level) is
   begin
      if Opt.Verbosity >= Lvl then
         Text_IO.Put_Line (Str);
      end if;
   end Put_Line;

   ---------------------------
   -- Show_Tree_Load_Errors --
   ---------------------------

   procedure Show_Tree_Load_Errors is
   begin
      if Tree.Log_Messages.Has_Error then
         --  In case both warnings and errors are present, only displpay the
         --  errors as they are probably responsible for the warnings.

         for C in Tree.Log_Messages.Iterate
           (Information => False,
            Warning     => False,
            Error       => True,
            Read        => False,
            Unread      => True)
         loop
            Put_Line (Log.Element (C).Format, Quiet);
         end loop;
      else
         for C in Tree.Log_Messages.Iterate
           (Information => Opt.Verbose,
            Warning     => Opt.Warnings,
            Error       => False,
            Read        => False,
            Unread      => True)
         loop
            Put_Line (Log.Element (C).Format, Regular);
         end loop;
      end if;
   end Show_Tree_Load_Errors;

begin
   --  Load the project tree

   if not GPRtools.Options.Load_Project
     (Opt,
      Absent_Dir_Error   => Project.Tree.Warning,
      Handle_Information => Opt.Verbose,
      Handle_Lint        => Opt.Verbose)
   then
      if Opt.Project_File.Is_Defined then
         Text_IO.Put_Line
           ("gprls: unable to process project file "
            & String (Opt.Filename.Name));
      else
         Text_IO.Put_Line
           ("gprls: unable to process default project file in "
            & String (Opt.Filename.Name));
      end if;

      return Command_Line.Failure;
   end if;

   if Opt.Only_Display_Paths then
      --  For the "gprls -v" usage

      Display_Paths;
      return Command_Line.Success;
   end if;

   Show_Tree_Load_Errors;

   pragma Assert
     (not Opt.Source_Parser
      or else GPR2.Source_Info.Parser.Registry.Exists
        (Ada_Language, Source_Info.Source), "Source parser is not registered");

   pragma Assert
     (GPR2.Source_Info.Parser.Registry.Exists
        (Ada_Language, Source_Info.LI), "ALI parser is not registered");

   --  Make sure the sources are up to date

   Tree.Update_Sources
     (Backends     => (Source_Info.Source => Opt.Source_Parser,
                       Source_Info.LI     => True),
      With_Runtime => (Opt.Gnatdist or else Opt.With_Predefined_Units));

   --
   --  Main processing
   --

   declare
      --  Cache some data to speed up later processing.
      --  The maps should have Value_Path keys to support case-insensitive FS.

      use type Project.Source.Object;
      use type Project.View.Object;
      use all type Project.Source.Naming_Exception_Kind;

      function Path_Equal
        (Left, Right : Project.Source.Source_Part) return Boolean
      is (Left.Source = Right.Source
          and then Left.Source.View.Namespace_Roots.First_Element =
            Right.Source.View.Namespace_Roots.First_Element
          and then Left.Index = Right.Index);

      type One_Type is range -1 .. 1;

      function Compare
        (Left, Right : Project.View.Object) return One_Type
      is (if Left < Right then -1 elsif Left = Right then 0 else 1);

      --  function Compare (Left, Right : Path_Name.Full_Name) return One_Type
      --  is (if Left < Right then -1 elsif Left = Right then 0 else 1);

      function Compare
        (Left, Right : Project.Source.Object) return One_Type
      is (if Left < Right then -1 elsif Left = Right then 0 else 1);

      function Compare
        (Left, Right : Unit_Index) return One_Type
      is (if Left < Right then -1 elsif Left = Right then 0 else 1);

      function Path_Less
        (Left, Right : Project.Source.Source_Part) return Boolean
      is (case Compare (Left.Source.View.Namespace_Roots.First_Element,
                        Right.Source.View.Namespace_Roots.First_Element)
          is
             when -1 => True,
             when  1 => False,
             when  0 =>
               (case Compare (Left.Source, Right.Source) is
                when -1 => True,
                when  1 => False,
                when  0 => Compare (Left.Index, Right.Index) = -1));

      package Sources_By_Path is new Ada.Containers.Indefinite_Ordered_Sets
        (Project.Source.Source_Part, "<" => Path_Less, "=" => Path_Equal);

      type File_Status is
        (OK,        -- matching timestamp
         Not_Same); -- non matching timestamp

      No_Obj : constant String := "<no_obj>";

      Position : Sources_By_Path.Cursor;
      Inserted : Boolean;

      Remains : GPR2.Containers.Value_Set := Opt.Files;
      Sources : Sources_By_Path.Set;
      --  The sources that we will browse. This set may be:
      --     - All the project sources when not in closure mode, possibly from
      --       the full project tree if All_Projects is True
      --     - The sources associated with the files given on the CL
      --     - In closure mode and no file given on the CL, the root project's
      --       main sources.

      procedure Display_Closures;

      procedure Display_Gnatdist;

      procedure Display_Normal;

      ----------------------
      -- Display_Closures --
      ----------------------

      procedure Display_Closures is
         Closures : Project.Source.Part_Set.Object (Sorted => True);
      begin
         if Sources.Is_Empty then
            Finish_Program (E_Errors, "no main specified for closure");
         end if;

         for S of Sources loop
            declare
               Deps : constant Project.Source.Part_Set.Object :=
                        S.Source.Dependencies (Closure => True,
                                               Sorted  => False);
            begin
               if Deps.Is_Empty then
                  --  If no dependencies, use only this one because without ALI
                  --  file we don't know dependency even on itself.

                  Closures.Insert (S);
               else
                  Closures.Union (Deps);
               end if;
            end;
         end loop;

         declare
            package String_Sorting is new String_Vector.Generic_Sorting;

            Output : String_Vector.Vector;
         begin
            for R of Closures loop
               if not R.Source.Is_Runtime then
                  if not GPR2.Project.Source.Artifact.Dependency
                           (R.Source, R.Index).Is_Defined
                  then
                     Text_IO.Put_Line
                       (File => Text_IO.Standard_Error,
                        Item =>
                          String (R.Source.View.Path_Name.Simple_Name) &
                          ": WARNING: the closure for " &
                          String (R.Source.Path_Name.Simple_Name) &
                          " is incomplete");
                  end if;

                  if R.Index not in Multi_Unit_Index then
                     Output.Append (R.Source.Path_Name.Value);
                  else
                     Output.Append
                       (R.Source.Path_Name.Value & " @" & R.Index'Image);
                  end if;
               end if;
            end loop;

            String_Sorting.Sort (Output);

            for O of Output loop
               Text_IO.Put_Line (O);
            end loop;
         end;

      end Display_Closures;

      ----------------------
      -- Display_Gnatdist --
      ----------------------

      procedure Display_Gnatdist is

         function Has_Dependency
           (S : Project.Source.Source_Part) return Boolean;

         --------------------
         -- Has_Dependency --
         --------------------

         function Has_Dependency
           (S : Project.Source.Source_Part) return Boolean is
         begin
            return GPR2.Project.Source.Artifact.Dependency
              (S.Source, S.Index).Is_Defined;
         end Has_Dependency;

         No_ALI : Boolean := True;

      begin
         for S of Sources loop
            if S.Index = 0 then
               for CU of S.Source.Units loop
                  if Has_Dependency ((S.Source, Index => CU.Index)) then
                     No_ALI := False;
                     Gnatdist.Output_ALI (S.Source, CU.Index);
                  end if;
               end loop;

            elsif Has_Dependency (S) then
               No_ALI := False;
               Gnatdist.Output_ALI (S.Source, S.Index);
            end if;

            if No_ALI then
               Gnatdist.Output_No_ALI (S.Source, S.Index);
            end if;
         end loop;
      end Display_Gnatdist;

      --------------------
      -- Display_Normal --
      --------------------

      procedure Display_Normal is
         use type Source_Info.Backend;

         procedure Output_Source
           (S          : Project.Source.Object;
            Idx        : Unit_Index;
            Build_Time : Ada.Calendar.Time;
            A          : Project.Source.Artifact.Object :=
                           Project.Source.Artifact.Undefined);

         -------------------
         -- Output_Source --
         -------------------

         procedure Output_Source
           (S          : Project.Source.Object;
            Idx        : Unit_Index;
            Build_Time : Ada.Calendar.Time;
            A          : Project.Source.Artifact.Object :=
                           Project.Source.Artifact.Undefined)
         is
            use type Calendar.Time;

            package SI renames GPR2.Source_Info;

            Status    : File_Status;
            Artifacts : Project.Source.Artifact.Object;

            function Check_Object_Code return Boolean;
            --  Returns true if source has object code and set Artifacts

            function No_Trail_Zero (Item : String) return String;
            --  Remove trailing zeroes with possible dot and leading space

            -----------------------
            -- Check_Object_Code --
            -----------------------

            function Check_Object_Code return Boolean is
               package PSA renames Project.Source.Artifact;
            begin
               if A.Is_Defined then
                  Artifacts := A;
               else
                  Artifacts := PSA.Create
                    (S,
                     Filter => (PSA.Object_File => True,
                                others          => False));
               end if;

               return Artifacts.Has_Object_Code;
            end Check_Object_Code;

            -------------------
            -- No_Trail_Zero --
            -------------------

            function No_Trail_Zero (Item : String) return String is
            begin
               for J in reverse Item'Range loop
                  if Item (J) /= '0' then
                     return Item
                       (Item'First +
                          (if Item (Item'First) = ' ' then 1 else 0) ..
                            J - (if Item (J) = '.' then 1 else 0));
                  end if;
               end loop;

               return Item;
            end No_Trail_Zero;

         begin
            --  For now we stick to the timestamp-based logic: if time stamps
            --  are equal, assume the file didn't change.

            if Build_Time = S.Timestamp (ALI => True)
              or else
                (not SI.Parser.Registry.Exists (S.Language, SI.None)
                 and then Check_Object_Code
                 and then Artifacts.Object_Code (Index => Idx).Exists
                 and then S.Timestamp (ALI => False) <
                        Artifacts.Object_Code (Index => Idx).Modification_Time)
            then
               Status := OK;

            else
               Status := Not_Same;
            end if;

            if Opt.Verbose then
               Text_IO.Put ("     Source => ");
               Text_IO.Put (S.Path_Name.Value);
               if S.Has_Index then
                  Text_IO.Put (" @");
                  Text_IO.Put (Idx'Image);
               end if;

               case Status is
                  when OK =>
                     Text_IO.Put (" unchanged");

                  when Not_Same =>
                     Text_IO.Put (" modified");
               end case;

            else
               if not Opt.Selective_Output then
                  Text_IO.Put ("    ");

                  case Status is
                     when OK =>
                        Text_IO.Put ("  OK ");

                     when Not_Same =>
                        Text_IO.Put (" DIF ");

                        if GPR2.Is_Debug ('F') then
                           if S.Is_Parsed (Idx) then
                              Text_IO.Put (S.Used_Backend (Idx)'Img);
                              Text_IO.Put (' ');

                              if S.Build_Timestamp (Idx) /=
                                S.Timestamp (ALI => True)
                              then
                                 Text_IO.Put
                                   (No_Trail_Zero
                                      (Duration'Image
                                           (S.Timestamp (ALI => True) -
                                                S.Build_Timestamp (Idx))));
                                 Text_IO.Put (' ');
                              end if;

                           else
                              Text_IO.Put ("not parsed ");
                           end if;
                        end if;
                  end case;
               end if;

               Text_IO.Put
                 (if S.Is_Runtime and then Opt.Hide_Runtime_Directory
                  then String (S.Path_Name.Simple_Name)
                  else S.Path_Name.Value);

               if Idx /= No_Index then
                  Text_IO.Put (" at index" & Idx'Image);
               end if;
            end if;

            Text_IO.New_Line;
         end Output_Source;

      begin
         for S of Sources loop
            declare
               use Project.Source;
               View      : constant Project.View.Object := S.Source.View;
               Artifacts : constant Project.Source.Artifact.Object :=
                             Project.Source.Artifact.Create
                               (S.Source,
                                Filter => (Artifact.Dependency_File => True,
                                           Artifact.Object_File     => True,
                                           others                   => False));
               Main_Unit : GPR2.Unit.Object;

               procedure Print_Unit_From
                 (Src : GPR2.Unit.Source_Unit_Identifier);

               function  Print_Unit (U_Sec : GPR2.Unit.Object) return Boolean;

               procedure Print_Object (Index : Unit_Index);

               procedure Print_Object (U_Sec : GPR2.Unit.Object);

               procedure Dependency_Output
                 (Dep_Source : Project.Source.Object;
                  Index      : Unit_Index;
                  Timestamp  : Ada.Calendar.Time);

               function Has_Dependency (Index : Unit_Index) return Boolean is
                 (Artifacts.Has_Dependency (Index)
                  and then
                    (Artifacts.Dependency (Index).Exists
                     or else Opt.Source_Parser));

               -----------------------
               -- Dependency_Output --
               -----------------------

               procedure Dependency_Output
                 (Dep_Source : Project.Source.Object;
                  Index      : Unit_Index;
                  Timestamp  : Ada.Calendar.Time) is
               begin
                  if Opt.With_Predefined_Units
                    or else not Dep_Source.Is_Runtime
                  then
                     Text_IO.Put ("   ");
                     Output_Source (S          => Dep_Source,
                                    Idx        => Index,
                                    Build_Time => Timestamp);
                  end if;
               end Dependency_Output;

               ------------------
               -- Print_Object --
               ------------------

               procedure Print_Object (Index : GPR2.Unit_Index) is
                  Obj_File : GPR2.Path_Name.Object;
               begin
                  if Opt.Print_Object_Files
                    and then not S.Source.Is_Aggregated
                  then
                     Obj_File := Artifacts.Object_Code (Index);

                     if Obj_File.Exists then
                        Text_IO.Put_Line (Obj_File.Value);
                     else
                        Text_IO.Put_Line (No_Obj);
                     end if;
                  end if;
               end Print_Object;

               ------------------
               -- Print_Object --
               ------------------

               procedure Print_Object (U_Sec : GPR2.Unit.Object) is
                  Unit_Info : Project.Unit_Info.Object;
               begin
                  Print_Object (U_Sec.Index);

                  if Opt.Print_Units and then Print_Unit (U_Sec) then
                     null;
                  end if;

                  if Opt.Print_Sources and then not Opt.Dependency_Mode then
                     Output_Source
                       (S.Source, S.Index, S.Source.Build_Timestamp (S.Index),
                        Artifacts);
                  end if;

                  if Opt.Verbose then
                     Unit_Info := S.Source.View.Unit (U_Sec.Name);

                     if Unit_Info.Has_Spec then
                        Print_Unit_From (Unit_Info.Spec);
                     end if;

                     if Unit_Info.Has_Body then
                        Print_Unit_From (Unit_Info.Main_Body);
                     end if;

                     for S of Unit_Info.Separates loop
                        Print_Unit_From (S);
                     end loop;
                  end if;
               end Print_Object;

               ----------------
               -- Print_Unit --
               ----------------

               function  Print_Unit
                 (U_Sec : GPR2.Unit.Object) return Boolean
               is
                  use type GPR2.Unit.Object;
               begin
                  if not Main_Unit.Is_Defined then
                     Main_Unit := U_Sec;
                  elsif Main_Unit = U_Sec then
                     return False;
                  end if;

                  if Opt.Verbose then
                     Text_IO.Put_Line ("   Unit =>");
                     Text_IO.Put ("     Name   => ");
                     Text_IO.Put (String (U_Sec.Name));
                     Text_IO.New_Line;

                     Text_IO.Put_Line
                       ("     Kind   => "
                        & (case U_Sec.Library_Item_Kind is
                             when GPR2.Unit.Is_Package    => "package",
                             when GPR2.Unit.Is_Subprogram => "subprogram",
                             when GPR2.Unit.Is_No_Body    => "no-body")
                        & ' '
                        & (case U_Sec.Kind is
                             when GPR2.Unit.Spec_Kind  => "spec",
                             when GPR2.Unit.Body_Kind  => "body",
                             when GPR2.Unit.S_Separate => "separate"));

                     if U_Sec.Is_Any_Flag_Set then
                        Text_IO.Put ("     Flags  =>");

                        for Flag in GPR2.Unit.Flag'Range loop
                           if U_Sec.Is_Flag_Set (Flag) then
                              Text_IO.Put (' ' & GPR2.Unit.Image (Flag));
                           end if;
                        end loop;

                        Text_IO.New_Line;
                     end if;
                  else
                     Text_IO.Put_Line ("   " & String (U_Sec.Name));
                  end if;

                  return True;
               end Print_Unit;

               ---------------------
               -- Print_Unit_From --
               ---------------------

               procedure Print_Unit_From
                 (Src : GPR2.Unit.Source_Unit_Identifier)
               is
                  U_Src : constant Project.Source.Object :=
                            View.Source (Src.Source);
               begin
                  if not Opt.Print_Units
                    or else
                      (Print_Unit (U_Src.Unit (Src.Index))
                       and then not Opt.Dependency_Mode
                       and then Opt.Print_Sources)
                  then
                     Output_Source (U_Src, Src.Index,
                                    U_Src.Build_Timestamp (Src.Index));
                  end if;
               end Print_Unit_From;

            begin
               if not S.Source.Has_Units then
                  Print_Object (No_Index);

                  if Opt.Print_Sources and then not Opt.Dependency_Mode then
                     Output_Source
                       (S.Source, No_Index,
                        S.Source.Build_Timestamp (No_Index),
                        Artifacts);
                  end if;

               elsif S.Index = No_Index then
                  for U_Sec of S.Source.Units loop
                     if Has_Dependency (U_Sec.Index) then
                        Print_Object (U_Sec);
                        exit when not Opt.Verbose;
                     end if;
                  end loop;

               elsif Has_Dependency (S.Index) then
                  Print_Object (S.Source.Unit (S.Index));
               end if;

               if Opt.Dependency_Mode and then Opt.Print_Sources then
                  if Opt.Verbose then
                     Text_IO.Put_Line ("   depends upon");
                  end if;

                  S.Source.Dependencies
                    (S.Index, Dependency_Output'Access);
               end if;
            end;
         end loop;
      end Display_Normal;

      View   : GPR2.Project.View.Object;
      Filter : GPR2.Project.Iterator_Control :=
                 GPR2.Project.Default_Iterator;

   begin
      if Opt.Verbose then
         Display_Paths;
      end if;

      if not Opt.Files.Is_Empty then
         --  Fill the various caches to get the sources from simple filenames
         --  and artefacts.

         for CV in
           Tree.Iterate ((Project.I_Extended => False, others => True))
         loop
            for S of Project.Tree.Element (CV).Sources loop
               declare
                  use Project.Source.Artifact;

                  Artifacts : Project.Source.Artifact.Object;
                  Dismiss   : Boolean with Unreferenced;

                  function Insert_Prefer_Body
                    (Key   : Filename_Type;
                     Kind  : GPR2.Unit.Library_Unit_Type;
                     Index : Unit_Index) return Boolean;

                  ------------------------
                  -- Insert_Prefer_Body --
                  ------------------------

                  function Insert_Prefer_Body
                    (Key   : Filename_Type;
                     Kind  : GPR2.Unit.Library_Unit_Type;
                     Index : Unit_Index) return Boolean
                  is
                     procedure Do_Insert (Index : Unit_Index);

                     ---------------
                     -- Do_Insert --
                     ---------------

                     procedure Do_Insert (Index : Unit_Index)
                     is
                        Position : Sources_By_Path.Cursor;
                        Inserted : Boolean;

                     begin
                        Sources.Insert ((S, Index), Position, Inserted);

                        if not Inserted
                          and then S.Is_Aggregated
                                   < Sources (Position).Source.Is_Aggregated
                        then
                           --  Prefer none aggregated, more information there

                           Sources.Replace_Element (Position, (S, Index));
                        end if;
                     end Do_Insert;

                  begin
                     if Kind /= GPR2.Unit.S_Spec
                       and then Opt.Files.Contains (String (Key))
                     then
                        Remains.Exclude (String (Key));

                        if S.Has_Units and then Index = No_Index then
                           for CU of S.Units loop
                              if CU.Kind not in
                                GPR2.Unit.S_Spec | GPR2.Unit.S_Separate
                              then
                                 Do_Insert (CU.Index);
                              end if;
                           end loop;

                        else
                           Do_Insert (Index);
                        end if;

                        return True;
                     end if;

                     return False;
                  end Insert_Prefer_Body;

                  function Insert_Prefer_Body
                    (Kind  : GPR2.Unit.Library_Unit_Type;
                     Index : Unit_Index) return Boolean
                  is
                    ((Artifacts.Has_Dependency (Index)
                     and then
                       (Insert_Prefer_Body
                          (Artifacts.Dependency (Index).Simple_Name,
                           Kind, Index)
                        or else
                        Insert_Prefer_Body
                          (Artifacts.Dependency (Index).Base_Filename,
                           Kind, Index)))
                     or else
                       (Artifacts.Has_Object_Code (Index)
                        and then
                        Insert_Prefer_Body
                          (Artifacts.Object_Code (Index).Simple_Name,
                           Kind, Index)));

                  use GPR2.Project.Source;

               begin
                  if not Insert_Prefer_Body
                    (S.Path_Name.Simple_Name, GPR2.Unit.S_Body, No_Index)
                  then
                     Artifacts := GPR2.Project.Source.Artifact.Create
                       (S, Filter => (Artifact.Dependency_File => True,
                                      Artifact.Object_File     => True,
                                      others                   => False));

                     if S.Has_Units then
                        for CU of S.Units loop
                           exit when Insert_Prefer_Body (CU.Kind, CU.Index);
                        end loop;
                     else
                        Dismiss := Insert_Prefer_Body (S.Kind, No_Index);
                     end if;
                  end if;
               end;
            end loop;
         end loop;

         --
         --  All along, we will exclude non-ada sources.
         --

         --  Fill the Sources set with the files given on the CL.
         --  Print "Can't find source for ..." if a file can't be matched with
         --  a compilable source from the root project (or from the project
         --   tree if All_Projects is set).

         for F of Remains loop
            Text_IO.Put_Line ("Can't find source for " & F);
         end loop;

      elsif Opt.Closure_Mode then
         --  If none was provided, then:
         --     - Either we're in closure mode, and we want to use the mains
         --       from the root project.

         if Tree.Root_Project.Has_Mains
           and then Tree.Root_Project.Mains.Is_Empty
         then
            Util.Output_Messages (Opt);
            GPRtools.Util.Fail_Program ("problems with main sources");
         end if;

         for S of Tree.Root_Project.Sources loop
            if Tree.Root_Project.Has_Mains
              and then S.Is_Main
              and then (not GPR2.Is_Debug ('1')
                        or else S.Language = Ada_Language)
            then
               Sources.Insert ((S, No_Index));
            end if;
         end loop;

      elsif Opt.All_Projects then
         --  - Or we're not, and we will use all the compilable sources (from
         --    the root project or the entire tree, depending on All_Sources).

         Filter (GPR2.Project.I_Runtime) := Opt.With_Predefined_Units;

         for C in Tree.Iterate (Kind => Filter) loop
            View := GPR2.Project.Tree.Element (C);

            if not View.Is_Extended then
               for Src of View.Sources (Compilable_Only => True) loop
                  if not GPR2.Is_Debug ('1')
                    or else Src.Language = Ada_Language
                  then
                     if Src.Has_Units then
                        for CU of Src.Units loop
                           if Src.Is_Compilable (CU.Index) then
                              Sources.Insert ((Src, CU.Index),
                                              Position, Inserted);
                           end if;
                        end loop;
                     else
                        Sources.Insert ((Src, No_Index), Position, Inserted);
                     end if;

                     --  Source could be already in the set because we
                     --  can have the same project in the All_Views
                     --  twice, one time for aggregated project another
                     --  time for the imported project. Besides that we
                     --  can have the same source in the aggregated
                     --  project and in the aggregating library project.

                     if not Inserted
                       and then Src.Is_Aggregated
                         < Sources_By_Path.Element
                             (Position).Source.Is_Aggregated
                     then
                        --  We prefer Is_Aggregated = False because it
                        --  has object files.
                        if Src.Has_Units then
                           for CU of Src.Units loop
                              Sources.Replace_Element
                                (Position, (Src, CU.Index));
                           end loop;
                        else
                           Sources.Replace_Element (Position, (Src, No_Index));
                        end if;
                     end if;
                  end if;
               end loop;
            end if;
         end loop;

      else
         for Src of Tree.Root_Project.Sources (Compilable_Only => True) loop
            if not GPR2.Is_Debug ('1')
              or else Src.Language = Ada_Language
            then
               if Src.Has_Units then
                  for CU of Src.Units loop
                     if Src.Is_Compilable (CU.Index) then
                        Sources.Insert ((Src, CU.Index));
                     end if;
                  end loop;
               else
                  Sources.Insert ((Src, No_Index));
               end if;
            end if;
         end loop;
      end if;

      --  Do nothing if no source was found

      if Sources.Is_Empty then
         return Command_Line.Success;
      end if;

      --  Check all sources and notify when no ALI file is present

      if not Opt.Source_Parser and then not Opt.Gnatdist then
         for S of Sources loop
            if S.Source.Has_Units then
               declare
                  Other : constant GPR2.Project.Source.Source_Part :=
                            S.Source.Other_Part_Unchecked (S.Index);
               begin
                  if S.Source.Kind (S.Index) /= Unit.S_Separate
                    and then not S.Source.Is_Parsed (S.Index)
                    and then
                      (not Other.Source.Is_Defined
                       or else not Other.Source.Is_Parsed (Other.Index))
                  then
                     if Opt.Closure_Mode then
                        Text_IO.Put_Line
                          (File => Text_IO.Standard_Error,
                           Item =>
                             String (S.Source.View.Path_Name.Simple_Name) &
                             ": WARNING: the closure for " &
                             String (S.Source.Path_Name.Simple_Name) &
                             " is incomplete");
                     end if;

                     if S.Source.Has_Naming_Exception
                       and then S.Source.Naming_Exception
                         = Project.Source.Multi_Unit
                     then
                        --  In case of multi-unit we have no information
                        --  until the unit is compiled. There is no need to
                        --  report that there is missing ALI in this case.
                        --  But we report that the status for this file is
                        --  unknown.

                        Text_IO.Put_Line
                          ("UNKNOWN status for unit " &
                             String (S.Source.Unit_Name (S.Index)) & " in " &
                             S.Source.Path_Name.Value & " at index" &
                             S.Index'Image);

                     else
                        Text_IO.Put_Line
                          ("Can't find ALI file for " &
                             S.Source.Path_Name.Value);
                     end if;
                  end if;
               end;
            end if;
         end loop;
      end if;

      --  We gathered all the sources:
      --  Process them according to the chosen mode.

      if Opt.Closure_Mode then
         Display_Closures;

      elsif Opt.Gnatdist then
         Display_Gnatdist;

      else
         Display_Normal;
         --  List the project sources (or the subset given in the CL) that have
         --  compilation artifacts (.o/.ali) i.e. only the bodies.
         --
         --  The options -o, -u, -s are used to select specific information to
         --  print.
         --
         --  With -d, for every item listed (in non-closure mode) we also
         --  develop the dependencies (D lines of ALI) with their status.
      end if;
   end;

   return Command_Line.Success;

exception
   when Project_Error | Processing_Error =>
      Show_Tree_Load_Errors;

      Finish_Program
        (E_Errors,
         "unable to process project file " & String (Opt.Filename.Name));

      return Command_Line.Failure;
end GPRls.Process;