gcc/
[official-gcc.git] / gcc / ada / prj-makr.adb
blob4f4ab43c08c63aa8d3d4c22ebf6560525a0f8df6
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- P R J . M A K R --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2001-2013, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Csets;
27 with Hostparm;
28 with Makeutl; use Makeutl;
29 with Opt;
30 with Output;
31 with Osint; use Osint;
32 with Prj; use Prj;
33 with Prj.Com;
34 with Prj.Env;
35 with Prj.Part;
36 with Prj.PP;
37 with Prj.Tree; use Prj.Tree;
38 with Prj.Util; use Prj.Util;
39 with Sdefault;
40 with Snames; use Snames;
41 with Stringt;
42 with Table; use Table;
43 with Tempdir;
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;
49 with System.CRTL;
50 with System.HTable;
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;
75 Path_Last : Natural;
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
138 procedure Write_Eol;
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,
151 Table_Initial => 10,
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,
161 Table_Initial => 10,
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
165 -- the project file.
167 type Source is record
168 File_Name : Name_Id;
169 Unit_Name : Name_Id;
170 Index : Int := 0;
171 Spec : Boolean;
172 end record;
174 package Sources is new Table.Table
175 (Table_Component_Type => Source,
176 Table_Index_Type => Natural,
177 Table_Low_Bound => 0,
178 Table_Initial => 10,
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,
187 Element => Boolean,
188 No_Element => False,
189 Key => Name_Id,
190 Hash => Prj.Hash,
191 Equal => "=");
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.
195 ---------
196 -- Dup --
197 ---------
199 function Dup (Fd : File_Descriptor) return File_Descriptor is
200 begin
201 return File_Descriptor (System.CRTL.dup (Integer (Fd)));
202 end Dup;
204 ----------
205 -- Dup2 --
206 ----------
208 procedure Dup2 (Old_Fd, New_Fd : File_Descriptor) is
209 Fd : Integer;
210 pragma Warnings (Off, Fd);
211 begin
212 Fd := System.CRTL.dup2 (Integer (Old_Fd), Integer (New_Fd));
213 end Dup2;
215 --------------
216 -- Finalize --
217 --------------
219 procedure Finalize is
220 Discard : Boolean;
221 pragma Warnings (Off, Discard);
223 Current_Source_Dir : Project_Node_Id := Empty_Node;
225 begin
226 if Project_File then
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
232 Project_Node :=
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
236 (Project_Node, Tree,
237 To => Default_Project_Node
238 (Of_Kind => N_Project_Declaration, In_Tree => Tree));
240 end if;
242 end if;
244 -- Delete the file if it already exists
246 Delete_File
247 (Path_Name (Directory_Last + 1 .. Path_Last),
248 Success => Discard);
250 -- Create a new one
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 ("""");
256 end if;
258 Output_FD := Create_New_File
259 (Path_Name (Directory_Last + 1 .. Path_Last),
260 Fmode => Text);
262 -- Fails if project file cannot be created
264 if Output_FD = Invalid_FD then
265 Prj.Com.Fail
266 ("cannot create new """ & Path_Name (1 .. Path_Last) & """");
267 end if;
269 if Project_File then
271 -- Delete the source list file, if it already exists
273 declare
274 Discard : Boolean;
275 pragma Warnings (Off, Discard);
276 begin
277 Delete_File
278 (Source_List_Path (1 .. Source_List_Last),
279 Success => Discard);
280 end;
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),
286 Fmode => Text);
288 if Source_List_FD = Invalid_FD then
289 Prj.Com.Fail
290 ("cannot create file """
291 & Source_List_Path (1 .. Source_List_Last)
292 & """");
293 end if;
295 if Opt.Verbose_Mode then
296 Output.Write_Str ("Naming project file name is """);
297 Output.Write_Str
298 (Project_Naming_File_Name (1 .. Project_Naming_Last));
299 Output.Write_Line ("""");
300 end if;
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 :=
308 Default_Project_Node
309 (Of_Kind => N_Project_Declaration, In_Tree => Tree);
310 Set_Project_Declaration_Of
311 (Project_Naming_Node, Tree, Project_Naming_Decl);
312 Naming_Package :=
313 Default_Project_Node
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).
321 declare
322 Decl_Item : constant Project_Node_Id :=
323 Default_Project_Node
324 (Of_Kind => N_Declarative_Item, In_Tree => Tree);
326 Attribute : constant Project_Node_Id :=
327 Default_Project_Node
328 (Of_Kind => N_Attribute_Declaration,
329 In_Tree => Tree,
330 And_Expr_Kind => List);
332 Expression : constant Project_Node_Id :=
333 Default_Project_Node
334 (Of_Kind => N_Expression,
335 In_Tree => Tree,
336 And_Expr_Kind => List);
338 Term : constant Project_Node_Id :=
339 Default_Project_Node
340 (Of_Kind => N_Term,
341 In_Tree => Tree,
342 And_Expr_Kind => List);
344 Empty_List : constant Project_Node_Id :=
345 Default_Project_Node
346 (Of_Kind => N_Literal_String_List,
347 In_Tree => Tree);
349 begin
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);
358 end;
360 -- Add a with clause on the naming project in the main project, if
361 -- there is not already one.
363 declare
364 With_Clause : Project_Node_Id :=
365 First_With_Clause_Of (Project_Node, Tree);
367 begin
368 while Present (With_Clause) loop
369 exit when
370 Prj.Tree.Name_Of (With_Clause, Tree) = Project_Naming_Id;
371 With_Clause := Next_With_Clause_Of (With_Clause, Tree);
372 end loop;
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
378 (With_Clause, Tree,
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
386 -- with clause.
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);
394 end if;
395 end;
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.
402 declare
403 Decl_Item : constant Project_Node_Id :=
404 Default_Project_Node
405 (Of_Kind => N_Declarative_Item,
406 In_Tree => Tree);
408 Naming : constant Project_Node_Id :=
409 Default_Project_Node
410 (Of_Kind => N_Package_Declaration,
411 In_Tree => Tree);
413 begin
414 Set_Next_Declarative_Item
415 (Decl_Item, Tree,
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
425 -- Naming.
427 Tree.Project_Nodes.Table (Naming).Comments :=
428 Naming_Package_Comments;
429 end;
431 -- Add an attribute declaration for Source_Dirs, initialized as an
432 -- empty list.
434 declare
435 Decl_Item : constant Project_Node_Id :=
436 Default_Project_Node
437 (Of_Kind => N_Declarative_Item,
438 In_Tree => Tree);
440 Attribute : constant Project_Node_Id :=
441 Default_Project_Node
442 (Of_Kind => N_Attribute_Declaration,
443 In_Tree => Tree,
444 And_Expr_Kind => List);
446 Expression : constant Project_Node_Id :=
447 Default_Project_Node
448 (Of_Kind => N_Expression,
449 In_Tree => Tree,
450 And_Expr_Kind => List);
452 Term : constant Project_Node_Id :=
453 Default_Project_Node
454 (Of_Kind => N_Term, In_Tree => Tree,
455 And_Expr_Kind => List);
457 begin
458 Set_Next_Declarative_Item
459 (Decl_Item, Tree,
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);
467 Source_Dirs_List :=
468 Default_Project_Node
469 (Of_Kind => N_Literal_String_List,
470 In_Tree => Tree,
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
475 -- Source_Dirs.
477 Tree.Project_Nodes.Table (Attribute).Comments :=
478 Source_Dirs_Comments;
479 end;
481 -- Put the source directories in attribute Source_Dirs
483 for Source_Dir_Index in 1 .. Source_Directories.Last loop
484 declare
485 Expression : constant Project_Node_Id :=
486 Default_Project_Node
487 (Of_Kind => N_Expression,
488 In_Tree => Tree,
489 And_Expr_Kind => Single);
491 Term : constant Project_Node_Id :=
492 Default_Project_Node
493 (Of_Kind => N_Term,
494 In_Tree => Tree,
495 And_Expr_Kind => Single);
497 Value : constant Project_Node_Id :=
498 Default_Project_Node
499 (Of_Kind => N_Literal_String,
500 In_Tree => Tree,
501 And_Expr_Kind => Single);
503 begin
504 if No (Current_Source_Dir) then
505 Set_First_Expression_In_List
506 (Source_Dirs_List, Tree, To => Expression);
507 else
508 Set_Next_Expression_In_List
509 (Current_Source_Dir, Tree, To => Expression);
510 end if;
512 Current_Source_Dir := Expression;
513 Set_First_Term (Expression, Tree, To => Term);
514 Set_Current_Term (Term, Tree, To => Value);
515 Name_Len := 0;
516 Add_Str_To_Name_Buffer
517 (Source_Directories.Table (Source_Dir_Index).all);
518 Set_String_Value_Of (Value, Tree, To => Name_Find);
519 end;
520 end loop;
522 -- Add an attribute declaration for Source_Files or Source_List_File
523 -- with the source list file name that will be created.
525 declare
526 Decl_Item : constant Project_Node_Id :=
527 Default_Project_Node
528 (Of_Kind => N_Declarative_Item,
529 In_Tree => Tree);
531 Attribute : constant Project_Node_Id :=
532 Default_Project_Node
533 (Of_Kind => N_Attribute_Declaration,
534 In_Tree => Tree,
535 And_Expr_Kind => Single);
537 Expression : constant Project_Node_Id :=
538 Default_Project_Node
539 (Of_Kind => N_Expression,
540 In_Tree => Tree,
541 And_Expr_Kind => Single);
543 Term : constant Project_Node_Id :=
544 Default_Project_Node
545 (Of_Kind => N_Term,
546 In_Tree => Tree,
547 And_Expr_Kind => Single);
549 Value : constant Project_Node_Id :=
550 Default_Project_Node
551 (Of_Kind => N_Literal_String,
552 In_Tree => Tree,
553 And_Expr_Kind => Single);
555 begin
556 Set_Next_Declarative_Item
557 (Decl_Item, Tree,
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;
578 else
579 Tree.Project_Nodes.Table (Attribute).Comments :=
580 Source_Files_Comments;
581 end if;
582 end;
584 -- Put the sources in the source list files and in the naming
585 -- project.
587 for Source_Index in 1 .. Sources.Last loop
589 -- Add the corresponding attribute in the
590 -- Naming package of the naming project.
592 declare
593 Current_Source : constant Source :=
594 Sources.Table (Source_Index);
596 Decl_Item : constant Project_Node_Id :=
597 Default_Project_Node
598 (Of_Kind =>
599 N_Declarative_Item,
600 In_Tree => Tree);
602 Attribute : constant Project_Node_Id :=
603 Default_Project_Node
604 (Of_Kind =>
605 N_Attribute_Declaration,
606 In_Tree => Tree);
608 Expression : constant Project_Node_Id :=
609 Default_Project_Node
610 (Of_Kind => N_Expression,
611 And_Expr_Kind => Single,
612 In_Tree => Tree);
614 Term : constant Project_Node_Id :=
615 Default_Project_Node
616 (Of_Kind => N_Term,
617 And_Expr_Kind => Single,
618 In_Tree => Tree);
620 Value : constant Project_Node_Id :=
621 Default_Project_Node
622 (Of_Kind => N_Literal_String,
623 And_Expr_Kind => Single,
624 In_Tree => Tree);
626 begin
627 -- Add source file name to the source list file if it is not
628 -- already there.
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
638 then
639 Prj.Com.Fail ("disk full");
640 end if;
641 end if;
643 -- For an Ada source, add entry in package Naming
645 if Current_Source.Unit_Name /= No_Name then
646 Set_Next_Declarative_Item
647 (Decl_Item,
648 To => First_Declarative_Item_Of
649 (Naming_Package, Tree),
650 In_Tree => Tree);
651 Set_First_Declarative_Item_Of
652 (Naming_Package,
653 To => Decl_Item,
654 In_Tree => Tree);
655 Set_Current_Item_Node
656 (Decl_Item,
657 To => Attribute,
658 In_Tree => Tree);
660 -- Is it a spec or a body?
662 if Current_Source.Spec then
663 Set_Name_Of
664 (Attribute, Tree,
665 To => Name_Spec);
666 else
667 Set_Name_Of
668 (Attribute, Tree,
669 To => Name_Body);
670 end if;
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);
679 Set_Expression_Of
680 (Attribute, Tree, To => Expression);
681 Set_First_Term
682 (Expression, Tree, To => Term);
683 Set_Current_Term
684 (Term, Tree, To => Value);
686 -- And set the name of the file
688 Set_String_Value_Of
689 (Value, Tree, To => Current_Source.File_Name);
690 Set_Source_Index_Of
691 (Value, Tree, To => Current_Source.Index);
692 end if;
693 end;
694 end loop;
696 -- Close the source list file
698 Close (Source_List_FD);
700 -- Output the project file
702 Prj.PP.Pretty_Print
703 (Project_Node, Tree,
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);
709 Close (Output_FD);
711 -- Delete the naming project file if it already exists
713 Delete_File
714 (Project_Naming_File_Name (1 .. Project_Naming_Last),
715 Success => Discard);
717 -- Create a new one
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 ("""");
724 end if;
726 Output_FD := Create_New_File
727 (Project_Naming_File_Name (1 .. Project_Naming_Last),
728 Fmode => Text);
730 -- Fails if naming project file cannot be created
732 if Output_FD = Invalid_FD then
733 Prj.Com.Fail
734 ("cannot create new """
735 & Project_Naming_File_Name (1 .. Project_Naming_Last)
736 & """");
737 end if;
739 -- Output the naming project file
741 Prj.PP.Pretty_Print
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);
747 Close (Output_FD);
749 else
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");
756 Write_Eol;
757 Write_A_String (" (");
758 Write_A_String
759 (Get_Name_String (Sources.Table (Index).Unit_Name));
760 Write_A_String (",");
761 Write_Eol;
763 if Sources.Table (Index).Spec then
764 Write_A_String (" Spec_File_Name => """);
766 else
767 Write_A_String (" Body_File_Name => """);
768 end if;
770 Write_A_String
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);
778 end if;
780 Write_A_String (");");
781 Write_Eol;
782 end if;
783 end loop;
785 Close (Output_FD);
786 end if;
787 end Finalize;
789 ----------------
790 -- Initialize --
791 ----------------
793 procedure Initialize
794 (File_Path : String;
795 Project_File : Boolean;
796 Preproc_Switches : Argument_List;
797 Very_Verbose : Boolean;
798 Flags : Processing_Flags)
800 begin
801 Makr.Very_Verbose := Initialize.Very_Verbose;
802 Makr.Project_File := Initialize.Project_File;
804 -- Do some needed initializations
806 Csets.Initialize;
807 Snames.Initialize;
808 Stringt.Initialize;
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
834 Path_Name := new
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;
840 else
841 Path_Name (1 .. Path_Last) := To_Lower (File_Path);
842 end if;
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;
852 exit;
853 end if;
854 end loop;
856 if Project_File then
857 if Path_Last < Project_File_Extension'Length + 1
858 or else Path_Name
859 (Path_Last - Project_File_Extension'Length + 1 .. Path_Last)
860 /= Project_File_Extension
861 then
862 Path_Last := Path_Name'Last;
863 end if;
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 ("""");
876 end if;
878 Part.Parse
879 (In_Tree => Tree,
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) =
898 Aggregate_Library
899 then
900 Prj.Com.Fail ("aggregate library projects are not supported");
902 else
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
909 declare
910 With_Clause : Project_Node_Id :=
911 First_With_Clause_Of (Project_Node, Tree);
912 Previous : Project_Node_Id := Empty_Node;
914 begin
915 while Present (With_Clause) loop
916 if Prj.Tree.Name_Of (With_Clause, Tree) =
917 Project_Naming_Id
918 then
919 if No (Previous) then
920 Set_First_With_Clause_Of
921 (Project_Node, Tree,
922 To => Next_With_Clause_Of (With_Clause, Tree));
923 else
924 Set_Next_With_Clause_Of
925 (Previous, Tree,
926 To => Next_With_Clause_Of (With_Clause, Tree));
927 end if;
929 exit;
930 end if;
932 Previous := With_Clause;
933 With_Clause := Next_With_Clause_Of (With_Clause, Tree);
934 end loop;
935 end;
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.
942 declare
943 Declaration : Project_Node_Id :=
944 First_Declarative_Item_Of
945 (Project_Declaration_Of
946 (Project_Node, Tree),
947 Tree);
948 Previous : Project_Node_Id := Empty_Node;
949 Current_Node : Project_Node_Id := Empty_Node;
951 Name : Name_Id;
952 Kind_Of_Node : Project_Node_Kind;
953 Comments : Project_Node_Id;
955 begin
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
963 then
964 Name := Prj.Tree.Name_Of (Current_Node, Tree);
966 if Nam_In (Name, Name_Source_Files,
967 Name_Source_List_File,
968 Name_Source_Dirs,
969 Name_Naming)
970 then
971 Comments :=
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;
985 end if;
987 if No (Previous) then
988 Set_First_Declarative_Item_Of
989 (Project_Declaration_Of (Project_Node, Tree),
990 Tree,
991 To => Next_Declarative_Item
992 (Declaration, Tree));
994 else
995 Set_Next_Declarative_Item
996 (Previous, Tree,
997 To => Next_Declarative_Item
998 (Declaration, Tree));
999 end if;
1001 else
1002 Previous := Declaration;
1003 end if;
1004 end if;
1006 Declaration := Next_Declarative_Item (Declaration, Tree);
1007 end loop;
1008 end;
1009 end if;
1010 end if;
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;
1016 end if;
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;
1047 Source_List_Path :=
1048 new String'(Output_Name (1 .. Output_Name_Last) &
1049 Source_List_File_Suffix);
1050 Source_List_Last :=
1051 Output_Name_Last + Source_List_File_Suffix'Length;
1053 -- Add the project file extension to the project name
1055 Output_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))
1067 then
1068 declare
1069 Discard : Boolean;
1070 Saved_Path : constant String :=
1071 Path_Name (1 .. Path_Last) & ".saved_";
1072 Nmb : Natural;
1074 begin
1075 Nmb := 0;
1076 loop
1077 declare
1078 Img : constant String := Nmb'Img;
1080 begin
1081 if not Is_Regular_File
1082 (Saved_Path & Img (2 .. Img'Last))
1083 then
1084 Copy_File
1085 (Name => Path_Name (1 .. Path_Last),
1086 Pathname => Saved_Path & Img (2 .. Img'Last),
1087 Mode => Overwrite,
1088 Success => Discard);
1089 exit;
1090 end if;
1092 Nmb := Nmb + 1;
1093 end;
1094 end loop;
1095 end;
1096 end if;
1097 end if;
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
1103 begin
1104 Change_Dir (Path_Name (1 .. Directory_Last));
1105 exception
1106 when Directory_Error =>
1107 Prj.Com.Fail
1108 ("unknown directory """
1109 & Path_Name (1 .. Directory_Last)
1110 & """");
1111 end;
1112 end if;
1113 end Initialize;
1115 -------------
1116 -- Process --
1117 -------------
1119 procedure Process
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);
1138 Last : Natural;
1139 Dir : Dir_Type;
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;
1148 begin
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;
1154 exit;
1155 end if;
1156 end loop;
1158 if Do_Process then
1159 if Opt.Verbose_Mode then
1160 Output.Write_Str ("Processing directory """);
1161 Output.Write_Str (Dir_Name);
1162 Output.Write_Line ("""");
1163 end if;
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.
1172 begin
1173 Open (Dir, Dir_Name);
1174 exception
1175 when Directory_Error =>
1176 Prj.Com.Fail ("cannot open directory """ & Dir_Name & """");
1177 end;
1179 -- Process each regular file in the directory
1181 File_Loop : loop
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));
1192 if Is_Regular_File
1193 (Dir_Name & Directory_Separator & Str (1 .. Last))
1194 then
1195 Matched := True;
1197 Name_Len := 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))
1207 then
1208 Matched := Excluded;
1209 exit;
1210 end if;
1211 end loop;
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
1218 Matched := False;
1220 for Index in Name_Patterns'Range loop
1222 Match
1223 (Canon (1 .. Last), Name_Patterns (Index))
1224 then
1225 Matched := True;
1226 exit;
1227 end if;
1228 end loop;
1229 end if;
1231 if Very_Verbose
1232 or else (Matched = True and then Opt.Verbose_Mode)
1233 then
1234 Output.Write_Str (" Checking """);
1235 Output.Write_Str (Str (1 .. Last));
1236 Output.Write_Line (""": ");
1237 end if;
1239 -- If the file name matches one of the regular expressions,
1240 -- parse it to get its unit name.
1242 if Matched = True then
1243 declare
1244 FD : File_Descriptor;
1245 Success : Boolean;
1246 Saved_Output : File_Descriptor;
1247 Saved_Error : File_Descriptor;
1248 Tmp_File : Path_Name_Type;
1250 begin
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
1256 declare
1257 Prefix_Gcc : String_Access :=
1258 Program_Name (Gcc, "gnatname");
1259 begin
1260 Gcc_Path :=
1261 Locate_Exec_On_Path (Prefix_Gcc.all);
1262 Free (Prefix_Gcc);
1263 end;
1265 if Gcc_Path = null then
1266 Prj.Com.Fail ("could not locate " & Gcc);
1267 end if;
1268 end if;
1270 -- Create the temporary file
1272 Tempdir.Create_Temp_File (FD, Tmp_File);
1274 if FD = Invalid_FD then
1275 Prj.Com.Fail
1276 ("could not create temporary file");
1278 else
1279 Temp_File_Name :=
1280 new String'(Get_Name_String (Tmp_File));
1281 end if;
1283 -- On VMS, a file created with Create_Temp_File cannot
1284 -- be used to redirect output.
1286 if Hostparm.OpenVMS then
1287 Close (FD);
1288 Delete_File (Temp_File_Name.all, Success);
1289 FD := Create_Output_Text_File (Temp_File_Name.all);
1290 end if;
1292 Args (Args'Last) := new String'
1293 (Dir_Name &
1294 Directory_Separator &
1295 Str (1 .. Last));
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
1318 Close (FD);
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.
1332 declare
1333 File : Text_File;
1334 Text_Line : String (1 .. 1_000);
1335 Text_Last : Natural;
1337 begin
1338 Open (File, Temp_File_Name.all);
1340 if not Is_Valid (File) then
1341 Prj.Com.Fail
1342 ("could not read temporary file " &
1343 Temp_File_Name.all);
1344 end if;
1346 Save_Last_Source_Index := Sources.Last;
1348 if End_Of_File (File) then
1349 if Opt.Verbose_Mode then
1350 if not Success then
1351 Output.Write_Str (" (process died) ");
1352 end if;
1353 end if;
1355 else
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
1363 if J >= 13 and then
1364 Text_Line (1 .. 4) = "Unit"
1365 then
1366 -- Add entry to Sources table
1368 Name_Len := J - 12;
1369 Name_Buffer (1 .. Name_Len) :=
1370 Text_Line (6 .. J - 7);
1371 Current_Source :=
1372 (Unit_Name => Name_Find,
1373 File_Name => File_Name_Id,
1374 Index => 0,
1375 Spec => Text_Line (J - 5 .. J) =
1376 "(spec)");
1378 Sources.Append (Current_Source);
1379 end if;
1381 exit Char_Loop;
1382 end if;
1383 end loop Char_Loop;
1384 end loop Line_Loop;
1385 end if;
1387 if Save_Last_Source_Index = Sources.Last then
1388 if Opt.Verbose_Mode then
1389 Output.Write_Line (" not a unit");
1390 end if;
1392 else
1393 if Sources.Last >
1394 Save_Last_Source_Index + 1
1395 then
1396 for Index in Save_Last_Source_Index + 1 ..
1397 Sources.Last
1398 loop
1399 Sources.Table (Index).Index :=
1400 Int (Index - Save_Last_Source_Index);
1401 end loop;
1402 end if;
1404 for Index in Save_Last_Source_Index + 1 ..
1405 Sources.Last
1406 loop
1407 Current_Source := Sources.Table (Index);
1409 if Opt.Verbose_Mode then
1410 if Current_Source.Spec then
1411 Output.Write_Str (" spec of ");
1413 else
1414 Output.Write_Str (" body of ");
1415 end if;
1417 Output.Write_Line
1418 (Get_Name_String
1419 (Current_Source.Unit_Name));
1420 end if;
1421 end loop;
1422 end if;
1424 Close (File);
1426 Delete_File (Temp_File_Name.all, Success);
1427 end;
1428 end;
1430 -- File name matches none of the regular expressions
1432 else
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))
1439 then
1440 Matched := True;
1441 exit;
1442 end if;
1443 end loop;
1444 end if;
1446 if Very_Verbose then
1447 case Matched is
1448 when False =>
1449 Output.Write_Line ("no match");
1451 when Excluded =>
1452 Output.Write_Line ("excluded");
1454 when True =>
1455 Output.Write_Line ("foreign source");
1456 end case;
1457 end if;
1459 if Matched = True then
1461 -- Add source file name without unit name
1463 Name_Len := 0;
1464 Add_Str_To_Name_Buffer (Canon (1 .. Last));
1465 Sources.Append
1466 ((File_Name => Name_Find,
1467 Unit_Name => No_Name,
1468 Index => 0,
1469 Spec => False));
1470 end if;
1471 end if;
1472 end if;
1473 end loop File_Loop;
1475 Close (Dir);
1476 end if;
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.
1482 if Recursively then
1483 Open (Dir, Dir_Name);
1485 loop
1486 Read (Dir, Str, Last);
1487 exit when Last = 0;
1489 -- Do not call itself for "." or ".."
1491 if Is_Directory
1492 (Dir_Name & Directory_Separator & Str (1 .. Last))
1493 and then Str (1 .. Last) /= "."
1494 and then Str (1 .. Last) /= ".."
1495 then
1496 Process_Directory
1497 (Dir_Name & Directory_Separator & Str (1 .. Last),
1498 Recursively => True);
1499 end if;
1500 end loop;
1502 Close (Dir);
1503 end if;
1504 end Process_Directory;
1506 -- Start of processing for Process
1508 begin
1509 Processed_Directories.Set_Last (0);
1511 -- Process each directory
1513 for Index in Directories'Range loop
1515 declare
1516 Dir_Name : constant String := Directories (Index).all;
1517 Last : Natural := Dir_Name'Last;
1518 Recursively : Boolean := False;
1519 Found : Boolean;
1520 Canonical : String (1 .. Dir_Name'Length) := Dir_Name;
1522 begin
1523 Canonical_Case_File_Name (Canonical);
1525 Found := False;
1526 for J in 1 .. Source_Directories.Last loop
1527 if Source_Directories.Table (J).all = Canonical then
1528 Found := True;
1529 exit;
1530 end if;
1531 end loop;
1533 if not Found then
1534 Source_Directories.Append (new String'(Canonical));
1535 end if;
1537 if Dir_Name'Length >= 4
1538 and then (Dir_Name (Last - 2 .. Last) = "/**")
1539 then
1540 Last := Last - 3;
1541 Recursively := True;
1542 end if;
1544 Process_Directory (Dir_Name (Dir_Name'First .. Last), Recursively);
1545 end;
1547 end loop;
1548 end Process;
1550 ----------------
1551 -- Write_Char --
1552 ----------------
1553 procedure Write_A_Char (C : Character) is
1554 begin
1555 Write_A_String ((1 => C));
1556 end Write_A_Char;
1558 ---------------
1559 -- Write_Eol --
1560 ---------------
1562 procedure Write_Eol is
1563 begin
1564 Write_A_String ((1 => ASCII.LF));
1565 end Write_Eol;
1567 --------------------
1568 -- Write_A_String --
1569 --------------------
1571 procedure Write_A_String (S : String) is
1572 Str : String (1 .. S'Length);
1574 begin
1575 if S'Length > 0 then
1576 Str := S;
1578 if Write (Output_FD, Str (1)'Address, Str'Length) /= Str'Length then
1579 Prj.Com.Fail ("disk full");
1580 end if;
1581 end if;
1582 end Write_A_String;
1584 end Prj.Makr;