------------------------------------------------------------------------------ -- -- -- GPR PROJECT MANAGER -- -- -- -- Copyright (C) 2000-2021, Free Software Foundation, Inc. -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada; use Ada; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Directories; use Ada.Directories; with Ada.Strings; use Ada.Strings; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants; with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers.Hashed_Maps; with Ada.Containers.Indefinite_Vectors; with Ada.Strings.Hash_Case_Insensitive; with Ada.Strings.Equal_Case_Insensitive; with GNAT.Directory_Operations; use GNAT.Directory_Operations; with GNAT.Dynamic_HTables; with GNAT.Regexp; use GNAT.Regexp; with GNAT.Table; with GPR.Com; with GPR.Debug; with GPR.Env; use GPR.Env; with GPR.Err; use GPR.Err; with GPR.Erroutc; use GPR.Erroutc; with GPR.Names; use GPR.Names; with GPR.Opt; use GPR.Opt; with GPR.Osint; use GPR.Osint; with GPR.Output; use GPR.Output; with GPR.Sinput; use GPR.Sinput; with GPR.Tree; use GPR.Tree; with GPR.Util; use GPR.Util; with GPR.Snames; use GPR.Snames; package body GPR.Nmsc is No_Continuation_String : aliased String := ""; Continuation_String : aliased String := "\"; -- Used in Check_Library for continuation error messages at the same -- location. type Name_Location is record Name : File_Name_Type; -- Key is duplicated, so that it is known when using functions Get_First -- and Get_Next, as these functions only return an Element. Location : Source_Ptr; Source : Source_Id := No_Source; Listed : Boolean := False; Found : Boolean := False; end record; No_Name_Location : constant Name_Location := (Name => No_File, Location => No_Location, Source => No_Source, Listed => False, Found => False); package Source_Names_Htable is new GNAT.Dynamic_HTables.Simple_HTable (Header_Num => Header_Num, Element => Name_Location, No_Element => No_Name_Location, Key => File_Name_Type, Hash => Hash, Equal => "="); -- File name information found in string list attribute (Source_Files or -- Source_List_File). Used to check that all referenced files were indeed -- found on the disk. type Unit_Exception is record Name : Name_Id; -- Key is duplicated, so that it is known when using functions Get_First -- and Get_Next, as these functions only return an Element. Spec : File_Name_Type; Impl : File_Name_Type; end record; No_Unit_Exception : constant Unit_Exception := (No_Name, No_File, No_File); package Unit_Exceptions_Htable is new GNAT.Dynamic_HTables.Simple_HTable (Header_Num => Header_Num, Element => Unit_Exception, No_Element => No_Unit_Exception, Key => Name_Id, Hash => Hash, Equal => "="); -- Record special naming schemes for Ada units (name of spec file and name -- of implementation file). The elements in this list come from the naming -- exceptions specified in the project files. type File_Found is record File : File_Name_Type := No_File; Excl_File : File_Name_Type := No_File; Excl_Line : Natural := 0; Found : Boolean := False; Location : Source_Ptr := No_Location; Project : Project_Id := No_Project; end record; No_File_Found : constant File_Found := (No_File, No_File, 0, False, No_Location, No_Project); package Excluded_Sources_Htable is new GNAT.Dynamic_HTables.Simple_HTable (Header_Num => Header_Num, Element => File_Found, No_Element => No_File_Found, Key => File_Name_Type, Hash => Hash, Equal => "="); -- A hash table to store the base names of excluded files, if any package Object_File_Names_Htable is new GNAT.Dynamic_HTables.Simple_HTable (Header_Num => Header_Num, Element => Source_Id, No_Element => No_Source, Key => File_Name_Type, Hash => Hash, Equal => "="); -- A hash table to store the object file names for a project, to check that -- two different sources have different object file names. type Project_Processing_Data is record Project : Project_Id; Source_Names : Source_Names_Htable.Instance; Unit_Exceptions : Unit_Exceptions_Htable.Instance; Excluded : Excluded_Sources_Htable.Instance; Source_List_File_Location : Source_Ptr; -- Location of the Source_List_File attribute, for error messages end record; -- This is similar to Tree_Processing_Data, but contains project-specific -- information which is only useful while processing the project, and can -- be discarded as soon as we have finished processing the project type Tree_Processing_Data is record Tree : Project_Tree_Ref; Node_Tree : GPR.Tree.Project_Node_Tree_Ref; Flags : GPR.Processing_Flags; In_Aggregate_Lib : Boolean; end record; -- Temporary data which is needed while parsing a project. It does not need -- to be kept in memory once a project has been fully loaded, but is -- necessary while performing consistency checks (duplicate sources,...) -- This data must be initialized before processing any project, and the -- same data is used for processing all projects in the tree. type Lib_Data is record Name : Name_Id; Proj : Project_Id; Tree : Project_Tree_Ref; end record; package Lib_Data_Table is new GNAT.Table (Table_Component_Type => Lib_Data, Table_Index_Type => Natural, Table_Low_Bound => 1, Table_Initial => 10, Table_Increment => 100); -- A table to record library names in order to check that two library -- projects do not have the same library names. type Error_Message (Length : Positive) is record Flags : Processing_Flags; Location : Source_Ptr; Project : Project_Id; Msg : String (1 .. Length); end record; package Hold_Errors is new Ada.Containers.Indefinite_Vectors (Positive, Error_Message); Errors_Holder : Hold_Errors.Vector; -- Keep error messages until decision is it error, warning or should be -- forgotten. procedure Initialize (Data : out Tree_Processing_Data; Tree : Project_Tree_Ref; Node_Tree : GPR.Tree.Project_Node_Tree_Ref; Flags : GPR.Processing_Flags); -- Initialize Data procedure Free (Data : in out Tree_Processing_Data); -- Free the memory occupied by Data procedure Initialize (Data : in out Project_Processing_Data; Project : Project_Id); procedure Free (Data : in out Project_Processing_Data); -- Initialize or free memory for a project-specific data procedure Find_Excluded_Sources (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data); -- Find the list of files that should not be considered as source files -- for this project. Sets the list in the Project.Excluded_Sources_Htable. procedure Override_Kind (Source : Source_Id; Kind : Source_Kind); -- Override the reference kind for a source file. This properly updates -- the unit data if necessary. procedure Load_Naming_Exceptions (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data); -- All source files in Data.First_Source are considered as naming -- exceptions, and copied into the Source_Names and Unit_Exceptions tables -- as appropriate. type Search_Type is (Search_Project_Files, Search_Source_Directories); -- Search_Project_Files is to find project files. -- Search_Source_Directories is to find source directories. procedure Expand_Subdirectory_Pattern (Project : Project_Id; Data : in out Tree_Processing_Data; Patterns : String_List_Id; Ignore : String_List_Id; Search_For : Search_Type; Resolve_Links : Boolean; Callback : access procedure (Path : Path_Information; Pattern_Index : Natural)); -- Search the subdirectories of Project's directory for files or -- directories that match the globbing patterns found in Patterns (for -- instance "**/*.adb"). Typically, Patterns will be the value of the -- Source_Dirs or Excluded_Source_Dirs attributes. -- -- Every time such a file or directory is found, the callback is called. -- Resolve_Links indicates whether we should resolve links while -- normalizing names. -- -- In the callback, Pattern_Index is the index within Patterns where the -- expanded pattern was found (1 for the first element of Patterns and -- all its matching directories, then 2,...). -- -- We use a generic and not an access-to-subprogram because in some cases -- this code is compiled with the restriction No_Implicit_Dynamic_Code. -- An error message is raised if a pattern does not match any file. procedure Add_Source (Id : out Source_Id; Data : in out Tree_Processing_Data; Project : Project_Id; Source_Dir_Rank : Natural; Lang_Id : Language_Ptr; Kind : Source_Kind; File_Name : File_Name_Type; Display_File : File_Name_Type; Naming_Exception : Naming_Exception_Type := No; Path : Path_Information := No_Path_Information; Alternate_Languages : Language_List := null; Unit : Name_Id := No_Name; Index : Int := 0; Locally_Removed : Boolean := False; Location : Source_Ptr := No_Location); -- Add a new source to the different lists: list of all sources in the -- project tree, list of source of a project and list of sources of a -- language. If Path is specified, the file is also added to -- Source_Paths_HT. Location is used for error messages function Canonical_Case_File_Name (Name : Name_Id) return File_Name_Type; -- Same as Osint.Canonical_Case_File_Name but applies to Name_Id. -- This alters Name_Buffer. function Suffix_Matches (Filename : String; Suffix : File_Name_Type) return Boolean; -- True if the file name ends with the given suffix. Always returns False -- if Suffix is No_Name. procedure Replace_Into_Name_Buffer (Str : String; Pattern : String; Replacement : Character); -- Copy Str into Name_Buffer, replacing Pattern with Replacement. Str is -- converted to lower-case at the same time. procedure Check_Abstract_Project (Project : Project_Id; Data : in out Tree_Processing_Data); -- Check abstract projects attributes procedure Check_Configuration (Project : Project_Id; Data : in out Tree_Processing_Data); -- Check the configuration attributes for the project procedure Check_If_Externally_Built (Project : Project_Id; Data : in out Tree_Processing_Data); -- Check attribute Externally_Built of project Project in project tree -- Data.Tree and modify its data Data if it has the value "true". procedure Check_Interfaces (Project : Project_Id; Data : in out Tree_Processing_Data); -- If a list of sources is specified in attribute Interfaces, set -- In_Interfaces only for the sources specified in the list. procedure Check_Library_Attributes (Project : Project_Id; No_Sources : Boolean; Data : in out Tree_Processing_Data); -- Check the library attributes of project Project in project tree -- and modify its data Data accordingly. If the declared library attributes -- makes the project a library project, report an error when No_Sources -- is True. procedure Check_Package_Naming (Project : Project_Id; Data : in out Tree_Processing_Data); -- Check the naming scheme part of Data, and initialize the naming scheme -- data in the config of the various languages. procedure Check_Programming_Languages (Project : Project_Id; Data : in out Tree_Processing_Data); -- Check attribute Languages for the project with data Data in project -- tree Data.Tree and set the components of Data for all the programming -- languages indicated in attribute Languages, if any. procedure Check_Stand_Alone_Library (Project : Project_Id; Data : in out Tree_Processing_Data); -- Check if project Project in project tree Data.Tree is a Stand-Alone -- Library project, and modify its data Data accordingly if it is one. procedure Check_Unit_Name (Name : String; Unit : out Name_Id); -- Check that a name is a valid unit name function Compute_Directory_Last (Dir : String) return Natural; -- Return the index of the last significant character in Dir. This is used -- to avoid duplicate '/' (slash) characters at the end of directory names. procedure Search_Directories (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data; For_All_Sources : Boolean); -- Search the source directories to find the sources. If For_All_Sources is -- True, check each regular file name against the naming schemes of the -- various languages. Otherwise consider only the file names in hash table -- Source_Names. If Allow_Duplicate_Basenames then files with identical -- base names are permitted within a project for source-based languages -- (never for unit based languages). procedure Check_File (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data; Source_Dir_Rank : Natural; Path : Path_Name_Type; Display_Path : Path_Name_Type; File_Name : File_Name_Type; Display_File_Name : File_Name_Type; Locally_Removed : Boolean; For_All_Sources : Boolean); -- Check if file File_Name is a valid source of the project. This is used -- in multi-language mode only. When the file matches one of the naming -- schemes, it is added to various htables through Add_Source and to -- Source_Paths_Htable. -- -- File_Name is the same as Display_File_Name, but has been normalized. -- They do not include the directory information. -- -- Path and Display_Path on the other hand are the full path to the file. -- Path must have been normalized (canonical casing and possibly links -- resolved). -- -- Source_Directory is the directory in which the file was found. It is -- neither normalized nor has had links resolved, and must not end with a -- a directory separator, to avoid duplicates later on. -- -- If For_All_Sources is True, then all possible file names are analyzed -- otherwise only those currently set in the Source_Names hash table. procedure Check_File_Naming_Schemes (Project : Project_Processing_Data; File_Name : File_Name_Type; Alternate_Languages : out Language_List; Language : out Language_Ptr; Display_Language_Name : out Name_Id; Unit : out Name_Id; Lang_Kind : out Language_Kind; Kind : out Source_Kind); -- Check if the file name File_Name conforms to one of the naming schemes -- of the project. If the file does not match one of the naming schemes, -- set Language to No_Language_Index. Filename is the name of the file -- being investigated. It has been normalized (case-folded). File_Name is -- the same value. procedure Get_Object_Directory (Project : Project_Id; Data : in out Tree_Processing_Data; No_Sources : Boolean := False); -- Get the object directory of a project. procedure Get_Directories (Project : Project_Id; Data : in out Tree_Processing_Data; No_Sources : out Boolean); -- Get the object directory, the exec directory and the source directories -- of a project. Set No_Sources to True if there are no sources in the -- project and the project is not an extending project. procedure Get_Mains (Project : Project_Id; Data : in out Tree_Processing_Data); -- Get the mains of a project from attribute Main, if it exists, and put -- them in the project data. procedure Get_Sources_From_File (Path : String; Location : Source_Ptr; Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data); -- Get the list of sources from a text file and put them in hash table -- Source_Names. procedure Find_Sources (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data); -- Process the Source_Files and Source_List_File attributes, and store the -- list of source files into the Source_Names htable. When these attributes -- are not defined, find all files matching the naming schemes in the -- source directories. If Allow_Duplicate_Basenames, then files with the -- same base names are authorized within a project for source-based -- languages (never for unit based languages) procedure Compute_Unit_Name (File_Name : File_Name_Type; Naming : Lang_Naming_Data; Kind : out Source_Kind; Unit : out Name_Id; Project : Project_Processing_Data); -- Check whether the file matches the naming scheme. If it does, -- compute its unit name. If Unit is set to No_Name on exit, none of the -- other out parameters are relevant. procedure Check_Illegal_Suffix (Project : Project_Id; Suffix : File_Name_Type; Dot_Replacement : File_Name_Type; Attribute_Name : String; Location : Source_Ptr; Data : in out Tree_Processing_Data); -- Display an error message if the given suffix is illegal for some reason. -- The name of the attribute we are testing is specified in Attribute_Name, -- which is used in the error message. Location is the location where the -- suffix is defined. procedure Locate_Directory (Project : Project_Id; Name : File_Name_Type; Path : out Path_Information; Dir_Exists : out Boolean; Data : in out Tree_Processing_Data; Create : String := ""; Location : Source_Ptr := No_Location; Must_Exist : Boolean := True; Externally_Built : Boolean := False); -- Locate a directory. Name is the directory name. Relative paths are -- resolved relative to the project's directory. If the directory does -- not exist: -- - if Must_Exit is False, we return without checking for its existence -- - otherwise, if Create is a non-empty string, it might get created, -- following the behavior prescribed by Create_Dirs. -- In all cases, Dir_Exists indicates whether the directory now exists. -- Create is also used for debugging traces to show which path we are -- computing. procedure Look_For_Sources (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data); -- Find all the sources of project Project in project tree Data.Tree and -- update its Data accordingly. This assumes that the special naming -- exceptions have already been processed. function Path_Name_Of (File_Name : File_Name_Type; Directory : Path_Name_Type) return String; -- Returns the path name of a (non project) file. Returns an empty string -- if file cannot be found. procedure Remove_Source (Tree : Project_Tree_Ref; Id : Source_Id; Replaced_By : Source_Id); -- Remove a file from the list of sources of a project. This might be -- because the file is replaced by another one in an extending project, -- or because a file was added as a naming exception but was not found -- in the end. procedure Report_No_Sources (Project : Project_Id; Lang_Name : String; Data : Tree_Processing_Data; Location : Source_Ptr; Continuation : Boolean := False); -- Report an error or a warning depending on the value of When_No_Sources -- when there are no sources for language Lang_Name. procedure Show_Source_Dirs (Project : Project_Id; Shared : Shared_Project_Tree_Data_Access); -- List all the source directories of a project procedure Write_Attr (Name, Value : String); -- Debug print a value for a specific property. Does nothing when not in -- debug mode procedure Error_Or_Warning (Flags : Processing_Flags; Kind : Error_Warning; Msg : String; Location : Source_Ptr; Project : Project_Id); -- Process a message depending on Kind. -- Error or Warning going to be printed. -- Silent going to be ignored. -- Decide_Later going to be kept until call to Messages_Decision, function No_Space_Img (N : Natural) return String; -- Image of a Natural without the initial space ---------------------- -- Error_Or_Warning -- ---------------------- procedure Error_Or_Warning (Flags : Processing_Flags; Kind : Error_Warning; Msg : String; Location : Source_Ptr; Project : Project_Id) is begin case Kind is when Silent => null; when Error => Error_Msg (Flags, Msg, Location, Project); when Warning => Error_Msg (Flags, "?" & Msg, Location, Project); when Decide_Later => Errors_Holder.Append (Error_Message'(Msg'Length, Flags, Location, Project, Msg)); end case; end Error_Or_Warning; ----------------------- -- Messages_Decision -- ----------------------- procedure Messages_Decision (Kind : Decided_Message) is begin for M of Errors_Holder loop Error_Or_Warning (M.Flags, Kind, M.Msg, M.Location, M.Project); end loop; Errors_Holder.Clear; end Messages_Decision; ------------------------------ -- Replace_Into_Name_Buffer -- ------------------------------ procedure Replace_Into_Name_Buffer (Str : String; Pattern : String; Replacement : Character) is Max : constant Integer := Str'Last - Pattern'Length + 1; J : Positive; begin Name_Len := 0; J := Str'First; while J <= Str'Last loop Name_Len := Name_Len + 1; if J <= Max and then Str (J .. J + Pattern'Length - 1) = Pattern then Name_Buffer (Name_Len) := Replacement; J := J + Pattern'Length; else Name_Buffer (Name_Len) := To_Lower (Str (J)); J := J + 1; end if; end loop; end Replace_Into_Name_Buffer; -------------------- -- Suffix_Matches -- -------------------- function Suffix_Matches (Filename : String; Suffix : File_Name_Type) return Boolean is Min_Prefix_Length : Natural := 0; begin if Suffix in No_File | Empty_File then return False; end if; declare Suf : String := Get_Name_String (Suffix); begin -- On non case-sensitive systems, use proper suffix casing Canonical_Case_File_Name (Suf); -- The file name must end with the suffix (which is not an extension) -- For instance a suffix "configure.in" must match a file with the -- same name. To avoid dummy cases, though, a suffix starting with -- '.' requires a file that is at least one character longer ('.cpp' -- should not match a file with the same name). if Suf (Suf'First) = '.' then Min_Prefix_Length := 1; end if; return Filename'Length >= Suf'Length + Min_Prefix_Length and then Filename (Filename'Last - Suf'Length + 1 .. Filename'Last) = Suf; end; end Suffix_Matches; ---------------- -- Write_Attr -- ---------------- procedure Write_Attr (Name, Value : String) is begin if Current_Verbosity = High then Debug_Output (Name & " = """ & Value & '"'); end if; end Write_Attr; ---------------- -- Add_Source -- ---------------- procedure Add_Source (Id : out Source_Id; Data : in out Tree_Processing_Data; Project : Project_Id; Source_Dir_Rank : Natural; Lang_Id : Language_Ptr; Kind : Source_Kind; File_Name : File_Name_Type; Display_File : File_Name_Type; Naming_Exception : Naming_Exception_Type := No; Path : Path_Information := No_Path_Information; Alternate_Languages : Language_List := null; Unit : Name_Id := No_Name; Index : Int := 0; Locally_Removed : Boolean := False; Location : Source_Ptr := No_Location) is Config : constant Language_Config := Lang_Id.Config; UData : Unit_Index; Add_Src : Boolean; Source : Source_Id; Prev_Unit : Unit_Index := No_Unit_Index; Source_To_Replace : Source_Id := No_Source; S_Or_B : Spec_Or_Body; begin -- Check if the same file name or unit is used in the prj tree Add_Src := True; if Kind = Sep then S_Or_B := Impl; else S_Or_B := Kind; end if; if Unit /= No_Name then Prev_Unit := Units_Htable.Get (Data.Tree.Units_HT, Unit); end if; if Prev_Unit /= No_Unit_Index and then Prev_Unit.File_Names (S_Or_B) /= null then -- Suspicious, we need to check later whether this is authorized Add_Src := False; Source := Prev_Unit.File_Names (S_Or_B); else Source := Source_Files_Htable.Get (Data.Tree.Source_Files_HT, File_Name); if Source /= No_Source and then Source.Index = Index then Add_Src := False; end if; end if; -- Always add the source if it is locally removed, to avoid incorrect -- duplicate checks. if Locally_Removed then Add_Src := True; -- A locally removed source may first replace a source in a project -- being extended. if Source /= No_Source and then Is_Extending (Project, Source.Project) and then Naming_Exception /= Inherited then Source_To_Replace := Source; end if; else -- Duplication of file/unit in same project is allowed if order of -- source directories is known, or if there is no compiler for the -- language. if Add_Src = False then Add_Src := True; if Project = Source.Project then if Prev_Unit = No_Unit_Index then if Data.Flags.Allow_Duplicate_Basenames then Add_Src := True; elsif Lang_Id.Config.Compiler_Driver = Empty_File then Add_Src := True; elsif Source_Dir_Rank /= Source.Source_Dir_Rank then Add_Src := False; else Error_Msg_File_1 := File_Name; Error_Msg (Data.Flags, "duplicate source file name {", Location, Project); Add_Src := False; end if; else if Source_Dir_Rank /= Source.Source_Dir_Rank then Add_Src := False; -- We might be seeing the same file through a different -- path (for instance because of symbolic links). elsif Source.Path.Name /= Path.Name then if not Source.Duplicate_Unit then Error_Msg_Name_1 := Unit; Error_Msg (Data.Flags, "\duplicate unit %%", Location, Project); if Verbose_Mode then Error_Msg_Name_1 := Name_Id (Source.Path.Name); Error_Msg (Data.Flags, "\ %%", Location, Project); Error_Msg_Name_1 := Name_Id (Path.Name); Error_Msg (Data.Flags, "\ %%", Location, Project); end if; Source.Duplicate_Unit := True; end if; Add_Src := False; end if; end if; -- Do not allow the same unit name in different projects, -- except if one is extending the other. -- For a file based language, the same file name replaces a -- file in a project being extended, but it is allowed to have -- the same file name in unrelated projects. elsif Is_Extending (Project, Source.Project) then if not Locally_Removed and then Naming_Exception /= Inherited then Source_To_Replace := Source; end if; elsif Prev_Unit /= No_Unit_Index and then Prev_Unit.File_Names (Kind) /= null and then not Source.Locally_Removed and then Source.Replaced_By = No_Source and then not Data.In_Aggregate_Lib then -- Path is set if this is a source we found on the disk, in -- which case we can provide more explicit error message. Path -- is unset when the source is added from one of the naming -- exceptions in the project. if Path /= No_Path_Information then Error_Msg_Name_1 := Unit; Error_Msg (Data.Flags, "unit %% cannot belong to several projects", Location, Project); Error_Msg_Name_1 := Project.Name; Error_Msg_Name_2 := Name_Id (Path.Display_Name); Error_Msg (Data.Flags, "\ project %%, %%", Location, Project); Error_Msg_Name_1 := Source.Project.Name; Error_Msg_Name_2 := Name_Id (Source.Path.Display_Name); Error_Msg (Data.Flags, "\ project %%, %%", Location, Project); else Error_Msg_Name_1 := Unit; Error_Msg_Name_2 := Source.Project.Name; Error_Msg (Data.Flags, "unit %% already belongs to project %%", Location, Project); end if; Add_Src := False; elsif not Source.Locally_Removed and then Source.Replaced_By /= No_Source and then not Data.Flags.Allow_Duplicate_Basenames and then Lang_Id.Config.Kind = Unit_Based and then Source.Language.Config.Kind = Unit_Based and then not Data.In_Aggregate_Lib then Error_Msg_File_1 := File_Name; Error_Msg_File_2 := File_Name_Type (Source.Project.Name); Error_Msg (Data.Flags, "{ is already a source of project {", Location, Project); -- Add the file anyway, to avoid further warnings like -- "language unknown". Add_Src := True; end if; end if; end if; if not Add_Src then return; end if; -- Add the new file Id := new Source_Data; if Current_Verbosity = High then Debug_Indent; Write_Str ("adding source File: "); Write_Str (Get_Name_String (Display_File)); if Index /= 0 then Write_Str (" at" & Index'Img); end if; if Lang_Id.Config.Kind = Unit_Based then Write_Str (" Unit: "); -- ??? in gprclean, it seems we sometimes pass an empty Unit name -- (see test extended_projects). if Unit /= No_Name then Write_Str (Get_Name_String (Unit)); end if; Write_Str (" Kind: "); Write_Str (Source_Kind'Image (Kind)); end if; Write_Eol; end if; Id.Project := Project; Id.Location := Location; Id.Source_Dir_Rank := Source_Dir_Rank; Id.Language := Lang_Id; Id.Kind := Kind; Id.Alternate_Languages := Alternate_Languages; Id.Locally_Removed := Locally_Removed; Id.Index := Index; Id.File := File_Name; Id.Display_File := Display_File; Id.Dep_Name := Dependency_Name (File_Name, Lang_Id.Config.Dependency_Kind); Id.Naming_Exception := Naming_Exception; Id.Object := Object_Name (File_Name, Config.Object_File_Suffix); Id.Switches := Switches_Name (File_Name); -- Add the source id to the Unit_Sources_HT hash table, if the unit name -- is not null. if Unit /= No_Name then -- Note: we might be creating a dummy unit here, when we in fact have -- a separate. For instance, file file-bar.adb will initially be -- assumed to be the IMPL of unit "file.bar". Only later on (in -- Check_Object_Files) will we parse those units that only have an -- impl and no spec to make sure whether we have a Separate in fact -- (that significantly reduces the number of times we need to parse -- the files, since we are then only interested in those with no -- spec). We still need those dummy units in the table, since that's -- the name we find in the ALI file UData := Units_Htable.Get (Data.Tree.Units_HT, Unit); if UData = No_Unit_Index then UData := new Unit_Data; UData.Name := Unit; if Naming_Exception /= Inherited then Units_Htable.Set (Data.Tree.Units_HT, Unit, UData); end if; end if; Id.Unit := UData; -- Note that this updates Unit information as well if Naming_Exception /= Inherited and then not Locally_Removed then Override_Kind (Id, Kind); end if; end if; if Path /= No_Path_Information then Id.Path := Path; Source_Paths_Htable.Set (Data.Tree.Source_Paths_HT, Path.Name, Id); end if; Id.Next_With_File_Name := Source_Files_Htable.Get (Data.Tree.Source_Files_HT, File_Name); Source_Files_Htable.Set (Data.Tree.Source_Files_HT, File_Name, Id); if Index /= 0 then Project.Has_Multi_Unit_Sources := True; end if; -- Add the source to the language list Id.Next_In_Lang := Lang_Id.First_Source; Lang_Id.First_Source := Id; if Source_To_Replace /= No_Source then Remove_Source (Data.Tree, Source_To_Replace, Id); end if; if Data.Tree.Replaced_Source_Number > 0 and then Replaced_Source_HTable.Get (Data.Tree.Replaced_Sources, Id.File) /= No_File then Replaced_Source_HTable.Remove (Data.Tree.Replaced_Sources, Id.File); Data.Tree.Replaced_Source_Number := Data.Tree.Replaced_Source_Number - 1; end if; end Add_Source; ------------------------------ -- Canonical_Case_File_Name -- ------------------------------ function Canonical_Case_File_Name (Name : Name_Id) return File_Name_Type is begin if Name = No_Name then return File_Name_Type (Name); else Get_Name_String (Name); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); return Name_Find; end if; end Canonical_Case_File_Name; --------------------------------- -- Process_Aggregated_Projects -- --------------------------------- procedure Process_Aggregated_Projects (Tree : Project_Tree_Ref; Project : Project_Id; Node_Tree : GPR.Tree.Project_Node_Tree_Ref; Flags : Processing_Flags) is Data : Tree_Processing_Data := (Tree => Tree, Node_Tree => Node_Tree, Flags => Flags, In_Aggregate_Lib => False); Project_Files : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Project_Files, Project.Decl.Attributes, Tree.Shared); Project_Path_For_Aggregate : GPR.Env.Project_Search_Path; procedure Found_Project_File (Path : Path_Information; Rank : Natural); -- Called for each project file aggregated by Project ------------------------ -- Found_Project_File -- ------------------------ procedure Found_Project_File (Path : Path_Information; Rank : Natural) is pragma Unreferenced (Rank); begin if Path.Name /= Project.Path.Name then Debug_Output ("aggregates: ", Name_Id (Path.Display_Name)); -- For usual "with" statement, this phase will have been done when -- parsing the project itself. However, for aggregate projects, we -- can only do this when processing the aggregate project, since -- the exact list of project files or project directories can -- depend on scenario variables. -- -- We only load the projects explicitly here, but do not process -- them. For the processing, GPR.Proc will take care of -- processing them, within the same call to Recursive_Process -- (thus avoiding the processing of a given project multiple -- times). -- -- ??? We might already have loaded the project Add_Aggregated_Project (Project, Path => Path.Display_Name); else Debug_Output ("pattern returned the aggregate itself, ignored"); end if; end Found_Project_File; -- Start of processing for Check_Aggregate_Project begin pragma Assert (Project.Qualifier in Aggregate_Project); if Project_Files.Default then Error_Msg_Name_1 := Snames.Name_Project_Files; Error_Msg (Flags, "Attribute %% must be specified in aggregate project", Project.Location, Project); return; end if; -- The aggregated projects are only searched relative to the directory -- of the aggregate project, not in the default project path. Initialize_Empty (Project_Path_For_Aggregate); Free (Project.Aggregated_Projects); -- Look for aggregated projects. For similarity with source files and -- dirs, the aggregated project files are not searched for on the -- project path, and are only found through the path specified in -- the Project_Files attribute. Expand_Subdirectory_Pattern (Project => Project, Data => Data, Patterns => Project_Files.Values, Ignore => Nil_String, Search_For => Search_Project_Files, Resolve_Links => Opt.Follow_Links_For_Files, Callback => Found_Project_File'Access); Free (Project_Path_For_Aggregate); end Process_Aggregated_Projects; ---------------------------- -- Check_Abstract_Project -- ---------------------------- procedure Check_Abstract_Project (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Source_Dirs : constant Variable_Value := Util.Value_Of (Name_Source_Dirs, Project.Decl.Attributes, Shared); Source_Files : constant Variable_Value := Util.Value_Of (Name_Source_Files, Project.Decl.Attributes, Shared); Source_List_File : constant Variable_Value := Util.Value_Of (Name_Source_List_File, Project.Decl.Attributes, Shared); Languages : constant Variable_Value := Util.Value_Of (Name_Languages, Project.Decl.Attributes, Shared); begin if Project.Source_Dirs /= Nil_String then if Source_Dirs.Values = Nil_String and then Source_Files.Values = Nil_String and then Languages.Values = Nil_String and then Source_List_File.Default then Project.Source_Dirs := Nil_String; else Error_Msg (Data.Flags, "non-empty set of sources can''t be defined in an abstract" & " project", Project.Location, Project); end if; end if; end Check_Abstract_Project; ------------------------- -- Check_Configuration -- ------------------------- procedure Check_Configuration (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Dot_Replacement : File_Name_Type := No_File; Casing : Casing_Type := All_Lower_Case; Separate_Suffix : File_Name_Type := No_File; Lang_Index : Language_Ptr := No_Language_Index; -- The index of the language data being checked procedure Process_Project_Level_Simple_Attributes; -- Process the simple attributes at the project level procedure Process_Project_Level_Array_Attributes; -- Process the associate array attributes at the project level procedure Process_Packages; -- Read the packages of the project ---------------------- -- Process_Packages -- ---------------------- procedure Process_Packages is Packages : Package_Id; Element : Package_Element; procedure Process_Binder (Arrays : Array_Id); -- Process the associated array attributes of package Binder procedure Process_Builder (Attributes : Variable_Id); -- Process the simple attributes of package Builder procedure Process_Clean (Attributes : Variable_Id); -- Process the simple attributes of package Clean procedure Process_Clean (Arrays : Array_Id); -- Process the associated array attributes of package Clean procedure Process_Compiler (Attributes : Variable_Id); -- Process the simple attributes of package Compiler procedure Process_Compiler (Arrays : Array_Id); -- Process the associated array attributes of package Compiler procedure Process_Naming (Attributes : Variable_Id); -- Process the simple attributes of package Naming procedure Process_Naming (Arrays : Array_Id); -- Process the associated array attributes of package Naming procedure Process_Linker (Attributes : Variable_Id); -- Process the simple attributes of package Linker of a -- configuration project. procedure Process_Linker (Arrays : Array_Id); -- Process the associated array attributes of package Linker procedure Resp_File_Format (Name : Name_Id; Format : out Response_File_Format; Success : out Boolean); -- Get a response file format named Name. Success is True if Name is -- a valine response file format name. -------------------- -- Process_Binder -- -------------------- procedure Process_Binder (Arrays : Array_Id) is Current_Array_Id : Array_Id; Current_Array : Array_Data; Element_Id : Array_Element_Id; Element : Array_Element; begin -- Process the associative array attribute of package Binder Current_Array_Id := Arrays; while Current_Array_Id /= No_Array loop Current_Array := Shared.Arrays.Table (Current_Array_Id); Element_Id := Current_Array.Value; while Element_Id /= No_Array_Element loop Element := Shared.Array_Elements.Table (Element_Id); if Element.Index /= All_Other_Names then -- Get the name of the language Lang_Index := Get_Language_From_Name (Project, Get_Name_String (Element.Index)); if Lang_Index /= No_Language_Index then if Current_Array.Name = Name_Driver then -- Attribute Driver () Lang_Index.Config.Binder_Driver := File_Name_Type (Element.Value.Value); elsif Current_Array.Name = Name_Required_Switches then Put (Into_List => Lang_Index.Config.Binder_Required_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); elsif Current_Array.Name = Name_Prefix then -- Attribute Prefix () Lang_Index.Config.Binder_Prefix := Element.Value.Value; elsif Current_Array.Name = Name_Objects_Path then -- Attribute Objects_Path () Lang_Index.Config.Objects_Path := Element.Value.Value; elsif Current_Array.Name = Name_Objects_Path_File then -- Attribute Objects_Path () Lang_Index.Config.Objects_Path_File := Element.Value.Value; end if; end if; end if; Element_Id := Element.Next; end loop; Current_Array_Id := Current_Array.Next; end loop; end Process_Binder; --------------------- -- Process_Builder -- --------------------- procedure Process_Builder (Attributes : Variable_Id) is Attribute_Id : Variable_Id; Attribute : Variable; begin -- Process non associated array attribute from package Builder Attribute_Id := Attributes; while Attribute_Id /= No_Variable loop Attribute := Shared.Variable_Elements.Table (Attribute_Id); if not Attribute.Value.Default then if Attribute.Name = Name_Executable_Suffix then -- Attribute Executable_Suffix: the suffix of the -- executables. Project.Config.Executable_Suffix := Attribute.Value.Value; end if; end if; Attribute_Id := Attribute.Next; end loop; end Process_Builder; ------------------- -- Process_Clean -- ------------------- procedure Process_Clean (Attributes : Variable_Id) is Attribute_Id : Variable_Id; Attribute : Variable; List : String_List_Id; begin -- Process non associated array attributes from package Clean Attribute_Id := Attributes; while Attribute_Id /= No_Variable loop Attribute := Shared.Variable_Elements.Table (Attribute_Id); if not Attribute.Value.Default then if Attribute.Name = Name_Artifacts_In_Exec_Dir then -- Attribute Artifacts_In_Exec_Dir: the list of file -- names to be cleaned in the exec dir of the main -- project. List := Attribute.Value.Values; if List /= Nil_String then Put (Into_List => Project.Config.Artifacts_In_Exec_Dir, From_List => List, In_Tree => Data.Tree); end if; elsif Attribute.Name = Name_Artifacts_In_Object_Dir then -- Attribute Artifacts_In_Exec_Dir: the list of file -- names to be cleaned in the object dir of every -- project. List := Attribute.Value.Values; if List /= Nil_String then Put (Into_List => Project.Config.Artifacts_In_Object_Dir, From_List => List, In_Tree => Data.Tree); end if; end if; end if; Attribute_Id := Attribute.Next; end loop; end Process_Clean; procedure Process_Clean (Arrays : Array_Id) is Current_Array_Id : Array_Id; Current_Array : Array_Data; Element_Id : Array_Element_Id; Element : Array_Element; List : String_List_Id; begin -- Process the associated array attributes of package Clean Current_Array_Id := Arrays; while Current_Array_Id /= No_Array loop Current_Array := Shared.Arrays.Table (Current_Array_Id); Element_Id := Current_Array.Value; while Element_Id /= No_Array_Element loop Element := Shared.Array_Elements.Table (Element_Id); -- Get the name of the language Lang_Index := Get_Language_From_Name (Project, Get_Name_String (Element.Index)); if Lang_Index /= No_Language_Index then -- Attribute Object_Artifact_Extensions () if Current_Array.Name = Name_Object_Artifact_Extensions then List := Element.Value.Values; if List /= Nil_String then Put (Into_List => Lang_Index.Config.Clean_Object_Artifacts, From_List => List, In_Tree => Data.Tree); end if; -- Attribute Source_Artifact_Extensions () elsif Current_Array.Name = Name_Source_Artifact_Extensions then List := Element.Value.Values; if List /= Nil_String then Put (Into_List => Lang_Index.Config.Clean_Source_Artifacts, From_List => List, In_Tree => Data.Tree); end if; end if; end if; Element_Id := Element.Next; end loop; Current_Array_Id := Current_Array.Next; end loop; end Process_Clean; ---------------------- -- Process_Compiler -- ---------------------- procedure Process_Compiler (Attributes : Variable_Id) is Attribute_Id : Variable_Id; Attribute : Variable; begin -- Process non associated array attributes from package Compiler Attribute_Id := Attributes; while Attribute_Id /= No_Variable loop Attribute := Shared.Variable_Elements.Table (Attribute_Id); if not Attribute.Value.Default then if Attribute.Name = Name_Max_Command_Line_Length then declare Value : Natural; begin Value := Natural'Value (Get_Name_String (Attribute.Value.Value)); if Project.Config.Max_Command_Line_Length = 0 or else Value < Project.Config.Max_Command_Line_Length then Project.Config.Max_Command_Line_Length := Value; end if; exception when Constraint_Error => Error_Msg (Data.Flags, "value must be positive or equal to 0", Attribute.Value.Location, Project); end; end if; end if; Attribute_Id := Attribute.Next; end loop; end Process_Compiler; procedure Process_Compiler (Arrays : Array_Id) is Current_Array_Id : Array_Id; Current_Array : Array_Data; Element_Id : Array_Element_Id; Element : Array_Element; List : String_List_Id; begin -- Process the associative array attribute of package Compiler Current_Array_Id := Arrays; while Current_Array_Id /= No_Array loop Current_Array := Shared.Arrays.Table (Current_Array_Id); Element_Id := Current_Array.Value; while Element_Id /= No_Array_Element loop Element := Shared.Array_Elements.Table (Element_Id); if Element.Index /= All_Other_Names then -- Get the name of the language Lang_Index := Get_Language_From_Name (Project, Get_Name_String (Element.Index)); if Lang_Index /= No_Language_Index then -- Attribute Dependency_Kind () if Current_Array.Name = Name_Dependency_Kind then Get_Name_String (Element.Value.Value); begin Lang_Index.Config.Dependency_Kind := Dependency_File_Kind'Value (Name_Buffer (1 .. Name_Len)); exception when Constraint_Error => Error_Msg (Data.Flags, "illegal value for Dependency_Kind", Element.Value.Location, Project); end; -- Attribute Dependency_Switches () elsif Current_Array.Name = Name_Dependency_Switches then if Lang_Index.Config.Dependency_Kind = None then Lang_Index.Config.Dependency_Kind := Makefile; end if; List := Element.Value.Values; if List /= Nil_String then Put (Into_List => Lang_Index.Config.Dependency_Option, From_List => List, In_Tree => Data.Tree); end if; -- Attribute Dependency_Driver () elsif Current_Array.Name = Name_Dependency_Driver then if Lang_Index.Config.Dependency_Kind = None then Lang_Index.Config.Dependency_Kind := Makefile; end if; List := Element.Value.Values; if List /= Nil_String then Put (Into_List => Lang_Index.Config.Compute_Dependency, From_List => List, In_Tree => Data.Tree); end if; -- Attribute Language_Kind () elsif Current_Array.Name = Name_Language_Kind then Get_Name_String (Element.Value.Value); begin Lang_Index.Config.Kind := Language_Kind'Value (Name_Buffer (1 .. Name_Len)); exception when Constraint_Error => Error_Msg (Data.Flags, "illegal value for Language_Kind", Element.Value.Location, Project); end; -- Attribute Include_Switches () elsif Current_Array.Name = Name_Include_Switches then List := Element.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "include option cannot be null", Element.Value.Location, Project); end if; Put (Into_List => Lang_Index.Config.Include_Option, From_List => List, In_Tree => Data.Tree); -- Attribute Include_Switches_Via_Spec -- () elsif Current_Array.Name = Name_Include_Switches_Via_Spec then List := Element.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "include switches via spec cannot be null", Element.Value.Location, Project); end if; Put (Into_List => Lang_Index.Config.Include_Switches_Via_Spec, From_List => List, In_Tree => Data.Tree); -- Attribute Include_Path () elsif Current_Array.Name = Name_Include_Path then Lang_Index.Config.Include_Path := Element.Value.Value; -- Attribute Include_Path_File () elsif Current_Array.Name = Name_Include_Path_File then Lang_Index.Config.Include_Path_File := Element.Value.Value; -- Attribute Driver () elsif Current_Array.Name = Name_Driver then Lang_Index.Config.Compiler_Driver := File_Name_Type (Element.Value.Value); -- Attributes Required_Switches () and -- Leading_Required_Switches (. elsif Current_Array.Name = Name_Required_Switches or else Current_Array.Name = Name_Leading_Required_Switches then Put (Into_List => Lang_Index.Config. Compiler_Leading_Required_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); -- Attribute Trailing_Required_Switches ( elsif Current_Array.Name = Name_Trailing_Required_Switches then Put (Into_List => Lang_Index.Config. Compiler_Trailing_Required_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); -- Attribute Multi_Unit_Switches () elsif Current_Array.Name = Name_Multi_Unit_Switches then Put (Into_List => Lang_Index.Config.Multi_Unit_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); -- Attribute Multi_Unit_Object_Separator (lang) elsif Current_Array.Name = Name_Multi_Unit_Object_Separator then Get_Name_String (Element.Value.Value); if Name_Len /= 1 then Error_Msg (Data.Flags, "multi-unit object separator must have " & "a single character", Element.Value.Location, Project); elsif Name_Buffer (1) = ' ' then Error_Msg (Data.Flags, "multi-unit object separator cannot be " & "a space", Element.Value.Location, Project); else Lang_Index.Config.Multi_Unit_Object_Separator := Name_Buffer (1); end if; -- Attribute Source_File_Switches () elsif Current_Array.Name = Name_Source_File_Switches then Put (Into_List => Lang_Index.Config.Source_File_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); -- Attribute Object_File_Suffix () elsif Current_Array.Name = Name_Object_File_Suffix then if Get_Name_String (Element.Value.Value) = "" then Error_Msg (Data.Flags, "object file suffix cannot be empty", Element.Value.Location, Project); else Lang_Index.Config.Object_File_Suffix := Element.Value.Value; end if; -- Attribute Object_File_Switches () elsif Current_Array.Name = Name_Object_File_Switches then Put (Into_List => Lang_Index.Config.Object_File_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); -- Attribute Object_Path_Switches () elsif Current_Array.Name = Name_Object_Path_Switches then Put (Into_List => Lang_Index.Config.Object_Path_Switches, From_List => Element.Value.Values, In_Tree => Data.Tree); -- Attribute Compiler_Pic_Option () elsif Current_Array.Name = Name_Pic_Option then List := Element.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "compiler PIC option cannot be null", Element.Value.Location, Project); end if; Put (Into_List => Lang_Index.Config.Compilation_PIC_Option, From_List => List, In_Tree => Data.Tree); -- Attribute Mapping_File_Switches () elsif Current_Array.Name = Name_Mapping_File_Switches then List := Element.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "mapping file switches cannot be null", Element.Value.Location, Project); end if; Put (Into_List => Lang_Index.Config.Mapping_File_Switches, From_List => List, In_Tree => Data.Tree); -- Attribute Mapping_Spec_Suffix () elsif Current_Array.Name = Name_Mapping_Spec_Suffix then Lang_Index.Config.Mapping_Spec_Suffix := File_Name_Type (Element.Value.Value); -- Attribute Mapping_Body_Suffix () elsif Current_Array.Name = Name_Mapping_Body_Suffix then Lang_Index.Config.Mapping_Body_Suffix := File_Name_Type (Element.Value.Value); -- Attribute Config_File_Switches () elsif Current_Array.Name = Name_Config_File_Switches then List := Element.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "config file switches cannot be null", Element.Value.Location, Project); end if; Put (Into_List => Lang_Index.Config.Config_File_Switches, From_List => List, In_Tree => Data.Tree); -- Attribute Objects_Path () elsif Current_Array.Name = Name_Objects_Path then Lang_Index.Config.Objects_Path := Element.Value.Value; -- Attribute Objects_Path_File () elsif Current_Array.Name = Name_Objects_Path_File then Lang_Index.Config.Objects_Path_File := Element.Value.Value; -- Attribute Config_Body_File_Name () elsif Current_Array.Name = Name_Config_Body_File_Name then Lang_Index.Config.Config_Body := Element.Value.Value; -- Attribute Config_Body_File_Name_Index () elsif Current_Array.Name = Name_Config_Body_File_Name_Index then Lang_Index.Config.Config_Body_Index := Element.Value.Value; -- Attribute Config_Body_File_Name_Pattern() elsif Current_Array.Name = Name_Config_Body_File_Name_Pattern then Lang_Index.Config.Config_Body_Pattern := Element.Value.Value; -- Attribute Config_Spec_File_Name () elsif Current_Array.Name = Name_Config_Spec_File_Name then Lang_Index.Config.Config_Spec := Element.Value.Value; -- Attribute Config_Spec_File_Name_Index () elsif Current_Array.Name = Name_Config_Spec_File_Name_Index then Lang_Index.Config.Config_Spec_Index := Element.Value.Value; -- Attribute Config_Spec_File_Name_Pattern() elsif Current_Array.Name = Name_Config_Spec_File_Name_Pattern then Lang_Index.Config.Config_Spec_Pattern := Element.Value.Value; -- Attribute Config_File_Dependency_Support () elsif Current_Array.Name = Name_Config_File_Dependency_Support then begin Lang_Index.Config.Config_File_Dependency_Support := Boolean'Value (Get_Name_String (Element.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "illegal value for " & "Config_File_Dependency_Support", Element.Value.Location, Project); end; -- Attribute Config_File_Unique () elsif Current_Array.Name = Name_Config_File_Unique then begin Lang_Index.Config.Config_File_Unique := Boolean'Value (Get_Name_String (Element.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "illegal value for Config_File_Unique", Element.Value.Location, Project); end; -- Attribute Response_File_Format () elsif Current_Array.Name = Name_Response_File_Format then declare Success : Boolean; begin Resp_File_Format (Element.Value.Value, Lang_Index.Config.Resp_File_Format, Success); if not Success then Error_Msg (Data.Flags, "illegal response file format", Element.Value.Location, Project); end if; end; -- Attribute Response_File_Switches () elsif Current_Array.Name = Name_Response_File_Switches then Put (Into_List => Lang_Index.Config.Resp_File_Options, From_List => Element.Value.Values, In_Tree => Data.Tree); end if; end if; end if; Element_Id := Element.Next; end loop; Current_Array_Id := Current_Array.Next; end loop; end Process_Compiler; -------------------- -- Process_Naming -- -------------------- procedure Process_Naming (Attributes : Variable_Id) is Attribute_Id : Variable_Id; Attribute : Variable; begin -- Process non associated array attribute from package Naming Attribute_Id := Attributes; while Attribute_Id /= No_Variable loop Attribute := Shared.Variable_Elements.Table (Attribute_Id); if not Attribute.Value.Default then if Attribute.Name = Name_Separate_Suffix then -- Attribute Separate_Suffix Get_Name_String (Attribute.Value.Value); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Separate_Suffix := Name_Find; elsif Attribute.Name = Name_Casing then -- Attribute Casing begin if Attribute.Value.Value /= No_Name then Casing := Value (Get_Name_String (Attribute.Value.Value)); end if; exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value for Casing", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Dot_Replacement then -- Attribute Dot_Replacement Dot_Replacement := File_Name_Type (Attribute.Value.Value); end if; end if; Attribute_Id := Attribute.Next; end loop; end Process_Naming; procedure Process_Naming (Arrays : Array_Id) is Current_Array_Id : Array_Id; Current_Array : Array_Data; Element_Id : Array_Element_Id; Element : Array_Element; begin -- Process the associative array attribute of package Naming Current_Array_Id := Arrays; while Current_Array_Id /= No_Array loop Current_Array := Shared.Arrays.Table (Current_Array_Id); Element_Id := Current_Array.Value; while Element_Id /= No_Array_Element loop Element := Shared.Array_Elements.Table (Element_Id); -- Get the name of the language Lang_Index := Get_Language_From_Name (Project, Get_Name_String (Element.Index)); if Lang_Index /= No_Language_Index and then Element.Value.Kind = Single and then Element.Value.Value /= No_Name then if Current_Array.Name = Name_Spec_Suffix or else Current_Array.Name = Name_Specification_Suffix then -- Attribute Spec_Suffix () Get_Name_String (Element.Value.Value); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Lang_Index.Config.Naming_Data.Spec_Suffix := Name_Find; elsif Current_Array.Name = Name_Implementation_Suffix or else Current_Array.Name = Name_Body_Suffix then Get_Name_String (Element.Value.Value); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); -- Attribute Body_Suffix () Lang_Index.Config.Naming_Data.Body_Suffix := Name_Find; Lang_Index.Config.Naming_Data.Separate_Suffix := Lang_Index.Config.Naming_Data.Body_Suffix; end if; end if; Element_Id := Element.Next; end loop; Current_Array_Id := Current_Array.Next; end loop; end Process_Naming; -------------------- -- Process_Linker -- -------------------- procedure Process_Linker (Attributes : Variable_Id) is Attribute_Id : Variable_Id; Attribute : Variable; begin -- Process non associated array attribute from package Linker Attribute_Id := Attributes; while Attribute_Id /= No_Variable loop Attribute := Shared.Variable_Elements.Table (Attribute_Id); if not Attribute.Value.Default then if Attribute.Name = Name_Driver then -- Attribute Linker'Driver: the default linker to use Project.Config.Linker := Path_Name_Type (Attribute.Value.Value); -- Linker'Driver is also used to link shared libraries -- if the obsolescent attribute Library_GCC has not been -- specified. if Project.Config.Shared_Lib_Driver = No_File then Project.Config.Shared_Lib_Driver := File_Name_Type (Attribute.Value.Value); end if; elsif Attribute.Name = Name_Required_Switches then -- Attribute Required_Switches: the minimum trailing -- options to use when invoking the linker Put (Into_List => Project.Config.Trailing_Linker_Required_Switches, From_List => Attribute.Value.Values, In_Tree => Data.Tree); elsif Attribute.Name = Name_Map_File_Option then Project.Config.Map_File_Option := Attribute.Value.Value; elsif Attribute.Name = Name_Max_Command_Line_Length then declare Value : Natural; begin Value := Natural'Value (Get_Name_String (Attribute.Value.Value)); if Project.Config.Max_Command_Line_Length = 0 or else Value < Project.Config.Max_Command_Line_Length then Project.Config.Max_Command_Line_Length := Value; end if; exception when Constraint_Error => Error_Msg (Data.Flags, "value must be positive or equal to 0", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Response_File_Format then declare Success : Boolean; begin Resp_File_Format (Name => Attribute.Value.Value, Format => Project.Config.Resp_File_Format, Success => Success); if not Success then Error_Msg (Data.Flags, "illegal response file format", Attribute.Value.Location, Project); end if; end; elsif Attribute.Name = Name_Response_File_Switches then Put (Into_List => Project.Config.Resp_File_Options, From_List => Attribute.Value.Values, In_Tree => Data.Tree); elsif Attribute.Name = Name_Export_File_Switch then Project.Config.Export_File_Switch := Attribute.Value.Value; elsif Attribute.Name = Name_Export_File_Format then declare Name : constant Name_Id := Get_Lower_Name_Id (Get_Name_String (Attribute.Value.Value)); begin if Name = Name_None then Project.Config.Export_File_Format := None; elsif Name = Name_Gnu then Project.Config.Export_File_Format := GNU; elsif Name = Name_Def then Project.Config.Export_File_Format := Def; elsif Name = Name_Flat then Project.Config.Export_File_Format := Flat; else Error_Msg (Data.Flags, "illegal export file format", Attribute.Value.Location, Project); end if; end; end if; end if; Attribute_Id := Attribute.Next; end loop; end Process_Linker; procedure Process_Linker (Arrays : Array_Id) is Current_Array_Id : Array_Id; Current_Array : Array_Data; Element_Id : Array_Element_Id; Element : Array_Element; begin -- Process the associated array attributes of package Clean Current_Array_Id := Arrays; while Current_Array_Id /= No_Array loop Current_Array := Shared.Arrays.Table (Current_Array_Id); if Current_Array.Name = Name_Unconditional_Linking then Element_Id := Current_Array.Value; while Element_Id /= No_Array_Element loop Element := Shared.Array_Elements.Table (Element_Id); -- Get the name of the language Lang_Index := Get_Language_From_Name (Project, Get_Name_String (Element.Index)); if Lang_Index /= No_Language_Index then -- Attribute Unconditional_Linking () begin Lang_Index.Unconditional_Linking := Boolean'Value (Get_Name_String (Element.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "illegal value for Unconditional_Linking", Element.Value.Location, Project); end; end if; Element_Id := Element.Next; end loop; end if; Current_Array_Id := Current_Array.Next; end loop; end Process_Linker; ---------------------- -- Resp_File_Format -- ---------------------- procedure Resp_File_Format (Name : Name_Id; Format : out Response_File_Format; Success : out Boolean) is Low_Name : constant Name_Id := Get_Lower_Name_Id (Get_Name_String (Name)); begin Success := True; if Low_Name = Name_None then Format := None; elsif Low_Name = Name_Gnu then Format := GNU; elsif Low_Name = Name_Object_List then Format := Object_List; elsif Low_Name = Name_Option_List then Format := Option_List; elsif Low_Name = Name_Gcc then Format := GCC; elsif Low_Name = Name_Gcc_Gnu then Format := GCC_GNU; elsif Low_Name = Name_Gcc_Option_List then Format := GCC_Option_List; elsif Low_Name = Name_Gcc_Object_List then Format := GCC_Object_List; else Success := False; Format := None; end if; end Resp_File_Format; -- Start of processing for Process_Packages begin Packages := Project.Decl.Packages; while Packages /= No_Package loop Element := Shared.Packages.Table (Packages); if Element.Name = Name_Binder then -- Process attributes of package Binder Process_Binder (Element.Decl.Arrays); elsif Element.Name = Name_Builder then -- Process attributes of package Builder Process_Builder (Element.Decl.Attributes); elsif Element.Name = Name_Clean then -- Process attributes of package Clean Process_Clean (Element.Decl.Attributes); Process_Clean (Element.Decl.Arrays); elsif Element.Name = Name_Compiler then -- Process attributes of package Compiler Process_Compiler (Element.Decl.Attributes); Process_Compiler (Element.Decl.Arrays); elsif Element.Name = Name_Linker then -- Process attributes of package Linker Process_Linker (Element.Decl.Attributes); Process_Linker (Element.Decl.Arrays); elsif Element.Name = Name_Naming then -- Process attributes of package Naming Process_Naming (Element.Decl.Attributes); Process_Naming (Element.Decl.Arrays); end if; Packages := Element.Next; end loop; end Process_Packages; --------------------------------------------- -- Process_Project_Level_Simple_Attributes -- --------------------------------------------- procedure Process_Project_Level_Simple_Attributes is Attribute_Id : Variable_Id; Attribute : Variable; List : String_List_Id; begin -- Process non associated array attribute at project level Attribute_Id := Project.Decl.Attributes; while Attribute_Id /= No_Variable loop Attribute := Shared.Variable_Elements.Table (Attribute_Id); if not Attribute.Value.Default then if Attribute.Name = Name_Target then -- Attribute Target: the target specified Project.Config.Target := Attribute.Value.Value; elsif Attribute.Name = Name_Library_Builder then -- Attribute Library_Builder: the application to invoke -- to build libraries. Project.Config.Library_Builder := Path_Name_Type (Attribute.Value.Value); elsif Attribute.Name = Name_Archive_Builder then -- Attribute Archive_Builder: the archive builder -- (usually "ar") and its minimum options (usually "cr"). List := Attribute.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "archive builder cannot be null", Attribute.Value.Location, Project); end if; Put (Into_List => Project.Config.Archive_Builder, From_List => List, In_Tree => Data.Tree); elsif Attribute.Name = Name_Archive_Builder_Append_Option then -- Attribute Archive_Builder: the archive builder -- (usually "ar") and its minimum options (usually "cr"). List := Attribute.Value.Values; if List /= Nil_String then Put (Into_List => Project.Config.Archive_Builder_Append_Option, From_List => List, In_Tree => Data.Tree); end if; elsif Attribute.Name = Name_Archive_Indexer then -- Attribute Archive_Indexer: the optional archive -- indexer (usually "ranlib") with its minimum options -- (usually none). List := Attribute.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "archive indexer cannot be null", Attribute.Value.Location, Project); end if; Put (Into_List => Project.Config.Archive_Indexer, From_List => List, In_Tree => Data.Tree); elsif Attribute.Name = Name_Object_Lister then -- Attribute Object_Lister: the optional object -- lister (usually "nm") with its minimum options. List := Attribute.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "object lister cannot be null", Attribute.Value.Location, Project); end if; Put (Into_List => Project.Config.Object_Lister, From_List => List, In_Tree => Data.Tree); elsif Attribute.Name = Name_Object_Lister_Matcher then -- Attribute Object_Lister_Matcher: mandatory when -- object lister (usually "nm") is defined. Project.Config.Object_Lister_Matcher := Attribute.Value.Value; elsif Attribute.Name = Name_Library_Partial_Linker then -- Attribute Library_Partial_Linker: the optional linker -- driver with its minimum options, to partially link -- archives. List := Attribute.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "partial linker cannot be null", Attribute.Value.Location, Project); end if; Put (Into_List => Project.Config.Lib_Partial_Linker, From_List => List, In_Tree => Data.Tree); elsif Attribute.Name = Name_Library_GCC then Project.Config.Shared_Lib_Driver := File_Name_Type (Attribute.Value.Value); Error_Msg (Data.Flags, "?Library_'G'C'C is an obsolescent attribute, " & "use Linker''Driver instead", Attribute.Value.Location, Project); elsif Attribute.Name = Name_Archive_Suffix then Project.Config.Archive_Suffix := File_Name_Type (Attribute.Value.Value); elsif Attribute.Name = Name_Linker_Executable_Option then -- Attribute Linker_Executable_Option: optional options -- to specify an executable name. Defaults to "-o". List := Attribute.Value.Values; if List = Nil_String then Error_Msg (Data.Flags, "linker executable option cannot be null", Attribute.Value.Location, Project); end if; Put (Into_List => Project.Config.Linker_Executable_Option, From_List => List, In_Tree => Data.Tree); elsif Attribute.Name = Name_Linker_Lib_Dir_Option then -- Attribute Linker_Lib_Dir_Option: optional options -- to specify a library search directory. Defaults to -- "-L". Get_Name_String (Attribute.Value.Value); if Name_Len = 0 then Error_Msg (Data.Flags, "linker library directory option cannot be empty", Attribute.Value.Location, Project); end if; Project.Config.Linker_Lib_Dir_Option := Attribute.Value.Value; elsif Attribute.Name = Name_Linker_Lib_Name_Option then -- Attribute Linker_Lib_Name_Option: optional options -- to specify the name of a library to be linked in. -- Defaults to "-l". Get_Name_String (Attribute.Value.Value); if Name_Len = 0 then Error_Msg (Data.Flags, "linker library name option cannot be empty", Attribute.Value.Location, Project); end if; Project.Config.Linker_Lib_Name_Option := Attribute.Value.Value; elsif Attribute.Name = Name_Run_Path_Option then -- Attribute Run_Path_Option: optional options to -- specify a path for libraries. List := Attribute.Value.Values; if List /= Nil_String then Put (Into_List => Project.Config.Run_Path_Option, From_List => List, In_Tree => Data.Tree); end if; elsif Attribute.Name = Name_Run_Path_Origin then Get_Name_String (Attribute.Value.Value); if Name_Len = 0 then Error_Msg (Data.Flags, "run path origin cannot be empty", Attribute.Value.Location, Project); end if; Project.Config.Run_Path_Origin := Attribute.Value.Value; elsif Attribute.Name = Name_Library_Install_Name_Option then Project.Config.Library_Install_Name_Option := Attribute.Value.Value; elsif Attribute.Name = Name_Separate_Run_Path_Options then declare pragma Unsuppress (All_Checks); begin Project.Config.Separate_Run_Path_Options := Boolean'Value (Get_Name_String (Attribute.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Attribute.Value.Value) & """ for Separate_Run_Path_Options", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Library_Support then declare pragma Unsuppress (All_Checks); begin Project.Config.Lib_Support := Library_Support'Value (Get_Name_String (Attribute.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Attribute.Value.Value) & """ for Library_Support", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Library_Encapsulated_Supported then declare pragma Unsuppress (All_Checks); begin Project.Config.Lib_Encapsulated_Supported := Boolean'Value (Get_Name_String (Attribute.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Attribute.Value.Value) & """ for Library_Encapsulated_Supported", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Shared_Library_Prefix then Project.Config.Shared_Lib_Prefix := File_Name_Type (Attribute.Value.Value); elsif Attribute.Name = Name_Shared_Library_Suffix then Project.Config.Shared_Lib_Suffix := File_Name_Type (Attribute.Value.Value); elsif Attribute.Name = Name_Symbolic_Link_Supported then declare pragma Unsuppress (All_Checks); begin Project.Config.Symbolic_Link_Supported := Boolean'Value (Get_Name_String (Attribute.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Attribute.Value.Value) & """ for Symbolic_Link_Supported", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Library_Major_Minor_Id_Supported then declare pragma Unsuppress (All_Checks); begin Project.Config.Lib_Maj_Min_Id_Supported := Boolean'Value (Get_Name_String (Attribute.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Attribute.Value.Value) & """ for Library_Major_Minor_Id_Supported", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Library_Auto_Init_Supported then declare pragma Unsuppress (All_Checks); begin Project.Config.Auto_Init_Supported := Boolean'Value (Get_Name_String (Attribute.Value.Value)); exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Attribute.Value.Value) & """ for Library_Auto_Init_Supported", Attribute.Value.Location, Project); end; elsif Attribute.Name = Name_Shared_Library_Minimum_Switches then List := Attribute.Value.Values; if List /= Nil_String then Put (Into_List => Project.Config.Shared_Lib_Min_Options, From_List => List, In_Tree => Data.Tree); end if; elsif Attribute.Name = Name_Library_Version_Switches then List := Attribute.Value.Values; if List /= Nil_String then Put (Into_List => Project.Config.Lib_Version_Options, From_List => List, In_Tree => Data.Tree); end if; elsif Attribute.Name = Name_Warning_Message then Project.Warning_Message := Attribute.Value.Value; if Project.Extended_By = No_Project and then Project.Warning_Message not in No_Name | The_Empty_String then Error_Msg (Data.Flags, "?" & Get_Name_String (Project.Warning_Message), Project.Location, Project); end if; end if; end if; Attribute_Id := Attribute.Next; end loop; end Process_Project_Level_Simple_Attributes; -------------------------------------------- -- Process_Project_Level_Array_Attributes -- -------------------------------------------- procedure Process_Project_Level_Array_Attributes is Current_Array_Id : Array_Id; Current_Array : Array_Data; Element_Id : Array_Element_Id; Element : Array_Element; List : String_List_Id; begin -- Process the associative array attributes at project level Current_Array_Id := Project.Decl.Arrays; while Current_Array_Id /= No_Array loop Current_Array := Shared.Arrays.Table (Current_Array_Id); Element_Id := Current_Array.Value; while Element_Id /= No_Array_Element loop Element := Shared.Array_Elements.Table (Element_Id); -- Get the name of the language Lang_Index := Get_Language_From_Name (Project, Get_Name_String (Element.Index)); if Lang_Index /= No_Language_Index then if Current_Array.Name = Name_Inherit_Source_Path then List := Element.Value.Values; if List /= Nil_String then Put (Into_List => Lang_Index.Config.Include_Compatible_Languages, From_List => List, In_Tree => Data.Tree, Lower_Case => True); end if; elsif Current_Array.Name = Name_Toolchain_Description then -- Attribute Toolchain_Description () Lang_Index.Config.Toolchain_Description := Element.Value.Value; elsif Current_Array.Name = Name_Toolchain_Version then -- Attribute Toolchain_Version () Lang_Index.Config.Toolchain_Version := Element.Value.Value; -- For Ada, set proper checksum computation mode, -- which has changed from version to version. if Lang_Index.Name = Name_Ada then declare Vers : constant String := Get_Name_String (Element.Value.Value); pragma Assert (Vers'First = 1); begin -- Version 6.3 or earlier if Vers'Length >= 8 and then Vers (1 .. 5) = GNAT_And_Space and then Vers (7) = '.' and then (Vers (6) < '6' or else (Vers (6) = '6' and then Vers (8) < '4')) then Checksum_GNAT_6_3 := True; -- Version 5.03 or earlier if Vers (6) < '5' or else (Vers (6) = '5' and then Vers (Vers'Last) < '4') then Checksum_GNAT_5_03 := True; -- Version 5.02 or earlier (no checksums) if Vers (6) /= '5' or else Vers (Vers'Last) < '3' then Checksum_Accumulate_Token_Checksum := False; end if; end if; end if; end; end if; elsif Current_Array.Name = Name_Required_Toolchain_Version then -- Attribute Required_Toolchain_Version () Lang_Index.Config.Required_Toolchain_Version := Element.Value.Value; elsif Current_Array.Name = Name_Runtime_Library_Dir then -- Attribute Runtime_Library_Dir () if Lang_Index.Config.Runtime_Library_Dirs = No_Name_List then Name_List_Table.Append (Shared.Name_Lists, New_Val => (Element.Value.Value, No_Name_List)); Lang_Index.Config.Runtime_Library_Dirs := Name_List_Table.Last (Shared.Name_Lists); end if; elsif Current_Array.Name = Name_Runtime_Library_Dirs then -- Attribute Runtime_Library_Dirs () declare Dirs : String_List_Id := Element.Value.Values; Elem : String_Element; begin Lang_Index.Config.Runtime_Library_Dirs := No_Name_List; Shared.Ada_Runtime_Dir := No_Name; while Dirs /= Nil_String loop Elem := Shared.String_Elements.Table (Dirs); Name_List_Table.Append (Shared.Name_Lists, New_Val => (Elem.Value, Lang_Index.Config.Runtime_Library_Dirs)); Lang_Index.Config.Runtime_Library_Dirs := Name_List_Table.Last (Shared.Name_Lists); Dirs := Elem.Next; end loop; end; elsif Current_Array.Name = Name_Runtime_Source_Dir then -- Attribute Runtime_Source_Dir () if Lang_Index.Config.Runtime_Source_Dirs = No_Name_List then Name_List_Table.Append (Shared.Name_Lists, New_Val => (Element.Value.Value, No_Name_List)); Lang_Index.Config.Runtime_Source_Dirs := Name_List_Table.Last (Shared.Name_Lists); end if; elsif Current_Array.Name = Name_Runtime_Source_Dirs then -- Attribute Runtime_Source_Dirs () declare Dirs : String_List_Id := Element.Value.Values; Elem : String_Element; begin Lang_Index.Config.Runtime_Source_Dirs := No_Name_List; while Dirs /= Nil_String loop Elem := Shared.String_Elements.Table (Dirs); Name_List_Table.Append (Shared.Name_Lists, New_Val => (Elem.Value, Lang_Index.Config.Runtime_Source_Dirs)); Lang_Index.Config.Runtime_Source_Dirs := Name_List_Table.Last (Shared.Name_Lists); Dirs := Elem.Next; end loop; end; elsif Current_Array.Name = Name_Runtime_Dir then declare Runtime_Dir : constant String := Get_Name_String (Element.Value.Value); procedure Get_Directories (Runtime_Dirs : in out Name_List_Index; Path_File_Name : String; Directory : String); -- Get the runtime source directories or the runtime -- library directories. --------------------- -- Get_Directories -- --------------------- procedure Get_Directories (Runtime_Dirs : in out Name_List_Index; Path_File_Name : String; Directory : String) is Path : constant String := Runtime_Dir & Directory_Separator & Path_File_Name; File : Ada.Text_IO.File_Type; Line : String (1 .. 1_000); Last : Natural; Last_Name : Name_List_Index; Local_Runtime_Dirs : Name_List_Index := No_Name_List; begin Runtime_Dirs := No_Name_List; if Is_Regular_File (Path) then Open (File, In_File, Path); while not End_Of_File (File) loop Get_Line (File, Line, Last); if Last > 0 then if Is_Absolute_Path (Line (1 .. Last)) then Name_Len := 0; else Set_Name_Buffer (Runtime_Dir & Directory_Separator); end if; Add_Str_To_Name_Buffer (Line (1 .. Last)); Name_List_Table.Append (Shared.Name_Lists, New_Val => (Name_Find, No_Name_List)); Last_Name := Name_List_Table.Last (Shared.Name_Lists); if Local_Runtime_Dirs = No_Name_List then Runtime_Dirs := Last_Name; else Shared.Name_Lists.Table (Local_Runtime_Dirs).Next := Last_Name; end if; Local_Runtime_Dirs := Last_Name; end if; end loop; Close (File); else Name_List_Table.Append (Shared.Name_Lists, New_Val => (Get_Name_Id (Runtime_Dir & Directory_Separator & Directory), No_Name_List)); Runtime_Dirs := Name_List_Table.Last (Shared.Name_Lists); end if; end Get_Directories; begin Lang_Index.Config.Runtime_Dir := Element.Value.Value; if Lang_Index.Name = Name_Ada then if Shared.Ada_Runtime_Dir = Element.Value.Value then Lang_Index.Config.Runtime_Library_Dirs := Shared.Ada_Runtime_Library_Dirs; Lang_Index.Config.Runtime_Source_Dirs := Shared.Ada_Runtime_Source_Dirs; Lang_Index.Config.Runtime_Library_Version := Shared.Ada_Runtime_Library_Version; else Get_Directories (Runtime_Dirs => Lang_Index.Config.Runtime_Source_Dirs, Path_File_Name => "ada_source_path", Directory => "adainclude"); Get_Directories (Runtime_Dirs => Lang_Index.Config.Runtime_Library_Dirs, Path_File_Name => "ada_object_path", Directory => "adalib"); declare Lib_Dirs : Name_List_Index := Lang_Index.Config.Runtime_Library_Dirs; Library_Dir : Name_Node; Version : Name_Id := No_Name; begin while Lib_Dirs /= No_Name_List loop Library_Dir := Shared.Name_Lists.Table (Lib_Dirs); if Is_Regular_File (Get_Name_String (Library_Dir.Name) & "/system.ali") then declare File : File_Type; Line : String (1 .. 1_000); Last : Natural; Start : Natural; begin Open (File, In_File, Get_Name_String (Library_Dir.Name) & "/system.ali"); Get_Line (File, Line, Last); Close (File); Start := Index (Line (1 .. Last), " v"); if Start /= 0 then Set_Name_Buffer (GNAT_And_Space); Add_Str_To_Name_Buffer (Line (Start + 2 .. Last - 1)); Version := Name_Find; end if; exit; end; end if; Lib_Dirs := Library_Dir.Next; end loop; if Version /= No_Name then Lang_Index.Config. Runtime_Library_Version := Version; else Lang_Index.Config. Runtime_Library_Version := Lang_Index.Config.Toolchain_Version; end if; Shared.Ada_Runtime_Dir := Element.Value.Value; Shared.Ada_Runtime_Library_Dirs := Lang_Index.Config.Runtime_Library_Dirs; Shared.Ada_Runtime_Source_Dirs := Lang_Index.Config.Runtime_Source_Dirs; Shared.Ada_Runtime_Library_Version := Lang_Index.Config.Runtime_Library_Version; end; end if; end if; end; elsif Current_Array.Name = Name_Runtime_Library_Version then -- Attribute Runtime_Library_Version () Lang_Index.Config.Runtime_Library_Version := Element.Value.Value; elsif Current_Array.Name = Name_Object_Generated then declare pragma Unsuppress (All_Checks); Value : Boolean; begin Value := Boolean'Value (Get_Name_String (Element.Value.Value)); Lang_Index.Config.Object_Generated := Value; -- If no object is generated, no object may be -- linked. if not Value then Lang_Index.Config.Objects_Linked := False; end if; exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Element.Value.Value) & """ for Object_Generated", Element.Value.Location, Project); end; elsif Current_Array.Name = Name_Objects_Linked then declare pragma Unsuppress (All_Checks); Value : Boolean; begin Value := Boolean'Value (Get_Name_String (Element.Value.Value)); -- No change if Object_Generated is False, as this -- forces Objects_Linked to be False too. if Lang_Index.Config.Object_Generated then Lang_Index.Config.Objects_Linked := Value; end if; exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Element.Value.Value) & """ for Objects_Linked", Element.Value.Location, Project); end; elsif Current_Array.Name = Name_Only_Dirs_With_Sources then declare pragma Unsuppress (All_Checks); Value : Boolean; begin Value := Boolean'Value (Get_Name_String (Element.Value.Value)); -- No change if Object_Generated is False, as this -- forces Objects_Linked to be False too. Lang_Index.Config.Only_Dirs_With_Sources := Value; exception when Constraint_Error => Error_Msg (Data.Flags, "invalid value """ & Get_Name_String (Element.Value.Value) & """ for Only_Dirs_With_Sources", Element.Value.Location, Project); end; end if; end if; Element_Id := Element.Next; end loop; Current_Array_Id := Current_Array.Next; end loop; end Process_Project_Level_Array_Attributes; -- Start of processing for Check_Configuration begin Process_Project_Level_Simple_Attributes; Process_Project_Level_Array_Attributes; Process_Packages; -- For unit based languages, set Casing, Dot_Replacement and -- Separate_Suffix in Naming_Data. Lang_Index := Project.Languages; while Lang_Index /= No_Language_Index loop if Lang_Index.Config.Required_Toolchain_Version /= No_Name and then Lang_Index.Config.Toolchain_Version /= Lang_Index.Config.Required_Toolchain_Version then declare function No_GNAT_Prefix (Id : Name_Id) return String; -- Returns version string without "GNAT " prefix for Ada -- language if prefix exists. Returns version string as is for -- non Ada languages. -------------------- -- No_GNAT_Prefix -- -------------------- function No_GNAT_Prefix (Id : Name_Id) return String is Result : constant String := Get_Name_String_Or_Null (Id); begin if Lang_Index.Name = Name_Ada and then Starts_With (Result, GNAT_And_Space) then return Result (Result'First + GNAT_And_Space'Length .. Result'Last); else return Result; end if; end No_GNAT_Prefix; TVC : constant String := No_GNAT_Prefix (Lang_Index.Config.Toolchain_Version); TVR : constant String := No_GNAT_Prefix (Lang_Index.Config.Required_Toolchain_Version); begin if TVC /= TVR then Error_Msg (Data.Flags, "Toolchain version " & (if TVC = "" then "" else '"' & TVC & """ ") & "for language " & Get_Name_String (Lang_Index.Name) & " differs from the required one """ & TVR & '"', No_Location, Project); end if; end; end if; if Lang_Index.Config.Kind = Unit_Based then Lang_Index.Config.Naming_Data.Casing := Casing; Lang_Index.Config.Naming_Data.Dot_Replacement := Dot_Replacement; if Separate_Suffix /= No_File then Lang_Index.Config.Naming_Data.Separate_Suffix := Separate_Suffix; end if; exit; end if; Lang_Index := Lang_Index.Next; end loop; -- Give empty names to various prefixes/suffixes, if they have not -- been specified in the configuration. if Project.Config.Archive_Suffix = No_File then Project.Config.Archive_Suffix := Empty_File; end if; if Project.Config.Shared_Lib_Prefix = No_File then Project.Config.Shared_Lib_Prefix := Empty_File; end if; if Project.Config.Shared_Lib_Suffix = No_File then Project.Config.Shared_Lib_Suffix := Empty_File; end if; Lang_Index := Project.Languages; while Lang_Index /= No_Language_Index loop if Is_Allowed_Language (Lang_Index.Name) then -- For all languages, Compiler_Driver should be specified. But -- there is no warning if it is not, as there may not be any -- source of the language. If there is such a source, then an -- error will be reported when trying to compile this source. if Lang_Index.Config.Compiler_Driver /= No_File or else Project.Externally_Built or else not Is_Allowed_Language (Lang_Index.Name) then if Lang_Index.Config.Kind = Unit_Based then -- For unit based languages, Dot_Replacement, Spec_Suffix -- and Body_Suffix need to be specified. if Lang_Index.Config.Naming_Data.Dot_Replacement = No_File then Error_Msg (Data.Flags, "Dot_Replacement not specified for " & Get_Name_String (Lang_Index.Name), No_Location, Project); end if; if Lang_Index.Config.Naming_Data.Spec_Suffix = No_File then Error_Msg (Data.Flags, "\Spec_Suffix not specified for " & Get_Name_String (Lang_Index.Name), No_Location, Project); end if; if Lang_Index.Config.Naming_Data.Body_Suffix = No_File then Error_Msg (Data.Flags, "\Body_Suffix not specified for " & Get_Name_String (Lang_Index.Name), No_Location, Project); end if; else -- For file based languages, either Spec_Suffix or -- Body_Suffix need to be specified. if Data.Flags.Require_Sources_Other_Lang and then Lang_Index.Config.Naming_Data.Spec_Suffix = No_File and then Lang_Index.Config.Naming_Data.Body_Suffix = No_File then Error_Msg_Name_1 := Lang_Index.Display_Name; Error_Msg (Data.Flags, "\no suffixes specified for %%", No_Location, Project); end if; end if; end if; end if; Lang_Index := Lang_Index.Next; end loop; end Check_Configuration; ------------------------------- -- Check_If_Externally_Built -- ------------------------------- procedure Check_If_Externally_Built (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Externally_Built : constant Variable_Value := Util.Value_Of (Name_Externally_Built, Project.Decl.Attributes, Shared); begin if not Externally_Built.Default then declare Lower_Value : constant String := To_Lower (Get_Name_String (Externally_Built.Value)); begin if Lower_Value = "true" then Project.Externally_Built := True; elsif Lower_Value /= "false" then Error_Msg (Data.Flags, "Externally_Built may only be true or false", Externally_Built.Location, Project); end if; end; end if; -- A virtual project extending an externally built project is itself -- externally built. if Project.Virtual and then Project.Extends /= No_Project then Project.Externally_Built := Project.Extends.Externally_Built; end if; if Project.Externally_Built then Debug_Output ("project is externally built"); else Debug_Output ("project is not externally built"); end if; end Check_If_Externally_Built; ---------------------- -- Check_Interfaces -- ---------------------- procedure Check_Interfaces (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Interfaces : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Interfaces, Project.Decl.Attributes, Shared); Library_Interface : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Interface, Project.Decl.Attributes, Shared); List : String_List_Id; Element : String_Element; Name : File_Name_Type; Iter : Source_Iterator; Source : Source_Id; Project_2 : Project_Id; Other : Source_Id; Unit_Found : Boolean; Interface_ALIs : String_List_Id := Nil_String; Other_Interfaces : String_List_Id := Nil_String; procedure Init_Interfaces; -- Set In_Interfaces to False for all sources. It will be set to True -- later for the sources in the [Library_]Interface list. -- Set In_Interfaces to True for sources from --src-subdirs directory. procedure Append_Interface_ALIs; procedure Append_Interfaces (List : in out String_List_Id; Value, Display_Value : File_Name_Type); ----------------------- -- Append_Interfaces -- ----------------------- procedure Append_Interfaces (List : in out String_List_Id; Value, Display_Value : File_Name_Type) is begin String_Element_Table.Increment_Last (Shared.String_Elements); Shared.String_Elements.Table (String_Element_Table.Last (Shared.String_Elements)) := (Value => Name_Id (Value), Index => 0, Display_Value => Name_Id (Display_Value), Location => No_Location, Next => List); List := String_Element_Table.Last (Shared.String_Elements); end Append_Interfaces; --------------------------- -- Append_Interface_ALIs -- --------------------------- procedure Append_Interface_ALIs is Src : Source_Id; begin if Source.Kind = Spec then Src := Other_Part (Source); end if; if Src = No_Source then Src := Source; end if; Append_Interfaces (Interface_ALIs, Src.Dep_Name, Src.Dep_Name); end Append_Interface_ALIs; --------------------- -- Init_Interfaces -- --------------------- procedure Init_Interfaces is begin Project_2 := Project; while Project_2 /= No_Project loop Iter := For_Each_Source (Data.Tree, Project_2); loop Source := GPR.Element (Iter); exit when Source = No_Source; if Source.In_Src_Subdir then Append_Interface_ALIs; else Source.In_Interfaces := False; end if; Next (Iter); end loop; Project_2 := Project_2.Extends; end loop; end Init_Interfaces; begin if not Interfaces.Default then Init_Interfaces; List := Interfaces.Values; while List /= Nil_String loop Element := Shared.String_Elements.Table (List); Name := Canonical_Case_File_Name (Element.Value); Project_2 := Project; Big_Loop : while Project_2 /= No_Project loop if Project.Qualifier = Aggregate_Library then -- For an aggregate library we want to consider sources of -- all aggregated projects. Iter := For_Each_Source (Data.Tree); else Iter := For_Each_Source (Data.Tree, Project_2); end if; loop Source := GPR.Element (Iter); exit when Source = No_Source; if Source.File = Name then if not Source.Locally_Removed then Source.In_Interfaces := True; Source.Declared_In_Interfaces := True; Other := Other_Part (Source); if Other /= No_Source then Other.In_Interfaces := True; Other.Declared_In_Interfaces := True; end if; -- Unit based case if Source.Language.Config.Kind = Unit_Based then Append_Interface_ALIs; -- File based case else Append_Interfaces (Other_Interfaces, Source.File, Source.Display_File); end if; Debug_Output ("interface: ", Name_Id (Source.Path.Name)); end if; exit Big_Loop; end if; Next (Iter); end loop; Project_2 := Project_2.Extends; end loop Big_Loop; if Source = No_Source and then not Languages_Are_Restricted then Error_Msg_File_1 := File_Name_Type (Element.Value); Error_Msg_Name_1 := Project.Name; Error_Msg (Data.Flags, "{ cannot be an interface of project %% " & "as it is not one of its sources", Element.Location, Project); end if; List := Element.Next; end loop; Project.Interfaces_Defined := True; Project.Lib_Interface_ALIs := Interface_ALIs; Project.Other_Interfaces := Other_Interfaces; elsif Project.Library and then not Library_Interface.Default then Init_Interfaces; List := Library_Interface.Values; while List /= Nil_String loop Element := Shared.String_Elements.Table (List); Name := File_Name_Type (Get_Lower_Name_Id (Get_Name_String (Element.Value))); Unit_Found := False; Project_2 := Project; Big_Loop_2 : while Project_2 /= No_Project loop if Project.Qualifier = Aggregate_Library then -- For an aggregate library we want to consider sources of -- all aggregated projects. Iter := For_Each_Source (Data.Tree); else Iter := For_Each_Source (Data.Tree, Project_2); end if; loop Source := GPR.Element (Iter); exit when Source = No_Source; if Source.Unit /= No_Unit_Index and then Source.Unit.Name = Name_Id (Name) then if not Source.Locally_Removed then Source.In_Interfaces := True; Source.Declared_In_Interfaces := True; Project.Interfaces_Defined := True; Other := Other_Part (Source); if Other /= No_Source then Other.In_Interfaces := True; Other.Declared_In_Interfaces := True; end if; Debug_Output ("interface: ", Name_Id (Source.Path.Name)); Append_Interface_ALIs; end if; Unit_Found := True; exit Big_Loop_2; end if; Next (Iter); end loop; Project_2 := Project_2.Extends; end loop Big_Loop_2; if not Unit_Found then Error_Msg_Name_1 := Name_Id (Name); Error_Msg (Data.Flags, "%% is not a unit of this project", Element.Location, Project); end if; List := Element.Next; end loop; Project.Lib_Interface_ALIs := Interface_ALIs; elsif Project.Extends /= No_Project and then Project.Extends.Interfaces_Defined then Project.Interfaces_Defined := True; Iter := For_Each_Source (Data.Tree, Project); loop Source := GPR.Element (Iter); exit when Source = No_Source; if not Source.Declared_In_Interfaces then Source.In_Interfaces := False; end if; Next (Iter); end loop; Project.Lib_Interface_ALIs := Project.Extends.Lib_Interface_ALIs; end if; end Check_Interfaces; ------------------------------ -- Check_Library_Attributes -- ------------------------------ -- This procedure is awfully long (over 700 lines) should be broken up??? procedure Check_Library_Attributes (Project : Project_Id; No_Sources : Boolean; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Attributes : constant Variable_Id := Project.Decl.Attributes; Lib_Dir : constant Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Dir, Attributes, Shared); Lib_Name : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Name, Attributes, Shared); Lib_Standalone : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Standalone, Attributes, Shared); Lib_Version : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Version, Attributes, Shared); Lib_ALI_Dir : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Ali_Dir, Attributes, Shared); Lib_GCC : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_GCC, Attributes, Shared); The_Lib_Kind : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Kind, Attributes, Shared); Imported_Project_List : Project_List; Continuation : String_Access := No_Continuation_String'Access; Support_For_Libraries : Library_Support; Library_Directory_Present : Boolean; procedure Check_Library (Proj : Project_Id; Extends : Boolean); -- Check if an imported or extended project if also a library project procedure Check_Aggregate_Library_Dirs; -- Check that the library directory and the library ALI directory of an -- aggregate library project are not the same as the object directory or -- the library directory of any of its aggregated projects. ---------------------------------- -- Check_Aggregate_Library_Dirs -- ---------------------------------- procedure Check_Aggregate_Library_Dirs is procedure Process_Aggregate (Proj : Project_Id); -- Recursive procedure to check the aggregated projects, as they may -- also be aggregated library projects. ----------------------- -- Process_Aggregate -- ----------------------- procedure Process_Aggregate (Proj : Project_Id) is Agg : Aggregated_Project_List; begin Agg := Proj.Aggregated_Projects; while Agg /= null loop Error_Msg_Name_1 := Agg.Project.Name; if Agg.Project.Qualifier /= Aggregate_Library and then Project.Library_ALI_Dir.Name = Agg.Project.Object_Directory.Name then Error_Msg (Data.Flags, "aggregate library 'A'L'I directory cannot be shared with" & " object directory of aggregated project %%", The_Lib_Kind.Location, Project); elsif Project.Library_ALI_Dir.Name = Agg.Project.Library_Dir.Name then Error_Msg (Data.Flags, "aggregate library 'A'L'I directory cannot be shared with" & " library directory of aggregated project %%", The_Lib_Kind.Location, Project); elsif Agg.Project.Qualifier /= Aggregate_Library and then Project.Library_Dir.Name = Agg.Project.Object_Directory.Name then Error_Msg (Data.Flags, "aggregate library directory cannot be shared with" & " object directory of aggregated project %%", The_Lib_Kind.Location, Project); elsif Project.Library_Dir.Name = Agg.Project.Library_Dir.Name then Error_Msg (Data.Flags, "aggregate library directory cannot be shared with" & " library directory of aggregated project %%", The_Lib_Kind.Location, Project); end if; if Agg.Project.Qualifier = Aggregate_Library then Process_Aggregate (Agg.Project); end if; Agg := Agg.Next; end loop; end Process_Aggregate; -- Start of processing for Check_Aggregate_Library_Dirs begin if Project.Qualifier = Aggregate_Library then Process_Aggregate (Project); end if; end Check_Aggregate_Library_Dirs; ------------------- -- Check_Library -- ------------------- procedure Check_Library (Proj : Project_Id; Extends : Boolean) is Src_Id : Source_Id; Iter : Source_Iterator; begin if Proj /= No_Project then if not Proj.Library then -- The only not library projects that are OK are those that -- have no sources. However, header files from non-Ada -- languages are OK, as there is nothing to compile. Iter := For_Each_Source (Data.Tree, Proj); loop Src_Id := GPR.Element (Iter); exit when Src_Id = No_Source or else Src_Id.Language.Config.Kind /= File_Based or else Src_Id.Kind /= Spec; Next (Iter); end loop; if Src_Id /= No_Source then Error_Msg_Name_1 := Project.Name; Error_Msg_Name_2 := Proj.Name; if Extends then if Project.Library_Kind /= Static then Error_Msg (Data.Flags, Continuation.all & "shared library project %% cannot extend " & "project %% that is not a library project", Project.Location, Project); Continuation := Continuation_String'Access; end if; elsif not Unchecked_Shared_Lib_Imports and then Project.Library_Kind /= Static then Error_Msg (Data.Flags, Continuation.all & "shared library project %% cannot import project %% " & "that is not a shared library project", Project.Location, Project); Continuation := Continuation_String'Access; end if; end if; elsif Project.Library_Kind /= Static and then not Lib_Standalone.Default and then To_Lower (Get_Name_String (Lib_Standalone.Value)) = "encapsulated" and then Proj.Library_Kind = Relocatable then -- An encapsulated library must depend only on static libraries Error_Msg_Name_1 := Project.Name; Error_Msg_Name_2 := Proj.Name; Error_Msg (Data.Flags, Continuation.all & "encapsulated library project %% cannot import shared " & "library project %%", Project.Location, Project); Continuation := Continuation_String'Access; elsif Project.Library_Kind /= Static and then Proj.Library_Kind = Static and then (Lib_Standalone.Default or else To_Lower (Get_Name_String (Lib_Standalone.Value)) /= "encapsulated") then Error_Msg_Name_1 := Project.Name; Error_Msg_Name_2 := Proj.Name; if Extends then Error_Msg (Data.Flags, Continuation.all & "shared library project %% cannot extend static " & "library project %%", Project.Location, Project); Continuation := Continuation_String'Access; elsif not Unchecked_Shared_Lib_Imports then Error_Msg (Data.Flags, Continuation.all & "shared library project %% cannot import static " & "library project %%", Project.Location, Project); Continuation := Continuation_String'Access; end if; end if; end if; end Check_Library; Dir_Exists : Boolean; -- Start of processing for Check_Library_Attributes begin Library_Directory_Present := Lib_Dir.Value not in No_Name | Empty_String; -- Special case of extending project if Project.Extends /= No_Project then -- If the project extended is a library project, we inherit the -- library name, if it is not redefined; we check that the library -- directory is specified. if Project.Extends.Library then if Project.Qualifier = Standard then Error_Msg (Data.Flags, "a standard project cannot extend a library project", Project.Location, Project); else if Lib_Name.Default then Project.Library_Name := Project.Extends.Library_Name; end if; if Lib_Dir.Default then if not Project.Virtual then Error_Msg (Data.Flags, "a project extending a library project must " & "specify an attribute Library_Dir", Project.Location, Project); else -- For a virtual project extending a library project, -- inherit library directory and library kind. Project.Library_Dir := Project.Extends.Library_Dir; Library_Directory_Present := True; Project.Library_Kind := Project.Extends.Library_Kind; end if; end if; end if; end if; end if; pragma Assert (Lib_Name.Kind = Single); if Lib_Name.Value = Empty_String then if Current_Verbosity = High and then Project.Library_Name = No_Name then Debug_Indent; Write_Line ("no library name"); end if; else -- There is no restriction on the syntax of library names Project.Library_Name := Lib_Name.Value; end if; if Project.Library_Name /= No_Name then if Current_Verbosity = High then Write_Attr ("Library name: ", Get_Name_String (Project.Library_Name)); end if; pragma Assert (Lib_Dir.Kind = Single); if not Library_Directory_Present then Debug_Output ("no library directory"); else -- Find path name (unless inherited), check that it is a directory if Project.Library_Dir = No_Path_Information then Locate_Directory (Project, File_Name_Type (Lib_Dir.Value), Path => Project.Library_Dir, Dir_Exists => Dir_Exists, Data => Data, Create => "library", Must_Exist => False, Location => Lib_Dir.Location, Externally_Built => Project.Externally_Built); else Dir_Exists := Is_Directory (Get_Name_String (Project.Library_Dir.Display_Name)); end if; if not Dir_Exists then if Directories_Must_Exist_In_Projects and then Project.Qualifier /= Abstract_Project then -- Get the absolute name of the library directory that does -- not exist, to report an error. Error_Msg_File_1 := File_Name_Type (Project.Library_Dir.Display_Name); Error_Or_Warning (Data.Flags, Data.Flags.Require_Obj_Dirs, "library directory { does not exist", Lib_Dir.Location, Project); end if; -- Checks for object/source directories elsif not Project.Externally_Built -- An aggregate library does not have sources or objects, so -- these tests are not required in this case. and then Project.Qualifier /= Aggregate_Library and then not No_Sources then -- Library directory cannot be the same as Object directory if Project.Library_Dir.Name = Project.Object_Directory.Name then Error_Msg (Data.Flags, "library directory cannot be the same " & "as object directory", Lib_Dir.Location, Project); Project.Library_Dir := No_Path_Information; else declare OK : Boolean := True; Dirs_Id : String_List_Id; Dir_Elem : String_Element; Pid : Project_List; begin -- The library directory cannot be the same as a source -- directory of the current project. Dirs_Id := Project.Source_Dirs; while Dirs_Id /= Nil_String loop Dir_Elem := Shared.String_Elements.Table (Dirs_Id); Dirs_Id := Dir_Elem.Next; if Project.Library_Dir.Name = Path_Name_Type (Dir_Elem.Value) then Error_Msg_File_1 := File_Name_Type (Dir_Elem.Value); Error_Msg (Data.Flags, "library directory cannot be the same " & "as source directory {", Lib_Dir.Location, Project); OK := False; exit; end if; end loop; if OK then -- The library directory cannot be the same as a -- source directory of another project either. Pid := Data.Tree.Projects; Project_Loop : loop exit Project_Loop when Pid = null; if Pid.Project /= Project then Dirs_Id := Pid.Project.Source_Dirs; Dir_Loop : while Dirs_Id /= Nil_String loop Dir_Elem := Shared.String_Elements.Table (Dirs_Id); Dirs_Id := Dir_Elem.Next; if Project.Library_Dir.Name = Path_Name_Type (Dir_Elem.Value) then Error_Msg_File_1 := File_Name_Type (Dir_Elem.Value); Error_Msg_Name_1 := Pid.Project.Name; Error_Msg (Data.Flags, "library directory cannot be the same " & "as source directory { of project %%", Lib_Dir.Location, Project); OK := False; exit Project_Loop; end if; end loop Dir_Loop; end if; Pid := Pid.Next; end loop Project_Loop; end if; if not OK then Project.Library_Dir := No_Path_Information; elsif Current_Verbosity = High then -- Display the Library directory in high verbosity Write_Attr ("Library directory", Get_Name_String (Project.Library_Dir.Display_Name)); end if; end; end if; end if; end if; end if; Project.Library := Project.Library_Dir /= No_Path_Information and then Project.Library_Name /= No_Name; if Project.Library and then No_Sources then Project.Library := False; Project.Library_Dir := No_Path_Information; Project.Library_Name := No_Name; Error_Msg (Data.Flags, "a project with no sources cannot be a library project", Project.Location, Project); return; elsif Project.Extends = No_Project then case Project.Qualifier is when Standard => if Project.Library then Error_Msg (Data.Flags, "a standard project cannot be a library project", Lib_Name.Location, Project); end if; when Library | Aggregate_Library => if not Project.Library then if Project.Library_Name = No_Name then Error_Msg (Data.Flags, "attribute Library_Name not declared", Project.Location, Project); if not Library_Directory_Present then Error_Msg (Data.Flags, "\attribute Library_Dir not declared", Project.Location, Project); end if; elsif Project.Library_Dir = No_Path_Information then Error_Msg (Data.Flags, "attribute Library_Dir not declared", Project.Location, Project); end if; end if; when others => null; end case; end if; if Project.Library then Support_For_Libraries := Project.Config.Lib_Support; if not Project.Externally_Built and then Support_For_Libraries = GPR.None then Error_Msg (Data.Flags, "?libraries are not supported on this platform", Lib_Name.Location, Project); Project.Library := False; else if Lib_ALI_Dir.Value = Empty_String then Debug_Output ("no library ALI directory specified"); Project.Library_ALI_Dir := Project.Library_Dir; else -- Find path name, check that it is a directory Locate_Directory (Project, File_Name_Type (Lib_ALI_Dir.Value), Path => Project.Library_ALI_Dir, Create => "library ALI", Dir_Exists => Dir_Exists, Data => Data, Must_Exist => False, Location => Lib_ALI_Dir.Location, Externally_Built => Project.Externally_Built); if not Dir_Exists and then Opt.Directories_Must_Exist_In_Projects then -- Get the absolute name of the library ALI directory that -- does not exist, to report an error. Error_Msg_File_1 := File_Name_Type (Project.Library_ALI_Dir.Display_Name); Error_Or_Warning (Data.Flags, Data.Flags.Require_Obj_Dirs, "library 'A'L'I directory { does not exist", Lib_ALI_Dir.Location, Project); end if; if not Project.Externally_Built and then Project.Library_ALI_Dir /= Project.Library_Dir then -- The library ALI directory cannot be the same as the -- Object directory. if Project.Library_ALI_Dir = Project.Object_Directory then Error_Msg (Data.Flags, "library 'A'L'I directory cannot be the same " & "as object directory", Lib_ALI_Dir.Location, Project); Project.Library_ALI_Dir := No_Path_Information; else declare OK : Boolean := True; Dirs_Id : String_List_Id; Dir_Elem : String_Element; Pid : Project_List; begin -- The library ALI directory cannot be the same as -- a source directory of the current project. Dirs_Id := Project.Source_Dirs; while Dirs_Id /= Nil_String loop Dir_Elem := Shared.String_Elements.Table (Dirs_Id); Dirs_Id := Dir_Elem.Next; if Project.Library_ALI_Dir.Name = Path_Name_Type (Dir_Elem.Value) then Error_Msg_File_1 := File_Name_Type (Dir_Elem.Value); Error_Msg (Data.Flags, "library 'A'L'I directory cannot be " & "the same as source directory {", Lib_ALI_Dir.Location, Project); OK := False; exit; end if; end loop; if OK then -- The library ALI directory cannot be the same as -- a source directory of another project either. Pid := Data.Tree.Projects; ALI_Project_Loop : loop exit ALI_Project_Loop when Pid = null; if Pid.Project /= Project then Dirs_Id := Pid.Project.Source_Dirs; ALI_Dir_Loop : while Dirs_Id /= Nil_String loop Dir_Elem := Shared.String_Elements.Table (Dirs_Id); Dirs_Id := Dir_Elem.Next; if Project.Library_ALI_Dir.Name = Path_Name_Type (Dir_Elem.Value) then Error_Msg_File_1 := File_Name_Type (Dir_Elem.Value); Error_Msg_Name_1 := Pid.Project.Name; Error_Msg (Data.Flags, "library 'A'L'I directory cannot " & "be the same as source directory " & "{ of project %%", Lib_ALI_Dir.Location, Project); OK := False; exit ALI_Project_Loop; end if; end loop ALI_Dir_Loop; end if; Pid := Pid.Next; end loop ALI_Project_Loop; end if; if not OK then Project.Library_ALI_Dir := No_Path_Information; elsif Current_Verbosity = High then -- Display Library ALI directory in high verbosity Write_Attr ("Library ALI dir", Get_Name_String (Project.Library_ALI_Dir.Display_Name)); end if; end; end if; end if; end if; pragma Assert (Lib_Version.Kind = Single); if Lib_Version.Value = Empty_String or else not Project.Config.Symbolic_Link_Supported then Debug_Output ("no library version specified"); else Project.Lib_Internal_Name := Lib_Version.Value; end if; pragma Assert (The_Lib_Kind.Kind = Single); if The_Lib_Kind.Value = Empty_String then Debug_Output ("no library kind specified"); elsif The_Lib_Kind.Value = No_Name then Error_Msg (Data.Flags, "incorrect value specified for library kind", The_Lib_Kind.Location, Project); Project.Library := False; else Get_Name_String (The_Lib_Kind.Value); declare Kind_Name : constant String := To_Lower (Name_Buffer (1 .. Name_Len)); OK : Boolean := True; begin if Kind_Name = "static" then Project.Library_Kind := Static; elsif Kind_Name = "static-pic" then Project.Library_Kind := Static_Pic; elsif Kind_Name = "dynamic" then Project.Library_Kind := Dynamic; elsif Kind_Name = "relocatable" then Project.Library_Kind := Relocatable; else Error_Msg (Data.Flags, "illegal value for Library_Kind", The_Lib_Kind.Location, Project); OK := False; end if; if Current_Verbosity = High and then OK then Write_Attr ("Library kind", Kind_Name); end if; if Project.Library_Kind /= Static then if not Project.Externally_Built and then Support_For_Libraries = GPR.Static_Only then Error_Msg (Data.Flags, "only static libraries are supported " & "on this platform", The_Lib_Kind.Location, Project); Project.Library := False; else -- Check if (obsolescent) attribute Library_GCC or -- Linker'Driver is declared. if Lib_GCC.Value /= Empty_String then Error_Msg (Data.Flags, "?Library_'G'C'C is an obsolescent attribute, " & "use Linker''Driver instead", Lib_GCC.Location, Project); Project.Config.Shared_Lib_Driver := File_Name_Type (Lib_GCC.Value); else declare Linker : constant Package_Id := Value_Of (Name_Linker, Project.Decl.Packages, Shared); Driver : constant Variable_Value := Value_Of (Name => No_Name, Attribute_Or_Array_Name => Name_Driver, In_Package => Linker, Shared => Shared); begin if Driver /= Nil_Variable_Value and then Driver.Value /= Empty_String then Project.Config.Shared_Lib_Driver := File_Name_Type (Driver.Value); end if; end; end if; end if; end if; end; end if; if Project.Library and then Project.Qualifier /= Aggregate_Library then Debug_Output ("this is a library project file"); Check_Library (Project.Extends, Extends => True); Compute_All_Imported_Projects (Project, Data.Tree); Imported_Project_List := Project.All_Imported_Projects; while Imported_Project_List /= null loop Check_Library (Imported_Project_List.Project, Extends => False); Imported_Project_List := Imported_Project_List.Next; end loop; end if; end if; end if; -- Check if Linker'Switches or Linker'Default_Switches are declared. -- Warn if they are declared, as it is a common error to think that -- library are "linked" with Linker switches. if Project.Library then declare Linker_Package_Id : constant Package_Id := Util.Value_Of (Name_Linker, Project.Decl.Packages, Shared); Linker_Package : Package_Element; Switches : Array_Element_Id := No_Array_Element; begin if Linker_Package_Id /= No_Package then Linker_Package := Shared.Packages.Table (Linker_Package_Id); Switches := Value_Of (Name => Name_Switches, In_Arrays => Linker_Package.Decl.Arrays, Shared => Shared); if Switches = No_Array_Element then Switches := Value_Of (Name => Name_Default_Switches, In_Arrays => Linker_Package.Decl.Arrays, Shared => Shared); end if; if Switches /= No_Array_Element then Error_Msg (Data.Flags, "?\Linker switches not taken into account in library " & "projects", No_Location, Project); end if; end if; end; end if; if Project.Extends /= No_Project and then Project.Extends.Library then -- Remove the library name from Lib_Data_Table for J in 1 .. Lib_Data_Table.Last loop if Lib_Data_Table.Table (J).Proj = Project.Extends then Lib_Data_Table.Table (J) := Lib_Data_Table.Table (Lib_Data_Table.Last); Lib_Data_Table.Set_Last (Lib_Data_Table.Last - 1); exit; end if; end loop; end if; if Project.Library and then not Lib_Name.Default then -- Check if the same library name is used in an other library project for J in 1 .. Lib_Data_Table.Last loop if Lib_Data_Table.Table (J).Name = Project.Library_Name and then Lib_Data_Table.Table (J).Tree = Data.Tree then Error_Msg_Name_1 := Lib_Data_Table.Table (J).Proj.Display_Name; Error_Msg (Data.Flags, "Library name cannot be the same as in project %%", Lib_Name.Location, Project); Project.Library := False; end if; end loop; end if; if Project.Library and then not Lib_Standalone.Default and then Project.Library_Kind not in Relocatable | Dynamic and then To_Lower (Get_Name_String (Lib_Standalone.Value)) = "encapsulated" then -- An encapsulated library must be a shared library Error_Msg_Name_1 := Project.Display_Name; Error_Msg (Data.Flags, Continuation.all & "encapsulated library project %%" & " must be a shared library project", Project.Location, Project); Continuation := Continuation_String'Access; end if; -- Check that aggregated libraries do not share the aggregate -- Library_ALI_Dir. if Project.Qualifier = Aggregate_Library then Check_Aggregate_Library_Dirs; end if; if Project.Library and not Data.In_Aggregate_Lib then -- Record the library name Lib_Data_Table.Append ((Name => Project.Library_Name, Proj => Project, Tree => Data.Tree)); end if; end Check_Library_Attributes; -------------------------- -- Check_Package_Naming -- -------------------------- procedure Check_Package_Naming (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Naming_Id : constant Package_Id := Util.Value_Of (Name_Naming, Project.Decl.Packages, Shared); Naming : Package_Element; Ada_Body_Suffix_Loc : Source_Ptr := No_Location; procedure Check_Naming; -- Check the validity of the Naming package (suffixes valid, ...) procedure Check_Common (Dot_Replacement : in out File_Name_Type; Casing : in out Casing_Type; Casing_Defined : out Boolean; Separate_Suffix : in out File_Name_Type; Sep_Suffix_Loc : out Source_Ptr); -- Check attributes common procedure Process_Exceptions_File_Based (Lang_Id : Language_Ptr; Kind : Source_Kind); procedure Process_Exceptions_Unit_Based (Lang_Id : Language_Ptr; Kind : Source_Kind); -- Process the naming exceptions for the two types of languages procedure Initialize_Naming_Data; -- Initialize internal naming data for the various languages ------------------ -- Check_Common -- ------------------ procedure Check_Common (Dot_Replacement : in out File_Name_Type; Casing : in out Casing_Type; Casing_Defined : out Boolean; Separate_Suffix : in out File_Name_Type; Sep_Suffix_Loc : out Source_Ptr) is Dot_Repl : constant Variable_Value := Util.Value_Of (Name_Dot_Replacement, Naming.Decl.Attributes, Shared); Casing_String : constant Variable_Value := Util.Value_Of (Name_Casing, Naming.Decl.Attributes, Shared); Sep_Suffix : constant Variable_Value := Util.Value_Of (Name_Separate_Suffix, Naming.Decl.Attributes, Shared); Dot_Repl_Loc : Source_Ptr; begin Sep_Suffix_Loc := No_Location; if not Dot_Repl.Default and then Dot_Repl.Value /= No_Name then pragma Assert (Dot_Repl.Kind = Single, "Dot_Replacement is not a string"); if Dot_Repl.Value = Empty_String then Error_Msg (Data.Flags, "Dot_Replacement cannot be empty", Dot_Repl.Location, Project); end if; Dot_Replacement := Canonical_Case_File_Name (Dot_Repl.Value); Dot_Repl_Loc := Dot_Repl.Location; declare Repl : constant String := Get_Name_String (Dot_Replacement); Not_OK : Boolean; subtype Printable_ASCII is Character range '!' .. '~'; begin -- Dot_Replacement cannot -- - be empty -- - start or end with an alphanumeric -- - be a single '_' -- - start with an '_' followed by an alphanumeric -- - contain a '.' except if it is "." Not_OK := Repl'Length = 0 or else Is_Alphanumeric (Repl (Repl'First)) or else Is_Alphanumeric (Repl (Repl'Last)) or else (Repl (Repl'First) = '_' and then (Repl'Length = 1 or else Is_Alphanumeric (Repl (Repl'First + 1)))) or else (Repl'Length > 1 and then Index (Source => Repl, Pattern => ".") /= 0); -- Dot_Replacement cannot include any character that is not -- printable ASCII except space (' '). if not Not_OK then for J in Repl'Range loop if not (Repl (J) in Printable_ASCII) then Not_OK := True; exit; end if; end loop; end if; if Not_OK then Error_Msg (Data.Flags, '"' & Repl & """ is illegal for Dot_Replacement", Dot_Repl_Loc, Project); end if; end; end if; if Dot_Replacement /= No_File then Write_Attr ("Dot_Replacement", Get_Name_String (Dot_Replacement)); end if; Casing_Defined := False; if not Casing_String.Default and then Casing_String.Value /= No_Name then pragma Assert (Casing_String.Kind = Single, "Casing is not a string"); declare Casing_Image : constant String := Get_Name_String (Casing_String.Value); begin if Casing_Image'Length = 0 then Error_Msg (Data.Flags, "Casing cannot be an empty string", Casing_String.Location, Project); end if; Casing := Value (Casing_Image); Casing_Defined := True; exception when Constraint_Error => Name_Len := Casing_Image'Length; Name_Buffer (1 .. Name_Len) := Casing_Image; Error_Msg_Name_1 := Name_Find; Error_Msg (Data.Flags, "%% is not a correct Casing", Casing_String.Location, Project); end; end if; Write_Attr ("Casing", Image (Casing)); if not Sep_Suffix.Default then if Sep_Suffix.Value = Empty_String then Error_Msg (Data.Flags, "Separate_Suffix cannot be empty", Sep_Suffix.Location, Project); else Separate_Suffix := Canonical_Case_File_Name (Sep_Suffix.Value); Sep_Suffix_Loc := Sep_Suffix.Location; Check_Illegal_Suffix (Project, Separate_Suffix, Dot_Replacement, "Separate_Suffix", Sep_Suffix.Location, Data); end if; end if; if Separate_Suffix /= No_File then Write_Attr ("Separate_Suffix", Get_Name_String (Separate_Suffix)); end if; end Check_Common; ----------------------------------- -- Process_Exceptions_File_Based -- ----------------------------------- procedure Process_Exceptions_File_Based (Lang_Id : Language_Ptr; Kind : Source_Kind) is Lang : constant Name_Id := Lang_Id.Name; Exceptions : Array_Element_Id; Exception_List : Variable_Value; Element_Id : String_List_Id; Element : String_Element; File_Name : File_Name_Type; Source : Source_Id; begin case Kind is when Impl | Sep => Exceptions := Value_Of (Name_Implementation_Exceptions, In_Arrays => Naming.Decl.Arrays, Shared => Shared); when Spec => Exceptions := Value_Of (Name_Specification_Exceptions, In_Arrays => Naming.Decl.Arrays, Shared => Shared); end case; Exception_List := Value_Of (Index => Lang, In_Array => Exceptions, Shared => Shared); if Exception_List /= Nil_Variable_Value then Element_Id := Exception_List.Values; while Element_Id /= Nil_String loop Element := Shared.String_Elements.Table (Element_Id); File_Name := Canonical_Case_File_Name (Element.Value); Source := Source_Files_Htable.Get (Data.Tree.Source_Files_HT, File_Name); while Source /= No_Source and then Source.Project /= Project loop Source := Source.Next_With_File_Name; end loop; if Source = No_Source then Add_Source (Id => Source, Data => Data, Project => Project, Source_Dir_Rank => 0, Lang_Id => Lang_Id, Kind => Kind, File_Name => File_Name, Display_File => File_Name_Type (Element.Value), Naming_Exception => Yes, Location => Element.Location); else -- Check if the file name is already recorded for another -- language or another kind. if Source.Language /= Lang_Id then Error_Msg (Data.Flags, "the same file cannot be a source of two languages", Element.Location, Project); elsif Source.Kind /= Kind then Error_Msg (Data.Flags, "the same file cannot be a source and a template", Element.Location, Project); end if; -- If the file is already recorded for the same -- language and the same kind, it means that the file -- name appears several times in the *_Exceptions -- attribute; so there is nothing to do. end if; Element_Id := Element.Next; end loop; end if; end Process_Exceptions_File_Based; ----------------------------------- -- Process_Exceptions_Unit_Based -- ----------------------------------- procedure Process_Exceptions_Unit_Based (Lang_Id : Language_Ptr; Kind : Source_Kind) is Exceptions : Array_Element_Id; Element : Array_Element; Unit : Name_Id; Index : Int; File_Name : File_Name_Type; Source : Source_Id; Naming_Exception : Naming_Exception_Type; begin case Kind is when Impl | Sep => Exceptions := Value_Of (Name_Body, In_Arrays => Naming.Decl.Arrays, Shared => Shared); if Exceptions = No_Array_Element then Exceptions := Value_Of (Name_Implementation, In_Arrays => Naming.Decl.Arrays, Shared => Shared); end if; when Spec => Exceptions := Value_Of (Name_Spec, In_Arrays => Naming.Decl.Arrays, Shared => Shared); if Exceptions = No_Array_Element then Exceptions := Value_Of (Name_Specification, In_Arrays => Naming.Decl.Arrays, Shared => Shared); end if; end case; while Exceptions /= No_Array_Element loop Element := Shared.Array_Elements.Table (Exceptions); if Element.Restricted then Naming_Exception := Inherited; else Naming_Exception := Yes; end if; File_Name := Canonical_Case_File_Name (Element.Value.Value); Index := Element.Value.Index; -- Check if it is a valid unit name Check_Unit_Name (Get_Name_String (Element.Index), Unit); if Unit = No_Name then Error_Msg_Name_1 := Element.Index; Error_Msg (Data.Flags, "%% is not a valid unit name.", Element.Value.Location, Project); end if; if Unit /= No_Name then Add_Source (Id => Source, Data => Data, Project => Project, Source_Dir_Rank => 0, Lang_Id => Lang_Id, Kind => Kind, File_Name => File_Name, Display_File => File_Name_Type (Element.Value.Value), Unit => Unit, Index => Index, Location => Element.Value.Location, Naming_Exception => Naming_Exception); end if; Exceptions := Element.Next; end loop; end Process_Exceptions_Unit_Based; ------------------ -- Check_Naming -- ------------------ procedure Check_Naming is Dot_Replacement : File_Name_Type := File_Name_Type (First_Name_Id + Character'Pos ('-')); Separate_Suffix : File_Name_Type := No_File; Casing : Casing_Type := All_Lower_Case; Casing_Defined : Boolean; Lang_Id : Language_Ptr; Sep_Suffix_Loc : Source_Ptr; Suffix : Variable_Value; Lang : Name_Id; function Name_Id_Hash (Key : Name_Id) return Ada.Containers.Hash_Type; function Name_Id_Hash (Key : Name_Id) return Ada.Containers.Hash_Type is begin return Ada.Strings.Hash_Case_Insensitive (Get_Name_String (Key)); end Name_Id_Hash; function Name_Id_Equal_Case_Insensitive (Left, Right : Name_Id) return Boolean; function Name_Id_Equal_Case_Insensitive (Left, Right : Name_Id) return Boolean is begin return Ada.Strings.Equal_Case_Insensitive (Get_Name_String (Left), Get_Name_String (Right)); end Name_Id_Equal_Case_Insensitive; package Suffix_Lang_Maps is new Ada.Containers.Hashed_Maps (Key_Type => Name_Id, -- Suffix Element_Type => Name_Id, -- Language Hash => Name_Id_Hash, Equivalent_Keys => Name_Id_Equal_Case_Insensitive, "=" => Name_Id_Equal_Case_Insensitive); Suffix_Lang_Map : Suffix_Lang_Maps.Map; use type Suffix_Lang_Maps.Cursor; begin Check_Common (Dot_Replacement => Dot_Replacement, Casing => Casing, Casing_Defined => Casing_Defined, Separate_Suffix => Separate_Suffix, Sep_Suffix_Loc => Sep_Suffix_Loc); -- For all unit based languages, if any, set the specified value -- of Dot_Replacement, Casing and/or Separate_Suffix. Do not -- systematically overwrite, since the defaults come from the -- configuration file. if Dot_Replacement /= No_File or else Casing_Defined or else Separate_Suffix /= No_File then Lang_Id := Project.Languages; while Lang_Id /= No_Language_Index loop if Lang_Id.Config.Kind = Unit_Based then if Dot_Replacement /= No_File then Lang_Id.Config.Naming_Data.Dot_Replacement := Dot_Replacement; end if; if Casing_Defined then Lang_Id.Config.Naming_Data.Casing := Casing; end if; end if; Lang_Id := Lang_Id.Next; end loop; end if; -- Next, get the spec and body suffixes Lang_Id := Project.Languages; while Lang_Id /= No_Language_Index loop Lang := Lang_Id.Name; -- Spec_Suffix Suffix := Value_Of (Name => Lang, Attribute_Or_Array_Name => Name_Spec_Suffix, In_Package => Naming_Id, Shared => Shared); if Suffix = Nil_Variable_Value then Suffix := Value_Of (Name => Lang, Attribute_Or_Array_Name => Name_Specification_Suffix, In_Package => Naming_Id, Shared => Shared); end if; if Suffix /= Nil_Variable_Value and then Suffix.Value /= No_Name then -- Check if there is an ambiguity for the spec suffix -- i.e. it is already associated (as a spec or body suffix) -- with another language. declare Associated_Lang : constant Suffix_Lang_Maps.Cursor := Suffix_Lang_Map.Find (Key => Suffix.Value); begin if Associated_Lang /= Suffix_Lang_Maps.No_Element then Error_Msg (Data.Flags, "Spec_Suffix (""" & Get_Name_String (Suffix.Value) & """) for language " & Get_Name_String (Lang_Id.Name) & " is also defined for language " & Get_Name_String (Suffix_Lang_Map (Suffix.Value)) & ".", Suffix.Location, Project); else Suffix_Lang_Map.Include (Key => Suffix.Value, New_Item => Lang_Id.Name); end if; end; Lang_Id.Config.Naming_Data.Spec_Suffix := File_Name_Type (Suffix.Value); Check_Illegal_Suffix (Project, Lang_Id.Config.Naming_Data.Spec_Suffix, Lang_Id.Config.Naming_Data.Dot_Replacement, "Spec_Suffix", Suffix.Location, Data); Write_Attr ("Spec_Suffix", Get_Name_String (Lang_Id.Config.Naming_Data.Spec_Suffix)); end if; -- Body_Suffix Suffix := Value_Of (Name => Lang, Attribute_Or_Array_Name => Name_Body_Suffix, In_Package => Naming_Id, Shared => Shared); if Suffix = Nil_Variable_Value then Suffix := Value_Of (Name => Lang, Attribute_Or_Array_Name => Name_Implementation_Suffix, In_Package => Naming_Id, Shared => Shared); end if; if Suffix /= Nil_Variable_Value and then Suffix.Value /= No_Name then -- Check if there is an ambiguity for the body suffix -- i.e. it is already associated (as a spec or body suffix) -- with another language. declare Associated_Lang : constant Suffix_Lang_Maps.Cursor := Suffix_Lang_Map.Find (Key => Suffix.Value); begin if Associated_Lang /= Suffix_Lang_Maps.No_Element then Error_Msg (Data.Flags, "Body_Suffix (""" & Get_Name_String (Suffix.Value) & """) for language " & Get_Name_String (Lang_Id.Name) & " is also defined for language " & Get_Name_String (Suffix_Lang_Map (Suffix.Value)) & ".", Suffix.Location, Project); else Suffix_Lang_Map.Include (Key => Suffix.Value, New_Item => Lang_Id.Name); end if; end; Lang_Id.Config.Naming_Data.Body_Suffix := File_Name_Type (Suffix.Value); -- The default value of separate suffix should be the same as -- the body suffix, so we need to compute that first. if Separate_Suffix = No_File then Lang_Id.Config.Naming_Data.Separate_Suffix := Lang_Id.Config.Naming_Data.Body_Suffix; Write_Attr ("Sep_Suffix", Get_Name_String (Lang_Id.Config.Naming_Data.Separate_Suffix)); else Lang_Id.Config.Naming_Data.Separate_Suffix := Separate_Suffix; end if; Check_Illegal_Suffix (Project, Lang_Id.Config.Naming_Data.Body_Suffix, Lang_Id.Config.Naming_Data.Dot_Replacement, "Body_Suffix", Suffix.Location, Data); Write_Attr ("Body_Suffix", Get_Name_String (Lang_Id.Config.Naming_Data.Body_Suffix)); elsif Separate_Suffix /= No_File then Lang_Id.Config.Naming_Data.Separate_Suffix := Separate_Suffix; end if; -- Spec_Suffix cannot be equal to Body_Suffix or Separate_Suffix, -- since that would cause a clear ambiguity. Note that we do allow -- a Spec_Suffix to have the same termination as one of these, -- which causes a potential ambiguity, but we resolve that by -- matching the longest possible suffix. if Lang_Id.Config.Naming_Data.Spec_Suffix /= No_File and then Lang_Id.Config.Naming_Data.Spec_Suffix = Lang_Id.Config.Naming_Data.Body_Suffix then Error_Msg (Data.Flags, "Body_Suffix (""" & Get_Name_String (Lang_Id.Config.Naming_Data.Body_Suffix) & """) cannot be the same as Spec_Suffix.", Ada_Body_Suffix_Loc, Project); end if; if Lang_Id.Config.Naming_Data.Body_Suffix /= Lang_Id.Config.Naming_Data.Separate_Suffix and then Lang_Id.Config.Naming_Data.Spec_Suffix = Lang_Id.Config.Naming_Data.Separate_Suffix then Error_Msg (Data.Flags, "Separate_Suffix (""" & Get_Name_String (Lang_Id.Config.Naming_Data.Separate_Suffix) & """) cannot be the same as Spec_Suffix.", Sep_Suffix_Loc, Project); end if; Lang_Id := Lang_Id.Next; end loop; -- Get the naming exceptions for all languages, but not for virtual -- projects. if not Project.Virtual and then Project.Source_Dirs /= Nil_String then for Kind in Spec_Or_Body loop Lang_Id := Project.Languages; while Lang_Id /= No_Language_Index loop case Lang_Id.Config.Kind is when File_Based => Process_Exceptions_File_Based (Lang_Id, Kind); when Unit_Based => Process_Exceptions_Unit_Based (Lang_Id, Kind); end case; Lang_Id := Lang_Id.Next; end loop; end loop; end if; end Check_Naming; ---------------------------- -- Initialize_Naming_Data -- ---------------------------- procedure Initialize_Naming_Data is Specs : Array_Element_Id := Util.Value_Of (Name_Spec_Suffix, Naming.Decl.Arrays, Shared); Impls : Array_Element_Id := Util.Value_Of (Name_Body_Suffix, Naming.Decl.Arrays, Shared); Lang : Language_Ptr; Lang_Name : Name_Id; Value : Variable_Value; Extended : Project_Id; begin -- At this stage, the project already contains the default extensions -- for the various languages. We now merge those suffixes read in the -- user project, and they override the default. while Specs /= No_Array_Element loop Lang_Name := Shared.Array_Elements.Table (Specs).Index; Lang := Get_Language_From_Name (Project, Name => Get_Name_String (Lang_Name)); -- An extending project inherits its parent projects' languages -- so if needed we should create entries for those languages if Lang = null then Extended := Project.Extends; while Extended /= null loop Lang := Get_Language_From_Name (Extended, Name => Get_Name_String (Lang_Name)); exit when Lang /= null; Extended := Extended.Extends; end loop; if Lang /= null then Lang := new Language_Data'(Lang.all); Lang.First_Source := null; Lang.Next := Project.Languages; Project.Languages := Lang; end if; end if; -- If language was not found in project or the projects it extends if Lang = null then Debug_Output ("ignoring spec naming data (lang. not in project): ", Lang_Name); else Value := Shared.Array_Elements.Table (Specs).Value; if Value.Kind = Single then Lang.Config.Naming_Data.Spec_Suffix := Canonical_Case_File_Name (Value.Value); end if; end if; Specs := Shared.Array_Elements.Table (Specs).Next; end loop; while Impls /= No_Array_Element loop Lang_Name := Shared.Array_Elements.Table (Impls).Index; Lang := Get_Language_From_Name (Project, Name => Get_Name_String (Lang_Name)); if Lang = null then Debug_Output ("ignoring impl naming data (lang. not in project): ", Lang_Name); else Value := Shared.Array_Elements.Table (Impls).Value; if Lang.Name = Name_Ada then Ada_Body_Suffix_Loc := Value.Location; end if; if Value.Kind = Single then Lang.Config.Naming_Data.Body_Suffix := Canonical_Case_File_Name (Value.Value); end if; end if; Impls := Shared.Array_Elements.Table (Impls).Next; end loop; end Initialize_Naming_Data; -- Start of processing for Check_Naming_Schemes begin -- No Naming package or parsing a configuration file? nothing to do if Naming_Id /= No_Package and then Project.Qualifier /= Configuration then Naming := Shared.Packages.Table (Naming_Id); Debug_Increase_Indent ("checking package Naming for ", Project.Name); Initialize_Naming_Data; Check_Naming; Debug_Decrease_Indent ("done checking package naming"); end if; end Check_Package_Naming; --------------------------------- -- Check_Programming_Languages -- --------------------------------- procedure Check_Programming_Languages (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Languages : Variable_Value := Nil_Variable_Value; Def_Lang : Variable_Value := Nil_Variable_Value; Def_Lang_Id : Name_Id; procedure Add_Language (Name, Display_Name : Name_Id); -- Add a new language to the list of languages for the project. -- Nothing is done if the language has already been defined ------------------ -- Add_Language -- ------------------ procedure Add_Language (Name, Display_Name : Name_Id) is Lang : Language_Ptr; begin Lang := Project.Languages; while Lang /= No_Language_Index loop if Name = Lang.Name then return; end if; Lang := Lang.Next; end loop; Lang := new Language_Data'(No_Language_Data); Lang.Next := Project.Languages; Project.Languages := Lang; Lang.Name := Name; Lang.Display_Name := Display_Name; end Add_Language; -- Start of processing for Check_Programming_Languages begin Project.Languages := null; Languages := GPR.Util.Value_Of (Name_Languages, Project.Decl.Attributes, Shared); Def_Lang := GPR.Util.Value_Of (Name_Default_Language, Project.Decl.Attributes, Shared); -- Check if languages are specified in this project if Languages.Default then -- Fail if there is no default language defined if Def_Lang.Default then Error_Msg (Data.Flags, "no languages defined for this project", Project.Location, Project); Def_Lang_Id := No_Name; else Def_Lang_Id := Get_Lower_Name_Id (Get_Name_String (Def_Lang.Value)); end if; if Def_Lang_Id /= No_Name then Get_Name_String (Def_Lang_Id); Name_Buffer (1) := To_Upper (Name_Buffer (1)); Add_Language (Name => Def_Lang_Id, Display_Name => Name_Find); end if; else declare Current : String_List_Id := Languages.Values; Element : String_Element; begin -- If there are no languages declared, there are no sources if Current = Nil_String then Project.Source_Dirs := Nil_String; if Project.Qualifier = Standard then Error_Msg (Data.Flags, "a standard project must have at least one language", Languages.Location, Project); end if; else -- Look through all the languages specified in attribute -- Languages. while Current /= Nil_String loop Element := Shared.String_Elements.Table (Current); Add_Language (Get_Lower_Name_Id (Get_Name_String (Element.Value)), Display_Name => Element.Value); Current := Element.Next; end loop; end if; end; end if; end Check_Programming_Languages; ------------------------------- -- Check_Stand_Alone_Library -- ------------------------------- procedure Check_Stand_Alone_Library (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Lib_Name : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Name, Project.Decl.Attributes, Shared); Lib_Standalone : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Standalone, Project.Decl.Attributes, Shared); Lib_Auto_Init : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Auto_Init, Project.Decl.Attributes, Shared); Lib_Src_Dir : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Src_Dir, Project.Decl.Attributes, Shared); Lib_Symbol_File : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Symbol_File, Project.Decl.Attributes, Shared); Lib_Symbol_Policy : constant GPR.Variable_Value := GPR.Util.Value_Of (Snames.Name_Library_Symbol_Policy, Project.Decl.Attributes, Shared); Auto_Init_Supported : Boolean; begin Auto_Init_Supported := Project.Config.Auto_Init_Supported; -- It is a stand-alone library project file if there is at least one -- unit in the declared or inherited interface. if Project.Lib_Interface_ALIs = Nil_String then if not Lib_Standalone.Default and then To_Lower (Get_Name_String (Lib_Standalone.Value)) /= "no" then Error_Msg (Data.Flags, "Library_Standalone valid only if library has Ada interfaces", Lib_Standalone.Location, Project); end if; else if Project.Standalone_Library = No then Project.Standalone_Library := Standard; end if; -- The name of a stand-alone library needs to have the syntax of an -- Ada identifier. declare Name : constant String := Get_Name_String (Project.Library_Name); OK : Boolean := Is_Letter (Name (Name'First)); Underline : Boolean := False; begin for J in Name'First + 1 .. Name'Last loop exit when not OK; if Is_Alphanumeric (Name (J)) then Underline := False; elsif Name (J) = '_' then if Underline then OK := False; else Underline := True; end if; else OK := False; end if; end loop; OK := OK and not Underline; if not OK then Error_Msg (Data.Flags, "Incorrect library name for a Stand-Alone Library", Lib_Name.Location, Project); return; end if; end; if Lib_Standalone.Default then Project.Standalone_Library := Standard; else Get_Name_String (Lib_Standalone.Value); Set_Casing (All_Lower_Case); if Name_Buffer (1 .. Name_Len) = "standard" then Project.Standalone_Library := Standard; elsif Name_Buffer (1 .. Name_Len) = "encapsulated" then Project.Standalone_Library := Encapsulated; elsif Name_Buffer (1 .. Name_Len) = "no" then Project.Standalone_Library := No; Error_Msg (Data.Flags, "wrong value for Library_Standalone " & "when Library_Interface defined", Lib_Standalone.Location, Project); else Error_Msg (Data.Flags, "invalid value for attribute Library_Standalone", Lib_Standalone.Location, Project); end if; end if; -- Check value of attribute Library_Auto_Init and set Lib_Auto_Init -- accordingly. if Lib_Auto_Init.Default then -- If no attribute Library_Auto_Init is declared, then set auto -- init only if it is supported. Project.Lib_Auto_Init := Auto_Init_Supported; else Get_Name_String (Lib_Auto_Init.Value); Set_Casing (All_Lower_Case); if Name_Buffer (1 .. Name_Len) = "false" then Project.Lib_Auto_Init := False; elsif Name_Buffer (1 .. Name_Len) = "true" then if Auto_Init_Supported then Project.Lib_Auto_Init := True; else -- Library_Auto_Init cannot be "true" if auto init is not -- supported. Error_Msg (Data.Flags, "library auto init not supported " & "on this platform", Lib_Auto_Init.Location, Project); end if; else Error_Msg (Data.Flags, "invalid value for attribute Library_Auto_Init", Lib_Auto_Init.Location, Project); end if; end if; -- If attribute Library_Src_Dir is defined and not the empty string, -- check if the directory exist and is not the object directory or -- one of the source directories. This is the directory where copies -- of the interface sources will be copied. Note that this directory -- may be the library directory. if Lib_Src_Dir.Value /= Empty_String then declare Dir_Id : constant File_Name_Type := File_Name_Type (Lib_Src_Dir.Value); Dir_Exists : Boolean; begin Locate_Directory (Project, Dir_Id, Path => Project.Library_Src_Dir, Dir_Exists => Dir_Exists, Data => Data, Must_Exist => False, Create => "library source copy", Location => Lib_Src_Dir.Location, Externally_Built => Project.Externally_Built); -- If directory does not exist, report an error if not Dir_Exists then if Opt.Directories_Must_Exist_In_Projects then -- Get the absolute name of the library directory that -- does not exist, to report an error. Error_Msg_File_1 := File_Name_Type (Project.Library_Src_Dir.Display_Name); Error_Or_Warning (Data.Flags, Data.Flags.Require_Obj_Dirs, "Directory { does not exist", Lib_Src_Dir.Location, Project); end if; -- Report error if it is the same as the object directory elsif Project.Library_Src_Dir = Project.Object_Directory then Error_Msg (Data.Flags, "directory to copy interfaces cannot be " & "the object directory", Lib_Src_Dir.Location, Project); Project.Library_Src_Dir := No_Path_Information; else declare Src_Dirs : String_List_Id; Src_Dir : String_Element; Pid : Project_List; begin -- Interface copy directory cannot be one of the source -- directory of the current project. Src_Dirs := Project.Source_Dirs; while Src_Dirs /= Nil_String loop Src_Dir := Shared.String_Elements.Table (Src_Dirs); -- Report error if it is one of the source directories if Project.Library_Src_Dir.Name = Path_Name_Type (Src_Dir.Value) then Error_Msg (Data.Flags, "directory to copy interfaces cannot " & "be one of the source directories", Lib_Src_Dir.Location, Project); Project.Library_Src_Dir := No_Path_Information; exit; end if; Src_Dirs := Src_Dir.Next; end loop; if Project.Library_Src_Dir /= No_Path_Information then -- It cannot be a source directory of any other -- project either. Pid := Data.Tree.Projects; Project_Loop : loop exit Project_Loop when Pid = null; Src_Dirs := Pid.Project.Source_Dirs; Dir_Loop : while Src_Dirs /= Nil_String loop Src_Dir := Shared.String_Elements.Table (Src_Dirs); -- Report error if it is one of the source -- directories. if Project.Library_Src_Dir.Name = Path_Name_Type (Src_Dir.Value) then Error_Msg_File_1 := File_Name_Type (Src_Dir.Value); Error_Msg_Name_1 := Pid.Project.Name; Error_Msg (Data.Flags, "directory to copy interfaces cannot " & "be the same as source directory { of " & "project %%", Lib_Src_Dir.Location, Project); Project.Library_Src_Dir := No_Path_Information; exit Project_Loop; end if; Src_Dirs := Src_Dir.Next; end loop Dir_Loop; Pid := Pid.Next; end loop Project_Loop; end if; end; -- In high verbosity, if there is a valid Library_Src_Dir, -- display its path name. if Project.Library_Src_Dir /= No_Path_Information and then Current_Verbosity = High then Write_Attr ("Directory to copy interfaces", Get_Name_String (Project.Library_Src_Dir.Name)); end if; end if; end; end if; -- Check the symbol related attributes -- First, the symbol policy if Lib_Symbol_Policy.Default then Project.Symbol_Data.Symbol_Policy := Restricted; else declare Value : constant String := To_Lower (Get_Name_String (Lib_Symbol_Policy.Value)); begin -- Symbol policy must have one of a limited number of values if Value = "unrestricted" then Project.Symbol_Data.Symbol_Policy := Unrestricted; elsif Value = "restricted" then Project.Symbol_Data.Symbol_Policy := Restricted; else Error_Msg (Data.Flags, "illegal value for Library_Symbol_Policy", Lib_Symbol_Policy.Location, Project); end if; end; end if; if not Lib_Symbol_File.Default then -- Library_Symbol_File is defined, check file exists Project.Symbol_Data.Symbol_File := Path_Name_Type (Lib_Symbol_File.Value); Get_Name_String (Lib_Symbol_File.Value); if Name_Len = 0 then Error_Msg (Data.Flags, "symbol file name cannot be an empty string", Lib_Symbol_File.Location, Project); else if not Is_Absolute_Path (Name_Buffer (1 .. Name_Len)) then Get_Name_String (Project.Directory.Display_Name); Get_Name_String_And_Append (Lib_Symbol_File.Value); Project.Symbol_Data.Symbol_File := Name_Find; end if; if not Is_Regular_File (Get_Name_String (Project.Symbol_Data.Symbol_File)) then Error_Msg_File_1 := File_Name_Type (Lib_Symbol_File.Value); Error_Msg_Warn := False; Error_Msg (Data.Flags, " 0; First : Positive; function Is_Reserved (Name : Name_Id) return Boolean; function Is_Reserved (S : String) return Boolean; -- Check that the given name is not an Ada 95 reserved word. The reason -- for the Ada 95 here is that we do not want to exclude the case of an -- Ada 95 unit called Interface (for example). In Ada 2005, such a unit -- name would be rejected anyway by the compiler. That means there is no -- requirement that the project file parser reject this. ----------------- -- Is_Reserved -- ----------------- function Is_Reserved (S : String) return Boolean is begin return Is_Reserved (Get_Name_Id (S)); end Is_Reserved; ----------------- -- Is_Reserved -- ----------------- function Is_Reserved (Name : Name_Id) return Boolean is begin if Name in Reserved_Ada_95 then Unit := No_Name; Debug_Output ("project reserved word: ", Name); return True; else return False; end if; end Is_Reserved; -- Start of processing for Check_Unit_Name begin Name_Len := The_Name'Length; Name_Buffer (1 .. Name_Len) := The_Name; Real_Name := Name_Find; if Is_Reserved (Real_Name) then return; end if; First := The_Name'First; for Index in The_Name'Range loop if Need_Letter then -- We need a letter (at the beginning, and following a dot), -- but we don't have one. if Is_Letter (The_Name (Index)) then Need_Letter := False; else OK := False; if Current_Verbosity = High then Debug_Indent; Write_Str (Index'Img); Write_Str (": '"); Write_Char (The_Name (Index)); Write_Line ("' is not a letter."); end if; exit; end if; elsif Last_Underscore and then (The_Name (Index) = '_' or else The_Name (Index) = '.') then -- Two underscores are illegal, and a dot cannot follow -- an underscore. OK := False; if Current_Verbosity = High then Debug_Indent; Write_Str (Index'Img); Write_Str (": '"); Write_Char (The_Name (Index)); Write_Line ("' is illegal here."); end if; exit; elsif The_Name (Index) = '.' then -- First, check if the name before the dot is not a reserved word if Is_Reserved (The_Name (First .. Index - 1)) then return; end if; First := Index + 1; -- We need a letter after a dot Need_Letter := True; elsif The_Name (Index) = '_' then Last_Underscore := True; else -- We need an letter or a digit Last_Underscore := False; if not Is_Alphanumeric (The_Name (Index)) then OK := False; if Current_Verbosity = High then Debug_Indent; Write_Str (Index'Img); Write_Str (": '"); Write_Char (The_Name (Index)); Write_Line ("' is not alphanumeric."); end if; exit; end if; end if; end loop; -- Cannot end with an underscore or a dot OK := OK and then not Need_Letter and then not Last_Underscore; if OK then if First /= Name'First and then Is_Reserved (The_Name (First .. The_Name'Last)) then return; end if; Unit := Real_Name; else -- Signal a problem with No_Name Unit := No_Name; end if; end Check_Unit_Name; ---------------------------- -- Compute_Directory_Last -- ---------------------------- function Compute_Directory_Last (Dir : String) return Natural is begin if Dir'Length > 1 and then Is_Directory_Separator (Dir (Dir'Last - 1)) then return Dir'Last - 1; else return Dir'Last; end if; end Compute_Directory_Last; -------------------------- -- Get_Object_Directory -- -------------------------- procedure Get_Object_Directory (Project : Project_Id; Data : in out Tree_Processing_Data; No_Sources : Boolean := False) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Object_Dir : constant Variable_Value := Util.Value_Of (Name_Object_Dir, Project.Decl.Attributes, Shared); Dir_Exists : Boolean; begin -- Set the object directory to its default which may be nil, if there -- is no sources in the project. if No_Sources then Project.Object_Directory := No_Path_Information; else Project.Object_Directory := Project.Directory; end if; -- Check the object directory if Object_Dir.Value not in No_Name | Empty_String then Get_Name_String (Object_Dir.Value); if Name_Len = 0 then Error_Msg (Data.Flags, "Object_Dir cannot be empty", Object_Dir.Location, Project); elsif Create_Dirs /= Never_Create_Dirs and then No_Sources and then Project.Extends = No_Project then -- Do not create an object directory for a non extending project -- with no sources. Locate_Directory (Project, File_Name_Type (Object_Dir.Value), Path => Project.Object_Directory, Dir_Exists => Dir_Exists, Data => Data, Location => Object_Dir.Location, Must_Exist => False, Externally_Built => Project.Externally_Built); else -- We check that the specified object directory does exist. -- However, even when it doesn't exist, we set it to a default -- value. This is for the benefit of tools that recover from -- errors; for example, these tools could create the non existent -- directory. We always return an absolute directory name though. Locate_Directory (Project, File_Name_Type (Object_Dir.Value), Path => Project.Object_Directory, Create => "object", Dir_Exists => Dir_Exists, Data => Data, Location => Object_Dir.Location, Must_Exist => False, Externally_Built => Project.Externally_Built); if not Dir_Exists and then not Project.Externally_Built and then not (Project.Qualifier = Abstract_Project) then if Opt.Directories_Must_Exist_In_Projects then -- The object directory does not exist, report an error if -- the project is not externally built. Error_Msg_File_1 := File_Name_Type (Object_Dir.Value); Error_Or_Warning (Data.Flags, Data.Flags.Require_Obj_Dirs, "object directory { not found", Object_Dir.Location, Project); end if; end if; end if; elsif not No_Sources and then (Subdirs /= null or else Build_Tree_Dir /= null) then Name_Len := 1; Name_Buffer (1) := '.'; Locate_Directory (Project, Name_Find, Path => Project.Object_Directory, Create => "object", Dir_Exists => Dir_Exists, Data => Data, Location => Object_Dir.Location, Externally_Built => Project.Externally_Built); end if; if Current_Verbosity = High then if Project.Object_Directory = No_Path_Information then Debug_Output ("no object directory"); else Write_Attr ("Object directory", Get_Name_String (Project.Object_Directory.Display_Name)); end if; end if; end Get_Object_Directory; --------------------- -- Get_Directories -- --------------------- procedure Get_Directories (Project : Project_Id; Data : in out Tree_Processing_Data; No_Sources : out Boolean) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Exec_Dir : constant Variable_Value := Util.Value_Of (Name_Exec_Dir, Project.Decl.Attributes, Shared); Source_Dirs : constant Variable_Value := Util.Value_Of (Name_Source_Dirs, Project.Decl.Attributes, Shared); Ignore_Source_Sub_Dirs : constant Variable_Value := Util.Value_Of (Name_Ignore_Source_Sub_Dirs, Project.Decl.Attributes, Shared); Excluded_Source_Dirs : constant Variable_Value := Util.Value_Of (Name_Excluded_Source_Dirs, Project.Decl.Attributes, Shared); Source_Files : constant Variable_Value := Util.Value_Of (Name_Source_Files, Project.Decl.Attributes, Shared); Last_Source_Dir : String_List_Id := Nil_String; Last_Src_Dir_Rank : Number_List_Index := No_Number_List; Languages : constant Variable_Value := GPR.Util.Value_Of (Name_Languages, Project.Decl.Attributes, Shared); procedure Add_To_Or_Remove_From_Source_Dirs (Path : Path_Information; Rank : Natural; Remove : Boolean); -- When Remove = False, Adds the directory Path_Id to the list of -- source_dirs if not already in the list. When Remove = True, -- removes directory Path_Id if in the list. procedure Add_To_Source_Dirs (Path : Path_Information; Rank : Natural); -- Adds the directory Path_Id to the list of source_dirs if not already -- in the list. procedure Remove_From_Source_Dirs (Path : Path_Information; Rank : Natural); -- Removes directory Path_Id if in the list. procedure Find_Source_Dirs (Patterns : String_List_Id; Ignore : String_List_Id; Callback : not null access procedure (Path : Path_Information; Pattern_Index : Natural) := Add_To_Source_Dirs'Access); ---------------------- -- Find_Source_Dirs -- ---------------------- procedure Find_Source_Dirs (Patterns : String_List_Id; Ignore : String_List_Id; Callback : not null access procedure (Path : Path_Information; Pattern_Index : Natural) := Add_To_Source_Dirs'Access) is begin Expand_Subdirectory_Pattern (Project => Project, Data => Data, Patterns => Patterns, Ignore => Ignore, Search_For => Search_Source_Directories, Resolve_Links => Opt.Follow_Links_For_Dirs, Callback => Callback); end Find_Source_Dirs; --------------------------------------- -- Add_To_Or_Remove_From_Source_Dirs -- --------------------------------------- procedure Add_To_Or_Remove_From_Source_Dirs (Path : Path_Information; Rank : Natural; Remove : Boolean) is List : String_List_Id; Prev : String_List_Id; Rank_List : Number_List_Index; Prev_Rank : Number_List_Index; Element : String_Element; begin Prev := Nil_String; Prev_Rank := No_Number_List; List := Project.Source_Dirs; Rank_List := Project.Source_Dir_Ranks; while List /= Nil_String loop Element := Shared.String_Elements.Table (List); exit when Element.Value = Name_Id (Path.Name); Prev := List; List := Element.Next; Prev_Rank := Rank_List; Rank_List := Shared.Number_Lists.Table (Prev_Rank).Next; end loop; -- The directory is in the list if List is not Nil_String if not Remove and then List = Nil_String then Debug_Output ("adding source dir=", Name_Id (Path.Display_Name)); String_Element_Table.Increment_Last (Shared.String_Elements); Element := (Value => Name_Id (Path.Name), Index => 0, Display_Value => Name_Id (Path.Display_Name), Location => No_Location, Next => Nil_String); Number_List_Table.Increment_Last (Shared.Number_Lists); if Last_Source_Dir = Nil_String then -- This is the first source directory Project.Source_Dirs := String_Element_Table.Last (Shared.String_Elements); Project.Source_Dir_Ranks := Number_List_Table.Last (Shared.Number_Lists); else -- We already have source directories, link the previous -- last to the new one. Shared.String_Elements.Table (Last_Source_Dir).Next := String_Element_Table.Last (Shared.String_Elements); Shared.Number_Lists.Table (Last_Src_Dir_Rank).Next := Number_List_Table.Last (Shared.Number_Lists); end if; -- And register this source directory as the new last Last_Source_Dir := String_Element_Table.Last (Shared.String_Elements); Shared.String_Elements.Table (Last_Source_Dir) := Element; Last_Src_Dir_Rank := Number_List_Table.Last (Shared.Number_Lists); Shared.Number_Lists.Table (Last_Src_Dir_Rank) := (Number => Rank, Next => No_Number_List); elsif Remove and then List /= Nil_String then -- Remove source dir if present if Prev = Nil_String then Project.Source_Dirs := Shared.String_Elements.Table (List).Next; Project.Source_Dir_Ranks := Shared.Number_Lists.Table (Rank_List).Next; else Shared.String_Elements.Table (Prev).Next := Shared.String_Elements.Table (List).Next; Shared.Number_Lists.Table (Prev_Rank).Next := Shared.Number_Lists.Table (Rank_List).Next; end if; end if; end Add_To_Or_Remove_From_Source_Dirs; ------------------------ -- Add_To_Source_Dirs -- ------------------------ procedure Add_To_Source_Dirs (Path : Path_Information; Rank : Natural) is begin Add_To_Or_Remove_From_Source_Dirs (Path, Rank, Remove => False); end Add_To_Source_Dirs; ----------------------------- -- Remove_From_Source_Dirs -- ----------------------------- procedure Remove_From_Source_Dirs (Path : Path_Information; Rank : Natural) is begin Add_To_Or_Remove_From_Source_Dirs (Path, Rank, Remove => True); end Remove_From_Source_Dirs; -- Local declarations Dir_Exists : Boolean; -- Start of processing for Get_Directories begin No_Sources := Project.Qualifier = Abstract_Project or else (((not Source_Files.Default and then Source_Files.Values = Nil_String) or else (not Source_Dirs.Default and then Source_Dirs.Values = Nil_String) or else (not Languages.Default and then Languages.Values = Nil_String)) and then Project.Extends = No_Project); Debug_Output ("starting to look for directories"); Get_Object_Directory (Project, Data, No_Sources); -- Check the exec directory -- We set the object directory to its default Project.Exec_Directory := Project.Object_Directory; if Exec_Dir.Value not in Empty_String | No_Name then Get_Name_String (Exec_Dir.Value); if Name_Len = 0 then Error_Msg (Data.Flags, "Exec_Dir cannot be empty", Exec_Dir.Location, Project); elsif Create_Dirs /= Never_Create_Dirs and then No_Sources and then Project.Extends = No_Project then -- Do not create an exec directory for a non extending project -- with no sources. Locate_Directory (Project, File_Name_Type (Exec_Dir.Value), Path => Project.Exec_Directory, Dir_Exists => Dir_Exists, Data => Data, Must_Exist => False, Location => Exec_Dir.Location, Externally_Built => Project.Externally_Built); else -- We check that the specified exec directory does exist Locate_Directory (Project, File_Name_Type (Exec_Dir.Value), Path => Project.Exec_Directory, Dir_Exists => Dir_Exists, Data => Data, Create => "exec", Must_Exist => False, Location => Exec_Dir.Location, Externally_Built => Project.Externally_Built); if not Dir_Exists and then not Project.Externally_Built and then not (Project.Qualifier = Abstract_Project) then if Opt.Directories_Must_Exist_In_Projects then Error_Msg_File_1 := File_Name_Type (Exec_Dir.Value); Error_Or_Warning (Data.Flags, Data.Flags.Require_Obj_Dirs, "exec directory { not found", Project.Location, Project); else Project.Exec_Directory := No_Path_Information; end if; end if; end if; end if; if Current_Verbosity = High then if Project.Exec_Directory = No_Path_Information then Debug_Output ("no exec directory"); else Debug_Output ("exec directory: ", Name_Id (Project.Exec_Directory.Display_Name)); end if; end if; -- Look for the source directories Debug_Output ("starting to look for source directories"); pragma Assert (Source_Dirs.Kind = List, "Source_Dirs is not a list"); if not Source_Files.Default and then Source_Files.Values = Nil_String then Project.Source_Dirs := Nil_String; if Project.Qualifier = Standard then Error_Msg (Data.Flags, "a standard project must have sources", Source_Files.Location, Project); end if; -- No source dirs: nothing further to do return; end if; -- If we were provided src-subdirs, add / in -- front of the source directories so that files found in this directory -- will override original source files. if Src_Subdirs /= null and then Project.Qualifier /= Abstract_Project and then not No_Sources then declare function Try_Src_Subdir (Prefix : Name_Id) return Boolean; -- Try to add source subdirectory in object directory Obj_Dir with -- Prefix or not if Prefix is No_Name. Returns True on success. function Get_Src_Subdir (Prefix : Name_Id) return String is ((if Prefix = No_Name then "" else Get_Name_String (Prefix) & '-') & Src_Subdirs.all & Directory_Separator); -- Returns --src-subdirs parameter either with project name prefix -- or not if PRefix is No_Name. -------------------- -- Try_Src_Subdir -- -------------------- function Try_Src_Subdir (Prefix : Name_Id) return Boolean is Src_Subdir : constant String := Get_Src_Subdir (Prefix); Name : Path_Name_Type; N : String := Get_Name_String (Project.Object_Directory.Name) & Src_Subdir; begin if Is_Directory (N) then Canonical_Case_File_Name (N); Name := Get_Path_Name_Id (N); -- Set Rank to 0 so that duplicate units are silently -- accepted. Add_To_Source_Dirs (Path => (Name => Name, Display_Name => Get_Path_Name_Id (Get_Name_String (Project.Object_Directory.Display_Name) & Src_Subdir)), Rank => 0); return True; end if; return False; end Try_Src_Subdir; begin if Try_Src_Subdir (Project.Name) or else Try_Src_Subdir (No_Name) then null; end if; end; end if; if Source_Dirs.Default then -- No Source_Dirs specified: the single source directory is the one -- containing the project file. Add_To_Source_Dirs (Path => Project.Directory, Rank => 1); else Find_Source_Dirs (Source_Dirs.Values, Ignore_Source_Sub_Dirs.Values); if Project.Source_Dirs = Nil_String and then Project.Qualifier = Standard then Error_Msg (Data.Flags, "a standard project must have source directories", Source_Dirs.Location, Project); end if; end if; if not Excluded_Source_Dirs.Default and then Excluded_Source_Dirs.Values /= Nil_String then Find_Source_Dirs (Excluded_Source_Dirs.Values, Nil_String, Remove_From_Source_Dirs'Access); end if; Debug_Output ("putting source directories in canonical cases"); declare Current : String_List_Id := Project.Source_Dirs; Element : String_Element; begin while Current /= Nil_String loop Element := Shared.String_Elements.Table (Current); if Element.Value /= No_Name then Element.Value := Name_Id (Canonical_Case_File_Name (Element.Value)); Shared.String_Elements.Table (Current) := Element; end if; Current := Element.Next; end loop; end; end Get_Directories; --------------- -- Get_Mains -- --------------- procedure Get_Mains (Project : Project_Id; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Mains : constant Variable_Value := GPR.Util.Value_Of (Name_Main, Project.Decl.Attributes, Shared); List : String_List_Id; Elem : String_Element; begin Project.Mains := Mains.Values; -- If no Mains were specified, and if we are an extending project, -- inherit the Mains from the project we are extending. if Mains.Default then if not Project.Library and then Project.Extends /= No_Project then Project.Mains := Project.Extends.Mains; end if; -- In a library project file, Main cannot be specified elsif Project.Library then Error_Msg (Data.Flags, "a library project file cannot have Main specified", Mains.Location, Project); else List := Mains.Values; while List /= Nil_String loop Elem := Shared.String_Elements.Table (List); if Elem.Value = Empty_String then Error_Msg (Data.Flags, "?a main cannot have an empty name", Elem.Location, Project); exit; end if; List := Elem.Next; end loop; end if; end Get_Mains; --------------------------- -- Get_Sources_From_File -- --------------------------- procedure Get_Sources_From_File (Path : String; Location : Source_Ptr; Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data) is File : GPR.Util.Text_File; Line : String (1 .. 250); Last : Natural; Source_Name : File_Name_Type; Name_Loc : Name_Location; begin if Current_Verbosity = High then Debug_Output ("opening """ & Path & '"'); end if; -- Open the file GPR.Util.Open (File, Path); if not GPR.Util.Is_Valid (File) then Error_Msg (Data.Flags, "file does not exist", Location, Project.Project); else -- Read the lines one by one while not GPR.Util.End_Of_File (File) loop GPR.Util.Get_Line (File, Line, Last); -- A non empty, non comment line should contain a file name if Last /= 0 and then (Last = 1 or else Line (1 .. 2) /= "--") then Name_Len := Last; Name_Buffer (1 .. Name_Len) := Line (1 .. Last); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Source_Name := Name_Find; -- Check that there is no directory information for J in 1 .. Last loop if Is_Directory_Separator (Line (J)) then Error_Msg_File_1 := Source_Name; Error_Msg (Data.Flags, "file name cannot include directory information ({)", Location, Project.Project); exit; end if; end loop; Name_Loc := Source_Names_Htable.Get (Project.Source_Names, Source_Name); if Name_Loc = No_Name_Location then Name_Loc := (Name => Source_Name, Location => Location, Source => No_Source, Listed => True, Found => False); else Name_Loc.Listed := True; end if; Source_Names_Htable.Set (Project.Source_Names, Source_Name, Name_Loc); end if; end loop; GPR.Util.Close (File); end if; end Get_Sources_From_File; ------------------ -- No_Space_Img -- ------------------ function No_Space_Img (N : Natural) return String is Image : constant String := N'Img; begin return Image (2 .. Image'Last); end No_Space_Img; ----------------------- -- Compute_Unit_Name -- ----------------------- procedure Compute_Unit_Name (File_Name : File_Name_Type; Naming : Lang_Naming_Data; Kind : out Source_Kind; Unit : out Name_Id; Project : Project_Processing_Data) is Filename : constant String := Get_Name_String (File_Name); Last : Integer := Filename'Last; Sep_Len : Integer; Body_Len : Integer; Spec_Len : Integer; Unit_Except : Unit_Exception; Masked : Boolean := False; begin Unit := No_Name; Kind := Spec; if Naming.Separate_Suffix = No_File or else Naming.Body_Suffix = No_File or else Naming.Spec_Suffix = No_File then return; end if; if Naming.Dot_Replacement = No_File then Debug_Output ("no dot_replacement specified"); return; end if; Sep_Len := Integer (Length_Of_Name (Naming.Separate_Suffix)); Spec_Len := Integer (Length_Of_Name (Naming.Spec_Suffix)); Body_Len := Integer (Length_Of_Name (Naming.Body_Suffix)); -- Choose the longest suffix that matches. If there are several matches, -- give priority to specs, then bodies, then separates. if Naming.Separate_Suffix /= Naming.Body_Suffix and then Suffix_Matches (Filename, Naming.Separate_Suffix) then Last := Filename'Last - Sep_Len; Kind := Sep; end if; if Filename'Last - Body_Len <= Last and then Suffix_Matches (Filename, Naming.Body_Suffix) then Last := Natural'Min (Last, Filename'Last - Body_Len); Kind := Impl; end if; if Filename'Last - Spec_Len <= Last and then Suffix_Matches (Filename, Naming.Spec_Suffix) then Last := Natural'Min (Last, Filename'Last - Spec_Len); Kind := Spec; end if; if Last = Filename'Last then Debug_Output ("no matching suffix"); return; end if; -- Check that the casing matches. -- The debug flag -du deactivates this. if not Debug.Debug_Flag_U and then File_Names_Case_Sensitive then case Naming.Casing is when All_Lower_Case => for J in Filename'First .. Last loop if Is_Letter (Filename (J)) and then not Is_Lower (Filename (J)) then Debug_Output ("invalid casing"); return; end if; end loop; when All_Upper_Case => for J in Filename'First .. Last loop if Is_Letter (Filename (J)) and then not Is_Upper (Filename (J)) then Debug_Output ("invalid casing"); return; end if; end loop; when Mixed_Case | Unknown => null; end case; end if; -- If Dot_Replacement is not a single dot, then there should not -- be any dot in the name. declare Dot_Repl : constant String := Get_Name_String (Naming.Dot_Replacement); begin if Dot_Repl /= "." then for Index in Filename'First .. Last loop if Filename (Index) = '.' then Debug_Output ("invalid name, contains dot"); return; end if; end loop; Replace_Into_Name_Buffer (Filename (Filename'First .. Last), Dot_Repl, '.'); else Name_Len := Last - Filename'First + 1; Name_Buffer (1 .. Name_Len) := Filename (Filename'First .. Last); Fixed.Translate (Source => Name_Buffer (1 .. Name_Len), Mapping => Lower_Case_Map); end if; end; -- In the standard GNAT naming scheme, check for special cases: children -- or separates of A, G, I or S, and run time sources. if Is_Standard_GNAT_Naming (Naming) and then Name_Len >= 3 then declare S1 : constant Character := Name_Buffer (1); S2 : constant Character := Name_Buffer (2); S3 : constant Character := Name_Buffer (3); begin if S1 = 'a' or else S1 = 'g' or else S1 = 'i' or else S1 = 's' then -- Children or separates of packages A, G, I or S. These names -- are x__ ... or x~... (where x is a, g, i, or s). Both -- versions (x__... and x~...) are allowed in all platforms, -- because it is not possible to know the platform before -- processing of the project files. if S2 = '_' and then S3 = '_' then Name_Buffer (2) := '.'; Name_Buffer (3 .. Name_Len - 1) := Name_Buffer (4 .. Name_Len); Name_Len := Name_Len - 1; elsif S2 = '~' then Name_Buffer (2) := '.'; elsif S2 = '.' then -- If it is potentially a run time source null; end if; end if; end; end if; -- Name_Buffer contains the name of the unit in lower-cases. Check -- that this is a valid unit name. -- The debug flag -du deactivates this. if not Debug.Debug_Flag_U then Check_Unit_Name (Name_Buffer (1 .. Name_Len), Unit); else Unit := Name_Find; end if; -- If there is a naming exception for the same unit, the file is not -- a source for the unit. if Unit /= No_Name then Unit_Except := Unit_Exceptions_Htable.Get (Project.Unit_Exceptions, Unit); if Kind = Spec then Masked := Unit_Except.Spec /= No_File and then Unit_Except.Spec /= File_Name; else Masked := Unit_Except.Impl /= No_File and then Unit_Except.Impl /= File_Name; end if; if Masked then if Current_Verbosity = High then Debug_Indent; Write_Str (" """ & Filename & """ contains the "); if Kind = Spec then Write_Str ("spec of a unit found in """); Write_Str (Get_Name_String (Unit_Except.Spec)); else Write_Str ("body of a unit found in """); Write_Str (Get_Name_String (Unit_Except.Impl)); end if; Write_Line (""" (ignored)"); end if; Unit := No_Name; end if; end if; if Unit /= No_Name and then Current_Verbosity = High then case Kind is when Spec => Debug_Output ("spec of", Unit); when Impl => Debug_Output ("body of", Unit); when Sep => Debug_Output ("sep of", Unit); end case; end if; end Compute_Unit_Name; -------------------------- -- Check_Illegal_Suffix -- -------------------------- procedure Check_Illegal_Suffix (Project : Project_Id; Suffix : File_Name_Type; Dot_Replacement : File_Name_Type; Attribute_Name : String; Location : Source_Ptr; Data : in out Tree_Processing_Data) is Suffix_Str : constant String := Get_Name_String (Suffix); begin if Suffix_Str'Length = 0 then -- Always valid return; elsif Index (Suffix_Str, ".") = 0 then Error_Msg_File_1 := Suffix; Error_Msg (Data.Flags, "{ is illegal for " & Attribute_Name & ": must have a dot", Location, Project); return; end if; -- Case of dot replacement is a single dot, and first character of -- suffix is also a dot. if Dot_Replacement /= No_File and then Get_Name_String (Dot_Replacement) = "." and then Suffix_Str (Suffix_Str'First) = '.' then for Index in Suffix_Str'First + 1 .. Suffix_Str'Last loop -- If there are multiple dots in the name if Suffix_Str (Index) = '.' then -- It is illegal to have a letter following the initial dot if Is_Letter (Suffix_Str (Suffix_Str'First + 1)) then Error_Msg_File_1 := Suffix; Error_Msg (Data.Flags, "{ is illegal for " & Attribute_Name & ": ambiguous prefix when Dot_Replacement is a dot", Location, Project); end if; return; end if; end loop; end if; end Check_Illegal_Suffix; ---------------------- -- Locate_Directory -- ---------------------- procedure Locate_Directory (Project : Project_Id; Name : File_Name_Type; Path : out Path_Information; Dir_Exists : out Boolean; Data : in out Tree_Processing_Data; Create : String := ""; Location : Source_Ptr := No_Location; Must_Exist : Boolean := True; Externally_Built : Boolean := False) is Parent : constant Path_Name_Type := Project.Directory.Display_Name; The_Parent : constant String := Get_Name_String (Parent); The_Parent_Last : constant Natural := Compute_Directory_Last (The_Parent); Full_Name : File_Name_Type; The_Name : File_Name_Type; Is_Relative : Boolean; begin -- Check if we have a root-object dir specified, if so relocate all -- artefact directories to it. if not Externally_Built and then Build_Tree_Dir /= null and then Create /= "" and then not Is_Absolute_Path (Get_Name_String (Name)) then Set_Name_Buffer (Build_Tree_Dir.all); if The_Parent_Last - The_Parent'First + 1 < Root_Dir'Length then Error_Msg_File_1 := Name; Error_Or_Warning (Data.Flags, Error, "{ cannot relocate deeper than " & Create & " directory", No_Location, Project); end if; Add_Str_To_Name_Buffer (Relative_Path (The_Parent (The_Parent'First .. The_Parent_Last), Root_Dir.all)); Get_Name_String_And_Append (Name); else if not Externally_Built and then Build_Tree_Dir /= null and then Create /= "" then -- Issue a warning that we cannot relocate absolute obj dir Error_Msg_File_1 := Name; Error_Or_Warning (Data.Flags, Warning, "{ cannot relocate absolute object directory", No_Location, Project); end if; Get_Name_String (Name); end if; -- Convert '/' to directory separator (for Windows) if Directory_Separator /= '/' then for J in 1 .. Name_Len loop if Name_Buffer (J) = '/' then Name_Buffer (J) := Directory_Separator; end if; end loop; end if; -- Add Subdirs.all if it is a directory that may be created and -- Subdirs is not null; if Create /= "" and then Subdirs /= null then if Name_Buffer (Name_Len) /= Directory_Separator then Add_Char_To_Name_Buffer (Directory_Separator); end if; Add_Str_To_Name_Buffer (Subdirs.all); end if; The_Name := Name_Find; if Current_Verbosity = High then Debug_Indent; Write_Str ("Locate_Directory ("""); Write_Str (Get_Name_String (The_Name)); Write_Str (""", in """); Write_Str (The_Parent); Write_Line (""")"); end if; Path := No_Path_Information; Dir_Exists := False; if Is_Absolute_Path (Get_Name_String (The_Name)) then Is_Relative := False; Full_Name := The_Name; else Is_Relative := True; Set_Name_Buffer (The_Parent (The_Parent'First .. The_Parent_Last)); Get_Name_String_And_Append (The_Name); Full_Name := Name_Find; end if; declare Full_Path_Name : String_Access := new String'(Get_Name_String (Full_Name)); begin -- We may proceed with directory creation depending on the value -- of Create_Dirs: if it is set to Create_All_Dirs, or if the dir -- is relative and Create_Dirs is set to Create_Relative_Dirs_Only. if (Create_Dirs = Create_All_Dirs or else (Create_Dirs = Create_Relative_Dirs_Only and then Is_Relative) or else Subdirs /= null) and then Create'Length > 0 and then Project.Qualifier /= Abstract_Project then if not Is_Directory (Full_Path_Name.all) then -- If project is externally built, do not create a subdir, -- use the specified directory, without the subdir. if Externally_Built then if Is_Absolute_Path (Get_Name_String (Name)) then Get_Name_String (Name); else Set_Name_Buffer (The_Parent (The_Parent'First .. The_Parent_Last)); Get_Name_String_And_Append (Name); end if; Free (Full_Path_Name); Full_Path_Name := new String'(Name_Buffer (1 .. Name_Len)); else begin Create_Path (Full_Path_Name.all); if not GPR.Opt.Quiet_Output then if Verbose_Mode then Write_Str (Create); Write_Str (" directory """); Write_Str (Full_Path_Name.all); Write_Str (""" created for project "); Write_Line (Get_Name_String (Project.Display_Name)); else Display (Section => Setup, Command => "mkdir", Argument => Create & " directory for project " & Get_Name_String (Project.Display_Name)); end if; end if; exception when Ada.Directories.Use_Error => -- Output message with name of directory. Note that we -- use the ~ insertion method here in case the name -- has special characters in it. Error_Msg_Strlen := Full_Path_Name'Length; Error_Msg_String (1 .. Error_Msg_Strlen) := Full_Path_Name.all; Error_Msg (Data.Flags, "could not create " & Create & " directory ~", Location, Project); end; end if; end if; end if; Dir_Exists := Is_Directory (Full_Path_Name.all); if not Must_Exist or Dir_Exists then declare Normed : constant String := Normalize_Pathname (Full_Path_Name.all, Directory => The_Parent (The_Parent'First .. The_Parent_Last), Resolve_Links => False, Case_Sensitive => True); Canonical_Path : constant String := Normalize_Pathname (Normed, Directory => The_Parent (The_Parent'First .. The_Parent_Last), Resolve_Links => Opt.Follow_Links_For_Dirs, Case_Sensitive => False); begin Name_Len := Normed'Length; Name_Buffer (1 .. Name_Len) := Normed; -- Directories should always end with a directory separator if Name_Buffer (Name_Len) /= Directory_Separator then Add_Char_To_Name_Buffer (Directory_Separator); end if; Path.Display_Name := Name_Find; Name_Len := Canonical_Path'Length; Name_Buffer (1 .. Name_Len) := Canonical_Path; if Name_Buffer (Name_Len) /= Directory_Separator then Add_Char_To_Name_Buffer (Directory_Separator); end if; Path.Name := Name_Find; end; end if; Free (Full_Path_Name); end; end Locate_Directory; --------------------------- -- Find_Excluded_Sources -- --------------------------- procedure Find_Excluded_Sources (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Current : String_List_Id; Element : String_Element; Location : Source_Ptr; Name : File_Name_Type; File : GPR.Util.Text_File; Line : String (1 .. 300); Last : Natural; Locally_Removed : Boolean := False; Proj : Project_Id := Project.Project; begin -- Look for excluded sources in the current project and in all the -- projects that extend it. while Proj /= No_Project loop declare Excluded_Source_List_File : constant Variable_Value := Util.Value_Of (Name_Excluded_Source_List_File, Proj.Decl.Attributes, Shared); Excluded_Sources : Variable_Value := Util.Value_Of (Name_Excluded_Source_Files, Proj.Decl.Attributes, Shared); begin -- If Excluded_Source_Files is not declared, check -- Locally_Removed_Files. if Excluded_Sources.Default then Locally_Removed := True; Excluded_Sources := Util.Value_Of (Name_Locally_Removed_Files, Proj.Decl.Attributes, Shared); end if; -- If there are excluded sources, put them in the table if not Excluded_Sources.Default then if not Excluded_Source_List_File.Default then if Locally_Removed then Error_Msg (Data.Flags, "?both attributes Locally_Removed_Files and " & "Excluded_Source_List_File are present", Excluded_Source_List_File.Location, Proj); else Error_Msg (Data.Flags, "?both attributes Excluded_Source_Files and " & "Excluded_Source_List_File are present", Excluded_Source_List_File.Location, Proj); end if; end if; Current := Excluded_Sources.Values; while Current /= Nil_String loop Element := Shared.String_Elements.Table (Current); Name := Canonical_Case_File_Name (Element.Value); -- If the element has no location, then use the location of -- Excluded_Sources to report possible errors. if Element.Location = No_Location then Location := Excluded_Sources.Location; else Location := Element.Location; end if; Excluded_Sources_Htable.Set (Project.Excluded, Name, (Name, No_File, 0, False, Location, Proj)); Current := Element.Next; end loop; elsif not Excluded_Source_List_File.Default then Location := Excluded_Source_List_File.Location; declare Source_File_Name : constant File_Name_Type := File_Name_Type (Excluded_Source_List_File.Value); Source_File_Line : Natural := 0; Source_File_Path_Name : constant String := Path_Name_Of (Source_File_Name, Proj.Directory.Name); begin if Source_File_Path_Name'Length = 0 then Error_Msg_File_1 := File_Name_Type (Excluded_Source_List_File.Value); Error_Msg (Data.Flags, "file with excluded sources { does not exist", Excluded_Source_List_File.Location, Proj); else -- Open the file GPR.Util.Open (File, Source_File_Path_Name); if not GPR.Util.Is_Valid (File) then Error_Msg (Data.Flags, "file does not exist", Location, Proj); else -- Read the lines one by one while not GPR.Util.End_Of_File (File) loop GPR.Util.Get_Line (File, Line, Last); Source_File_Line := Source_File_Line + 1; -- Non empty, non comment line should contain a -- file name if Last /= 0 and then (Last = 1 or else Line (1 .. 2) /= "--") then Name_Len := Last; Name_Buffer (1 .. Name_Len) := Line (1 .. Last); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Name := Name_Find; -- Check that there is no directory information for J in 1 .. Last loop if Is_Directory_Separator (Line (J)) then Error_Msg_File_1 := Name; Error_Msg (Data.Flags, "file name cannot include " & "directory information ({)", Location, Proj); exit; end if; end loop; Excluded_Sources_Htable.Set (Project.Excluded, Name, (Name, Source_File_Name, Source_File_Line, False, Location, Proj)); end if; end loop; GPR.Util.Close (File); end if; end if; end; end if; end; Proj := Proj.Extended_By; end loop; end Find_Excluded_Sources; ------------------ -- Find_Sources -- ------------------ procedure Find_Sources (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Sources : constant Variable_Value := Util.Value_Of (Name_Source_Files, Project.Project.Decl.Attributes, Shared); Source_List_File : constant Variable_Value := Util.Value_Of (Name_Source_List_File, Project.Project.Decl.Attributes, Shared); Name_Loc : Name_Location; Has_Explicit_Sources : Boolean; begin pragma Assert (Sources.Kind = List, "Source_Files is not a list"); pragma Assert (Source_List_File.Kind = Single, "Source_List_File is not a single string"); Project.Source_List_File_Location := Source_List_File.Location; -- If the user has specified a Source_Files attribute if not Sources.Default then if not Source_List_File.Default then Error_Msg (Data.Flags, "?both attributes source_files and " & "source_list_file are present", Source_List_File.Location, Project.Project); end if; -- Sources is a list of file names declare Current : String_List_Id := Sources.Values; Element : String_Element; Location : Source_Ptr; Name : File_Name_Type; begin if Current = Nil_String then -- This project contains no source. For projects that don't -- extend other projects, this also means that there is no -- need for an object directory, if not specified. if Project.Project.Extends = No_Project and then Project.Project.Object_Directory = Project.Project.Directory and then not (Project.Project.Qualifier = Aggregate_Library) then Project.Project.Object_Directory := No_Path_Information; end if; end if; while Current /= Nil_String loop Element := Shared.String_Elements.Table (Current); Name := Canonical_Case_File_Name (Element.Value); Get_Name_String (Element.Value); -- If the element has no location, then use the location of -- Sources to report possible errors. if Element.Location = No_Location then Location := Sources.Location; else Location := Element.Location; end if; -- Check that there is no directory information for J in 1 .. Name_Len loop if Is_Directory_Separator (Name_Buffer (J)) then Error_Msg_File_1 := Name; Error_Msg (Data.Flags, "file name cannot include directory " & "information ({)", Location, Project.Project); exit; end if; end loop; -- Check whether the file is already there: the same file name -- may be in the list. If the source is missing, the error will -- be on the first mention of the source file name. Name_Loc := Source_Names_Htable.Get (Project.Source_Names, Name); if Name_Loc = No_Name_Location then Name_Loc := (Name => Name, Location => Location, Source => No_Source, Listed => True, Found => False); else Name_Loc.Listed := True; end if; Source_Names_Htable.Set (Project.Source_Names, Name, Name_Loc); Current := Element.Next; end loop; Has_Explicit_Sources := True; end; -- If we have no Source_Files attribute, check the Source_List_File -- attribute. elsif not Source_List_File.Default then -- Source_List_File is the name of the file that contains the source -- file names. declare Source_File_Path_Name : constant String := Path_Name_Of (File_Name_Type (Source_List_File.Value), Project.Project. Directory.Display_Name); begin Has_Explicit_Sources := True; if Source_File_Path_Name'Length = 0 then Error_Msg_File_1 := File_Name_Type (Source_List_File.Value); Error_Msg (Data.Flags, "file with sources { does not exist", Source_List_File.Location, Project.Project); else Get_Sources_From_File (Source_File_Path_Name, Source_List_File.Location, Project, Data); end if; end; else -- Neither Source_Files nor Source_List_File has been specified. Find -- all the files that satisfy the naming scheme in all the source -- directories. Has_Explicit_Sources := False; end if; -- Remove any exception that is not in the specified list of sources if Has_Explicit_Sources then declare Source : Source_Id; Iter : Source_Iterator; NL : Name_Location; Again : Boolean; begin Iter_Loop : loop Again := False; Iter := For_Each_Source (Data.Tree, Project.Project); Source_Loop : loop Source := GPR.Element (Iter); exit Source_Loop when Source = No_Source; if Source.Naming_Exception /= No then NL := Source_Names_Htable.Get (Project.Source_Names, Source.File); if NL /= No_Name_Location and then not NL.Listed then -- Remove the exception Source_Names_Htable.Set (Project.Source_Names, Source.File, No_Name_Location); Remove_Source (Data.Tree, Source, No_Source); if Source.Naming_Exception = Yes then Error_Msg_Name_1 := Name_Id (Source.File); Error_Msg (Data.Flags, "? unknown source file %%", NL.Location, Project.Project); end if; Again := True; exit Source_Loop; end if; end if; Next (Iter); end loop Source_Loop; exit Iter_Loop when not Again; end loop Iter_Loop; end; end if; Search_Directories (Project, Data => Data, For_All_Sources => Sources.Default and then Source_List_File.Default); -- Check if all exceptions have been found declare Source : Source_Id; Iter : Source_Iterator; Found : Boolean := False; begin Iter := For_Each_Source (Data.Tree, Project.Project); loop Source := GPR.Element (Iter); exit when Source = No_Source; -- If the full source path is unknown for this source_id, there -- could be several reasons: -- * we simply did not find the file itself, this is an error -- * we have a multi-unit source file. Another Source_Id from -- the same file has received the full path, so we need to -- propagate it. if Source.Path = No_Path_Information then if Source.Naming_Exception = Yes then if Source.Unit /= No_Unit_Index then Found := False; if Source.Index /= 0 then -- Only multi-unit files declare S : Source_Id := Source_Files_Htable.Get (Data.Tree.Source_Files_HT, Source.File); begin while S /= null loop if S.Path /= No_Path_Information then Source.Path := S.Path; Found := True; if Current_Verbosity = High then Debug_Output ("setting full path for " & Get_Name_String (Source.File) & " at" & Source.Index'Img & " to " & Get_Name_String (Source.Path.Name)); end if; exit; end if; S := S.Next_With_File_Name; end loop; end; end if; if not Found then Error_Msg_Name_1 := Name_Id (Source.Display_File); Error_Msg_Name_2 := Source.Unit.Name; Error_Or_Warning (Data.Flags, Data.Flags.Missing_Source_Files, "\source file %% for unit %% not found", No_Location, Project.Project); end if; end if; if Source.Path = No_Path_Information then Remove_Source (Data.Tree, Source, No_Source); end if; elsif Source.Naming_Exception = Inherited then Remove_Source (Data.Tree, Source, No_Source); end if; end if; Next (Iter); end loop; end; -- It is an error if a source file name in a source list or in a source -- list file is not found. if Has_Explicit_Sources then declare NL : Name_Location; First_Error : Boolean; begin NL := Source_Names_Htable.Get_First (Project.Source_Names); First_Error := True; while NL /= No_Name_Location loop if not NL.Found then Error_Msg_File_1 := NL.Name; if First_Error then Error_Or_Warning (Data.Flags, Data.Flags.Missing_Source_Files, "source file { not found", NL.Location, Project.Project); First_Error := False; else Error_Or_Warning (Data.Flags, Data.Flags.Missing_Source_Files, "\source file { not found", NL.Location, Project.Project); end if; end if; NL := Source_Names_Htable.Get_Next (Project.Source_Names); end loop; end; end if; end Find_Sources; ---------------- -- Initialize -- ---------------- procedure Initialize (Data : out Tree_Processing_Data; Tree : Project_Tree_Ref; Node_Tree : GPR.Tree.Project_Node_Tree_Ref; Flags : GPR.Processing_Flags) is begin Data.Tree := Tree; Data.Node_Tree := Node_Tree; Data.Flags := Flags; end Initialize; ---------- -- Free -- ---------- procedure Free (Data : in out Tree_Processing_Data) is pragma Unreferenced (Data); begin null; end Free; ---------------- -- Initialize -- ---------------- procedure Initialize (Data : in out Project_Processing_Data; Project : Project_Id) is begin Data.Project := Project; end Initialize; ---------- -- Free -- ---------- procedure Free (Data : in out Project_Processing_Data) is begin Source_Names_Htable.Reset (Data.Source_Names); Unit_Exceptions_Htable.Reset (Data.Unit_Exceptions); Excluded_Sources_Htable.Reset (Data.Excluded); end Free; ------------------------------- -- Check_File_Naming_Schemes -- ------------------------------- procedure Check_File_Naming_Schemes (Project : Project_Processing_Data; File_Name : File_Name_Type; Alternate_Languages : out Language_List; Language : out Language_Ptr; Display_Language_Name : out Name_Id; Unit : out Name_Id; Lang_Kind : out Language_Kind; Kind : out Source_Kind) is Filename : constant String := Get_Name_String (File_Name); Config : Language_Config; Tmp_Lang : Language_Ptr; Header_File : Boolean := False; -- True if we found at least one language for which the file is a header -- In such a case, we search for all possible languages where this is -- also a header (C and C++ for instance), since the file might be used -- for several such languages. procedure Check_File_Based_Lang; -- Does the naming scheme test for file-based languages. For those, -- there is no Unit. Just check if the file name has the implementation -- or, if it is specified, the template suffix of the language. -- -- Returns True if the file belongs to the current language and we -- should stop searching for matching languages. Not that a given header -- file could belong to several languages (C and C++ for instance). Thus -- if we found a header we'll check whether it matches other languages. --------------------------- -- Check_File_Based_Lang -- --------------------------- procedure Check_File_Based_Lang is begin if not Header_File and then Suffix_Matches (Filename, Config.Naming_Data.Body_Suffix) then Unit := No_Name; Kind := Impl; Language := Tmp_Lang; Debug_Output ("implementation of language ", Display_Language_Name); elsif Suffix_Matches (Filename, Config.Naming_Data.Spec_Suffix) then Debug_Output ("header of language ", Display_Language_Name); if Header_File then Alternate_Languages := new Language_List_Element' (Language => Language, Next => Alternate_Languages); else Header_File := True; Kind := Spec; Unit := No_Name; Language := Tmp_Lang; end if; end if; end Check_File_Based_Lang; -- Start of processing for Check_File_Naming_Schemes begin Language := No_Language_Index; Alternate_Languages := null; Display_Language_Name := No_Name; Unit := No_Name; Lang_Kind := File_Based; Kind := Spec; Tmp_Lang := Project.Project.Languages; while Tmp_Lang /= No_Language_Index loop if Current_Verbosity = High then Debug_Output ("testing language " & Get_Name_String (Tmp_Lang.Name) & " Header_File=" & Header_File'Img); end if; Display_Language_Name := Tmp_Lang.Display_Name; Config := Tmp_Lang.Config; Lang_Kind := Config.Kind; case Config.Kind is when File_Based => Check_File_Based_Lang; exit when Kind = Impl; when Unit_Based => -- We know it belongs to a least a file_based language, no -- need to check unit-based ones. if not Header_File then Compute_Unit_Name (File_Name => File_Name, Naming => Config.Naming_Data, Kind => Kind, Unit => Unit, Project => Project); if Unit /= No_Name then Language := Tmp_Lang; exit; end if; end if; end case; Tmp_Lang := Tmp_Lang.Next; end loop; if Language = No_Language_Index then Debug_Output ("not a source of any language"); end if; end Check_File_Naming_Schemes; ------------------- -- Override_Kind -- ------------------- procedure Override_Kind (Source : Source_Id; Kind : Source_Kind) is begin -- If the file was previously already associated with a unit, change it if Source.Unit /= null and then Source.Kind in Spec_Or_Body and then Source.Unit.File_Names (Source.Kind) /= null then -- If we had another file referencing the same unit (for instance it -- was in an extended project), that source file is in fact invisible -- from now on, and in particular doesn't belong to the same unit. -- If the source is an inherited naming exception, then it may not -- really exist: the source potentially replaced is left untouched. if Source.Unit.File_Names (Source.Kind) /= Source then Source.Unit.File_Names (Source.Kind).Unit := No_Unit_Index; end if; Source.Unit.File_Names (Source.Kind) := null; end if; Source.Kind := Kind; if Current_Verbosity = High and then Source.File /= No_File then Debug_Output ("override kind for " & Get_Name_String (Source.File) & " idx=" & Source.Index'Img & " kind=" & Source.Kind'Img); end if; if Source.Unit /= null then if Source.Kind = Spec then Source.Unit.File_Names (Spec) := Source; else Source.Unit.File_Names (Impl) := Source; end if; end if; end Override_Kind; ---------------- -- Check_File -- ---------------- procedure Check_File (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data; Source_Dir_Rank : Natural; Path : Path_Name_Type; Display_Path : Path_Name_Type; File_Name : File_Name_Type; Display_File_Name : File_Name_Type; Locally_Removed : Boolean; For_All_Sources : Boolean) is Name_Loc : Name_Location := Source_Names_Htable.Get (Project.Source_Names, File_Name); Check_Name : Boolean := False; Alternate_Languages : Language_List; Language : Language_Ptr; Source : Source_Id; Src_Ind : Source_File_Index; Unit : Name_Id; Display_Language_Name : Name_Id; Lang_Kind : Language_Kind; Kind : Source_Kind := Spec; begin if Current_Verbosity = High then Debug_Increase_Indent ("checking file (rank=" & Source_Dir_Rank'Img & ")", Name_Id (Display_Path)); end if; if Name_Loc = No_Name_Location then -- Source_Dir_Rank = 0 mean that source file is from directory -- defined in --src-subdir parameter. Check_Name := For_All_Sources or else Source_Dir_Rank = 0; else if Name_Loc.Found then -- Check if it is OK to have the same file name in several -- source directories. if Name_Loc.Source /= No_Source and then Source_Dir_Rank = Name_Loc.Source.Source_Dir_Rank then Error_Msg_File_1 := File_Name; Error_Msg (Data.Flags, "{ is found in several source directories", Name_Loc.Location, Project.Project); end if; else Name_Loc.Found := True; Source_Names_Htable.Set (Project.Source_Names, File_Name, Name_Loc); if Name_Loc.Source = No_Source then Check_Name := True; else -- Set the full path for the source_id (which might have been -- created when parsing the naming exceptions, and therefore -- might not have the full path). -- We only set this for this source_id, but not for other -- source_id in the same file (case of multi-unit source files) -- For the latter, they will be set in Find_Sources when we -- check that all source_id have known full paths. -- Doing this later saves one htable lookup per file in the -- common case where the user is not using multi-unit files. Name_Loc.Source.Path := (Path, Display_Path); Source_Paths_Htable.Set (Data.Tree.Source_Paths_HT, Path, Name_Loc.Source); -- Check if this is a subunit if Name_Loc.Source.Unit /= No_Unit_Index and then Name_Loc.Source.Kind = Impl then Src_Ind := Sinput.Load_File (Get_Name_String (Display_Path)); if Source_File_Is_Subunit (Src_Ind) then Override_Kind (Name_Loc.Source, Sep); end if; end if; -- If this is an inherited naming exception, make sure that -- the naming exception it replaces is no longer a source. if Name_Loc.Source.Naming_Exception = Inherited then declare Proj : Project_Id := Name_Loc.Source.Project.Extends; Iter : Source_Iterator; Src : Source_Id; begin while Proj /= No_Project loop Iter := For_Each_Source (Data.Tree, Proj); Src := GPR.Element (Iter); while Src /= No_Source loop if Src.File = Name_Loc.Source.File then Src.Replaced_By := Name_Loc.Source; exit; end if; Next (Iter); Src := GPR.Element (Iter); end loop; Proj := Proj.Extends; end loop; end; if Name_Loc.Source.Unit /= No_Unit_Index then if Name_Loc.Source.Kind = Spec then Name_Loc.Source.Unit.File_Names (Spec) := Name_Loc.Source; elsif Name_Loc.Source.Kind = Impl then Name_Loc.Source.Unit.File_Names (Impl) := Name_Loc.Source; end if; Units_Htable.Set (Data.Tree.Units_HT, Name_Loc.Source.Unit.Name, Name_Loc.Source.Unit); end if; end if; end if; end if; end if; if Check_Name then Check_File_Naming_Schemes (Project => Project, File_Name => File_Name, Alternate_Languages => Alternate_Languages, Language => Language, Display_Language_Name => Display_Language_Name, Unit => Unit, Lang_Kind => Lang_Kind, Kind => Kind); if Language = No_Language_Index then -- A file name in a list must be a source of a language. if Data.Flags.Error_On_Unknown_Language and then not Languages_Are_Restricted and then Name_Loc.Found then Error_Msg_File_1 := File_Name; Error_Msg (Data.Flags, "language unknown for {", Name_Loc.Location, Project.Project); end if; else Add_Source (Id => Source, Project => Project.Project, Source_Dir_Rank => Source_Dir_Rank, Lang_Id => Language, Kind => Kind, Data => Data, Alternate_Languages => Alternate_Languages, File_Name => File_Name, Display_File => Display_File_Name, Unit => Unit, Locally_Removed => Locally_Removed, Path => (Path, Display_Path)); if Source /= null then Source.In_Src_Subdir := Source_Dir_Rank = 0; end if; -- If it is a source specified in a list, update the entry in -- the Source_Names table. if Name_Loc.Found and then Name_Loc.Source = No_Source then Name_Loc.Source := Source; Source_Names_Htable.Set (Project.Source_Names, File_Name, Name_Loc); end if; end if; end if; Debug_Decrease_Indent; end Check_File; --------------------------------- -- Expand_Subdirectory_Pattern -- --------------------------------- procedure Expand_Subdirectory_Pattern (Project : Project_Id; Data : in out Tree_Processing_Data; Patterns : String_List_Id; Ignore : String_List_Id; Search_For : Search_Type; Resolve_Links : Boolean; Callback : access procedure (Path : Path_Information; Pattern_Index : Natural)) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; package Recursive_Dirs renames Path_Name_HTable; -- Hash table stores recursive source directories, to avoid looking -- several times, and to avoid cycles that may be introduced by symbolic -- links. File_Pattern : GNAT.Regexp.Regexp; -- Pattern to use when matching file names Visited : Recursive_Dirs.Instance; procedure Find_Pattern (Pattern_Id : Name_Id; Rank : Natural; Location : Source_Ptr); -- Find a specific pattern function Recursive_Find_Dirs (Path : Path_Information; Rank : Natural) return Boolean; -- Search all the subdirectories (recursively) of Path. -- Return True if at least one file or directory was processed function Subdirectory_Matches (Path : Path_Information; Rank : Natural) return Boolean; -- Called when a matching directory was found. If the user is in fact -- searching for files, we then search for those files matching the -- pattern within the directory. -- Return True if at least one file or directory was processed -------------------------- -- Subdirectory_Matches -- -------------------------- function Subdirectory_Matches (Path : Path_Information; Rank : Natural) return Boolean is Dir : Dir_Type; Name : String (1 .. 250); Last : Natural; Found : Path_Information; Success : Boolean := False; begin case Search_For is when Search_Source_Directories => Callback (Path, Rank); return True; when Search_Project_Files => Open (Dir, Get_Name_String (Path.Display_Name)); loop Read (Dir, Name, Last); exit when Last = 0; if Name (Name'First .. Last) /= "." and then Name (Name'First .. Last) /= ".." and then Match (Name (Name'First .. Last), File_Pattern) then Get_Name_String (Path.Display_Name); Add_Str_To_Name_Buffer (Name (Name'First .. Last)); Found.Display_Name := Name_Find; Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Found.Name := Name_Find; Callback (Found, Rank); Success := True; end if; end loop; Close (Dir); return Success; end case; exception when Directory_Error => return False; end Subdirectory_Matches; ------------------------- -- Recursive_Find_Dirs -- ------------------------- function Recursive_Find_Dirs (Path : Path_Information; Rank : Natural) return Boolean is Path_Str : constant String := Get_Name_String (Path.Display_Name); Dir : Dir_Type; Name : String (1 .. 250); Last : Natural; Success : Boolean := False; begin Debug_Output ("looking for subdirs of ", Name_Id (Path.Display_Name)); if Recursive_Dirs.Get (Visited, Path.Name) then return Success; end if; Recursive_Dirs.Set (Visited, Path.Name, True); Success := Subdirectory_Matches (Path, Rank) or Success; Open (Dir, Path_Str); loop Read (Dir, Name, Last); exit when Last = 0; if Name (1 .. Last) /= "." and then Name (1 .. Last) /= ".." then declare Path_Name : constant String := Normalize_Pathname (Name => Name (1 .. Last), Directory => Path_Str, Resolve_Links => Resolve_Links) & Directory_Separator; Path2 : Path_Information; OK : Boolean := True; begin if Is_Directory (Path_Name) then if Ignore /= Nil_String then declare Dir_Name : String := Name (1 .. Last); List : String_List_Id := Ignore; begin Canonical_Case_File_Name (Dir_Name); while List /= Nil_String loop Get_Name_String (Shared.String_Elements.Table (List).Value); if Name_Len > 0 then Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); File_Pattern := Compile (Name_Buffer (1 .. Name_Len), Glob => True, Case_Sensitive => File_Names_Case_Sensitive ); OK := not Match (Dir_Name, File_Pattern); exit when not OK; end if; List := Shared.String_Elements.Table (List).Next; end loop; end; end if; if OK then Path2.Display_Name := Get_Path_Name_Id (Path_Name); Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Path2.Name := Name_Find; Success := Recursive_Find_Dirs (Path2, Rank) or Success; end if; end if; end; end if; end loop; Close (Dir); return Success; exception when Directory_Error => return Success; end Recursive_Find_Dirs; ------------------ -- Find_Pattern -- ------------------ procedure Find_Pattern (Pattern_Id : Name_Id; Rank : Natural; Location : Source_Ptr) is Pattern : constant String := Get_Name_String (Pattern_Id); Pattern_End : Natural := Pattern'Last; Recursive : Boolean; Dir : File_Name_Type; Path_Name : Path_Information; Dir_Exists : Boolean; Success : Boolean; Has_Error : Boolean := False; Msg_Mode : Error_Warning; begin Debug_Increase_Indent ("Find_Pattern", Pattern_Id); -- If we are looking for files, find the pattern for the files if Search_For = Search_Project_Files then while Pattern_End >= Pattern'First and then not Is_Directory_Separator (Pattern (Pattern_End)) loop Pattern_End := Pattern_End - 1; end loop; if Pattern_End = Pattern'Last then Error_Msg_File_1 := File_Name_Type (Pattern_Id); Error_Or_Warning (Data.Flags, Data.Flags.Missing_Source_Files, "Missing file name or pattern in {", Location, Project); Debug_Decrease_Indent; return; end if; if Current_Verbosity = High then Debug_Indent; Write_Str ("file_pattern="); Write_Str (Pattern (Pattern_End + 1 .. Pattern'Last)); Write_Str (" dir_pattern="); Write_Line (Pattern (Pattern'First .. Pattern_End)); end if; File_Pattern := Compile (Pattern (Pattern_End + 1 .. Pattern'Last), Glob => True, Case_Sensitive => File_Names_Case_Sensitive); -- If we had just "*.gpr", this is equivalent to "./*.gpr" if Pattern_End > Pattern'First then Pattern_End := Pattern_End - 1; -- Skip directory separator end if; end if; Recursive := Pattern_End - 1 >= Pattern'First and then Pattern (Pattern_End - 1 .. Pattern_End) = "**" and then (Pattern_End - 1 = Pattern'First or else Is_Directory_Separator (Pattern (Pattern_End - 2))); if Recursive then Pattern_End := Pattern_End - 2; if Pattern_End > Pattern'First then Pattern_End := Pattern_End - 1; -- Skip '/' end if; end if; Name_Len := Pattern_End - Pattern'First + 1; Name_Buffer (1 .. Name_Len) := Pattern (Pattern'First .. Pattern_End); Dir := Name_Find; Locate_Directory (Project => Project, Name => Dir, Path => Path_Name, Dir_Exists => Dir_Exists, Data => Data, Must_Exist => False); if not Dir_Exists then Msg_Mode := (case Search_For is when Search_Source_Directories => Data.Flags.Missing_Source_Files, when Search_Project_Files => Data.Flags.Missing_Project_Files); Error_Or_Warning (Data.Flags, Msg_Mode, '"' & Get_Name_String (Dir) & """ is not a valid directory", Location, Project); Has_Error := Msg_Mode = Error; end if; if not Has_Error then -- Links have been resolved if necessary, and Path_Name -- always ends with a directory separator. if Recursive then Success := Recursive_Find_Dirs (Path_Name, Rank); else Success := Subdirectory_Matches (Path_Name, Rank); end if; if not Success then case Search_For is when Search_Source_Directories => null; -- Error can't occur when Search_Project_Files => Error_Or_Warning (Data.Flags, Data.Flags.Missing_Project_Files, "file """ & Get_Name_String (Pattern_Id) & """ not found", Location, Project); end case; end if; end if; Debug_Decrease_Indent ("done Find_Pattern"); end Find_Pattern; -- Local variables Pattern_Id : String_List_Id := Patterns; Element : String_Element; Rank : Natural := 1; -- Start of processing for Expand_Subdirectory_Pattern begin while Pattern_Id /= Nil_String loop Element := Shared.String_Elements.Table (Pattern_Id); if Element.Value /= No_Name then Find_Pattern (Element.Value, Rank, Element.Location); end if; Rank := Rank + 1; Pattern_Id := Element.Next; end loop; Recursive_Dirs.Reset (Visited); end Expand_Subdirectory_Pattern; ------------------------ -- Search_Directories -- ------------------------ procedure Search_Directories (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data; For_All_Sources : Boolean) is Shared : constant Shared_Project_Tree_Data_Access := Data.Tree.Shared; Source_Dir : String_List_Id; Element : String_Element; Src_Dir_Rank : Number_List_Index; Num_Nod : Number_Node; Dir : Dir_Type; Name : String (1 .. 1_000); Last : Natural; File_Name : File_Name_Type; Display_File_Name : File_Name_Type; begin Debug_Increase_Indent ("looking for sources of", Project.Project.Name); -- Loop through subdirectories Src_Dir_Rank := Project.Project.Source_Dir_Ranks; Source_Dir := Project.Project.Source_Dirs; while Source_Dir /= Nil_String loop begin Num_Nod := Shared.Number_Lists.Table (Src_Dir_Rank); Element := Shared.String_Elements.Table (Source_Dir); -- Use Element.Value in this test, not Display_Value, because we -- want the symbolic links to be resolved when appropriate. if Element.Value /= No_Name then declare Source_Directory : constant String := Get_Name_String (Element.Value) & Directory_Separator; Dir_Last : constant Natural := Compute_Directory_Last (Source_Directory); Display_Source_Directory : constant String := Get_Name_String (Element.Display_Value) & Directory_Separator; -- Display_Source_Directory is to allow us to open a UTF-8 -- encoded directory on Windows. begin if Current_Verbosity = High then Debug_Increase_Indent ("Source_Dir (node=" & Num_Nod.Number'Img & ") """ & Source_Directory (Source_Directory'First .. Dir_Last) & '"'); end if; -- We look to every entry in the source directory Open (Dir, Display_Source_Directory); loop Read (Dir, Name, Last); exit when Last = 0; -- In fast project loading mode (without -eL), the user -- guarantees that no directory has a name which is a -- valid source name, so we can avoid doing a system call -- here. This provides a very significant speed up on -- slow file systems (remote files for instance). if not Opt.Follow_Links_For_Files or else Is_Regular_File (Display_Source_Directory & Name (1 .. Last)) then Name_Len := Last; Name_Buffer (1 .. Name_Len) := Name (1 .. Last); Display_File_Name := Name_Find; if Osint.File_Names_Case_Sensitive then File_Name := Display_File_Name; else Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); File_Name := Name_Find; end if; declare Path_Name : constant String := Normalize_Pathname (Name (1 .. Last), Directory => Source_Directory (Source_Directory'First .. Dir_Last), Resolve_Links => Opt.Follow_Links_For_Files, Case_Sensitive => True); Path : Path_Name_Type; FF : File_Found := Excluded_Sources_Htable.Get (Project.Excluded, File_Name); To_Remove : Boolean := False; begin Name_Len := Path_Name'Length; Name_Buffer (1 .. Name_Len) := Path_Name; if Osint.File_Names_Case_Sensitive then Path := Name_Find; else Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); Path := Name_Find; end if; if FF /= No_File_Found then if not FF.Found then FF.Found := True; Excluded_Sources_Htable.Set (Project.Excluded, File_Name, FF); Debug_Output ("excluded source ", Name_Id (Display_File_Name)); -- Will mark the file as removed, but we -- still need to add it to the list: if we -- don't, the file will not appear in the -- mapping file and will cause the compiler -- to fail. To_Remove := True; end if; end if; -- Preserve the user's original casing and use of -- links. The display_value (a directory) already -- ends with a directory separator by construction, -- so no need to add one. Get_Name_String (Element.Display_Value); Get_Name_String_And_Append (Name_Id (Display_File_Name)); Check_File (Project => Project, Source_Dir_Rank => Num_Nod.Number, Data => Data, Path => Path, Display_Path => Name_Find, File_Name => File_Name, Locally_Removed => To_Remove, Display_File_Name => Display_File_Name, For_All_Sources => For_All_Sources); end; else if Current_Verbosity = High then Debug_Output ("ignore " & Name (1 .. Last)); end if; end if; end loop; Debug_Decrease_Indent; Close (Dir); end; end if; exception when Directory_Error => null; end; Source_Dir := Element.Next; Src_Dir_Rank := Num_Nod.Next; end loop; Debug_Decrease_Indent ("end looking for sources."); end Search_Directories; ---------------------------- -- Load_Naming_Exceptions -- ---------------------------- procedure Load_Naming_Exceptions (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data) is Source : Source_Id; Iter : Source_Iterator; begin Iter := For_Each_Source (Data.Tree, Project.Project); loop Source := GPR.Element (Iter); exit when Source = No_Source; -- An excluded file cannot also be an exception file name if Excluded_Sources_Htable.Get (Project.Excluded, Source.File) /= No_File_Found then Error_Msg_File_1 := Source.File; Error_Msg (Data.Flags, "\{ cannot be both excluded and an exception file name", No_Location, Project.Project); end if; Debug_Output ("naming exception: adding source file to source_Names: ", Name_Id (Source.File)); Source_Names_Htable.Set (Project.Source_Names, K => Source.File, E => Name_Location' (Name => Source.File, Location => Source.Location, Source => Source, Listed => False, Found => False)); -- If this is an Ada exception, record in table Unit_Exceptions if Source.Unit /= No_Unit_Index then declare Unit_Except : Unit_Exception := Unit_Exceptions_Htable.Get (Project.Unit_Exceptions, Source.Unit.Name); begin Unit_Except.Name := Source.Unit.Name; if Source.Kind = Spec then Unit_Except.Spec := Source.File; else Unit_Except.Impl := Source.File; end if; Unit_Exceptions_Htable.Set (Project.Unit_Exceptions, Source.Unit.Name, Unit_Except); end; end if; Next (Iter); end loop; end Load_Naming_Exceptions; ---------------------- -- Look_For_Sources -- ---------------------- procedure Look_For_Sources (Project : in out Project_Processing_Data; Data : in out Tree_Processing_Data) is Object_Files : Object_File_Names_Htable.Instance; Iter : Source_Iterator; Src : Source_Id; procedure Check_Object (Src : Source_Id); -- Check if object file name of Src is already used in the project tree, -- and report an error if so. procedure Check_Object_Files; -- Check that no two sources of this project have the same object file procedure Mark_Excluded_Sources; -- Mark as such the sources that are declared as excluded procedure Check_Missing_Sources; -- Check whether one of the languages has no sources, and report an -- error when appropriate procedure Get_Sources_From_Source_Info; -- Get the source information from the tables that were created when a -- source info file was read. --------------------------- -- Check_Missing_Sources -- --------------------------- procedure Check_Missing_Sources is Extending : constant Boolean := Project.Project.Extends /= No_Project; Language : Language_Ptr; Source : Source_Id; Alt_Lang : Language_List; Continuation : Boolean := False; Iter : Source_Iterator; begin if not Project.Project.Externally_Built and then not Extending then Language := Project.Project.Languages; while Language /= No_Language_Index loop if Is_Allowed_Language (Language.Name) then -- If there are no sources for this language, check if there -- are sources for which this is an alternate language. if Language.First_Source = No_Source and then (Data.Flags.Require_Sources_Other_Lang or else Language.Name = Name_Ada) then Iter := For_Each_Source (In_Tree => Data.Tree, Project => Project.Project); Source_Loop : loop Source := Element (Iter); exit Source_Loop when Source = No_Source or else Source.Language = Language; Alt_Lang := Source.Alternate_Languages; while Alt_Lang /= null loop exit Source_Loop when Alt_Lang.Language = Language; Alt_Lang := Alt_Lang.Next; end loop; Next (Iter); end loop Source_Loop; if Source = No_Source then Report_No_Sources (Project.Project, Get_Name_String (Language.Display_Name), Data, Project.Source_List_File_Location, Continuation); Continuation := True; end if; end if; end if; Language := Language.Next; end loop; end if; end Check_Missing_Sources; ------------------ -- Check_Object -- ------------------ procedure Check_Object (Src : Source_Id) is Source : Source_Id; begin Source := Object_File_Names_Htable.Get (Object_Files, Src.Object); -- We cannot just check on "Source /= Src", since we might have -- two different entries for the same file (and since that's -- the same file it is expected that it has the same object) if Source /= No_Source and then Source.Replaced_By = No_Source and then Source.Path /= Src.Path and then Source.Index = 0 and then Src.Index = 0 and then Is_Extending (Src.Project, Source.Project) then Error_Msg_File_1 := Src.File; Error_Msg_File_2 := Source.File; Error_Msg (Data.Flags, "\{ and { have the same object file name", No_Location, Project.Project); else Object_File_Names_Htable.Set (Object_Files, Src.Object, Src); end if; end Check_Object; --------------------------- -- Mark_Excluded_Sources -- --------------------------- procedure Mark_Excluded_Sources is Source : Source_Id := No_Source; Excluded : File_Found; Proj : Project_Id; begin -- Minor optimization: if there are no excluded files, no need to -- traverse the list of sources. We cannot however also check whether -- the existing exceptions have ".Found" set to True (indicating we -- found them before) because we need to do some final processing on -- them in any case. if Excluded_Sources_Htable.Get_First (Project.Excluded) /= No_File_Found then -- The excluded files are not only those declared in the current -- project, but also those declared in the projects that extend -- the current project, if there are any. Proj := Project.Project; while Proj /= No_Project loop Iter := For_Each_Source (Data.Tree, Proj); while GPR.Element (Iter) /= No_Source loop Source := GPR.Element (Iter); Excluded := Excluded_Sources_Htable.Get (Project.Excluded, Source.File); if Excluded /= No_File_Found then Source.In_Interfaces := False; Source.Locally_Removed := True; if Proj = Project.Project then Source.Suppressed := True; end if; if Current_Verbosity = High then Debug_Indent; Write_Str ("removing file "); Write_Line (Get_Name_String (Excluded.File) & " " & Get_Name_String (Source.Project.Name)); end if; Excluded_Sources_Htable.Remove (Project.Excluded, Source.File); end if; Next (Iter); end loop; Proj := Proj.Extends; end loop; end if; -- If we have any excluded element left, that means we did not find -- the source file Excluded := Excluded_Sources_Htable.Get_First (Project.Excluded); while Excluded /= No_File_Found loop if not Excluded.Found then -- Check if the file belongs to another imported project to -- provide a better error message. Src := GPR.Find_Source (In_Tree => Data.Tree, Project => Project.Project, In_Imported_Only => True, Base_Name => Excluded.File); if (Src = No_Source and then Excluded.Project = Project.Project) or else Src /= No_Source then Error_Msg_File_1 := Excluded.File; Error_Msg (Data.Flags, (if Excluded.Excl_File = No_File then "" else "in " & Get_Name_String (Excluded.Excl_File) & ':' & No_Space_Img (Excluded.Excl_Line) & ": ") & (if Src = No_Source then "unknown file {" else "cannot remove a source from an imported project" & ": {"), Excluded.Location, Project.Project); end if; end if; Excluded := Excluded_Sources_Htable.Get_Next (Project.Excluded); end loop; end Mark_Excluded_Sources; ------------------------ -- Check_Object_Files -- ------------------------ procedure Check_Object_Files is Iter : Source_Iterator; Src_Id : Source_Id; Src_Ind : Source_File_Index; begin Iter := For_Each_Source (Data.Tree); loop Src_Id := GPR.Element (Iter); exit when Src_Id = No_Source; if Is_Compilable (Src_Id) and then Src_Id.Language.Config.Object_Generated and then Is_Extending (Project.Project, Src_Id.Project) then if Src_Id.Unit = No_Unit_Index then if Src_Id.Kind = Impl then Check_Object (Src_Id); end if; else case Src_Id.Kind is when Spec => if Other_Part (Src_Id) = No_Source then Check_Object (Src_Id); end if; when Sep => null; when Impl => if Other_Part (Src_Id) /= No_Source then Check_Object (Src_Id); else -- Check if it is a subunit Src_Ind := Sinput.Load_File (Get_Name_String (Src_Id.Path.Display_Name)); if Source_File_Is_Subunit (Src_Ind) then Override_Kind (Src_Id, Sep); else Check_Object (Src_Id); end if; end if; end case; end if; end if; Next (Iter); end loop; end Check_Object_Files; ---------------------------------- -- Get_Sources_From_Source_Info -- ---------------------------------- procedure Get_Sources_From_Source_Info is Iter : Source_Info_Iterator; Src : Source_Info; Id : Source_Id; Lang_Id : Language_Ptr; begin Initialize (Iter, Project.Project.Name); loop Src := Source_Info_Of (Iter); exit when Src = No_Source_Info; Id := new Source_Data; Id.Project := Project.Project; Lang_Id := Project.Project.Languages; while Lang_Id /= No_Language_Index and then Lang_Id.Name /= Src.Language loop Lang_Id := Lang_Id.Next; end loop; if Lang_Id = No_Language_Index then GPR.Com.Fail ("unknown language " & Get_Name_String (Src.Language) & " for project " & Get_Name_String (Src.Project) & " in source info file"); end if; Id.Language := Lang_Id; Id.Kind := Src.Kind; Id.Index := Src.Index; Id.Path := (Display_Name => Path_Name_Type (Src.Display_Path_Name), Name => Path_Name_Type (Src.Path_Name)); Id.File := Get_File_Name_Id (Directories.Simple_Name (Get_Name_String (Src.Path_Name))); Id.Next_With_File_Name := Source_Files_Htable.Get (Data.Tree.Source_Files_HT, Id.File); Source_Files_Htable.Set (Data.Tree.Source_Files_HT, Id.File, Id); Id.Display_File := Get_File_Name_Id (Directories.Simple_Name (Get_Name_String (Src.Display_Path_Name))); Id.Dep_Name := Dependency_Name (Id.File, Id.Language.Config.Dependency_Kind); Id.Naming_Exception := Src.Naming_Exception; Id.Object := Object_Name (Id.File, Id.Language.Config.Object_File_Suffix); Id.Switches := Switches_Name (Id.File); -- Add the source id to the Unit_Sources_HT hash table, if the -- unit name is not null. if Src.Unit_Name /= No_Name then declare UData : Unit_Index := Units_Htable.Get (Data.Tree.Units_HT, Src.Unit_Name); begin if UData = No_Unit_Index then UData := new Unit_Data; UData.Name := Src.Unit_Name; Units_Htable.Set (Data.Tree.Units_HT, Src.Unit_Name, UData); end if; Id.Unit := UData; end; -- Note that this updates Unit information as well Override_Kind (Id, Id.Kind); end if; if Src.Index /= 0 then Project.Project.Has_Multi_Unit_Sources := True; end if; -- Add the source to the language list Id.Next_In_Lang := Id.Language.First_Source; Id.Language.First_Source := Id; Next (Iter); end loop; end Get_Sources_From_Source_Info; -- Start of processing for Look_For_Sources begin if Data.Tree.Source_Info_File_Exists then Get_Sources_From_Source_Info; else if Project.Project.Source_Dirs /= Nil_String then Find_Excluded_Sources (Project, Data); if Project.Project.Languages /= No_Language_Index then Load_Naming_Exceptions (Project, Data); Find_Sources (Project, Data); Mark_Excluded_Sources; Check_Object_Files; Check_Missing_Sources; end if; end if; Object_File_Names_Htable.Reset (Object_Files); end if; end Look_For_Sources; ------------------ -- Path_Name_Of -- ------------------ function Path_Name_Of (File_Name : File_Name_Type; Directory : Path_Name_Type) return String is Result : String_Access; The_Directory : constant String := Get_Name_String (Directory); begin Debug_Output ("Path_Name_Of file name=", Name_Id (File_Name)); Debug_Output ("Path_Name_Of directory=", Name_Id (Directory)); Get_Name_String (File_Name); Result := Locate_Regular_File (File_Name => Name_Buffer (1 .. Name_Len), Path => The_Directory); if Result = null then return ""; else declare R : constant String := Result.all; begin Free (Result); return R; end; end if; end Path_Name_Of; ------------------- -- Remove_Source -- ------------------- procedure Remove_Source (Tree : Project_Tree_Ref; Id : Source_Id; Replaced_By : Source_Id) is Source : Source_Id; begin if Current_Verbosity = High then Debug_Indent; Write_Str ("removing source "); Write_Str (Get_Name_String (Id.File)); if Id.Index /= 0 then Write_Str (" at" & Id.Index'Img); end if; Write_Eol; end if; if Replaced_By /= No_Source then Id.Replaced_By := Replaced_By; Replaced_By.Declared_In_Interfaces := Id.Declared_In_Interfaces; if Id.File /= Replaced_By.File then declare Replacement : constant File_Name_Type := Replaced_Source_HTable.Get (Tree.Replaced_Sources, Id.File); begin Replaced_Source_HTable.Set (Tree.Replaced_Sources, Id.File, Replaced_By.File); if Replacement = No_File then Tree.Replaced_Source_Number := Tree.Replaced_Source_Number + 1; end if; end; end if; end if; Id.In_Interfaces := False; Id.Locally_Removed := True; -- ??? Should we remove the source from the unit ? The file is not used, -- so probably should not be referenced from the unit. On the other hand -- it might give useful additional info -- if Id.Unit /= null then -- Id.Unit.File_Names (Id.Kind) := null; -- end if; Source := Id.Language.First_Source; if Source = Id then Id.Language.First_Source := Id.Next_In_Lang; else -- Do not crash if the source cannot be found while Source /= No_Source and then Source.Next_In_Lang /= Id loop Source := Source.Next_In_Lang; end loop; if Source /= No_Source then Source.Next_In_Lang := Id.Next_In_Lang; end if; end if; end Remove_Source; ----------------------- -- Report_No_Sources -- ----------------------- procedure Report_No_Sources (Project : Project_Id; Lang_Name : String; Data : Tree_Processing_Data; Location : Source_Ptr; Continuation : Boolean := False) is Language_Name : String (1 .. Lang_Name'Length * 2); Language_Last : Natural := 0; begin -- Make sure that the language name will be displayed as is in the -- error message and not modified, for example if it is in capital -- letters. for J in Lang_Name'Range loop Language_Last := Language_Last + 1; Language_Name (Language_Last) := '''; Language_Last := Language_Last + 1; Language_Name (Language_Last) := Lang_Name (J); end loop; case Data.Flags.When_No_Sources is when Silent | Decide_Later => null; when Warning | Error => declare Msg : constant String := " Get_Object_Directory (Project, Data); Check_Aggregated; if Project.Object_Directory = No_Path_Information then Project.Object_Directory := Project.Directory; end if; when others => Get_Directories (Project, Data, No_Sources); Check_Programming_Languages (Project, Data); if Current_Verbosity = High then Show_Source_Dirs (Project, Shared); end if; if Project.Qualifier = Abstract_Project then Check_Abstract_Project (Project, Data); end if; end case; -- Check configuration. Check_Configuration (Project, Data); if not Data.Flags.Check_Configuration_Only then if Project.Qualifier /= Aggregate then -- Externally built projects are allowed to be library projects -- even when they have no sources. No_Sources := No_Sources and then not Project.Externally_Built; -- A project with no sources cannot be a library project if No_Sources and then Project.Qualifier = Library then Error_Msg (Data.Flags, "a project with no sources " & "cannot be a library project", Project.Location, Project); else Check_Library_Attributes (Project, No_Sources, Data); end if; Check_Package_Naming (Project, Data); -- An aggregate library has no source, no need to look for them if Project.Qualifier /= Aggregate_Library then Look_For_Sources (Prj_Data, Data); end if; Check_Interfaces (Project, Data); -- If this library is part of an aggregated library don't check -- it as it has no sources by itself and so interface won't be -- found. if Project.Library and not In_Aggregate_Lib then Check_Stand_Alone_Library (Project, Data); end if; Get_Mains (Project, Data); end if; Free (Prj_Data); end if; Debug_Decrease_Indent ("done check"); end Check; --------------------- -- Recursive_Check -- --------------------- procedure Recursive_Check (Project : Project_Id; Prj_Tree : Project_Tree_Ref; Context : Project_Context; Data : in out Tree_Processing_Data) is begin if Current_Verbosity = High then Debug_Increase_Indent ("Processing_Naming_Scheme for project", Project.Name); end if; Data.Tree := Prj_Tree; Data.In_Aggregate_Lib := Context.In_Aggregate_Lib; Check (Project, Context.In_Aggregate_Lib, Data); if Current_Verbosity = High then Debug_Decrease_Indent ("done Processing_Naming_Scheme"); end if; end Recursive_Check; procedure Check_All_Projects is new For_Every_Project_Imported_Context (Tree_Processing_Data, Recursive_Check); -- Comment required??? -- Local Variables Data : Tree_Processing_Data; -- Start of processing for Process_Naming_Scheme begin Lib_Data_Table.Init; Initialize (Data, Tree => Tree, Node_Tree => Node_Tree, Flags => Flags); Check_All_Projects (Root_Project, Tree, Data, Imported_First => True); Free (Data); -- Adjust language configs for projects that are extended declare List : Project_List; Proj : Project_Id; Exte : Project_Id; Lang : Language_Ptr; Elng : Language_Ptr; begin List := Tree.Projects; while List /= null loop Proj := List.Project; Exte := Proj; while Exte.Extended_By /= No_Project loop Exte := Exte.Extended_By; end loop; if Exte /= Proj then Lang := Proj.Languages; if Lang /= No_Language_Index then loop Elng := Get_Language_From_Name (Exte, Get_Name_String (Lang.Name)); exit when Elng /= No_Language_Index; Exte := Exte.Extends; end loop; if Elng /= Lang then Lang.Config := Elng.Config; end if; end if; end if; List := List.Next; end loop; end; end Process_Naming_Scheme; end GPR.Nmsc;