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.

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](Repository Setup).

For a simple example of how a repository structure can be used as a source of metadata, consider the following hypothetical structure definition:

STRUCTURE DEPARTMENT
    DEPT_ID           ,A10   ;Department ID (primary key)
    DEPT_NAME         ,A30   ;Department name
    DEPT_MANAGER      ,D4    ;Managers employee ID
ENDSTRUCTURE

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