From 6285351074778dba940105e1cb040a43e8514416 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 15 Oct 2021 13:36:10 -0500 Subject: [PATCH 01/12] Always copy all platform files and let CMake decide which one to use --- .../org/lflang/generator/c/CGenerator.xtend | 97 +++++++++---------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index e619c530b9..4e3b321cc7 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1146,69 +1146,60 @@ class CGenerator extends GeneratorBase { } /** - * Add the appropriate platform files to 'coreFiles'. These platform files - * are specific to the OS/underlying hardware, which is detected here automatically. + * Add the platform files to 'coreFiles'. + * + * Also if useCmake is set to false, detect the host OS + * and add the appropriate platform support file(s) to + * the compile command (compileAdditionalSources). */ def addPlatformFiles(ArrayList coreFiles) { - // Check the operating system - val OS = System.getProperty("os.name").toLowerCase(); - // FIXME: allow for cross-compiling - // Based on the detected operating system, copy the required files - // to enable platform-specific functionality. See lib/c/reactor-c/core/platform.h - // for more detail. - if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) { - // Mac support - coreFiles.add("platform/lf_POSIX_threads_support.c") - coreFiles.add("platform/lf_C11_threads_support.c") - coreFiles.add("platform/lf_POSIX_threads_support.h") - coreFiles.add("platform/lf_C11_threads_support.h") - coreFiles.add("platform/lf_macos_support.c") - coreFiles.add("platform/lf_macos_support.h") - coreFiles.add("platform/lf_unix_clock_support.c") - // If there is no main reactor, then compilation will produce a .o file requiring further linking. - // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file - // will detect and use the appropriate platform file based on the platform that cmake is invoked on. - if (mainDef !== null && !targetConfig.useCmake) { + // If useCmake is set to true, we don't need to add platform files. + // The CMakeLists.txt file will detect and use the appropriate platform + // file based on the platform that cmake is invoked on. + // If useCmake is set to false, we have to detect the operating system + // here and add the appropriate platform support source file to the compile + // command. + if (mainDef !== null && !targetConfig.useCmake) { + // Check the operating system + val OS = System.getProperty("os.name").toLowerCase(); + // FIXME: allow for cross-compiling + // Based on the detected operating system, copy the required files + // to enable platform-specific functionality. See lib/c/reactor-c/core/platform.h + // for more detail. + if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) { + // Mac support targetConfig.compileAdditionalSources.add( - "core" + File.separator + "platform" + File.separator + "lf_macos_support.c" + "core" + File.separator + "platform" + File.separator + "lf_macos_support.c" ); - } - } else if (OS.indexOf("win") >= 0) { - // Windows support - coreFiles.add("platform/lf_C11_threads_support.c") - coreFiles.add("platform/lf_C11_threads_support.h") - coreFiles.add("platform/lf_windows_support.c") - coreFiles.add("platform/lf_windows_support.h") - // For 64-bit epoch time - coreFiles.add("platform/lf_unix_clock_support.c") - // If there is no main reactor, then compilation will produce a .o file requiring further linking. - // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file - // will detect and use the appropriate platform file based on the platform that cmake is invoked on. - if (mainDef !== null && !targetConfig.useCmake) { + } else if (OS.indexOf("win") >= 0) { + // Windows support targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_windows_support.c" ) - } - } else if (OS.indexOf("nux") >= 0) { - // Linux support - coreFiles.add("platform/lf_POSIX_threads_support.c") - coreFiles.add("platform/lf_C11_threads_support.c") - coreFiles.add("platform/lf_POSIX_threads_support.h") - coreFiles.add("platform/lf_C11_threads_support.h") - coreFiles.add("platform/lf_linux_support.c") - coreFiles.add("platform/lf_linux_support.h") - coreFiles.add("platform/lf_unix_clock_support.c") - // If there is no main reactor, then compilation will produce a .o file requiring further linking. - // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file - // will detect and use the appropriate platform file based on the platform that cmake is invoked on. - if (mainDef !== null && !targetConfig.useCmake) { + } else if (OS.indexOf("nux") >= 0) { + // Linux support targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_linux_support.c" ) - } - } else { - errorReporter.reportError("Platform " + OS + " is not supported") - } + } else { + errorReporter.reportError("Platform " + OS + " is not supported") + } + } + + coreFiles.addAll( + "platform/lf_POSIX_threads_support.c", + "platform/lf_C11_threads_support.c", + "platform/lf_C11_threads_support.h", + "platform/lf_POSIX_threads_support.h", + "platform/lf_POSIX_threads_support.c", + "platform/lf_unix_clock_support.c", + "platform/lf_macos_support.c", + "platform/lf_macos_support.h", + "platform/lf_windows_support.c", + "platform/lf_windows_support.h", + "platform/lf_linux_support.c", + "platform/lf_linux_support.h" + ) } /** From e55348bae47ceaeef8e864744090e9d6246d4efd Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Wed, 20 Oct 2021 23:02:12 -0700 Subject: [PATCH 02/12] use cmake in c-target dockerfile generation --- .../org/lflang/generator/c/CGenerator.xtend | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 4e3b321cc7..e71e5c75ba 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1242,8 +1242,12 @@ class CGenerator extends GeneratorBase { if (file.exists) { file.delete } - // The Docker configuration uses gcc, so config.compiler is ignored here. - var compileCommand = '''gcc «targetConfig.compilerFlags.join(" ")» src-gen/«filename».c -o bin/«filename»''' + // The Docker configuration uses cmake, so config.compiler is ignored here. + var compileCommand = ''' + cmake -S src-gen -B bin && \ + cd bin && \ + make all + ''' if (!targetConfig.buildCommands.nullOrEmpty) { compileCommand = targetConfig.buildCommands.join(' ') } @@ -1254,17 +1258,22 @@ class CGenerator extends GeneratorBase { pr(contents, ''' # Generated docker file for «topLevelName».lf in «srcGenPath». # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution - FROM «targetConfig.dockerOptions.from» - WORKDIR /lingua-franca - COPY src-gen/core src-gen/core - COPY "src-gen/«filename».c" "src-gen/ctarget.h" "src-gen/" + FROM «targetConfig.dockerOptions.from» AS builder + WORKDIR /lingua-franca/«topLevelName» + RUN set -ex && apk add --no-cache gcc musl-dev cmake make + COPY src-gen/«topLevelName»/core src-gen/core + COPY "src-gen/«topLevelName»/ctarget.h" "src-gen/«topLevelName»/ctarget.c" "src-gen/" + COPY "src-gen/«topLevelName»/«filename».c" "src-gen/«topLevelName»/CMakeLists.txt" "src-gen/" «additionalFiles» RUN set -ex && \ - apk add --no-cache gcc musl-dev && \ mkdir bin && \ - «compileCommand» && \ - apk del gcc musl-dev && \ - rm -rf src-gen + «compileCommand» + + FROM «targetConfig.dockerOptions.from» + WORKDIR /lingua-franca + RUN mkdir bin + COPY --from=builder /lingua-franca/«topLevelName»/bin/«filename» ./bin/«filename» + # Use ENTRYPOINT not CMD so that command-line arguments go through ENTRYPOINT ["./bin/«filename»"] ''') @@ -1425,7 +1434,7 @@ class CGenerator extends GeneratorBase { lf_thread_create(&_fed.inbound_p2p_handling_thread_id, handle_p2p_connections_from_federates, NULL); ''') } - + for (remoteFederate : federate.outboundP2PConnections) { pr('''connect_to_federate(«remoteFederate.id»);''') } From 1eccad937d637d49285fceffa8f1b5defd24a243 Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Thu, 21 Oct 2021 00:31:46 -0700 Subject: [PATCH 03/12] add Ccpp target docker file support --- org.lflang/src/org/lflang/generator/c/CGenerator.xtend | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index e71e5c75ba..f0771b4210 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1255,15 +1255,19 @@ class CGenerator extends GeneratorBase { if (!targetConfig.fileNames.nullOrEmpty) { additionalFiles = '''COPY "«targetConfig.fileNames.join('" "')»" "src-gen/"''' } + pr(contents, ''' # Generated docker file for «topLevelName».lf in «srcGenPath». # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution FROM «targetConfig.dockerOptions.from» AS builder WORKDIR /lingua-franca/«topLevelName» - RUN set -ex && apk add --no-cache gcc musl-dev cmake make + RUN set -ex && apk add --no-cache gcc musl-dev g++ cmake make COPY src-gen/«topLevelName»/core src-gen/core + # If target is C, .c file is copied. If target is CCPP, .cpp file is copied COPY "src-gen/«topLevelName»/ctarget.h" "src-gen/«topLevelName»/ctarget.c" "src-gen/" - COPY "src-gen/«topLevelName»/«filename».c" "src-gen/«topLevelName»/CMakeLists.txt" "src-gen/" + COPY "src-gen/«topLevelName»/CMakeLists.txt" \ + "src-gen/«topLevelName»/«filename».[c]" \ + "src-gen/«topLevelName»/«filename».cp[p]" "src-gen/" «additionalFiles» RUN set -ex && \ mkdir bin && \ From 8903b5711c6a3aeec5d95cf4760dda26b0b4d5e7 Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Thu, 21 Oct 2021 17:29:38 -0700 Subject: [PATCH 04/12] add docker build message --- .../org/lflang/generator/c/CGenerator.xtend | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index f0771b4210..ab16feba12 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1255,19 +1255,24 @@ class CGenerator extends GeneratorBase { if (!targetConfig.fileNames.nullOrEmpty) { additionalFiles = '''COPY "«targetConfig.fileNames.join('" "')»" "src-gen/"''' } + var dockerCompiler = 'gcc' + var fileExtension = 'c' + + if (CCppMode) { + dockerCompiler = 'g++' + fileExtension = 'cpp' + } pr(contents, ''' # Generated docker file for «topLevelName».lf in «srcGenPath». # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution FROM «targetConfig.dockerOptions.from» AS builder WORKDIR /lingua-franca/«topLevelName» - RUN set -ex && apk add --no-cache gcc musl-dev g++ cmake make + RUN set -ex && apk add --no-cache «dockerCompiler» musl-dev cmake make COPY src-gen/«topLevelName»/core src-gen/core - # If target is C, .c file is copied. If target is CCPP, .cpp file is copied COPY "src-gen/«topLevelName»/ctarget.h" "src-gen/«topLevelName»/ctarget.c" "src-gen/" COPY "src-gen/«topLevelName»/CMakeLists.txt" \ - "src-gen/«topLevelName»/«filename».[c]" \ - "src-gen/«topLevelName»/«filename».cp[p]" "src-gen/" + "src-gen/«topLevelName»/«filename».«fileExtension»" "src-gen/" «additionalFiles» RUN set -ex && \ mkdir bin && \ @@ -1283,6 +1288,14 @@ class CGenerator extends GeneratorBase { ''') writeSourceCodeToFile(contents.toString.getBytes, dockerFile) println("Dockerfile written to " + dockerFile) + println(''' + ##################################### + To build the docker image, use: + + docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «fileConfig.getOutPath» + + ##################################### + ''') } /** From 8a455871f778c71e69f1a17dc961c9e9b1f98fdc Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Thu, 21 Oct 2021 18:44:16 -0700 Subject: [PATCH 05/12] add docker property for python target --- org.lflang/src/org/lflang/TargetProperty.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/TargetProperty.java b/org.lflang/src/org/lflang/TargetProperty.java index 8e83b88ccf..899d8bdbdb 100644 --- a/org.lflang/src/org/lflang/TargetProperty.java +++ b/org.lflang/src/org/lflang/TargetProperty.java @@ -167,7 +167,7 @@ public enum TargetProperty { * true or false, or a dictionary of options. */ DOCKER("docker", UnionType.DOCKER_UNION, - Arrays.asList(Target.C, Target.CCPP), (config, value) -> { + Arrays.asList(Target.C, Target.CCPP, Target.Python), (config, value) -> { if (value.getLiteral() != null) { if (ASTUtils.toBoolean(value)) { config.dockerOptions = new DockerOptions(); From 8aa32cb0ce450c9b15e232ef556ccc1778665fe0 Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Thu, 21 Oct 2021 19:36:49 -0700 Subject: [PATCH 06/12] add docker file generation for python target --- .../lflang/generator/PythonGenerator.xtend | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index 8e2473dbc5..5fd7aef6bf 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -1053,13 +1053,21 @@ class PythonGenerator extends CGenerator { targetConfig.noCompile = true; targetConfig.useCmake = false; // Force disable the CMake because // it interferes with the Python target functionality + var dockerOptions = targetConfig.dockerOptions; + targetConfig.dockerOptions = null; super.doGenerate(resource, fsa, context) targetConfig.noCompile = compileStatus + targetConfig.dockerOptions = dockerOptions if (errorsOccurred) return; - + + // Create docker file. + if (targetConfig.dockerOptions !== null) { + writeDockerFile(topLevelName) + } + var baseFileName = topLevelName for (federate : federates) { if (isFederated) { @@ -1092,7 +1100,6 @@ class PythonGenerator extends CGenerator { printRunInfo(); } } - } // Restore filename topLevelName = baseFileName @@ -1699,6 +1706,51 @@ class PythonGenerator extends CGenerator { } } + /** + * Write a Dockerfile for the current federate as given by filename. + * The file will go into src-gen/filename.Dockerfile. + * If there is no main reactor, then no Dockerfile will be generated + * (it wouldn't be very useful). + * @param The root filename (without any extension). + */ + override writeDockerFile(String filename) { + if (this.mainDef === null) { + return + } + + var srcGenPath = fileConfig.getSrcGenPath + val dockerFile = srcGenPath + File.separator + filename + '.Dockerfile' + val contents = new StringBuilder() + + // If a dockerfile exists, remove it. + var file = new File(dockerFile) + if (file.exists) { + file.delete + } + + pr(contents, ''' + # Generated docker file for «topLevelName».lf in «srcGenPath». + # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution + FROM python:alpine + WORKDIR /lingua-franca/«topLevelName» + COPY src-gen/«topLevelName» src-gen + RUN set -ex && apk add --no-cache gcc musl-dev \ + && cd src-gen && python3 setup.py install && cd .. \ + && apk del gcc musl-dev + ENTRYPOINT ["python3", "src-gen/«filename».py"] + ''') + writeSourceCodeToFile(contents.toString.getBytes, dockerFile) + println("Dockerfile written to " + dockerFile) + println(''' + ##################################### + To build the docker image, use: + + docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «fileConfig.getOutPath» + + ##################################### + ''') + } + /** * Convert C types to formats used in Py_BuildValue and PyArg_PurseTuple. * This is unused but will be useful to enable inter-compatibility between From 6a66af7107b46c34d5004dbc8d5ebcdcfd31cbb5 Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Fri, 29 Oct 2021 12:13:14 -0700 Subject: [PATCH 07/12] add dockerfile generation for RTI --- .../org/lflang/generator/c/CGenerator.xtend | 91 ++++++++++++++++--- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index ab16feba12..2ae283750f 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -27,6 +27,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package org.lflang.generator.c import java.io.File +import java.nio.file.Path import java.util.ArrayList import java.util.Collection import java.util.LinkedHashMap @@ -841,6 +842,15 @@ class CGenerator extends GeneratorBase { // Create docker file. if (targetConfig.dockerOptions !== null) { + if (isFederated) { + var rtiPath = fileConfig.getSrcGenBasePath().resolve("RTI") + var rtiDir = rtiPath.toFile() + if (!rtiDir.exists()) { + rtiDir.mkdirs() + writeRTIDockerFile(rtiPath, rtiDir) + copyRtiFiles(rtiDir, coreFiles) + } + } writeDockerFile(topLevelName) } @@ -1226,15 +1236,15 @@ class CGenerator extends GeneratorBase { * The file will go into src-gen/filename.Dockerfile. * If there is no main reactor, then no Dockerfile will be generated * (it wouldn't be very useful). - * @param The root filename (without any extension). + * @param the name given to the docker file (without any extension). */ - def writeDockerFile(String filename) { + def writeDockerFile(String dockerFileName) { if (this.mainDef === null) { return } var srcGenPath = fileConfig.getSrcGenPath - val dockerFile = srcGenPath + File.separator + filename + '.Dockerfile' + val dockerFile = srcGenPath + File.separator + dockerFileName + '.Dockerfile' val contents = new StringBuilder() // If a dockerfile exists, remove it. @@ -1264,15 +1274,15 @@ class CGenerator extends GeneratorBase { } pr(contents, ''' - # Generated docker file for «topLevelName».lf in «srcGenPath». + # Generated docker file for «topLevelName» in «srcGenPath». # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution FROM «targetConfig.dockerOptions.from» AS builder WORKDIR /lingua-franca/«topLevelName» RUN set -ex && apk add --no-cache «dockerCompiler» musl-dev cmake make - COPY src-gen/«topLevelName»/core src-gen/core - COPY "src-gen/«topLevelName»/ctarget.h" "src-gen/«topLevelName»/ctarget.c" "src-gen/" - COPY "src-gen/«topLevelName»/CMakeLists.txt" \ - "src-gen/«topLevelName»/«filename».«fileExtension»" "src-gen/" + COPY core src-gen/core + COPY ctarget.h ctarget.c src-gen/ + COPY CMakeLists.txt \ + «topLevelName».«fileExtension» src-gen/ «additionalFiles» RUN set -ex && \ mkdir bin && \ @@ -1281,22 +1291,79 @@ class CGenerator extends GeneratorBase { FROM «targetConfig.dockerOptions.from» WORKDIR /lingua-franca RUN mkdir bin - COPY --from=builder /lingua-franca/«topLevelName»/bin/«filename» ./bin/«filename» + COPY --from=builder /lingua-franca/«topLevelName»/bin/«topLevelName» ./bin/«topLevelName» # Use ENTRYPOINT not CMD so that command-line arguments go through - ENTRYPOINT ["./bin/«filename»"] + ENTRYPOINT ["./bin/«topLevelName»"] + ''') + writeSourceCodeToFile(contents.toString.getBytes, dockerFile) + println('''Dockerfile for «topLevelName» written to ''' + dockerFile) + println(''' + ##################################### + To build the docker image, use: + + docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «srcGenPath» + + ##################################### + ''') + } + + /** + * Write a Dockerfile for the RTI at rtiDir. + * The file will go into src-gen/RTI/rti.Dockerfile. + * @param the directory where rti.Dockerfile will be written to. + */ + def writeRTIDockerFile(Path rtiPath, File rtiDir) { + val dockerFileName = 'rti.Dockerfile' + val dockerFile = rtiDir + File.separator + dockerFileName + val contents = new StringBuilder() + pr(contents, ''' + # Generated docker file for RTI in «rtiDir». + # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution + FROM alpine:latest + WORKDIR /lingua-franca/RTI + COPY core core + WORKDIR core/federated/RTI + RUN set -ex && apk add --no-cache gcc musl-dev cmake make && \ + mkdir build && \ + cd build && \ + cmake ../ && \ + make && \ + make install + + # Use ENTRYPOINT not CMD so that command-line arguments go through + ENTRYPOINT ["./build/RTI"] ''') writeSourceCodeToFile(contents.toString.getBytes, dockerFile) - println("Dockerfile written to " + dockerFile) + println("Dockerfile for RTI written to " + dockerFile) println(''' ##################################### To build the docker image, use: - docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «fileConfig.getOutPath» + docker build -t rti -f «dockerFile» «rtiDir» ##################################### ''') } + + /** + * Write a Dockerfile for the RTI at rtiDir. + * The file will go into src-gen/RTI/rti.Dockerfile. + * @param the directory where rti.Dockerfile is located. + * @param the core files used for code generation in the current target. + */ + def copyRtiFiles(File rtiDir, ArrayList coreFiles) { + var rtiFiles = newArrayList() + rtiFiles.addAll(coreFiles) + + // add the RTI files on top of the coreFiles + rtiFiles.addAll( + "federated/RTI/rti.h", + "federated/RTI/rti.c", + "federated/RTI/CMakeLists.txt" + ) + fileConfig.copyFilesFromClassPath("/lib/c/reactor-c/core", rtiDir + File.separator + "core", rtiFiles) + } /** * Initialize clock synchronization (if enabled) and its related options for a given federate. From 7104686fa162a89286b6497f65b0f59f34f468ad Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Fri, 29 Oct 2021 12:29:20 -0700 Subject: [PATCH 08/12] add containerized federated example --- .../HelloWorld.lf | 67 +++++++++++++++++++ .../README.md | 51 ++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf create mode 100644 example/C/src/ContainerizedFederatedHelloWorld/README.md diff --git a/example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf b/example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf new file mode 100644 index 0000000000..83f4b3b164 --- /dev/null +++ b/example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf @@ -0,0 +1,67 @@ +/** + * Containerized distributed LF program where a MessageGenerator creates a string + * message that is sent via the RTI (runtime infrastructure) to a + * receiver that prints the message. + * + * For run instructions, see README. + * + * @author Edward A. Lee + */ +target C { + timeout: 10 secs, + docker: true +}; + +/** + * Reactor that generates a sequence of messages, one per second. + * The message will be a string consisting of a prefix string followed + * by a count. + * @param prefix The prefix string. + * @output message The message. + */ +reactor MessageGenerator(prefix:string("")) { + // Output type char* instead of string is used for dynamically + // allocated character arrays (as opposed to static constant strings). + output message:char*; + state count:int(1); + // Send first message after 1 sec so that the startup reactions + // do not factor into the transport time measurement on the first message. + timer t(1 sec, 1 sec); + reaction(t) -> message {= + // With NULL, 0 arguments, snprintf tells us how many bytes are needed. + // Add one for the null terminator. + int length = snprintf(NULL, 0, "%s %d", self->prefix, self->count) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(message, length); + // Populate the output string and increment the count. + snprintf(message->value, length, "%s %d", self->prefix, self->count++); + + tag_t tag = get_current_tag(); + info_print("At (elapsed) logical tag (%lld, %u), source sends message: %s", + tag.time - start_time, tag.microstep, + message->value + ); + =} +} + +/** + * Reactor that prints the current tag and an incoming string. + * + * @input message The message. + */ +reactor PrintMessage { + input message:char*; + reaction(message) {= + tag_t tag = get_current_tag(); + info_print("At (elapsed) logical tag (%lld, %u), print receives: %s", + tag.time - start_time, tag.microstep, + message->value + ); + =} +} + +federated reactor HelloWorld at rti { + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage(); + source.message -> print.message; +} \ No newline at end of file diff --git a/example/C/src/ContainerizedFederatedHelloWorld/README.md b/example/C/src/ContainerizedFederatedHelloWorld/README.md new file mode 100644 index 0000000000..4b784a6c37 --- /dev/null +++ b/example/C/src/ContainerizedFederatedHelloWorld/README.md @@ -0,0 +1,51 @@ +# ContainerizedFederatedHelloWorld +This is a basic example showing containerized federated execution. + +For more details, see: https://github.com/icyphy/lingua-franca/wiki/ + +## Run instructions +Put HelloWorld.lf in a src directory. +Then, run: +```bash +lfc src/HelloWorld.lf +``` + +There would be 3 build messages, 1 for the RTI and 2 for the reactors, indicating where the docker file is generated, as well as the instruction to build the docker file. + +A message would look something like this: +``` +Dockerfile for written to / +##################################### +To build the docker image, use: + + docker build -t -f / + +##################################### +``` + +If you cannot find the build message for the RTI, try a clean build using: +```bash +lfc --clean src/HelloWorld.lf +``` + +Then, use the printed commands to build the 3 docker images. + +Set up a docker network using +```bash +docker network create lf +``` + +Open 3 terminals, 1 for the RTI and 1 for each reactor. + +Run the RTI: +```bash +docker run --rm -it --network=lf --name rti rti -i 1 -n 2 +``` + +Run the two reactors: +```bash +docker run --rm -it --network=lf helloworld_a -i 1 +``` +```bash +docker run --rm -it --network=lf helloworld_b -i 1 +``` \ No newline at end of file From 64d69caf8b3e0db7413462a23c0f21e1f1a582aa Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Fri, 29 Oct 2021 22:49:20 -0700 Subject: [PATCH 09/12] fix add platform files --- .../org/lflang/generator/c/CGenerator.xtend | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 64b0806888..aa08497747 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1158,11 +1158,8 @@ class CGenerator extends GeneratorBase { } /** - * Add the platform files to 'coreFiles'. - * - * Also if useCmake is set to false, detect the host OS - * and add the appropriate platform support file(s) to - * the compile command (compileAdditionalSources). + * Add the appropriate platform files to 'coreFiles'. These platform files + * are specific to the OS/underlying hardware, which is detected here automatically. */ def addPlatformFiles(ArrayList coreFiles) { // All platforms use this one. @@ -1175,49 +1172,52 @@ class CGenerator extends GeneratorBase { // for more detail. if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) { // Mac support - coreFiles.add("platform/lf_POSIX_threads_support.c") - coreFiles.add("platform/lf_C11_threads_support.c") - coreFiles.add("platform/lf_POSIX_threads_support.h") - coreFiles.add("platform/lf_C11_threads_support.h") - coreFiles.add("platform/lf_macos_support.c") - coreFiles.add("platform/lf_macos_support.h") - coreFiles.add("platform/lf_unix_clock_support.c") // If there is no main reactor, then compilation will produce a .o file requiring further linking. // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file // will detect and use the appropriate platform file based on the platform that cmake is invoked on. if (mainDef !== null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( - "core" + File.separator + "platform" + File.separator + "lf_macos_support.c" + "core" + File.separator + "platform" + File.separator + "lf_macos_support.c" ); - } else if (OS.indexOf("win") >= 0) { - // Windows support + } + } else if (OS.indexOf("win") >= 0) { + // Windows support + // If there is no main reactor, then compilation will produce a .o file requiring further linking. + // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file + // will detect and use the appropriate platform file based on the platform that cmake is invoked on. + if (mainDef !== null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_windows_support.c" ) - } else if (OS.indexOf("nux") >= 0) { - // Linux support + } + } else if (OS.indexOf("nux") >= 0) { + // Linux support + // If there is no main reactor, then compilation will produce a .o file requiring further linking. + // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file + // will detect and use the appropriate platform file based on the platform that cmake is invoked on. + if (mainDef !== null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_linux_support.c" ) - } else { - errorReporter.reportError("Platform " + OS + " is not supported") } + } else { + errorReporter.reportError("Platform " + OS + " is not supported") } coreFiles.addAll( - "platform/lf_POSIX_threads_support.c", - "platform/lf_C11_threads_support.c", - "platform/lf_C11_threads_support.h", - "platform/lf_POSIX_threads_support.h", - "platform/lf_POSIX_threads_support.c", - "platform/lf_unix_clock_support.c", - "platform/lf_macos_support.c", - "platform/lf_macos_support.h", - "platform/lf_windows_support.c", - "platform/lf_windows_support.h", - "platform/lf_linux_support.c", - "platform/lf_linux_support.h" - ) + "platform/lf_POSIX_threads_support.c", + "platform/lf_C11_threads_support.c", + "platform/lf_C11_threads_support.h", + "platform/lf_POSIX_threads_support.h", + "platform/lf_POSIX_threads_support.c", + "platform/lf_unix_clock_support.c", + "platform/lf_macos_support.c", + "platform/lf_macos_support.h", + "platform/lf_windows_support.c", + "platform/lf_windows_support.h", + "platform/lf_linux_support.c", + "platform/lf_linux_support.h" + ) } /** From 10cd128345dea07eee015d8d1635470c749b5271 Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Sat, 30 Oct 2021 11:54:53 -0700 Subject: [PATCH 10/12] update code according to code review --- .../README.md | 2 +- .../org/lflang/generator/GeneratorBase.xtend | 28 +++++++++++++++++++ .../lflang/generator/PythonGenerator.xtend | 12 ++------ .../org/lflang/generator/c/CGenerator.xtend | 21 +------------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/example/C/src/ContainerizedFederatedHelloWorld/README.md b/example/C/src/ContainerizedFederatedHelloWorld/README.md index 4b784a6c37..034cfd11c5 100644 --- a/example/C/src/ContainerizedFederatedHelloWorld/README.md +++ b/example/C/src/ContainerizedFederatedHelloWorld/README.md @@ -1,7 +1,7 @@ # ContainerizedFederatedHelloWorld This is a basic example showing containerized federated execution. -For more details, see: https://github.com/icyphy/lingua-franca/wiki/ +For more details, see: [Containerized Execution in Lingua Franca](https://github.com/lf-lang/lingua-franca/wiki/Containerized-Execution) ## Run instructions Put HelloWorld.lf in a src directory. diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index 12b53ec62c..2801003246 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -952,6 +952,34 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes return false } + /** + * Copy the core files needed to build the RTI within a container. + * + * @param the directory where rti.Dockerfile is located. + * @param the core files used for code generation in the current target. + */ + def copyRtiFiles(File rtiDir, ArrayList coreFiles) { + var rtiFiles = newArrayList() + rtiFiles.addAll(coreFiles) + + // add the RTI files on top of the coreFiles + rtiFiles.addAll( + "federated/RTI/rti.h", + "federated/RTI/rti.c", + "federated/RTI/CMakeLists.txt" + ) + fileConfig.copyFilesFromClassPath("/lib/c/reactor-c/core", rtiDir + File.separator + "core", rtiFiles) + } + + /** + * Write a Dockerfile for the current federate as given by filename. + * @param the name given to the docker file (without any extension). + */ + def writeDockerFile(String dockerFileName) { + throw new UnsupportedOperationException("This target does not support docker file generation.") + } + + /** * Parsed error message from a compiler is returned here. */ diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index 5fd7aef6bf..fb69b75ae5 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -1053,21 +1053,13 @@ class PythonGenerator extends CGenerator { targetConfig.noCompile = true; targetConfig.useCmake = false; // Force disable the CMake because // it interferes with the Python target functionality - var dockerOptions = targetConfig.dockerOptions; - targetConfig.dockerOptions = null; super.doGenerate(resource, fsa, context) targetConfig.noCompile = compileStatus - targetConfig.dockerOptions = dockerOptions if (errorsOccurred) return; - // Create docker file. - if (targetConfig.dockerOptions !== null) { - writeDockerFile(topLevelName) - } - var baseFileName = topLevelName for (federate : federates) { if (isFederated) { @@ -1733,7 +1725,7 @@ class PythonGenerator extends CGenerator { # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution FROM python:alpine WORKDIR /lingua-franca/«topLevelName» - COPY src-gen/«topLevelName» src-gen + COPY . src-gen RUN set -ex && apk add --no-cache gcc musl-dev \ && cd src-gen && python3 setup.py install && cd .. \ && apk del gcc musl-dev @@ -1745,7 +1737,7 @@ class PythonGenerator extends CGenerator { ##################################### To build the docker image, use: - docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «fileConfig.getOutPath» + docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «srcGenPath» ##################################### ''') diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index aa08497747..93903a06af 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1246,7 +1246,7 @@ class CGenerator extends GeneratorBase { * (it wouldn't be very useful). * @param the name given to the docker file (without any extension). */ - def writeDockerFile(String dockerFileName) { + override writeDockerFile(String dockerFileName) { if (this.mainDef === null) { return } @@ -1354,25 +1354,6 @@ class CGenerator extends GeneratorBase { ''') } - /** - * Write a Dockerfile for the RTI at rtiDir. - * The file will go into src-gen/RTI/rti.Dockerfile. - * @param the directory where rti.Dockerfile is located. - * @param the core files used for code generation in the current target. - */ - def copyRtiFiles(File rtiDir, ArrayList coreFiles) { - var rtiFiles = newArrayList() - rtiFiles.addAll(coreFiles) - - // add the RTI files on top of the coreFiles - rtiFiles.addAll( - "federated/RTI/rti.h", - "federated/RTI/rti.c", - "federated/RTI/CMakeLists.txt" - ) - fileConfig.copyFilesFromClassPath("/lib/c/reactor-c/core", rtiDir + File.separator + "core", rtiFiles) - } - /** * Initialize clock synchronization (if enabled) and its related options for a given federate. * From d7b2690b63b79f1e22a62c228011ad9d0bbf5b9e Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Mon, 1 Nov 2021 00:04:50 -0700 Subject: [PATCH 11/12] fix code according to PR comment --- .../HelloWorld.lf | 67 ------------------- .../docker/HelloWorldContainerized.lf | 22 ++++++ .../docker}/README.md | 10 +-- .../lflang/generator/PythonGenerator.xtend | 11 ++- .../org/lflang/generator/c/CGenerator.xtend | 11 ++- 5 files changed, 37 insertions(+), 84 deletions(-) delete mode 100644 example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf create mode 100644 example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf rename example/C/src/{ContainerizedFederatedHelloWorld => DistributedHelloWorld/docker}/README.md (82%) diff --git a/example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf b/example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf deleted file mode 100644 index 83f4b3b164..0000000000 --- a/example/C/src/ContainerizedFederatedHelloWorld/HelloWorld.lf +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Containerized distributed LF program where a MessageGenerator creates a string - * message that is sent via the RTI (runtime infrastructure) to a - * receiver that prints the message. - * - * For run instructions, see README. - * - * @author Edward A. Lee - */ -target C { - timeout: 10 secs, - docker: true -}; - -/** - * Reactor that generates a sequence of messages, one per second. - * The message will be a string consisting of a prefix string followed - * by a count. - * @param prefix The prefix string. - * @output message The message. - */ -reactor MessageGenerator(prefix:string("")) { - // Output type char* instead of string is used for dynamically - // allocated character arrays (as opposed to static constant strings). - output message:char*; - state count:int(1); - // Send first message after 1 sec so that the startup reactions - // do not factor into the transport time measurement on the first message. - timer t(1 sec, 1 sec); - reaction(t) -> message {= - // With NULL, 0 arguments, snprintf tells us how many bytes are needed. - // Add one for the null terminator. - int length = snprintf(NULL, 0, "%s %d", self->prefix, self->count) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(message, length); - // Populate the output string and increment the count. - snprintf(message->value, length, "%s %d", self->prefix, self->count++); - - tag_t tag = get_current_tag(); - info_print("At (elapsed) logical tag (%lld, %u), source sends message: %s", - tag.time - start_time, tag.microstep, - message->value - ); - =} -} - -/** - * Reactor that prints the current tag and an incoming string. - * - * @input message The message. - */ -reactor PrintMessage { - input message:char*; - reaction(message) {= - tag_t tag = get_current_tag(); - info_print("At (elapsed) logical tag (%lld, %u), print receives: %s", - tag.time - start_time, tag.microstep, - message->value - ); - =} -} - -federated reactor HelloWorld at rti { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; -} \ No newline at end of file diff --git a/example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf b/example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf new file mode 100644 index 0000000000..70161f6946 --- /dev/null +++ b/example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf @@ -0,0 +1,22 @@ +/** + * Containerized distributed LF program where a MessageGenerator creates a string + * message that is sent via the RTI (runtime infrastructure) to a + * receiver that prints the message. + * + * For run instructions, see README. + * + * @author Edward A. Lee + */ +target C { + timeout: 10 secs, + docker: true +}; + +import MessageGenerator from "../HelloWorld.lf" +import PrintMessage from "../HelloWorld.lf" + +federated reactor HelloWorldContainerized at rti { + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage(); + source.message -> print.message; +} \ No newline at end of file diff --git a/example/C/src/ContainerizedFederatedHelloWorld/README.md b/example/C/src/DistributedHelloWorld/docker/README.md similarity index 82% rename from example/C/src/ContainerizedFederatedHelloWorld/README.md rename to example/C/src/DistributedHelloWorld/docker/README.md index 034cfd11c5..0d9bb582ca 100644 --- a/example/C/src/ContainerizedFederatedHelloWorld/README.md +++ b/example/C/src/DistributedHelloWorld/docker/README.md @@ -7,7 +7,7 @@ For more details, see: [Containerized Execution in Lingua Franca](https://github Put HelloWorld.lf in a src directory. Then, run: ```bash -lfc src/HelloWorld.lf +lfc HelloWorldContainerized.lf ``` There would be 3 build messages, 1 for the RTI and 2 for the reactors, indicating where the docker file is generated, as well as the instruction to build the docker file. @@ -25,7 +25,7 @@ To build the docker image, use: If you cannot find the build message for the RTI, try a clean build using: ```bash -lfc --clean src/HelloWorld.lf +lfc --clean HelloWorld.lf ``` Then, use the printed commands to build the 3 docker images. @@ -39,13 +39,13 @@ Open 3 terminals, 1 for the RTI and 1 for each reactor. Run the RTI: ```bash -docker run --rm -it --network=lf --name rti rti -i 1 -n 2 +docker run --rm -it --network=lf --name=rti rti -i 1 -n 2 ``` Run the two reactors: ```bash -docker run --rm -it --network=lf helloworld_a -i 1 +docker run --rm -it --network=lf helloworldcontainerized_source -i 1 ``` ```bash -docker run --rm -it --network=lf helloworld_b -i 1 +docker run --rm -it --network=lf helloworldcontainerized_print -i 1 ``` \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index fb69b75ae5..fdcc33644d 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -1706,20 +1706,19 @@ class PythonGenerator extends CGenerator { * @param The root filename (without any extension). */ override writeDockerFile(String filename) { - if (this.mainDef === null) { - return - } - var srcGenPath = fileConfig.getSrcGenPath val dockerFile = srcGenPath + File.separator + filename + '.Dockerfile' - val contents = new StringBuilder() - // If a dockerfile exists, remove it. var file = new File(dockerFile) if (file.exists) { file.delete } + if (this.mainDef === null) { + return + } + + val contents = new StringBuilder() pr(contents, ''' # Generated docker file for «topLevelName».lf in «srcGenPath». # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 93903a06af..be10e62bff 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1247,19 +1247,18 @@ class CGenerator extends GeneratorBase { * @param the name given to the docker file (without any extension). */ override writeDockerFile(String dockerFileName) { - if (this.mainDef === null) { - return - } - var srcGenPath = fileConfig.getSrcGenPath val dockerFile = srcGenPath + File.separator + dockerFileName + '.Dockerfile' - val contents = new StringBuilder() - // If a dockerfile exists, remove it. var file = new File(dockerFile) if (file.exists) { file.delete } + if (this.mainDef === null) { + return + } + + val contents = new StringBuilder() // The Docker configuration uses cmake, so config.compiler is ignored here. var compileCommand = ''' cmake -S src-gen -B bin && \ From 014c0055580661661317b99dc2a0465970d91733 Mon Sep 17 00:00:00 2001 From: Hou Seng Wong Date: Mon, 1 Nov 2021 09:38:20 -0700 Subject: [PATCH 12/12] fix code according to code review --- .../org/lflang/generator/c/CGenerator.xtend | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index be10e62bff..be5a948fc8 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -538,6 +538,14 @@ class CGenerator extends GeneratorBase { "federated/clock-sync.c" ); createFederatedLauncher(coreFiles); + + var rtiPath = fileConfig.getSrcGenBasePath().resolve("RTI") + var rtiDir = rtiPath.toFile() + if (!rtiDir.exists()) { + rtiDir.mkdirs() + } + writeRTIDockerFile(rtiPath, rtiDir) + copyRtiFiles(rtiDir, coreFiles) } // Perform distinct code generation into distinct files for each federate. @@ -844,15 +852,6 @@ class CGenerator extends GeneratorBase { // Create docker file. if (targetConfig.dockerOptions !== null) { - if (isFederated) { - var rtiPath = fileConfig.getSrcGenBasePath().resolve("RTI") - var rtiDir = rtiPath.toFile() - if (!rtiDir.exists()) { - rtiDir.mkdirs() - writeRTIDockerFile(rtiPath, rtiDir) - copyRtiFiles(rtiDir, coreFiles) - } - } writeDockerFile(topLevelName) } @@ -1323,6 +1322,15 @@ class CGenerator extends GeneratorBase { def writeRTIDockerFile(Path rtiPath, File rtiDir) { val dockerFileName = 'rti.Dockerfile' val dockerFile = rtiDir + File.separator + dockerFileName + var srcGenPath = fileConfig.getSrcGenPath + // If a dockerfile exists, remove it. + var file = new File(dockerFile) + if (file.exists) { + file.delete + } + if (this.mainDef === null) { + return + } val contents = new StringBuilder() pr(contents, ''' # Generated docker file for RTI in «rtiDir».