cfad47cfa3/t3compiler/tads3/tcmakecl.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header$";
4
#endif
5
6
/* 
7
 *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
8
 *   
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
10
 *   on using and copying this software.  
11
 */
12
/*
13
Name
14
  tcmakecl.cpp - TADS Compiler "Make" command line tool
15
Function
16
  Command-line interface to "make" program build utility
17
Notes
18
  
19
Modified
20
  07/11/99 MJRoberts  - Creation
21
*/
22
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "os.h"
28
#include "t3_os.h"
29
#include "t3std.h"
30
#include "tcmake.h"
31
#include "tchostsi.h"
32
#include "vmerr.h"
33
#include "tcvsn.h"
34
#include "resload.h"
35
#include "tcmain.h"
36
#include "tclibprs.h"
37
#include "t3test.h"
38
#include "tccmdutl.h"
39
#include "rcmain.h"
40
41
/*
42
 *   Library parser.  This is a specialized version of the library parser
43
 *   that we use to expand library arguments into their corresponding source
44
 *   file lists.  
45
 */
46
class CTcLibParserCmdline: public CTcLibParser
47
{
48
public:
49
    /*
50
     *   Process a command-line argument that refers to a library.  We'll
51
     *   read the library and add each file mentioned in the library to the
52
     *   source module list.
53
     *   
54
     *   Returns zero on success, non-zero if any errors occur.  Fills in
55
     *   '*nodef' on return to indicate whether or not a "nodef" flag
56
     *   appeared in the library.  
57
     */
58
    static int process_lib_arg(CTcHostIfc *hostifc, CTcMake *mk,
59
                               CRcResList *res_list,
60
                               const char *lib_name, const char *lib_url,
61
                               int *nodef)
62
    {
63
        char path[OSFNMAX];
64
        char full_name[OSFNMAX];
65
66
        /* add a default "tl" extension to the library name */
67
        strcpy(full_name, lib_name);
68
        os_defext(full_name, "tl");
69
70
        /* extract the path name from the library's name */
71
        os_get_path_name(path, sizeof(path), lib_name);
72
73
        /* 
74
         *   add the directory containing the library to the include path,
75
         *   if it's not already there 
76
         */
77
        mk->maybe_add_include_path(path);
78
79
        /* set up our library parser object */
80
        CTcLibParserCmdline parser(hostifc, mk, res_list, full_name, lib_url);
81
82
        /* scan the library and add its files to the command line */
83
        parser.parse_lib();
84
85
        /* set the 'nodef' indication for the caller */
86
        *nodef = parser.nodef_;
87
88
        /* if there are any errors, return non-zero */
89
        return parser.get_err_cnt();
90
    }
91
92
protected:
93
    /* instantiate */
94
    CTcLibParserCmdline(CTcHostIfc *hostifc, CTcMake *mk,
95
                        CRcResList *res_list,
96
                        const char *lib_name, const char *lib_url)
97
        : CTcLibParser(lib_name)
98
    {
99
        /* remember our host interface */
100
        hostifc_ = hostifc;
101
102
        /* remember our 'make' control object */
103
        mk_ = mk;
104
105
        /* remember our resource list container */
106
        res_list_ = res_list;
107
108
        /* remember the URL to the library */
109
        lib_name_ = lib_copy_str(lib_name);
110
        lib_url_ = lib_copy_str(lib_url);
111
112
        /* no "nodef" flag yet */
113
        nodef_ = FALSE;
114
    }
115
116
    ~CTcLibParserCmdline()
117
    {
118
        /* delete the library name and URL strings */
119
        lib_free_str(lib_name_);
120
        lib_free_str(lib_url_);
121
    }
122
123
    /* process a source file entry in the library */
124
    void scan_full_source(const char *val, const char *fname)
125
    {
126
        char url[OSFNMAX*2 + 20];
127
        CTcMakeModule *mod;
128
        
129
        /* add the source module to our module list */
130
        mod = mk_->add_module(fname, 0, 0);
131
132
        /* 
133
         *   build the full URL for the module - this is the library URL
134
         *   prefix plus the "source:" value (not the full filename - simply
135
         *   the original unretouched value of the "source:" variable) 
136
         */
137
        sprintf(url, "%.*s%.*s", (int)OSFNMAX, lib_url_, (int)OSFNMAX, val);
138
139
        /* set the module's URL */
140
        mod->set_url(url);
141
142
        /* set the module's original name and library name */
143
        mod->set_orig_name(val);
144
        mod->set_from_lib(lib_name_, lib_url_);
145
    }
146
147
    /* process a sub-library entry in the library */
148
    void scan_full_library(const char *val, const char *fname)
149
    {
150
        char url[OSFNMAX*2 + 20];
151
        int nodef;
152
        
153
        /* 
154
         *   build the library URL prefix - this is the parent library URL
155
         *   prefix, plus our "library:" value (not the full filename -
156
         *   simply the original unretouched "library:" variable value),
157
         *   plus a slash 
158
         */
159
        sprintf(url, "%.*s%.*s/", (int)OSFNMAX, lib_url_, (int)OSFNMAX, val);
160
161
        /* parse the sub-library */
162
        if (process_lib_arg(hostifc_, mk_, res_list_, fname, url, &nodef))
163
        {
164
            /* 
165
             *   error parsing the sub-library - count it in our own error
166
             *   count, so we know the parsing failed 
167
             */
168
            ++err_cnt_;
169
        }
170
171
        /* 
172
         *   if the sub-library had a 'nodef' flag, count it as a 'nodef'
173
         *   flag in this library 
174
         */
175
        if (nodef)
176
            nodef_ = TRUE;
177
    }
178
179
    /* process a resource entry in the library */
180
    void scan_full_resource(const char *val, const char *fname)
181
    {
182
        /* add the resource to the list */
183
        res_list_->add_file(fname, val, TRUE);
184
    }
185
186
    /* display an error */
187
    void err_msg(const char *msg, ...)
188
    {
189
        va_list args;
190
191
        /* display the error */
192
        va_start(args, msg);
193
        hostifc_->v_print_err(msg, args);
194
        hostifc_->print_err("\n");
195
        va_end(args);
196
    }
197
198
    /* look up a preprocessor symbol */
199
    int get_pp_symbol(char *dst, size_t dst_len,
200
                      const char *sym_name, size_t sym_len)
201
    {
202
        const char *val;
203
        size_t val_len;
204
205
        /* ask the 'make' control object for the expansion */
206
        val = mk_->look_up_pp_sym(sym_name, sym_len);
207
208
        /* if we didn't find a value, return undefined */
209
        if (val == 0)
210
            return -1;
211
212
        /* if it fits in the caller's result buffer, copy it */
213
        val_len = strlen(val);
214
        if (val_len <= dst_len)
215
            memcpy(dst, val, val_len);
216
217
        /* return the value length */
218
        return val_len;
219
    }
220
221
    /* scan a "nodef" flag */
222
    void scan_nodef() { nodef_ = TRUE; }
223
224
    /* our 'make' control object */
225
    CTcMake *mk_;
226
227
    /* our resource list container */
228
    CRcResList *res_list_;
229
230
    /* host system interface */
231
    CTcHostIfc *hostifc_;
232
233
    /* the library name */
234
    char *lib_name_;
235
236
    /* 
237
     *   the library URL - this is the common prefix for the URL of every
238
     *   member of the library 
239
     */
240
    char *lib_url_;
241
242
    /* flag: we've seen a "nodef" flag */
243
    int nodef_;
244
};
245
246
247
/* ------------------------------------------------------------------------ */
248
/*
249
 *   Helper object for CTcCommandUtil::parse_opt_file 
250
 */
