diff --git a/tools/Makefile.host b/tools/Makefile.host index 0b12bec7631..2531c207f7b 100644 --- a/tools/Makefile.host +++ b/tools/Makefile.host @@ -63,14 +63,14 @@ all: b16$(HOSTEXEEXT) bdf-converter$(HOSTEXEEXT) cmpconfig$(HOSTEXEEXT) \ mksymtab$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkversion$(HOSTEXEEXT) \ cnvwindeps$(HOSTEXEEXT) nxstyle$(HOSTEXEEXT) initialconfig$(HOSTEXEEXT) \ gencromfs$(HOSTEXEEXT) convert-comments$(HOSTEXEEXT) lowhex$(HOSTEXEEXT) \ - detab$(HOSTEXEEXT) rmcr$(HOSTEXEEXT) + detab$(HOSTEXEEXT) rmcr$(HOSTEXEEXT) incdir$(HOSTEXEEXT) default: mkconfig$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) \ - cnvwindeps$(HOSTEXEEXT) + cnvwindeps$(HOSTEXEEXT) incdir$(HOSTEXEEXT) ifdef HOSTEXEEXT .PHONY: b16 bdf-converter cmpconfig clean configure kconfig2html mkconfig \ mkdeps mksymtab mksyscall mkversion cnvwindeps nxstyle initialconfig \ - gencromfs convert-comments lowhex detab rmcr + gencromfs convert-comments lowhex detab rmcr incdir else .PHONY: clean endif @@ -221,6 +221,15 @@ ifdef HOSTEXEEXT rmcr: rmcr$(HOSTEXEEXT) endif +# incdir - Generate compiler-specific include paths + +incdir$(HOSTEXEEXT): incdir.c + $(Q) $(HOSTCC) $(HOSTCFLAGS) -o incdir$(HOSTEXEEXT) incdir.c + +ifdef HOSTEXEEXT +incdir: incdir$(HOSTEXEEXT) +endif + # cnvwindeps - Convert dependences generated by a Windows native toolchain # for use in a Cygwin/POSIX build environment diff --git a/tools/README.txt b/tools/README.txt index e824a4e9e53..3a70e5611f3 100644 --- a/tools/README.txt +++ b/tools/README.txt @@ -605,8 +605,8 @@ ide_exporter.py Obsoleted/stm32f429i_disco/ltcd/template and at Obsoleted/stm3220g-eval/template. -incdir.sh and incdir.bat ------------------------- +incdir.sh, incdir.bat, and incdir.c +----------------------------------- Different compilers have different conventions for specifying lists of include file paths on the compiler command line. This incdir.sh @@ -617,6 +617,8 @@ incdir.sh and incdir.bat build. However, there is currently only one compiler supported in that context: MinGW-GCC. + incdir.c is a higher performance version of incdir.sh, converted to C. + indent.sh --------- diff --git a/tools/incdir.c b/tools/incdir.c new file mode 100644 index 00000000000..0eaaf43429e --- /dev/null +++ b/tools/incdir.c @@ -0,0 +1,484 @@ +/**************************************************************************** + * tools/incdir.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 +#include +#include +#include +#include +#include +#include +#include + +#ifdef HOST_CYGWIN +# include +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum pathtype_e +{ + USER_PATH = 0, + SYSTEM_PATH +}; + +enum os_e +{ + OS_UNKNOWN = 0, + OS_LINUX, + OS_WINDOWS, + OS_CYGWIN, + OS_MSYS, + OS_WSL, + OS_MACOS, + OS_BSD +}; + +enum compiler_e +{ + COMPILER_UNKNOWN = 0, + COMPILER_GCC, + COMPILER_CLANG, + COMPILER_MINGW, + COMPILER_SDCC, + COMPILER_ZDSII +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void show_advice(const char *progname, int exitcode) +{ + fprintf(stderr, "\nUSAGE: %s [-h] [-w] [-s] " + " [ [ ...]]\n", + progname); + fprintf(stderr, "Try '%s -h' for more information\n", progname); + + exit(exitcode); +} + +static void show_help(const char *progname, int exitcode) +{ + fprintf(stderr, "%s is a tool for flexible generation of include path " + "arguments for a\n", + progname); + fprintf(stderr, "variety of different compilers in a variety of " + "compilation environments\n"); + fprintf(stderr, "\nUSAGE: %s [-w] [-s] " + " [ [ ...]]\n", + progname); + fprintf(stderr, " %s -h\n\n", progname); + + fprintf(stderr, "Where:\n"); + fprintf(stderr, " \n"); + fprintf(stderr, " The full path to your compiler\n"); + fprintf(stderr, " [ [ ...]]\n"); + fprintf(stderr, " A list of include directories\n"); + fprintf(stderr, " -w\n"); + fprintf(stderr, " The compiler is a Windows native tool and requires " + "Windows\n"); + fprintf(stderr, " style pathnames like C:\\Program Files\n"); + fprintf(stderr, " -s\n"); + fprintf(stderr, " Generate standard, system header file paths instead " + "of normal user\n"); + fprintf(stderr, " header file paths.\n"); + fprintf(stderr, " -h\n"); + fprintf(stderr, " Shows this help text and exits.\n"); + + exit(exitcode); +} + +static enum os_e get_os(const char *ccpath) +{ + struct utsname buf; + int ret; + + /* Check for MinGW which implies a Windows native environment */ + + if (strstr(ccpath, "mingw") != NULL) + { + return OS_WINDOWS; + } + + /* Get the context names */ + + ret = uname(&buf); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: uname failed: %s\n", strerror(errcode)); + exit(EXIT_FAILURE); + } + + if (strcmp(buf.sysname, "Linux") == 0) + { + return OS_LINUX; /* Or OS_WSL */ + } + else if (strncmp(buf.sysname, "CYGWIN", 6) == 0) + { + return OS_CYGWIN; + } + else if (strncmp(buf.sysname, "MINGW", 5) == 0) + { + return OS_CYGWIN; + } + else if (strncmp(buf.sysname, "MSYS", 4) == 0) + { + return OS_CYGWIN; + } + else if (strcmp(buf.sysname, "Darwin") == 0) + { + return OS_MACOS; + } + else if (strcmp(buf.sysname, "FreeBSD") == 0 || + strcmp(buf.sysname, "OpenBSD") == 0 || + strcmp(buf.sysname, "GNU/kFreeBSD") == 0) + { + return OS_BSD; + } + else + { + return OS_UNKNOWN; + } +} + +static enum compiler_e get_compiler(const char *ccpath, enum os_e os) +{ + /* Let's assume that all GCC compiler paths contain the string gcc or + * g++ and no non-GCC compiler paths include these substrings + */ + + if (strstr(ccpath, "gcc") != NULL || + strstr(ccpath, "g++") != NULL) + { + return COMPILER_GCC; + } + else if (strstr(ccpath, "clang") != NULL) + { + return COMPILER_CLANG; + } + else if (strstr(ccpath, "sdcc") != NULL) + { + return COMPILER_SDCC; + } + else if (strstr(ccpath, "mingw") != NULL) + { + return COMPILER_MINGW; + } + else if (strstr(ccpath, "ez8cc") != NULL || + strstr(ccpath, "zneocc") != NULL || + strstr(ccpath, "ez80cc") != NULL) + { + return COMPILER_ZDSII; + } + else + { + return COMPILER_UNKNOWN; + } +} + +static int my_asprintf(char **strp, const char *fmt, ...) +{ + va_list ap; + ssize_t bufsize; + char *buffer; + + /* Get the size of the buffer */ + + va_start(ap, fmt); + bufsize = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (bufsize <= 0) + { + fprintf(stderr, "ERROR: vsnprintf() failed.\n"); + exit (EXIT_FAILURE); + } + + buffer = malloc(bufsize + 1); + if (buffer == NULL) + { + fprintf(stderr, "ERROR: Failed allocated vsnprintf() buffer.\n"); + exit (EXIT_FAILURE); + } + + va_start(ap, fmt); + vsnprintf(buffer, bufsize + 1, fmt, ap); + va_end(ap); + + *strp = buffer; + return bufsize; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char **argv, char **envp) +{ +#ifdef HOST_CYGWIN + char *convpath = NULL; +#endif + enum pathtype_e pathtype = USER_PATH; + enum os_e os; + enum compiler_e compiler; + const char *progname = argv[0]; + const char *ccpath; + const char *cmdarg; + char * const *dirlist; + size_t respsize = 0; + char *response = NULL; + bool wintool = false; + int ndirs; + int ret; + int ch; + int i; + + /* Handle command line options */ + + while ((ch = getopt(argc, argv, "wsh")) >= 0) + { + switch (ch) + { + case 'w': + wintool = true; + break; + + case 's': + pathtype = SYSTEM_PATH; + break; + + case 'h': + show_help(progname, EXIT_SUCCESS); + } + } + + if (optind >= argc) + { + fprintf(stderr, "ERROR: Missing \n"); + show_advice(progname, EXIT_FAILURE); + } + + ccpath = argv[optind]; + optind++; + + if (optind >= argc) + { + fprintf(stderr, "ERROR: At least one directory must be supplied\n"); + show_advice(progname, EXIT_FAILURE); + } + + dirlist = &argv[optind]; + ndirs = argc - optind; + + /* Most compilers support CFLAG options like '-I' to add include + * file header paths. Some (like the Zilog tools), do not. This script + * makes the selection of header file paths compiler independent. + * + * Below are all known compiler names (as found in the board/ Make.defs + * files). If a new compiler is used that has some unusual syntax, then + * additional logic needs to be added to this file. + * + * NAME Syntax + * $(CROSSDEV)gcc -I -I -I ... + * sdcc -I -I -I ... + * $(ZDSBINDIR)/ez8cc.exe -usrinc:':::...` + * $(ZDSBINDIR)/zneocc.exe -usrinc:':::...` + * $(ZDSBINDIR)/ez80cc.exe -usrinc:':::...` + * + * Furthermore, just to make matters more difficult, with Windows based + * toolchains, we have to use the full windows-style paths to the header + * files. + */ + + os = get_os(ccpath); + if (os == OS_UNKNOWN) + { + fprintf(stderr, "ERROR: Operating system not recognized.\n"); + show_advice(progname, EXIT_FAILURE); + } + + compiler = get_compiler(ccpath, os); + if (compiler == COMPILER_UNKNOWN) + { + fprintf(stderr, "ERROR: Compiler not recognized.\n"); + show_advice(progname, EXIT_FAILURE); + } + + /* Select system or user header file path command line option */ + + if (compiler == COMPILER_ZDSII) + { + cmdarg = (pathtype == SYSTEM_PATH) ? "-stdinc:" : "-usrinc:"; + wintool = true; + } + else + { + cmdarg = (pathtype == SYSTEM_PATH) ? "-isystem" : "-I"; + } + + /* Now process each directory in the directory list */ + + for (i = 0; i < ndirs; i++) + { + const char *dirname; + const char *incpath; + char *saveresp; + char *segment = NULL; + size_t segsize; + + dirname = dirlist[i]; + +#ifdef HOST_CYGWIN + /* Check if the path needs to be extended for Windows-based tools under + * Cygwin: + * + * wintool == true: The platform is Cygwin and we are using a windows + * native tool + */ + + if (os == OS_CYGWIN && wintool) + { + ssize_t bufsize; + + bufsize = cygwin_conv_path(CCP_POSIX_TO_WIN_A, dirname, NULL, 0); + convpath = (char *)malloc(bufsize); + if (convpath == NULL) + { + fprintf(stderr, "ERROR: Faile to allocate buffer.\n"); + exit(EXIT_FAILURE); + } + + (void)cygwin_conv_path(CCP_POSIX_TO_WIN_A, dirname, convpath, + bufsize); + incpath = convpath; + } + else +#endif + { + incpath = dirname; + } + + /* Handle the output using the selected format */ + + if (compiler == COMPILER_ZDSII) + { + /* FORM: -stdinc: 'dir1;dir2;...;dirN' + * -usrinc: 'dir1;dir2;...;dirN' + */ + + /* Treat the first directory differently */ + + if (response == NULL) + { + if (i == ndirs - 1) + { + ret = my_asprintf(&segment, "%s '%s'", cmdarg, incpath); + } + else + { + ret = my_asprintf(&segment, "%s '%s", cmdarg, incpath); + } + } + else + { + if (i == ndirs - 1) + { + ret = my_asprintf(&segment, ";%s'", incpath); + } + else + { + ret = my_asprintf(&segment, ";%s", incpath); + } + } + } + else + { + /* FORM: -isystem: "dir1" -isystem "dir2" ... -isystem "dirN" + * -I: "dir1" -I "dir2" ... -I "dirN" + */ + + /* Treat the first directory differently */ + + if (response == NULL) + { + ret = my_asprintf(&segment, "%s \"%s\"", cmdarg, incpath); + } + else + { + ret = my_asprintf(&segment, " %s \"%s\"", cmdarg, incpath); + } + } + + if (ret < 0) + { + fprintf(stderr, "ERROR: my_asprintf failed.\n"); + exit(EXIT_FAILURE); + } + + /* Append the new response segment */ + + saveresp = response; + segsize = ret; + respsize += (response == NULL) ? segsize + 1 : segsize; + + response = (char *)malloc(respsize); + if (response == NULL) + { + fprintf(stderr, "ERROR: Failed to allocate response.\n"); + exit(EXIT_FAILURE); + } + + if (saveresp == NULL) + { + strncpy(response, segment, respsize); + } + else + { + snprintf(response, respsize, "%s%s", saveresp, segment); + } + + /* Clean up for the next pass */ + + if (segment != NULL) + { + free(segment); + segment = NULL; + } + +#ifdef HOST_CYGWIN + if (convpath != NULL) + { + free(convpath); + convpath = NULL; + } +#endif + } + + fputs(response, stdout); + return EXIT_SUCCESS; +}