| /**************************************************************************** |
| * tools/mkdeps.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 |
| ****************************************************************************/ |
| |
| #define _FILE_OFFSET_BITS 64 |
| |
| #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> |
| #include <dirent.h> |
| |
| #ifdef HOST_CYGWIN |
| # include <sys/cygwin.h> |
| #endif |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define MAX_BUFFER (16384) |
| #define MAX_EXPAND (16384) |
| #define MAX_SHQUOTE (16384) |
| |
| /* 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 */ |
| |
| # 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 |
| |
| /**************************************************************************** |
| * 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 char *g_objpath = NULL; |
| static char *g_suffix = ".o"; |
| static int g_debug = 0; |
| static bool g_winnative = false; |
| #ifdef HOST_CYGWIN |
| static bool g_winpath = false; |
| #endif |
| |
| static char g_command[MAX_BUFFER]; |
| static char g_path[MAX_PATH]; |
| #ifdef HOST_CYGWIN |
| static char g_expand[MAX_EXPAND]; |
| static char g_dequoted[MAX_PATH]; |
| static char g_posixpath[MAX_PATH]; |
| #endif |
| #ifndef CONFIG_WINDOWS_NATIVE |
| static char g_shquote[MAX_SHQUOTE]; |
| #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, const 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 = 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 [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\n"); |
| fprintf(stderr, " 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\n"); |
| fprintf(stderr, " checked. Each file is expected\n"); |
| fprintf(stderr, " to reside in the current directory unless\n"); |
| fprintf(stderr, " --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\n"); |
| fprintf(stderr, " file. Instead, look in <path> to see\n"); |
| fprintf(stderr, " if the file resides there. --dep-path may be\n"); |
| fprintf(stderr, " used multiple times to specify\n"); |
| fprintf(stderr, " multiple alternative location\n"); |
| fprintf(stderr, " --obj-path <path>\n"); |
| fprintf(stderr, " The final objects will not reside in this path\n"); |
| fprintf(stderr, " but, rather, at the path provided by\n"); |
| fprintf(stderr, " <path>. if provided multiple time, only the last\n"); |
| fprintf(stderr, " --obj-path will be used.\n"); |
| fprintf(stderr, " --obj-suffix <suffix>\n"); |
| fprintf(stderr, " If an object path is provided, then the extension\n"); |
| fprintf(stderr, " will be assumed to be .o. This\n"); |
| fprintf(stderr, " default suffix can be overridden with this\n"); |
| fprintf(stderr, " command line option.\n"); |
| fprintf(stderr, " --winnative\n"); |
| fprintf(stderr, " By default, a POSIX-style environment is assumed\n"); |
| fprintf(stderr, " (e.g., Linux, Cygwin, etc.) This option is\n"); |
| fprintf(stderr, " inform the tool that is working in a pure Windows\n"); |
| fprintf(stderr, " native environment.\n"); |
| #ifdef HOST_CYGWIN |
| fprintf(stderr, " --winpaths\n"); |
| fprintf(stderr, " This option is useful when using a Windows native\n"); |
| fprintf(stderr, " toolchain in a POSIX environment (such such as\n"); |
| fprintf(stderr, " Cygwin). In this case, will CC\n"); |
| fprintf(stderr, " generates dependency lists using Windows paths\n"); |
| fprintf(stderr, " (e.g., C:\\blablah\\blabla).\n"); |
| #endif |
| fprintf(stderr, " --help\n"); |
| fprintf(stderr, " Shows this message and exits\n"); |
| exit(exitcode); |
| } |
| |
| /**************************************************************************** |
| * Name: do_shquote |
| * |
| * Description: |
| * Escape the given string for use with the shell. |
| * |
| * The idea was taken from: |
| * https://netbsd.gw.com/cgi-bin/man-cgi?shquote++NetBSD-current |
| * However, this implementation doesn't try to elide extraneous quotes. |
| ****************************************************************************/ |
| #ifndef CONFIG_WINDOWS_NATIVE |
| static const char *do_shquote(const char *argument) |
| { |
| const char *src; |
| char *dest; |
| int len; |
| |
| src = argument; |
| dest = g_shquote; |
| len = 0; |
| |
| if (len < sizeof(g_shquote)) |
| { |
| *dest++ = '\''; |
| len++; |
| } |
| |
| while (*src && len < sizeof(g_shquote)) |
| { |
| if (*src == '\'') |
| { |
| /* Expand single quote to '\'' */ |
| |
| if (len + 4 > sizeof(g_shquote)) |
| { |
| break; |
| } |
| |
| src++; |
| memcpy(dest, "\'\\\'\'", 4); |
| dest += 4; |
| len += 4; |
| } |
| else |
| { |
| *dest++ = *src++; |
| len++; |
| } |
| } |
| |
| if (*src || len + 2 > sizeof(g_shquote)) |
| { |
| fprintf(stderr, |
| "ERROR: Truncated during shquote string is too long" |
| "[%zu/%zu]\n", strlen(argument), sizeof(g_shquote)); |
| exit(EXIT_FAILURE); |
| } |
| |
| *dest++ = '\''; |
| *dest = '\0'; |
| return g_shquote; |
| } |
| #endif |
| |
| static void parse_args(int argc, char **argv) |
| { |
| char *args = NULL; |
| int argidx; |
| int group = 0; |
| |
| /* Always look in the current directory */ |
| |
| g_altpath = strdup("."); |
| |
| /* Accumulate CFLAGS up to "--" */ |
| |
| for (argidx = 1; argidx < argc; argidx++) |
| { |
| if (strcmp(argv[argidx], "--") == 0) |
| { |
| g_cc = g_cflags; |
| g_cflags = args; |
| args = NULL; |
| group++; |
| } |
| 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], "--obj-path") == 0) |
| { |
| argidx++; |
| if (argidx >= argc) |
| { |
| show_usage(argv[0], "ERROR: Missing argument to --obj-path", |
| EXIT_FAILURE); |
| } |
| |
| g_objpath = argv[argidx]; |
| } |
| else if (strcmp(argv[argidx], "--obj-suffix") == 0) |
| { |
| argidx++; |
| if (argidx >= argc) |
| { |
| show_usage(argv[0], "ERROR: Missing argument to --obj-suffix", |
| EXIT_FAILURE); |
| } |
| |
| g_suffix = argv[argidx]; |
| } |
| else if (strcmp(argv[argidx], "--winnative") == 0) |
| { |
| g_winnative = true; |
| } |
| #ifdef HOST_CYGWIN |
| else if (strcmp(argv[argidx], "--winpath") == 0) |
| { |
| g_winpath = true; |
| } |
| #endif |
| else if (strcmp(argv[argidx], "--help") == 0) |
| { |
| show_usage(argv[0], NULL, EXIT_SUCCESS); |
| } |
| else |
| { |
| const char *arg = argv[argidx]; |
| |
| /* This condition means "perform shquote for |
| * g_cflags, but not g_cc or g_files". |
| * |
| * It isn't safe to escape g_cc because, for some reasons, |
| * Makefile passes it as a single argument like: |
| * |
| * $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) |
| * |
| * It isn't safe to escape g_files because |
| * do_dependency() uses them as bare filenames as well. |
| * (In addition to passing them to system().) |
| */ |
| #ifndef CONFIG_WINDOWS_NATIVE |
| if (group == 1) |
| { |
| arg = do_shquote(arg); |
| } |
| |
| #endif |
| append(&args, arg); |
| } |
| } |
| |
| /* The final thing accumulated is the list of files */ |
| |
| g_files = args; |
| |
| 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)"); |
| if (g_objpath) |
| { |
| fprintf(stderr, " OBJDIR : [%s]\n", g_objpath); |
| fprintf(stderr, " SUFFIX : [%s]\n", g_suffix); |
| } |
| else |
| { |
| fprintf(stderr, " OBJDIR : (None)\n"); |
| } |
| |
| #ifdef HOST_CYGWIN |
| fprintf(stderr, " Windows Paths : [%s]\n", |
| g_winpath ? "TRUE" : "FALSE"); |
| #endif |
| fprintf(stderr, " Windows Native : [%s]\n", |
| g_winnative ? "TRUE" : "FALSE"); |
| } |
| |
| /* Check for required parameters */ |
| |
| 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 generation\n"); |
| exit(EXIT_SUCCESS); |
| } |
| |
| #ifdef HOST_CYGWIN |
| if (g_winnative && g_winpath) |
| { |
| show_usage(argv[0], |
| "ERROR: Both --winnative and --winpath makes no sense", |
| EXIT_FAILURE); |
| } |
| #endif |
| } |
| |
| static const char *do_expand(const char *argument) |
| { |
| #ifdef HOST_CYGWIN |
| if (g_winpath) |
| { |
| 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" |
| "[%zu/%u]\n", strlen(argument), MAX_EXPAND); |
| exit(EXIT_FAILURE); |
| } |
| |
| *dest = '\0'; |
| return g_expand; |
| } |
| else |
| #endif |
| { |
| return argument; |
| } |
| } |
| |
| #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 != '\\' || (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 |
| |
| static const char *convert_path(const char *path) |
| { |
| #ifdef HOST_CYGWIN |
| if (g_winpath) |
| { |
| const char *retptr; |
| ssize_t size; |
| ssize_t ret; |
| bool quoted; |
| |
| quoted = dequote_path(path); |
| if (quoted) |
| { |
| retptr = g_posixpath; |
| } |
| else |
| { |
| retptr = &g_posixpath[1]; |
| } |
| |
| size = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted, |
| NULL, 0); |
| if (size > (MAX_PATH - 3)) |
| { |
| fprintf(stderr, "# ERROR: POSIX path too long: %zd\n", size); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted, |
| &g_posixpath[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_posixpath[0] = '"'; |
| g_posixpath[size] = '"'; |
| } |
| |
| g_posixpath[size + 1] = '\0'; |
| return retptr; |
| } |
| else |
| #endif |
| { |
| return path; |
| } |
| } |
| |
| static void do_dependency(const char *file) |
| { |
| static const char moption[] = " -M "; |
| struct stat buf; |
| char *alloc; |
| char *altpath; |
| char *path; |
| char *lasts; |
| char separator; |
| int cmdlen; |
| int pathlen; |
| int filelen; |
| int totallen; |
| int ret; |
| |
| /* Initialize the separator */ |
| |
| #ifdef HOST_CYGWIN |
| separator = (g_winnative || g_winpath) ? '\\' : '/'; |
| #else |
| separator = g_winnative ? '\\' : '/'; |
| #endif |
| |
| /* 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 " -MT " */ |
| |
| if (g_objpath) |
| { |
| char tmp[NAME_MAX + 6]; |
| char *dupname; |
| char *objname; |
| char *dotptr; |
| const char *expanded; |
| |
| dupname = strdup(file); |
| if (!dupname) |
| { |
| fprintf(stderr, "ERROR: Failed to dup: %s\n", file); |
| exit(EXIT_FAILURE); |
| } |
| |
| objname = basename(dupname); |
| dotptr = strrchr(objname, '.'); |
| if (dotptr) |
| { |
| *dotptr = '\0'; |
| } |
| |
| snprintf(tmp, NAME_MAX + 6, " -MT %s%c%s%s ", |
| g_objpath, separator, objname, g_suffix); |
| expanded = do_expand(tmp); |
| |
| cmdlen += strlen(expanded); |
| 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, expanded); |
| free(dupname); |
| } |
| |
| /* 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 */ |
| |
| if (g_cflags) |
| { |
| const char *expanded; |
| |
| expanded = do_expand(g_cflags); |
| cmdlen += strlen(expanded); |
| |
| 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, expanded); |
| } |
| |
| /* 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 modify 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) |
| { |
| const char *expanded; |
| const char *converted; |
| |
| /* Create a full path to the file */ |
| |
| pathlen = strlen(path); |
| if (pathlen >= MAX_PATH) |
| { |
| fprintf(stderr, "ERROR: Path is too long [%d/%d]: %s\n", |
| pathlen, MAX_PATH, path); |
| exit(EXIT_FAILURE); |
| } |
| |
| strcpy(g_path, path); |
| |
| if (g_path[pathlen] != '\0') |
| { |
| fprintf(stderr, "ERROR: Missing NUL terminator\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (g_path[pathlen - 1] != separator) |
| { |
| g_path[pathlen] = separator; |
| g_path[pathlen + 1] = '\0'; |
| pathlen++; |
| } |
| |
| filelen = strlen(file); |
| pathlen += filelen; |
| if (pathlen >= MAX_PATH) |
| { |
| fprintf(stderr, "ERROR: Path+file is too long [%d/%d]\n", |
| pathlen, MAX_PATH); |
| exit(EXIT_FAILURE); |
| } |
| |
| strcat(g_path, 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_path); |
| } |
| |
| converted = convert_path(g_path); |
| ret = stat(converted, &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_path); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* Append the expanded path to the command */ |
| |
| expanded = do_expand(g_path); |
| pathlen = strlen(expanded); |
| totallen = cmdlen + pathlen; |
| |
| if (totallen >= MAX_BUFFER) |
| { |
| fprintf(stderr, "ERROR: Path string is too long [%d/%d]: %s\n", |
| totallen, MAX_BUFFER, g_path); |
| exit(EXIT_FAILURE); |
| } |
| |
| strcat(g_command, expanded); |
| |
| /* Okay.. we have everything. 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). |
| */ |
| |
| 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_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); |
| } |
| |
| /**************************************************************************** |
| * 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-natvive tool |
| * being using in a POSIX/Cygwin environment. |
| */ |
| |
| do_dependency(file); |
| files = NULL; |
| } |
| |
| return EXIT_SUCCESS; |
| } |