1dad62d3b7
tools/configure.c: Add missing '\n' in printf statement tools/configure.c: Add missed -g option to getopt() string tools/configure.c and tools/configure.sh: Fix Windows native pre-build kconfig-conf incompability. Looks like prebuilt Windows native kconfig-conf interprets "..\apps" as "..apps" (possibly '\a' as escape-sequence) so expand winnative path to double-backslashed variant "..\\apps". tools/mkdeps.c: Fix '\0' missing in MinGW. Implicit bug. There are 2 cases. 1. Under Linux. The code works as planned: '\n' is always replaced with '\0' due to sprintf fills n-1 bytes and reaches buffer length limit. 2. Under Windows/MinGW. There is memory corruption. Seems like it`s a bug inside MinGW/snprintf. Snprintf fills consecutively "oldbase",' ',"str",'\n', but does not inserts trailing '\0' instead of '\n'. And when next append() occurs, strlen() returns garbage-appended "oldbase". So the fix just removes '\n' and reserves space for '\0'. tools/link.bat: Fix .fakelink creation configs/Makefile and tools/Config.mk: Move single file copy to the new function COPYFILE. This fixes the Windows native build case when there is no cp or cp does not recognize Windows paths.
868 lines
22 KiB
C
868 lines
22 KiB
C
/****************************************************************************
|
|
* tools/mkdeps.c
|
|
*
|
|
* Copyright (C) 2012-2013 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 <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
|
|
|
|
/****************************************************************************
|
|
* 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
|
|
|
|
/****************************************************************************
|
|
* 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 [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, " --obj-path <path>\n");
|
|
fprintf(stderr, " The final objects will not reside in this path but, rather, at the path provided by\n");
|
|
fprintf(stderr, " <path>. if provided multiple time, only the last --obj-path will be used.\n");
|
|
fprintf(stderr, " --obj-suffix <suffix>\n");
|
|
fprintf(stderr, " If and object path is provided, then the extension will be assumed to be .o. This\n");
|
|
fprintf(stderr, " default suffix can be overrided with this command line option.\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 HOST_CYGWIN
|
|
fprintf(stderr, " --winpaths\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).\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], "--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
|
|
{
|
|
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)");
|
|
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 generataion\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 [%lu/%u]\n",
|
|
(unsigned long)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[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
|
|
|
|
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: %lu\n",
|
|
(unsigned long)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;
|
|
}
|