Subversion Repositories DevTools

Rev

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