1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* main.c */ |
4 | /* */ |
5 | /* Main module for the cl65 compile-and-link utility */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1999-2013, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | /* Check out if we have a spawn() function on the system, or if we must use |
37 | ** our own. |
38 | */ |
39 | #if defined(_WIN32) |
40 | # define HAVE_SPAWN 1 |
41 | #else |
42 | # define NEED_SPAWN 1 |
43 | #endif |
44 | |
45 | /* GCC strictly follows http://c-faq.com/ansi/constmismatch.html and issues an |
46 | ** 'incompatible pointer type' warning - that can't be suppressed via #pragma. |
47 | ** The spawnvp() prototype of MinGW (http://www.mingw.org/) differs from the |
48 | ** one of MinGW-w64 (http://mingw-w64.sourceforge.net/) regarding constness. |
49 | ** So there's no alternative to actually distinguish these environments :-( |
50 | */ |
51 | #define SPAWN_ARGV_CONST_CAST |
52 | #if defined(__MINGW32__) |
53 | # include <_mingw.h> |
54 | # if !defined(__MINGW64_VERSION_MAJOR) |
55 | # undef SPAWN_ARGV_CONST_CAST |
56 | # define SPAWN_ARGV_CONST_CAST (const char* const *) |
57 | # endif |
58 | #endif |
59 | |
60 | |
61 | #include <stdio.h> |
62 | #include <stdlib.h> |
63 | #include <string.h> |
64 | #include <ctype.h> |
65 | #include <errno.h> |
66 | #if defined(HAVE_SPAWN) |
67 | # include <process.h> |
68 | #endif |
69 | |
70 | /* common */ |
71 | #include "attrib.h" |
72 | #include "cmdline.h" |
73 | #include "filetype.h" |
74 | #include "fname.h" |
75 | #include "mmodel.h" |
76 | #include "searchpath.h" |
77 | #include "strbuf.h" |
78 | #include "target.h" |
79 | #include "version.h" |
80 | #include "xmalloc.h" |
81 | |
82 | /* cl65 */ |
83 | #include "global.h" |
84 | #include "error.h" |
85 | |
86 | |
87 | |
88 | /*****************************************************************************/ |
89 | /* Data */ |
90 | /*****************************************************************************/ |
91 | |
92 | |
93 | |
94 | /* Struct that describes a command */ |
95 | typedef struct CmdDesc CmdDesc; |
96 | struct CmdDesc { |
97 | char* Name; /* The command name */ |
98 | |
99 | unsigned ArgCount; /* Count of arguments */ |
100 | unsigned ArgMax; /* Maximum count of arguments */ |
101 | char** Args; /* The arguments */ |
102 | |
103 | unsigned FileCount; /* Count of files to translate */ |
104 | unsigned FileMax; /* Maximum count of files */ |
105 | char** Files; /* The files */ |
106 | }; |
107 | |
108 | /* Command descriptors for the different programs */ |
109 | static CmdDesc CC65 = { 0, 0, 0, 0, 0, 0, 0 }; |
110 | static CmdDesc CA65 = { 0, 0, 0, 0, 0, 0, 0 }; |
111 | static CmdDesc CO65 = { 0, 0, 0, 0, 0, 0, 0 }; |
112 | static CmdDesc LD65 = { 0, 0, 0, 0, 0, 0, 0 }; |
113 | static CmdDesc GRC = { 0, 0, 0, 0, 0, 0, 0 }; |
114 | |
115 | /* Variables controlling the steps we're doing */ |
116 | static int DoLink = 1; |
117 | static int DoAssemble = 1; |
118 | |
119 | /* The name of the output file, NULL if none given */ |
120 | static const char* OutputName = 0; |
121 | |
122 | /* The name of the linker configuration file if given */ |
123 | static const char* LinkerConfig = 0; |
124 | |
125 | /* The name of the first input file. This will be used to construct the |
126 | ** executable file name if no explicit name is given. |
127 | */ |
128 | static const char* FirstInput = 0; |
129 | |
130 | /* The names of the files for dependency generation */ |
131 | static const char* DepName = 0; |
132 | static const char* FullDepName = 0; |
133 | |
134 | /* Remember if we should link a module */ |
135 | static int Module = 0; |
136 | |
137 | /* Extension used for a module */ |
138 | #define MODULE_EXT ".o65" |
139 | |
140 | /* Name of the target specific runtime library */ |
141 | static char* TargetLib = 0; |
142 | static int NoTargetLib = 0; |
143 | |
144 | |
145 | |
146 | /*****************************************************************************/ |
147 | /* Include the system specific spawn function */ |
148 | /*****************************************************************************/ |
149 | |
150 | |
151 | |
152 | #if defined(NEED_SPAWN) |
153 | # if defined(_AMIGA) |
154 | # include "spawn-amiga.inc" |
155 | # else |
156 | # include "spawn-unix.inc" |
157 | # endif |
158 | #endif |
159 | |
160 | |
161 | |
162 | /*****************************************************************************/ |
163 | /* Credential functions */ |
164 | /*****************************************************************************/ |
165 | |
166 | |
167 | |
168 | static void DisableAssembling (void) |
169 | { |
170 | DoAssemble = 0; |
171 | } |
172 | |
173 | |
174 | |
175 | static void DisableLinking (void) |
176 | { |
177 | DoLink = 0; |
178 | } |
179 | |
180 | |
181 | |
182 | static void DisableAssemblingAndLinking (void) |
183 | { |
184 | DisableAssembling (); |
185 | DisableLinking (); |
186 | } |
187 | |
188 | |
189 | |
190 | /*****************************************************************************/ |
191 | /* Command structure handling */ |
192 | /*****************************************************************************/ |
193 | |
194 | |
195 | |
196 | static char* CmdAllocArg (const char* Arg, unsigned Len) |
197 | /* Alloc (potentially quoted) argument */ |
198 | { |
199 | char* Alloc; |
200 | |
201 | /* The Microsoft docs say on spawnvp(): |
202 | ** Spaces embedded in strings may cause unexpected behavior; for example, |
203 | ** passing _spawn the string "hi there" will result in the new process getting |
204 | ** two arguments, "hi" and "there". If the intent was to have the new process |
205 | ** open a file named "hi there", the process would fail. You can avoid this by |
206 | ** quoting the string: "\"hi there\"". |
207 | */ |
208 | #if defined(_WIN32) |
209 | /* Quote argument if it contains space(s) */ |
210 | if (memchr (Arg, ' ', Len)) { |
211 | Alloc = xmalloc (Len + 3); |
212 | Alloc[0] = '"'; |
213 | memcpy (Alloc + 1, Arg, Len); |
214 | Alloc[Len + 1] = '"'; |
215 | Alloc[Len + 2] = '\0'; |
216 | } else |
217 | #endif |
218 | { |
219 | Alloc = xmalloc (Len + 1); |
220 | memcpy (Alloc, Arg, Len); |
221 | Alloc[Len] = '\0'; |
222 | } |
223 | return Alloc; |
224 | } |
225 | |
226 | |
227 | |
228 | static void CmdExpand (CmdDesc* Cmd) |
229 | /* Expand the argument vector */ |
230 | { |
231 | unsigned NewMax = Cmd->ArgMax + 10; |
232 | char** NewArgs = xmalloc (NewMax * sizeof (char*)); |
233 | memcpy (NewArgs, Cmd->Args, Cmd->ArgMax * sizeof (char*)); |
234 | xfree (Cmd->Args); |
235 | Cmd->Args = NewArgs; |
236 | Cmd->ArgMax = NewMax; |
237 | } |
238 | |
239 | |
240 | |
241 | static void CmdAddArg (CmdDesc* Cmd, const char* Arg) |
242 | /* Add a new argument to the command */ |
243 | { |
244 | /* Expand the argument vector if needed */ |
245 | if (Cmd->ArgCount >= Cmd->ArgMax) { |
246 | CmdExpand (Cmd); |
247 | } |
248 | |
249 | /* Add a copy of the new argument, allow a NULL pointer */ |
250 | if (Arg) { |
251 | Cmd->Args[Cmd->ArgCount++] = CmdAllocArg (Arg, strlen (Arg)); |
252 | } else { |
253 | Cmd->Args[Cmd->ArgCount++] = 0; |
254 | } |
255 | } |
256 | |
257 | |
258 | |
259 | static void CmdAddArg2 (CmdDesc* Cmd, const char* Arg1, const char* Arg2) |
260 | /* Add a new argument pair to the command */ |
261 | { |
262 | CmdAddArg (Cmd, Arg1); |
263 | CmdAddArg (Cmd, Arg2); |
264 | } |
265 | |
266 | |
267 | |
268 | static void CmdAddArgList (CmdDesc* Cmd, const char* ArgList) |
269 | /* Add a list of arguments separated by commas */ |
270 | { |
271 | const char* Arg = ArgList; |
272 | const char* P = Arg; |
273 | |
274 | while (1) { |
275 | if (*P == '\0' || *P == ',') { |
276 | |
277 | /* End of argument, add it */ |
278 | unsigned Len = P - Arg; |
279 | |
280 | /* Expand the argument vector if needed */ |
281 | if (Cmd->ArgCount >= Cmd->ArgMax) { |
282 | CmdExpand (Cmd); |
283 | } |
284 | |
285 | /* Add the new argument */ |
286 | Cmd->Args[Cmd->ArgCount++] = CmdAllocArg (Arg, Len); |
287 | |
288 | /* If the argument was terminated by a comma, skip it, otherwise |
289 | ** we're done. |
290 | */ |
291 | if (*P == ',') { |
292 | /* Start over at next char */ |
293 | Arg = ++P; |
294 | } else { |
295 | break; |
296 | } |
297 | } else { |
298 | /* Skip other chars */ |
299 | ++P; |
300 | } |
301 | } |
302 | |
303 | } |
304 | |
305 | |
306 | |
307 | static void CmdDelArgs (CmdDesc* Cmd, unsigned LastValid) |
308 | /* Remove all arguments with an index greater than LastValid */ |
309 | { |
310 | while (Cmd->ArgCount > LastValid) { |
311 | Cmd->ArgCount--; |
312 | xfree (Cmd->Args [Cmd->ArgCount]); |
313 | Cmd->Args [Cmd->ArgCount] = 0; |
314 | } |
315 | } |
316 | |
317 | |
318 | |
319 | static void CmdAddFile (CmdDesc* Cmd, const char* File) |
320 | /* Add a new file to the command */ |
321 | { |
322 | /* Expand the file vector if needed */ |
323 | if (Cmd->FileCount == Cmd->FileMax) { |
324 | unsigned NewMax = Cmd->FileMax + 10; |
325 | char** NewFiles = xmalloc (NewMax * sizeof (char*)); |
326 | memcpy (NewFiles, Cmd->Files, Cmd->FileMax * sizeof (char*)); |
327 | xfree (Cmd->Files); |
328 | Cmd->Files = NewFiles; |
329 | Cmd->FileMax = NewMax; |
330 | } |
331 | |
332 | /* If the file name is not NULL (which is legal and is used to terminate |
333 | ** the file list), check if the file name does already exist in the file |
334 | ** list and print a warning if so. Regardless of the search result, add |
335 | ** the file. |
336 | */ |
337 | if (File) { |
338 | unsigned I; |
339 | for (I = 0; I < Cmd->FileCount; ++I) { |
340 | if (strcmp (Cmd->Files[I], File) == 0) { |
341 | /* Duplicate file */ |
342 | Warning ("Duplicate file in argument list: '%s'" , File); |
343 | /* No need to search further */ |
344 | break; |
345 | } |
346 | } |
347 | |
348 | /* Add the file */ |
349 | Cmd->Files [Cmd->FileCount++] = xstrdup (File); |
350 | } else { |
351 | /* Add a NULL pointer */ |
352 | Cmd->Files [Cmd->FileCount++] = 0; |
353 | } |
354 | } |
355 | |
356 | |
357 | |
358 | static void CmdInit (CmdDesc* Cmd, const char* Path, const char* Name) |
359 | /* Initialize the command using the given path and name of the executable */ |
360 | { |
361 | char* FullName; |
362 | |
363 | FullName = (char*) xmalloc (strlen (Path) + strlen (Name) + 1); |
364 | strcpy (FullName, Path); |
365 | strcat (FullName, Name); |
366 | |
367 | /* Remember the command */ |
368 | Cmd->Name = xstrdup (FullName); |
369 | |
370 | /* Use the command name as first argument */ |
371 | CmdAddArg (Cmd, FullName); |
372 | |
373 | xfree (FullName); |
374 | } |
375 | |
376 | |
377 | |
378 | static void CmdSetOutput (CmdDesc* Cmd, const char* File) |
379 | /* Set the output file in a command desc */ |
380 | { |
381 | CmdAddArg2 (Cmd, "-o" , File); |
382 | } |
383 | |
384 | |
385 | |
386 | static void CmdSetTarget (CmdDesc* Cmd, target_t Target) |
387 | /* Set the output file in a command desc */ |
388 | { |
389 | CmdAddArg2 (Cmd, "-t" , GetTargetName (Target)); |
390 | } |
391 | |
392 | |
393 | |
394 | static void CmdPrint (CmdDesc* Cmd, FILE* F) |
395 | /* Output the command line encoded in the command desc */ |
396 | { |
397 | unsigned I; |
398 | for (I = 0; I < Cmd->ArgCount && Cmd->Args[I] != 0; ++I) { |
399 | fprintf (F, "%s " , Cmd->Args[I]); |
400 | } |
401 | } |
402 | |
403 | |
404 | |
405 | /*****************************************************************************/ |
406 | /* Target handling */ |
407 | /*****************************************************************************/ |
408 | |
409 | |
410 | |
411 | static void SetTargetFiles (void) |
412 | /* Set the target system files */ |
413 | { |
414 | /* Get a pointer to the system name and its length */ |
415 | const char* TargetName = GetTargetName (Target); |
416 | unsigned TargetNameLen = strlen (TargetName); |
417 | |
418 | /* Set the library file */ |
419 | TargetLib = xmalloc (TargetNameLen + 4 + 1); |
420 | memcpy (TargetLib, TargetName, TargetNameLen); |
421 | strcpy (TargetLib + TargetNameLen, ".lib" ); |
422 | } |
423 | |
424 | |
425 | |
426 | /*****************************************************************************/ |
427 | /* Subprocesses */ |
428 | /*****************************************************************************/ |
429 | |
430 | |
431 | |
432 | static void ExecProgram (CmdDesc* Cmd) |
433 | /* Execute a subprocess with the given name/parameters. Exit on errors. */ |
434 | { |
435 | int Status; |
436 | |
437 | /* If in debug mode, output the command line we will execute */ |
438 | if (Debug) { |
439 | printf ("Executing: " ); |
440 | CmdPrint (Cmd, stdout); |
441 | printf ("\n" ); |
442 | } |
443 | |
444 | /* Call the program */ |
445 | Status = spawnvp (P_WAIT, Cmd->Name, SPAWN_ARGV_CONST_CAST Cmd->Args); |
446 | |
447 | /* Check the result code */ |
448 | if (Status < 0) { |
449 | /* Error executing the program */ |
450 | Error ("Cannot execute '%s': %s" , Cmd->Name, strerror (errno)); |
451 | } else if (Status != 0) { |
452 | /* Called program had an error */ |
453 | exit (Status); |
454 | } |
455 | } |
456 | |
457 | |
458 | |
459 | static void Link (void) |
460 | /* Link the resulting executable */ |
461 | { |
462 | unsigned I; |
463 | |
464 | /* Since linking is always the final step, if we have an output file name |
465 | ** given, set it here. If we don't have an explicit output name given, |
466 | ** try to build one from the name of the first input file. |
467 | */ |
468 | if (OutputName) { |
469 | |
470 | CmdSetOutput (&LD65, OutputName); |
471 | |
472 | } else if (FirstInput && FindExt (FirstInput)) { /* Only if ext present! */ |
473 | |
474 | const char* Extension = Module? MODULE_EXT : "" ; |
475 | char* Output = MakeFilename (FirstInput, Extension); |
476 | CmdSetOutput (&LD65, Output); |
477 | xfree (Output); |
478 | |
479 | } |
480 | |
481 | /* If we have a linker config file given, add it to the command line. |
482 | ** Otherwise pass the target to the linker if we have one. |
483 | */ |
484 | if (LinkerConfig) { |
485 | if (Module) { |
486 | Error ("Cannot use -C and --module together" ); |
487 | } |
488 | CmdAddArg2 (&LD65, "-C" , LinkerConfig); |
489 | } else if (Module) { |
490 | CmdSetTarget (&LD65, TGT_MODULE); |
491 | } else { |
492 | CmdSetTarget (&LD65, Target); |
493 | } |
494 | |
495 | /* Add all object files as parameters */ |
496 | for (I = 0; I < LD65.FileCount; ++I) { |
497 | CmdAddArg (&LD65, LD65.Files [I]); |
498 | } |
499 | |
500 | /* Add the target library if it is not disabled */ |
501 | if (!NoTargetLib) |
502 | { |
503 | /* Determine which target library is needed */ |
504 | SetTargetFiles (); |
505 | |
506 | if (TargetLib) { |
507 | CmdAddArg (&LD65, TargetLib); |
508 | } |
509 | } |
510 | |
511 | /* Terminate the argument list with a NULL pointer */ |
512 | CmdAddArg (&LD65, 0); |
513 | |
514 | /* Call the linker */ |
515 | ExecProgram (&LD65); |
516 | } |
517 | |
518 | |
519 | |
520 | static void AssembleFile (const char* File, unsigned ArgCount) |
521 | /* Common routine to assemble a file. Will be called by Assemble() and |
522 | ** AssembleIntermediate(). Adds options common for both routines and |
523 | ** assembles the file. Will remove excess arguments after assembly. |
524 | */ |
525 | { |
526 | /* Set the target system */ |
527 | CmdSetTarget (&CA65, Target); |
528 | |
529 | /* Check if this is the last processing step */ |
530 | if (DoLink) { |
531 | /* We're linking later. Add the output file of the assembly |
532 | ** the the file list of the linker. The name of the output |
533 | ** file is that of the input file with ".s" replaced by ".o". |
534 | */ |
535 | char* ObjName = MakeFilename (File, ".o" ); |
536 | CmdAddFile (&LD65, ObjName); |
537 | xfree (ObjName); |
538 | } else { |
539 | /* This is the final step. If an output name is given, set it */ |
540 | if (OutputName) { |
541 | CmdSetOutput (&CA65, OutputName); |
542 | } |
543 | } |
544 | |
545 | /* Add the file as argument for the assembler */ |
546 | CmdAddArg (&CA65, File); |
547 | |
548 | /* Add a NULL pointer to terminate the argument list */ |
549 | CmdAddArg (&CA65, 0); |
550 | |
551 | /* Run the assembler */ |
552 | ExecProgram (&CA65); |
553 | |
554 | /* Remove the excess arguments */ |
555 | CmdDelArgs (&CA65, ArgCount); |
556 | } |
557 | |
558 | |
559 | |
560 | static void AssembleIntermediate (const char* SourceFile) |
561 | /* Assemble an intermediate file which was generated by a previous processing |
562 | ** step with SourceFile as input. The -dep options won't be added and |
563 | ** the intermediate assembler file is removed after assembly. |
564 | */ |
565 | { |
566 | /* Generate the name of the assembler output file from the source file |
567 | ** name. It's the same name with the extension replaced by ".s" |
568 | */ |
569 | char* AsmName = MakeFilename (SourceFile, ".s" ); |
570 | |
571 | /* Assemble the intermediate assembler file */ |
572 | AssembleFile (AsmName, CA65.ArgCount); |
573 | |
574 | /* Remove the input file */ |
575 | if (remove (AsmName) < 0) { |
576 | Warning ("Cannot remove temporary file '%s': %s" , |
577 | AsmName, strerror (errno)); |
578 | } |
579 | |
580 | /* Free the assembler file name which was allocated from the heap */ |
581 | xfree (AsmName); |
582 | } |
583 | |
584 | |
585 | |
586 | static void Assemble (const char* File) |
587 | /* Assemble the given file */ |
588 | { |
589 | /* Remember the current assembler argument count */ |
590 | unsigned ArgCount = CA65.ArgCount; |
591 | |
592 | /* We aren't assembling an intermediate file, but one requested by the |
593 | ** user. So add a few options here if they were given on the command |
594 | ** line. |
595 | */ |
596 | if (DepName && *DepName) { |
597 | CmdAddArg2 (&CA65, "--create-dep" , DepName); |
598 | } |
599 | if (FullDepName && *FullDepName) { |
600 | CmdAddArg2 (&CA65, "--create-full-dep" , FullDepName); |
601 | } |
602 | |
603 | /* Use the common routine */ |
604 | AssembleFile (File, ArgCount); |
605 | } |
606 | |
607 | |
608 | |
609 | static void Compile (const char* File) |
610 | /* Compile the given file */ |
611 | { |
612 | /* Remember the current compiler argument count */ |
613 | unsigned ArgCount = CC65.ArgCount; |
614 | |
615 | /* Set the target system */ |
616 | CmdSetTarget (&CC65, Target); |
617 | |
618 | /* Check if this is the final step */ |
619 | if (DoAssemble) { |
620 | /* We will assemble this file later. If a dependency file is to be |
621 | ** generated, set the dependency target to be the final object file, |
622 | ** not the intermediate assembler file. But beware: There may be an |
623 | ** output name specified for the assembler. |
624 | */ |
625 | if (DepName || FullDepName) { |
626 | /* Was an output name for the assembler specified? */ |
627 | if (!DoLink && OutputName) { |
628 | /* Use this name as the dependency target */ |
629 | CmdAddArg2 (&CC65, "--dep-target" , OutputName); |
630 | } else { |
631 | /* Use the object file name as the dependency target */ |
632 | char* ObjName = MakeFilename (File, ".o" ); |
633 | CmdAddArg2 (&CC65, "--dep-target" , ObjName); |
634 | xfree (ObjName); |
635 | } |
636 | } |
637 | } else { |
638 | /* If we won't assemble, this is the final step. In this case, set |
639 | ** the output name if it was given. |
640 | */ |
641 | if (OutputName) { |
642 | CmdSetOutput (&CC65, OutputName); |
643 | } |
644 | } |
645 | |
646 | /* Add the file as argument for the compiler */ |
647 | CmdAddArg (&CC65, File); |
648 | |
649 | /* Add a NULL pointer to terminate the argument list */ |
650 | CmdAddArg (&CC65, 0); |
651 | |
652 | /* Run the compiler */ |
653 | ExecProgram (&CC65); |
654 | |
655 | /* Remove the excess arguments */ |
656 | CmdDelArgs (&CC65, ArgCount); |
657 | |
658 | /* If this is not the final step, assemble the generated file, then |
659 | ** remove it |
660 | */ |
661 | if (DoAssemble) { |
662 | /* Assemble the intermediate file and remove it */ |
663 | AssembleIntermediate (File); |
664 | } |
665 | } |
666 | |
667 | |
668 | |
669 | static void CompileRes (const char* File) |
670 | /* Compile the given geos resource file */ |
671 | { |
672 | /* Remember the current assembler argument count */ |
673 | unsigned ArgCount = GRC.ArgCount; |
674 | |
675 | /* Resource files need an geos-apple or geos-cbm target but this |
676 | ** is checked within grc65. |
677 | */ |
678 | CmdSetTarget (&GRC, Target); |
679 | |
680 | /* Add the file as argument for the resource compiler */ |
681 | CmdAddArg (&GRC, File); |
682 | |
683 | /* Add a NULL pointer to terminate the argument list */ |
684 | CmdAddArg (&GRC, 0); |
685 | |
686 | /* Run the compiler */ |
687 | ExecProgram (&GRC); |
688 | |
689 | /* Remove the excess arguments */ |
690 | CmdDelArgs (&GRC, ArgCount); |
691 | |
692 | /* If this is not the final step, assemble the generated file, then |
693 | ** remove it |
694 | */ |
695 | if (DoAssemble) { |
696 | /* Assemble the intermediate file and remove it */ |
697 | AssembleIntermediate (File); |
698 | } |
699 | } |
700 | |
701 | |
702 | |
703 | static void ConvertO65 (const char* File) |
704 | /* Convert an o65 object file into an assembler file */ |
705 | { |
706 | /* Remember the current converter argument count */ |
707 | unsigned ArgCount = CO65.ArgCount; |
708 | |
709 | /* If we won't assemble, this is the final step. In this case, set the |
710 | ** output name. |
711 | */ |
712 | if (!DoAssemble && OutputName) { |
713 | CmdSetOutput (&CO65, OutputName); |
714 | } |
715 | |
716 | /* Add the file as argument for the object file converter */ |
717 | CmdAddArg (&CO65, File); |
718 | |
719 | /* Add a NULL pointer to terminate the argument list */ |
720 | CmdAddArg (&CO65, 0); |
721 | |
722 | /* Run the converter */ |
723 | ExecProgram (&CO65); |
724 | |
725 | /* Remove the excess arguments */ |
726 | CmdDelArgs (&CO65, ArgCount); |
727 | |
728 | /* If this is not the final step, assemble the generated file, then |
729 | ** remove it |
730 | */ |
731 | if (DoAssemble) { |
732 | /* Assemble the intermediate file and remove it */ |
733 | AssembleIntermediate (File); |
734 | } |
735 | } |
736 | |
737 | |
738 | |
739 | /*****************************************************************************/ |
740 | /* Code */ |
741 | /*****************************************************************************/ |
742 | |
743 | |
744 | |
745 | static void Usage (void) |
746 | /* Print usage information and exit */ |
747 | { |
748 | printf ("Usage: %s [options] file [...]\n" |
749 | "Short options:\n" |
750 | " -c\t\t\t\tCompile and assemble, but don't link\n" |
751 | " -d\t\t\t\tDebug mode\n" |
752 | " -g\t\t\t\tAdd debug info\n" |
753 | " -h\t\t\t\tHelp (this text)\n" |
754 | " -l name\t\t\tCreate an assembler listing file\n" |
755 | " -m name\t\t\tCreate a map file\n" |
756 | " -mm model\t\t\tSet the memory model\n" |
757 | " -o name\t\t\tName the output file\n" |
758 | " -r\t\t\t\tEnable register variables\n" |
759 | " -t sys\t\t\tSet the target system\n" |
760 | " -u sym\t\t\tForce an import of symbol 'sym'\n" |
761 | " -v\t\t\t\tVerbose mode\n" |
762 | " -vm\t\t\t\tVerbose map file\n" |
763 | " -C name\t\t\tUse linker config file\n" |
764 | " -Cl\t\t\t\tMake local variables static\n" |
765 | " -D sym[=defn]\t\t\tDefine a preprocessor symbol\n" |
766 | " -E\t\t\t\tStop after the preprocessing stage\n" |
767 | " -I dir\t\t\tSet a compiler include directory path\n" |
768 | " -L path\t\t\tSpecify a library search path\n" |
769 | " -Ln name\t\t\tCreate a VICE label file\n" |
770 | " -O\t\t\t\tOptimize code\n" |
771 | " -Oi\t\t\t\tOptimize code, inline runtime functions\n" |
772 | " -Or\t\t\t\tOptimize code, honour the register keyword\n" |
773 | " -Os\t\t\t\tOptimize code, inline known C functions\n" |
774 | " -S\t\t\t\tCompile, but don't assemble and link\n" |
775 | " -T\t\t\t\tInclude source as comment\n" |
776 | " -V\t\t\t\tPrint the version number\n" |
777 | " -W name[,...]\t\t\tSuppress compiler warnings\n" |
778 | " -Wa options\t\t\tPass options to the assembler\n" |
779 | " -Wc options\t\t\tPass options to the compiler\n" |
780 | " -Wl options\t\t\tPass options to the linker\n" |
781 | "\n" |
782 | "Long options:\n" |
783 | " --add-source\t\t\tInclude source as comment\n" |
784 | " --all-cdecl\t\t\tMake functions default to __cdecl__\n" |
785 | " --asm-args options\t\tPass options to the assembler\n" |
786 | " --asm-define sym[=v]\t\tDefine an assembler symbol\n" |
787 | " --asm-include-dir dir\t\tSet an assembler include directory\n" |
788 | " --bin-include-dir dir\t\tSet an assembler binary include directory\n" |
789 | " --bss-label name\t\tDefine and export a BSS segment label\n" |
790 | " --bss-name seg\t\tSet the name of the BSS segment\n" |
791 | " --cc-args options\t\tPass options to the compiler\n" |
792 | " --cfg-path path\t\tSpecify a config file search path\n" |
793 | " --check-stack\t\t\tGenerate stack overflow checks\n" |
794 | " --code-label name\t\tDefine and export a CODE segment label\n" |
795 | " --code-name seg\t\tSet the name of the CODE segment\n" |
796 | " --codesize x\t\t\tAccept larger code by factor x\n" |
797 | " --config name\t\t\tUse linker config file\n" |
798 | " --cpu type\t\t\tSet CPU type\n" |
799 | " --create-dep name\t\tCreate a make dependency file\n" |
800 | " --create-full-dep name\tCreate a full make dependency file\n" |
801 | " --data-label name\t\tDefine and export a DATA segment label\n" |
802 | " --data-name seg\t\tSet the name of the DATA segment\n" |
803 | " --debug\t\t\tDebug mode\n" |
804 | " --debug-info\t\t\tAdd debug info\n" |
805 | " --feature name\t\tSet an emulation feature\n" |
806 | " --force-import sym\t\tForce an import of symbol 'sym'\n" |
807 | " --help\t\t\tHelp (this text)\n" |
808 | " --include-dir dir\t\tSet a compiler include directory path\n" |
809 | " --ld-args options\t\tPass options to the linker\n" |
810 | " --lib file\t\t\tLink this library\n" |
811 | " --lib-path path\t\tSpecify a library search path\n" |
812 | " --list-targets\t\tList all available targets\n" |
813 | " --listing name\t\tCreate an assembler listing file\n" |
814 | " --list-bytes n\t\tNumber of bytes per assembler listing line\n" |
815 | " --mapfile name\t\tCreate a map file\n" |
816 | " --memory-model model\t\tSet the memory model\n" |
817 | " --module\t\t\tLink as a module\n" |
818 | " --module-id id\t\tSpecify a module ID for the linker\n" |
819 | " --no-target-lib\t\tDon't link the target library\n" |
820 | " --o65-model model\t\tOverride the o65 model\n" |
821 | " --obj file\t\t\tLink this object file\n" |
822 | " --obj-path path\t\tSpecify an object file search path\n" |
823 | " --print-target-path\t\tPrint the target file path\n" |
824 | " --register-space b\t\tSet space available for register variables\n" |
825 | " --register-vars\t\tEnable register variables\n" |
826 | " --rodata-name seg\t\tSet the name of the RODATA segment\n" |
827 | " --signed-chars\t\tDefault characters are signed\n" |
828 | " --standard std\t\tLanguage standard (c89, c99, cc65)\n" |
829 | " --start-addr addr\t\tSet the default start address\n" |
830 | " --static-locals\t\tMake local variables static\n" |
831 | " --target sys\t\t\tSet the target system\n" |
832 | " --version\t\t\tPrint the version number\n" |
833 | " --verbose\t\t\tVerbose mode\n" |
834 | " --zeropage-label name\t\tDefine and export a ZEROPAGE segment label\n" |
835 | " --zeropage-name seg\t\tSet the name of the ZEROPAGE segment\n" , |
836 | ProgName); |
837 | } |
838 | |
839 | |
840 | |
841 | static void OptAddSource (const char* Opt attribute ((unused)), |
842 | const char* Arg attribute ((unused))) |
843 | /* Strict source code as comments to the generated asm code */ |
844 | { |
845 | CmdAddArg (&CC65, "-T" ); |
846 | } |
847 | |
848 | |
849 | static void OptAllCDecl (const char* Opt attribute ((unused)), |
850 | const char* Arg attribute ((unused))) |
851 | /* Make functions default to __cdecl__ */ |
852 | { |
853 | CmdAddArg (&CC65, "--all-cdecl" ); |
854 | } |
855 | |
856 | |
857 | |
858 | static void OptAsmArgs (const char* Opt attribute ((unused)), const char* Arg) |
859 | /* Pass arguments to the assembler */ |
860 | { |
861 | CmdAddArgList (&CA65, Arg); |
862 | } |
863 | |
864 | |
865 | |
866 | static void OptAsmDefine (const char* Opt attribute ((unused)), const char* Arg) |
867 | /* Define an assembler symbol (assembler) */ |
868 | { |
869 | CmdAddArg2 (&CA65, "-D" , Arg); |
870 | } |
871 | |
872 | |
873 | |
874 | static void OptAsmIncludeDir (const char* Opt attribute ((unused)), const char* Arg) |
875 | /* Include directory (assembler) */ |
876 | { |
877 | CmdAddArg2 (&CA65, "-I" , Arg); |
878 | } |
879 | |
880 | |
881 | |
882 | static void OptBinIncludeDir (const char* Opt attribute ((unused)), const char* Arg) |
883 | /* Binary include directory (assembler) */ |
884 | { |
885 | CmdAddArg2 (&CA65, "--bin-include-dir" , Arg); |
886 | } |
887 | |
888 | |
889 | |
890 | static void OptBssLabel (const char* Opt attribute ((unused)), const char* Arg) |
891 | /* Handle the --bss-label option */ |
892 | { |
893 | CmdAddArg2 (&CO65, "--bss-label" , Arg); |
894 | } |
895 | |
896 | |
897 | |
898 | static void OptBssName (const char* Opt attribute ((unused)), const char* Arg) |
899 | /* Handle the --bss-name option */ |
900 | { |
901 | CmdAddArg2 (&CC65, "--bss-name" , Arg); |
902 | CmdAddArg2 (&CO65, "--bss-name" , Arg); |
903 | } |
904 | |
905 | |
906 | |
907 | static void OptCCArgs (const char* Opt attribute ((unused)), const char* Arg) |
908 | /* Pass arguments to the compiler */ |
909 | { |
910 | CmdAddArgList (&CC65, Arg); |
911 | } |
912 | |
913 | |
914 | |
915 | static void OptCfgPath (const char* Opt attribute ((unused)), const char* Arg) |
916 | /* Config file search path (linker) */ |
917 | { |
918 | CmdAddArg2 (&LD65, "--cfg-path" , Arg); |
919 | } |
920 | |
921 | |
922 | |
923 | static void OptCheckStack (const char* Opt attribute ((unused)), |
924 | const char* Arg attribute ((unused))) |
925 | /* Handle the --check-stack option */ |
926 | { |
927 | CmdAddArg (&CC65, "--check-stack" ); |
928 | } |
929 | |
930 | |
931 | |
932 | static void OptCodeLabel (const char* Opt attribute ((unused)), const char* Arg) |
933 | /* Handle the --code-label option */ |
934 | { |
935 | CmdAddArg2 (&CO65, "--code-label" , Arg); |
936 | } |
937 | |
938 | |
939 | |
940 | static void OptCodeName (const char* Opt attribute ((unused)), const char* Arg) |
941 | /* Handle the --code-name option */ |
942 | { |
943 | CmdAddArg2 (&CC65, "--code-name" , Arg); |
944 | CmdAddArg2 (&CO65, "--code-name" , Arg); |
945 | } |
946 | |
947 | |
948 | |
949 | static void OptCodeSize (const char* Opt attribute ((unused)), const char* Arg) |
950 | /* Handle the --codesize option */ |
951 | { |
952 | CmdAddArg2 (&CC65, "--codesize" , Arg); |
953 | } |
954 | |
955 | |
956 | |
957 | static void OptConfig (const char* Opt attribute ((unused)), const char* Arg) |
958 | /* Config file (linker) */ |
959 | { |
960 | if (LinkerConfig) { |
961 | Error ("Cannot specify -C/--config twice" ); |
962 | } |
963 | LinkerConfig = Arg; |
964 | } |
965 | |
966 | |
967 | |
968 | static void OptCPU (const char* Opt attribute ((unused)), const char* Arg) |
969 | /* Handle the --cpu option */ |
970 | { |
971 | /* Add the cpu type to the assembler and compiler */ |
972 | CmdAddArg2 (&CA65, "--cpu" , Arg); |
973 | CmdAddArg2 (&CC65, "--cpu" , Arg); |
974 | } |
975 | |
976 | |
977 | |
978 | static void OptCreateDep (const char* Opt attribute ((unused)), const char* Arg) |
979 | /* Handle the --create-dep option */ |
980 | { |
981 | /* Add the file name to the compiler */ |
982 | CmdAddArg2 (&CC65, "--create-dep" , Arg); |
983 | |
984 | /* Remember the file name for the assembler */ |
985 | DepName = Arg; |
986 | } |
987 | |
988 | |
989 | |
990 | static void OptCreateFullDep (const char* Opt attribute ((unused)), const char* Arg) |
991 | /* Handle the --create-full-dep option */ |
992 | { |
993 | /* Add the file name to the compiler */ |
994 | CmdAddArg2 (&CC65, "--create-full-dep" , Arg); |
995 | |
996 | /* Remember the file name for the assembler */ |
997 | FullDepName = Arg; |
998 | } |
999 | |
1000 | |
1001 | |
1002 | static void OptDataLabel (const char* Opt attribute ((unused)), const char* Arg) |
1003 | /* Handle the --data-label option */ |
1004 | { |
1005 | CmdAddArg2 (&CO65, "--data-label" , Arg); |
1006 | } |
1007 | |
1008 | |
1009 | |
1010 | static void OptDataName (const char* Opt attribute ((unused)), const char* Arg) |
1011 | /* Handle the --data-name option */ |
1012 | { |
1013 | CmdAddArg2 (&CC65, "--data-name" , Arg); |
1014 | CmdAddArg2 (&CO65, "--data-name" , Arg); |
1015 | } |
1016 | |
1017 | |
1018 | |
1019 | static void OptDebug (const char* Opt attribute ((unused)), |
1020 | const char* Arg attribute ((unused))) |
1021 | /* Debug mode (compiler and cl65 utility) */ |
1022 | { |
1023 | CmdAddArg (&CC65, "-d" ); |
1024 | CmdAddArg (&CO65, "-d" ); |
1025 | Debug = 1; |
1026 | } |
1027 | |
1028 | |
1029 | |
1030 | static void OptDebugInfo (const char* Opt attribute ((unused)), |
1031 | const char* Arg attribute ((unused))) |
1032 | /* Debug Info - add to compiler and assembler */ |
1033 | { |
1034 | CmdAddArg (&CC65, "-g" ); |
1035 | CmdAddArg (&CA65, "-g" ); |
1036 | CmdAddArg (&CO65, "-g" ); |
1037 | } |
1038 | |
1039 | |
1040 | |
1041 | static void OptFeature (const char* Opt attribute ((unused)), const char* Arg) |
1042 | /* Emulation features for the assembler */ |
1043 | { |
1044 | CmdAddArg2 (&CA65, "--feature" , Arg); |
1045 | } |
1046 | |
1047 | |
1048 | |
1049 | static void OptForceImport (const char* Opt attribute ((unused)), const char* Arg) |
1050 | /* Emulation features for the assembler */ |
1051 | { |
1052 | CmdAddArg2 (&LD65, "-u" , Arg); |
1053 | } |
1054 | |
1055 | |
1056 | |
1057 | static void OptHelp (const char* Opt attribute ((unused)), |
1058 | const char* Arg attribute ((unused))) |
1059 | /* Print help - cl65 */ |
1060 | { |
1061 | Usage (); |
1062 | exit (EXIT_SUCCESS); |
1063 | } |
1064 | |
1065 | |
1066 | |
1067 | static void OptIncludeDir (const char* Opt attribute ((unused)), const char* Arg) |
1068 | /* Include directory (compiler) */ |
1069 | { |
1070 | CmdAddArg2 (&CC65, "-I" , Arg); |
1071 | } |
1072 | |
1073 | |
1074 | |
1075 | static void OptLdArgs (const char* Opt attribute ((unused)), const char* Arg) |
1076 | /* Pass arguments to the linker */ |
1077 | { |
1078 | CmdAddArgList (&LD65, Arg); |
1079 | } |
1080 | |
1081 | |
1082 | |
1083 | static void OptLib (const char* Opt attribute ((unused)), const char* Arg) |
1084 | /* Library file follows (linker) */ |
1085 | { |
1086 | CmdAddArg2 (&LD65, "--lib" , Arg); |
1087 | } |
1088 | |
1089 | |
1090 | |
1091 | static void OptLibPath (const char* Opt attribute ((unused)), const char* Arg) |
1092 | /* Library search path (linker) */ |
1093 | { |
1094 | CmdAddArg2 (&LD65, "--lib-path" , Arg); |
1095 | } |
1096 | |
1097 | |
1098 | |
1099 | static void OptListBytes (const char* Opt attribute ((unused)), const char* Arg) |
1100 | /* Set the maximum number of bytes per asm listing line */ |
1101 | { |
1102 | CmdAddArg2 (&CA65, "--list-bytes" , Arg); |
1103 | } |
1104 | |
1105 | |
1106 | |
1107 | static void OptListing (const char* Opt attribute ((unused)), const char* Arg) |
1108 | /* Create an assembler listing */ |
1109 | { |
1110 | CmdAddArg2 (&CA65, "-l" , Arg); |
1111 | } |
1112 | |
1113 | |
1114 | |
1115 | static void OptListTargets (const char* Opt attribute ((unused)), |
1116 | const char* Arg attribute ((unused))) |
1117 | /* List all targets */ |
1118 | { |
1119 | target_t T; |
1120 | |
1121 | /* List the targets */ |
1122 | for (T = TGT_NONE; T < TGT_COUNT; ++T) { |
1123 | printf ("%s\n" , GetTargetName (T)); |
1124 | } |
1125 | |
1126 | /* Terminate */ |
1127 | exit (EXIT_SUCCESS); |
1128 | } |
1129 | |
1130 | |
1131 | |
1132 | static void OptMapFile (const char* Opt attribute ((unused)), const char* Arg) |
1133 | /* Create a map file */ |
1134 | { |
1135 | /* Create a map file (linker) */ |
1136 | CmdAddArg2 (&LD65, "-m" , Arg); |
1137 | } |
1138 | |
1139 | |
1140 | |
1141 | static void OptMemoryModel (const char* Opt attribute ((unused)), const char* Arg) |
1142 | /* Set the memory model */ |
1143 | { |
1144 | mmodel_t MemoryModel = FindMemoryModel (Arg); |
1145 | if (MemoryModel == MMODEL_UNKNOWN) { |
1146 | Error ("Unknown memory model: %s" , Arg); |
1147 | } else if (MemoryModel == MMODEL_HUGE) { |
1148 | Error ("Unsupported memory model: %s" , Arg); |
1149 | } else { |
1150 | CmdAddArg2 (&CA65, "-mm" , Arg); |
1151 | CmdAddArg2 (&CC65, "-mm" , Arg); |
1152 | } |
1153 | } |
1154 | |
1155 | |
1156 | |
1157 | static void OptModule (const char* Opt attribute ((unused)), |
1158 | const char* Arg attribute ((unused))) |
1159 | /* Link as a module */ |
1160 | { |
1161 | Module = 1; |
1162 | } |
1163 | |
1164 | |
1165 | |
1166 | static void OptModuleId (const char* Opt attribute ((unused)), const char* Arg) |
1167 | /* Specify a module if for the linker */ |
1168 | { |
1169 | /* Pass it straight to the linker */ |
1170 | CmdAddArg2 (&LD65, "--module-id" , Arg); |
1171 | } |
1172 | |
1173 | |
1174 | |
1175 | static void OptNoTargetLib (const char* Opt attribute ((unused)), |
1176 | const char* Arg attribute ((unused))) |
1177 | /* Disable the target library */ |
1178 | { |
1179 | NoTargetLib = 1; |
1180 | } |
1181 | |
1182 | |
1183 | |
1184 | static void OptO65Model (const char* Opt attribute ((unused)), const char* Arg) |
1185 | /* Handle the --o65-model option */ |
1186 | { |
1187 | CmdAddArg2 (&CO65, "-m" , Arg); |
1188 | } |
1189 | |
1190 | |
1191 | |
1192 | static void OptObj (const char* Opt attribute ((unused)), const char* Arg) |
1193 | /* Object file follows (linker) */ |
1194 | { |
1195 | CmdAddArg2 (&LD65, "--obj" , Arg); |
1196 | } |
1197 | |
1198 | |
1199 | |
1200 | static void OptObjPath (const char* Opt attribute ((unused)), const char* Arg) |
1201 | /* Object file search path (linker) */ |
1202 | { |
1203 | CmdAddArg2 (&LD65, "--obj-path" , Arg); |
1204 | } |
1205 | |
1206 | |
1207 | |
1208 | static void OptPrintTargetPath (const char* Opt attribute ((unused)), |
1209 | const char* Arg attribute ((unused))) |
1210 | /* Print the target file path */ |
1211 | { |
1212 | char* TargetPath; |
1213 | |
1214 | SearchPaths* TargetPaths = NewSearchPath (); |
1215 | AddSubSearchPathFromEnv (TargetPaths, "CC65_HOME" , "target" ); |
1216 | #if defined(CL65_TGT) && !defined(_WIN32) |
1217 | AddSearchPath (TargetPaths, STRINGIZE (CL65_TGT)); |
1218 | #endif |
1219 | AddSubSearchPathFromWinBin (TargetPaths, "target" ); |
1220 | |
1221 | TargetPath = GetSearchPath (TargetPaths, 0); |
1222 | while (*TargetPath) { |
1223 | if (*TargetPath == ' ') { |
1224 | /* Escape spaces */ |
1225 | putchar ('\\'); |
1226 | } |
1227 | putchar (*TargetPath++); |
1228 | } |
1229 | putchar ('\n'); |
1230 | exit (EXIT_SUCCESS); |
1231 | } |
1232 | |
1233 | |
1234 | |
1235 | static void OptRegisterSpace (const char* Opt attribute ((unused)), const char* Arg) |
1236 | /* Handle the --register-space option */ |
1237 | { |
1238 | CmdAddArg2 (&CC65, "--register-space" , Arg); |
1239 | } |
1240 | |
1241 | |
1242 | |
1243 | static void OptRegisterVars (const char* Opt attribute ((unused)), |
1244 | const char* Arg attribute ((unused))) |
1245 | /* Handle the --register-vars option */ |
1246 | { |
1247 | CmdAddArg (&CC65, "-r" ); |
1248 | } |
1249 | |
1250 | |
1251 | |
1252 | static void OptRodataName (const char* Opt attribute ((unused)), const char* Arg) |
1253 | /* Handle the --rodata-name option */ |
1254 | { |
1255 | CmdAddArg2 (&CC65, "--rodata-name" , Arg); |
1256 | } |
1257 | |
1258 | |
1259 | |
1260 | static void OptSignedChars (const char* Opt attribute ((unused)), |
1261 | const char* Arg attribute ((unused))) |
1262 | /* Make default characters signed */ |
1263 | { |
1264 | CmdAddArg (&CC65, "-j" ); |
1265 | } |
1266 | |
1267 | |
1268 | |
1269 | static void OptStandard (const char* Opt attribute ((unused)), const char* Arg) |
1270 | /* Set the language standard */ |
1271 | { |
1272 | CmdAddArg2 (&CC65, "--standard" , Arg); |
1273 | } |
1274 | |
1275 | |
1276 | |
1277 | static void OptStartAddr (const char* Opt attribute ((unused)), const char* Arg) |
1278 | /* Set the default start address */ |
1279 | { |
1280 | CmdAddArg2 (&LD65, "-S" , Arg); |
1281 | } |
1282 | |
1283 | |
1284 | |
1285 | static void OptStaticLocals (const char* Opt attribute ((unused)), |
1286 | const char* Arg attribute ((unused))) |
1287 | /* Place local variables in static storage */ |
1288 | { |
1289 | CmdAddArg (&CC65, "-Cl" ); |
1290 | } |
1291 | |
1292 | |
1293 | |
1294 | static void OptTarget (const char* Opt attribute ((unused)), const char* Arg) |
1295 | /* Set the target system */ |
1296 | { |
1297 | Target = FindTarget (Arg); |
1298 | if (Target == TGT_UNKNOWN) { |
1299 | Error ("No such target system: '%s'" , Arg); |
1300 | } else if (Target == TGT_MODULE) { |
1301 | Error ("Cannot use 'module' as target, use --module instead" ); |
1302 | } |
1303 | } |
1304 | |
1305 | |
1306 | |
1307 | static void OptVerbose (const char* Opt attribute ((unused)), |
1308 | const char* Arg attribute ((unused))) |
1309 | /* Verbose mode (compiler, assembler, linker) */ |
1310 | { |
1311 | CmdAddArg (&CC65, "-v" ); |
1312 | CmdAddArg (&CA65, "-v" ); |
1313 | CmdAddArg (&CO65, "-v" ); |
1314 | CmdAddArg (&LD65, "-v" ); |
1315 | } |
1316 | |
1317 | |
1318 | |
1319 | static void OptVersion (const char* Opt attribute ((unused)), |
1320 | const char* Arg attribute ((unused))) |
1321 | /* Print version number */ |
1322 | { |
1323 | fprintf (stderr, "%s V%s\n" , ProgName, GetVersionAsString ()); |
1324 | exit(EXIT_SUCCESS); |
1325 | } |
1326 | |
1327 | |
1328 | |
1329 | static void OptZeropageLabel (const char* Opt attribute ((unused)), const char* Arg) |
1330 | /* Handle the --zeropage-label option */ |
1331 | { |
1332 | CmdAddArg2 (&CO65, "--zeropage-label" , Arg); |
1333 | } |
1334 | |
1335 | |
1336 | |
1337 | static void OptZeropageName (const char* Opt attribute ((unused)), const char* Arg) |
1338 | /* Handle the --zeropage-name option */ |
1339 | { |
1340 | CmdAddArg2 (&CO65, "--zeropage-name" , Arg); |
1341 | } |
1342 | |
1343 | |
1344 | |
1345 | int main (int argc, char* argv []) |
1346 | /* Utility main program */ |
1347 | { |
1348 | /* Program long options */ |
1349 | static const LongOpt OptTab[] = { |
1350 | { "--add-source" , 0, OptAddSource }, |
1351 | { "--all-cdecl" , 0, OptAllCDecl }, |
1352 | { "--asm-args" , 1, OptAsmArgs }, |
1353 | { "--asm-define" , 1, OptAsmDefine }, |
1354 | { "--asm-include-dir" , 1, OptAsmIncludeDir }, |
1355 | { "--bin-include-dir" , 1, OptBinIncludeDir }, |
1356 | { "--bss-label" , 1, OptBssLabel }, |
1357 | { "--bss-name" , 1, OptBssName }, |
1358 | { "--cc-args" , 1, OptCCArgs }, |
1359 | { "--cfg-path" , 1, OptCfgPath }, |
1360 | { "--check-stack" , 0, OptCheckStack }, |
1361 | { "--code-label" , 1, OptCodeLabel }, |
1362 | { "--code-name" , 1, OptCodeName }, |
1363 | { "--codesize" , 1, OptCodeSize }, |
1364 | { "--config" , 1, OptConfig }, |
1365 | { "--cpu" , 1, OptCPU }, |
1366 | { "--create-dep" , 1, OptCreateDep }, |
1367 | { "--create-full-dep" , 1, OptCreateFullDep }, |
1368 | { "--data-label" , 1, OptDataLabel }, |
1369 | { "--data-name" , 1, OptDataName }, |
1370 | { "--debug" , 0, OptDebug }, |
1371 | { "--debug-info" , 0, OptDebugInfo }, |
1372 | { "--feature" , 1, OptFeature }, |
1373 | { "--force-import" , 1, OptForceImport }, |
1374 | { "--help" , 0, OptHelp }, |
1375 | { "--include-dir" , 1, OptIncludeDir }, |
1376 | { "--ld-args" , 1, OptLdArgs }, |
1377 | { "--lib" , 1, OptLib }, |
1378 | { "--lib-path" , 1, OptLibPath }, |
1379 | { "--list-targets" , 0, OptListTargets }, |
1380 | { "--listing" , 1, OptListing }, |
1381 | { "--list-bytes" , 1, OptListBytes }, |
1382 | { "--mapfile" , 1, OptMapFile }, |
1383 | { "--memory-model" , 1, OptMemoryModel }, |
1384 | { "--module" , 0, OptModule }, |
1385 | { "--module-id" , 1, OptModuleId }, |
1386 | { "--no-target-lib" , 0, OptNoTargetLib }, |
1387 | { "--o65-model" , 1, OptO65Model }, |
1388 | { "--obj" , 1, OptObj }, |
1389 | { "--obj-path" , 1, OptObjPath }, |
1390 | { "--print-target-path" , 0, OptPrintTargetPath}, |
1391 | { "--register-space" , 1, OptRegisterSpace }, |
1392 | { "--register-vars" , 0, OptRegisterVars }, |
1393 | { "--rodata-name" , 1, OptRodataName }, |
1394 | { "--signed-chars" , 0, OptSignedChars }, |
1395 | { "--standard" , 1, OptStandard }, |
1396 | { "--start-addr" , 1, OptStartAddr }, |
1397 | { "--static-locals" , 0, OptStaticLocals }, |
1398 | { "--target" , 1, OptTarget }, |
1399 | { "--verbose" , 0, OptVerbose }, |
1400 | { "--version" , 0, OptVersion }, |
1401 | { "--zeropage-label" , 1, OptZeropageLabel }, |
1402 | { "--zeropage-name" , 1, OptZeropageName }, |
1403 | }; |
1404 | |
1405 | char* CmdPath; |
1406 | unsigned I; |
1407 | |
1408 | /* Initialize the cmdline module */ |
1409 | InitCmdLine (&argc, &argv, "cl65" ); |
1410 | |
1411 | /* Initialize the command descriptors */ |
1412 | if (argc == 0) { |
1413 | CmdPath = xstrdup ("" ); |
1414 | } else { |
1415 | char* Ptr; |
1416 | CmdPath = xstrdup (argv[0]); |
1417 | Ptr = strrchr (CmdPath, '/'); |
1418 | if (Ptr == 0) { |
1419 | Ptr = strrchr (CmdPath, '\\'); |
1420 | } |
1421 | if (Ptr == 0) { |
1422 | *CmdPath = '\0'; |
1423 | } else { |
1424 | *(Ptr + 1) = '\0'; |
1425 | } |
1426 | } |
1427 | CmdInit (&CC65, CmdPath, "cc65" ); |
1428 | CmdInit (&CA65, CmdPath, "ca65" ); |
1429 | CmdInit (&CO65, CmdPath, "co65" ); |
1430 | CmdInit (&LD65, CmdPath, "ld65" ); |
1431 | CmdInit (&GRC, CmdPath, "grc65" ); |
1432 | xfree (CmdPath); |
1433 | |
1434 | /* Our default target is the C64 instead of "none" */ |
1435 | Target = TGT_C64; |
1436 | |
1437 | /* Check the parameters */ |
1438 | I = 1; |
1439 | while (I < ArgCount) { |
1440 | |
1441 | /* Get the argument */ |
1442 | const char* Arg = ArgVec[I]; |
1443 | |
1444 | /* Check for an option */ |
1445 | if (Arg [0] == '-') { |
1446 | |
1447 | switch (Arg [1]) { |
1448 | |
1449 | case '-': |
1450 | LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0])); |
1451 | break; |
1452 | |
1453 | case 'C': |
1454 | if (Arg[2] == 'l' && Arg[3] == '\0') { |
1455 | /* Make local variables static */ |
1456 | OptStaticLocals (Arg, 0); |
1457 | } else { |
1458 | /* Specify linker config file */ |
1459 | OptConfig (Arg, GetArg (&I, 2)); |
1460 | } |
1461 | break; |
1462 | |
1463 | case 'D': |
1464 | /* Define a preprocessor symbol (compiler) */ |
1465 | CmdAddArg2 (&CC65, "-D" , GetArg (&I, 2)); |
1466 | break; |
1467 | |
1468 | case 'I': |
1469 | /* Include directory (compiler) */ |
1470 | OptIncludeDir (Arg, GetArg (&I, 2)); |
1471 | break; |
1472 | |
1473 | case 'L': |
1474 | if (Arg[2] == 'n' && Arg[3] == '\0') { |
1475 | /* VICE label file (linker) */ |
1476 | CmdAddArg2 (&LD65, "-Ln" , GetArg (&I, 3)); |
1477 | } else { |
1478 | /* Library search path (linker) */ |
1479 | OptLibPath (Arg, GetArg (&I, 2)); |
1480 | } |
1481 | break; |
1482 | |
1483 | case 'O': |
1484 | /* Optimize code (compiler, also covers -Oi and others) */ |
1485 | CmdAddArg (&CC65, Arg); |
1486 | break; |
1487 | |
1488 | case 'S': |
1489 | /* Dont assemble and link the created files */ |
1490 | DisableAssemblingAndLinking (); |
1491 | break; |
1492 | |
1493 | case 'T': |
1494 | /* Include source as comment (compiler) */ |
1495 | OptAddSource (Arg, 0); |
1496 | break; |
1497 | |
1498 | case 'V': |
1499 | /* Print version number */ |
1500 | OptVersion (Arg, 0); |
1501 | break; |
1502 | |
1503 | case 'E': |
1504 | /* Forward -E to compiler */ |
1505 | CmdAddArg (&CC65, Arg); |
1506 | DisableAssemblingAndLinking (); |
1507 | break; |
1508 | |
1509 | case 'W': |
1510 | if (Arg[2] == 'a' && Arg[3] == '\0') { |
1511 | /* -Wa: Pass options to assembler */ |
1512 | OptAsmArgs (Arg, GetArg (&I, 3)); |
1513 | } else if (Arg[2] == 'c' && Arg[3] == '\0') { |
1514 | /* -Wc: Pass options to compiler */ |
1515 | /* Remember -Wc sub arguments in cc65 arg struct */ |
1516 | OptCCArgs (Arg, GetArg (&I, 3)); |
1517 | } else if (Arg[2] == 'l' && Arg[3] == '\0') { |
1518 | /* -Wl: Pass options to linker */ |
1519 | OptLdArgs (Arg, GetArg (&I, 3)); |
1520 | } else { |
1521 | /* Anything else: Suppress warnings (compiler) */ |
1522 | CmdAddArg2 (&CC65, "-W" , GetArg (&I, 2)); |
1523 | } |
1524 | break; |
1525 | |
1526 | case 'c': |
1527 | /* Don't link the resulting files */ |
1528 | DisableLinking (); |
1529 | break; |
1530 | |
1531 | case 'd': |
1532 | /* Debug mode (compiler) */ |
1533 | OptDebug (Arg, 0); |
1534 | break; |
1535 | |
1536 | case 'g': |
1537 | /* Debugging - add to compiler and assembler */ |
1538 | OptDebugInfo (Arg, 0); |
1539 | break; |
1540 | |
1541 | case 'h': |
1542 | case '?': |
1543 | /* Print help - cl65 */ |
1544 | OptHelp (Arg, 0); |
1545 | break; |
1546 | |
1547 | case 'j': |
1548 | /* Default characters are signed */ |
1549 | OptSignedChars (Arg, 0); |
1550 | break; |
1551 | |
1552 | case 'l': |
1553 | /* Create an assembler listing */ |
1554 | OptListing (Arg, GetArg (&I, 2)); |
1555 | break; |
1556 | |
1557 | case 'm': |
1558 | /* Create a map file (linker) */ |
1559 | OptMapFile (Arg, GetArg (&I, 2)); |
1560 | break; |
1561 | |
1562 | case 'o': |
1563 | /* Name the output file */ |
1564 | OutputName = GetArg (&I, 2); |
1565 | break; |
1566 | |
1567 | case 'r': |
1568 | /* Enable register variables */ |
1569 | OptRegisterVars (Arg, 0); |
1570 | break; |
1571 | |
1572 | case 't': |
1573 | /* Set target system - compiler, assembler and linker */ |
1574 | OptTarget (Arg, GetArg (&I, 2)); |
1575 | break; |
1576 | |
1577 | case 'u': |
1578 | /* Force an import (linker) */ |
1579 | OptForceImport (Arg, GetArg (&I, 2)); |
1580 | break; |
1581 | |
1582 | case 'v': |
1583 | if (Arg [2] == 'm') { |
1584 | /* Verbose map file (linker) */ |
1585 | CmdAddArg (&LD65, "-vm" ); |
1586 | } else { |
1587 | /* Verbose mode (compiler, assembler, linker) */ |
1588 | OptVerbose (Arg, 0); |
1589 | } |
1590 | break; |
1591 | |
1592 | default: |
1593 | UnknownOption (Arg); |
1594 | } |
1595 | } else { |
1596 | |
1597 | /* Remember the first file name */ |
1598 | if (FirstInput == 0) { |
1599 | FirstInput = Arg; |
1600 | } |
1601 | |
1602 | /* Determine the file type by the extension */ |
1603 | switch (GetFileType (Arg)) { |
1604 | |
1605 | case FILETYPE_C: |
1606 | /* Compile the file */ |
1607 | Compile (Arg); |
1608 | break; |
1609 | |
1610 | case FILETYPE_ASM: |
1611 | /* Assemble the file */ |
1612 | if (DoAssemble) { |
1613 | Assemble (Arg); |
1614 | } |
1615 | break; |
1616 | |
1617 | case FILETYPE_OBJ: |
1618 | case FILETYPE_LIB: |
1619 | /* Add to the linker files */ |
1620 | CmdAddFile (&LD65, Arg); |
1621 | break; |
1622 | |
1623 | case FILETYPE_GR: |
1624 | /* Add to the resource compiler files */ |
1625 | CompileRes (Arg); |
1626 | break; |
1627 | |
1628 | case FILETYPE_O65: |
1629 | /* Add the the object file converter files */ |
1630 | ConvertO65 (Arg); |
1631 | break; |
1632 | |
1633 | default: |
1634 | Error ("Don't know what to do with '%s'" , Arg); |
1635 | |
1636 | } |
1637 | |
1638 | } |
1639 | |
1640 | /* Next argument */ |
1641 | ++I; |
1642 | } |
1643 | |
1644 | /* Check if we had any input files */ |
1645 | if (FirstInput == 0) { |
1646 | Warning ("No input files" ); |
1647 | } |
1648 | |
1649 | /* Link the given files if requested and if we have any */ |
1650 | if (DoLink && LD65.FileCount > 0) { |
1651 | Link (); |
1652 | } |
1653 | |
1654 | /* Return an apropriate exit code */ |
1655 | return EXIT_SUCCESS; |
1656 | } |
1657 | |