Subversion Repositories DevTools

Rev

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