Plasma GitLab Archive
Projects Blog Knowledge


Jump to:  OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long)
Index:  All • Variables • Functions • Objects • Targets • Options

Chapter 5  OMake concepts and syntax

Projects are specified to omake with OMakefiles. The OMakefile has a format similar to a Makefile. An OMakefile has three main kinds of syntactic objects: variable definitions, function definitions, and rule definitions.

5.1  Variables

Variables are defined with the following syntax. The name is any sequence of alphanumeric characters, underscore _, and hyphen -.

   <name> = <value>

Values are defined as a sequence of literal characters and variable expansions. A variable expansion has the form $(<name>), which represents the value of the <name> variable in the current environment. Some examples are shown below.

   CC = gcc
   CFLAGS = -Wall -g
   COMMAND = $(CC) $(CFLAGS) -O2

In this example, the value of the COMMAND variable is the string gcc -Wall -g -O2.

Unlike make(1), variable expansion is eager and pure (see also the section on Scoping). That is, variable values are expanded immediately and new variable definitions do not affect old ones. For example, suppose we extend the previous example with following variable definitions.

   X = $(COMMAND)
   COMMAND = $(COMMAND) -O3
   Y = $(COMMAND)

In this example, the value of the X variable is the string gcc -Wall -g -O2 as before, and the value of the Y variable is gcc -Wall -g -O2 -O3.

5.2  Adding to a variable definition

Variables definitions may also use the += operator, which adds the new text to an existing definition. The following two definitions are equivalent.

   # Add options to the CFLAGS variable
   CFLAGS = $(CFLAGS) -Wall -g

   # The following definition is equivalent
   CFLAGS += -Wall -g

5.3  Arrays

Arrays can be defined by appending the [] sequence to the variable name and defining initial values for the elements as separate lines. Whitespace is significant on each line. The following code sequence prints c d e.

    X[] =
        a b
        c d e
        f

    println($(nth 2, $(X)))

5.4  Special characters and quoting

The following characters are special to omake: $():,=#\. To treat any of these characters as normal text, they should be escaped with the backslash character \.

    DOLLAR = \$

Newlines may also be escaped with a backslash to concatenate several lines.

    FILES = a.c\
            b.c\
            c.c

Note that the backslash is not an escape for any other character, so the following works as expected (that is, it preserves the backslashes in the string).

    DOSTARGET = C:\WINDOWS\control.ini

An alternative mechanism for quoting special text is the use $"..." escapes. The number of double-quotations is arbitrary. The outermost quotations are not included in the text.

    A = $""String containing "quoted text" ""
    B = $"""Multi-line
        text.
        The # character is not special"""

5.5  Function definitions

Functions are defined using the following syntax.

   <name>(<params>) =
      <indented-body>

The parameters are a comma-separated list of identifiers, and the body must be placed on a separate set of lines that are indented from the function definition itself. For example, the following text defines a function that concatenates its arguments, separating them with a colon.

    ColonFun(a, b) =
        return($(a):$(b))

The return expression can be used to return a value from the function. A return statement is not required; if it is omitted, the returned value is the value of the last expression in the body to be evaluated. NOTE: as of version 0.9.6, return is a control operation, causing the function to immediately return. In the following example, when the argument a is true, the function f immediately returns the value 1 without evaluating the print statement.

    f(a) =
       if $(a)
          return 1
       println(The argument is false)
       return 0

In many cases, you may wish to return a value from a section or code block without returning from the function. In this case, you would use the value operator. In fact, the value operator is not limited to functions, it can be used any place where a value is required. In the following definition, the variable X is defined as 1 or 2, depending on the value of a, then result is printed, and returned from the function.

    f_value(a) =
       X =
          if $(a)
             value 1
          else
             value 2
       println(The value of X is $(X))
       value $(X)

Functions are called using the GNU-make syntax, $(<name> <args)), where <args> is a comma-separated list of values. For example, in the following program, the variable X contains the value foo:bar.

   X = $(ColonFun foo, bar)

If the value of a function is not needed, the function may also be called using standard function call notation. For example, the following program prints the string “She says: Hello world”.

    Printer(name) =
        println($(name) says: Hello world)

    Printer(She)

5.5.1  Keyword arguments

This feature will be introduced in version 0.9.9.0.

Functions can also have keyword parameters and arguments. The syntax of a keyword parameter/argument is <id> = <expression>. Keyword arguments and normal anonymous arguments are completely separate. Keyword arguments are always optional, and if they occur, they must always be named, and they can occur in any order. It is an error to pass a keyword argument to a function that does not define it as a keyword parameter.

    osh>f(x, y = 1, z) =
           add($(mul $x, 100), $(mul $y, 10), $z)
    - : <fun 0>
    osh>f(1, y = 2, 3)
    - : 123 : Int
    osh>f(1, 3, y = 2)
    - : 123 : Int
    osh>f(1, 3)
    - : 113 : Int
    osh>f(1, 2, 3)
    *** omake error:
       File -: line 11, characters 0-10
       arity mismatch: expected 2 args, got 3
    osh>f(z = 7)
    *** omake error:
       File -: line 12, characters 0-8
       no such keyword: z

