| /**************************************************************************** |
| * tools/configure.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * 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 <sys/stat.h> |
| |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <libgen.h> |
| #include <errno.h> |
| |
| #include "cfgparser.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define BUFFER_SIZE 1024 |
| |
| #ifdef WIN32 |
| # define strndup(x, y) strdup(x) |
| #endif |
| |
| #define HOST_NOCHANGE 0 |
| #define HOST_LINUX 1 |
| #define HOST_MACOS 2 |
| #define HOST_WINDOWS 3 |
| #define HOST_BSD 4 |
| |
| #define WINDOWS_NATIVE 1 |
| #define WINDOWS_CYGWIN 2 |
| #define WINDOWS_MSYS 3 |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static void show_usage(const char *progname, int exitcode); |
| static void dumpcfgs(void); |
| static void debug(const char *fmt, ...); |
| static void parse_args(int argc, char **argv); |
| static int run_make(const char *arg); |
| static bool filecmp(const char *f1, const char *f2); |
| static bool check_directory(const char *directory); |
| static void verify_directory(const char *directory); |
| static bool verify_optiondir(const char *directory); |
| static bool verify_file(const char *path); |
| static void find_topdir(void); |
| typedef void (*config_callback)(const char *boarddir, const char *archname, |
| const char *chipname, const char *boardname, |
| const char *configname, void *data); |
| static void config_search(const char *boarddir, |
| config_callback callback, void *data); |
| static void archname_callback(const char *boarddir, const char *archname, |
| const char *chipname, const char *boardname, |
| const char *configname, void *data); |
| static void find_archname(void); |
| static void enumerate_callback(const char *boarddir, const char *archname, |
| const char *chipname, const char *boardname, |
| const char *configname, void *data); |
| static void enumerate_configs(void); |
| static void check_configdir(void); |
| static void check_configured(void); |
| static void read_configfile(void); |
| static void read_versionfile(void); |
| static void get_verstring(void); |
| static bool verify_appdir(const char *appdir); |
| static void check_appdir(void); |
| static void check_configuration(void); |
| static void copy_file(const char *srcpath, |
| const char *destpath, mode_t mode); |
| static void substitute(char *str, int ch1, int ch2); |
| static char *double_appdir_backslashes(char *old_appdir); |
| static void copy_optional(void); |
| static void enable_feature(const char *destconfig, const char *varname); |
| static void disable_feature(const char *destconfig, const char *varname); |
| static void set_host(const char *destconfig); |
| static void configure(void); |
| static void refresh(void); |
| static void save_original_config(void); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_WINDOWS_NATIVE |
| static char g_delim = '\\'; /* Delimiter to use when forming paths */ |
| static bool g_winpaths = true; /* True: Windows style paths */ |
| #else |
| static char g_delim = '/'; /* Delimiter to use when forming paths */ |
| static bool g_winpaths = false; /* False: POSIX style paths */ |
| #endif |
| static bool g_debug = false; /* Enable debug output */ |
| static bool g_enforce = false; /* Enfore distclean */ |
| static bool g_distclean = false; /* Distclean if configured */ |
| |
| static const char *g_appdir = NULL; /* Relative path to the application directory */ |
| static const char *g_archdir = NULL; /* Name of architecture subdirectory */ |
| static const char *g_chipdir = NULL; /* Name of chip subdirectory */ |
| static const char *g_boarddir = NULL; /* Name of board subdirectory */ |
| static char *g_configdir = NULL; /* Name of configuration subdirectory */ |
| |
| static char *g_topdir = NULL; /* Full path to top-level NuttX build directory */ |
| static char *g_apppath = NULL; /* Full path to the application directory */ |
| static char *g_configtop = NULL; /* Full path to the top-level configuration directory */ |
| static char *g_configpath = NULL; /* Full path to the configuration sub-directory */ |
| static char *g_scriptspath = NULL; /* Full path to the scripts sub-directory */ |
| static char *g_verstring = "0.0"; /* Version String */ |
| |
| static char *g_srcdefconfig = NULL; /* Source defconfig file */ |
| static char *g_srcmakedefs = NULL; /* Source Make.defs file */ |
| |
| static char **g_makeargv = NULL; /* Arguments pass to make */ |
| |
| static bool g_winnative = false; /* True: Windows native configuration */ |
| static bool g_oldnative = false; /* True: Was Windows native configuration */ |
| static bool g_needapppath = true; /* Need to add app path to the .config file */ |
| |
| static uint8_t g_host = HOST_NOCHANGE; |
| static uint8_t g_windows = WINDOWS_CYGWIN; |
| |
| static char g_buffer[BUFFER_SIZE]; /* Scratch buffer for forming full paths */ |
| |
| static struct variable_s *g_configvars = NULL; |
| static struct variable_s *g_versionvars = NULL; |
| |
| /* Optional configuration files */ |
| |
| static const char *g_optfiles[] = |
| { |
| ".gdbinit", |
| ".cproject", |
| ".project" |
| }; |
| |
| #define N_OPTFILES (sizeof(g_optfiles) / sizeof(const char *)) |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static void show_usage(const char *progname, int exitcode) |
| { |
| fprintf(stderr, "\nUSAGE: %s [-d] [-E] [-e] [-b|f] [-L] [-l|m|c|g|n|B] " |
| "[-a <app-dir>] <board-name>:<config-name> [make-opts]\n", |
| progname); |
| fprintf(stderr, "\nUSAGE: %s [-h]\n", progname); |
| fprintf(stderr, "\nWhere:\n"); |
| fprintf(stderr, " -d:\n"); |
| fprintf(stderr, " Enables debug output\n"); |
| fprintf(stderr, " -E:\n"); |
| fprintf(stderr, " Enforce distclean if already configured\n"); |
| fprintf(stderr, " -e:\n"); |
| fprintf(stderr, " Performs distclean if configuration changed\n"); |
| fprintf(stderr, " -b:\n"); |
| #ifdef CONFIG_WINDOWS_NATIVE |
| fprintf(stderr, " Informs the tool that it should use Windows style\n"); |
| fprintf(stderr, " paths like C:\\Program Files instead of POSIX\n"); |
| fprintf(stderr, " style paths are used like /usr/local/bin. Windows\n"); |
| fprintf(stderr, " style paths are used by default.\n"); |
| #else |
| fprintf(stderr, " Informs the tool that it should use Windows style\n"); |
| fprintf(stderr, " paths like C:\\Program Files. By default, POSIX\n"); |
| fprintf(stderr, " style paths like /usr/local/bin are used.\n"); |
| #endif |
| fprintf(stderr, " -f:\n"); |
| #ifdef CONFIG_WINDOWS_NATIVE |
| fprintf(stderr, " Informs the tool that it should use POSIX style\n"); |
| fprintf(stderr, " paths like /usr/local/bin. By default, Windows\n"); |
| fprintf(stderr, " style paths like C:\\Program Files are used.\n"); |
| #else |
| fprintf(stderr, " Informs the tool that it should use POSIX style\n"); |
| fprintf(stderr, " paths like /usr/local/bin instead of Windows\n"); |
| fprintf(stderr, " style paths like C:\\Program Files are used.\n"); |
| fprintf(stderr, " POSIX style paths are used by default.\n"); |
| #endif |
| fprintf(stderr, " [-l|m|c|g|n]\n"); |
| fprintf(stderr, " Selects the host environment.\n"); |
| fprintf(stderr, " -l Selects the Linux (l) host environment.\n"); |
| fprintf(stderr, " -m Selects the macOS (m) host environment.\n"); |
| fprintf(stderr, " -B Selects the *BSD (B) host environment.\n"); |
| fprintf(stderr, " -c Selects the Windows Cygwin (c) environment.\n"); |
| fprintf(stderr, " -g Selects the Windows MinGW/MSYS environment.\n"); |
| fprintf(stderr, " -n Selects the Windows native (n) environment.\n"); |
| fprintf(stderr, " Default: Use host setup in the defconfig file.\n"); |
| fprintf(stderr, " Default Windows: Cygwin.\n"); |
| fprintf(stderr, " -L:\n"); |
| fprintf(stderr, " Lists all available configurations.\n"); |
| fprintf(stderr, " -a <app-dir>:\n"); |
| fprintf(stderr, " Informs the configuration tool where the\n"); |
| fprintf(stderr, " application build directory. This is a relative\n"); |
| fprintf(stderr, " path from the top-level NuttX build directory.\n"); |
| fprintf(stderr, " But default, this tool will look in the usual\n"); |
| fprintf(stderr, " places to locate the application directory:\n"); |
| fprintf(stderr, " ..%capps or\n", g_delim); |
| fprintf(stderr, " ..%capps-xx.yy where xx.yy is the version number.\n", |
| g_delim); |
| fprintf(stderr, " <board-name>:\n"); |
| fprintf(stderr, " Identifies the board. This must correspond to a\n"); |
| fprintf(stderr, " board directory under nuttx%cboards%c.\n", |
| g_delim, g_delim); |
| fprintf(stderr, " <config-name>:\n"); |
| fprintf(stderr, " Identifies the specific configuration for the\n"); |
| fprintf(stderr, " selected <board-name>. This must correspond to\n"); |
| fprintf(stderr, " a sub-directory under the board directory at\n"); |
| fprintf(stderr, " under nuttx%cboards%c<board-name>%cconfigs%c.\n", |
| g_delim, g_delim, g_delim, g_delim); |
| fprintf(stderr, " [make-opts]:\n"); |
| fprintf(stderr, " Options directly pass to make\n"); |
| fprintf(stderr, " -h:\n"); |
| fprintf(stderr, " Prints this message and exits.\n"); |
| exit(exitcode); |
| } |
| |
| static void dumpcfgs(void) |
| { |
| find_topdir(); |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cboards", g_topdir, g_delim); |
| verify_directory(g_buffer); |
| g_configtop = strdup(g_buffer); |
| enumerate_configs(); |
| free(g_configtop); |
| exit(EXIT_SUCCESS); |
| } |
| |
| static void debug(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| if (g_debug) |
| { |
| va_start(ap, fmt); |
| vprintf(fmt, ap); |
| va_end(ap); |
| } |
| } |
| |
| static void parse_args(int argc, char **argv) |
| { |
| char *ptr; |
| int ch; |
| |
| /* Parse command line options */ |
| |
| while ((ch = getopt(argc, argv, "a:bcdEefghLlmBnu")) > 0) |
| { |
| switch (ch) |
| { |
| case 'a' : |
| g_appdir = optarg; |
| break; |
| |
| case 'b' : |
| g_delim = '\\'; |
| g_winpaths = false; |
| break; |
| |
| case 'c' : |
| g_host = HOST_WINDOWS; |
| g_windows = WINDOWS_CYGWIN; |
| break; |
| |
| case 'd' : |
| g_debug = true; |
| break; |
| |
| case 'E' : |
| g_enforce = true; |
| break; |
| |
| case 'e' : |
| g_distclean = true; |
| break; |
| |
| case 'f' : |
| g_delim = '/'; |
| g_winpaths = true; |
| break; |
| |
| case 'g' : |
| g_host = HOST_WINDOWS; |
| g_windows = WINDOWS_MSYS; |
| break; |
| |
| case 'h' : |
| show_usage(argv[0], EXIT_SUCCESS); |
| |
| case 'L' : |
| dumpcfgs(); |
| |
| case 'l' : |
| g_host = HOST_LINUX; |
| break; |
| |
| case 'm' : |
| g_host = HOST_MACOS; |
| break; |
| |
| case 'B' : |
| g_host = HOST_BSD; |
| break; |
| |
| case 'n' : |
| g_host = HOST_WINDOWS; |
| g_windows = WINDOWS_NATIVE; |
| break; |
| |
| case '?' : |
| fprintf(stderr, "ERROR: Unrecognized option: %c\n", optopt); |
| show_usage(argv[0], EXIT_FAILURE); |
| |
| case ':' : |
| fprintf(stderr, "ERROR: Missing option argument, option: %c\n", |
| optopt); |
| show_usage(argv[0], EXIT_FAILURE); |
| |
| default: |
| fprintf(stderr, "ERROR: Unexpected option: %c\n", ch); |
| show_usage(argv[0], EXIT_FAILURE); |
| } |
| } |
| |
| /* There should be exactly one argument following the options */ |
| |
| if (optind >= argc) |
| { |
| fprintf(stderr, "ERROR: Missing <board-name>:<config-name>\n"); |
| show_usage(argv[0], EXIT_FAILURE); |
| } |
| |
| /* The required option should be the board directory name and the |
| * configuration directory name separated by ':', '/' or '\'. Any are |
| * acceptable in this context. Or using the custom board relative or |
| * absolute path directly here. |
| */ |
| |
| g_boarddir = argv[optind]; |
| optind++; |
| |
| if (!verify_optiondir(g_boarddir)) |
| { |
| ptr = strchr(g_boarddir, ':'); |
| if (ptr == NULL) |
| { |
| ptr = strchr(g_boarddir, '/'); |
| if (!ptr) |
| { |
| ptr = strchr(g_boarddir, '\\'); |
| } |
| } |
| |
| if (ptr == NULL) |
| { |
| fprintf(stderr, "ERROR: Invalid <board-name>:<config-name>\n"); |
| show_usage(argv[0], EXIT_FAILURE); |
| } |
| |
| *ptr++ = '\0'; |
| g_configdir = ptr; |
| } |
| else |
| { |
| /* custom board case with relative or absolute path */ |
| |
| g_configpath = strdup(g_boarddir); |
| } |
| |
| /* The left arguments will pass to make */ |
| |
| g_makeargv = &argv[optind]; |
| } |
| |
| static int run_make(const char *arg) |
| { |
| char **argv; |
| |
| snprintf(g_buffer, BUFFER_SIZE, "make %s", arg); |
| |
| for (argv = g_makeargv; *argv; argv++) |
| { |
| strncat(g_buffer, " ", BUFFER_SIZE - 1); |
| strncat(g_buffer, *argv, BUFFER_SIZE - 1); |
| } |
| |
| return system(g_buffer); |
| } |
| |
| static bool filecmp(const char *f1, const char *f2) |
| { |
| FILE *stream1; |
| FILE *stream2; |
| char ch1; |
| char ch2; |
| |
| stream1 = fopen(f1, "r"); |
| stream2 = fopen(f2, "r"); |
| |
| if (stream1 == NULL || stream2 == NULL) |
| { |
| return false; |
| } |
| |
| do |
| { |
| ch1 = fgetc(stream1); |
| ch2 = fgetc(stream2); |
| |
| if (ch1 != ch2) |
| { |
| return false; |
| } |
| } |
| while (ch1 != EOF && ch2 != EOF); |
| |
| fclose(stream1); |
| fclose(stream2); |
| |
| return true; |
| } |
| |
| static bool check_directory(const char *directory) |
| { |
| struct stat buf; |
| |
| if (stat(directory, &buf) < 0) |
| { |
| debug("stat of %s failed: %s\n", directory, strerror(errno)); |
| return false; |
| } |
| |
| if (!S_ISDIR(buf.st_mode)) |
| { |
| debug("%s exists but is not a directory\n", directory); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void verify_directory(const char *directory) |
| { |
| struct stat buf; |
| |
| if (stat(directory, &buf) < 0) |
| { |
| fprintf(stderr, "ERROR: stat of %s failed: %s\n", |
| directory, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (!S_ISDIR(buf.st_mode)) |
| { |
| fprintf(stderr, "ERROR: %s exists but is not a directory\n", |
| directory); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static bool verify_optiondir(const char *directory) |
| { |
| struct stat buf; |
| |
| if (stat(directory, &buf) < 0) |
| { |
| /* It may be okay if the directory does not exist */ |
| |
| /* It may be okay if the file does not exist */ |
| |
| int errcode = errno; |
| if (errcode == ENOENT) |
| { |
| debug("verify_optiondir: stat of %s failed: %s\n", |
| directory, strerror(errno)); |
| return false; |
| } |
| else |
| { |
| fprintf(stderr, "ERROR: stat of %s failed: %s\n", |
| directory, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| if (!S_ISDIR(buf.st_mode)) |
| { |
| fprintf(stderr, "ERROR: %s exists but is not a directory\n", |
| directory); |
| exit(EXIT_FAILURE); |
| } |
| |
| return true; |
| } |
| |
| static bool verify_file(const char *path) |
| { |
| struct stat buf; |
| |
| if (stat(path, &buf) < 0) |
| { |
| /* It may be okay if the file does not exist */ |
| |
| int errcode = errno; |
| if (errcode == ENOENT) |
| { |
| debug("verify_file: stat of %s failed: %s\n", |
| path, strerror(errno)); |
| return false; |
| } |
| else |
| { |
| fprintf(stderr, "ERROR: stat of %s failed: %s\n", |
| path, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| if (!S_ISREG(buf.st_mode)) |
| { |
| fprintf(stderr, "ERROR: %s exists but is not a regular file\n", path); |
| exit(EXIT_FAILURE); |
| } |
| |
| return true; |
| } |
| |
| static void find_topdir(void) |
| { |
| char *currdir; |
| |
| /* Get and verify the top-level NuttX directory */ |
| |
| /* First get the current directory. We expect this to be either |
| * the nuttx root directory or the tools subdirectory. |
| */ |
| |
| if (getcwd(g_buffer, BUFFER_SIZE) == NULL) |
| { |
| fprintf(stderr, "ERROR: getcwd failed: %s\n", strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* Assume that we are in the tools sub-directory and the directory above |
| * is the nuttx root directory. |
| */ |
| |
| currdir = strdup(g_buffer); |
| g_topdir = strdup(dirname(g_buffer)); |
| |
| debug("get_topdir: Checking parent directory: %s\n", g_topdir); |
| verify_directory(g_topdir); |
| |
| /* Check if the current directory is the nuttx root directory. |
| * If so, then the tools directory should be a sub-directory. |
| */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%ctools", currdir, g_delim); |
| debug("get_topdir: Checking topdir/tools=%s\n", g_buffer); |
| if (check_directory(g_buffer)) |
| { |
| /* There is a tools sub-directory under the current directory. |
| * We must have already been in the nuttx root directory. We |
| * will find out for sure in later tests. |
| */ |
| |
| free(g_topdir); |
| g_topdir = currdir; |
| } |
| else |
| { |
| /* Yes, we are probably in the tools/ sub-directory */ |
| |
| free(currdir); |
| if (chdir(g_topdir) < 0) |
| { |
| fprintf(stderr, "ERROR: Failed to ch to %s\n", g_topdir); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| |
| static void config_search(const char *boarddir, |
| config_callback callback, void *data) |
| { |
| DIR *dir; |
| struct dirent *dp; |
| struct stat buf; |
| char *parent; |
| char *child; |
| |
| /* Skip over any leading '/' or '\\'. This happens on the first second |
| * call because the starting boarddir is "" |
| */ |
| |
| if (boarddir[0] == g_delim) |
| { |
| boarddir++; |
| } |
| |
| /* Get the full directory path and open it */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c%s", g_configtop, g_delim, boarddir); |
| dir = opendir(g_buffer); |
| if (!dir) |
| { |
| fprintf(stderr, "ERROR: Could not open %s: %s\n", |
| g_buffer, strerror(errno)); |
| return; |
| } |
| |
| /* Make a copy of the path to the directory */ |
| |
| parent = strdup(g_buffer); |
| |
| /* Visit each entry in the directory */ |
| |
| while ((dp = readdir(dir)) != NULL) |
| { |
| /* Ignore directory entries that start with '.' */ |
| |
| if (dp->d_name[0] == '.') |
| { |
| continue; |
| } |
| |
| /* Get a properly terminated copy of d_name (if d_name is long it may |
| * not include a NUL terminator. |
| */ |
| |
| child = strndup(dp->d_name, NAME_MAX); |
| |
| /* Get the full path to d_name and stat the file/directory */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c%s", parent, g_delim, child); |
| if (stat(g_buffer, &buf) < 0) |
| { |
| fprintf(stderr, "ERROR: stat of %s failed: %s\n", |
| g_buffer, strerror(errno)); |
| free(child); |
| continue; |
| } |
| |
| /* If it is a directory, then recurse */ |
| |
| if (S_ISDIR(buf.st_mode)) |
| { |
| char *tmppath; |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c%s", |
| boarddir, g_delim, child); |
| tmppath = strdup(g_buffer); |
| config_search(tmppath, callback, data); |
| free(tmppath); |
| } |
| |
| /* If it is a regular file named 'defconfig' then we have found a |
| * configuration directory. We could terminate the search in this case |
| * because we do not expect sub-directories within configuration |
| * directories. |
| */ |
| |
| else if (S_ISREG(buf.st_mode) && strcmp("defconfig", child) == 0) |
| { |
| char *archname; |
| char *chipname; |
| char *boardname; |
| char *configname; |
| char *delim; |
| |
| /* Get the board directory near the beginning of the 'boarddir': |
| * <archdir>/<chipdir>/<boarddir>/configs/<configdir> |
| */ |
| |
| /* Make a modifiable copy */ |
| |
| strncpy(g_buffer, boarddir, BUFFER_SIZE - 1); |
| |
| /* Save the <archdir> */ |
| |
| archname = g_buffer; |
| |
| delim = strchr(g_buffer, g_delim); |
| if (delim == NULL) |
| { |
| debug("ERROR: delimiter not found in path: %s\n", boarddir); |
| } |
| else |
| { |
| /* Save the <chipdir> */ |
| |
| *delim = '\0'; |
| chipname = delim + 1; |
| |
| delim = strchr(chipname, g_delim); |
| if (delim == NULL) |
| { |
| debug("ERROR: delimiter not found in path: %s\n", |
| chipname); |
| } |
| else |
| { |
| /* Save the <boardir> */ |
| |
| *delim = '\0'; |
| boardname = delim + 1; |
| |
| delim = strchr(boardname, g_delim); |
| if (delim == NULL) |
| { |
| debug("ERROR: delimiter not found in path: %s\n", |
| boardname); |
| } |
| else |
| { |
| /* Save the <configdir> */ |
| |
| *delim = '\0'; |
| delim = strrchr(delim + 1, g_delim); |
| if (delim == NULL) |
| { |
| debug("ERROR: directory not found in path: %s\n", |
| boardname); |
| } |
| else |
| { |
| configname = delim + 1; |
| callback(boarddir, archname, chipname, |
| boardname, configname, data); |
| } |
| } |
| } |
| } |
| } |
| |
| free(child); |
| } |
| |
| free(parent); |
| closedir(dir); |
| } |
| |
| static void archname_callback(const char *boarddir, const char *archname, |
| const char *chipname, const char *boardname, |
| const char *configname, void *data) |
| { |
| if (strcmp(g_boarddir, boardname) == 0 && |
| strcmp(g_configdir, configname) == 0) |
| { |
| g_archdir = strdup(archname); |
| g_chipdir = strdup(chipname); |
| } |
| } |
| |
| static void find_archname(void) |
| { |
| config_search("", archname_callback, NULL); |
| if (g_archdir == NULL || g_chipdir == NULL) |
| { |
| g_archdir = "unknown"; |
| g_chipdir = "unknown"; |
| } |
| } |
| |
| static void enumerate_callback(const char *boarddir, const char *archname, |
| const char *chipname, const char *boardname, |
| const char *configname, void *data) |
| { |
| fprintf(stderr, " %s:%s\n", boardname, configname); |
| } |
| |
| static void enumerate_configs(void) |
| { |
| fprintf(stderr, "Options for <board-name>:<config-name> include:\n\n"); |
| config_search("", enumerate_callback, NULL); |
| } |
| |
| static void check_configdir(void) |
| { |
| if (g_configpath && verify_optiondir(g_configpath)) |
| { |
| /* Get the path to the custom board scripts directory */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c..%c..%cscripts", |
| g_configpath, g_delim, g_delim, g_delim); |
| if (verify_optiondir(g_buffer)) |
| { |
| g_scriptspath = strdup(g_buffer); |
| } |
| } |
| else |
| { |
| /* Get the path to the top level configuration directory: boards/ */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cboards", g_topdir, g_delim); |
| debug("check_configdir: Checking configtop=%s\n", g_buffer); |
| |
| verify_directory(g_buffer); |
| g_configtop = strdup(g_buffer); |
| |
| /* Get and verify the path to the selected configuration: |
| * boards/<archdir>/<chipdir>/<boarddir>/configs/<configdir> |
| */ |
| |
| find_archname(); |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cboards%c%s%c%s%c%s%cconfigs%c%s", |
| g_topdir, g_delim, g_delim, g_archdir, g_delim, g_chipdir, |
| g_delim, g_boarddir, g_delim, g_delim, g_configdir); |
| debug("check_configdir: Checking configpath=%s\n", g_buffer); |
| |
| if (!verify_optiondir(g_buffer)) |
| { |
| fprintf(stderr, "ERROR: No configuration at %s\n", g_buffer); |
| fprintf(stderr, "Run tools/configure -L" |
| " to list available configurations.\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| g_configpath = strdup(g_buffer); |
| |
| /* Get and verify the path to the scripts directory: |
| * boards/<archdir>/<chipdir>/<boarddir>/scripts |
| */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cboards%c%s%c%s%c%s%cscripts", |
| g_topdir, g_delim, g_delim, g_archdir, g_delim, |
| g_chipdir, g_delim, g_boarddir, g_delim); |
| debug("check_configdir: Checking scripts path=%s\n", g_buffer); |
| |
| g_scriptspath = NULL; |
| if (verify_optiondir(g_buffer)) |
| { |
| g_scriptspath = strdup(g_buffer); |
| } |
| } |
| } |
| |
| static void check_configured(void) |
| { |
| /* If we are already configured then there will be a .config and |
| * a Make.defs file in the top-level directory. |
| */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c.config", g_topdir, g_delim); |
| debug("check_configured: Checking %s\n", g_buffer); |
| |
| if (!verify_file(g_buffer)) |
| { |
| return; |
| } |
| |
| if (g_enforce) |
| { |
| run_make("distclean"); |
| } |
| else |
| { |
| char *defcfgpath = NULL; |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cdefconfig", |
| g_configpath, g_delim); |
| defcfgpath = strdup(g_buffer); |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cdefconfig", |
| g_topdir, g_delim); |
| |
| if (filecmp(g_buffer, defcfgpath)) |
| { |
| fprintf(stderr, "No configuration change.\n"); |
| free(defcfgpath); |
| exit(EXIT_SUCCESS); |
| } |
| else |
| { |
| free(defcfgpath); |
| if (g_distclean) |
| { |
| run_make("distclean"); |
| } |
| else |
| { |
| fprintf(stderr, "Already configured!\n"); |
| fprintf(stderr, "Please 'make distclean' and try again.\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| } |
| |
| static void read_configfile(void) |
| { |
| FILE *stream; |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cdefconfig", g_configpath, g_delim); |
| stream = fopen(g_buffer, "r"); |
| if (!stream) |
| { |
| fprintf(stderr, "ERROR: failed to open %s for reading: %s\n", |
| g_buffer, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| parse_file(stream, &g_configvars); |
| fclose(stream); |
| } |
| |
| static void read_versionfile(void) |
| { |
| FILE *stream; |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c.version", g_topdir, g_delim); |
| stream = fopen(g_buffer, "r"); |
| if (!stream) |
| { |
| /* It may not be an error if there is no .version file */ |
| |
| debug("Failed to open %s for reading: %s\n", |
| g_buffer, strerror(errno)); |
| } |
| else |
| { |
| parse_file(stream, &g_versionvars); |
| fclose(stream); |
| } |
| } |
| |
| static void get_verstring(void) |
| { |
| struct variable_s *var; |
| |
| if (g_versionvars) |
| { |
| var = find_variable("CONFIG_VERSION_STRING", g_versionvars); |
| if (var && var->val) |
| { |
| g_verstring = strdup(var->val); |
| } |
| } |
| |
| debug("get_verstring: Version string=%s\n", g_verstring); |
| } |
| |
| static bool verify_appdir(const char *appdir) |
| { |
| /* Does this directory exist? */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c%s", g_topdir, g_delim, appdir); |
| debug("verify_appdir: Checking apppath=%s\n", g_buffer); |
| if (verify_optiondir(g_buffer)) |
| { |
| /* Yes.. Use this application directory path */ |
| |
| g_appdir = strdup(appdir); |
| g_apppath = strdup(g_buffer); |
| return true; |
| } |
| |
| debug("verify_appdir: apppath=%s does not exist\n", g_buffer); |
| return false; |
| } |
| |
| static void check_appdir(void) |
| { |
| char tmp[16]; |
| |
| /* Get and verify the full path to the application directory */ |
| |
| /* Was the appdir provided on the command line? */ |
| |
| debug("check_appdir: Command line appdir=%s\n", |
| g_appdir ? g_appdir : "<null>"); |
| |
| if (!g_appdir) |
| { |
| /* If no application directory was provided on the command line and we |
| * are switching between a windows native host and some other host then |
| * ignore any path to the apps/ directory in the defconfig file. It |
| * will most certainly not be in a usable form. |
| */ |
| |
| if (g_winnative == g_oldnative) |
| { |
| /* No, was the path provided in the configuration? */ |
| |
| struct variable_s *var = |
| find_variable("CONFIG_APPS_DIR", g_configvars); |
| |
| if (var != NULL) |
| { |
| debug("check_appdir: Config file appdir=%s\n", |
| var->val ? var->val : "<null>"); |
| |
| /* Yes.. does this directory exist? */ |
| |
| if (var->val && verify_appdir(var->val)) |
| { |
| /* We are using the CONFIG_APPS_DIR setting already in the |
| * defconfig file. |
| */ |
| |
| g_needapppath = false; |
| return; |
| } |
| } |
| } |
| |
| /* Now try some canned locations */ |
| |
| /* Try ../apps-xx.yy where xx.yy is the version string */ |
| |
| snprintf(tmp, 16, "..%capps-%s", g_delim, g_verstring); |
| debug("check_appdir: Try appdir=%s\n", tmp); |
| if (verify_appdir(tmp)) |
| { |
| return; |
| } |
| |
| /* Try ../apps with no version */ |
| |
| snprintf(tmp, 16, "..%capps", g_delim); |
| debug("check_appdir: Try appdir=%s\n", tmp); |
| if (verify_appdir(tmp)) |
| { |
| return; |
| } |
| |
| /* Try ../apps-xx.yy where xx.yy are the NuttX version number */ |
| |
| fprintf(stderr, "ERROR: Could not find the path to the appdir\n"); |
| exit(EXIT_FAILURE); |
| } |
| else if (!verify_appdir(g_appdir)) |
| { |
| fprintf(stderr, "ERROR: Command line path to appdir does not exist\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void check_configuration(void) |
| { |
| struct variable_s *var; |
| |
| /* Check if this is a Windows native configuration */ |
| |
| var = find_variable("CONFIG_WINDOWS_NATIVE", g_configvars); |
| if (var && var->val && strcmp("y", var->val) == 0) |
| { |
| debug("check_configuration: Windows native configuration\n"); |
| g_oldnative = true; |
| } |
| |
| /* If we are going to some host other than windows native or to a windows |
| * native host, then don't ignore what is in the defconfig file. |
| */ |
| |
| if (g_host == HOST_NOCHANGE) |
| { |
| /* Use whatever we found in the configuration file */ |
| |
| g_winnative = g_oldnative; |
| } |
| else if (g_host == HOST_WINDOWS && g_windows == WINDOWS_NATIVE) |
| { |
| /* The new configuration is windows native */ |
| |
| g_winnative = true; |
| } |
| |
| /* All configurations must provide a defconfig and Make.defs file */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cdefconfig", g_configpath, g_delim); |
| debug("check_configuration: Checking %s\n", g_buffer); |
| if (!verify_file(g_buffer)) |
| { |
| fprintf(stderr, "ERROR: No configuration in %s\n", g_configpath); |
| fprintf(stderr, " No defconfig file found.\n"); |
| fprintf(stderr, "Run tools/configure -L" |
| " to list available configurations.\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| g_srcdefconfig = strdup(g_buffer); |
| |
| /* Try the Make.defs file */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cMake.defs", g_configpath, g_delim); |
| debug("check_configuration: Checking %s\n", g_buffer); |
| if (!verify_file(g_buffer)) |
| { |
| /* An alternative location is the scripts/ directory */ |
| |
| if (g_scriptspath != NULL) |
| { |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cMake.defs", |
| g_scriptspath, g_delim); |
| debug("check_configuration: Checking %s\n", g_buffer); |
| if (!verify_file(g_buffer)) |
| { |
| /* Let’s check if there is a script in the common directory */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, |
| "%s%c..%c..%c..%ccommon%cscripts%cMake.defs", |
| g_configpath, g_delim, g_delim, g_delim, g_delim, |
| g_delim, g_delim); |
| if (!verify_file(g_buffer)) |
| { |
| fprintf(stderr, "ERROR: No Make.defs file found\n"); |
| fprintf(stderr, "Run tools/configure -L" |
| " to list available configurations.\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| else |
| { |
| fprintf(stderr, "ERROR: No Make.defs file in %s\n", g_configpath); |
| fprintf(stderr, "Run tools/configure -L" |
| " to list available configurations.\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| g_srcmakedefs = strdup(g_buffer); |
| } |
| |
| static void copy_file(const char *srcpath, |
| const char *destpath, mode_t mode) |
| { |
| int nbytesread; |
| int nbyteswritten; |
| int rdfd; |
| int wrfd; |
| |
| /* Open the source file for reading */ |
| |
| rdfd = open(srcpath, O_RDONLY); |
| if (rdfd < 0) |
| { |
| fprintf(stderr, "ERROR: Failed to open %s for reading: %s\n", |
| srcpath, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* Now open the destination for writing */ |
| |
| wrfd = open(destpath, O_WRONLY | O_CREAT | O_TRUNC, mode); |
| if (wrfd < 0) |
| { |
| fprintf(stderr, "ERROR: Failed to open %s for writing: %s\n", |
| destpath, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* Now copy the file */ |
| |
| for (; ; ) |
| { |
| do |
| { |
| nbytesread = read(rdfd, g_buffer, BUFFER_SIZE); |
| if (nbytesread == 0) |
| { |
| /* End of file */ |
| |
| close(rdfd); |
| close(wrfd); |
| return; |
| } |
| else if (nbytesread < 0) |
| { |
| /* EINTR is not an error (but will still stop the copy) */ |
| |
| fprintf(stderr, "ERROR: Read failure: %s\n", strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| while (nbytesread <= 0); |
| |
| do |
| { |
| nbyteswritten = write(wrfd, g_buffer, nbytesread); |
| if (nbyteswritten >= 0) |
| { |
| nbytesread -= nbyteswritten; |
| } |
| else |
| { |
| /* EINTR is not an error (but will still stop the copy) */ |
| |
| fprintf(stderr, "ERROR: Write failure: %s\n", strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| while (nbytesread > 0); |
| } |
| } |
| |
| static void substitute(char *str, int ch1, int ch2) |
| { |
| for (; *str; str++) |
| { |
| if (*str == ch1) |
| { |
| *str = ch2; |
| } |
| } |
| } |
| |
| static char *double_appdir_backslashes(char *old_appdir) |
| { |
| char *new_appdir = NULL; |
| char *p_old = NULL; |
| char *p_new = NULL; |
| int oldlen = 0; |
| int occurrences = 0; |
| int alloclen = 0; |
| |
| p_old = old_appdir; |
| while ((p_old = strchr(p_old, '\\')) != NULL) |
| { |
| occurrences++; |
| p_old++; |
| } |
| |
| if (occurrences != 0) |
| { |
| oldlen = strlen(old_appdir); |
| alloclen = oldlen + occurrences + sizeof((char) '\0'); |
| new_appdir = malloc(alloclen); |
| |
| if (new_appdir != NULL) |
| { |
| p_old = old_appdir; |
| p_new = new_appdir; |
| while (oldlen) |
| { |
| if (*p_old != '\\') |
| { |
| *p_new++ = *p_old; |
| } |
| else |
| { |
| *p_new++ = '\\'; |
| *p_new++ = '\\'; |
| } |
| |
| ++p_old; |
| --oldlen; |
| } |
| |
| *p_new = '\0'; |
| } |
| } |
| else |
| { |
| new_appdir = strdup(old_appdir); |
| } |
| |
| return new_appdir; |
| } |
| |
| static void copy_optional(void) |
| { |
| int i; |
| |
| for (i = 0; i < N_OPTFILES; i++) |
| { |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c%s", |
| g_configpath, g_delim, g_optfiles[i]); |
| |
| if (verify_file(g_buffer)) |
| { |
| char *optsrc = strdup(g_buffer); |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c%s", |
| g_topdir, g_delim, g_optfiles[i]); |
| |
| debug("copy_optional: Copying from %s to %s\n", optsrc, g_buffer); |
| copy_file(optsrc, g_buffer, 0644); |
| |
| free(optsrc); |
| } |
| } |
| } |
| |
| static void enable_feature(const char *destconfig, const char *varname) |
| { |
| int ret; |
| |
| snprintf(g_buffer, BUFFER_SIZE, |
| "kconfig-tweak --file %s --enable %s", |
| destconfig, varname); |
| |
| ret = system(g_buffer); |
| |
| #ifdef WEXITSTATUS |
| if (ret < 0 || WEXITSTATUS(ret) != 0) |
| #else |
| if (ret < 0) |
| #endif |
| { |
| fprintf(stderr, "ERROR: Failed to enable %s\n", varname); |
| fprintf(stderr, " command: %s\n", g_buffer); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void disable_feature(const char *destconfig, const char *varname) |
| { |
| int ret; |
| |
| snprintf(g_buffer, BUFFER_SIZE, |
| "kconfig-tweak --file %s --disable %s", |
| destconfig, varname); |
| |
| ret = system(g_buffer); |
| |
| #ifdef WEXITSTATUS |
| if (ret < 0 || WEXITSTATUS(ret) != 0) |
| #else |
| if (ret < 0) |
| #endif |
| { |
| fprintf(stderr, "ERROR: Failed to disable %s\n", varname); |
| fprintf(stderr, " command: %s\n", g_buffer); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| /* Select the host build development environment */ |
| |
| static void set_host(const char *destconfig) |
| { |
| switch (g_host) |
| { |
| case HOST_LINUX: |
| { |
| printf(" Select the Linux host\n"); |
| |
| enable_feature(destconfig, "CONFIG_HOST_LINUX"); |
| disable_feature(destconfig, "CONFIG_HOST_WINDOWS"); |
| disable_feature(destconfig, "CONFIG_HOST_MACOS"); |
| disable_feature(destconfig, "CONFIG_HOST_BSD"); |
| |
| disable_feature(destconfig, "CONFIG_WINDOWS_NATIVE"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_CYGWIN"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_MSYS"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_OTHER"); |
| |
| enable_feature(destconfig, "CONFIG_SIM_X8664_SYSTEMV"); |
| disable_feature(destconfig, "CONFIG_SIM_X8664_MICROSOFT"); |
| } |
| break; |
| |
| case HOST_MACOS: |
| { |
| printf(" Select the macOS host\n"); |
| |
| disable_feature(destconfig, "CONFIG_HOST_LINUX"); |
| disable_feature(destconfig, "CONFIG_HOST_WINDOWS"); |
| disable_feature(destconfig, "CONFIG_HOST_BSD"); |
| enable_feature(destconfig, "CONFIG_HOST_MACOS"); |
| |
| disable_feature(destconfig, "CONFIG_WINDOWS_NATIVE"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_CYGWIN"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_MSYS"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_OTHER"); |
| |
| enable_feature(destconfig, "CONFIG_SIM_X8664_SYSTEMV"); |
| disable_feature(destconfig, "CONFIG_SIM_X8664_MICROSOFT"); |
| } |
| break; |
| |
| case HOST_BSD: |
| { |
| printf(" Select the BSD host\n"); |
| |
| disable_feature(destconfig, "CONFIG_HOST_LINUX"); |
| disable_feature(destconfig, "CONFIG_HOST_WINDOWS"); |
| disable_feature(destconfig, "CONFIG_HOST_MACOS"); |
| enable_feature(destconfig, "CONFIG_HOST_BSD"); |
| |
| disable_feature(destconfig, "CONFIG_WINDOWS_NATIVE"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_CYGWIN"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_MSYS"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_OTHER"); |
| |
| enable_feature(destconfig, "CONFIG_SIM_X8664_SYSTEMV"); |
| disable_feature(destconfig, "CONFIG_SIM_X8664_MICROSOFT"); |
| } |
| break; |
| |
| case HOST_WINDOWS: |
| { |
| enable_feature(destconfig, "CONFIG_HOST_WINDOWS"); |
| disable_feature(destconfig, "CONFIG_HOST_LINUX"); |
| disable_feature(destconfig, "CONFIG_HOST_MACOS"); |
| disable_feature(destconfig, "CONFIG_HOST_BSD"); |
| |
| disable_feature(destconfig, "CONFIG_WINDOWS_OTHER"); |
| |
| enable_feature(destconfig, "CONFIG_SIM_X8664_MICROSOFT"); |
| disable_feature(destconfig, "CONFIG_SIM_X8664_SYSTEMV"); |
| |
| switch (g_windows) |
| { |
| case WINDOWS_CYGWIN: |
| printf(" Select Windows/Cygwin host\n"); |
| enable_feature(destconfig, "CONFIG_WINDOWS_CYGWIN"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_MSYS"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_NATIVE"); |
| break; |
| |
| case WINDOWS_MSYS: |
| printf(" Select Windows/MSYS host\n"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_CYGWIN"); |
| enable_feature(destconfig, "CONFIG_WINDOWS_MSYS"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_NATIVE"); |
| break; |
| |
| case WINDOWS_NATIVE: |
| printf(" Select Windows native host\n"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_CYGWIN"); |
| disable_feature(destconfig, "CONFIG_WINDOWS_MSYS"); |
| enable_feature(destconfig, "CONFIG_EXPERIMENTAL"); |
| enable_feature(destconfig, "CONFIG_WINDOWS_NATIVE"); |
| break; |
| |
| default: |
| fprintf(stderr, |
| "ERROR: Unrecognized windows configuration: %d\n", |
| g_windows); |
| exit(EXIT_FAILURE); |
| } |
| } |
| break; |
| |
| case HOST_NOCHANGE: |
| break; |
| |
| default: |
| { |
| fprintf(stderr, "ERROR: Unrecognized host configuration: %d\n", |
| g_host); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| |
| static void configure(void) |
| { |
| char *destconfig; |
| |
| /* Copy the defconfig to toplevel */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cdefconfig", g_topdir, g_delim); |
| copy_file(g_srcdefconfig, g_buffer, 0644); |
| |
| /* Copy the defconfig file as .config */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c.config", g_topdir, g_delim); |
| destconfig = strdup(g_buffer); |
| debug("configure: Copying from %s to %s\n", g_srcdefconfig, destconfig); |
| copy_file(g_srcdefconfig, destconfig, 0644); |
| |
| /* Copy the Make.defs file as Make.defs */ |
| |
| snprintf(g_buffer, BUFFER_SIZE, "%s%cMake.defs", g_topdir, g_delim); |
| debug("configure: Copying from %s to %s\n", g_srcmakedefs, g_buffer); |
| copy_file(g_srcmakedefs, g_buffer, 0644); |
| |
| /* Copy optional files */ |
| |
| copy_optional(); |
| |
| /* Select the host build development environment */ |
| |
| set_host(destconfig); |
| |
| /* If we did not use the CONFIG_APPS_DIR that was in the defconfig config |
| * file, then append the correct application information to the tail of the |
| * .config file |
| */ |
| |
| if (g_needapppath) |
| { |
| FILE *stream; |
| char *appdir = strdup(g_appdir); |
| char *boardcfg = strdup(g_boarddir); |
| |
| /* One complexity is if we are using Windows paths, but the |
| * configuration needs POSIX paths (or vice versa). |
| */ |
| |
| if (g_winpaths != g_winnative) |
| { |
| /* Not the same */ |
| |
| if (g_winpaths) |
| { |
| /* Using Windows paths, but the configuration wants POSIX |
| * paths. |
| */ |
| |
| substitute(appdir, '\\', '/'); |
| } |
| else |
| { |
| /* Using POSIX paths, but the configuration wants Windows |
| * paths. |
| */ |
| |
| substitute(appdir, '/', '\\'); |
| } |
| } |
| |
| /* Looks like prebuilt winnative kconfig-conf interprets "..\apps" as |
| * "..apps" (possibly '\a' as escape-sequence) so expand winnative path |
| * to double-backslashed variant "..\\apps". |
| */ |
| |
| if (g_winnative) |
| { |
| char *tmp_appdir = double_appdir_backslashes(appdir); |
| if (NULL == tmp_appdir) |
| { |
| fprintf(stderr, |
| "ERROR: Failed to double appdir backslashes\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| free(appdir); |
| appdir = tmp_appdir; |
| } |
| |
| /* Open the file for appending */ |
| |
| stream = fopen(destconfig, "a"); |
| if (!stream) |
| { |
| fprintf(stderr, |
| "ERROR: Failed to open %s for append mode: %s\n", |
| destconfig, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| fprintf(stream, "\n# Application configuration\n\n"); |
| fprintf(stream, "CONFIG_APPS_DIR=\"%s\"\n", appdir); |
| |
| substitute(boardcfg, '\\', '/'); |
| fprintf(stream, "CONFIG_BASE_DEFCONFIG=\"%s\"\n", boardcfg); |
| |
| fclose(stream); |
| free(appdir); |
| } |
| |
| free(destconfig); |
| } |
| |
| static void refresh(void) |
| { |
| int ret; |
| |
| printf(" Refreshing...\n"); |
| fflush(stdout); |
| |
| ret = run_make("olddefconfig"); |
| putchar('\n'); |
| |
| #ifdef WEXITSTATUS |
| if (ret < 0 || WEXITSTATUS(ret) != 0) |
| #else |
| if (ret < 0) |
| #endif |
| { |
| fprintf(stderr, "ERROR: Failed to refresh configurations\n"); |
| fprintf(stderr, " kconfig-conf --olddefconfig Kconfig\n"); |
| } |
| } |
| |
| static void save_original_config(void) |
| { |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c.config", g_topdir, g_delim); |
| char *source_config = strdup(g_buffer); |
| snprintf(g_buffer, BUFFER_SIZE, "%s%c.config.orig", g_topdir, g_delim); |
| char *dest_config = strdup(g_buffer); |
| |
| FILE *src_file = fopen(source_config, "r"); |
| FILE *dest_file = fopen(dest_config, "w"); |
| |
| if (src_file == NULL || dest_file == NULL) |
| { |
| fprintf(stderr, "ERROR: Failed to open files\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| debug("save_original_config: Copying from %s to %s\n", |
| source_config, dest_config); |
| |
| while (fgets(g_buffer, BUFFER_SIZE, src_file) != NULL) |
| { |
| if (strstr(g_buffer, "CONFIG_BASE_DEFCONFIG") == NULL) |
| { |
| fputs(g_buffer, dest_file); |
| } |
| } |
| |
| fclose(src_file); |
| fclose(dest_file); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| int main(int argc, char **argv, char **envp) |
| { |
| debug("main: Checking arguments\n"); |
| parse_args(argc, argv); |
| |
| debug("main: Checking NuttX Directories\n"); |
| find_topdir(); |
| check_configdir(); |
| check_configured(); |
| |
| debug("main: Reading the configuration/version files\n"); |
| read_configfile(); |
| read_versionfile(); |
| get_verstring(); |
| |
| debug("main: Checking Configuration Directory\n"); |
| check_configuration(); |
| |
| debug("main: Checking Application Directories\n"); |
| check_appdir(); |
| debug("main: Using apppath=%s\n", g_apppath ? g_apppath : "<null>"); |
| |
| debug("main: Configuring\n"); |
| configure(); |
| |
| debug("main: Refresh configuration\n"); |
| refresh(); |
| |
| debug("main: Save original configuration\n"); |
| save_original_config(); |
| return EXIT_SUCCESS; |
| } |