You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2020/02/24 03:44:28 UTC

[incubator-nuttx] branch master updated: tools/zds/zdsgen.c: WIP Compiler/Assembler Wrapper

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 9cd188e  tools/zds/zdsgen.c:  WIP Compiler/Assembler Wrapper
9cd188e is described below

commit 9cd188e3c782ac8ab5354a55c090c6b3dcd1044a
Author: Gregory Nutt <gn...@nuttx.org>
AuthorDate: Sun Feb 23 16:40:04 2020 -0600

    tools/zds/zdsgen.c:  WIP Compiler/Assembler Wrapper
    
    And additional wrapper that is needed for the ZDS-II build is a wrapper for the compiler and assemble.  This is needed because the ZDS-II cannot control the name or location of the output of the output object file:  it is always in the same directory as the source file and with the same name as the source file except with the .obj extension.
    
    This was handled in the past with the MOVEOBJ definition which was specifically called Makefiles to move the objects to the correct position.  However, now there is a new behavior:  Output object files may also be named differently with added decoration in the file name.  This is done in the current apps/ directory build.
    
    There is currently some ugly implementation that includes a long sequence of Bash code to handle the moving and/or renaming.  There is nothing in place for the Windows native case.
    
    This wrapper, when complete, will clean up the Makefiles, improve build performance, and provide a solution that is portable to both the Cywin/MSYS environment as well as to the Windows native environment.
---
 tools/zds/.gitignore |   2 +
 tools/zds/Makefile   |  13 +-
 tools/zds/zdsar.c    |  38 ++-
 tools/zds/zdsgen.c   | 688 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 713 insertions(+), 28 deletions(-)

diff --git a/tools/zds/.gitignore b/tools/zds/.gitignore
index 7720a66..4c8c9ff 100644
--- a/tools/zds/.gitignore
+++ b/tools/zds/.gitignore
@@ -1,3 +1,5 @@
 /zdsar
 /zdsar.exe
+/zdsgen
+/zdsgen.exe
 /*.dSYM
diff --git a/tools/zds/Makefile b/tools/zds/Makefile
index 1b79b4c..611f0c8 100644
--- a/tools/zds/Makefile
+++ b/tools/zds/Makefile
@@ -50,8 +50,8 @@ endif
 
 # Targets
 
-all: zdsar.exe
-default: zdsar.exe
+all: zdsar.exe zdsgen.exe
+default: all
 .PHONY: clean
 
 # Add CFLAGS=-g on the make command line to build debug versions
@@ -59,11 +59,16 @@ default: zdsar.exe
 CFLAGS = -O2 -Wall -Wstrict-prototypes -Wshadow -I.
 CFLAGS += -DHAVE_STRTOK_C=1
 
-# zdsar - Convert virtual addresses in nuttx.hex to physical addresses
+# zdsar - Wrapper for the ZDS-II librarian
 
-zdsar.exe: zdsar.c zdsar.c
+zdsar.exe: zdsar.c
 	$(Q) $(HOSTCC) $(HOSTCFLAGS) -o zdsar.exe zdsar.c
 
+# zdsgen - Wrapper for the ZDS-II compiler and assembler
+
+zdsgen.exe: zdsgen.c
+	$(Q) $(HOSTCC) $(HOSTCFLAGS) -o zdsgen.exe zdsgen.c
+
 clean:
 	@rm -f *.o *.a *.dSYM *~ .*.swp
 	@rm -f zdsar zdsar.exe
diff --git a/tools/zds/zdsar.c b/tools/zds/zdsar.c
index 6b9561f..598512d 100644
--- a/tools/zds/zdsar.c
+++ b/tools/zds/zdsar.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * tools/zdsar.c
+ * tools/zds/zdsar.c
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -99,17 +99,6 @@
 #endif
 
 /****************************************************************************
- * Private Types
- ****************************************************************************/
-
-enum slashmode_e
-{
-  MODE_FSLASH  = 0,
-  MODE_BSLASH  = 1,
-  MODE_DBLBACK = 2
-};
-
-/****************************************************************************
  * Private Data
  ****************************************************************************/
 
@@ -124,11 +113,11 @@ static int   g_debug      = 0;       /* Debug output enabled if >0 */
 static char  g_command[MAX_BUFFER];  /* Full librarian command */
 static char  g_initial_wd[MAX_PATH]; /* Initial working directory */
 static char  g_path[MAX_PATH];       /* Buffer for expanding paths */