5.5.2  Function arguments

This feature will be introduced in version 0.9.9.0.

Function arguments have the syntax <parameters> => <exp>. For example, the function foreach 10.6.1 takes function as the first argument.

    osh>foreach(i => $(add $i, 1), 2 3 4)
    - : <array 3 4 5> : Array

In many cases, you may wish to specify the body of the function as an indented block. The ... indicator specifies that the body is an indented block.

    osh>foreach(i => ..., 2 3 4)
            add($i, 1)
    - : <array 3 4 5> : Array

5.6  Comments

Comments begin with the # character and continue to the end of the line.

5.7  File inclusion

Files may be included with the include or open form. The included file must use the same syntax as an OMakefile.

    include $(Config_file)

The open operation is similar to an include, but the file is included at most once.

    open Config

    # Repeated opens are ignored, so this
    # line has no effect.
    open Config

If the file specified is not an absolute filenmame, both include and open operations search for the file based on the OMAKEPATH variable. In case of the open directive, the search is performed at parse time, and the argument to open may not contain any expressions.

5.8  Scoping, sections

Scopes in omake are defined by indentation level. When indentation is increased, such as in the body of a function, a new scope is introduced.

The section form can also be used to define a new scope. For example, the following code prints the line X = 2, followed by the line X = 1.

    X = 1
    section
        X = 2
        println(X = $(X))

    println(X = $(X))

This result may seem surprising–the variable definition within the section is not visible outside the scope of the section.

The export form, which will be described in detail in Section 7.3, can be used to circumvent this restriction by exporting variable values from an inner scope. For example, if we modify the previous example by adding an export expression, the new value for the X variable is retained, and the code prints the line X = 2 twice.

    X = 1
    section
        X = 2
        println(X = $(X))
        export

    println(X = $(X))

There are also cases where separate scoping is quite important. For example, each OMakefile is evaluated in its own scope. Since each part of a project may have its own configuration, it is important that variable definitions in one OMakefile do not affect the definitions in another.

To give another example, in some cases it is convenient to specify a separate set of variables for different build targets. A frequent idiom in this case is to use the section command to define a separate scope.

   section
      CFLAGS += -g
      %.c: %.y
          $(YACC) $<
      .SUBDIRS: foo

   .SUBDIRS: bar baz

In this example, the -g option is added to the CFLAGS variable by the foo subdirectory, but not by the bar and baz directories. The implicit rules are scoped as well and in this example, the newly added yacc rule will be inherited by the foo subdirectory, but not by the bar and baz ones; furthermore this implicit rule will not be in scope in the current directory.

5.9  Conditionals

Top level conditionals have the following form.

    if <test>
       <true-clause>
    elseif <text>
       <elseif-clause>
    else
       <else-clause>

The <test> expression is evaluated, and if it evaluates to a true value (see Section 10.2 for more information on logical values, and Boolean functions), the code for the <true-clause> is evaluated; otherwise the remaining clauses are evaluated. There may be multiple elseif clauses; both the elseif and else clauses are optional. Note that the clauses are indented, so they introduce new scopes.

When viewed as a predicate, a value corresponds to the Boolean false, if its string representation is the empty string, or one of the strings false, no, nil, undefined, or 0. All other values are true.

The following example illustrates a typical use of a conditional. The OSTYPE variable is the current machine architecture.

    # Common suffixes for files
    if $(equal $(OSTYPE), Win32)
       EXT_LIB = .lib
       EXT_OBJ = .obj
       EXT_ASM = .asm
       EXE = .exe
       export
    elseif $(mem $(OSTYPE), Unix Cygwin)
       EXT_LIB = .a
       EXT_OBJ = .o
       EXT_ASM = .s
       EXE =
       export
    else
       # Abort on other architectures
       eprintln(OS type $(OSTYPE) is not recognized)
       exit(1)

5.10  Matching

Pattern matching is performed with the switch and match forms.

    switch <string>
    case <pattern1>
        <clause1>
    case <pattern2>
        <clause2>
    ...
    default
       <default-clause>

The number of cases is arbitrary. The default clause is optional; however, if it is used it should be the last clause in the pattern match.

For switch, the string is compared with the patterns literally.

    switch $(HOST)
    case mymachine
        println(Building on mymachine)
    default
        println(Building on some other machine)

