contract_build/workspace/
profile.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
17use toml::value;
18
19/// Subset of cargo profile settings to configure defaults for building contracts.
20///
21/// All fields are optional, and if not set, the default value from cargo will be used.
22/// See https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles.
23#[derive(Default)]
24pub struct Profile {
25    pub opt_level: Option<OptLevel>,
26    pub lto: Option<Lto>,
27    // `None` means use rustc default.
28    pub codegen_units: Option<u32>,
29    pub panic: Option<PanicStrategy>,
30    pub overflow_checks: Option<bool>,
31}
32
33impl Profile {
34    /// The preferred set of defaults for compiling a release build of a contract
35    pub fn default_contract_release() -> Profile {
36        Profile {
37            opt_level: Some(OptLevel::Z),
38            lto: Some(Lto::Fat),
39            codegen_units: Some(1),
40            panic: Some(PanicStrategy::Abort),
41            overflow_checks: Some(true),
42        }
43    }
44
45    /// Set any unset profile settings from the config.
46    ///
47    /// Therefore:
48    ///   - If the user has explicitly defined a profile setting, it will not be
49    ///     overwritten.
50    ///   - If a profile setting is not defined, the value from this profile instance will
51    ///     be added
52    pub(super) fn merge(&self, profile: &mut value::Table) {
53        fn set_value_if_vacant<T>(
54            key: &'static str,
55            value: Option<T>,
56            profile: &mut value::Table,
57        ) where
58            T: Into<value::Value>,
59        {
60            if let Some(value) = value
61                && !profile.contains_key(key)
62            {
63                profile.insert(key.into(), value.into());
64            }
65        }
66        set_value_if_vacant(
67            "opt-level",
68            self.opt_level.map(OptLevel::to_toml_value),
69            profile,
70        );
71        set_value_if_vacant("lto", self.lto.map(Lto::to_toml_value), profile);
72        set_value_if_vacant("codegen-units", self.codegen_units, profile);
73        set_value_if_vacant(
74            "panic",
75            self.panic.map(PanicStrategy::to_toml_value),
76            profile,
77        );
78        set_value_if_vacant("overflow-checks", self.overflow_checks, profile);
79    }
80}
81
82/// The [`opt-level`](https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level) setting
83#[allow(unused)]
84#[derive(Clone, Copy)]
85pub enum OptLevel {
86    NoOptimizations,
87    O1,
88    O2,
89    O3,
90    S,
91    Z,
92}
93
94impl OptLevel {
95    fn to_toml_value(self) -> value::Value {
96        match self {
97            OptLevel::NoOptimizations => 0.into(),
98            OptLevel::O1 => 1.into(),
99            OptLevel::O2 => 2.into(),
100            OptLevel::O3 => 3.into(),
101            OptLevel::S => "s".into(),
102            OptLevel::Z => "z".into(),
103        }
104    }
105}
106
107/// The [`link-time-optimization`](https://doc.rust-lang.org/cargo/reference/profiles.html#lto) setting.
108#[derive(Clone, Copy)]
109#[allow(unused)]
110pub enum Lto {
111    /// Sets `lto = false`
112    ThinLocal,
113    /// Sets `lto = "fat"`, the equivalent of `lto = true`
114    Fat,
115    /// Sets `lto = "thin"`
116    Thin,
117    /// Sets `lto = "off"`
118    Off,
119}
120
121impl Lto {
122    fn to_toml_value(self) -> value::Value {
123        match self {
124            Lto::ThinLocal => false.into(),
125            Lto::Fat => "fat".into(),
126            Lto::Thin => "thin".into(),
127            Lto::Off => "off".into(),
128        }
129    }
130}
131
132/// The `panic` setting.
133#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
134#[allow(unused)]
135pub enum PanicStrategy {
136    Unwind,
137    Abort,
138}
139
140impl PanicStrategy {
141    fn to_toml_value(self) -> value::Value {
142        match self {
143            PanicStrategy::Unwind => "unwind".into(),
144            PanicStrategy::Abort => "abort".into(),
145        }
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use pretty_assertions::assert_eq;
153
154    #[test]
155    fn merge_profile_inserts_preferred_defaults() {
156        let profile = Profile::default_contract_release();
157
158        // no `[profile.release]` section specified
159        let manifest_toml = "";
160        let mut expected = value::Table::new();
161        expected.insert("opt-level".into(), value::Value::String("z".into()));
162        expected.insert("lto".into(), value::Value::String("fat".into()));
163        expected.insert("codegen-units".into(), value::Value::Integer(1));
164        expected.insert("panic".into(), value::Value::String("abort".into()));
165        expected.insert("overflow-checks".into(), value::Value::Boolean(true));
166
167        let mut manifest_profile = toml::from_str(manifest_toml).unwrap();
168
169        profile.merge(&mut manifest_profile);
170
171        assert_eq!(expected, manifest_profile)
172    }
173
174    #[test]
175    fn merge_profile_preserves_user_defined_settings() {
176        let profile = Profile::default_contract_release();
177
178        let manifest_toml = r#"
179            panic = "unwind"
180            lto = false
181            opt-level = 3
182            codegen-units = 256
183            overflow-checks = false
184        "#;
185        let mut expected = value::Table::new();
186        expected.insert("opt-level".into(), value::Value::Integer(3));
187        expected.insert("lto".into(), value::Value::Boolean(false));
188        expected.insert("codegen-units".into(), value::Value::Integer(256));
189        expected.insert("panic".into(), value::Value::String("unwind".into()));
190        expected.insert("overflow-checks".into(), value::Value::Boolean(false));
191
192        let mut manifest_profile = toml::from_str(manifest_toml).unwrap();
193
194        profile.merge(&mut manifest_profile);
195
196        assert_eq!(expected, manifest_profile)
197    }
198}