Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mixed-target federations #2099

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
29 changes: 25 additions & 4 deletions core/src/main/java/org/lflang/federated/extensions/CExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,19 @@ protected void deserialize(
result.pr("lf_set(" + receiveRef + ", " + value + ");");
}
}
case PROTO -> throw new UnsupportedOperationException(
"Protobuf serialization is not supported yet.");
case PROTO -> {
// In C, the type of the action is uint8*, so it will be a token type.
value = action.getName() + "->value";
var length = action.getName() + "->length";
var portType = types.getTargetType(ASTUtils.getInferredType(((Port) receivingPort.getVariable())));
var prefix = portType.toLowerCase().replace("*", "");
// FIXME: Protobufs does weird things converting cammel case to snake case and vice versa.
// The following "toLowerCase()" call will only work for single-word types.
result.pr(portType + " unpacked = " + prefix + "__unpack(NULL, " + length + ", " + value + ");");
// FIXME: Should generate and set destructor and copy constructor for this type.
// See: https://www.lf-lang.org/docs/handbook/target-language-details?target=c#dynamically-allocated-data
result.pr("lf_set(" + receiveRef + ", unpacked);");
}
case ROS2 -> {
var portType = ASTUtils.getInferredType(((Port) receivingPort.getVariable()));
var portTypeStr = types.getTargetType(portType);
Expand Down Expand Up @@ -408,8 +419,18 @@ protected void serializeAndSend(
result.pr(sendingFunction + "(" + commonArgs + ", " + pointerExpression + ");");
}
}
case PROTO -> throw new UnsupportedOperationException(
"Protobuf serialization is not supported yet.");
case PROTO -> {
// FIXME: Protobufs does weird things converting camel case to snake case and vice versa.
// The following "toLowerCase()" call will only work for single-word types.
var targetType = types.getTargetType(type).toLowerCase();
var prefix = targetType.replace("*", "");
result.pr("size_t _lf_message_length = " + prefix + "__get_packed_size(" + sendRef + "->value);");
result.pr("uint8_t* buffer = (uint8_t*)malloc(_lf_message_length);");
result.pr(prefix + "__pack(" + sendRef + "->value, buffer);");
result.pr(sendingFunction + "(" + commonArgs + ", buffer);");
}
// throw new UnsupportedOperationException(
// "Protobuf serialization is not supported yet.");
case ROS2 -> {
var typeStr = types.getTargetType(type);
if (CUtil.isTokenType(type, types)) {
Expand Down
43 changes: 25 additions & 18 deletions core/src/main/java/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import static org.lflang.ast.ASTUtils.toText;
import static org.lflang.util.StringUtil.addDoubleQuotes;

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import java.io.File;
import java.io.IOException;
Expand All @@ -44,6 +43,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -1440,7 +1440,7 @@ private void generateStartTimeStep(ReactorInstance instance) {

temp.pr("// Add port " + port.getFullName() + " to array of is_present fields.");

if (!Objects.equal(port.getParent(), instance)) {
if (!port.getParent().equals(instance)) {
// The port belongs to contained reactor, so we also have
// iterate over the instance bank members.
temp.startScopedBlock();
Expand Down Expand Up @@ -1474,7 +1474,7 @@ private void generateStartTimeStep(ReactorInstance instance) {

enclaveInfo.numIsPresentFields += port.getWidth() * port.getParent().getTotalWidth();

if (!Objects.equal(port.getParent(), instance)) {
if (!port.getParent().equals(instance)) {
temp.pr("count++;");
temp.endScopedBlock();
temp.endScopedBlock();
Expand Down Expand Up @@ -1602,25 +1602,31 @@ private void generateTimerInitializations(ReactorInstance instance) {
* <p>Run, if possible, the proto-c protocol buffer code generator to produce the required .h and
* .c files.
*
* @param filename Name of the file to process.
* @param file Path of the .proto file to process.
*/
public void processProtoFile(String filename) {
public void processProtoFile(Path file) {
var fileName = file.getFileName().toString();
var directory = Objects.requireNonNullElse(file.getParent(), "");
var protoc =
commandFactory.createCommand(
"protoc-c",
List.of("--c_out=" + this.fileConfig.getSrcGenPath(), filename),
List.of(
"--c_out=" + this.fileConfig.getSrcGenPath(),
"--proto_path=" + directory,
fileName),
fileConfig.srcPath);
if (protoc == null) {
messageReporter.nowhere().error("Processing .proto files requires protoc-c >= 1.3.3.");
return;
}
var returnCode = protoc.run();
if (returnCode == 0) {
var nameSansProto = filename.substring(0, filename.length() - 6);
targetConfig.compileAdditionalSources.add(
fileConfig.getSrcGenPath().resolve(nameSansProto + ".pb-c.c").toString());
} else {
messageReporter.nowhere().error("protoc-c returns error code " + returnCode);
var returnCode = protoc.run();
if (returnCode == 0) {
messageReporter.nowhere().info("Successfully compiled " + file);
var nameSansProto = fileName.substring(0, fileName.length() - 6);
targetConfig.compileAdditionalSources.add(
fileConfig.getSrcGenPath().resolve(nameSansProto + ".pb-c.c").toString());
} else {
messageReporter.nowhere().error("protoc-c failed:" + protoc.getErrors());
}
}
}

Expand Down Expand Up @@ -1999,10 +2005,10 @@ protected void setUpGeneralParameters() {
}
}

/** Iterate over the .proto files specified in the 'proto' target property and compile them. */
protected void handleProtoFiles() {
// Handle .proto files.
for (String file : targetConfig.get(ProtobufsProperty.INSTANCE)) {
this.processProtoFile(file);
this.processProtoFile(Path.of(file));
}
}

Expand Down Expand Up @@ -2033,10 +2039,11 @@ protected String generateTopLevelPreambles(Reactor reactor) {
.collect(Collectors.toSet())
.forEach(it -> builder.pr(toText(it.getCode())));
for (String file : targetConfig.get(ProtobufsProperty.INSTANCE)) {
var dotIndex = file.lastIndexOf(".");
var fileName = Path.of(file).getFileName().toString();
var dotIndex = fileName.lastIndexOf(".");
var rootFilename = file;
if (dotIndex > 0) {
rootFilename = file.substring(0, dotIndex);
rootFilename = fileName.substring(0, dotIndex);
}
code.pr("#include " + addDoubleQuotes(rootFilename + ".pb-c.h"));
builder.pr("#include " + addDoubleQuotes(rootFilename + ".pb-c.h"));
Expand Down
39 changes: 18 additions & 21 deletions core/src/main/java/org/lflang/generator/python/PythonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -63,7 +64,6 @@
import org.lflang.target.property.CompilerProperty;
import org.lflang.target.property.ProtobufsProperty;
import org.lflang.util.FileUtil;
import org.lflang.util.LFCommand;
import org.lflang.util.StringUtil;

/**
Expand Down Expand Up @@ -280,39 +280,36 @@ protected String generateTopLevelPreambles(Reactor ignored) {
@Override
protected void handleProtoFiles() {
for (String name : targetConfig.get(ProtobufsProperty.INSTANCE)) {
this.processProtoFile(name);
this.processProtoFile(Path.of(name));
int dotIndex = name.lastIndexOf(".");
String rootFilename = dotIndex > 0 ? name.substring(0, dotIndex) : name;
pythonPreamble.pr("import " + rootFilename + "_pb2 as " + rootFilename);
protoNames.add(rootFilename);
}
}

/**
* Process a given .proto file.
*
* <p>Run, if possible, the proto-c protocol buffer code generator to produce the required .h and
* .c files.
*
* @param filename Name of the file to process.
*/
@Override
public void processProtoFile(String filename) {
LFCommand protoc =
public void processProtoFile(Path file) {
var fileName = file.getFileName().toString();
var directory = Objects.requireNonNullElse(file.getParent(), "");
var protoc =
commandFactory.createCommand(
"protoc",
List.of("--python_out=" + fileConfig.getSrcGenPath(), filename),
List.of(
"--python_out=" + this.fileConfig.getSrcGenPath(),
"--proto_path=" + directory,
fileName),
fileConfig.srcPath);

if (protoc == null) {
messageReporter.nowhere().error("Processing .proto files requires libprotoc >= 3.6.1");
return;
}
int returnCode = protoc.run();
if (returnCode == 0) {
pythonRequiredModules.add("google-api-python-client");
messageReporter.nowhere().error("Processing .proto files requires protoc-c >= 1.3.3.");
} else {
messageReporter.nowhere().error("protoc returns error code " + returnCode);
var returnCode = protoc.run();
if (returnCode == 0) {
messageReporter.nowhere().info("Successfully compiled " + file);
pythonRequiredModules.add("google-api-python-client");
} else {
messageReporter.nowhere().error("protoc-c failed:" + protoc.getErrors());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

syntax = "proto3";

message ProtoHelloWorld {
message Hello {
string name = 1;
int32 number = 2;
repeated string sentence = 3;
}
18 changes: 8 additions & 10 deletions test/C/src/serialization/PersonProtocolBuffers.lf
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@
* on the examples at https://github.com/protobuf-c/protobuf-c/wiki/Examples. This example just
* packs and unpacks a message.
*
* To run this example first install the protocol buffers compiler from
* https://github.com/protocolbuffers/protobuf. It is also available from homebrew on a Mac via
* To run this test, first install the protocol buffers compiler from
* https://github.com/protocolbuffers/protobuf as well as the C plugin which comes from
* https://github.com/protobuf-c/protobuf-c.
*
* $ brew install protobuf
*
* Building protobuf from source is slow, so avoid doing that if possible. Next install the C plugin
* for protocol buffers from
* (Building protobuf from source is slow, so avoid doing that if possible.)
*
* https://github.com/protobuf-c/protobuf-c
*
* The code generator assumes that executables are installed within the PATH. On a Mac, this is
* typically at /usr/local/bin.
* On Mac, you can install these dependencies via Homebrew:
* $ brew install protobuf
* $ brew install protobuf-c
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
*/
target C {
protobufs: Person.proto
Expand All @@ -32,6 +29,7 @@ main reactor {
person.email = "eal@berkeley.edu";

// Pack the message into buffer.
person__init(&person);
len = person__get_packed_size(&person);
buffer = (uint8_t*)malloc(len);
person__pack(&person, buffer);
Expand Down
63 changes: 63 additions & 0 deletions test/C/src/serialization/ProtoFederated.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* This example creates a Protocol Buffer message and passes it to another reactor without packing
* and unpacking. This demonstrates that local communication, within one shared-memory machine, need
* not incur the overhead of packing and unpacking.
*
* To run this example first install the protocol buffers compiler from
* https://github.com/protocolbuffers/protobuf. It is also available from homebrew on a Mac via
*
* $ brew install protobuf
*
* Building protobuf from source is slow, so avoid doing that if possible. Next install the C plugin
* for protocol buffers from
*
* https://github.com/protobuf-c/protobuf-c
*
* The code generator assumes that executables are installed within the PATH. On a Mac, this is
* typically at /usr/local/bin.
*/
target C {
protobufs: [Hello.proto],
timeout: 1 s
}

reactor SourceProto {
output out: Hello*

reaction(startup) -> out {=
Hello* value = (Hello*)malloc(sizeof(Hello));
hello__init(value);
value->name = "Hello World";
value->number = 42;
lf_set(out, value);
=}
}

reactor SinkProto {
preamble {=
// FIXME: Ideally, this function would be generated by the tool
// processing the .proto file.
// Destructor to use at the receiving end, which frees the memory used to unpack the message.
void hello_unpacked_destructor(void* hello) {
Hello* cast = (Hello*)hello;
hello__free_unpacked(cast, NULL);
}
// FIXME: Should also provide a copy constructor.
=}

input in: Hello*

reaction(startup) in {=
// FIXME: Ideally, this would be automatically generated.
lf_set_destructor(in, hello_unpacked_destructor);
=}
reaction(in) {=
printf("Received: name=\"%s\", number=%d.\n", in->value->name, in->value->number);
=}
}

federated reactor {
s = new SourceProto()
d = new SinkProto()
s.out -> d.in serializer "proto"
}
23 changes: 10 additions & 13 deletions test/C/src/serialization/ProtoNoPacking.lf
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@
* and unpacking. This demonstrates that local communication, within one shared-memory machine, need
* not incur the overhead of packing and unpacking.
*
* To run this example first install the protocol buffers compiler from
* https://github.com/protocolbuffers/protobuf. It is also available from homebrew on a Mac via
* To run this test, first install the protocol buffers compiler from
* https://github.com/protocolbuffers/protobuf as well as the C plugin which comes from
* https://github.com/protobuf-c/protobuf-c.
*
* $ brew install protobuf
*
* Building protobuf from source is slow, so avoid doing that if possible. Next install the C plugin
* for protocol buffers from
* (Building protobuf from source is slow, so avoid doing that if possible.)
*
* https://github.com/protobuf-c/protobuf-c
*
* The code generator assumes that executables are installed within the PATH. On a Mac, this is
* typically at /usr/local/bin.
* On Mac, you can install these dependencies via Homebrew:
* $ brew install protobuf
* $ brew install protobuf-c
*/
target C {
protobufs: [ProtoHelloWorld.proto]
protobufs: [Hello.proto]
}

reactor SourceProto {
output out: ProtoHelloWorld
output out: Hello

reaction(startup) -> out {=
out->value.name = "Hello World";
Expand All @@ -31,7 +28,7 @@ reactor SourceProto {
}

reactor SinkProto {
input in: ProtoHelloWorld
input in: Hello

reaction(in) {=
printf("Received: name=\"%s\", number=%d.\n", in->value.name, in->value.number);
Expand Down
Loading