Patterns need not be constant strings. The following function tests for a literal match against pattern1, and a match against pattern2 with ## delimiters.

   Switch2(s, pattern1, pattern2) =
      switch $(s)
      case $(pattern1)
          println(Pattern1)
      case $"##$(pattern2)##"
          println(Pattern2)
      default
          println(Neither pattern matched)

For match the patterns are egrep(1)-style regular expressions. The numeric variables $1, $2, ... can be used to retrieve values that are matched by \(...\) expressions.

    match $(NODENAME)@$(SYSNAME)@$(RELEASE)
    case $"mymachine.*@\(.*\)@\(.*\)"
        println(Compiling on mymachine; sysname $1 and release $2 are ignored)

    case $".*@Linux@.*2\.4\.\(.*\)"
        println(Compiling on a Linux 2.4 system; subrelease is $1)

    default
        eprintln(Machine configuration not implemented)
        exit(1)

5.11  Objects

OMake is an object-oriented language. Generally speaking, an object is a value that contains fields and methods. An object is defined with a . suffix for a variable. For example, the following object might be used to specify a point (1, 5) on the two-dimensional plane.

    Coord. =
        x = 1
        y = 5
        print(message) =
           println($"$(message): the point is ($(x), $(y)")

    # Define X to be 5
    X = $(Coord.x)

    # This prints the string, "Hi: the point is (1, 5)"
    Coord.print(Hi)

The fields x and y represent the coordinates of the point. The method print prints out the position of the point.

5.12  Classes

We can also define classes. For example, suppose we wish to define a generic Point class with some methods to create, move, and print a point. A class is really just an object with a name, defined with the class directive.

    Point. =
        class Point

        # Default values for the fields
        x = 0
        y = 0

        # Create a new point from the coordinates
        new(x, y) =
           this.x = $(x)
           this.y = $(y)
           return $(this)

        # Move the point to the right
        move-right() =
           x = $(add $(x), 1)
           return $(this)

        # Print the point
        print() =
           println($"The point is ($(x), $(y)")

    p1 = $(Point.new 1, 5)
    p2 = $(p1.move-right)

    # Prints "The point is (1, 5)"
    p1.print()

    # Prints "The point is (2, 5)"
    p2.print()

Note that the variable $(this) is used to refer to the current object. Also, classes and objects are functional—the new and move-right methods return new objects. In this example, the object p2 is a different object from p1, which retains the original (1, 5) coordinates.

5.13  Inheritance

Classes and objects support inheritance (including multiple inheritance) with the extends directive. The following definition of Point3D defines a point with x, y, and z fields. The new object inherits all of the methods and fields of the parent classes/objects.

    Z. =
       z = 0

    Point3D. =
       extends $(Point)
       extends $(Z)
       class Point3D

       print() =
          println($"The 3D point is ($(x), $(y), $(z))")

    # The "new" method was not redefined, so this
    # defines a new point (1, 5, 0).
    p = $(Point3D.new 1, 5)

5.14  static.

The static. object is used to specify values that are persistent across runs of OMake. They are frequently used for configuring a project. Configuring a project can be expensive, so the static. object ensure that the configuration is performed just once. In the following (somewhat trivial) example, a static section is used to determine if the LATEX command is available. The $(where latex) function returns the full pathname for latex, or false if the command is not found.

   static. =
      LATEX_ENABLED = false
      print(--- Determining if LaTeX is installed )
      if $(where latex)
          LATEX_ENABLED = true
          export

      if $(LATEX_ENABLED)
         println($'(enabled)')
      else
         println($'(disabled)')

The OMake standard library provides a number of useful functions for programming the static. tests, as described in Chapter 15. Using the standard library, the above can be rewritten as

   open configure/Configure
   static. =
      LATEX_ENABLED = $(CheckProg latex)

As a matter of style, a static. section that is used for configuration should print what it is doing using the ConfMsgChecking and ConfMsgResult functions (of course, most of helper functions in the standard library would do that automatically).

5.14.1  .STATIC

This feature will be introduced in version 0.9.8.5.

There is also a rule form of static section. The syntax can be any of the following three forms.

    # Export all variables defined by the body
    .STATIC:
        <body>

    # Specify file-dependencies
    .STATIC: <dependencies>
        <body>

    # Specify which variables to export, as well as file dependencies
    .STATIC: <vars>: <dependencies>
        <body>

The <vars> are the variable names to be defined, the <dependencies> are file dependencies—the rule is re-evaluated if one of the dependencies is changed. The <vars> and <dependencies> can be omitted; if so, all variables defined in the <body> are exported.

For example, the final example of the previous section can also be implemented as follows.

    open configure/Configure
    .STATIC:
        LATEX_ENABLED = $(CheckProg latex)

The effect is much the same as using static. (instead of .STATIC). However, in most cases .STATIC is preferred, for two reasons.

