From 499b58724f9d92a05cdcafa5e32ccbf1f0d4a9be Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 17 Mar 2023 23:01:39 -0700 Subject: [PATCH 01/22] Major refactoring of parts of the federated package --- org.lflang/src/org/lflang/TargetConfig.java | 66 +++++- org.lflang/src/org/lflang/TargetProperty.java | 2 +- .../src/org/lflang/ast/FormattingUtils.java | 4 +- .../federated/extensions/CExtension.java | 33 ++- .../federated/extensions/CExtensionUtils.java | 14 +- .../extensions/FedTargetExtension.java | 13 +- .../extensions/FedTargetExtensionFactory.java | 4 +- .../federated/extensions/TSExtension.java | 9 +- .../federated/generator/FedASTUtils.java | 18 +- .../federated/generator/FedEmitter.java | 11 +- .../federated/generator/FedFileConfig.java | 32 +++ .../federated/generator/FedGenerator.java | 195 ++++++++++-------- .../federated/generator/FedImportEmitter.java | 2 +- .../federated/generator/FedMainEmitter.java | 2 +- .../generator/FedPreambleEmitter.java | 7 +- .../generator/FedReactorEmitter.java | 2 +- .../federated/generator/FedTargetEmitter.java | 49 +---- .../federated/generator/FederateInstance.java | 21 +- .../{FedCLauncher.java => CBuildConfig.java} | 58 ++---- .../launcher/FedLauncherFactory.java | 64 ------ ...auncher.java => FedLauncherGenerator.java} | 115 +++++------ .../federated/launcher/FedPyLauncher.java | 83 -------- ...{FedTSLauncher.java => TsBuildConfig.java} | 40 ++-- .../org/lflang/generator/GeneratorUtils.java | 76 +------ .../src/org/lflang/generator/MainContext.java | 2 +- .../org/lflang/generator/ts/TSGenerator.kt | 6 +- 26 files changed, 368 insertions(+), 560 deletions(-) rename org.lflang/src/org/lflang/federated/launcher/{FedCLauncher.java => CBuildConfig.java} (56%) delete mode 100644 org.lflang/src/org/lflang/federated/launcher/FedLauncherFactory.java rename org.lflang/src/org/lflang/federated/launcher/{FedLauncher.java => FedLauncherGenerator.java} (87%) delete mode 100644 org.lflang/src/org/lflang/federated/launcher/FedPyLauncher.java rename org.lflang/src/org/lflang/federated/launcher/{FedTSLauncher.java => TsBuildConfig.java} (68%) diff --git a/org.lflang/src/org/lflang/TargetConfig.java b/org.lflang/src/org/lflang/TargetConfig.java index 446460d5c1..7702d15711 100644 --- a/org.lflang/src/org/lflang/TargetConfig.java +++ b/org.lflang/src/org/lflang/TargetConfig.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Properties; import java.util.Set; import org.lflang.TargetProperty.BuildType; @@ -38,7 +39,9 @@ import org.lflang.TargetProperty.LogLevel; import org.lflang.TargetProperty.Platform; import org.lflang.TargetProperty.SchedulerOption; +import org.lflang.TargetProperty.UnionType; import org.lflang.generator.rust.RustTargetConfig; +import org.lflang.lf.KeyValuePair; import org.lflang.lf.TargetDecl; /** @@ -52,10 +55,71 @@ public class TargetConfig { public final Target target; - public TargetConfig(TargetDecl target) { + public TargetConfig(TargetDecl target) { // FIXME: eliminate this constructor if we can this.target = Target.fromDecl(target); } + public TargetConfig(Properties args, + TargetDecl target, + ErrorReporter errorReporter) { + this(target); + if (target.getConfig() != null) { + List pairs = target.getConfig().getPairs(); + TargetProperty.set(this, pairs != null ? pairs : List.of(), errorReporter); + } + if (args.containsKey("no-compile")) { + this.noCompile = true; + } + if (args.containsKey("docker")) { + var arg = args.getProperty("docker"); + if (Boolean.parseBoolean(arg)) { + this.dockerOptions = new DockerOptions(); + } else { + this.dockerOptions = null; + } + // FIXME: this is pretty ad-hoc and does not account for more complex overrides yet. + } + if (args.containsKey("build-type")) { + this.cmakeBuildType = (BuildType) UnionType.BUILD_TYPE_UNION.forName(args.getProperty("build-type")); + } + if (args.containsKey("logging")) { + this.logLevel = LogLevel.valueOf(args.getProperty("logging").toUpperCase()); + } + if (args.containsKey("workers")) { + this.workers = Integer.parseInt(args.getProperty("workers")); + } + if (args.containsKey("threading")) { + this.threading = Boolean.parseBoolean(args.getProperty("threading")); + } + if (args.containsKey("target-compiler")) { + this.compiler = args.getProperty("target-compiler"); + } + if (args.containsKey("scheduler")) { + this.schedulerType = SchedulerOption.valueOf( + args.getProperty("scheduler") + ); + this.setByUser.add(TargetProperty.SCHEDULER); + } + if (args.containsKey("target-flags")) { + this.compilerFlags.clear(); + if (!args.getProperty("target-flags").isEmpty()) { + this.compilerFlags.addAll(List.of( + args.getProperty("target-flags").split(" ") + )); + } + } + if (args.containsKey("runtime-version")) { + this.runtimeVersion = args.getProperty("runtime-version"); + } + if (args.containsKey("external-runtime-path")) { + this.externalRuntimePath = args.getProperty("external-runtime-path"); + } + if (args.containsKey(TargetProperty.KEEPALIVE.description)) { + this.keepalive = Boolean.parseBoolean( + args.getProperty(TargetProperty.KEEPALIVE.description)); + } + } + /** * Keep track of every target property that is explicitly set by the user. */ diff --git a/org.lflang/src/org/lflang/TargetProperty.java b/org.lflang/src/org/lflang/TargetProperty.java index f0de831bf4..1c580b603e 100644 --- a/org.lflang/src/org/lflang/TargetProperty.java +++ b/org.lflang/src/org/lflang/TargetProperty.java @@ -974,7 +974,7 @@ public static TargetDecl extractTargetDecl(Target target, TargetConfig config) { * @param config The configuration object to update. * @param properties AST node that holds all the target properties. */ - public static void update(TargetConfig config, List properties,ErrorReporter err) { + public static void update(TargetConfig config, List properties, ErrorReporter err) { properties.forEach(property -> { TargetProperty p = forName(property.getName()); if (p != null) { diff --git a/org.lflang/src/org/lflang/ast/FormattingUtils.java b/org.lflang/src/org/lflang/ast/FormattingUtils.java index bd9fbaf3d8..0e148f218b 100644 --- a/org.lflang/src/org/lflang/ast/FormattingUtils.java +++ b/org.lflang/src/org/lflang/ast/FormattingUtils.java @@ -55,8 +55,8 @@ public static String render(EObject object, int lineLength) { } /** Return a function that renders AST nodes for the given target. */ - public static Function renderer(TargetDecl targetDecl) { - return object -> render(object, DEFAULT_LINE_LENGTH, Target.fromDecl(targetDecl), true); + public static Function renderer(Target target) { + return object -> render(object, DEFAULT_LINE_LENGTH, target, true); } /** diff --git a/org.lflang/src/org/lflang/federated/extensions/CExtension.java b/org.lflang/src/org/lflang/federated/extensions/CExtension.java index d9ed222920..fda727d735 100644 --- a/org.lflang/src/org/lflang/federated/extensions/CExtension.java +++ b/org.lflang/src/org/lflang/federated/extensions/CExtension.java @@ -45,6 +45,7 @@ import org.lflang.federated.generator.FedConnectionInstance; import org.lflang.federated.generator.FedFileConfig; import org.lflang.federated.generator.FederateInstance; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.federated.serialization.FedROS2CPPSerialization; import org.lflang.generator.CodeBuilder; import org.lflang.generator.GeneratorBase; @@ -83,18 +84,10 @@ public void initializeTargetConfig( int numOfFederates, FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter, - LinkedHashMap federationRTIProperties + RtiConfig rtiConfig ) throws IOException { - if(GeneratorUtils.isHostWindows()) { - errorReporter.reportError( - "Federated LF programs with a C target are currently not supported on Windows. " + - "Exiting code generation." - ); - // Return to avoid compiler errors - return; - } - CExtensionUtils.handleCompileDefinitions(federate, numOfFederates, federationRTIProperties); + CExtensionUtils.handleCompileDefinitions(federate, numOfFederates, rtiConfig); generateCMakeInclude(federate, fileConfig); @@ -488,11 +481,11 @@ public String getNetworkBufferType() { public String generatePreamble( FederateInstance federate, FedFileConfig fileConfig, - LinkedHashMap federationRTIProperties, + RtiConfig rtiConfig, ErrorReporter errorReporter ) throws IOException { // Put the C preamble in a `include/_federate.name + _preamble.h` file - String cPreamble = makePreamble(federate, fileConfig, federationRTIProperties, errorReporter); + String cPreamble = makePreamble(federate, fileConfig, rtiConfig, errorReporter); String relPath = "include" + File.separator + "_" + federate.name + "_preamble.h"; Path fedPreamblePath = fileConfig.getSrcPath().resolve(relPath); Files.createDirectories(fedPreamblePath.getParent()); @@ -509,7 +502,7 @@ public String generatePreamble( protected String makePreamble( FederateInstance federate, FedFileConfig fileConfig, - LinkedHashMap federationRTIProperties, + RtiConfig rtiConfig, ErrorReporter errorReporter) { var code = new CodeBuilder(); @@ -532,7 +525,7 @@ protected String makePreamble( code.pr(generateSerializationPreamble(federate, fileConfig)); - code.pr(generateExecutablePreamble(federate, federationRTIProperties, errorReporter)); + code.pr(generateExecutablePreamble(federate, rtiConfig, errorReporter)); code.pr(generateInitializeTriggers(federate, errorReporter)); @@ -582,12 +575,12 @@ private String generateInitializeTriggers(FederateInstance federate, ErrorReport * Generate code for an executed preamble. * */ - private String generateExecutablePreamble(FederateInstance federate, LinkedHashMap federationRTIProperties, ErrorReporter errorReporter) { + private String generateExecutablePreamble(FederateInstance federate, RtiConfig rtiConfig, ErrorReporter errorReporter) { CodeBuilder code = new CodeBuilder(); code.pr(generateCodeForPhysicalActions(federate, errorReporter)); - code.pr(generateCodeToInitializeFederate(federate, federationRTIProperties)); + code.pr(generateCodeToInitializeFederate(federate, rtiConfig)); code.pr(CExtensionUtils.allocateTriggersForFederate(federate)); @@ -600,10 +593,10 @@ void _lf_executable_preamble() { /** * Generate code to initialize the {@code federate}. - * @param federationRTIProperties Properties related to the RTI. + * @param rtiConfig * @return The generated code */ - private String generateCodeToInitializeFederate(FederateInstance federate, LinkedHashMap federationRTIProperties) { + private String generateCodeToInitializeFederate(FederateInstance federate, RtiConfig rtiConfig) { CodeBuilder code = new CodeBuilder(); code.pr("// ***** Start initializing the federated execution. */"); code.pr(String.join("\n", @@ -672,12 +665,12 @@ private String generateCodeToInitializeFederate(FederateInstance federate, Linke code.pr(String.join("\n", "// Connect to the RTI. This sets _fed.socket_TCP_RTI and _lf_rti_socket_UDP.", - "connect_to_rti("+addDoubleQuotes(federationRTIProperties.get("host").toString())+", "+ federationRTIProperties.get("port")+");" + "connect_to_rti("+addDoubleQuotes(rtiConfig.getHost())+", "+ rtiConfig.getPort()+");" )); // Disable clock synchronization for the federate if it resides on the same host as the RTI, // unless that is overridden with the clock-sync-options target property. - if (CExtensionUtils.clockSyncIsOn(federate, federationRTIProperties)) { + if (CExtensionUtils.clockSyncIsOn(federate, rtiConfig)) { code.pr("synchronize_initial_physical_clock_with_rti(_fed.socket_TCP_RTI);"); } diff --git a/org.lflang/src/org/lflang/federated/extensions/CExtensionUtils.java b/org.lflang/src/org/lflang/federated/extensions/CExtensionUtils.java index a24bcbae58..aabc77ecd5 100644 --- a/org.lflang/src/org/lflang/federated/extensions/CExtensionUtils.java +++ b/org.lflang/src/org/lflang/federated/extensions/CExtensionUtils.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern; @@ -17,6 +16,7 @@ import org.lflang.TimeValue; import org.lflang.federated.generator.FedFileConfig; import org.lflang.federated.generator.FederateInstance; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.federated.serialization.FedROS2CPPSerialization; import org.lflang.federated.serialization.SupportedSerializers; import org.lflang.generator.CodeBuilder; @@ -239,7 +239,7 @@ static boolean isSharedPtrType(InferredType type, CTypes types) { public static void handleCompileDefinitions( FederateInstance federate, int numOfFederates, - LinkedHashMap federationRTIProperties + RtiConfig rtiConfig ) { federate.targetConfig.setByUser.add(TargetProperty.COMPILE_DEFINITIONS); federate.targetConfig.compileDefinitions.put("FEDERATED", ""); @@ -250,7 +250,7 @@ public static void handleCompileDefinitions( handleAdvanceMessageInterval(federate); - initializeClockSynchronization(federate, federationRTIProperties); + initializeClockSynchronization(federate, rtiConfig); } /** @@ -276,9 +276,9 @@ private static void handleAdvanceMessageInterval(FederateInstance federate) { } } - static boolean clockSyncIsOn(FederateInstance federate, LinkedHashMap federationRTIProperties) { + static boolean clockSyncIsOn(FederateInstance federate, RtiConfig rtiConfig) { return federate.targetConfig.clockSync != ClockSyncMode.OFF - && (!federationRTIProperties.get("host").toString().equals(federate.host) + && (!rtiConfig.getHost().equals(federate.host) || federate.targetConfig.clockSyncOptions.localFederatesOn); } @@ -290,10 +290,10 @@ static boolean clockSyncIsOn(FederateInstance federate, LinkedHashMap federationRTIProperties + RtiConfig rtiConfig ) { // Check if clock synchronization should be enabled for this federate in the first place - if (clockSyncIsOn(federate, federationRTIProperties)) { + if (clockSyncIsOn(federate, rtiConfig)) { System.out.println("Initial clock synchronization is enabled for federate " + federate.id ); diff --git a/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java b/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java index 7fb42bf007..4c41994826 100644 --- a/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java +++ b/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java @@ -1,21 +1,16 @@ package org.lflang.federated.extensions; import java.io.IOException; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import org.lflang.ErrorReporter; -import org.lflang.FileConfig; import org.lflang.InferredType; -import org.lflang.Target; -import org.lflang.TargetConfig; import org.lflang.TargetProperty.CoordinationType; import org.lflang.TimeValue; import org.lflang.federated.generator.FedConnectionInstance; import org.lflang.federated.generator.FedFileConfig; import org.lflang.federated.generator.FederateInstance; -import org.lflang.federated.serialization.SupportedSerializers; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.generator.LFGeneratorContext; import org.lflang.lf.Action; import org.lflang.lf.Reaction; @@ -33,7 +28,7 @@ public interface FedTargetExtension { * @param errorReporter Used to report errors. */ void initializeTargetConfig(LFGeneratorContext context, int numOfFederates, FederateInstance federate, FedFileConfig fileConfig, - ErrorReporter errorReporter, LinkedHashMap federationRTIProperties) throws IOException; + ErrorReporter errorReporter, RtiConfig rtiConfig) throws IOException; /** * Generate code for the body of a reaction that handles the @@ -120,13 +115,13 @@ default void annotateReaction(Reaction reaction) {} * Add necessary preamble to the source to set up federated execution. * * @param federate - * @param federationRTIProperties + * @param rtiConfig * @param errorReporter * @return */ String generatePreamble(FederateInstance federate, FedFileConfig fileConfig, - LinkedHashMap federationRTIProperties, + RtiConfig rtiConfig, ErrorReporter errorReporter) throws IOException; } diff --git a/org.lflang/src/org/lflang/federated/extensions/FedTargetExtensionFactory.java b/org.lflang/src/org/lflang/federated/extensions/FedTargetExtensionFactory.java index f3ab17514d..5caafc1c0c 100644 --- a/org.lflang/src/org/lflang/federated/extensions/FedTargetExtensionFactory.java +++ b/org.lflang/src/org/lflang/federated/extensions/FedTargetExtensionFactory.java @@ -12,8 +12,8 @@ public class FedTargetExtensionFactory { /** * Given a target, return the appropriate extension. */ - public static FedTargetExtension getExtension(TargetDecl target) { - switch (Target.fromDecl(target)) { + public static FedTargetExtension getExtension(Target target) { + switch (target) { case CCPP: case C: return new CExtension(); case Python: return new PythonExtension(); diff --git a/org.lflang/src/org/lflang/federated/extensions/TSExtension.java b/org.lflang/src/org/lflang/federated/extensions/TSExtension.java index 21b469228e..fb152ea7cc 100644 --- a/org.lflang/src/org/lflang/federated/extensions/TSExtension.java +++ b/org.lflang/src/org/lflang/federated/extensions/TSExtension.java @@ -16,6 +16,7 @@ import org.lflang.federated.generator.FedConnectionInstance; import org.lflang.federated.generator.FedFileConfig; import org.lflang.federated.generator.FederateInstance; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.generator.GeneratorBase; import org.lflang.generator.LFGeneratorContext; import org.lflang.generator.ReactorInstance; @@ -28,7 +29,7 @@ public class TSExtension implements FedTargetExtension { @Override - public void initializeTargetConfig(LFGeneratorContext context, int numOfFederates, FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter, LinkedHashMap federationRTIProperties) throws IOException { + public void initializeTargetConfig(LFGeneratorContext context, int numOfFederates, FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter, RtiConfig rtiConfig) throws IOException { } @@ -88,7 +89,7 @@ public String getNetworkBufferType() { */ @Override public String generatePreamble(FederateInstance federate, FedFileConfig fileConfig, - LinkedHashMap federationRTIProperties, + RtiConfig rtiConfig, ErrorReporter errorReporter) { var minOutputDelay = getMinOutputDelay(federate, fileConfig, errorReporter); return @@ -118,8 +119,8 @@ public String generatePreamble(FederateInstance federate, FedFileConfig fileConf .stream() .map(Variable::getName) .collect(Collectors.joining(",", "\"", "\"")), - federationRTIProperties.get("host"), - federationRTIProperties.get("port"), + rtiConfig.getHost(), + rtiConfig.getPort(), federate.sendsTo.keySet().stream() .map(e->String.valueOf(e.id)) .collect(Collectors.joining(",")) diff --git a/org.lflang/src/org/lflang/federated/generator/FedASTUtils.java b/org.lflang/src/org/lflang/federated/generator/FedASTUtils.java index 21fb3a0729..7a7eb1f183 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/generator/FedASTUtils.java @@ -208,7 +208,7 @@ private static Action createNetworkAction(FedConnectionInstance connection) { Type action_type = factory.createType(); action_type.setId( FedTargetExtensionFactory.getExtension( - connection.srcFederate.target + connection.srcFederate.targetConfig.target ).getNetworkBufferType() ); action.setType(action_type); @@ -264,7 +264,7 @@ private static void addNetworkReceiverReaction( setReactionBankIndex(networkReceiverReaction, connection.getDstBank()); // FIXME: do not create a new extension every time it is used - FedTargetExtensionFactory.getExtension(connection.srcFederate.target) + FedTargetExtensionFactory.getExtension(connection.srcFederate.targetConfig.target) .annotateReaction(networkReceiverReaction); // The connection is 'physical' if it uses the ~> notation. @@ -298,7 +298,7 @@ private static void addNetworkReceiverReaction( networkReceiverReaction.setCode(factory.createCode()); networkReceiverReaction.getCode().setBody( FedTargetExtensionFactory.getExtension( - connection.dstFederate.target).generateNetworkReceiverBody( + connection.dstFederate.targetConfig.target).generateNetworkReceiverBody( networkAction, sourceRef, destRef, @@ -357,7 +357,7 @@ private static void addNetworkInputControlReaction( setReactionBankIndex(reaction, connection.getDstBank()); // FIXME: do not create a new extension every time it is used - FedTargetExtensionFactory.getExtension(connection.srcFederate.target) + FedTargetExtensionFactory.getExtension(connection.srcFederate.targetConfig.target) .annotateReaction(reaction); // Create a new action that will be used to trigger the @@ -397,7 +397,7 @@ private static void addNetworkInputControlReaction( reaction.getCode() .setBody( FedTargetExtensionFactory - .getExtension(connection.dstFederate.target) + .getExtension(connection.dstFederate.targetConfig.target) .generateNetworkInputControlReactionBody( receivingPortID, maxSTP, @@ -721,7 +721,7 @@ private static void addNetworkSenderReaction( Reaction networkSenderReaction = factory.createReaction(); // FIXME: do not create a new extension every time it is used - FedTargetExtensionFactory.getExtension(connection.srcFederate.target) + FedTargetExtensionFactory.getExtension(connection.srcFederate.targetConfig.target) .annotateReaction(networkSenderReaction); // If the sender or receiver is in a bank of reactors, then we want @@ -750,7 +750,7 @@ private static void addNetworkSenderReaction( networkSenderReaction.getTriggers().add(sourceRef); networkSenderReaction.setCode(factory.createCode()); networkSenderReaction.getCode().setBody( - FedTargetExtensionFactory.getExtension(connection.srcFederate.target) + FedTargetExtensionFactory.getExtension(connection.srcFederate.targetConfig.target) .generateNetworkSenderBody( sourceRef, destRef, @@ -797,7 +797,7 @@ private static void addNetworkOutputControlReaction(FedConnectionInstance connec setReactionBankIndex(reaction, connection.getSrcBank()); // FIXME: do not create a new extension every time it is used - FedTargetExtensionFactory.getExtension(connection.srcFederate.target) + FedTargetExtensionFactory.getExtension(connection.srcFederate.targetConfig.target) .annotateReaction(reaction); // We use an action at the top-level to manually @@ -845,7 +845,7 @@ private static void addNetworkOutputControlReaction(FedConnectionInstance connec reaction.setCode(factory.createCode()); reaction.getCode().setBody( - FedTargetExtensionFactory.getExtension(connection.srcFederate.target) + FedTargetExtensionFactory.getExtension(connection.srcFederate.targetConfig.target) .generateNetworkOutputControlReactionBody(newPortRef, connection)); ASTUtils.addReactionAttribute(reaction, "_unordered"); diff --git a/org.lflang/src/org/lflang/federated/generator/FedEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedEmitter.java index e86af3ab77..9ae5c9d2cc 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedEmitter.java @@ -8,6 +8,7 @@ import java.util.Map; import org.lflang.ErrorReporter; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.generator.CodeMap; import org.lflang.generator.LFGeneratorContext; import org.lflang.lf.Reactor; @@ -20,18 +21,18 @@ public class FedEmitter { private final FedFileConfig fileConfig; private final Reactor originalMainReactor; private final ErrorReporter errorReporter; - private final LinkedHashMap federationRTIProperties; + private final RtiConfig rtiConfig; public FedEmitter( FedFileConfig fileConfig, Reactor originalMainReactor, ErrorReporter errorReporter, - LinkedHashMap federationRTIProperties + RtiConfig rtiConfig ) { this.fileConfig = fileConfig; this.originalMainReactor = originalMainReactor; this.errorReporter = errorReporter; - this.federationRTIProperties = federationRTIProperties; + this.rtiConfig = rtiConfig; } /** @@ -55,9 +56,9 @@ Map generateFederate( String federateCode = String.join( "\n", - new FedTargetEmitter().generateTarget(context, numOfFederates, federate, fileConfig, errorReporter, federationRTIProperties), + new FedTargetEmitter().generateTarget(context, numOfFederates, federate, fileConfig, errorReporter, rtiConfig), new FedImportEmitter().generateImports(federate, fileConfig), - new FedPreambleEmitter().generatePreamble(federate, fileConfig, federationRTIProperties, errorReporter), + new FedPreambleEmitter().generatePreamble(federate, fileConfig, rtiConfig, errorReporter), new FedReactorEmitter().generateReactorDefinitions(federate), new FedMainEmitter().generateMainReactor( federate, diff --git a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java index 8e9df19d6e..3ec2b05813 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java +++ b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java @@ -27,10 +27,13 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import org.eclipse.emf.ecore.resource.Resource; import org.lflang.FileConfig; +import org.lflang.TargetProperty; import org.lflang.util.FileUtil; /** @@ -94,4 +97,33 @@ public void doClean() throws IOException { super.doClean(); FileUtil.deleteDirectory(this.getFedGenPath()); } + + /** + * Relativize target properties that involve paths like files and cmake-include to be + * relative to the generated .lf file for the federate. + */ + public void relativizePaths(FedTargetConfig targetConfig) { + relativizePathList(targetConfig.protoFiles); + relativizePathList(targetConfig.fileNames); + relativizePathList(targetConfig.cmakeIncludes); + } + + private void relativizePathList(List paths) { + List tempList = new ArrayList<>(); + paths.forEach( f -> { + tempList.add(relativizePath(f)); + }); + paths.clear(); + paths.addAll(tempList); + } + + private String relativizePath(String path) { + Path resolvedPath = this.srcPath.resolve(path).toAbsolutePath(); + return this.getSrcPath().relativize(resolvedPath).toString(); + } + + + + + } diff --git a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java index 9a98e90291..249cdba02a 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java +++ b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java @@ -1,5 +1,6 @@ package org.lflang.federated.generator; +import static org.lflang.ASTUtils.convertToEmptyListIfNull; import static org.lflang.generator.DockerGenerator.dockerGeneratorFactory; import java.io.IOException; @@ -27,9 +28,7 @@ import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.resource.XtextResourceSet; import org.eclipse.xtext.util.RuntimeIOException; -import org.eclipse.xtext.xbase.lib.CollectionLiterals; import org.eclipse.xtext.xbase.lib.Exceptions; -import org.eclipse.xtext.xbase.lib.Pair; import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; import org.lflang.ASTUtils; @@ -38,13 +37,13 @@ import org.lflang.LFStandaloneSetup; import org.lflang.Target; import org.lflang.TargetConfig; +import org.lflang.TargetProperty; import org.lflang.TargetProperty.CoordinationType; -import org.lflang.federated.launcher.FedLauncher; -import org.lflang.federated.launcher.FedLauncherFactory; +import org.lflang.federated.launcher.FedLauncherGenerator; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.generator.CodeMap; import org.lflang.generator.DockerData; import org.lflang.generator.FedDockerComposeGenerator; -import org.lflang.generator.GeneratorResult; import org.lflang.generator.GeneratorResult.Status; import org.lflang.generator.GeneratorUtils; import org.lflang.generator.IntegratedBuilder; @@ -61,7 +60,6 @@ import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; import org.lflang.lf.Reactor; -import org.lflang.lf.TargetDecl; import com.google.inject.Injector; @@ -103,13 +101,9 @@ public synchronized void report(int id, int x, Procedure1 callback) { * A map from federate IDs to federate instances. */ private final Map federateByID = new LinkedHashMap<>(); - /** - * The federation RTI properties, which defaults to 'localhost: 15045'. - */ - final LinkedHashMap federationRTIProperties = CollectionLiterals.newLinkedHashMap( - Pair.of("host", "localhost"), - Pair.of("port", 0) // Indicator to use the default port, typically 15045. - ); + + final RtiConfig rtiConfig = new RtiConfig(); + /** * A map from instantiations to the federate instances for that * instantiation. @@ -147,7 +141,7 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws Reactor fedReactor = FedASTUtils.findFederatedReactor(resource); // Extract some useful information about the federation - analyzeFederates(fedReactor); + analyzeFederates(fedReactor, context); // Find all the connections between federates. // For each connection between federates, replace it in the @@ -156,13 +150,11 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws // for logical connections. replaceFederateConnectionsWithProxies(fedReactor); - createLauncher(fileConfig, errorReporter, federationRTIProperties); - FedEmitter fedEmitter = new FedEmitter( fileConfig, ASTUtils.toDefinition(mainDef.getReactorClass()), errorReporter, - federationRTIProperties + rtiConfig ); // Generate code for each federate Map lf2lfCodeMapMap = new HashMap<>(); @@ -178,35 +170,40 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws } Map codeMapMap = compileFederates(context, lf2lfCodeMapMap, subContexts -> { - if (context.getTargetConfig().dockerOptions == null) return; - final List services = new ArrayList<>(); - // 1. create a Dockerfile for each federate - for (SubContext subContext : subContexts) {// Inherit Docker options from main context - subContext.getTargetConfig().dockerOptions = context.getTargetConfig().dockerOptions; - var dockerGenerator = dockerGeneratorFactory(subContext); - var dockerData = dockerGenerator.generateDockerData(); - try { - dockerData.writeDockerFile(); - } catch (IOException e) { - throw new RuntimeIOException(e); - } - services.add(dockerData); - } - // 2. create a docker-compose.yml for the federation - try { - // FIXME: https://issue.lf-lang.org/1559 - // It appears that the rtiHost information should come from federationRTIproperties, - // which is a kludge and not in scope here. - new FedDockerComposeGenerator(context, "localhost").writeDockerComposeFile(services); - } catch (IOException e) { - throw new RuntimeIOException(e); - } + createDockerFiles(context, subContexts); + (new FedLauncherGenerator(this.targetConfig, this.fileConfig, this.errorReporter)).createLauncher(federates, rtiConfig); }); context.finish(Status.COMPILED, codeMapMap); return false; } + private void createDockerFiles(LFGeneratorContext context, List subContexts) { + if (context.getTargetConfig().dockerOptions == null) return; + final List services = new ArrayList<>(); + // 1. create a Dockerfile for each federate + for (SubContext subContext : subContexts) {// Inherit Docker options from main context + subContext.getTargetConfig().dockerOptions = context.getTargetConfig().dockerOptions; + var dockerGenerator = dockerGeneratorFactory(subContext); + var dockerData = dockerGenerator.generateDockerData(); + try { + dockerData.writeDockerFile(); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + services.add(dockerData); + } + // 2. create a docker-compose.yml for the federation + try { + // FIXME: https://issue.lf-lang.org/1559 + // It appears that the rtiHost information should come from federationRTIproperties, + // which is a kludge and not in scope here. + new FedDockerComposeGenerator(context, "localhost").writeDockerComposeFile(services); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + } + /** * Check if a clean was requested from the standalone compiler and perform * the clean step. @@ -224,52 +221,58 @@ private void cleanIfNeeded(LFGeneratorContext context) { /** * Create a launcher for the federation. - * @param fileConfig - * @param errorReporter - * @param federationRTIProperties */ public void createLauncher( - FedFileConfig fileConfig, - ErrorReporter errorReporter, + LFGeneratorContext context, + List subContexts, LinkedHashMap federationRTIProperties ) { - FedLauncher launcher; - if (federates.size() == 0) { - // no federates, use target properties of main file - TargetDecl targetDecl = GeneratorUtils.findTarget(fileConfig.resource); - launcher = FedLauncherFactory.getLauncher(Target.fromDecl(targetDecl), - targetConfig, - fileConfig, - errorReporter); - } else { - launcher = FedLauncherFactory.getLauncher( - federates.get(0), // FIXME: This would not work for mixed-target programs. - fileConfig, - errorReporter - ); - } - try { - launcher.createLauncher( - federates, - federationRTIProperties - ); - } catch (IOException e) { - errorReporter.reportError(e.getMessage()); - } - // System.out.println(PythonInfoGenerator.generateFedRunInfo(fileConfig)); + +// FedLauncher launcher; +// if (federates.size() == 0) { +// // no federates, use target properties of main file +// TargetDecl targetDecl = GeneratorUtils.findTarget(fileConfig.resource); +// launcher = FedLauncherFactory.getLauncher(Target.fromDecl(targetDecl), +// targetConfig, +// fileConfig, +// errorReporter); +// } else { +// launcher = FedLauncherFactory.getLauncher( +// federates.get(0), // FIXME: This would not work for mixed-target programs. +// fileConfig, +// errorReporter +// ); +// } +// try { +// launcher.createLauncher( +// federates, +// federationRTIProperties +// ); +// } catch (IOException e) { +// errorReporter.reportError(e.getMessage()); +// } +// +// // System.out.println(PythonInfoGenerator.generateFedRunInfo(fileConfig)); } /** Return whether federated execution is supported for {@code resource}. */ private boolean federatedExecutionIsSupported(Resource resource) { - var target = Target.fromDecl(GeneratorUtils.findTarget(resource)); - var ret = List.of(Target.C, Target.Python, Target.TS, Target.CPP, Target.CCPP).contains(target); - if (!ret) { + var target = Target.fromDecl(GeneratorUtils.findTargetDecl(resource)); + var targetOK = List.of(Target.C, Target.Python, Target.TS, Target.CPP, Target.CCPP).contains(target); + if (!targetOK) { errorReporter.reportError( "Federated execution is not supported with target " + target + "." ); } - return ret; + if(target.equals(Target.C) && GeneratorUtils.isHostWindows()) { + errorReporter.reportError( + "Federated LF programs with a C target are currently not supported on Windows." + ); + targetOK = false; + } + + return targetOK; } private Map compileFederates( @@ -315,7 +318,7 @@ private Map compileFederates( props.put("docker", "false"); TargetConfig subConfig = GeneratorUtils.getTargetConfig( - props, GeneratorUtils.findTarget(subFileConfig.resource), subContextErrorReporter + props, GeneratorUtils.findTargetDecl(subFileConfig.resource), subContextErrorReporter ); SubContext subContext = new SubContext(context, IntegratedBuilder.VALIDATED_PERCENT_PROGRESS, 100) { @Override @@ -393,34 +396,34 @@ private void setFederationRTIProperties(LFGeneratorContext context) { String port = matcher.group(3); if (host != null) { - federationRTIProperties.put("host", host); + rtiConfig.setHost(host); } if (port != null) { - federationRTIProperties.put("port", port); + rtiConfig.setPort(Integer.parseInt(port)); } if (user != null) { - federationRTIProperties.put("user", user); + rtiConfig.setUser(user); } } /** * Analyze the federation and record various properties of it. * - * @param fedReactor The federated reactor that contains all federates' instances. + * @param federation The federated reactor that contains all federates' instances. */ - private void analyzeFederates(Reactor fedReactor) { + private void analyzeFederates(Reactor federation, LFGeneratorContext context) { // Create an instantiation for the fed reactor because there isn't one. // Creating a definition for the main reactor because there isn't one. mainDef = LfFactory.eINSTANCE.createInstantiation(); - mainDef.setName(fedReactor.getName()); - mainDef.setReactorClass(fedReactor); + mainDef.setName(federation.getName()); + mainDef.setReactorClass(federation); // Make sure that if no federation RTI properties were given in the // cmdline, then those specified in the lf file are not lost - if (federationRTIProperties.get("host").equals("localhost") && - fedReactor.getHost() != null && - !fedReactor.getHost().getAddr().equals("localhost")) { - federationRTIProperties.put("host", fedReactor.getHost().getAddr()); + if (rtiConfig.getHost().equals("localhost") && + federation.getHost() != null && + !federation.getHost().getAddr().equals("localhost")) { + rtiConfig.setHost(federation.getHost().getAddr()); } // Since federates are always within the main (federated) reactor, @@ -429,15 +432,15 @@ private void analyzeFederates(Reactor fedReactor) { List mainReactorContext = new ArrayList<>(); mainReactorContext.add(mainDef); - // Create a FederateInstance for each top-level reactor. - for (Instantiation instantiation : ASTUtils.allInstantiations(fedReactor)) { + // Create a FederateInstance for each instance in the top-level reactor. + for (Instantiation instantiation : ASTUtils.allInstantiations(federation)) { int bankWidth = ASTUtils.width(instantiation.getWidthSpec(), mainReactorContext); if (bankWidth < 0) { errorReporter.reportError(instantiation, "Cannot determine bank width! Assuming width of 1."); // Continue with a bank width of 1. bankWidth = 1; } - List federateInstances = getFederateInstances(instantiation, bankWidth); + List federateInstances = getFederateInstances(instantiation, bankWidth, context); if (federatesByInstantiation == null) { federatesByInstantiation = new LinkedHashMap<>(); } @@ -454,14 +457,24 @@ private void analyzeFederates(Reactor fedReactor) { * @param bankWidth The width specified for the instantiation. * @return A list of federate instance (of type @see FederateInstance). */ - private List getFederateInstances(Instantiation instantiation, int bankWidth) { + private List getFederateInstances(Instantiation instantiation, int bankWidth, LFGeneratorContext context) { // Create one federate instance for each instance in a bank of reactors. List federateInstances = new ArrayList<>(bankWidth); + for (int i = 0; i < bankWidth; i++) { // Assign an integer ID to the federate. int federateID = federates.size(); - FederateInstance federateInstance = new FederateInstance(instantiation, federateID, i, errorReporter); - federateInstance.bankIndex = i; + var resource = instantiation.getReactorClass().eResource(); + var federateTargetConfig = new FedTargetConfig(context, resource); + // FIXME: do we need this? + //var federateFileConfig = LFGenerator.createFileConfig(resource, fileConfig.getSrcGenPath(), false); + FederateInstance federateInstance = new FederateInstance( + instantiation, + federateID, + i, + federateTargetConfig, + fileConfig, + errorReporter); federates.add(federateInstance); federateInstances.add(federateInstance); federateByID.put(federateID, federateInstance); diff --git a/org.lflang/src/org/lflang/federated/generator/FedImportEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedImportEmitter.java index 86923c8914..95351237a7 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedImportEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedImportEmitter.java @@ -54,7 +54,7 @@ String generateImports(FederateInstance federate, FedFileConfig fileConfig) { ); return new_import; }) - .map(FormattingUtils.renderer(federate.target)) + .map(FormattingUtils.renderer(federate.targetConfig.target)) .collect(Collectors.joining("\n"))); return importStatements.getCode(); diff --git a/org.lflang/src/org/lflang/federated/generator/FedMainEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedMainEmitter.java index ee3b64ab4a..54e215264f 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedMainEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedMainEmitter.java @@ -41,7 +41,7 @@ String generateMainReactor(FederateInstance federate, Reactor originalMainReacto "Modes at the top level are not supported under federated execution." ); } - var renderer = FormattingUtils.renderer(federate.target); + var renderer = FormattingUtils.renderer(federate.targetConfig.target); return String .join( diff --git a/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java index 827a31fe33..20771e7848 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java @@ -8,6 +8,7 @@ import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.federated.extensions.FedTargetExtensionFactory; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.generator.CodeBuilder; import org.lflang.lf.Model; import org.lflang.lf.Preamble; @@ -20,7 +21,7 @@ public FedPreambleEmitter() {} * Add necessary code to the source and necessary build support to * enable the requested serializations in 'enabledSerializations' */ - String generatePreamble(FederateInstance federate, FedFileConfig fileConfig, LinkedHashMap federationRTIProperties, ErrorReporter errorReporter) + String generatePreamble(FederateInstance federate, FedFileConfig fileConfig, RtiConfig rtiConfig, ErrorReporter errorReporter) throws IOException { CodeBuilder preambleCode = new CodeBuilder(); @@ -38,8 +39,8 @@ String generatePreamble(FederateInstance federate, FedFileConfig fileConfig, Lin )); } - preambleCode.pr(FedTargetExtensionFactory.getExtension(federate.target).generatePreamble( - federate, fileConfig, federationRTIProperties, errorReporter)); + preambleCode.pr(FedTargetExtensionFactory.getExtension(federate.targetConfig.target).generatePreamble( + federate, fileConfig, rtiConfig, errorReporter)); return preambleCode.getCode(); } diff --git a/org.lflang/src/org/lflang/federated/generator/FedReactorEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedReactorEmitter.java index 066b344417..976049601c 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedReactorEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedReactorEmitter.java @@ -18,7 +18,7 @@ String generateReactorDefinitions(FederateInstance federate) { .getReactors() .stream() .filter(federate::contains) - .map(FormattingUtils.renderer(federate.target)) + .map(FormattingUtils.renderer(federate.targetConfig.target)) .collect(Collectors.joining("\n")); } } diff --git a/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java index 7143eb18c5..ec71a57c71 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java @@ -13,6 +13,7 @@ import org.lflang.TargetProperty; import org.lflang.ast.FormattingUtils; import org.lflang.federated.extensions.FedTargetExtensionFactory; +import org.lflang.federated.launcher.RtiConfig; import org.lflang.generator.GeneratorUtils; import org.lflang.generator.LFGeneratorContext; @@ -24,62 +25,30 @@ String generateTarget( FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter, - LinkedHashMap federationRTIProperties + RtiConfig rtiConfig ) throws IOException { - federate.targetConfig = - GeneratorUtils.getTargetConfig( - context.getArgs(), - federate.target, - errorReporter - ); - // FIXME: Should we merge some properties with the main .lf file if the federate is imported? - // https://issue.lf-lang.org/1560 - var fedReactorClass = federate.instantiation.getReactorClass(); - if (!fedReactorClass.eResource().equals(fileConfig.resource)) { - // Merge some target properties of the main .lf file. - var target = GeneratorUtils.findTarget(fileConfig.resource); - if (target.getConfig() != null) { - // Merge properties - TargetProperty.update( - federate.targetConfig, - convertToEmptyListIfNull(target.getConfig().getPairs()), - errorReporter - ); - } - } - - relativizeTargetPaths(federate, fileConfig); - clearFederatedTargetPropertiesI(federate); - - FedTargetExtensionFactory.getExtension(federate.target) + // FIXME: First of all, this is not an initialization; there is all sorts of stuff happening + // in the implementations of this method. Second, true initialization stuff should happen + // when the target config is constructed, not when we're doing code generation. + FedTargetExtensionFactory.getExtension(federate.targetConfig.target) .initializeTargetConfig( context, numOfFederates, federate, fileConfig, errorReporter, - federationRTIProperties + rtiConfig ); - return FormattingUtils.renderer(federate.target).apply( + return FormattingUtils.renderer(federate.targetConfig.target).apply( TargetProperty.extractTargetDecl( - Target.fromDecl(federate.target), + federate.targetConfig.target, federate.targetConfig ) ); } - /** - * Clear target properties that should not end up in the generated .lf file - * for {@code federate}. - */ - private void clearFederatedTargetPropertiesI(FederateInstance federate) { - federate.targetConfig.setByUser.remove(TargetProperty.CLOCK_SYNC); - federate.targetConfig.setByUser.remove(TargetProperty.CLOCK_SYNC_OPTIONS); - } - - /** * Relativize target properties that involve paths like files and cmake-include to be * relative to the generated .lf file for the federate. diff --git a/org.lflang/src/org/lflang/federated/generator/FederateInstance.java b/org.lflang/src/org/lflang/federated/generator/FederateInstance.java index fd309dc4a1..38532afcdf 100644 --- a/org.lflang/src/org/lflang/federated/generator/FederateInstance.java +++ b/org.lflang/src/org/lflang/federated/generator/FederateInstance.java @@ -25,6 +25,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.lflang.federated.generator; +import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; @@ -39,6 +40,8 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.ASTUtils; import org.lflang.ErrorReporter; +import org.lflang.FileConfig; +import org.lflang.Target; import org.lflang.TargetConfig; import org.lflang.TimeValue; import org.lflang.federated.serialization.SupportedSerializers; @@ -47,6 +50,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.generator.PortInstance; import org.lflang.generator.ReactionInstance; import org.lflang.generator.ReactorInstance; +import org.lflang.generator.SubContext; import org.lflang.generator.TriggerInstance; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; @@ -62,7 +66,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Reactor; import org.lflang.lf.ReactorDecl; import org.lflang.lf.StateVar; -import org.lflang.lf.TargetDecl; import org.lflang.lf.Timer; import org.lflang.lf.TriggerRef; import org.lflang.lf.VarRef; @@ -83,6 +86,8 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY */ public class FederateInstance { + private final FileConfig fileConfig; + /** * Construct a new instance with the specified instantiation of * of a top-level reactor. The federate will be given the specified @@ -97,15 +102,16 @@ public FederateInstance( Instantiation instantiation, int id, int bankIndex, + TargetConfig targetConfig, + FileConfig fileConfig, ErrorReporter errorReporter) { this.instantiation = instantiation; this.id = id; this.bankIndex = bankIndex; this.errorReporter = errorReporter; - this.target = GeneratorUtils.findTarget( - ASTUtils.toDefinition(instantiation.getReactorClass()).eResource() - ); - this.targetConfig = new TargetConfig(target); // FIXME: this is actually set in FedTargetEmitter. Why? + this.targetConfig = targetConfig; + this.fileConfig = fileConfig; + if (instantiation != null) { this.name = instantiation.getName(); // If the instantiation is in a bank, then we have to append @@ -248,11 +254,6 @@ public Instantiation getInstantiation() { */ public List networkReactions = new ArrayList<>(); - /** - * Target of the federate. - */ - public TargetDecl target; - /** * Parsed target config of the federate. */ diff --git a/org.lflang/src/org/lflang/federated/launcher/FedCLauncher.java b/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java similarity index 56% rename from org.lflang/src/org/lflang/federated/launcher/FedCLauncher.java rename to org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java index 0b45444be9..e6d05083f2 100644 --- a/org.lflang/src/org/lflang/federated/launcher/FedCLauncher.java +++ b/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java @@ -26,10 +26,8 @@ package org.lflang.federated.launcher; import java.io.File; -import java.io.IOException; import org.lflang.ErrorReporter; -import org.lflang.TargetConfig; import org.lflang.federated.generator.FedFileConfig; import org.lflang.federated.generator.FederateInstance; import org.lflang.generator.c.CCompiler; @@ -39,43 +37,25 @@ * that are written in C. * * @author Soroush Bateni + * @author Marten Lohstroh */ -public class FedCLauncher extends FedLauncher { +public class CBuildConfig extends BuildConfig { - /** - * Create an instance of FedCLauncher. - * - * @param targetConfig The current target configuration. - * @param fileConfig The current file configuration. - * @param errorReporter A error reporter for reporting any errors or warnings during the code generation - */ - public FedCLauncher( - TargetConfig targetConfig, - FedFileConfig fileConfig, - ErrorReporter errorReporter - ) { - super(targetConfig, fileConfig, errorReporter); + public CBuildConfig(FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { + super(federate, fileConfig, errorReporter); } - /** - * Return the compile command for a federate. - * - * @param federate The federate to compile. - * @throws IOException - */ @Override - protected - String compileCommandForFederate(FederateInstance federate) { - TargetConfig localTargetConfig = targetConfig; + public String compileCommand() { String commandToReturn = ""; // FIXME: Hack to add platform support only for linux systems. // We need to fix the CMake build command for remote federates. String linuxPlatformSupport = "core" + File.separator + "platform" + File.separator + "lf_linux_support.c"; - if (!localTargetConfig.compileAdditionalSources.contains(linuxPlatformSupport)) { - localTargetConfig.compileAdditionalSources.add(linuxPlatformSupport); + if (!federate.targetConfig.compileAdditionalSources.contains(linuxPlatformSupport)) { + federate.targetConfig.compileAdditionalSources.add(linuxPlatformSupport); } - CCompiler cCompiler= new CCompiler(localTargetConfig, fileConfig, errorReporter, false); + CCompiler cCompiler= new CCompiler(federate.targetConfig, fileConfig, errorReporter, false); commandToReturn = String.join(" ", cCompiler.compileCCommand( fileConfig.name+"_"+federate.name, @@ -84,28 +64,14 @@ String compileCommandForFederate(FederateInstance federate) { return commandToReturn; } - /** - * Return the command that will execute a remote federate, assuming that the current - * directory is the top-level project folder. This is used to create a launcher script - * for federates. - * - * @param federate The federate to execute. - */ @Override - protected - String executeCommandForRemoteFederate(FederateInstance federate) { + public + String remoteExecuteCommand() { return "bin/"+fileConfig.name+"_"+federate.name+" -i '$FEDERATION_ID'"; } - /** - * Return the command that will execute a local federate. - * This is used to create a launcher script for federates. - * - * @param federate The federate to execute. - */ @Override - protected - String executeCommandForLocalFederate(FedFileConfig fileConfig, FederateInstance federate) { - return fileConfig.getGenPath().resolve("bin/"+federate.name)+" -i $FEDERATION_ID"; + public String localExecuteCommand() { + return fileConfig.binPath.resolve(fileConfig.name)+" -i $FEDERATION_ID"; } } diff --git a/org.lflang/src/org/lflang/federated/launcher/FedLauncherFactory.java b/org.lflang/src/org/lflang/federated/launcher/FedLauncherFactory.java deleted file mode 100644 index c92cea1807..0000000000 --- a/org.lflang/src/org/lflang/federated/launcher/FedLauncherFactory.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.lflang.federated.launcher; - -import org.lflang.ErrorReporter; -import org.lflang.Target; -import org.lflang.TargetConfig; -import org.lflang.federated.generator.FedFileConfig; -import org.lflang.federated.generator.FederateInstance; - -/** - * Helper class to get the appropriate launcher generator. - * - * FIXME: This architecture needs to be redesigned for multi-target federations. - */ -public class FedLauncherFactory { - - public static FedLauncher getLauncher ( - FederateInstance federate, - FedFileConfig fileConfig, - ErrorReporter errorReporter - ) { - return getLauncher(Target.fromDecl(federate.target), federate.targetConfig, fileConfig, errorReporter); - } - - /** - * Return a launcher generator. - * @param target The target to generate for. - * @param targetConfig The target config of the federate. - * @param fileConfig The file config for the federate. - * @param errorReporter The error reporter to use. - * @return null if not supported, an instance of {@code #FedLauncher} otherwise. - */ - public static FedLauncher getLauncher( - Target target, - TargetConfig targetConfig, - FedFileConfig fileConfig, - ErrorReporter errorReporter - ) { - switch (target) { - case C: - case CCPP: - return new FedCLauncher( - targetConfig, - fileConfig, - errorReporter - ); - case CPP: - case Rust: - return null; - case TS: - return new FedTSLauncher( - targetConfig, - fileConfig, - errorReporter - ); - case Python: - return new FedPyLauncher( - targetConfig, - fileConfig, - errorReporter - ); - } - return null; - } -} diff --git a/org.lflang/src/org/lflang/federated/launcher/FedLauncher.java b/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java similarity index 87% rename from org.lflang/src/org/lflang/federated/launcher/FedLauncher.java rename to org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java index 48a7149729..99d608e687 100644 --- a/org.lflang/src/org/lflang/federated/launcher/FedLauncher.java +++ b/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java @@ -26,15 +26,17 @@ package org.lflang.federated.launcher; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; import org.lflang.ErrorReporter; +import org.lflang.FileConfig; +import org.lflang.Target; import org.lflang.TargetConfig; import org.lflang.TargetProperty.ClockSyncMode; import org.lflang.federated.generator.FedFileConfig; @@ -46,8 +48,7 @@ * @author Edward A. Lee * @author Soroush Bateni */ -public class FedLauncher { - +public class FedLauncherGenerator { protected TargetConfig targetConfig; protected FedFileConfig fileConfig; protected ErrorReporter errorReporter; @@ -57,44 +58,12 @@ public class FedLauncher { * @param fileConfig The current file configuration. * @param errorReporter A error reporter for reporting any errors or warnings during the code generation */ - public FedLauncher(TargetConfig targetConfig, FedFileConfig fileConfig, ErrorReporter errorReporter) { + public FedLauncherGenerator(TargetConfig targetConfig, FedFileConfig fileConfig, ErrorReporter errorReporter) { this.targetConfig = targetConfig; this.fileConfig = fileConfig; this.errorReporter = errorReporter; } - /** - * Return the compile command for a federate. - * - * @param federate The federate to compile. - */ - protected String compileCommandForFederate(FederateInstance federate) { - throw new UnsupportedOperationException("Don't know how to compile the federates."); - } - - /** - * Return the command that will execute a remote federate, assuming that the current - * directory is the top-level project folder. This is used to create a launcher script - * for federates. - * - * @param federate The federate to execute. - */ - protected String executeCommandForRemoteFederate(FederateInstance federate) { - throw new UnsupportedOperationException("Don't know how to execute the federates."); - } - - /** - * Return the command that will execute a local federate, assuming that the current - * directory is the top-level project folder. This is used to create a launcher script - * for federates. - * - * @param federate The federate to execute. - */ - protected String executeCommandForLocalFederate(FedFileConfig fileConfig, - FederateInstance federate) { - throw new UnsupportedOperationException("Don't know how to execute the federates."); - } - /** * Create the launcher shell scripts. This will create one or two files * in the output path (bin directory). The first has name equal to @@ -131,13 +100,13 @@ protected String executeCommandForLocalFederate(FedFileConfig fileConfig, * openssl version * * @param federates A list of federate instances in the federation - * @param federationRTIProperties Contains relevant properties of the RTI. + * @param rtiConfig * Can have values for 'host', 'dir', and 'user' */ public void createLauncher( List federates, - LinkedHashMap federationRTIProperties - ) throws IOException { + RtiConfig rtiConfig + ) { // NOTE: It might be good to use screen when invoking the RTI // or federates remotely, so you can detach and the process keeps running. // However, I was unable to get it working properly. @@ -154,12 +123,10 @@ public void createLauncher( StringBuilder distCode = new StringBuilder(); shCode.append(getSetupCode() + "\n"); String distHeader = getDistHeader(); - Object host = federationRTIProperties.get("host"); - Object target = host; + String host = rtiConfig.getHost(); + String target = host; - Path path = Path.of(federationRTIProperties.get("dir") == null ? "LinguaFrancaRemote" : federationRTIProperties.get("dir").toString()); - - Object user = federationRTIProperties.get("user"); + String user = rtiConfig.getUser(); if (user != null) { target = user + "@" + host; } @@ -199,17 +166,18 @@ public void createLauncher( // Index used for storing pids of federates int federateIndex = 0; for (FederateInstance federate : federates) { + var buildConfig = getBuildConfig(federate.targetConfig.target, federate, fileConfig, errorReporter); if (federate.isRemote) { Path fedRelSrcGenPath = fileConfig.getOutPath().relativize(fileConfig.getSrcGenPath()).resolve(federate.name); if(distCode.length() == 0) distCode.append(distHeader + "\n"); String logFileName = String.format("log/%s_%s.log", fileConfig.name, federate.name); - String compileCommand = compileCommandForFederate(federate); + String compileCommand = buildConfig.compileCommand(); // FIXME: Should $FEDERATION_ID be used to ensure unique directories, executables, on the remote host? - distCode.append(getDistCode(path, federate, fedRelSrcGenPath, logFileName, fileConfig.getSrcGenPath(), compileCommand) + "\n"); - String executeCommand = executeCommandForRemoteFederate(federate); - shCode.append(getFedRemoteLaunchCode(federate, path, logFileName, executeCommand, federateIndex++) + "\n"); + distCode.append(getDistCode(rtiConfig.getDirectory(), federate, fedRelSrcGenPath, logFileName, fileConfig.getSrcGenPath(), compileCommand) + "\n"); + String executeCommand = buildConfig.remoteExecuteCommand(); + shCode.append(getFedRemoteLaunchCode(federate, rtiConfig.getDirectory(), logFileName, executeCommand, federateIndex++) + "\n"); } else { - String executeCommand = executeCommandForLocalFederate(fileConfig, federate); + String executeCommand = buildConfig.localExecuteCommand(); shCode.append(getFedLocalLaunchCode(federate, executeCommand, federateIndex++) + "\n"); } } @@ -232,7 +200,11 @@ public void createLauncher( // Create bin directory for the script. if (!Files.exists(fileConfig.binPath)) { - Files.createDirectories(fileConfig.binPath); + try { + Files.createDirectories(fileConfig.binPath); + } catch (IOException e) { + errorReporter.reportError("Unable to create directory: " + fileConfig.binPath); + } } System.out.println("##### Generating launcher for federation " @@ -246,9 +218,19 @@ public void createLauncher( file.delete(); } - FileOutputStream fOut = new FileOutputStream(file); - fOut.write(shCode.toString().getBytes()); - fOut.close(); + FileOutputStream fOut = null; + try { + fOut = new FileOutputStream(file); + } catch (FileNotFoundException e) { + errorReporter.reportError("Unable to find file: " + file); + } + try { + fOut.write(shCode.toString().getBytes()); + fOut.close(); + } catch (IOException e) { + errorReporter.reportError("Unable to write to file: " + file); + } + if (!file.setExecutable(true, false)) { errorReporter.reportWarning("Unable to make launcher script executable."); } @@ -260,11 +242,17 @@ public void createLauncher( file.delete(); } if (distCode.length() > 0) { - fOut = new FileOutputStream(file); - fOut.write(distCode.toString().getBytes()); - fOut.close(); - if (!file.setExecutable(true, false)) { - errorReporter.reportWarning("Unable to make distributor script executable."); + try { + fOut = new FileOutputStream(file); + fOut.write(distCode.toString().getBytes()); + fOut.close(); + if (!file.setExecutable(true, false)) { + errorReporter.reportWarning("Unable to make file executable: " + file); + } + } catch (FileNotFoundException e) { + errorReporter.reportError("Unable to find file: " + file); + } catch (IOException e) { + errorReporter.reportError("Unable to write to file " + file); } } } @@ -451,7 +439,7 @@ private String getUserHost(Object user, Object host) { private String getFedRemoteLaunchCode( FederateInstance federate, - Object path, + Path path, String logFileName, String executeCommand, int federateIndex @@ -482,4 +470,13 @@ private String getFedLocalLaunchCode(FederateInstance federate, String executeCo executeCommand, federateIndex); } + + private BuildConfig getBuildConfig(Target target, FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { + return switch(target) { + case C, CCPP -> new CBuildConfig(federate, fileConfig, errorReporter); + case Python -> new PyBuildConfig(federate, fileConfig, errorReporter); + case TS -> new TsBuildConfig(federate, fileConfig, errorReporter); + case CPP, Rust -> throw new UnsupportedOperationException(); + }; + } } diff --git a/org.lflang/src/org/lflang/federated/launcher/FedPyLauncher.java b/org.lflang/src/org/lflang/federated/launcher/FedPyLauncher.java deleted file mode 100644 index 414caf2ee4..0000000000 --- a/org.lflang/src/org/lflang/federated/launcher/FedPyLauncher.java +++ /dev/null @@ -1,83 +0,0 @@ -/************* - * Copyright (c) 2021, The University of California at Berkeley. - * Copyright (c) 2021, The University of Texas at Dallas. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - ***************/ - -package org.lflang.federated.launcher; - -import org.lflang.ErrorReporter; -import org.lflang.TargetConfig; -import org.lflang.federated.generator.FedFileConfig; -import org.lflang.federated.generator.FederateInstance; - -/** - * Utility class that can be used to create a launcher for federated LF programs - * that are written in Python. - * - * @author Soroush Bateni - * - */ -public class FedPyLauncher extends FedLauncher { - /** - * Create an instance of FedPyLauncher. - * - * @param targetConfig The current target configuration. - * @param fileConfig The current file configuration. - * @param errorReporter A error reporter for reporting any errors or warnings during the code generation - */ - public FedPyLauncher( - TargetConfig targetConfig, - FedFileConfig fileConfig, - ErrorReporter errorReporter - ) { - super(targetConfig, fileConfig, errorReporter); - } - - /** - * Return the command that will execute a remote federate, assuming that the current - * directory is the top-level project folder. This is used to create a launcher script - * for federates. - * - * @param federate The federate to execute. - */ - @Override - protected - String executeCommandForRemoteFederate(FederateInstance federate) { - return "python3 src-gen/"+fileConfig.name+"/"+federate.name+"/"+fileConfig.name+"_"+federate.name+" -i '$FEDERATION_ID'"; - } - - /** - * Return the command that will execute a local federate, assuming that the current - * directory is the top-level project folder. This is used to create a launcher script - * for federates. - * - * @param federate The federate to execute. - */ - @Override - protected - String executeCommandForLocalFederate(FedFileConfig fileConfig, FederateInstance federate) { - return "python3 " + fileConfig.getSrcGenPath() + "/" + federate.name + "/" + federate.name+".py -i $FEDERATION_ID"; - } -} diff --git a/org.lflang/src/org/lflang/federated/launcher/FedTSLauncher.java b/org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java similarity index 68% rename from org.lflang/src/org/lflang/federated/launcher/FedTSLauncher.java rename to org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java index 67dbd134fa..1879f35244 100644 --- a/org.lflang/src/org/lflang/federated/launcher/FedTSLauncher.java +++ b/org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java @@ -37,34 +37,26 @@ * @author Soroush Bateni * @author Hokeun Kim */ -public class FedTSLauncher extends FedLauncher { +public class TsBuildConfig extends BuildConfig { - /** - * Create an instance of FedCLauncher. - * - * @param targetConfig The current target configuration. - * @param fileConfig The current file configuration. - * @param errorReporter A error reporter for reporting any errors or warnings during the code generation - */ - public FedTSLauncher( - TargetConfig targetConfig, - FedFileConfig fileConfig, - ErrorReporter errorReporter - ) { - super(targetConfig, fileConfig, errorReporter); + + public TsBuildConfig(FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { + super(federate, fileConfig, errorReporter); + } + + @Override + public String compileCommand() { + return null; } - - /** - * Return the command that will execute a local federate, assuming that the current - * directory is the top-level project folder. This is used to create a launcher script - * for federates. - * - * @param federate The federate to execute. - */ + @Override - protected - String executeCommandForLocalFederate(FedFileConfig fileConfig, FederateInstance federate) { + public String localExecuteCommand() { String jsFilename = federate.name + ".js"; return "node "+fileConfig.getSrcGenPath().resolve(federate.name).resolve("dist").resolve(jsFilename)+" -i $FEDERATION_ID"; } + + @Override + public String remoteExecuteCommand() { + return null; + } } diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index 32ad03e08b..3f3e08cc7e 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -1,11 +1,8 @@ package org.lflang.generator; -import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Properties; @@ -13,19 +10,12 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.xtext.generator.IGeneratorContext; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.validation.CheckMode; -import org.eclipse.xtext.validation.IResourceValidator; -import org.eclipse.xtext.validation.Issue; import org.eclipse.xtext.xbase.lib.IteratorExtensions; import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.FileConfig; -import org.lflang.Target; import org.lflang.TargetConfig; import org.lflang.TargetConfig.DockerOptions; import org.lflang.TargetProperty.BuildType; @@ -34,19 +24,13 @@ import org.lflang.generator.LFGeneratorContext.Mode; import org.lflang.TargetProperty; import org.lflang.TargetProperty.SchedulerOption; -import org.lflang.graph.InstantiationGraph; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; -import org.lflang.lf.Import; import org.lflang.lf.Instantiation; import org.lflang.lf.KeyValuePair; import org.lflang.lf.KeyValuePairs; -import org.lflang.lf.Model; -import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; import org.lflang.lf.TargetDecl; -import org.lflang.util.FileUtil; -import org.lflang.util.IteratorUtil; /** * A helper class with functions that may be useful for code @@ -65,7 +49,7 @@ private GeneratorUtils() { /** * Return the target declaration found in the given resource. */ - public static TargetDecl findTarget(Resource resource) { + public static TargetDecl findTargetDecl(Resource resource) { return findAll(resource, TargetDecl.class).iterator().next(); } @@ -81,62 +65,8 @@ public static TargetConfig getTargetConfig( TargetDecl target, ErrorReporter errorReporter ) { - final TargetConfig targetConfig = new TargetConfig(target); // FIXME: why not just do all of this in the constructor? - if (target.getConfig() != null) { - List pairs = target.getConfig().getPairs(); - TargetProperty.set(targetConfig, pairs != null ? pairs : List.of(), errorReporter); - } - if (args.containsKey("no-compile")) { - targetConfig.noCompile = true; - } - if (args.containsKey("docker")) { - var arg = args.getProperty("docker"); - if (Boolean.parseBoolean(arg)) { - targetConfig.dockerOptions = new DockerOptions(); - } else { - targetConfig.dockerOptions = null; - } - // FIXME: this is pretty ad-hoc and does not account for more complex overrides yet. - } - if (args.containsKey("build-type")) { - targetConfig.cmakeBuildType = (BuildType) UnionType.BUILD_TYPE_UNION.forName(args.getProperty("build-type")); - } - if (args.containsKey("logging")) { - targetConfig.logLevel = LogLevel.valueOf(args.getProperty("logging").toUpperCase()); - } - if (args.containsKey("workers")) { - targetConfig.workers = Integer.parseInt(args.getProperty("workers")); - } - if (args.containsKey("threading")) { - targetConfig.threading = Boolean.parseBoolean(args.getProperty("threading")); - } - if (args.containsKey("target-compiler")) { - targetConfig.compiler = args.getProperty("target-compiler"); - } - if (args.containsKey("scheduler")) { - targetConfig.schedulerType = SchedulerOption.valueOf( - args.getProperty("scheduler") - ); - targetConfig.setByUser.add(TargetProperty.SCHEDULER); - } - if (args.containsKey("target-flags")) { - targetConfig.compilerFlags.clear(); - if (!args.getProperty("target-flags").isEmpty()) { - targetConfig.compilerFlags.addAll(List.of( - args.getProperty("target-flags").split(" ") - )); - } - } - if (args.containsKey("runtime-version")) { - targetConfig.runtimeVersion = args.getProperty("runtime-version"); - } - if (args.containsKey("external-runtime-path")) { - targetConfig.externalRuntimePath = args.getProperty("external-runtime-path"); - } - if (args.containsKey(TargetProperty.KEEPALIVE.description)) { - targetConfig.keepalive = Boolean.parseBoolean( - args.getProperty(TargetProperty.KEEPALIVE.description)); - } + final TargetConfig targetConfig = new TargetConfig(args, target, errorReporter); + return targetConfig; } diff --git a/org.lflang/src/org/lflang/generator/MainContext.java b/org.lflang/src/org/lflang/generator/MainContext.java index e7d442e1d3..93be617905 100644 --- a/org.lflang/src/org/lflang/generator/MainContext.java +++ b/org.lflang/src/org/lflang/generator/MainContext.java @@ -164,7 +164,7 @@ public void reportProgress(String message, int percentage) { */ public void loadTargetConfig() { this.targetConfig = GeneratorUtils.getTargetConfig( - args, GeneratorUtils.findTarget(fileConfig.resource), errorReporter + args, GeneratorUtils.findTargetDecl(fileConfig.resource), errorReporter ); } } diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 21ea083e49..3fd3675d52 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -301,7 +301,7 @@ class TSGenerator( if (ret != 0) { val errors: String = pnpmInstall.errors.toString() errorReporter.reportError( - GeneratorUtils.findTarget(resource), + GeneratorUtils.findTargetDecl(resource), "ERROR: pnpm install command failed" + if (errors.isBlank()) "." else ":\n$errors") } installProtoBufsIfNeeded(true, path, context.cancelIndicator) @@ -318,10 +318,10 @@ class TSGenerator( if (npmInstall.run(context.cancelIndicator) != 0) { errorReporter.reportError( - GeneratorUtils.findTarget(resource), + GeneratorUtils.findTargetDecl(resource), "ERROR: npm install command failed: " + npmInstall.errors.toString()) errorReporter.reportError( - GeneratorUtils.findTarget(resource), "ERROR: npm install command failed." + + GeneratorUtils.findTargetDecl(resource), "ERROR: npm install command failed." + "\nFor installation instructions, see: https://www.npmjs.com/get-npm") return } From 75ec26aa7d5cf1036a4c02598ca5ebb91bce6730 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 17 Mar 2023 23:02:15 -0700 Subject: [PATCH 02/22] Add new files --- .../federated/generator/FedTargetConfig.java | 75 +++++++++++++++++++ .../federated/launcher/BuildConfig.java | 40 ++++++++++ .../federated/launcher/PyBuildConfig.java | 22 ++++++ .../lflang/federated/launcher/RtiConfig.java | 53 +++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java create mode 100644 org.lflang/src/org/lflang/federated/launcher/BuildConfig.java create mode 100644 org.lflang/src/org/lflang/federated/launcher/PyBuildConfig.java create mode 100644 org.lflang/src/org/lflang/federated/launcher/RtiConfig.java diff --git a/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java b/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java new file mode 100644 index 0000000000..5d6b6eee41 --- /dev/null +++ b/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java @@ -0,0 +1,75 @@ +package org.lflang.federated.generator; + +import static org.lflang.ASTUtils.convertToEmptyListIfNull; + +import org.lflang.ErrorReporter; +import org.lflang.TargetConfig; +import org.lflang.TargetProperty; +import org.lflang.generator.GeneratorUtils; +import org.lflang.generator.LFGeneratorContext; +import org.eclipse.emf.ecore.resource.Resource; + +/** + * Subclass of TargetConfig with a specialized constructor for creating configurations for federates. + * @author Marten Lohstroh + */ +public class FedTargetConfig extends TargetConfig { + + /** + * Create a configuration for a federate given a main context and the resource in which the class + * of the federate is specified. + * @param context The generator context. + * @param federateResource The resource in which to find the reactor class of the federate. + */ + public FedTargetConfig(LFGeneratorContext context, Resource federateResource) { + // Create target config based on the main .lf file + super( + context.getArgs(), + GeneratorUtils.findTargetDecl(context.getFileConfig().resource), + context.getErrorReporter() + ); + + mergeImportedConfig( + federateResource, + context.getFileConfig().resource, + context.getErrorReporter() + ); + + clearPropertiesToIgnore(); + + ((FedFileConfig)context.getFileConfig()).relativizePaths(this); + + } + + /** + * If the federate that target configuration applies to is imported, merge target properties + * declared in the file that it was imported from. + * @param federateResource The resource where the class of the federate is specified. + * @param mainResource The resource in which the federation (i.e., main reactor) is specified. + * @param errorReporter An error reporter to use when problems are encountered. + */ + private void mergeImportedConfig( + Resource federateResource, + Resource mainResource, + ErrorReporter errorReporter) { + // If the federate is imported, then update the configuration based on target properties + // in the imported file. + if (!federateResource.equals(mainResource)) { + var importedTargetDecl = GeneratorUtils.findTargetDecl(federateResource); + // Merge properties + TargetProperty.update( + this, + convertToEmptyListIfNull(importedTargetDecl.getConfig().getPairs()), + errorReporter + ); + } + } + + /** + * Method for the removal of things that should not appear in the target config of a federate. + */ + private void clearPropertiesToIgnore() { + this.setByUser.remove(TargetProperty.CLOCK_SYNC); + this.setByUser.remove(TargetProperty.CLOCK_SYNC_OPTIONS); + } +} diff --git a/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java b/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java new file mode 100644 index 0000000000..3adb0edbcf --- /dev/null +++ b/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java @@ -0,0 +1,40 @@ +package org.lflang.federated.launcher; + +import org.lflang.ErrorReporter; +import org.lflang.federated.generator.FedFileConfig; +import org.lflang.federated.generator.FederateInstance; + +public abstract class BuildConfig { + + protected final FederateInstance federate; + protected final ErrorReporter errorReporter; + + protected final FedFileConfig fileConfig; + + public BuildConfig(FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { + this.errorReporter = errorReporter; + this.federate = federate; + this.fileConfig = fileConfig; + } + + /** + * Return the compile command for the federate that this build configuration belongs to. + */ + public String compileCommand() { + throw new UnsupportedOperationException(); + } + + /** + * Return the command that will execute the federate that this build configuration belongs to + * locally, assuming that the current directory is the top-level project folder. + */ + public abstract String localExecuteCommand(); + + /** + * Return the command that will execute the federate that this build configuration belongs to + * remotely, assuming that the current directory is the top-level project folder. + */ + public String remoteExecuteCommand() { + throw new UnsupportedOperationException(); + } +} diff --git a/org.lflang/src/org/lflang/federated/launcher/PyBuildConfig.java b/org.lflang/src/org/lflang/federated/launcher/PyBuildConfig.java new file mode 100644 index 0000000000..85d6dc4a0f --- /dev/null +++ b/org.lflang/src/org/lflang/federated/launcher/PyBuildConfig.java @@ -0,0 +1,22 @@ +package org.lflang.federated.launcher; + +import org.lflang.ErrorReporter; +import org.lflang.federated.generator.FedFileConfig; +import org.lflang.federated.generator.FederateInstance; + +public class PyBuildConfig extends BuildConfig { + + public PyBuildConfig(FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { + super(federate, fileConfig, errorReporter); + } + + @Override + public String localExecuteCommand() { + return "python3 " + fileConfig.getSrcGenPath() + "/" + federate.name + "/" + federate.name+".py -i $FEDERATION_ID"; + } + + @Override + public String remoteExecuteCommand() { + return "python3 src-gen/"+fileConfig.name+"/"+federate.name+"/"+fileConfig.name+"_"+federate.name+" -i '$FEDERATION_ID'"; + } +} diff --git a/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java b/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java new file mode 100644 index 0000000000..4d30d00e77 --- /dev/null +++ b/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java @@ -0,0 +1,53 @@ +package org.lflang.federated.launcher; + +import java.nio.file.Path; + +public class RtiConfig { + + private Path directory; + + private String host; + + private int port; + + private String user; + + public RtiConfig() { + this.directory = Path.of("LinguaFrancaRemote"); + this.host = "localhost"; + this.port = 0; + } + + public Path getDirectory() { + return directory; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getUser() { + return user; + } + + public void setDirectory(Path directory) { + this.directory = directory; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + public void setUser(String user) { + this.user = user; + } + +} From 28e3f86582d5a3f3ccdebbf85034530ff3a2641a Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 00:02:49 -0700 Subject: [PATCH 03/22] Fix issue with federated C tests --- .../src/org/lflang/federated/generator/FedFileConfig.java | 5 +++++ .../src/org/lflang/federated/launcher/CBuildConfig.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java index 3ec2b05813..097cf2eb8f 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java +++ b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java @@ -92,6 +92,11 @@ public Path getFedGenPath() { return srcPkgPath.resolve("fed-gen").resolve(this.name); } + /** + * Return the path to the directory in which the executables of compiled federates are stored. + */ + public Path getFedBinPath() { return getFedGenPath().resolve("bin"); } + @Override public void doClean() throws IOException { super.doClean(); diff --git a/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java b/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java index e6d05083f2..2395cba83b 100644 --- a/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java +++ b/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java @@ -72,6 +72,6 @@ String remoteExecuteCommand() { @Override public String localExecuteCommand() { - return fileConfig.binPath.resolve(fileConfig.name)+" -i $FEDERATION_ID"; + return fileConfig.getFedBinPath().resolve(federate.name)+" -i $FEDERATION_ID"; } } From b5b2bc17019e4353ad58ebe7a8084ef1a1c4a6cc Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 00:19:20 -0700 Subject: [PATCH 04/22] Fix NPE --- .../lflang/federated/generator/FedFileConfig.java | 10 +++++----- .../federated/generator/FedTargetConfig.java | 15 +++++++++------ .../lflang/federated/launcher/TsBuildConfig.java | 5 +---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java index 097cf2eb8f..e360a8f14d 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java +++ b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java @@ -122,13 +122,13 @@ private void relativizePathList(List paths) { paths.addAll(tempList); } + /** + * + * @param path + * @return + */ private String relativizePath(String path) { Path resolvedPath = this.srcPath.resolve(path).toAbsolutePath(); return this.getSrcPath().relativize(resolvedPath).toString(); } - - - - - } diff --git a/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java b/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java index 5d6b6eee41..9af20347c2 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java +++ b/org.lflang/src/org/lflang/federated/generator/FedTargetConfig.java @@ -56,12 +56,15 @@ private void mergeImportedConfig( // in the imported file. if (!federateResource.equals(mainResource)) { var importedTargetDecl = GeneratorUtils.findTargetDecl(federateResource); - // Merge properties - TargetProperty.update( - this, - convertToEmptyListIfNull(importedTargetDecl.getConfig().getPairs()), - errorReporter - ); + var targetProperties = importedTargetDecl.getConfig(); + if (targetProperties != null) { + // Merge properties + TargetProperty.update( + this, + convertToEmptyListIfNull(targetProperties.getPairs()), + errorReporter + ); + } } } diff --git a/org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java b/org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java index 1879f35244..87b7ffaeb1 100644 --- a/org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java +++ b/org.lflang/src/org/lflang/federated/launcher/TsBuildConfig.java @@ -36,6 +36,7 @@ * * @author Soroush Bateni * @author Hokeun Kim + * @author Marten Lohstroh */ public class TsBuildConfig extends BuildConfig { @@ -55,8 +56,4 @@ public String localExecuteCommand() { return "node "+fileConfig.getSrcGenPath().resolve(federate.name).resolve("dist").resolve(jsFilename)+" -i $FEDERATION_ID"; } - @Override - public String remoteExecuteCommand() { - return null; - } } From 383af4cd3dd2671cc43453538dbd386fdb579962 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 18:22:23 -0700 Subject: [PATCH 05/22] Marked authenticated federation test as failing --- .../C/src/federated/{ => failing}/SimpleFederatedAuthenticated.lf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/C/src/federated/{ => failing}/SimpleFederatedAuthenticated.lf (100%) diff --git a/test/C/src/federated/SimpleFederatedAuthenticated.lf b/test/C/src/federated/failing/SimpleFederatedAuthenticated.lf similarity index 100% rename from test/C/src/federated/SimpleFederatedAuthenticated.lf rename to test/C/src/federated/failing/SimpleFederatedAuthenticated.lf From ef492b06234847bbeca74eabed7fed4bf03be141 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 21:10:22 -0700 Subject: [PATCH 06/22] Various cleanups --- .../federated/generator/FedGenerator.java | 94 ++++--------------- .../federated/generator/FederateInstance.java | 4 - .../launcher/FedLauncherGenerator.java | 3 +- 3 files changed, 17 insertions(+), 84 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java index 249cdba02a..5917168438 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java +++ b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java @@ -1,12 +1,10 @@ package org.lflang.federated.generator; -import static org.lflang.ASTUtils.convertToEmptyListIfNull; import static org.lflang.generator.DockerGenerator.dockerGeneratorFactory; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -29,7 +27,6 @@ import org.eclipse.xtext.resource.XtextResourceSet; import org.eclipse.xtext.util.RuntimeIOException; import org.eclipse.xtext.xbase.lib.Exceptions; -import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; import org.lflang.ASTUtils; import org.lflang.ErrorReporter; @@ -37,7 +34,6 @@ import org.lflang.LFStandaloneSetup; import org.lflang.Target; import org.lflang.TargetConfig; -import org.lflang.TargetProperty; import org.lflang.TargetProperty.CoordinationType; import org.lflang.federated.launcher.FedLauncherGenerator; import org.lflang.federated.launcher.RtiConfig; @@ -60,33 +56,12 @@ import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; import org.lflang.lf.Reactor; +import org.lflang.util.Averager; import com.google.inject.Injector; public class FedGenerator { - /** Average asynchronously reported numbers and do something with them. */ - private static class Averager { - private final int n; - private final int[] reports; - - /** Create an averager of reports from {@code n} processes. */ - public Averager(int n) { - this.n = n; - reports = new int[n]; - } - - /** - * Receive {@code x} from process {@code id} and invoke {@code callback} - * on the mean of the numbers most recently reported by the processes. - */ - public synchronized void report(int id, int x, Procedure1 callback) { - assert 0 <= id && id < n; - reports[id] = x; - callback.apply(Arrays.stream(reports).sum() / n); - } - } - private final FedFileConfig fileConfig; private final ErrorReporter errorReporter; /** @@ -97,10 +72,6 @@ public synchronized void report(int id, int x, Procedure1 callback) { * A list of federate instances. */ private final List federates = new ArrayList<>(); - /** - * A map from federate IDs to federate instances. - */ - private final Map federateByID = new LinkedHashMap<>(); final RtiConfig rtiConfig = new RtiConfig(); @@ -171,13 +142,21 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws Map codeMapMap = compileFederates(context, lf2lfCodeMapMap, subContexts -> { createDockerFiles(context, subContexts); - (new FedLauncherGenerator(this.targetConfig, this.fileConfig, this.errorReporter)).createLauncher(federates, rtiConfig); + generateLaunchScript(); }); context.finish(Status.COMPILED, codeMapMap); return false; } + private void generateLaunchScript() { + new FedLauncherGenerator( + this.targetConfig, + this.fileConfig, + this.errorReporter + ).doGenerate(federates, rtiConfig); + } + private void createDockerFiles(LFGeneratorContext context, List subContexts) { if (context.getTargetConfig().dockerOptions == null) return; final List services = new ArrayList<>(); @@ -207,7 +186,7 @@ private void createDockerFiles(LFGeneratorContext context, List subC /** * Check if a clean was requested from the standalone compiler and perform * the clean step. - * @param context + * @param context Context in which the generator operates */ private void cleanIfNeeded(LFGeneratorContext context) { if (context.getArgs().containsKey(BuildParm.CLEAN.getKey())) { @@ -219,43 +198,6 @@ private void cleanIfNeeded(LFGeneratorContext context) { } } - /** - * Create a launcher for the federation. - */ - public void createLauncher( - LFGeneratorContext context, - List subContexts, - LinkedHashMap federationRTIProperties - ) { - - -// FedLauncher launcher; -// if (federates.size() == 0) { -// // no federates, use target properties of main file -// TargetDecl targetDecl = GeneratorUtils.findTarget(fileConfig.resource); -// launcher = FedLauncherFactory.getLauncher(Target.fromDecl(targetDecl), -// targetConfig, -// fileConfig, -// errorReporter); -// } else { -// launcher = FedLauncherFactory.getLauncher( -// federates.get(0), // FIXME: This would not work for mixed-target programs. -// fileConfig, -// errorReporter -// ); -// } -// try { -// launcher.createLauncher( -// federates, -// federationRTIProperties -// ); -// } catch (IOException e) { -// errorReporter.reportError(e.getMessage()); -// } -// -// // System.out.println(PythonInfoGenerator.generateFedRunInfo(fileConfig)); - } - /** Return whether federated execution is supported for {@code resource}. */ private boolean federatedExecutionIsSupported(Resource resource) { var target = Target.fromDecl(GeneratorUtils.findTargetDecl(resource)); @@ -298,7 +240,7 @@ private Map compileFederates( var compileThreadPool = Executors.newFixedThreadPool(numOfCompileThreads); System.out.println("******** Using "+numOfCompileThreads+" threads to compile the program."); Map codeMapMap = new ConcurrentHashMap<>(); - List subContexts = Collections.synchronizedList(new ArrayList()); + List subContexts = Collections.synchronizedList(new ArrayList<>()); Averager averager = new Averager(federates.size()); final var threadSafeErrorReporter = new SynchronizedErrorReporter(errorReporter); for (int i = 0; i < federates.size(); i++) { @@ -466,18 +408,14 @@ private List getFederateInstances(Instantiation instantiation, int federateID = federates.size(); var resource = instantiation.getReactorClass().eResource(); var federateTargetConfig = new FedTargetConfig(context, resource); - // FIXME: do we need this? - //var federateFileConfig = LFGenerator.createFileConfig(resource, fileConfig.getSrcGenPath(), false); FederateInstance federateInstance = new FederateInstance( instantiation, federateID, i, federateTargetConfig, - fileConfig, errorReporter); federates.add(federateInstance); federateInstances.add(federateInstance); - federateByID.put(federateID, federateInstance); if (instantiation.getHost() != null) { federateInstance.host = instantiation.getHost().getAddr(); @@ -502,16 +440,16 @@ private List getFederateInstances(Instantiation instantiation, * Replace connections between federates in the AST with proxies that * handle sending and receiving data. * - * @param fedReactor + * @param federate Reactor class of the federate. */ - private void replaceFederateConnectionsWithProxies(Reactor fedReactor) { + private void replaceFederateConnectionsWithProxies(Reactor federate) { // Each connection in the AST may represent more than one connection between // federate instances because of banks and multiports. We need to generate communication // for each of these. To do this, we create a ReactorInstance so that we don't have // to duplicate the rather complicated logic in that class. We specify a depth of 1, // so it only creates the reactors immediately within the top level, not reactors // that those contain. - ReactorInstance mainInstance = new ReactorInstance(fedReactor, errorReporter); + ReactorInstance mainInstance = new ReactorInstance(federate, errorReporter); for (ReactorInstance child : mainInstance.children) { for (PortInstance output : child.outputs) { @@ -520,7 +458,7 @@ private void replaceFederateConnectionsWithProxies(Reactor fedReactor) { } // Remove the connections at the top level - fedReactor.getConnections().clear(); + federate.getConnections().clear(); // There will be AST transformations that invalidate some info // cached in ReactorInstance. FIXME: most likely not needed anymore diff --git a/org.lflang/src/org/lflang/federated/generator/FederateInstance.java b/org.lflang/src/org/lflang/federated/generator/FederateInstance.java index 38532afcdf..92faefe400 100644 --- a/org.lflang/src/org/lflang/federated/generator/FederateInstance.java +++ b/org.lflang/src/org/lflang/federated/generator/FederateInstance.java @@ -86,8 +86,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY */ public class FederateInstance { - private final FileConfig fileConfig; - /** * Construct a new instance with the specified instantiation of * of a top-level reactor. The federate will be given the specified @@ -103,14 +101,12 @@ public FederateInstance( int id, int bankIndex, TargetConfig targetConfig, - FileConfig fileConfig, ErrorReporter errorReporter) { this.instantiation = instantiation; this.id = id; this.bankIndex = bankIndex; this.errorReporter = errorReporter; this.targetConfig = targetConfig; - this.fileConfig = fileConfig; if (instantiation != null) { this.name = instantiation.getName(); diff --git a/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java b/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java index 99d608e687..5c575419b5 100644 --- a/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java +++ b/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java @@ -35,7 +35,6 @@ import java.util.List; import org.lflang.ErrorReporter; -import org.lflang.FileConfig; import org.lflang.Target; import org.lflang.TargetConfig; import org.lflang.TargetProperty.ClockSyncMode; @@ -103,7 +102,7 @@ public FedLauncherGenerator(TargetConfig targetConfig, FedFileConfig fileConfig, * @param rtiConfig * Can have values for 'host', 'dir', and 'user' */ - public void createLauncher( + public void doGenerate( List federates, RtiConfig rtiConfig ) { From 0893bd5d396624d1d229457916fd8921125d9f90 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 22:22:28 -0700 Subject: [PATCH 07/22] Add missing file --- org.lflang/src/org/lflang/util/Averager.java | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 org.lflang/src/org/lflang/util/Averager.java diff --git a/org.lflang/src/org/lflang/util/Averager.java b/org.lflang/src/org/lflang/util/Averager.java new file mode 100644 index 0000000000..81349c9db1 --- /dev/null +++ b/org.lflang/src/org/lflang/util/Averager.java @@ -0,0 +1,27 @@ +package org.lflang.util; + +import java.util.Arrays; + +import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; + +/** Average asynchronously reported numbers and do something with them. */ +public class Averager { + private final int n; + private final int[] reports; + + /** Create an averager of reports from {@code n} processes. */ + public Averager(int n) { + this.n = n; + reports = new int[n]; + } + + /** + * Receive {@code x} from process {@code id} and invoke {@code callback} + * on the mean of the numbers most recently reported by the processes. + */ + public synchronized void report(int id, int x, Procedure1 callback) { + assert 0 <= id && id < n; + reports[id] = x; + callback.apply(Arrays.stream(reports).sum() / n); + } +} From 41d7d7eab0189a118921885b29a488917714c324 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 22:43:03 -0700 Subject: [PATCH 08/22] Mostly comments --- .../federated/generator/FedGenerator.java | 74 ++++++++++++++----- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java index 5917168438..710b5f2f70 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java +++ b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java @@ -62,19 +62,34 @@ public class FedGenerator { - private final FedFileConfig fileConfig; - private final ErrorReporter errorReporter; + /** - * The current target configuration. + * */ - private final TargetConfig targetConfig; + private final ErrorReporter errorReporter; + /** * A list of federate instances. */ private final List federates = new ArrayList<>(); + /** + * File configuration to be used during the LF code generation stage (not the target code + * generation stage of individual federates). + */ + private final FedFileConfig fileConfig; + + /** + * Configuration of the RTI. + */ final RtiConfig rtiConfig = new RtiConfig(); + /** + * Target configuration of the federation; drawn from the file + * in which the federated reactor is defined. + */ + private final TargetConfig targetConfig; + /** * A map from instantiations to the federate instances for that * instantiation. @@ -90,12 +105,26 @@ public class FedGenerator { */ private Instantiation mainDef; + /** + * Create a new generator and initialize a file configuration, target configuration, and error + * reporter. + * @param context + */ public FedGenerator(LFGeneratorContext context) { this.fileConfig = (FedFileConfig) context.getFileConfig(); this.targetConfig = context.getTargetConfig(); this.errorReporter = context.getErrorReporter(); } + /** + * Produce LF code for each federate in a separate file, then invoke a target-specific code + * generator for each of those files. + * + * @param resource The resource that has the federated main reactor in it + * @param context The context in which to carry out the code generation. + * @return False if no errors have occurred, true otherwise. + * @throws IOException + */ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws IOException { if (!federatedExecutionIsSupported(resource)) return true; cleanIfNeeded(context); @@ -109,17 +138,17 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws processCLIArguments(context); // Find the federated reactor - Reactor fedReactor = FedASTUtils.findFederatedReactor(resource); + Reactor federation = FedASTUtils.findFederatedReactor(resource); // Extract some useful information about the federation - analyzeFederates(fedReactor, context); + analyzeFederates(federation, context); // Find all the connections between federates. // For each connection between federates, replace it in the // AST with an action (which inherits the delay) and four reactions. // The action will be physical for physical connections and logical // for logical connections. - replaceFederateConnectionsWithProxies(fedReactor); + replaceFederateConnectionsWithProxies(federation); FedEmitter fedEmitter = new FedEmitter( fileConfig, @@ -127,7 +156,8 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws errorReporter, rtiConfig ); - // Generate code for each federate + + // Generate LF code for each federate. Map lf2lfCodeMapMap = new HashMap<>(); for (FederateInstance federate : federates) { lf2lfCodeMapMap.putAll(fedEmitter.generateFederate( @@ -135,6 +165,7 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws )); } + // Do not invoke target code generators if --no-compile flag is used. if (context.getTargetConfig().noCompile) { context.finish(Status.GENERATED, lf2lfCodeMapMap); return false; @@ -157,6 +188,11 @@ private void generateLaunchScript() { ).doGenerate(federates, rtiConfig); } + /** + * Generate a Dockerfile for each federate and a docker-compose.yml for the federation. + * @param context The main context in which the federation has been compiled. + * @param subContexts The subcontexts in which the federates have been compiled. + */ private void createDockerFiles(LFGeneratorContext context, List subContexts) { if (context.getTargetConfig().dockerOptions == null) return; final List services = new ArrayList<>(); @@ -174,10 +210,10 @@ private void createDockerFiles(LFGeneratorContext context, List subC } // 2. create a docker-compose.yml for the federation try { - // FIXME: https://issue.lf-lang.org/1559 - // It appears that the rtiHost information should come from federationRTIproperties, - // which is a kludge and not in scope here. - new FedDockerComposeGenerator(context, "localhost").writeDockerComposeFile(services); + new FedDockerComposeGenerator( + context, + rtiConfig.getHost() + ).writeDockerComposeFile(services); } catch (IOException e) { throw new RuntimeIOException(e); } @@ -201,7 +237,9 @@ private void cleanIfNeeded(LFGeneratorContext context) { /** Return whether federated execution is supported for {@code resource}. */ private boolean federatedExecutionIsSupported(Resource resource) { var target = Target.fromDecl(GeneratorUtils.findTargetDecl(resource)); - var targetOK = List.of(Target.C, Target.Python, Target.TS, Target.CPP, Target.CCPP).contains(target); + var targetOK = List.of( + Target.C, Target.Python, Target.TS, Target.CPP, Target.CCPP + ).contains(target); if (!targetOK) { errorReporter.reportError( "Federated execution is not supported with target " + target + "." @@ -440,16 +478,16 @@ private List getFederateInstances(Instantiation instantiation, * Replace connections between federates in the AST with proxies that * handle sending and receiving data. * - * @param federate Reactor class of the federate. + * @param federation Reactor class of the federation. */ - private void replaceFederateConnectionsWithProxies(Reactor federate) { + private void replaceFederateConnectionsWithProxies(Reactor federation) { // Each connection in the AST may represent more than one connection between - // federate instances because of banks and multiports. We need to generate communication + // federation instances because of banks and multiports. We need to generate communication // for each of these. To do this, we create a ReactorInstance so that we don't have // to duplicate the rather complicated logic in that class. We specify a depth of 1, // so it only creates the reactors immediately within the top level, not reactors // that those contain. - ReactorInstance mainInstance = new ReactorInstance(federate, errorReporter); + ReactorInstance mainInstance = new ReactorInstance(federation, errorReporter); for (ReactorInstance child : mainInstance.children) { for (PortInstance output : child.outputs) { @@ -458,7 +496,7 @@ private void replaceFederateConnectionsWithProxies(Reactor federate) { } // Remove the connections at the top level - federate.getConnections().clear(); + federation.getConnections().clear(); // There will be AST transformations that invalidate some info // cached in ReactorInstance. FIXME: most likely not needed anymore From e59fdf39bc133dc9ec53deef08171677713d74e0 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 22:46:07 -0700 Subject: [PATCH 09/22] More comments --- .../federated/generator/FedFileConfig.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java index e360a8f14d..2b7f499e46 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java +++ b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java @@ -33,7 +33,6 @@ import org.eclipse.emf.ecore.resource.Resource; import org.lflang.FileConfig; -import org.lflang.TargetProperty; import org.lflang.util.FileUtil; /** @@ -46,10 +45,11 @@ */ public class FedFileConfig extends FileConfig { - public FedFileConfig(Resource resource, Path srcGenBasePath, boolean useHierarchicalBin) throws IOException { - // FIMXE: It is unclear to me that we need this class. + public FedFileConfig( + Resource resource, + Path srcGenBasePath, + boolean useHierarchicalBin) throws IOException { super(resource, srcGenBasePath, useHierarchicalBin); - } public FedFileConfig(FileConfig fileConfig) throws IOException { @@ -113,19 +113,20 @@ public void relativizePaths(FedTargetConfig targetConfig) { relativizePathList(targetConfig.cmakeIncludes); } + /** + * Relativize each path in the given list. + * @param paths The paths to relativize. + */ private void relativizePathList(List paths) { List tempList = new ArrayList<>(); - paths.forEach( f -> { - tempList.add(relativizePath(f)); - }); + paths.forEach(f -> tempList.add(relativizePath(f))); paths.clear(); paths.addAll(tempList); } /** - * - * @param path - * @return + * Relativize a single path. + * @param path The path to relativize. */ private String relativizePath(String path) { Path resolvedPath = this.srcPath.resolve(path).toAbsolutePath(); From 4055e33d9238b86357692b975a16502123a79573 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 22:56:05 -0700 Subject: [PATCH 10/22] Link issue --- .../extensions/FedTargetExtension.java | 1 - .../federated/generator/FedTargetEmitter.java | 38 +------------------ 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java b/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java index 4c41994826..7aa13d6cfc 100644 --- a/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java +++ b/org.lflang/src/org/lflang/federated/extensions/FedTargetExtension.java @@ -1,7 +1,6 @@ package org.lflang.federated.extensions; import java.io.IOException; -import java.util.LinkedHashMap; import org.lflang.ErrorReporter; import org.lflang.InferredType; diff --git a/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java index ec71a57c71..1616286048 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedTargetEmitter.java @@ -1,20 +1,12 @@ package org.lflang.federated.generator; -import static org.lflang.ASTUtils.convertToEmptyListIfNull; - import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; import org.lflang.ErrorReporter; -import org.lflang.Target; import org.lflang.TargetProperty; import org.lflang.ast.FormattingUtils; import org.lflang.federated.extensions.FedTargetExtensionFactory; import org.lflang.federated.launcher.RtiConfig; -import org.lflang.generator.GeneratorUtils; import org.lflang.generator.LFGeneratorContext; public class FedTargetEmitter { @@ -29,8 +21,9 @@ String generateTarget( ) throws IOException { // FIXME: First of all, this is not an initialization; there is all sorts of stuff happening - // in the implementations of this method. Second, true initialization stuff should happen + // in the C implementation of this method. Second, true initialization stuff should happen // when the target config is constructed, not when we're doing code generation. + // See https://issues.lf-lang.org/1667 FedTargetExtensionFactory.getExtension(federate.targetConfig.target) .initializeTargetConfig( context, @@ -48,31 +41,4 @@ String generateTarget( ) ); } - - /** - * Relativize target properties that involve paths like files and cmake-include to be - * relative to the generated .lf file for the federate. - */ - private void relativizeTargetPaths(FederateInstance federate, FedFileConfig fileConfig) { - // FIXME: Should we relativize here or calculate absolute paths? - relativizePathList(federate.targetConfig.protoFiles, fileConfig); - - relativizePathList(federate.targetConfig.fileNames, fileConfig); - - relativizePathList(federate.targetConfig.cmakeIncludes, fileConfig); - } - - private void relativizePathList(List paths, FedFileConfig fileConfig) { - List tempList = new ArrayList<>(); - paths.forEach( f -> { - tempList.add(relativizePath(f, fileConfig)); - }); - paths.clear(); - paths.addAll(tempList); - } - - private String relativizePath(String path, FedFileConfig fileConfig) { - Path resolvedPath = fileConfig.srcPath.resolve(path).toAbsolutePath(); - return fileConfig.getSrcPath().relativize(resolvedPath).toString(); - } } From 0a50e964435de7e5316aab1e03e053b26b2e9a62 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 23:01:59 -0700 Subject: [PATCH 11/22] Simplification and javadoc --- .../launcher/FedLauncherGenerator.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java b/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java index 5c575419b5..4b4d748058 100644 --- a/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java +++ b/org.lflang/src/org/lflang/federated/launcher/FedLauncherGenerator.java @@ -165,7 +165,7 @@ public void doGenerate( // Index used for storing pids of federates int federateIndex = 0; for (FederateInstance federate : federates) { - var buildConfig = getBuildConfig(federate.targetConfig.target, federate, fileConfig, errorReporter); + var buildConfig = getBuildConfig(federate, fileConfig, errorReporter); if (federate.isRemote) { Path fedRelSrcGenPath = fileConfig.getOutPath().relativize(fileConfig.getSrcGenPath()).resolve(federate.name); if(distCode.length() == 0) distCode.append(distHeader + "\n"); @@ -470,8 +470,19 @@ private String getFedLocalLaunchCode(FederateInstance federate, String executeCo federateIndex); } - private BuildConfig getBuildConfig(Target target, FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { - return switch(target) { + /** + * Create a build configuration of the appropriate target. + * + * @param federate The federate to which the build configuration applies. + * @param fileConfig The file configuration of the federation to which the federate belongs. + * @param errorReporter An error reporter to report problems. + * @return + */ + private BuildConfig getBuildConfig( + FederateInstance federate, + FedFileConfig fileConfig, + ErrorReporter errorReporter) { + return switch(federate.targetConfig.target) { case C, CCPP -> new CBuildConfig(federate, fileConfig, errorReporter); case Python -> new PyBuildConfig(federate, fileConfig, errorReporter); case TS -> new TsBuildConfig(federate, fileConfig, errorReporter); From 0c1137d17f0f01535cad88828731d6594fb4936e Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 23:06:22 -0700 Subject: [PATCH 12/22] Use TargetConfig constructor directly --- .../federated/generator/FedGenerator.java | 2 +- .../org/lflang/generator/GeneratorUtils.java | 23 ------------------- .../src/org/lflang/generator/MainContext.java | 2 +- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java index 710b5f2f70..18d5770165 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java +++ b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java @@ -297,7 +297,7 @@ private Map compileFederates( } props.put("docker", "false"); - TargetConfig subConfig = GeneratorUtils.getTargetConfig( + TargetConfig subConfig = new TargetConfig( props, GeneratorUtils.findTargetDecl(subFileConfig.resource), subContextErrorReporter ); SubContext subContext = new SubContext(context, IntegratedBuilder.VALIDATED_PERCENT_PROGRESS, 100) { diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index 3f3e08cc7e..709e1943bd 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Properties; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; @@ -17,13 +16,8 @@ import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.TargetConfig; -import org.lflang.TargetConfig.DockerOptions; -import org.lflang.TargetProperty.BuildType; -import org.lflang.TargetProperty.LogLevel; -import org.lflang.TargetProperty.UnionType; import org.lflang.generator.LFGeneratorContext.Mode; import org.lflang.TargetProperty; -import org.lflang.TargetProperty.SchedulerOption; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; import org.lflang.lf.Instantiation; @@ -53,23 +47,6 @@ public static TargetDecl findTargetDecl(Resource resource) { return findAll(resource, TargetDecl.class).iterator().next(); } - /** - * Set the appropriate target properties based on the target properties of - * the main .lf file and the given command-line arguments, if applicable. - * @param args The commandline arguments to process. - * @param target The target properties AST node. - * @param errorReporter The error reporter to which errors should be sent. - */ - public static TargetConfig getTargetConfig( - Properties args, - TargetDecl target, - ErrorReporter errorReporter - ) { - final TargetConfig targetConfig = new TargetConfig(args, target, errorReporter); - - return targetConfig; - } - /** * Look for physical actions in 'resource'. * If appropriate, set keepalive to true in diff --git a/org.lflang/src/org/lflang/generator/MainContext.java b/org.lflang/src/org/lflang/generator/MainContext.java index 93be617905..5ee53e5f83 100644 --- a/org.lflang/src/org/lflang/generator/MainContext.java +++ b/org.lflang/src/org/lflang/generator/MainContext.java @@ -163,7 +163,7 @@ public void reportProgress(String message, int percentage) { * reflected in the target configuration. */ public void loadTargetConfig() { - this.targetConfig = GeneratorUtils.getTargetConfig( + this.targetConfig = new TargetConfig( args, GeneratorUtils.findTargetDecl(fileConfig.resource), errorReporter ); } From fd90b525070c389cbb82c26bc207c8f3f14abdea Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 18 Mar 2023 23:35:51 -0700 Subject: [PATCH 13/22] Comments only --- .../federated/launcher/BuildConfig.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java b/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java index 3adb0edbcf..c1a01bfa47 100644 --- a/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java +++ b/org.lflang/src/org/lflang/federated/launcher/BuildConfig.java @@ -4,13 +4,33 @@ import org.lflang.federated.generator.FedFileConfig; import org.lflang.federated.generator.FederateInstance; +/** + * A collection of methods used for building target code for federates. + */ public abstract class BuildConfig { + /** + * The federate that this configuration applies to. + */ protected final FederateInstance federate; + + /** + * An error reporter to report problems. + */ protected final ErrorReporter errorReporter; + /** + * The file configuration of the federation that the federate belongs to. + */ protected final FedFileConfig fileConfig; + /** + * Create a new build configuration. + * + * @param federate The federate that this configuration applies to. + * @param fileConfig The file configuration of the federation that the federate belongs to. + * @param errorReporter An error reporter to report problems. + */ public BuildConfig(FederateInstance federate, FedFileConfig fileConfig, ErrorReporter errorReporter) { this.errorReporter = errorReporter; this.federate = federate; From dec8e9ac3a040123bf75a0dbab36fdf03c4728db Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 19 Mar 2023 11:24:58 -0700 Subject: [PATCH 14/22] Update org.lflang/src/org/lflang/TargetConfig.java Co-authored-by: Edward A. Lee --- org.lflang/src/org/lflang/TargetConfig.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/TargetConfig.java b/org.lflang/src/org/lflang/TargetConfig.java index 7702d15711..a8afa97cdb 100644 --- a/org.lflang/src/org/lflang/TargetConfig.java +++ b/org.lflang/src/org/lflang/TargetConfig.java @@ -59,9 +59,11 @@ public TargetConfig(TargetDecl target) { // FIXME: eliminate this constructor if this.target = Target.fromDecl(target); } - public TargetConfig(Properties args, + public TargetConfig( + Properties args, TargetDecl target, - ErrorReporter errorReporter) { + ErrorReporter errorReporter + ) { this(target); if (target.getConfig() != null) { List pairs = target.getConfig().getPairs(); From f3ecec2597cc7f28694cc09599d971d943ad2b45 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 19 Mar 2023 11:25:10 -0700 Subject: [PATCH 15/22] Update org.lflang/src/org/lflang/federated/generator/FedFileConfig.java Co-authored-by: Edward A. Lee --- .../src/org/lflang/federated/generator/FedFileConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java index 2b7f499e46..42f5c60dc7 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java +++ b/org.lflang/src/org/lflang/federated/generator/FedFileConfig.java @@ -48,7 +48,8 @@ public class FedFileConfig extends FileConfig { public FedFileConfig( Resource resource, Path srcGenBasePath, - boolean useHierarchicalBin) throws IOException { + boolean useHierarchicalBin + ) throws IOException { super(resource, srcGenBasePath, useHierarchicalBin); } From 3203987c69639ff477ea571e1d1517b2610bc802 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 19 Mar 2023 11:25:38 -0700 Subject: [PATCH 16/22] Update org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java Co-authored-by: Edward A. Lee --- org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java b/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java index 2395cba83b..bd88a719b4 100644 --- a/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java +++ b/org.lflang/src/org/lflang/federated/launcher/CBuildConfig.java @@ -65,8 +65,7 @@ public String compileCommand() { } @Override - public - String remoteExecuteCommand() { + public String remoteExecuteCommand() { return "bin/"+fileConfig.name+"_"+federate.name+" -i '$FEDERATION_ID'"; } From f505959f8688b98eabfd8a05ffb8fe1a71f55a41 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 19 Mar 2023 16:52:19 -0700 Subject: [PATCH 17/22] Comments only --- .../lflang/federated/launcher/RtiConfig.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java b/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java index 4d30d00e77..81c608d5b3 100644 --- a/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java +++ b/org.lflang/src/org/lflang/federated/launcher/RtiConfig.java @@ -2,52 +2,91 @@ import java.nio.file.Path; +/** + * Class for storing configuration settings pertaining to the RTI. + * @author Marten Lohstroh + */ public class RtiConfig { private Path directory; + /** + * The host on which the RTI process is to be spawned. + */ private String host; + /** + * The port on which to connect to the RTI process. + */ private int port; + /** + * The username used to gain access to the host where the RTI is to be spawned. + */ private String user; + /** + * Construct a new RTI configuration with all options set to their defaults. + */ public RtiConfig() { this.directory = Path.of("LinguaFrancaRemote"); this.host = "localhost"; this.port = 0; } + /** + * Return the directory to create on the remote host. + */ public Path getDirectory() { return directory; } + /** + * Return the host on which the RTI process is to be spawned. + */ public String getHost() { return host; } + /** + * Return the port on which to connect to the RTI process. + */ public int getPort() { return port; } + /** + * Return the username used to gain access to the host where the RTI is to be spawned. + */ public String getUser() { return user; } + /** + * Set the directory to create on the remote host. + */ public void setDirectory(Path directory) { this.directory = directory; } + /** + * Set the host on which the RTI process is to be spawned. + */ public void setHost(String host) { this.host = host; } + /** + * Set the port on which to connect to the RTI process. + */ public void setPort(int port) { this.port = port; } + /** + * Set the username used to gain access to the host where the RTI is to be spawned. + */ public void setUser(String user) { this.user = user; } - } From 2ed2a2560b52052d2cdd4ebf3cb63c53ef7845fd Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 19 Mar 2023 16:56:56 -0700 Subject: [PATCH 18/22] More comments. --- org.lflang/src/org/lflang/TargetConfig.java | 64 +++++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/org.lflang/src/org/lflang/TargetConfig.java b/org.lflang/src/org/lflang/TargetConfig.java index a8afa97cdb..05cc9a5e40 100644 --- a/org.lflang/src/org/lflang/TargetConfig.java +++ b/org.lflang/src/org/lflang/TargetConfig.java @@ -53,14 +53,28 @@ */ public class TargetConfig { + /** + * The target of this configuration (e.g., C, TypeScript, Python). + */ public final Target target; + /** + * Create a new target configuration based on the given target declaration AST node only. + * @param target AST node of a target declaration. + */ public TargetConfig(TargetDecl target) { // FIXME: eliminate this constructor if we can this.target = Target.fromDecl(target); } + /** + * Create a new target configuration based on the given commandline arguments and target + * declaration AST node. + * @param cliArgs Arguments passed on the commandline. + * @param target AST node of a target declaration. + * @param errorReporter An error reporter to report problems. + */ public TargetConfig( - Properties args, + Properties cliArgs, TargetDecl target, ErrorReporter errorReporter ) { @@ -69,11 +83,11 @@ public TargetConfig( List pairs = target.getConfig().getPairs(); TargetProperty.set(this, pairs != null ? pairs : List.of(), errorReporter); } - if (args.containsKey("no-compile")) { + if (cliArgs.containsKey("no-compile")) { this.noCompile = true; } - if (args.containsKey("docker")) { - var arg = args.getProperty("docker"); + if (cliArgs.containsKey("docker")) { + var arg = cliArgs.getProperty("docker"); if (Boolean.parseBoolean(arg)) { this.dockerOptions = new DockerOptions(); } else { @@ -81,44 +95,44 @@ public TargetConfig( } // FIXME: this is pretty ad-hoc and does not account for more complex overrides yet. } - if (args.containsKey("build-type")) { - this.cmakeBuildType = (BuildType) UnionType.BUILD_TYPE_UNION.forName(args.getProperty("build-type")); + if (cliArgs.containsKey("build-type")) { + this.cmakeBuildType = (BuildType) UnionType.BUILD_TYPE_UNION.forName(cliArgs.getProperty("build-type")); } - if (args.containsKey("logging")) { - this.logLevel = LogLevel.valueOf(args.getProperty("logging").toUpperCase()); + if (cliArgs.containsKey("logging")) { + this.logLevel = LogLevel.valueOf(cliArgs.getProperty("logging").toUpperCase()); } - if (args.containsKey("workers")) { - this.workers = Integer.parseInt(args.getProperty("workers")); + if (cliArgs.containsKey("workers")) { + this.workers = Integer.parseInt(cliArgs.getProperty("workers")); } - if (args.containsKey("threading")) { - this.threading = Boolean.parseBoolean(args.getProperty("threading")); + if (cliArgs.containsKey("threading")) { + this.threading = Boolean.parseBoolean(cliArgs.getProperty("threading")); } - if (args.containsKey("target-compiler")) { - this.compiler = args.getProperty("target-compiler"); + if (cliArgs.containsKey("target-compiler")) { + this.compiler = cliArgs.getProperty("target-compiler"); } - if (args.containsKey("scheduler")) { + if (cliArgs.containsKey("scheduler")) { this.schedulerType = SchedulerOption.valueOf( - args.getProperty("scheduler") + cliArgs.getProperty("scheduler") ); this.setByUser.add(TargetProperty.SCHEDULER); } - if (args.containsKey("target-flags")) { + if (cliArgs.containsKey("target-flags")) { this.compilerFlags.clear(); - if (!args.getProperty("target-flags").isEmpty()) { + if (!cliArgs.getProperty("target-flags").isEmpty()) { this.compilerFlags.addAll(List.of( - args.getProperty("target-flags").split(" ") + cliArgs.getProperty("target-flags").split(" ") )); } } - if (args.containsKey("runtime-version")) { - this.runtimeVersion = args.getProperty("runtime-version"); + if (cliArgs.containsKey("runtime-version")) { + this.runtimeVersion = cliArgs.getProperty("runtime-version"); } - if (args.containsKey("external-runtime-path")) { - this.externalRuntimePath = args.getProperty("external-runtime-path"); + if (cliArgs.containsKey("external-runtime-path")) { + this.externalRuntimePath = cliArgs.getProperty("external-runtime-path"); } - if (args.containsKey(TargetProperty.KEEPALIVE.description)) { + if (cliArgs.containsKey(TargetProperty.KEEPALIVE.description)) { this.keepalive = Boolean.parseBoolean( - args.getProperty(TargetProperty.KEEPALIVE.description)); + cliArgs.getProperty(TargetProperty.KEEPALIVE.description)); } } From a01e8db22cc06a6e6d1108d46d34e5311b2863e5 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 19 Mar 2023 17:02:59 -0700 Subject: [PATCH 19/22] Fetch tracing option from commandline. --- org.lflang/src/org/lflang/TargetConfig.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/TargetConfig.java b/org.lflang/src/org/lflang/TargetConfig.java index 05cc9a5e40..23e99541da 100644 --- a/org.lflang/src/org/lflang/TargetConfig.java +++ b/org.lflang/src/org/lflang/TargetConfig.java @@ -110,6 +110,9 @@ public TargetConfig( if (cliArgs.containsKey("target-compiler")) { this.compiler = cliArgs.getProperty("target-compiler"); } + if (cliArgs.containsKey("tracing")) { + this.tracing = new TracingOptions(); + } if (cliArgs.containsKey("scheduler")) { this.schedulerType = SchedulerOption.valueOf( cliArgs.getProperty("scheduler") @@ -277,9 +280,6 @@ public TargetConfig( * * This is now a wrapped class to account for overloaded definitions * of defining platform (either a string or dictionary of values) - * - * @author Samuel Berkun - * @author Anirudh Rengarajan */ public PlatformOptions platformOptions = new PlatformOptions(); @@ -373,7 +373,7 @@ public static class ClockSyncOptions { public int attenuation = 10; /** - * Whether or not to collect statistics while performing clock synchronization. + * Whether to collect statistics while performing clock synchronization. * This setting is only considered when clock synchronization has been activated. * The default is true. */ From 44d36f24f21a74d3c1ee551b76f57aeb71f8f07b Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 20 Mar 2023 00:09:31 -0700 Subject: [PATCH 20/22] Basic test to verify that target config propagates to federates. --- .../tests/compiler/TargetConfigTests.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java new file mode 100644 index 0000000000..ff79411a42 --- /dev/null +++ b/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java @@ -0,0 +1,90 @@ +package org.lflang.tests.compiler; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.nio.file.Files; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.xtext.generator.JavaIoFileSystemAccess; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.eclipse.xtext.testing.util.ParseHelper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.lflang.federated.generator.FedFileConfig; +import org.lflang.generator.LFGenerator; +import org.lflang.generator.LFGeneratorContext.Mode; +import org.lflang.generator.MainContext; +import org.lflang.lf.Model; +import org.lflang.tests.LFInjectorProvider; + +@ExtendWith(InjectionExtension.class) +@InjectWith(LFInjectorProvider.class) + +/** + * + */ +class TargetConfigTests { + + @Inject + ParseHelper parser; + + @Inject + LFGenerator generator; + + @Inject + JavaIoFileSystemAccess fileAccess; + + @Inject + Provider resourceSetProvider; + + private void assertHasTargetProperty(Model model, String name) { + Assertions.assertNotNull(model); + Assertions.assertTrue( + model.getTarget().getConfig().getPairs().stream().anyMatch( + p -> p.getName().equals(name) + ) + ); + } + + @Test + public void testParsing() throws Exception { + assertHasTargetProperty(parser.parse(""" + target C { + tracing: true + } + """), "tracing"); + } + + @Test + public void testFederation() throws Exception { + fileAccess.setOutputPath("junit"); // FIXME: this does not appear to do anything. Why? + fileAccess.setCurrentSource("Federation.lf"); + Model federation = parser.parse(""" + target C { + tracing: true + } + reactor Foo { + + } + federated reactor { + a = new Foo() + b = new Foo() + } + """, URI.createFileURI("Federation.lf"), resourceSetProvider.get()); + assertHasTargetProperty(federation, "tracing"); + + var resource = federation.eResource(); + var context = new MainContext(Mode.STANDALONE, resource, fileAccess, () -> false); + + generator.doGenerate(resource, fileAccess, context); + + String lfSrc = Files.readAllLines( + ((FedFileConfig)context.getFileConfig()).getSrcPath().resolve("a.lf") + ).stream().reduce("\n", String::concat); + Model federate = parser.parse(lfSrc); + assertHasTargetProperty(federate, "tracing"); + } +} From 3fdb15d011fc9f69139cf1de708a789281d21293 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 20 Mar 2023 00:19:18 -0700 Subject: [PATCH 21/22] Address FIXME --- .../src/org/lflang/tests/compiler/TargetConfigTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java index ff79411a42..c2a4c9cc86 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java @@ -60,8 +60,8 @@ public void testParsing() throws Exception { @Test public void testFederation() throws Exception { - fileAccess.setOutputPath("junit"); // FIXME: this does not appear to do anything. Why? - fileAccess.setCurrentSource("Federation.lf"); + fileAccess.setOutputPath("src-gen"); + Model federation = parser.parse(""" target C { tracing: true @@ -73,7 +73,7 @@ public void testFederation() throws Exception { a = new Foo() b = new Foo() } - """, URI.createFileURI("Federation.lf"), resourceSetProvider.get()); + """, URI.createFileURI("tmp/src/Federation.lf"), resourceSetProvider.get()); assertHasTargetProperty(federation, "tracing"); var resource = federation.eResource(); From 88823b1fe86e2f16a953b2133701bddf7cfe65d1 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 20 Mar 2023 15:09:41 -0700 Subject: [PATCH 22/22] Comments + disabled test on Windows --- .../lflang/tests/compiler/TargetConfigTests.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java index c2a4c9cc86..593e302d25 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/TargetConfigTests.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.lflang.federated.generator.FedFileConfig; +import org.lflang.generator.GeneratorUtils; import org.lflang.generator.LFGenerator; import org.lflang.generator.LFGeneratorContext.Mode; import org.lflang.generator.MainContext; @@ -24,7 +25,7 @@ @InjectWith(LFInjectorProvider.class) /** - * + * Tests for checking that target properties adequately translate into the target configuration. */ class TargetConfigTests { @@ -49,6 +50,10 @@ private void assertHasTargetProperty(Model model, String name) { ); } + /** + * Check that tracing target property affects the target configuration. + * @throws Exception + */ @Test public void testParsing() throws Exception { assertHasTargetProperty(parser.parse(""" @@ -58,6 +63,11 @@ public void testParsing() throws Exception { """), "tracing"); } + /** + * Check that when a federation has the "tracing" target property set, the generated federates + * will also have it set. + * @throws Exception + */ @Test public void testFederation() throws Exception { fileAccess.setOutputPath("src-gen"); @@ -79,6 +89,8 @@ public void testFederation() throws Exception { var resource = federation.eResource(); var context = new MainContext(Mode.STANDALONE, resource, fileAccess, () -> false); + if (GeneratorUtils.isHostWindows()) return; + generator.doGenerate(resource, fileAccess, context); String lfSrc = Files.readAllLines(