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}