Skip to content

Commit

Permalink
feat: add sovereign.toml manifest file (#792)
Browse files Browse the repository at this point in the history
* feat: add sovereign.toml manifest file

This commit introduces a manifest file that can be parsed by derive
macros and have its contents read at compile-time.

It will read recursively the directory of the call site of the
macro until it either finds a `sovereign.toml` file, or the path is
depleted.

The implementation for the source file location of proc macros depends
on `procmacro2_semver_exempt`, and it will fallback to the manifest path
of the `sov-modules-macros` library. The fallback may contain multiple
edge cases as cargo will cache the downloaded crate into a
`$HOME/.cargo` dir, making a recursion tree impossible to reach the
workspace under normal circumstances.

The aforementioned problem will be handled on the issue #786

* update manifest fn to return computed path

* fix unit tests for manifest path

* adjust link check for manifest test
  • Loading branch information
vlopes11 authored Sep 7, 2023
1 parent 1adbfc9 commit 2242b67
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export RUSTFLAGS='--cfg procmacro2_semver_exempt'
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions module-system/sov-modules-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ proc-macro2 = "1.0"
quote = "1.0"
schemars = { workspace = true }
syn = { version = "1.0", features = ["full"] }
toml = "0.7"

[features]
default = []
Expand Down
4 changes: 4 additions & 0 deletions module-system/sov-modules-macros/sovereign.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# this file exists only for test purposes

[section]
value = 25
1 change: 1 addition & 0 deletions module-system/sov-modules-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod cli_parser;
mod common;
mod default_runtime;
mod dispatch;
mod manifest;
mod module_call_json_schema;
mod module_info;
#[cfg(feature = "native")]
Expand Down
83 changes: 83 additions & 0 deletions module-system/sov-modules-macros/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::fs;
use std::path::{Path, PathBuf};

use proc_macro2::Span;
use toml::Table;

const MANIFEST_NAME: &str = "sovereign.toml";

/// Reads a `sovereign.toml` manifest file from the directory tree of the sov-modules-macros.
///
/// If the `RUSTFLAGS=--cfg procmacro2_semver_exempt` environment variable is set, then will read
/// the file from the directory of the provided span. Otherwise, will recurse from the manifest of
/// the `sov-modules-macros`. Warning: the latter approach might have edge cases as the compilation
/// of the `sov-modules-macros` might be performed under the
/// `$HOME.cargo/registry/src/index.crates.io-_/sov-modules-macros-_` folder.
///
/// Tracking issue: https://github.com/Sovereign-Labs/sovereign-sdk/issues/786
#[allow(dead_code)]
pub fn fetch_manifest_toml(span: Span) -> anyhow::Result<(PathBuf, Table)> {
#[cfg(procmacro2_semver_exempt)]
let initial_path = span
.source_file()
.path()
.canonicalize()
.map_err(|e| {
anyhow::anyhow!("failed access base dir for sovereign manifest file from the span: {e}")
})?
.parent()
.map(|p| p.to_path_buf())
.ok_or_else(|| {
anyhow::anyhow!("Could not open the directory of the parent of the provided span")
})?;

let _ = span;

#[cfg(not(procmacro2_semver_exempt))]
let initial_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.canonicalize()
.map_err(|e| anyhow::anyhow!("failed access base dir for sovereign manifest file: {e}"))?;

fetch_manifest_toml_from_path(initial_path)
}

fn fetch_manifest_toml_from_path<P>(initial_path: P) -> anyhow::Result<(PathBuf, Table)>
where
P: AsRef<Path>,
{
let path: PathBuf;
let mut current_path = initial_path.as_ref();
loop {
if current_path.join(MANIFEST_NAME).exists() {
path = current_path.join(MANIFEST_NAME);
break;
}

current_path = current_path
.parent()
.ok_or_else(|| anyhow::anyhow!("Could not find a parent {MANIFEST_NAME}"))?;
}

let manifest = fs::read_to_string(&path)
.map_err(|e| anyhow::anyhow!("Could not read the parent `{}`: {e}", path.display()))?;

let manifest = toml::from_str(&manifest)
.map_err(|e| anyhow::anyhow!("Could not parse `{}`: {}", path.display(), e))?;

Ok((path, manifest))
}

#[test]
fn fetch_manifest_works() {
let path = env!("CARGO_MANIFEST_DIR");
let path = PathBuf::from(path).join("src").join("invalid");
let (path, manifest) = fetch_manifest_toml_from_path(path).unwrap();

let expected_path = env!("CARGO_MANIFEST_DIR");
let expected_path = PathBuf::from(expected_path).join("sovereign.toml");
let expected = fs::read_to_string(&expected_path).unwrap();
let expected = toml::from_str(&expected).unwrap();

assert_eq!(path, expected_path);
assert_eq!(manifest, expected);
}

0 comments on commit 2242b67

Please sign in to comment.