| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T M A I N -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- $Revision: 1.1 $ |
| -- -- |
| -- Copyright (C) 1992-2001 Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 2, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING. If not, write -- |
| -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- |
| -- MA 02111-1307, USA. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Csets; |
| with GNAT.Case_Util; |
| with GNAT.OS_Lib; use GNAT.OS_Lib; |
| with Namet; use Namet; |
| with Opt; |
| with Osint; use Osint; |
| with Output; use Output; |
| with Prj; use Prj; |
| with Prj.Env; |
| with Prj.Ext; use Prj.Ext; |
| with Prj.Pars; |
| with Prj.Util; use Prj.Util; |
| with Snames; use Snames; |
| with Stringt; use Stringt; |
| with Table; |
| with Types; use Types; |
| |
| procedure Gnatmain is |
| |
| Ada_Include_Path : constant String := "ADA_INCLUDE_PATH"; |
| Ada_Objects_Path : constant String := "ADA_OBJECTS_PATH"; |
| |
| type Tool_Type is (None, List, Xref, Find, Stub, Make, Comp, Bind, Link); |
| |
| -- The tool that is going to be called |
| |
| Tool : Tool_Type := None; |
| |
| -- For each tool, Tool_Package_Names contains the name of the |
| -- corresponding package in the project file. |
| |
| Tool_Package_Names : constant array (Tool_Type) of Name_Id := |
| (None => No_Name, |
| List => Name_Gnatls, |
| Xref => Name_Cross_Reference, |
| Find => Name_Finder, |
| Stub => Name_Gnatstub, |
| Comp => No_Name, |
| Make => No_Name, |
| Bind => No_Name, |
| Link => No_Name); |
| |
| -- For each tool, Tool_Names contains the name of the executable |
| -- to be spawned. |
| |
| Gnatmake : constant String_Access := new String'("gnatmake"); |
| |
| Tool_Names : constant array (Tool_Type) of String_Access := |
| (None => null, |
| List => new String'("gnatls"), |
| Xref => new String'("gnatxref"), |
| Find => new String'("gnatfind"), |
| Stub => new String'("gnatstub"), |
| Comp => Gnatmake, |
| Make => Gnatmake, |
| Bind => Gnatmake, |
| Link => Gnatmake); |
| |
| Project_File : String_Access; |
| Project : Prj.Project_Id; |
| Current_Verbosity : Prj.Verbosity := Prj.Default; |
| |
| -- This flag indicates a switch -p (for gnatxref and gnatfind) for |
| -- an old fashioned project file. -p cannot be used in conjonction |
| -- with -P. |
| |
| Old_Project_File_Used : Boolean := False; |
| |
| Next_Arg : Positive; |
| |
| -- A table to keep the switches on the command line |
| |
| package Saved_Switches is new Table.Table ( |
| Table_Component_Type => String_Access, |
| Table_Index_Type => Integer, |
| Table_Low_Bound => 1, |
| Table_Initial => 20, |
| Table_Increment => 100, |
| Table_Name => "Gnatmain.Saved_Switches"); |
| |
| -- A table to keep the switches from the project file |
| |
| package Switches is new Table.Table ( |
| Table_Component_Type => String_Access, |
| Table_Index_Type => Integer, |
| Table_Low_Bound => 1, |
| Table_Initial => 20, |
| Table_Increment => 100, |
| Table_Name => "Gnatmain.Switches"); |
| |
| procedure Add_Switch (Argv : String; And_Save : Boolean); |
| -- Add a switch in one of the tables above |
| |
| procedure Display (Program : String; Args : Argument_List); |
| -- Displays Program followed by the arguments in Args |
| |
| function Index (Char : Character; Str : String) return Natural; |
| -- Returns the first occurrence of Char in Str. |
| -- Returns 0 if Char is not in Str. |
| |
| procedure Scan_Arg (Argv : String; And_Save : Boolean); |
| -- Scan and process arguments. Argv is a single argument. |
| |
| procedure Usage; |
| -- Output usage |
| |
| ---------------- |
| -- Add_Switch -- |
| ---------------- |
| |
| procedure Add_Switch (Argv : String; And_Save : Boolean) is |
| begin |
| if And_Save then |
| Saved_Switches.Increment_Last; |
| Saved_Switches.Table (Saved_Switches.Last) := new String'(Argv); |
| |
| else |
| Switches.Increment_Last; |
| Switches.Table (Switches.Last) := new String'(Argv); |
| end if; |
| end Add_Switch; |
| |
| ------------- |
| -- Display -- |
| ------------- |
| |
| procedure Display (Program : String; Args : Argument_List) is |
| begin |
| if not Opt.Quiet_Output then |
| Write_Str (Program); |
| |
| for J in Args'Range loop |
| Write_Str (" "); |
| Write_Str (Args (J).all); |
| end loop; |
| |
| Write_Eol; |
| end if; |
| end Display; |
| |
| ----------- |
| -- Index -- |
| ----------- |
| |
| function Index (Char : Character; Str : String) return Natural is |
| begin |
| for Index in Str'Range loop |
| if Str (Index) = Char then |
| return Index; |
| end if; |
| end loop; |
| |
| return 0; |
| end Index; |
| |
| -------------- |
| -- Scan_Arg -- |
| -------------- |
| |
| procedure Scan_Arg (Argv : String; And_Save : Boolean) is |
| begin |
| pragma Assert (Argv'First = 1); |
| |
| if Argv'Length = 0 then |
| return; |
| end if; |
| |
| if Argv (1) = Switch_Character or else Argv (1) = '-' then |
| |
| if Argv'Length = 1 then |
| Fail ("switch character cannot be followed by a blank"); |
| end if; |
| |
| -- The two style project files (-p and -P) cannot be used together |
| |
| if (Tool = Find or else Tool = Xref) |
| and then Argv (2) = 'p' |
| then |
| Old_Project_File_Used := True; |
| if Project_File /= null then |
| Fail ("-P and -p cannot be used together"); |
| end if; |
| end if; |
| |
| -- -q Be quiet: do not output tool command |
| |
| if Argv (2 .. Argv'Last) = "q" then |
| Opt.Quiet_Output := True; |
| |
| -- Only gnatstub and gnatmake have a -q switch |
| |
| if Tool = Stub or else Tool_Names (Tool) = Gnatmake then |
| Add_Switch (Argv, And_Save); |
| end if; |
| |
| -- gnatmake will take care of the project file related switches |
| |
| elsif Tool_Names (Tool) = Gnatmake then |
| Add_Switch (Argv, And_Save); |
| |
| -- -vPx Specify verbosity while parsing project files |
| |
| elsif Argv'Length = 4 and then Argv (2 .. 3) = "vP" then |
| case Argv (4) is |
| when '0' => |
| Current_Verbosity := Prj.Default; |
| when '1' => |
| Current_Verbosity := Prj.Medium; |
| when '2' => |
| Current_Verbosity := Prj.High; |
| when others => |
| null; |
| end case; |
| |
| -- -Pproject_file Specify project file to be used |
| |
| elsif Argv'Length >= 3 and then Argv (2) = 'P' then |
| |
| -- Only one -P switch can be used |
| |
| if Project_File /= null then |
| Fail (Argv & ": second project file forbidden (first is """ & |
| Project_File.all & """)"); |
| |
| -- The two style project files (-p and -P) cannot be used together |
| |
| elsif Old_Project_File_Used then |
| Fail ("-p and -P cannot be used together"); |
| |
| else |
| Project_File := new String'(Argv (3 .. Argv'Last)); |
| end if; |
| |
| -- -Xexternal=value Specify an external reference to be used |
| -- in project files |
| |
| elsif Argv'Length >= 5 and then Argv (2) = 'X' then |
| declare |
| Equal_Pos : constant Natural := |
| Index ('=', Argv (3 .. Argv'Last)); |
| begin |
| if Equal_Pos >= 4 and then |
| Equal_Pos /= Argv'Last then |
| Add (External_Name => Argv (3 .. Equal_Pos - 1), |
| Value => Argv (Equal_Pos + 1 .. Argv'Last)); |
| else |
| Fail (Argv & " is not a valid external assignment."); |
| end if; |
| end; |
| |
| else |
| Add_Switch (Argv, And_Save); |
| end if; |
| |
| else |
| Add_Switch (Argv, And_Save); |
| end if; |
| |
| end Scan_Arg; |
| |
| ----------- |
| -- Usage -- |
| ----------- |
| |
| procedure Usage is |
| begin |
| Write_Str ("Usage: "); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" list switches [list of object files]"); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" xref switches file1 file2 ..."); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" find switches pattern[:sourcefile[:line[:column]]] " & |
| "[file1 file2 ...]"); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" stub switches filename [directory]"); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" comp switches files"); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" make switches [files]"); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" bind switches files"); |
| Write_Eol; |
| |
| Osint.Write_Program_Name; |
| Write_Str (" link switches files"); |
| Write_Eol; |
| |
| Write_Eol; |
| |
| Write_Str ("switches interpreted by "); |
| Osint.Write_Program_Name; |
| Write_Str (" for List Xref and Find:"); |
| Write_Eol; |
| |
| Write_Str (" -q Be quiet: do not output tool command"); |
| Write_Eol; |
| |
| Write_Str (" -Pproj Use GNAT Project File proj"); |
| Write_Eol; |
| |
| Write_Str (" -vPx Specify verbosity when parsing " & |
| "GNAT Project Files"); |
| Write_Eol; |
| |
| Write_Str (" -Xnm=val Specify an external reference for " & |
| "GNAT Project Files"); |
| Write_Eol; |
| |
| Write_Eol; |
| |
| Write_Str ("all other arguments are transmited to the tool"); |
| Write_Eol; |
| |
| Write_Eol; |
| |
| end Usage; |
| |
| begin |
| |
| Osint.Initialize (Unspecified); |
| |
| Namet.Initialize; |
| Csets.Initialize; |
| |
| Snames.Initialize; |
| |
| Prj.Initialize; |
| |
| if Arg_Count = 1 then |
| Usage; |
| return; |
| end if; |
| |
| -- Get the name of the tool |
| |
| declare |
| Tool_Name : String (1 .. Len_Arg (1)); |
| |
| begin |
| Fill_Arg (Tool_Name'Address, 1); |
| GNAT.Case_Util.To_Lower (Tool_Name); |
| |
| if Tool_Name = "list" then |
| Tool := List; |
| |
| elsif Tool_Name = "xref" then |
| Tool := Xref; |
| |
| elsif Tool_Name = "find" then |
| Tool := Find; |
| |
| elsif Tool_Name = "stub" then |
| Tool := Stub; |
| |
| elsif Tool_Name = "comp" then |
| Tool := Comp; |
| |
| elsif Tool_Name = "make" then |
| Tool := Make; |
| |
| elsif Tool_Name = "bind" then |
| Tool := Bind; |
| |
| elsif Tool_Name = "link" then |
| Tool := Link; |
| |
| else |
| Fail ("first argument needs to be ""list"", ""xref"", ""find""" & |
| ", ""stub"", ""comp"", ""make"", ""bind"" or ""link"""); |
| end if; |
| end; |
| |
| Next_Arg := 2; |
| |
| -- Get the command line switches that follow the name of the tool |
| |
| Scan_Args : while Next_Arg < Arg_Count loop |
| declare |
| Next_Argv : String (1 .. Len_Arg (Next_Arg)); |
| |
| begin |
| Fill_Arg (Next_Argv'Address, Next_Arg); |
| Scan_Arg (Next_Argv, And_Save => True); |
| end; |
| |
| Next_Arg := Next_Arg + 1; |
| end loop Scan_Args; |
| |
| -- If a switch -P was specified, parse the project file. |
| -- Project_File is always null if we are going to invoke gnatmake, |
| -- that is when Tool is Comp, Make, Bind or Link. |
| |
| if Project_File /= null then |
| |
| Prj.Pars.Set_Verbosity (To => Current_Verbosity); |
| |
| Prj.Pars.Parse |
| (Project => Project, |
| Project_File_Name => Project_File.all); |
| |
| if Project = Prj.No_Project then |
| Fail ("""" & Project_File.all & """ processing failed"); |
| end if; |
| |
| -- Check if a package with the name of the tool is in the project file |
| -- and if there is one, get the switches, if any, and scan them. |
| |
| declare |
| Data : Prj.Project_Data := Prj.Projects.Table (Project); |
| Pkg : Prj.Package_Id := |
| Prj.Util.Value_Of |
| (Name => Tool_Package_Names (Tool), |
| In_Packages => Data.Decl.Packages); |
| Element : Package_Element; |
| Default_Switches_Array : Array_Element_Id; |
| Switches : Prj.Variable_Value; |
| Current : Prj.String_List_Id; |
| The_String : String_Element; |
| |
| begin |
| if Pkg /= No_Package then |
| Element := Packages.Table (Pkg); |
| |
| -- Packages Gnatls and Gnatstub have a single attribute Switches, |
| -- that is not an associative array. |
| |
| if Tool = List or else Tool = Stub then |
| Switches := |
| Prj.Util.Value_Of |
| (Variable_Name => Name_Switches, |
| In_Variables => Element.Decl.Attributes); |
| |
| -- Packages Cross_Reference (for gnatxref) and Finder |
| -- (for gnatfind) have an attributed Default_Switches, |
| -- an associative array, indexed by the name of the |
| -- programming language. |
| else |
| Default_Switches_Array := |
| Prj.Util.Value_Of |
| (Name => Name_Default_Switches, |
| In_Arrays => Packages.Table (Pkg).Decl.Arrays); |
| Switches := Prj.Util.Value_Of |
| (Index => Name_Ada, |
| In_Array => Default_Switches_Array); |
| |
| end if; |
| |
| -- If there are switches specified in the package of the |
| -- project file corresponding to the tool, scan them. |
| |
| case Switches.Kind is |
| when Prj.Undefined => |
| null; |
| |
| when Prj.Single => |
| if String_Length (Switches.Value) > 0 then |
| String_To_Name_Buffer (Switches.Value); |
| Scan_Arg |
| (Name_Buffer (1 .. Name_Len), |
| And_Save => False); |
| end if; |
| |
| when Prj.List => |
| Current := Switches.Values; |
| while Current /= Prj.Nil_String loop |
| The_String := String_Elements.Table (Current); |
| |
| if String_Length (The_String.Value) > 0 then |
| String_To_Name_Buffer (The_String.Value); |
| Scan_Arg |
| (Name_Buffer (1 .. Name_Len), |
| And_Save => False); |
| end if; |
| |
| Current := The_String.Next; |
| end loop; |
| end case; |
| end if; |
| end; |
| |
| -- Set up the environment variables ADA_INCLUDE_PATH and |
| -- ADA_OBJECTS_PATH. |
| |
| Setenv |
| (Name => Ada_Include_Path, |
| Value => Prj.Env.Ada_Include_Path (Project).all); |
| Setenv |
| (Name => Ada_Objects_Path, |
| Value => Prj.Env.Ada_Objects_Path |
| (Project, Including_Libraries => False).all); |
| |
| end if; |
| |
| -- Gather all the arguments, those from the project file first, |
| -- locate the tool and call it with the arguments. |
| |
| declare |
| Args : Argument_List (1 .. Switches.Last + Saved_Switches.Last + 4); |
| Arg_Num : Natural := 0; |
| Tool_Path : String_Access; |
| Success : Boolean; |
| |
| procedure Add (Arg : String_Access); |
| |
| procedure Add (Arg : String_Access) is |
| begin |
| Arg_Num := Arg_Num + 1; |
| Args (Arg_Num) := Arg; |
| end Add; |
| |
| begin |
| |
| case Tool is |
| when Comp => |
| Add (new String'("-u")); |
| Add (new String'("-f")); |
| |
| when Bind => |
| Add (new String'("-b")); |
| |
| when Link => |
| Add (new String'("-l")); |
| |
| when others => |
| null; |
| |
| end case; |
| |
| for Index in 1 .. Switches.Last loop |
| Arg_Num := Arg_Num + 1; |
| Args (Arg_Num) := Switches.Table (Index); |
| end loop; |
| |
| for Index in 1 .. Saved_Switches.Last loop |
| Arg_Num := Arg_Num + 1; |
| Args (Arg_Num) := Saved_Switches.Table (Index); |
| end loop; |
| |
| Tool_Path := GNAT.OS_Lib.Locate_Exec_On_Path (Tool_Names (Tool).all); |
| |
| if Tool_Path = null then |
| Fail ("error, unable to locate " & Tool_Names (Tool).all); |
| end if; |
| |
| Display (Tool_Names (Tool).all, Args (1 .. Arg_Num)); |
| |
| GNAT.OS_Lib.Spawn (Tool_Path.all, Args (1 .. Arg_Num), Success); |
| |
| end; |
| |
| end Gnatmain; |