1
0
Fork 0
forked from nuttx/nuttx-update

Add suppport for choice prompts and help text to the Kconfig documentation tool

This commit is contained in:
Gregory Nutt 2013-04-20 14:18:08 -06:00
parent 637ac296f8
commit 27c28f542e
3 changed files with 479 additions and 49 deletions

View file

@ -96,7 +96,8 @@ config APPS_DIR
the NuttX top build direcory. If you had an application the NuttX top build direcory. If you had an application
directory and the NuttX directory each in separate directory directory and the NuttX directory each in separate directory
trees like this: trees like this:
<pre>
build build
|-nuttx |-nuttx
| | | |
@ -104,6 +105,7 @@ config APPS_DIR
`-application `-application
| |
`- Makefile `- Makefile
</pre>
Then you would set CONFIG_APPS_DIR=../application. Then you would set CONFIG_APPS_DIR=../application.
@ -238,11 +240,13 @@ config ARCH_STDBOOL_H
However, that header includes logic to redirect the inclusion of an However, that header includes logic to redirect the inclusion of an
architecture specific header file like: architecture specific header file like:
<pre>
#ifdef CONFIG_ARCH_STDBOOL_H #ifdef CONFIG_ARCH_STDBOOL_H
# include <arch/stdbool.h> # include <arch/stdbool.h>
#else #else
... ...
#endif #endif
</pre>
Recall that that include path, include/arch, is a symbolic link and Recall that that include path, include/arch, is a symbolic link and
will refer to a version of stdbool.h at nuttx/arch/<architecture>/include/stdbool.h. will refer to a version of stdbool.h at nuttx/arch/<architecture>/include/stdbool.h.
@ -259,9 +263,11 @@ config ARCH_MATH_H
will then include the architecture-specific version of math.h that you will then include the architecture-specific version of math.h that you
must provide at nuttx/arch/>architecture</include/math.h. must provide at nuttx/arch/>architecture</include/math.h.
<pre>
#ifdef CONFIG_ARCH_MATH_H #ifdef CONFIG_ARCH_MATH_H
# include <arch/math.h> # include <arch/math.h>
#endif #endif
</pre>
So for the architectures that define ARCH_MATH_H=y, include/math.h So for the architectures that define ARCH_MATH_H=y, include/math.h
will be the redirecting math.h header file; for the architectures will be the redirecting math.h header file; for the architectures

View file

@ -82,6 +82,14 @@ cmdconfig.c
This C file can be used to build a utility for comparing two NuttX This C file can be used to build a utility for comparing two NuttX
configuration files. configuration files.
kconfig2html.c
--------------
This is a C file that can be used build a utility for converting the
NuttX configuration in the Kconfig files to an HTML document. This
auto-generated documentation will, eventually, replace the manually
updated configuratin documentation that is fallling woefully behind.
mkexport.sh and Makefile.export mkexport.sh and Makefile.export
------------------------------- -------------------------------

View file

@ -56,6 +56,7 @@
#define SCRATCH_SIZE 1024 #define SCRATCH_SIZE 1024
#define MAX_DEPENDENCIES 100 #define MAX_DEPENDENCIES 100
#define MAX_LEVELS 100 #define MAX_LEVELS 100
#define TAB_SIZE 4
#define TMPFILE_NAME "kconfig2html-tmp.dat" #define TMPFILE_NAME "kconfig2html-tmp.dat"
@ -78,6 +79,7 @@ enum token_type_e
TOKEN_ENDMENU, TOKEN_ENDMENU,
TOKEN_CHOICE, TOKEN_CHOICE,
TOKEN_ENDCHOICE, TOKEN_ENDCHOICE,
TOKEN_PROMPT,
TOKEN_IF, TOKEN_IF,
TOKEN_ENDIF, TOKEN_ENDIF,
TOKEN_SOURCE TOKEN_SOURCE
@ -128,6 +130,12 @@ struct config_s
char *cdefault; char *cdefault;
}; };
struct choice_s
{
char *cprompt;
char *cdefault;
};
/**************************************************************************** /****************************************************************************
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
@ -139,6 +147,7 @@ static FILE *g_tmpfile;
static char *g_lasts; static char *g_lasts;
static bool g_debug; static bool g_debug;
static bool g_internal; static bool g_internal;
static bool g_preread;
static const char *g_kconfigroot; static const char *g_kconfigroot;
static const char *g_appsdir; static const char *g_appsdir;
static int g_paranum[MAX_LEVELS]; static int g_paranum[MAX_LEVELS];
@ -163,6 +172,7 @@ static struct reserved_s g_reserved[] =
{TOKEN_ENDMENU, "endmenu"}, {TOKEN_ENDMENU, "endmenu"},
{TOKEN_CHOICE, "choice"}, {TOKEN_CHOICE, "choice"},
{TOKEN_ENDCHOICE, "endchoice"}, {TOKEN_ENDCHOICE, "endchoice"},
{TOKEN_PROMPT, "prompt"},
{TOKEN_SOURCE, "source"}, {TOKEN_SOURCE, "source"},
{TOKEN_IF, "if"}, {TOKEN_IF, "if"},
{TOKEN_ENDIF, "endif"}, {TOKEN_ENDIF, "endif"},
@ -177,6 +187,14 @@ static struct reserved_s g_reserved[] =
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: show_usage
*
* Description:
* Show usage of this program and exit with the specified error code
*
****************************************************************************/
static void show_usage(const char *progname, int exitcode) static void show_usage(const char *progname, int exitcode)
{ {
fprintf(stderr, "USAGE: %s [-d] [-i] [-a <apps directory>] {-o <out file>] [<Kconfig root>]\n", progname); fprintf(stderr, "USAGE: %s [-d] [-i] [-a <apps directory>] {-o <out file>] [<Kconfig root>]\n", progname);
@ -193,7 +211,13 @@ static void show_usage(const char *progname, int exitcode)
exit(exitcode); exit(exitcode);
} }
/* Skip over any spaces */ /****************************************************************************
* Name: skip_space
*
* Description:
* Skip over any spaces
*
****************************************************************************/
static char *skip_space(char *ptr) static char *skip_space(char *ptr)
{ {
@ -201,7 +225,13 @@ static char *skip_space(char *ptr)
return ptr; return ptr;
} }
/* Debug output */ /****************************************************************************
* Name: debug
*
* Description:
* Debug output (conditional)
*
****************************************************************************/
static void debug(const char *fmt, ...) static void debug(const char *fmt, ...)
{ {
@ -215,7 +245,13 @@ static void debug(const char *fmt, ...)
} }
} }
/* HTML output */ /****************************************************************************
* Name: output
*
* Description:
* Output to the final HTML file
*
****************************************************************************/
static void output(const char *fmt, ...) static void output(const char *fmt, ...)
{ {
@ -226,6 +262,14 @@ static void output(const char *fmt, ...)
va_end(ap); va_end(ap);
} }
/****************************************************************************
* Name: body
*
* Description:
* HTML body output to a temporary file.
*
****************************************************************************/
static void body(const char *fmt, ...) static void body(const char *fmt, ...)
{ {
va_list ap; va_list ap;
@ -235,6 +279,14 @@ static void body(const char *fmt, ...)
va_end(ap); va_end(ap);
} }
/****************************************************************************
* Name: dequote
*
* Description:
* Remove quotation marks from a string.
*
****************************************************************************/
static char *dequote(char *ptr) static char *dequote(char *ptr)
{ {
int len; int len;
@ -270,6 +322,15 @@ static char *dequote(char *ptr)
return ptr; return ptr;
} }
/****************************************************************************
* Name: read_line
*
* Description:
* Read a new line, skipping over leading white space and ignore lines
* that contain only comments.
*
****************************************************************************/
/* Read the next line from the Kconfig file */ /* Read the next line from the Kconfig file */
static char *read_line(FILE *stream) static char *read_line(FILE *stream)
@ -278,39 +339,52 @@ static char *read_line(FILE *stream)
for (;;) for (;;)
{ {
g_line[LINE_SIZE] = '\0'; /* Is there already valid data in the line buffer? This can happen while parsing
if (!fgets(g_line, LINE_SIZE, stream)) * help text and we read one line too far.
*/
if (!g_preread)
{ {
return NULL; g_line[LINE_SIZE] = '\0';
if (!fgets(g_line, LINE_SIZE, stream))
{
return NULL;
}
} }
else
g_preread = false;
/* Replace all whitespace characters with spaces to simplify parsing */
for (ptr = g_line; *ptr; ptr++)
{ {
/* Replace all whitespace characters with spaces to simplify parsing */ if (isspace(((int)*ptr)))
for (ptr = g_line; *ptr; ptr++)
{ {
if (isspace(((int)*ptr))) *ptr = ' ';
{
*ptr = ' ';
}
} }
}
/* Skip any leading whitespace. Ignore empty lines and lines that /* Skip any leading whitespace. Ignore empty lines and lines that
* contain only comments. * contain only comments.
*/ */
ptr = skip_space(g_line); ptr = skip_space(g_line);
if (*ptr && *ptr != '#' && *ptr != '\n') if (*ptr && *ptr != '#' && *ptr != '\n')
{ {
return ptr; return ptr;
}
} }
} }
} }
/* Read the next line from the Kconfig file */ /****************************************************************************
* Name: tokenize
*
* Description:
* Check if this string corresponds to a string in the reserved word table.
*
****************************************************************************/
enum token_type_e tokenize(const char *token) static enum token_type_e tokenize(const char *token)
{ {
struct reserved_s *ptr; struct reserved_s *ptr;
@ -325,6 +399,14 @@ enum token_type_e tokenize(const char *token)
return ptr->ttype; return ptr->ttype;
} }
/****************************************************************************
* Name: MY_strtok_r
*
* Description:
* A replacement that can be used if your platform does not support strtok_r.
*
****************************************************************************/
#ifndef HAVE_STRTOK_R #ifndef HAVE_STRTOK_R
static char *MY_strtok_r(char *str, const char *delim, char **saveptr) static char *MY_strtok_r(char *str, const char *delim, char **saveptr)
{ {
@ -395,6 +477,16 @@ static char *MY_strtok_r(char *str, const char *delim, char **saveptr)
#define strtok_r MY_strtok_r #define strtok_r MY_strtok_r
#endif #endif
/****************************************************************************
* Name: findchar
*
* Description:
* Find a character in a string. This differs from strchr() because it
* skips over quoted characters. For example, if you are searching for
* '"', encountering '"' will terminate the search, but "\"" will not.
*
****************************************************************************/
static char *findchar(char *ptr, char ch) static char *findchar(char *ptr, char ch)
{ {
bool escaped = false; bool escaped = false;
@ -426,6 +518,14 @@ static char *findchar(char *ptr, char ch)
return NULL; return NULL;
} }
/****************************************************************************
* Name: getstring
*
* Description:
* Extract a quoted string
*
****************************************************************************/
static char *getstring(char *ptr) static char *getstring(char *ptr)
{ {
char *endptr; char *endptr;
@ -453,6 +553,14 @@ static char *getstring(char *ptr)
return ptr; return ptr;
} }
/****************************************************************************
* Name: push_dependency
*
* Description:
* Add the new dependency to the current list of dependencies.
*
****************************************************************************/
static void push_dependency(const char *dependency) static void push_dependency(const char *dependency)
{ {
int ndx = g_ndependencies; int ndx = g_ndependencies;
@ -467,6 +575,14 @@ static void push_dependency(const char *dependency)
g_ndependencies = ndx + 1; g_ndependencies = ndx + 1;
} }
/****************************************************************************
* Name: pop_dependency
*
* Description:
* Remove the last, pushed dependency
*
****************************************************************************/
static void pop_dependency(void) static void pop_dependency(void)
{ {
int ndx = g_ndependencies - 1; int ndx = g_ndependencies - 1;
@ -485,6 +601,14 @@ static void pop_dependency(void)
g_ndependencies = ndx; g_ndependencies = ndx;
} }
/****************************************************************************
* Name: incr_level
*
* Description:
* Increment the paragraph numbering level
*
****************************************************************************/
static void incr_level(void) static void incr_level(void)
{ {
int ndx = g_level; int ndx = g_level;
@ -499,6 +623,14 @@ static void incr_level(void)
g_level = ndx + 1; g_level = ndx + 1;
} }
/****************************************************************************
* Name: decr_level
*
* Description:
* Decrease the paragraph numbering level.
*
****************************************************************************/
static void decr_level(void) static void decr_level(void)
{ {
int ndx = g_level; int ndx = g_level;
@ -515,6 +647,14 @@ static void decr_level(void)
g_level = ndx; g_level = ndx;
} }
/****************************************************************************
* Name: incr_paranum
*
* Description:
* Increment the paragraph number at this level
*
****************************************************************************/
static void incr_paranum(void) static void incr_paranum(void)
{ {
int ndx = g_level - 1; int ndx = g_level - 1;
@ -528,6 +668,14 @@ static void incr_paranum(void)
g_paranum[ndx]++; g_paranum[ndx]++;
} }
/****************************************************************************
* Name: get_paranum
*
* Description:
* Return a string for this paragraph (uses g_scratch[]).
*
****************************************************************************/
static const char *get_paranum(void) static const char *get_paranum(void)
{ {
char buffer[16]; char buffer[16];
@ -548,6 +696,15 @@ static const char *get_paranum(void)
return g_scratch; return g_scratch;
} }
/****************************************************************************
* Name: type2str
*
* Description:
* Return a string given a member of the configuration variable type
* enumeration.
*
****************************************************************************/
static const char *type2str(enum config_type_e valtype) static const char *type2str(enum config_type_e valtype)
{ {
switch (valtype) switch (valtype)
@ -571,7 +728,140 @@ static const char *type2str(enum config_type_e valtype)
return "Unknown"; return "Unknown";
} }
static inline char *process_config(FILE *stream, const char *configname, const char *kconfigdir) /****************************************************************************
* Name: process_help
*
* Description:
* Read and generate HTML for the help text that is expected after the
* configuration configuration variable description.
*
****************************************************************************/
static inline void process_help(FILE *stream)
{
char *ptr;
int help_indent = 0;
int indent;
bool blank;
bool done;
bool newpara;
/* Read each comment line */
newpara = true;
for (;;)
{
/* Read the next line of comment text */
g_line[LINE_SIZE] = '\0';
if (!fgets(g_line, LINE_SIZE, stream))
{
break;
}
/* What is the indentation level? The first help line sets the
* indentation level. The first line encounter with lower
* indentation terminates the help.
*/
ptr = g_line;
indent = 0;
blank = false;
done = false;
while (!done)
{
int ch = (int)*ptr;
switch (ch)
{
case ' ':
indent++;
ptr++;
break;
case '\t':
indent += TAB_SIZE;
ptr++;
break;
case '#':
#if 0
blank = true;
#endif
done = true;
break;
case '\n':
case '\0':
blank = true;
done = true;
break;
default:
done = true;
break;
}
}
/* Blank lines are a special case */
if (blank)
{
/* Avoid putting an empty paragraph at the end of the help */
if (!newpara)
{
body("</p>\n");
newpara = true;
}
continue;
}
/* Check the indentation level */
if (indent == 0)
{
g_preread = true;
break;
}
else if (!help_indent)
{
help_indent = indent;
}
else if (indent < help_indent)
{
g_preread = true;
break;
}
/* Add the next line of help text */
if (newpara)
{
body("</p>\n");
newpara = false;
}
body(" %s", ptr);
}
if (!newpara)
{
body("</p>\n");
}
}
/****************************************************************************
* Name: process_config
*
* Description:
* Process one configuration variable paragraph
*
****************************************************************************/
static inline char *process_config(FILE *stream, const char *configname,
const char *kconfigdir)
{ {
enum token_type_e tokid; enum token_type_e tokid;
struct config_s config; struct config_s config;
@ -702,9 +992,9 @@ static inline char *process_config(FILE *stream, const char *configname, const c
break; break;
} }
/* Break out on the first unhandled token */ /* Break out on the help token (or the first unhandled token) */
if (token != NULL) if (help || token != NULL)
{ {
break; break;
} }
@ -793,6 +1083,8 @@ static inline char *process_config(FILE *stream, const char *configname, const c
if (help) if (help)
{ {
process_help(stream);
token = NULL;
} }
else if (!config.cdesc) else if (!config.cdesc)
{ {
@ -824,6 +1116,134 @@ static inline char *process_config(FILE *stream, const char *configname, const c
return token; return token;
} }
/****************************************************************************
* Name: process_choice
*
* Description:
* Process a choice paragraph
*
****************************************************************************/
static char *parse_kconfigfile(FILE *stream, const char *kconfigdir); /* Forward reference */
static inline char *process_choice(FILE *stream, const char *kconfigdir)
{
enum token_type_e tokid;
struct choice_s choice;
const char *paranum;
char *token;
char *ptr;
/* Get the choice information */
memset(&choice, 0, sizeof(struct choice_s));
/* Process each line in the choice */
while ((ptr = read_line(stream)) != NULL)
{
/* Process the first token on the Kconfig file line */
g_lasts = NULL;
token = strtok_r(ptr, " ", &g_lasts);
if (token != NULL)
{
tokid = tokenize(token);
switch (tokid)
{
case TOKEN_PROMPT:
{
/* Get the prompt string */
ptr = getstring(g_lasts);
if (ptr)
{
choice.cprompt = strdup(ptr);
}
/* Indicate that the line has been consumed */
token = NULL;
}
break;
case TOKEN_DEFAULT:
{
char *value = strtok_r(NULL, " ", &g_lasts);
choice.cdefault = strdup(value);
token = NULL;
}
break;
default:
{
debug("Unhandled token: %s\n", token);
}
break;
}
/* Break out on the first unhandled token */
if (token != NULL)
{
break;
}
}
}
paranum = get_paranum();
output("<li><a href=\"#choice_%d\">%s Choice", g_choice_number, paranum);
body("\n<h3><a name=\"choice_%d\">%s Choice", g_choice_number, paranum);
if (choice.cprompt)
{
output(": %s", choice.cprompt);
body(": %s", choice.cprompt);
}
output("</a></li>\n");
body("</a></h3>\n");
g_choice_number++;
/* Show the configuration file */
body("<p><i>Kconfig file</i>: <code>%s/Kconfig</code>\n</p>", kconfigdir);
body("<p><i>Options</i>:</p>", kconfigdir);
body("<ul>\n");
/* Free allocated memory */
if (choice.cprompt)
{
free(choice.cprompt);
}
if (choice.cdefault)
{
free(choice.cdefault);
}
/* Increment the nesting level */
incr_level();
debug("process_choice: Recursing for TOKEN_CHOICE\n");
debug(" kconfigdir: %s\n", kconfigdir);
debug(" level: %d\n", g_level);
/* Then recurse */
g_inchoice++;
return parse_kconfigfile(stream, kconfigdir);
}
/****************************************************************************
* Name: parse_kconfigfile
*
* Description:
* Parse a Kconfig file.
*
****************************************************************************/
static void process_kconfigfile(const char *kconfigdir); /* Forward reference */ static void process_kconfigfile(const char *kconfigdir); /* Forward reference */
static char *parse_kconfigfile(FILE *stream, const char *kconfigdir) static char *parse_kconfigfile(FILE *stream, const char *kconfigdir)
{ {
@ -938,27 +1358,7 @@ static char *parse_kconfigfile(FILE *stream, const char *kconfigdir)
case TOKEN_CHOICE: case TOKEN_CHOICE:
{ {
paranum = get_paranum(); token = process_choice(stream, kconfigdir);
body("\n<h3>%s Choice</h3>\n<ul>\n", paranum);
output("<li><a href=\"#choice_%d\">%s Choice</a></li>\n",
g_choice_number, paranum);
body("\n<h3><a name=\"choice_%d\">%s Choice</a></h3>\n",
g_choice_number, paranum);
g_choice_number++;
/* Increment the nesting level */
incr_level();
debug("parse_kconfigfile: Recursing for TOKEN_CHOICE\n");
debug(" kconfigdir: %s\n", kconfigdir);
debug(" level: %d\n", g_level);
/* Then recurse */
g_inchoice++;
token = parse_kconfigfile(stream, kconfigdir);
} }
break; break;
@ -1021,6 +1421,14 @@ static char *parse_kconfigfile(FILE *stream, const char *kconfigdir)
return token; return token;
} }
/****************************************************************************
* Name: process_kconfigfile
*
* Description:
* Open and parse a Kconfig file
*
****************************************************************************/
static void process_kconfigfile(const char *kconfigdir) static void process_kconfigfile(const char *kconfigdir)
{ {
FILE *stream; FILE *stream;
@ -1057,6 +1465,14 @@ static void process_kconfigfile(const char *kconfigdir)
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: main
*
* Description:
* Program entry point.
*
****************************************************************************/
int main(int argc, char **argv, char **envp) int main(int argc, char **argv, char **envp)
{ {
char *outfile; char *outfile;