-static char  g_objpath[MAX_PATH];    /* Path to the object files */
+static char  g_objpath[MAX_PATH];    /* Temporary for path generation */
 #ifdef HOST_CYGWIN
-static char  g_expand[MAX_EXPAND];   /* Expanded path */
-static char  g_dequoted[MAX_PATH];   /* De-quoted path */
-static char  g_posixpath[MAX_PATH];  /* Full POSIX path */
+static char  g_expand[MAX_EXPAND];   /* Temporary for expanded path */
+static char  g_dequoted[MAX_PATH];   /* Temporary for de-quoted path */
+static char  g_hostpath[MAX_PATH];   /* Temporary for host path conversions */
 #endif
 
 /****************************************************************************
@@ -352,7 +341,7 @@ static bool dequote_path(const char *winpath)
 #endif
 
 /* If using Cygwin with a Window's Toolchain, then we have to convert the
- * POSIX path to a Windows path.
+ * POSIX path to a Windows or POSIX path.
  */
 
 #ifdef HOST_CYGWIN
@@ -366,11 +355,11 @@ static const char *convert_path(const char *path, cygwin_conv_path_t what)
   quoted = dequote_path(path);
   if (quoted)
     {
-      retptr = g_posixpath;
+      retptr = g_hostpath;
     }
   else
     {
-      retptr = &g_posixpath[1];
+      retptr = &g_hostpath[1];
     }
 
   size = cygwin_conv_path(what | CCP_RELATIVE, g_dequoted, NULL, 0);
@@ -382,7 +371,7 @@ static const char *convert_path(const char *path, cygwin_conv_path_t what)
     }
 
   ret = cygwin_conv_path(what | CCP_RELATIVE, g_dequoted,
-                         &g_posixpath[1], MAX_PATH - 3);
+                         &g_hostpath[1], MAX_PATH - 3);
   if (ret < 0)
     {
       fprintf(stderr, "# ERROR: cygwin_conv_path '%s' failed: %s\n",
@@ -393,11 +382,11 @@ static const char *convert_path(const char *path, cygwin_conv_path_t what)
   if (quoted)
     {
       size++;
-      g_posixpath[0] = '"';
-      g_posixpath[size] = '"';
+      g_hostpath[0] = '"';
+      g_hostpath[size] = '"';
     }
 
-  g_posixpath[size + 1] = '\0';
+  g_hostpath[size + 1] = '\0';
   return retptr;
 }
 #endif
@@ -467,7 +456,7 @@ static void parse_args(int argc, char **argv)
   char *objpath = NULL;
   int argidx;
 
-  /* Accumulate ARFLAGS up to "--" */
+  /* Parse arguments */
 
   for (argidx = 1; argidx < argc; argidx++)
     {
@@ -753,6 +742,7 @@ static void do_archive(void)
    * to the librarian.
    */
 
+  lasts = NULL;
   for (; ; )
     {
       /* Copy the librarian into the command buffer */
diff --git a/tools/zds/zdsgen.c b/tools/zds/zdsgen.c
new file mode 100644
index 0000000..df9ea8d
--- /dev/null
+++ b/tools/zds/zdsgen.c
@@ -0,0 +1,688 @@
+/****************************************************************************
+ * tools/zds/zdsgen.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <errno.h>
+
+#ifdef HOST_CYGWIN
+#  include <sys/cygwin.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MAX_BUFFER  (4096)
+#define MAX_EXPAND  (2048)
+
+/* MAX_PATH might be defined in stdlib.h */
+
+#if !defined(MAX_PATH)
+#  define MAX_PATH  (512)
+#endif
+
+/* NAME_MAX is typically defined in limits.h */
+
+#if !defined(NAME_MAX)
+
+  /* FILENAME_MAX might be defined in stdio.h */
+
+#  if defined(FILENAME_MAX)
+#    define NAME_MAX FILENAME_MAX
+#  else
+
+  /* MAXNAMELEN might be defined in dirent.h */
+
+#    include <dirent.h>
+#    if defined(MAXNAMLEN)
+#      define NAME_MAX MAXNAMLEN
+#    else
+
+  /* Lets not let a silly think like this stop us... just make something up */
+
+#      define NAME_MAX 256
+#    endif
+#  endif
+#endif
+
+/* Name of the host.  The ZDS-II toolchain runs only on Windows.  Therefore,
+ * the only options are (1) Windows native, or (2) Cygwin or environments
+ * that derive for Cygwin (like MSYS2).
+ */
+
+#define WINSEPARATOR '\\'
+
+#if defined(HOST_NATIVE)
+#  define SEPARATOR '\\'
+#  define HOSTNAME  "Native" /* Windows native */
+#elif defined(HOST_CYGWIN)
+#  define SEPARATOR '/'
+#  define HOSTNAME  "Cywgin" /* Cygwin or MSYS under Windows */
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum slashmode_e
+{
+  MODE_FSLASH  = 0,
+  MODE_BSLASH  = 1,
+  MODE_DBLBACK = 2
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static char *g_exe     = NULL;     /* Executable, either compiler or assembler */
+static char *g_flags   = NULL;     /* Assembler or compiler flags */
+static char *g_infile  = NULL;     /* The input source file */
+static char *g_outfile = NULL;     /* The output object file */
+static int   g_debug   = 0;
+
+static char g_command[MAX_BUFFER]; /* The command to be executed */
+static char g_path[MAX_PATH];      /* Temporary for path generation */
+#ifdef HOST_CYGWIN
+static char g_expand[MAX_EXPAND];  /* Temporary for path expansion */
+static char g_dequoted[MAX_PATH];  /* Temporary for dequoting paths */
+static char g_hostpath[MAX_PATH];  /* Temporary for host path conversions */
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* MinGW does not seem to provide strtok_r */
+
+#ifndef HAVE_STRTOK_R
+static char *my_strtok_r(char *str, const char *delim, char **saveptr)
+{
+  char *pbegin;
+  char *pend = NULL;
+
+  /* Decide if we are starting a new string or continuing from
+   * the point we left off.
+   */
+
+  if (str)
+    {
+      pbegin = str;
+    }
+  else if (saveptr && *saveptr)
+    {
+      pbegin = *saveptr;
+    }
+  else
+    {
+      return NULL;
+    }
+
+  /* Find the beginning of the next token */
+
+  for (;
+       *pbegin && strchr(delim, *pbegin) != NULL;
+       pbegin++);
+
+  /* If we are at the end of the string with nothing
+   * but delimiters found, then return NULL.
+   */
+
+  if (!*pbegin)
+    {
+      return NULL;
+    }
+
+  /* Find the end of the token */
+
+  for (pend = pbegin + 1;
+       *pend && strchr(delim, *pend) == NULL;
+       pend++);
+
+  /* pend either points to the end of the string or to
+   * the first delimiter after the string.
+   */
+
+  if (*pend)
+    {
+      /* Turn the delimiter into a null terminator */
+
+      *pend++ = '\0';
+    }
+
+  /* Save the pointer where we left off and return the
+   * beginning of the token.
+   */
+
+  if (saveptr)
+    {
+      *saveptr = pend;
+    }
+
+  return pbegin;
+}
+
+#undef strtok_r
+#define strtok_r my_strtok_r
+#endif
+
+static void append(char **base, char *str)
+{
+  char *oldbase;
+  char *newbase;
+  int alloclen;
+
+  oldbase = *base;
+  if (!oldbase)
+    {
+      newbase = strdup(str);
+      if (!newbase)
+        {
+          fprintf(stderr, "ERROR: Failed to strdup %s\n", str);
+          exit(EXIT_FAILURE);
+        }
+    }
+  else
+    {
+      alloclen = strlen(oldbase) + strlen(str) + sizeof((char) ' ') +
+                 sizeof((char) '\0');
+      newbase = (char *)malloc(alloclen);
+      if (!newbase)
+        {
+          fprintf(stderr, "ERROR: Failed to allocate %d bytes\n", alloclen);
+          exit(EXIT_FAILURE);
+        }
+
+      snprintf(newbase, alloclen, "%s %s", oldbase, str);
+      free(oldbase);
+    }
+
+  *base = newbase;
+}
+
+static void show_usage(const char *progname, const char *msg, int exitcode)
+{
+  if (msg)
+    {
+      fprintf(stderr, "\n");
+      fprintf(stderr, "%s:\n", msg);
+    }
+
+  fprintf(stderr, "\n");
+  fprintf(stderr, "%s  [--debug] <EXE> -- <FLAGS> -- <infile> <outfile>\n",
+          progname);
+  fprintf(stderr, "%s  [--help]\n\n", progname);
+  fprintf(stderr, "Where:\n");
+  fprintf(stderr, "  <EXE>\n");
+  fprintf(stderr, "    A variable number of arguments that define how to"
+                  " execute the code\n");
+  fprintf(stderr, "    generation tool (either the compiler or the "
+                  "assembler)\n");
+  fprintf(stderr, "  <FLAGS>\n");
+  fprintf(stderr, "    The compiler compilation flags\n");
+  fprintf(stderr, "  <inile>\n");
+  fprintf(stderr, "    One or more C files whose dependencies will be "
+                  "checked.  Each file is expected\n");
+  fprintf(stderr, "    to reside in the current directory\n");
+  fprintf(stderr, "  <outfile>\n");
+  fprintf(stderr, "    The name of the binary output file to be "
+                  "generated\n");
+  fprintf(stderr, "  --debug\n");
+  fprintf(stderr, "    Enable script debug\n");
+  fprintf(stderr, "  --help\n");
+  fprintf(stderr, "    Shows this message and exits\n");
+  exit(exitcode);
+}
+
+static void parse_args(int argc, char **argv)
+{
+  char *args = NULL;
+  int argidx;
+
+  /* Parse arguments */
+
+  for (argidx = 1; argidx < argc; argidx++)
+    {
+      if (strcmp(argv[argidx], "--") == 0)
+        {
+          g_exe   = g_flags;
+          g_flags = args;
+          args    = NULL;
+        }
+      else if (strcmp(argv[argidx], "--debug") == 0)
+        {
+          g_debug++;
+        }
+      else if (strcmp(argv[argidx], "--help") == 0)
+        {
+          show_usage(argv[0], NULL, EXIT_SUCCESS);
+        }
+      else if (g_exe == NULL)
+        {
+          append(&args, argv[argidx]);
+        }
+      else
+        {
+          break;
+        }
+    }
+
+  /* The penultimate argument should be the input source file */
+
+  if (argidx < argc)
+    {
+      g_infile = argv[argidx];
+      argidx++;
+    }
+  else
+    {
+      show_usage(argv[0], "ERROR: Missing input source file",
+                 EXIT_FAILURE);
+    }
+
+  /* The last argument should be the output object file */
+
+  if (argidx < argc)
+    {
+      g_outfile = argv[argidx];
+      argidx++;
+    }
+  else
+    {
+      show_usage(argv[0], "ERROR: Missing output object file",
+                 EXIT_FAILURE);
+    }
+
+  if (g_debug)
+    {
+      fprintf(stderr, "SELECTIONS\n");
+      fprintf(stderr, "  Host Environ   : [%s]\n",
+              HOSTNAME);
+      fprintf(stderr, "  Tool           : [%s]\n",
+              g_exe ? g_exe : "(None)");
+      fprintf(stderr, "  Tool flags     : [%s]\n",
+              g_flags ? g_flags : "(None)");
+      fprintf(stderr, "  Source file    : [%s]\n",
+              g_infile ? g_infile : "(None)");
+      fprintf(stderr, "  Object file    : [%s]\n",
+              g_outfile ? g_outfile : "(None)");
+    }
+
+  /* Check for required parameters */
+
+  if (g_exe == NULL)
+    {
+      show_usage(argv[0], "ERROR: No compiler specified", EXIT_FAILURE);
+    }
+
+  if (g_infile == NULL)
+    {
+      /* Don't report an error -- this happens normally in some configurations */
+
+      printf("# No source files specified\n");
+      exit(EXIT_SUCCESS);
+    }
+}
+
+static const char *do_expand(const char *argument)
+{
+#ifdef HOST_CYGWIN
+  const char *src;
+  char *dest;
+  int len;
+
+  src  = argument;
+  dest = g_expand;
+  len  = 0;
+
+  while (*src && len < MAX_EXPAND)
+    {
+      if (*src == '\\')
+        {
+          /* Copy backslash */
+
+          *dest++ = *src++;
+          if (++len >= MAX_EXPAND)
+            {
+              break;
+            }
+
+          /* Already expanded? */
+
+          if (*src == '\\')
+            {
+              /* Yes... just copy all consecutive backslashes */
+
+              do
+                {
+                  *dest++ = *src++;
+                  if (++len >= MAX_EXPAND)
+                    {
+                      break;
+                    }
+                }
+              while (*src == '\\');
+            }
+          else
+            {
+              /* No.. expeand */
+
+              *dest++ = '\\';
+              if (++len >= MAX_EXPAND)
+                {
+                  break;
+                }
+            }
+        }
+      else
+        {
+          *dest++ = *src++;
+          len++;
+        }
+    }
+
+  if (*src)
+    {
+      fprintf(stderr, "ERROR: Truncated during expansion string is "
+                      "too long [%lu/%u]\n",
+              (unsigned long)strlen(argument), MAX_EXPAND);
+      exit(EXIT_FAILURE);
+    }
+
+  *dest = '\0';
+  return g_expand;
+#else
+  return argument;
+#endif
+}
+
+#ifdef HOST_CYGWIN
+static bool dequote_path(const char *winpath)
+{
+  char *dest = g_dequoted;
+  const char *src = winpath;
+  int len = 0;
+  bool quoted = false;
+
+  while (*src && len < MAX_PATH)
+    {
+      if (src[0] != '\\' || (src[1] != ' ' && src[1] != '(' && src[1] != ')'))
+        {
+          *dest++ = *src;
+          len++;
+        }
+      else
+        {
+          quoted = true;
+        }
+
+      src++;
+    }
+
+  if (*src || len >= MAX_PATH)
+    {
+      fprintf(stderr, "# ERROR: Path truncated\n");
+      exit(EXIT_FAILURE);
+    }
+
+  *dest = '\0';
+  return quoted;
+}
+#endif
+
+/* If using Cygwin with a Window's Toolchain, then we have to convert the
+ * POSIX path to a Windows or POSIX path.
+ */
+
+#ifdef HOST_CYGWIN
+static const char *convert_path(const char *path, cygwin_conv_path_t what)
+{
+  const char *retptr;
+  ssize_t size;
+  ssize_t ret;
+  bool quoted;
+
+  quoted = dequote_path(path);
+  if (quoted)
+    {
+      retptr = g_hostpath;
+    }
+  else
+    {
+      retptr = &g_hostpath[1];
+    }
+
+  size = cygwin_conv_path(what | CCP_RELATIVE, g_dequoted, NULL, 0);
+  if (size > (MAX_PATH - 3))
+    {
+      fprintf(stderr, "# ERROR: POSIX path too long: %lu\n",
+              (unsigned long)size);
+      exit(EXIT_FAILURE);
+    }
+
+  ret = cygwin_conv_path(what | CCP_RELATIVE, g_dequoted,
+                         &g_hostpath[1], MAX_PATH - 3);
+  if (ret < 0)
+    {
+      fprintf(stderr, "# ERROR: cygwin_conv_path '%s' failed: %s\n",
+              g_dequoted, strerror(errno));
+      exit(EXIT_FAILURE);
+    }
+
+  if (quoted)
+    {
+      size++;
+      g_hostpath[0] = '"';
+      g_hostpath[size] = '"';
+    }
+
+  g_hostpath[size + 1] = '\0';
+  return retptr;
+}
+#endif
+
+static const char *convert_path_windows(const char *path)
+{
+#ifdef HOST_CYGWIN
+  return convert_path(path, CCP_POSIX_TO_WIN_A);
+#else
+  return path;
+#endif
+}
+
+static const char *convert_path_posix(const char *path)
+{
+#ifdef HOST_CYGWIN
+  return convert_path(path, CCP_WIN_A_TO_POSIX);
+#else
+  return path;
+#endif
+}
+
+static void do_generate(void)
+{
+  const char *hostpath;
+  const char *toolpath;
+  const char *expanded;
+  int cmdlen;
+  int ret;
+
+  /* First remove any existing .obj, lst, and .src files at the path of the
+   * new output object file.
+   */
+
+#warning Missing logic
+
+  /* Copy the tool path into the command buffer.  NOTE that
+   * convert_path_posix() is a no-op in Windows native mode.
+   */
+
+  hostpath = convert_path_posix(g_exe);
+  cmdlen   = strlen(hostpath);
+  if (cmdlen + 1 >= MAX_BUFFER)
+    {
+      fprintf(stderr, "ERROR: Tool string is too long [%d/%d]: %s\n",
+              cmdlen, MAX_BUFFER, hostpath);
+      exit(EXIT_FAILURE);
+    }
+
+  strcpy(g_command, hostpath);
+
+  /* Followed by a space */
+
+  if (cmdlen + 2 >= MAX_BUFFER)
+    {
+      fprintf(stderr, "ERROR: No room for whitespace [%d/%d]: %d\n",
+              cmdlen, MAX_BUFFER, 2);
+      exit(EXIT_FAILURE);
+    }
+
+  g_command[cmdlen]     = ' ';
+  g_command[cmdlen + 1] = '\0';
+  cmdlen++;
+
+  /* Copy the tool flags into the command buffer */
+
+  if (g_flags)
+    {
+      expanded = do_expand(g_flags);
+      cmdlen  += strlen(expanded);
+
+      if (cmdlen + 1 >= MAX_BUFFER)
+        {
+          fprintf(stderr, "ERROR: CFLAG string is too long [%d/%d]: %s\n",
+                  cmdlen, MAX_BUFFER, g_flags);
+          exit(EXIT_FAILURE);
+        }
+
+      strcat(g_command, expanded);
+    }
+
+  /* Add a space */
+
+  if (cmdlen + 2 >= MAX_BUFFER)
+    {
+      fprintf(stderr, "ERROR: No room for whitespace [%d/%d]: %d\n",
+              cmdlen, MAX_BUFFER, 2);
+      exit(EXIT_FAILURE);
+    }
+
+  g_command[cmdlen]     = ' ';
+  g_command[cmdlen + 1] = '\0';
+  cmdlen++;
+
+  /* Add the input source file.NOTE that convert_path_windows() is a no-op
+   * in Windows native mode.
+   */
+
+  toolpath = convert_path_windows(g_infile);
+  expanded = do_expand(toolpath);
+  cmdlen  += strlen(expanded);
+
+  if (cmdlen + 1 >= MAX_BUFFER)
+    {
+      fprintf(stderr, "ERROR: Input source file name is too long [%d/%d]: %s\n",
+              cmdlen, MAX_BUFFER, expanded);
+      exit(EXIT_FAILURE);
+    }
+
+  strcat(g_command, expanded);
+
+  /* Generate the output object file */
+
+  if (g_debug)
+    {
+      fprintf(stderr, "Executing: %s\n", g_command);
+    }
+
+  ret = system(g_command);
+#ifdef WEXITSTATUS
+  if (ret < 0 || WEXITSTATUS(ret) != 0)
+    {
+      if (ret < 0)
+        {
+          fprintf(stderr, "ERROR: system failed: %s\n", strerror(errno));
+        }
+      else
+        {
+          fprintf(stderr, "ERROR: %s failed: %d\n", g_exe, WEXITSTATUS(ret));
+        }
+
+      fprintf(stderr, "       command: %s\n", g_command);
+      exit(EXIT_FAILURE);
+    }
+#else
+  if (ret < 0)
+    {
+      fprintf(stderr, "ERROR: system failed: %s\n", strerror(errno));
+      fprintf(stderr, "       command: %s\n", g_command);
+      exit(EXIT_FAILURE);
+    }
+#endif
+
+  /* We don't really know that the command succeeded... Let's
+   * assume that it did
+   */
+
+  /* Now, check if we have to move or rename the output gerated by the
+   * compiler/assembly.  We can determine this by the just checking if
+   * the selected object file now exists after we deleted it above.  In
+   * that case we need to do nothing.
+   *
+   * In the case where we are moving the object file to sub-directory
+   * (like bin/), then we don't need to do that either.  That will be
+   * handled by the MOVEOBJ define.
+   *
+   * REVISIT:  Do we want to remove the MOVEOBJ macro?  We should do
+   * that.
+   *
+   * Otherwise, rename the output object file.
+   */
+
+#warning Missing logic
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, char **argv, char **envp)
+{
+  /* Parse command line parameters */
+
+  parse_args(argc, argv);
+
+  /* Generate the object file from the source. */
+
+  do_generate();
+  return EXIT_SUCCESS;
+}