Skip to content

Code Generation

Steve Ives edited this page Apr 1, 2020 · 28 revisions

Harmony Core Logo

Code Generation

Almost all Harmony Core development environments rely heavily on the automatic generation of source code by CodeGen.

What is CodeGen

CodeGen is a tool that software developers working in a Synergy Development Environment can use to generate source code. That code might be Synergy DBL code, or might be source code for some other language. CodeGen is not restricted to producing code for any particular development environment or programming language.

Of course, you can’t just use CodeGen to generate any piece of source code that you can imagine. Before code can be generated a developer must declare the rules that define how a given piece of source code should be created. These rules are defined in template files. CodeGen interprets the code and instructions that a developer has defined in a template file in order to produce useful output.

Defining rules in a template file is only part of the story though. To be really useful CodeGen also needs to have another source of information that defines a context for what is to be generated.

Most software applications revolve around the collection, presentation, manipulation, and storage of data. That data is ultimately stored in some type of persistent storage such as in a collection of data files, or in tables in a relational database. When developers work on a particular piece of source code they combine the knowledge that they have about an application's data (metadata) with rules or "business logic", in order to create source code that addresses a particular requirement. CodeGen does the same thing.

When developers work in Synergy/DE they have access to an excellent source of metadata that is called the Synergy/DE Repository. A repository holds extensive information about the data structures used by an application, and the attributes of those data structures. A repository can also contain information about the relationships between the various data structures, and even about underlying data storage mechanisms. A Synergy repository is a very rich source of metadata and is the primary source of metadata used by CodeGen.

It’s all about metadata! CodeGen takes information about a data structure and combines it with rules that have been defined in a template file in order to create useful code.

You don't need to be a CodeGen expert to be successful with Harmony Core development, but if you want to learn more then you should refer to the CodeGen documentation. If you have CodeGen installed on your Windows development system then you can access the local documentation via the command:

codegen -docs

Or you can view the documentation for the latest version of CodeGen on-line.

In the remainder of this document, we'll try to give you a very basic understanding of how CodeGen works, so that you understand what is happening when you generate code.

Repository Structures

Synergy applications define their primary data structures via record definitions. For example, a record definition for a simple CUSTOMER record might look something like this:

RECORD CUSTOMER             ; Customer record
  CUSTOMER_NUMBER    ,D6    ; Customer number                                   
  NAME               ,A30   ; Customer name                                     
  STREET             ,A25   ; Street address                                    
  CITY               ,A20   ; City                                              
  STATE              ,A2    ; State                                             
  ZIP_CODE           ,D9    ; Zip code                                          
  CONTACT            ,A25   ; Contact name                                      
  PHONE              ,D10   ; Phone number                                      
  FAX                ,D10   ; Fax number                                        
  FAVORITE_ITEM      ,D6    ; Customers favorite item                           
  PAYMENT_TERMS_CODE ,A2    ; Payment terms code                                
  TAX_ID             ,D9    ; Customers tax ID number                           
  CREDIT_LIMIT       ,D7.2  ; Credit limit                                      
ENDRECORD

The record definition defines an overall name and description for the record and goes on to define the name, data type and size of each field within the record. Comments are also used to provide a description of each field.

Think of a repository STRUCTURE as a glorified record definition, it includes all of the same information as a record definition, and more. For example:

Structure CUSTOMER    DBL ISAM
   Description "Customer record"
 
Field CUSTOMER_NUMBER   Type DECIMAL   Size 6
   Description "Customer number"
   Long Description
      "SAMPLE_DATA=355232;"
   Report Just LEFT   Input Just LEFT
   Required
 
Field NAME   Type ALPHA   Size 30
   Description "Customer name"
   Long Description
      "SAMPLE_DATA=Abe's Nursery;"
   Required
 
Field STREET   Type ALPHA   Size 25
   Description "Street address"
   Long Description
      "SAMPLE_DATA=1032 Main Street;"
   Required
 
