2017-06-02 18:17:30 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* tools/initialconfig.c
|
|
|
|
*
|
2024-09-10 14:15:58 +02:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*
|
2021-02-19 11:57:46 +01:00
|
|
|
* 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
|
2017-06-02 18:17:30 +02:00
|
|
|
*
|
2021-02-19 11:57:46 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2017-06-02 18:17:30 +02:00
|
|
|
*
|
2021-02-19 11:57:46 +01:00
|
|
|
* 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.
|
2017-06-02 18:17:30 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
2018-01-30 15:21:51 +01:00
|
|
|
#define _GNU_SOURCE 1
|
2017-06-02 18:17:30 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#define MAX_LINE 512
|
|
|
|
#define MAX_ARCHITECTURES 32
|
|
|
|
#define MAX_MCUS 64
|
|
|
|
#define MAX_BOARDS 128
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Types
|
|
|
|
****************************************************************************/
|
|
|
|
|
2019-02-27 15:41:08 +01:00
|
|
|
typedef int (*direntcb_t)(const char *dirpath, struct dirent *entry,
|
|
|
|
void *arg);
|
2017-06-02 18:17:30 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Data
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_WINDOWS_NATIVE
|
|
|
|
static char g_delim = '\\'; /* Delimiter to use when forming paths */
|
|
|
|
#else
|
|
|
|
static char g_delim = '/'; /* Delimiter to use when forming paths */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const char g_archdir[] = "arch"; /* Architecture directory */
|
2019-08-05 15:24:57 +02:00
|
|
|
static const char g_configdir[] = "boards"; /* Board configuration directory */
|
2017-06-02 18:17:30 +02:00
|
|
|
|
|
|
|
static char *g_arch[MAX_ARCHITECTURES]; /* List of architecture names */
|
|
|
|
static int g_narch; /* Number of architecture names */
|
|
|
|
static char *g_selected_arch; /* Selected architecture name */
|
|
|
|
static char *g_selected_family; /* Selected architecture family name */
|
|
|
|
|
|
|
|
static char *g_mcu[MAX_MCUS]; /* List of MCU names */
|
|
|
|
static int g_nmcu; /* Number of MCU names */
|
|
|
|
static char *g_selected_mcu; /* Selected MCU name */
|
|
|
|
|
|
|
|
static char *g_board[MAX_BOARDS]; /* List of board names */
|
|
|
|
static int g_nboard; /* Number of board names */
|
|
|
|
static char *g_selected_board; /* Selected board name */
|
|
|
|
|
|
|
|
static char g_line[MAX_LINE + 1]; /* Line read from config file */
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: skip_space
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Skip over any spaces
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static char *skip_space(char *ptr)
|
|
|
|
{
|
|
|
|
while (*ptr && isspace((int)*ptr)) ptr++;
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: find_name_end
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Find the end of a variable string
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static char *find_name_end(char *ptr)
|
|
|
|
{
|
|
|
|
while (*ptr && (isalnum((int)*ptr) || *ptr == '_')) ptr++;
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: find_value_end
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Find the end of a value string
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static char *find_value_end(char *ptr)
|
|
|
|
{
|
|
|
|
while (*ptr && !isspace((int)*ptr))
|
|
|
|
{
|
|
|
|
if (*ptr == '"')
|
|
|
|
{
|
2021-02-19 11:29:43 +01:00
|
|
|
do ptr++; while (*ptr && *ptr != '"');
|
|
|
|
if (*ptr) ptr++;
|
2017-06-02 18:17:30 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-02-19 11:29:43 +01:00
|
|
|
do ptr++; while (*ptr && !isspace((int)*ptr) && *ptr != '"');
|
2017-06-02 18:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: read_line
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Read the next line from the configuration file
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static char *read_line(FILE *stream)
|
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
|
2019-02-27 15:41:08 +01:00
|
|
|
for (; ; )
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
|
|
|
g_line[MAX_LINE] = '\0';
|
|
|
|
if (!fgets(g_line, MAX_LINE, stream))
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ptr = skip_space(g_line);
|
|
|
|
if (*ptr && *ptr != '#' && *ptr != '\n')
|
|
|
|
{
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: parse_line
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Parse the line from the configuration file into a variable name
|
|
|
|
* string and a value string.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static void parse_line(char *ptr, char **varname, char **varval)
|
|
|
|
{
|
|
|
|
/* Skip over any leading spaces */
|
|
|
|
|
|
|
|
ptr = skip_space(ptr);
|
|
|
|
|
|
|
|
/* The first no-space is the beginning of the variable name */
|
|
|
|
|
|
|
|
*varname = skip_space(ptr);
|
|
|
|
*varval = NULL;
|
|
|
|
|
|
|
|
/* Parse to the end of the variable name */
|
|
|
|
|
|
|
|
ptr = find_name_end(ptr);
|
|
|
|
|
|
|
|
/* An equal sign is expected next, perhaps after some white space */
|
|
|
|
|
|
|
|
if (*ptr && *ptr != '=')
|
|
|
|
{
|
|
|
|
/* Some else follows the variable name. Terminate the variable
|
|
|
|
* name and skip over any spaces.
|
|
|
|
*/
|
|
|
|
|
|
|
|
*ptr = '\0';
|
|
|
|
ptr = skip_space(ptr + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify that the equal sign is present */
|
|
|
|
|
|
|
|
if (*ptr == '=')
|
|
|
|
{
|
|
|
|
/* Make sure that the variable name is terminated (this was already
|
|
|
|
* done if the name was followed by white space.
|
|
|
|
*/
|
|
|
|
|
|
|
|
*ptr = '\0';
|
|
|
|
|
|
|
|
/* The variable value should follow =, perhaps separated by some
|
|
|
|
* white space.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ptr = skip_space(ptr + 1);
|
|
|
|
if (*ptr)
|
|
|
|
{
|
|
|
|
/* Yes.. a variable follows. Save the pointer to the start
|
|
|
|
* of the variable string.
|
|
|
|
*/
|
|
|
|
|
|
|
|
*varval = ptr;
|
|
|
|
|
|
|
|
/* Find the end of the variable string and make sure that it
|
|
|
|
* is terminated.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ptr = find_value_end(ptr);
|
|
|
|
*ptr = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: find_variable
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Return true if the selected variable exists. Also return the value of
|
|
|
|
* the variable.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static bool find_variable(const char *configpath, const char *varname,
|
|
|
|
char **varvalue)
|
|
|
|
{
|
|
|
|
FILE *stream;
|
|
|
|
char *tmpname;
|
|
|
|
char *tmpvalue;
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
stream = fopen(configpath, "r");
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: failed to open %s for reading: %s\n",
|
|
|
|
configpath, strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop until the entire file has been parsed. */
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Read the next line from the file */
|
|
|
|
|
|
|
|
ptr = read_line(stream);
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
/* Parse the line into a variable and a value field */
|
|
|
|
|
|
|
|
tmpname = NULL;
|
|
|
|
tmpvalue = NULL;
|
|
|
|
parse_line(ptr, &tmpname, &tmpvalue);
|
|
|
|
|
|
|
|
/* Make sure that both a variable name and value name were found. */
|
|
|
|
|
|
|
|
if (tmpname == NULL || tmpvalue == NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this the variable name and value we are looking for */
|
|
|
|
|
|
|
|
if (strcmp(varname, tmpname) == 0)
|
|
|
|
{
|
|
|
|
/* Yes.. return the value of the variable */
|
|
|
|
|
|
|
|
*varvalue = tmpvalue;
|
|
|
|
fclose(stream);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (ptr);
|
|
|
|
|
|
|
|
/* Return failure */
|
|
|
|
|
|
|
|
fclose(stream);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: check_variable
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Return true if the selected variable exists in the configuration file
|
|
|
|
* and has the specified value.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static bool check_variable(const char *configpath, const char *varname,
|
|
|
|
const char *varvalue)
|
|
|
|
{
|
|
|
|
char *tmpvalue;
|
|
|
|
|
|
|
|
if (find_variable(configpath, varname, &tmpvalue))
|
|
|
|
{
|
|
|
|
/* The variable name exists. Does it have a value? Does the value
|
|
|
|
* match varvalue?
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (tmpvalue != NULL && strcmp(varvalue, tmpvalue) == 0)
|
|
|
|
{
|
|
|
|
/* Yes.. return success */
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return failure */
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: test_filepath
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Test if a regular file exists at this path.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static bool test_filepath(const char *filepath)
|
|
|
|
{
|
|
|
|
struct stat statbuf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = stat(filepath, &statbuf);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_ISREG(statbuf.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: test_dirpath
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Test if a regular file exists at this path.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static bool test_dirpath(const char *filepath)
|
|
|
|
{
|
|
|
|
struct stat statbuf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = stat(filepath, &statbuf);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_ISDIR(statbuf.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: foreach_dirent
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Given a directory path, call the provided function for each entry in
|
|
|
|
* the directory.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int foreach_dirent(const char *dirpath, direntcb_t cb, void *arg)
|
|
|
|
{
|
|
|
|
DIR *dirp;
|
2022-06-23 18:20:55 +02:00
|
|
|
struct dirent *entry;
|
2017-06-02 18:17:30 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
dirp = opendir(dirpath);
|
|
|
|
if (dirp == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: Failed to open directory '%s': %s\n",
|
|
|
|
dirpath, strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2019-02-27 15:41:08 +01:00
|
|
|
for (; ; )
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
2022-06-23 18:20:55 +02:00
|
|
|
/* To distinguish between end of stream and error, set
|
|
|
|
* errno to 0 and verify whether its value changed if
|
|
|
|
* readdir returned NULL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
entry = readdir(dirp);
|
|
|
|
if (entry == NULL && errno != 0)
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2022-06-23 18:20:55 +02:00
|
|
|
"ERROR: Failed to read directory '%s' entry: %s\n",
|
|
|
|
dirpath, strerror(errno));
|
2017-06-02 18:17:30 +02:00
|
|
|
closedir(dirp);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
if (entry == NULL)
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip over the . and .. hard links */
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
if (strcmp(entry->d_name, ".") == 0 ||
|
|
|
|
strcmp(entry->d_name, "..") == 0)
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = cb(dirpath, entry, arg);
|
2017-06-02 18:17:30 +02:00
|
|
|
if (ret != 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dirp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: enum_architectures
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Enumerate all architecture directory names.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2019-02-27 15:41:08 +01:00
|
|
|
static int enum_architectures(const char *dirpath, struct dirent *entry,
|
|
|
|
void *arg)
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
2022-06-23 18:20:55 +02:00
|
|
|
int ret;
|
2017-06-02 18:17:30 +02:00
|
|
|
char *archpath;
|
|
|
|
char *testpath;
|
|
|
|
|
|
|
|
/* All architecture directories should contain a Kconfig file, an include/
|
|
|
|
* directory, and a src/ directory.
|
|
|
|
*/
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&archpath, "%s%c%s", dirpath, g_delim, entry->d_name);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to archpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = asprintf(&testpath, "%s%cKconfig", archpath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to testpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_filepath(testpath))
|
|
|
|
{
|
|
|
|
free(testpath);
|
2022-06-23 18:20:55 +02:00
|
|
|
|
|
|
|
ret = asprintf(&testpath, "%s%cinclude", archpath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to testpath/include\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_dirpath(testpath))
|
|
|
|
{
|
|
|
|
free(testpath);
|
2022-06-23 18:20:55 +02:00
|
|
|
|
|
|
|
ret = asprintf(&testpath, "%s%csrc", archpath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to testpath/src\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_dirpath(testpath))
|
|
|
|
{
|
|
|
|
if (g_narch >= MAX_ARCHITECTURES)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2021-02-19 11:29:43 +01:00
|
|
|
"ERROR: Too many architecture directories found\n");
|
2017-06-02 18:17:30 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_arch[g_narch] = strdup(entry->d_name);
|
|
|
|
g_narch++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(testpath);
|
|
|
|
free(archpath);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: enum_mcus
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Enumerate all MCU directory names.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int enum_mcus(const char *dirpath, struct dirent *entry, void *arg)
|
|
|
|
{
|
2022-06-23 18:20:55 +02:00
|
|
|
int ret;
|
2017-06-02 18:17:30 +02:00
|
|
|
char *mcupath;
|
|
|
|
char *testpath;
|
|
|
|
|
|
|
|
/* All MCU directories should contain a Kconfig and a Make.defs file. */
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&mcupath, "%s%c%s", dirpath, g_delim, entry->d_name);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to mcupath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = asprintf(&testpath, "%s%cKconfig", mcupath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to archpath/Kconfig\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_filepath(testpath))
|
|
|
|
{
|
|
|
|
free(testpath);
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&testpath, "%s%cMake.defs", mcupath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to testpath/Make.defs\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_filepath(testpath))
|
|
|
|
{
|
|
|
|
if (g_nmcu >= MAX_MCUS)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Too many MCU directories found\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_mcu[g_nmcu] = strdup(entry->d_name);
|
|
|
|
g_nmcu++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(testpath);
|
|
|
|
free(mcupath);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: enum_board_configurations
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Enumerate all configurations for boards find the configuration
|
|
|
|
* directory for the selected MCU.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int enum_board_configurations(const char *dirpath,
|
|
|
|
struct dirent *entry, void *arg)
|
|
|
|
{
|
|
|
|
char *configpath;
|
|
|
|
char *varvalue;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* All board directories should contain a defconfig file. */
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&configpath, "%s%c%s%cdefconfig",
|
|
|
|
dirpath, g_delim, entry->d_name, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to configpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_filepath(configpath))
|
|
|
|
{
|
|
|
|
/* We don't want all board configurations, we only want the name of
|
|
|
|
* the board that includes a configuration with:
|
|
|
|
*
|
|
|
|
* CONFIG_ARCH_CHIP="xxxx"
|
|
|
|
*
|
|
|
|
* Where xxxx is the selected MCU name.
|
|
|
|
*/
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&varvalue, "\"%s\"", g_selected_mcu);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to varvalue\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (check_variable(configpath, "CONFIG_ARCH_CHIP", varvalue))
|
|
|
|
{
|
|
|
|
/* Found it... add the board name to the list of boards for the
|
|
|
|
* selected MCU.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (g_nboard >= MAX_BOARDS)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Too many board configurations found\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_board[g_nboard] = strdup(arg);
|
|
|
|
g_nboard++;
|
|
|
|
|
|
|
|
/* If we have not yet extracted the architecture family, then do
|
|
|
|
* that here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (g_selected_family == NULL)
|
|
|
|
{
|
|
|
|
char *family;
|
|
|
|
|
|
|
|
if (find_variable(configpath, "CONFIG_ARCH_FAMILY", &family))
|
|
|
|
{
|
|
|
|
g_selected_family = strdup(family);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop the enumeration if we find a match. Continue if not...
|
|
|
|
* that is because one board might possible support multiple
|
|
|
|
* architectures.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(varvalue);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(configpath);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: enum_boards
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Enumerate all boards find the configuration directory for the selected
|
|
|
|
* MCU.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int enum_boards(const char *dirpath, struct dirent *entry, void *arg)
|
|
|
|
{
|
2022-06-23 18:20:55 +02:00
|
|
|
int ret = 0;
|
2017-06-02 18:17:30 +02:00
|
|
|
char *boardpath;
|
|
|
|
char *testpath;
|
|
|
|
|
|
|
|
/* All board directories should contain a Kconfig file, an include/
|
|
|
|
* directory, and a src/ directory.
|
|
|
|
*/
|
|
|
|
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&boardpath, "%s%c%s", dirpath, g_delim, entry->d_name);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to boardpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = asprintf(&testpath, "%s%cKconfig", boardpath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to testpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_filepath(testpath))
|
|
|
|
{
|
|
|
|
free(testpath);
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&testpath, "%s%cinclude", boardpath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to testpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_dirpath(testpath))
|
|
|
|
{
|
|
|
|
free(testpath);
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&testpath, "%s%csrc", boardpath, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to archpath\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
if (test_dirpath(testpath))
|
|
|
|
{
|
|
|
|
/* Enumerate the board configurations */
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
foreach_dirent(boardpath, enum_board_configurations,
|
|
|
|
entry->d_name);
|
2017-06-02 18:17:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(testpath);
|
|
|
|
free(boardpath);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: list_select
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Select one value from a list.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
char *list_select(char **list, unsigned nitems)
|
|
|
|
{
|
|
|
|
char ch;
|
|
|
|
int ndx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Show the list */
|
|
|
|
|
|
|
|
for (i = 0, ch = '1'; i < nitems; i++)
|
|
|
|
{
|
|
|
|
printf(" %c. %s\n", ch, list[i]);
|
|
|
|
if (ch == '9')
|
|
|
|
{
|
|
|
|
ch = 'a';
|
|
|
|
}
|
|
|
|
else if (ch == 'z')
|
|
|
|
{
|
|
|
|
ch = 'A';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ch++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-27 15:41:08 +01:00
|
|
|
for (; ; )
|
2017-06-02 18:17:30 +02:00
|
|
|
{
|
|
|
|
bool input = false;
|
|
|
|
|
|
|
|
printf("Enter [1");
|
|
|
|
if (nitems > 1)
|
2021-02-19 11:29:43 +01:00
|
|
|
{
|
|
|
|
printf("-%c", nitems >= 9 ? '9' : '0' + nitems);
|
|
|
|
if (nitems > 9)
|
|
|
|
{
|
|
|
|
printf(",a");
|
|
|
|
if (nitems > 10)
|
|
|
|
{
|
|
|
|
printf("-%c", 'a' + nitems - 10);
|
|
|
|
if (nitems > 35)
|
|
|
|
{
|
|
|
|
printf(",A");
|
|
|
|
if (nitems > 36)
|
|
|
|
{
|
|
|
|
printf("-%c", 'A' + nitems - 36);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-02 18:17:30 +02:00
|
|
|
|
|
|
|
printf("]: ");
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ch = getchar();
|
|
|
|
if (ch >= '1' && ch <= '9')
|
|
|
|
{
|
|
|
|
ndx = ch - '1';
|
|
|
|
}
|
|
|
|
else if (ch >= 'a' && ch <= 'z')
|
|
|
|
{
|
|
|
|
ndx = ch - 'a' + 9;
|
|
|
|
}
|
|
|
|
else if (ch >= 'A' && ch <= 'Z')
|
|
|
|
{
|
|
|
|
ndx = ch - 'A' + 35;
|
|
|
|
}
|
|
|
|
else if (ch == '\n')
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("Invalid selection: %c -- Try again\n", ch);
|
|
|
|
input = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ndx < nitems)
|
|
|
|
{
|
|
|
|
return list[ndx];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("Invalid selection: %c -- Try again\n", ch);
|
|
|
|
input = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: create_config
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Generate a bogus .config file. There is only sufficient information
|
|
|
|
* in this bogus .config to estable the correct symbolic links.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static void create_config(void)
|
|
|
|
{
|
|
|
|
FILE *stream;
|
|
|
|
|
|
|
|
stream = fopen(".config", "w");
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: failed to open .config for writing: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stream, "CONFIG_ARCH=\"%s\"\n", g_selected_arch);
|
|
|
|
if (g_selected_family != NULL)
|
|
|
|
{
|
|
|
|
fprintf(stream, "CONFIG_ARCH_FAMILY=%s\n", g_selected_family);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stream, "CONFIG_ARCH_CHIP=\"%s\"\n", g_selected_mcu);
|
|
|
|
fprintf(stream, "CONFIG_ARCH_BOARD=\"%s\"\n", g_selected_board);
|
|
|
|
|
|
|
|
fclose(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: main
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Program entry point.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2022-06-23 18:20:55 +02:00
|
|
|
int ret;
|
2017-06-02 18:17:30 +02:00
|
|
|
char *archpath;
|
|
|
|
|
2017-06-02 19:24:19 +02:00
|
|
|
/* Enumerate all of the architectures */
|
2017-06-02 18:17:30 +02:00
|
|
|
|
|
|
|
g_narch = 0;
|
|
|
|
foreach_dirent(g_archdir, enum_architectures, NULL);
|
|
|
|
|
|
|
|
/* Select an architecture */
|
|
|
|
|
|
|
|
printf("Select an architecture:\n");
|
|
|
|
g_selected_arch = list_select(g_arch, g_narch);
|
|
|
|
|
2017-06-02 19:24:19 +02:00
|
|
|
/* Enumerate the MCUs for the selected architecture */
|
2017-06-02 18:17:30 +02:00
|
|
|
|
|
|
|
g_nmcu = 0;
|
2022-06-23 18:20:55 +02:00
|
|
|
ret = asprintf(&archpath, "%s%c%s%csrc",
|
|
|
|
g_archdir, g_delim, g_selected_arch, g_delim);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: asprintf() failed to archpath/src\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-02 18:17:30 +02:00
|
|
|
foreach_dirent(archpath, enum_mcus, NULL);
|
|
|
|
|
|
|
|
/* Select an MCU */
|
|
|
|
|
2017-06-03 00:12:21 +02:00
|
|
|
printf("Select an MCU for architecture=%s:\n", g_selected_arch);
|
2017-06-02 18:17:30 +02:00
|
|
|
g_selected_mcu = list_select(g_mcu, g_nmcu);
|
|
|
|
|
2017-06-02 19:24:19 +02:00
|
|
|
/* Enumerate the boards for the selected MCU */
|
2017-06-02 18:17:30 +02:00
|
|
|
|
|
|
|
g_nboard = 0;
|
|
|
|
foreach_dirent(g_configdir, enum_boards, NULL);
|
|
|
|
|
|
|
|
/* Select an board */
|
|
|
|
|
|
|
|
printf("Select a board for MCU=%s:\n", g_selected_mcu);
|
|
|
|
g_selected_board = list_select(g_board, g_nboard);
|
|
|
|
|
|
|
|
/* Then output a bogus .config file with enough information to establish
|
|
|
|
* the correct symbolic links
|
|
|
|
*/
|
|
|
|
|
|
|
|
create_config();
|
|
|
|
return 0;
|
|
|
|
}
|