Skip to content

Commit

Permalink
Merge pull request #2232 from lf-lang/target-property-handling
Browse files Browse the repository at this point in the history
Bugfixes in handling of target properties across imports
  • Loading branch information
lhstrh authored Jun 30, 2024
2 parents ff7c984 + adfb734 commit 5621d82
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 291 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
package org.lflang.federated.generator;

import static org.lflang.ast.ASTUtils.convertToEmptyListIfNull;

import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import org.eclipse.emf.ecore.resource.Resource;
import org.lflang.MessageReporter;
import org.lflang.ast.ASTUtils;
import org.lflang.generator.GeneratorUtils;
import org.lflang.generator.LFGeneratorContext;
import org.lflang.lf.KeyValuePair;
import org.lflang.target.Target;
import org.lflang.target.TargetConfig;
import org.lflang.target.property.FileListProperty;
import org.lflang.util.FileUtil;

/**
* Subclass of TargetConfig with a specialized constructor for creating configurations for
Expand Down Expand Up @@ -43,7 +33,7 @@ public FederateTargetConfig(LFGeneratorContext context, Resource federateResourc
load(federationResource, reporter);

// Load properties from the federate file
mergeImportedConfig(federateResource, federationResource, reporter);
mergeImportedConfig(federateResource, federationResource, p -> p.loadFromFederate(), reporter);

// Load properties from the generator context
load(context.getArgs(), reporter);
Expand All @@ -52,66 +42,4 @@ public FederateTargetConfig(LFGeneratorContext context, Resource federateResourc

this.validate(reporter);
}

/**
* 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 messageReporter An error reporter to use when problems are encountered.
*/
private void mergeImportedConfig(
Resource federateResource, Resource mainResource, MessageReporter messageReporter) {
// 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);
var targetProperties = importedTargetDecl.getConfig();
if (targetProperties != null) {
// Merge properties
update(
this,
convertToEmptyListIfNull(targetProperties.getPairs()),
getRelativePath(mainResource, federateResource),
messageReporter);
}
}
}

private Path getRelativePath(Resource source, Resource target) {
return FileUtil.toPath(source.getURI())
.getParent()
.relativize(FileUtil.toPath(target.getURI()).getParent());
}

