Skip to content

Homework part 3 ‐ Code Generation

Ármin Zavada edited this page Dec 1, 2024 · 5 revisions

Overview

The goal of this homework is to get familiarized with code generation using the Jinja2 template engine. In this task we will provide an infrastructure-as-code environment for complex Cyber–physical systems, which enables us to model how CPS is deployed across several computer nodes.

Troubleshooting

Due to the use of Conda in the homework there are some recurring errors preventing teams from running the build on their computers, or in some cases, on GitHub itself.

Please, if you encounter any problem, check the following solutions, and if none of them work, reach out to us!

Special characters in the directory structure

Problem: Build works on GitHub Actions, but Gradle fails on my machine during task envSetup.

Multiplatform software in general do not like directory paths containing special characters, for example ' ', '*', etc. Make sure you are not using any of these, otherwise Conda will not install properly!

Too long path on Windows

Problem: Build works on GitHub Actions, but Gradle fails on my machine during task envSetup.

Must operating systems limit the maximum characters paths may have. Unfortunately, Windows comes with an irrealistically low limit (250), which is easily reachable. You can increase the limit by enabling the "long paths" feature. A step-by-step guide exists online: https://www.supportyourtech.com/articles/how-to-enable-long-paths-in-windows-11-step-by-step-guide/

Too long path on GitHub Actions

Problem: Build works locally, but it fails on GitHub.

Linux usually has a really long path limit, however, GitHub runners are limited as well. Some teams in certain cases can reach this limit, if their team name (repository name) is longer than ~15-20 characters. To fix this issue, we set Conda install directory to be the user home directory, which is a substantially shorter path.

To fix this issue, please update to the latest commit on 'hw3-starter' branch!

Fixed in commit 5545b03.

Development environment

Project starter

The initial project is available here: https://github.com/ftsrg-edu/ase-labs/tree/hw3-starter To start the homework, copy and paste the contents of the specified branch into your homework repository or clone your repository and execute the following commands inside it:

git remote add ase-labs https://github.com/ftsrg-edu/ase-labs.git
git fetch ase-labs
git switch hw3-starter
git push -u origin

We recommend the use of IntelliJ Idea, but any other text editor is sufficient.

Code-generation infrastructure

The code-generation infrastructure is similar to the laboratory setup. In the project's root directory you can find the following relevant directories:

  • models, that contains the .json files that provide the values for the code generator. You should familiarize yourself with the structure of this data.
  • jinja-templates, that contains the .j2 Jinja2 template files. You will edit these files to complete the homework.
  • python-scripts, that contains the .py Python scripts generating the code using the .json and .j2 files as input. You should not modify these files!
  • subprojects, that contains the sub gradle projects, one for each of the tasks.

Automated testing infrastructure

The homework comes with automated testing, providing you early feedback on your solutions. Please note, that these tests only serve as helpful feedback, but does not necessarily indicate your grade, as we will do manual review as well!

The automated tests first generate the code, then compile the projects, and finally, run JUnit5 tests that check the semantic correctness of the generated code. The checks do not consider the code style or quality. However, you are advised to generate human-readable code to help yourself during debugging.

The checks are split across the 4 homework tasks. The current task is specified in the gradle.properties file, initially homeworkTask=0. This works by conditionally including the subprojects for specific homeworkTask values. Please check that the gradle build task executes with the initial task value without failure!

Tasks

IMPORTANT: the only files you may modify are the template files inside the jinja-templates folder and the homeworkTask value in the gradle.properties file!

Task 1. Example code generation - Computer types

Preparations

Set homeworkTask=1 in the gradle.properties file to check this task. This should include the subprojects/computer-types subproject by Gradle. In this task we will complete the jinja-templates/computer-type.java.j2 template file to generate Computer types to the subprojects/computer-types/src/gen/java folder.

Model description

This task uses the computer-types.json model file. It contains a single list called computer_types. The generator script runs Jinja on each of the computer type inside the computer_types list, generating a {{ name }}.java file for each.

A Computer type is of the following form:

  • name: the name of the Type
  • os: the operating system the computer runs
  • description: the specific computer type's description
  • resources: the resources this type of computer comes with
    • memory: the available phisical memory
    • cpu: the installed CPU
    • storage: the available phisical storage

Specification

  • The template file must contain a single public class with the Computer type's name.
  • The class must extend from the Computer class.
  • The class must have a constructor with two parameters: String name, String ip, which it should forward to the super constructor.
  • The class must override the String getOs() function, and return the os value of the given computer type.
  • The class must override the String getDescription() function, and return the description value of the given computer type.
  • The class must override the ComputerResources getResources() function, and return a new ComputerResrouces class with the following constructor parameters:
    • resources.memory
    • resources.cpu
    • resources.storage

