asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Automating Repetitive Tasks with NMAKE
By Ian Lin
Rating: 3.5 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Prerequisites

    This article assumes some familiarity with Microsoft .NET framework and C#. Microsoft Visual Studio .NET is helpful for examining the code sample.

    Introduction

    One of the lesser known tools in Visual Studio is NMAKE. It is "under the radar" because its capability is not glamorous. In addition, the colossal amalgamation of technology in Microsoft .NET can be a great place for hide-and-seek. Any tool that has not been labeled as "the best thing since sliced bread" can become lost in the crowd.

    "NMAKE.exe" (a.k.a., the Microsoft Program Maintenance Utility) is a 32-bit tool that builds projects based on commands within a description file. This tool has been around for quite some time. If you have experience with Visual C++ or other C/C++ compilers, chances are good that you have spent time with this tool.

    NMAKE is beneficial to developers for storing the build configuration (for source/revision control). To systems administrator and/or DOS command prompt aficionados, this tool could be useful when scriptable actions are based upon timestamps of files.

    I. Overview of NMAKE

    NMAKE utilizes a file (by default, named "makefile") that contains description blocks for creating specific named outputs (called "targets"). Each target may be dependent upon associated files (called "dependents"). For example, if a console application ("MyApp.exe") is created by compiling two source files ("Source1.cs" , "Source2.cs"), then the description block can be defined as follows:

    
        MyApp.exe:  Source1.cs Source2.cs
                    csc /out:MyApp.exe Source1.cs Source2.cs
    
    
    In this description block, "MyApp.exe" is the target, "Source1.cs" and "Source2.cs" are the dependents, and "csc /out:MyApp.exe Source1.cs Source2.cs" is the command. When any dependent file has been changed (based upon the examination of file timestamps; i.e., timestamp of either "Source1.cs" or "Source2.cs" is newer than "MyApp.exe"), NMAKE will re-compile the "MyApp.exe" application based on the command of this description block. Be wary of utilities that can modify the timestamp of dependent files (e.g., "touch.exe"). This can trigger un-expected compilation by NMAKE.

    Please note that each description block may contain multiple commands and/or targets; though the former is more likely. It is also possible for description blocks to contain no dependents. For a review of advanced topics in NMAKE, please refer to the MSDN documentation.

    Alternatively, the above description block is equivalent to the following (using special filename macros, see Resources below):

    
        MyApp.exe:  Source1.cs Source2.cs
                    csc /out:$@ $**
    
    
    Using filename macros can reduce the possibility of typing errors. A side-benefit is that modifications to targets and dependents may require no change to the command. This is highly recommended when you become familiar with the special macros and where the usage is appropriate.

    Conditional logic can be created in a make file by using the appropriate preprocessing directives (such as !IF ... !ELSEIF ... !ELSE ... !ENDIF). The expression in the conditional logic usually involves one or more macros.

    A macro in a make file is similar to a string variable in common programming languages. Since a macro can tunnel from the NMAKE command line to the make file, it is often utilized to select/affect the desired build.

    At this point, you may be wondering about "how to denote the comments?" Good coding guidelines recommend that code be made self-explanatory (i.e., use strategically placed comments within the code). Contents of NMAKE description files (a.k.a. make file) can be annotated. This is accomplished by using the pound sign (#). Annotated content begins with the pound sign (#) followed by the comment texts. The comment symbol of NMAKE remains the same as the counter part in UNIX shell script.

    The description blocks are usually arranged "top-down", with the most generic description block placed first. To allow the default creation of all defined targets, the first description block that contains other targets as its dependents (but without commands) can be introduced. A description block should be added as the last block to allow the clean-up of output/temporary files. It should contain the DOS commands for removing unwanted files. This is a special command block that contains no dependents.

    If you feel dizzy from these terms/concepts, fear not. A picture is worth a thousand words; an illustrated example arrives shortly.

    II. An Example

    A. Motivation

    While learning .NET Remoting (see Resources), I decided to consolidate the practice source code into a small number of files. To reduce typing mistakes, I also wanted to automate the process of compiling/re-compiling the executables. For both efficiency and ergonomics, this is a win-win situation (no pun intended, although most of us work with Windows daily). The prospect of repetitive typing is not appealing, especially to people who do not touch-type. Although the DOS command line recall and history buffer can be helpful, it is cumbersome to retrieve the proper DOS commands because of the issuance of intermediate/disbursed commands (i.e., the number of command history recall is not constant). An automated approach would be a welcome relief to aching wrists.

    Admittedly somewhat complex for a small code base, this NMAKE demonstration will combine C# compilation options, DOS batch file, and DOS batch script commands.

    B. Organization

    The overall solution is assembled with the following parts:

    1. DOS batch file ("makefile.bat") -
      This is the file being invoked from the DOS command prompt. It contains the preliminary validation of parameters and provides "test mode" (displaying the commands without execution). After successful validation, it invokes NMAKE with appropriate parameters.
    2. Macros file ("macros.def") -
      For brevity, macro processing logic is separated into this file to reduce clutters in the main make file. This file performs the bulk of macro parameter validations and is included into the main make file by the NMAKE preprocessing directive (!INCLUDE). Please be aware that NMAKE macros are CASE-SENSITIVE!
    3. Make file ("makefile") - This main make file contains the various description blocks for building the files. NMAKE preprocessing directives allow the selection of description blocks based upon the macros defined in the NMAKE command-line.
    4. Source files - Majority of source files are the dependents of the description block. They include the C# source code, icon, and .NET configuration files. Conditional compilation is used to organize the C# code blocks.

    Although conditional compilation is frowned upon by some, it can be a viable solution where performance is critical and/or resources are scarce (e.g., embedded devices and real-time applications). Clarity can be achieved through proper placement, organization, and simple logic expression. To accomplish this objective, Visual Studio code editor outlining feature is employed by enclosing code blocks using #region ... #endregion.


    Figure 1. Using #region ... #endregion to enclose code blocks.

    C. Data Flow

    The primary interface to the user is the DOS batch file. It passes the appropriate command line parameters to NMAKE. To permit "dry-run" of the make file, "test mode" is provided to display the command sequence only.

    Dry-run is performed by specifying "test" as the first parameter to the DOS batch file ("makefile.bat"). Behind the scenes, this is accomplished by using the "/N" option of NMAKE.


    Figure 2. Content of "makefile.bat"

    All parameters to the DOS batch file (excluding "test") are passed along to NMAKE. These parameters can include both macro definitions and targets. On the DOS command line, a macro definition is specified as "<macro name>=<value>". This distinguishes it from the target.


    Figure 3. A sample "makefile.bat" command line.

    Within the make file, the target and macro definitions determine the desired build configuration. Additionally, the macros are passed along to the C# source file (through C# compiler options). The macros are also required within the C# source code for conditional compilation (see Figure 1).

    For this example, only the definition/existence of the macros is needed. Thus, macros for conditional compilation can be assigned any arbitrary value, including null/empty string. To reduce typing, this is utilized on command line.

    D. Details

    DOS batch file ("makefile.bat") contains the following:

    
        @ECHO OFF
        if "%1" == ""           goto End
        if "%1" == "test"       (if "%2" == "" (goto End) ELSE nmake /n /nologo /f 
    makefile %2 %3 %4 %5 %6 %7 %8 %9)
        if NOT "%1" == "test"   nmake /s /nologo /f makefile %1 %2 %3 %4 %5 %6 %7 %8
    
        :End
        if %ERRORLEVEL% EQU 0   echo Makefile completed.
    
    
    As briefly described above, this file detects "test mode" and passes along the appropriate parameters to NMAKE. Positional parameter (e.g., "%1") is utilized to determine whether dry-run has been specified. The default number of positional parameters is sufficient for this example, due to the limited number of macros required. However, it is possible to extend the number of parameters by utilizing the "SHIFT" keyword.

    Please pay close attention when specifying macros and targets because NMAKE is CASE-SENSITIVE. This can be the source of significant debug time.

    The make file for this example consists of two files: the macros file and the main make file. Macros file ("macros.def") contains the following: (for clarity, this has been abridged)

    
        #*** Consolidate CSC compiler switches ***
        define=""
        !IF DEFINED(CAO) && DEFINED(SOAPsuds) && DEFINED(ConfigFile)
        define=$(defConfigFile) $(defCAO)
        !ELSEIF DEFINED(CAO) && DEFINED(defCAO)
        define=$(defCAO)
        !ELSEIF DEFINED(SAO) && DEFINED(defSAO)
        define=$(defSAO)
        !ELSEIF DEFINED(CAO) || DEFINED(SAO)
        !ERROR Invalid combination of macros.
        !ENDIF
    
    
    As the name suggests, the macros file contains the bulk of the code that processes macros. The conditional logic using the NMAKE preprocessing directives are illustrated here. Regrettably, code indentation is lacking, since the assignment of macro must begin with the first character (leading white spaces are not allowed).

    It is important to stress once more: macros are CASE-SENSITIVE. This is a common source of bugs, and it is not easy to discover visually. The main make file ("makefile") contains the description blocks that define the build configuration. An abbreviated version is listed below:

    
        #*** Obtain macro definitions from external file ***
        !INCLUDE macros.def
    
        !IF DEFINED(CAO) && DEFINED(SOAPsuds)
        #*** Rules: CAO with SOAPsuds scenario ***
        !MESSAGE Buliding CAO SOAPsuds...
    
        all:            Server.exe Meta.dll Client.exe
    
        Server.exe:     AssemblyInfo.cs Network.ico Server.cs
                  	csc /t:exe /win32icon:Network.ico /m:Server.StartUp /out:$@ 
    $(debug) $(define) /nologo 
    AssemblyInfo.cs Server.cs
        !IF DEFINED(ConfigFile)
                        @copy config\Server_CAO_SOAPsuds.config Server.exe.config /v/y
        !ENDIF
    
        Meta.dll:       Server.exe
                        soapsuds -ia:Server -oa:$@ -nowp
    
        Client.exe:     Meta.dll AssemblyInfo.cs Network.ico Client.cs
                        csc /t:exe /r:Meta.dll /win32icon:Network.ico 
    /m:Client.StartUp /out:$@ $(debug) $(define) 
    /nologo AssemblyInfo.cs Client.cs
        !IF DEFINED(ConfigFile)
                        @copy config\Client_CAO_SOAPsuds.config Client.exe.config /v/y
        !ENDIF
    
    
        !ELSE
    
        ...
    
        !ENDIF
    
    
        #*** Rule: Clean-up generated/extraneous files ***
        clean:
                        -@ echo Clean-up...
                        -@ if EXIST Shared.dll          del /f /q Shared.dll
                        -@ if EXIST Shared.pdb          del /f /q Shared.pdb
                        -@ if EXIST Meta.dll            del /f /q Meta.dll
                        -@ if EXIST Client.exe          del /f /q Client.exe
                        -@ if EXIST Client.pdb          del /f /q Client.pdb
                        -@ if EXIST Client.exe.config   del /f /q Client.exe.config
                        -@ if EXIST Server.exe          del /f /q Server.exe
                        -@ if EXIST Server.pdb          del /f /q Server.pdb
                        -@ if EXIST Server.exe.config   del /f /q Server.exe.config
                        -@ if EXIST Server2.exe         del /f /q Server2.exe
                        -@ if EXIST Server2.pdb         del /f /q Server2.pdb
                        -@ if EXIST Server2.exe.config  del /f /q Server2.exe.config
    
    
    
    As mentioned previously, the bulk of macro processing has been separated into a macros file ("macros.def"). To re-assemble the code, NMAKE preprocessing directive (!INCLUDE) is needed to incorporate the contents of the macros file. In this example, macro processing needs to be performed first. Therefore, the inclusion of the macros file must be the first command statement.

    Please note that description blocks are grouped together, using the conditional logic constructed with NMAKE preprocessing directives. Even though there are identical target names, they are not contained within the same bracketed group; therefore, collision is avoided. However, without the separation, the effect of description blocks having identical targets will be cumulative. For additional information, please refer to NMAKE documentation.

    The macros started the journey from the DOS command line to NMAKE and finally arrives at the C# compiler of the command. Macros are consolidated into a macro named "define". Its instantiation is denoted by "$(define)" within the make file.

    A special description block, with the target name of "all", is placed first. It contains no commands. This block enables the default creation of all targets in situations where no targets are specified. It contains all other targets as its dependents, with the exception of itself and the special target named "clean".

    For cleaning up targets and intermediate files, another special description block is introduced as the last block. Its target is intuitively named "clean" and contains no associated dependents. The included commands perform the removal of these files. Please note that each command is prefixed by a command modifier ("-"). This signifies that any error returned by the associated command will be ignored. In the default configuration, NMAKE would terminate processing when it encounters errors in any command execution. Each command contains the DOS batch command for the file removal. Note the at sign (@) following the command modifier is part of DOS. It notifies the DOS command processor to disable the default echoing of commands. To avoid errors, the file removal command ("del ...") would not be executed unless the file exists.

    To further enhance this example to be as self-contained as possible:

    1. Explicitly specify all referenced assemblies, without reliance on default inclusion by the "CSC.RSP" response file.

    2. Include the "vsvars32.bat" to initialize the Visual Studio .NET command prompt environment. This would allow the sample to run from the regular DOS command prompt.

    To illustrate the typical content of the C# source code, the abbreviated listing of "Server.cs" is presented here:

    
        using ...
    
        namespace Server
        {
        ...
        #region Class_CAO_SOAPsuds
        #if CAO && SOAPsuds
            // Client-Activated-Object (CAO)
            public class CAOobject: MarshalByRefObject, IDisposable
            {
                ...
            }
        #endif
        #endregion
    
        class StartUp
        {
            /// <summary>The main entry point for the 
    application.</summary>
            static void Main(string[] args)
            {
        #region Remote_Channel
        #if ConfigFile
                    string configfile = 
    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
                    RemotingConfiguration.Configure(configfile);
        #else
                    HttpChannel chnl = new HttpChannel(1234);
                    ChannelServices.RegisterChannel(chnl);
        #endif
        #endregion
    
                    ...
    
        #region RemoteRegister_CAO_SOAPsuds
        #if CAO && SOAPsuds
            #if !ConfigFile
                    //*************************************************
                    // CAO
                    //*************************************************
                    // URL = 
    http://<hostname>:<port>/<ApplicationName>
                    RemotingConfiguration.ApplicationName = "ServerCAO";
                    RemotingConfiguration.RegisterActivatedServiceType(typeof(CAOobject));
            #endif
        #endif
        #endregion
    
                    Console.WriteLine("Server.Main(): server started");
    
                    Console.ReadLine();
                }
            }
        } 
    
    
    The C# source code utilizes the macros from the command line, in the form of compilation symbols. These symbols are derived from the macros by the make file and are necessary for conditional compilation of the C# source code.

    As stated previously, the clarity of conditional compilation is dependent upon the strategic placement. With the assistance of Visual Studio .NET's code inlining feature (the #region ... #endregion construct), each code block is enclosed within an annotated/collapsible code region.

    Exploration of .NET Remoting is beyond the scope of this article. For additional information, please consult information listed in the Resources/Links section.

    E. Usage

    With all said and done, the final objective is simplicity and lightweight. Sample applications are listed here to illustrate the ease of use. Please note that the commands must be executed from Visual Studio .NET's command prompt and within the directory where the source files are located.

    To compile executable files for SAO/Singleton scenario:

    C:\> makefile "SAO=" "Singleton="

    The above is equivalent to the following:

    C:\> makefile "SAO=" "Singleton=" all

    To remove generated files, use the following command:

    C:\> makefile clean

    To review the commands without execution ("test mode"):

    C:\> makefile test clean

    Hopefully, these short-and-sweet commands will satisfy most critics.

    III. Conclusion

    NMAKE is a handy utility. It can rescue aching wrists by automating dull and repetitive tasks. Hopefully some portions of this article are helpful in reducing compilation drudgery.

    
    make "practice=" perfect
    
    

    IV. Disclaimers

    The sample code associated with this article has been developed with Microsoft Windows 2000 Server and Visual Studio .NET Enterprise Architect. It is provided AS IS. The author and publisher make no guarantee about its usability and will disavow any knowledge of your actions. Please carefully review all code samples before use. (i.e., use at your own risk!)

    About the Author

    Ian Lin has been developing software solutions utilizing various products from Microsoft and others. Not to be out-done by peers, he holds professional certifications and memberships. He believes that software planning is sufficiently challenging, without having territory dispute to hinder quality (of work and life). Off-beat humor is occasionally dispensed by him at idotnet2@yahoo.com.

    Resources/Links

    1) "Manage Cross-Platform Projects with the make Utility" by Hasmet Akgun
    http://www.devx.com/cplus/Article/9766

    2) "GNU Make: A Program for Directing Recompilation" by Richard M. Stallman and Roland McGrath
    http://www.gnu.org/manual/make-3.79.1/html_chapter/make_toc.html

    3) NMAKE reference in MSDN
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_asug_Overview.3a_.NMAKE_Reference.asp

    4) DOS batch command reference ("help" at DOS command prompt)

    5) C# compiler help ("csc /?" at Visual Studio .NET command prompt)

    6) C# Language reference - conditional compilation

    7) Duwamish7 C# - Visual Studio .NET Enterprise sample (see "Duwamish.mak")

    8) "Advanced .NET Remoting" by Ingo Rammer. Published by APress, ISBN 1-59059-025-2
    http://www.dotnetremoting.cc/

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info

    Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers