/**************************************************************************** * 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 ****************************************************************************/ #ifndef CONFIG_WINDOWS_NATIVE #include <sys/utsname.h> #endif #include <stdbool.h> #include <stdlib.h> #include <stdarg.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <libgen.h> #include <errno.h> #ifdef HOST_CYGWIN # include <sys/cygwin.h> #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] <compiler-path> " "<dir1> [<dir2> [<dir3> ...]]\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] <compiler-path> " "<dir1> [<dir2> [<dir3> ...]]\n", progname); fprintf(stderr, " %s -h\n\n", progname); fprintf(stderr, "Where:\n"); fprintf(stderr, " <compiler-path>\n"); fprintf(stderr, " The full path to your compiler\n"); fprintf(stderr, " <dir1> [<dir2> [<dir3> ...]]\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(void) { #ifdef CONFIG_WINDOWS_NATIVE return OS_WINDOWS; #else struct utsname buf; int ret; /* 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 { fprintf(stderr, "ERROR: Unknown operating system: %s\n", buf.sysname); return OS_UNKNOWN; } #endif } static enum compiler_e get_compiler(char *ccname) { /* Let's assume that all GCC compiler paths contain the string gcc or * g++ and no non-GCC compiler paths include these substrings. * * If the compiler is called cc, let's assume that is GCC too. */ if (strstr(ccname, "gcc") != NULL || strstr(ccname, "g++") != NULL || strncmp(ccname, "cc.", 3) == 0) { return COMPILER_GCC; } else if (strstr(ccname, "clang") != NULL) { return COMPILER_CLANG; } else if (strstr(ccname, "sdcc") != NULL) { return COMPILER_SDCC; } else if (strstr(ccname, "mingw") != NULL) { return COMPILER_MINGW; } else if (strstr(ccname, "ez8cc") != NULL || strstr(ccname, "zneocc") != NULL || strstr(ccname, "ez80cc") != NULL) { return COMPILER_ZDSII; } else { /* Unknown compiler. Assume GCC-compatible */ return COMPILER_GCC; } } 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; bool wintool = false; #endif enum pathtype_e pathtype = USER_PATH; enum os_e os; enum compiler_e compiler; const char *progname = argv[0]; const char *cmdarg; char *ccname; char * const *dirlist; size_t respsize = 0; char *response = NULL; int ndirs; int ret; int ch; int i; /* Handle command line options */ while ((ch = getopt(argc, argv, "wsh")) >= 0) { switch (ch) { case 'w': #ifdef HOST_CYGWIN wintool = true; #endif break; case 's': pathtype = SYSTEM_PATH; break; case 'h': show_help(progname, EXIT_SUCCESS); } } if (optind >= argc) { fprintf(stderr, "ERROR: Missing <compiler-path>\n"); show_advice(progname, EXIT_FAILURE); } ccname = basename(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<dir>' 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<dir1> -I<dir2> -I<dir3> ... * sdcc -I<dir2> -I<dir2> -I<dir3> ... * $(ZDSBINDIR)/ez8cc.exe -usrinc:'<dir1>:<dir2>:<dir3>:...` * $(ZDSBINDIR)/zneocc.exe -usrinc:'<dir1>:<dir2>:<dir3>:...` * $(ZDSBINDIR)/ez80cc.exe -usrinc:'<dir1>:<dir2>:<dir3>:...` * * 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(); if (os == OS_UNKNOWN) { fprintf(stderr, "ERROR: Operating system not recognized\n"); show_advice(progname, EXIT_FAILURE); } compiler = get_compiler(ccname); 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:"; #ifdef HOST_CYGWIN wintool = true; #endif } else if (compiler == COMPILER_SDCC) { cmdarg = "-I"; } 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: Failed to allocate buffer.\n"); exit(EXIT_FAILURE); } 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 (saveresp != NULL) { free(saveresp); } if (segment != NULL) { free(segment); segment = NULL; } #ifdef HOST_CYGWIN if (convpath != NULL) { free(convpath); convpath = NULL; } #endif } fputs(response, stdout); free(response); return EXIT_SUCCESS; }