Merge branch 'host-data-json'
Fixes CLOWNFISH-83.
diff --git a/compiler/perl/lib/Clownfish/CFC.pm b/compiler/perl/lib/Clownfish/CFC.pm
index c38c3c3..d869e00 100644
--- a/compiler/perl/lib/Clownfish/CFC.pm
+++ b/compiler/perl/lib/Clownfish/CFC.pm
@@ -237,39 +237,6 @@
verify_args( \%new_PARAMS, %args ) or confess $@;
return _new( @args{qw( dest )} );
}
-
- # Recreate host language specific metadata of dependencies.
- #
- # TODO: This should be done automatically when building the hierarchy.
- # But since the current approach relies on runtime introspection, the Perl
- # modules for all dependencies must first be loaded.
- sub inherit_metadata {
- my $self = shift;
-
- require Clownfish;
-
- for my $class (@{ $self->ordered_classes }) {
- next if !$class->included || $class->inert;
-
- my $class_name = $class->get_name;
- my $rt_class = Clownfish::Class->fetch_class($class_name)
- or die("Class $class_name not found");
-
- for my $rt_method (@{ $rt_class->get_methods }) {
- if ($rt_method->is_excluded_from_host) {
- my $method = $class->method($rt_method->get_name);
- $method->exclude_from_host;
- }
- else {
- my $alias = $rt_method->get_host_alias;
- if (defined($alias)) {
- my $method = $class->method($rt_method->get_name);
- $method->set_host_alias($alias);
- }
- }
- }
- }
- }
}
{
diff --git a/compiler/perl/lib/Clownfish/CFC.xs b/compiler/perl/lib/Clownfish/CFC.xs
index 74bce9d..8caa0b7 100644
--- a/compiler/perl/lib/Clownfish/CFC.xs
+++ b/compiler/perl/lib/Clownfish/CFC.xs
@@ -764,6 +764,12 @@
PPCODE:
CFCHierarchy_build(self);
+void
+read_host_data_json(self)
+ CFCHierarchy *self;
+PPCODE:
+ CFCHierarchy_read_host_data_json(self, "perl");
+
int
propagate_modified(self, ...)
CFCHierarchy *self;
@@ -1186,6 +1192,7 @@
included = 16
prereq_parcels = 20
inherited_parcels = 22
+ get_xs_module = 24
PPCODE:
{
START_SET_OR_GET_SWITCH
@@ -1239,6 +1246,11 @@
FREEMEM(parcels);
}
break;
+ case 24: {
+ const char *xs_module = CFCParcel_get_host_module_name(self);
+ retval = newSVpvn(xs_module, strlen(xs_module));
+ }
+ break;
END_SET_OR_GET_SWITCH
}
@@ -1884,6 +1896,13 @@
PPCODE:
CFCBindCore_copy_headers(self, dest_dir);
+void
+write_host_data_json(self, dest_dir)
+ CFCBindCore *self;
+ const char *dest_dir;
+PPCODE:
+ CFCBindCore_write_host_data_json(self, dest_dir, "perl");
+
MODULE = Clownfish::CFC PACKAGE = Clownfish::CFC::Binding::Core::Function
diff --git a/compiler/perl/lib/Clownfish/CFC/Perl/Build.pm b/compiler/perl/lib/Clownfish/CFC/Perl/Build.pm
index f1c351f..58b9296 100644
--- a/compiler/perl/lib/Clownfish/CFC/Perl/Build.pm
+++ b/compiler/perl/lib/Clownfish/CFC/Perl/Build.pm
@@ -223,6 +223,7 @@
$hierarchy->add_include_dir($include_dir);
}
$hierarchy->build;
+ $hierarchy->read_host_data_json;
# Process all Binding classes in buildlib.
my $pm_filepaths = $self->rscan_dir( $BUILDLIB_DIR, qr/\.pm$/ );
@@ -288,14 +289,13 @@
footer => '',
);
print "Writing Clownfish autogenerated files...\n";
+ my $inc_dir = catdir( $self->blib, 'arch', 'Clownfish', '_include' );
my $cfh_modified = $core_binding->write_all_modified;
if ($cfh_modified) {
unlink('typemap');
print "Writing typemap...\n";
$self->add_to_cleanup('typemap');
$perl_binding->write_xs_typemap;
-
- my $inc_dir = catdir( $self->blib, 'arch', 'Clownfish', '_include' );
$core_binding->copy_headers($inc_dir);
}
@@ -316,6 +316,8 @@
);
}
+ $core_binding->write_host_data_json($inc_dir);
+
print "Writing POD...\n";
my $pod_files = $perl_binding->write_pod;
$self->add_to_cleanup($_) for @$pod_files;
@@ -397,8 +399,8 @@
push @$c_files, @{ $self->rscan_dir( $source_dir, qr/\.c$/ ) };
}
my $autogen_src_dir = catdir( $AUTOGEN_DIR, 'source' );
- for my $parcel ( @{ $module->{parcels} } ) {
- $parcel = Clownfish::CFC::Model::Parcel->acquire($parcel);
+ for my $parcel_name ( @{ $module->{parcels} } ) {
+ my $parcel = Clownfish::CFC::Model::Parcel->acquire($parcel_name);
my $prefix = $parcel->get_prefix;
# Assume *_parcel.c is built with make.
push @$c_files, catfile( $autogen_src_dir, "${prefix}parcel.c" )
@@ -488,10 +490,8 @@
my $lib_file = catfile( $archdir, "$class_name.$Config{dlext}" );
if ( !$self->up_to_date( [ @objects, $AUTOGEN_DIR ], $lib_file ) ) {
my $linker_flags = $self->extra_linker_flags;
- if ( $module->{xs_prereqs} ) {
- push @$linker_flags,
- $self->cf_linker_flags( @{ $module->{xs_prereqs} } );
- }
+ my @xs_prereqs = $self->_cf_find_xs_prereqs($module);
+ push @$linker_flags, $self->cf_linker_flags(@xs_prereqs);
$cbuilder->link(
module_name => $module_name,
objects => \@objects,
@@ -510,6 +510,47 @@
}
}
+sub _cf_find_xs_prereqs {
+ my ( $self, $module ) = @_;
+
+ my $modules = $self->clownfish_params('modules');
+ my @xs_prereqs;
+
+ for my $parcel_name ( @{ $module->{parcels} } ) {
+ my $parcel = Clownfish::CFC::Model::Parcel->acquire($parcel_name);
+
+ for my $prereq_parcel ( @{ $parcel->prereq_parcels } ) {
+ my $prereq_module;
+
+ if ($prereq_parcel->included) {
+ $prereq_module = $prereq_parcel->get_xs_module;
+ }
+ else {
+ my $prereq_parcel_name = $prereq_parcel->get_name;
+
+ for my $candidate (@$modules) {
+ my @matches = grep {
+ $_ eq $prereq_parcel_name
+ } @{ $candidate->{parcels} };
+
+ if (@matches) {
+ $prereq_module = $candidate->{name};
+ last;
+ }
+ }
+ }
+
+ die("No XS module found for parcel $parcel_name")
+ if !defined($prereq_module);
+ push @xs_prereqs, $prereq_module
+ if $prereq_module ne $module->{name}
+ && !grep { $_ eq $prereq_module } @xs_prereqs;
+ }
+ }
+
+ return @xs_prereqs;
+}
+
sub ACTION_code {
my $self = shift;
@@ -627,12 +668,10 @@
name => 'My::Module',
c_source_dirs => 'xs',
parcels => [ 'MyModule' ],
- xs_prereqs => [ 'Clownfish' ],
},
{
name => 'My::Module::Test',
parcels => [ 'TestMyModule' ],
- xs_prereqs => [ 'Clownfish', 'My::Module' ],
},
],
},
diff --git a/compiler/src/CFCBindClass.c b/compiler/src/CFCBindClass.c
index ac8b8fd..75b77e9 100644
--- a/compiler/src/CFCBindClass.c
+++ b/compiler/src/CFCBindClass.c
@@ -642,3 +642,41 @@
return short_names;
}
+char*
+CFCBindClass_host_data_json(CFCBindClass *self) {
+ if (CFCClass_final(self->client)) { return CFCUtil_strdup(""); }
+
+ CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client);
+ char *methods_json = CFCUtil_strdup("");
+
+ for (int i = 0; fresh_methods[i] != NULL; i++) {
+ CFCMethod *method = fresh_methods[i];
+ char *method_json = CFCBindMeth_host_data_json(method);
+ if (method_json[0] != '\0') {
+ const char *sep = methods_json[0] == '\0' ? "" : ",\n";
+ methods_json = CFCUtil_cat(methods_json, sep, method_json, NULL);
+ }
+ FREEMEM(method_json);
+ }
+
+ char *json;
+
+ if (methods_json[0] == '\0') {
+ json = CFCUtil_strdup("");
+ }
+ else {
+ const char *class_name = CFCClass_get_name(self->client);
+
+ const char *pattern =
+ " \"%s\": {\n"
+ " \"methods\": {\n"
+ "%s\n"
+ " }\n"
+ " }";
+ json = CFCUtil_sprintf(pattern, class_name, methods_json);
+ }
+
+ FREEMEM(methods_json);
+ return json;
+}
+
diff --git a/compiler/src/CFCBindClass.h b/compiler/src/CFCBindClass.h
index 4780bdc..450e0aa 100644
--- a/compiler/src/CFCBindClass.h
+++ b/compiler/src/CFCBindClass.h
@@ -54,6 +54,11 @@
char*
CFCBindClass_to_c_data(CFCBindClass *self);
+/** Return host-specific data for the class as JSON fragment.
+ */
+char*
+CFCBindClass_host_data_json(CFCBindClass *self);
+
#ifdef __cplusplus
}
#endif
diff --git a/compiler/src/CFCBindCore.c b/compiler/src/CFCBindCore.c
index c835cc9..95e0206 100644
--- a/compiler/src/CFCBindCore.c
+++ b/compiler/src/CFCBindCore.c
@@ -74,6 +74,10 @@
static char*
S_charmony_alloca_defines();
+static void
+S_write_host_data_json(CFCBindCore *self, CFCParcel *parcel,
+ const char *dest_dir, const char *host_lang);
+
static const CFCMeta CFCBINDCORE_META = {
"Clownfish::CFC::Binding::Core",
sizeof(CFCBindCore),
@@ -759,4 +763,86 @@
FREEMEM(default_dest);
}
+void
+CFCBindCore_write_host_data_json(CFCBindCore *self, const char *dest_dir,
+ const char *host_lang) {
+ CFCParcel **parcels = CFCParcel_all_parcels();
+
+ for (size_t i = 0; parcels[i] != NULL; i++) {
+ CFCParcel *parcel = parcels[i];
+ if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) {
+ S_write_host_data_json(self, parcel, dest_dir, host_lang);
+ }
+ }
+}
+
+static void
+S_write_host_data_json(CFCBindCore *self, CFCParcel *parcel,
+ const char *dest_dir, const char *host_lang) {
+ const char *prefix = CFCParcel_get_prefix(parcel);
+ const char *parcel_name = CFCParcel_get_name(parcel);
+ CFCVersion *version = CFCParcel_get_version(parcel);
+ const char *vstring = CFCVersion_get_vstring(version);
+ char *json_pairs = CFCUtil_strdup("");
+
+ const char *host_module_name = CFCParcel_get_host_module_name(parcel);
+ if (host_module_name != NULL) {
+ const char *pattern = " \"host_module\": \"%s\"";
+ char *pair = CFCUtil_sprintf(pattern, host_module_name);
+ json_pairs = CFCUtil_cat(json_pairs, pair, NULL);
+ FREEMEM(pair);
+ }
+
+ char *classes_json = CFCUtil_strdup("");
+ CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
+
+ for (size_t i = 0; ordered[i] != NULL; i++) {
+ CFCClass *klass = ordered[i];
+ const char *class_prefix = CFCClass_get_prefix(klass);
+ if (strcmp(class_prefix, prefix) != 0) { continue; }
+
+ CFCBindClass *class_binding = CFCBindClass_new(klass);
+
+ char *class_json = CFCBindClass_host_data_json(class_binding);
+ if (class_json[0] != '\0') {
+ const char *sep = classes_json[0] == '\0' ? "" : ",\n";
+ classes_json = CFCUtil_cat(classes_json, sep, class_json, NULL);
+ }
+
+ FREEMEM(class_json);
+ CFCBase_decref((CFCBase*)class_binding);
+ }
+ FREEMEM(ordered);
+
+ if (classes_json[0] != '\0') {
+ const char *pattern =
+ " \"classes\": {\n"
+ "%s\n"
+ " }";
+ char *pair = CFCUtil_sprintf(pattern, classes_json);
+ const char *sep = json_pairs[0] == '\0' ? "" : ",\n";
+ json_pairs = CFCUtil_cat(json_pairs, sep, pair, NULL);
+ FREEMEM(pair);
+ }
+
+ char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP "%s"
+ CHY_DIR_SEP "parcel_%s.json", dest_dir,
+ parcel_name, vstring, host_lang);
+ remove(filepath);
+
+ if (json_pairs[0] != '\0') {
+ const char *pattern =
+ "{\n"
+ "%s\n"
+ "}\n";
+ char *json = CFCUtil_sprintf(pattern, json_pairs);
+ CFCUtil_write_file(filepath, json, strlen(json));
+ FREEMEM(json);
+ }
+
+ FREEMEM(filepath);
+ FREEMEM(classes_json);
+ FREEMEM(json_pairs);
+}
+
diff --git a/compiler/src/CFCBindCore.h b/compiler/src/CFCBindCore.h
index eff3efe..aa40bb2 100644
--- a/compiler/src/CFCBindCore.h
+++ b/compiler/src/CFCBindCore.h
@@ -66,6 +66,12 @@
void
CFCBindCore_copy_headers(CFCBindCore *self, const char *dest_dir);
+/* Write host-specific data to a JSON file for each source parcel.
+ */
+void
+CFCBindCore_write_host_data_json(CFCBindCore *self, const char *dest_dir,
+ const char *host_lang);
+
#ifdef __cplusplus
}
#endif
diff --git a/compiler/src/CFCBindMethod.c b/compiler/src/CFCBindMethod.c
index 400d554..d57a29e 100644
--- a/compiler/src/CFCBindMethod.c
+++ b/compiler/src/CFCBindMethod.c
@@ -228,3 +228,37 @@
return buf;
}
+char*
+CFCBindMeth_host_data_json(CFCMethod *method) {
+ if (!CFCMethod_novel(method)) { return CFCUtil_strdup(""); }
+
+ int excluded = CFCMethod_excluded_from_host(method);
+ const char *alias = CFCMethod_get_host_alias(method);
+ char *pair = NULL;
+ char *json = NULL;
+
+ if (excluded) {
+ pair = CFCUtil_strdup("\"excluded\": true");
+ }
+ else if (alias) {
+ pair = CFCUtil_sprintf("\"alias\": \"%s\"", alias);
+ }
+
+ if (pair) {
+ const char *method_name = CFCMethod_get_name(method);
+
+ const char *pattern =
+ " \"%s\": {\n"
+ " %s\n"
+ " }";
+ json = CFCUtil_sprintf(pattern, method_name, pair);
+
+ FREEMEM(pair);
+ }
+ else {
+ json = CFCUtil_strdup("");
+ }
+
+ return json;
+}
+
diff --git a/compiler/src/CFCBindMethod.h b/compiler/src/CFCBindMethod.h
index 4928c70..f1b0bd4 100644
--- a/compiler/src/CFCBindMethod.h
+++ b/compiler/src/CFCBindMethod.h
@@ -57,6 +57,12 @@
char*
CFCBindMeth_imp_declaration(struct CFCMethod *method, struct CFCClass *klass);
+/** Return a JSON fragment for method data specified by the host bindings
+ * (alias or excluded).
+ */
+char*
+CFCBindMeth_host_data_json(struct CFCMethod *method);
+
#ifdef __cplusplus
}
#endif
diff --git a/compiler/src/CFCClass.c b/compiler/src/CFCClass.c
index 7fa7ff0..ea39848 100644
--- a/compiler/src/CFCClass.c
+++ b/compiler/src/CFCClass.c
@@ -33,6 +33,7 @@
#include "CFCUtil.h"
#include "CFCVariable.h"
#include "CFCFileSpec.h"
+#include "CFCJson.h"
static CFCClass **registry = NULL;
static size_t registry_size = 0;
@@ -741,6 +742,22 @@
return ladder;
}
+void
+CFCClass_read_host_data_json(CFCClass *self, CFCJson *hash, const char *path) {
+ CFCJson *method_hash = CFCJson_find_hash_elem(hash, "methods");
+ if (!method_hash) { return; }
+
+ CFCJson **children = CFCJson_get_children(method_hash);
+ for (int i = 0; children[i]; i += 2) {
+ const char *method_name = CFCJson_get_string(children[i]);
+ CFCMethod *method = CFCClass_method(self, method_name);
+ if (!method) {
+ CFCUtil_die("Method '%s' in '%s' not found", method_name, path);
+ }
+ CFCMethod_read_host_data_json(method, children[i+1], path);
+ }
+}
+
CFCMethod**
CFCClass_fresh_methods(CFCClass *self) {
return self->fresh_methods;
diff --git a/compiler/src/CFCClass.h b/compiler/src/CFCClass.h
index 5da05f5..91fa0cf 100644
--- a/compiler/src/CFCClass.h
+++ b/compiler/src/CFCClass.h
@@ -37,6 +37,7 @@
struct CFCMethod;
struct CFCVariable;
struct CFCFileSpec;
+struct CFCJson;
/** Return true if the string is a valid class name.
*/
@@ -167,6 +168,12 @@
CFCClass**
CFCClass_tree_to_ladder(CFCClass *self);
+/** Read host-specific data for the class from a JSON hash.
+ */
+void
+CFCClass_read_host_data_json(CFCClass *self, struct CFCJson *hash,
+ const char *path);
+
/** Return an array of all methods implemented in this class.
* Must not be freed by the caller.
*/
diff --git a/compiler/src/CFCHierarchy.c b/compiler/src/CFCHierarchy.c
index c33c4f8..19a45ec 100644
--- a/compiler/src/CFCHierarchy.c
+++ b/compiler/src/CFCHierarchy.c
@@ -528,11 +528,7 @@
--path_part_len;
}
- char *path_part = (char*)MALLOCATE(path_part_len + 1);
- memcpy(path_part, src, path_part_len);
- path_part[path_part_len] = '\0';
-
- return path_part;
+ return CFCUtil_strndup(src, path_part_len);
}
static void
@@ -561,6 +557,19 @@
}
}
+void
+CFCHierarchy_read_host_data_json(CFCHierarchy *self, const char *host_lang) {
+ CHY_UNUSED_VAR(self);
+ CFCParcel **parcels = CFCParcel_all_parcels();
+
+ for (int i = 0; parcels[i]; ++i) {
+ CFCParcel *parcel = parcels[i];
+ if (CFCParcel_included(parcel)) {
+ CFCParcel_read_host_data_json(parcel, host_lang);
+ }
+ }
+}
+
int
CFCHierarchy_propagate_modified(CFCHierarchy *self, int modified) {
// Seed the recursive write.
diff --git a/compiler/src/CFCHierarchy.h b/compiler/src/CFCHierarchy.h
index ed9df25..4177cc4 100644
--- a/compiler/src/CFCHierarchy.h
+++ b/compiler/src/CFCHierarchy.h
@@ -68,6 +68,11 @@
void
CFCHierarchy_build(CFCHierarchy *self);
+/** Read host-specific data for included parcels.
+ */
+void
+CFCHierarchy_read_host_data_json(CFCHierarchy *self, const char *host_lang);
+
/** Visit all File objects in the hierarchy. If a parent node is modified, mark
* all of its children as modified.
*
diff --git a/compiler/src/CFCJson.c b/compiler/src/CFCJson.c
index 262b943..1f3a396 100644
--- a/compiler/src/CFCJson.c
+++ b/compiler/src/CFCJson.c
@@ -277,3 +277,18 @@
return self->kids;
}
+CFCJson*
+CFCJson_find_hash_elem(CFCJson *self, const char *key) {
+ if (self->type != CFCJSON_HASH) {
+ CFCUtil_die("Not a JSON hash");
+ }
+
+ for (int i = 0; self->kids[i]; i += 2) {
+ if (strcmp(self->kids[i]->string, key) == 0) {
+ return self->kids[i+1];
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/compiler/src/CFCJson.h b/compiler/src/CFCJson.h
index 97d7081..052a1b8 100644
--- a/compiler/src/CFCJson.h
+++ b/compiler/src/CFCJson.h
@@ -49,6 +49,9 @@
CFCJson**
CFCJson_get_children(CFCJson *self);
+CFCJson*
+CFCJson_find_hash_elem(CFCJson *self, const char *key);
+
#ifdef __cplusplus
}
#endif
diff --git a/compiler/src/CFCMethod.c b/compiler/src/CFCMethod.c
index a8585fb..1d449e4 100644
--- a/compiler/src/CFCMethod.c
+++ b/compiler/src/CFCMethod.c
@@ -26,6 +26,7 @@
#include "CFCParamList.h"
#include "CFCDocuComment.h"
#include "CFCVariable.h"
+#include "CFCJson.h"
#ifndef true
#define true 1
@@ -252,6 +253,35 @@
}
void
+CFCMethod_read_host_data_json(CFCMethod *self, CFCJson *hash,
+ const char *path) {
+ int excluded = false;
+ const char *alias = NULL;
+
+ CFCJson **children = CFCJson_get_children(hash);
+ for (int i = 0; children[i]; i += 2) {
+ const char *key = CFCJson_get_string(children[i]);
+
+ if (strcmp(key, "excluded") == 0) {
+ excluded = CFCJson_get_bool(children[i+1]);
+ }
+ else if (strcmp(key, "alias") == 0) {
+ alias = CFCJson_get_string(children[i+1]);
+ }
+ else {
+ CFCUtil_die("Unexpected key '%s' in '%s'", key, path);
+ }
+ }
+
+ if (excluded) {
+ CFCMethod_exclude_from_host(self);
+ }
+ else if (alias) {
+ CFCMethod_set_host_alias(self, alias);
+ }
+}
+
+void
CFCMethod_set_host_alias(CFCMethod *self, const char *alias) {
if (!alias || !alias[0]) {
CFCUtil_die("Missing required param 'alias'");
diff --git a/compiler/src/CFCMethod.h b/compiler/src/CFCMethod.h
index 643a10d..37c4773 100644
--- a/compiler/src/CFCMethod.h
+++ b/compiler/src/CFCMethod.h
@@ -37,6 +37,7 @@
struct CFCClass;
struct CFCParamList;
struct CFCDocuComment;
+struct CFCJson;
/**
* @param exposure See Clownfish::CFC::Model::Symbol. Defaults to "parcel"
@@ -106,6 +107,12 @@
int
CFCMethod_can_be_bound(CFCMethod *method);
+/** Read host-specific data for the method from a JSON hash.
+ */
+void
+CFCMethod_read_host_data_json(CFCMethod *self, struct CFCJson *hash,
+ const char *path);
+
/**
* Find the first declaration of the method in the class hierarchy.
*/
diff --git a/compiler/src/CFCParcel.c b/compiler/src/CFCParcel.c
index 3b79d71..25cc77c 100644
--- a/compiler/src/CFCParcel.c
+++ b/compiler/src/CFCParcel.c
@@ -29,11 +29,13 @@
#include "CFCVersion.h"
#include "CFCUtil.h"
#include "CFCJson.h"
+#include "CFCClass.h"
struct CFCParcel {
CFCBase base;
char *name;
char *nickname;
+ char *host_module_name;
CFCVersion *version;
CFCVersion *major_version;
CFCFileSpec *file_spec;
@@ -356,6 +358,7 @@
CFCParcel_destroy(CFCParcel *self) {
FREEMEM(self->name);
FREEMEM(self->nickname);
+ FREEMEM(self->host_module_name);
CFCBase_decref((CFCBase*)self->version);
CFCBase_decref((CFCBase*)self->major_version);
CFCBase_decref((CFCBase*)self->file_spec);
@@ -395,6 +398,24 @@
return self->nickname;
}
+const char*
+CFCParcel_get_host_module_name(CFCParcel *self) {
+ return self->host_module_name;
+}
+
+void
+CFCParcel_set_host_module_name(CFCParcel *self, const char *name) {
+ if (self->host_module_name != NULL) {
+ if (strcmp(self->host_module_name, name) != 0) {
+ CFCUtil_die("Conflicting host modules '%s' and '%s' for parcel %s",
+ self->host_module_name, name, self->name);
+ }
+ }
+ else {
+ self->host_module_name = CFCUtil_strdup(name);
+ }
+}
+
int
CFCParcel_is_installed(CFCParcel *self) {
return self->is_installed;
@@ -520,6 +541,44 @@
}
void
+CFCParcel_read_host_data_json(CFCParcel *self, const char *host_lang) {
+ const char *source_dir = CFCParcel_get_source_dir(self);
+ char *path = CFCUtil_sprintf("%s" CHY_DIR_SEP "parcel_%s.json", source_dir,
+ host_lang);
+
+ size_t len;
+ char *json = CFCUtil_slurp_text(path, &len);
+ CFCJson *extra_data = CFCJson_parse(json);
+ if (!extra_data) {
+ CFCUtil_die("Invalid JSON in file '%s'", path);
+ }
+
+ CFCJson *host_module_json
+ = CFCJson_find_hash_elem(extra_data, "host_module");
+ if (host_module_json) {
+ const char *name = CFCJson_get_string(host_module_json);
+ CFCParcel_set_host_module_name(self, name);
+ }
+
+ CFCJson *class_hash = CFCJson_find_hash_elem(extra_data, "classes");
+ if (class_hash) {
+ CFCJson **children = CFCJson_get_children(class_hash);
+ for (int i = 0; children[i]; i += 2) {
+ const char *class_name = CFCJson_get_string(children[i]);
+ CFCClass *klass = CFCClass_fetch_singleton(class_name);
+ if (!klass) {
+ CFCUtil_die("Class '%s' in '%s' not found", class_name, path);
+ }
+ CFCClass_read_host_data_json(klass, children[i+1], path);
+ }
+ }
+
+ CFCJson_destroy(extra_data);
+ FREEMEM(json);
+ FREEMEM(path);
+}
+
+void
CFCParcel_add_struct_sym(CFCParcel *self, const char *struct_sym) {
size_t num_struct_syms = self->num_struct_syms + 1;
size_t size = (num_struct_syms + 1) * sizeof(char*);
diff --git a/compiler/src/CFCParcel.h b/compiler/src/CFCParcel.h
index ddcc2bd..d173079 100644
--- a/compiler/src/CFCParcel.h
+++ b/compiler/src/CFCParcel.h
@@ -90,6 +90,12 @@
const char*
CFCParcel_get_nickname(CFCParcel *self);
+const char*
+CFCParcel_get_host_module_name(CFCParcel *self);
+
+void
+CFCParcel_set_host_module_name(CFCParcel *self, const char *name);
+
int
CFCParcel_is_installed(CFCParcel *self);
@@ -164,6 +170,11 @@
int
CFCParcel_has_prereq(CFCParcel *self, CFCParcel *parcel);
+/** Read host-specific data for an included parcel from a JSON file.
+ */
+void
+CFCParcel_read_host_data_json(CFCParcel *self, const char *host_lang);
+
void
CFCParcel_add_struct_sym(CFCParcel *self, const char *struct_sym);
diff --git a/compiler/src/CFCPerl.c b/compiler/src/CFCPerl.c
index 62a7f48..e9ef8bd 100644
--- a/compiler/src/CFCPerl.c
+++ b/compiler/src/CFCPerl.c
@@ -540,14 +540,21 @@
char *hand_rolled_xs = CFCUtil_strdup("");
for (size_t i = 0; parcels[i]; ++i) {
+ CFCParcel *parcel = parcels[i];
+
+ // Set host_module_name for parcel.
+ if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) {
+ CFCParcel_set_host_module_name(parcel, boot_class);
+ }
+
// Bake the parcel privacy defines into the XS, so it can be compiled
// without any extra compiler flags.
- const char *privacy_sym = CFCParcel_get_privacy_sym(parcels[i]);
+ const char *privacy_sym = CFCParcel_get_privacy_sym(parcel);
privacy_syms = CFCUtil_cat(privacy_syms, "#define ", privacy_sym,
"\n", NULL);
// Bootstrap calls.
- const char *prefix = CFCParcel_get_prefix(parcels[i]);
+ const char *prefix = CFCParcel_get_prefix(parcel);
includes = CFCUtil_cat(includes, "#include \"", prefix, "perl.h\"\n",
NULL);
bootstrap_calls = CFCUtil_cat(bootstrap_calls, " ", prefix,
diff --git a/runtime/perl/buildlib/Clownfish/Build.pm b/runtime/perl/buildlib/Clownfish/Build.pm
index 0efe35a..c038ebc 100644
--- a/runtime/perl/buildlib/Clownfish/Build.pm
+++ b/runtime/perl/buildlib/Clownfish/Build.pm
@@ -85,7 +85,6 @@
name => 'Clownfish::Test',
parcels => [ 'TestClownfish' ],
make_target => 'test_objects',
- xs_prereqs => [ 'Clownfish' ],
},
],
};