251
class CMainOptHelper: public CTcOptFileHelper
252
{
253
public:
254
    /* allocate an option string */
255
    char *alloc_opt_file_str(size_t len) { return lib_alloc_str(len); }
256
257
    /* free an option string previously allocated */
258
    void free_opt_file_str(char *str) { lib_free_str(str); }
259
260
    /* process a comment (we ignore comments) */
261
    void process_comment_line(const char *) { }
262
263
    /* process a non-comment line (ignore it) */
264
    void process_non_comment_line(const char *) { }
265
266
    /* process a configuration section line (ignore it) */
267
    void process_config_line(const char *, const char *, int) { }
268
};
269
270
/* ------------------------------------------------------------------------ */
271
/*
272
 *   Make a file path relative to the option file path, if we indeed have an
273
 *   option file path.  'buf' is a buffer the caller provides where we can
274
 *   build the full path, if necessary.  We'll return a pointer either to the
275
 *   buffer containing the combined path, or to the original filename if we
276
 *   decided not to use the relative path.  
277
 */
278
static char *make_opt_file_relative(char *buf, size_t buflen,
279
                                    int read_opt_file,
280
                                    const char *opt_file_path,
281
                                    char *fname)
282
{
283
    char lcl[OSFNMAX];
284
    
285
    /* 
286
     *   if we haven't read an option file, we don't have an option file path
287
     *   to use as the relative root, so use the original filename unchanged 
288
     */
289
    if (!read_opt_file)
290
        return fname;
291
292
    /* convert the name to local conventions */
293
    os_cvt_url_dir(buf, buflen < sizeof(lcl) ? buflen : sizeof(lcl),
294
                   fname, FALSE);
295
296
    /* if the filename is absolute, use it as given */
297
    if (os_is_file_absolute(buf))
298
        return buf;
299
300
    /* 
301
     *   we have a relative filename and an option file, so build the given
302
     *   name relative to the option file path, using the version that we
303
     *   converted to local conventions 
304
     */
305
    strcpy(lcl, buf);
306
    os_build_full_path(buf, buflen, opt_file_path, lcl);
307
308
    /* return the caller's buffer where we built the full path */
309
    return buf;
310
}
311
                                    
312
313
314
/* ------------------------------------------------------------------------ */
315
/*
316
 *   Program main entrypoint 
317
 */
318
int main(int argc, char **argv)
319
{
320
    CTcHostIfcStdio *hostifc = new CTcHostIfcStdio();
321
    int curarg;
322
    char *p;
323
    int force_build = FALSE;
324
    int force_link = FALSE;
325
    CTcMake *mk;
326
    int err_cnt, warn_cnt;
327
    int image_specified = FALSE;
328
    char dirbuf[OSFNMAX + 4096];
329
    int show_banner;
330
    const char *opt_file = 0;
331
    char opt_file_path[OSFNMAX];
332
    int read_opt_file = FALSE;
333
    int usage_err = FALSE;
334
    int add_def_mod = TRUE;
335
    int compile_only = FALSE;
336
    int pp_only = FALSE;
337
    osfildef *string_fp = 0;
338
    osfildef *assembly_fp = 0;
339
    int sym_dir_set = FALSE;
340
    int obj_dir_set = FALSE;
341
    int lib_err = FALSE;
342
    CMainOptHelper opt_helper;
343
    int opt_file_path_warning = FALSE;
344
    int need_opt_file_path_warning;
345
    const size_t SUPPRESS_LIST_MAX = 100;
346
    int suppress_list[SUPPRESS_LIST_MAX];
347
    size_t suppress_cnt;
348
    CTcMakePath *sys_inc_entry;
349
    CTcMakePath *sys_lib_entry;
350
    int verbose = FALSE;
351
    int pedantic = FALSE;
352
    int res_recurse = TRUE;
353
    CRcResList *res_list;
354
    int warnings_as_errors = FALSE;
355
356
    /* we don't have any warning codes to suppress yet */
357
    suppress_cnt = 0;
358
359
    /* no errors or warnings yet */
360
    err_cnt = warn_cnt = 0;
361
362
    /* create a resource list, in case we have resource options */
363
    res_list = new CRcResList();
364
365
    /* initialize the error subsystem */
366
    {
367
        CResLoader *res_loader;
368
        char buf[OSFNMAX];
369
370
        /* create a resource loader */
371
        os_get_special_path(buf, sizeof(buf), argv[0], OS_GSP_T3_RES);
372
        res_loader = new CResLoader(buf);
373
374
        /* tell the resource loader the executable filename, if possible */
375
        if (os_get_exe_filename(buf, sizeof(buf), argv[0]))
376
            res_loader->set_exe_filename(buf);
377
378
        /* initialize the error subsystem */
379
        CTcMain::tc_err_init(1024, res_loader);
380
381
        /* done with the resource loader */
382
        delete res_loader;
383
    }
384
385
    /* create the 'make' object */
386
    mk = new CTcMake();
387
388
    /* presume we'll show the banner and progress messages */
389
    show_banner = TRUE;
390
391
    /* 
392
     *   Add the default system header directory to the system include path.
393
     *   System include paths always follow any user-specified paths in the
394
     *   search order.  
395
     */
396
    os_get_special_path(dirbuf, sizeof(dirbuf), argv[0], OS_GSP_T3_INC);
397
    sys_inc_entry = mk->add_sys_include_path(dirbuf);
398
399
    /*
400
     *   Add the user library search path list to the source search path.
401
     *   This list comes from platform-specific global configuration data
402
     *   (such as a environment variable on Unix), so we want it to come
403
     *   after any search list specified in the command-line options; ensure
404
     *   that we search these locations last by making them "system" paths,
405
     *   since system paths follow all command-line paths.  
406
     */
407
    os_get_special_path(dirbuf, sizeof(dirbuf), argv[0], OS_GSP_T3_USER_LIBS);
408
    for (p = dirbuf ; *p != '\0' ; )
409
    {
410
        char *start;
411
        char *nxt;
412
413
        /* remember where this list element starts */
414
        start = p;
415
416
        /* find the next path separator character */
417
        for ( ; *p != '\0' && *p != OSPATHSEP ; ++p) ;
418
419
        /* if there's another path element, note its start */
420
        nxt = p;
421
        if (*nxt == OSPATHSEP)
422
            ++nxt;
423
424
        /* null-terminate the path at the separator */
425
        *p = '\0';
426
427
        /* add this path */
428
        mk->add_sys_source_path(start);
429
430
        /* continue scanning at the next list element */
431
        p = nxt;
432
    }
433
434
    /*
435
     *   Add the default system library directory to the source path.
436
     *   System source paths always follow user-specified paths in the
437
     *   search order.  
438
     */
439
    os_get_special_path(dirbuf, sizeof(dirbuf), argv[0], OS_GSP_T3_LIB);
440
    sys_lib_entry = mk->add_sys_source_path(dirbuf);
441
442
parse_options:
443
    /* read the options */
444
    for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg)
