Skip to content

Commit

Permalink
Merge branch 'master' into run-zephyr-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
erlingrj authored Mar 8, 2024
2 parents b523481 + 07cf4ba commit 90337a8
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import org.lflang.target.property.ClockSyncOptionsProperty.ClockSyncOptions;
import org.lflang.target.property.CmakeIncludeProperty;
import org.lflang.target.property.CompileDefinitionsProperty;
import org.lflang.target.property.CompilerFlagsProperty;
import org.lflang.target.property.CoordinationOptionsProperty;
import org.lflang.target.property.CoordinationProperty;
import org.lflang.target.property.type.ClockSyncModeType.ClockSyncMode;
Expand Down Expand Up @@ -247,8 +246,6 @@ public static void initializeClockSynchronization(
messageReporter
.nowhere()
.info("Will collect clock sync statistics for federate " + federate.id);
// Add libm to the compiler flags
CompilerFlagsProperty.INSTANCE.update(federate.targetConfig, List.of("-lm"));
}
messageReporter
.nowhere()
Expand Down
42 changes: 30 additions & 12 deletions core/src/main/java/org/lflang/federated/generator/FedGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,13 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws
// If the RTI is to be built locally, set up a build environment for it.
prepareRtiBuildEnvironment(context);

var useDocker = context.getTargetConfig().get(DockerProperty.INSTANCE).enabled();

Map<Path, CodeMap> codeMapMap =
compileFederates(
context,
lf2lfCodeMapMap,
subContexts -> {
createDockerFiles(context, subContexts);
generateLaunchScript();
// If an error has occurred during codegen of any federate, report it.
subContexts.forEach(
c -> {
Expand All @@ -195,12 +195,35 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws
.error("Failure during code generation of " + c.getFileConfig().srcFile);
}
});
if (useDocker) {
buildUsingDocker(context, subContexts);
} else {
generateLaunchScript();
}
});

context.finish(Status.COMPILED, codeMapMap);
return false;
}

/**
* Create Dockerfiles and docker-compose.yml, build, and create a launcher.
*
* @param context The main generator context.
* @param subContexts The context for the federates.
*/
private void buildUsingDocker(LFGeneratorContext context, List<SubContext> subContexts) {
try {
var dockerGen = new FedDockerComposeGenerator(context, rtiConfig.getHost());
dockerGen.writeDockerComposeFile(createDockerFiles(context, subContexts));
if (dockerGen.build()) {
dockerGen.createLauncher();
}
} catch (IOException e) {
context.getErrorReporter().nowhere().error("Unsuccessful Docker build.");
}
}

/**
* Prepare a build environment for the rti alongside the generated sources of the federates.
*
Expand Down Expand Up @@ -235,12 +258,12 @@ private void generateLaunchScript() {
* @param context The main context in which the federation has been compiled.
* @param subContexts The subcontexts in which the federates have been compiled.
*/
private void createDockerFiles(LFGeneratorContext context, List<SubContext> subContexts) {
if (!context.getTargetConfig().get(DockerProperty.INSTANCE).enabled()) return;
private List<DockerData> createDockerFiles(
LFGeneratorContext context, List<SubContext> subContexts) {
final List<DockerData> services = new ArrayList<>();
// 1. create a Dockerfile for each federate
for (SubContext subContext : subContexts) { // Inherit Docker options from main context

for (SubContext subContext : subContexts) {
// Inherit Docker options from main context
DockerProperty.INSTANCE.override(
subContext.getTargetConfig(), context.getTargetConfig().get(DockerProperty.INSTANCE));
var dockerGenerator = dockerGeneratorFactory(subContext);
Expand All @@ -252,12 +275,7 @@ private void createDockerFiles(LFGeneratorContext context, List<SubContext> subC
}
services.add(dockerData);
}
// 2. create a docker-compose.yml for the federation
try {
new FedDockerComposeGenerator(context, rtiConfig.getHost()).writeDockerComposeFile(services);
} catch (IOException e) {
throw new RuntimeIOException(e);
}
return services;
}

/**
Expand Down
14 changes: 0 additions & 14 deletions core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import org.lflang.target.property.BuildTypeProperty;
import org.lflang.target.property.CmakeIncludeProperty;
import org.lflang.target.property.CompileDefinitionsProperty;
import org.lflang.target.property.CompilerFlagsProperty;
import org.lflang.target.property.CompilerProperty;
import org.lflang.target.property.PlatformProperty;
import org.lflang.target.property.ProtobufsProperty;
Expand Down Expand Up @@ -399,19 +398,6 @@ CodeBuilder generateCMakeCode(
cMakeCode.newLine();
}

// Set the compiler flags
// We can detect a few common libraries and use the proper target_link_libraries to find them
for (String compilerFlag : targetConfig.get(CompilerFlagsProperty.INSTANCE)) {
messageReporter
.nowhere()
.warning(
"Using the flags target property with cmake is dangerous.\n"
+ " Use cmake-include instead.");
cMakeCode.pr("add_compile_options( " + compilerFlag + " )");
cMakeCode.pr("add_link_options( " + compilerFlag + ")");
}
cMakeCode.newLine();

// Add the install option
cMakeCode.pr(installCode);
cMakeCode.newLine();
Expand Down
114 changes: 50 additions & 64 deletions core/src/main/java/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import java.util.stream.Stream;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.lflang.FileConfig;
import org.lflang.ast.ASTUtils;
import org.lflang.ast.DelayedConnectionTransformation;
Expand Down Expand Up @@ -445,17 +444,6 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
CompileDefinitionsProperty.INSTANCE.update(
targetConfig, Map.of("NUMBER_OF_WATCHDOGS", String.valueOf(nWatchdogs)));

// Create docker file.
if (targetConfig.get(DockerProperty.INSTANCE).enabled() && mainDef != null) {
try {
var dockerData = getDockerGenerator(context).generateDockerData();
dockerData.writeDockerFile();
(new DockerComposeGenerator(context)).writeDockerComposeFile(List.of(dockerData));
} catch (IOException e) {
throw new RuntimeException("Error while writing Docker files", e);
}
}

var isArduino =
targetConfig.getOrDefault(PlatformProperty.INSTANCE).platform() == Platform.ARDUINO;

Expand Down Expand Up @@ -519,54 +507,13 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
return;
}

// If this code generator is directly compiling the code, compile it now so that we
// clean it up after, removing the #line directives after errors have been reported.
if (!targetConfig.get(NoCompileProperty.INSTANCE)
&& !targetConfig.get(DockerProperty.INSTANCE).enabled()
&& IterableExtensions.isNullOrEmpty(targetConfig.get(BuildCommandsProperty.INSTANCE))
// This code is unreachable in LSP_FAST mode, so that check is omitted.
&& context.getMode() != LFGeneratorContext.Mode.LSP_MEDIUM) {
// FIXME: Currently, a lack of main is treated as a request to not produce
// a binary and produce a .o file instead. There should be a way to control
// this.
// Create an anonymous Runnable class and add it to the compileThreadPool
// so that compilation can happen in parallel.
var cleanCode = code.removeLines("#line");

var execName = lfModuleName;
var threadFileConfig = fileConfig;
var generator =
this; // FIXME: currently only passed to report errors with line numbers in the Eclipse
// IDE
var CppMode = cppMode;
// generatingContext.reportProgress(
// String.format("Generated code for %d/%d executables. Compiling...", federateCount,
// federates.size()),
// 100 * federateCount / federates.size()
// ); // FIXME: Move to FedGenerator
// Create the compiler to be used later

var cCompiler = new CCompiler(targetConfig, threadFileConfig, messageReporter, CppMode);
try {
if (!cCompiler.runCCompiler(generator, context)) {
// If compilation failed, remove any bin files that may have been created.
CUtil.deleteBinFiles(threadFileConfig);
// If finish has already been called, it is illegal and makes no sense. However,
// if finish has already been called, then this must be a federated execution.
context.unsuccessfulFinish();
} else {
context.finish(GeneratorResult.Status.COMPILED, null);
}
cleanCode.writeToFile(targetFile);
} catch (IOException e) {
Exceptions.sneakyThrow(e);
}
}
var customBuildCommands = targetConfig.get(BuildCommandsProperty.INSTANCE);
var dockerBuild = targetConfig.get(DockerProperty.INSTANCE);

// If a build directive has been given, invoke it now.
// Note that the code does not get cleaned in this case.
if (!targetConfig.get(NoCompileProperty.INSTANCE)) {
if (!IterableExtensions.isNullOrEmpty(targetConfig.get(BuildCommandsProperty.INSTANCE))) {
if (targetConfig.get(NoCompileProperty.INSTANCE)) {
context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, null));
} else if (context.getMode() != LFGeneratorContext.Mode.LSP_MEDIUM) {
if (customBuildCommands != null && !customBuildCommands.isEmpty()) {
CUtil.runBuildCommand(
fileConfig,
targetConfig,
Expand All @@ -575,18 +522,57 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
this::reportCommandErrors,
context.getMode());
context.finish(GeneratorResult.Status.COMPILED, null);
} else if (dockerBuild.enabled()) {
buildUsingDocker();
} else {
var cleanCode = code.removeLines("#line");
var cCompiler = new CCompiler(targetConfig, fileConfig, messageReporter, cppMode);
var success = false;
try {
success = cCompiler.runCCompiler(this, context);
} catch (IOException e) {
messageReporter.nowhere().error("Unexpected error during compilation.");
} finally {
if (!success) {
// If compilation failed, remove any bin files that may have been created.
messageReporter.nowhere().error("Compilation was unsuccessful.");
CUtil.deleteBinFiles(fileConfig);
context.unsuccessfulFinish();
} else {
try {
cleanCode.writeToFile(targetFile);
} catch (IOException e) {
messageReporter
.nowhere()
.warning("Generated code may still contain line directives.");
}
context.finish(GeneratorResult.Status.COMPILED, null);
}
}
}
if (!errorsOccurred()) {
messageReporter.nowhere().info("Compiled binary is in " + fileConfig.binPath);
}
} else {
context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, null));
}

// In case we are in Eclipse, make sure the generated code is visible.
GeneratorUtils.refreshProject(resource, context.getMode());
}

/** Create Dockerfiles and docker-compose.yml, build, and create a launcher. */
private void buildUsingDocker() {
// Create docker file.
var dockerCompose = new DockerComposeGenerator(context);
var dockerData = getDockerGenerator(context).generateDockerData();
try {
dockerData.writeDockerFile();
dockerCompose.writeDockerComposeFile(List.of(dockerData));
} catch (IOException e) {
throw new RuntimeException("Error while writing Docker files", e);
}
var success = dockerCompose.build();
if (success && mainDef != null) {
dockerCompose.createLauncher();
}
}

private void generateCodeFor(String lfModuleName) throws IOException {
code.pr(generateDirectives());
code.pr(new CMainFunctionGenerator(targetConfig).generateCode());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package org.lflang.generator.docker;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.lflang.generator.LFGeneratorContext;
import org.lflang.util.FileUtil;
import org.lflang.util.LFCommand;

/**
* Code generator for docker-compose configurations.
Expand All @@ -15,14 +18,10 @@
*/
public class DockerComposeGenerator {

/** Path to the docker-compose.yml file. */
protected final Path path;

/** Context of the code generator. */
protected final LFGeneratorContext context;

public DockerComposeGenerator(LFGeneratorContext context) {
this.path = context.getFileConfig().getSrcGenPath().resolve("docker-compose.yml");
this.context = context;
}

Expand Down Expand Up @@ -52,22 +51,7 @@ protected String generateDockerServices(List<DockerData> services) {
%s
"""
.formatted(
services.stream()
.map(data -> getServiceDescription(data))
.collect(Collectors.joining("\n")));
}

/** Return the command to build and run using the docker-compose configuration. */
public String getUsageInstructions() {
return """
#####################################
To build and run:
pushd %s && docker compose up --build
To return to the current working directory afterwards:
popd
#####################################
"""
.formatted(path.getParent());
services.stream().map(this::getServiceDescription).collect(Collectors.joining("\n")));
}

/** Turn given docker data into a string. */
Expand Down Expand Up @@ -116,7 +100,53 @@ public void writeDockerComposeFile(List<DockerData> services, String networkName
var contents =
String.join(
"\n", this.generateDockerServices(services), this.generateDockerNetwork(networkName));
FileUtil.writeToFile(contents, path);
context.getErrorReporter().nowhere().info(getUsageInstructions());
FileUtil.writeToFile(
contents, context.getFileConfig().getSrcGenPath().resolve("docker-compose.yml"));
}

/**
* Build using docker compose.
*
* @return {@code true} if successful,{@code false} otherwise.
*/
public boolean build() {
return Objects.requireNonNull(
LFCommand.get(
"docker",
List.of("compose", "build"),
false,
context.getFileConfig().getSrcGenPath()))
.run()
== 0;
}

/** Create a launcher script that invokes Docker. */
public void createLauncher() {
var fileConfig = context.getFileConfig();
var packageRoot = fileConfig.srcPkgPath;
var srcGenPath = fileConfig.getSrcGenPath();
var file = fileConfig.binPath.resolve(fileConfig.name).toFile();
var script =
"""
#!/bin/bash
set -euo pipefail
cd $(dirname "$0")
cd ..
cd "%s"
docker compose up
"""
.formatted(packageRoot.relativize(srcGenPath));
var messageReporter = context.getErrorReporter();
try {
var writer = new BufferedWriter(new FileWriter(file));
writer.write(script);
writer.close();
} catch (IOException e) {
messageReporter.nowhere().warning("Unable to write launcher to: " + file.getAbsolutePath());
}

if (!file.setExecutable(true, false)) {
messageReporter.nowhere().warning("Unable to make launcher script executable.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ protected String generateDockerFileContent() {
COPY include /reactor-c/include
WORKDIR /reactor-c/core/federated/RTI
%s
RUN mkdir build && \\
RUN rm -rf build && \\
mkdir build && \\
cd build && \\
cmake ../ && \\
make && \\
Expand Down
Loading

0 comments on commit 90337a8

Please sign in to comment.