Subversion Repositories DevTools

Rev

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