1 /*****************************************************************************/
5 /* Main module for the cl65 compile and link utility */
9 /* (C) 1999-2000 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
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. */
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: */
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 */
32 /*****************************************************************************/
41 #if defined(__WATCOMC__) || defined(_MSC_VER)
42 # include <process.h> /* DOS, OS/2 and Windows */
44 # include "spawn.h" /* All others */
47 #include "../common/cmdline.h"
48 #include "../common/fname.h"
49 #include "../common/version.h"
50 #include "../common/xmalloc.h"
57 /*****************************************************************************/
59 /*****************************************************************************/
63 /* Struct that describes a command */
64 typedef struct CmdDesc_ CmdDesc;
66 char* Name; /* The command name */
68 unsigned ArgCount; /* Count of arguments */
69 unsigned ArgMax; /* Maximum count of arguments */
70 char** Args; /* The arguments */
72 unsigned FileCount; /* Count of files to translate */
73 unsigned FileMax; /* Maximum count of files */
74 char** Files; /* The files */
77 /* Command descriptors for the different programs */
78 static CmdDesc CC65 = { 0, 0, 0, 0, 0, 0, 0 };
79 static CmdDesc CA65 = { 0, 0, 0, 0, 0, 0, 0 };
80 static CmdDesc LD65 = { 0, 0, 0, 0, 0, 0, 0 };
91 /* Default file type, used if type unknown */
92 static unsigned DefaultFileType = FILETYPE_UNKNOWN;
94 /* Variables controlling the steps we're doing */
95 static int DontLink = 0;
96 static int DontAssemble = 0;
98 /* The name of the output file, NULL if none given */
99 static const char* OutputName = 0;
101 /* The name of the linker configuration file if given */
102 static const char* LinkerConfig = 0;
104 /* The name of the first input file. This will be used to construct the
105 * executable file name if no explicit name is given.
107 static const char* FirstInput = 0;
109 /* The target system */
114 TGT_ATARI = TGT_FIRSTREAL,
125 } Target = TGT_UNKNOWN;
127 /* Names of the target systems sorted by target name */
128 static const char* TargetNames [] = {
142 /* Name of the crt0 object file and the runtime library */
143 static char* TargetCRT0 = 0;
144 static char* TargetLib = 0;
148 /*****************************************************************************/
149 /* Determine a file type */
150 /*****************************************************************************/
154 static unsigned GetFileType (const char* File)
155 /* Determine the type of the given file */
157 /* Table mapping extensions to file types */
158 static const struct {
162 { ".c", FILETYPE_C },
163 { ".s", FILETYPE_ASM },
164 { ".asm", FILETYPE_ASM },
165 { ".o", FILETYPE_OBJ },
166 { ".obj", FILETYPE_OBJ },
167 { ".a", FILETYPE_LIB },
168 { ".lib", FILETYPE_LIB },
173 /* Determine the file type by the extension */
174 const char* Ext = FindExt (File);
176 /* Do we have an extension? */
178 return DefaultFileType;
181 /* Check for known extensions */
182 for (I = 0; I < sizeof (FileTypes) / sizeof (FileTypes [0]); ++I) {
183 if (strcmp (FileTypes [I].Ext, Ext) == 0) {
185 return FileTypes [I].Type;
189 /* Not found, return the default */
190 return DefaultFileType;
195 /*****************************************************************************/
196 /* Command structure handling */
197 /*****************************************************************************/
201 static void CmdAddArg (CmdDesc* Cmd, const char* Arg)
202 /* Add a new argument to the command */
204 /* Expand the argument vector if needed */
205 if (Cmd->ArgCount == Cmd->ArgMax) {
206 unsigned NewMax = Cmd->ArgMax + 10;
207 char** NewArgs = xmalloc (NewMax * sizeof (char*));
208 memcpy (NewArgs, Cmd->Args, Cmd->ArgMax * sizeof (char*));
211 Cmd->ArgMax = NewMax;
214 /* Add a copy of the new argument, allow a NULL pointer */
216 Cmd->Args [Cmd->ArgCount++] = xstrdup (Arg);
218 Cmd->Args [Cmd->ArgCount++] = 0;
224 static void CmdDelArgs (CmdDesc* Cmd, unsigned LastValid)
225 /* Remove all arguments with an index greater than LastValid */
227 while (Cmd->ArgCount > LastValid) {
229 xfree (Cmd->Args [Cmd->ArgCount]);
230 Cmd->Args [Cmd->ArgCount] = 0;
236 static void CmdAddFile (CmdDesc* Cmd, const char* File)
237 /* Add a new file to the command */
239 /* Expand the file vector if needed */
240 if (Cmd->FileCount == Cmd->FileMax) {
241 unsigned NewMax = Cmd->FileMax + 10;
242 char** NewFiles = xmalloc (NewMax * sizeof (char*));
243 memcpy (NewFiles, Cmd->Files, Cmd->FileMax * sizeof (char*));
245 Cmd->Files = NewFiles;
246 Cmd->FileMax = NewMax;
249 /* Add a copy of the file name, allow a NULL pointer */
251 Cmd->Files [Cmd->FileCount++] = xstrdup (File);
253 Cmd->Files [Cmd->FileCount++] = 0;
259 static void CmdInit (CmdDesc* Cmd, const char* Path)
260 /* Initialize the command using the given path to the executable */
262 /* Remember the command */
263 Cmd->Name = xstrdup (Path);
265 /* Use the command name as first argument */
266 CmdAddArg (Cmd, Path);
271 static void CmdSetOutput (CmdDesc* Cmd, const char* File)
272 /* Set the output file in a command desc */
274 CmdAddArg (Cmd, "-o");
275 CmdAddArg (Cmd, File);
280 static void CmdSetTarget (CmdDesc* Cmd, int Target)
281 /* Set the output file in a command desc */
283 if (Target == TGT_UNKNOWN) {
284 /* Use C64 as default */
288 if (Target != TGT_NONE) {
289 CmdAddArg (Cmd, "-t");
290 CmdAddArg (Cmd, TargetNames[Target]);
296 /*****************************************************************************/
297 /* Target handling */
298 /*****************************************************************************/
302 static int MapTarget (const char* Name)
303 /* Map a target name to a system code. Abort on errors */
307 /* Check for a numeric target */
308 if (isdigit (*Name)) {
309 int Target = atoi (Name);
310 if (Target >= 0 && Target < TGT_COUNT) {
315 /* Check for a target string */
316 for (I = 0; I < TGT_COUNT; ++I) {
317 if (strcmp (TargetNames [I], Name) == 0) {
323 Error ("No such target system: `%s'", Name);
324 return -1; /* Not reached */
329 static void SetTargetFiles (void)
330 /* Set the target system files */
332 /* Determine the names of the default startup and library file */
333 if (Target >= TGT_FIRSTREAL) {
335 /* Get a pointer to the system name and its length */
336 const char* TargetName = TargetNames [Target];
337 unsigned TargetNameLen = strlen (TargetName);
339 /* Set the startup file */
340 TargetCRT0 = xmalloc (TargetNameLen + 2 + 1);
341 strcpy (TargetCRT0, TargetName);
342 strcat (TargetCRT0, ".o");
344 /* Set the library file */
345 TargetLib = xmalloc (TargetNameLen + 4 + 1);
346 strcpy (TargetLib, TargetName);
347 strcat (TargetLib, ".lib");
354 static void SetTargetByName (const char* Name)
355 /* Set the target system by name */
357 Target = MapTarget (Name);
363 /*****************************************************************************/
365 /*****************************************************************************/
369 static void ExecProgram (CmdDesc* Cmd)
370 /* Execute a subprocess with the given name/parameters. Exit on errors. */
372 /* Call the program */
373 int Status = spawnvp (P_WAIT, Cmd->Name, Cmd->Args);
375 /* Check the result code */
377 /* Error executing the program */
378 Error ("Cannot execute `%s': %s", Cmd->Name, strerror (errno));
379 } else if (Status != 0) {
380 /* Called program had an error */
387 static void Link (void)
388 /* Link the resulting executable */
392 /* If we have a linker config file given, set the linker config file.
393 * Otherwise set the target system.
396 CmdAddArg (&LD65, "-C");
397 CmdAddArg (&LD65, LinkerConfig);
399 if (Target == TGT_UNKNOWN) {
400 /* Use c64 instead */
404 CmdSetTarget (&LD65, Target);
407 /* Since linking is always the final step, if we have an output file name
408 * given, set it here. If we don't have an explicit output name given,
409 * try to build one from the name of the first input file.
413 CmdAddArg (&LD65, "-o");
414 CmdAddArg (&LD65, OutputName);
416 } else if (FirstInput && FindExt (FirstInput)) { /* Only if ext present! */
418 char* Output = MakeFilename (FirstInput, "");
419 CmdAddArg (&LD65, "-o");
420 CmdAddArg (&LD65, Output);
425 /* If we have a startup file, add its name as a parameter */
427 CmdAddArg (&LD65, TargetCRT0);
430 /* Add all object files as parameters */
431 for (I = 0; I < LD65.FileCount; ++I) {
432 CmdAddArg (&LD65, LD65.Files [I]);
435 /* Add the system runtime library */
437 CmdAddArg (&LD65, TargetLib);
440 /* Terminate the argument list with a NULL pointer */
441 CmdAddArg (&LD65, 0);
443 /* Call the linker */
449 static void Assemble (const char* File)
450 /* Assemble the given file */
452 /* Remember the current assembler argument count */
453 unsigned ArgCount = CA65.ArgCount;
455 /* If we won't link, this is the final step. In this case, set the
458 if (DontLink && OutputName) {
459 CmdSetOutput (&CA65, OutputName);
461 /* The object file name will be the name of the source file
462 * with .s replaced by ".o". Add this file to the list of
465 char* ObjName = MakeFilename (File, ".o");
466 CmdAddFile (&LD65, ObjName);
470 /* Add the file as argument for the assembler */
471 CmdAddArg (&CA65, File);
473 /* Add a NULL pointer to terminate the argument list */
474 CmdAddArg (&CA65, 0);
476 /* Run the assembler */
479 /* Remove the excess arguments */
480 CmdDelArgs (&CA65, ArgCount);
485 static void Compile (const char* File)
486 /* Compile the given file */
490 /* Remember the current assembler argument count */
491 unsigned ArgCount = CC65.ArgCount;
493 /* Set the target system */
494 CmdSetTarget (&CC65, Target);
496 /* If we won't link, this is the final step. In this case, set the
499 if (DontAssemble && OutputName) {
500 CmdSetOutput (&CC65, OutputName);
502 /* The assembler file name will be the name of the source file
503 * with .c replaced by ".s".
505 AsmName = MakeFilename (File, ".s");
508 /* Add the file as argument for the compiler */
509 CmdAddArg (&CC65, File);
511 /* Add a NULL pointer to terminate the argument list */
512 CmdAddArg (&CC65, 0);
514 /* Run the compiler */
517 /* Remove the excess arguments */
518 CmdDelArgs (&CC65, ArgCount);
520 /* If this is not the final step, assemble the generated file, then
525 if (remove (AsmName) < 0) {
526 Warning ("Cannot remove temporary file `%s': %s",
527 AsmName, strerror (errno));
535 /*****************************************************************************/
537 /*****************************************************************************/
541 static void Usage (void)
542 /* Print usage information and exit */
545 "Usage: %s [options] file\n"
547 " -A\t\t\tStrict ANSI mode\n"
548 " -C name\t\tUse linker config file\n"
549 " -Cl\t\t\tMake local variables static\n"
550 " -D sym[=defn]\t\tDefine a preprocessor symbol\n"
551 " -I dir\t\tSet a compiler include directory path\n"
552 " -Ln name\t\tCreate a VICE label file\n"
553 " -O\t\t\tOptimize code\n"
554 " -Oi\t\t\tOptimize code, inline functions\n"
555 " -Or\t\t\tOptimize code, honour the register keyword\n"
556 " -Os\t\t\tOptimize code, inline known C funtions\n"
557 " -S\t\t\tCompile but don't assemble and link\n"
558 " -V\t\t\tPrint the version number\n"
559 " -W\t\t\tSuppress warnings\n"
560 " -c\t\t\tCompiler and assemble but don't link\n"
561 " -d\t\t\tDebug mode\n"
562 " -g\t\t\tAdd debug info\n"
563 " -h\t\t\tHelp (this text)\n"
564 " -m name\t\tCreate a map file\n"
565 " -o name\t\tName the output file\n"
566 " -t sys\t\tSet the target system\n"
567 " -v\t\t\tVerbose mode\n"
568 " -vm\t\t\tVerbose map file\n"
571 " --ansi\t\tStrict ANSI mode\n"
572 " --asm-include-dir dir\tSet an assembler include directory\n"
573 " --debug\t\tDebug mode\n"
574 " --debug-info\t\tAdd debug info\n"
575 " --help\t\tHelp (this text)\n"
576 " --include-dir dir\tSet a compiler include directory path\n"
577 " --target sys\t\tSet the target system\n"
578 " --version\t\tPrint the version number\n"
579 " --verbose\t\tVerbose mode\n",
585 static void OptAnsi (const char* Opt, const char* Arg)
586 /* Strict ANSI mode (compiler) */
588 CmdAddArg (&CC65, "-A");
593 static void OptAsmIncludeDir (const char* Opt, const char* Arg)
594 /* Include directory (assembler) */
599 CmdAddArg (&CA65, "-I");
600 CmdAddArg (&CA65, Arg);
605 static void OptDebug (const char* Opt, const char* Arg)
606 /* Debug mode (compiler) */
608 CmdAddArg (&CC65, "-d");
613 static void OptDebugInfo (const char* Opt, const char* Arg)
614 /* Debug Info - add to compiler and assembler */
616 CmdAddArg (&CC65, "-g");
617 CmdAddArg (&CA65, "-g");
622 static void OptHelp (const char* Opt, const char* Arg)
623 /* Print help - cl65 */
631 static void OptIncludeDir (const char* Opt, const char* Arg)
632 /* Include directory (compiler) */
637 CmdAddArg (&CC65, "-I");
638 CmdAddArg (&CC65, Arg);
643 static void OptTarget (const char* Opt, const char* Arg)
644 /* Set the target system */
649 SetTargetByName (Arg);
654 static void OptVerbose (const char* Opt, const char* Arg)
655 /* Verbose mode (compiler, assembler, linker) */
657 CmdAddArg (&CC65, "-v");
658 CmdAddArg (&CA65, "-v");
659 CmdAddArg (&LD65, "-v");
664 static void OptVersion (const char* Opt, const char* Arg)
665 /* Print version number */
668 "cl65 V%u.%u.%u - (C) Copyright 1998-2000 Ullrich von Bassewitz\n",
669 VER_MAJOR, VER_MINOR, VER_PATCH);
674 int main (int argc, char* argv [])
675 /* Utility main program */
677 /* Program long options */
678 static const LongOpt OptTab[] = {
679 { "--ansi", 0, OptAnsi },
680 { "--asm-include-dir", 1, OptAsmIncludeDir },
681 { "--debug", 0, OptDebug },
682 { "--debug-info", 0, OptDebugInfo },
683 { "--help", 0, OptHelp },
684 { "--include-dir", 1, OptIncludeDir },
685 { "--target", 1, OptTarget },
686 { "--verbose", 0, OptVerbose },
687 { "--version", 0, OptVersion },
692 /* Initialize the cmdline module */
693 InitCmdLine (argc, argv, "cl65");
695 /* Initialize the command descriptors */
696 CmdInit (&CC65, "cc65");
697 CmdInit (&CA65, "ca65");
698 CmdInit (&LD65, "ld65");
700 /* Check the parameters */
704 /* Get the argument */
705 const char* Arg = argv [I];
707 /* Check for an option */
708 if (Arg [0] == '-') {
713 LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
717 /* Strict ANSI mode (compiler) */
722 if (Arg[2] == 'l' && Arg[3] == '\0') {
723 /* Make local variables static */
724 CmdAddArg (&CC65, "-Cl");
726 /* Specify linker config file */
727 LinkerConfig = GetArg (&I, 2);
732 /* Define a preprocessor symbol (compiler) */
733 CmdAddArg (&CC65, "-D");
734 CmdAddArg (&CC65, GetArg (&I, 2));
738 /* Include directory (compiler) */
739 OptIncludeDir (Arg, GetArg (&I, 2));
744 /* VICE label file (linker) */
745 CmdAddArg (&LD65, "-Ln");
746 CmdAddArg (&LD65, GetArg (&I, 3));
753 /* Optimize code (compiler, also covers -Oi and others) */
754 CmdAddArg (&CC65, Arg);
758 /* Dont assemble and link the created files */
759 DontLink = DontAssemble = 1;
763 /* Include source as comment (compiler) */
764 CmdAddArg (&CC65, "-T");
768 /* Print version number */
773 /* Suppress warnings - compiler and assembler */
774 CmdAddArg (&CC65, "-W");
775 CmdAddArg (&CA65, "-W");
776 CmdAddArg (&CA65, "0");
780 /* Don't link the resulting files */
785 /* Debug mode (compiler) */
790 /* Debugging - add to compiler and assembler */
791 OptDebugInfo (Arg, 0);
796 /* Print help - cl65 */
801 /* Create a map file (linker) */
802 CmdAddArg (&LD65, "-m");
803 CmdAddArg (&LD65, GetArg (&I, 2));
807 /* Name the output file */
808 OutputName = GetArg (&I, 2);
812 /* Set target system - compiler and linker */
813 OptTarget (Arg, GetArg (&I, 2));
817 if (Arg [2] == 'm') {
818 /* Verbose map file (linker) */
819 CmdAddArg (&LD65, "-vm");
821 /* Verbose mode (compiler, assembler, linker) */
831 /* Remember the first file name */
832 if (FirstInput == 0) {
836 /* Determine the file type by the extension */
837 switch (GetFileType (Arg)) {
840 /* Compile the file */
845 /* Assemble the file */
853 /* Add to the linker files */
854 CmdAddFile (&LD65, Arg);
858 Error ("Don't know what to do with `%s'", Arg);
868 /* Check if we had any input files */
869 if (FirstInput == 0) {
870 Warning ("No input files");
873 /* Link the given files if requested and if we have any */
874 if (DontLink == 0 && LD65.FileCount > 0) {
878 /* Return an apropriate exit code */