Statistics
| Branch: | Revision:

root / src / utils / opp_makemake @ master

History | View | Annotate | Download (47.8 KB)

1
#!/usr/bin/env perl
2
#
3
# Creates a makefile for a given OMNeT++/OMNEST model.
4
# Assumes that .ned, .msg, .cc and .h files are in one directory.
5
# The name of the program defaults to the name of the directory ('myproject').
6
#
7
# Author: Andras Varga
8
#
9

    
10
use Cwd;
11
use Config;
12

    
13
$ARG0 = $0;
14
$progname = $ARG0;
15

    
16
#$arch = $Config{'archname'};
17
#$isCygwin = ($arch =~ /cygwin/i);
18
$isWindows = defined $ENV{OS} && $ENV{OS} =~ /windows/i;
19
$isMINGW = defined $ENV{MSYSTEM} && $ENV{MSYSTEM} =~ /mingw/i;
20

    
21
sub makemake();
22

    
23
makemake();
24
exit 0;
25

    
26

    
27
sub usage()
28
{
29
    print <<END
30
$progname: create a Makefile for an OMNeT++/OMNEST model, based on
31
source files in current directory
32

    
33
$progname [options] [object files or libraries]
34
    -h, --help            This help text
35
    -f, --force           Force overwriting existing Makefile
36
    --nmake               Generate Makefile.vc for Visual C++ and nmake.exe
37
    -e ext, --ext ext     C++ source file extension, usually "cc" or "cpp".
38
                          By default, this is determined by looking at
39
                          existing files in the directory.
40
    -o filename           Name of simulation executable or library to be built.
41
                          Defaults to the project name.
42
    -O directory, --out directory
43
                          Specifies the name of the output directory tree
44
                          for out-of-directory build; defaults to "out".
45
                          The actual output directory will be
46
                          <projectroot>/<dir>/\$(CONFIGNAME)/<subpath>,
47
                          where <subpath> is the project-relative path of
48
                          the current directory (for <projectroot> see -P).
49
                          An absolute directory path can also be specified,
50
                          resulting in <absolute-dir>/\$(CONFIGNAME)/<subpath>.
51
    --deep                Generates a "deep" Makefile. A deep Makefile will
52
                          cover the whole source tree under the make directory,
53
                          not just files in that directory. Directories may be
54
                          excluded using -X. The include path will contain all
55
                          directories in the source tree. Deep is not
56
                          compatible with -r (recursive make), but -d
57
                          (directory to recurse into) can still be used.
58
    -r, --recurse         Causes make to recursively descend into all subdir-
59
                          ectories; subdirectories are expected to contain
60
                          makefiles themselves. If you need to maintain a
61
                          specific order, declare dependencies in the
62
                          makefrag(.vc) file. Subdirectories may be excluded
63
                          using -X. See also -d.
64
    -X directory, -Xdirectory, --except directory
65
                          With -r and --deep option: ignore the given directory.
66
                          If the argument is a relative path, it is interpreted
67
                          as relative to the current directory. Wildcards
68
                          (like -X *_backups or -X */tmp) are accepted.
69
    -dsubdir, -d subdir, --subdir subdir
70
                          Causes make to recursively descend into the given
71
                          directory. The directory does not need to be the
72
                          child of the make directory. When used with --deep,
73
                          -d also implies -X for that directory.
74
    --no-deep-includes    Normally, when a deep makefile is created, all source
75
                          directories will get added to the include path.
76
                          Specifying this option turns off that behavior, and
77
                          lets the user explicitly specify all include dirs.
78
    -P directory, --projectdir directory
79
                          Specifies the project root directory. Any absolute
80
                          path (-I, -L, object file names, etc.) that points
81
                          into the project's subtree will be converted to
82
                          relative, to ease compiling the project in a
83
                          different directory. Defaults to first ancestor
84
                          directory that contains a ".project" file.
85
    -M mode, --mode mode  Selects the build mode, "debug" or "release".
86
                          Defaults to "debug". This setting can still be
87
                          overridden on the make command line, by specifying
88
                          "MODE=debug" or "MODE=release" as an extra argument.
89
    -Dname[=value], -D name[=value], --define name[=value]
90
                          Preprocessor symbol to be passed to the C++ compiler.
91
    -Kname=value, -K name=value, --makefile-define name=value
92
                          Defines a makefile variable, i.e. causes name=value
93
                          line to get inserted into the makefile.
94
    -n, --nolink          Produce object files but do not create executable or
95
                          library. Useful for models with parts in several
96
                          directories. With this option, -u and -l have
97
                          no effect.
98
    -s, --make-so         Build shared library (.so or .dll). Useful if you want
99
                          to load the model dynamically (via the load-libs=
100
                          omnetpp.ini or the -l Cmdenv/Tkenv command-line option).
101
    -a, --make-lib        Create static library (.a or .lib).
102
    -S, --fordll          Compile C++ files for use in DLLs; see -p option.
103
                          The -s (build shared library) option implies this one.
104
    -w, --withobjects     Link with all object files found in -I directories,
105
                          or add them to the created library. OBSOLETE.
106
    -u name, --userinterface name
107
                          Selects the user interface libraries to link with.
108
                          Possible values are "all", "Cmdenv", and "Tkenv".
109
                          Defaults to "all".
110
    -Idir                 Additional NED and C++ include directory
111
    -Ldir                 Add a directory to the library path
112
    -llibrary             Additional library to link against. On Unix, -lfoo
113
                          will link with libfoo.so or libfoo.a; on Windows,
114
                          with foo.lib. The library path (see -L) will be
115
                          searched in both cases.
116
    -p symbol, -psymbol   The DLL export/import symbol. It will cause
117
                          -P<symbol>_API to be passed to opp_msgc. Also,
118
                          if target is a DLL or --fordll is specified, the
119
                          <symbol>_EXPORT macro will be passed to the compiler;
120
                          the source code is expected to be set up so that
121
                          dll-public declarations are annotated with
122
                          <symbol>_API, and <symbol>_EXPORT causes <symbol>_API
123
                          to be defined as __declspec(dllexport).
124
    -i filename, --includefragment filename
125
                          Append file to near end of Makefile. The file
126
                          makefrag.vc (if exists) is appended automatically
127
                          if no -i options are given. This option is useful
128
                          if a source file (.ned, .msg or .cc) is to be
129
                          generated from other files.
130
    object file or library
131
                          Arguments will simply get added to the linker (or
132
                          librarian) command line. Thus, wildcards and macros
133
                          are accepted, and will be resolved at build time.
134
                          Useful macros are \$O (the output location of this
135
                          makefile), \$(PROJECT_OUTPUT_PATH) and \$(CONFIGNAME);
136
                          see the generated makefiles for more. On Unix you
137
                          need to single-quote them ('\$O') against getting
138
                          resolved prematurely by the shell.
139

    
140
Default output is Makefile (with --nmake it is Makefile.vc), which you can
141
invoke by typing "make" (with nmake: "nmake -f Makefile.vc").
142

    
143
The contents of the makefrag file (with --nmake: makefrag.vc) will be built
144
into the generated makefile, letting you to override the default target,
145
change variables, etc.
146

    
147
END
148
}
149

    
150
sub makemake()
151
{
152
    if ($isWindows && $ENV{OS} ne "Windows_NT") {
153
        error("this program can only be used on Windows NT/2000/XP, but your OS environment variable says '$ENV{OS}'\n");
154
    }
155

    
156
    #
157
    # process command line args
158
    #
159
    @args = @ARGV;
160
    $isNMake = 0;
161
    $projectDir = "";
162
    $type = "EXE";
163
    $target = "";
164
    $outRoot = "";
165
    $isDeep = 0;
166
    $isRecursive = 0;
167
    $noDeepIncludes = 0;
168
    $force = 0;
169
    $defaultMode = "";
170
    $userInterface = "ALL";
171
    $ccExt = "";
172
    $configFile = "";
173
    $dllSymbol = "";
174
    $compileForDll = 0;
175
    $ignoreNedFiles = 1;
176
    @fragmentFiles = ();
177
    @submakeDirs = ();
178
    @exceptSubdirs = ();
179
    @includeDirs = ();
180
    @libDirs = ();
181
    @libs = ();
182
    @defines = ();
183
    @makefileVariables = ();
184
    @extraArgs = ();
185

    
186
    # process arg vector
187
    while (@ARGV)
188
    {
189
        $arg = shift @ARGV;
190
        if ($arg eq "-h" || $arg eq "--help") {
191
            usage();
192
            exit(1);
193
        }
194
        elsif ($arg eq "-f" || $arg eq "--force") {
195
            $force = 1;
196
        }
197
        elsif ($arg eq "--nmake") {
198
            $isNMake = 1;
199
        }
200
        elsif ($arg eq "-e" || $arg eq "--ext") {
201
            $ccExt = shift @ARGV;
202
        }
203
        elsif ($arg eq "-o") {
204
            $target = shift @ARGV;
205
        }
206
        elsif ($arg =~ /^-o/) {
207
            $target = substr($arg, 2);
208
        }
209
        elsif ($arg eq "-O" || $arg eq "--out") {
210
            $outRoot = shift @ARGV;
211
        }
212
        elsif ($arg =~ /^-O/) {
213
            $outRoot = substr($arg, 2);
214
        }
215
        elsif ($arg eq "--deep") {
216
            $isDeep = 1;
217
        }
218
        elsif ($arg eq "-r" || $arg eq "--recurse") {
219
            $isRecursive = 1;
220
        }
221
        elsif ($arg eq "-X" || $arg eq "--except") {
222
            push(@exceptSubdirs, shift @ARGV);
223
        }
224
        elsif ($arg =~ /^-X/) {
225
            my $dir = substr($arg, 2);
226
            push(@exceptSubdirs, $dir);
227
        }
228
        elsif ($arg eq "--no-deep-includes") {
229
            $noDeepIncludes = 1;
230
        }
231
        elsif ($arg eq "-D" || $arg eq "--define") {
232
            push(@defines, shift @ARGV);
233
        }
234
        elsif ($arg =~ /^-D/) {
235
            my $define = substr($arg, 2);
236
            push(@defines, $define);
237
        }
238
        elsif ($arg eq "-K" || $arg eq "--makefile-define") {
239
            push(@makefileVariables, shift @ARGV);
240
        }
241
        elsif ($arg =~ /^-K/) {
242
            my $makefileVariable = substr($arg, 2);
243
            push(@makefileVariables, $makefileVariable);
244
        }
245
        elsif ($arg eq "-N" || $arg eq "--ignore-ned") {
246
            error("obsolete option $arg, please remove (dynamic NED loading is now the default)");
247
        }
248
        elsif ($arg eq "-P" || $arg eq "--projectdir") {
249
            $projectDir = shift @ARGV;
250
        }
251
        elsif ($arg =~ /^-P/) {
252
            $projectDir = substr($arg, 2);
253
        }
254
        elsif ($arg eq "-M" || $arg eq "--mode") {
255
            $defaultMode = shift @ARGV;
256
        }
257
        elsif ($arg =~ /^-M/) {
258
            $defaultMode = substr($arg, 2);
259
        }
260
        elsif ($arg eq "-c" || $arg eq "--configfile") {
261
            error("option $arg is no longer supported, config file is located using variables (OMNETPP_CONFIGFILE or OMNETPP_ROOT), or by invoking opp_configfilepath");
262
        }
263
        elsif ($arg eq "-d" || $arg eq "--subdir") {
264
            push(@submakeDirs, shift @ARGV);
265
        }
266
        elsif ($arg =~ /^-d/) {
267
            my $subdir = substr($arg, 2);
268
            push(@submakeDirs, $subdir);
269
        }
270
        elsif ($arg eq "-n" || $arg eq "--nolink") {
271
            $type = "NOLINK";
272
        }
273
        elsif ($arg eq "-s" || $arg eq "--make-so") {
274
            $type = "SHAREDLIB";
275
        }
276
        elsif ($arg eq "-a" || $arg eq "--make-lib") {
277
            $type = "STATICLIB";
278
        }
279
        elsif ($arg eq "-S" || $arg eq "--fordll") {
280
            $compileForDll = 1;
281
        }
282
        elsif ($arg eq "-w" || $arg eq "--withobjects") {
283
            error("$progname: $arg: obsolete option, please remove");
284
        }
285
        elsif ($arg eq "-x" || $arg eq "--notstamp") {
286
            error("$progname: $arg: obsolete option, please remove");
287
        }
288
        elsif ($arg eq "-u" || $arg eq "--userinterface") {
289
            $userInterface = shift @ARGV;
290
            $userInterface = uc($userInterface);
291
            if ($userInterface ne "ALL" && $userInterface ne "CMDENV" && $userInterface ne "TKENV") {
292
                error("$progname: -u: specify All, Cmdenv or Tkenv");
293
            }
294
        }
295
        elsif ($arg eq "-i" || $arg eq "--includefragment") {
296
            push(@fragmentFiles, shift @ARGV);
297
        }
298
        elsif ($arg eq "-I") {
299
            push(@includeDirs, shift @ARGV);
300
        }
301
        elsif ($arg =~ /^-I/) {
302
            my $dir = substr($arg, 2);
303
            push(@includeDirs, $dir);
304
        }
305
        elsif ($arg eq "-L") {
306
            push(@libDirs, shift @ARGV);
307
        }
308
        elsif ($arg =~ /^-L/) {
309
            my $dir = substr($arg, 2);
310
            push(@libDirs, $dir);
311
        }
312
        elsif ($arg =~ /^-l/) {
313
            my $lib = substr($arg, 2);
314
            push(@libs, $lib);
315
        }
316
        elsif ($arg eq "-p") {
317
            $dllSymbol = shift @ARGV;
318
        }
319
        elsif ($arg =~ /^-p/) {
320
            $dllSymbol = substr($arg, 2);
321
        }
322
        elsif ($arg =~ /^--meta:/) {
323
            error("$progname: --meta options not supported: they rely on information only available inside the IDE");
324
        }
325
        elsif ($arg eq "--") {
326
            last;
327
        }
328
        else {
329
            error("unrecognized option: $arg") if ($arg =~ /^-/);
330
            push(@extraArgs, $arg);
331
        }
332
    }
333

    
334
    # process args after "--"
335
    while (@ARGV)
336
    {
337
        $arg = shift @ARGV;
338
        push(@extraArgs, $arg);
339
    }
340

    
341

    
342
    #
343
    # Prepare the variables for the template
344
    #
345

    
346
    $makefile = $isNMake ? "Makefile.vc" : "Makefile";
347
    if (-f $makefile && $force ne 1) {
348
        error("use -f to force overwriting existing $makefile");
349
    }
350

    
351
    if ($type eq "SHAREDLIB") {
352
        $compileForDll = 1;
353
    }
354

    
355
    $folder = cwd;
356

    
357
    print "Creating $makefile in $folder...\n";
358

    
359
    @objs = ();
360
    @generatedHeaders = ();
361
    @extraObjs = ();
362
    @ccfiles = ();
363
    @cppfiles = ();
364
    @nedfiles = ();
365
    @msgfiles = ();
366
    @msgccfiles = ();
367
    @msghfiles = ();
368
    @sourceDirs = ();
369
    @backslashedSourceDirs = ();
370

    
371
    # determine, and clean up format of project root dir
372
    if ($projectDir eq "") {
373
        # try to find project root directory (go up until we find a ".project" file)
374
        my $dir = cwd;
375
        $dir =~ s|\\|/|g;
376
        for (;;) {
377
            if (-f "$dir/.project") {
378
                $projectDir = $dir;
379
                last;
380
            }
381
            if ($dir =~ m|/|) {
382
                $dir =~ s|/[^/]*$||;
383
            }
384
            else {
385
                $projectDir = ".";
386
                last;
387
            }
388
        }
389
    }
390
    elsif (! -d $projectDir) {
391
        error("specified project directory \"$projectDir\" does not exist");
392
    }
393

    
394
    if ($projectDir ne "") {
395
        $projectDir = existingDirtoCanonicalAbsolute($projectDir);
396
        #print "Project directory: $projectDir\n";
397
        if (!($folder =~ /^\Q$projectDir\E/)) {
398
            error("current directory is not under the given project directory \"$projectDir\"");
399
        }
400
    }
401

    
402
    $projectName = $projectDir;
403
    $projectName  =~ s/[\/\\]$//;  # remove trailing slash/backslash
404
    $projectName  =~ s/.*[\/\\]//; # keep only part after last slash/backslash
405
    $target = $target ne "" ? $target : $projectName;
406

    
407
    # target should only be a name, cannot contain relative path
408
    if ($target =~ /.*[\/\\].*/) {
409
        error("target (-o option) should only be a name, it cannot contain relative path");
410
    }
411

    
412
    # recursive and deep do not mix
413
    if ($isDeep) {
414
        $isRecursive = 0;
415
    }
416

    
417
    $makecommand = $isNMake ? "nmake /nologo /f Makefile.vc" : $ENV{MAKE} ? $ENV{MAKE} : "make";
418

    
419
    # find configuser.vc / Makefile.inc
420
    $comspec = $ENV{COMSPEC};
421
    $comspec =~ s!\\!\\\\!g;
422
    $configFile = $isNMake ? `$comspec /c opp_configfilepath.cmd` : `opp_configfilepath`;
423
    if ($? != 0) {
424
        error("opp_configfilepath returned nonzero exit code -- make sure it exists and is in the PATH");
425
    }
426
    $configFile =~ s/^\s*(.*?)\s*$/$1/gs;
427
    if ($configFile eq "" || ! -f $configFile) {
428
        error("opp_configfilepath didn't return the name of an existing directory, result was \"$configFile\"");
429
    }
430

    
431
    # determine outdir (defaults to "out")
432
    if ($outRoot eq "") {$outRoot = "out";}
433
    my $outRootAbs = (!isRelative($outRoot) || $projectDir eq "") ? $outRoot : "$projectDir/$outRoot";
434
    my $outRootRel = abs2rel($outRootAbs, $projectDir);
435
    $outDir = canonicalize("$outRootRel");
436

    
437
    # determine subpath: the project-relative path of this folder
438
    my $subpath = cwd();
439
    $subpath =~ s|^\Q$projectDir\E||;
440

    
441
    # collect source files
442
    if ($isDeep) {
443
        my @allExcludedDirs = ();
444
        push(@allExcludedDirs, $outDir);
445
        push(@allExcludedDirs, @exceptSubdirs);
446
        push(@allExcludedDirs, @submakeDirs);
447
        @sourceDirs = collectDirs(".", \@allExcludedDirs);
448
        error("too many subdirs for --deep") if (@sourceDirs > 1000);
449
    } else {
450
        @sourceDirs = ();
451
        if (isGoodDir(".", \@exceptSubdirs)) {
452
            @sourceDirs = (".");
453
        }
454
    }
455

    
456
    foreach $f (@sourceDirs) {
457
        my $ff = ($f eq ".") ? "" : "$f/";
458
        push(@ccfiles,  glob("$ff*.cc"));
459
        push(@cppfiles, glob("$ff*.cpp"));
460
        push(@msgfiles, glob("$ff*.msg"));
461
        push(@nedfiles, glob("$ff*.ned"));
462
    }
463

    
464
    foreach my $i (@sourceDirs) {
465
        my $i2 = $i;
466
        $i2 =~ s|/|\\|g;
467
        push(@backslashedSourceDirs, $i2);
468
    }
469

    
470
    # include dirs and lib dirs (make them relative to the project dir)
471
    @tmp = @includeDirs;
472
    @includeDirs = ();
473
    foreach $i (@tmp) {
474
        push(@includeDirs, abs2rel($i,$projectDir));
475
    }
476
    if (!$noDeepIncludes) {
477
        foreach $i (@sourceDirs) {
478
            push(@includeDirs, $i);
479
        }
480
    }
481
    @tmp = @libDirs;
482
    @libDirs = ();
483
    foreach $i (@tmp) {
484
        push(@libDirs, abs2rel($i,$projectDir));
485
    }
486

    
487
    # try to determine if .cc or .cpp files are used
488
    if ($ccExt eq "") {
489
        if (!@ccfiles && @cppfiles) {
490
            $ccExt = "cpp";
491
        }
492
        elsif (@ccfiles && !@cppfiles) {
493
            $ccExt = "cc";
494
        }
495
        elsif (@ccfiles && @cppfiles) {
496
            error("you have both .cc and .cpp files -- specify -e cc or -e cpp option to select which set of files to use");
497
        }
498
        else {
499
            $ccExt = "cc";  # if no files, use .cc extension
500
        }
501
    }
502
    else {
503
        if ($ccExt eq "cc" && !@ccfiles && @cppfiles) {
504
            warning("you specified -e cc but you have only .cpp files!");
505
        }
506
        if ($ccExt eq "cpp" && @ccfiles && !@cppfiles) {
507
            warning("you specified -e cpp but you have only .cc files!");
508
        }
509
    }
510

    
511
    $objExt = $isNMake ? "obj" : "o";
512

    
513
    $targetPrefix = "";
514
    $targetSuffix = "";
515
    if ($type eq "EXE") {
516
        $targetSuffix = $isNMake ? ".exe" : "\$(EXE_SUFFIX)";
517
    } elsif ($type eq "SHAREDLIB") {
518
        $targetSuffix = $isNMake ? ".dll" : "\$(SHARED_LIB_SUFFIX)";
519
        $targetPrefix = $isNMake ? "" : "lib";
520
    } elsif ($type eq "STATICLIB") {
521
        $targetSuffix = $isNMake ? ".lib" : "\$(A_LIB_SUFFIX)";
522
        $targetPrefix = $isNMake ? "" : "lib";
523
    }
524

    
525
    # prepare submakeDirs. First, check that all specified subdirs exist
526
    foreach $subdir (@submakeDirs) {
527
        if (! -d $subdir) {
528
            error("subdirectory '$subdir' does not exist");
529
        }
530
    }
531

    
532
    if ($isRecursive) {
533
        foreach $f (glob("*")) {
534
            if (isGoodDir($f, \@exceptSubdirs)) {
535
                push(@submakeDirs, $f);
536
            }
537
        }
538
    }
539

    
540
    # create names for subdir targets
541
    @submakeNames = ();
542
    foreach $i (@submakeDirs) {
543
        my $i2 = $i;
544
        $i2 =~ s/[^a-zA-Z0-9_]/__/g;
545
        push(@submakeNames, $i2);
546
    }
547

    
548
    # process extraArgs (do ".o" <--> ".obj" translation; NOTE: we don't try to
549
    # translate library names, because libs are supposed to be given via -L/-l)
550
    @extraObjs = ();
551
    foreach $i (@extraArgs) {
552
        my $i2 = $i;
553
        $i2 =~ s/^'(.*)'$/$1/; # remove possible quotes (cmd.exe does not do it)
554
        $i2 =~ s/\.o(bj)?$/.$objExt/;
555
        push(@extraObjs, $i2);
556
    }
557

    
558
    @sources = ();
559
    push(@sources, @ccfiles) if ($ccExt eq "cc");
560
    push(@sources, @cppfiles) if ($ccExt eq "cpp");
561
    push(@sources, @msgfiles);
562
    push(@sources, @nedfiles) if (!$ignoreNedFiles);
563
    foreach $i (@sources)
564
    {
565
        $i =~ s/\*[^ ]*//g;
566
        $i =~ s/[^ ]*_n\.$ccExt$//g;
567
        $i =~ s/[^ ]*_m\.$ccExt$//g;
568
        $i =~ s/\.ned$/_n.$objExt/g;
569
        $i =~ s/\.msg$/_m.$objExt/g;
570
        $i =~ s/\.$ccExt$/.$objExt/g;
571
        if ($i ne '') {
572
            push(@objs, "\$O/$i");
573
        }
574
    }
575

    
576
    foreach $i (@msgfiles) {
577
        $h = $i; $h =~ s/\.msg$/_m.h/;
578
        $cc = $i; $cc =~ s/\.msg$/_m.$ccExt/;
579
        push(@generatedHeaders, $h);
580
        push(@msgccfiles, $cc);
581
        push(@msghfiles, $h);
582
    }
583

    
584
    $makefrags = "";
585
    if (@fragmentFiles) {
586
        foreach $frag (@fragmentFiles) {
587
            $makefrags .= "# inserted from file '$frag':\n";
588
            $makefrags .= readTextFile($frag) . "\n";
589
        }
590
    }
591
    else {
592
        $makefragFilename = $isNMake ? "makefrag.vc" : "makefrag";
593
        if (-f $makefragFilename) {
594
            $makefrags .= "# inserted from file '$makefragFilename':\n";
595
            $makefrags .= readTextFile($makefragFilename) . "\n";
596
        }
597
    }
598

    
599
    # defines
600
    if ($compileForDll && $dllSymbol ne "") {
601
        push(@defines, $dllSymbol."_EXPORT");
602
    }
603

    
604
    $deps = "";  # we'll run opp_makedep afterwards
605

    
606
    # XP has 8K limit on line length, so we may have to use the inline file feature of nmake
607
    $approximateLinkerLineLength = 500 + length(quoteJoin(\@objs)) + length(quoteJoin(\@extraObjs)) + 2*length(quoteJoin(\@libs)) + 2*length(quoteJoin(\@libDirs));
608
    $isLongLinkerLine = $approximateLinkerLineLength > 8000;
609

    
610
    # fill in template variables
611
    %m = (
612
        "rem" => "",  # allows putting comments into the template
613
        "lbrace" =>  "{",
614
        "rbrace" =>  "}",
615
        "nmake" =>  $isNMake,
616
        "targetprefix" => $targetPrefix,
617
        "target" =>   $target,
618
        "targetsuffix" => $targetSuffix,
619
        "outdir" => $outDir,
620
        "subpath" => $subpath,
621
        "isdeep" => $isDeep,
622
        "progname" => "opp_makemake",  # $isNMake ? "opp_nmakemake" : "opp_makemake",
623
        "args" =>  join(" ", @args),
624
        "configfile" =>  $configFile,
625
        "-L" =>  $isNMake ? "/libpath:" : "-L",
626
        "-l" =>  $isNMake ? "" : "-l",
627
        ".lib" =>  $isNMake ? ".lib" : "",
628
        "-out" =>  $isNMake ? "/out:" : "-o ", # note space after "-o" -- OS/X needs it
629
        "cc" =>  $ccExt,
630
        "obj" =>  $objExt,
631
        "deps" =>  $deps,
632
        "exe" =>  $type eq "EXE",
633
        "sharedlib" =>  $type eq "SHAREDLIB",
634
        "staticlib" =>  $type eq "STATICLIB",
635
        "nolink" =>  $type eq "NOLINK",
636
        "defaultmode" =>  $defaultMode,
637
        "allenv" => ($userInterface =~ /^A/) ne "",
638
        "cmdenv" =>  ($userInterface =~ /^C/) ne "",
639
        "tkenv" =>  ($userInterface =~ /^T/) ne "",
640
        "extraobjs" =>  quoteJoin(\@extraObjs),
641
        "includepath" =>  prefixQuoteJoin(\@includeDirs, "-I"),
642
        "libpathdirs" =>  \@libDirs,
643
        "libs" =>  \@libs,
644
        "defines" => prefixQuoteJoin(\@defines, "-D"),
645
        "makefilevariables" => \@makefileVariables,
646
        "makecommand" =>  $makecommand,
647
        "makefile" =>  $isNMake ? "Makefile.vc" : "Makefile",
648
        "makefrags" =>  $makefrags,
649
        "msgccfiles" =>  \@msgccfiles,
650
        "msghfiles" =>  \@msghfiles,
651
        "msgfiles" =>  \@msgfiles,
652
        "objs" =>  quoteJoin(\@objs),
653
        "submakedirs" =>  \@submakeDirs,
654
        "submakenames" =>  \@submakeNames,
655
        "dllsymbol" =>  $dllSymbol,
656
        "sourcedirs" => \@sourceDirs,
657
        "backslashedsourcedirs" => \@backslashedSourceDirs,
658
        "nmake_inlinefile" => ($isNMake && $isLongLinkerLine) ? "@<<\n" : "",
659
        "nmake_inlineend" => ($isNMake && $isLongLinkerLine) ? "\n<<" : "",
660
        "gcclongline" => (!$isNMake && $isLongLinkerLine && $isWindows)
661
    );
662

    
663
    my $content = substituteIntoTemplate(template(), \%m, "{", "}");
664

    
665
    open(OUT, ">$makefile");
666
    print OUT $content;
667
    close OUT;
668

    
669
    print "$makefile created, running \"$makecommand depend\" to add dependencies...\n";
670
    runprog("$makecommand depend");
671
}
672

    
673
sub collectDirs($;$);
674
sub collectDirs($;$)
675
{
676
    my ($dir,$exceptDirsRef) = @_;
677
    my @exceptDirs = @$exceptDirsRef;
678
    my @result = ();
679
    if (isGoodDir($dir, \@exceptDirs)) {
680
        push(@result, canonicalize($dir));
681
        foreach $f (glob("$dir/*")) {
682
            push(@result, collectDirs($f, \@exceptDirs));
683
        }
684
    }
685
    return @result;
686
}
687

    
688

    
689
sub isGoodDir($;$)
690
{
691
    my ($dirpath,$exceptDirsRef) = @_;
692
    my @exceptDirs = @$exceptDirsRef;
693
    if (!-d $dirpath) {
694
        return 0;
695
    }
696

    
697
    # skip dot directories, also CVS, SVN, etc aux dirs
698
    my @IGNORABLE_DIRS = ("CVS", "RCS", "SCCS", "_darcs", "blib", ".git", ".svn", ".git", ".bzr", ".hg", "backups");
699

    
700
    my $dirname = $dirpath;
701
    $dirname =~ s|^.*/||;
702
    if ($dirname =~ /^\.(.+)/ || grep(/^\Q$dirname\E$/, @IGNORABLE_DIRS)) {
703
        return 0;
704
    }
705

    
706
    # check exceptdirs. For that, we convert everything to canonical absolute path
707
    my $absDir = existingDirtoCanonicalAbsolute($dirpath);
708
    foreach my $exceptDirPattern (@exceptDirs) {
709
        foreach my $exceptDir (glob($exceptDirPattern)) {
710
            if (-d $exceptDir) {
711
                my $absExceptDir = existingDirtoCanonicalAbsolute($exceptDir);
712
                if ($absDir eq $absExceptDir) {
713
                    return 0;
714
                }
715
            }
716
        }
717

    
718
    }
719
    return 1;
720
}
721

    
722
#
723
# Performs template substitution. Constructs understood are:
724
#  - {foo} gets replaced by the value of "foo";
725
#  - {bar?some text} gets replaced by "some text" if value of "bar" is true*.
726
#  - {~bar?some text} gets replaced by "some text" if value of "bar" is false*
727
#  - {bar:} only keep the rest of the line if value of "bar" is true*
728
#  - {~bar:} only keep the rest of the line if value of "bar" is false*
729
#  - {@i1:list1,i2:list2,...} ... {i1} ...{/@}  parallel iteration list1, list2 etc.
730
# * true/false are interpreted as in Perl: "" and "0" are false, everything else is true.
731
#
732
# Newlines inside {...} are not permitted; this allows detecting errors caused
733
# by misplaced braces. Also, nesting is not supported.
734
#
735
# Lines starting with ### treated as comments and will be removed from the output
736
#
737
sub substituteIntoTemplate($;$;$;$);
738
sub substituteIntoTemplate($;$;$;$)
739
{
740
    my ($template,$mapref,$startTag,$endTag) = @_;
741
    my %map = %$mapref;
742

    
743
    $template =~ s/\n[ \t]*###.*\n/\n/g; # remove whole-line comments
744
    $template =~ s/[ \t]*###.*\n/\n/g;   # remove end-line comments
745

    
746
    my $buf = "";
747
    my $startTagLen = length($startTag);
748
    my $endTagLen = length($endTag);
749

    
750
    my $current = 0;
751
    while (1) {
752
        my $start = index($template, $startTag, $current);
753
        if ($start == -1) {
754
            last;
755
        }
756
        else {
757
            my $end = index($template, $endTag, $start);
758
            if ($end != -1) {
759
                $end += $endTagLen;
760
                my $tag = substr2($template, $start, $end);
761
                #print("processing $tag\n");
762
                my $key = substr2($template, $start+$startTagLen, $end-$endTagLen);
763
                if (index($key, "\n") != -1) {
764
                    die("template error: newline inside \"$tag\" (misplaced start/end tag?)");
765
                }
766
                my $isLoop = substr($key, 0, 1) eq "@";
767
                if ($isLoop) {
768
                    $key = substr($key, 1);  # drop "@"
769
                }
770
                my $isNegated = substr($key, 0, 1) eq "~";
771
                if ($isNegated) {
772
                    $key = substr($key, 1);  # drop "~"
773
                }
774
                my $isOptLine = substr($key,-1,1) eq ":" && index($key, "?") == -1;
775
                if ($isOptLine) {
776
                    $key = substr($key, 0, length($key)-1);  # drop trailing ":"
777
                }
778
                my $questionmarkPos = index($key, "?");
779
                my $substringAfterQuestionmark = $questionmarkPos == -1 ? "" : substr($key, $questionmarkPos+1);
780
                if ($questionmarkPos != -1) {
781
                    $key = substr2($key, 0, $questionmarkPos); # drop "?..." from key
782
                }
783

    
784
                # determine replacement string, and possibly adjust start/end
785
                my $replacement = "";
786
                if ($isLoop) {
787
                    # basic loop syntax: {@i:list1,j:list2,...} ... {i} ... {/@}
788
                    # this is parallel iteration, not nested loops!
789
                    # first, find loop close tag {/@}
790
                    my $loopEndTag = "$startTag/\@$endTag"; # "{/var}"
791
                    my $balance = 1;
792
                    my $pos = $end-1;  # because we'll start with +1
793
                    while ($balance != 0) {
794
                        $pos = indexOfEither($template, "$startTag\@", $loopEndTag, $pos+1);
795
                        if ($pos == -1) {
796
                            error("template error: missing loop end marker $loopEndTag");
797
                        }
798
                        my $isStartTag = substr($template, $pos+$startTagLen,1) eq '@';
799
                        $balance += $isStartTag ? 1 : -1;
800
                    }
801
                    my $loopEndPos = $pos;
802
                    my $loopBody = substr2($template, $end, $loopEndPos);
803
                    $end = $loopEndPos + length($loopEndTag);
804
                    # parse loop spec: "i:list1,j:list2,..."
805
                    my @loopVars = ();
806
                    my @loopLists = ();
807
                    foreach $loopSpec (split(",",  $key)) {
808
                        if (!($loopSpec =~ / *([a-zA-Z0-9_]+) *: *([a-zA-Z0-9_]+) */)) {
809
                            error("template error: syntax error in loop tag $tag, $startTag\@var1:list1,var2:list2,...$endTag expected in body");
810
                        }
811
                        push(@loopVars, $1);
812
                        push(@loopLists, $2);
813
                    }
814
                    # execute loop: iterate in parallel on all loop variables
815
                    my $length = @{getFromMapAsList(\%map, $loopLists[0])};
816
                    for (my $i=0; $i<$length; $i++) {
817
                        for (my $j=0; $j<@loopVars; $j++) {
818
                            my $loopVarJ = $loopVars[$j];
819
                            my @loopListJ = @{getFromMapAsList(\%map, $loopLists[$j])};
820
                            if (@loopListJ != $length) {
821
                                error("template error: list lengths differ in $tag");
822
                            }
823
                            $map{$loopVarJ} = $loopListJ[$i];
824
                        }
825
                        $replacement .= substituteIntoTemplate($loopBody, \%map, $startTag, $endTag);
826
                    }
827
                    # remove loop variables
828
                    for (my $j=0; $j<@loopVars; $j++) {
829
                        undef $map{$loopVars[$j]};
830
                    }
831
                }
832
                elsif ($isOptLine) {
833
                    # replacing a whole line
834
                    my $condition = getFromMapAsBool(\%map, $key);
835
                    if ($isNegated ? !$condition : $condition) {
836
                        # put line in: all variables OK
837
                    }
838
                    else {
839
                        # omit line
840
                        my $endLine = index($template, "\n", $end);
841
                        if ($endLine == -1) {
842
                            $endLine = length($template);
843
                        }
844
                        $replacement = "";
845
                        $end = $endLine + 1;
846
                    }
847
                }
848
                elsif ($questionmarkPos != -1) {
849
                    # conditional
850
                    $replacement = getFromMapAsBool(\%map, $key)!=$isNegated ? $substringAfterQuestionmark : "";
851
                }
852
                else {
853
                    # plain replacement
854
                    if ($isNegated) {
855
                        die("template error: wrong syntax \"$tag\" (possible missing \"?\")");
856
                    }
857
                    $replacement = getFromMapAsString(\%map, $key);
858
                }
859

    
860
                # do it: replace substring(start, end) with replacement, unless replacement==null
861
                $buf .= substr2($template, $current, $start);  # template code up to the {...}
862
                $buf .= $replacement;
863
                $current = $end;
864
            }
865
        }
866
    }
867
    $buf .= substr($template, $current);  # rest of the template
868
    $buf =~ s/ +\n/\n/sg;            # remove spaces at line end
869
    $buf =~ s/\n\n\n+/\n\n/sg;       # remove multiple empty lines
870
    return $buf;
871
}
872

    
873
sub substr2($;$;$)
874
{
875
    my($string, $startoffset, $endoffset) = @_;
876
    return substr($string, $startoffset, $endoffset - $startoffset);
877
}
878

    
879
sub quoteJoin($)
880
{
881
    my($listref) = @_;
882
    return prefixQuoteJoin($listref, "");
883
}
884

    
885
sub prefixQuoteJoin($,$)
886
{
887
    my($listref,$prefix) = @_;
888
    @list = @$listref;
889
    $sep = (@list > 5) ? " \\\n    " : " ";
890
    $result = "";
891
    foreach $i (@list) {
892
        $result .= $sep . $prefix . quote($i);
893
    }
894
    return $result eq "" ? "" : substr($result, 1); # chop off leading space
895
}
896

    
897
sub indexOfEither($;$;$;$)
898
{
899
    my($template,$substring1,$substring2,$from) = @_;
900
    my $index1 = index($template, $substring1, $from);
901
    my $index2 = index($template, $substring2, $from);
902
    return $index2 if ($index1 == -1);
903
    return $index1 if ($index2 == -1);
904
    return $index1 < $index2 ? $index1 : $index2;
905
}
906

    
907
# for substituteIntoTemplate()
908
sub getFromMapAsString($;$)
909
{
910
    my($mapref,$key) = @_;
911
    my %map = %$mapref;
912
    die("template error: undefined template parameter '$key'") if (!defined($map{$key}));
913
    return $map{$key};
914
}
915

    
916
# for substituteIntoTemplate()
917
sub getFromMapAsBool($;$)
918
{
919
    my($mapref,$key) = @_;
920
    my %map = %$mapref;
921
    die("template error: undefined template parameter '$key'") if (!defined($map{$key}));
922
    $value = $map{$key};
923

    
924
    if (ref($value) eq 'ARRAY') {
925
        my @list = @$value;
926
        return @list;  # =length; 0 if list is empty
927
    } else {
928
        return $value ? 1 : 0;
929
    }
930
}
931

    
932
# for substituteIntoTemplate()
933
sub getFromMapAsList($;$)
934
{
935
    my($mapref,$key) = @_;
936
    my %map = %$mapref;
937
    die("template error: undefined template parameter '$key'") if (!defined($map{$key}));
938
    my $value = $map{$key};
939
    die("template error: list value expected for template parameter '$key', got '$value'") if (ref($value) ne 'ARRAY');
940
    return $value;
941
}
942

    
943
#
944
# Converts absolute path $inputpath to relative path (relative to the current
945
# directory $referencedir), provided that both $inputpath and $referencedir are under a
946
# "project base directory" $projectdir. Otherwise it returns the original path.
947
# All "\" are converted to "/".
948
#
949
sub abs2rel($;$;$;)
950
{
951
    my($inputpath, $projectdir, $referencedir) = @_;
952

    
953
    if (!defined($projectdir) || $projectdir eq '') {
954
        return $inputpath;
955
    }
956
    if (!defined($referencedir) || $referencedir eq '') {
957
        $referencedir = cwd;
958
    }
959

    
960
    # some normalization
961
    $inputpath =~ s|\\|/|g;
962
    $referencedir =~ s|\\|/|g;
963
    $projectdir =~ s|\\|/|g;
964

    
965
    $inputpath =~ s|/\./|/|g;
966
    $referencedir =~ s|/\./|/|g;
967
    $projectdir =~ s|/\./|/|g;
968

    
969
    $inputpath =~ s|//+|/|g;
970
    $referencedir =~ s|//+|/|g;
971
    $projectdir =~ s|//+|/|g;
972

    
973
    $referencedir =~ s|/*$|/|;
974
    $projectdir =~ s|/*$|/|;
975

    
976
    if (!($inputpath =~ /^\Q$projectdir\E/i && $referencedir =~ /^\Q$projectdir\E/i)) {
977
        return $inputpath;
978
    }
979

    
980
    while (1)
981
    {
982
       # keep cutting off common prefixes until no more
983
       if (!($inputpath =~ m|^(.*?/)|)) {
984
           last;
985
       }
986
       my $prefix = $1;
987
       if ($referencedir =~ /^\Q$prefix\E/i) {
988
           $inputpath =~ s/^\Q$prefix\E//i;
989
           $referencedir =~ s/^\Q$prefix\E//i;
990
       } else {
991
           last;
992
       }
993
    }
994

    
995
    # assemble relative path: change every directory name in $referencedir to "..",
996
    # then add $inputpath to it.
997
    $referencedir =~ s|[^/]+|..|g;
998
    my $rel = $referencedir.$inputpath;
999

    
1000
    return $rel;
1001
}
1002

    
1003
sub isRelative($)
1004
{
1005
    # a path is absolute if it begins with "/" or "\", or contains ":/" or ":\"
1006
    my($path) = @_;
1007
    return !($path =~ /^[\/\\]/ || $path =~ /:[\/\\]/);
1008
}
1009

    
1010
#
1011
# Convert a path to an absolute path in canonical form.
1012
# Path must point to a directory which currently exists.
1013
#
1014
sub existingDirtoCanonicalAbsolute($)
1015
{
1016
    my($dir) = @_;
1017
    die "argument must be an existing directory: $dir" unless (-d $dir);
1018
    $old = cwd();
1019
    chdir($dir);
1020
    $ret = cwd();
1021
    chdir($old);
1022
    return $ret;
1023
}
1024

    
1025
sub canonicalize($)
1026
{
1027
    my($path) = @_;
1028
    $path =~ s|\\|/|g;  # backslash -> fwd slash
1029
    $path =~ s|//+|/|g;  # xx//xx -> xx/xx
1030
    $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx
1031
    $path =~ s|^(\./)+||s unless $path eq "./"; # ./xx -> xx
1032
    $path =~ s|/$||g;  # xx/ -> xx
1033

    
1034
    # # now we'll need to replace "/<subdir>/../" with "/" as many times as we can;
1035
    # # unless <subdir> is "." or ".."
1036
    # $path .= "/" if ($path =~ /\/\.\.$/);  # add "/" if it ends in "/.."
1037
    # $path =~ s|/../../|/..//../|g;  # prevent /../../ from matching
1038
    # $path =~ s|/../../|/..//../|g;  # once again, to handle /../../../
1039
    # while ($path =~ s|/[^/]+/\.\./|/|g) {
1040
    #     $path =~ s|//+|/|g;
1041
    #     $path =~ s|/../../|/..//../|g;
1042
    #     $path =~ s|/../../|/..//../|g;
1043
    # };
1044
    # $path =~ s|//+|/|g;  # xx//xx -> xx/xx
1045

    
1046
    return $path;
1047
}
1048

    
1049
sub quote($)
1050
{
1051
    my($dir) = @_;
1052
    if ($dir =~ / /) {$dir = "\"$dir\"";}
1053
    return $dir;
1054
}
1055

    
1056
sub readTextFile($)
1057
{
1058
    my($file) = @_;
1059
    open(INFILE, $file) || die "cannot open $file";
1060
    read(INFILE, $content, 1000000) || die "cannot read $file";
1061
    return $content;
1062
}
1063

    
1064
sub runprog
1065
{
1066
    my $cmd = shift;
1067
    if ($isWindows && !$isMINGW) {
1068
        system($ENV{COMSPEC}, "/c", $cmd);
1069
    } else {
1070
        system($cmd);
1071
    }
1072
}
1073

    
1074
sub error($)
1075
{
1076
    my($text) = @_;
1077
    print STDERR "$progname: error: $text\n";
1078
    exit(1);
1079
}
1080

    
1081
sub warning($)
1082
{
1083
    my($text) = @_;
1084
    print STDERR "$progname: warning: $text\n";
1085
}
1086

    
1087
sub template()
1088
{
1089
    #
1090
    # NOTE: the following template must be kept in sync with the file:
1091
    # <omnetpp>/ui/org.omnetpp.cdt/src/org/omnetpp/cdt/makefile/Makefile.TEMPLATE
1092
    #
1093
    return <<'ENDTEMPLATE'
1094
#
1095
# OMNeT++/OMNEST Makefile for {targetprefix}{target}
1096
#
1097
# This file was generated with the command:
1098
#  {progname} {args}
1099
#
1100

    
1101
### Some definitions first; note that we only print them if there're going to be needed
1102
{~nolink:}# Name of target to be created (-o option)
1103
{~nolink:}TARGET = {targetprefix}{target}{targetsuffix}
1104
{~nolink:}
1105
{exe:}# User interface (uncomment one) (-u option)
1106
{exe:}{~allenv?#}USERIF_LIBS = $(ALL_ENV_LIBS) # that is, $(TKENV_LIBS) $(CMDENV_LIBS)
1107
{exe:}{~cmdenv?#}USERIF_LIBS = $(CMDENV_LIBS)
1108
{exe:}{~tkenv?#}USERIF_LIBS = $(TKENV_LIBS)
1109
{exe:}
1110
{sourcedirs:}# C++ include paths (with -I)
1111
{sourcedirs:}INCLUDE_PATH = {includepath}
1112
{sourcedirs:}
1113
{~nolink:}# Additional object and library files to link with
1114
{~nolink:}EXTRA_OBJS = {extraobjs}
1115
{~nolink:}
1116
{~nolink:}{~staticlib:}# Additional libraries (-L, -l options)
1117
{~nolink:}{~staticlib:}LIBS ={@dir:libpathdirs} {-L}{dir}{/@} {@lib:libs} {-l}{lib}{.lib}{/@}
1118
{~nolink:}{~staticlib:}{libpathdirs:}{~nmake:}LIBS +={@dir:libpathdirs} -Wl,-rpath,`abspath {dir}`{/@}
1119
{~nolink:}{~staticlib:}
1120
# Output directory
1121
### Note: these variables are public API (see help text), don't change
1122
PROJECT_OUTPUT_DIR = {outdir}
1123
PROJECTRELATIVE_PATH = {subpath}
1124
O = $(PROJECT_OUTPUT_DIR)/$(CONFIGNAME)/$(PROJECTRELATIVE_PATH)
1125

    
1126
{sourcedirs:}# Object files for local .{cc} and .msg files
1127
{sourcedirs:}OBJS = {objs}
1128
{sourcedirs:}
1129
{sourcedirs:}# Message files
1130
{sourcedirs:}MSGFILES ={@msg:msgfiles} \
1131
{sourcedirs:}    {msg}{/@}
1132
{sourcedirs:}
1133
{defaultmode:}# Default mode (-M option); can be overridden with {nmake?n}make MODE=debug (or =release)
1134
{defaultmode:}{nmake?!}ifndef MODE
1135
{defaultmode:}MODE = {defaultmode}
1136
{defaultmode:}{nmake?!}endif
1137
{defaultmode:}
1138
{makefilevariables:}# Other makefile variables (-K)
1139
{@d:makefilevariables}{d}
1140
{/@}
1141

    
1142
{nmake:}MAKE=nmake -nologo -f Makefile.vc
1143
{nmake:}
1144
#------------------------------------------------------------------------------
1145

    
1146
# Pull in OMNeT++ configuration (Makefile.inc or configuser.vc)
1147
{nmake:}!if "$(OMNETPP_CONFIGFILE)"!=""
1148
{nmake:}CONFIGFILE = $(OMNETPP_CONFIGFILE)
1149
{nmake:}!elseif "$(OMNETPP_ROOT)"!=""
1150
{nmake:}CONFIGFILE = $(OMNETPP_ROOT)/configuser.vc
1151
{nmake:}!else
1152
{nmake:}CONFIGFILE = {configfile}
1153
{nmake:}!endif
1154
{nmake:}
1155
{nmake:}!if !exist($(CONFIGFILE))
1156
{nmake:}!error '$(CONFIGFILE)' does not exist -- set OMNETPP_ROOT or OMNETPP_CONFIGFILE to point to configuser.vc. From the IDE, set the OMNeT++ install location on the Window | Preferences dialog.
1157
{nmake:}!endif
1158
{nmake:}
1159
{nmake:}!include $(CONFIGFILE)
1160

    
1161
{~nmake:}ifneq ("$(OMNETPP_CONFIGFILE)","")
1162
{~nmake:}CONFIGFILE = $(OMNETPP_CONFIGFILE)
1163
{~nmake:}else
1164
{~nmake:}ifneq ("$(OMNETPP_ROOT)","")
1165
{~nmake:}CONFIGFILE = $(OMNETPP_ROOT)/Makefile.inc
1166
{~nmake:}else
1167
{~nmake:}CONFIGFILE = $(shell opp_configfilepath)
1168
{~nmake:}endif
1169
{~nmake:}endif
1170
{~nmake:}
1171
{~nmake:}ifeq ("$(wildcard $(CONFIGFILE))","")
1172
{~nmake:}$(error Config file '$(CONFIGFILE)' does not exist -- add the OMNeT++ bin directory to the path so that opp_configfilepath can be found, or set the OMNETPP_CONFIGFILE variable to point to Makefile.inc)
1173
{~nmake:}endif
1174
{~nmake:}
1175
{~nmake:}include $(CONFIGFILE)
1176

    
1177
### # Check that MODE is valid (not enabled, to allow implementing other MODEs)
1178
### {nmake:}!if "$(MODE)"!="debug" && "$(MODE)"!="release"
1179
### {nmake:}error MODE must be "debug" or "release"
1180
### {nmake:}!endif
1181
### {~nmake:}ifneq ($(MODE),"debug")
1182
### {~nmake:}ifneq ($(MODE),"release")
1183
### {~nmake:}$(error MODE must be "debug" or "release")
1184
### {~nmake:}endif
1185
### {~nmake:}endif
1186

    
1187
{~nolink:}{~staticlib:}# Simulation kernel and user interface libraries
1188
{~nolink:}{~staticlib:}OMNETPP_LIB_SUBDIR = $(OMNETPP_LIB_DIR)/$(TOOLCHAIN_NAME)
1189
{exe:}OMNETPP_LIBS = {-L}"$(OMNETPP_LIB_SUBDIR)" {-L}"$(OMNETPP_LIB_DIR)" $(USERIF_LIBS) $(KERNEL_LIBS) $(SYS_LIBS)
1190
{sharedlib:}OMNETPP_LIBS = {-L}"$(OMNETPP_LIB_SUBDIR)" {-L}"$(OMNETPP_LIB_DIR)" {-l}oppenvir$D{.lib} $(KERNEL_LIBS) $(SYS_LIBS)
1191

    
1192
{sourcedirs:}COPTS = $(CFLAGS) {defines} $(INCLUDE_PATH) -I$(OMNETPP_INCL_DIR)
1193
{sourcedirs:}MSGCOPTS = $(INCLUDE_PATH){dllsymbol? -P}{dllsymbol}{dllsymbol?_API}
1194
{sourcedirs:}
1195
#------------------------------------------------------------------------------
1196
# User-supplied makefile fragment(s)
1197
# >>>
1198
{makefrags:}{makefrags}
1199
# <<<
1200
#------------------------------------------------------------------------------
1201

    
1202
# Main target
1203
{~nolink:}all: $(TARGET)
1204
{~nolink:}
1205
{~nolink:}$(TARGET) : $O/$(TARGET)
1206
{~nolink:}{nmake:}	copy $(O:/=\)\$(TARGET) .
1207
{~nolink:}{nmake:}	-copy $(O:/=\)\$(TARGET).manifest . >NUL
1208
{sharedlib:}{nmake:}	-copy $(O:/=\)\$(TARGET:.dll=.lib) . >NUL
1209
{~nolink:}{~nmake:}	$(LN) $O/$(TARGET) .
1210
{~nolink:}
1211
### Rules for $TARGET. Note that end product will be softlinked into the
1212
### Makefile's directory; on systems that don't support soft links it will
1213
### be copied. Note that MinGW's "ln -s" also actually copies the file.
1214
{exe:}$O/$(TARGET): {sourcedirs?$(OBJS)} {submakedirs?submakedirs} {~nmake?$(wildcard }$(EXTRA_OBJS){~nmake?)} {makefile}
1215
{exe:}{nmake:}	-@md $(O:/=\) 2>nul
1216
{exe:}{~nmake:}	@$(MKPATH) $O
1217
{gcclongline:}{exe:}	echo >.tmp$$$$ {sourcedirs?$(OBJS)} $(EXTRA_OBJS) $(WHOLE_ARCHIVE_ON) $(LIBS) $(WHOLE_ARCHIVE_OFF) $(OMNETPP_LIBS) && $(CXX) $(LDFLAGS) {-out}$O/$(TARGET) @.tmp$$$$ && rm .tmp$$$$
1218
{~gcclongline:}{exe:}	$({nmake?LINK}{~nmake?CXX}) $(LDFLAGS) {-out}$O/$(TARGET) {nmake_inlinefile} {sourcedirs?$(OBJS)} $(EXTRA_OBJS) $(WHOLE_ARCHIVE_ON) $(LIBS) $(WHOLE_ARCHIVE_OFF) $(OMNETPP_LIBS) {nmake_inlineend}
1219
{sharedlib:}$O/$(TARGET): {sourcedirs?$(OBJS)} {submakedirs?submakedirs} {~nmake?$(wildcard }$(EXTRA_OBJS){~nmake?)} {makefile}
1220
{sharedlib:}{nmake:}	-@md $(O:/=\) 2>nul
1221
{sharedlib:}{~nmake:}	@$(MKPATH) $O
1222
{gcclongline:}{sharedlib:}	echo >.tmp$$$$ {sourcedirs?$(OBJS)} $(EXTRA_OBJS) $(LIBS) $(OMNETPP_LIBS) $(LDFLAGS) && $(SHLIB_LD) {-out}$O/$(TARGET) @.tmp$$$$ && rm .tmp$$$$
1223
{~gcclongline:}{sharedlib:}	$(SHLIB_LD) {-out}$O/$(TARGET) {nmake_inlinefile} {sourcedirs?$(OBJS)} $(EXTRA_OBJS) $(LIBS) $(OMNETPP_LIBS) $(LDFLAGS) {nmake_inlineend}
1224
{sharedlib:}{~nmake:}	$(SHLIB_POSTPROCESS) $O/$(TARGET)
1225
{staticlib:}$O/$(TARGET): {sourcedirs?$(OBJS)} {submakedirs?submakedirs} {~nmake?$(wildcard }$(EXTRA_OBJS){~nmake?)} {makefile}
1226
{staticlib:}{nmake:}	-@md $(O:/=\) 2>nul
1227
{staticlib:}{~nmake:}	@$(MKPATH) $O
1228
{gcclongline:}{staticlib:}	echo >.tmp$$$$ {sourcedirs?$(OBJS)} $(EXTRA_OBJS) && $(AR) $O/$(TARGET) @.tmp$$$$ && rm .tmp$$$$
1229
{~gcclongline:}{staticlib:}	$(AR) {nmake?/out:}$O/$(TARGET) {nmake_inlinefile} {sourcedirs?$(OBJS)} $(EXTRA_OBJS) {nmake_inlineend}
1230
{nolink:}all: {sourcedirs?$(OBJS)} {submakedirs?submakedirs} {makefile}
1231
{nolink:}	@{nmake?rem}{~nmake?#} Do nothing
1232

    
1233
### Subdir targets. Note that nmake.exe does not support .PHONY (that's why we need
1234
### the *_dir targets), and does not print "Entering"/"Leaving" messages like gnu make.
1235
{submakedirs:}submakedirs: {@i:submakenames} {i}_dir{/@}
1236
{submakedirs:}
1237
{~nmake:}.PHONY:{@i:submakenames} {i}{/@}
1238
{~nmake:}{@i:submakenames}{i}: {i}_dir
1239
{~nmake:}{/@}
1240
{submakedirs:}
1241
{@i:submakenames,dir:submakedirs}{i}_dir:
1242
{nmake:}	echo [Entering {dir}] && cd {dir} && $(MAKE) && echo [Leaving {dir}]
1243
{~nmake:}	cd {dir} && $(MAKE)
1244

    
1245
{/@}
1246

    
1247
{sourcedirs:}.SUFFIXES: .{cc}
1248
{sourcedirs:}
1249
### Pattern rules for cc files. For gnu make a single rule is enough;
1250
### for nmake we need per-directory rules, plus one for "." (even though
1251
### "." is in sourcedirs), because nmake doesn't recognize "$O/." as "$O"
1252
{sourcedirs:}{~nmake:}$O/%.{obj}: %.{cc}
1253
{sourcedirs:}{~nmake:}	@$(MKPATH) $(dir $@)
1254
{sourcedirs:}{~nmake:}	$(CXX) -c $(COPTS) -o $@ $<
1255
{sourcedirs:}{nmake:}{lbrace}.{rbrace}.{cc}{lbrace}$O{rbrace}.{obj}:
1256
{sourcedirs:}{nmake:}	-@md $(O:/=\) 2>nul
1257
{sourcedirs:}{nmake:}	$(CXX) -c $(COPTS) /Fo"$@" -Tp $<
1258
{@dir:sourcedirs,bsdir:backslashedsourcedirs}
1259
{nmake:}{lbrace}{dir}{rbrace}.{cc}{lbrace}$O/{dir}{rbrace}.{obj}:
1260
{nmake:}	-@md $(O:/=\)\{bsdir} 2>nul
1261
{nmake:}	$(CXX) -c $(COPTS) /Fo"$@" -Tp $<
1262
{/@}
1263
### Pattern rules for msg files. For gnu make one rule is enough,
1264
### nmake needs per-file rules
1265
{sourcedirs:}{~nmake:}%_m.{cc} %_m.h: %.msg
1266
{sourcedirs:}{~nmake:}	$(MSGC) -s _m.{cc} $(MSGCOPTS) $?
1267
{sourcedirs:}{~nmake:}
1268
{@msg:msgfiles,m_cc:msgccfiles,m_h:msghfiles}
1269
{nmake:}{m_cc} {m_h} : {msg}
1270
{nmake:}	$(MSGC:/=\) -s _m.{cc} $(MSGCOPTS) {msg}
1271
{/@}
1272

    
1273
### Utility target for running opp_msgc; otherwise unused by this makefile
1274
msgheaders: {sourcedirs?$(MSGFILES:.msg=_m.h)}
1275
{@i:submakedirs}	cd {i} && $(MAKE) msgheaders
1276
{/@}
1277

    
1278
### clean, depend, etc.
1279
clean:
1280
{nmake:}	-rmdir /s /q $(O:/=\) 2>nul
1281
{nmake:}	-del $(VC_AUX_FILES) 2>nul
1282
{nmake:}{exe:}	-del $(TARGET) $(TARGET:.exe=.lib) $(TARGET:.exe=.dll) 2>nul
1283
{nmake:}{sharedlib:}	-del $(TARGET) $(TARGET:.dll=.exe) $(TARGET:.dll=.lib) 2>nul
1284
{nmake:}{staticlib:}	-del $(TARGET) $(TARGET:.lib=.exe) $(TARGET:.lib=.dll) 2>nul
1285
{nmake:}{nolink:}	-del {target}.exe {target}.dll {target}.lib 2>nul
1286
{~nmake:}	-rm -rf $O  ### must be done separately, because it fails on MinGW ('rm' is bogus)
1287
{~nmake:}	-rm -f {target} {target}.exe lib{target}.so lib{target}.a lib{target}.dll lib{target}.dylib
1288
{~nmake:}{@dir:sourcedirs}	-rm -f {dir}/*_m.{cc} {dir}/*_m.h
1289
{~nmake:}{/@}
1290
{nmake:}{@bsdir:backslashedsourcedirs}	-del {bsdir}\*_m.{cc} {bsdir}\*_m.h 2>nul
1291
{nmake:}{/@}
1292
{@i:submakedirs}	-cd {i} && $(MAKE) clean
1293
{/@}
1294

    
1295
cleanall: clean
1296
{nmake:}	-rmdir /s /q $(PROJECT_OUTPUT_DIR:/=\) 2>nul
1297
{~nmake:}	-rm -rf $(PROJECT_OUTPUT_DIR)
1298

    
1299
depend:
1300
{sourcedirs:}	$(MAKEDEPEND) $(INCLUDE_PATH) -f Makefile{nmake?.vc} -P{nmake?^}{~nmake?\$}$O/ -- $(MSG_CC_FILES) {@d:sourcedirs} {d}/*.{cc}{/@}
1301
{@i:submakedirs}{nmake:}	-cd {i} && if exist Makefile.vc $(MAKE) depend
1302
{~nmake:}	-cd {i} && if [ -f Makefile ]; then $(MAKE) depend; fi
1303
{/@}
1304

    
1305
{sourcedirs:}# DO NOT DELETE THIS LINE -- make depend depends on it.
1306
{sourcedirs:}{deps}
1307
ENDTEMPLATE
1308
}
1309

    
1310