445
    {
446
        char subopt;
447
        char relbuf[OSFNMAX];
448
449
        /* 
450
         *   if we have a "-source" or "-lib" argument, we're done with
451
         *   options, and we're on to the module list 
452
         */
453
        if (strcmp(argv[curarg], "-source") == 0
454
            || strcmp(argv[curarg], "-lib") == 0
455
            || strcmp(argv[curarg], "-res") == 0)
456
            break;
457
458
        /* 
459
         *   figure out which option we have, starting with the first letter
460
         *   after the '-' 
461
         */
462
        switch(argv[curarg][1])
463
        {
464
        case 'f':
465
            /* read an options file - make sure we don't have one already */
466
            if (opt_file != 0)
467
            {
468
                /* can't read a file from a file */
469
                printf("error: only one option file (-f) is allowed\n");
470
                err_cnt = 1;
471
                goto done;
472
            }
473
474
            /* 
475
             *   remember the options file name - we'll get around to the
476
             *   actual reading of the file after we finish with the
477
             *   command line options 
478
             */
479
            opt_file = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1);
480
            if (opt_file == 0)
481
                goto missing_option_arg;
482
            break;
483
            
484
        case 'd':
485
            /* set debug mode */
486
            mk->set_debug(TRUE);
487
            break;
488
489
        case 'D':
490
            /* add preprocessor symbol definition */
491
            p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1);
492
            if (p != 0)
493
            {
494
                char *eq;
495
496
                /* see if there's an '=' in the string */
497
                for (eq = p ; *eq != '\0' && *eq != '=' ; ++eq) ;
498
499
                /* if the '=' is present, replace it with a null byte */
500
                if (*eq == '=')
501
                {
502
                    /* replace it */
503
                    *eq = '\0';
504
505
                    /* skip to the start of the replacement text */
506
                    ++eq;
507
                }
508
509
                /* add the symbol definition */
510
                mk->def_pp_sym(p, eq);
511
            }
512
            else
513
                goto missing_option_arg;
514
            break;
515
516
        case 'e':
517
            /* check for longer option names */
518
            if (strlen(argv[curarg]) >= 7
519
                && memcmp(argv[curarg], "-errnum", 7) == 0)
520
            {
521
                /* check for "+" or "-" suffixes */
522
                if (strcmp(argv[curarg] + 7, "+") == 0
523
                    || argv[curarg][7] == '\0')
524
                {
525
                    /* turn on error number display */
526
                    mk->set_show_err_numbers(TRUE);
527
                }
528
                else if (strcmp(argv[curarg] + 7, "-") == 0)
529
                {
530
                    /* turn off error number display */
531
                    mk->set_show_err_numbers(FALSE);
532
                }
533
                else
534
                {
535
                    /* invalid option */
536
                    goto bad_option;
537
                }
538
            }
539
            else
540
            {
541
                /* invalid option */
542
                goto bad_option;
543
            }
544
            break;
545
546
        case 'U':
547
            /* add preprocess symbol un-definition */
548
            p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1);
549
            if (p != 0)
550
                mk->undef_pp_sym(p);
551
            else
552
                goto missing_option_arg;
553
            break;
554
555
        case 'c':
556
            /* see what follows */
557
            switch (argv[curarg][2])
558
            {
559
            case 's':
560
                /* it's a character set specification */
561
                p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2);
562
                if (p != 0)
563
                    mk->set_source_charset(p);
564
                else
565
                    goto missing_option_arg;
566
                break;
567
568
            case 'l':
569
                /* check for "-clean" */
570
                if (strcmp(argv[curarg], "-clean") == 0)
571
                {
572
                    /* set "clean" mode */
573
                    mk->set_clean_mode(TRUE);
574
                }
575
                else
576
                {
577
                    /* invalid option */
578
                    goto bad_option;
579
                }
580
                break;
581
582
            case '\0':
583
                /* set compile-only (no link) mode */
584
                mk->set_do_link(FALSE);
585
                compile_only = TRUE;
586
                break;
587
588
            default:
589
                /* invalid option */
590
                goto bad_option;
591
            }
592
            break;
593
594
        case 'a':
595
            /* see what follows */
596
            switch(argv[curarg][2])
597
            {
598
            case 'l':
599
                /* force only the link phase */
600
                force_link = TRUE;
601
                break;
602
603
            case '\0':
604
                force_build = TRUE;
605
                break;
606
607
            default:
608
                /* invalid option */
609
                goto bad_option;
610
            }
611
            break;
612
            
613
        case 'v':
614
            /* set verbose mode */
615
            mk->set_verbose(TRUE);
616
            verbose = TRUE;
617
            break;
618
            
619
        case 'I':
620
            /* add a #include path */
621
            p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1);
622
            if (p != 0)
623
            {
624
                /* make it relative to the option file path if appropriate */
625
                p = make_opt_file_relative(relbuf, sizeof(relbuf),
626
                                           read_opt_file, opt_file_path, p);
627
                
628
                /* add the path */
629
                mk->add_include_path(p);
630
            }
631
            else
632
                goto missing_option_arg;
633
            break;
634
            
635
        case 'o':
636
            /* set the image file name */
637
            p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1);
638
            if (p != 0)
639
            {
640
                /* make it relative to the option file path if appropriate */
641
                p = make_opt_file_relative(relbuf, sizeof(relbuf),
642
                                           read_opt_file, opt_file_path, p);
643
644
                /* set the image file name */
645
                mk->set_image_file(p);
646
647
                /* note that we have an explicit image file name */
648
                image_specified = TRUE;
649
            }
650
            else
651
                goto missing_option_arg;
652
            break;
653
654
        case 'O':
655
            switch(argv[curarg][2])
656
            {
657
            case 's':
658
                /* 
659
                 *   if we already have a string capture file, close the
660
                 *   old one 
661
                 */
662
                if (string_fp != 0)
663
                {
664
                    osfcls(string_fp);
665
                    string_fp = 0;
666
                }
667
                
668
                /* set the string file */
669
                p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2);
670
                if (p == 0)
671
                    goto missing_option_arg;
672
673
                /* make it relative to the option file path if appropriate */
674
                p = make_opt_file_relative(relbuf, sizeof(relbuf),
675
                                           read_opt_file, opt_file_path, p);
676
677
                /* open the string file */
678
                string_fp = osfopwt(p, OSFTTEXT);
679
                if (string_fp == 0)
680
                {
681
                    printf("error: unable to create string capture file "
682
                           "\"%s\"\n", p);
683
                    goto done;
684
                }
