| /**************************************************************************** |
| * 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 = 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 = 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; |
| } |