Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor doGenerate in C generator and parts of Python and Typescript docker generators #1141

Merged
merged 23 commits into from
May 7, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ public void createLauncher(
if(distCode.length() == 0) distCode.append(distHeader + "\n");
String logFileName = String.format("log/%s_%s.log", fedFileConfig.name, federate.name);
String compileCommand = compileCommandForFederate(federate);
// '''«targetConfig.compiler» src-gen/«topLevelName»_«federate.name».c -o bin/«topLevelName»_«federate.name» -pthread «targetConfig.compilerFlags.join(" ")»'''
// FIXME: Should $FEDERATION_ID be used to ensure unique directories, executables, on the remote host?
distCode.append(getDistCode(path, federate, fedRelSrcGenPath, logFileName, fedFileConfig, compileCommand) + "\n");
String executeCommand = executeCommandForRemoteFederate(federate);
Expand Down
72 changes: 0 additions & 72 deletions org.lflang/src/org/lflang/generator/DockerComposeGenerator.java

This file was deleted.

257 changes: 257 additions & 0 deletions org.lflang/src/org/lflang/generator/DockerGeneratorBase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package org.lflang.generator;

import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import org.lflang.FileConfig;
import org.lflang.TargetConfig;
import org.lflang.util.FileUtil;

/**
* The base class for docker file related code generation.
*
* @author{Hou Seng Wong <housengw@berkeley.edu>}
*/
public class DockerGeneratorBase {
/**
* The docker compose services representing each federate.
* Ideally, this would be a list of Strings instead of a StringBuilder.
*/
protected StringBuilder composeServices;

/**
* A docker file will be generated for each lingua franca module.
* This maps the name of the LF module to the data related to the docker
* file for that module.
*/
protected Map<String, Map<Key, Object>> moduleNameToData;

/**
* Indicates whether or not the program is federated.
*/
protected final boolean isFederated;

/**
* The number of federates this docker generator have added using `addFederate` so far.
*/
protected int nFederates;

/**
* In federated execution, the host of the rti.
*/
protected String host = null;

/**
* Generates the docker file related code for the Python target.
* The type specified in the following javadoc refers to the
* type of the object stored in `moduleNameToData.get(lfModuleName)`
*/
protected enum Key {
/**
* A `Path` object that is the absolute path to the docker file.
*/
DOCKER_FILE_PATH,
/**
* A `String` object that is the content of the docker file
* to be generated.
*/
DOCKER_FILE_CONTENT,
/**
* A `String` object that is the name of the docker compose
* service for the LF module.
*/
DOCKER_COMPOSE_SERVICE_NAME,
/**
* A `String` object that is the build context of the
* docker container.
*/
DOCKER_BUILD_CONTEXT,
}

housengw marked this conversation as resolved.
Show resolved Hide resolved
/**
* The constructor for the base docker file generation class.
* @param isFederated True if federated execution. False otherwise.
*/
public DockerGeneratorBase(boolean isFederated) {
moduleNameToData = new HashMap<>();
composeServices = new StringBuilder();
this.isFederated = isFederated;
nFederates = 0;
}

housengw marked this conversation as resolved.
Show resolved Hide resolved
/**
* Set the `host` of the container
* that launches the RTI.
* @param host The host to set.
*/
public void setHost(String host) {
this.host = host;
}

/**
* Add a federate to the list of federates to generate docker files for.
*
* @param lfModuleName The module name of the federate.
* @param federateName The name of the federate's reactor.
* @param fileConfig The file config.
* fileConfig.srcGenPath is assumed to point at
* where the docker file should be generated.
* @param targetConfig The target config.
*/
public void addFederate(
String lfModuleName,
String federateName,
FileConfig fileConfig,
TargetConfig targetConfig
) {
Map<Key, Object> k = new HashMap<>();
Path dockerPath = fileConfig.getSrcGenPath().resolve(lfModuleName + ".Dockerfile");
k.put(Key.DOCKER_FILE_PATH, dockerPath);

var dockerFileContent = generateDockerFileContent(lfModuleName);
k.put(Key.DOCKER_FILE_CONTENT, dockerFileContent);
k.put(Key.DOCKER_COMPOSE_SERVICE_NAME, isFederated ? federateName : lfModuleName.toLowerCase());
k.put(Key.DOCKER_BUILD_CONTEXT, isFederated ? federateName : ".");
moduleNameToData.put(lfModuleName, k);
nFederates++;

appendFederateToDockerComposeServices(
composeServices,
(String) k.get(Key.DOCKER_COMPOSE_SERVICE_NAME),
(String) k.get(Key.DOCKER_BUILD_CONTEXT),
dockerPath.getFileName().toString());
}

/**
* Write the docker files generated for the federates added using `addFederate` so far.
*
* @param fileConfig The fileConfig.
* fileConfig.srcGenPath is assumed to point at
* where the docker-compose.yml file should be generated.
*/
public void writeDockerFiles(FileConfig fileConfig) throws IOException {
var dockerComposeFilePath = fileConfig.getSrcGenPath().resolve("docker-compose.yml");
for (String lfModuleName : moduleNameToData.keySet()) {
var k = moduleNameToData.get(lfModuleName);
var dockerFilePath = (Path) k.get(Key.DOCKER_FILE_PATH);
if (dockerFilePath.toFile().exists()) {
dockerFilePath.toFile().delete();
}
var contents = (String) k.get(Key.DOCKER_FILE_CONTENT);
FileUtil.writeToFile(contents, dockerFilePath);
System.out.println(getDockerBuildCommand(
lfModuleName, dockerFilePath,
dockerComposeFilePath.getParent(),
(String) k.get(Key.DOCKER_COMPOSE_SERVICE_NAME)));
}

if (isFederated && host != null) {
appendRtiToDockerComposeServices(
composeServices,
"lflang/rti:rti",
host,
nFederates
);
}
writeFederatesDockerComposeFile(dockerComposeFilePath, composeServices, "lf");
}

/**
* A template function for generating target-specific docker file content.
*
* @param lfModuleName The name of the LF module currently generating.
*/
protected String generateDockerFileContent(
String lfModuleName
) {
throw new UnsupportedOperationException("Docker file content is not implemented");
}

/**
* Write a Dockerfile for the current federate as given by filename.
* @param The directory where the docker compose file is generated.
* @param The name of the docker file.
* @param The name of the federate.
*/
public String getDockerComposeCommand() {
String OS = System.getProperty("os.name").toLowerCase();
return (OS.indexOf("nux") >= 0) ? "docker-compose" : "docker compose";
}

/**
* Write a Dockerfile for the current federate as given by filename.
* @param The directory where the docker compose file is generated.
* @param The name of the docker file.
* @param The name of the federate.
*/
public String getDockerBuildCommand(
String lfModuleName,
Path dockerFilePath,
Path dockerComposeDir,
String dockerComposeServiceName
) {
return String.join("\n",
"Dockerfile for "+lfModuleName+" written to "+dockerFilePath,
"#####################################",
"To build the docker image, go to "+dockerComposeDir+" and run:",
"",
" "+getDockerComposeCommand()+" build "+dockerComposeServiceName,
"",
"#####################################"
);
}

/**
* Write the docker-compose.yml for orchestrating the federates.
* @param the directory to write the docker-compose.yml
* @param content of the "services" section of the docker-compose.yml
* @param the name of the network hosting the federation
*/
private void writeFederatesDockerComposeFile(
Path dockerComposeFilePath,
StringBuilder dockerComposeServices,
String networkName
) throws IOException {
var contents = new CodeBuilder();
contents.pr(String.join("\n",
"version: \"3.9\"",
"services:",
dockerComposeServices.toString(),
"networks:",
" lingua-franca:",
" name: "+networkName
));
FileUtil.writeToFile(contents.toString(), dockerComposeFilePath);
}

/**
* Append a service to the "services" section of the docker-compose.yml file.
* @param the content of the "services" section of the docker-compose.yml file.
* @param the name of the federate to be added to "services".
* @param the name of the federate's Dockerfile.
*/
private void appendFederateToDockerComposeServices(StringBuilder dockerComposeServices, String federateName, String context, String dockerFileName) {
var tab = " ".repeat(4);
dockerComposeServices.append(tab+federateName+":\n");
dockerComposeServices.append(tab+tab+"build:\n");
dockerComposeServices.append(tab+tab+tab+"context: "+context+"\n");
dockerComposeServices.append(tab+tab+tab+"dockerfile: "+dockerFileName+"\n");
dockerComposeServices.append(tab+tab+"command: -i 1\n");
}

/**
* Append the RTI to the "services" section of the docker-compose.yml file.
* @param the content of the "services" section of the docker-compose.yml file.
* @param the name given to the RTI in the "services" section.
* @param the tag of the RTI's image.
* @param the number of federates.
*/
private void appendRtiToDockerComposeServices(StringBuilder dockerComposeServices, String dockerImageName, String hostName, int n) {
var tab = " ".repeat(4);
dockerComposeServices.append(tab+"rti:\n");
dockerComposeServices.append(tab+tab+"image: "+dockerImageName+"\n");
dockerComposeServices.append(tab+tab+"hostname: "+hostName+"\n");
dockerComposeServices.append(tab+tab+"command: -i 1 -n "+n+"\n");
}
}
Loading