gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/lcc/lcc.c
1 /*
2 * lcc [ option ]... [ file | -llib ]...
3 * front end for the ANSI C compiler
4 */
5 static char rcsid[] = "$Id: lcc.c,v 2.0 " BUILDDATE " " BUILDTIME " gbdk-2020 Exp $";
6
7 #include <stdio.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10 #include <stdbool.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <ctype.h>
14 #include <signal.h>
15
16 #ifdef _WIN32
17 # include <io.h>
18 # include <process.h>
19 #else
20 # include <unistd.h>
21 # include <sys/types.h>
22 # include <sys/wait.h>
23 #endif
24
25 #include "gb.h"
26 #include "list.h"
27 #include "targets.h"
28
29 #ifndef TEMPDIR
30 #define TEMPDIR "/tmp"
31 #endif
32
33 void *alloc(int);
34 extern char *basepath(char *);
35 extern char *path_stripext(char *);
36 extern char *path_newext(char *, char *);
37 static int callsys(char *[]);
38 extern char *concat(const char *, const char *);
39 static void compose(char *[], List, List, List);
40 static void error(char *, char *);
41 static char *exists(char *);
42 static char *first(char *);
43 static int filename(char *, char *);
44 static void help(void);
45 static void initinputs(void);
46 static void interrupt(int);
47 static void opt(char *);
48 extern int main(int, char *[]);
49 extern char *replace(const char *, int, int);
50 static void rm(List);
51 extern char *strsave(const char *);
52 extern char *stringf(const char *, ...);
53 extern int suffix(char *, char *[], int);
54 extern char *tempname(char *);
55
56 static bool arg_has_searchkey(char *, char *);
57
58 // Adds linker default required vars if not present (defined by user)
59 static void Fixllist();
60
61 static void handle_autobanking(void);
62 static int handle_file_preprocess_only(char *name, char *base);
63
64 static void warn_obsolete_option(char * arg);
65
66 // These get populated from _class using finalise() in gb.c
67 extern char *cpp[], *include[], *com[], *as[], *bankpack[], *ld[], *ihxcheck[], *mkbin[], *postproc[], inputs[], *suffixes[], *rom_extension;
68 extern bool has_postproc_stage;
69 extern arg_entry *llist0_defaults;
70 extern int llist0_defaults_len;
71
72 extern int option(char *);
73 extern void set_gbdk_dir(char*);
74
75 void finalise(void);
76
77 static int errcnt; /* number of errors */
78 static int Eflag; /* -E specified */
79 static int Eflag_preproc_to_file; // --save-procroc specified
80 static int Sflag; /* -S specified */
81 static int cflag; /* -c specified */
82 static int Kflag; /* -K specified */
83 static int autobankflag; /* -K specified */
84 int verbose; /* incremented for each -v */
85 static List bankpack_flags; /* bankpack flags */
86 static List ihxchecklist; /* ihxcheck flags */
87 static List postproclist; /* postproc flags */
88 static List mkbinlist; /* loader files, flags */
89
90 // Index entries for llist[]
91 #define L_ARGS 0
92 #define L_FILES 1
93 #define L_LKFILES 2
94 static List llist[3]; /* [2] = .lkfiles, [1] = linker object file list, [0] = linker flags */
95
96 static List alist; /* assembler flags */
97 List clist; /* compiler flags */
98 static List plist; /* preprocessor flags */
99 static List ilist; /* list of additional includes from LCCINPUTS */
100 static List rmlist; /* list of files to remove */
101 static char *outfile; /* ld output file or -[cS] object file */
102 static int ac; /* argument count */
103 static char **av; /* argument vector */
104 char *tempdir = TEMPDIR; /* directory for temporary files */
105 char *progname;
106 static List lccinputs; /* list of input directories */
107 char bankpack_newext[1024] = {'\0'};
108 static int ihx_inputs = 0; // Number of ihx files present in input list
109 static char ihxFile[256] = {'\0'};
110 static char binFile[256] = {'\0'};
111
112
113 int main(int argc, char *argv[]) {
114 int i, j, nf;
115
116 progname = argv[0];
117 ac = argc + 50;
118 av = alloc(ac * sizeof(char *));
119 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
120 signal(SIGINT, interrupt);
121 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
122 signal(SIGTERM, interrupt);
123 #ifdef SIGHUP
124 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
125 signal(SIGHUP, interrupt);
126 #endif
127 if (getenv("TMP"))
128 tempdir = getenv("TMP");
129 else if (getenv("TEMP"))
130 tempdir = getenv("TEMP");
131 else if (getenv("TMPDIR"))
132 tempdir = getenv("TMPDIR");
133 assert(tempdir);
134
135 // Remove trailing slashes
136 i = strlen(tempdir);
137 for (; (i > 0) && ((tempdir[i - 1] == '/') || (tempdir[i - 1] == '\\')); i--)
138 tempdir[i - 1] = '\0';
139 if (argc <= 1) {
140 help();
141 exit(0);
142 }
143 // clist = append("-D__LCC__", 0);
144 initinputs();
145 if (getenv("GBDKDIR"))
146 option(stringf("--prefix=%s", getenv("GBDKDIR")));
147 else
148 set_gbdk_dir(argv[0]);
149 for (nf = 0, i = j = 1; i < argc; i++) {
150 if (strcmp(argv[i], "-o") == 0) {
151 if (++i < argc) {
152 // Don't allow output file to have ".c" or ".i" extension (first two in suffixes[])
153 if (suffix(argv[i], suffixes, 2) != SUFX_NOMATCH) {
154 error("-o would overwrite %s", argv[i]);
155 exit(8);
156 }
157 // Valid output file found
158 outfile = argv[i];
159 continue;
160 }
161 else {
162 error("unrecognized option `%s'", argv[i - 1]);
163 exit(8);
164 }
165 }
166 // This option is obsolete
167 // -----------------------------------------
168 else if (strcmp(argv[i], "-target") == 0) {
169 // -target name is ignored
170 warn_obsolete_option(argv[i]);
171 // Skip past argument second param if present
172 if (argv[i + 1] && *argv[i + 1] != '-')
173 i++;
174 continue;
175 }
176 // -----------------------------------------
177 else if (*argv[i] == '-' && argv[i][1] != 'l') {
178 opt(argv[i]);
179 continue;
180 }
181 else if (*argv[i] != '-') {
182 // Count number of (.ihx) files
183 if (suffix(argv[i], (char * []){EXT_IHX}, 1) != SUFX_NOMATCH)
184 ihx_inputs++;
185 // Count number of (.c, .i, .asm, .s) files
186 else if (suffix(argv[i], suffixes, 3) != SUFX_NOMATCH)
187 nf++;
188 }
189 argv[j++] = argv[i];
190 }
191
192 // Ignore -o output request if:
193 // * compile only (-c) *OR* compile to ASM (-S) is specified
194 // * and there are 2 or more source files (.c, .i, .asm, .s) in the input list (what "nf" seems to count)
195 // Instead, it will generate output matching each input filename
196 if ((cflag || Sflag) && outfile && nf != 1) {
197 fprintf(stderr, "%s: -o %s ignored\n", progname, outfile);
198 outfile = 0;
199 }
200
201 // When .ihx is an input only ihxcheck and makebin will be called.
202 // Warn that all source files won't be processed
203 if ((ihx_inputs > 0) && (nf > 0)) {
204 fprintf(stderr, "%s: Warning: .ihx file present as input, all other input files ignored\n", progname);
205 }
206
207 // Add includes
208 argv[j] = 0;
209
210 // This copies settings from port:platform "class" structure
211 // into command strings used for compose()
212 finalise();
213
214 for (i = 0; include[i]; i++)
215 clist = append(include[i], clist);
216 if (ilist) {
217 List b = ilist;
218 do {
219 b = b->link;
220 clist = append(b->str, clist);
221 } while (b != ilist);
222 }
223 ilist = 0;
224 for (i = 1; argv[i]; i++)
225 // Process arguments
226 if (*argv[i] == '-')
227 opt(argv[i]);
228 else {
229 // Process filenames
230 char *name = exists(argv[i]);
231 if (name) {
232 if (strcmp(name, argv[i]) != 0
233 || ((nf > 1) && (suffix(name, suffixes, 3) != SUFX_NOMATCH)) ) // Does it match: .c, .i, .asm, .s
234
235 fprintf(stderr, "%s:\n", name);
236 // Send input filename argument to "filename processor"
237 // which will add them to llist[n] in some form most of the time
238 filename(name, 0);
239 }
240 else
241 error("can't find `%s'", argv[i]);
242 }
243
244
245 // Perform Link / ihxcheck / makebin stages
246 //
247 // Don't perform these stages if any of the following were requested:
248 // -E : Preprocessor only
249 // -c : Compile only
250 // -S : Compile to Assembly
251 if (errcnt == 0 && !Eflag && !cflag && !Sflag &&
252 (llist[L_FILES] || llist[L_LKFILES] || ((ihxFile[0] != '\0') && ihx_inputs))) {
253
254 int target_is_ihx = 0;
255
256 // if outfile is not specified, set it to default rom extension for active port:platform
257 if(!outfile)
258 outfile = concat("a", rom_extension);
259
260 // If an .ihx file is present as input skip link related stages
261 if (ihx_inputs > 0) {
262
263 // Only one .ihx can be used for input, warn that others will be ignored
264 if (ihx_inputs > 1)
265 fprintf(stderr, "%s: Warning: Multiple (%d) .ihx files present as input, only one (%s) will be used\n", progname, ihx_inputs, ihxFile);
266 }
267 else {
268
269 //file.gb to file.ihx (don't use tmpfile because maps and other stuffs are created there)
270 // Check to see if output target is a .ihx file
271 target_is_ihx = (suffix(outfile, (char *[]){EXT_IHX}, 1) != SUFX_NOMATCH);
272
273 // Build ihx file name from output name
274 sprintf(ihxFile, "%s%s", path_stripext(outfile), EXT_IHX);
275
276 // Only remove .ihx from the delete-list if it's not the final target
277 if (!target_is_ihx)
278 append(ihxFile, rmlist);
279
280 // If auto bank assignment is enabled, modify obj files before linking
281 // Will alter: llist[L_FILES] and llist[L_LKFILES]
282 if (autobankflag)
283 handle_autobanking();
284
285 // Copy any pending linkerfiles into the linker list (with "-f" as preceding arg)
286 llist[L_FILES] = list_add_to_another(llist[L_FILES], llist[L_LKFILES], NULL, "-f");
287 // Call linker (add output ihxfile in compose $3)
288 Fixllist(); // Fixlist adds required default linker vars if not added by user
289 compose(ld, llist[L_ARGS], llist[L_FILES], append(ihxFile, 0));
290
291 if (callsys(av))
292 errcnt++;
293 } // end: non-ihx input file handling
294
295 // ihxcheck (test for multiple writes to the same ROM address)
296 if (!Kflag) {
297 compose(ihxcheck, ihxchecklist, append(ihxFile, 0), 0);
298 if (callsys(av))
299 errcnt++;
300 }
301
302 // No need to makebin (.ihx -> .gb [or other rom_extension]) if .ihx is final target
303 if (!target_is_ihx)
304 {
305 if(errcnt == 0)
306 {
307 // makebin - use output filename unless there is a post-process step
308 if (has_postproc_stage == false)
309 sprintf(binFile, "%s", outfile);
310 else
311 sprintf(binFile, "%s", path_newext(outfile, EXT_ROM));
312
313 // makebin - if autobanking and no ROM bank size specified (-yo *) then add ROM auto-size (-yo A)
314 if ((autobankflag) && (find("-yo", mkbinlist) == 0)) {
315 mkbinlist = append("-yo", mkbinlist);
316 mkbinlist = append("A", mkbinlist);
317 }
318
319 compose(mkbin, mkbinlist, append(ihxFile, 0), append(binFile, 0));
320 if (callsys(av))
321 errcnt++;
322
323 // Post-process step (such as makecom, makenes), if applicable
324 // This won't apply if the targets .postproc template entry is empty ("")
325 if (has_postproc_stage && (errcnt == 0)) {
326 compose(postproc, postproclist, append(binFile, 0), append(outfile, 0));
327 if (callsys(av))
328 errcnt++;
329 }
330 }
331 }
332 }
333 rm(rmlist);
334 if (verbose > 0)
335 fprintf(stderr, "\n");
336 return errcnt ? EXIT_FAILURE : EXIT_SUCCESS;
337 }
338
339
340
341 // Check whether string "arg" has "searchkey" at the first possible
342 // occurrence of searchkey's starting character in arg (typically "." or "_")
343 static bool arg_has_searchkey(char * arg, char * searchkey) {
344 char * str_start = strchr(arg, searchkey[0]);
345
346 if (str_start)
347 return (strncmp(str_start, searchkey, strlen(searchkey)) == 0);
348 else
349 return false;
350 }
351
352
353 // Adds linker default required vars if not present (defined by user)
354 // Uses data from targets.c for per port/platform defaults
355 static void Fixllist()
356 {
357 int c;
358
359 // Iterate through linker list entries
360 if(llist[L_ARGS]) {
361 List b = llist[L_ARGS];
362 do {
363 b = b->link;
364 // Only -g and -b settings are supported at this time
365 if(b->str[1] == 'g' || b->str[1] == 'b')
366 {
367 // '-g' and '-b' now have their values separated by a space.
368 // That splits them into two consecutive llist items,
369 // so try advancing to the next item to access it's value.
370 // Example: b = "-g", next = ".STACK=0xE000"
371 if (b != llist[L_ARGS])
372 b = b->link;
373 else
374 break; // end of list
375
376 // Check current linker arg to see if any default settings are present
377 // If they do, flag them as present so they don't need to be added later
378 for (c = 0; c < llist0_defaults_len; c++)
379 if (arg_has_searchkey(b->str, llist0_defaults[c].searchkey))
380 llist0_defaults[c].found = true;
381 }
382 } while (b != llist[L_ARGS]);
383 }
384
385 // Add required default settings to the linker list if they weren't found
386 for (c = 0; c < llist0_defaults_len; c++)
387 if (llist0_defaults[c].found == false) {
388 // Add the entry to the linker llist[L_ARGS], flag first then value
389 llist[L_ARGS] = append(llist0_defaults[c].addflag, llist[L_ARGS]);
390 llist[L_ARGS] = append(llist0_defaults[c].addvalue, llist[L_ARGS]);
391 }
392 }
393
394
395 /* alloc - allocate n bytes or die */
396 void *alloc(int n) {
397 static char *avail, *limit;
398
399 n = (n + sizeof(char *) - 1)&~(sizeof(char *) - 1);
400 if (n >= limit - avail) {
401 avail = malloc(n + 4 * 1024);
402 assert(avail);
403 limit = avail + n + 4 * 1024;
404 }
405 avail += n;
406 return avail - n;
407 }
408
409
410 /* basepath - return base name for name, e.g. /usr/drh/foo.c => foo */
411 char *basepath(char *name) {
412 char *s, *b, *t = 0;
413
414 for (b = s = name; *s; s++)
415 if (*s == '/' || *s == '\\') {
416 b = s + 1;
417 t = 0;
418 }
419 else if (*s == '.')
420 t = s;
421 s = strsave(b);
422 if (t)
423 s[t - b] = 0;
424 return s;
425 }
426
427 // path_stripext - return a new string of path [name] with extension removed
428 // e.g. /usr/drh/foo.c => /usr/drh/foo
429 char *path_stripext(char *name) {
430 char * copy_str = strsave(name);
431 char * end_str = copy_str + strlen(copy_str);
432
433 // Work from end of string backward,
434 // truncate string at first "." char
435 while (end_str > copy_str) {
436 if (*end_str == '.') {
437 *end_str = '\0';
438 break;
439 }
440 end_str--;
441 }
442 return copy_str;
443 }
444
445
446 // path_newext - return a new string of path [name] with extension replaced
447 // e.g. /usr/drh/foo.c => /usr/drh/foo
448 char *path_newext(char *name, char *new_ext) {
449 return stringf("%s%s", path_stripext(name), new_ext);
450 }
451
452
453 // Check if an extension matches the end of a filename
454 int matches_ext(const char * filename, const char * ext)
455 {
456 // Only test match if filename is larger than extension
457 // Then check the end of the filename for [ext] length of chars
458 if (strlen(filename) >= strlen(ext))
459 return strncmp(filename + strlen(filename) - strlen(ext), ext, strlen(ext)) == 0;
460 else return 0;
461 }
462
463
464 #ifndef WIN32
465 #define _P_WAIT 0
466
467 static int _spawnvp(int mode, const char *cmdname, char *argv[]) {
468 int status;
469 pid_t pid, n;
470
471 switch (pid = fork()) {
472 case -1:
473 fprintf(stderr, "%s: no more processes\n", progname);
474 return 100;
475 case 0:
476 execv(cmdname, argv);
477 fprintf(stderr, "%s: ", progname);
478 perror(cmdname);
479 fflush(stdout);
480 exit(100);
481 }
482 while ((n = wait(&status)) != pid && n != -1)
483 ;
484 if (n == -1)
485 status = -1;
486 if (status & 0377) {
487 fprintf(stderr, "%s: fatal error in %s\n", progname, cmdname);
488 status |= 0400;
489 }
490 return (status >> 8) & 0377;
491 }
492 #endif
493
494 /* removes quotes from src and stores it on dst */
495 void removeQuotes(char* src, char* dst)
496 {
497 while(*src != '\0')
498 {
499 if(*src != '\"')
500 {
501 if(*dst != *src)
502 *(dst) = *src;
503 dst ++;
504 }
505 src ++;
506 }
507 if(*dst != '\0')
508 *dst = '\0';
509 }
510
511 /* turns "C:\Users\Zalo\Desktop\gb\gbdk 2020\build\gbdk\"bin/sdcpp
512 into "C:\Users\Zalo\Desktop\gb\gbdk 2020\build\gbdk\bin/sdcpp" */
513 void fixQuotes(char* str)
514 {
515 while(*str != '\"' && *str != '\0')
516 str ++;
517
518 char* src = str;
519 if(*src == '\"')
520 {
521 *(str ++) = '\"';
522 while(*src != '\0')
523 {
524 if(*src != '\"')
525 *(str ++) = *src;
526 src ++;
527 }
528 *(str ++) = '\"';
529 *(str ++) = '\0';
530 }
531 }
532
533 /* callsys - execute the command described by av[0...], return status */
534 static int callsys(char **av) {
535 int i, status = 0;
536 static char **argv;
537 static int argc;
538
539 for (i = 0; av[i] != NULL; i++)
540 ;
541 if (i + 1 > argc) {
542 argc = i + 1;
543 if (argv == NULL)
544 argv = malloc(argc * sizeof *argv);
545 else {
546 char **argv0 = argv;
547 argv = realloc(argv0, argc * sizeof *argv);
548 if (argv == NULL)
549 free(argv0);
550 }
551 assert(argv);
552 }
553 for (i = 0; status == 0 && av[i] != NULL; ) {
554 int j = 0;
555 char *s;
556 for (; av[i] != NULL && (s = strchr(av[i], '\n')) == NULL; i++)
557 argv[j++] = av[i];
558 if (s != NULL) {
559 if (s > av[i])
560 argv[j++] = stringf("%.*s", s - av[i], av[i]);
561 if (s[1] != '\0')
562 av[i] = s + 1;
563 else
564 i++;
565 }
566 argv[j] = NULL;
567 if (verbose > 0) {
568 int k;
569 fprintf(stderr, "%s", argv[0]);
570 for (k = 1; argv[k] != NULL; k++)
571 fprintf(stderr, " %s", argv[k]);
572 fprintf(stderr, "\n");
573 }
574 if (verbose < 2)
575 {
576 char argv_0_no_quotes[256];
577 removeQuotes(argv[0], argv_0_no_quotes);
578 for(char** it = argv; *it != 0; it ++)
579 {
580 #ifdef __WIN32__
581 fixQuotes(*it); //On windows quotes must be kept, and fixed
582 #else
583 removeQuotes(*it, *it); //On macos, quotes must be fully removed from args
584 #endif
585 }
586 //For future reference:
587 //_spawnvp requires _FileName to not have quotes
588 //_Arguments must have quotes on windows, but not in macos
589 //Quoted strings must begin and end with quotes, no quotes in the middle
590 status = _spawnvp(_P_WAIT, argv_0_no_quotes, argv);
591 }
592 if (status == -1) {
593 fprintf(stderr, "%s: ", progname);
594 perror(argv[0]);
595 }
596 }
597 return status;
598 }
599
600 /* concat - return concatenation of strings s1 and s2 */
601 char *concat(const char *s1, const char *s2) {
602 int n = strlen(s1);
603 char *s = alloc(n + strlen(s2) + 1);
604
605 strcpy(s, s1);
606 strcpy(s + n, s2);
607 return s;
608 }
609
610 /* compose - compose cmd into av substituting a, b, c for $1, $2, $3, resp. */
611 static void compose(char *cmd[], List a, List b, List c) {
612 int i, j;
613 List lists[3];
614
615 lists[0] = a;
616 lists[1] = b;
617 lists[2] = c;
618 for (i = j = 0; cmd[i]; i++) {
619 char *s = strchr(cmd[i], '$');
620 if (s && isdigit(s[1])) {
621 int k = s[1] - '0';
622 assert(k >= 1 && k <= 3);
623 b = lists[k - 1];
624 if (b) {
625 b = b->link;
626 av[j] = alloc(strlen(cmd[i]) + strlen(b->str) - 1);
627 strncpy(av[j], cmd[i], s - cmd[i]);
628 av[j][s - cmd[i]] = '\0';
629 strcat(av[j], b->str);
630 strcat(av[j++], s + 2);
631 while (b != lists[k - 1]) {
632 b = b->link;
633 assert(j < ac);
634 av[j++] = b->str;
635 };
636 }
637 }
638 else if (*cmd[i]) {
639 assert(j < ac);
640 av[j++] = cmd[i];
641 }
642 }
643 av[j] = NULL;
644 }
645
646 /* error - issue error msg according to fmt, bump error count */
647 static void error(char *fmt, char *msg) {
648 fprintf(stderr, "%s: ", progname);
649 fprintf(stderr, fmt, msg);
650 fprintf(stderr, "\n");
651 errcnt++;
652 }
653
654 /* exists - if `name' readable return its path name or return null */
655 static char *exists(char *name) {
656 List b;
657
658 if ((name[0] == '/' || name[0] == '\\' || name[2] == ':')
659 && access(name, 4) == 0)
660 return name;
661 if (!(name[0] == '/' || name[0] == '\\' || name[2] == ':')
662 && (b = lccinputs))
663 do {
664 b = b->link;
665 if (b->str[0]) {
666 char buf[1024];
667 sprintf(buf, "%s/%s", b->str, name);
668 if (access(buf, 4) == 0)
669 return strsave(buf);
670 }
671 else if (access(name, 4) == 0)
672 return name;
673 } while (b != lccinputs);
674 if (verbose > 1)
675 return name;
676 return 0;
677 }
678
679 /* first - return first component in semicolon separated list */
680 static char *first(char *list) {
681 char *s = strchr(list, ';');
682
683 if (s) {
684 char buf[1024];
685 size_t len = s - list;
686 if(len >= sizeof(buf)) len = sizeof(buf)-1;
687 strncpy(buf, list, len);
688 buf[len] = '\0';
689 return strsave(buf);
690 }
691 else
692 return list;
693 }
694
695 /* filename - process file name argument `name', return status */
696 static int filename(char *name, char *base) {
697 int status = 0;
698 static char *stemp, *itemp;
699
700 if (base == 0)
701 base = basepath(name);
702
703 // Handle all available suffixes except .gb (last in list)
704 switch (suffix(name, suffixes, 5)) {
705 case 0: /* C source files */
706 if (Eflag) {
707 // If Preprocess only was requested
708 status = handle_file_preprocess_only(name, base);
709 }
710 else {
711 char *ofile;
712 if ((cflag || Sflag) && outfile)
713 ofile = outfile;
714 else if (cflag)
715 ofile = concat(base, EXT_O);
716 else if (Sflag) {
717 // When compiling to asm only, set outfile as .asm
718 ofile = concat(base, EXT_ASM);
719 }
720 else
721 {
722 ofile = tempname(EXT_O);
723
724 // Remove generated files of these extensions upon completion
725 char* ofileBase = basepath(ofile);
726 rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, EXT_ASM), rmlist);
727 rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, EXT_LST), rmlist);
728 rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, EXT_SYM), rmlist);
729 rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, EXT_ADB), rmlist);
730 }
731
732 compose(com, clist, append(name, 0), append(ofile, 0));
733 status = callsys(av);
734 if (!find(ofile, llist[L_FILES]))
735 llist[L_FILES] = append(ofile, llist[L_FILES]);
736 }
737 break;
738 case 2: /* assembly language files */
739 if (Eflag)
740 break; // Skip asm files if pre-process only specified
741 if (!Sflag) {
742 char *ofile;
743 if (cflag && outfile)
744 ofile = outfile;
745 else if (cflag)
746 ofile = concat(base, EXT_O);
747 else
748 ofile = tempname(EXT_O);
749 compose(as, alist, append(name, 0), append(ofile, 0));
750 status = callsys(av);
751 if (!find(ofile, llist[L_FILES]))
752 llist[L_FILES] = append(ofile, llist[L_FILES]);
753 }
754 break;
755 case 3: /* object files */
756 if (!find(name, llist[L_FILES]))
757 llist[L_FILES] = append(name, llist[L_FILES]);
758 break;
759 case 4: // .ihx files
760 // Apply "name" as .ihx file (there can be only one as input)
761 strncpy(ihxFile, name, sizeof(ihxFile) - 1);
762 break;
763 default: // Files with unmatched or no extension
764
765 // If Preprocess only was requested and it's a .h file
766 if ((Eflag) && matches_ext(name, EXT_H)) {
767 status = handle_file_preprocess_only(name, base);
768 break;
769 }
770 llist[L_FILES] = append(name, llist[L_FILES]);
771 break;
772 }
773 if (status)
774 errcnt++;
775 return status;
776 }
777
778 /* help - print help message */
779 static void help(void) {
780 static char *msgs[] = {
781 "", " [ option | file ]...\n",
782 " except for -l, options are processed left-to-right before files\n",
783 " unrecognized options are taken to be linker options\n",
784 "-c compile only\n",
785 "-debug Turns on --debug for compiler, -y (.cdb), -j (.noi), -w (wide .map format) for linker\n",
786 " -Wa-l (assembler .lst), -Wl-u (.lst -> .rst address update)\n",
787 "-Dname=def define the preprocessor symbol `name'\n",
788 "-E only run preprocessor on named .c and .h files files -> stdout\n",
789 "--save-preproc Use with -E for output to *.i files instead of stdout\n",
790 "-help or -? print this message\n",
791 "-Idir add `dir' to the beginning of the list of #include directories\n",
792 "-K don't run ihxcheck test on linker ihx output\n",
793 "-lx search library `x'\n",
794 "-m select port and platform: \"-m[port]:[plat]\" ports:sm83,z80,mos6502 plats:ap,duck,gb,sms,gg,nes\n",
795 "-N do not search the standard directories for #include files\n",
796 "-no-crt do not auto-include the gbdk crt0.o runtime in linker list\n",
797 "-no-libs do not auto-include the gbdk libs in linker list\n",
798 "-o file leave the output in `file'\n",
799 "-S compile to assembly language\n",
800 "-autobank auto-assign banks set to 255 (bankpack)\n"
801 "-tempdir=dir place temporary files in `dir/'", "\n"
802 "-Uname undefine the preprocessor symbol `name'\n",
803 "-v show commands as they are executed; 2nd -v suppresses execution\n",
804 "-Woarg specify system-specific `arg'\n",
805 "-W[pfablimnc]arg pass `arg' to the (p)preprocessor, (f)compiler, (a)assembler, (b)bankpack,\n"
806 " (l)linker, (i)ihxcheck, (m)makebin, (n)makenes, (c)makecom\n",
807 0 };
808 int i;
809 char *s;
810
811 msgs[0] = progname;
812 for (i = 0; msgs[i]; i++) {
813 fprintf(stderr, "%s", msgs[i]);
814 if (strncmp("-tempdir", msgs[i], 8) == 0 && tempdir)
815 fprintf(stderr, "; default=%s", tempdir);
816 }
817 #define xx(v) if ((s = getenv(#v))) fprintf(stderr, #v "=%s\n", s)
818 xx(LCCINPUTS);
819 xx(LCCDIR);
820 #ifdef WIN32
821 xx(include);
822 xx(lib);
823 #endif
824 #undef xx
825 }
826
827 /* initinputs - if LCCINPUTS or include is defined, use them to initialize various lists */
828 static void initinputs(void) {
829 char *s = getenv("LCCINPUTS");
830 List list, b;
831
832 if (s == 0 || (s = inputs)[0] == 0)
833 s = ".";
834 if (s) {
835 lccinputs = path2list(s);
836 b = lccinputs;
837 if (b)
838 do {
839 b = b->link;
840 if (strcmp(b->str, ".") != 0) {
841 ilist = append(concat("-I", b->str), ilist);
842 if (strstr(com[1], "win32") == NULL)
843 llist[L_ARGS] = append(concat("-L", b->str), llist[L_ARGS]);
844 }
845 else
846 b->str = "";
847 } while (b != lccinputs);
848 }
849 #ifdef WIN32
850 if (list = b = path2list(getenv("include")))
851 do {
852 b = b->link;
853 ilist = append(stringf("-I\"%s\"", b->str), ilist);
854 } while (b != list);
855 #endif
856 }
857
858 /* interrupt - catch interrupt signals */
859 static void interrupt(int n) {
860 rm(rmlist);
861 exit(n = 100);
862 }
863
864 /* opt - process option in arg */
865 static void opt(char *arg) {
866 switch (arg[1]) { /* multi-character options */
867
868 // Obsolete (multi-character) options that just emit a warning
869 // -----------------------------------------
870 case 'p': // -p -pg emit profiling code; see prof(1) and gprof(1)
871 case 'B': // -Bdir/ use the compiler named `dir/rcc' (Also -Bstatic -Bdynamic)
872 warn_obsolete_option(arg);
873 return;
874
875 case 's': // -static specify static libraries (default is dynamic)
876 if (strcmp(arg, "-static") == 0) {
877 warn_obsolete_option(arg);
878 }
879 return;
880 // -----------------------------------------
881
882 case '-': // --* options
883 if (strcmp(arg, "--save-preproc") == 0) {
884 Eflag_preproc_to_file = true;
885 return;
886 }
887 // If no match for "--*" options here then break instead of
888 // return in case they need to get processed in option() below
889 break;
890 case 'W': /* -Wxarg */
891 if (arg[2] && arg[3])
892 switch (arg[2]) {
893 case 'o':
894 if (option(&arg[3]))
895 return;
896 break;
897 case 'p': /* Preprocessor */
898 clist = append(&arg[3], clist);
899 return;
900 case 'f': /* Compiler */
901 if (strcmp(&arg[3], "-C") || option("-b")) {
902 clist = append(&arg[3], clist);
903 return;
904 }
905 break; /* and fall thru */
906 case 'a': /* Assembler */
907 alist = append(&arg[3], alist);
908 return;
909 case 'i': /* ihxcheck arg list */
910 ihxchecklist = append(&arg[3], ihxchecklist);
911 return;
912 case 'n': /* makenes arg list */
913 postproclist = append(&arg[3], postproclist);
914 return;
915 case 'c': /* makecom arg list */
916 postproclist = append(&arg[3], postproclist);
917 return;
918 case 'b': /* auto bankpack_flags arg list */
919 bankpack_flags = append(&arg[3], bankpack_flags);
920 // -Wb-ext=[.some-extension]
921 // If bankpack is going to rewrite input object files to a new extension
922 // then save that extension for rewriting the linker list (llist[L_FILES])
923 if (strstr(&arg[3], "-ext=") != NULL) {
924 if (arg[8]) {
925 sprintf(bankpack_newext, "%s", &arg[8]);
926 }
927 }
928 return;
929 case 'l': /* Linker */
930 if(arg[4] == 'y' && (arg[5] == 't' || arg[5] == 'o' || arg[5] == 'a' || arg[5] == 'p') && (arg[6] != '\0' && arg[6] != ' '))
931 goto makebinoption; //automatically pass -yo -ya -yt -yp options to makebin (backwards compatibility)
932 {
933 // If using linker file for sdldgb (-f file[.lk]).
934 // Starting at arg[5] should be name of the linkerfile
935 if ((arg[4] == 'f') && (arg[5])) {
936 // Items in llist[L_LKFILES] get added to llist[L_FILES] right before the linker is called.
937 // That avoids sending to bankpack with the "-f" flag mixed in with the names of the .o object files.
938 llist[L_LKFILES] = append(stringf(&arg[5]), llist[L_LKFILES]);
939 } else {
940 //sdldgb requires spaces between -k and the path
941 llist[L_ARGS] = append(stringf("%c%c", arg[3], arg[4]), llist[L_ARGS]); //splitting the args into 2 works on Win and Linux
942 if (arg[5]) {
943 llist[L_ARGS] = append(&arg[5], llist[L_ARGS]); // Add filename separately if present
944 }
945 }
946 }
947 return;
948 case 'm': /* Makebin */
949 makebinoption:{
950 char *tmp = malloc(256);
951 char *tmp2 = malloc(256);
952 tmp2[0] = '\0'; // Zero out second arg by default
953 if (arg[4] == 'y') {
954 sprintf(tmp, "%c%c%c", arg[3], arg[4], arg[5]); //-yo -ya -yt -yl -yk -yn -yp
955 if (!(arg[5] == 'c' || arg[5] == 'C' || arg[5] == 's' || arg[5] == 'S' || arg[5] == 'j' || arg[5] == 'p')) // Don't add second arg for -yc -yC -ys -yS -yj
956 sprintf(tmp2, "%s", &arg[6]);
957 // -yp of SDCC 4.1.0's makebin erroneously does not use a space between flag and it's value
958 // So append trailing values to first arg that would otherwise go in the second arg
959 if (arg[5] == 'p')
960 sprintf(tmp, "%s", &arg[3]);
961
962 // If MBC option is present for makebin (-Wl-yt <n> or -Wm-yt <n>) then make a copy for bankpack to use
963 if (arg[5] == 't')
964 bankpack_flags = append(&arg[3], bankpack_flags);
965 } else if ((arg[4] == 'x') && arg[5] && arg[6]) {
966 // SMS options
967 // Print "-" plus first two option chars into first arg
968 // and any trailing option chars into a separate arg
969 sprintf(tmp, "%c%c%c", arg[3], arg[4], arg[5]); //-xo -xj -xv
970 if(arg[6])
971 sprintf(tmp2, "%s", &arg[6]);
972 } else {
973 sprintf(tmp, "%c%c", arg[3], arg[4]); //-s
974 if(arg[5])
975 sprintf(tmp2, "%s", &arg[5]);
976 }
977 mkbinlist = append(tmp, mkbinlist);
978 if (tmp2[0] != '\0') // Only append second argument if it's populated
979 mkbinlist = append(tmp2, mkbinlist);
980 }return;
981 }
982 fprintf(stderr, "%s: %s ignored\n", progname, arg);
983 return;
984 case 'd':
985 if (strcmp(arg, "-debug") == 0) {
986 // Load default debug options
987 clist = append("--debug", clist); // Debug for sdcc compiler
988 llist[L_ARGS] = append("-y", llist[L_ARGS]); // linker: .cdb output for sdldgb
989 llist[L_ARGS] = append("-j", llist[L_ARGS]); // linker: .noi output
990
991 alist = append("-l", alist); // assembler: .lst output
992 // -Wl-u currently requires a patch to SDCC so it warns instead errors out and
993 // build fails if no matching .lst file is present for an object file.
994 // For example: When the user includes a pre-built object file for a music driver
995 llist[L_ARGS] = append("-u", llist[L_ARGS]); // linker: .lst -> .rst address update
996 llist[L_ARGS] = append("-w", llist[L_ARGS]); // linker: wide listing in .map file
997 return;
998 }
999
1000 // This option is obsolete
1001 // -----------------------------------------
1002 // -dn set switch statement density to `n'
1003 warn_obsolete_option(arg);
1004 // -----------------------------------------
1005 return;
1006 case 't': /* -t -tname -tempdir=dir */
1007 if (strncmp(arg, "-tempdir=", 9) == 0)
1008 tempdir = arg + 9;
1009 else
1010 clist = append(arg, clist);
1011 return;
1012 case 'D': /* -Dname -Dname=def */
1013 case 'U': /* -Uname */
1014 case 'I': /* -Idir */
1015 clist = append(arg, clist);
1016 return;
1017 case 'K':
1018 Kflag++;
1019 return;
1020 case 'a':
1021 if (strcmp(arg, "-autobank") == 0) {
1022 autobankflag++;
1023 return;
1024 }
1025 case 'n':
1026 if (strcmp(arg, "-no-crt") == 0) {
1027 option(arg); // Clear crt0 entry in linker compose string
1028 return;
1029 }
1030 else if (strcmp(arg, "-no-libs") == 0) {
1031 option(arg); // Clear libs entry in linker compose string
1032 return;
1033 }
1034 case 'h':
1035 if (strcmp(arg, "-help") == 0) {
1036 static int printed = 0;
1037 case '?':
1038 if (!printed)
1039 help();
1040 printed = 1;
1041 return;
1042 }
1043 }
1044 if (arg[2] == 0)
1045 switch (arg[1]) { /* single-character options */
1046
1047 // Obsolete (single character) options that just emit a warning
1048 // -----------------------------------------
1049 case 'A': // -A warn about nonANSI usage; 2nd -A warns more
1050 case 'b': // -b emit expression-level profiling code; see bprint(1)
1051 case 'g': // -g produce symbol table information for debuggers
1052 case 'G': // -G append("-g3", clist); append("-N", llist[L_ARGS]);
1053 case 'n': // -n emit code to check for dereferencing zero pointers
1054 case 'O': // -O is ignored
1055 case 'P': // -P print ANSI-style declarations for globals
1056 case 't': // -t -tname emit function tracing calls to printf or to `name'
1057 case 'w': // -w suppress warnings
1058 warn_obsolete_option(arg);
1059 return;
1060 // -----------------------------------------
1061
1062 case 'S': // Requested compile to assembly only
1063 Sflag++;
1064 option(arg); // Update composing the compile stage, use of -S instead of -c
1065 return;
1066 case 'E':
1067 Eflag++; // Preprocess files only
1068 return;
1069 case 'c':
1070 cflag++;
1071 return;
1072 case 'N':
1073 if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
1074 clist = append("-nostdinc", clist);
1075 include[0] = 0;
1076 ilist = 0;
1077 return;
1078 case 'v':
1079 if (verbose++ == 0) {
1080 fprintf(stderr, "%s %s\n", progname, rcsid);
1081 }
1082 return;
1083 }
1084 if (option(arg))
1085 return;
1086 if (cflag || Sflag || Eflag)
1087 fprintf(stderr, "%s: %s ignored\n", progname, arg);
1088 else
1089 llist[L_FILES] = append(arg, llist[L_FILES]);
1090 }
1091
1092
1093 /* replace - copy str, then replace occurrences of from with to, return the copy */
1094 char *replace(const char *str, int from, int to) {
1095 char *s = strsave(str), *p = s;
1096
1097 for (; (p = strchr(p, from)) != NULL; p++)
1098 *p = to;
1099 return s;
1100 }
1101
1102 /* rm - remove files in list */
1103 static void rm(List list) {
1104 if (list) {
1105 List b = list;
1106 if (verbose)
1107 fprintf(stderr, "rm");
1108 do {
1109 if (verbose)
1110 fprintf(stderr, " %s", b->str);
1111 if (verbose < 2)
1112 remove(b->str);
1113 } while ((b = b->link) != list);
1114 if (verbose)
1115 fprintf(stderr, "\n");
1116 }
1117 }
1118
1119 /* strsave - return a saved copy of string str */
1120 char *strsave(const char *str) {
1121 return strcpy(alloc(strlen(str) + 1), str);
1122 }
1123
1124 /* stringf - format and return a string */
1125 char *stringf(const char *fmt, ...) {
1126 char buf[1024];
1127 va_list ap;
1128 int n;
1129
1130 va_start(ap, fmt);
1131 n = vsnprintf(buf, sizeof(buf), fmt, ap);
1132 va_end(ap);
1133 return strsave(buf);
1134 }
1135
1136 /* suffix - if one of tails[0..n-1] holds a proper suffix of name, return its index */
1137 int suffix(char *name, char *tails[], int n) {
1138 int i, len = strlen(name);
1139
1140 for (i = 0; i < n; i++) {
1141 char *s = tails[i], *t;
1142 for (; (t = strchr(s, ';')); s = t + 1) {
1143 int m = t - s;
1144 if (len > m && strncmp(&name[len - m], s, m) == 0)
1145 return i;
1146 }
1147 if (*s) {
1148 int m = strlen(s);
1149 if (len > m && strncmp(&name[len - m], s, m) == 0)
1150 return i;
1151 }
1152 }
1153 return SUFX_NOMATCH;
1154 }
1155
1156 /* tempname - generate a temporary file name in tempdir with given suffix */
1157 char *tempname(char *suffix) {
1158 static int n;
1159 char *name = stringf("%s/lcc%d%d%s", tempdir, getpid(), n++, suffix);
1160
1161 if (strstr(com[1], "win32") != NULL)
1162 name = replace(name, '/', '\\');
1163 rmlist = append(name, rmlist);
1164 return name;
1165 }
1166
1167
1168 // Performs the autobanking stage
1169 //
1170 // Should be called prior to doing compose() for the linker
1171 //
1172 static void handle_autobanking(void) {
1173
1174 // bankpack will be populated if supported by active port:platform
1175 if (bankpack[0][0] != '\0') {
1176
1177 char * bankpack_linkerfile_name = tempname(EXT_LK);
1178 rmlist = append(bankpack_linkerfile_name, rmlist); // Delete the linkerfile when done
1179 // Always use a linkerfile when using bankpack through lcc
1180 // Writes all input object files out to [bankpack_linkerfile_name]
1181 bankpack_flags = append(stringf("%s%s","-lkout=", bankpack_linkerfile_name), bankpack_flags);
1182
1183 // Add linkerfile entries (usually *.lk) to the bankpack arg list if any are present
1184 bankpack_flags = list_add_to_another(bankpack_flags, llist[L_LKFILES], "-lkin=", NULL);
1185
1186 // Prepare the bankpack command line, then execute it
1187 compose(bankpack, bankpack_flags, llist[L_FILES], 0);
1188 if (callsys(av))
1189 errcnt++;
1190
1191 // Clear out the objects file and linkerfiles from their lists
1192 // Then replace them with the filename passed to bankpack for "-lkout="
1193 llist[L_FILES] = list_remove_all(llist[L_FILES]);
1194 llist[L_LKFILES] = list_remove_all(llist[L_LKFILES]);
1195 llist[L_LKFILES] = append(stringf("%s", bankpack_linkerfile_name), llist[L_LKFILES]);
1196 }
1197 else
1198 fprintf(stderr, "Warning: bankpack enabled but not supported by active port:platform\n");
1199 }
1200
1201 // Triggered by -E flag
1202 // Called for files with .c, unmatched extension, or no extension
1203 // Output with -o > <somefile>.i is blocked earlier (same as is for output to .c)
1204 //
1205 // Note: This follows the existing convention for -c and -S where the output file
1206 // is just the input file with the path stripped off and placed into the working dir.
1207 // Not actually sure that's what anyone wants, but not gonna rock that boat for now.
1208 // Otherwise using "name" instead of base would use the full path for output.
1209 static int handle_file_preprocess_only(char *name, char *base) {
1210
1211 // Default for preprocess only is to stdout with no output file
1212 char *ofile;
1213
1214 // Save preprocessor output to a file if requested
1215 if (Eflag_preproc_to_file) {
1216 ofile = concat("-o", concat(base, EXT_I));
1217 compose(cpp, clist, append(name, 0), append(ofile, 0));
1218 }
1219 else
1220 compose(cpp, clist, append(name, 0), 0);
1221
1222 return callsys(av); // return call status
1223 }
1224
1225
1226 static void warn_obsolete_option(char * arg) {
1227 fprintf(stderr, "lcc: Warning: argument \"%s\" ignored, no longer supported\n", arg);
1228 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.