File : gnatdll.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                         GNAT COMPILER COMPONENTS                         --
   4 --                                                                          --
   5 --                              G N A T D L L                               --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 1997-2013, Free Software Foundation, Inc.         --
  10 --                                                                          --
  11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  12 -- terms of the  GNU General Public License as published  by the Free Soft- --
  13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
  14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  17 -- for  more details.  You should have  received  a copy of the GNU General --
  18 -- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
  19 -- http://www.gnu.org/licenses for a complete copy of the license.          --
  20 --                                                                          --
  21 -- GNAT was originally developed  by the GNAT team at  New York University. --
  22 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
  23 --                                                                          --
  24 ------------------------------------------------------------------------------
  25 
  26 --  GNATDLL is a Windows specific tool for building a DLL.
  27 --  Both relocatable and non-relocatable DLL's are supported
  28 
  29 with Gnatvsn;
  30 with MDLL.Fil; use MDLL.Fil;
  31 with MDLL.Utl; use MDLL.Utl;
  32 with Switch;   use Switch;
  33 
  34 with Ada.Text_IO;           use Ada.Text_IO;
  35 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
  36 with Ada.Exceptions;        use Ada.Exceptions;
  37 with Ada.Command_Line;      use Ada.Command_Line;
  38 
  39 with GNAT.OS_Lib;       use GNAT.OS_Lib;
  40 with GNAT.Command_Line; use GNAT.Command_Line;
  41 
  42 procedure Gnatdll is
  43 
  44    use type GNAT.OS_Lib.Argument_List;
  45 
  46    procedure Syntax;
  47    --  Print out usage
  48 
  49    procedure Check (Filename : String);
  50    --  Check that the file whose name is Filename exists
  51 
  52    procedure Parse_Command_Line;
  53    --  Parse the command line arguments passed to gnatdll
  54 
  55    procedure Check_Context;
  56    --  Check the context before running any commands to build the library
  57 
  58    Syntax_Error : exception;
  59    --  Raised when a syntax error is detected, in this case a usage info will
  60    --  be displayed.
  61 
  62    Context_Error : exception;
  63    --  Raised when some files (specified on the command line) are missing to
  64    --  build the DLL.
  65 
  66    Help : Boolean := False;
  67    --  Help will be set to True the usage information is to be displayed
  68 
  69    Version : constant String := Gnatvsn.Gnat_Version_String;
  70    --  Why should it be necessary to make a copy of this
  71 
  72    Default_DLL_Address : constant String := "0x11000000";
  73    --  Default address for non relocatable DLL (Win32)
  74 
  75    Lib_Filename : Unbounded_String := Null_Unbounded_String;
  76    --  The DLL filename that will be created (.dll)
  77 
  78    Def_Filename : Unbounded_String := Null_Unbounded_String;
  79    --  The definition filename (.def)
  80 
  81    List_Filename : Unbounded_String := Null_Unbounded_String;
  82    --  The name of the file containing the objects file to put into the DLL
  83 
  84    DLL_Address : Unbounded_String := To_Unbounded_String (Default_DLL_Address);
  85    --  The DLL's base address
  86 
  87    Gen_Map_File : Boolean := False;
  88    --  Set to True if a map file is to be generated
  89 
  90    Objects_Files : Argument_List_Access := MDLL.Null_Argument_List_Access;
  91    --  List of objects to put inside the library
  92 
  93    Ali_Files : Argument_List_Access := MDLL.Null_Argument_List_Access;
  94    --  For each Ada file specified, we keep a record of the corresponding
  95    --  ALI file. This list of SLI files is used to build the binder program.
  96 
  97    Options : Argument_List_Access := MDLL.Null_Argument_List_Access;
  98    --  A list of options set in the command line
  99 
 100    Largs_Options : Argument_List_Access := MDLL.Null_Argument_List_Access;
 101    Bargs_Options : Argument_List_Access := MDLL.Null_Argument_List_Access;
 102    --  GNAT linker and binder args options
 103 
 104    type Build_Mode_State is (Import_Lib, Dynamic_Lib, Dynamic_Lib_Only, Nil);
 105    --  Import_Lib means only the .a file will be created, Dynamic_Lib means
 106    --  that both the DLL and the import library will be created.
 107    --  Dynamic_Lib_Only means that only the DLL will be created (no import
 108    --  library).
 109 
 110    Build_Mode : Build_Mode_State := Nil;
 111    --  Will be set when parsing the command line
 112 
 113    Must_Build_Relocatable : Boolean := True;
 114    --  True means build a relocatable DLL, will be set to False if a
 115    --  non-relocatable DLL must be built.
 116 
 117    ------------
 118    -- Syntax --
 119    ------------
 120 
 121    procedure Syntax is
 122       procedure P (Str : String) renames Put_Line;
 123    begin
 124       P ("Usage : gnatdll [options] [list-of-files]");
 125       New_Line;
 126       P ("[list-of-files] a list of Ada libraries (.ali) and/or " &
 127          "foreign object files");
 128       New_Line;
 129       P ("[options] can be");
 130       P ("   -h            Help - display this message");
 131       P ("   -v            Verbose");
 132       P ("   -q            Quiet");
 133       P ("   -k            Remove @nn suffix from exported names");
 134       P ("   -g            Generate debugging information");
 135       P ("   -Idir         Specify source and object files search path");
 136       P ("   -l file       File contains a list-of-files to be added to "
 137          & "the library");
 138       P ("   -e file       Definition file containing exports");
 139       P ("   -d file       Put objects in the relocatable dynamic "
 140          & "library <file>");
 141       P ("   -b addr       Set base address for the relocatable DLL");
 142       P ("                 default address is " & Default_DLL_Address);
 143       P ("   -a[addr]      Build non-relocatable DLL at address <addr>");
 144       P ("                 if <addr> is not specified use "
 145          & Default_DLL_Address);
 146       P ("   -m            Generate map file");
 147       P ("   -n            No-import - do not create the import library");
 148       P ("   -bargs opts   opts are passed to the binder");
 149       P ("   -largs opts   opts are passed to the linker");
 150    end Syntax;
 151 
 152    -----------
 153    -- Check --
 154    -----------
 155 
 156    procedure Check (Filename : String) is
 157    begin
 158       if not Is_Regular_File (Filename) then
 159          Raise_Exception
 160            (Context_Error'Identity, "Error: " & Filename & " not found.");
 161       end if;
 162    end Check;
 163 
 164    ------------------------
 165    -- Parse_Command_Line --
 166    ------------------------
 167 
 168    procedure Parse_Command_Line is
 169 
 170       procedure Add_File (Filename : String);
 171       --  Add one file to the list of file to handle
 172 
 173       procedure Add_Files_From_List (List_Filename : String);
 174       --  Add the files listed in List_Filename (one by line) to the list
 175       --  of file to handle
 176 
 177       Max_Files   : constant := 5_000;
 178       Max_Options : constant :=   100;
 179       --  These are arbitrary limits, a better way will be to use linked list.
 180       --  No, a better choice would be to use tables ???
 181       --  Limits on what???
 182 
 183       Ofiles : Argument_List (1 .. Max_Files);
 184       O      : Positive := Ofiles'First;
 185       --  List of object files to put in the library. O is the next entry
 186       --  to be used.
 187 
 188       Afiles : Argument_List (1 .. Max_Files);
 189       A      : Positive := Afiles'First;
 190       --  List of ALI files. A is the next entry to be used
 191 
 192       Gopts  : Argument_List (1 .. Max_Options);
 193       G      : Positive := Gopts'First;
 194       --  List of gcc options. G is the next entry to be used
 195 
 196       Lopts  : Argument_List (1 .. Max_Options);
 197       L      : Positive := Lopts'First;
 198       --  A list of -largs options (L is next entry to be used)
 199 
 200       Bopts  : Argument_List (1 .. Max_Options);
 201       B      : Positive := Bopts'First;
 202       --  A list of -bargs options (B is next entry to be used)
 203 
 204       Build_Import : Boolean := True;
 205       --  Set to False if option -n if specified (no-import)
 206 
 207       --------------
 208       -- Add_File --
 209       --------------
 210 
 211       procedure Add_File (Filename : String) is
 212       begin
 213          if Is_Ali (Filename) then
 214             Check (Filename);
 215 
 216             --  Record it to generate the binder program when
 217             --  building dynamic library
 218 
 219             Afiles (A) := new String'(Filename);
 220             A := A + 1;
 221 
 222          elsif Is_Obj (Filename) then
 223             Check (Filename);
 224 
 225             --  Just record this object file
 226 
 227             Ofiles (O) := new String'(Filename);
 228             O := O + 1;
 229 
 230          else
 231             --  Unknown file type
 232 
 233             Raise_Exception
 234               (Syntax_Error'Identity,
 235                "don't know what to do with " & Filename & " !");
 236          end if;
 237       end Add_File;
 238 
 239       -------------------------
 240       -- Add_Files_From_List --
 241       -------------------------
 242 
 243       procedure Add_Files_From_List (List_Filename : String) is
 244          File   : File_Type;
 245          Buffer : String (1 .. 500);
 246          Last   : Natural;
 247 
 248       begin
 249          Open (File, In_File, List_Filename);
 250 
 251          while not End_Of_File (File) loop
 252             Get_Line (File, Buffer, Last);
 253             Add_File (Buffer (1 .. Last));
 254          end loop;
 255 
 256          Close (File);
 257 
 258       exception
 259          when Name_Error =>
 260             Raise_Exception
 261               (Syntax_Error'Identity,
 262                "list-of-files file " & List_Filename & " not found.");
 263       end Add_Files_From_List;
 264 
 265    --  Start of processing for Parse_Command_Line
 266 
 267    begin
 268       Initialize_Option_Scan ('-', False, "bargs largs");
 269 
 270       --  scan gnatdll switches
 271 
 272       loop
 273          case Getopt ("g h v q k a? b: d: e: l: n m I:") is
 274 
 275             when ASCII.NUL =>
 276                exit;
 277 
 278             when 'h' =>
 279                Help := True;
 280 
 281             when 'g' =>
 282                Gopts (G) := new String'("-g");
 283                G := G + 1;
 284 
 285             when 'v' =>
 286 
 287                --  Turn verbose mode on
 288 
 289                MDLL.Verbose := True;
 290                if MDLL.Quiet then
 291                   Raise_Exception
 292                     (Syntax_Error'Identity,
 293                      "impossible to use -q and -v together.");
 294                end if;
 295 
 296             when 'q' =>
 297 
 298                --  Turn quiet mode on
 299 
 300                MDLL.Quiet := True;
 301                if MDLL.Verbose then
 302                   Raise_Exception
 303                     (Syntax_Error'Identity,
 304                      "impossible to use -v and -q together.");
 305                end if;
 306 
 307             when 'k' =>
 308 
 309                MDLL.Kill_Suffix := True;
 310 
 311             when 'a' =>
 312 
 313                if Parameter = "" then
 314 
 315                   --  Default address for a relocatable dynamic library.
 316                   --  address for a non relocatable dynamic library.
 317 
 318                   DLL_Address := To_Unbounded_String (Default_DLL_Address);
 319 
 320                else
 321                   DLL_Address := To_Unbounded_String (Parameter);
 322                end if;
 323 
 324                Must_Build_Relocatable := False;
 325 
 326             when 'b' =>
 327 
 328                DLL_Address := To_Unbounded_String (Parameter);
 329 
 330                Must_Build_Relocatable := True;
 331 
 332             when 'e' =>
 333 
 334                Def_Filename := To_Unbounded_String (Parameter);
 335 
 336             when 'd' =>
 337 
 338                --  Build a non relocatable DLL
 339 
 340                Lib_Filename := To_Unbounded_String (Parameter);
 341 
 342                if Def_Filename = Null_Unbounded_String then
 343                   Def_Filename := To_Unbounded_String
 344                     (Ext_To (Parameter, "def"));
 345                end if;
 346 
 347                Build_Mode := Dynamic_Lib;
 348 
 349             when 'm' =>
 350 
 351                Gen_Map_File := True;
 352 
 353             when 'n' =>
 354 
 355                Build_Import := False;
 356 
 357             when 'l' =>
 358                List_Filename := To_Unbounded_String (Parameter);
 359 
 360             when 'I' =>
 361                Gopts (G) := new String'("-I" & Parameter);
 362                G := G + 1;
 363 
 364             when others =>
 365                raise Invalid_Switch;
 366          end case;
 367       end loop;
 368 
 369       --  Get parameters
 370 
 371       loop
 372          declare
 373             File : constant String := Get_Argument (Do_Expansion => True);
 374          begin
 375             exit when File'Length = 0;
 376             Add_File (File);
 377          end;
 378       end loop;
 379 
 380       --  Get largs parameters
 381 
 382       Goto_Section ("largs");
 383 
 384       loop
 385          case Getopt ("*") is
 386             when ASCII.NUL =>
 387                exit;
 388 
 389             when others =>
 390                Lopts (L) := new String'(Full_Switch);
 391                L := L + 1;
 392          end case;
 393       end loop;
 394 
 395       --  Get bargs parameters
 396 
 397       Goto_Section ("bargs");
 398 
 399       loop
 400          case Getopt ("*") is
 401 
 402             when ASCII.NUL =>
 403                exit;
 404 
 405             when others =>
 406                Bopts (B) := new String'(Full_Switch);
 407                B := B + 1;
 408 
 409          end case;
 410       end loop;
 411 
 412       --  if list filename has been specified, parse it
 413 
 414       if List_Filename /= Null_Unbounded_String then
 415          Add_Files_From_List (To_String (List_Filename));
 416       end if;
 417 
 418       --  Check if the set of parameters are compatible
 419 
 420       if Build_Mode = Nil and then not Help and then not MDLL.Verbose then
 421          Raise_Exception (Syntax_Error'Identity, "nothing to do.");
 422       end if;
 423 
 424       --  -n option but no file specified
 425 
 426       if not Build_Import
 427         and then A = Afiles'First
 428         and then O = Ofiles'First
 429       then
 430          Raise_Exception
 431            (Syntax_Error'Identity,
 432             "-n specified but there are no objects to build the library.");
 433       end if;
 434 
 435       --  Check if we want to build an import library (option -e and
 436       --  no file specified)
 437 
 438       if Build_Mode = Dynamic_Lib
 439         and then A = Afiles'First
 440         and then O = Ofiles'First
 441       then
 442          Build_Mode := Import_Lib;
 443       end if;
 444 
 445       --  If map file is to be generated, add linker option here
 446 
 447       if Gen_Map_File and then Build_Mode = Import_Lib then
 448          Raise_Exception
 449            (Syntax_Error'Identity,
 450             "Can't generate a map file for an import library.");
 451       end if;
 452 
 453       --  Check if only a dynamic library must be built
 454 
 455       if Build_Mode = Dynamic_Lib and then not Build_Import then
 456          Build_Mode := Dynamic_Lib_Only;
 457       end if;
 458 
 459       if O /= Ofiles'First then
 460          Objects_Files := new Argument_List'(Ofiles (1 .. O - 1));
 461       end if;
 462 
 463       if A /= Afiles'First then
 464          Ali_Files     := new Argument_List'(Afiles (1 .. A - 1));
 465       end if;
 466 
 467       if G /= Gopts'First then
 468          Options       := new Argument_List'(Gopts (1 .. G - 1));
 469       end if;
 470 
 471       if L /= Lopts'First then
 472          Largs_Options := new Argument_List'(Lopts (1 .. L - 1));
 473       end if;
 474 
 475       if B /= Bopts'First then
 476          Bargs_Options := new Argument_List'(Bopts (1 .. B - 1));
 477       end if;
 478 
 479    exception
 480       when Invalid_Switch    =>
 481          Raise_Exception
 482            (Syntax_Error'Identity,
 483             Message => "Invalid Switch " & Full_Switch);
 484 
 485       when Invalid_Parameter =>
 486          Raise_Exception
 487            (Syntax_Error'Identity,
 488             Message => "No parameter for " & Full_Switch);
 489    end Parse_Command_Line;
 490 
 491    -------------------
 492    -- Check_Context --
 493    -------------------
 494 
 495    procedure Check_Context is
 496    begin
 497       Check (To_String (Def_Filename));
 498 
 499       --  Check that each object file specified exists and raise exception
 500       --  Context_Error if it does not.
 501 
 502       for F in Objects_Files'Range loop
 503          Check (Objects_Files (F).all);
 504       end loop;
 505    end Check_Context;
 506 
 507    procedure Check_Version_And_Help is new Check_Version_And_Help_G (Syntax);
 508 
 509 --  Start of processing for Gnatdll
 510 
 511 begin
 512    Check_Version_And_Help ("GNATDLL", "1997");
 513 
 514    if Ada.Command_Line.Argument_Count = 0 then
 515       Help := True;
 516    else
 517       Parse_Command_Line;
 518    end if;
 519 
 520    if MDLL.Verbose or else Help then
 521       New_Line;
 522       Put_Line ("GNATDLL " & Version & " - Dynamic Libraries Builder");
 523       New_Line;
 524    end if;
 525 
 526    MDLL.Utl.Locate;
 527 
 528    if Help
 529      or else (MDLL.Verbose and then Ada.Command_Line.Argument_Count = 1)
 530    then
 531       Syntax;
 532    else
 533       Check_Context;
 534 
 535       case Build_Mode is
 536          when Import_Lib =>
 537             MDLL.Build_Import_Library
 538               (To_String (Lib_Filename),
 539                To_String (Def_Filename));
 540 
 541          when Dynamic_Lib =>
 542             MDLL.Build_Dynamic_Library
 543               (Objects_Files.all,
 544                Ali_Files.all,
 545                Options.all,
 546                Bargs_Options.all,
 547                Largs_Options.all,
 548                To_String (Lib_Filename),
 549                To_String (Def_Filename),
 550                To_String (DLL_Address),
 551                Build_Import => True,
 552                Relocatable  => Must_Build_Relocatable,
 553                Map_File     => Gen_Map_File);
 554 
 555          when Dynamic_Lib_Only =>
 556             MDLL.Build_Dynamic_Library
 557               (Objects_Files.all,
 558                Ali_Files.all,
 559                Options.all,
 560                Bargs_Options.all,
 561                Largs_Options.all,
 562                To_String (Lib_Filename),
 563                To_String (Def_Filename),
 564                To_String (DLL_Address),
 565                Build_Import => False,
 566                Relocatable  => Must_Build_Relocatable,
 567                Map_File     => Gen_Map_File);
 568 
 569          when Nil =>
 570             null;
 571       end case;
 572    end if;
 573 
 574    Set_Exit_Status (Success);
 575 
 576 exception
 577    when SE : Syntax_Error =>
 578       Put_Line ("Syntax error : " & Exception_Message (SE));
 579       New_Line;
 580       Syntax;
 581       Set_Exit_Status (Failure);
 582 
 583    when E : MDLL.Tools_Error | Context_Error =>
 584       Put_Line (Exception_Message (E));
 585       Set_Exit_Status (Failure);
 586 
 587    when others =>
 588       Put_Line ("gnatdll: INTERNAL ERROR. Please report");
 589       Set_Exit_Status (Failure);
 590 end Gnatdll;