Subversion Repositories DevTools

Rev

Rev 307 | Rev 343 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
#
2
# Module name   : CSHARP
3
# Module type   : Makefile system
4
# Compiler(s)   : ANSI C
5
# Environment(s): WIN32
6
#
7
# Description:
8
#       CSHARP for Windows
9
#
10
#............................................................................#
11
use strict;
12
use warnings;
13
use MakeEntry;
14
 
15
#
16
#   Global data
17
#
18
my %resource_files;
19
my $pdb_none;
20
 
255 dpurdie 21
my $toolset_info;
22
my $toolset_version = '1.1';
23
my %ToolsetVersion =
24
    (
25
    '1.1' => { 'def'      => 'CSHARP.DEF',              # Def file to use
26
               'pragma'   => 0,                         # True: Compiler supports #pragma
27
               'warnings' => '',                        # Comma sep list of warnings to ignore
28
             },
29
 
30
    '2.0' => { 'def'      => 'CSHARP2005.DEF',
31
               'pragma'   => 1,
32
               'warnings' => '1668',
33
             },
291 dpurdie 34
 
35
    '3.5' => { 'def'      => 'CSHARP2008.DEF',
36
               'pragma'   => 1,
37
               'warnings' => '1668',
38
             },
255 dpurdie 39
    );
40
 
41
 
227 dpurdie 42
##############################################################################
43
#   ToolsetInit()
44
#       Runtime initialisation
45
#
46
##############################################################################
47
 
48
ToolsetInit();
49
 
50
sub ToolsetInit
51
{
52
 
53
    #.. Parse arguments (Toolset arguments)
54
    #
55
    Debug( "csharp(@::ScmToolsetArgs)" );
56
 
57
    foreach $_ ( @::ScmToolsetArgs ) {
58
        if (/^--Version=(.*)/) {                # MS SDK Version
59
            $toolset_version = $1;
60
 
61
        } else {
62
            Message( "csharp toolset: unknown option $_ -- ignored\n" );
63
        }
64
    }
65
 
66
    #.. Parse arguments (platform arguments)
67
    #
68
    Debug( "csharp(@::ScmPlatformArgs)" );
69
 
70
    foreach $_ ( @::ScmPlatformArgs ) {
71
        if (/^--product=(.*)/) {                # GBE product
72
 
73
        } elsif (/^--Version=(.*)/) {           # MS SDK Version
74
            $toolset_version = $1;
75
 
76
        } else {
77
            Message( "csharp toolset: unknown platform argument $_ -- ignored\n" );
78
        }
79
    }
80
 
255 dpurdie 81
    #.. Validate SDK version
82
    #   Currently supported versions are described in a HASH
227 dpurdie 83
    #
255 dpurdie 84
    $toolset_info = $ToolsetVersion{$toolset_version};
85
    Error( "csharp toolset: Unknown version: $toolset_version" ) unless ( defined $toolset_info );
86
 
227 dpurdie 87
    #.. Standard.rul requirements
88
    #
89
    $::s    = undef;
90
    $::o    = '';
91
    $::a    = 'netmodule';
92
    $::so   = 'dll';
93
    $::exe  = '.exe';
94
 
95
    #.. Toolset configuration
96
    #
97
    $::ScmToolsetVersion = "1.0.0";             # our version
98
    $::ScmToolsetGenerate = 0;                  # generate optional
99
    $::ScmToolsetProgDependancies = 0;          # handle Prog dependancies myself
100
    %::ScmToolsetProgSource = (                 # handle these files directly
101
            '.cs'       => '',                  # Will be flagged as "CSRCS"
102
            '.resx'     => '--Resource=',       # Will be passed with prefix
103
            '.dtd'      => '--Dtd=',            # Will be passed with prefix
104
            );
105
 
106
    #.. define Visual C/C+ environment
107
    Init( "csharp" );
255 dpurdie 108
    ToolsetDefines( $toolset_info->{'def'} );
227 dpurdie 109
    ToolsetRules( "csharp.rul" );
110
#    ToolsetRules( "standard.rul" );
111
 
112
 
113
    #.. Extend the CompilerOption directive
114
    #   Create a standard data structure
115
    #   This is a hash of hashes
116
    #       The first hash is keyed by CompileOption keyword
117
    #       The second hash contains pairs of values to set or remove
118
    #
119
    %::ScmToolsetCompilerOptions =
120
    (
121
        #
122
        #   Control the thread model to use
123
        #   This will affect the compiler options and the linker options
124
        #
125
        'noaddlibs'          => { 'ADDLINKLIBS' , undef },      # Don't add link libs
126
        'addlibs'            => { 'ADDLINKLIBS' , '1' },        # default
127
        'nowarn='            => { 'NOWARNLIST'  ,\&NoWarns },   # Suppress warnings
128
        'nopdb'              => { 'PDB_NONE', 1 },              # Disable all PDB files
129
        'pdb'                => { 'PDB_NONE', undef },          # Enable PDB files: Default
130
        'subsystem:windows'  => { 'LDSUBSYSTEM' , 'winexe' },
131
        'subsystem:console'  => { 'LDSUBSYSTEM' , 'exe' },
132
    );
133
 
134
    #
135
    #   Set default options
136
    #
137
    $::ScmCompilerOpts{'ADDLINKLIBS'} = '1';
255 dpurdie 138
    $::ScmCompilerOpts{'NOWARNLIST'} = $toolset_info->{'warnings'};
227 dpurdie 139
    $::ScmCompilerOpts{'LDSUBSYSTEM'} = 'winexe';
140
}
141
 
142
 
