diff --git a/build/parseSpec.c b/build/parseSpec.c index c80802bafa..5b2003216f 100644 --- a/build/parseSpec.c +++ b/build/parseSpec.c @@ -176,15 +176,21 @@ static int expandMacrosInSpecBuf(rpmSpec spec, int strip) char *lbuf = NULL; int isComment = 0; - /* Don't expand macros (eg. %define) in false branch of %if clause */ - if (!spec->readStack->reading) - return 0; lbuf = spec->lbuf; SKIPSPACE(lbuf); if (lbuf[0] == '#') isComment = 1; + /* Don't expand macros after %elif (resp. %elifarch, %elifos) in false branch */ + if ((ISMACROWITHARG(lbuf, "%elif")) || (ISMACROWITHARG(lbuf, "%elifos")) || + (ISMACROWITHARG(lbuf, "%elifarch"))) { + if (!spec->readStack->readable) + return 0; + /* Don't expand macros (eg. %define) in false branch of %if clause */ + } else if (!spec->readStack->reading) { + return 0; + } if (rpmExpandMacros(spec->macros, spec->lbuf, &lbuf, 0) < 0) { rpmlog(RPMLOG_ERR, _("line %d: %s\n"), @@ -360,7 +366,8 @@ do { \ int readLine(rpmSpec spec, int strip) { char *s; - int match; + int match = 0; + int lineType = OTHER_LINE; struct ReadLevelEntry *rl; OFI_t *ofi = spec->fileStack; int rc; @@ -399,29 +406,69 @@ int readLine(rpmSpec spec, int strip) s = spec->line; SKIPSPACE(s); - match = -1; - if (!spec->readStack->reading && ISMACROWITHARG(s, "%if")) { - match = 0; - } else if (ISMACROWITHARG(s, "%ifarch")) { + if (ISMACROWITHARG(s, "%ifarch")) { + lineType = IF_LINE; ARGMATCH(s, "%{_target_cpu}", match); } else if (ISMACROWITHARG(s, "%ifnarch")) { + lineType = IF_LINE; ARGMATCH(s, "%{_target_cpu}", match); match = !match; } else if (ISMACROWITHARG(s, "%ifos")) { + lineType = IF_LINE; ARGMATCH(s, "%{_target_os}", match); } else if (ISMACROWITHARG(s, "%ifnos")) { + lineType = IF_LINE; ARGMATCH(s, "%{_target_os}", match); match = !match; } else if (ISMACROWITHARG(s, "%if")) { + lineType = IF_LINE; s += 3; - match = parseExpressionBoolean(s); - if (match < 0) { + if (spec->readStack->reading) { + match = parseExpressionBoolean(s); + if (match < 0) { + rpmlog(RPMLOG_ERR, + _("%s:%d: bad %%if condition: %s\n"), + ofi->fileName, ofi->lineNum, s); + return PART_ERROR; + } + } + } else if (ISMACROWITHARG(s, "%elifarch")) { + lineType = ELIF_LINE; + ARGMATCH(s, "%{_target_cpu}", match); + if (!spec->readStack->elifEnabled) { + rpmlog(RPMLOG_ERR, + _("%s:%d: Got %%elifarch with no %%if\n"), + ofi->fileName, ofi->lineNum); + return PART_ERROR; + } + } else if (ISMACROWITHARG(s, "%elifos")) { + lineType = ELIF_LINE; + ARGMATCH(s, "%{_target_os}", match); + if (!spec->readStack->elifEnabled) { rpmlog(RPMLOG_ERR, - _("%s:%d: bad %%if condition\n"), + _("%s:%d: Got %%elifos with no %%if\n"), ofi->fileName, ofi->lineNum); return PART_ERROR; } + } else if (ISMACROWITHARG(s, "%elif")) { + lineType = ELIF_LINE; + s += 5; + if (spec->readStack->readable) { + match = parseExpressionBoolean(s); + if ((match < 0)) { + rpmlog(RPMLOG_ERR, + _("%s:%d: Bad %%elif condition: %s"), + ofi->fileName, ofi->lineNum, s); + return PART_ERROR; + } + } if (!spec->readStack->elifEnabled) { + rpmlog(RPMLOG_ERR, + _("%s:%d: Got %%elif with no %%if\n"), + ofi->fileName, ofi->lineNum); + return PART_ERROR; + } } else if (ISMACRO(s, "%else")) { + spec->readStack->elifEnabled = 0; if (! spec->readStack->next) { /* Got an else with no %if ! */ rpmlog(RPMLOG_ERR, @@ -430,7 +477,7 @@ int readLine(rpmSpec spec, int strip) return PART_ERROR; } spec->readStack->reading = - spec->readStack->next->reading && ! spec->readStack->reading; + spec->readStack->next->reading && spec->readStack->readable; spec->line[0] = '\0'; } else if (ISMACRO(s, "%endif")) { if (! spec->readStack->next) { @@ -467,13 +514,21 @@ int readLine(rpmSpec spec, int strip) goto retry; } - if (match != -1) { + if (lineType == IF_LINE) { rl = xmalloc(sizeof(*rl)); rl->reading = spec->readStack->reading && match; rl->next = spec->readStack; rl->lineNum = ofi->lineNum; + rl->readable = (!rl->reading) && (spec->readStack->reading); + rl->elifEnabled = 1; spec->readStack = rl; spec->line[0] = '\0'; + } else if (lineType == ELIF_LINE) { + spec->readStack->reading = match && spec->readStack->readable; + if (spec->readStack->reading) + spec->readStack->readable = 0; + spec->line[0] = '\0'; + match = -1; } if (! spec->readStack->reading) { diff --git a/build/rpmbuild_internal.h b/build/rpmbuild_internal.h index 948632a163..6b4b9edd4f 100644 --- a/build/rpmbuild_internal.h +++ b/build/rpmbuild_internal.h @@ -34,6 +34,8 @@ struct TriggerFileEntry { typedef struct ReadLevelEntry { int reading; int lineNum; + int elifEnabled; + int readable; struct ReadLevelEntry * next; } RLE_t; @@ -190,6 +192,15 @@ typedef enum rpmParseState_e { } rpmParseState; +/** \ingroup rpmbuild + * Bit(s) to control type of the current spec file line + */ +enum ifTypes { + OTHER_LINE = 0, /*!< other lines */ + IF_LINE = (1 << 0), /*!< %if or %ifos or %ifnos or %ifarch or %ifnarch line */ + ELIF_LINE = (1 << 1) /*!< %elif or %elifos or %elifarch line */ +}; + #define STRIP_NOTHING 0 #define STRIP_TRAILINGSPACE (1 << 0) #define STRIP_COMMENTS (1 << 1) diff --git a/build/spec.c b/build/spec.c index e414e4102e..0a31d8840e 100644 --- a/build/spec.c +++ b/build/spec.c @@ -268,6 +268,8 @@ rpmSpec newSpec(void) spec->readStack = xcalloc(1, sizeof(*spec->readStack)); spec->readStack->next = NULL; spec->readStack->reading = 1; + spec->readStack->readable = 1; + spec->readStack->elifEnabled = 0; spec->rootDir = NULL; spec->prep = NULL; diff --git a/tests/data/SPECS/iftest.spec b/tests/data/SPECS/iftest.spec new file mode 100644 index 0000000000..314f7f9e45 --- /dev/null +++ b/tests/data/SPECS/iftest.spec @@ -0,0 +1,116 @@ +Name: iftest +Version: 1.0 +Release: 1 +Summary: Testing %if, %elif, %else options +License: GPL +Group: Testing +BuildArch: noarch +%if 0%{?testif} == 2 + %if 0%{?rhel} == 6 +Requires: pkg1 + %elif 0%{?rhel} == 7 +Requires: pkg2 + %else +Requires: pkg3 + %endif +%endif + +%description +%{summary} + +%build +%define actecho0 exit +%define actecho1 0 + +%if 0%{?testif} == 2 + + %if 0%{?fedora} >= 23 + %{actecho0} %{actecho1} + %elif 0%{?fedora} >= 10 || 0%{?rhel} >= 6 + %{actecho0} %{actecho1} + %else + %{actecho0} %{actecho1} + %endif + +%elif 0%{testif} == 1 + + %if %{variable1} + %{action1} + %elif %{variable2} + %{action2} + %elif %{variable3} + %{action3} + %else + %{action4} + %endif + +%else + + %if 0 + exit 1 + %elif 0 + exit 1 + %else + %global iftest1_macro defined + %endif + %{!?iftest1_macro:exit 1} + + %if 0 + exit 1 + %elif 0 + exit 1 + %elif 1 + %global iftest2_macro defined + %elif 1 + exit 1 + %elif 0 + exit 1 + %else + exit 1 + %endif + %{!?iftest2_macro:exit 1} + + %if 1 + %global iftest3_macro defined + %elif 0 + exit 1 + %elif 1 + exit 1 + %else + exit 1 + %endif + %{!?iftest3_macro:exit 1} + + %if 0 + exit 1 + %elif 1 + %if 0 + exit 1 + %elif 0 + exit 1 + %elif 1 + %global iftest4_macro defined + %else + exit 1 + %endif + %elif 1 + exit 1 + %endif + %{!?iftest4_macro:exit 1} + + %if 0 + exit 0 + %elif 0 + exit 0 + %elif 1 + %global iftest5_macro defined + %elif 1 + exit 1 + %elif 0 + exit 1 + %else + exit 1 + %endif + %{!?iftest5_macro:exit 1} + +%endif diff --git a/tests/rpmmacro.at b/tests/rpmmacro.at index 8875490db1..ea39c30e57 100644 --- a/tests/rpmmacro.at +++ b/tests/rpmmacro.at @@ -454,3 +454,106 @@ runroot rpm --macros "/data/macros.testfile" \ macro_2 ]) AT_CLEANUP + +AT_SETUP([%if, %else, %elif test basic]) +AT_KEYWORDS([if, else, elif]) +AT_CHECK([ +runroot rpmbuild -ba --quiet \ + --define "testif 0" \ + /data/SPECS/iftest.spec +], +[0], +[]) +AT_CLEANUP + +AT_SETUP([%if, %else, %elif test existing script]) +AT_KEYWORDS([if, else, elif]) +AT_CHECK([ +runroot rpmbuild -ba --quiet \ + --define "testif 2" \ + --define "fedora 1" \ + /data/SPECS/iftest.spec +], +[0], +[]) +AT_CLEANUP + +AT_SETUP([%if, %else, %elif test 1]) +AT_KEYWORDS([if, else, elif]) +AT_CHECK([ +runroot rpmbuild -ba --quiet \ + --define "testif 1" \ + --define "variable1 1" \ + --define "variable2 1" \ + --define "variable3 1" \ + --define "action1 echo 0" \ + --define "action2 exit 1" \ + --define "action3 exit 1" \ + --define "action4 exit 1" \ + /data/SPECS/iftest.spec +], +[0], +[]) +AT_CLEANUP + + + +AT_SETUP([%if, %else, %elif test 2]) +AT_KEYWORDS([if, else, elif]) +AT_CHECK([ +runroot rpmbuild -ba --quiet \ + --define "testif 1" \ + --define "variable1 0" \ + --define "variable2 0" \ + --define "variable3 1" \ + --define "action1 echo 0" \ + --define "action2 echo 0" \ + --define "action3 exit 1" \ + --define "action4 echo 0" \ + /data/SPECS/iftest.spec > /dev/null 2>&1 +], +[1], +[]) +AT_CLEANUP + + + +AT_SETUP([%if, %else, %elif test 3]) +AT_KEYWORDS([if, else, elif]) +AT_CHECK([ +runroot rpmbuild -ba --quiet \ + --define "testif 1" \ + --define "variable1 0" \ + --define "variable2 1" \ + --define "variable3 rubbish:4-3" \ + --define "action1 exit 1" \ + --define "action2 echo 0" \ + --define "action3 exit 1" \ + --define "action4 exit 1" \ + /data/SPECS/iftest.spec +], +[0], +[]) +AT_CLEANUP + + +AT_SETUP([%if, %else, %elif test 4]) +AT_KEYWORDS([if, else, elif]) +AT_CHECK([ +runroot rpmbuild -ba --quiet \ + --define "testif 1" \ + --define "variable1 0" \ + --define "variable2 rubbish:4-3" \ + --define "variable3 1" \ + --define "action1 exit 1" \ + --define "action2 echo 0" \ + --define "action3 exit 1" \ + --define "action4 exit 1" \ + /data/SPECS/iftest.spec +], +[1], +[], +[error: parse error in expression +error: /data/SPECS/iftest.spec:39: Bad %elif condition: rubbish:4-3 +]) +AT_CLEANUP