Skip to content

Commit

Permalink
Support C federated tests with Rust RTI
Browse files Browse the repository at this point in the history
- Most of C tests except federated tests are removed.
  • Loading branch information
chanijjani committed Feb 18, 2024
1 parent 150cf6a commit 16bd835
Show file tree
Hide file tree
Showing 57 changed files with 2,371 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.lflang.tests.runtime.CTest;
import org.lflang.tests.runtime.CppTest;
import org.lflang.tests.runtime.PythonTest;
import org.lflang.tests.runtime.RustRtiTest;
import org.lflang.tests.runtime.RustTest;
import org.lflang.tests.runtime.TypeScriptTest;

Expand Down Expand Up @@ -90,6 +91,8 @@ private static Class<? extends TestBase> getTestInstance(Target target) {
return PythonTest.class;
case Rust:
return RustTest.class;
case RustRti:
return RustRtiTest.class;
default:
throw new IllegalArgumentException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.lflang.tests;

import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.lflang.target.Target;
import org.lflang.tests.TestRegistry.TestCategory;

/**
* A collection of JUnit tests to perform on a given set of targets.
*
* @author Marten Lohstroh
* @author Chanhee Lee
*/
public abstract class SimplifiedRuntimeTest extends TestBase {

/**
* Construct a test instance that runs tests for a single target.
*
* @param target The target to run tests for.
*/
protected SimplifiedRuntimeTest(Target target) {
super(target);
}

/** Whether to enable {@link #runFederatedTests()}. */
protected boolean supportsFederatedExecution() {
return false;
}

@Test
public void runFederatedTestsWithRustRti() {
Assumptions.assumeTrue(supportsFederatedExecution(), Message.NO_FEDERATION_SUPPORT);
runTestsForTargetsWithRustRti(
Message.DESC_FEDERATED_WITH_RUST_RTI,
TestCategory.FEDERATED::equals,
Transformers::noChanges,
Configurators::noChanges,
TestLevel.EXECUTION,
false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*************
* Copyright (c) 2019-2024, The University of California at Berkeley.
*
* 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.tests.runtime;

import org.junit.jupiter.api.Test;
import org.lflang.target.Target;
import org.lflang.tests.SimplifiedRuntimeTest;

/**
* Collection of tests for the C target with Rust RTI.
*
* <p>Tests that are implemented in the base class are still overridden so that each test can be
* easily invoked individually from IDEs with JUnit support like Eclipse and IntelliJ. This is
* typically done by right-clicking on the name of the test method and then clicking "Run".*
*
* @author Marten Lohstroh
* @author Chanhee Lee
*/
public class RustRtiTest extends SimplifiedRuntimeTest {

public RustRtiTest() {
super(Target.RustRti);
}

@Override
protected boolean supportsSingleThreadedExecution() {
return true;
}

@Override
protected boolean supportsFederatedExecution() {
return true;
}

@Test
@Override
public void runFederatedTestsWithRustRti() {
super.runFederatedTestsWithRustRti();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,97 @@ public boolean doGenerate(Resource resource, LFGeneratorContext context) throws
return false;
}

/**
* 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.
*/
public boolean doGenerateForRustRTI(Resource resource, LFGeneratorContext context)
throws IOException {
if (!federatedExecutionIsSupported(resource)) return true;
cleanIfNeeded(context);

// In a federated execution, we need keepalive to be true,
// otherwise a federate could exit simply because it hasn't received
// any messages.
KeepaliveProperty.INSTANCE.override(targetConfig, true);

// Process command-line arguments
processCLIArguments(context);

// Find the federated reactor
Reactor federation = FedASTUtils.findFederatedReactor(resource);

// Make sure the RTI host is set correctly.
setRTIHost(federation);

// Create the FederateInstance objects.
ReactorInstance main = createFederateInstances(federation, context);

// Insert reactors that split multiports into many ports.
insertIndexers(main, resource);

// Clear banks so that each bank member becomes a single federate.
for (Instantiation instantiation : ASTUtils.allInstantiations(federation)) {
instantiation.setWidthSpec(null);
instantiation.setWidthSpec(null);
}

// Find all the connections between federates.
// For each connection between federates, replace it in the
// AST with an action (which inherits the delay) and three reactions.
// The action will be physical for physical connections and logical
// for logical connections.
replaceFederateConnectionsWithProxies(federation, main, resource);

FedEmitter fedEmitter =
new FedEmitter(
fileConfig,
ASTUtils.toDefinition(mainDef.getReactorClass()),
messageReporter,
rtiConfig);

// Generate LF code for each federate.
Map<Path, CodeMap> lf2lfCodeMapMap = new HashMap<>();
for (FederateInstance federate : federates) {
lf2lfCodeMapMap.putAll(fedEmitter.generateFederate(context, federate, federates.size()));
}

// Do not invoke target code generators if --no-compile flag is used.
if (context.getTargetConfig().get(NoCompileProperty.INSTANCE)) {
context.finish(Status.GENERATED, lf2lfCodeMapMap);
return false;
}

// If the RTI is to be built locally, set up a build environment for it.
prepareRtiBuildEnvironment(context);

Map<Path, CodeMap> codeMapMap =
compileFederates(
context,
lf2lfCodeMapMap,
subContexts -> {
createDockerFiles(context, subContexts);
generateLaunchScriptForRustRti();
// If an error has occurred during codegen of any federate, report it.
subContexts.forEach(
c -> {
if (c.getErrorReporter().getErrorsOccurred()) {
context
.getErrorReporter()
.at(c.getFileConfig().srcFile)
.error("Failure during code generation of " + c.getFileConfig().srcFile);
}
});
});

context.finish(Status.COMPILED, codeMapMap);
return false;
}

/**
* Prepare a build environment for the rti alongside the generated sources of the federates.
*
Expand Down Expand Up @@ -229,6 +320,11 @@ private void generateLaunchScript() {
.doGenerate(federates, rtiConfig);
}

private void generateLaunchScriptForRustRti() {
new FedLauncherGenerator(this.targetConfig, this.fileConfig, this.messageReporter)
.doGenerateForRustRTI(federates, new RtiConfig());
}

/**
* Generate a Dockerfile for each federate and a docker-compose.yml for the federation.
*
Expand Down
39 changes: 38 additions & 1 deletion core/src/main/java/org/lflang/generator/LFGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static FileConfig createFileConfig(
}

return switch (target) {
case CCPP, C -> new CFileConfig(resource, srcGenBasePath, useHierarchicalBin);
case CCPP, C, RustRti -> new CFileConfig(resource, srcGenBasePath, useHierarchicalBin);
case Python -> new PyFileConfig(resource, srcGenBasePath, useHierarchicalBin);
case CPP -> new CppFileConfig(resource, srcGenBasePath, useHierarchicalBin);
case Rust -> new RustFileConfig(resource, srcGenBasePath, useHierarchicalBin);
Expand All @@ -82,6 +82,7 @@ private GeneratorBase createGenerator(LFGeneratorContext context) {
case CPP -> new CppGenerator(context, scopeProvider);
case TS -> new TSGenerator(context);
case Rust -> new RustGenerator(context, scopeProvider);
case RustRti -> new CGenerator(context, true);
};
}

Expand Down Expand Up @@ -121,6 +122,42 @@ public void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorCont
}
}

public void doGenerateForRustRTI(
Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
assert injector != null;
final LFGeneratorContext lfContext;
if (context instanceof LFGeneratorContext) {
lfContext = (LFGeneratorContext) context;
} else {
lfContext = LFGeneratorContext.lfGeneratorContextOf(resource, fsa, context);
}

// The fastest way to generate code is to not generate any code.
if (lfContext.getMode() == LFGeneratorContext.Mode.LSP_FAST) return;

if (FedASTUtils.findFederatedReactor(resource) != null) {
try {
FedGenerator fedGenerator = new FedGenerator(lfContext);
injector.injectMembers(fedGenerator);
generatorErrorsOccurred = fedGenerator.doGenerateForRustRTI(resource, lfContext);
} catch (IOException e) {
throw new RuntimeIOException("Error during federated code generation", e);
}

} else {
final GeneratorBase generator = createGenerator(lfContext);

if (generator != null) {
generator.doGenerate(resource, lfContext);
generatorErrorsOccurred = generator.errorsOccurred();
}
}
final MessageReporter messageReporter = lfContext.getErrorReporter();
if (messageReporter instanceof LanguageServerMessageReporter) {
((LanguageServerMessageReporter) messageReporter).publishDiagnostics();
}
}

/** Return true if errors occurred in the last call to doGenerate(). */
public boolean errorsOccurred() {
return generatorErrorsOccurred;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static DockerGenerator dockerGeneratorFactory(LFGeneratorContext context)
case C, CCPP -> new CDockerGenerator(context);
case TS -> new TSDockerGenerator(context);
case Python -> new PythonDockerGenerator(context);
case CPP, Rust -> throw new IllegalArgumentException(
case CPP, Rust, RustRti -> throw new IllegalArgumentException(
"No Docker support for " + target + " yet.");
};
}
Expand Down
15 changes: 11 additions & 4 deletions core/src/main/java/org/lflang/target/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,13 @@ public enum Target {
// In our Rust implementation, the only reserved keywords
// are those that are a valid expression. Others may be escaped
// with the syntax r#keyword.
Arrays.asList("self", "true", "false")),
RustRti(
"RustRti",
true,
// In our Rust implementation, the only reserved keywords
// are those that are a valid expression. Others may be escaped
// with the syntax r#keyword.
Arrays.asList("self", "true", "false"));

/** String representation of this target. */
Expand Down Expand Up @@ -460,7 +467,7 @@ public boolean isReservedIdent(String ident) {
/** Return true if the target supports federated execution. */
public boolean supportsFederated() {
return switch (this) {
case C, CCPP, Python, TS -> true;
case C, CCPP, Python, TS, RustRti -> true;
default -> false;
};
}
Expand All @@ -476,7 +483,7 @@ public boolean supportsInheritance() {
/** Return true if the target supports multiports and banks of reactors. */
public boolean supportsMultiports() {
return switch (this) {
case C, CCPP, CPP, Python, Rust, TS -> true;
case C, CCPP, CPP, Python, Rust, TS, RustRti -> true;
default -> false;
};
}
Expand All @@ -501,7 +508,7 @@ public boolean supportsReactionDeclarations() {
public boolean buildsUsingDocker() {
return switch (this) {
case TS -> false;
case C, CCPP, CPP, Python, Rust -> true;
case C, CCPP, CPP, Python, Rust, RustRti -> true;
};
}

Expand Down Expand Up @@ -639,7 +646,7 @@ public void initialize(TargetConfig config) {
SingleThreadedProperty.INSTANCE,
TracingProperty.INSTANCE,
WorkersProperty.INSTANCE);
case Rust -> config.register(
case Rust, RustRti -> config.register(
BuildTypeProperty.INSTANCE,
CargoDependenciesProperty.INSTANCE,
CargoFeaturesProperty.INSTANCE,
Expand Down
Loading

0 comments on commit 16bd835

Please sign in to comment.