Field CITY   Type ALPHA   Size 20
   Description "City"
   Long Description
      "SAMPLE_DATA=Springfield;"
   Required
 
Field STATE   Type ALPHA   Size 2
   Description "State"
   Long Description
      "SAMPLE_DATA=MO;"
   Required
 
Field ZIP_CODE   Type DECIMAL   Size 9
   Description "Zip code"
   Long Description
      "SAMPLE_DATA=64127;"
   Report Just LEFT   Input Just LEFT
   Required
 
Field CONTACT   Type ALPHA   Size 25
   Description "Contact name"
   Long Description
      "SAMPLE_DATA=Abe Albright;"
 
Field PHONE   Template PHONE
   Description "Phone number"
   Long Description
      "SAMPLE_DATA=(555) 123-4567;"
   Required
 
Field FAX   Template PHONE
   Description "Fax number"
   Long Description
      "SAMPLE_DATA=(555) 987-6543;"
 
Field FAVORITE_ITEM   Type DECIMAL   Size 6
   Description "Customers favorite item"
   Long Description
      "SAMPLE_DATA=7;"
   Report Just LEFT   Input Just LEFT
   Required
 
Field PAYMENT_TERMS_CODE   Type ALPHA   Size 2
   Description "Payment terms code"
   Long Description
      "SAMPLE_DATA=30;"
   Required
   Selection List 0 0 0  Entries "CA", "30", "60", "90"
 
Field TAX_ID   Type DECIMAL   Size 9
   Description "Customers tax ID number"
   Long Description
      "SAMPLE_DATA=546874521;"
      "HARMONY_ROLES=Manager;"
 
Field CREDIT_LIMIT   Type DECIMAL   Size 7   Precision 2
   Description "Credit limit"
   Long Description
      "SAMPLE_DATA=5000;"
   Required
 
Key CUSTOMER_NUMBER   ACCESS   Order ASCENDING   Dups NO
   Segment FIELD   CUSTOMER_NUMBER  SegType DECIMAL
 
Key STATE   ACCESS   Order ASCENDING   Dups YES   Insert END   Modifiable YES
   Krf 001
   Description "State"
   Segment FIELD   STATE  SegType ALPHA  SegOrder ASCENDING
 
Key ZIP   ACCESS   Order ASCENDING   Dups YES   Insert END   Modifiable YES
   Krf 002
   Description "Zip code"
   Segment FIELD   ZIP_CODE  SegType DECIMAL  SegOrder ASCENDING
 
Key PAYMENT_TERMS   ACCESS   Order ASCENDING   Dups YES   Insert END
   Modifiable YES   Krf 003
   Description "Payment terms code"
   Segment FIELD   PAYMENT_TERMS_CODE  SegType ALPHA  SegOrder ASCENDING
 
Key FAVORITE_ITEM   FOREIGN
   Segment FIELD   FAVORITE_ITEM
 
Relation  2   CUSTOMERS CUSTOMER_NUMBER   ORDERS CUSTOMER_NUMBER
 
Relation  3   CUSTOMERS FAVORITE_ITEM   ITEMS ITEM_NUMBER

Template Files

Template files are text files that define some piece of generic source code. They contain a combination of actual source code and tokens that have special meaning to CodeGen. Template files have a .tpl file extension. When CodeGen processes a template file, any tokens that are encountered in the template cause it to take some action. Some tokens cause some piece of information obtained from the metadata source to be written to the output file, while other tokens cause CodeGen to perform some specific action.

Most templates are written to create output based on a source of metadata, which is usually a repository structure. Most of the useful metadata is found in repository STRUCTURE and FIELD definitions, but some tokens require you to define KEYS, TAGS, RELATIONS and FILES in the repository also. For more information, refer to Repository Setup.

If a programmer wanted to be able to generate code for Synergy external subroutines which would accept the value of the primary key for a record and then read and return the record, they might write a template like this:

