Austin Group Defect Tracker

Aardvark Mark III

Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
ID Category Severity Type Date Submitted Last Update
0000805 [1003.1(2008)/Issue 7] Shell and Utilities Objection Enhancement Request 2013-11-30 02:50 2013-12-02 12:59
Reporter dwheeler View Status public  
Assigned To ajosey
Priority normal Resolution Open  
Status Under Review  
Name David A. Wheeler
User Reference
Section make
Page Number 2937-2938
Line Number 97115-97166
Interp Status ---
Final Accepted Text
Summary 0000805: Add Make conditionals
Description Almost all “make” implementations include some kind of if-then-else conditional. Most implementations support at least two types of conditionals: an “is-defined” conditional (e.g., “ifdef” or “.ifdef” or “#ifdef”) and a more general conditional (e.g, “if” or “ifeq” or “#if”). Examples of make implementations that provide conditionals include GNU make, makepp, FreeBSD/OpenBSD/NetBSD make (pmake), Microsoft nmake, AT&T/Alcatel-Lucent nmake, imake, and fastmake. Makefile generators that use make syntax (with extensions), like automake, also support make conditionals.

Conditionals are important for creating exchangeable makefiles, because they enable creating makefiles that automatically build on a variety of different systems (by automatically varying various settings). Unsurprisingly, conditionals are widely used in makefiles.

Yet the POSIX make specification fails to provide a standard mechanism for if-then-else conditionals, so conditionals cannot be used portably in makefiles. Tools like automake work around this by generating portable makefiles through workarounds, but workarounds should not be necessary, especially since most make implementations already support if-then-else conditionals.

The general functionality of if-then-else conditionals is similar between different makes, but their syntax and semantics vary widely. Thus, we must pick an approach that is not currently implemented by all. That’s okay, as long as it’s a syntax that could be added by the others (so it’s best to avoid notations that would interfere with others’ notation). I think it comes down to a decision between GNU make, the *BSD makes, AT&T nmake, or a new notation based on them:

GNU make and makepp have compatible syntax (makepp copies GNU make). They use ifdef|ifndef for is-defined, and ifeq|ifneq as a more general comparator. GNU make is very widely used (it’s the usual “make” in Linux and MacOS, and is widely available elsewhere), so this notation is widely used. The ifdef|ifndef notation is quite clean. A disadvantage is that the ifeq|ifneq notation is clunky, limiting, and non-intuitive. In particular, it doesn’t support clean complex expressions (with && and ||) in a simple way, and it’s not visually obvious that “equal-to” is intended (there’s no “=” or “==”).
The *BSDs use .ifdef|.ifndef for is-defined, and .if for a more general comparitor. (Fastmake uses a similar (but not identical) “.IF”/“.ELSE”/“.ENDIF” syntax for its conditional.) The “.ifdef” syntax allow || and &&, which is nice, but since ANY setting defines a value, it’s hard to undefine a value on the command line, which I believe is a serious weakness in its semantics. The *BSDs “.if” syntax is cleaner, but semantically it “automagically” determines if == is a numeric or string comparison (e.g., and interpret “0x” specially), which I think is dangerous.
AT&T/Alcatel-Lucent nmake use “if”. The conditional syntax has slightly unusual semantics (initial single-quote means interpret as string, initial double-quote interpret as pattern, no quote interpret as number). There is no separate “ifdef” syntax, but that could be added. Paul Smith (GNU make) has sent me email that he really does not like the “if” syntax where the same operator syntax has different semantic meanings depending on the quote (or not) attached to the first operator.
POSIX could make its own syntax, building on existing approaches, just like printf(1), building on existing experience.

Here are some other alternatives:
Microsoft nmake begins with “!IF”. I don’t see evidence that they’re striving for POSIX compliance, and “!” isn’t especially reserved, so I’ll ignore it.
Opus software uses “%if…%elif…%else…%endif”, but it’s unmaintained as far as I can tell, and “%” interferes with pattern matching.
Imake is built on cpp, so it uses cpp syntax (e.g., #ifdef).

Given the concerns listed above, I think the best approach is to add new syntax (#4 in the first list), building on the capabilities of GNU make, BSD make (pmake), and nmake. The following approach uses GNU make’s “ifdef” for variable definitions (which are easier to disable on the command line or via include files), but I’ve added BSD make’s .ifdef capabilities which support ||, &&, and !. Both GNU make “ifeq” and BSD make’s “.if” have issues, so I propose a new syntax “iftrue” that is strongly influenced by the BSD make syntax. I did not use “if” because that would conflict with the syntax of other tools (e.g., automake and nmake). Existing simple GNU make files that only use “ifdef” would be portable, and existing makefiles that use nonstandard conditionals can continue to use them (though they’d continue to be nonstandard). This proposal does not add macro functions, but is completely consistent with them; see bug report 512. The description below does not create a new syntax for escaping values, but existing substitutions could be used (e.g., x$(space)y could be used for “x y”).

Earlier drafts of this proposal required commands to begin on the left edge, since indented make conditionals could be visually confused with an “if” in a rule command. However, this could easily lead to confusion when make conditionals are nested.

NOTE: It’s also helpful to support some related capabilities, but these are out-of-scope of this proposal (though they would work well together):
Macro functions such as $(if CONDITION, THEN [,ELSE]). See: [^]
Recursive variable indirection. See: [^]

I’ve collected more information here: [^]

Note: I'm flexible about the syntax added, the key is getting a syntax and semantics that people can agree on.
Desired Action In line 97115, change “include lines,” to “include lines, conditional statements,”.

Before line 97166 (right after the “include lines”) section, add a new section, “Conditional statements” with the following content:


If the word “ifdef”, “ifndef”, “iftrue”, “else”, or “endif” appears at the beginning of a line after 0 or more <blank> characters, and if followed by or one or more <blank> characters or the end of line, the line is part of a conditional statement. A conditional statement is of the form:

<conditional_statement> ::= <begin_condition> <content>
                                             (<continued_condition> <content>)*
                                              (<else_condition> <content>)? <endif_line>
<begin_condition> ::= ( “ifdef” <space>+ <ifdef_condition>
                                    | “ifndef” <space>+ <ifdef_condition>
                                    | “iftrue” <space>+ <if_condition> ) <eol>
<continued_condition> ::= “else” <space>+ <begin_condition>
<else_condition> ::= “else” <eol>
<content> ::= <line>*
<endif_line> ::= “endif” <eol>
<eol> ::= <return>? <newline>
<ifdef_condition> ::= “(” <ifdef_condition> “)” |
                                   “!”? <value> { “&&” <ifdef_condition> | “||” <ifdef_condition> }
<if_condition> ::= <expression> { “&&” <if_condition> | “||” <if_condition> }
<expression> ::= “(” <expression> “)” | “!” <space>+ <expression> ||
                             <value> [ <op> <value> ]
<value> ::= ( <macro_expansion> | <other_character> )+
<op> ::= “==” || “!=” | “-lt” | “-le” | “-gt” | “-ge” | “-eq” | “-ne”

The precedence from highest to lowest is parentheses, “!” (not), “&&” (logical and) and” (logical or). When executed both “&&” and “||” short-circuit, left-to-right. An “other_character” is a character other than “$”, whitespace, or a control character (note that newline is a control character). A <macro_expansion> begins with “$” and is described in the “Macros” section below, e.g., “${HOME}” is a macro expansion. Outside of macro expansions, <space> characters in <ifdef_condition> and <if_condition> are ignored except that they separate tokens.

An “op” is a string or numeric operator. The string operators are “==” (case-sensitive string equality) and “!=” (case-sensitive string inequality). The numeric operators are “-lt” (numeric less-than), “-le” (numeric less-than or equal-to), “-gt” (numeric greater-than), “-ge” (numeric greater-than or equal-to), “-eq” (numeric equality), and “-ne” (numeric inequality). An operand to a numeric operators that begins with “0x” is considered a number in base 16.

If an expression consists of just a value without the optional “op” it is considered true if it evaluates to at least one character, and false otherwise. In ifdef_condition, a <value> is evaluated and considered the name of a macro to determine if it is defined. A macro is considered defined if ifdef_condition if it has a nonempty value before evaluation (use “::=” to precalculate a macro).

Conditional statements are evaluated as the makefile is read in, in the order specified. If the relevant condition evaluates as true, then the content is read and used; otherwise its content is ignored.
Tags No tags attached.
Attached Files

- Relationships

-  Notes
steffen (reporter)
2013-12-02 12:59

I think it's worth adding a note that conditionals should have file-scope.
Even though all make(1)s that i use and which do support conditionals already implement conditionals like that, it is an ambiguity than can be avoided.

- Issue History
Date Modified Username Field Change
2013-11-30 02:50 dwheeler New Issue
2013-11-30 02:50 dwheeler Status New => Under Review
2013-11-30 02:50 dwheeler Assigned To => ajosey
2013-11-30 02:50 dwheeler Name => David A. Wheeler
2013-11-30 02:50 dwheeler Section => make
2013-11-30 02:50 dwheeler Page Number => 2937-2938
2013-11-30 02:50 dwheeler Line Number => 97115-97166
2013-12-02 12:59 steffen Note Added: 0002036

Mantis 1.1.6[^]
Copyright © 2000 - 2008 Mantis Group
Powered by Mantis Bugtracker