685
686
                /* set the string capture file in the build object */
687
                mk->set_string_capture(string_fp);
688
689
                /* done */
690
                break;
691
692
            default:
693
                /* invalid suboption */
694
                goto bad_option;
695
            }
696
            break;
697
698
        case 'F':
699
            /* presume we won't need an option file path warning */
700
            need_opt_file_path_warning = FALSE;
701
702
            /* note the sub-option letter */
703
            subopt = argv[curarg][2];
704
705
            /* get the filename/path argument */
706
            p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2);
707
            if (p == 0)
708
                goto missing_option_arg;
709
710
            /* make it relative to the option file path */
711
            p = make_opt_file_relative(relbuf, sizeof(relbuf),
712
                                       read_opt_file, opt_file_path, p);
713
714
            /* 
715
             *   if this is an absolute path, note it so we can warn about it
716
             *   if this is in an option file 
717
             */
718
            need_opt_file_path_warning = os_is_file_absolute(p);
719
720
            /* check the suboption */
721
            switch(subopt)
722
            {
723
            case 'L':
724
                /* override the system library path */
725
                sys_lib_entry->set_path(p);
726
                break;
727
728
            case 'I':
729
                /* override the system include path */
730
                sys_inc_entry->set_path(p);
731
                break;
732
733
            case 's':
734
                /* add the source path */
735
                mk->add_source_path(p);
736
                break;
737
                
738
            case 'y':
739
                /* set the symbol path */
740
                mk->set_symbol_dir(p);
741
742
                /* remember that we have a symbol directory */
743
                sym_dir_set = TRUE;
744
                break;
745
            
746
            case 'o':
747
                /* set the object file directory */
748
                mk->set_object_dir(p);
749
750
                /* remember that we've set this path */
751
                obj_dir_set = TRUE;
752
                break;
753
754
            case 'a':
755
                /* 
756
                 *   Set the assembly listing file.  Note that this is an
757
                 *   undocumented option - at the moment, the assembly
758
                 *   listing is a bit rough, and is meant for internal TADS 3
759
                 *   development use, to facilitate analysis of the
760
                 *   compiler's code generation.  
761
                 */
762
763
                /* close any previous assembly listing file */
764
                if (assembly_fp != 0)
765
                {
766
                    osfcls(assembly_fp);
767
                    assembly_fp = 0;
768
                }
769
770
                /* open the listing file */
771
                assembly_fp = osfopwt(p, OSFTTEXT);
772
                if (assembly_fp == 0)
773
                {
774
                    printf("error: unable to create assembly listing file "
775
                           "\"%s\"\n", p);
776
                    goto done;
777
                }
778
779
                /* set the listing file */
780
                mk->set_assembly_listing(assembly_fp);
781
782
                /* done */
783
                break;
784
785
            default:
786
                /* invalid option */
787
                goto bad_option;
788
            }
789
790
            /* 
791
             *   if we're reading from a command file, and we haven't already
792
             *   found reason to warn about absolute paths, note that we need
793
             *   to warn now 
794
             */
795
            if (read_opt_file
796
                && need_opt_file_path_warning)
797
                opt_file_path_warning = TRUE;
798
799
            /* done */
800
            break;
801
802
        case 'G':
803
            /* code generation options */
804
            if (strcmp(argv[curarg], "-Gstg") == 0)
805
            {
806
                /* turn on sourceTextGroup mode */
807
                mk->set_source_text_group_mode(TRUE);
808
            }
809
            else
810
                goto bad_option;
811
            break;
812
813
        case 'n':
814
            /* check what we have */
815
            if (strcmp(argv[curarg], "-nopre") == 0)
816
            {
817
                /* explicitly turn off preinit mode */
818
                mk->set_preinit(FALSE);
819
            }
820
            else if (strcmp(argv[curarg], "-nobanner") == 0)
821
            {
822
                /* turn off the banner */
823
                show_banner = FALSE;
824
            }
825
            else if (strcmp(argv[curarg], "-nodef") == 0)
826
            {
827
                /* don't add the default modules */
828
                add_def_mod = FALSE;
829
            }
830
            else
831
                goto bad_option;
832
            break;
833
834
        case 'p':
835
            /* check what we have */
836
            if (strcmp(argv[curarg], "-pre") == 0)
837
            {
838
                /* explicitly turn on preinit mode */
839
                mk->set_preinit(TRUE);
840
            }
841
            else
842
                goto bad_option;
843
            break;
844
845
        case 'P':
846
            /* 
847
             *   A preprocess-only mode.  The plain -P generates
848
             *   preprocessed source to standard output; -Pi generates only
849
             *   a list of names of #included files to standard output.  
850
             */
851
            if (strcmp(argv[curarg], "-P") == 0)
852
            {
853
                /* set preprocess-only mode */
854
                mk->set_pp_only(TRUE);
855
                pp_only = TRUE;
856
            }
857
            else if (strcmp(argv[curarg], "-Pi") == 0)
858
            {
859
                /* set list-includes mode */
860
                mk->set_list_includes(TRUE);
861
                pp_only = TRUE;
862
            }
863
            break;
864
865
        case 'q':
866
            /* check the full option name */
867
            if (strcmp(argv[curarg], "-quotefname") == 0)
868
            {
869
                /* they want quoted filenames in error messages */
870
                mk->set_err_quoted_fnames(TRUE);
871
            }
872
            else if (strcmp(argv[curarg], "-q") == 0)
873
            {
874
                /* quiet mode - turn off banner and progress messages */
875
                show_banner = FALSE;
876
                hostifc->set_show_progress(FALSE);
877
            }
878
            else
879
                goto bad_option;
880
            break;
881
882
        case 's':
883
            /* check the full option name */
884
            if (strcmp(argv[curarg], "-statprefix") == 0)
885
            {
886
                /* get the argument */
887
                p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 10);
888
                
889
                /* set the status message prefix text */
890
                if (p != 0)
891
                    hostifc->set_status_prefix(p);
892
                else
893
                    goto missing_option_arg;
894
            }
895
            else
896
            {
897
                /* unknown option - report usage error */
898
                goto bad_option;
899
            }
900
            break;
901
902
        case 't':
903
            /* check the full option name */
904
            if (strcmp(argv[curarg], "-test") == 0)
905
            {
906
                /* 
907
                 *   it's the secret test-mode option - the test scripts use
908
                 *   this to set the reporting mode so that we suppress path
909
                 *   names in progress reports, so that the test logs can be
910
                 *   insensitive to local path name conventions 
911
                 */
912
                mk->set_test_report_mode(TRUE);
913
914
                /* perform any necessary test initialization */
915
                test_init();
916
            }
917
            else
918
            {
919
                /* unknown option - report usage error */
920
                goto bad_option;
921
            }
922
            break;
923
924
        case 'w':
925
            /* warning level/mode - see what level they're setting */
926
            switch(argv[curarg][2])