<CODEGEN_FILENAME>Get<StructureName>.dbl</CODEGEN_FILENAME>
;//<CATEGORY>Synergy Subroutines</CATEGORY>
;//<DESCRIPTION>Creates a subroutine that returns a record from a file</DESCRIPTION>
;//--------------------------------------------------------
;// Template author: Jodah Veloper
;// Revision:        1.0
;// Date:            4/1/2020
;//--------------------------------------------------------
; Description: Returns a <STRUCTURE_NAME> record
; Author:      <AUTHOR>
; Created:     <DATE> at <TIME>

.include "<STRUCTURE_NAME>" repository, structure="str<StructureName>", end

subroutine Get<StructureName>
    <PRIMARY_KEY>
    <SEGMENT_LOOP>
    required in  a<SegmentName>, <segment_spec>
    </SEGMENT_LOOP>
    </PRIMARY_KEY>
    required out a<StructureName>, str<StructureName>

    stack record
        ch<StructureName>, int
    endrecord
proc
    <PRIMARY_KEY>
    <SEGMENT_LOOP>
    a<StructureName>.<segment_name> = a<SegmentName>
    </SEGMENT_LOOP>
    </PRIMARY_KEY>

    try
    begin
        open(ch<StructureName>=0,i:i,"<FILE_NAME>")
        read(ch<StructureName>,a<StructureName>,%keyval(ch<StructureName>,a<StructureName>,0))
    end
    catch (ex)
    begin
        clear a<StructureName>
    end
    finally
    begin
        if (ch<StructureName>&&%chopen(ch<StructureName>))
            close ch<StructureName>
    end
    endtry

    xreturn

endsubroutine

Having created the template, the developer could then process the template in conjunction with the repository structure DEPARTMENT, and CodeGen would create an output file like the one below. Notice that the template file includes a <CODEGEN_FILENAME> rule, so in this case, the name of the output file would be GetDepartment.dbl. The file would contain:

; Description: Returns a DEPARTMENT record
; Author:      Jodah Veloper
; Created:     11/28/2014 at 12:00

.include "DEPARTMENT" repository, structure="strDepartment", end

subroutine GetDepartment
    required in  aDeptId, a10
    required out aDepartment, strDepartment

    stack record
        chDepartment, int
    endrecord
proc
    aDepartment.dept_id = aDeptId

    try
    begin
        open(chDepartment=0,i:i,"DAT:department.ism")
        read(chDepartment,aDepartment,%keyval(chDepartment,aDepartment,0))
    end
    catch (ex)
    begin
        clear aDepartment
    end
    finally
    begin
        if (chDepartment&&%chopen(chDepartment))
            close chDepartment
    end
    endtry

    xreturn

endsubroutine

Code Generation in Harmony Core Environments

The harmonycore and harmonydemo solution templates provide two files that can be used to drive the required code generation. These files are both found in the Solution Items folder in the Visual Studio solution, and are named:

  • regen.bat
  • UserDefinedTokens.tkn

The solution templates also provide various CodeGen Template Files, each of which defines the basic content for each type of source file that can be used to build up a Harmony Core solution.

Code Generation Batch File (regen.bat)

You will find this batch file in the main solution folder, and also you can access it in Visual Studio via the Solution Items folder in Solution Explorer. The file is pre-configured with commands that will perform the code generation required to build out a typical Harmony Core REST APIs, based on various options that are selectable within the file.

Towards the top of the file you will see a section of code that defines several environment variables, like this:

rem ================================================================================================================================
rem Specify the names of the projects to generate code into:

set ServicesProject=Services
set ModelsProject=Services.Models
set ControllersProject=Services.Controllers
set HostProject=Services.Host
set TestProject=Services.Test

These environment variables are used to define the folders that various types of code will be generated into. The values are pre-configured to match the project folders that were provided by the project template.

Just below you will see a section of code that looks like this:

rem ==================================================================================
rem Specify the names of the repository structures to generate code from:

set DATA_STRUCTURES=
set FILE_STRUCTURES=%DATA_STRUCTURES%

