iota_move/
build.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{fs, path::Path};
6
7use clap::Parser;
8use iota_move_build::{BuildConfig, check_invalid_dependencies, check_unpublished_dependencies};
9use move_cli::base;
10use move_package::BuildConfig as MoveBuildConfig;
11use serde_json::json;
12
13use crate::manage_package::resolve_lock_file_path;
14
15const LAYOUTS_DIR: &str = "layouts";
16const STRUCT_LAYOUTS_FILENAME: &str = "struct_layouts.yaml";
17
18/// Build a move package.
19#[derive(Parser)]
20#[group(id = "iota-move-build")]
21pub struct Build {
22    /// Include the contents of packages in dependencies that haven't been
23    /// published (only relevant when dumping bytecode as base64)
24    #[arg(long, global = true)]
25    pub with_unpublished_dependencies: bool,
26    /// Whether we are printing in base64.
27    #[arg(long, global = true)]
28    pub dump_bytecode_as_base64: bool,
29    /// Don't specialize the package to the active chain when dumping bytecode
30    /// as Base64. This allows building to proceed without a network connection
31    /// or active environment, but it will not be able to automatically
32    /// determine the addresses of its dependencies.
33    #[arg(long, global = true, requires = "dump_bytecode_as_base64")]
34    pub ignore_chain: bool,
35    /// If true, generate struct layout schemas for all struct types passed into
36    /// `entry` functions declared by modules in this package These layout
37    /// schemas can be consumed by clients (e.g., the TypeScript SDK) to enable
38    /// serialization/deserialization of transaction arguments and events.
39    #[arg(long, global = true)]
40    pub generate_struct_layouts: bool,
41    /// The chain ID, if resolved. Required when the dump_bytecode_as_base64 is
42    /// true, for automated address management, where package addresses are
43    /// resolved for the respective chain in the Move.lock file.
44    #[arg(skip)]
45    pub chain_id: Option<String>,
46}
47
48impl Build {
49    pub fn execute(
50        &self,
51        path: Option<&Path>,
52        build_config: MoveBuildConfig,
53    ) -> anyhow::Result<()> {
54        let rerooted_path = base::reroot_path(path)?;
55        let build_config = resolve_lock_file_path(build_config, Some(&rerooted_path))?;
56        Self::execute_internal(
57            &rerooted_path,
58            build_config,
59            self.with_unpublished_dependencies,
60            self.dump_bytecode_as_base64,
61            self.generate_struct_layouts,
62            self.chain_id.clone(),
63        )
64    }
65
66    pub fn execute_internal(
67        rerooted_path: &Path,
68        config: MoveBuildConfig,
69        with_unpublished_deps: bool,
70        dump_bytecode_as_base64: bool,
71        generate_struct_layouts: bool,
72        chain_id: Option<String>,
73    ) -> anyhow::Result<()> {
74        let pkg = BuildConfig {
75            config,
76            run_bytecode_verifier: true,
77            print_diags_to_stderr: true,
78            chain_id,
79        }
80        .build(rerooted_path)?;
81        if dump_bytecode_as_base64 {
82            check_invalid_dependencies(&pkg.dependency_ids.invalid)?;
83            if !with_unpublished_deps {
84                check_unpublished_dependencies(&pkg.dependency_ids.unpublished)?;
85            }
86
87            println!(
88                "{}",
89                json!({
90                    "modules": pkg.get_package_base64(with_unpublished_deps),
91                    "dependencies": pkg.get_dependency_storage_package_ids(),
92                    "digest": pkg.get_package_digest(with_unpublished_deps),
93                })
94            )
95        }
96
97        if generate_struct_layouts {
98            let layout_str = serde_yaml::to_string(&pkg.generate_struct_layouts()).unwrap();
99            // store under <package_path>/build/<package_name>/layouts/struct_layouts.yaml
100            let dir_name = rerooted_path
101                .join("build")
102                .join(pkg.package.compiled_package_info.package_name.as_str())
103                .join(LAYOUTS_DIR);
104            let layout_filename = dir_name.join(STRUCT_LAYOUTS_FILENAME);
105            fs::create_dir_all(dir_name)?;
106            fs::write(layout_filename, layout_str)?
107        }
108
109        pkg.package
110            .compiled_package_info
111            .build_flags
112            .update_lock_file_toolchain_version(rerooted_path, env!("CARGO_PKG_VERSION").into())?;
113
114        Ok(())
115    }
116}