Bentley Communities
Bentley Communities
  • Site
  • User
  • Site
  • Search
  • User
MicroStation Programming
  • Product Communities
  • Developers and Programming
  • MicroStation Programming
  • Cancel
MicroStation Programming
MicroStation Programming - Wiki Creating a Makefile and Using the bmake Utility
    • Sign In
    • MicroStation Programming - Wiki
    • -MicroStation CONNECT Edition
      • +MicroStation SDK
      • -MicroStation MDL
        • MicroStation CONNECT SDK - Introduction
        • +MDL FAQ
        • -MDL Topics
          • +CONNECT APIs
          • MDL Constraints
          • -MDL Make Files
            • Creating a Makefile and Using the bmake Utility
          • +MDL Tasks
          • +MDL Transforms
      • +MicroStation VBA
      • +Training
      • +Code Examples and Utilities
      • +Code Snippets
    • +MicroStation V8

     
     Questions about this article, topic, or product? Click here. 

    Creating a Makefile and Using the bmake Utility

    NOTES
    1. The content below is available in the MicroStation SDK Help (MicroStationAPI.chm) under the Topic of the same name.
    2. The content is provided in the context of MicroStation V8i and prior (multi-platform) releases and is pending updates specific to MicroStation CONNECT.
    3. The MicroStation/MSPP SDK makefile implementation closely parallels GNU make. For additional information, see also: Introduction to MakeFiles     
    Creating a Makefile and Using the bmake Utility

    The MDL development environment includes a utility called bmake that produces executable images from makefiles. These makefiles contain the commands and directives needed to build your application. bmake was developed by and for Bentley Systems specifically for MicroStation development.

    In programming environments such as MDL, changes to one file often causes other files to become obsolete. bmake recognizes when files have changed and detects and recreates any obsolete files based on the relationships between the file creation times.

    For example, suppose you make a change to one of your MDL source files. For your change to be reflected in the application's executable, you must first run the MDL compiler on your source (.mc) file to generate or make its object (.mo) file. Next, you must run the MDL linker on your object file(s) to create the application (.ma) file.

    In your makefile, you would first list your object file as a target file and tell bmake that it depends on the source file. When the target file is older than any of its dependents, the build commands that recreate the target (in this case, the MDL compiler) are executed. Next you list your application file as a target file that depends on the object file. bmake always processes makefiles linearly from beginning to end, so that both the object and the application files are rebuilt (assuming there are no errors).

    Using file extensions to differentiate types of files is good programming practice. bmake exploits this by allowing you to define rules that describe how to create a file with one extension from a file of another extension. After you define these rules, you do not have to explicitly tell bmake how to recreate every target file. It infers the build commands from the file extension of the target and its dependencies and your rules.

    In many instances, you will collect your rules and definitions into a single make include (.mki) file like the MicroStation mdl.mki file which contains the default macros and build rules for MicroStation MDL applications. This file is then included by your makefile to provide the needed macro and rule definitions.

    You should use bmake instead of similar programs you may have used in other development environments for the following reasons:

    • bmake allows include files for defining items common to many makefiles.
    • bmake supports conditional compilation in makefiles (as in ifdef, ifndef, elif, else, endif).
    • bmake allows the inclusion of other makefiles and make include files containing default rules and macros.
    • bmake allows different rules that apply to files in different directories.
    • bmake allows multiple targets for the same dependency list.
    • bmake does not have the typical memory limitations of other make programs.
    • bmake can create link files from the dependency list in a makefile, making it easier to add new source files.
    • bmake lets you define constants on the command line.
    • bmake is available on every platform that MDL is available on.
    • It would be possible to use bmake for development with other products if a new set of rules were created. Be aware, however, that one of bmake's greatest strengths is its platform independence, so any advantage of using it would be lost if the rules were not designed with this in mind.
    Makefile format

    The makefile format is similar to the format for input files to other make programs. Each makefile must contain a series of records, each of which must be one of the following types:

    • Macro
    • Rule
    • Dependency
    • Conditional
    • Rules, conditionals and dependencies must be separated by blank lines.

    A `\' (backslash) as the last character on a line signals that the line continues. (To end a line with the backslash character, use two consecutive backslashes). No spaces can follow the backslash if it is used to signal the line continues.

    The `#' character denotes a comment. bmake ignores the remainder of the line following this character. Comment lines cannot be continued over multiple lines.

    Each makefile record type is described below.

    Example

    A listing of the makefile for basic.ma can be found in "A Complete Example".

    Macros

    There are three locations where a bmake macro may be defined:

    1. In the body of a makefile (or in BMAKE_OPT).
    2. As an environment variable, outside of bmake.
    3. On the command line using the -d command line option.

    bmake looks for definitions in the above order, so macros defined in makefiles take precedence over those defined elsewhere.

    Macros defined in a makefile are not case sensitive.

    To define a macro in the body of a makefile, use one of the following formats:

    format
    description
    macro = definition
    Standard assignment. This is the most commonly used method.
    macro =% definition
    Standard but value being assigned is expanded before assignment.
    macro + definition
    Definition is appended to existing macro value.
    macro +% definition
    Definition is appended to existing macro value, and is expanded before being appended.

    bmake performs macro substitution when it reads $_macro_ in the input makefile, where the `_' characters signify the type of expansion as shown in the table below.

    macro
    description
    $(name)
    Expand by iterative substitution.
    ${name}
    Expand by iterative substitution removing the last character if it is a path separator character.
    $[name]
    Expand by value; expand the macro to its literal string value without doing any further substitution.

    These macros are reserved and expand as follows:

    macro
    expansion
    $@
    the current target file
    $?
    all dependency files that are newer than the target file
    $=
    the newest dependency file
    $<
    the current dependency file
    $*
    the base filename of the target file
    $%
    the directory of the first dependency

    bmake also predefines macros for each operating system platform. The list of macros can be obtained by typing:

    bmake -p 
    
    Example

    This is a simple macro from basic.mke:

    %if defined (_MakeFilePath) 
    baseDir = $(_MakeFilePath) 
    %else 
    baseDir = $(MS)/mdl/examples/basic/ 
    %endif 

    
    
    Advanced Example

    Suppose you have some modules that you want to compile with the default .c.o rule, except you want -I$(appDir) included on the compiler command line of the PC. Here is one way to accomplish this:

    %if pm386 
       savecopt =% $[copt] 
       copt + -I${appDir} 
    %endif 
       myfile1$(oext) : myfile1.c 
       myfile2$(oext) : myfile2.c 
    %if pm386 
       copt =% $[savecopt] 
    %endif

    
    
    Rules

    A rule in a makefile defines how to build targets with a given extension from a dependency with a given extension. The general format of a rule is as follows:

    .[dir1;dir2;...;dirN]depExt.targetExt: 
       buildCommands

    
    

    If the directories section [dir1;dir2;...] is missing, the rule applies to files in any directory. If a target file depends on more than one file, the extension of the first one in the list is used to find the rule. If more than one rule applies to a target, the last rule in the makefile has precedence. For this reason, you should put rules that are directory-specific after any non-directory-specific rules in the makefile.

    Example (from mdl.mki)
    .mt.r: 
         $(msg) 
         > $(o)temp.cmd 
         -o$@ 
    %if privateInc 
         -i$(privateInc) 
    %endif 
         $(rscCompIncs) 
         $(altIncs) 
         -i$(publishInc)  
         -i$(publishIdsInc) 
         -i$(stdlibInc) 
         $%$*.mt$(RTypeCmd)   @$(o)temp.cmd 
         <
         $(RTypeCmd) 	@$(o)temp.cmd 
         ~time
    
    
    Dependencies

    A dependency in a makefile specifies that one or more target files depends on one or more dependent files. The general format of a dependency is as follows:

    target1 target2 ... targetN: dep1 dep2 ... depN 
       buildCommands
     

    If any dependent files are newer than any target files, the build commands are executed.

    Dependencies are always processed in their makefile order, so you should organize your makefiles accordingly. (That is, if one target depends on another target, its dependency should appear later in the makefile.)

    Build commands are a sequence of lines, executed one after the other, to build the target file. If a build command returns an error, bmake terminates. If there are no build commands, bmake attempts to use a predefined rule.

    Example
    $(o)basic.mo : $(baseDir)basic.mc $(baseDir)basic.h 

    (where (o) has been defined as the object directory.)

    The first character in a build command can have special significance:

    character
    meaning
    -
    Ignore the status returned from this command. Do not terminate if the command returns an error.
    @
    Do not echo, even when bmake is not operating in silent mode.
    |
    Echo only this line, even in silent mode. This is useful for informational messages to the user.
    ~
    Use a built-in bmake command (see below).
    >filename
    Write all lines in the makefile until a < to the specified file. This is typically used to write the dependencies to a file to generate a link file.

    The following are built-in bmake commands:

    command
    description
    ~CURRENT
    Print the current time.
    ~TIME
    Set the target file's modification date to the date of the newest dependency file. This should generally be the last build command for a target file.

    Conditional

    A conditional includes or excludes sections of the makefile, depending on whether macros are present. The following types of conditionals are supported:

    directive
    description
    %ifdef
    If the macro exists, include subsequent lines.
    %if
    If the expression yields a non-zero result, include the subsequent lines. The conditional %if defined is functionally equivalent to %ifdef.
    %ifndef
    If the macro does not exist, include subsequent lines.
    %else
    Use with %ifdef and %ifndef.
    %elif
    Use with %ifdef and %ifndef.
    %endif
    Close an ifdef or if block.
    %undef
    Undefines a macro.
    %iffile
    If the named file exists, include subsequent lines.
    %ifnofile
    If the named file does not exist, include subsequent lines.

    The always: directive precedes one or more commands that are executed regardless of condition.

    Example
    %if defined (_MakeFilePath) 
    Expressions

    Expressions can be written using the operators in the table below to act on constant values, exit codes from commands, strings, chars, macros, and file-system paths.

    operator
    associative
    integer constant
    string or char constant
    +
    binary, unary
    addition
    string or char concatenation
    -
    binary, unary
    subtraction
     
    *
    binary
    multiplication
     
    /
    binary
    division
     
    %
    binary
    modulus
     
    &
    binary
    bitwise AND
     
    |
    binary
    bitwise OR
     
    ^
    binary
    bitwise XOR
     
    &&
    binary
    logical AND
     
    ||
    binary
    logical OR
     
    <<
    binary
    left shift
     
    >>
    binary
    right shift
     
    ==
    binary
    equality
    same string or char
    !=
    binary
    inequality
    different string or char
    <
    binary
    less than
     
    >
    binary
    greater than
     
    <=
    binary
    less than or equal to
     
    >=
    binary
    greater than or equal to
     
    Example

    An example of using expressions is shown below.

    PERSON = "This person is " 
    %if PERSON + "Ben Franklin" == "This person is Ben Franklin" 
       do something 
    %elif PERSON + "Winston Churchill" == "This person is Winston Churchill" 
       do something else 
    %endif
    Include Files

    As mentioned earlier, bmake allows you to include other files (.mki files) into the current makefile using the %include preprocessor directive.

    The files included must be written using the same syntax as bmake makefiles. That is they must be other makefiles or files containing default macro and rule definitions. Examples of this can be found in all distributed MDL example applications.

    Example
    %include mdl.mki 
    ... 
    %include $(baseDir)basicrsc.mki 

    As shown above, basic.mke includes three additional files to assist in, and complete, the application build process:

    file
    description
    mdl.mki
    This is the main MicroStation MDL make include file. It contains the default rules and macro definitions that should always be used when building MDL applications, Dynamic Link Libraries (DLL), and external programs.
    basicrsc.mki
    This include file is actually another makefile which contains the steps necessary to complete the building of the basic application. The steps defined in this file cause the language-specific portions of the application to be compiled and combined with the generic portions to create the final application (.ma) file. It is recommended that the language-specific portions of an application be separated from the rest of the application if the application needs to be delivered in different languages. This makefile is listed in the "Complete Example" chapter with basic.mke.
    Starting the bmake utility

    After installation, the bmake utility is in the $(MS)\mdl\bin directory.

    - Many of the MDL tools, including bmake, require that the MS environment variable be set so that they can find the MicroStation home directory. If you performed a standard installation of the MicroStation SDK, the definition of MS is done when the MicroStation Developer Shell is started:

    The command line syntax for starting the bmake utility is as follows:

    BMAKE [OPTIONS] MAKEFILE

    - If a makefile name is not specified on the command line, the bmake utility will look for a .mke file with the same rootname as the current directory. For example, in the directory for the myapp example, entering bmake without the name of the makefile would build myapp.mke.

    Normally, [options] are not specified on the command line, but in the BMAKE_OPT environment variable so that the user does not have to specify them every time bmake is used. The MicroStation Developer Shell defines BMAKE_OPT.

    bmake supports multiple include paths. They may be specified via either the command line or the environment variable BMAKE_OPT.

    The standard BMAKE_OPT definitions are as follows:

         set BMAKE_OPT=-I$(MS)\mdl\include     
    

    Therefore, the command to make basic.ma without BMAKE_OPT set would be:

              BMAKE -I$(MS)\MDL\INCLUDE BASIC

    But with BMAKE_OPT set, which is the default, it would be shortened to:

              BMAKE BASIC

    Command line options are as follows:

    option
    description
    -a
    Always build. For every dependency, execute the build command regardless of whether dependent files are newer than the target files. This option causes bmake to remake all files.
    -d
    Define a macro. For example, including -ddebug=1 on the command line is the same as specifying debug=1 in the makefile.
    -f[ext]
    Filter targets (only build files with [ext] file extensions.
    -i
    Ignore errors. Ordinarily, bmake terminates when a build command returns a non-zero exit status. With this option, bmake ignores errors and continues.
    -l
    List targets. bmake prints the target file before executing build commands (helpful for debugging).
    -m
    Missing files are acceptable. Ordinarily, bmake terminates if any dependency files are missing. With this option, bmake continues.
    -n
    No execution. List, but do not execute the build commands.
    -p
    Print macros as defined (helpful for debugging makefiles).
    -q
    Quiet mode (no salutations).
    -s
    Enter silent mode. Do not print build commands as they are executed.
    -t
    Touch target files. Set the target file date to the date of the latest dependency file rather than executing the build commands.
    -v
    Print macros in verbose mode.
    -x[exe]
    Run [exe] on error.
    -C
    Print conditional level.
    -I
    Specify the include (*.mki) file path.
    -L
    Log file path
    -P
    Always process this makefile.
    -X
    Generate _MakeFilePath using DOS 8.3 Format
    -aD
    Delete target files. When specified on the command line, bmake will define a macro BMAKE_DELETE_ALL_TARGETS.
    - The - (dash) character in front of a command line option specifies that option for the current make file only. If the make file you specify on the command line calls other make files, the command line options are not passed on. Use the + (plus) character in place of the - (dash) to pass the command line option on to subsequent make files.
    Popup
    • bmake
    • make files
    • Share
    • History
    • More
    • Cancel
    • Robert Hook Created by Bentley Colleague Robert Hook
    • When: Tue, Sep 15 2020 10:49 AM
    • Robert Hook Last revision by Bentley Colleague Robert Hook
    • When: Mon, Feb 28 2022 2:53 PM
    • Revisions: 3
    • Comments: 0
    Recommended
    Related
    Communities
    • Home
    • Getting Started
    • Community Central
    • Products
    • Support
    • Secure File Upload
    • Feedback
    Support and Services
    • Home
    • Product Support
    • Downloads
    • Subscription Services Portal
    Training and Learning
    • Home
    • About Bentley Institute
    • My Learning History
    • Reference Books
    Social Media
    •    LinkedIn
    •    Facebook
    •    Twitter
    •    YouTube
    •    RSS Feed
    •    Email

    © 2023 Bentley Systems, Incorporated  |  Contact Us  |  Privacy |  Terms of Use  |  Cookies