Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/config/mkmakefile_v2.cpp
- This file was added.
/*- | |||||
* Copyright (c) 2021 Hans Petter Selasky. All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include "hp2sat.h" | |||||
#include "y.tab.h" | |||||
#include "config.h" | |||||
#include "configvers.h" | |||||
#include <sys/param.h> | |||||
#include <err.h> | |||||
/* | |||||
* # this is a comment | |||||
* include "filename" | |||||
* option standard { filename(s) } | |||||
* option [!]keyword { filename(s) } | |||||
* [ implies { implication(s) } ] | | |||||
* [ no-obj ] | |||||
* [ compile-with "compile rule" [no-implicit-rule] ] | |||||
* [ dependency "dependency-list"] [ before-depend ] | |||||
* [ clean "file-list"] [ warning "text warning" ] | |||||
* [ obj-prefix "file prefix"] | |||||
* [ nowerror ] [ local ] | |||||
*/ | |||||
struct item; | |||||
typedef STAILQ_HEAD(,item) item_head_t; | |||||
typedef STAILQ_ENTRY(item) item_entry_t; | |||||
struct item { | |||||
item_entry_t entry; | |||||
char *name; | |||||
}; | |||||
static char * | |||||
item_new(const char *name, item_head_t *phead) | |||||
{ | |||||
item *pitem; | |||||
pitem = new item; | |||||
pitem->name = ns(name); | |||||
STAILQ_INSERT_TAIL(phead, pitem, entry); | |||||
return (pitem->name); | |||||
} | |||||
static char * | |||||
item_get(item_head_t *phead) | |||||
{ | |||||
item *pitem; | |||||
char *retval; | |||||
pitem = STAILQ_FIRST(phead); | |||||
if (pitem == 0) | |||||
return (0); | |||||
STAILQ_REMOVE_HEAD(phead, entry); | |||||
retval = pitem->name; | |||||
delete pitem; | |||||
return (retval); | |||||
} | |||||
static void | |||||
errout(const char *fmt, ...) | |||||
{ | |||||
va_list ap; | |||||
va_start(ap, fmt); | |||||
vfprintf(stderr, fmt, ap); | |||||
va_end(ap); | |||||
exit(1); | |||||
} | |||||
static void | |||||
read_items(FILE *fp, item_head_t *phead, const char *fname) | |||||
{ | |||||
char *wd; | |||||
while (true) { | |||||
wd = get_word(fp, "{}"); | |||||
if (wd == 0) | |||||
continue; | |||||
else if (wd == (char *)EOF || eq(wd, "}")) | |||||
errout("%s: %s Expected opening bracket as part of item list\n", fname, item_get(phead)); | |||||
if (eq(wd, "{")) { | |||||
while (true) { | |||||
wd = get_word(fp, "{}"); | |||||
if (wd == 0) | |||||
continue; | |||||
else if (wd == (char *)EOF || eq(wd, "{")) | |||||
errout("%s: %s Expected closing bracket as part of item list\n", fname, item_get(phead)); | |||||
else if (eq(wd, "}")) | |||||
break; | |||||
else if (eq(wd, "!")) | |||||
errout("%s: %s Please don't use space after '!'\n", fname, item_get(phead)); | |||||
else | |||||
item_new(wd, phead); | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
static void | |||||
import_options(ANDMAP_HEAD_t *phead) | |||||
{ | |||||
struct device *dp; | |||||
struct opt *op; | |||||
/* set standard true */ | |||||
(new ANDMAP("!standard"))->insert_tail(phead); | |||||
STAILQ_FOREACH(dp, &dtab, d_next) | |||||
(new ANDMAP(OPTION(dp->d_name).toggleInverted()))->insert_tail(phead); | |||||
SLIST_FOREACH(op, &opt, op_next) { | |||||
if (op->op_value != 0) | |||||
continue; | |||||
(new ANDMAP(OPTION(op->op_name).toggleInverted()))->insert_tail(phead); | |||||
} | |||||
} | |||||
static void | |||||
read_file_v2_generic(char *fname, ANDMAP_HEAD_t *phead) | |||||
{ | |||||
char ifname[MAXPATHLEN]; | |||||
struct file_list *tp; | |||||
FILE *fp; | |||||
const char *objprefix; | |||||
char *wd; | |||||
char *compilewith; | |||||
char *depends; | |||||
char *clean; | |||||
char *warning; | |||||
char *option; | |||||
item_head_t files; | |||||
item_head_t implications; | |||||
int filetype; | |||||
int f_flags; | |||||
STAILQ_INIT(&files); | |||||
STAILQ_INIT(&implications); | |||||
fp = fopen(fname, "r"); | |||||
if (fp == 0) | |||||
err(1, "%s", fname); | |||||
filetype = -1; | |||||
next_word: | |||||
wd = get_word(fp, 0); | |||||
next: | |||||
if (wd == (char *)EOF) { | |||||
if (filetype != -1) | |||||
goto flush_option; | |||||
(void) fclose(fp); | |||||
return; | |||||
} else if (wd == 0) { | |||||
} else if (wd[0] == '#') { | |||||
if (filetype != -1) | |||||
goto flush_option; | |||||
while (((wd = get_word(fp, 0)) != (char *)EOF) && wd) | |||||
; | |||||
} else if (eq(wd, "include")) { | |||||
if (filetype != -1) | |||||
goto flush_option; | |||||
wd = get_quoted_word(fp); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: missing include filename.\n", fname); | |||||
(void) snprintf(ifname, sizeof(ifname), "../../%s", wd); | |||||
read_file_v2_generic(ifname, phead); | |||||
while (((wd = get_word(fp, 0)) != (char *)EOF) && wd) | |||||
; | |||||
} else if (eq(wd, "option")) { | |||||
if (filetype != -1) | |||||
goto flush_option; | |||||
wd = get_word(fp, NULL); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: No word after 'option' keyword\n", fname); | |||||
option = ns(wd); | |||||
read_items(fp, &files, fname); | |||||
objprefix = ""; | |||||
compilewith = 0; | |||||
depends = 0; | |||||
clean = 0; | |||||
warning = 0; | |||||
filetype = NORMAL; | |||||
f_flags = 0; | |||||
} else if (filetype == -1) { | |||||
errout("%s: Cannot parse '%s' when no file is specified\n", fname, wd); | |||||
} else if (eq(wd, "implies")) { | |||||
read_items(fp, &implications, fname); | |||||
} else if (eq(wd, "no-ctfconvert")) { | |||||
f_flags |= NO_CTFCONVERT; | |||||
} else if (eq(wd, "no-obj")) { | |||||
f_flags |= NO_OBJ | NO_CTFCONVERT; | |||||
} else if (eq(wd, "no-implicit-rule")) { | |||||
if (compilewith == 0) { | |||||
errout("%s: alternate rule required when " | |||||
"\"no-implicit-rule\" is specified for" | |||||
" %s.\n", | |||||
fname, item_get(&files)); | |||||
} | |||||
f_flags |= NO_IMPLCT_RULE; | |||||
} else if (eq(wd, "before-depend")) { | |||||
f_flags |= BEFORE_DEPEND; | |||||
} else if (eq(wd, "dependency")) { | |||||
if (depends != 0) | |||||
errout("%s: %s duplicate dependency string.\n", fname, item_get(&files)); | |||||
wd = get_quoted_word(fp); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: %s missing dependency string.\n", fname, item_get(&files)); | |||||
depends = ns(wd); | |||||
} else if (eq(wd, "clean")) { | |||||
if (clean != 0) | |||||
errout("%s: %s duplicate clean file list.\n", fname, item_get(&files)); | |||||
wd = get_quoted_word(fp); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: %s missing clean file list.\n", fname, item_get(&files)); | |||||
clean = ns(wd); | |||||
} else if (eq(wd, "compile-with")) { | |||||
if (compilewith != 0) | |||||
errout("%s: %s duplicate compile command string.\n", fname, item_get(&files)); | |||||
wd = get_quoted_word(fp); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: %s missing compile command string.\n", fname, item_get(&files)); | |||||
compilewith = ns(wd); | |||||
} else if (eq(wd, "warning")) { | |||||
if (warning != 0) | |||||
errout("%s: %s duplicate warning text string.\n", fname, item_get(&files)); | |||||
wd = get_quoted_word(fp); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: %s missing warning text string.\n", fname, item_get(&files)); | |||||
warning = ns(wd); | |||||
} else if (eq(wd, "obj-prefix")) { | |||||
if (objprefix[0] != 0) | |||||
errout("%s: %s duplicate object prefix string.\n", fname, item_get(&files)); | |||||
wd = get_quoted_word(fp); | |||||
if (wd == (char *)EOF || wd == 0) | |||||
errout("%s: %s missing object prefix string.\n", fname, item_get(&files)); | |||||
objprefix = ns(wd); | |||||
} else if (eq(wd, "nowerror")) { | |||||
f_flags |= NOWERROR; | |||||
} else if (eq(wd, "local")) { | |||||
filetype = LOCAL; | |||||
} else if (eq(wd, "no-depend")) { | |||||
filetype = NODEPEND; | |||||
} else { | |||||
errout("%s: %s Cannot parse '%s' when file is specified\n", fname, item_get(&files), wd); | |||||
} | |||||
goto next_word; | |||||
flush_option: | |||||
/* add all implications */ | |||||
while (STAILQ_FIRST(&implications)) { | |||||
char *imp; | |||||
imp = item_get(&implications); | |||||
(new ANDMAP(ANDMAP(option) & | |||||
ANDMAP(OPTION(imp).toggleInverted())))->insert_tail(phead); | |||||
free(imp); | |||||
} | |||||
/* register option at least, to track usage */ | |||||
tp = new_fent(); | |||||
tp->f_option = option; | |||||
/* add all files */ | |||||
while (STAILQ_FIRST(&files)) { | |||||
char *str; | |||||
str = item_get(&files); | |||||
tp = new_fent(); | |||||
tp->f_fn = str; | |||||
tp->f_type = filetype; | |||||
tp->f_option = option; | |||||
if (filetype == LOCAL) | |||||
tp->f_srcprefix = ""; | |||||
else | |||||
tp->f_srcprefix = "$S/"; | |||||
tp->f_flags |= f_flags; | |||||
tp->f_compilewith = compilewith; | |||||
tp->f_depends = depends; | |||||
tp->f_clean = clean; | |||||
tp->f_warn = warning; | |||||
tp->f_objprefix = objprefix; | |||||
} | |||||
filetype = -1; | |||||
goto next; | |||||
} | |||||
static int | |||||
option_compare(const void *a, const void *b) | |||||
{ | |||||
return ((const OPTION *)a)[0].compare(((const OPTION * )b)[0]); | |||||
} | |||||
void | |||||
read_file_v2(char *fname) | |||||
{ | |||||
STAILQ_HEAD(file_list_head, file_list) files; | |||||
ANDMAP_HEAD_t andmap; | |||||
struct file_list *tp; | |||||
struct file_list *tq; | |||||
struct device *dp; | |||||
OPTION conflict; | |||||
OPTION *popt; | |||||
size_t nopt; | |||||
TAILQ_INIT(&andmap); | |||||
STAILQ_INIT(&files); | |||||
/* first import all existing options into the andmap */ | |||||
import_options(&andmap); | |||||
/* parse file tree */ | |||||
read_file_v2_generic(fname, &andmap); | |||||
/* get list of all options */ | |||||
nopt = 0; | |||||
for (ANDMAP *pa = TAILQ_FIRST(&andmap); pa; pa = pa->next()) | |||||
nopt += pa->nummaps; | |||||
popt = new OPTION [nopt]; | |||||
nopt = 0; | |||||
for (ANDMAP *pa = TAILQ_FIRST(&andmap); pa; pa = pa->next()) { | |||||
for (uint8_t x = 0; x != pa->nummaps; x++) | |||||
popt[nopt++] = pa->options[x]; | |||||
} | |||||
/* clear inversion flag */ | |||||
for (size_t x = 0; x != nopt; x++) { | |||||
if (popt[x].isInverted()) | |||||
popt[x].toggleInverted(); | |||||
} | |||||
/* get uniq set of options */ | |||||
if (nopt > 1) { | |||||
size_t y = 0; | |||||
mergesort(popt, nopt, sizeof(popt[0]), &option_compare); | |||||
for (size_t x = 1; x != nopt; x++) { | |||||
if (popt[y] != popt[x]) | |||||
popt[++y] = popt[x]; | |||||
} | |||||
nopt = y + 1; | |||||
} | |||||
if (hp2sat_solve(&andmap, popt, nopt, &conflict)) { | |||||
if (conflict.isInverted()) | |||||
conflict.toggleInverted(); | |||||
errout("%s: %s Option has a conflict and cannot be determined\n", fname, conflict.option); | |||||
} | |||||
hp2sat_free(&andmap); | |||||
STAILQ_CONCAT(&files, &ftab); | |||||
while ((tp = STAILQ_FIRST(&files)) != 0) { | |||||
STAILQ_REMOVE_HEAD(&files, f_next); | |||||
OPTION test(tp->f_option); | |||||
/* check if option is disabled */ | |||||
for (size_t x = 0; x != nopt; x++) { | |||||
if (popt[x] == test) | |||||
goto skip; | |||||
} | |||||
/* set flag, that device option is used */ | |||||
STAILQ_FOREACH(dp, &dtab, d_next) { | |||||
if (eq(dp->d_name, tp->f_option)) { | |||||
dp->d_done |= DEVDONE; | |||||
break; | |||||
} | |||||
} | |||||
/* skip entries with no filename */ | |||||
if (tp->f_fn == 0) | |||||
goto skip; | |||||
/* check for duplicate rules on same filename */ | |||||
for (tq = STAILQ_FIRST(&ftab); tq; tq = STAILQ_NEXT(tq, f_next)) { | |||||
if (tq->f_fn == 0) | |||||
continue; | |||||
if (eq(tp->f_fn, tq->f_fn)) { | |||||
fprintf(stderr, "WARNING: %s File '%s' has multiple rules (%s), ignored.\n", | |||||
fname, tp->f_fn, tp->f_option); | |||||
goto skip; | |||||
} | |||||
} | |||||
STAILQ_INSERT_TAIL(&ftab, tp, f_next); | |||||
continue; | |||||
skip: | |||||
free(tp); | |||||
} | |||||
delete [] popt; | |||||
} |