From 8248785e1d70b5365e2fc17e17dc2ba861c88dd1 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Wed, 22 May 2024 14:05:41 +0100 Subject: make: add support for ifeq/ifneq Add support for the conditional directives ifeq and ifneq. These follow GNU make in allowing things like: ifeq (arg1,arg2) ifeq 'arg1' 'arg2' In the second case single or double quotes may be used. Macros are expanded in the two arguments and the resulting strings are compared. Adds 240-248 bytes. --- miscutils/make.c | 98 +++++++++++++++++++++++++++++++++++++++++++--------- testsuite/make.tests | 14 ++++++++ 2 files changed, 96 insertions(+), 16 deletions(-) diff --git a/miscutils/make.c b/miscutils/make.c index c69dcec8a..063d0662d 100644 --- a/miscutils/make.c +++ b/miscutils/make.c @@ -1419,9 +1419,66 @@ enum { #define IFDEF 0 #define IFNDEF 1 +#define IFEQ 2 +#define IFNEQ 3 #define ELSE 0 #define ENDIF 1 +/* + * Extract strings following ifeq/ifneq and compare them. + * Return -1 on error. + */ +static int +compare_strings(char *arg1) +{ + char *arg2, *end, term, *t1, *t2; + int ret; + + // Get first string terminator. + if (arg1[0] == '(') + term = ','; + else if (arg1[0] == '"' || arg1[0] == '\'') + term = arg1[0]; + else + return -1; + + arg2 = find_char(++arg1, term); + if (arg2 == NULL) + return -1; + *arg2++ = '\0'; + + // Get second string terminator. + if (term == ',') { + term = ')'; + } else { + // Skip spaces between quoted strings. + while (isspace(arg2[0])) + arg2++; + if (arg2[0] == '"' || arg2[0] == '\'') + term = arg2[0]; + else + return -1; + ++arg2; + } + + end = find_char(arg2, term); + if (end == NULL) + return -1; + *end++ = '\0'; + + if (gettok(&end) != NULL) { + warning("unexpected text"); + } + + t1 = expand_macros(arg1, FALSE); + t2 = expand_macros(arg2, FALSE); + + ret = strcmp(t1, t2) == 0; + free(t1); + free(t2); + return ret; +} + /* * Process conditional directives and return TRUE if the current line * should be skipped. @@ -1429,7 +1486,7 @@ enum { static int skip_line(const char *str1) { - char *copy, *q, *token, *next_token; + char *copy, *q, *token; bool new_level = TRUE; // Default is to return skip flag for current level int ret = cstate[clevel] & SKIP_LINE; @@ -1441,11 +1498,9 @@ skip_line(const char *str1) copy = xstrdup(str1); q = process_line(copy); if ((token = gettok(&q)) != NULL) { - next_token = gettok(&q); - switch (index_in_strings("else\0endif\0", token)) { case ENDIF: - if (next_token != NULL) + if (gettok(&q) != NULL) error_unexpected("text"); if (clevel == 0) error_unexpected(token); @@ -1463,23 +1518,39 @@ skip_line(const char *str1) else cstate[clevel] &= ~SKIP_LINE; - if (next_token == NULL) { + token = gettok(&q); + if (token == NULL) { // Simple else with no conditional directive cstate[clevel] &= ~EXPECT_ELSE; ret = TRUE; goto end; } else { // A conditional directive is now required ('else if'). - token = next_token; - next_token = gettok(&q); new_level = FALSE; } - break; } - key = index_in_strings("ifdef\0ifndef\0", token); + key = index_in_strings("ifdef\0ifndef\0ifeq\0ifneq\0", token); if (key != -1) { - if (next_token != NULL && gettok(&q) == NULL) { + int match; + + if (key == IFDEF || key == IFNDEF) { + // ifdef/ifndef: find out if macro is defined. + char *name = gettok(&q); + if (name != NULL && gettok(&q) == NULL) { + char *t = expand_macros(name, FALSE); + struct macro *mp = getmp(t); + match = mp != NULL && mp->m_val[0] != '\0'; + free(t); + } else { + match = -1; + } + } else { + // ifeq/ifneq: compare strings. + match = compare_strings(q); + } + + if (match >= 0) { if (new_level) { // Start a new level. if (clevel == IF_MAX) @@ -1494,17 +1565,12 @@ skip_line(const char *str1) } if (!(cstate[clevel] & GOT_MATCH)) { - char *t = expand_macros(next_token, FALSE); - struct macro *mp = getmp(t); - int match = mp != NULL && mp->m_val[0] != '\0'; - - if (key == IFNDEF) + if (key == IFNDEF || key == IFNEQ) match = !match; if (match) { cstate[clevel] &= ~SKIP_LINE; cstate[clevel] |= GOT_MATCH; } - free(t); } } else { error("invalid condition"); diff --git a/testsuite/make.tests b/testsuite/make.tests index 5119a77d3..ba3855973 100755 --- a/testsuite/make.tests +++ b/testsuite/make.tests @@ -553,6 +553,20 @@ src.o: src.c src.h ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null +# ifeq/ifneq conditionals are supported +testing 'make support ifeq and ifneq conditionals' \ + "make -f -" "A OK\nB OK\n" "" ' +A = a +B = b +target: +ifeq ($(A),a) + @echo A OK +endif +ifneq "a" "$B" + @echo B OK +endif +' + # An empty original suffix indicates that every word should have # the new suffix added. If neither suffix is provided the words # remain unchanged. -- cgit v1.2.3-55-g6feb