aboutsummaryrefslogtreecommitdiff
path: root/nuttx/tools/mkdeps.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/tools/mkdeps.c')
-rw-r--r--nuttx/tools/mkdeps.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/nuttx/tools/mkdeps.c b/nuttx/tools/mkdeps.c
new file mode 100644
index 000000000..64d81cbd7
--- /dev/null
+++ b/nuttx/tools/mkdeps.c
@@ -0,0 +1,721 @@
+/****************************************************************************
+ * tools/mkdeps.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * 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 <errno.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MAX_BUFFER (4096)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum slashmode_e
+{
+ MODE_FSLASH = 0,
+ MODE_BSLASH = 1,
+ MODE_DBLBACK = 2
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static char *g_cc = NULL;
+static char *g_cflags = NULL;
+static char *g_files = NULL;
+static char *g_altpath = NULL;
+static int g_debug = 0;
+static bool g_winnative = false;
+#ifdef HAVE_WINPATH
+static bool g_winpath = false;
+static char *g_topdir = NULL;
+#endif
+
+static char g_command[MAX_BUFFER];
+
+/****************************************************************************
+ * 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;
+}
+
+#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) + 2;
+ newbase = (char *)malloc(alloclen);
+ if (!newbase)
+ {
+ fprintf(stderr, "ERROR: Failed to allocate %d bytes\n", alloclen);
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(newbase, alloclen, "%s %s\n", 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 [OPTIONS] CC -- CFLAGS -- file [file [file...]]\n",
+ progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where:\n");
+ fprintf(stderr, " CC\n");
+ fprintf(stderr, " A variable number of arguments that define how to execute the compiler\n");
+ fprintf(stderr, " CFLAGS\n");
+ fprintf(stderr, " The compiler compilation flags\n");
+ fprintf(stderr, " file\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 unless --dep-path is provided on the command line\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "And [OPTIONS] include:\n");
+ fprintf(stderr, " --dep-debug\n");
+ fprintf(stderr, " Enable script debug\n");
+ fprintf(stderr, " --dep-path <path>\n");
+ fprintf(stderr, " Do not look in the current directory for the file. Instead, look in <path> to see\n");
+ fprintf(stderr, " if the file resides there. --dep-path may be used multiple times to specify\n");
+ fprintf(stderr, " multiple alternative location\n");
+ fprintf(stderr, " --winnative\n");
+ fprintf(stderr, " By default, a POSIX-style environment is assumed (e.g., Linux, Cygwin, etc.) This option is\n");
+ fprintf(stderr, " inform the tool that is working in a pure Windows native environment.\n");
+#ifdef HAVE_WINPATH
+ fprintf(stderr, " --winpaths <TOPDIR>\n");
+ fprintf(stderr, " This option is useful when using a Windows native toolchain in a POSIX environment (such\n");
+ fprintf(stderr, " such as Cygwin). In this case, will CC generates dependency lists using Windows paths\n");
+ fprintf(stderr, " (e.g., C:\\blablah\\blabla). This switch instructs the script to use 'cygpath' to convert\n");
+ fprintf(stderr, " the Windows paths to Cygwin POSIXE paths.\n");
+#endif
+ 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;
+
+ /* Accumulate CFLAGS up to "--" */
+
+ for (argidx = 1; argidx < argc; argidx++)
+ {
+ if (strcmp(argv[argidx], "--") == 0)
+ {
+ g_cc = g_cflags;
+ g_cflags = args;
+ args = NULL;
+ }
+ else if (strcmp(argv[argidx], "--dep-debug") == 0)
+ {
+ g_debug++;
+ }
+ else if (strcmp(argv[argidx], "--dep-path") == 0)
+ {
+ argidx++;
+ if (argidx >= argc)
+ {
+ show_usage(argv[0], "ERROR: Missing argument to --dep-path", EXIT_FAILURE);
+ }
+
+ if (args)
+ {
+ append(&args, argv[argidx]);
+ }
+ else
+ {
+ append(&g_altpath, argv[argidx]);
+ }
+ }
+ else if (strcmp(argv[argidx], "--winnative") == 0)
+ {
+ g_winnative = true;
+ }
+#ifdef HAVE_WINPATH
+ else if (strcmp(argv[argidx], "--winpath") == 0)
+ {
+ g_winpath = true;
+ if (g_topdir)
+ {
+ free(g_topdir);
+ }
+
+ argidx++;
+ if (argidx >= argc)
+ {
+ show_usage(argv[0], "ERROR: Missing argument to --winpath", EXIT_FAILURE);
+ }
+
+ g_topdir = strdup(argv[argidx]);
+ }
+#endif
+ else if (strcmp(argv[argidx], "--help") == 0)
+ {
+ show_usage(argv[0], NULL, EXIT_SUCCESS);
+ }
+ else
+ {
+ append(&args, argv[argidx]);
+ }
+ }
+
+ /* The final thing accumulated is the list of files */
+
+ g_files = args;
+
+ /* If no paths were specified, then look in the current directory only */
+
+ if (!g_altpath)
+ {
+ g_altpath = strdup(".");
+ }
+
+ if (g_debug)
+ {
+ fprintf(stderr, "SELECTIONS\n");
+ fprintf(stderr, " CC : [%s]\n", g_cc ? g_cc : "(None)");
+ fprintf(stderr, " CFLAGS : [%s]\n", g_cflags ? g_cflags : "(None)");
+ fprintf(stderr, " FILES : [%s]\n", g_files ? g_files : "(None)");
+ fprintf(stderr, " PATHS : [%s]\n", g_altpath ? g_altpath : "(None)");
+#ifdef HAVE_WINPATH
+ fprintf(stderr, " Windows Paths : [%s]\n", g_winpath ? "TRUE" : "FALSE");
+ if (g_winpath)
+ {
+ fprintf(stderr, " TOPDIR : [%s]\n", g_topdir);
+ }
+#endif
+ fprintf(stderr, " Windows Native : [%s]\n", g_winnative ? "TRUE" : "FALSE");
+ }
+
+ /* Check for required paramters */
+
+ if (!g_cc)
+ {
+ show_usage(argv[0], "ERROR: No compiler specified", EXIT_FAILURE);
+ }
+
+ if (!g_files)
+ {
+ /* Don't report an error -- this happens normally in some configurations */
+
+ printf("# No files specified for dependency generataion\n");
+ exit(EXIT_SUCCESS);
+ }
+
+#ifdef HAVE_WINPATH
+ if (g_winnative && g_winpath)
+ {
+ show_usage(argv[0], "ERROR: Both --winnative and --winpapth makes no sense", EXIT_FAILURE);
+ }
+#endif
+}
+
+static void do_dependency(const char *file, char separator)
+{
+ static const char moption[] = " -M ";
+ struct stat buf;
+ char *alloc;
+ char *altpath;
+ char *path;
+ char *lasts;
+ int cmdlen;
+ int pathlen;
+ int filelen;
+ int totallen;
+ int ret;
+
+ /* Copy the compiler into the command buffer */
+
+ cmdlen = strlen(g_cc);
+ if (cmdlen >= MAX_BUFFER)
+ {
+ fprintf(stderr, "ERROR: Compiler string is too long [%d/%d]: %s\n",
+ cmdlen, MAX_BUFFER, g_cc);
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(g_command, g_cc);
+
+ /* Copy " -M " */
+
+ cmdlen += strlen(moption);
+ if (cmdlen >= MAX_BUFFER)
+ {
+ fprintf(stderr, "ERROR: Option string is too long [%d/%d]: %s\n",
+ cmdlen, MAX_BUFFER, moption);
+ exit(EXIT_FAILURE);
+ }
+
+ strcat(g_command, moption);
+
+ /* Copy the CFLAGS into the command buffer */
+
+ cmdlen += strlen(g_cflags);
+ if (cmdlen >= MAX_BUFFER)
+ {
+ fprintf(stderr, "ERROR: CFLAG string is too long [%d/%d]: %s\n",
+ cmdlen, MAX_BUFFER, g_cflags);
+ exit(EXIT_FAILURE);
+ }
+
+ strcat(g_command, g_cflags);
+
+ /* Add a space */
+
+ g_command[cmdlen] = ' ';
+ cmdlen++;
+ g_command[cmdlen] = '\0';
+
+ /* Make a copy of g_altpath. We need to do this because at least the version
+ * of strtok_r above does modifie it.
+ */
+
+ alloc = strdup(g_altpath);
+ if (!alloc)
+ {
+ fprintf(stderr, "ERROR: Failed to strdup paths\n");
+ exit(EXIT_FAILURE);
+ }
+
+ altpath = alloc;
+
+ /* Try each path. This loop will continue until each path has been tried
+ * (failure) or until stat() finds the file
+ */
+
+ while ((path = strtok_r(altpath, " ", &lasts)) != NULL)
+ {
+ /* Create a full path to the file */
+
+ pathlen = strlen(path);
+ totallen = cmdlen + pathlen;
+ if (totallen >= MAX_BUFFER)
+ {
+ fprintf(stderr, "ERROR: Path is too long [%d/%d]: %s\n",
+ totallen, MAX_BUFFER, path);
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(&g_command[cmdlen], path);
+
+ if (g_command[totallen] != '\0')
+ {
+ fprintf(stderr, "ERROR: Missing NUL terminator\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (g_command[totallen-1] != separator)
+ {
+ g_command[totallen] = separator;
+ g_command[totallen+1] = '\0';
+ pathlen++;
+ totallen++;
+ }
+
+ filelen = strlen(file);
+ totallen += filelen;
+ if (totallen >= MAX_BUFFER)
+ {
+ fprintf(stderr, "ERROR: Path+file is too long [%d/%d]\n",
+ totallen, MAX_BUFFER);
+ exit(EXIT_FAILURE);
+ }
+
+ strcat(g_command, file);
+
+ /* Check that a file actually exists at this path */
+
+ if (g_debug)
+ {
+ fprintf(stderr, "Trying path=%s file=%s fullpath=%s\n",
+ path, file, &g_command[cmdlen]);
+ }
+
+ ret = stat(&g_command[cmdlen], &buf);
+ if (ret < 0)
+ {
+ altpath = NULL;
+ continue;
+ }
+
+ if (!S_ISREG(buf.st_mode))
+ {
+ fprintf(stderr, "ERROR: File %s exists but is not a regular file\n",
+ &g_command[cmdlen]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Okay.. we have. Create the dependency. One a failure to start the
+ * compiler, system() will return -1; Otherwise, the returned value
+ * from the compiler is in WEXITSTATUS(ret).
+ */
+
+ 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_cc, 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 */
+
+ free(alloc);
+ return;
+ }
+
+ printf("# ERROR: File \"%s\" not found at any location\n", file);
+ exit(EXIT_FAILURE);
+}
+
+/* Convert a Cygwin path to a Windows path */
+
+#ifdef HAVE_WINPATH
+static char *cywin2windows(const char *str, const char *append, enum slashmode_e mode)
+{
+ static const char cygdrive[] = "/cydrive";
+ const char *src = src;
+ char *dest;
+ char *newpath;
+ char *allocpath = NULL;
+ int srclen = strlen(str);
+ int alloclen = 0;
+ int drive = 0;
+ int lastchar;
+
+ /* Skip any leading whitespace */
+
+ while (isspace(*str)) str++;
+
+ /* Were we asked to append something? */
+
+ if (append)
+ {
+ char *tmp;
+
+ alloclen = sizeof(str) + sizeof(append) + 1;
+ allocpath = (char *)malloc(alloclen);
+ if (!allocpath)
+ {
+ fprintf(stderr, "ERROR: Failed to allocate %d bytes\n", alloclen);
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(allocpath, alloclen, "%s/%s", str, append);
+ }
+
+ /* Looking for path of the form /cygdrive/c/bla/bla/bla */
+
+ if (strcasecmp(src, cygdrive) == 0)
+ {
+ int cygsize = sizeof(cygdrive);
+ if (src[cygsize] == '/')
+ {
+ cygsize++;
+ srclen -= cygsize;
+ src += cygsize;
+
+ if (srclen <= 0)
+ {
+ fprintf(stderr, "ERROR: Unhandled path: \"%s\"\n", str);
+ exit(EXIT_FAILURE);
+ }
+
+ drive = toupper(*src);
+ if (drive < 'A' || drive > 'Z')
+ {
+ fprintf(stderr, "ERROR: Drive charager: \"%s\"\n", str);
+ exit(EXIT_FAILURE);
+ }
+
+ srclen--;
+ src++;
+ alloclen = 2;
+ }
+ }
+
+ /* Determine the size of the new path */
+
+ alloclen += sizeof(src) + 1;
+ if (mode == MODE_DBLBACK)
+ {
+ const char *tmpptr;
+ for (tmpptr = src; *tmpptr; tmpptr++)
+ {
+ if (*tmpptr == '/') alloclen++;
+ }
+ }
+
+ /* Allocate memory for the new path */
+
+ newpath = (char *)malloc(alloclen);
+ if (!newpath)
+ {
+ fprintf(stderr, "ERROR: Failed to allocate %d bytes\n", alloclen);
+ exit(EXIT_FAILURE);
+ }
+
+ dest = newpath;
+
+ /* Copy the drive character */
+
+ if (drive)
+ {
+ *dest++ = drive;
+ *dest++ = ':';
+ }
+
+ /* Copy each character from the source, making modifications for foward
+ * slashes as required.
+ */
+
+ lastchar = '\0';
+ for (; *src; src++)
+ {
+ if (mode != MODE_FSLASH && *src == '/')
+ {
+ if (lastchar != '/')
+ {
+ *dest++ = '\\';
+ if (mode == MODE_DBLBACK)
+ {
+ *dest++ = '\\';
+ }
+ }
+ }
+ else
+ {
+ *dest++ = *src;
+ }
+
+ lastchar = *src;
+ }
+
+ *dest++ = '\0';
+ if (allocpath)
+ {
+ free(allocpath);
+ }
+ return dest;
+}
+#endif
+
+#ifdef HAVE_WINPATH
+static void do_winpath(char *file)
+{
+ /* The file is in POSIX format. CC expects Windows format to generate the
+ * dependencies, but GNU make expect the resulting dependencies to be back
+ * in POSIX format. What a mess!
+ */
+
+ char *path = cywin2windows(g_topdir, file, MODE_FSLASH);
+
+ /* Then get the dependency and perform conversions on it to make it
+ * palatable to the Cygwin make.
+ */
+#warning "Missing logic"
+
+ free(path);
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, char **argv, char **envp)
+{
+ char *lasts;
+ char *files;
+ char *file;
+
+ /* Parse command line parameters */
+
+ parse_args(argc, argv);
+
+ /* Then generate dependencies for each path on the command line. NOTE
+ * strtok_r will clobber the files list. But that is okay because we are
+ * only going to traverse it once.
+ */
+
+ files = g_files;
+ while ((file = strtok_r(files, " ", &lasts)) != NULL)
+ {
+ /* Check if we need to do path conversions for a Windows-natvie tool
+ * being using in a POSIX/Cygwin environment.
+ */
+
+#ifdef HAVE_WINPATH
+ if (g_winpath)
+ {
+ do_winpath(file);
+ }
+ else
+#endif
+ {
+ do_dependency(file, g_winnative ? '\\' : '/');
+ }
+
+ files = NULL;
+ }
+
+ return EXIT_SUCCESS;
+}