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

Generate shared events metadata #794

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
53ec07e
WIP attempting to read event metadata marker
ascjones Oct 21, 2022
7efd87e
Introduce ink_event_metadata_externs to MetadataPackage
ascjones Oct 21, 2022
9f52c5e
Merge branch 'master' into aj/shared-events-metadata
ascjones Oct 21, 2022
a239b4e
Fix up ink crate rewriting
ascjones Oct 21, 2022
65b7eba
Generate the metadata-gen/main.rs calling the event metadata extern fns.
ascjones Oct 21, 2022
4f22111
Pass in event specs to __ink_generate_metadata
ascjones Oct 25, 2022
4b7afcf
Merge branch 'master' into aj/shared-events-metadata
ascjones Oct 27, 2022
326c882
Merge branch 'master' into aj/shared-events-metadata
ascjones Oct 28, 2022
1e6ca1c
Fmt
ascjones Oct 31, 2022
3ea91e2
Read event definition ids from custom section
ascjones Nov 2, 2022
9f58f18
Revert "Read event definition ids from custom section"
ascjones Nov 2, 2022
f086b53
Merge branch 'master' into aj/shared-events-metadata
ascjones Nov 7, 2022
963ec5c
Merge branch 'master' into aj/shared-events-metadata
ascjones Nov 28, 2022
1185d13
Bump serde from 1.0.147 to 1.0.148
dependabot[bot] Nov 28, 2022
c7eb0e0
transcode: update ink! deps to beta, remove env_logger (#843)
ascjones Nov 28, 2022
2d3df01
Bump clap from 4.0.27 to 4.0.29
dependabot[bot] Nov 29, 2022
30e5c2b
Merge branch 'master' into aj/shared-events-metadata
ascjones Nov 30, 2022
4ff5c65
Merge branch 'master' into aj/shared-events-metadata
ascjones Dec 1, 2022
f20fa4e
Cargo.lock
ascjones Dec 1, 2022
43473c6
Restore quote and proc-macro2 deps after merge
ascjones Dec 1, 2022
8bfc7b3
Merge branch 'master' into aj/shared-events-metadata
ascjones Dec 6, 2022
19d970c
Merge branch 'master' into aj/shared-events-metadata
ascjones Dec 9, 2022
396948e
Merge branch 'master' into aj/shared-events-metadata
ascjones Jan 4, 2023
293e970
Merge branch 'master' into aj/shared-events-metadata
ascjones Jan 5, 2023
884887b
Merge branch 'master' into aj/shared-events-metadata
ascjones Jan 11, 2023
1aadb68
Merge branch 'master' into aj/shared-events-metadata
ascjones Jan 25, 2023
539805c
Merge branch 'master' into aj/shared-events-metadata
ascjones Feb 3, 2023
4988f25
Merge branch 'master' into aj/shared-events-metadata
ascjones Feb 13, 2023
1a707f0
Format string
ascjones Feb 13, 2023
d210c9e
Merge branch 'master' into aj/shared-events-metadata
ascjones Feb 27, 2023
76de333
Cargo.lock
ascjones Mar 27, 2023
9fedc66
Merge branch 'master' into aj/shared-events-metadata
ascjones Mar 27, 2023
cf2516c
Fix errors after merge
ascjones Mar 27, 2023
b74df5b
Fmt
ascjones Mar 27, 2023
8e0d501
Merge branch 'master' into aj/shared-events-metadata
ascjones Apr 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ scale = { package = "parity-scale-codec", version = "3.0.0", features = ["derive
toml = "0.7.3"
tracing = "0.1.37"
parity-wasm = "0.45.0"
proc-macro2 = "1"
quote = "1"
semver = { version = "1.0.17", features = ["serde"] }
serde = { version = "1", default-features = false, features = ["derive"] }
serde_json = "1.0.96"
Expand Down
19 changes: 19 additions & 0 deletions crates/build/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ use std::{
};
use url::Url;

const INK_EVENT_METADATA_SECTION_PREFIX: &str = "__ink_event_metadata_";

/// Artifacts resulting from metadata generation.
#[derive(serde::Serialize)]
pub struct MetadataArtifacts {
Expand Down Expand Up @@ -180,6 +182,22 @@ pub(crate) fn execute(
Ok(())
};

let module: parity_wasm::elements::Module =
parity_wasm::deserialize_file(&crate_metadata.original_wasm)?;
let ink_event_metadata_externs = module
.custom_sections()
.filter_map(|section| {
if section
.name()
.starts_with(INK_EVENT_METADATA_SECTION_PREFIX)
{
Some(section.name().to_owned())
} else {
None
}
})
.collect::<Vec<_>>();

if unstable_options.original_manifest {
generate_metadata(&crate_metadata.manifest_path)?;
} else {
Expand All @@ -192,6 +210,7 @@ pub(crate) fn execute(
})?
.with_metadata_gen_package(
crate_metadata.manifest_path.absolute_directory()?,
ink_event_metadata_externs,
)?
.using_temp(generate_metadata)?;
}
Expand Down
44 changes: 23 additions & 21 deletions crates/build/src/workspace/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use anyhow::{
};

use super::{
metadata,
metadata::MetadataPackage,
Profile,
};
use crate::OptimizationPasses;
Expand Down Expand Up @@ -124,7 +124,7 @@ pub struct Manifest {
path: ManifestPath,
toml: value::Table,
/// True if a metadata package should be generated for this manifest
metadata_package: bool,
metadata_package: Option<MetadataPackage>,
}

impl Manifest {
Expand All @@ -138,7 +138,7 @@ impl Manifest {
Ok(Manifest {
path: manifest_path,
toml,
metadata_package: false,
metadata_package: None,
})
}

Expand Down Expand Up @@ -269,7 +269,10 @@ impl Manifest {
}

/// Adds a metadata package to the manifest workspace for generating metadata
pub fn with_metadata_package(&mut self) -> Result<&mut Self> {
pub fn with_metadata_package(
&mut self,
ink_event_metadata_externs: Vec<String>,
) -> Result<&mut Self> {
let workspace = self
.toml
.entry("workspace")
Expand Down Expand Up @@ -298,7 +301,20 @@ impl Manifest {
members.push(METADATA_PACKAGE_PATH.into());
}

self.metadata_package = true;
let contract_package_name = self
.toml
.get("package")
.ok_or_else(|| anyhow::anyhow!("package section not found"))?
.get("name")
.ok_or_else(|| anyhow::anyhow!("[package] name field not found"))?
.as_str()
.ok_or_else(|| anyhow::anyhow!("[package] name should be a string"))?
.to_owned();

self.metadata_package = Some(MetadataPackage::new(
contract_package_name,
ink_event_metadata_externs,
));
Ok(self)
}

Expand Down Expand Up @@ -359,7 +375,7 @@ impl Manifest {
.context(format!("Creating directory '{}'", dir.display()))?;
}

if self.metadata_package {
if let Some(metadata_package) = &self.metadata_package {
let dir = if let Some(manifest_dir) = manifest_path.directory() {
manifest_dir.join(METADATA_PACKAGE_PATH)
} else {
Expand All @@ -369,15 +385,6 @@ impl Manifest {
fs::create_dir_all(&dir)
.context(format!("Creating directory '{}'", dir.display()))?;

let contract_package_name = self
.toml
.get("package")
.ok_or_else(|| anyhow::anyhow!("package section not found"))?
.get("name")
.ok_or_else(|| anyhow::anyhow!("[package] name field not found"))?
.as_str()
.ok_or_else(|| anyhow::anyhow!("[package] name should be a string"))?;

let ink_crate = self
.toml
.get("dependencies")
Expand All @@ -394,12 +401,7 @@ impl Manifest {
.as_table()
.ok_or_else(|| anyhow::anyhow!("[features] section should be a table"))?;

metadata::generate_package(
dir,
contract_package_name,
ink_crate.clone(),
features,
)?;
metadata_package.generate(dir, ink_crate.clone(), features)?;
}

let updated_toml = toml::to_string(&self.toml)?;
Expand Down
186 changes: 123 additions & 63 deletions crates/build/src/workspace/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,70 +24,130 @@ use toml::{
Value,
};

/// Generates a cargo workspace package `metadata-gen` which will be invoked via `cargo run` to
/// generate contract metadata.
///
/// # Note
///
/// `ink!` dependencies are copied from the containing contract workspace to ensure the same
/// versions are utilized.
pub(super) fn generate_package<P: AsRef<Path>>(
target_dir: P,
contract_package_name: &str,
mut ink_crate_dependency: Table,
contract_features: &Table,
) -> Result<()> {
let dir = target_dir.as_ref();
tracing::debug!(
"Generating metadata package for {} in {}",
contract_package_name,
dir.display()
);

let cargo_toml = include_str!("../../templates/generate-metadata/_Cargo.toml");
let main_rs = include_str!("../../templates/generate-metadata/main.rs");

let mut cargo_toml: Table = toml::from_str(cargo_toml)?;
let deps = cargo_toml
.get_mut("dependencies")
.expect("[dependencies] section specified in the template")
.as_table_mut()
.expect("[dependencies] is a table specified in the template");

// initialize contract dependency
let contract = deps
.get_mut("contract")
.expect("contract dependency specified in the template")
.as_table_mut()
.expect("contract dependency is a table specified in the template");
contract.insert("package".into(), contract_package_name.into());

// make ink_metadata dependency use default features
ink_crate_dependency.remove("default-features");
ink_crate_dependency.remove("features");
ink_crate_dependency.remove("optional");

// add ink dependencies copied from contract manifest
deps.insert("ink".into(), ink_crate_dependency.into());

// add features from contract
let features = cargo_toml
.entry("features")
.or_insert(Value::Table(Default::default()))
.as_table_mut()
.ok_or_else(|| anyhow::anyhow!("features should be a table"))?;

for (feature, _) in contract_features {
if feature != "default" && feature != "std" {
features.insert(
feature.to_string(),
Value::Array(vec![format!("contract/{feature}").into()]),
);
/// Info for generating a metadata package.
pub struct MetadataPackage {
contract_package_name: String,
ink_event_metadata_externs: Vec<String>,
}

impl MetadataPackage {
/// Construct a new [`MetadataPackage`].
pub fn new(
contract_package_name: String,
ink_event_metadata_externs: Vec<String>,
) -> Self {
Self {
ink_event_metadata_externs,
contract_package_name,
}
}

let cargo_toml = toml::to_string(&cargo_toml)?;
fs::write(dir.join("Cargo.toml"), cargo_toml)?;
fs::write(dir.join("main.rs"), main_rs)?;
Ok(())
/// Generates a cargo workspace package `metadata-gen` which will be invoked via `cargo run` to
/// generate contract metadata.
///
/// # Note
///
/// `ink!` dependencies are copied from the containing contract workspace to ensure the same
/// versions are utilized.
pub fn generate<P: AsRef<Path>>(
&self,
target_dir: P,
mut ink_crate_dependency: Table,
contract_features: &Table,
) -> Result<()> {
let dir = target_dir.as_ref();
tracing::debug!(
"Generating metadata package for {} in {}",
self.contract_package_name,
dir.display()
);

let cargo_toml = include_str!("../../templates/generate-metadata/_Cargo.toml");
let main_rs = self.generate_main();

let mut cargo_toml: Table = toml::from_str(cargo_toml)?;
let deps = cargo_toml
.get_mut("dependencies")
.expect("[dependencies] section specified in the template")
.as_table_mut()
.expect("[dependencies] is a table specified in the template");

// initialize contract dependency
let contract = deps
.get_mut("contract")
.expect("contract dependency specified in the template")
.as_table_mut()
.expect("contract dependency is a table specified in the template");
contract.insert("package".into(), self.contract_package_name.clone().into());

// make ink_metadata dependency use default features
ink_crate_dependency.remove("default-features");
ink_crate_dependency.remove("features");
ink_crate_dependency.remove("optional");

// add ink dependencies copied from contract manifest
deps.insert("ink".into(), ink_crate_dependency.into());

// add features from contract
let features = cargo_toml
.entry("features")
.or_insert(Value::Table(Default::default()))
.as_table_mut()
.ok_or_else(|| anyhow::anyhow!("features should be a table"))?;

for (feature, _) in contract_features {
if feature != "default" && feature != "std" {
features.insert(
feature.to_string(),
Value::Array(vec![format!("contract/{feature}").into()]),
);
}
}

let cargo_toml = toml::to_string(&cargo_toml)?;
fs::write(dir.join("Cargo.toml"), cargo_toml)?;
fs::write(dir.join("main.rs"), main_rs.to_string())?;
Ok(())
}

/// Generate the `main.rs` file to be executed to generate the metadata.
fn generate_main(&self) -> proc_macro2::TokenStream {
let ink_event_metadata_fns = self
.ink_event_metadata_externs
.iter()
.map(|event_metadata_fn| quote::format_ident!("{}", event_metadata_fn))
.collect::<Vec<_>>();

quote::quote!(
extern crate contract;

extern "Rust" {
// Note: The ink! metadata codegen generates an implementation for this function,
// which is what we end up linking to here.
fn __ink_generate_metadata(
events: ::ink::prelude::vec::Vec<::ink::metadata::EventSpec>
) -> ::ink::metadata::InkProject;

// All `#[ink::event_definition]`s export a unique function to fetch their
// respective metadata, which we link to here.
#( fn #ink_event_metadata_fns () -> ::ink::metadata::EventSpec; )*
}

fn main() -> Result<(), std::io::Error> {
let metadata = unsafe {
__ink_generate_metadata(
::ink::prelude::vec![
#(
#ink_event_metadata_fns ()
),*
]
)
};

let contents = serde_json::to_string_pretty(&metadata)?;
print!("{contents}");
Ok(())
}
)
}
}
3 changes: 2 additions & 1 deletion crates/build/src/workspace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ impl Workspace {
pub(super) fn with_metadata_gen_package(
&mut self,
package_path: PathBuf,
ink_event_metadata_externs: Vec<String>,
) -> Result<&mut Self> {
self.with_contract_manifest(&package_path, |manifest| {
manifest.with_metadata_package()?;
manifest.with_metadata_package(ink_event_metadata_externs)?;
Ok(())
})
}
Expand Down
14 changes: 0 additions & 14 deletions crates/build/templates/generate-metadata/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +0,0 @@
extern crate contract;

extern "Rust" {
// Note: The ink! metdata codegen generates an implementation for this function,
// which is what we end up linking to here.
fn __ink_generate_metadata() -> ink::metadata::InkProject;
}

fn main() -> Result<(), std::io::Error> {
let metadata = unsafe { __ink_generate_metadata() };
let contents = serde_json::to_string_pretty(&metadata)?;
print!("{contents}");
Ok(())
}