use of org.lflang.federated.FedFileConfig in project lingua-franca by lf-lang.
the class FedCLauncher method compileCommandForFederate.
/**
* Return the compile command for a federate.
*
* @param federate The federate to compile.
* @throws IOException
*/
@Override
protected String compileCommandForFederate(FederateInstance federate) {
FedFileConfig fedFileConfig = null;
TargetConfig localTargetConfig = targetConfig;
try {
fedFileConfig = new FedFileConfig(fileConfig, federate.name);
} catch (IOException e) {
errorReporter.reportError("Failed to create file config for federate " + federate.name);
return "";
}
String commandToReturn = "";
// FIXME: Hack to add platform support only for linux systems.
// We need to fix the CMake build command for remote federates.
String linuxPlatformSupport = "core" + File.separator + "platform" + File.separator + "lf_linux_support.c";
if (!localTargetConfig.compileAdditionalSources.contains(linuxPlatformSupport)) {
localTargetConfig.compileAdditionalSources.add(linuxPlatformSupport);
}
CCompiler cCompiler = new CCompiler(localTargetConfig, fedFileConfig, errorReporter);
commandToReturn = String.join(" ", cCompiler.compileCCommand(fileConfig.name + "_" + federate.name, false).toString());
return commandToReturn;
}
use of org.lflang.federated.FedFileConfig in project lingua-franca by lf-lang.
the class FedLauncher method createLauncher.
/**
* Create the launcher shell scripts. This will create one or two files
* in the output path (bin directory). The first has name equal to
* the filename of the source file without the ".lf" extension.
* This will be a shell script that launches the
* RTI and the federates. If, in addition, either the RTI or any
* federate is mapped to a particular machine (anything other than
* the default "localhost" or "0.0.0.0"), then this will generate
* a shell script in the bin directory with name filename_distribute.sh
* that copies the relevant source files to the remote host and compiles
* them so that they are ready to execute using the launcher.
*
* A precondition for this to work is that the user invoking this
* code generator can log into the remote host without supplying
* a password. Specifically, you have to have installed your
* public key (typically found in ~/.ssh/id_rsa.pub) in
* ~/.ssh/authorized_keys on the remote host. In addition, the
* remote host must be running an ssh service.
* On an Arch Linux system using systemd, for example, this means
* running:
*
* sudo systemctl <start|enable> ssh.service
*
* Enable means to always start the service at startup, whereas
* start means to just start it this once.
*
* On MacOS, open System Preferences from the Apple menu and
* click on the "Sharing" preference panel. Select the checkbox
* next to "Remote Login" to enable it.
*
* In addition, every host must have OpenSSL installed, with at least
* version 1.1.1a. You can check the version with
*
* openssl version
*
* @param coreFiles The files from the core directory that must be
* copied to the remote machines.
* @param federates A list of federate instances in the federation
* @param federationRTIProperties Contains relevant properties of the RTI.
* Can have values for 'host', 'dir', and 'user'
*/
public void createLauncher(ArrayList<String> coreFiles, List<FederateInstance> federates, LinkedHashMap<String, Object> federationRTIProperties) throws IOException {
// NOTE: It might be good to use screen when invoking the RTI
// or federates remotely so you can detach and the process keeps running.
// However, I was unable to get it working properly.
// What this means is that the shell that invokes the launcher
// needs to remain live for the duration of the federation.
// If that shell is killed, the federation will die.
// Hence, it is reasonable to launch the federation on a
// machine that participates in the federation, for example,
// on the machine that runs the RTI. The command I tried
// to get screen to work looks like this:
// ssh -t «target» cd «path»; screen -S «filename»_«federate.name» -L bin/«filename»_«federate.name» 2>&1
// var outPath = binGenPath
StringBuilder shCode = new StringBuilder();
StringBuilder distCode = new StringBuilder();
shCode.append(getSetupCode() + "\n");
String distHeader = getDistHeader();
Object host = federationRTIProperties.get("host");
Object target = host;
Object path = federationRTIProperties.get("dir");
if (path == null)
path = "LinguaFrancaRemote";
Object user = federationRTIProperties.get("user");
if (user != null) {
target = user + "@" + host;
}
String RTILaunchString = getRtiCommand(federates);
// Launch the RTI in the foreground.
if (host.equals("localhost") || host.equals("0.0.0.0")) {
// FIXME: the paths below will not work on Windows
shCode.append(getLaunchCode(RTILaunchString) + "\n");
} else {
// Copy the source code onto the remote machine and compile it there.
if (distCode.length() == 0)
distCode.append(distHeader + "\n");
String logFileName = String.format("log/%s_RTI.log", fileConfig.name);
// Launch the RTI on the remote machine using ssh and screen.
// The -t argument to ssh creates a virtual terminal, which is needed by screen.
// The -S gives the session a name.
// The -L option turns on logging. Unfortunately, the -Logfile giving the log file name
// is not standardized in screen. Logs go to screenlog.0 (or screenlog.n).
// FIXME: Remote errors are not reported back via ssh from screen.
// How to get them back to the local machine?
// Perhaps use -c and generate a screen command file to control the logfile name,
// but screen apparently doesn't write anything to the log file!
//
// The cryptic 2>&1 reroutes stderr to stdout so that both are returned.
// The sleep at the end prevents screen from exiting before outgoing messages from
// the federate have had time to go out to the RTI through the socket.
RTILaunchString = getRtiCommand(federates);
shCode.append(getRemoteLaunchCode(host, target, logFileName, RTILaunchString) + "\n");
}
// Index used for storing pids of federates
int federateIndex = 0;
for (FederateInstance federate : federates) {
if (federate.isRemote) {
FedFileConfig fedFileConfig = new FedFileConfig(fileConfig, federate.name);
Path fedRelSrcGenPath = fedFileConfig.getSrcGenBasePath().relativize(fedFileConfig.getSrcGenPath());
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);
shCode.append(getFedRemoteLaunchCode(federate, path, logFileName, executeCommand, federateIndex++) + "\n");
} else {
String executeCommand = executeCommandForLocalFederate(fileConfig, federate);
shCode.append(getFedLocalLaunchCode(federate, executeCommand, federateIndex++) + "\n");
}
}
if (host.equals("localhost") || host.equals("0.0.0.0")) {
// Local PID managements
shCode.append("echo \"#### Bringing the RTI back to foreground so it can receive Control-C.\"" + "\n");
shCode.append("fg %1" + "\n");
}
// Wait for launched processes to finish
shCode.append(String.join("\n", "echo \"RTI has exited. Wait for federates to exit.\"", "# Wait for launched processes to finish.", "# The errors are handled separately via trap.", "for pid in \"${pids[@]}\"", "do", " wait $pid", "done", "echo \"All done.\"") + "\n");
// Write the launcher file.
// Delete file previously produced, if any.
File file = fileConfig.binPath.resolve(fileConfig.name).toFile();
if (file.exists()) {
file.delete();
}
FileOutputStream fOut = new FileOutputStream(file);
fOut.write(shCode.toString().getBytes());
fOut.close();
if (!file.setExecutable(true, false)) {
errorReporter.reportWarning("Unable to make launcher script executable.");
}
// Write the distributor file.
// Delete the file even if it does not get generated.
file = fileConfig.binPath.resolve(fileConfig.name + "_distribute.sh").toFile();
if (file.exists()) {
file.delete();
}
if (distCode.length() > 0) {
fOut = new FileOutputStream(file);
fOut.write(distCode.toString().getBytes());
fOut.close();
if (!file.setExecutable(true, false)) {
errorReporter.reportWarning("Unable to make distributor script executable.");
}
}
}
use of org.lflang.federated.FedFileConfig in project lingua-franca by lf-lang.
the class PythonGenerator method doGenerate.
/**
* Generate C code from the Lingua Franca model contained by the
* specified resource. This is the main entry point for code
* generation.
* @param resource The resource containing the source code.
* @param context Context relating to invocation of the code generator.
*/
@Override
public void doGenerate(Resource resource, LFGeneratorContext context) {
// If there are federates, assign the number of threads in the CGenerator to 1
if (isFederated) {
targetConfig.threads = 1;
}
// Prevent the CGenerator from compiling the C code.
// The PythonGenerator will compiler it.
boolean compileStatus = targetConfig.noCompile;
targetConfig.noCompile = true;
// Force disable the CMake because
targetConfig.useCmake = false;
// it interferes with the Python target functionality
int cGeneratedPercentProgress = (IntegratedBuilder.VALIDATED_PERCENT_PROGRESS + 100) / 2;
super.doGenerate(resource, new SubContext(context, IntegratedBuilder.VALIDATED_PERCENT_PROGRESS, cGeneratedPercentProgress));
SubContext compilingFederatesContext = new SubContext(context, cGeneratedPercentProgress, 100);
targetConfig.noCompile = compileStatus;
if (errorsOccurred()) {
context.unsuccessfulFinish();
return;
}
String baseFileName = topLevelName;
// Keep a separate file config for each federate
FileConfig oldFileConfig = fileConfig;
var federateCount = 0;
Map<Path, CodeMap> codeMaps = new HashMap<>();
for (FederateInstance federate : federates) {
federateCount++;
if (isFederated) {
topLevelName = baseFileName + '_' + federate.name;
try {
fileConfig = new FedFileConfig(fileConfig, federate.name);
} catch (IOException e) {
throw Exceptions.sneakyThrow(e);
}
}
// Don't generate code if there is no main reactor
if (this.main != null) {
try {
Map<Path, CodeMap> codeMapsForFederate = generatePythonFiles(federate);
codeMaps.putAll(codeMapsForFederate);
PyUtil.copyTargetFiles(fileConfig);
if (!targetConfig.noCompile) {
compilingFederatesContext.reportProgress(String.format("Validating %d/%d sets of generated files...", federateCount, federates.size()), 100 * federateCount / federates.size());
// If there are no federates, compile and install the generated code
new PythonValidator(fileConfig, errorReporter, codeMaps, protoNames).doValidate(context);
if (!errorsOccurred() && !Objects.equal(context.getMode(), LFGeneratorContext.Mode.LSP_MEDIUM)) {
compilingFederatesContext.reportProgress(String.format("Validation complete. Compiling and installing %d/%d Python modules...", federateCount, federates.size()), 100 * federateCount / federates.size());
// Why is this invoked here if the current federate is not a parameter?
pythonCompileCode(context);
}
} else {
System.out.println(PythonInfoGenerator.generateSetupInfo(fileConfig));
}
} catch (Exception e) {
throw Exceptions.sneakyThrow(e);
}
if (!isFederated) {
System.out.println(PythonInfoGenerator.generateRunInfo(fileConfig, topLevelName));
}
}
fileConfig = oldFileConfig;
}
if (isFederated) {
System.out.println(PythonInfoGenerator.generateFedRunInfo(fileConfig));
}
// Restore filename
topLevelName = baseFileName;
if (errorReporter.getErrorsOccurred()) {
context.unsuccessfulFinish();
} else if (!isFederated) {
context.finish(GeneratorResult.Status.COMPILED, topLevelName + ".py", fileConfig.getSrcGenPath(), fileConfig, codeMaps, "python3");
} else {
context.finish(GeneratorResult.Status.COMPILED, fileConfig.name, fileConfig.binPath, fileConfig, codeMaps, "bash");
}
}
Aggregations