First, a .STATIC section is lazy, meaning that it is not evaluated until one of its variables is resolved. In this example, if $(LATEX_ENABLED) is never evaluated, the section need never be evaluated either. This is in contrast to the static. section, which always evaluates its body at least once.

A second reason is that a .STATIC section allows for file dependencies, which are useful when the .STATIC section is used for memoization. For example, suppose we wish to create a dictionary from a table that has key-value pairs. By using a .STATIC section, we can perform this computation only when the input file changes (not on every fun of omake). In the following example the function awk 11.11.5 is used to parse the file table-file. When a line is encountered with the form key = value, the key/value pair is added the the TABLE.

    .STATIC: table-file
        TABLE = $(Map)
        awk(table-file)
        case $'^\([[:alnum:]]+\) *= *\(.*\)'
            TABLE = $(TABLE.add $1, $2)
            export

It is appropriate to think of a .STATIC section as a rule that must be recomputed whenever the dependencies of the rule change. The targets of the rule are the variables it exports (in this case, the TABLE variable).

5.15  Constants

Internally, OMake represents values in several forms, which we list here.

  • int
    • Constructor: $(int <i>) 10.4.1.
    • Object: Int 13.1.4.
    • An integer is a represented with finite precision using the OCaml representation (31 bits on a 32 platform, and 63 bits on a 64 bit platform).
    • See also: arithmetic 10.4.3.
  • float
    • Constructor: $(float <x>) 10.4.2.
    • Object: Float 13.1.5.
    • A float is a floating-point value, represented in IEEE 64-bit format.
    • See also: arithmetic 10.4.3.
  • array
    • Constructor: $(array <v1>, ..., <vn>) 10.3.1.
    • Object: Array 13.1.7.
    • An array is a finite list of values. Arrays are also defined with an array definition
          X[] =
              <v1>
              ...
              <vn>
      
    • See also: nth 10.3.5, nth-tl 10.3.8, length 10.3.4, …
  • string
    • Object: String 13.1.8.
    • By default, all constant character sequences represent strings, so the simple way to construct a string is to write it down. Internally, the string may be parsed as several pieces. A string often represents an array of values separated by whitespace.
          osh>S = This is a string
          - : <sequence
             "This" : Sequence
             ' ' : White
             "is" : Sequence
             ' ' : White
             "a" : Sequence
             ' ' : White
             "string" : Sequence>
             : Sequence
          osh>length($S)
          - : 4 : Int
      
    • A data string is a string where whitespace is significant. It represents a single value, not an array. The constructors are the quotations $"..." and $'...'.
          osh>S = $'''This is a string'''
          - : <data "This is a string"> : String
      
    • See also: Quoted strings 8.2.
  • file
    • Constructor: $(file <names>) 11.1.1.
    • Object: File 13.1.13.
    • A file object represents the abstract name for a file. The file object can be viewed as an absolute name; the string representation depends on the current directory.
          osh>name = $(file foo)
          - : /Users/jyh/projects/omake/0.9.8.x/foo : File
          osh>echo $(name)
          foo
          osh>cd ..
          - : /Users/jyh/projects/omake : Dir
          osh>echo $(name)
          0.9.8.x/foo
      
    • See also: vmount 11.6.1.
  • directory
    • Constructor: $(dir <names>) 11.1.1.
    • Object: Dir 13.1.14.
    • A directory object is like a file object, but it represents a directory.
  • map (dictionary)
    • Object: Map 13.1.2.
    • A map/dictionary is a table that maps values to values. The Map object is the empty map. The data structure is persistent, and all operations are pure and functional. The special syntax $|key| can be used for keys that are strings.
          osh>table = $(Map)
          osh>table = $(table.add x, int)
          osh>table. +=
                  $|y| = int
          osh>table.find(y)
          - : "int" : Sequence
      
  • channel
    • Constructor: $(fopen <filename>, <mode>) 11.8.4.
    • Objects: InChannel 13.1.16, OutChannel 13.1.17.
    • Channels are used for buffered input/output.
  • function
    • Constructor: $(fun <params>, <body>) 10.5.1.
    • Object: Fun 13.1.9.
    • Functions can be defined in several ways.
      • As an anonymous function,
            $(fun i, j, $(add $i, $j))
        
      • As a named function,
            f(i, j) =
                add($i, $j)
        
      • This feature will be introduced in version 0.9.9.0.As an anonymous function argument.
            osh>foreach(i => $(add $i, 1), 1 2 3)
            - : <array 2 3 4> : Array
        
  • lexer
    • Object: Lexer 11.11.9.
    • This object represents a lexer.
  • parser
    • Object: Parser 11.11.13.
    • This object represents a parser.
Jump to:  OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long)
Index:  All • Variables • Functions • Objects • Targets • Options
This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml