forked from rooch-network/rooch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rooch framework release compatibility
- Loading branch information
Showing
8 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "rooch-framework-release" | ||
version = "0.1.0" | ||
|
||
# Workspace inherited keys | ||
authors = { workspace = true } | ||
edition = { workspace = true } | ||
homepage = { workspace = true } | ||
license = { workspace = true } | ||
publish = { workspace = true } | ||
repository = { workspace = true } | ||
rust-version = { workspace = true } | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
clap = { features = [ "derive", ], workspace = true } | ||
itertools = { workspace = true } | ||
|
||
move-binary-format = { workspace = true } | ||
moveos-stdlib-builder = { workspace = true } | ||
rooch-genesis-builder = { workspace = true } | ||
rooch-types = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Rooch standard library | ||
|
||
Rooch stdlib include the MoveOS standard library and Rooch framework. | ||
|
||
This crate is used to compile the moveos-stdlib and rooch-framework, and then generate the compiled Move bytecode, documentation, and error descriptions for use by the Move Explain tool. | ||
|
||
## Compile and save the latest stdlib | ||
|
||
1. Compile | ||
|
||
```bash | ||
cargo run --package rooch-framework-release --bin rooch-framework-release | ||
``` | ||
|
||
This command will compile the moveos-stdlib and rooch-framework, and then check the compatibility with previous one (if exists), and finally save the new compiled stdlib. | ||
|
||
## Release a new version | ||
|
||
1. Compile with given version number | ||
|
||
```bash | ||
cargo run --package rooch-framework-release --bin rooch-framework-release -- --version 5 | ||
``` | ||
|
||
All modified source files and generated files should be committed. |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// Copyright (c) RoochNetwork | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use clap::Parser; | ||
use itertools::Itertools; | ||
use move_binary_format::{ | ||
compatibility::Compatibility, errors::PartialVMResult, normalized::Module, CompiledModule, | ||
}; | ||
use moveos_stdlib_builder::Stdlib; | ||
use rooch_genesis_builder::build_stdlib; | ||
use rooch_types::stdlib_version::StdlibVersion; | ||
use std::collections::HashMap; | ||
use std::path::Path; | ||
use std::path::PathBuf; | ||
|
||
#[derive(Parser)] | ||
#[clap( | ||
name = "rooch-framework-release", | ||
author = "The Rooch Core Contributors" | ||
)] | ||
struct StdlibOpts { | ||
/// print debug log | ||
#[clap(long)] | ||
debug: bool, | ||
|
||
/// Version number for compiled stdlib, starting from 1. | ||
#[clap(short = 'v', long, value_name = "VERSION")] | ||
version: Option<u64>, | ||
|
||
/// don't check compatibility between the old and new standard library | ||
#[clap(short = 'n', long)] | ||
no_check_compatibility: bool, | ||
} | ||
|
||
fn main() { | ||
let opts: StdlibOpts = StdlibOpts::parse(); | ||
|
||
let curr_stdlib = build_stdlib().unwrap(); | ||
let version = StdlibVersion::new(opts.version.unwrap_or(0)); | ||
let pre_version = if let Some(version_num) = opts.version { | ||
if version_num > 1 { | ||
Some(StdlibVersion::new(version_num - 1)) | ||
} else { | ||
None | ||
} | ||
} else { | ||
// Read dirname in compiled dir, to get the max version number | ||
let max_version = current_max_version(); | ||
if max_version > 0 { | ||
Some(StdlibVersion::new(max_version)) | ||
} else { | ||
None | ||
} | ||
}; | ||
|
||
// check compatibility | ||
if let Some(pre_version) = pre_version { | ||
if !opts.no_check_compatibility { | ||
let prev_stdlib = Stdlib::load_from_file(stdlib_output_file(&pre_version.as_string())) | ||
.expect(&format!( | ||
"load previous stdlib (version {:}) failed", | ||
pre_version.as_string() | ||
)); | ||
assert_stdlib_compatibility(&curr_stdlib, &prev_stdlib); | ||
} | ||
} | ||
curr_stdlib | ||
.save_to_file(stdlib_output_file(&version.as_string())) | ||
.unwrap(); | ||
} | ||
|
||
/// Check whether the new stdlib is compatible with the old stdlib | ||
fn assert_stdlib_compatibility(curr_stdlib: &Stdlib, prev_stdlib: &Stdlib) { | ||
let new_modules = curr_stdlib | ||
.all_modules() | ||
.expect("Extract modules from new stdlib failed"); | ||
let old_modules = prev_stdlib | ||
.all_modules() | ||
.expect("Extract modules from old stdlib failed"); | ||
let new_modules_map = new_modules | ||
.into_iter() | ||
.map(|module| (module.self_id(), module)) | ||
.collect::<HashMap<_, _>>(); | ||
let old_modules_map = old_modules | ||
.into_iter() | ||
.map(|module| (module.self_id(), module)) | ||
.collect::<HashMap<_, _>>(); | ||
|
||
let incompatible_module_ids = new_modules_map | ||
.values() | ||
.into_iter() | ||
.filter_map(|module| { | ||
let module_id = module.self_id(); | ||
if let Some(old_module) = old_modules_map.get(&module_id) { | ||
let compatibility = check_compiled_module_compat(old_module, module); | ||
if compatibility.is_err() { | ||
Some(module_id) | ||
} else { | ||
None | ||
} | ||
} else { | ||
println!("Module {:?} is new module.", module_id); | ||
None | ||
} | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
if !incompatible_module_ids.is_empty() { | ||
eprintln!( | ||
"Modules {} is incompatible with previous version!", | ||
incompatible_module_ids | ||
.into_iter() | ||
.map(|module_id| module_id.to_string()) | ||
.join(","), | ||
); | ||
std::process::exit(1); | ||
} | ||
|
||
let deleted_module_ids = old_modules_map | ||
.keys() | ||
.into_iter() | ||
.filter_map(|module_id| { | ||
if !new_modules_map.contains_key(module_id) { | ||
Some(module_id.clone()) | ||
} else { | ||
None | ||
} | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
if !deleted_module_ids.is_empty() { | ||
eprintln!( | ||
"Modules {} is deleted!", | ||
deleted_module_ids | ||
.into_iter() | ||
.map(|module_id| module_id.to_string()) | ||
.join(","), | ||
); | ||
std::process::exit(1); | ||
} | ||
} | ||
|
||
/// check module compatibility | ||
fn check_compiled_module_compat( | ||
new_module: &CompiledModule, | ||
old_module: &CompiledModule, | ||
) -> PartialVMResult<()> { | ||
let new_m = Module::new(new_module); | ||
let old_m = Module::new(old_module); | ||
// TODO: config compatibility through global configuration | ||
let compat = Compatibility::full_check(); | ||
compat.check(&old_m, &new_m) | ||
} | ||
|
||
/// Read max version number except `latest` from stdlib release dir | ||
fn current_max_version() -> u64 { | ||
let mut max_version = 0; | ||
for entry in release_dir().read_dir().unwrap() { | ||
let entry = entry.unwrap(); | ||
let dirname = entry.file_name(); | ||
if let Some(dirname_str) = dirname.to_str() { | ||
if let Ok(version) = dirname_str.parse::<u64>() { | ||
if version > max_version { | ||
max_version = version; | ||
} | ||
} | ||
} | ||
} | ||
max_version | ||
} | ||
|
||
fn stdlib_output_file(version_str: &str) -> PathBuf { | ||
let version_dir = release_dir().join(version_str); | ||
if !version_dir.exists() { | ||
std::fs::create_dir(&version_dir).expect(&format!("Create dir {:?} failed", version_dir)); | ||
} | ||
version_dir.join("stdlib") | ||
} | ||
|
||
fn release_dir() -> PathBuf { | ||
path_in_crate(format!("../rooch-framework-release/compiled/",)) | ||
} | ||
|
||
fn path_in_crate<S>(relative: S) -> PathBuf | ||
where | ||
S: AsRef<Path>, | ||
{ | ||
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); | ||
path.push(relative); | ||
path | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright (c) RoochNetwork | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use anyhow::Result; | ||
use serde::{Deserialize, Serialize}; | ||
use std::cmp::Ordering; | ||
use std::fmt::Formatter; | ||
use std::fmt::{Debug, Display}; | ||
use std::str::FromStr; | ||
|
||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] | ||
pub enum StdlibVersion { | ||
Latest, | ||
Version(VersionNumber), | ||
} | ||
|
||
type VersionNumber = u64; | ||
|
||
impl StdlibVersion { | ||
pub fn new(version: u64) -> Self { | ||
if version == 0 { | ||
StdlibVersion::Latest | ||
} else { | ||
StdlibVersion::Version(version) | ||
} | ||
} | ||
|
||
pub fn as_string(&self) -> String { | ||
match self { | ||
StdlibVersion::Latest => "latest".to_string(), | ||
StdlibVersion::Version(version) => format!("{}", version), | ||
} | ||
} | ||
|
||
pub fn version(&self) -> u64 { | ||
match self { | ||
StdlibVersion::Latest => 0, | ||
StdlibVersion::Version(version) => *version, | ||
} | ||
} | ||
|
||
pub fn is_latest(&self) -> bool { | ||
matches!(self, StdlibVersion::Latest) | ||
} | ||
|
||
/// If `version`` is compatible with previous version | ||
pub fn compatible_with_previous(_version: &StdlibVersion) -> bool { | ||
true | ||
} | ||
} | ||
|
||
impl PartialOrd for StdlibVersion { | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl Ord for StdlibVersion { | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
match (self, other) { | ||
(StdlibVersion::Latest, StdlibVersion::Latest) => Ordering::Equal, | ||
(StdlibVersion::Latest, _) => Ordering::Greater, | ||
(_, StdlibVersion::Latest) => Ordering::Less, | ||
(StdlibVersion::Version(self_v), StdlibVersion::Version(other_v)) => { | ||
self_v.cmp(other_v) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Default for StdlibVersion { | ||
fn default() -> Self { | ||
StdlibVersion::Latest | ||
} | ||
} | ||
|
||
impl FromStr for StdlibVersion { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
match s { | ||
"latest" => Ok(StdlibVersion::Latest), | ||
s => Ok(Self::new(s.parse()?)), | ||
} | ||
} | ||
} | ||
|
||
impl Display for StdlibVersion { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
StdlibVersion::Latest => f.write_str("latest"), | ||
StdlibVersion::Version(version) => f.write_str(version.to_string().as_str()), | ||
} | ||
} | ||
} |