927
            {
928
                int num;
929
                int enable;
930
931
            case '0':
932
                /* no warnings */
933
                mk->set_warnings(FALSE);
934
                mk->set_pedantic(FALSE);
935
                break;
936
937
            case '1':
938
                /* normal warnings, but not pedantic warnings */
939
                mk->set_warnings(TRUE);
940
                mk->set_pedantic(FALSE);
941
                break;
942
943
            case '2':
944
                /* show all warnings, even pedantic ones */
945
                mk->set_warnings(TRUE);
946
                mk->set_pedantic(TRUE);
947
                pedantic = TRUE;
948
                break;
949
950
            case '+':
951
            case '-':
952
                /* 
953
                 *   Add/remove a warning to/from the list of warnings to
954
                 *   suppress.  First, note whether we're enabling or
955
                 *   disabling the warning.  
956
                 */
957
                enable = (argv[curarg][2] == '+');
958
959
                /* get the warning number */
960
                p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2);
961
                if (p == 0)
962
                    goto missing_option_arg;
963
964
                /* convert it to a number */
965
                num = atoi(p);
966
967
                /* 
968
                 *   add it to the list if we're disabling it, otherwise
969
                 *   remove it from the list (since every warning is enabled
970
                 *   by default) 
971
                 */
972
                if (!enable)
973
                {
974
                    /* if we don't have room, we can't add it */
975
                    if (suppress_cnt >= SUPPRESS_LIST_MAX)
976
                    {
977
                        /* tell them what's wrong */
978
                        printf("Too many -w-# options - maximum %d.\n",
979
                               (int)SUPPRESS_LIST_MAX);
980
981
                        /* abort */
982
                        goto done;
983
                    }
984
985
                    /* add the new list entry */
986
                    suppress_list[suppress_cnt++] = num;
987
                }
988
                else
989
                {
990
                    size_t rem;
991
                    int *src;
992
                    int *dst;
993
994
                    /* 
995
                     *   scan the list for an existing instance of this
996
                     *   number, and remove any we find 
997
                     */
998
                    for (rem = suppress_cnt, src = dst = suppress_list ;
999
                         rem != 0 ; ++src, --rem)
1000
                    {
1001
                        /* if this one isn't to be suppressed, keep it */
1002
                        if (*src != num)
1003
                            *dst++ = *src;
1004
                    }
1005
1006
                    /* calculate the new count */
1007
                    suppress_cnt = dst - suppress_list;
1008
                }
1009
1010
                /* remember the updated list */
1011
                mk->set_suppress_list(suppress_list, suppress_cnt);
1012
1013
                /* done */
1014
                break;
1015
1016
            case 'e':
1017
                /* turn "warnings as errors" mode on or off */
1018
                switch (argv[curarg][3])
1019
                {
1020
                case '\0':
1021
                case '+':
1022
                    warnings_as_errors = TRUE;
1023
                    mk->set_warnings_as_errors(TRUE);
1024
                    break;
1025
1026
                case '-':
1027
                    warnings_as_errors = FALSE;
1028
                    mk->set_warnings_as_errors(FALSE);
1029
                    break;
1030
1031
                default:
1032
                    goto bad_option;
1033
                }
1034
                break;
1035
1036
            default:
1037
                /* invalid option */
1038
                goto bad_option;
1039
            }
1040
            break;
1041
1042
        case '?':
1043
            /* take '?' as an explicit request to show the usage message */
1044
            goto show_usage;
1045
1046
        case 'h':
1047
            /* check for "-help" */
1048
            if (strcmp(argv[curarg], "-help") == 0)
1049
                goto show_usage;
1050
1051
            /* other '-h*' options are unrecognized */
1052
            goto bad_option;
1053
            
1054
        default:
1055
        bad_option:
1056
            /* invalid - describe the problem */
1057
            printf("Error: Invalid option: \"%s\"\n"
1058
                   "(Type \"t3make -help\" for a summary of the "
1059
                   "command syntax)\n", argv[curarg]);
1060
1061
            /* abort */
1062
            goto done;
1063
1064
        missing_option_arg:
1065
            /* missing option argument */
1066
            printf("Error: Missing argument for option \"%s\"\n"
1067
                   "(Type \"t3make -help\" for a summary of the "
1068
                   "command syntax)\n", argv[curarg]);
1069
1070
            /* abort */
1071
            goto done;
1072
        }
1073
    }
1074
1075
    /* 
1076
     *   if there are no module arguments, and no project file was
1077
     *   specified, look for a default project file 
1078
     */
1079
    if (curarg >= argc && !usage_err && opt_file == 0)
1080
    {
1081
        /* 
1082
         *   if the default project file exists, try reading it; otherwise,
1083
         *   show a usage error 
1084
         */
1085
        if (!osfacc(T3_DEFAULT_PROJ_FILE))
1086
            opt_file = T3_DEFAULT_PROJ_FILE;
1087
        else
1088
        {
1089
            /* 
1090
             *   if there are no arguments at all, just show the usage
1091
             *   message; otherwise, explain that we need some source files 
1092
             */
1093
            if (argc == 1)
1094
            {
1095
                /* just fall through to the usage message */
1096
                usage_err = TRUE;
1097
            }
1098
            else
1099
            {
1100
                /* explain that we need source files */
1101
                printf("Error: No source file(s) specified.\n"
1102
                       "(Type \"t3make -help\" for help with the "
1103
                       "command syntax.\n");
1104
                goto done;
1105
            }
1106
        }
1107
    }
1108
1109
    /* 
1110
     *   Show the banner, unless they explicitly asked us not to (and even
1111
     *   then, show it anyway if we're going to show the "usage" message).
1112
     */
1113
    if ((show_banner || usage_err) && (opt_file == 0 || read_opt_file))
1114
    {
1115
        char patch_id[10];
1116
        
1117
        /* 
1118
         *   Generate the patch number or development build iteration number,
1119
         *   if appropriate.  If there's a patch number, give it precedence,
1120
         *   appending it as an extra dot-number suffix; if there's a build
1121
         *   number, show it as an alphabetic suffix.  If neither is defined,
1122
         *   add nothing, so we'll just have a three-part dotted version
1123
         *   number.  
1124
         */
1125
        if (TC_VSN_PATCH != 0)
1126
            sprintf(patch_id, ".%d", TC_VSN_PATCH);
1127
        else if (TC_VSN_DEVBUILD != 0)
1128
            sprintf(patch_id, "%c", TC_VSN_DEVBUILD + 'a' - 1);
1129
        else
1130
            patch_id[0] = '\0';
1131
1132
        /* show the banner */
1133
        /* copyright-date-string */
1134
        printf("TADS Compiler %d.%d.%d%s  "
1135
               "Copyright 1999, 2007 Michael J. Roberts\n",
1136
               TC_VSN_MAJOR, TC_VSN_MINOR, TC_VSN_REV, patch_id);
1137
    }
1138
1139
    /* 
1140
     *   warn about absolute paths in the options file, if necessary; do this
1141
     *   only in pedantic mode, though, since it's only a usage suggestion 
1142
     */
1143
    if (opt_file_path_warning && pedantic)
