1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2009, 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 ------------------------------------------------------------------------------
29 with Osint
; use Osint
;
34 with Prj
.Tree
; use Prj
.Tree
;
35 with Prj
.Util
; use Prj
.Util
;
36 with Snames
; use Snames
;
37 with Table
; use Table
;
39 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
40 with GNAT
.Directory_Operations
; use GNAT
.Directory_Operations
;
42 with System
.Case_Util
; use System
.Case_Util
;
45 package body Prj
.Makr
is
47 -- Packages of project files where unknown attributes are errors
49 -- All the following need comments ??? All global variables and
50 -- subprograms must be fully commented.
52 Very_Verbose
: Boolean := False;
53 -- Set in call to Initialize to indicate very verbose output
55 Project_File
: Boolean := False;
56 -- True when gnatname is creating/modifying a project file. False when
57 -- gnatname is creating a configuration pragmas file.
59 Tree
: constant Project_Node_Tree_Ref
:= new Project_Node_Tree_Data
;
60 -- The project tree where the project file is parsed
62 Args
: Argument_List_Access
;
63 -- The list of arguments for calls to the compiler to get the unit names
64 -- and kinds (spec or body) in the Ada sources.
66 Path_Name
: String_Access
;
70 Directory_Last
: Natural := 0;
72 Output_Name
: String_Access
;
73 Output_Name_Last
: Natural;
74 Output_Name_Id
: Name_Id
;
76 Project_Naming_File_Name
: String_Access
;
77 -- String (1 .. Output_Name'Length + Naming_File_Suffix'Length);
79 Project_Naming_Last
: Natural;
80 Project_Naming_Id
: Name_Id
:= No_Name
;
82 Source_List_Path
: String_Access
;
83 -- (1 .. Output_Name'Length + Source_List_File_Suffix'Length);
84 Source_List_Last
: Natural;
86 Source_List_FD
: File_Descriptor
;
88 Project_Node
: Project_Node_Id
:= Empty_Node
;
89 Project_Declaration
: Project_Node_Id
:= Empty_Node
;
90 Source_Dirs_List
: Project_Node_Id
:= Empty_Node
;
92 Project_Naming_Node
: Project_Node_Id
:= Empty_Node
;
93 Project_Naming_Decl
: Project_Node_Id
:= Empty_Node
;
94 Naming_Package
: Project_Node_Id
:= Empty_Node
;
95 Naming_Package_Comments
: Project_Node_Id
:= Empty_Node
;
97 Source_Files_Comments
: Project_Node_Id
:= Empty_Node
;
98 Source_Dirs_Comments
: Project_Node_Id
:= Empty_Node
;
99 Source_List_File_Comments
: Project_Node_Id
:= Empty_Node
;
101 Naming_String
: aliased String := "naming";
103 Gnatname_Packages
: aliased String_List
:= (1 => Naming_String
'Access);
105 Packages_To_Check_By_Gnatname
: constant String_List_Access
:=
106 Gnatname_Packages
'Access;
108 function Dup
(Fd
: File_Descriptor
) return File_Descriptor
;
110 procedure Dup2
(Old_Fd
, New_Fd
: File_Descriptor
);
112 Gcc
: constant String := "gcc";
113 Gcc_Path
: String_Access
:= null;
115 Non_Empty_Node
: constant Project_Node_Id
:= 1;
116 -- Used for the With_Clause of the naming project
118 type Matched_Type
is (True, False, Excluded
);
120 Naming_File_Suffix
: constant String := "_naming";
121 Source_List_File_Suffix
: constant String := "_source_list.txt";
123 Output_FD
: File_Descriptor
;
124 -- To save the project file and its naming project file
127 -- Output an empty line
129 procedure Write_A_Char
(C
: Character);
130 -- Write one character to Output_FD
132 procedure Write_A_String
(S
: String);
133 -- Write a String to Output_FD
135 package Processed_Directories
is new Table
.Table
136 (Table_Component_Type
=> String_Access
,
137 Table_Index_Type
=> Natural,
138 Table_Low_Bound
=> 0,
140 Table_Increment
=> 100,
141 Table_Name
=> "Prj.Makr.Processed_Directories");
142 -- The list of already processed directories for each section, to avoid
143 -- processing several times the same directory in the same section.
145 package Source_Directories
is new Table
.Table
146 (Table_Component_Type
=> String_Access
,
147 Table_Index_Type
=> Natural,
148 Table_Low_Bound
=> 0,
150 Table_Increment
=> 100,
151 Table_Name
=> "Prj.Makr.Source_Directories");
152 -- The complete list of directories to be put in attribute Source_Dirs in
155 type Source
is record
162 package Sources
is new Table
.Table
163 (Table_Component_Type
=> Source
,
164 Table_Index_Type
=> Natural,
165 Table_Low_Bound
=> 0,
167 Table_Increment
=> 100,
168 Table_Name
=> "Prj.Makr.Sources");
169 -- The list of Ada sources found, with their unit name and kind, to be put
170 -- in the source attribute and package Naming of the project file, or in
171 -- the pragmas Source_File_Name in the configuration pragmas file.
177 function Dup
(Fd
: File_Descriptor
) return File_Descriptor
is
179 return File_Descriptor
(System
.CRTL
.dup
(Integer (Fd
)));
186 procedure Dup2
(Old_Fd
, New_Fd
: File_Descriptor
) is
188 pragma Warnings
(Off
, Fd
);
190 Fd
:= System
.CRTL
.dup2
(Integer (Old_Fd
), Integer (New_Fd
));
197 procedure Finalize
is
199 pragma Warnings
(Off
, Discard
);
201 Current_Source_Dir
: Project_Node_Id
:= Empty_Node
;
205 -- If there were no already existing project file, or if the parsing
206 -- was unsuccessful, create an empty project node with the correct
207 -- name and its project declaration node.
209 if No
(Project_Node
) then
211 Default_Project_Node
(Of_Kind
=> N_Project
, In_Tree
=> Tree
);
212 Set_Name_Of
(Project_Node
, Tree
, To
=> Output_Name_Id
);
213 Set_Project_Declaration_Of
215 To
=> Default_Project_Node
216 (Of_Kind
=> N_Project_Declaration
, In_Tree
=> Tree
));
222 -- Delete the file if it already exists
225 (Path_Name
(Directory_Last
+ 1 .. Path_Last
),
230 if Opt
.Verbose_Mode
then
231 Output
.Write_Str
("Creating new file """);
232 Output
.Write_Str
(Path_Name
(Directory_Last
+ 1 .. Path_Last
));
233 Output
.Write_Line
("""");
236 Output_FD
:= Create_New_File
237 (Path_Name
(Directory_Last
+ 1 .. Path_Last
),
240 -- Fails if project file cannot be created
242 if Output_FD
= Invalid_FD
then
244 ("cannot create new """ & Path_Name
(1 .. Path_Last
) & """");
249 -- Delete the source list file, if it already exists
253 pragma Warnings
(Off
, Discard
);
256 (Source_List_Path
(1 .. Source_List_Last
),
260 -- And create a new source list file, fail if file cannot be created
262 Source_List_FD
:= Create_New_File
263 (Name
=> Source_List_Path
(1 .. Source_List_Last
),
266 if Source_List_FD
= Invalid_FD
then
268 ("cannot create file """
269 & Source_List_Path
(1 .. Source_List_Last
)
273 if Opt
.Verbose_Mode
then
274 Output
.Write_Str
("Naming project file name is """);
276 (Project_Naming_File_Name
(1 .. Project_Naming_Last
));
277 Output
.Write_Line
("""");
280 -- Create the naming project node
282 Project_Naming_Node
:=
283 Default_Project_Node
(Of_Kind
=> N_Project
, In_Tree
=> Tree
);
284 Set_Name_Of
(Project_Naming_Node
, Tree
, To
=> Project_Naming_Id
);
285 Project_Naming_Decl
:=
287 (Of_Kind
=> N_Project_Declaration
, In_Tree
=> Tree
);
288 Set_Project_Declaration_Of
289 (Project_Naming_Node
, Tree
, Project_Naming_Decl
);
292 (Of_Kind
=> N_Package_Declaration
, In_Tree
=> Tree
);
293 Set_Name_Of
(Naming_Package
, Tree
, To
=> Name_Naming
);
295 -- Add an attribute declaration for Source_Files as an empty list (to
296 -- indicate there are no sources in the naming project) and a package
297 -- Naming (that will be filled later).
300 Decl_Item
: constant Project_Node_Id
:=
302 (Of_Kind
=> N_Declarative_Item
, In_Tree
=> Tree
);
304 Attribute
: constant Project_Node_Id
:=
306 (Of_Kind
=> N_Attribute_Declaration
,
308 And_Expr_Kind
=> List
);
310 Expression
: constant Project_Node_Id
:=
312 (Of_Kind
=> N_Expression
,
314 And_Expr_Kind
=> List
);
316 Term
: constant Project_Node_Id
:=
320 And_Expr_Kind
=> List
);
322 Empty_List
: constant Project_Node_Id
:=
324 (Of_Kind
=> N_Literal_String_List
,
328 Set_First_Declarative_Item_Of
329 (Project_Naming_Decl
, Tree
, To
=> Decl_Item
);
330 Set_Next_Declarative_Item
(Decl_Item
, Tree
, Naming_Package
);
331 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Attribute
);
332 Set_Name_Of
(Attribute
, Tree
, To
=> Name_Source_Files
);
333 Set_Expression_Of
(Attribute
, Tree
, To
=> Expression
);
334 Set_First_Term
(Expression
, Tree
, To
=> Term
);
335 Set_Current_Term
(Term
, Tree
, To
=> Empty_List
);
338 -- Add a with clause on the naming project in the main project, if
339 -- there is not already one.
342 With_Clause
: Project_Node_Id
:=
343 First_With_Clause_Of
(Project_Node
, Tree
);
346 while Present
(With_Clause
) loop
348 Prj
.Tree
.Name_Of
(With_Clause
, Tree
) = Project_Naming_Id
;
349 With_Clause
:= Next_With_Clause_Of
(With_Clause
, Tree
);
352 if No
(With_Clause
) then
353 With_Clause
:= Default_Project_Node
354 (Of_Kind
=> N_With_Clause
, In_Tree
=> Tree
);
355 Set_Next_With_Clause_Of
357 To
=> First_With_Clause_Of
(Project_Node
, Tree
));
358 Set_First_With_Clause_Of
359 (Project_Node
, Tree
, To
=> With_Clause
);
360 Set_Name_Of
(With_Clause
, Tree
, To
=> Project_Naming_Id
);
362 -- We set the project node to something different than
363 -- Empty_Node, so that Prj.PP does not generate a limited
366 Set_Project_Node_Of
(With_Clause
, Tree
, Non_Empty_Node
);
368 Name_Len
:= Project_Naming_Last
;
369 Name_Buffer
(1 .. Name_Len
) :=
370 Project_Naming_File_Name
(1 .. Project_Naming_Last
);
371 Set_String_Value_Of
(With_Clause
, Tree
, To
=> Name_Find
);
375 Project_Declaration
:= Project_Declaration_Of
(Project_Node
, Tree
);
377 -- Add a package Naming in the main project, that is a renaming of
378 -- package Naming in the naming project.
381 Decl_Item
: constant Project_Node_Id
:=
383 (Of_Kind
=> N_Declarative_Item
,
386 Naming
: constant Project_Node_Id
:=
388 (Of_Kind
=> N_Package_Declaration
,
392 Set_Next_Declarative_Item
394 To
=> First_Declarative_Item_Of
(Project_Declaration
, Tree
));
395 Set_First_Declarative_Item_Of
396 (Project_Declaration
, Tree
, To
=> Decl_Item
);
397 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Naming
);
398 Set_Name_Of
(Naming
, Tree
, To
=> Name_Naming
);
399 Set_Project_Of_Renamed_Package_Of
400 (Naming
, Tree
, To
=> Project_Naming_Node
);
402 -- Attach the comments, if any, that were saved for package
405 Tree
.Project_Nodes
.Table
(Naming
).Comments
:=
406 Naming_Package_Comments
;
409 -- Add an attribute declaration for Source_Dirs, initialized as an
413 Decl_Item
: constant Project_Node_Id
:=
415 (Of_Kind
=> N_Declarative_Item
,
418 Attribute
: constant Project_Node_Id
:=
420 (Of_Kind
=> N_Attribute_Declaration
,
422 And_Expr_Kind
=> List
);
424 Expression
: constant Project_Node_Id
:=
426 (Of_Kind
=> N_Expression
,
428 And_Expr_Kind
=> List
);
430 Term
: constant Project_Node_Id
:=
432 (Of_Kind
=> N_Term
, In_Tree
=> Tree
,
433 And_Expr_Kind
=> List
);
436 Set_Next_Declarative_Item
438 To
=> First_Declarative_Item_Of
(Project_Declaration
, Tree
));
439 Set_First_Declarative_Item_Of
440 (Project_Declaration
, Tree
, To
=> Decl_Item
);
441 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Attribute
);
442 Set_Name_Of
(Attribute
, Tree
, To
=> Name_Source_Dirs
);
443 Set_Expression_Of
(Attribute
, Tree
, To
=> Expression
);
444 Set_First_Term
(Expression
, Tree
, To
=> Term
);
447 (Of_Kind
=> N_Literal_String_List
,
449 And_Expr_Kind
=> List
);
450 Set_Current_Term
(Term
, Tree
, To
=> Source_Dirs_List
);
452 -- Attach the comments, if any, that were saved for attribute
455 Tree
.Project_Nodes
.Table
(Attribute
).Comments
:=
456 Source_Dirs_Comments
;
459 -- Put the source directories in attribute Source_Dirs
461 for Source_Dir_Index
in 1 .. Source_Directories
.Last
loop
463 Expression
: constant Project_Node_Id
:=
465 (Of_Kind
=> N_Expression
,
467 And_Expr_Kind
=> Single
);
469 Term
: constant Project_Node_Id
:=
473 And_Expr_Kind
=> Single
);
475 Value
: constant Project_Node_Id
:=
477 (Of_Kind
=> N_Literal_String
,
479 And_Expr_Kind
=> Single
);
482 if No
(Current_Source_Dir
) then
483 Set_First_Expression_In_List
484 (Source_Dirs_List
, Tree
, To
=> Expression
);
486 Set_Next_Expression_In_List
487 (Current_Source_Dir
, Tree
, To
=> Expression
);
490 Current_Source_Dir
:= Expression
;
491 Set_First_Term
(Expression
, Tree
, To
=> Term
);
492 Set_Current_Term
(Term
, Tree
, To
=> Value
);
494 Add_Str_To_Name_Buffer
495 (Source_Directories
.Table
(Source_Dir_Index
).all);
496 Set_String_Value_Of
(Value
, Tree
, To
=> Name_Find
);
500 -- Add an attribute declaration for Source_Files or Source_List_File
501 -- with the source list file name that will be created.
504 Decl_Item
: constant Project_Node_Id
:=
506 (Of_Kind
=> N_Declarative_Item
,
509 Attribute
: constant Project_Node_Id
:=
511 (Of_Kind
=> N_Attribute_Declaration
,
513 And_Expr_Kind
=> Single
);
515 Expression
: constant Project_Node_Id
:=
517 (Of_Kind
=> N_Expression
,
519 And_Expr_Kind
=> Single
);
521 Term
: constant Project_Node_Id
:=
525 And_Expr_Kind
=> Single
);
527 Value
: constant Project_Node_Id
:=
529 (Of_Kind
=> N_Literal_String
,
531 And_Expr_Kind
=> Single
);
534 Set_Next_Declarative_Item
536 To
=> First_Declarative_Item_Of
(Project_Declaration
, Tree
));
537 Set_First_Declarative_Item_Of
538 (Project_Declaration
, Tree
, To
=> Decl_Item
);
539 Set_Current_Item_Node
(Decl_Item
, Tree
, To
=> Attribute
);
541 Set_Name_Of
(Attribute
, Tree
, To
=> Name_Source_List_File
);
542 Set_Expression_Of
(Attribute
, Tree
, To
=> Expression
);
543 Set_First_Term
(Expression
, Tree
, To
=> Term
);
544 Set_Current_Term
(Term
, Tree
, To
=> Value
);
545 Name_Len
:= Source_List_Last
;
546 Name_Buffer
(1 .. Name_Len
) :=
547 Source_List_Path
(1 .. Source_List_Last
);
548 Set_String_Value_Of
(Value
, Tree
, To
=> Name_Find
);
550 -- If there was no comments for attribute Source_List_File, put
551 -- those for Source_Files, if they exist.
553 if Present
(Source_List_File_Comments
) then
554 Tree
.Project_Nodes
.Table
(Attribute
).Comments
:=
555 Source_List_File_Comments
;
557 Tree
.Project_Nodes
.Table
(Attribute
).Comments
:=
558 Source_Files_Comments
;
562 -- Put the sources in the source list files and in the naming
565 for Source_Index
in 1 .. Sources
.Last
loop
567 -- Add the corresponding attribute in the
568 -- Naming package of the naming project.
571 Current_Source
: constant Source
:=
572 Sources
.Table
(Source_Index
);
574 Decl_Item
: constant Project_Node_Id
:=
580 Attribute
: constant Project_Node_Id
:=
583 N_Attribute_Declaration
,
586 Expression
: constant Project_Node_Id
:=
588 (Of_Kind
=> N_Expression
,
589 And_Expr_Kind
=> Single
,
592 Term
: constant Project_Node_Id
:=
595 And_Expr_Kind
=> Single
,
598 Value
: constant Project_Node_Id
:=
600 (Of_Kind
=> N_Literal_String
,
601 And_Expr_Kind
=> Single
,
605 -- Add source file name to the source list file
607 Get_Name_String
(Current_Source
.File_Name
);
608 Add_Char_To_Name_Buffer
(ASCII
.LF
);
609 if Write
(Source_List_FD
,
610 Name_Buffer
(1)'Address,
611 Name_Len
) /= Name_Len
613 Prj
.Com
.Fail
("disk full");
616 -- For an Ada source, add entry in package Naming
618 if Current_Source
.Unit_Name
/= No_Name
then
619 Set_Next_Declarative_Item
621 To
=> First_Declarative_Item_Of
622 (Naming_Package
, Tree
),
624 Set_First_Declarative_Item_Of
628 Set_Current_Item_Node
633 -- Is it a spec or a body?
635 if Current_Source
.Spec
then
645 -- Get the name of the unit
647 Get_Name_String
(Current_Source
.Unit_Name
);
648 To_Lower
(Name_Buffer
(1 .. Name_Len
));
649 Set_Associative_Array_Index_Of
650 (Attribute
, Tree
, To
=> Name_Find
);
653 (Attribute
, Tree
, To
=> Expression
);
655 (Expression
, Tree
, To
=> Term
);
657 (Term
, Tree
, To
=> Value
);
659 -- And set the name of the file
662 (Value
, Tree
, To
=> Current_Source
.File_Name
);
664 (Value
, Tree
, To
=> Current_Source
.Index
);
669 -- Close the source list file
671 Close
(Source_List_FD
);
673 -- Output the project file
677 W_Char
=> Write_A_Char
'Access,
678 W_Eol
=> Write_Eol
'Access,
679 W_Str
=> Write_A_String
'Access,
680 Backward_Compatibility
=> False);
683 -- Delete the naming project file if it already exists
686 (Project_Naming_File_Name
(1 .. Project_Naming_Last
),
691 if Opt
.Verbose_Mode
then
692 Output
.Write_Str
("Creating new naming project file """);
693 Output
.Write_Str
(Project_Naming_File_Name
694 (1 .. Project_Naming_Last
));
695 Output
.Write_Line
("""");
698 Output_FD
:= Create_New_File
699 (Project_Naming_File_Name
(1 .. Project_Naming_Last
),
702 -- Fails if naming project file cannot be created
704 if Output_FD
= Invalid_FD
then
706 ("cannot create new """
707 & Project_Naming_File_Name
(1 .. Project_Naming_Last
)
711 -- Output the naming project file
714 (Project_Naming_Node
, Tree
,
715 W_Char
=> Write_A_Char
'Access,
716 W_Eol
=> Write_Eol
'Access,
717 W_Str
=> Write_A_String
'Access,
718 Backward_Compatibility
=> False);
722 -- For each Ada source, write a pragma Source_File_Name to the
723 -- configuration pragmas file.
725 for Index
in 1 .. Sources
.Last
loop
726 if Sources
.Table
(Index
).Unit_Name
/= No_Name
then
727 Write_A_String
("pragma Source_File_Name");
729 Write_A_String
(" (");
731 (Get_Name_String
(Sources
.Table
(Index
).Unit_Name
));
732 Write_A_String
(",");
735 if Sources
.Table
(Index
).Spec
then
736 Write_A_String
(" Spec_File_Name => """);
739 Write_A_String
(" Body_File_Name => """);
743 (Get_Name_String
(Sources
.Table
(Index
).File_Name
));
745 Write_A_String
("""");
747 if Sources
.Table
(Index
).Index
/= 0 then
748 Write_A_String
(", Index =>");
749 Write_A_String
(Sources
.Table
(Index
).Index
'Img);
752 Write_A_String
(");");
767 Project_File
: Boolean;
768 Preproc_Switches
: Argument_List
;
769 Very_Verbose
: Boolean;
770 Flags
: Processing_Flags
)
773 Makr
.Very_Verbose
:= Initialize
.Very_Verbose
;
774 Makr
.Project_File
:= Initialize
.Project_File
;
776 -- Do some needed initializations
781 Prj
.Initialize
(No_Project_Tree
);
782 Prj
.Tree
.Initialize
(Tree
);
784 Sources
.Set_Last
(0);
785 Source_Directories
.Set_Last
(0);
787 -- Initialize the compiler switches
789 Args
:= new Argument_List
(1 .. Preproc_Switches
'Length + 6);
790 Args
(1) := new String'("-c");
791 Args (2) := new String'("-gnats");
792 Args
(3) := new String'("-gnatu");
793 Args (4 .. 3 + Preproc_Switches'Length) := Preproc_Switches;
794 Args (4 + Preproc_Switches'Length) := new String'("-x");
795 Args
(5 + Preproc_Switches
'Length) := new String'("ada");
797 -- Get the path and file names
800 String (1 .. File_Path'Length + Project_File_Extension'Length);
801 Path_Last := File_Path'Length;
803 if File_Names_Case_Sensitive then
804 Path_Name (1 .. Path_Last) := File_Path;
806 Path_Name (1 .. Path_Last) := To_Lower (File_Path);
809 Path_Name (Path_Last + 1 .. Path_Name'Last) :=
810 Project_File_Extension;
812 -- Get the end of directory information, if any
814 for Index in reverse 1 .. Path_Last loop
815 if Path_Name (Index) = Directory_Separator then
816 Directory_Last := Index;
822 if Path_Last < Project_File_Extension'Length + 1
824 (Path_Last - Project_File_Extension'Length + 1 .. Path_Last)
825 /= Project_File_Extension
827 Path_Last := Path_Name'Last;
830 Output_Name := new String'(To_Lower
(Path_Name
(1 .. Path_Last
)));
831 Output_Name_Last
:= Output_Name
'Last - 4;
833 -- If there is already a project file with the specified name, parse
834 -- it to get the components that are not automatically generated.
836 if Is_Regular_File
(Output_Name
(1 .. Path_Last
)) then
837 if Opt
.Verbose_Mode
then
838 Output
.Write_Str
("Parsing already existing project file """);
839 Output
.Write_Str
(Output_Name
.all);
840 Output
.Write_Line
("""");
845 Project
=> Project_Node
,
846 Project_File_Name
=> Output_Name
.all,
847 Always_Errout_Finalize
=> False,
848 Store_Comments
=> True,
849 Is_Config_File
=> False,
851 Current_Directory
=> Get_Current_Dir
,
852 Packages_To_Check
=> Packages_To_Check_By_Gnatname
);
854 -- Fail if parsing was not successful
856 if No
(Project_Node
) then
857 Fail
("parsing of existing project file failed");
860 -- If parsing was successful, remove the components that are
861 -- automatically generated, if any, so that they will be
862 -- unconditionally added later.
864 -- Remove the with clause for the naming project file
867 With_Clause
: Project_Node_Id
:=
868 First_With_Clause_Of
(Project_Node
, Tree
);
869 Previous
: Project_Node_Id
:= Empty_Node
;
872 while Present
(With_Clause
) loop
873 if Prj
.Tree
.Name_Of
(With_Clause
, Tree
) =
876 if No
(Previous
) then
877 Set_First_With_Clause_Of
879 To
=> Next_With_Clause_Of
(With_Clause
, Tree
));
881 Set_Next_With_Clause_Of
883 To
=> Next_With_Clause_Of
(With_Clause
, Tree
));
889 Previous
:= With_Clause
;
890 With_Clause
:= Next_With_Clause_Of
(With_Clause
, Tree
);
894 -- Remove attribute declarations of Source_Files,
895 -- Source_List_File, Source_Dirs, and the declaration of
896 -- package Naming, if they exist, but preserve the comments
897 -- attached to these nodes.
900 Declaration
: Project_Node_Id
:=
901 First_Declarative_Item_Of
902 (Project_Declaration_Of
903 (Project_Node
, Tree
),
905 Previous
: Project_Node_Id
:= Empty_Node
;
906 Current_Node
: Project_Node_Id
:= Empty_Node
;
909 Kind_Of_Node
: Project_Node_Kind
;
910 Comments
: Project_Node_Id
;
913 while Present
(Declaration
) loop
914 Current_Node
:= Current_Item_Node
(Declaration
, Tree
);
916 Kind_Of_Node
:= Kind_Of
(Current_Node
, Tree
);
918 if Kind_Of_Node
= N_Attribute_Declaration
or else
919 Kind_Of_Node
= N_Package_Declaration
921 Name
:= Prj
.Tree
.Name_Of
(Current_Node
, Tree
);
923 if Name
= Name_Source_Files
or else
924 Name
= Name_Source_List_File
or else
925 Name
= Name_Source_Dirs
or else
929 Tree
.Project_Nodes
.Table
(Current_Node
).Comments
;
931 if Name
= Name_Source_Files
then
932 Source_Files_Comments
:= Comments
;
934 elsif Name
= Name_Source_List_File
then
935 Source_List_File_Comments
:= Comments
;
937 elsif Name
= Name_Source_Dirs
then
938 Source_Dirs_Comments
:= Comments
;
940 elsif Name
= Name_Naming
then
941 Naming_Package_Comments
:= Comments
;
944 if No
(Previous
) then
945 Set_First_Declarative_Item_Of
946 (Project_Declaration_Of
(Project_Node
, Tree
),
948 To
=> Next_Declarative_Item
949 (Declaration
, Tree
));
952 Set_Next_Declarative_Item
954 To
=> Next_Declarative_Item
955 (Declaration
, Tree
));
959 Previous
:= Declaration
;
963 Declaration
:= Next_Declarative_Item
(Declaration
, Tree
);
969 if Directory_Last
/= 0 then
970 Output_Name
(1 .. Output_Name_Last
- Directory_Last
) :=
971 Output_Name
(Directory_Last
+ 1 .. Output_Name_Last
);
972 Output_Name_Last
:= Output_Name_Last
- Directory_Last
;
975 -- Get the project name id
977 Name_Len
:= Output_Name_Last
;
978 Name_Buffer
(1 .. Name_Len
) := Output_Name
(1 .. Name_Len
);
979 Output_Name_Id
:= Name_Find
;
981 -- Create the project naming file name
983 Project_Naming_Last
:= Output_Name_Last
;
984 Project_Naming_File_Name
:=
985 new String'(Output_Name (1 .. Output_Name_Last) &
987 Project_File_Extension);
988 Project_Naming_Last :=
989 Project_Naming_Last + Naming_File_Suffix'Length;
991 -- Get the project naming id
993 Name_Len := Project_Naming_Last;
994 Name_Buffer (1 .. Name_Len) :=
995 Project_Naming_File_Name (1 .. Name_Len);
996 Project_Naming_Id := Name_Find;
998 Project_Naming_Last :=
999 Project_Naming_Last + Project_File_Extension'Length;
1001 -- Create the source list file name
1003 Source_List_Last := Output_Name_Last;
1005 new String'(Output_Name
(1 .. Output_Name_Last
) &
1006 Source_List_File_Suffix
);
1008 Output_Name_Last
+ Source_List_File_Suffix
'Length;
1010 -- Add the project file extension to the project name
1013 (Output_Name_Last
+ 1 ..
1014 Output_Name_Last
+ Project_File_Extension
'Length) :=
1015 Project_File_Extension
;
1016 Output_Name_Last
:= Output_Name_Last
+ Project_File_Extension
'Length;
1020 -- Change the current directory to the directory of the project file,
1021 -- if any directory information is specified.
1023 if Directory_Last
/= 0 then
1025 Change_Dir
(Path_Name
(1 .. Directory_Last
));
1027 when Directory_Error
=>
1029 ("unknown directory """
1030 & Path_Name
(1 .. Directory_Last
)
1041 (Directories
: Argument_List
;
1042 Name_Patterns
: Regexp_List
;
1043 Excluded_Patterns
: Regexp_List
;
1044 Foreign_Patterns
: Regexp_List
)
1046 procedure Process_Directory
(Dir_Name
: String; Recursively
: Boolean);
1047 -- Look for Ada and foreign sources in a directory, according to the
1048 -- patterns. When Recursively is True, after looking for sources in
1049 -- Dir_Name, look also in its subdirectories, if any.
1051 -----------------------
1052 -- Process_Directory --
1053 -----------------------
1055 procedure Process_Directory
(Dir_Name
: String; Recursively
: Boolean) is
1056 Matched
: Matched_Type
:= False;
1057 Str
: String (1 .. 2_000
);
1058 Canon
: String (1 .. 2_000
);
1061 Do_Process
: Boolean := True;
1063 Temp_File_Name
: String_Access
:= null;
1064 Save_Last_Source_Index
: Natural := 0;
1065 File_Name_Id
: Name_Id
:= No_Name
;
1067 Current_Source
: Source
;
1070 -- Avoid processing the same directory more than once
1072 for Index
in 1 .. Processed_Directories
.Last
loop
1073 if Processed_Directories
.Table
(Index
).all = Dir_Name
then
1074 Do_Process
:= False;
1080 if Opt
.Verbose_Mode
then
1081 Output
.Write_Str
("Processing directory """);
1082 Output
.Write_Str
(Dir_Name
);
1083 Output
.Write_Line
("""");
1086 Processed_Directories
. Increment_Last
;
1087 Processed_Directories
.Table
(Processed_Directories
.Last
) :=
1088 new String'(Dir_Name);
1090 -- Get the source file names from the directory. Fails if the
1091 -- directory does not exist.
1094 Open (Dir, Dir_Name);
1096 when Directory_Error =>
1097 Prj.Com.Fail ("cannot open directory """ & Dir_Name & """");
1100 -- Process each regular file in the directory
1103 Read (Dir, Str, Last);
1104 exit File_Loop when Last = 0;
1106 -- Copy the file name and put it in canonical case to match
1107 -- against the patterns that have themselves already been put
1108 -- in canonical case.
1110 Canon (1 .. Last) := Str (1 .. Last);
1111 Canonical_Case_File_Name (Canon (1 .. Last));
1114 (Dir_Name & Directory_Separator & Str (1 .. Last))
1119 Name_Buffer (1 .. Name_Len) := Str (1 .. Last);
1120 File_Name_Id := Name_Find;
1122 -- First, check if the file name matches at least one of
1123 -- the excluded expressions;
1125 for Index in Excluded_Patterns'Range loop
1127 Match (Canon (1 .. Last), Excluded_Patterns (Index))
1129 Matched := Excluded;
1134 -- If it does not match any of the excluded expressions,
1135 -- check if the file name matches at least one of the
1136 -- regular expressions.
1138 if Matched = True then
1141 for Index in Name_Patterns'Range loop
1144 (Canon (1 .. Last), Name_Patterns (Index))
1153 or else (Matched = True and then Opt.Verbose_Mode)
1155 Output.Write_Str (" Checking """);
1156 Output.Write_Str (Str (1 .. Last));
1157 Output.Write_Line (""": ");
1160 -- If the file name matches one of the regular expressions,
1161 -- parse it to get its unit name.
1163 if Matched = True then
1165 FD : File_Descriptor;
1167 Saved_Output : File_Descriptor;
1168 Saved_Error : File_Descriptor;
1171 -- If we don't have the path of the compiler yet,
1172 -- get it now. The compiler name may have a prefix,
1173 -- so we get the potentially prefixed name.
1175 if Gcc_Path = null then
1177 Prefix_Gcc : String_Access :=
1178 Program_Name (Gcc, "gnatname");
1181 Locate_Exec_On_Path (Prefix_Gcc.all);
1185 if Gcc_Path = null then
1186 Prj.Com.Fail ("could not locate " & Gcc);
1190 -- If we don't have yet the file name of the
1191 -- temporary file, get it now.
1193 if Temp_File_Name = null then
1194 Create_Temp_File (FD, Temp_File_Name);
1196 if FD = Invalid_FD then
1198 ("could not create temporary file");
1202 Delete_File (Temp_File_Name.all, Success);
1205 Args (Args'Last) := new String'
1207 Directory_Separator
&
1210 -- Create the temporary file
1212 FD
:= Create_Output_Text_File
1213 (Name
=> Temp_File_Name
.all);
1215 if FD
= Invalid_FD
then
1217 ("could not create temporary file");
1220 -- Save the standard output and error
1222 Saved_Output
:= Dup
(Standout
);
1223 Saved_Error
:= Dup
(Standerr
);
1225 -- Set standard output and error to the temporary file
1227 Dup2
(FD
, Standout
);
1228 Dup2
(FD
, Standerr
);
1230 -- And spawn the compiler
1232 Spawn
(Gcc_Path
.all, Args
.all, Success
);
1234 -- Restore the standard output and error
1236 Dup2
(Saved_Output
, Standout
);
1237 Dup2
(Saved_Error
, Standerr
);
1239 -- Close the temporary file
1243 -- And close the saved standard output and error to
1244 -- avoid too many file descriptors.
1246 Close
(Saved_Output
);
1247 Close
(Saved_Error
);
1249 -- Now that standard output is restored, check if
1250 -- the compiler ran correctly.
1252 -- Read the lines of the temporary file:
1253 -- they should contain the kind and name of the unit.
1257 Text_Line
: String (1 .. 1_000
);
1258 Text_Last
: Natural;
1261 Open
(File
, Temp_File_Name
.all);
1263 if not Is_Valid
(File
) then
1265 ("could not read temporary file");
1268 Save_Last_Source_Index
:= Sources
.Last
;
1270 if End_Of_File
(File
) then
1271 if Opt
.Verbose_Mode
then
1273 Output
.Write_Str
(" (process died) ");
1278 Line_Loop
: while not End_Of_File
(File
) loop
1279 Get_Line
(File
, Text_Line
, Text_Last
);
1281 -- Find the first closing parenthesis
1283 Char_Loop
: for J
in 1 .. Text_Last
loop
1284 if Text_Line
(J
) = ')' then
1286 Text_Line
(1 .. 4) = "Unit"
1288 -- Add entry to Sources table
1291 Name_Buffer
(1 .. Name_Len
) :=
1292 Text_Line
(6 .. J
- 7);
1294 (Unit_Name
=> Name_Find
,
1295 File_Name
=> File_Name_Id
,
1297 Spec
=> Text_Line
(J
- 5 .. J
) =
1300 Sources
.Append
(Current_Source
);
1309 if Save_Last_Source_Index
= Sources
.Last
then
1310 if Opt
.Verbose_Mode
then
1311 Output
.Write_Line
(" not a unit");
1316 Save_Last_Source_Index
+ 1
1318 for Index
in Save_Last_Source_Index
+ 1 ..
1321 Sources
.Table
(Index
).Index
:=
1322 Int
(Index
- Save_Last_Source_Index
);
1326 for Index
in Save_Last_Source_Index
+ 1 ..
1329 Current_Source
:= Sources
.Table
(Index
);
1331 if Opt
.Verbose_Mode
then
1332 if Current_Source
.Spec
then
1333 Output
.Write_Str
(" spec of ");
1336 Output
.Write_Str
(" body of ");
1341 (Current_Source
.Unit_Name
));
1348 Delete_File
(Temp_File_Name
.all, Success
);
1352 -- File name matches none of the regular expressions
1355 -- If file is not excluded, see if this is foreign source
1357 if Matched
/= Excluded
then
1358 for Index
in Foreign_Patterns
'Range loop
1359 if Match
(Canon
(1 .. Last
),
1360 Foreign_Patterns
(Index
))
1368 if Very_Verbose
then
1371 Output
.Write_Line
("no match");
1374 Output
.Write_Line
("excluded");
1377 Output
.Write_Line
("foreign source");
1381 if Matched
= True then
1383 -- Add source file name without unit name
1386 Add_Str_To_Name_Buffer
(Canon
(1 .. Last
));
1388 ((File_Name
=> Name_Find
,
1389 Unit_Name
=> No_Name
,
1400 -- If Recursively is True, call itself for each subdirectory.
1401 -- We do that, even when this directory has already been processed,
1402 -- because all of its subdirectories may not have been processed.
1405 Open
(Dir
, Dir_Name
);
1408 Read
(Dir
, Str
, Last
);
1411 -- Do not call itself for "." or ".."
1414 (Dir_Name
& Directory_Separator
& Str
(1 .. Last
))
1415 and then Str
(1 .. Last
) /= "."
1416 and then Str
(1 .. Last
) /= ".."
1419 (Dir_Name
& Directory_Separator
& Str
(1 .. Last
),
1420 Recursively
=> True);
1426 end Process_Directory
;
1428 -- Start of processing for Process
1431 Processed_Directories
.Set_Last
(0);
1433 -- Process each directory
1435 for Index
in Directories
'Range loop
1438 Dir_Name
: constant String := Directories
(Index
).all;
1439 Last
: Natural := Dir_Name
'Last;
1440 Recursively
: Boolean := False;
1442 Canonical
: String (1 .. Dir_Name
'Length) := Dir_Name
;
1445 Canonical_Case_File_Name
(Canonical
);
1448 for J
in 1 .. Source_Directories
.Last
loop
1449 if Source_Directories
.Table
(J
).all = Canonical
then
1456 Source_Directories
.Append
(new String'(Canonical));
1459 if Dir_Name'Length >= 4
1460 and then (Dir_Name (Last - 2 .. Last) = "/**")
1463 Recursively := True;
1466 Process_Directory (Dir_Name (Dir_Name'First .. Last), Recursively);
1475 procedure Write_A_Char (C : Character) is
1477 Write_A_String ((1 => C));
1484 procedure Write_Eol is
1486 Write_A_String ((1 => ASCII.LF));
1489 --------------------
1490 -- Write_A_String --
1491 --------------------
1493 procedure Write_A_String (S : String) is
1494 Str : String (1 .. S'Length);
1497 if S'Length > 0 then
1500 if Write (Output_FD, Str (1)'Address, Str'Length) /= Str'Length then
1501 Prj.Com.Fail ("disk full");