Walkthrough

First, create the class stump using the name value for the class name:

package hu.bme.mit.ase.cps.types.computers;

public class {{ name }} extends Computer {

    public {{ name }}(String name, String ip) {
        super(name, ip);
    }

}

Next, add the two overridden functions:

package hu.bme.mit.ase.cps.types.computers;

public class {{ name }} extends Computer {

    public {{ name }}(String name, String ip) {
        super(name, ip);
    }

    @Override
    String getOs() {
        return "{{ os }}";
    }

    @Override
    String getDescription() {
        return "{{ description }}";
    }

}

And finally, add the last overridden function:

package hu.bme.mit.ase.cps.types.computers;

public class {{ name }} extends Computer {

    public {{ name }}(String name, String ip) {
        super(name, ip);
    }

    @Override
    String getOs() {
        return "{{ os }}";
    }

    @Override
    String getDescription() {
        return "{{ description }}";
    }

    @Override
    ComputerResources getResources() {
        return new ComputerResources(
                "{{ resources.memory }}",
                "{{ resources.cpu }}",
                "{{ resources.storage }}"
        );
    }

}

Now, the gradle build task should generate 5 classes for the 5 specified Computer types, compile the subprojects/computer-types project, and run all tests without failure.

Task 2. Simple code generation - Software types

Preparations

Set homeworkTask=2 in the gradle.properties file to check this task. This should include the subprojects/software-types subproject by Gradle. In this task we will complete the jinja-templates/software-type.java.j2 template file to generate Software types to the subprojects/software-types/src/gen/java folder.

Model description

This task uses the software-types.json model file. It contains a single list called software_types. The generator script runs Jinja on each of the software type inside the software_types list, generating a {{ name }}.java file for each.

A Software type is of the following form:

  • name: the name of the Type
  • description: the specific software type's description
  • port: OPTIONAL, specifies which port this software uses
  • dependencies: list of the names of the software this type depends on

Specification

  • The template file must contain a single public class with the Software type's name.
  • The class must extend from the Software class.
  • The class must override the String getName() function, and return the name value of the given software type.
  • The class must override the String getDescription() function, and return the description value of the given software type.
  • The class must override the String getPort() function, and return the port value of the given software type if it exists, and -1 if it is not specified.
  • The class must override the List<String> getDependencies() function, and return a list of the software specified in the dependencies list.
    • Note: you should use the List.of(arg1, arg2, arg3, ...) function to return a new list of the specified arguments.

Only advance to the next task if gradle build executes without any problems!

Task 3. Intermediate code generation - Software repository

Preparations

Set homeworkTask=3 in the gradle.properties file to check this task. This should include the subprojects/software-repository subproject by Gradle. In this task we will complete the jinja-templates/software-repository.java.j2 template file to generate a single SoftwareRepository class to the subprojects/software-repository/src/gen/java folder.

Model description

This task uses the software-types.json model file.

Specification

  • The template file must contain a single public class with the following name: SoftwareRepository
  • The SoftwareRepository class must extend the Repository class.
  • The class must override the public void registerRepository() function in the following way:
    • the Repository class contains some function that can register a new software (look at the implementation to find out which!)
    • you must call this function with each of the Software type defined and generated in the previous task

Only advance to the next task if gradle build executes without any problems!

Task 4. Advanced code generation - Deployments

Preparations

Set homeworkTask=4 in the gradle.properties file to check this task. This should include the subprojects/deployments subproject by Gradle. In this task we will complete the jinja-templates/deployment.java.j2 template file to generate Deployment classes to the subprojects/deployments/src/gen/java folder.

Model description

This task uses the deployments.json model file. It contains a single list called deployments. The generator script runs Jinja on each of the deployment inside the deployments list, generating a {{ name }}.java file for each.

A Deployment is of the following form:

  • name: the name of the Deployment
  • computers: the computers to instantiate, with specific computer type, ip, and software to be installed
  • connections: which computers to connect to each other

Specification

  • The template file must contain a single public class with the deployment's name.
  • The class must extend the Deployment class.
  • The class must override the String getName() function, and return the name value of the given deployment.
  • The class must override the void deployComputers(CPS system) function.
    • deploy each specified computer using the appropriate function of the CPS class.
    • install each requested software on the specific computers using the appropriate function of the CPS class.
    • connect the specified computers to each other using the appropriate function of the CPS class.
  • The class must override the boolean checkDeployment(CPS system) function.
    • check the reachability between each connected computers using the appropriate function of the CPS class.
    • the function must return true if and only if all connected computers pass the reachability check.

Background materials