1675 lines
47 KiB
C
1675 lines
47 KiB
C
/****************************************************************************
|
||
* 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;
|
||
}
|