diff --git a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java index 2cf6f58239..b680f9f51d 100644 --- a/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CCmakeGenerator.java @@ -310,30 +310,22 @@ CodeBuilder generateCMakeCode( case ZEPHYR: cMakeCode.pr( setUpMainTargetZephyr( - hasMain, - executableName, - Stream.concat(additionalSources.stream(), sources.stream()))); + hasMain, context, Stream.concat(additionalSources.stream(), sources.stream()))); break; case RP2040: cMakeCode.pr( setUpMainTargetRp2040( - hasMain, - executableName, - Stream.concat(additionalSources.stream(), sources.stream()))); + hasMain, context, Stream.concat(additionalSources.stream(), sources.stream()))); break; case FLEXPRET: cMakeCode.pr( setUpMainTargetFlexPRET( - hasMain, - executableName, - Stream.concat(additionalSources.stream(), sources.stream()))); + hasMain, context, Stream.concat(additionalSources.stream(), sources.stream()))); break; default: cMakeCode.pr( setUpMainTarget.getCmakeCode( - hasMain, - executableName, - Stream.concat(additionalSources.stream(), sources.stream()))); + hasMain, context, Stream.concat(additionalSources.stream(), sources.stream()))); } // Ensure that the math library is linked @@ -495,16 +487,16 @@ CodeBuilder generateCMakeCode( public interface SetUpMainTarget { // Implementation note: This indirection is necessary because the Python // target produces a shared object file, not an executable. - String getCmakeCode(boolean hasMain, String executableName, Stream cSources); + String getCmakeCode(boolean hasMain, LFGeneratorContext context, Stream cSources); } /** Generate the C-target-specific code for configuring the executable produced by the build. */ private static String setUpMainTarget( - boolean hasMain, String executableName, Stream cSources) { + boolean hasMain, LFGeneratorContext context, Stream cSources) { var code = new CodeBuilder(); code.pr("add_subdirectory(core)"); code.newLine(); - code.pr("set(LF_MAIN_TARGET " + executableName + ")"); + code.pr("set(LF_MAIN_TARGET " + context.getFileConfig().name + ")"); code.newLine(); if (hasMain) { @@ -524,7 +516,7 @@ private static String setUpMainTarget( } private static String setUpMainTargetZephyr( - boolean hasMain, String executableName, Stream cSources) { + boolean hasMain, LFGeneratorContext context, Stream cSources) { var code = new CodeBuilder(); code.pr("add_subdirectory(core)"); code.newLine(); @@ -535,7 +527,7 @@ private static String setUpMainTargetZephyr( code.pr("target_sources("); } else { code.pr("# Declare a new library target and list all its sources"); - code.pr("set(LF_MAIN_TARGET" + executableName + ")"); + code.pr("set(LF_MAIN_TARGET" + context.getFileConfig().name + ")"); code.pr("add_library("); } code.indent(); @@ -553,7 +545,7 @@ private static String setUpMainTargetZephyr( } private static String setUpMainTargetRp2040( - boolean hasMain, String executableName, Stream cSources) { + boolean hasMain, LFGeneratorContext context, Stream cSources) { var code = new CodeBuilder(); // initialize sdk code.pr("pico_sdk_init()"); @@ -563,7 +555,7 @@ private static String setUpMainTargetRp2040( code.pr("target_link_libraries(reactor-c PUBLIC pico_multicore)"); code.pr("target_link_libraries(reactor-c PUBLIC pico_sync)"); code.newLine(); - code.pr("set(LF_MAIN_TARGET " + executableName + ")"); + code.pr("set(LF_MAIN_TARGET " + context.getFileConfig().name + ")"); if (hasMain) { code.pr("# Declare a new executable target and list all its sources"); @@ -584,7 +576,7 @@ private static String setUpMainTargetRp2040( } private static String setUpMainTargetFlexPRET( - boolean hasMain, String executableName, Stream cSources) { + boolean hasMain, LFGeneratorContext context, Stream cSources) { var code = new CodeBuilder(); code.pr("add_subdirectory(core)"); code.newLine(); @@ -593,7 +585,7 @@ private static String setUpMainTargetFlexPRET( code.pr("add_subdirectory($ENV{FP_SDK_PATH} BINARY_DIR)"); code.newLine(); - code.pr("set(LF_MAIN_TARGET " + executableName + ")"); + code.pr("set(LF_MAIN_TARGET " + context.getFileConfig().name + ")"); code.newLine(); if (hasMain) { diff --git a/core/src/main/java/org/lflang/generator/python/PythonGenerator.java b/core/src/main/java/org/lflang/generator/python/PythonGenerator.java index 854c8f108a..a540ec8017 100644 --- a/core/src/main/java/org/lflang/generator/python/PythonGenerator.java +++ b/core/src/main/java/org/lflang/generator/python/PythonGenerator.java @@ -60,8 +60,10 @@ import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; import org.lflang.target.Target; +import org.lflang.target.TargetConfig; import org.lflang.target.property.DockerProperty; import org.lflang.target.property.ProtobufsProperty; +import org.lflang.target.property.PythonVersionProperty; import org.lflang.util.FileUtil; import org.lflang.util.LFCommand; import org.lflang.util.StringUtil; @@ -565,15 +567,20 @@ protected void additionalPostProcessingForModes() { PythonModeGenerator.generateResetReactionsIfNeeded(reactors); } + private static String getPythonVersion(TargetConfig config) { + String property = config.get(PythonVersionProperty.INSTANCE); + return property.isEmpty() ? "3.10.0...<3.11.0" : property + " EXACT"; + } + private static String setUpMainTarget( - boolean hasMain, String executableName, Stream cSources) { + boolean hasMain, LFGeneratorContext context, Stream cSources) { return (""" set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_compile_definitions(_PYTHON_TARGET_ENABLED) add_subdirectory(core) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}) set(LF_MAIN_TARGET ) - find_package(Python 3.10.0...<3.11.0 REQUIRED COMPONENTS Interpreter Development) + find_package(Python REQUIRED COMPONENTS Interpreter Development) Python_add_library( ${LF_MAIN_TARGET} MODULE @@ -593,7 +600,8 @@ private static String setUpMainTarget( target_link_libraries(${LF_MAIN_TARGET} PRIVATE ${Python_LIBRARIES}) target_compile_definitions(${LF_MAIN_TARGET} PUBLIC MODULE_NAME=) """) - .replace("", generatePythonModuleName(executableName)); + .replace("", generatePythonModuleName(context.getFileConfig().name)) + .replace("", getPythonVersion(context.getTargetConfig())); // The use of fileConfig.name will break federated execution, but that's fine } diff --git a/core/src/main/java/org/lflang/target/Target.java b/core/src/main/java/org/lflang/target/Target.java index 30048f5a58..da52237f62 100644 --- a/core/src/main/java/org/lflang/target/Target.java +++ b/core/src/main/java/org/lflang/target/Target.java @@ -49,6 +49,7 @@ import org.lflang.target.property.PlatformProperty; import org.lflang.target.property.PrintStatisticsProperty; import org.lflang.target.property.ProtobufsProperty; +import org.lflang.target.property.PythonVersionProperty; import org.lflang.target.property.Ros2DependenciesProperty; import org.lflang.target.property.Ros2Property; import org.lflang.target.property.RuntimeVersionProperty; @@ -640,7 +641,8 @@ public void initialize(TargetConfig config) { SingleThreadedProperty.INSTANCE, TracingProperty.INSTANCE, TracePluginProperty.INSTANCE, - WorkersProperty.INSTANCE); + WorkersProperty.INSTANCE, + PythonVersionProperty.INSTANCE); case Rust -> config.register( BuildTypeProperty.INSTANCE, diff --git a/core/src/main/java/org/lflang/target/property/PythonVersionProperty.java b/core/src/main/java/org/lflang/target/property/PythonVersionProperty.java new file mode 100644 index 0000000000..291002e37f --- /dev/null +++ b/core/src/main/java/org/lflang/target/property/PythonVersionProperty.java @@ -0,0 +1,35 @@ +package org.lflang.target.property; + +import org.lflang.MessageReporter; +import org.lflang.lf.LfPackage.Literals; +import org.lflang.target.TargetConfig; + +/** Directive for specifying a specific version of the reactor runtime library. */ +public final class PythonVersionProperty extends StringProperty { + + /** Singleton target property instance. */ + public static final PythonVersionProperty INSTANCE = new PythonVersionProperty(); + + private PythonVersionProperty() { + super(); + } + + @Override + public String name() { + return "python-version"; + } + + @Override + public void validate(TargetConfig config, MessageReporter reporter) { + String version = config.get(PythonVersionProperty.INSTANCE); + if (!version.isEmpty() && !version.contains("3.10")) { + reporter + .at(config.lookup(this), Literals.KEY_VALUE_PAIR__NAME) + .warning( + "Python " + + version + + " is currently unsupported and untested. As such, it may fail in unexpected" + + " ways."); + } + } +}