Mash shell scripts are line-oriented. Each line is a statement. A statement can be a command to run, a definition of how to build a given target, a variable declaration, an ``if'' statement, a ``switch'' statement, a ``while'' loop, etc.
Here is a sample Mashfile.
set variable = expressionThe expression on the right-hand side of the equal-sign is evaluated as explained in the section on expressions.
A plus-equal sign in a declaration tells Mash to append the value of the expression to the end of the variable's current value. For example:
set CFLAGS = -O -g -Wall set CFLAGS += -DPOSIX -DNO_STRDUPwill set
CFLAGS
to the five element list
-O -g -Wall -DPOSIX -DNO_STRDUP
.
if boolean expression statement else statementThe boolean expression is evaluated as described in the section on expressions. The ``else'' clause is optional. There is no ``endif'' statement. It is not needed because the conditional statement clearly ends with the statement that follows the ``if'' or the ``else''.
As in C, a block of statements in curly braces always counts as one statement. Thus, you can conditionally execute multiple statements in the form:
if boolean expression { statement1 statement2 } else { statement3 statement4 }
switch expression case tag statementThe switch statement can contain any number of cases. There is no endswitch statement. The switch statement is over when a line does not begin with the word "case" or the word "default". Each "case" line can contain multiple tags, separated by commas. If the switched expression matches any of the tags, then only the statement following that tag will be executed. (It does not fall through to the next case statement.) Optionally, there can be a line containing "default" and no tags. This represents a case that is always taken when reached, so do not put any more cases after the default case. The switched expression may be a multi-element list. For example, on a Linux/i386 system, the following code will output the number 2:
switch $SYS_NAME $SYS_ARCH case Solaris SPARC, Irix MIPS echo 1 case Linux i386, OSF/1 Alpha echo 2 case HP/UX HP-PA, AIX PowerPC echo 3 default echo 4
foreach variable_name expression statementFor each string in the expression, it will set the given variable's value to that string and execute the statement. For example, the following code will output the numbers 1, 2, and 3 on separate lines:
foreach x (1 2 3) echo $xThe parentheses around (1 2 3) are optional. You may always put parentheses around any expression, and I feel that they make the statement more readable in this case.
while boolean expression statementThe given statement will be executed repeatedly until the given boolean expression becomes false. There is no guarantee that this will ever happen.
include file1 file2 file3...Mash replaces the "include" command with the contents of each file it names. If the named file does not exist in the working directory, then Mash searches for it in each directory named by the MASH_INCLUDE_PATH variable. This variable is initialized to something meaningful on your system, so you should never replace it's value, just append to it. For example:
set MASH_INCLUDE_PATH += ./config/ include config.mash
echo expressionoutputs the value of the given expression.
target target-expression: depends-expression statementA rule tells Mash how to build targets. target-expression is evaluated as a list of targets that are built using the rule. depends-expression, which is optional, tells what files the targets that use this rule depend on.
The statement (or statement block) of a rule is not executed immediately. Initially, when mash reads the Mashfile, it evaluates the target-expression and depends-expression, and it stores the target's statement (unevaluated) in memory. After the entire Mashfile has been parsed, mash begins to build the targets specified on the command line. (If no targets were specified, mash starts to build the "all" target.)
To build a target, mash first attempts to recursively build every target named in the depends-expression of the target's rule. If any of these targets fails to build successfully, mash exits. If every target is built successfully, then mash checks for the existance of a file with the same name as the target. If that file does not exist, or if that file is older than some file listed in the depends-expression, then mash executes the target rule's statement.
A target is considered to have built successfully if the rule's statement executes successfully, or if the target file is found to be newer than all it's dependencies, or if there is no rule that specifies the target but a file with the name of the target exists. In the first case, where the statement executes successfully, it is not necessary for a file with the name of the target to be created.
In a rule's depends-expression, the variable $target is given special meaning. For example,
target foo bar: ${target}.osays that foo depends on foo.o and bar depends on bar.o. It does not say that foo depends on bar.o or that bar depends on foo.o.
In a rule's statement (or statement block), the variable $target is used the same way, and the variable $depends is set to the entire depends-expression.
The statements in a rule can be Mash built-in commands or shell commands. That is, when the first word of a statement in a rule is not a built-in command, mash tries to find and run the named program.
There are other statements for defining rules: the "suffix" statement and the "depend" statement. I should document these.
How tokens are evaluated:
Globbing: When a token contains a star or a question-mark, that token is evaluated as a (possibly-empty) list of files. The list of files is determined using the method of globbing detailed in the Posix.2 standard.
Variables A dollar-sign followed by a variable name is evaluated as the value of that variable. The name of the variable may optionally be enclosed in parentheses or curly braces. The value of the variable is substituted exactly. The value of the variable is not evaluated. For example, if the value of the variable contains a star, then the value of the expression will contain a star. The star will not be replaced by a list of files. If a token contains text before or after the variable, then that text is concatenated to the value. For example:
set variable1 = 1 2 3 set variable2 = prefix${variable1}suffixwill set
variable2
to the three-element list
prefix1 2 3suffix
.Functions: In a token, an ampersand followed by a function name followed by a list of arguments in parentheses is replaced by the value of that function call. Here is a simple example of a function call that will output the word "filename":
set variable = &basename(filename.c)I plan to someday document the list of built-in functions supported by Mash.
Literal mode: When an apostrophe appears in a token, Mash enters literal mode. In this mode, all characters in the Mashfile go into the string until the closing apostrophe. This includes whitespace, newlines, non-printing characters, etc. There is no escape character in literal mode.
A literal string may be concatenated with a non-literal token. For example, the line:
set variable = '*'*will set
variable
to the list of all files in the current
working directory that begin with a star.Semicolon: A semicolon ends the current token and the current list. It is exactly the same as a newline. For example:
set var1 = foo; set var2 = barNumber sign: A number sign begins a comment. It ends the current token and the current list. All the text after a number sign until the end of line is ignored.
Parentheses: You may optionally put any expression in parentheses. Sometimes, it looks nice. For example, the following code sets two variables to the exact same three element list:
set var1 = a b c set var2 = (a b c)Within parentheses, newlines are treated as regular spaces.