OSDN Git Service

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