Subversion Repositories DevTools

Rev

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