/**
* Update the given configuration using the given target properties.
*
* @param config The configuration object to update.
* @param pairs AST node that holds all the target properties.
* @param relativePath The path from the main resource to the resource from which the new
* properties originate.
*/
public void update(
TargetConfig config, List<KeyValuePair> pairs, Path relativePath, MessageReporter err) {
pairs.forEach(
pair -> {
var p = config.forName(pair.getName());
if (p.isPresent()) {
var value = pair.getValue();
var property = p.get();
if (property instanceof FileListProperty fileListProperty) {
var files =
ASTUtils.elementToListOfStrings(value).stream()
.map(relativePath::resolve) // assume all paths are relative
.map(Objects::toString)
.toList();
fileListProperty.update(config, files);
} else {
p.get().update(this, pair, err);
}
}
});
}
}
70 changes: 29 additions & 41 deletions core/src/main/java/org/lflang/generator/GeneratorBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -80,18 +79,12 @@
*/
public abstract class GeneratorBase extends AbstractLFValidator {

////////////////////////////////////////////
//// Public fields.

/** The main (top-level) reactor instance. */
public ReactorInstance main;

/** An error reporter for reporting any errors or warnings during the code generation */
public MessageReporter messageReporter;

////////////////////////////////////////////
//// Protected fields.

/** The current target configuration. */
protected final TargetConfig targetConfig;

Expand Down Expand Up @@ -125,9 +118,6 @@ public Instantiation getMainDef() {
*/
protected List<Reactor> reactors = new ArrayList<>();

/** The set of resources referenced reactor classes reside in. */
protected Set<LFResource> resources = new LinkedHashSet<>(); // FIXME: Why do we need this?

/**
* Graph that tracks dependencies between instantiations. This is a graph where each node is a
* Reactor (not a ReactorInstance) and an arc from Reactor A to Reactor B means that B contains an
Expand All @@ -147,9 +137,6 @@ public Instantiation getMainDef() {
/** Indicates whether the program has any watchdogs. This is used to check for support. */
public boolean hasWatchdogs = false;

// //////////////////////////////////////////
// // Private fields.

/** A list ot AST transformations to apply before code generation */
private final List<AstTransformation> astTransformations = new ArrayList<>();

Expand All @@ -170,8 +157,26 @@ protected void registerTransformation(AstTransformation transformation) {
astTransformations.add(transformation);
}

// //////////////////////////////////////////
// // Code generation functions to override for a concrete code generator.
/**
* If the given reactor is defined in another file, process its target properties so that they are
* reflected in the target configuration.
*/
private void loadTargetProperties(Resource resource) {
var mainFileConfig = this.context.getFileConfig();
if (resource != mainFileConfig.resource) {
this.context
.getTargetConfig()
.mergeImportedConfig(
LFGenerator.createFileConfig(
resource,
mainFileConfig.getSrcGenBasePath(),
mainFileConfig.useHierarchicalBin)
.resource,
mainFileConfig.resource,
p -> p.loadFromImport(),
this.messageReporter);
}
}

/**
* Generate code from the Lingua Franca model contained by the specified resource.
Expand Down Expand Up @@ -218,46 +223,29 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
}
}

// Process target files. Copy each of them into the src-gen dir.
// FIXME: Should we do this here? This doesn't make sense for federates the way it is
// done here.
copyUserFiles(this.targetConfig, context.getFileConfig());

// Collect reactors and create an instantiation graph.
// These are needed to figure out which resources we need
// to validate, which happens in setResources().
setReactorsAndInstantiationGraph(context.getMode());

List<Resource> allResources = GeneratorUtils.getResources(reactors);
resources.addAll(
allResources
.stream() // FIXME: This filter reproduces the behavior of the method it replaces. But
// why must it be so complicated? Why are we worried about weird corner cases
// like this?
.filter(
it ->
!Objects.equal(it, context.getFileConfig().resource)
|| mainDef != null && it == mainDef.getReactorClass().eResource())
.map(
it ->
GeneratorUtils.getLFResource(
it, context.getFileConfig().getSrcGenBasePath(), context, messageReporter))
.toList());
Set<Resource> allResources = GeneratorUtils.getResources(reactors);

GeneratorUtils.accommodatePhysicalActionsIfPresent(
allResources,
getTarget().setsKeepAliveOptionAutomatically(),
targetConfig,
messageReporter);
// FIXME: Should the GeneratorBase pull in {@code files} from imported
// resources?

// Load target properties for all resources.
allResources.forEach(r -> loadTargetProperties(r));

for (AstTransformation transformation : astTransformations) {
transformation.applyTransformation(reactors);
}

// Transform connections that reside in mutually exclusive modes and are otherwise conflicting
// This should be done before creating the instantiation graph
transformConflictingConnectionsInModalReactors();
transformConflictingConnectionsInModalReactors(allResources);

// Invoke these functions a second time because transformations
// may have introduced new reactors!
Expand Down Expand Up @@ -426,9 +414,9 @@ protected void checkWatchdogSupport(boolean isSupported) {
* Finds and transforms connections into forwarding reactions iff the connections have the same
* destination as other connections or reaction in mutually exclusive modes.
*/
private void transformConflictingConnectionsInModalReactors() {
for (LFResource r : resources) {
var transform = ASTUtils.findConflictingConnectionsInModalReactors(r.eResource);
private void transformConflictingConnectionsInModalReactors(Set<Resource> resources) {
for (Resource r : resources) {
var transform = ASTUtils.findConflictingConnectionsInModalReactors(r);
if (!transform.isEmpty()) {
var factory = LfFactory.eINSTANCE;
for (Connection connection : transform) {
Expand Down
64 changes: 21 additions & 43 deletions core/src/main/java/org/lflang/generator/GeneratorUtils.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
package org.lflang.generator;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.xbase.lib.IteratorExtensions;
import org.lflang.FileConfig;
import org.lflang.MessageReporter;
import org.lflang.ast.ASTUtils;
import org.lflang.generator.LFGeneratorContext.Mode;
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.ImportedReactor;
import org.lflang.lf.Instantiation;
import org.lflang.lf.KeyValuePair;
import org.lflang.lf.KeyValuePairs;
import org.lflang.lf.Reactor;
import org.lflang.lf.TargetDecl;
import org.lflang.target.TargetConfig;
Expand All @@ -45,7 +40,7 @@ public static TargetDecl findTargetDecl(Resource resource) {
* targetConfig}. This is a helper function for setTargetConfig. It should not be used elsewhere.
*/
public static void accommodatePhysicalActionsIfPresent(
List<Resource> resources,
Set<Resource> resources,
boolean setsKeepAliveOptionAutomatically,
TargetConfig targetConfig,
MessageReporter messageReporter) {
Expand Down Expand Up @@ -84,50 +79,33 @@ public static <T> Iterable<T> findAll(Resource resource, Class<T> nodeType) {
}

/**
* Return the resources that provide the given reactors.
* Return the resources that provide the given reactors and their ancestors.
*
* @param reactors The reactors for which to find containing resources.
* @return the resources that provide the given reactors.
*/
public static List<Resource> getResources(Iterable<Reactor> reactors) {
HashSet<Resource> visited = new HashSet<>();
List<Resource> resources = new ArrayList<>();
public static Set<Resource> getResources(Iterable<Reactor> reactors) {
Set<Resource> visited = new LinkedHashSet<>();
for (Reactor r : reactors) {
Resource resource = r.eResource();
if (!visited.contains(resource)) {
visited.add(resource);
resources.add(resource);
if (!visited.contains(r.eResource())) {
addInheritedResources(r, visited);
}
}
return resources;
return visited;
}

/**
* Return the {@code LFResource} representation of the given resource.
*
* @param resource The {@code Resource} to be represented as an {@code LFResource}
* @param srcGenBasePath The root directory for any generated sources associated with the
* resource.
* @param context The generator invocation context.
* @param messageReporter An error message acceptor.
* @return the {@code LFResource} representation of the given resource.
*/
public static LFResource getLFResource(
Resource resource,
Path srcGenBasePath,
LFGeneratorContext context,
MessageReporter messageReporter) {
var target = ASTUtils.targetDecl(resource);
KeyValuePairs config = target.getConfig();
var targetConfig = new TargetConfig(resource, context.getArgs(), messageReporter);
if (config != null) {
List<KeyValuePair> pairs = config.getPairs();
targetConfig.load(pairs != null ? pairs : List.of(), messageReporter);
/** Collect all resources associated with reactor through class inheritance. */
private static void addInheritedResources(Reactor reactor, Set<Resource> resources) {
resources.add(reactor.eResource());
for (var s : reactor.getSuperClasses()) {
if (!resources.contains(s)) {
if (s instanceof ImportedReactor i) {
addInheritedResources(i.getReactorClass(), resources);
} else if (s instanceof Reactor r) {
addInheritedResources(r, resources);
}
}
}
FileConfig fc =
LFGenerator.createFileConfig(
resource, srcGenBasePath, context.getFileConfig().useHierarchicalBin);
return new LFResource(resource, fc, targetConfig);
}

/**
Expand Down
47 changes: 0 additions & 47 deletions core/src/main/java/org/lflang/generator/LFResource.java

This file was deleted.

Loading

0 comments on commit 5621d82

Please sign in to comment.