Subversion Repositories DevTools

Rev

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