contract_build/workspace/
mod.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17mod manifest;
18mod metadata;
19mod profile;
20
21#[doc(inline)]
22pub use self::{
23    manifest::{
24        Manifest,
25        ManifestPath,
26    },
27    profile::{
28        Lto,
29        OptLevel,
30        PanicStrategy,
31        Profile,
32    },
33};
34
35use anyhow::Result;
36use cargo_metadata::{
37    Metadata as CargoMetadata,
38    Package,
39    PackageId,
40};
41
42use std::path::{
43    Path,
44    PathBuf,
45};
46
47/// Make a copy of a contract project manifest, allow modifications to be made to it,
48/// rewrite the paths to point to the original project files, then write to a temporary
49/// directory.
50///
51/// This allows custom amendments to be made to the manifest files without editing the
52/// originals directly.
53pub struct Workspace {
54    workspace_root: PathBuf,
55    root_package: Package,
56    root_manifest: Manifest,
57}
58
59impl Workspace {
60    /// Create a new Workspace from the supplied cargo metadata and the id of the root
61    /// contract package.
62    pub fn new(metadata: &CargoMetadata, root_package: &PackageId) -> Result<Self> {
63        let root_package = metadata
64            .packages
65            .iter()
66            .find(|p| p.id == *root_package)
67            .ok_or_else(|| {
68                anyhow::anyhow!("The root package should be a workspace member")
69            })?;
70
71        let manifest_path = ManifestPath::new(&root_package.manifest_path)?;
72        let root_manifest = Manifest::new(manifest_path)?;
73
74        Ok(Workspace {
75            workspace_root: metadata.workspace_root.clone().into(),
76            root_package: root_package.clone(),
77            root_manifest,
78        })
79    }
80
81    /// Amend the root package manifest using the supplied function.
82    ///
83    /// # Note
84    ///
85    /// The root package is the current workspace package being built, not to be confused
86    /// with the workspace root (where the top level workspace `Cargo.toml` is
87    /// defined).
88    pub fn with_root_package_manifest<F>(&mut self, f: F) -> Result<&mut Self>
89    where
90        F: FnOnce(&mut Manifest) -> Result<()>,
91    {
92        f(&mut self.root_manifest)?;
93        Ok(self)
94    }
95
96    /// Generates a package to invoke for generating contract metadata.
97    ///
98    /// The contract metadata will be generated for the package found at `package_path`.
99    pub(super) fn with_metadata_gen_package(&mut self) -> Result<&mut Self> {
100        self.root_manifest.with_metadata_package()?;
101        Ok(self)
102    }
103
104    /// Writes the amended manifest to the `target` directory. Relative paths will be
105    /// rewritten to absolute paths from the original project root.
106    ///
107    /// Returns the path of the new manifest.
108    pub fn write<P: AsRef<Path>>(&mut self, target: P) -> Result<ManifestPath> {
109        // replace the original workspace root with the temporary directory
110        let mut new_path: PathBuf = target.as_ref().into();
111        new_path.push(
112            self.root_package
113                .manifest_path
114                .strip_prefix(&self.workspace_root)?,
115        );
116        let new_manifest = ManifestPath::new(new_path)?;
117
118        self.root_manifest.rewrite_relative_paths()?;
119        self.root_manifest.write(&new_manifest)?;
120
121        Ok(new_manifest)
122    }
123
124    /// Write the amended manifest file to a temporary directory, then execute the
125    /// supplied function with the temporary manifest path before the directory is
126    /// cleaned up.
127    pub fn using_temp<F>(&mut self, f: F) -> Result<()>
128    where
129        F: FnOnce(&ManifestPath) -> Result<()>,
130    {
131        let tmp_dir = tempfile::Builder::new()
132            .prefix("cargo-contract_")
133            .tempdir()?;
134        tracing::debug!("Using temp workspace at '{}'", tmp_dir.path().display());
135        let tmp_root_manifest_path = self.write(&tmp_dir)?;
136
137        // copy the `Cargo.lock` file
138        let src_lockfile = self.workspace_root.clone().join("Cargo.lock");
139        let dest_lockfile = tmp_dir.path().join("Cargo.lock");
140        if src_lockfile.exists() {
141            tracing::debug!(
142                "Copying '{}' to ' '{}'",
143                src_lockfile.display(),
144                dest_lockfile.display()
145            );
146            std::fs::copy(src_lockfile, dest_lockfile)?;
147        }
148
149        f(&tmp_root_manifest_path)
150    }
151}