1144
    {
1145
        /* show the warning */
1146
        printf("Warning: absolute path found in -Fs/-Fo/-Fy option "
1147
               "in options (-f) file.\n");
1148
1149
        /* add more explanation if in verbose mode */
1150
        if (verbose)
1151
            printf("It's better not to use absolute paths in the "
1152
                   "options file, because this\n"
1153
                   "ties the options file to the particular "
1154
                   "directory layout of your machine.  \n"
1155
                   "If possible, refer only to subfolders of the "
1156
                   "folder containing the options\n"
1157
                   "file, and refer to the subfolders using "
1158
                   "relative path notation.\n");
1159
    }
1160
1161
    /* if a usage error occurred, display the usage message */
1162
    if (usage_err)
1163
    {
1164
    show_usage:
1165
        printf("usage: t3make [options] module ... [-res resources]\n"
1166
               "Options:\n"
1167
               "  -a      - rebuild all files, "
1168
               "even if up to date\n"
1169
               "  -al     - relink, even if image file is up to date\n"
1170
               "  -c      - compile only (do not create image file)\n"
1171
               "  -clean  - delete all derived (symbol, object, image) "
1172
               "files\n"
1173
               "  -cs xxx - source file character set is 'xxx'\n"
1174
               "  -d      - compile for debugging (include symbols)\n"
1175
               "  -D x=y  - define preprocessor symbol 'x' with value 'y'\n"
1176
               "  -errnum - show numeric error codes with error messages\n"
1177
               "  -f file - read command line options from 'file'\n"
1178
               "  -I dir  - add 'dir' to #include search path\n"
1179
               "  -Fs dir - add 'dir' to source file search path\n"
1180
               "  -Fy dir - put symbol files in directory 'dir'\n"
1181
               "  -Fo dir - put object files in directory 'dir'\n"
1182
               "  -FI dir - override the standard system include path\n"
1183
               "  -FL dir - override the standard system library path\n"
1184
               "  -Gstg   - generate sourceTextGroup properties\n"
1185
               "  -nobanner - suppress version/copyright banner\n"
1186
               "  -nodef  - do not include default library modules in build\n"
1187
               "  -nopre  - do not run pre-initialization, regardless of "
1188
               "debug mode\n"
1189
               "  -o img  - set image file name to 'img'\n"
1190
               "  -Os str - write all strings found in compiled files to "
1191
               "text file 'str'\n"
1192
               "  -P      - preprocess only; write preprocessed source "
1193
               "to console\n"
1194
               "  -Pi     - preprocess only; write only list of #include "
1195
               "files to console\n"
1196
               "  -pre    - run pre-initialization, regardless of "
1197
               "debug mode\n"
1198
               "  -q      - quiet mode: suppress banner and "
1199
               "progress reports\n"
1200
               "  -quotefname - quote filenames in error messages\n"
1201
               "  -statprefix txt - set status-message prefix text\n"
1202
               "  -U x    - un-define preprocessor symbol 'x'\n"
1203
               "  -v      - display verbose error messages\n"
1204
               "  -w#     - warning level: 0=none, 1=standard "
1205
               "(default), 2=pedantic\n"
1206
               "  -w-#    - suppress the given warning number\n"
1207
               "  -w+#    - enable the given warning number\n"
1208
               "  -we     - treat warnings as errors (-we- to disable)\n"
1209
               "\n"
1210
               "Modules: Each entry in the module list can have one "
1211
               "of these forms:\n"
1212
               "\n"
1213
               "  filename           - specify the name of a source or "
1214
               "library file; the\n"
1215
               "                       file's type is inferred from its "
1216
               "filename suffix\n"
1217
               "  -source filename   - specify the name of a source file\n"
1218
               "  -lib filename      - specify the name of a library\n"
1219
               "\n"
1220
               "The default filename suffix for a source file is \".t\", "
1221
               "and the default for a\n"
1222
               "library is \".tl\".  You can omit the \"-source\" or "
1223
               "\"-lib\" specifier for any\n"
1224
               "file if its name includes the correct default suffix for "
1225
               "its type, and its\n"
1226
               "name doesn't begin with a \"-\".\n"
1227
               "Immediately following each library, you can add one or more "
1228
               "\"-x filename\"\n"
1229
               "options to exclude library members.  Each \"-x\" excludes "
1230
               "one library member.\n"
1231
               "The filename specified in a \"-x\" option must exactly "
1232
               "match the filename as it\n"
1233
               "appears in the library \"source:\" line.\n"
1234
               "\n"
1235
               "If no modules are specified, the compiler will attempt to "
1236
               "find '" T3_DEFAULT_PROJ_FILE "'\n"
1237
               "in the current directory, and will read it to obtain build "
1238
               "instructions.\n"
1239
               "\n"
1240
               "Resources: Multi-media resources can be bundled into the "
1241
               "image file using the\n"
1242
               "-res option.  After the -res option, specify any desired "
1243
               "resource options:\n"
1244
               "\n"
1245
               "  -recurse   - recurse into subdirectories for subsequent "
1246
               "items (default)\n"
1247
               "  -norecurse - do not recurse into subdirectories for "
1248
               "subsequent items\n"
1249
               "  file       - add 'file' as a multimedia resource\n"
1250
               "  file=alias - add 'file', using 'alias' as the resource "
1251
               "name\n"
1252
               "  dir        - add all files within directory 'dir' (and all "
1253
               "files in all\n"
1254
               "               subdirectories of 'dir', if -recurse option "
1255
               "is in effect)\n"
1256
               "\n"
1257
               "When a file is specified without an '=alias' name, the "
1258
               "stored resource name is\n"
1259
               "the original filename converted to URL-style notation.  If "
1260
               "an '=alias' name is\n"
1261
               "specified, then the stored resource will be named with the "
1262
               "alias name.\n"
1263
               "\n"
1264
               "Note that resource options are ignored when compiling "
1265
               "for debugging.\n");
1266
1267
        /* terminate */
1268
        goto done;
1269
    }
1270
1271
    /* add the modules */
1272
    for ( ; curarg < argc ; ++curarg)
