blob: 82f32747948b71ca36d2ef93a2f3bcf6dcd6451b [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- G N A T N A M E --
-- --
-- B o d y --
-- --
-- Copyright (C) 2001-2014, 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 3, 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 COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Command_Line; use GNAT.Command_Line;
with GNAT.Dynamic_Tables;
with GNAT.OS_Lib; use GNAT.OS_Lib;
with Opt;
with Osint; use Osint;
with Output; use Output;
with Prj; use Prj;
with Prj.Makr;
with Switch; use Switch;
with Table;
with System.Regexp; use System.Regexp;
procedure Gnatname is
Subdirs_Switch : constant String := "--subdirs=";
Usage_Output : Boolean := False;
-- Set to True when usage is output, to avoid multiple output
Usage_Needed : Boolean := False;
-- Set to True by -h switch
Version_Output : Boolean := False;
-- Set to True when version is output, to avoid multiple output
Very_Verbose : Boolean := False;
-- Set to True with -v -v
Create_Project : Boolean := False;
-- Set to True with a -P switch
File_Path : String_Access := new String'("gnat.adc");
-- Path name of the file specified by -c or -P switch
File_Set : Boolean := False;
-- Set to True by -c or -P switch.
-- Used to detect multiple -c/-P switches.
package Patterns is new GNAT.Dynamic_Tables
(Table_Component_Type => String_Access,
Table_Index_Type => Natural,
Table_Low_Bound => 0,
Table_Initial => 10,
Table_Increment => 100);
-- Table to accumulate the patterns
type Argument_Data is record
Directories : Patterns.Instance;
Name_Patterns : Patterns.Instance;
Excluded_Patterns : Patterns.Instance;
Foreign_Patterns : Patterns.Instance;
end record;
package Arguments is new Table.Table
(Table_Component_Type => Argument_Data,
Table_Index_Type => Natural,
Table_Low_Bound => 0,
Table_Initial => 10,
Table_Increment => 100,
Table_Name => "Gnatname.Arguments");
-- Table to accumulate directories and patterns
package Preprocessor_Switches is new Table.Table
(Table_Component_Type => String_Access,
Table_Index_Type => Natural,
Table_Low_Bound => 0,
Table_Initial => 10,
Table_Increment => 100,
Table_Name => "Gnatname.Preprocessor_Switches");
-- Table to store the preprocessor switches to be used in the call
-- to the compiler.
procedure Output_Version;
-- Print name and version
procedure Usage;
-- Print usage
procedure Scan_Args;
-- Scan the command line arguments
procedure Add_Source_Directory (S : String);
-- Add S in the Source_Directories table
procedure Get_Directories (From_File : String);
-- Read a source directory text file
--------------------------
-- Add_Source_Directory --
--------------------------
procedure Add_Source_Directory (S : String) is
begin
Patterns.Append
(Arguments.Table (Arguments.Last).Directories, new String'(S));
end Add_Source_Directory;
---------------------
-- Get_Directories --
---------------------
procedure Get_Directories (From_File : String) is
File : Ada.Text_IO.File_Type;
Line : String (1 .. 2_000);
Last : Natural;
begin
Open (File, In_File, From_File);
while not End_Of_File (File) loop
Get_Line (File, Line, Last);
if Last /= 0 then
Add_Source_Directory (Line (1 .. Last));
end if;
end loop;
Close (File);
exception
when Name_Error =>
Fail ("cannot open source directory file """ & From_File & '"');
end Get_Directories;
--------------------
-- Output_Version --
--------------------
procedure Output_Version is
begin
if not Version_Output then
Version_Output := True;
Output.Write_Eol;
Display_Version ("GNATNAME", "2001");
end if;
end Output_Version;
---------------
-- Scan_Args --
---------------
procedure Scan_Args is
procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
Project_File_Name_Expected : Boolean;
Pragmas_File_Expected : Boolean;
Directory_Expected : Boolean;
Dir_File_Name_Expected : Boolean;
Foreign_Pattern_Expected : Boolean;
Excluded_Pattern_Expected : Boolean;
procedure Check_Regular_Expression (S : String);
-- Compile string S into a Regexp, fail if any error
-----------------------------
-- Check_Regular_Expression--
-----------------------------
procedure Check_Regular_Expression (S : String) is
Dummy : Regexp;
pragma Warnings (Off, Dummy);
begin
Dummy := Compile (S, Glob => True);
exception
when Error_In_Regexp =>
Fail ("invalid regular expression """ & S & """");
end Check_Regular_Expression;
-- Start of processing for Scan_Args
begin
-- First check for --version or --help
Check_Version_And_Help ("GNATNAME", "2001");
-- Now scan the other switches
Project_File_Name_Expected := False;
Pragmas_File_Expected := False;
Directory_Expected := False;
Dir_File_Name_Expected := False;
Foreign_Pattern_Expected := False;
Excluded_Pattern_Expected := False;
for Next_Arg in 1 .. Argument_Count loop
declare
Next_Argv : constant String := Argument (Next_Arg);
Arg : String (1 .. Next_Argv'Length) := Next_Argv;
begin
if Arg'Length > 0 then
-- -P xxx
if Project_File_Name_Expected then
if Arg (1) = '-' then
Fail ("project file name missing");
else
File_Set := True;
File_Path := new String'(Arg);
Project_File_Name_Expected := False;
end if;
-- -c file
elsif Pragmas_File_Expected then
File_Set := True;
File_Path := new String'(Arg);
Create_Project := False;
Pragmas_File_Expected := False;
-- -d xxx
elsif Directory_Expected then
Add_Source_Directory (Arg);
Directory_Expected := False;
-- -D xxx
elsif Dir_File_Name_Expected then
Get_Directories (Arg);
Dir_File_Name_Expected := False;
-- -f xxx
elsif Foreign_Pattern_Expected then
Patterns.Append
(Arguments.Table (Arguments.Last).Foreign_Patterns,
new String'(Arg));
Check_Regular_Expression (Arg);
Foreign_Pattern_Expected := False;
-- -x xxx
elsif Excluded_Pattern_Expected then
Patterns.Append
(Arguments.Table (Arguments.Last).Excluded_Patterns,
new String'(Arg));
Check_Regular_Expression (Arg);
Excluded_Pattern_Expected := False;
-- There must be at least one Ada pattern or one foreign
-- pattern for the previous section.
-- --and
elsif Arg = "--and" then
if Patterns.Last
(Arguments.Table (Arguments.Last).Name_Patterns) = 0
and then
Patterns.Last
(Arguments.Table (Arguments.Last).Foreign_Patterns) = 0
then
Try_Help;
return;
end if;
-- If no directory were specified for the previous section,
-- then the directory is the project directory.
if Patterns.Last
(Arguments.Table (Arguments.Last).Directories) = 0
then
Patterns.Append
(Arguments.Table (Arguments.Last).Directories,
new String'("."));
end if;
-- Add and initialize another component to Arguments table
declare
New_Arguments : Argument_Data;
pragma Warnings (Off, New_Arguments);
-- Declaring this defaulted initialized object ensures
-- that the new allocated component of table Arguments
-- is correctly initialized.
-- This is VERY ugly, Table should never be used with
-- data requiring default initialization. We should
-- find a way to avoid violating this rule ???
begin
Arguments.Append (New_Arguments);
end;
Patterns.Init
(Arguments.Table (Arguments.Last).Directories);
Patterns.Set_Last
(Arguments.Table (Arguments.Last).Directories, 0);
Patterns.Init
(Arguments.Table (Arguments.Last).Name_Patterns);
Patterns.Set_Last
(Arguments.Table (Arguments.Last).Name_Patterns, 0);
Patterns.Init
(Arguments.Table (Arguments.Last).Excluded_Patterns);
Patterns.Set_Last
(Arguments.Table (Arguments.Last).Excluded_Patterns, 0);
Patterns.Init
(Arguments.Table (Arguments.Last).Foreign_Patterns);
Patterns.Set_Last
(Arguments.Table (Arguments.Last).Foreign_Patterns, 0);
-- Subdirectory switch
elsif Arg'Length > Subdirs_Switch'Length
and then Arg (1 .. Subdirs_Switch'Length) = Subdirs_Switch
then
Subdirs :=
new String'(Arg (Subdirs_Switch'Length + 1 .. Arg'Last));
-- --no-backup
elsif Arg = "--no-backup" then
Opt.No_Backup := True;
-- -c
elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-c" then
if File_Set then
Fail ("only one -P or -c switch may be specified");
end if;
if Arg'Length = 2 then
Pragmas_File_Expected := True;
if Next_Arg = Argument_Count then
Fail ("configuration pragmas file name missing");
end if;
else
File_Set := True;
File_Path := new String'(Arg (3 .. Arg'Last));
Create_Project := False;
end if;
-- -d
elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-d" then
if Arg'Length = 2 then
Directory_Expected := True;
if Next_Arg = Argument_Count then
Fail ("directory name missing");
end if;
else
Add_Source_Directory (Arg (3 .. Arg'Last));
end if;
-- -D
elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-D" then
if Arg'Length = 2 then
Dir_File_Name_Expected := True;
if Next_Arg = Argument_Count then
Fail ("directory list file name missing");
end if;
else
Get_Directories (Arg (3 .. Arg'Last));
end if;
-- -eL
elsif Arg = "-eL" then
Opt.Follow_Links_For_Files := True;
Opt.Follow_Links_For_Dirs := True;
-- -f
elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-f" then
if Arg'Length = 2 then
Foreign_Pattern_Expected := True;
if Next_Arg = Argument_Count then
Fail ("foreign pattern missing");
end if;
else
Patterns.Append
(Arguments.Table (Arguments.Last).Foreign_Patterns,
new String'(Arg (3 .. Arg'Last)));
Check_Regular_Expression (Arg (3 .. Arg'Last));
end if;
-- -gnatep or -gnateD
elsif Arg'Length > 7 and then
(Arg (1 .. 7) = "-gnatep" or else Arg (1 .. 7) = "-gnateD")
then
Preprocessor_Switches.Append (new String'(Arg));
-- -h
elsif Arg = "-h" then
Usage_Needed := True;
-- -p
elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-P" then
if File_Set then
Fail ("only one -c or -P switch may be specified");
end if;
if Arg'Length = 2 then
if Next_Arg = Argument_Count then
Fail ("project file name missing");
else
Project_File_Name_Expected := True;
end if;
else
File_Set := True;
File_Path := new String'(Arg (3 .. Arg'Last));
end if;
Create_Project := True;
-- -v
elsif Arg = "-v" then
if Opt.Verbose_Mode then
Very_Verbose := True;
else
Opt.Verbose_Mode := True;
end if;
-- -x
elsif Arg'Length >= 2 and then Arg (1 .. 2) = "-x" then
if Arg'Length = 2 then
Excluded_Pattern_Expected := True;
if Next_Arg = Argument_Count then
Fail ("excluded pattern missing");
end if;
else
Patterns.Append
(Arguments.Table (Arguments.Last).Excluded_Patterns,
new String'(Arg (3 .. Arg'Last)));
Check_Regular_Expression (Arg (3 .. Arg'Last));
end if;
-- Junk switch starting with minus
elsif Arg (1) = '-' then
Fail ("wrong switch: " & Arg);
-- Not a recognized switch, assume file name
else
Canonical_Case_File_Name (Arg);
Patterns.Append
(Arguments.Table (Arguments.Last).Name_Patterns,
new String'(Arg));
Check_Regular_Expression (Arg);
end if;
end if;
end;
end loop;
end Scan_Args;
-----------
-- Usage --
-----------
procedure Usage is
begin
if not Usage_Output then
Usage_Needed := False;
Usage_Output := True;
Write_Str ("Usage: ");
Osint.Write_Program_Name;
Write_Line (" [switches] naming-pattern [naming-patterns]");
Write_Line (" {--and [switches] naming-pattern [naming-patterns]}");
Write_Eol;
Write_Line ("switches:");
Display_Usage_Version_And_Help;
Write_Line (" --subdirs=dir real obj/lib/exec dirs are subdirs");
Write_Line (" --no-backup do not create backup of project file");
Write_Eol;
Write_Line (" --and use different patterns");
Write_Eol;
Write_Line (" -cfile create configuration pragmas file");
Write_Line (" -ddir use dir as one of the source " &
"directories");
Write_Line (" -Dfile get source directories from file");
Write_Line (" -eL follow symbolic links when processing " &
"project files");
Write_Line (" -fpat foreign pattern");
Write_Line (" -gnateDsym=v preprocess with symbol definition");
Write_Line (" -gnatep=data preprocess files with data file");
Write_Line (" -h output this help message");
Write_Line (" -Pproj update or create project file proj");
Write_Line (" -v verbose output");
Write_Line (" -v -v very verbose output");
Write_Line (" -xpat exclude pattern pat");
end if;
end Usage;
-- Start of processing for Gnatname
begin
-- Add the directory where gnatname is invoked in front of the
-- path, if gnatname is invoked with directory information.
declare
Command : constant String := Command_Name;
begin
for Index in reverse Command'Range loop
if Command (Index) = Directory_Separator then
declare
Absolute_Dir : constant String :=
Normalize_Pathname
(Command (Command'First .. Index));
PATH : constant String :=
Absolute_Dir &
Path_Separator &
Getenv ("PATH").all;
begin
Setenv ("PATH", PATH);
end;
exit;
end if;
end loop;
end;
-- Initialize tables
Arguments.Set_Last (0);
declare
New_Arguments : Argument_Data;
pragma Warnings (Off, New_Arguments);
-- Declaring this defaulted initialized object ensures that the new
-- allocated component of table Arguments is correctly initialized.
begin
Arguments.Append (New_Arguments);
end;
Patterns.Init (Arguments.Table (1).Directories);
Patterns.Set_Last (Arguments.Table (1).Directories, 0);
Patterns.Init (Arguments.Table (1).Name_Patterns);
Patterns.Set_Last (Arguments.Table (1).Name_Patterns, 0);
Patterns.Init (Arguments.Table (1).Excluded_Patterns);
Patterns.Set_Last (Arguments.Table (1).Excluded_Patterns, 0);
Patterns.Init (Arguments.Table (1).Foreign_Patterns);
Patterns.Set_Last (Arguments.Table (1).Foreign_Patterns, 0);
Preprocessor_Switches.Set_Last (0);
-- Get the arguments
Scan_Args;
if Opt.Verbose_Mode then
Output_Version;
end if;
if Usage_Needed then
Usage;
end if;
-- If no Ada or foreign pattern was specified, print the usage and return
if Patterns.Last (Arguments.Table (Arguments.Last).Name_Patterns) = 0
and then
Patterns.Last (Arguments.Table (Arguments.Last).Foreign_Patterns) = 0
then
if Argument_Count = 0 then
Usage;
elsif not Usage_Output then
Try_Help;
end if;
return;
end if;
-- If no source directory was specified, use the current directory as the
-- unique directory. Note that if a file was specified with directory
-- information, the current directory is the directory of the specified
-- file.
if Patterns.Last
(Arguments.Table (Arguments.Last).Directories) = 0
then
Patterns.Append
(Arguments.Table (Arguments.Last).Directories, new String'("."));
end if;
-- Initialize
declare
Prep_Switches : Argument_List
(1 .. Integer (Preprocessor_Switches.Last));
begin
for Index in Prep_Switches'Range loop
Prep_Switches (Index) := Preprocessor_Switches.Table (Index);
end loop;
Prj.Makr.Initialize
(File_Path => File_Path.all,
Project_File => Create_Project,
Preproc_Switches => Prep_Switches,
Very_Verbose => Very_Verbose,
Flags => Gnatmake_Flags);
end;
-- Process each section successively
for J in 1 .. Arguments.Last loop
declare
Directories : Argument_List
(1 .. Integer
(Patterns.Last (Arguments.Table (J).Directories)));
Name_Patterns : Prj.Makr.Regexp_List
(1 .. Integer
(Patterns.Last (Arguments.Table (J).Name_Patterns)));
Excl_Patterns : Prj.Makr.Regexp_List
(1 .. Integer
(Patterns.Last (Arguments.Table (J).Excluded_Patterns)));
Frgn_Patterns : Prj.Makr.Regexp_List
(1 .. Integer
(Patterns.Last (Arguments.Table (J).Foreign_Patterns)));
begin
-- Build the Directories and Patterns arguments
for Index in Directories'Range loop
Directories (Index) :=
Arguments.Table (J).Directories.Table (Index);
end loop;
for Index in Name_Patterns'Range loop
Name_Patterns (Index) :=
Compile
(Arguments.Table (J).Name_Patterns.Table (Index).all,
Glob => True);
end loop;
for Index in Excl_Patterns'Range loop
Excl_Patterns (Index) :=
Compile
(Arguments.Table (J).Excluded_Patterns.Table (Index).all,
Glob => True);
end loop;
for Index in Frgn_Patterns'Range loop
Frgn_Patterns (Index) :=
Compile
(Arguments.Table (J).Foreign_Patterns.Table (Index).all,
Glob => True);
end loop;
-- Call Prj.Makr.Process where the real work is done
Prj.Makr.Process
(Directories => Directories,
Name_Patterns => Name_Patterns,
Excluded_Patterns => Excl_Patterns,
Foreign_Patterns => Frgn_Patterns);
end;
end loop;
-- Finalize
Prj.Makr.Finalize;
if Opt.Verbose_Mode then
Write_Eol;
end if;
end Gnatname;