232 lines
7.3 KiB
Python
232 lines
7.3 KiB
Python
"""
|
|
|
|
Creates the Pezkuwi-SDK umbrella crate that re-exports all other crates.
|
|
|
|
This re-creates the `umbrella/` folder. Ensure that it does not contain any changes you want to keep.
|
|
|
|
Usage:
|
|
python3 generate-umbrella.py --sdk <path> --version <version>
|
|
|
|
Example:
|
|
python3 generate-umbrella.py --sdk .. --version 1.11.0
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import toml
|
|
import shutil
|
|
import subprocess
|
|
|
|
from cargo_workspace import Workspace
|
|
|
|
"""
|
|
Crate names that should be excluded from the umbrella crate.
|
|
"""
|
|
def exclude(crate):
|
|
name = crate.name
|
|
if crate.metadata.get("pezkuwi-sdk.exclude-from-umbrella", False):
|
|
return True
|
|
|
|
# No fuzzers or examples:
|
|
if "example" in name or name.endswith("fuzzer"):
|
|
return True
|
|
|
|
# No runtime crates:
|
|
if name.endswith("-runtime"):
|
|
# Note: this is a bit hacky. We should use custom crate metadata instead.
|
|
return name != "sp-runtime" and name != "pezbp-runtime" and name != "frame-try-runtime"
|
|
|
|
# Exclude snowbridge crates.
|
|
if name.startswith("snowbridge-"):
|
|
return True
|
|
|
|
return False
|
|
|
|
def main(path, version):
|
|
delete_umbrella(path)
|
|
workspace = Workspace.from_path(path)
|
|
print(f'Indexed {workspace}')
|
|
|
|
std_crates = [] # name -> path. use list for sorting
|
|
nostd_crates = []
|
|
for crate in workspace.crates:
|
|
if crate.name == 'pezkuwi-sdk':
|
|
continue
|
|
if not crate.publish:
|
|
print(f"Skipping {crate.name} as it is not published")
|
|
continue
|
|
|
|
lib_path = os.path.dirname(crate.abs_path)
|
|
manifest_path = os.path.join(lib_path, "Cargo.toml")
|
|
lib_path = os.path.join(lib_path, "src", "lib.rs")
|
|
path = os.path.dirname(crate.rel_path)
|
|
|
|
# Guess which crates support no_std. Proc-macro crates are always no_std:
|
|
with open(manifest_path, "r") as f:
|
|
manifest = toml.load(f)
|
|
if 'lib' in manifest and 'proc-macro' in manifest['lib']:
|
|
if manifest['lib']['proc-macro']:
|
|
nostd_crates.append((crate, path))
|
|
continue
|
|
|
|
# Crates without a lib.rs cannot be no_std
|
|
if not os.path.exists(lib_path):
|
|
print(f"Skipping {crate.name} as it does not have a 'src/lib.rs'")
|
|
continue
|
|
if exclude(crate):
|
|
print(f"Skipping {crate.name} as it is in the exclude list")
|
|
continue
|
|
|
|
# No search for a no_std attribute:
|
|
with open(lib_path, "r") as f:
|
|
nostd_crate = False
|
|
for line in f:
|
|
line = line.strip()
|
|
if line == "#![no_std]" or line == '#![cfg_attr(not(feature = "std"), no_std)]':
|
|
nostd_crate = True
|
|
break
|
|
elif "no_std" in line:
|
|
print(line)
|
|
|
|
if nostd_crate:
|
|
nostd_crates.append((crate, path))
|
|
else:
|
|
std_crates.append((crate, path))
|
|
|
|
# Sort by name
|
|
std_crates.sort(key=lambda x: x[0].name)
|
|
nostd_crates.sort(key=lambda x: x[0].name)
|
|
|
|
runtime_crates = [crate for crate in nostd_crates if 'frame' in crate[0].name or crate[0].name.startswith('sp-')]
|
|
all_crates = std_crates + nostd_crates
|
|
all_crates.sort(key=lambda x: x[0].name)
|
|
dependencies = {}
|
|
|
|
for (crate, path) in nostd_crates:
|
|
dependencies[crate.name] = {"path": f"../{path}", "default-features": False, "optional": True}
|
|
|
|
for (crate, path) in std_crates:
|
|
dependencies[crate.name] = {"path": f"../{path}", "default-features": False, "optional": True}
|
|
|
|
# The empty features are filled by Zepter
|
|
features = {
|
|
"default": [ "std" ],
|
|
"std": [],
|
|
"runtime-benchmarks": [],
|
|
"try-runtime": [],
|
|
"serde": [],
|
|
"experimental": [],
|
|
"with-tracing": [],
|
|
"runtime-full": list([f"{d.name}" for d, _ in nostd_crates]),
|
|
"runtime": list([f"{d.name}" for d, _ in runtime_crates]),
|
|
"node": ["std"] + list([f"{d.name}" for d, _ in std_crates]),
|
|
"tuples-96": [],
|
|
}
|
|
|
|
manifest = {
|
|
"package": {
|
|
"name": "pezkuwi-sdk",
|
|
"version": version,
|
|
"edition": { "workspace": True },
|
|
"authors": { "workspace": True },
|
|
"description": "Pezkuwi SDK umbrella crate.",
|
|
"homepage": { "workspace": True },
|
|
"repository": { "workspace": True },
|
|
"license": "Apache-2.0",
|
|
"metadata": { "docs": { "rs": {
|
|
"features": ["runtime-full", "node"],
|
|
"targets": ["x86_64-unknown-linux-gnu"]
|
|
}}}
|
|
},
|
|
"dependencies": dependencies,
|
|
"features": features,
|
|
}
|
|
|
|
umbrella_dir = os.path.join(workspace.path, "umbrella")
|
|
manifest_path = os.path.join(umbrella_dir, "Cargo.toml")
|
|
lib_path = os.path.join(umbrella_dir, "src", "lib.rs")
|
|
# create all dir
|
|
os.makedirs(os.path.dirname(lib_path), exist_ok=True)
|
|
# Write the manifest
|
|
with open(manifest_path, "w") as f:
|
|
toml_manifest = toml.dumps(manifest)
|
|
f.write(toml_manifest)
|
|
print(f"Wrote {manifest_path}")
|
|
# Format with taplo to match CI expectations
|
|
taplo_config = os.path.join(workspace.path, ".config", "taplo.toml")
|
|
if os.path.exists(taplo_config):
|
|
try:
|
|
subprocess.run(["taplo", "format", "--config", taplo_config, manifest_path], check=True, capture_output=True)
|
|
print(f"Formatted {manifest_path} with taplo")
|
|
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
print(f"Warning: Could not format with taplo: {e}")
|
|
# and the lib.rs
|
|
with open(lib_path, "w") as f:
|
|
f.write('''// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
//! Pezkuwi SDK umbrella crate re-exporting all other published crates.
|
|
//!
|
|
//! This helps to set a single version number for all your dependencies. Docs are in the
|
|
//! `pezkuwi-sdk-docs` crate.
|
|
|
|
// This file is auto-generated and checked by the CI. You can edit it manually, but it must be
|
|
// exactly the way that the CI expects it.
|
|
''')
|
|
|
|
for crate, _ in all_crates:
|
|
use = crate.name.replace("-", "_")
|
|
desc = crate.description if crate.description.endswith(".") else crate.description + "."
|
|
f.write(f'\n/// {desc}')
|
|
f.write(f'\n#[cfg(feature = "{crate.name}")]\n')
|
|
f.write(f"pub use {use};\n")
|
|
|
|
print(f"Wrote {lib_path}")
|
|
|
|
add_to_workspace(workspace.path)
|
|
|
|
"""
|
|
Delete the umbrella folder and remove the umbrella crate from the workspace.
|
|
"""
|
|
def delete_umbrella(path):
|
|
# remove the umbrella crate from the workspace
|
|
manifest = os.path.join(path, "Cargo.toml")
|
|
manifest = open(manifest, "r").read()
|
|
manifest = re.sub(r'\s+"umbrella",\n', "", manifest)
|
|
with open(os.path.join(path, "Cargo.toml"), "w") as f:
|
|
f.write(manifest)
|
|
umbrella_dir = os.path.join(path, "umbrella")
|
|
if os.path.exists(umbrella_dir):
|
|
print(f"Deleting {umbrella_dir}")
|
|
os.remove(os.path.join(umbrella_dir, "Cargo.toml"))
|
|
shutil.rmtree(os.path.join(umbrella_dir, "src"))
|
|
|
|
"""
|
|
Create the umbrella crate and add it to the workspace.
|
|
"""
|
|
def add_to_workspace(path):
|
|
manifest = os.path.join(path, "Cargo.toml")
|
|
manifest = open(manifest, "r").read()
|
|
manifest = re.sub(r'^members = \[', 'members = [\n "umbrella",', manifest, flags=re.M)
|
|
with open(os.path.join(path, "Cargo.toml"), "w") as f:
|
|
f.write(manifest)
|
|
|
|
os.chdir(path) # hack
|
|
os.system("cargo metadata --format-version 1 > /dev/null") # update the lockfile
|
|
os.system(f"zepter") # enable the features
|
|
os.system(f"taplo format --config .config/taplo.toml Cargo.toml umbrella/Cargo.toml")
|
|
os.system(f"cargo fmt -- umbrella/src/lib.rs") # format lib.rs for rustfmt compliance
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(description="Create a pezkuwi-sdk crate")
|
|
parser.add_argument("--sdk", type=str, default="pezkuwi-sdk", help="Path to the pezkuwi-sdk crate")
|
|
parser.add_argument("--version", type=str, help="Version of the pezkuwi-sdk crate")
|
|
return parser.parse_args()
|
|
|
|
if __name__ == "__main__":
|
|
args = parse_args()
|
|
main(args.sdk, args.version)
|