Subversion Repositories DevTools

Rev

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