/* Check out if we have a spawn() function on the system, or if we must use
- * our own.
- */
+** our own.
+*/
#if defined(_WIN32)
# define HAVE_SPAWN 1
#else
# define NEED_SPAWN 1
#endif
-#if defined(_MSC_VER)
-# pragma warning(disable : 4996)
+
+/* GCC strictly follows http://c-faq.com/ansi/constmismatch.html and issues an
+** 'incompatible pointer type' warning - that can't be suppressed via #pragma.
+** The spawnvp() prototype of MinGW (http://www.mingw.org/) differs from the
+** one of MinGW-w64 (http://mingw-w64.sourceforge.net/) regarding constness.
+** So there's no alternative to actually distinguish these environments :-(
+*/
+#define SPAWN_ARGV_CONST_CAST
+#if defined(__MINGW32__)
+# include <_mingw.h>
+# if !defined(__MINGW64_VERSION_MAJOR)
+# undef SPAWN_ARGV_CONST_CAST
+# define SPAWN_ARGV_CONST_CAST (const char* const *)
+# endif
#endif
#include "filetype.h"
#include "fname.h"
#include "mmodel.h"
+#include "searchpath.h"
#include "strbuf.h"
#include "target.h"
#include "version.h"
static const char* LinkerConfig = 0;
/* The name of the first input file. This will be used to construct the
- * executable file name if no explicit name is given.
- */
+** executable file name if no explicit name is given.
+*/
static const char* FirstInput = 0;
/* The names of the files for dependency generation */
/* Name of the target specific runtime library */
static char* TargetLib = 0;
+static int NoStdLib = 0;
#if defined(NEED_SPAWN)
-# if defined(SPAWN_AMIGA)
+# if defined(_AMIGA)
# include "spawn-amiga.inc"
# else
# include "spawn-unix.inc"
+/*****************************************************************************/
+/* Credential functions */
+/*****************************************************************************/
+
+
+
+static void DisableAssembling (void)
+{
+ DoAssemble = 0;
+}
+
+
+
+static void DisableLinking (void)
+{
+ DoLink = 0;
+}
+
+
+
+static void DisableAssemblingAndLinking (void)
+{
+ DisableAssembling ();
+ DisableLinking ();
+}
+
+
+
/*****************************************************************************/
/* Command structure handling */
/*****************************************************************************/
+static char* CmdAllocArg (const char* Arg, unsigned Len)
+/* Alloc (potentially quoted) argument */
+{
+ char* Alloc;
+
+/* The Microsoft docs say on spawnvp():
+** Spaces embedded in strings may cause unexpected behavior; for example,
+** passing _spawn the string "hi there" will result in the new process getting
+** two arguments, "hi" and "there". If the intent was to have the new process
+** open a file named "hi there", the process would fail. You can avoid this by
+** quoting the string: "\"hi there\"".
+*/
+#if defined(_WIN32)
+ /* Quote argument if it contains space(s) */
+ if (memchr (Arg, ' ', Len)) {
+ Alloc = xmalloc (Len + 3);
+ Alloc[0] = '"';
+ memcpy (Alloc + 1, Arg, Len);
+ Alloc[Len + 1] = '"';
+ Alloc[Len + 2] = '\0';
+ } else
+#endif
+ {
+ Alloc = xmalloc (Len + 1);
+ memcpy (Alloc, Arg, Len);
+ Alloc[Len] = '\0';
+ }
+ return Alloc;
+}
+
+
+
static void CmdExpand (CmdDesc* Cmd)
/* Expand the argument vector */
{
/* Add a copy of the new argument, allow a NULL pointer */
if (Arg) {
- Cmd->Args[Cmd->ArgCount++] = xstrdup (Arg);
+ Cmd->Args[Cmd->ArgCount++] = CmdAllocArg (Arg, strlen (Arg));
} else {
Cmd->Args[Cmd->ArgCount++] = 0;
}
}
/* Add the new argument */
- Cmd->Args[Cmd->ArgCount] = memcpy (xmalloc (Len + 1), Arg, Len);
- Cmd->Args[Cmd->ArgCount][Len] = '\0';
- ++Cmd->ArgCount;
+ Cmd->Args[Cmd->ArgCount++] = CmdAllocArg (Arg, Len);
/* If the argument was terminated by a comma, skip it, otherwise
- * we're done.
- */
+ ** we're done.
+ */
if (*P == ',') {
/* Start over at next char */
Arg = ++P;
}
/* If the file name is not NULL (which is legal and is used to terminate
- * the file list), check if the file name does already exist in the file
- * list and print a warning if so. Regardless of the search result, add
- * the file.
- */
+ ** the file list), check if the file name does already exist in the file
+ ** list and print a warning if so. Regardless of the search result, add
+ ** the file.
+ */
if (File) {
unsigned I;
for (I = 0; I < Cmd->FileCount; ++I) {
static void SetTargetFiles (void)
/* Set the target system files */
{
- /* Determine the names of the target specific library file */
- if (Target != TGT_NONE) {
-
- /* Get a pointer to the system name and its length */
- const char* TargetName = GetTargetName (Target);
- unsigned TargetNameLen = strlen (TargetName);
+ /* Get a pointer to the system name and its length */
+ const char* TargetName = GetTargetName (Target);
+ unsigned TargetNameLen = strlen (TargetName);
- /* Set the library file */
- TargetLib = xmalloc (TargetNameLen + 4 + 1);
- memcpy (TargetLib, TargetName, TargetNameLen);
- strcpy (TargetLib + TargetNameLen, ".lib");
-
- }
+ /* Set the library file */
+ TargetLib = xmalloc (TargetNameLen + 4 + 1);
+ memcpy (TargetLib, TargetName, TargetNameLen);
+ strcpy (TargetLib + TargetNameLen, ".lib");
}
}
/* Call the program */
- Status = spawnvp (P_WAIT, Cmd->Name, (const char* const *) Cmd->Args);
+ Status = spawnvp (P_WAIT, Cmd->Name, SPAWN_ARGV_CONST_CAST Cmd->Args);
/* Check the result code */
if (Status < 0) {
unsigned I;
/* Since linking is always the final step, if we have an output file name
- * given, set it here. If we don't have an explicit output name given,
- * try to build one from the name of the first input file.
- */
+ ** given, set it here. If we don't have an explicit output name given,
+ ** try to build one from the name of the first input file.
+ */
if (OutputName) {
CmdSetOutput (&LD65, OutputName);
}
/* If we have a linker config file given, add it to the command line.
- * Otherwise pass the target to the linker if we have one.
- */
+ ** Otherwise pass the target to the linker if we have one.
+ */
if (LinkerConfig) {
if (Module) {
Error ("Cannot use -C and --module together");
CmdSetTarget (&LD65, Target);
}
- /* Determine which target libraries are needed */
- SetTargetFiles ();
-
/* Add all object files as parameters */
for (I = 0; I < LD65.FileCount; ++I) {
CmdAddArg (&LD65, LD65.Files [I]);
}
- /* Add the system runtime library */
- if (TargetLib) {
- CmdAddArg (&LD65, TargetLib);
+ /* Add the standard runtime library if it is not disabled */
+ if (!NoStdLib)
+ {
+ /* Determine which target library is needed */
+ SetTargetFiles ();
+
+ if (TargetLib) {
+ CmdAddArg (&LD65, TargetLib);
+ }
}
/* Terminate the argument list with a NULL pointer */
static void AssembleFile (const char* File, unsigned ArgCount)
/* Common routine to assemble a file. Will be called by Assemble() and
- * AssembleIntermediate(). Adds options common for both routines and
- * assembles the file. Will remove excess arguments after assembly.
- */
+** AssembleIntermediate(). Adds options common for both routines and
+** assembles the file. Will remove excess arguments after assembly.
+*/
{
/* Set the target system */
CmdSetTarget (&CA65, Target);
/* Check if this is the last processing step */
if (DoLink) {
/* We're linking later. Add the output file of the assembly
- * the the file list of the linker. The name of the output
- * file is that of the input file with ".s" replaced by ".o".
- */
+ ** the the file list of the linker. The name of the output
+ ** file is that of the input file with ".s" replaced by ".o".
+ */
char* ObjName = MakeFilename (File, ".o");
CmdAddFile (&LD65, ObjName);
xfree (ObjName);
static void AssembleIntermediate (const char* SourceFile)
/* Assemble an intermediate file which was generated by a previous processing
- * step with SourceFile as input. The -dep options won't be added and
- * the intermediate assembler file is removed after assembly.
- */
+** step with SourceFile as input. The -dep options won't be added and
+** the intermediate assembler file is removed after assembly.
+*/
{
/* Generate the name of the assembler output file from the source file
- * name. It's the same name with the extension replaced by ".s"
- */
+ ** name. It's the same name with the extension replaced by ".s"
+ */
char* AsmName = MakeFilename (SourceFile, ".s");
/* Assemble the intermediate assembler file */
unsigned ArgCount = CA65.ArgCount;
/* We aren't assembling an intermediate file, but one requested by the
- * user. So add a few options here if they were given on the command
- * line.
- */
+ ** user. So add a few options here if they were given on the command
+ ** line.
+ */
if (DepName && *DepName) {
CmdAddArg2 (&CA65, "--create-dep", DepName);
}
/* Check if this is the final step */
if (DoAssemble) {
/* We will assemble this file later. If a dependency file is to be
- * generated, set the dependency target to be the final object file,
- * not the intermediate assembler file. But beware: There may be an
- * output name specified for the assembler.
- */
+ ** generated, set the dependency target to be the final object file,
+ ** not the intermediate assembler file. But beware: There may be an
+ ** output name specified for the assembler.
+ */
if (DepName || FullDepName) {
/* Was an output name for the assembler specified? */
if (!DoLink && OutputName) {
}
} else {
/* If we won't assemble, this is the final step. In this case, set
- * the output name if it was given.
- */
+ ** the output name if it was given.
+ */
if (OutputName) {
CmdSetOutput (&CC65, OutputName);
}
CmdDelArgs (&CC65, ArgCount);
/* If this is not the final step, assemble the generated file, then
- * remove it
- */
+ ** remove it
+ */
if (DoAssemble) {
/* Assemble the intermediate file and remove it */
AssembleIntermediate (File);
unsigned ArgCount = GRC.ArgCount;
/* Resource files need an geos-apple or geos-cbm target but this
- * is checked within grc65.
- */
+ ** is checked within grc65.
+ */
CmdSetTarget (&GRC, Target);
/* Add the file as argument for the resource compiler */
CmdDelArgs (&GRC, ArgCount);
/* If this is not the final step, assemble the generated file, then
- * remove it
- */
+ ** remove it
+ */
if (DoAssemble) {
/* Assemble the intermediate file and remove it */
AssembleIntermediate (File);
unsigned ArgCount = CO65.ArgCount;
/* If we won't assemble, this is the final step. In this case, set the
- * output name.
- */
+ ** output name.
+ */
if (!DoAssemble && OutputName) {
CmdSetOutput (&CO65, OutputName);
}
CmdDelArgs (&CO65, ArgCount);
/* If this is not the final step, assemble the generated file, then
- * remove it
- */
+ ** remove it
+ */
if (DoAssemble) {
/* Assemble the intermediate file and remove it */
AssembleIntermediate (File);
" -C name\t\t\tUse linker config file\n"
" -Cl\t\t\t\tMake local variables static\n"
" -D sym[=defn]\t\t\tDefine a preprocessor symbol\n"
+ " -E\t\t\t\tStop after the preprocessing stage\n"
" -I dir\t\t\tSet a compiler include directory path\n"
" -L path\t\t\tSpecify a library search path\n"
" -Ln name\t\t\tCreate a VICE label file\n"
"\n"
"Long options:\n"
" --add-source\t\t\tInclude source as comment\n"
+ " --all-cdecl\t\t\tMake functions default to __cdecl__\n"
" --asm-args options\t\tPass options to the assembler\n"
" --asm-define sym[=v]\t\tDefine an assembler symbol\n"
" --asm-include-dir dir\t\tSet an assembler include directory\n"
" --memory-model model\t\tSet the memory model\n"
" --module\t\t\tLink as a module\n"
" --module-id id\t\tSpecify a module ID for the linker\n"
+ " --no-std-lib\t\t\tDon't link standard runtime library\n"
" --o65-model model\t\tOverride the o65 model\n"
" --obj file\t\t\tLink this object file\n"
" --obj-path path\t\tSpecify an object file search path\n"
+ " --print-target-path\t\tPrint the target file path\n"
" --register-space b\t\tSet space available for register variables\n"
" --register-vars\t\tEnable register variables\n"
" --rodata-name seg\t\tSet the name of the RODATA segment\n"
}
+static void OptAllCDecl (const char* Opt attribute ((unused)),
+ const char* Arg attribute ((unused)))
+/* Make functions default to __cdecl__ */
+{
+ CmdAddArg (&CC65, "--all-cdecl");
+}
+
+
static void OptAsmArgs (const char* Opt attribute ((unused)), const char* Arg)
/* Pass arguments to the assembler */
+static void OptNoStdLib (const char* Opt attribute ((unused)),
+ const char* Arg attribute ((unused)))
+/* Disable standard runtime library */
+{
+ NoStdLib = 1;
+}
+
+
+
static void OptO65Model (const char* Opt attribute ((unused)), const char* Arg)
/* Handle the --o65-model option */
{
+static void OptPrintTargetPath (const char* Opt attribute ((unused)),
+ const char* Arg attribute ((unused)))
+/* Print the target file path */
+{
+ char* TargetPath;
+
+ SearchPaths* TargetPaths = NewSearchPath ();
+ AddSubSearchPathFromEnv (TargetPaths, "CC65_HOME", "target");
+#if defined(CL65_TGT) && !defined(_WIN32)
+ AddSearchPath (TargetPaths, STRINGIZE (CL65_TGT));
+#endif
+ AddSubSearchPathFromWinBin (TargetPaths, "target");
+
+ TargetPath = GetSearchPath (TargetPaths, 0);
+ while (*TargetPath) {
+ if (*TargetPath == ' ') {
+ /* Escape spaces */
+ putchar ('\\');
+ }
+ putchar (*TargetPath++);
+ }
+ putchar ('\n');
+ exit (EXIT_SUCCESS);
+}
+
+
+
static void OptRegisterSpace (const char* Opt attribute ((unused)), const char* Arg)
/* Handle the --register-space option */
{
const char* Arg attribute ((unused)))
/* Print version number */
{
- fprintf (stderr, "cl65 V%s\n", GetVersionAsString ());
+ fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ());
+ exit(EXIT_SUCCESS);
}
{
/* Program long options */
static const LongOpt OptTab[] = {
- { "--add-source", 0, OptAddSource },
- { "--asm-args", 1, OptAsmArgs },
- { "--asm-define", 1, OptAsmDefine },
- { "--asm-include-dir", 1, OptAsmIncludeDir },
- { "--bin-include-dir", 1, OptBinIncludeDir },
- { "--bss-label", 1, OptBssLabel },
- { "--bss-name", 1, OptBssName },
- { "--cc-args", 1, OptCCArgs },
- { "--cfg-path", 1, OptCfgPath },
- { "--check-stack", 0, OptCheckStack },
- { "--code-label", 1, OptCodeLabel },
- { "--code-name", 1, OptCodeName },
- { "--codesize", 1, OptCodeSize },
- { "--config", 1, OptConfig },
- { "--cpu", 1, OptCPU },
- { "--create-dep", 1, OptCreateDep },
- { "--create-full-dep", 1, OptCreateFullDep },
- { "--data-label", 1, OptDataLabel },
- { "--data-name", 1, OptDataName },
- { "--debug", 0, OptDebug },
- { "--debug-info", 0, OptDebugInfo },
- { "--feature", 1, OptFeature },
- { "--force-import", 1, OptForceImport },
- { "--help", 0, OptHelp },
- { "--include-dir", 1, OptIncludeDir },
- { "--ld-args", 1, OptLdArgs },
- { "--lib", 1, OptLib },
- { "--lib-path", 1, OptLibPath },
- { "--list-targets", 0, OptListTargets },
- { "--listing", 1, OptListing },
- { "--list-bytes", 1, OptListBytes },
- { "--mapfile", 1, OptMapFile },
- { "--memory-model", 1, OptMemoryModel },
- { "--module", 0, OptModule },
- { "--module-id", 1, OptModuleId },
- { "--o65-model", 1, OptO65Model },
- { "--obj", 1, OptObj },
- { "--obj-path", 1, OptObjPath },
- { "--register-space", 1, OptRegisterSpace },
- { "--register-vars", 0, OptRegisterVars },
- { "--rodata-name", 1, OptRodataName },
- { "--signed-chars", 0, OptSignedChars },
- { "--standard", 1, OptStandard },
- { "--start-addr", 1, OptStartAddr },
- { "--static-locals", 0, OptStaticLocals },
- { "--target", 1, OptTarget },
- { "--verbose", 0, OptVerbose },
- { "--version", 0, OptVersion },
- { "--zeropage-label", 1, OptZeropageLabel },
- { "--zeropage-name", 1, OptZeropageName },
+ { "--add-source", 0, OptAddSource },
+ { "--all-cdecl", 0, OptAllCDecl },
+ { "--asm-args", 1, OptAsmArgs },
+ { "--asm-define", 1, OptAsmDefine },
+ { "--asm-include-dir", 1, OptAsmIncludeDir },
+ { "--bin-include-dir", 1, OptBinIncludeDir },
+ { "--bss-label", 1, OptBssLabel },
+ { "--bss-name", 1, OptBssName },
+ { "--cc-args", 1, OptCCArgs },
+ { "--cfg-path", 1, OptCfgPath },
+ { "--check-stack", 0, OptCheckStack },
+ { "--code-label", 1, OptCodeLabel },
+ { "--code-name", 1, OptCodeName },
+ { "--codesize", 1, OptCodeSize },
+ { "--config", 1, OptConfig },
+ { "--cpu", 1, OptCPU },
+ { "--create-dep", 1, OptCreateDep },
+ { "--create-full-dep", 1, OptCreateFullDep },
+ { "--data-label", 1, OptDataLabel },
+ { "--data-name", 1, OptDataName },
+ { "--debug", 0, OptDebug },
+ { "--debug-info", 0, OptDebugInfo },
+ { "--feature", 1, OptFeature },
+ { "--force-import", 1, OptForceImport },
+ { "--help", 0, OptHelp },
+ { "--include-dir", 1, OptIncludeDir },
+ { "--ld-args", 1, OptLdArgs },
+ { "--lib", 1, OptLib },
+ { "--lib-path", 1, OptLibPath },
+ { "--list-targets", 0, OptListTargets },
+ { "--listing", 1, OptListing },
+ { "--list-bytes", 1, OptListBytes },
+ { "--mapfile", 1, OptMapFile },
+ { "--memory-model", 1, OptMemoryModel },
+ { "--module", 0, OptModule },
+ { "--module-id", 1, OptModuleId },
+ { "--no-std-lib", 0, OptNoStdLib },
+ { "--o65-model", 1, OptO65Model },
+ { "--obj", 1, OptObj },
+ { "--obj-path", 1, OptObjPath },
+ { "--print-target-path", 0, OptPrintTargetPath},
+ { "--register-space", 1, OptRegisterSpace },
+ { "--register-vars", 0, OptRegisterVars },
+ { "--rodata-name", 1, OptRodataName },
+ { "--signed-chars", 0, OptSignedChars },
+ { "--standard", 1, OptStandard },
+ { "--start-addr", 1, OptStartAddr },
+ { "--static-locals", 0, OptStaticLocals },
+ { "--target", 1, OptTarget },
+ { "--verbose", 0, OptVerbose },
+ { "--version", 0, OptVersion },
+ { "--zeropage-label", 1, OptZeropageLabel },
+ { "--zeropage-name", 1, OptZeropageName },
};
char* CmdPath;
case 'S':
/* Dont assemble and link the created files */
- DoAssemble = 0;
- DoLink = 0;
+ DisableAssemblingAndLinking ();
break;
case 'T':
OptVersion (Arg, 0);
break;
+ case 'E':
+ /* Forward -E to compiler */
+ CmdAddArg (&CC65, Arg);
+ DisableAssemblingAndLinking ();
+ break;
+
case 'W':
if (Arg[2] == 'a' && Arg[3] == '\0') {
/* -Wa: Pass options to assembler */
OptAsmArgs (Arg, GetArg (&I, 3));
} else if (Arg[2] == 'c' && Arg[3] == '\0') {
/* -Wc: Pass options to compiler */
+ /* Remember -Wc sub arguments in cc65 arg struct */
OptCCArgs (Arg, GetArg (&I, 3));
} else if (Arg[2] == 'l' && Arg[3] == '\0') {
/* -Wl: Pass options to linker */
case 'c':
/* Don't link the resulting files */
- DoLink = 0;
+ DisableLinking ();
break;
case 'd':