143
#-------------------------------------------------------------------------------
144
# Function        : NoWarns
145
#
146
# Description     : ScmToolsetCompilerOptions  extension function
255 dpurdie 147
#                   Accumulates the NoWarn options as a comma seperated list
227 dpurdie 148
#
149
# Inputs          : $key        - Name of the Option
150
#                   $value      - Option Value. Comma sep list of numbers
255 dpurdie 151
#                   $ukey       - User key (within $::ScmCompilerOpts)
227 dpurdie 152
#
153
# Returns         : New sting to save
154
#
155
sub NoWarns
156
{
255 dpurdie 157
    my ($key, $value, $ukey) = @_;
158
    my @NoWarnList =  split (',', $::ScmCompilerOpts{$ukey});
261 dpurdie 159
    UniquePush ( \@NoWarnList, split (',', $value) );
227 dpurdie 160
    return join ',', @NoWarnList;
161
}
162
 
163
##############################################################################
164
#   ToolsetPreprocess()
165
#       Process collected data before the makefile is generated
166
#       This, optional, routine is called from within MakefileGenerate()
167
#       It allows the toolset to massage any of the collected data before
168
#       the makefile is created
169
#
170
##############################################################################
171
sub ToolsetPreprocess
172
{
173
    #
174
    #   Extract the current state of PDB_NONE
175
    #   Are PDB files to be constructed.
176
    #
177
    $pdb_none = $::ScmCompilerOpts{'PDB_NONE'};
178
}
179
 
180
##############################################################################
181
#   ToolsetPostprocess
182
#       Process collected data as the makefile is generated
183
#       This, optional, routine is called from within MakefileGenerate()
184
#       It allows the toolset to massage any of the collected data before
185
#       the makefile is finally closed created
186
#
187
##############################################################################
188
 
189
sub ToolsetPostprocess
190
{
191
    #
192
    #   Generate Recipes to create Resource Files
193
    #   This is done outside of the Prog and Lib routines
194
    #   so that they can be agregated
195
    #
196
    #   Note: don't make the makefile a dependant as changes to the
197
    #         makefile won't affect the file
198
    #
199
    for my $resource ( sort keys %resource_files )
200
    {
201
        my $src  = $resource_files{$resource}{src};
202
        my $root = $resource_files{$resource}{root};
203
 
204
        my $me = MakeEntry::New (*MAKEFILE, $resource );
205
        $me->AddComment ("Build Resource: $root" );
261 dpurdie 206
#        $me->AddDependancy ( '$(SCM_MAKEFILE)' );
227 dpurdie 207
        $me->AddDependancy ( $src );
208
        $me->AddRecipe ( '$(RESGEN)' );
209
        $me->Print();
210
 
211
        #
212
        #   Add to the deletion list
213
        #
214
        ToolsetGenerate( $resource );
215
    }
216
}
217
 