rem DATA_STRUCTURES Is a list all of structures that you wish to generate models and
rem                 controllers for. In other words it declares all of the "entities"
rem                 that are being represented and exposed by the environment.
rem
rem FILE_STRUCTURES If you don't have multi-record format files then this should be the
rem                 same as DATA_STRUCTURES. But if you do then FILE_STRUCTURES should
rem                 only list ONE of the structures assigned to each file, so this list
rem                 will be a subset of DATA_STRUCTURES.

The DATA_STRUCTURES environment variable is used to define which repository structures will be processed by CodeGen, and hence which source files will be produced. Simply list the named of those structures, separated by spaces, like this:

set DATA_STRUCTURES=CUSTOMERS ITEMS ORDERS ORDER_ITEMS VENDORS

In the sample environment we have implemented a mechanism that enables appropriate primary key values to be generated when new records are added via POST operations. This basically involves a “system parameters” relative file that defines data such as “next customer number” and “next order number”.

Further down in the file you will find a block of code that looks like this:

rem ================================================================================
rem Comment or uncomment the following lines to enable or disable optional features:

rem set ENABLE_GET_ALL=-define ENABLE_GET_ALL
rem set ENABLE_GET_ONE=-define ENABLE_GET_ONE
rem set ENABLE_SELF_HOST_GENERATION=YES
rem set ENABLE_CREATE_TEST_FILES=-define ENABLE_CREATE_TEST_FILES
rem set ENABLE_SWAGGER_DOCS=-define ENABLE_SWAGGER_DOCS
rem set ENABLE_POSTMAN_TESTS=YES
rem set ENABLE_ALTERNATE_KEYS=-define ENABLE_ALTERNATE_KEYS
rem set ENABLE_COUNT=-define ENABLE_COUNT
rem set ENABLE_PROPERTY_ENDPOINTS=-define ENABLE_PROPERTY_ENDPOINTS
rem set ENABLE_PROPERTY_VALUE_DOCS=-define ENABLE_PROPERTY_VALUE_DOCS
rem set ENABLE_SELECT=-define ENABLE_SELECT
rem set ENABLE_FILTER=-define ENABLE_FILTER
rem set ENABLE_ORDERBY=-define ENABLE_ORDERBY
rem set ENABLE_TOP=-define ENABLE_TOP
rem set ENABLE_SKIP=-define ENABLE_SKIP
rem set ENABLE_RELATIONS=-define ENABLE_RELATIONS
rem set ENABLE_PUT=-define ENABLE_PUT
rem set ENABLE_POST=-define ENABLE_POST
rem set ENABLE_PATCH=-define ENABLE_PATCH
rem set ENABLE_DELETE=-define ENABLE_DELETE
rem set ENABLE_SPROC=-define ENABLE_SPROC
rem set ENABLE_AUTHENTICATION=-define ENABLE_AUTHENTICATION
rem set ENABLE_FIELD_SECURITY=-define ENABLE_FIELD_SECURITY
rem set ENABLE_UNIT_TEST_GENERATION=YES
rem set ENABLE_CASE_SENSITIVE_URL=-define ENABLE_CASE_SENSITIVE_URL
rem set ENABLE_CORS=-define ENABLE_CORS
rem set ENABLE_IIS_SUPPORT=-define ENABLE_IIS_SUPPORT
rem set ENABLE_OVERLAYS=-f o

Each of these represents an option to enable a certain piece of functionality in your Harmony Core environment. Some of these options cause addiditional files to be generated, while others affect what code is generated in some of the source files.

As you can see all of the options are commented out by default, so they are not active. You can remove the rem statement before an option in order to enable the option.

All of these options and exactly what each option is used for is discussed throughout the various pages of this documentation.

User Defined Tokens File

UserDefinedTokens.tkn is a CodeGen user-defined tokens file that is used to define values for several things. Thes values are inserted into various places in various generated source files.

The fact that the values are provided via user defined tokens gives you the opportunity to change the values to whatever you need to in order to build the REST API the way you want it.

