From 0ca3d1bbca065d4497a1ce4e7fa7acbd8413faf5 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 16 Jun 2024 17:52:05 -0700 Subject: [PATCH 01/14] First steps towards Docker support for C++ --- .../generator/docker/CDockerGenerator.java | 2 +- .../generator/docker/CppDockerGenerator.java | 48 +++++++++++++++++++ .../generator/docker/TSDockerGenerator.java | 1 + .../main/java/org/lflang/target/Target.java | 1 + .../org/lflang/generator/cpp/CppGenerator.kt | 25 ++++++---- .../Cpp/src/docker/HelloWorldContainerized.lf | 12 +++++ 6 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java create mode 100644 test/Cpp/src/docker/HelloWorldContainerized.lf diff --git a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java index afe67d8147..e8c114a98c 100644 --- a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java @@ -40,7 +40,7 @@ public List 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"; if (builderBase().equals(defaultImage())) { return "RUN set -ex && apk add --no-cache %s musl-dev cmake make".formatted(compiler); } else { diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java new file mode 100644 index 0000000000..efe89a78e8 --- /dev/null +++ b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java @@ -0,0 +1,48 @@ +package org.lflang.generator.docker; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import org.lflang.generator.LFGeneratorContext; +import org.lflang.generator.c.CCompiler; +import org.lflang.generator.c.CFileConfig; +import org.lflang.generator.cpp.CppGenerator; +import org.lflang.generator.cpp.CppPlatformGenerator; +import org.lflang.target.Target; + +/** + * Generates the docker file related code for the Typescript target. + * + * @author Marten Lohstroh + */ +public class CppDockerGenerator extends CDockerGenerator { + + private CppPlatformGenerator platformGenerator; + + /** Construct a new Docker generator. */ + public CppDockerGenerator(LFGeneratorContext context, CppPlatformGenerator platformGenerator) { + super(context); + this.platformGenerator = platformGenerator; + } + + @Override + protected List defaultBuildCommands() { + try { + return List.of( + "mkdir -p bin", + String.format( + "%s -DCMAKE_INSTALL_BINDIR=./bin -S src-gen -B bin", platformGenerator), + "cd bin", + "make all", + "cd .."); + } catch (IOException e) { + context + .getErrorReporter() + .nowhere() + .error("Unable to create file configuration for Docker container"); + throw new RuntimeException(e); + } + } + +} diff --git a/core/src/main/java/org/lflang/generator/docker/TSDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/TSDockerGenerator.java index d369634fa0..1e81439886 100644 --- a/core/src/main/java/org/lflang/generator/docker/TSDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/TSDockerGenerator.java @@ -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 { diff --git a/core/src/main/java/org/lflang/target/Target.java b/core/src/main/java/org/lflang/target/Target.java index d30e7ff982..30048f5a58 100644 --- a/core/src/main/java/org/lflang/target/Target.java +++ b/core/src/main/java/org/lflang/target/Target.java @@ -610,6 +610,7 @@ public void initialize(TargetConfig config) { BuildTypeProperty.INSTANCE, CmakeIncludeProperty.INSTANCE, CompilerProperty.INSTANCE, + DockerProperty.INSTANCE, ExportDependencyGraphProperty.INSTANCE, ExportToYamlProperty.INSTANCE, ExternalRuntimePathProperty.INSTANCE, diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt index 0f1645b856..afa377f76c 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt @@ -27,13 +27,14 @@ 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.nio.file.Files @@ -59,6 +60,7 @@ class CppGenerator( const val MINIMUM_CMAKE_VERSION = "3.5" const val CPP_VERSION = "20" + } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { @@ -67,8 +69,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)) @@ -83,7 +84,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)) @@ -94,10 +94,14 @@ 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) { + buildUsingDocker(); } else { - context.unsuccessfulFinish() + if (platformGenerator.doCompile(context)) { + context.finish(GeneratorResult.Status.COMPILED, codeMaps) + } else { + context.unsuccessfulFinish() + } } } } @@ -184,11 +188,12 @@ 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()) } diff --git a/test/Cpp/src/docker/HelloWorldContainerized.lf b/test/Cpp/src/docker/HelloWorldContainerized.lf new file mode 100644 index 0000000000..85cdcc195e --- /dev/null +++ b/test/Cpp/src/docker/HelloWorldContainerized.lf @@ -0,0 +1,12 @@ +target Cpp { + // To test generating a custom trace file name. + logging: error, + docker: true, + build-type: Debug +} + +import HelloWorld2 from "../HelloWorld.lf" + +main reactor { + a = new HelloWorld2() +} From b58903ffe5d8353d72f66906a12ecbdbd73a2d7c Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 16 Jun 2024 18:08:44 -0700 Subject: [PATCH 02/14] Added FIXME --- .../generator/docker/CppDockerGenerator.java | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java index efe89a78e8..c445765048 100644 --- a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java @@ -1,15 +1,8 @@ package org.lflang.generator.docker; -import java.io.IOException; -import java.nio.file.Path; import java.util.List; - import org.lflang.generator.LFGeneratorContext; -import org.lflang.generator.c.CCompiler; -import org.lflang.generator.c.CFileConfig; -import org.lflang.generator.cpp.CppGenerator; import org.lflang.generator.cpp.CppPlatformGenerator; -import org.lflang.target.Target; /** * Generates the docker file related code for the Typescript target. @@ -28,21 +21,7 @@ public CppDockerGenerator(LFGeneratorContext context, CppPlatformGenerator platf @Override protected List defaultBuildCommands() { - try { - return List.of( - "mkdir -p bin", - String.format( - "%s -DCMAKE_INSTALL_BINDIR=./bin -S src-gen -B bin", platformGenerator), - "cd bin", - "make all", - "cd .."); - } catch (IOException e) { - context - .getErrorReporter() - .nowhere() - .error("Unable to create file configuration for Docker container"); - throw new RuntimeException(e); - } + // FIXME + return List.of(); } - } From 00e4151b04bee0e74861cda1e3944da89a722b98 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 17 Jun 2024 18:04:27 -0700 Subject: [PATCH 03/14] CppDockerGenerator.java passes smoke test --- .../generator/docker/CppDockerGenerator.java | 79 ++++++++++++++++++- .../org/lflang/generator/cpp/CppGenerator.kt | 25 +++++- .../generator/cpp/CppPlatformGenerator.kt | 2 +- .../cpp/CppStandaloneCmakeGenerator.kt | 6 ++ .../generator/cpp/CppStandaloneGenerator.kt | 12 +-- .../Cpp/src/docker/HelloWorldContainerized.lf | 3 +- 6 files changed, 113 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java index c445765048..5bc8f2c31c 100644 --- a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java @@ -1,15 +1,20 @@ 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 Typescript target. + * Generates the docker file related code for the C++ target. * * @author Marten Lohstroh */ -public class CppDockerGenerator extends CDockerGenerator { +public class CppDockerGenerator extends DockerGenerator { private CppPlatformGenerator platformGenerator; @@ -19,9 +24,75 @@ public CppDockerGenerator(LFGeneratorContext context, CppPlatformGenerator platf 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 defaultEntryPoint() { + return List.of("./bin/" + context.getFileConfig().name); + } + @Override protected List defaultBuildCommands() { - // FIXME - return List.of(); + var mkdirCommand = List.of("mkdir", "-p", "build", "&&", "mkdir", "-p", "bin"); + var cmakeCommand = new ArrayList(); + 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 = + 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 args) { + return args.stream().map(it -> "\"" + it + "\"").collect(Collectors.joining(" ")); } } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt index afa377f76c..9c63a713f6 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt @@ -37,6 +37,8 @@ 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 @@ -95,7 +97,27 @@ class CppGenerator( "Code generation complete. Compiling...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS ) if (targetConfig.get(DockerProperty.INSTANCE).enabled) { - buildUsingDocker(); + FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen")) + 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 { if (platformGenerator.doCompile(context)) { context.finish(GeneratorResult.Status.COMPILED, codeMaps) @@ -196,4 +218,3 @@ class CppGenerator( override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = CppDockerGenerator(context, getPlatformGenerator()) } - diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt index 220770b6a9..6f0f295897 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt @@ -28,7 +28,7 @@ abstract class CppPlatformGenerator(protected val generator: CppGenerator) { abstract fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean - protected val cmakeArgs: List + val cmakeArgs: List get() = listOf( "-DCMAKE_BUILD_TYPE=${targetConfig.get(BuildTypeProperty.INSTANCE)}", "-DREACTOR_CPP_VALIDATE=${if (targetConfig.get(NoRuntimeValidationProperty.INSTANCE)) "OFF" else "ON"}", diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index 2c041fe6d2..5d36e8e327 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -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) + | |${if (targetConfig.get(ExternalRuntimePathProperty.INSTANCE) != null) "find_package(reactor-cpp PATHS ${targetConfig.get(ExternalRuntimePathProperty.INSTANCE)})" else ""} | |set(LF_MAIN_TARGET ${fileConfig.name}) @@ -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() diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt index dd03db376d..1640432fd4 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -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()) @@ -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 if (version.compareVersion("3.12.0") < 0) { diff --git a/test/Cpp/src/docker/HelloWorldContainerized.lf b/test/Cpp/src/docker/HelloWorldContainerized.lf index 85cdcc195e..a7ddb6205c 100644 --- a/test/Cpp/src/docker/HelloWorldContainerized.lf +++ b/test/Cpp/src/docker/HelloWorldContainerized.lf @@ -1,6 +1,5 @@ target Cpp { - // To test generating a custom trace file name. - logging: error, + logging: error, // To test generating a custom trace file name. docker: true, build-type: Debug } From f3f37a82de67eeaf91286829439694c1ca2ad940 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 17 Jun 2024 22:43:39 -0700 Subject: [PATCH 04/14] Address comments from code review --- .../generator/docker/CppDockerGenerator.java | 7 +-- .../generator/docker/DockerGenerator.java | 8 ++++ .../org/lflang/generator/cpp/CppGenerator.kt | 44 ++++++++++--------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java index 5bc8f2c31c..db56a682cd 100644 --- a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java @@ -2,7 +2,6 @@ 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; @@ -88,11 +87,7 @@ protected List defaultBuildCommands() { CppStandaloneGenerator.Companion.buildTypeToCmakeConfig( context.getTargetConfig().get(BuildTypeProperty.INSTANCE))); return Stream.of(mkdirCommand, cmakeCommand, makeCommand, installCommand) - .map(CppDockerGenerator::argListToCommand) + .map(DockerGenerator::argListToCommand) .toList(); } - - static String argListToCommand(List args) { - return args.stream().map(it -> "\"" + it + "\"").collect(Collectors.joining(" ")); - } } diff --git a/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java index 6ee2847ead..59bc91b9e8 100644 --- a/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/DockerGenerator.java @@ -233,4 +233,12 @@ public static DockerGenerator dockerGeneratorFactory(LFGeneratorContext context) throw new IllegalArgumentException("No Docker support for " + target + " yet."); }; } + + /** + * Convert an argument list, starting with the command to execute, into a string that can be + * executed by a POSIX-compliant shell. + */ + public static String argListToCommand(List args) { + return args.stream().map(it -> "\"" + it + "\"").collect(Collectors.joining(" ")); + } } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt index 9c63a713f6..4fd410d73d 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt @@ -97,26 +97,7 @@ class CppGenerator( "Code generation complete. Compiling...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS ) if (targetConfig.get(DockerProperty.INSTANCE).enabled) { - FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen")) - 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() - } + copySrcGenBaseDirIntoSrcGenDir() buildUsingDocker() } else { if (platformGenerator.doCompile(context)) { @@ -128,6 +109,29 @@ class CppGenerator( } } + private fun copySrcGenBaseDirIntoSrcGenDir() { + FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen")) + 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() + } + } + private fun fetchReactorCpp(version: String) { val libPath = fileConfig.srcGenBasePath.resolve("reactor-cpp-$version") // abort if the directory already exists From e7d510ec05b07dac411f2e68a7c6d783a9b8018f Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 17 Jun 2024 22:51:00 -0700 Subject: [PATCH 05/14] Update core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java --- .../main/java/org/lflang/generator/docker/CDockerGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java index e8c114a98c..afe67d8147 100644 --- a/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CDockerGenerator.java @@ -40,7 +40,7 @@ public List defaultEntryPoint() { @Override protected String generateRunForInstallingDeps() { var config = context.getTargetConfig(); - var compiler = config.target == Target.CCPP || config.target == Target.CPP ? "g++" : "gcc"; + var compiler = config.target == Target.CCPP ? "g++" : "gcc"; if (builderBase().equals(defaultImage())) { return "RUN set -ex && apk add --no-cache %s musl-dev cmake make".formatted(compiler); } else { From 7c98324450bb1555ae86e4802757be52e1a7f26c Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 18 Jun 2024 22:14:58 -0700 Subject: [PATCH 06/14] Address failure to load dynamic libraries Observation: this will not work well with cross-compilation. It will not really make sense for the two base images to differ. --- .../lflang/generator/docker/CppDockerGenerator.java | 11 +++++++++++ .../generator/docker/PythonDockerGenerator.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java index db56a682cd..471f6f9ce6 100644 --- a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java @@ -51,6 +51,17 @@ public List defaultEntryPoint() { return List.of("./bin/" + context.getFileConfig().name); } + + @Override + protected String generateCopyOfExecutable() { + return String.join( + "\n", + super.generateCopyOfExecutable(), + "COPY --from=builder /usr/local/lib /usr/local/lib", + "COPY --from=builder /usr/lib /usr/lib", + "COPY --from=builder /lingua-franca ."); + } + @Override protected List defaultBuildCommands() { var mkdirCommand = List.of("mkdir", "-p", "build", "&&", "mkdir", "-p", "bin"); diff --git a/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java index 9ad1081bca..046b60fbdb 100644 --- a/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java @@ -36,7 +36,7 @@ protected String generateCopyOfExecutable() { "\n", super.generateCopyOfExecutable(), "COPY --from=builder /lingua-franca/%s/src-gen ./src-gen" - .formatted(lfModuleName, lfModuleName, lfModuleName)); + .formatted(lfModuleName)); } @Override From 227c29f5565738687e14b80b825ccfa4922c1d21 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 18 Jun 2024 23:44:38 -0700 Subject: [PATCH 07/14] Refactor according to code review --- .../org/lflang/tests/runtime/CppRos2Test.java | 2 + .../generator/docker/CppDockerGenerator.java | 44 +++--------------- .../docker/PythonDockerGenerator.java | 3 +- .../generator/cpp/CppPlatformGenerator.kt | 2 + .../lflang/generator/cpp/CppRos2Generator.kt | 30 +++++++----- .../generator/cpp/CppStandaloneGenerator.kt | 46 ++++++++++++++----- 6 files changed, 65 insertions(+), 62 deletions(-) diff --git a/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java b/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java index 334f9ef7aa..9ddae14d32 100644 --- a/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java +++ b/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java @@ -5,6 +5,7 @@ import org.lflang.lf.Element; import org.lflang.lf.LfFactory; import org.lflang.target.Target; +import org.lflang.target.property.DockerProperty; import org.lflang.target.property.Ros2Property; import org.lflang.tests.TestBase; import org.lflang.tests.Transformers; @@ -33,6 +34,7 @@ public void runWithRos2() { it -> true, Transformers::noChanges, config -> { + if (config.get(DockerProperty.INSTANCE) != null) return false; Ros2Property.INSTANCE.override(config, true); return true; }, diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java index 471f6f9ce6..149d9e1c76 100644 --- a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java @@ -1,12 +1,9 @@ package org.lflang.generator.docker; -import java.util.ArrayList; import java.util.List; 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. @@ -51,7 +48,6 @@ public List defaultEntryPoint() { return List.of("./bin/" + context.getFileConfig().name); } - @Override protected String generateCopyOfExecutable() { return String.join( @@ -64,40 +60,12 @@ protected String generateCopyOfExecutable() { @Override protected List defaultBuildCommands() { - var mkdirCommand = List.of("mkdir", "-p", "build", "&&", "mkdir", "-p", "bin"); - var cmakeCommand = new ArrayList(); - 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 = - 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) + var mkdirCommand = List.of("mkdir", "-p", "build"); + return Stream.concat( + Stream.of(mkdirCommand), + platformGenerator + .getBuildCommands(List.of("-DREACTOR_CPP_LINK_EXECINFO=ON"), false) + .stream()) .map(DockerGenerator::argListToCommand) .toList(); } diff --git a/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java index 046b60fbdb..6a55669752 100644 --- a/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java +++ b/core/src/main/java/org/lflang/generator/docker/PythonDockerGenerator.java @@ -35,8 +35,7 @@ protected String generateCopyOfExecutable() { return String.join( "\n", super.generateCopyOfExecutable(), - "COPY --from=builder /lingua-franca/%s/src-gen ./src-gen" - .formatted(lfModuleName)); + "COPY --from=builder /lingua-franca/%s/src-gen ./src-gen".formatted(lfModuleName)); } @Override diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt index 6f0f295897..e193b45deb 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt @@ -37,4 +37,6 @@ abstract class CppPlatformGenerator(protected val generator: CppGenerator) { "-DREACTOR_CPP_LOG_LEVEL=${targetConfig.get(LoggingProperty.INSTANCE).severity}", "-DLF_SRC_PKG_PATH=${fileConfig.srcPkgPath}", ) + + abstract fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean = true): List> } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt index 76969269c0..a2a75471b7 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt @@ -1,7 +1,9 @@ package org.lflang.generator.cpp +import jakarta.ws.rs.NotSupportedException import org.lflang.generator.LFGeneratorContext import org.lflang.util.FileUtil +import org.lflang.util.LFCommand import java.nio.file.Path /** C++ platform generator for the ROS2 platform.*/ @@ -46,24 +48,30 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator ) return false } - val colconCommand = commandFactory.createCommand( - "colcon", listOf( + "colcon", colconArgs(), fileConfig.outPath) + val returnCode = colconCommand?.run(context.cancelIndicator) + if (returnCode != 0 && !messageReporter.errorsOccurred) { + // If errors occurred but none were reported, then the following message is the best we can do. + messageReporter.nowhere().error("colcon failed with error code $returnCode") + } + + return !messageReporter.errorsOccurred + } + + private fun colconArgs(additionalCmakeArgs: List = listOf()): List { + return listOf( "build", "--packages-select", fileConfig.name, packageGenerator.reactorCppName, "--cmake-args", "-DLF_REACTOR_CPP_SUFFIX=${packageGenerator.reactorCppSuffix}", - ) + cmakeArgs, - fileConfig.outPath - ) - val returnCode = colconCommand?.run(context.cancelIndicator); - if (returnCode != 0 && !messageReporter.errorsOccurred) { - // If errors occurred but none were reported, then the following message is the best we can do. - messageReporter.nowhere().error("colcon failed with error code $returnCode") - } + ) + cmakeArgs + additionalCmakeArgs + } - return !messageReporter.errorsOccurred + override fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean): List> { +// return listOf(listOf("colcon") + colconArgs(additionalCmakeArgs)) + throw NotImplementedError("Docker file generation for ROS 2 interoperability is not supported") } } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt index 1640432fd4..d27763bde4 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -2,8 +2,10 @@ package org.lflang.generator.cpp import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.docker.DockerGenerator import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CompilerProperty +import org.lflang.target.property.type.BuildTypeType import org.lflang.target.property.type.BuildTypeType.BuildType import org.lflang.toUnixString import org.lflang.util.FileUtil @@ -11,6 +13,8 @@ import org.lflang.util.LFCommand import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import java.util.stream.Stream +import kotlin.io.path.name /** C++ platform generator for the default native platform without additional dependencies.*/ class CppStandaloneGenerator(generator: CppGenerator) : @@ -98,6 +102,13 @@ class CppStandaloneGenerator(generator: CppGenerator) : return !messageReporter.errorsOccurred } + override fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean): List> { + val cmakeCommand = createCmakeCommand(Path.of("./build"), Path.of("."), additionalCmakeArgs, "src-gen") + val makeCommand = createMakeCommand(fileConfig.buildPath, "3.12.0", fileConfig.name) + val installCommand = createMakeCommand(Path.of("./build"), "3.12.0", "install") + return listOf(cmakeCommand, makeCommand, installCommand).map { it.command() } + } + private fun checkCmakeVersion(): String? { // get the installed cmake version and make sure it is at least 3.5 val cmd = commandFactory.createCommand("cmake", listOf("--version"), fileConfig.buildPath) @@ -139,38 +150,51 @@ class CppStandaloneGenerator(generator: CppGenerator) : return 0 } - private fun createMakeCommand(buildPath: Path, version: String, target: String): LFCommand { + private fun createMakeCommand(buildPath: Path, version: String, target: String, parallelize: Boolean = false): LFCommand { val makeArgs: List + val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) if (version.compareVersion("3.12.0") < 0) { messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") makeArgs = - listOf("--build", ".", "--target", target, "--config", buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE))) - } else { + listOf("--build", buildPath.name, "--target", target, "--config", cmakeConfig) + } else if (parallelize) { val cores = Runtime.getRuntime().availableProcessors() makeArgs = listOf( "--build", - ".", + buildPath.name, "--target", target, "--parallel", cores.toString(), "--config", - buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) + cmakeConfig + ) + } else { + makeArgs = listOf( + "--build", + buildPath.name, + "--target", + target, + "--config", + cmakeConfig ) } - return commandFactory.createCommand("cmake", makeArgs, buildPath) + return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) } - private fun createCmakeCommand(buildPath: Path, outPath: Path): LFCommand { + private fun createCmakeCommand(buildPath: Path, outPath: Path, additionalCmakeArgs: List = listOf(), sourcesRoot: String? = null): LFCommand { val cmd = commandFactory.createCommand( "cmake", - cmakeArgs + listOf( + cmakeArgs + additionalCmakeArgs + listOf( "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", - "-DCMAKE_INSTALL_BINDIR=${outPath.relativize(fileConfig.binPath).toUnixString()}", - fileConfig.srcGenBasePath.toUnixString() + "-DCMAKE_INSTALL_BINDIR=${if (outPath.isAbsolute) outPath.relativize(fileConfig.binPath).toUnixString() else fileConfig.binPath.name}", + "-S", + sourcesRoot ?: fileConfig.srcGenBasePath.toUnixString(), + "-B", + buildPath.name ), - buildPath + buildPath.parent ) // prepare cmake From 75cfbaf2542f79252bdabf9102ea33a7cdcaacd7 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 13:07:01 +0200 Subject: [PATCH 08/14] clean up code for parallel builds --- .../generator/cpp/CppStandaloneGenerator.kt | 56 ++++++++----------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt index d27763bde4..2e97831d5e 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -2,10 +2,8 @@ package org.lflang.generator.cpp import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext -import org.lflang.generator.docker.DockerGenerator import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CompilerProperty -import org.lflang.target.property.type.BuildTypeType import org.lflang.target.property.type.BuildTypeType.BuildType import org.lflang.toUnixString import org.lflang.util.FileUtil @@ -13,7 +11,6 @@ import org.lflang.util.LFCommand import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import java.util.stream.Stream import kotlin.io.path.name /** C++ platform generator for the default native platform without additional dependencies.*/ @@ -73,16 +70,22 @@ class CppStandaloneGenerator(generator: CppGenerator) : Files.createDirectories(fileConfig.buildPath) val version = checkCmakeVersion() + var parallelize = true + if (version != null && version.compareVersion("3.12.0") < 0) { + messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") + parallelize = false + } + if (version != null) { val cmakeReturnCode = runCmake(context) if (cmakeReturnCode == 0 && runMake) { // If cmake succeeded, run make - val makeCommand = createMakeCommand(fileConfig.buildPath, version, fileConfig.name) + val makeCommand = createMakeCommand(fileConfig.buildPath, parallelize, fileConfig.name) val makeReturnCode = CppValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) var installReturnCode = 0 if (makeReturnCode == 0) { - val installCommand = createMakeCommand(fileConfig.buildPath, version, "install") + val installCommand = createMakeCommand(fileConfig.buildPath, parallelize, "install") installReturnCode = installCommand.run(context.cancelIndicator) if (installReturnCode == 0) { println("SUCCESS (compiling generated C++ code)") @@ -104,8 +107,8 @@ class CppStandaloneGenerator(generator: CppGenerator) : override fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean): List> { val cmakeCommand = createCmakeCommand(Path.of("./build"), Path.of("."), additionalCmakeArgs, "src-gen") - val makeCommand = createMakeCommand(fileConfig.buildPath, "3.12.0", fileConfig.name) - val installCommand = createMakeCommand(Path.of("./build"), "3.12.0", "install") + val makeCommand = createMakeCommand(fileConfig.buildPath, true, fileConfig.name) + val installCommand = createMakeCommand(Path.of("./build"), true, "install") return listOf(cmakeCommand, makeCommand, installCommand).map { it.command() } } @@ -150,34 +153,19 @@ class CppStandaloneGenerator(generator: CppGenerator) : return 0 } - private fun createMakeCommand(buildPath: Path, version: String, target: String, parallelize: Boolean = false): LFCommand { - val makeArgs: List + private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String, ): LFCommand { val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) - if (version.compareVersion("3.12.0") < 0) { - messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") - makeArgs = - listOf("--build", buildPath.name, "--target", target, "--config", cmakeConfig) - } else if (parallelize) { - val cores = Runtime.getRuntime().availableProcessors() - makeArgs = listOf( - "--build", - buildPath.name, - "--target", - target, - "--parallel", - cores.toString(), - "--config", - cmakeConfig - ) - } else { - makeArgs = listOf( - "--build", - buildPath.name, - "--target", - target, - "--config", - cmakeConfig - ) + val makeArgs: MutableList = listOf( + "--build", + buildPath.name, + "--target", + target, + "--config", + cmakeConfig + ).toMutableList() + + if (parallelize) { + makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) } return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) From 4ba586988bf3baac8722ad58e60566b1131d334a Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 13:29:27 +0200 Subject: [PATCH 09/14] keep all build information in a single place --- .../generator/docker/CppDockerGenerator.java | 72 ----------------- .../org/lflang/generator/cpp/CppGenerator.kt | 4 +- .../generator/cpp/CppPlatformGenerator.kt | 3 +- .../lflang/generator/cpp/CppRos2Generator.kt | 10 ++- .../generator/cpp/CppStandaloneGenerator.kt | 77 ++++++++++++++++--- 5 files changed, 76 insertions(+), 90 deletions(-) delete mode 100644 core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java diff --git a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java b/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java deleted file mode 100644 index 149d9e1c76..0000000000 --- a/core/src/main/java/org/lflang/generator/docker/CppDockerGenerator.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.lflang.generator.docker; - -import java.util.List; -import java.util.stream.Stream; -import org.lflang.generator.LFGeneratorContext; -import org.lflang.generator.cpp.CppPlatformGenerator; - -/** - * 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 defaultEntryPoint() { - return List.of("./bin/" + context.getFileConfig().name); - } - - @Override - protected String generateCopyOfExecutable() { - return String.join( - "\n", - super.generateCopyOfExecutable(), - "COPY --from=builder /usr/local/lib /usr/local/lib", - "COPY --from=builder /usr/lib /usr/lib", - "COPY --from=builder /lingua-franca ."); - } - - @Override - protected List defaultBuildCommands() { - var mkdirCommand = List.of("mkdir", "-p", "build"); - return Stream.concat( - Stream.of(mkdirCommand), - platformGenerator - .getBuildCommands(List.of("-DREACTOR_CPP_LINK_EXECINFO=ON"), false) - .stream()) - .map(DockerGenerator::argListToCommand) - .toList(); - } -} diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt index 4fd410d73d..d64afb4d01 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt @@ -30,14 +30,12 @@ import org.eclipse.emf.ecore.resource.Resource 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 @@ -220,5 +218,5 @@ class CppGenerator( override fun getTargetTypes(): TargetTypes = CppTypes - override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = CppDockerGenerator(context, getPlatformGenerator()) + override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = getPlatformGenerator().getDockerGenerator(context) } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt index e193b45deb..30b9a78d6e 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt @@ -4,6 +4,7 @@ import org.lflang.MessageReporter import org.lflang.target.TargetConfig import org.lflang.generator.GeneratorCommandFactory import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.docker.DockerGenerator import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.LoggingProperty import org.lflang.target.property.NoRuntimeValidationProperty @@ -38,5 +39,5 @@ abstract class CppPlatformGenerator(protected val generator: CppGenerator) { "-DLF_SRC_PKG_PATH=${fileConfig.srcPkgPath}", ) - abstract fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean = true): List> + abstract fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt index a2a75471b7..06bfadc98e 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.cpp import jakarta.ws.rs.NotSupportedException import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.docker.DockerGenerator import org.lflang.util.FileUtil import org.lflang.util.LFCommand import java.nio.file.Path @@ -59,6 +60,10 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator return !messageReporter.errorsOccurred } + override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator { + TODO("Not yet implemented") + } + private fun colconArgs(additionalCmakeArgs: List = listOf()): List { return listOf( "build", @@ -70,8 +75,5 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator ) + cmakeArgs + additionalCmakeArgs } - override fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean): List> { -// return listOf(listOf("colcon") + colconArgs(additionalCmakeArgs)) - throw NotImplementedError("Docker file generation for ROS 2 interoperability is not supported") - } + } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt index 2e97831d5e..fda17fb0c4 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.cpp import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.docker.DockerGenerator import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CompilerProperty import org.lflang.target.property.type.BuildTypeType.BuildType @@ -22,6 +23,8 @@ class CppStandaloneGenerator(generator: CppGenerator) : BuildType.TEST -> "Debug" else -> type.toString() } + + const val DEFAULT_BASE_IMAGE: String = "alpine:latest" } override fun generatePlatformFiles() { @@ -105,13 +108,6 @@ class CppStandaloneGenerator(generator: CppGenerator) : return !messageReporter.errorsOccurred } - override fun getBuildCommands(additionalCmakeArgs: List, parallelize: Boolean): List> { - val cmakeCommand = createCmakeCommand(Path.of("./build"), Path.of("."), additionalCmakeArgs, "src-gen") - val makeCommand = createMakeCommand(fileConfig.buildPath, true, fileConfig.name) - val installCommand = createMakeCommand(Path.of("./build"), true, "install") - return listOf(cmakeCommand, makeCommand, installCommand).map { it.command() } - } - private fun checkCmakeVersion(): String? { // get the installed cmake version and make sure it is at least 3.5 val cmd = commandFactory.createCommand("cmake", listOf("--version"), fileConfig.buildPath) @@ -153,7 +149,7 @@ class CppStandaloneGenerator(generator: CppGenerator) : return 0 } - private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String, ): LFCommand { + private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) val makeArgs: MutableList = listOf( "--build", @@ -171,12 +167,19 @@ class CppStandaloneGenerator(generator: CppGenerator) : return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) } - private fun createCmakeCommand(buildPath: Path, outPath: Path, additionalCmakeArgs: List = listOf(), sourcesRoot: String? = null): LFCommand { + private fun createCmakeCommand( + buildPath: Path, + outPath: Path, + additionalCmakeArgs: List = listOf(), + sourcesRoot: String? = null + ): LFCommand { val cmd = commandFactory.createCommand( "cmake", cmakeArgs + additionalCmakeArgs + listOf( "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", - "-DCMAKE_INSTALL_BINDIR=${if (outPath.isAbsolute) outPath.relativize(fileConfig.binPath).toUnixString() else fileConfig.binPath.name}", + "-DCMAKE_INSTALL_BINDIR=${ + if (outPath.isAbsolute) outPath.relativize(fileConfig.binPath).toUnixString() else fileConfig.binPath.name + }", "-S", sourcesRoot ?: fileConfig.srcGenBasePath.toUnixString(), "-B", @@ -191,4 +194,58 @@ class CppStandaloneGenerator(generator: CppGenerator) : } return cmd } + + inner class CppDockerGenerator(context: LFGeneratorContext?) : DockerGenerator(context) { + override fun generateCopyForSources(): String { + return "COPY src-gen src-gen" + } + + override fun defaultImage(): String { + return DEFAULT_BASE_IMAGE + } + + override fun generateRunForInstallingDeps(): String { + return if (builderBase() == defaultImage()) { + ("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 { + "# (Skipping installation of build dependencies; custom base image.)" + } + } + + override fun defaultEntryPoint(): List { + return listOf("./bin/" + context.fileConfig.name) + } + + override fun generateCopyOfExecutable(): String { + return java.lang.String.join( + "\n", + super.generateCopyOfExecutable(), + "COPY --from=builder /usr/local/lib /usr/local/lib", + "COPY --from=builder /usr/lib /usr/lib", + "COPY --from=builder /lingua-franca ." + ) + } + + override fun defaultBuildCommands(): List { + val mkdirCommand = listOf("mkdir", "-p", "build") + val commands = listOf( + mkdirCommand, + createCmakeCommand( + Path.of("./build"), + Path.of("."), + listOf("-DREACTOR_CPP_LINK_EXECINFO=ON"), + "src-gen" + ).command(), + createMakeCommand(fileConfig.buildPath, true, fileConfig.name).command(), + createMakeCommand(Path.of("./build"), true, "install").command() + ) + return commands.map { argListToCommand(it) } + } + } + + override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = CppDockerGenerator(context) } + + From 64d9be7ace72bd56b9aaa847028215b5ed18b452 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 16:13:33 +0200 Subject: [PATCH 10/14] ros2 support for docker generation --- .../generator/cpp/CppPlatformGenerator.kt | 2 +- .../lflang/generator/cpp/CppRos2Generator.kt | 49 ++++++++++++++++--- .../generator/cpp/CppRos2PackageGenerator.kt | 2 +- .../generator/cpp/CppStandaloneGenerator.kt | 4 +- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt index 30b9a78d6e..799d0125c2 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppPlatformGenerator.kt @@ -29,7 +29,7 @@ abstract class CppPlatformGenerator(protected val generator: CppGenerator) { abstract fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean - val cmakeArgs: List + protected val cmakeArgs: List get() = listOf( "-DCMAKE_BUILD_TYPE=${targetConfig.get(BuildTypeProperty.INSTANCE)}", "-DREACTOR_CPP_VALIDATE=${if (targetConfig.get(NoRuntimeValidationProperty.INSTANCE)) "OFF" else "ON"}", diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt index 06bfadc98e..70aa9cdb4a 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt @@ -1,10 +1,9 @@ package org.lflang.generator.cpp -import jakarta.ws.rs.NotSupportedException import org.lflang.generator.LFGeneratorContext import org.lflang.generator.docker.DockerGenerator +import org.lflang.target.property.DockerProperty import org.lflang.util.FileUtil -import org.lflang.util.LFCommand import java.nio.file.Path /** C++ platform generator for the ROS2 platform.*/ @@ -15,6 +14,10 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator private val nodeGenerator = CppRos2NodeGenerator(mainReactor, targetConfig, fileConfig); private val packageGenerator = CppRos2PackageGenerator(generator, nodeGenerator.nodeName) + companion object { + const val DEFAULT_BASE_IMAGE: String = "ros:rolling-ros-base" + } + override fun generatePlatformFiles() { FileUtil.writeToFile( nodeGenerator.generateHeader(), @@ -33,7 +36,11 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator packagePath.resolve("CMakeLists.txt"), true ) - val scriptPath = fileConfig.binPath.resolve(fileConfig.name); + val scriptPath = + if (targetConfig.get(DockerProperty.INSTANCE).enabled) + fileConfig.srcGenPath.resolve("bin").resolve(fileConfig.name) + else + fileConfig.binPath.resolve(fileConfig.name) FileUtil.writeToFile(packageGenerator.generateBinScript(), scriptPath) scriptPath.toFile().setExecutable(true); } @@ -51,7 +58,7 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator } val colconCommand = commandFactory.createCommand( "colcon", colconArgs(), fileConfig.outPath) - val returnCode = colconCommand?.run(context.cancelIndicator) + val returnCode = colconCommand?.run(context.cancelIndicator) if (returnCode != 0 && !messageReporter.errorsOccurred) { // If errors occurred but none were reported, then the following message is the best we can do. messageReporter.nowhere().error("colcon failed with error code $returnCode") @@ -60,10 +67,6 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator return !messageReporter.errorsOccurred } - override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator { - TODO("Not yet implemented") - } - private fun colconArgs(additionalCmakeArgs: List = listOf()): List { return listOf( "build", @@ -75,5 +78,35 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator ) + cmakeArgs + additionalCmakeArgs } + inner class CppDockerGenerator(context: LFGeneratorContext?) : DockerGenerator(context) { + override fun generateCopyForSources() = + """ + COPY src-gen src-gen + COPY bin bin + """.trimIndent() + + override fun defaultImage(): String = DEFAULT_BASE_IMAGE + + override fun generateRunForInstallingDeps(): String = "" + + override fun defaultEntryPoint(): List = listOf("./bin/" + fileConfig.name) + + override fun generateCopyOfExecutable(): String = + """ + ${super.generateCopyOfExecutable()} + COPY --from=builder lingua-franca/${fileConfig.name}/install install + """.trimIndent() + + override fun defaultBuildCommands(): List { + val commands = listOf( + listOf(".", "/opt/ros/rolling/setup.sh"), + listOf("mkdir", "-p", "build"), + listOf("colcon") + colconArgs(listOf("-DREACTOR_CPP_LINK_EXECINFO=ON")), + ) + return commands.map { argListToCommand(it) } + } + } + + override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = CppDockerGenerator(context) } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2PackageGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2PackageGenerator.kt index ea7568e2c4..c3ce84c747 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2PackageGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2PackageGenerator.kt @@ -113,7 +113,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str return """ |#!/bin/bash |script_dir="$S(dirname -- "$S(readlink -f -- "${S}0")")" - |source "$S{script_dir}/$relPath/install/setup.sh" + |source "$S{script_dir}/$relPath/install/setup.bash" |ros2 run ${fileConfig.name} ${fileConfig.name}_exe """.trimMargin() } diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt index fda17fb0c4..1dddb69bd1 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -195,7 +195,7 @@ class CppStandaloneGenerator(generator: CppGenerator) : return cmd } - inner class CppDockerGenerator(context: LFGeneratorContext?) : DockerGenerator(context) { + inner class StandaloneDockerGenerator(context: LFGeneratorContext?) : DockerGenerator(context) { override fun generateCopyForSources(): String { return "COPY src-gen src-gen" } @@ -245,7 +245,7 @@ class CppStandaloneGenerator(generator: CppGenerator) : } } - override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = CppDockerGenerator(context) + override fun getDockerGenerator(context: LFGeneratorContext?): DockerGenerator = StandaloneDockerGenerator(context) } From 647c8ebb780d137a0aa902c25f9823da606e8b07 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 16:17:24 +0200 Subject: [PATCH 11/14] use more Kotlin --- .../generator/cpp/CppStandaloneGenerator.kt | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt index 1dddb69bd1..3e52038193 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -196,13 +196,10 @@ class CppStandaloneGenerator(generator: CppGenerator) : } inner class StandaloneDockerGenerator(context: LFGeneratorContext?) : DockerGenerator(context) { - override fun generateCopyForSources(): String { - return "COPY src-gen src-gen" - } - override fun defaultImage(): String { - return DEFAULT_BASE_IMAGE - } + override fun generateCopyForSources(): String = "COPY src-gen src-gen" + + override fun defaultImage(): String = DEFAULT_BASE_IMAGE override fun generateRunForInstallingDeps(): String { return if (builderBase() == defaultImage()) { @@ -214,19 +211,15 @@ class CppStandaloneGenerator(generator: CppGenerator) : } } - override fun defaultEntryPoint(): List { - return listOf("./bin/" + context.fileConfig.name) - } + override fun defaultEntryPoint(): List = listOf("./bin/" + context.fileConfig.name) - override fun generateCopyOfExecutable(): String { - return java.lang.String.join( - "\n", - super.generateCopyOfExecutable(), - "COPY --from=builder /usr/local/lib /usr/local/lib", - "COPY --from=builder /usr/lib /usr/lib", - "COPY --from=builder /lingua-franca ." - ) - } + override fun generateCopyOfExecutable(): String = + """ + ${super.generateCopyOfExecutable()} + COPY --from=builder /usr/local/lib /usr/local/lib + COPY --from=builder /usr/lib /usr/lib + COPY --from=builder /lingua-franca . + """.trimIndent() override fun defaultBuildCommands(): List { val mkdirCommand = listOf("mkdir", "-p", "build") From c1f4502f92d583951b065c1d5a53f70bfdcbb063 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 16:25:11 +0200 Subject: [PATCH 12/14] run ros2 tests also with docker --- .../java/org/lflang/tests/runtime/CppRos2Test.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java b/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java index 9ddae14d32..334f9ef7aa 100644 --- a/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java +++ b/core/src/integrationTest/java/org/lflang/tests/runtime/CppRos2Test.java @@ -5,7 +5,6 @@ import org.lflang.lf.Element; import org.lflang.lf.LfFactory; import org.lflang.target.Target; -import org.lflang.target.property.DockerProperty; import org.lflang.target.property.Ros2Property; import org.lflang.tests.TestBase; import org.lflang.tests.Transformers; @@ -34,7 +33,6 @@ public void runWithRos2() { it -> true, Transformers::noChanges, config -> { - if (config.get(DockerProperty.INSTANCE) != null) return false; Ros2Property.INSTANCE.override(config, true); return true; }, From e5cfc623f9ec81aa6b8a241147977fbf0adaa5d2 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 16:28:39 +0200 Subject: [PATCH 13/14] no need to link to execinfo in ros2 --- .../kotlin/org/lflang/generator/cpp/CppRos2Generator.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt index 70aa9cdb4a..2df1fdaa92 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppRos2Generator.kt @@ -67,7 +67,7 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator return !messageReporter.errorsOccurred } - private fun colconArgs(additionalCmakeArgs: List = listOf()): List { + private fun colconArgs(): List { return listOf( "build", "--packages-select", @@ -75,7 +75,7 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator packageGenerator.reactorCppName, "--cmake-args", "-DLF_REACTOR_CPP_SUFFIX=${packageGenerator.reactorCppSuffix}", - ) + cmakeArgs + additionalCmakeArgs + ) + cmakeArgs } inner class CppDockerGenerator(context: LFGeneratorContext?) : DockerGenerator(context) { @@ -101,7 +101,7 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator val commands = listOf( listOf(".", "/opt/ros/rolling/setup.sh"), listOf("mkdir", "-p", "build"), - listOf("colcon") + colconArgs(listOf("-DREACTOR_CPP_LINK_EXECINFO=ON")), + listOf("colcon") + colconArgs(), ) return commands.map { argListToCommand(it) } } From c5f50f310d14f4e772104c93749c519feb84f31a Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 19 Jun 2024 16:36:16 +0200 Subject: [PATCH 14/14] comment and function rename --- .../kotlin/org/lflang/generator/cpp/CppGenerator.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt index d64afb4d01..adfbbb9dda 100644 --- a/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/cpp/CppGenerator.kt @@ -95,7 +95,7 @@ class CppGenerator( "Code generation complete. Compiling...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS ) if (targetConfig.get(DockerProperty.INSTANCE).enabled) { - copySrcGenBaseDirIntoSrcGenDir() + copySrcGenBaseDirIntoDockerDir() buildUsingDocker() } else { if (platformGenerator.doCompile(context)) { @@ -107,9 +107,15 @@ class CppGenerator( } } - private fun copySrcGenBaseDirIntoSrcGenDir() { + /** + * Copy the contents of the entire src-gen directory to a nested src-gen directory next to the generated Dockerfile. + */ + private fun copySrcGenBaseDirIntoDockerDir() { FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen")) try { + // We need to copy in two steps via a temporary directory, as the target directory + // is located within the source directory. Without the temporary directory, copying + // fails as we modify the source while writing the target. val tempDir = Files.createTempDirectory(context.fileConfig.outPath, "src-gen-directory") try { FileUtil.copyDirectoryContents(context.fileConfig.srcGenBasePath, tempDir, false)