1273
    {
1274
        int is_source;
1275
        int is_lib;
1276
        char relbuf[OSFNMAX];
1277
1278
        /* we don't know the file type yet */
1279
        is_source = FALSE;
1280
        is_lib = FALSE;
1281
1282
        /* check to see if we have a module type option */
1283
        if (argv[curarg][0] == '-')
1284
        {
1285
            if (strcmp(argv[curarg], "-source") == 0)
1286
            {
1287
                /* note that we have a source file, and skip the option */
1288
                is_source = TRUE;
1289
                ++curarg;
1290
            }
1291
            else if (strcmp(argv[curarg], "-lib") == 0)
1292
            {
1293
                /* note that we have a library, and skip the option */
1294
                is_lib = TRUE;
1295
                ++curarg;
1296
            }
1297
            else if (strcmp(argv[curarg], "-res") == 0)
1298
            {
1299
                /* 
1300
                 *   Resource arguments follow - we're done with modules.
1301
                 *   Skip the "-res" argument and exit the module loop.  
1302
                 */
1303
                ++curarg;
1304
                break;
1305
            }
1306
            else
1307
            {
1308
                /* invalid option in the module list */
1309
                goto bad_option;
1310
            }
1311
1312
            /* 
1313
             *   if we have no filename argument following the type
1314
             *   specifier, it's an error 
1315
             */
1316
            if (curarg == argc)
1317
            {
1318
                --curarg;
1319
                goto missing_option_arg;
1320
            }
1321
        }
1322
1323
        /* 
1324
         *   if we didn't find an explicit type specifier, infer the type
1325
         *   from the filename suffix 
1326
         */
1327
        if (!is_source && !is_lib)
1328
        {
1329
            size_t len;
1330
1331
            /* 
1332
             *   if we have a ".tl" suffix, assume it's a library file;
1333
             *   otherwise, assume it's a source file 
1334
             */
1335
            if ((len = strlen(argv[curarg])) > 3
1336
                && stricmp(argv[curarg] + len - 3, ".tl") == 0)
1337
            {
1338
                /* ".tl" - it's a library file */
1339
                is_lib = TRUE;
1340
            }
1341
            else
1342
            {
1343
                /* something else - assume it's a source file */
1344
                is_source = TRUE;
1345
            }
1346
        }
1347
1348
        /* make the filename relative to the option file path */
1349
        p = make_opt_file_relative(relbuf, sizeof(relbuf),
1350
                                   read_opt_file, opt_file_path,
1351
                                   argv[curarg]);
1352
1353
        /* process the file according to its type */
1354
        if (is_source)
1355
        {
1356
            CTcMakeModule *mod;
1357
1358
            /* add this file to the module list */
1359
            mod = mk->add_module(p, 0, 0);
1360
1361
            /* set the module's original name to the name as given */
1362
            mod->set_orig_name(argv[curarg]);
1363
1364
            /* 
1365
             *   if no image has been specified yet already, use this
1366
             *   module's name as the image name, with the suffix ".t3" 
1367
             */
1368
            if (!image_specified)
1369
            {
1370
                char buf[OSFNMAX];
1371
1372
                /* 
1373
                 *   build the default image filename by changing the
1374
                 *   module's extension to "t3" (or adding the "t3"
1375
                 *   extension if the module didn't have one to begin with) 
1376
                 */
1377
                strcpy(buf, p);
1378
                os_remext(buf);
1379
                os_addext(buf, "t3");
1380
1381
                /* set the filename */
1382
                mk->set_image_file(buf);
1383
1384
                /* note that we know the image file's name now */
1385
                image_specified = TRUE;
1386
            }
1387
        }
1388
        else
1389
        {
1390
            CTcMakeModule lib_mod;
1391
            CTcMakeModule *last_pre_lib_mod;
1392
            char fname[OSFNMAX];
1393
            char orig_fname[OSFNMAX];
1394
            int nodef;
1395
1396
            /* add a default "tl" extension */
1397
            strcpy(fname, p);
1398
            os_defext(fname, "tl");
1399
1400
            /* likewise, add a default "tl" extension to the original name */
1401
            strcpy(orig_fname, argv[curarg]);
1402
            os_defext(orig_fname, "tl");
1403
1404
            /* 
1405
             *   set up a module for the library, so we can easily search
1406
             *   the source path for the module 
1407
             */
1408
            lib_mod.set_module_name(fname);
1409
            lib_mod.set_search_source_name(orig_fname);
1410
1411
            /* get the module name by searching the path if necessary */
1412
            mk->get_srcfile(fname, &lib_mod);
1413
1414
            /* remember the last module before this library's modules */
1415
            last_pre_lib_mod = mk->get_last_module();
1416
                
1417
            /* 
1418
             *   Process the library.  Since this is a top-level library
1419
             *   (rather than a sub-library included from within another
1420
             *   library), there is no prefix to the URL's of the member
1421
             *   items. 
1422
             */
1423
            if (CTcLibParserCmdline::process_lib_arg(
1424
                hostifc, mk, res_list, fname, "", &nodef))
1425
                lib_err = TRUE;
1426
1427
            /* if there was a 'nodef' flag, note it */
1428
            if (nodef)
1429
                add_def_mod = FALSE;
1430
1431
            /* process any exclusion options */
1432
            while (curarg + 1 < argc && strcmp(argv[curarg+1], "-x") == 0)
1433
            {
1434
                CTcMakeModule *mod;
1435
                const char *url;
1436
1437
                /* skip to the "-x" */
1438
                ++curarg;
1439
                
1440
                /* if the filename is missing, report a usage error */
1441
                if (curarg + 1 >= argc)
1442
                    goto missing_option_arg;
1443
1444
                /* skip to the -x's argument and retrieve it */
1445
                url = argv[++curarg];
1446
1447
                /* 
1448
                 *   Start with the next module after the last module before
1449
                 *   this library.  If there was nothing before this library,
1450
                 *   then this library's first module is the first module in
1451
                 *   the entire program,.  
1452
                 */
1453
                if ((mod = last_pre_lib_mod) != 0)
1454
                    mod = mod->get_next();
1455
                else
1456
                    mod = mk->get_first_module();
1457
1458
                /* scan the list of library modules for a match */
1459
                for ( ; mod != 0 ; mod = mod->get_next())
1460
                {
1461
                    /* if this module has the excluded URL, exclude it */
1462
                    if (stricmp(mod->get_url(), argv[curarg]) == 0)
1463
                    {
1464
                        /* mark this module as excluded */
1465
                        mod->set_excluded(TRUE);
1466
1467
                        /* 
1468
                         *   no need to look any further - assume each URL
1469
                         *   is unique 
1470
                         */
1471
                        break;
1472
                    }
1473
                }
1474
            }
1475
        }
1476
    }
1477
1478
    /* if we have any more arguments, they're for resource files */
1479
    for ( ; curarg < argc ; ++curarg)
1480
    {
1481
        char relbuf[OSFNMAX];
1482
1483
        /* check for a resource bundler option */
1484
        if (argv[curarg][0] == '-')
1485
        {
1486
            /* check the argument */
1487
            switch(argv[curarg][1])
1488
            {
1489
            case 'n':
1490
                /* check for '-norecurse' */
1491
                if (strcmp(argv[curarg], "-norecurse") == 0)
1492
                    res_recurse = FALSE;
1493
                else
1494
                    goto bad_option;
1495
                break;
1496
1497
            case 'r':
1498
                /* check for '-recurse' */
1499
                if (strcmp(argv[curarg], "-recurse") == 0)
1500
                    res_recurse = TRUE;
1501
                else
1502
                    goto bad_option;
1503
                break;
1504
1505
            default:
1506
                /* unknown option */
1507
                goto bad_option;
1508
            }
1509
        }
1510
        else
1511
        {
1512
            char *p;
1513
            char *fname;
1514
            char *alias;
1515
1516
            /* 
1517
             *   It's not an option, so it must be a file.  Scan for an
1518
             *   alias, which is introduced with an '=' character.  
1519
             */
1520
            for (p = fname = argv[curarg] ; *p != '\0' && *p != '=' ; ++p) ;
1521
            if (*p == '=')
1522
            {
1523
                /* 
1524
                 *   overwrite the '=' with a null byte, so that the filename
1525
                 *   ends here 
1526
                 */
1527
                *p = '\0';
1528
1529
                /* the alias starts after the '=' */
1530
                alias = p + 1;
1531
            }
1532
            else
1533
            {
1534
                /* there's no alias */
1535
                alias = 0;
1536
            }
1537
1538
            /* make the filename relative to the option file path */
1539
            fname = make_opt_file_relative(relbuf, sizeof(relbuf),
1540
                                           read_opt_file, opt_file_path,
1541
                                           fname);
1542
1543
            /* add this file to the resource list*/
1544
            res_list->add_file(fname, alias, res_recurse);
1545
        }
1546
    }