218
#-------------------------------------------------------------------------------
219
# Function        : Toolset_genres
220
#
221
# Description     : Internal function to assist in the creation of a resource
222
#                   In many cases it will create an entry for later processing
223
#
224
# Inputs          : $subdir         - Root of the target directory for the generated
225
#                                     resource file
226
#                   $src            - Path to the sorce resource file
227
#
228
# Returns         : Path to the generated resource file
229
#                   This will be FQN named file
230
#                   Path to the associated .CS file
231
#
232
# Notes           : Create and maintain the %resource_files hash
233
#                   Key is the path to the compiled file
234
#                   Values are:
235
#                       {src}   - Path to the source file
236
#                       {root}  - Basic file name (Display Purposes Only)
237
#
238
#                   Need to create a '.resource' file with a FQN name
239
#                   This is not that simple. Need to
240
#                       1) Extract the (optional) ThisName from the .resx file
241
#                          If not specified then the ThisName is the rootfilename
242
#                          without any .as[pca]x extension.
243
#                       2) Extract the namespace from the associated .cs file
244
#                       3) FQN = NameSpace.ThisName
245
#
246
#
247
sub Toolset_genres
248
{
249
    my ($subdir, $src ) = @_;
250
 
251
    #
252
    #   Ensure that the .cs file also exists
253
    #
254
    (my $csfile = $src) =~ s~\.resx$~.cs~;
255
    Error ("csharp toolset: Resx File without a .cs file",
256
           "File: $src") unless ( -f $src );
257
 
258
    #
259
    #   Scan the .resx file looking for the ThisName element
260
    #   A very simple and crude parser
261
    #
262
    my $ThisName;
263
    open ( SCAN, '<', $src ) || Error ("Cannot open file for reading: $!", "File: $src" );
264
    while ( <SCAN> )
265
    {
266
        if ( m~\<data name=\"\$this\.Name\"\>~ )
267
        {
268
            # Next line will contain the needed data item
269
            my $element = <SCAN>;
270
            $element =~ m~\<.+\>(.+)\</.+\>~;
271
            $ThisName = $1;
272
            Error ("csharp toolset: Resx parsing: Bad this.Name", "File: $src") unless $ThisName;
273
            $ThisName =~ s~\s+~~g;
274
            last;
275
        }
276
    }
277
    close SCAN;
278
 
279
    #
280
    #   Name not found
281
    #   Use a default. Filename with any .aspx, .asax, .ascx removed
282
    #
283
    unless ( $ThisName )
284
    {
285
        $ThisName = StripDirExt($src);
286
        $ThisName =~ s~\.as[pac]x~~i;
287
    }
288
 
289
    #
290
    #   Scan the.cs file looking for the namespace
291
    #   A very simple and crude parser
292
    #
293
    my $NameSpace;
294
    open ( SCAN, '<', $csfile ) || Error ("Cannot open file for reading: $!", "File: $csfile" );
295
    while ( <SCAN> )
296
    {
297
        if ( m~namespace\s+(\S+)~ )
298
        {
299
            $NameSpace = $1;
300
            last;
301
        }
302
    }
303
    close SCAN;
304
    Error ("csharp toolset: Resx parsing: NameSpace not found", "File: $csfile") unless $NameSpace;
305
 
306
    #
307
    #   Need to create an output file name that is a function of the FQN
308
    #
309
    my $root = "$NameSpace.$ThisName.resources";
310
    my $resource = $subdir . '/' . $root;
311
 
312
    $resource_files{$resource}{src} = $src;
313
    $resource_files{$resource}{root} = $root;
314
 
315
    return $resource, $csfile;
316
}
317
 
318
 
319
#-------------------------------------------------------------------------------
320
# Function        : Toolset_gensnk
321
#
322
# Description     : Function to create a wrapper file for the processing
323
#                   of a StrongNameKey file
324
#
325
#                   Create only one wrapper per SNK file
326
#
327
# Inputs          : $name       - Name of component
328
#                   $snk        - Path to the SNK file
329
#
330
# Returns         : Path to the wrapper file
331
#
332
my %snk_data;
333
sub Toolset_gensnk
334
{
335
    my ($name, $snk ) = @_;
336
    my $file = StripDirExt( $snk );
337
 
338
    #
339
    #   Only create the file once
340
    #   Otherwise we will get nasty make messages
341
    #
342
 
343
    if ( exists $snk_data{$snk} )
344
    {
345
        return $snk_data{$snk}{output};
346
    }
347
 
348
    #
349
    #   Determine the target name
350
    #   Create the source file in the currentt directory
351
    #   If we build it in the OBJ directory we get two files
352
    #
353
    my $snk_file = '$(OBJDIR)/' . "Jats_${file}.cs";
354
    $snk_data{$snk}{output} = $snk_file;
355
    ToolsetGenerate( $snk_file );
356
 
357
    #
358
    #   Determine the Tag Name
359
    #   Used to conatin information in the makefile
360
    #
361
    my $tag = "${file}_snk";
362
 
363
    #
364
    #   Create Rules and Recipes to create the SNK wrapper file
365
    #
366
    my $me = MakeEntry::New (*MAKEFILE, $snk_file );
367
    $me->AddComment ("Build Strong Name Key File Wrapper: $snk" );
368
    $me->AddDependancy ( $snk );
261 dpurdie 369
    $me->AddDependancy ( '$(SCM_MAKEFILE)' );
227 dpurdie 370
    $me->AddRecipe ( '$(call GenSnkWrapper,' . $tag .  ')' );
371
    $me->Print();
372
 
373
    #
374
    #   Create the data t be placed into the wrapper file
375
    #
376
    my ($io) = ToolsetPrinter::New();
377
 
378
    my $ms_snk = $snk;
379
 
380
    $io->Label( "SNK Wrapper file content", $tag );    # label
381
    $io->SetTag( $tag );                                # macro tag
255 dpurdie 382
    $io->Cmd( '// This is JATS GENERATED FILE' );
383
    $io->Cmd( '//    Do not edit' );
384
    $io->Cmd( '//    Do not version control' );
385
 
227 dpurdie 386
    $io->Cmd( 'using System.Reflection;' );
387
    $io->Cmd( 'using System.Runtime.CompilerServices;' );
388
 
389
    $io->Cmd( '//' );
390
    $io->Cmd( '// In order to sign your assembly you must specify a key to use. Refer to the' );
391
    $io->Cmd( '// Microsoft .NET Framework documentation for more information on assembly signing.' );
392
    $io->Cmd( '//' );
393
    $io->Cmd( '// Use the attributes below to control which key is used for signing.' );
394
    $io->Cmd( '//' );
395
    $io->Cmd( '// Notes:' );
396
    $io->Cmd( '//   (*) If no key is specified, the assembly is not signed.' );
397
    $io->Cmd( '//   (*) KeyName refers to a key that has been installed in the Crypto Service' );
398
    $io->Cmd( '//       Provider (CSP) on your machine. KeyFile refers to a file which contains' );
399
    $io->Cmd( '//       a key.' );
400
    $io->Cmd( '//   (*) If the KeyFile and the KeyName values are both specified, the' );
401
    $io->Cmd( '//       following processing occurs:' );
402
    $io->Cmd( '//       (1) If the KeyName can be found in the CSP, that key is used.' );
403
    $io->Cmd( '//       (2) If the KeyName does not exist and the KeyFile does exist, the key' );
404
    $io->Cmd( '//           in the KeyFile is installed into the CSP and used.' );
405
    $io->Cmd( '//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.' );
406
    $io->Cmd( '//       When specifying the KeyFile, the location of the KeyFile should be' );
407
    $io->Cmd( '//       relative to the project output directory which is' );
408
    $io->Cmd( '//       %Project Directory%\obj\<configuration>. For example, if your KeyFile is' );
409
    $io->Cmd( '//       located in the project directory, you would specify the AssemblyKeyFile' );
410
    $io->Cmd( '//       attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]' );
411
    $io->Cmd( '//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework' );
412
    $io->Cmd( '//       documentation for more information on this.' );
413
    $io->Cmd( '//' );
414
 
415
    $io->Cmd( '[assembly: AssemblyDelaySign(false)]' );
255 dpurdie 416
    $io->Cmd( '#pragma warning disable 1699' ) if ($toolset_info->{'pragma'});
227 dpurdie 417
    $io->Cmd( '[assembly: AssemblyKeyFile(@"'. $snk  .'")]' );
255 dpurdie 418
    $io->Cmd( '#pragma warning restore 1699' ) if ($toolset_info->{'pragma'});
227 dpurdie 419
    $io->Cmd( '[assembly: AssemblyKeyName("")]' );
420
    $io->Newline();
421
 
422
    #
423
    #   Return the path to where the file will be created
424
    #
425
    return $snk_file;
426
}
427
 
428
 
429
###############################################################################
430
#   ToolsetLD( $name, \@args, \@objs, \@libraries )
431
#       This subroutine takes the user options and builds the rules
432
#       required to link the program 'name'.
433
#
434
#   Arguments:
435
#       $name       - Name of the target program
436
#       $pArgs      - Ref to an array of argumennts
437
#       $pObjs      - Ref to an array of object files
438
#       $pLibs      - Ref to an array of libraries
439
#
440
#   Output:
441
#       Makefile recipes to create the Program
442
#
443
#   Notes:
444
#       This Program Builder will handle its own dependancies
445
#       It will also create rules and recipes to construct various
446
#       parts directly fromm source
447
#
448
#   Options:
449
#       --Resource=file.resx
450
#       --Dtd=file.dtd
451
#       --Icon=file
452
#       --Entry=xxxxx                   # Entry point
453
#       --Console                       # Console app
454
#       --Windows                       # Windows app (default)
455
#       --DLL                           # As a DLL (No P|D)
456
#       --Doc
457
#       --NoPDB
458
#       CSharpSourceFile
459
#
460
#
461
###############################################################################
462
 
463
sub ToolsetLD
464
{
465
    my ( $name, $pArgs, $pObjs, $pLibs ) = @_;
466
    my ( @reslist, @resources, @csource, @dtd );
467
    my $no_pdb = $pdb_none;
468
    my $entry;
469
    my $noaddlibs;
470
    my $icon;
471
    my $docFile;
472
    my ($base, $root, $full );
473
    my $link_target = $::ScmCompilerOpts{'LDSUBSYSTEM'};
474
    my $snk;
475
    my $is_a_dll;
476
 
477
    #.. Parse arguments
478
    #
479
    foreach ( @$pArgs ) {
480
        if (/^--Resource=(.+)/) {               # Resource definition
481
            push @reslist, MakeSrcResolve($1);
482
 
483
        } elsif (/^--Dtd=(.+)/) {               # dtd definition
484
            push @dtd, MakeSrcResolve($1);
485
 
486
        } elsif (/^--Icon=(.+)/) {
487
            Error ("csharp LD: Only one Icon file allowed") if ( $icon );
488
            $icon = MakeSrcResolve($1);
489
 
490
        } elsif (/^--StrongNameKey=(.+)/) {
491
            Error ("csharp LD: Only one SNK file allowed") if ( $snk );
492
            $snk = MakeSrcResolve($1);
493
 
494
        } elsif (/^--Entry=(.+)/) {
495
            $entry = $1;
496
 
497
        } elsif (/^--Doc/) {
498
            $docFile = 1;
499
 
500
        } elsif (/^--Windows/) {
501
            $link_target = 'winexe';
502
 
503
        } elsif (/^--Console/) {
504
            $link_target = 'exe';
505
 
506
        } elsif (/^--DLL/) {
507
            $is_a_dll = 1;
508
 
509
        } elsif (/^--NoPDB$/) {
510
            $no_pdb = 1;
511
 
512
        } elsif ( !/^-/ ) {
513
            push @csource, MakeSrcResolve($_);
514
 
515
        } else {
516
            Message( "csharp LD: unknown option $_ -- ignored\n" );
517
 
518
        }
519
    }
520
 
521
    #
522
    #   Determine the target output name
523
    #
524
    $base = $name;
525
    $root = "\$(BINDIR)/$base";
526
    $full = $root . $::exe;
527
    $docFile = "$root.xml" if ( $docFile );
528
 
529
    #
530
    #   Special case for DLLs that need to be created without a D or P mangled
531
    #   into the name. Create them as Progs just to fool the system
532
    #   Used when creating specialised web services
533
    #
534
    if ( $is_a_dll )
535
    {
536
        #
537
        #   Create a phony target
538
        #   The EXE name is not actually created, but the EXE target needs to be retained
539
        #
540
        my $exe_name = $root . $::exe;
541
        my $dll_name = $root . '.' . $::so;
542
        $full = $dll_name;
543
        $link_target = 'library';
544
 
545
        my $me = MakeEntry::New (*MAKEFILE, $exe_name, '--Phony' );
546
        $me->AddComment ("Build Program: $name as a DLL" );
547
        $me->AddDependancy ( $dll_name );
548
        $me->Print();
255 dpurdie 549
 
550
        #
551
        #   Need to specifically clean this up, since we have fiddled with the
552
        #   name of the generated file
553
        #
554
        ToolsetGenerate( $dll_name );
227 dpurdie 555
    }
556
 
557
    #
558
    #   Create Rules and Recipes to convert the .resx files to .resource files
559
    #
560
    foreach my $res ( @reslist )
561
    {
562
        my ($res, $cs) = Toolset_genres ('$(OBJDIR)', $res );
563
 
564
        UniquePush ( \@resources, $res );
565
        UniquePush ( \@csource, $cs );
566
    }
567
 
568
    #
569
    #   Create Rules and Recipes to provide Assembly instructions
570
    #   for the creation of a StrongNameKey
571
    #
572
    if ( $snk )
573
    {
574
        UniquePush ( \@csource, Toolset_gensnk ($name, $snk ) );
575
    }
576
 
335 dpurdie 577
    my ($io) = ToolsetPrinter::New();
578
    my $dep = $io->SetLdTarget( $name );
579
 
227 dpurdie 580
    #
581
    #   Create Rules and Recipes to create the Program
582
    #   This will be a combination of source, libraries and resources
583
    #
584
    my $me = MakeEntry::New (*MAKEFILE, $full );
585
    $me->AddComment ("Build Program: $name" );
586
    $me->AddName    ( $docFile ) if ( $docFile );
335 dpurdie 587
    $me->AddDependancy ( $dep );
261 dpurdie 588
    $me->AddDependancy ( '$(SCM_MAKEFILE)' );
227 dpurdie 589
    $me->AddDependancy ( @resources );
590
    $me->AddDependancy ( @csource );
591
    $me->AddDependancy ( @dtd );
592
    $me->AddDependancy ( $icon );
593
    $me->AddRecipe ( '$(CSC)' );
594
    $me->Print();
595
 
596
 
597
    #
598
    #.. Compiler command file
599
    #       Now piece together a variable $(name_ld) which ends up in
600
    #       the command file linking the application.
601
    #
602
    $io->Label( "Linker commands", $name );     # label
603
    $io->SetTag( "${name}_ld" );                # macro tag
604
 
605
    $io->Label( "Linker Command File", $name ); # label
606
 
607
    #
608
    #   Basic options
609
    #
610
    $io->Cmd( "/target:$link_target" );
611
    $io->Cmd("/doc:$docFile") if ( $docFile ) ;
612
    $io->Cmd( "/win32icon:\$(subst /,\\\\,$icon)" ) if $icon;
613
    $io->Cmd( "/main:$entry" ) if $entry;
614
 
615
    #
616
    #   Add in the Resource Files
617
    #          the source files
618
    #          the libraries
619
    #
620
    $io->Cmd( "/res:\$(subst /,\\\\,$_)" ) foreach @resources;
621
    $io->Cmd( "/res:\$(subst /,\\\\,$_)" ) foreach @dtd;
622
    $io->Cmd( "\$(subst /,\\\\,$_)" ) foreach @csource;
623
    $io->LibList( $name, $pLibs, \&ToolsetLibRecipe );
335 dpurdie 624
    $io->Newline();
227 dpurdie 625
 
626
 
335 dpurdie 627
    #.. Dependency link,
628
    #   Create a library dependency file
629
    #       Create command file to build applicaton dependency list
630
    #       from the list of dependent libraries
227 dpurdie 631
    #
335 dpurdie 632
    #       Create makefile directives to include the dependency
633
    #       list into the makefile.
227 dpurdie 634
    #
335 dpurdie 635
    $io->DepRules( $pLibs, \&ToolsetLibRecipe, $full );
636
    $io->LDDEPEND();
227 dpurdie 637
 
638
    #
639
    #   Files to clean up
640
    #
641
    ToolsetGenerate( "$root.ld" );
642
    ToolsetGenerate( "$root.pdb" );
643
    ToolsetGenerate( $docFile ) if $docFile;
644
 
645
 
646
    #.. Package up files that are a part of the program
647
    #
648
    PackageProgAddFiles ( $name, $full );
649
    PackageProgAddFiles ( $name, "$root.pdb", "Class=debug" ) unless ( $no_pdb );
650
    PackageProgAddFiles ( $name, $docFile, "Class=map" ) if ( $docFile );
651
}
652
 
653
###############################################################################
289 dpurdie 654
#   ToolsetSHLD( $name, \@args, \@objs, \@libraries, $ver )
227 dpurdie 655
#       This subroutine takes the user options and builds the rules
656
#       required to link the program 'name'.
657
#
658
#   Arguments:
659
#       $name       - Name of the target program
660
#       $pArgs      - Ref to an array of argumennts
661
#       $pObjs      - Ref to an array of object files
662
#       $pLibs      - Ref to an array of libraries
289 dpurdie 663
#       $ver        - Library Version string
227 dpurdie 664
#
665
#   Output:
666
#       Makefile recipes to create the DLL
667
#       Will create both versioned and unversioned DLLs
668
#
669
#   Notes:
670
#       This Library Builder will handle its own dependancies
671
#       It will also create rules and recipes to construct various
672
#       parts directly from source
673
#
674
#       This is SO close to the ToolsetLD function that its not funny
675
#
676
#   Options:
677
#       --Resource=file.resx
678
#       --Icon=file
679
#       --StrongNameKey=file
680
#       --Doc
681
#       --NoPDB
682
#       CSharpSourceFile
683
#
684
#
685
###############################################################################
686
sub ToolsetSHLD
687
{
688
    #
689
    #   Note: Use globals to kill warnings from internal sub
690
    #         Use _ prefix so that they don't get saved in Makefile_x.cfg
691
    #         Init as they are global
692
    #
289 dpurdie 693
    our ( $_name, $_pArgs, $_pObjs, $_pLibs, $_ver ) = @_;
227 dpurdie 694
    our ( @_reslist, @_resources, @_csource, @_dtd ) = ();
695
    our $_no_pdb = $pdb_none;
696
    our $_noaddlibs = 0;
697
    our $_icon = undef;
698
    our $_docFile = undef;
699
    our $_snk = undef;
700
 
701
    #.. Parse arguments
702
    #
703
    foreach ( @$_pArgs ) {
704
        if (/^--Resource=(.+)/) {               # Resource definition
705
            push @_reslist, MakeSrcResolve($1);
706
 
707
        } elsif (/^--Dtd=(.+)/) {               # dtd definition
708
            push @_dtd, MakeSrcResolve($1);
709
 
710
        } elsif (/^--Icon=(.+)/) {
711
            Error ("csharp SHLD: Only one Icon file allowed") if ( $_icon );
712
            $_icon = MakeSrcResolve($1);
713
 
714
        } elsif (/^--StrongNameKey=(.+)/) {
715
            Error ("csharp SHLD: Only one SNK file allowed") if ( $_snk );
716
            $_snk = MakeSrcResolve($1);
717
 
718
        } elsif (/^--Doc/) {
719
            $_docFile = 1;
720
 
721
        } elsif (/^--NoPDB$/) {
722
            $_no_pdb = 1;
723
 
724
        } elsif ( !/^-/ ) {
725
            push @_csource, MakeSrcResolve($_);
726
 
727
        } else {
728
            Message( "csharp SHLD: unknown option $_ -- ignored\n" );
729
 
730
        }
731
    }
732
 
733
    #
734
    #   Create Rules and Recipes to convert the .resx files to .resource files
735
    #
736
    foreach my $res ( @_reslist )
737
    {
738
        my ($res, $cs) = Toolset_genres ('$(OBJDIR)/' . $_name, $res );
739
 
740
        UniquePush ( \@_resources, $res );
741
        UniquePush ( \@_csource, $cs );
742
    }
743
 
744
    #
745
    #   Create Rules and Recipes to provide Assembly instructions
746
    #   for the creation of a StrongNameKey
747
    #
748
    if ( $_snk )
749
    {
750
        UniquePush ( \@_csource, Toolset_gensnk ($_name, $_snk ) );
751
    }
752
 
753
    #
754
    #   Build Rules
755
    #   $1  - Base Name
756
    #   $2  - Name of the output DLL
757
    #
758
    sub BuildSHLD
759
    {
760
        my ($name, $lib ) = @_;
761
        my ($root, $full, $link_target);
762
 
763
        #
764
        #   Determine the target output name
765
        #
766
        $root = "\$(LIBDIR)/$lib";
767
        $full = "$root.$::so";
768
        $link_target = "library";
769
        $_docFile = "$full.xml" if ($_docFile);
770
 
335 dpurdie 771
        my ($io) = ToolsetPrinter::New();
772
        my $dep = $io->SetShldTarget( $lib );
773
 
227 dpurdie 774
        #
775
        #   Create Rules and Recipes to create the Program
776
        #   This will be a combination of source, libraries and resources
777
        #
778
        my $me = MakeEntry::New (*MAKEFILE, $full );
779
        $me->AddComment ("Build Shared Library: $name" );
780
        $me->AddName    ( $_docFile ) if ( $_docFile );
261 dpurdie 781
        $me->AddDependancy ( '$(SCM_MAKEFILE)' );
335 dpurdie 782
        $me->AddDependancy ( $dep );
227 dpurdie 783
        $me->AddDependancy ( @_resources );
784
        $me->AddDependancy ( @_csource );
785
        $me->AddDependancy ( @_dtd );
786
        $me->AddDependancy ( $_icon );
787
        $me->AddRecipe ( '$(CSC)' );
788
        $me->Print();
789
 
790
 
791
        #
792
        #.. Compiler command file
793
        #       Now piece together a variable $(name_ld) which ends up in
794
        #       the command file linking the application.
795
        #
796
 
797
        $io->Label( "Linker commands", $name );     # label
798
        $io->SetTag( "${lib}_ld" );                 # macro tag
799
 
800
        $io->Label( "Linker Command File", $lib ); # label
801
 
802
        #
803
        #   Basic options
804
        #
805
        $io->Cmd( "/target:$link_target" );
806
        $io->Cmd( "/doc:$_docFile")                    if ( $_docFile ) ;
807
        $io->Cmd( "/win32icon:\$(subst /,\\\\,$_icon)" ) if $_icon;
808
 
809
        #
810
        #   Add in the Resource Files
811
        #          the source files
812
        #          the libraries
813
        #
814
        $io->Cmd( "/res:\$(subst /,\\\\,$_)" ) foreach @_resources;
815
        $io->Cmd( "/res:\$(subst /,\\\\,$_)" ) foreach @_dtd;
816
        $io->Cmd( "\$(subst /,\\\\,$_)" ) foreach @_csource;
817
        $io->LibList( $name, $_pLibs, \&ToolsetLibRecipe );
335 dpurdie 818
        $io->Newline();
227 dpurdie 819
 
335 dpurdie 820
        #.. Dependency link,
821
        #   Create a library dependency file
822
        #       Create command file to build applicaton dependency list
823
        #       from the list of dependent libraries
227 dpurdie 824
        #
335 dpurdie 825
        #       Create makefile directives to include the dependency
826
        #       list into the makefile.
227 dpurdie 827
        #
335 dpurdie 828
        $io->DepRules( $_pLibs, \&ToolsetLibRecipe, $full );
829
        $io->SHLDDEPEND( $name, $lib  );
227 dpurdie 830
 
831
        #
832
        #   Files to clean up
833
        #
834
        ToolsetGenerate( "$root.ld" );
835
        ToolsetGenerate( "$root.pdb" );
836
        ToolsetGenerate( $_docFile ) if $_docFile;
837
 
838
 
839
        #.. Package up files that are a part of the Library
840
        #
841
        PackageShlibAddFiles ( $name, $full );
842
        PackageShlibAddFiles ( $name, "$root.pdb", "Class=debug" ) unless ( $_no_pdb );
843
        PackageShlibAddFiles ( $name, $_docFile  , "Class=map" ) if ( $_docFile );
844
 
845
        #
846
        #   Return the full name of the created DLL.
847
        #
848
        return $full;
849
    }
850
 
851
    #
852
    #   Generate DLLs
853
    #
854
    #       a) Unversioned DLL  $_name$(GBE_TYPE).dll
855
    #       b) Versioned DLL    $_name$(GBE_TYPE).xx.xx.xx.dll
856
    #
289 dpurdie 857
    my $fver   = BuildSHLD( "$_name", "$_name\$(GBE_TYPE).$_ver" );
227 dpurdie 858
    my $funver = BuildSHLD( "$_name", "$_name\$(GBE_TYPE)" );
859
 
860
    #
861
    #   Create a dependancy between the version and unversioned DLLs
862
    #
863
    my $me = MakeEntry::New (*MAKEFILE, $fver );
864
    $me->AddComment ("Link Version and Unversioned Images: $_name" );
865
    $me->AddDependancy ( $funver );
866
    $me->Print();
867
 
868
}
869
 
870
########################################################################
871
#
872
#   Generate a linker/depend library recipe.  This is a helper function
873
#   used within this toolset.
874
#
875
#   Arguments:
876
#       $io         I/O stream
877
#
878
#       $target     Name of the target
879
#
880
#       $lib        Library specification
881
#
882
#       $dp         If building a depend list, the full target name.
883
#
884
########################################################################
885
 
886
sub ToolsetLibRecipe
887
{
888
    my ($io, $target, $lib, $dp) = @_;
889
 
890
    if ( !defined($dp) ) {                      # linker
891
        $io->Cmd( "/reference:\$(subst /,\\\\,\$(strip $lib)).$::so" );
892
 
893
    } else {                                    # depend
255 dpurdie 894
        $io->Cmd( "$dp:\t@(vglob2,$lib.$::so,CS_LIB)" );
227 dpurdie 895
    }
896
}
897
 
898
########################################################################
899
#
900
#   Generate a project from the provided project solution file
901
#   This is aimed at .NET work
902
#
903
#   Arguments   : $name             - Base name of the project
904
#                 $solution         - Path to the solutionn file
905
#                 $pArgs            - Project specific options
906
#
907
########################################################################
908
 
909
my $project_defines_done = 0;
910
sub ToolsetPROJECT
911
{
912
    my( $name, $solution ,$pArgs ) = @_;
913
    my $buildcmd = 'devenv =DSW= /build =TYPE= /useenv /out =LOG=';
914
    my $cleancmd = 'devenv =DSW= /clean =TYPE= /useenv';
915
 
916
    #
917
    #   Process options
918
    #
919
    foreach ( @$pArgs ) {
920
        Message( "csharp PROJECT: unknown option $_ -- ignored\n" );
921
    }
922
 
923
    my ($io) = ToolsetPrinter::New();
924
 
925
    #
926
    #   Setup toolset pecific difinitions. Once
927
    #
928
    unless( $project_defines_done )
929
    {
930
        $project_defines_done = 1;
931
        $io->PrtLn( "ifeq \"\$(DEBUG)\" \"1\"");
932
        $io->PrtLn( "PROJECT_CMD\t:= DEBUG");
933
        $io->PrtLn( "else");
934
        $io->PrtLn( "PROJECT_CMD\t:= RELEASE");
935
        $io->PrtLn( "endif");
936
        $io->Newline();
937
    }
938
 
939
    #
940
    #   Process the build and clean commands
941
    #       Substitute arguments
942
    #           =TYPE=
943
    #           =LOG=
944
    #           =DSW=
945
    #
946
    $buildcmd =~ s~=TYPE=~\$\(PROJECT_CMD)~g;
947
    $buildcmd =~ s~=LOG=~$name\$(GBE_TYPE).log~g;
948
    $buildcmd =~ s~=DSW=~$solution~g;
949
 
950
    $cleancmd =~ s~=TYPE=~\$\(PROJECT_CMD)~g;
951
    $cleancmd =~ s~=LOG=~$name\$(GBE_TYPE).log~g;
952
    $cleancmd =~ s~=DSW=~$solution~g;
953
 
954
    #
955
    #   Generate the recipe to create the project
956
    #   Use the set_<PLATFORM>.sh file to extend the DLL search path
957
    #
958
    $io->Label( "Build project", $name );
959
    $io->PrtLn( "Project_$name: $solution \$(INTERFACEDIR)/set_$::ScmPlatform.sh" );
960
 
961
    $io->PrtLn( "\t\$(XX_PRE)( \$(rm) -f $name\$(GBE_TYPE).log; \\" );
962
    $io->PrtLn( "\t. \$(INTERFACEDIR)/set_$::ScmPlatform.sh; \\" );
255 dpurdie 963
    $io->PrtLn( "\t\$(show_environment); \\" );
227 dpurdie 964
    $io->PrtLn( "\t$buildcmd; \\" );
965
    $io->PrtLn( "\tret=\$\$?; \\" );
966
    $io->PrtLn( "\t\$(GBE_BIN)/cat $name\$(GBE_TYPE).log; \\" );
967
    $io->PrtLn( "\texit \$\$ret )" );
968
    $io->Newline();
969
 
970
    #
971
    #   Generate the recipe to clean the project
972
    #
973
    $io->Label( "Clean project", $name );
974
    $io->PrtLn( "ProjectClean_$name: $solution" );
975
    $io->PrtLn( "\t-\$(XX_PRE)$cleancmd" );
976
    $io->PrtLn( "\t-\$(XX_PRE)\$(rm) -f $name\$(GBE_TYPE).log" );
977
    $io->Newline();
978
 
979
}
980
 
981
#-------------------------------------------------------------------------------
982
# Function        : ToolsetTESTFRAMEWORK_NUNIT
983
#
984
# Description     : Toolset specfic support for the NUNIT Test FrameWork
985
#                   Accessed with RunTest ('*', --FrameWork=nunit, ... );
986
#
987
#                   Manipulates the pEntry structure to allow JATS to
988
#                   construct a test entry to run Nunit tests
989
#
990
# Inputs          : $pEntry                 - Unit Test Hash
991
#
992
# Returns         : Modified Hash
993
#
994
sub ToolsetTESTFRAMEWORK_NUNIT
995
{
996
    my ($pEntry) = @_;
997
    my $test_dll_name;
998
    my @copy_dlls;
999
    my %copy_dll_flags;
1000
 
1001
    #
1002
    #   Extract base name of DLL under test
1003
    #   Thsi will not have any extension.
1004
    #
1005
    $test_dll_name = $pEntry->{'prog'};
1006
    Error ("Nunit Framework. No TestDLL specified") unless $test_dll_name;
1007
 
1008
    #
1009
    #   Process the FrameWork Options
1010
    #
1011
    foreach  ( @{$pEntry->{'framework_opts'}} )
1012
    {
1013
        if ( m/^--Uses=(.+)/ ) {
1014
            my ($dll, @opts) = split (',', $1 );
1015
            push @copy_dlls, $dll;
1016
            foreach  ( @opts )
1017
            {
1018
                if ( m~^--NonJats~i ) {
1019
                    $copy_dll_flags{$dll}{'NonJats'} = 1;
1020
                } elsif ( m~--Jats~ ) {
1021
                    $copy_dll_flags{$dll}{'NonJats'} = 0;
1022
                } else {
1023
                    Error ("Nunit Framework. Unknown sub option to --Uses: $_");
1024
                }
1025
            }
1026
        } else {
1027
            Error ("Nunit Framework. Unknown option: $_");
1028
        }
1029
    }
1030
 
1031
    #
1032
    #   Locate the Nunit essentials
261 dpurdie 1033
    #       This list may change with each version of nunit
1034
    #       Look for a known file and use its contents
1035
    #       Format:
1036
    #           One file name per line
1037
    #           Line comments only
1038
    #           Comment marker is a #
1039
    #           First one MUST be the executable
227 dpurdie 1040
    #
261 dpurdie 1041
    my @nunit_files;
227 dpurdie 1042
 
261 dpurdie 1043
    my $mfile = 'nunit-jats-manifest.txt';
1044
    my $nunit_file = ToolExtensionProgram ( $mfile );
1045
    Error ("Cannot find Nunit Jats Manifest: $mfile") unless ( $nunit_file );
1046
    open (JM, $nunit_file ) || Error( "Cannot open file: $nunit_file", "Reason: $!" );
1047
    while ( <JM> )
1048
    {
1049
        s~\s+$~~;                   # Remove trailing white space
1050
        s~^\s+~~;                   # Remove Leading whitespace
1051
        next unless ( $_ );         # Skip block lines
1052
        next if ( m~^#~ );          # Skip comments
1053
        Verbose ("Nunit File: $_");
1054
        push @nunit_files, $_;
1055
    }
1056
    close JM;
1057
 
1058
    #
1059
    #   Locate all the required files
1060
    #   The first one will be the console executable
1061
    #
227 dpurdie 1062
    my @nunit_framework;
1063
    foreach my $file ( @nunit_files )
1064
    {
1065
        my $path = ToolExtensionProgram ($file );
1066
        Error ("Cannot locate nunit file: $file") unless ( $path );
1067
        push @nunit_framework, $path;
1068
    }
261 dpurdie 1069
    my $nunit_console = $nunit_framework[0];
1070
    Error ("Nunit console executable not specified") unless ( $nunit_console );
227 dpurdie 1071
 
1072
    #
1073
    #   Locate the test DLL.
1074
    #   This will be created locally within this makefile
1075
    #   It will be a known shared library
1076
    #
1077
    Errror( "TestDLL does not appear to be locally created: $test_dll_name" )
289 dpurdie 1078
        unless ( $::SHLIBS->Get($test_dll_name) );
227 dpurdie 1079
 
1080
    #
1081
    #   Hard bit. Determine the name/path of the DLL under test
1082
    #   It will have been created within this makefile
1083
    #   This is not a physical file.
1084
    #
1085
    $test_dll_name = $test_dll_name . '$(GBE_TYPE).' . $::so;
1086
    my $test_dll = '$(LIBDIR)/' . $test_dll_name;
1087
 
1088
    #
1089
    #   Other hard bit
1090
    #   Locate the other dll's needed by this test
1091
    #   Need to use P in production and D in Debug unless otherwise specified
1092
    #   These might be in:
1093
    #       an external package
1094
    #       within the local directory
1095
    #       the current makefile
1096
    #   ie: We can only determine the location of the files at run-time
1097
    #
1098
    #   The mechanism used is:
1099
    #       Create makefile recipe entries to
1100
    #           Use cmdfile to create a command file with copy command
1101
    #           Execute the command file
1102
    #
1103
    #   Complications include:
1104
    #       The windows shell used does not honour 'set -e', so we need to
1105
    #       detect error conditions ourselves
1106
    #
1107
    my $ofile = "\$(TESTDIR)/$pEntry->{test_name}.cmd";
1108
    push @{$pEntry->{'ShellRecipe'}}, "rm -f $ofile";
1109
 
1110
    my @cmds;
307 dpurdie 1111
    push @cmds, "\$(cmdfile) -wk1W0o$ofile";
227 dpurdie 1112
    foreach my $dll ( @copy_dlls )
1113
    {
1114
        #
1115
        #   Generate in-line commands to locate and copy in the required
1116
        #   DLL's. The 'cmdfile' utility can be used to do this at runtime
1117
        #
1118
        my $dll_name = $dll;
1119
        $dll_name .= '$(GBE_TYPE)' unless ( $copy_dll_flags{$dll}{'NonJats'} );
1120
 
307 dpurdie 1121
        push @cmds, '"cp -f \"@(vglob2,' . "$dll_name.$::so" . ',PATH)\" $(TESTDIR) || exit 98\n"';
227 dpurdie 1122
    }
1123
    push @cmds, "|| exit 99";
1124
    push @{$pEntry->{'ShellRecipe'}}, \@cmds;
1125
    push @{$pEntry->{'ShellRecipe'}}, ". $ofile";
1126
 
1127
 
1128
    #
1129
    #   Add items to the Unit Test Hash
1130
    #       command     - command to execute to run the test program
1131
    #       prog        - test command/script that must be in the test dir
1132
    #       copyprog    - Prog must be copied in
1133
    #       args        - Arguments to the test
1134
    #       copyin      - Array of files to copy into the test directory
1135
    #       copyonce    - Array of files to copy only once
1136
    #       prereq      - Prerequiste conditions
1137
    #       testdir     - Symbolic name of the test directory
1138
    #       ShellRecipe - Recipe Bits to add
1139
    #
1140
    $pEntry->{'command'}  = $nunit_console . ' ' . $test_dll_name;
1141
    unshift @{$pEntry->{args}}, "/xml=$pEntry->{test_name}.xml";
1142
    $pEntry->{'prog'} = $test_dll_name;
1143
    $pEntry->{'copyprog'} = 0;
1144
    push @{$pEntry->{'copyin'}}, $test_dll;
261 dpurdie 1145
    push @{$pEntry->{'copyonce'}}, @nunit_framework;
227 dpurdie 1146
    $pEntry->{'testdir'}  = 'TESTDIR';
1147
 
1148
    #   
1149
    #   Create the Test Directory
1150
    #   Tests will be done in a .NUNIT subdir
1151
    #
1152
    MkdirRule( "\$(TESTDIR)", 'TESTDIR', '--Path=$(GBE_PLATFORM)$(GBE_TYPE).NUNIT', '--RemoveAll' );
1153
 
1154
    #
1155
    #   Files created by the Unit Test
1156
    #
1157
    ToolsetGenerate ( "\$(TESTDIR)/$pEntry->{test_name}.xml"  );
1158
    ToolsetGenerate ( $ofile  );
1159
 
1160
}
1161
 
1162
#.. Successful termination
1163
1;
1164