diff options
Diffstat (limited to 'nuttx/tools/mkdeps.c')
-rw-r--r-- | nuttx/tools/mkdeps.c | 721 |
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; +} |