1547
1548
    /*
1549
     *   If an option file was specified or implied, and we haven't read
1550
     *   it yet, read it now.  
1551
     */
1552
    if (opt_file != 0 && !read_opt_file)
1553
    {
1554
        osfildef *fp;
1555
        int new_argc;
1556
        char **new_argv;
1557
        char new_opt_file[OSFNMAX];
1558
1559
        /* if the options file doesn't exist, try adding the suffix 't3m' */
1560
        if (osfacc(opt_file))
1561
        {
1562
            strcpy(new_opt_file, opt_file);
1563
            os_defext(new_opt_file, "t3m");
1564
            opt_file = new_opt_file;
1565
        }
1566
1567
        /* open the options file */
1568
        fp = osfoprt(opt_file, OSFTTEXT);
1569
        if (fp == 0)
1570
        {
1571
            printf("error: unable to read option file \"%s\"\n", opt_file);
1572
            err_cnt = 1;
1573
            goto done;
1574
        }
1575
1576
        /* 
1577
         *   First, parse the file simply to count the arguments.  Add in one
1578
         *   extra argument to make room for making a copy of argv[0].  
1579
         */
1580
        new_argc = CTcCommandUtil::parse_opt_file(fp, 0, &opt_helper) + 1;
1581
1582
        /* rewind the file to parse it again */
1583
        osfseek(fp, 0, OSFSK_SET);
1584
1585
        /* allocate a new argument list */
1586
        new_argv = (char **)t3malloc(new_argc * sizeof(new_argv[0]));
1587
1588
        /* copy the program name from argv[0] */
1589
        new_argv[0] = argv[0];
1590
1591
        /* 
1592
         *   Re-read the file, saving the arguments.  Note that we want to
1593
         *   start saving the arguments from the file at index 1 in the new
1594
         *   argv array, because we reserve argv[0] to hold the original
1595
         *   program name argument.  
1596
         */
1597
        CTcCommandUtil::parse_opt_file(fp, new_argv + 1, &opt_helper);
1598
1599
        /* done with the file - close it */
1600
        osfcls(fp);
1601
1602
        /* start over with the new argument vector */
1603
        argv = new_argv;
1604
        argc = new_argc;
1605
        
1606
        /* 
1607
         *   note that we've read the options file - we can only do this
1608
         *   once per run 
1609
         */
1610
        read_opt_file = TRUE;
1611
1612
        /* 
1613
         *   Note the option file's path prefix.  We'll assume that any
1614
         *   relative filename paths mentioned in the option file are meant
1615
         *   to be relative to the folder containing the option file itself. 
1616
         */
1617
        os_get_path_name(opt_file_path, sizeof(opt_file_path), opt_file);
1618
1619
        /* go add the new command options */
1620
        goto parse_options;
1621
    }
1622
1623
    /* 
1624
     *   Add the default object modules, if appropriate.  Do not add the
1625
     *   default modules unless we're linking. 
1626
     */
1627
    if (add_def_mod && !compile_only && !pp_only)
1628
    {
1629
        char srcname[OSFNMAX];
1630
        CTcMakeModule *mod;
1631
1632
        /* build the full path to the "_main" library module */
1633
        os_build_full_path(srcname, sizeof(srcname),
1634
                           sys_lib_entry->get_path(), "_main");
1635
1636
        /* add this as the first module of our compilation */
1637
        mod = mk->add_module_first(srcname, 0, 0);
1638
1639
        /* set its original name, and note it's from the system library */
1640
        mod->set_orig_name("_main");
1641
        mod->set_from_syslib();
1642
    }
1643
1644
    /*
1645
     *   If the symbol file and object file directory paths weren't
1646
     *   explicitly set, set these to the same path used by the image file.
1647
     *   This will put all of the output files in the same place, if they
1648
     *   didn't explicitly tell us where to put the different kinds of
1649
     *   generated files.  
1650
     */
1651
    os_get_path_name(dirbuf, sizeof(dirbuf), mk->get_image_file());
1652
    if (!sym_dir_set)
1653
        mk->set_symbol_dir(dirbuf);
1654
    if (!obj_dir_set)
1655
        mk->set_object_dir(dirbuf);
1656
1657
    /* if we encountered any errors parsing a library, we can't proceed */
1658
    if (lib_err)
1659
        goto done;
1660
1661
    /* build the program */
1662
    mk->build(hostifc, &err_cnt, &warn_cnt,
1663
              force_build, force_link, res_list, argv[0]);
1664
1665
done:
1666
    /* terminate the error subsystem */
1667
    CTcMain::tc_err_term();
1668
1669
    /* delete our 'make' object */
1670
    delete mk;
1671
1672
    /* show the error and warning counts if non-zero */
1673
    if (err_cnt != 0 || warn_cnt != 0)
1674
        printf("Errors:   %d\n"
1675
               "Warnings: %d\n", err_cnt, warn_cnt);
1676
1677
    /* if we read an options file, delete the memory used for the options */
1678
    if (read_opt_file)
1679
    {
1680
        int i;
1681
        
1682
        /* 
1683
         *   delete the argv strings (except for argv[0], which came from
1684
         *   the caller)
1685
         */
1686
        for (i = 1 ; i < argc ; ++i)
1687
            opt_helper.free_opt_file_str(argv[i]);
1688
1689
        /* delete the argv vector itself */
1690
        t3free(argv);
1691
    }
1692
1693
    /* if we have a string capture file, close it */
1694
    if (string_fp != 0)
1695
        osfcls(string_fp);
1696
1697
    /* if we have an aassembly listing file, close it */
1698
    if (assembly_fp != 0)
1699
        osfcls(assembly_fp);
1700
1701
    /* delete the host interface */
1702
    delete hostifc;
1703
1704
    /* delete the resource list */
1705
    delete res_list;
1706
1707
    /* show any unfreed memory (if we're in a debug build) */
1708
    t3_list_memory_blocks(0);
1709
1710
    /* 
1711
     *   exit with an appropriate exit code, depending on whether we had
1712
     *   any errors or not 
1713
     */
1714
    return (err_cnt != 0 || (warnings_as_errors && warn_cnt != 0)
1715
            ? OSEXFAIL : OSEXSUCC);
1716
}
1717