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

Docker support for C++ target #2322

Merged
merged 14 commits into from
Jun 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public List<String> defaultEntryPoint() {
@Override
protected String generateRunForInstallingDeps() {
var config = context.getTargetConfig();
var compiler = config.target == Target.CCPP ? "g++" : "gcc";
var compiler = config.target == Target.CCPP || config.target == Target.CPP ? "g++" : "gcc";
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
if (builderBase().equals(defaultImage())) {
return "RUN set -ex && apk add --no-cache %s musl-dev cmake make".formatted(compiler);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.lflang.generator.docker;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.lflang.generator.LFGeneratorContext;
import org.lflang.generator.cpp.CppPlatformGenerator;
import org.lflang.generator.cpp.CppStandaloneGenerator;
import org.lflang.target.property.BuildTypeProperty;

/**
* Generates the docker file related code for the C++ target.
*
* @author Marten Lohstroh
*/
public class CppDockerGenerator extends DockerGenerator {

private CppPlatformGenerator platformGenerator;

/** Construct a new Docker generator. */
public CppDockerGenerator(LFGeneratorContext context, CppPlatformGenerator platformGenerator) {
super(context);
this.platformGenerator = platformGenerator;
}

@Override
protected String generateCopyForSources() {
return "COPY src-gen src-gen";
}

public static final String DEFAULT_BASE_IMAGE = "alpine:latest";

@Override
public String defaultImage() {
return DEFAULT_BASE_IMAGE;
}

@Override
protected String generateRunForInstallingDeps() {
if (builderBase().equals(defaultImage())) {
return "RUN set -ex && apk add --no-cache g++ musl-dev cmake make && apk add --no-cache"
+ " --update --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main/"
+ " libexecinfo-dev";
} else {
return "# (Skipping installation of build dependencies; custom base image.)";
}
}

@Override
public List<String> defaultEntryPoint() {
return List.of("./bin/" + context.getFileConfig().name);
}

@Override
protected List<String> defaultBuildCommands() {
var mkdirCommand = List.of("mkdir", "-p", "build", "&&", "mkdir", "-p", "bin");
var cmakeCommand = new ArrayList<String>();
cmakeCommand.add("cmake");
cmakeCommand.addAll(platformGenerator.getCmakeArgs());
cmakeCommand.addAll(
List.of(
"-DCMAKE_INSTALL_BINDIR=bin",
"-DCMAKE_INSTALL_PREFIX=.",
"-DREACTOR_CPP_LINK_EXECINFO=ON",
"-S",
"src-gen",
"-B",
"build"));
var makeCommand =
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
List.of(
"cmake",
"--build",
"build",
"--target",
context.getFileConfig().name,
"--config",
CppStandaloneGenerator.Companion.buildTypeToCmakeConfig(
context.getTargetConfig().get(BuildTypeProperty.INSTANCE)));
var installCommand =
List.of(
"cmake",
"--build",
"build",
"--target",
"install",
"--config",
CppStandaloneGenerator.Companion.buildTypeToCmakeConfig(
context.getTargetConfig().get(BuildTypeProperty.INSTANCE)));
return Stream.of(mkdirCommand, cmakeCommand, makeCommand, installCommand)
.map(CppDockerGenerator::argListToCommand)
.toList();
}

static String argListToCommand(List<String> args) {
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
return args.stream().map(it -> "\"" + it + "\"").collect(Collectors.joining(" "));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Generates the docker file related code for the Typescript target.
*
* @author Hou Seng Wong
* @author Marten Lohstroh
*/
public class TSDockerGenerator extends DockerGenerator {

Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/lflang/target/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ public void initialize(TargetConfig config) {
BuildTypeProperty.INSTANCE,
CmakeIncludeProperty.INSTANCE,
CompilerProperty.INSTANCE,
DockerProperty.INSTANCE,
ExportDependencyGraphProperty.INSTANCE,
ExportToYamlProperty.INSTANCE,
ExternalRuntimePathProperty.INSTANCE,
Expand Down
48 changes: 37 additions & 11 deletions core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@
package org.lflang.generator.cpp

import org.eclipse.emf.ecore.resource.Resource
import org.lflang.target.Target
import org.lflang.generator.*
import org.lflang.generator.GeneratorUtils.canGenerate
import org.lflang.generator.LFGeneratorContext.Mode
import org.lflang.generator.docker.CppDockerGenerator
import org.lflang.generator.docker.DockerGenerator
import org.lflang.isGeneric
import org.lflang.scoping.LFGlobalScopeProvider
import org.lflang.target.Target
import org.lflang.target.property.*
import org.lflang.util.FileUtil
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path

Expand All @@ -59,6 +62,7 @@ class CppGenerator(
const val MINIMUM_CMAKE_VERSION = "3.5"

const val CPP_VERSION = "20"

}

override fun doGenerate(resource: Resource, context: LFGeneratorContext) {
Expand All @@ -67,8 +71,7 @@ class CppGenerator(
if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return

// create a platform-specific generator
val platformGenerator: CppPlatformGenerator =
if (targetConfig.get(Ros2Property.INSTANCE)) CppRos2Generator(this) else CppStandaloneGenerator(this)
val platformGenerator: CppPlatformGenerator = getPlatformGenerator()

// generate all core files
generateFiles(platformGenerator.srcGenPath, getAllImportedResources(resource))
Expand All @@ -83,7 +86,6 @@ class CppGenerator(
context.reportProgress(
"Code generation complete. Validating generated code...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS
)

if (platformGenerator.doCompile(context)) {
CppValidator(fileConfig, messageReporter, codeMaps).doValidate(context)
context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps))
Expand All @@ -94,10 +96,34 @@ class CppGenerator(
context.reportProgress(
"Code generation complete. Compiling...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS
)
if (platformGenerator.doCompile(context)) {
context.finish(GeneratorResult.Status.COMPILED, codeMaps)
if (targetConfig.get(DockerProperty.INSTANCE).enabled) {
FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen"))
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
try {
val tempDir = Files.createTempDirectory(context.fileConfig.outPath, "src-gen-directory")
try {
FileUtil.copyDirectoryContents(context.fileConfig.srcGenBasePath, tempDir, false)
FileUtil.copyDirectoryContents(tempDir, context.fileConfig.srcGenPath.resolve("src-gen"), false)
} catch (e: IOException) {
context.errorReporter.nowhere()
.error("Failed to copy sources to make them accessible to Docker: " + if (e.message == null) "No cause given" else e.message)
e.printStackTrace()
} finally {
FileUtil.deleteDirectory(tempDir)
}
if (errorsOccurred()) {
return
}
} catch (e: IOException) {
context.errorReporter.nowhere().error("Failed to create temporary directory.")
e.printStackTrace()
}
buildUsingDocker()
} else {
context.unsuccessfulFinish()
if (platformGenerator.doCompile(context)) {
context.finish(GeneratorResult.Status.COMPILED, codeMaps)
} else {
context.unsuccessfulFinish()
}
}
}
}
Expand Down Expand Up @@ -184,11 +210,11 @@ class CppGenerator(
}
}

private fun getPlatformGenerator() = if (targetConfig.get(Ros2Property.INSTANCE)) CppRos2Generator(this) else CppStandaloneGenerator(this)

override fun getTarget() = Target.CPP

override fun getTargetTypes(): TargetTypes = CppTypes
override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator {
TODO("Not yet implemented")
}
}

override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = CppDockerGenerator(context, getPlatformGenerator())
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ abstract class CppPlatformGenerator(protected val generator: CppGenerator) {

abstract fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean

protected val cmakeArgs: List<String>
val cmakeArgs: List<String>
get() = listOf(
"-DCMAKE_BUILD_TYPE=${targetConfig.get(BuildTypeProperty.INSTANCE)}",
"-DREACTOR_CPP_VALIDATE=${if (targetConfig.get(NoRuntimeValidationProperty.INSTANCE)) "OFF" else "ON"}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat
|cmake_minimum_required(VERSION 3.5)
|project(${fileConfig.name} VERSION 0.0.0 LANGUAGES CXX)
|
|option(REACTOR_CPP_LINK_EXECINFO "Link against execinfo" OFF)
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
|
|${if (targetConfig.get(ExternalRuntimePathProperty.INSTANCE) != null) "find_package(reactor-cpp PATHS ${targetConfig.get(ExternalRuntimePathProperty.INSTANCE)})" else ""}
|
|set(LF_MAIN_TARGET ${fileConfig.name})
Expand All @@ -167,6 +169,10 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat
|)
|target_link_libraries($S{LF_MAIN_TARGET} $reactorCppTarget)
|
|if(REACTOR_CPP_LINK_EXECINFO)
| target_link_libraries($S{LF_MAIN_TARGET} execinfo)
|endif()
|
|if(MSVC)
| target_compile_options($S{LF_MAIN_TARGET} PRIVATE /W4)
|else()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import java.nio.file.Paths
class CppStandaloneGenerator(generator: CppGenerator) :
CppPlatformGenerator(generator) {

companion object {
fun buildTypeToCmakeConfig(type: BuildType) = when (type) {
BuildType.TEST -> "Debug"
else -> type.toString()
}
}

override fun generatePlatformFiles() {

// generate the main source file (containing main())
Expand Down Expand Up @@ -132,11 +139,6 @@ class CppStandaloneGenerator(generator: CppGenerator) :
return 0
}

private fun buildTypeToCmakeConfig(type: BuildType) = when (type) {
BuildType.TEST -> "Debug"
else -> type.toString()
}

private fun createMakeCommand(buildPath: Path, version: String, target: String): LFCommand {
val makeArgs: List<String>
if (version.compareVersion("3.12.0") < 0) {
Expand Down
11 changes: 11 additions & 0 deletions test/Cpp/src/docker/HelloWorldContainerized.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
target Cpp {
logging: error, // To test generating a custom trace file name.
docker: true,
build-type: Debug
}

import HelloWorld2 from "../HelloWorld.lf"

main reactor {
a = new HelloWorld2()
}
Loading