1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2013, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 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. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
28 with Makeutl
; use Makeutl
;
31 with Osint
; use Osint
;
37 with Prj
.Tree
; use Prj
.Tree
;
38 with Prj
.Util
; use Prj
.Util
;
40 with Snames
; use Snames
;
42 with Table
; use Table
;
45 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
46 with GNAT
.Directory_Operations
; use GNAT
.Directory_Operations
;
48 with System
.Case_Util
; use System
.Case_Util
;
52 package body Prj
.Makr
is
54 -- Packages of project files where unknown attributes are errors
56 -- All the following need comments ??? All global variables and
57 -- subprograms must be fully commented.
59 Very_Verbose
: Boolean := False;
60 -- Set in call to Initialize to indicate very verbose output
62 Project_File
: Boolean := False;
63 -- True when gnatname is creating/modifying a project file. False when
64 -- gnatname is creating a configuration pragmas file.
66 Tree
: constant Project_Node_Tree_Ref
:= new Project_Node_Tree_Data
;
67 -- The project tree where the project file is parsed
69 Args
: Argument_List_Access
;
70 -- The list of arguments for calls to the compiler to get the unit names
71 -- and kinds (spec or body) in the Ada sources.
73 Path_Name
: String_Access
;
77 Directory_Last
: Natural := 0;
79 Output_Name
: String_Access
;
80 Output_Name_Last
: Natural;
81 Output_Name_Id
: Name_Id
;
83 Project_Naming_File_Name
: String_Access
;
84 -- String (1 .. Output_Name'Length + Naming_File_Suffix'Length);
86 Project_Naming_Last
: Natural;
87 Project_Naming_Id
: Name_Id
:= No_Name
;
89 Source_List_Path
: String_Access
;
90 -- (1 .. Output_Name'Length + Source_List_File_Suffix'Length);
91 Source_List_Last
: Natural;
93 Source_List_FD
: File_Descriptor
;
95 Project_Node
: Project_Node_Id
:= Empty_Node
;
96 Project_Declaration
: Project_Node_Id
:= Empty_Node
;
97 Source_Dirs_List
: Project_Node_Id
:= Empty_Node
;
99 Project_Naming_Node
: Project_Node_Id
:= Empty_Node
;
100 Project_Naming_Decl
: Project_Node_Id
:= Empty_Node
;
101 Naming_Package
: Project_Node_Id
:= Empty_Node
;
102 Naming_Package_Comments
: Project_Node_Id
:= Empty_Node
;
104 Source_Files_Comments
: Project_Node_Id
:= Empty_Node
;
105 Source_Dirs_Comments
: Project_Node_Id
:= Empty_Node
;
106 Source_List_File_Comments
: Project_Node_Id
:= Empty_Node
;
108 Naming_String
: aliased String := "naming";
110 Gnatname_Packages
: aliased String_List
:= (1 => Naming_String
'Access);
112 Packages_To_Check_By_Gnatname
: constant String_List_Access
:=
113 Gnatname_Packages
'Access;
115 function Dup
(Fd
: File_Descriptor
) return File_Descriptor
;
117 procedure Dup2
(Old_Fd
, New_Fd
: File_Descriptor
);
119 Gcc
: constant String := "gcc";
120 Gcc_Path
: String_Access
:= null;
122 Non_Empty_Node
: constant Project_Node_Id
:= 1;
123 -- Used for the With_Clause of the naming project
125 -- Turn off warnings for now around this redefinition of True and False,
126 -- but it really seems a bit horrible to do this redefinition ???
128 pragma Warnings
(Off
);
129 type Matched_Type
is (True, False, Excluded
);
130 pragma Warnings
(On
);
132 Naming_File_Suffix
: constant String := "_naming";
133 Source_List_File_Suffix
: constant String := "_source_list.txt";
135 Output_FD
: File_Descriptor
;
136 -- To save the project file and its naming project file
139 -- Output an empty line
141 procedure Write_A_Char
(C
: Character);
142 -- Write one character to Output_FD
144 procedure Write_A_String
(S
: String);
145 -- Write a String to Output_FD
147 package Processed_Directories
is new Table
.Table
148 (Table_Component_Type
=> String_Access
,
149 Table_Index_Type
=> Natural,
150 Table_Low_Bound
=> 0,
152 Table_Increment
=> 100,
153 Table_Name
=> "Prj.Makr.Processed_Directories");
154 -- The list of already processed directories for each section, to avoid
155 -- processing several times the same directory in the same section.
157 package Source_Directories
is new Table
.Table
158 (Table_Component_Type
=> String_Access
,
159 Table_Index_Type
=> Natural,
160 Table_Low_Bound
=> 0,
162 Table_Increment
=> 100,
163 Table_Name
=> "Prj.Makr.Source_Directories");
164 -- The complete list of directories to be put in attribute Source_Dirs in
167 type Source
is record
174 package Sources
is new Table
.Table
175 (Table_Component_Type
=> Source
,
176 Table_Index_Type
=> Natural,
177 Table_Low_Bound
=> 0,
179 Table_Increment
=> 100,
180 Table_Name
=> "Prj.Makr.Sources");
181 -- The list of Ada sources found, with their unit name and kind, to be put
182 -- in the source attribute and package Naming of the project file, or in
183 -- the pragmas Source_File_Name in the configuration pragmas file.
185 package Source_Files
is new System
.HTable
.Simple_HTable
186 (Header_Num
=> Prj
.Header_Num
,
192 -- Hash table to keep track of source file names, to avoid putting several
193 -- times the same file name in case of multi-unit files.
199 function Dup
(Fd
: File_Descriptor
) return File_Descriptor
is
201 return File_Descriptor
(System
.CRTL
.dup
(Integer (Fd
)));
208 procedure Dup2
(Old_Fd
, New_Fd
: File_Descriptor
) is
210 pragma Warnings
(Off
, Fd
);
212 Fd
:= System
.CRTL
.dup2
(Integer (Old_Fd
), Integer (New_Fd
));
219 procedure Finalize
is
221 pragma Warnings
(Off
, Discard
);
223 Current_Source_Dir
: Project_Node_Id
:= Empty_Node
;
227 -- If there were no already existing project file, or if the parsing
228 -- was unsuccessful, create an empty project node with the correct
229 -- name and its project declaration node.
231 if No
(Project_Node
) then
233 Default_Project_Node
(Of_Kind
=> N_Project
, In_Tree
=> Tree
);
234 Set_Name_Of
(Project_Node
, Tree
, To
=> Output_Name_Id
);
235 Set_Project_Declaration_Of
237 To
=> Default_Project_Node
238 (Of_Kind
=> N_Project_Declaration
, In_Tree
=> Tree
));
244 -- Delete the file if it already exists
247 (Path_Name
(Directory_Last
+ 1 .. Path_Last
),
252 if Opt
.Verbose_Mode
then
253 Output
.Write_Str
("Creating new file """);
254 Output
.Write_Str
(Path_Name
(Directory_Last
+ 1 .. Path_Last
));
255 Output
.Write_Line
("""");
258 Output_FD
:= Create_New_File
259 (Path_Name
(Directory_Last
+ 1 .. Path_Last
),
262 -- Fails if project file cannot be created
264 if Output_FD
= Invalid_FD
then
266 ("cannot create new """ & Path_Name
(1 .. Path_Last
) & """");
271 -- Delete the source list file, if it already exists
275 pragma Warnings
(Off
, Discard
);
278 (Source_List_Path
(1 .. Source_List_Last
),
282 -- And create a new source list file, fail if file cannot be created
284 Source_List_FD
:= Create_New_File
285 (Name
=> Source_List_Path
(1 .. Source_List_Last
),
288 if Source_List_FD
= Invalid_FD
then
290 ("cannot create file """
291 & Source_List_Path
(1 .. Source_List_Last
)
295 if Opt
.Verbose_Mode
then
296 Output
.Write_Str
("Naming project file name is """);
298 (Project_Naming_File_Name
(1 .. Project_Naming_Last
));
299 Output
.Write_Line
("""");
302 -- Create the naming project node
304 Project_Naming_Node
:=
305 Default_Project_Node
(Of_Kind
=> N_Project
, In_Tree
=> Tree
);
306 Set_Name_Of
(Project_Naming_Node
, Tree
, To
=> Project_Naming_Id
);
307 Project_Naming_Decl
:=
309 (Of_Kind
=> N_Project_Declaration
, In_Tree
=> Tree
);
310 Set_Project_Declaration_Of
311 (Project_Naming_Node
, Tree
, Project_Naming_Decl
);
314 (Of_Kind
=> N_Package_Declaration
, In_Tree
=> Tree
);
315 Set_Name_Of
(Naming_Package
, Tree
, To
=> Name_Naming
);
317 -- Add an attribute declaration for Source_Files as an empty list (to
318 -- indicate there are no sources in the naming project) and a package
319 -- Naming (that will be filled later).
322 Decl_Item
: constant Project_Node_Id
:=
324 (Of_Kind
=> N_Declarative_Item
, In_Tree
=> Tree
);
326 Attribute
: constant Project_Node_Id
:=
328 (Of_Kind
=> N_Attribute_Declaration
,
330 And_Expr_Kind
=> List
);
332 Expression
: constant Project_Node_Id
:=
334 (Of_Kind
=> N_Expression
,
336 And_Expr_Kind
=> List
);
338 Term
: constant Project_Node_Id
:=
342 And_Expr_Kind
=> List
);
344 Empty_List
: constant Project_Node_Id
:=
346 (Of_Kind
=> N_Literal_String_List
,
350 Set_First_Declarative_Item_Of
351 (Project_Naming_Decl
, Tree
, To
=> Decl_Item
);
352 Set_Next_Declarative_Item
(Decl_Item
, Tree
, Naming_Package
);
353 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Attribute
);
354 Set_Name_Of
(Attribute
, Tree
, To
=> Name_Source_Files
);
355 Set_Expression_Of
(Attribute
, Tree
, To
=> Expression
);
356 Set_First_Term
(Expression
, Tree
, To
=> Term
);
357 Set_Current_Term
(Term
, Tree
, To
=> Empty_List
);
360 -- Add a with clause on the naming project in the main project, if
361 -- there is not already one.
364 With_Clause
: Project_Node_Id
:=
365 First_With_Clause_Of
(Project_Node
, Tree
);
368 while Present
(With_Clause
) loop
370 Prj
.Tree
.Name_Of
(With_Clause
, Tree
) = Project_Naming_Id
;
371 With_Clause
:= Next_With_Clause_Of
(With_Clause
, Tree
);
374 if No
(With_Clause
) then
375 With_Clause
:= Default_Project_Node
376 (Of_Kind
=> N_With_Clause
, In_Tree
=> Tree
);
377 Set_Next_With_Clause_Of
379 To
=> First_With_Clause_Of
(Project_Node
, Tree
));
380 Set_First_With_Clause_Of
381 (Project_Node
, Tree
, To
=> With_Clause
);
382 Set_Name_Of
(With_Clause
, Tree
, To
=> Project_Naming_Id
);
384 -- We set the project node to something different than
385 -- Empty_Node, so that Prj.PP does not generate a limited
388 Set_Project_Node_Of
(With_Clause
, Tree
, Non_Empty_Node
);
390 Name_Len
:= Project_Naming_Last
;
391 Name_Buffer
(1 .. Name_Len
) :=
392 Project_Naming_File_Name
(1 .. Project_Naming_Last
);
393 Set_String_Value_Of
(With_Clause
, Tree
, To
=> Name_Find
);
397 Project_Declaration
:= Project_Declaration_Of
(Project_Node
, Tree
);
399 -- Add a package Naming in the main project, that is a renaming of
400 -- package Naming in the naming project.
403 Decl_Item
: constant Project_Node_Id
:=
405 (Of_Kind
=> N_Declarative_Item
,
408 Naming
: constant Project_Node_Id
:=
410 (Of_Kind
=> N_Package_Declaration
,
414 Set_Next_Declarative_Item
416 To
=> First_Declarative_Item_Of
(Project_Declaration
, Tree
));
417 Set_First_Declarative_Item_Of
418 (Project_Declaration
, Tree
, To
=> Decl_Item
);
419 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Naming
);
420 Set_Name_Of
(Naming
, Tree
, To
=> Name_Naming
);
421 Set_Project_Of_Renamed_Package_Of
422 (Naming
, Tree
, To
=> Project_Naming_Node
);
424 -- Attach the comments, if any, that were saved for package
427 Tree
.Project_Nodes
.Table
(Naming
).Comments
:=
428 Naming_Package_Comments
;
431 -- Add an attribute declaration for Source_Dirs, initialized as an
435 Decl_Item
: constant Project_Node_Id
:=
437 (Of_Kind
=> N_Declarative_Item
,
440 Attribute
: constant Project_Node_Id
:=
442 (Of_Kind
=> N_Attribute_Declaration
,
444 And_Expr_Kind
=> List
);
446 Expression
: constant Project_Node_Id
:=
448 (Of_Kind
=> N_Expression
,
450 And_Expr_Kind
=> List
);
452 Term
: constant Project_Node_Id
:=
454 (Of_Kind
=> N_Term
, In_Tree
=> Tree
,
455 And_Expr_Kind
=> List
);
458 Set_Next_Declarative_Item
460 To
=> First_Declarative_Item_Of
(Project_Declaration
, Tree
));
461 Set_First_Declarative_Item_Of
462 (Project_Declaration
, Tree
, To
=> Decl_Item
);
463 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Attribute
);
464 Set_Name_Of
(Attribute
, Tree
, To
=> Name_Source_Dirs
);
465 Set_Expression_Of
(Attribute
, Tree
, To
=> Expression
);
466 Set_First_Term
(Expression
, Tree
, To
=> Term
);
469 (Of_Kind
=> N_Literal_String_List
,
471 And_Expr_Kind
=> List
);
472 Set_Current_Term
(Term
, Tree
, To
=> Source_Dirs_List
);
474 -- Attach the comments, if any, that were saved for attribute
477 Tree
.Project_Nodes
.Table
(Attribute
).Comments
:=
478 Source_Dirs_Comments
;
481 -- Put the source directories in attribute Source_Dirs
483 for Source_Dir_Index
in 1 .. Source_Directories
.Last
loop
485 Expression
: constant Project_Node_Id
:=
487 (Of_Kind
=> N_Expression
,
489 And_Expr_Kind
=> Single
);
491 Term
: constant Project_Node_Id
:=
495 And_Expr_Kind
=> Single
);
497 Value
: constant Project_Node_Id
:=
499 (Of_Kind
=> N_Literal_String
,
501 And_Expr_Kind
=> Single
);
504 if No
(Current_Source_Dir
) then
505 Set_First_Expression_In_List
506 (Source_Dirs_List
, Tree
, To
=> Expression
);
508 Set_Next_Expression_In_List
509 (Current_Source_Dir
, Tree
, To
=> Expression
);
512 Current_Source_Dir
:= Expression
;
513 Set_First_Term
(Expression
, Tree
, To
=> Term
);
514 Set_Current_Term
(Term
, Tree
, To
=> Value
);
516 Add_Str_To_Name_Buffer
517 (Source_Directories
.Table
(Source_Dir_Index
).all);
518 Set_String_Value_Of
(Value
, Tree
, To
=> Name_Find
);
522 -- Add an attribute declaration for Source_Files or Source_List_File
523 -- with the source list file name that will be created.
526 Decl_Item
: constant Project_Node_Id
:=
528 (Of_Kind
=> N_Declarative_Item
,
531 Attribute
: constant Project_Node_Id
:=
533 (Of_Kind
=> N_Attribute_Declaration
,
535 And_Expr_Kind
=> Single
);
537 Expression
: constant Project_Node_Id
:=
539 (Of_Kind
=> N_Expression
,
541 And_Expr_Kind
=> Single
);
543 Term
: constant Project_Node_Id
:=
547 And_Expr_Kind
=> Single
);
549 Value
: constant Project_Node_Id
:=
551 (Of_Kind
=> N_Literal_String
,
553 And_Expr_Kind
=> Single
);
556 Set_Next_Declarative_Item
558 To
=> First_Declarative_Item_Of
(Project_Declaration
, Tree
));
559 Set_First_Declarative_Item_Of
560 (Project_Declaration
, Tree
, To
=> Decl_Item
);
561 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Attribute
);
563 Set_Name_Of
(Attribute
, Tree
, To
=> Name_Source_List_File
);
564 Set_Expression_Of
(Attribute
, Tree
, To
=> Expression
);
565 Set_First_Term
(Expression
, Tree
, To
=> Term
);
566 Set_Current_Term
(Term
, Tree
, To
=> Value
);
567 Name_Len
:= Source_List_Last
;
568 Name_Buffer
(1 .. Name_Len
) :=
569 Source_List_Path
(1 .. Source_List_Last
);
570 Set_String_Value_Of
(Value
, Tree
, To
=> Name_Find
);
572 -- If there was no comments for attribute Source_List_File, put
573 -- those for Source_Files, if they exist.
575 if Present
(Source_List_File_Comments
) then
576 Tree
.Project_Nodes
.Table
(Attribute
).Comments
:=
577 Source_List_File_Comments
;
579 Tree
.Project_Nodes
.Table
(Attribute
).Comments
:=
580 Source_Files_Comments
;
584 -- Put the sources in the source list files and in the naming
587 for Source_Index
in 1 .. Sources
.Last
loop
589 -- Add the corresponding attribute in the
590 -- Naming package of the naming project.
593 Current_Source
: constant Source
:=
594 Sources
.Table
(Source_Index
);
596 Decl_Item
: constant Project_Node_Id
:=
602 Attribute
: constant Project_Node_Id
:=
605 N_Attribute_Declaration
,
608 Expression
: constant Project_Node_Id
:=
610 (Of_Kind
=> N_Expression
,
611 And_Expr_Kind
=> Single
,
614 Term
: constant Project_Node_Id
:=
617 And_Expr_Kind
=> Single
,
620 Value
: constant Project_Node_Id
:=
622 (Of_Kind
=> N_Literal_String
,
623 And_Expr_Kind
=> Single
,
627 -- Add source file name to the source list file if it is not
630 if not Source_Files
.Get
(Current_Source
.File_Name
) then
631 Source_Files
.Set
(Current_Source
.File_Name
, True);
632 Get_Name_String
(Current_Source
.File_Name
);
633 Add_Char_To_Name_Buffer
(ASCII
.LF
);
635 if Write
(Source_List_FD
,
636 Name_Buffer
(1)'Address,
637 Name_Len
) /= Name_Len
639 Prj
.Com
.Fail
("disk full");
643 -- For an Ada source, add entry in package Naming
645 if Current_Source
.Unit_Name
/= No_Name
then
646 Set_Next_Declarative_Item
648 To
=> First_Declarative_Item_Of
649 (Naming_Package
, Tree
),
651 Set_First_Declarative_Item_Of
655 Set_Current_Item_Node
660 -- Is it a spec or a body?
662 if Current_Source
.Spec
then
672 -- Get the name of the unit
674 Get_Name_String
(Current_Source
.Unit_Name
);
675 To_Lower
(Name_Buffer
(1 .. Name_Len
));
676 Set_Associative_Array_Index_Of
677 (Attribute
, Tree
, To
=> Name_Find
);
680 (Attribute
, Tree
, To
=> Expression
);
682 (Expression
, Tree
, To
=> Term
);
684 (Term
, Tree
, To
=> Value
);
686 -- And set the name of the file
689 (Value
, Tree
, To
=> Current_Source
.File_Name
);
691 (Value
, Tree
, To
=> Current_Source
.Index
);
696 -- Close the source list file
698 Close
(Source_List_FD
);
700 -- Output the project file
704 W_Char
=> Write_A_Char
'Access,
705 W_Eol
=> Write_Eol
'Access,
706 W_Str
=> Write_A_String
'Access,
707 Backward_Compatibility
=> False,
708 Max_Line_Length
=> 79);
711 -- Delete the naming project file if it already exists
714 (Project_Naming_File_Name
(1 .. Project_Naming_Last
),
719 if Opt
.Verbose_Mode
then
720 Output
.Write_Str
("Creating new naming project file """);
721 Output
.Write_Str
(Project_Naming_File_Name
722 (1 .. Project_Naming_Last
));
723 Output
.Write_Line
("""");
726 Output_FD
:= Create_New_File
727 (Project_Naming_File_Name
(1 .. Project_Naming_Last
),
730 -- Fails if naming project file cannot be created
732 if Output_FD
= Invalid_FD
then
734 ("cannot create new """
735 & Project_Naming_File_Name
(1 .. Project_Naming_Last
)
739 -- Output the naming project file
742 (Project_Naming_Node
, Tree
,
743 W_Char
=> Write_A_Char
'Access,
744 W_Eol
=> Write_Eol
'Access,
745 W_Str
=> Write_A_String
'Access,
746 Backward_Compatibility
=> False);
750 -- For each Ada source, write a pragma Source_File_Name to the
751 -- configuration pragmas file.
753 for Index
in 1 .. Sources
.Last
loop
754 if Sources
.Table
(Index
).Unit_Name
/= No_Name
then
755 Write_A_String
("pragma Source_File_Name");
757 Write_A_String
(" (");
759 (Get_Name_String
(Sources
.Table
(Index
).Unit_Name
));
760 Write_A_String
(",");
763 if Sources
.Table
(Index
).Spec
then
764 Write_A_String
(" Spec_File_Name => """);
767 Write_A_String
(" Body_File_Name => """);
771 (Get_Name_String
(Sources
.Table
(Index
).File_Name
));
773 Write_A_String
("""");
775 if Sources
.Table
(Index
).Index
/= 0 then
776 Write_A_String
(", Index =>");
777 Write_A_String
(Sources
.Table
(Index
).Index
'Img);
780 Write_A_String
(");");
795 Project_File
: Boolean;
796 Preproc_Switches
: Argument_List
;
797 Very_Verbose
: Boolean;
798 Flags
: Processing_Flags
)
801 Makr
.Very_Verbose
:= Initialize
.Very_Verbose
;
802 Makr
.Project_File
:= Initialize
.Project_File
;
804 -- Do some needed initializations
810 Prj
.Initialize
(No_Project_Tree
);
812 Prj
.Tree
.Initialize
(Root_Environment
, Flags
);
813 Prj
.Env
.Initialize_Default_Project_Path
814 (Root_Environment
.Project_Path
,
815 Target_Name
=> Sdefault
.Target_Name
.all);
817 Prj
.Tree
.Initialize
(Tree
);
819 Sources
.Set_Last
(0);
820 Source_Directories
.Set_Last
(0);
822 -- Initialize the compiler switches
824 Args
:= new Argument_List
(1 .. Preproc_Switches
'Length + 6);
825 Args
(1) := new String'("-c");
826 Args (2) := new String'("-gnats");
827 Args
(3) := new String'("-gnatu");
828 Args (4 .. 3 + Preproc_Switches'Length) := Preproc_Switches;
829 Args (4 + Preproc_Switches'Length) := new String'("-x");
830 Args
(5 + Preproc_Switches
'Length) := new String'("ada");
832 -- Get the path and file names
835 String (1 .. File_Path'Length + Project_File_Extension'Length);
836 Path_Last := File_Path'Length;
838 if File_Names_Case_Sensitive then
839 Path_Name (1 .. Path_Last) := File_Path;
841 Path_Name (1 .. Path_Last) := To_Lower (File_Path);
844 Path_Name (Path_Last + 1 .. Path_Name'Last) :=
845 Project_File_Extension;
847 -- Get the end of directory information, if any
849 for Index in reverse 1 .. Path_Last loop
850 if Path_Name (Index) = Directory_Separator then
851 Directory_Last := Index;
857 if Path_Last < Project_File_Extension'Length + 1
859 (Path_Last - Project_File_Extension'Length + 1 .. Path_Last)
860 /= Project_File_Extension
862 Path_Last := Path_Name'Last;
865 Output_Name := new String'(To_Lower
(Path_Name
(1 .. Path_Last
)));
866 Output_Name_Last
:= Output_Name
'Last - 4;
868 -- If there is already a project file with the specified name, parse
869 -- it to get the components that are not automatically generated.
871 if Is_Regular_File
(Output_Name
(1 .. Path_Last
)) then
872 if Opt
.Verbose_Mode
then
873 Output
.Write_Str
("Parsing already existing project file """);
874 Output
.Write_Str
(Output_Name
.all);
875 Output
.Write_Line
("""");
880 Project
=> Project_Node
,
881 Project_File_Name
=> Output_Name
.all,
882 Errout_Handling
=> Part
.Finalize_If_Error
,
883 Store_Comments
=> True,
884 Is_Config_File
=> False,
885 Env
=> Root_Environment
,
886 Current_Directory
=> Get_Current_Dir
,
887 Packages_To_Check
=> Packages_To_Check_By_Gnatname
);
889 -- Fail if parsing was not successful
891 if No
(Project_Node
) then
892 Prj
.Com
.Fail
("parsing of existing project file failed");
894 elsif Project_Qualifier_Of
(Project_Node
, Tree
) = Aggregate
then
895 Prj
.Com
.Fail
("aggregate projects are not supported");
897 elsif Project_Qualifier_Of
(Project_Node
, Tree
) =
900 Prj
.Com
.Fail
("aggregate library projects are not supported");
903 -- If parsing was successful, remove the components that are
904 -- automatically generated, if any, so that they will be
905 -- unconditionally added later.
907 -- Remove the with clause for the naming project file
910 With_Clause
: Project_Node_Id
:=
911 First_With_Clause_Of
(Project_Node
, Tree
);
912 Previous
: Project_Node_Id
:= Empty_Node
;
915 while Present
(With_Clause
) loop
916 if Prj
.Tree
.Name_Of
(With_Clause
, Tree
) =
919 if No
(Previous
) then
920 Set_First_With_Clause_Of
922 To
=> Next_With_Clause_Of
(With_Clause
, Tree
));
924 Set_Next_With_Clause_Of
926 To
=> Next_With_Clause_Of
(With_Clause
, Tree
));
932 Previous
:= With_Clause
;
933 With_Clause
:= Next_With_Clause_Of
(With_Clause
, Tree
);
937 -- Remove attribute declarations of Source_Files,
938 -- Source_List_File, Source_Dirs, and the declaration of
939 -- package Naming, if they exist, but preserve the comments
940 -- attached to these nodes.
943 Declaration
: Project_Node_Id
:=
944 First_Declarative_Item_Of
945 (Project_Declaration_Of
946 (Project_Node
, Tree
),
948 Previous
: Project_Node_Id
:= Empty_Node
;
949 Current_Node
: Project_Node_Id
:= Empty_Node
;
952 Kind_Of_Node
: Project_Node_Kind
;
953 Comments
: Project_Node_Id
;
956 while Present
(Declaration
) loop
957 Current_Node
:= Current_Item_Node
(Declaration
, Tree
);
959 Kind_Of_Node
:= Kind_Of
(Current_Node
, Tree
);
961 if Kind_Of_Node
= N_Attribute_Declaration
or else
962 Kind_Of_Node
= N_Package_Declaration
964 Name
:= Prj
.Tree
.Name_Of
(Current_Node
, Tree
);
966 if Nam_In
(Name
, Name_Source_Files
,
967 Name_Source_List_File
,
972 Tree
.Project_Nodes
.Table
(Current_Node
).Comments
;
974 if Name
= Name_Source_Files
then
975 Source_Files_Comments
:= Comments
;
977 elsif Name
= Name_Source_List_File
then
978 Source_List_File_Comments
:= Comments
;
980 elsif Name
= Name_Source_Dirs
then
981 Source_Dirs_Comments
:= Comments
;
983 elsif Name
= Name_Naming
then
984 Naming_Package_Comments
:= Comments
;
987 if No
(Previous
) then
988 Set_First_Declarative_Item_Of
989 (Project_Declaration_Of
(Project_Node
, Tree
),
991 To
=> Next_Declarative_Item
992 (Declaration
, Tree
));
995 Set_Next_Declarative_Item
997 To
=> Next_Declarative_Item
998 (Declaration
, Tree
));
1002 Previous
:= Declaration
;
1006 Declaration
:= Next_Declarative_Item
(Declaration
, Tree
);
1012 if Directory_Last
/= 0 then
1013 Output_Name
(1 .. Output_Name_Last
- Directory_Last
) :=
1014 Output_Name
(Directory_Last
+ 1 .. Output_Name_Last
);
1015 Output_Name_Last
:= Output_Name_Last
- Directory_Last
;
1018 -- Get the project name id
1020 Name_Len
:= Output_Name_Last
;
1021 Name_Buffer
(1 .. Name_Len
) := Output_Name
(1 .. Name_Len
);
1022 Output_Name_Id
:= Name_Find
;
1024 -- Create the project naming file name
1026 Project_Naming_Last
:= Output_Name_Last
;
1027 Project_Naming_File_Name
:=
1028 new String'(Output_Name (1 .. Output_Name_Last) &
1029 Naming_File_Suffix &
1030 Project_File_Extension);
1031 Project_Naming_Last :=
1032 Project_Naming_Last + Naming_File_Suffix'Length;
1034 -- Get the project naming id
1036 Name_Len := Project_Naming_Last;
1037 Name_Buffer (1 .. Name_Len) :=
1038 Project_Naming_File_Name (1 .. Name_Len);
1039 Project_Naming_Id := Name_Find;
1041 Project_Naming_Last :=
1042 Project_Naming_Last + Project_File_Extension'Length;
1044 -- Create the source list file name
1046 Source_List_Last := Output_Name_Last;
1048 new String'(Output_Name
(1 .. Output_Name_Last
) &
1049 Source_List_File_Suffix
);
1051 Output_Name_Last
+ Source_List_File_Suffix
'Length;
1053 -- Add the project file extension to the project name
1056 (Output_Name_Last
+ 1 ..
1057 Output_Name_Last
+ Project_File_Extension
'Length) :=
1058 Project_File_Extension
;
1059 Output_Name_Last
:= Output_Name_Last
+ Project_File_Extension
'Length;
1061 -- Back up project file if it already exists (not needed in VMS since
1062 -- versioning of files takes care of this requirement on VMS).
1064 if not Hostparm
.OpenVMS
1065 and then not Opt
.No_Backup
1066 and then Is_Regular_File
(Path_Name
(1 .. Path_Last
))
1070 Saved_Path
: constant String :=
1071 Path_Name
(1 .. Path_Last
) & ".saved_";
1078 Img
: constant String := Nmb
'Img;
1081 if not Is_Regular_File
1082 (Saved_Path
& Img
(2 .. Img
'Last))
1085 (Name
=> Path_Name
(1 .. Path_Last
),
1086 Pathname
=> Saved_Path
& Img
(2 .. Img
'Last),
1088 Success
=> Discard
);
1099 -- Change the current directory to the directory of the project file,
1100 -- if any directory information is specified.
1102 if Directory_Last
/= 0 then
1104 Change_Dir
(Path_Name
(1 .. Directory_Last
));
1106 when Directory_Error
=>
1108 ("unknown directory """
1109 & Path_Name
(1 .. Directory_Last
)
1120 (Directories
: Argument_List
;
1121 Name_Patterns
: Regexp_List
;
1122 Excluded_Patterns
: Regexp_List
;
1123 Foreign_Patterns
: Regexp_List
)
1125 procedure Process_Directory
(Dir_Name
: String; Recursively
: Boolean);
1126 -- Look for Ada and foreign sources in a directory, according to the
1127 -- patterns. When Recursively is True, after looking for sources in
1128 -- Dir_Name, look also in its subdirectories, if any.
1130 -----------------------
1131 -- Process_Directory --
1132 -----------------------
1134 procedure Process_Directory
(Dir_Name
: String; Recursively
: Boolean) is
1135 Matched
: Matched_Type
:= False;
1136 Str
: String (1 .. 2_000
);
1137 Canon
: String (1 .. 2_000
);
1140 Do_Process
: Boolean := True;
1142 Temp_File_Name
: String_Access
:= null;
1143 Save_Last_Source_Index
: Natural := 0;
1144 File_Name_Id
: Name_Id
:= No_Name
;
1146 Current_Source
: Source
;
1149 -- Avoid processing the same directory more than once
1151 for Index
in 1 .. Processed_Directories
.Last
loop
1152 if Processed_Directories
.Table
(Index
).all = Dir_Name
then
1153 Do_Process
:= False;
1159 if Opt
.Verbose_Mode
then
1160 Output
.Write_Str
("Processing directory """);
1161 Output
.Write_Str
(Dir_Name
);
1162 Output
.Write_Line
("""");
1165 Processed_Directories
. Increment_Last
;
1166 Processed_Directories
.Table
(Processed_Directories
.Last
) :=
1167 new String'(Dir_Name);
1169 -- Get the source file names from the directory. Fails if the
1170 -- directory does not exist.
1173 Open (Dir, Dir_Name);
1175 when Directory_Error =>
1176 Prj.Com.Fail ("cannot open directory """ & Dir_Name & """");
1179 -- Process each regular file in the directory
1182 Read (Dir, Str, Last);
1183 exit File_Loop when Last = 0;
1185 -- Copy the file name and put it in canonical case to match
1186 -- against the patterns that have themselves already been put
1187 -- in canonical case.
1189 Canon (1 .. Last) := Str (1 .. Last);
1190 Canonical_Case_File_Name (Canon (1 .. Last));
1193 (Dir_Name & Directory_Separator & Str (1 .. Last))
1198 Name_Buffer (1 .. Name_Len) := Str (1 .. Last);
1199 File_Name_Id := Name_Find;
1201 -- First, check if the file name matches at least one of
1202 -- the excluded expressions;
1204 for Index in Excluded_Patterns'Range loop
1206 Match (Canon (1 .. Last), Excluded_Patterns (Index))
1208 Matched := Excluded;
1213 -- If it does not match any of the excluded expressions,
1214 -- check if the file name matches at least one of the
1215 -- regular expressions.
1217 if Matched = True then
1220 for Index in Name_Patterns'Range loop
1223 (Canon (1 .. Last), Name_Patterns (Index))
1232 or else (Matched = True and then Opt.Verbose_Mode)
1234 Output.Write_Str (" Checking """);
1235 Output.Write_Str (Str (1 .. Last));
1236 Output.Write_Line (""": ");
1239 -- If the file name matches one of the regular expressions,
1240 -- parse it to get its unit name.
1242 if Matched = True then
1244 FD : File_Descriptor;
1246 Saved_Output : File_Descriptor;
1247 Saved_Error : File_Descriptor;
1248 Tmp_File : Path_Name_Type;
1251 -- If we don't have the path of the compiler yet,
1252 -- get it now. The compiler name may have a prefix,
1253 -- so we get the potentially prefixed name.
1255 if Gcc_Path = null then
1257 Prefix_Gcc : String_Access :=
1258 Program_Name (Gcc, "gnatname");
1261 Locate_Exec_On_Path (Prefix_Gcc.all);
1265 if Gcc_Path = null then
1266 Prj.Com.Fail ("could not locate " & Gcc);
1270 -- Create the temporary file
1272 Tempdir.Create_Temp_File (FD, Tmp_File);
1274 if FD = Invalid_FD then
1276 ("could not create temporary file");
1280 new String'(Get_Name_String
(Tmp_File
));
1283 -- On VMS, a file created with Create_Temp_File cannot
1284 -- be used to redirect output.
1286 if Hostparm
.OpenVMS
then
1288 Delete_File
(Temp_File_Name
.all, Success
);
1289 FD
:= Create_Output_Text_File
(Temp_File_Name
.all);
1292 Args
(Args
'Last) := new String'
1294 Directory_Separator &
1297 -- Save the standard output and error
1299 Saved_Output := Dup (Standout);
1300 Saved_Error := Dup (Standerr);
1302 -- Set standard output and error to the temporary file
1304 Dup2 (FD, Standout);
1305 Dup2 (FD, Standerr);
1307 -- And spawn the compiler
1309 Spawn (Gcc_Path.all, Args.all, Success);
1311 -- Restore the standard output and error
1313 Dup2 (Saved_Output, Standout);
1314 Dup2 (Saved_Error, Standerr);
1316 -- Close the temporary file
1320 -- And close the saved standard output and error to
1321 -- avoid too many file descriptors.
1323 Close (Saved_Output);
1324 Close (Saved_Error);
1326 -- Now that standard output is restored, check if
1327 -- the compiler ran correctly.
1329 -- Read the lines of the temporary file:
1330 -- they should contain the kind and name of the unit.
1334 Text_Line : String (1 .. 1_000);
1335 Text_Last : Natural;
1338 Open (File, Temp_File_Name.all);
1340 if not Is_Valid (File) then
1342 ("could not read temporary file " &
1343 Temp_File_Name.all);
1346 Save_Last_Source_Index := Sources.Last;
1348 if End_Of_File (File) then
1349 if Opt.Verbose_Mode then
1351 Output.Write_Str (" (process died) ");
1356 Line_Loop : while not End_Of_File (File) loop
1357 Get_Line (File, Text_Line, Text_Last);
1359 -- Find the first closing parenthesis
1361 Char_Loop : for J in 1 .. Text_Last loop
1362 if Text_Line (J) = ')' then
1364 Text_Line (1 .. 4) = "Unit"
1366 -- Add entry to Sources table
1369 Name_Buffer (1 .. Name_Len) :=
1370 Text_Line (6 .. J - 7);
1372 (Unit_Name => Name_Find,
1373 File_Name => File_Name_Id,
1375 Spec => Text_Line (J - 5 .. J) =
1378 Sources.Append (Current_Source);
1387 if Save_Last_Source_Index = Sources.Last then
1388 if Opt.Verbose_Mode then
1389 Output.Write_Line (" not a unit");
1394 Save_Last_Source_Index + 1
1396 for Index in Save_Last_Source_Index + 1 ..
1399 Sources.Table (Index).Index :=
1400 Int (Index - Save_Last_Source_Index);
1404 for Index in Save_Last_Source_Index + 1 ..
1407 Current_Source := Sources.Table (Index);
1409 if Opt.Verbose_Mode then
1410 if Current_Source.Spec then
1411 Output.Write_Str (" spec of ");
1414 Output.Write_Str (" body of ");
1419 (Current_Source.Unit_Name));
1426 Delete_File (Temp_File_Name.all, Success);
1430 -- File name matches none of the regular expressions
1433 -- If file is not excluded, see if this is foreign source
1435 if Matched /= Excluded then
1436 for Index in Foreign_Patterns'Range loop
1437 if Match (Canon (1 .. Last),
1438 Foreign_Patterns (Index))
1446 if Very_Verbose then
1449 Output.Write_Line ("no match");
1452 Output.Write_Line ("excluded");
1455 Output.Write_Line ("foreign source");
1459 if Matched = True then
1461 -- Add source file name without unit name
1464 Add_Str_To_Name_Buffer (Canon (1 .. Last));
1466 ((File_Name => Name_Find,
1467 Unit_Name => No_Name,
1478 -- If Recursively is True, call itself for each subdirectory.
1479 -- We do that, even when this directory has already been processed,
1480 -- because all of its subdirectories may not have been processed.
1483 Open (Dir, Dir_Name);
1486 Read (Dir, Str, Last);
1489 -- Do not call itself for "." or ".."
1492 (Dir_Name & Directory_Separator & Str (1 .. Last))
1493 and then Str (1 .. Last) /= "."
1494 and then Str (1 .. Last) /= ".."
1497 (Dir_Name & Directory_Separator & Str (1 .. Last),
1498 Recursively => True);
1504 end Process_Directory;
1506 -- Start of processing for Process
1509 Processed_Directories.Set_Last (0);
1511 -- Process each directory
1513 for Index in Directories'Range loop
1516 Dir_Name : constant String := Directories (Index).all;
1517 Last : Natural := Dir_Name'Last;
1518 Recursively : Boolean := False;
1520 Canonical : String (1 .. Dir_Name'Length) := Dir_Name;
1523 Canonical_Case_File_Name (Canonical);
1526 for J in 1 .. Source_Directories.Last loop
1527 if Source_Directories.Table (J).all = Canonical then
1534 Source_Directories.Append (new String'(Canonical
));
1537 if Dir_Name
'Length >= 4
1538 and then (Dir_Name
(Last
- 2 .. Last
) = "/**")
1541 Recursively
:= True;
1544 Process_Directory
(Dir_Name
(Dir_Name
'First .. Last
), Recursively
);
1553 procedure Write_A_Char
(C
: Character) is
1555 Write_A_String
((1 => C
));
1562 procedure Write_Eol
is
1564 Write_A_String
((1 => ASCII
.LF
));
1567 --------------------
1568 -- Write_A_String --
1569 --------------------
1571 procedure Write_A_String
(S
: String) is
1572 Str
: String (1 .. S
'Length);
1575 if S
'Length > 0 then
1578 if Write
(Output_FD
, Str
(1)'Address, Str
'Length) /= Str
'Length then
1579 Prj
.Com
.Fail
("disk full");