The user defined tokens file looks like this:

;
; User defined tokens for the Harmony Core sample environment
;
<SERVICES_NAMESPACE>Services</SERVICES_NAMESPACE>
<MODELS_NAMESPACE>Services.Models</MODELS_NAMESPACE>
<CLIENT_MODELS_NAMESPACE>Services.Test.Models</CLIENT_MODELS_NAMESPACE>
<UNIT_TESTS_NAMESPACE>Services.Test.UnitTests</UNIT_TESTS_NAMESPACE>
;
<DATA_FOLDER>SampleData</DATA_FOLDER>
;
<API_DOCS_PATH>api-docs</API_DOCS_PATH>
<API_TITLE>Harmony Core Sample API</API_TITLE>
<API_VERSION>1.0.0</API_VERSION>
<API_DESCRIPTION>This environment presents an example of using Harmony Core to expose a collection of RESTful Web Service endpoints that allow you to interact with a small sample dataset.</API_DESCRIPTION>
<API_TERMS_URL>/license.html</API_TERMS_URL>
<API_CONTACT_EMAIL>[email protected]</API_CONTACT_EMAIL>
<API_LICENSE_NAME>BSD 2-Clause License</API_LICENSE_NAME>
<API_LICENSE_URL>https://github.com/Synergex/HarmonyCore/blob/master/LICENSE.md</API_LICENSE_URL>
<API_ENABLE_QUERY_PARAMS>(MaxExpansionDepth=4)</API_ENABLE_QUERY_PARAMS>
;
<SERVER_PROTOCOL>https</SERVER_PROTOCOL>
<SERVER_NAME>localhost</SERVER_NAME>
<SERVER_HTTP_PORT>8085</SERVER_HTTP_PORT>
<SERVER_HTTPS_PORT>8086</SERVER_HTTPS_PORT>
<SERVER_BASE_PATH>odata</SERVER_BASE_PATH>
;
<OAUTH_SERVER>http://localhost:5000</OAUTH_SERVER>
<OAUTH_API>api1</OAUTH_API>
<OAUTH_CLIENT>ro.client</OAUTH_CLIENT>
<OAUTH_SECRET>CBF7EBE6-D46E-41A7-903B-766A280616C3</OAUTH_SECRET>
<OAUTH_TEST_USER>jodah</OAUTH_TEST_USER>
<OAUTH_TEST_PASSWORD>P@ssw0rd</OAUTH_TEST_PASSWORD>
;
<ROLES_GET>Employee,Manager</ROLES_GET>
<ROLES_POST>Manager</ROLES_POST>
<ROLES_PUT>Manager</ROLES_PUT>
<ROLES_PATCH>Manager</ROLES_PATCH>
<ROLES_DELETE>Manager</ROLES_DELETE>
;
<BRIDGE_SMC_INTERFACE>SampleXfplEnv</BRIDGE_SMC_INTERFACE>
;

Automating Code Generation in Visual Studio

It is likely that as you develop you may need to generate your code on many occasions, as you add more and more data files to the REST API for example. If you wish you can execute the regen.bat file manually:

  • From the Tools menu select Comand Prompt (x64). It is important to open the command prompt in this way so that the command prompt inherits your environment settings from the Visual Studio environment.
  • In the command prompt, move to the solution folder where regen.bat is located.
  • Execute the batch file.

If you prefer you can add a custom tool to the Visual Studio tools menu to make it easier to execute the regen.bat file. To do so:

  • From the menu select Tools > External Tools
  • Click the Add button to add a new tool
  • Set the Title to Generate Code
  • Set the Command to $(SolutionDir)regen.bat
  • set the Initial directory to $(SolutionDir)
  • Check the Use Output window option
  • Click the OK button to create the custom tool.

Having taken these steps you can now use the menu option Tools > Generate Code each time you need to execute the regen.bat script, and you will see the output from the code generator in the Output window.

Clone this wiki locally