libgir/codegen/sys/
cargo_toml.rs

1use std::{collections::HashMap, fs::File, io::prelude::*};
2
3use log::info;
4use toml::{self, value::Table, Value};
5
6use super::collect_versions;
7use crate::{config::Config, env::Env, file_saver::save_to_file, nameutil, version::Version};
8
9pub fn generate(env: &Env) -> String {
10    info!("Generating sys Cargo.toml for {}", env.config.library_name);
11
12    let path = env.config.target_path.join("Cargo.toml");
13
14    let mut toml_str = String::new();
15    if let Ok(mut file) = File::open(&path) {
16        file.read_to_string(&mut toml_str).unwrap();
17    }
18    let empty = toml_str.trim().is_empty();
19    let mut root_table = toml::from_str(&toml_str).unwrap_or_else(|_| Table::new());
20    let crate_name = get_crate_name(&env.config, &root_table);
21
22    if empty {
23        fill_empty(&mut root_table, env, &crate_name);
24    }
25    fill_in(&mut root_table, env);
26
27    save_to_file(&path, env.config.make_backup, |w| {
28        w.write_all(toml::to_string(&root_table).unwrap().as_bytes())
29    });
30
31    crate_name
32}
33
34fn fill_empty(root: &mut Table, env: &Env, crate_name: &str) {
35    let package_name = nameutil::exported_crate_name(crate_name);
36
37    {
38        let package = upsert_table(root, "package");
39        set_string(package, "name", package_name);
40        set_string(package, "version", "0.0.1");
41        set_string(package, "edition", "2021");
42    }
43
44    {
45        let lib = upsert_table(root, "lib");
46        set_string(lib, "name", crate_name);
47    }
48
49    let deps = upsert_table(root, "dependencies");
50    for ext_lib in &env.config.external_libraries {
51        let ext_package = if ext_lib.crate_name == "cairo" {
52            format!("{}-sys-rs", ext_lib.crate_name)
53        } else if ext_lib.crate_name == "gdk_pixbuf" {
54            "gdk-pixbuf-sys".into()
55        } else {
56            format!("{}-sys", ext_lib.crate_name)
57        };
58        let repo_url = match ext_package.as_str() {
59            "cairo-sys-rs" | "gdk-pixbuf-sys" | "gio-sys" | "gobject-sys" | "glib-sys"
60            | "graphene-sys" | "pango-sys" | "pangocairo-sys" => {
61                "https://github.com/gtk-rs/gtk-rs-core"
62            }
63            "atk-sys" | "gdk-sys" | "gdkwayland-sys" | "gdkx11-sys" | "gtk-sys" => {
64                "https://github.com/gtk-rs/gtk3-rs"
65            }
66            "gdk4-wayland-sys" | "gdk4-x11-sys" | "gdk4-sys" | "gsk4-sys" | "gtk4-sys" => {
67                "https://github.com/gtk-rs/gtk4-rs"
68            }
69            "gstreamer-sys"
70            | "gstreamer-app-sys"
71            | "gstreamer-audio-sys"
72            | "gstreamer-base-sys"
73            | "gstreamer-check-sys"
74            | "gstreamer-controller-sys"
75            | "gstreamer-editing-services-sys"
76            | "gstreamer-gl-sys"
77            | "gstreamer-gl-egl-sys"
78            | "gstreamer-gl-wayland-sys"
79            | "gstreamer-gl-x11-sys"
80            | "gstreamer-mpegts-sys"
81            | "gstreamer-net-sys"
82            | "gstreamer-pbutils-sys"
83            | "gstreamer-player-sys"
84            | "gstreamer-rtp-sys"
85            | "gstreamer-rtsp-sys"
86            | "gstreamer-rtsp-server-sys"
87            | "gstreamer-sdp-sys"
88            | "gstreamer-tag-sys"
89            | "gstreamer-video-sys"
90            | "gstreamer-webrtc-sys"
91            | "gstreamer-allocators-sys" => "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs",
92            &_ => "ADD GIT REPOSITORY URL HERE",
93        };
94        let dep = upsert_table(deps, ext_package);
95        set_string(dep, "git", repo_url);
96    }
97}
98
99fn fill_in(root: &mut Table, env: &Env) {
100    {
101        let package = upsert_table(root, "package");
102        package
103            .entry("build")
104            .or_insert_with(|| Value::String("build.rs".into()));
105        // set_string(package, "version", "0.2.0");
106    }
107
108    {
109        let deps = upsert_table(root, "dependencies");
110        set_string(deps, "libc", "0.2");
111    }
112
113    {
114        let build_deps = upsert_table(root, "build-dependencies");
115        set_string(build_deps, "system-deps", "7");
116    }
117
118    {
119        let dev_deps = upsert_table(root, "dev-dependencies");
120        set_string(dev_deps, "shell-words", "1.0.0");
121        set_string(dev_deps, "tempfile", "3");
122        unset(dev_deps, "tempdir");
123    }
124
125    {
126        let features = upsert_table(root, "features");
127        let versions = collect_versions(env);
128        versions.keys().fold(None::<Version>, |prev, &version| {
129            let prev_array: Vec<Value> =
130                get_feature_dependencies(version, prev, &env.config.feature_dependencies)
131                    .iter()
132                    .map(|s| Value::String(s.clone()))
133                    .collect();
134            features.insert(version.to_feature(), Value::Array(prev_array));
135            Some(version)
136        });
137    }
138
139    {
140        let meta = upsert_table(root, "package");
141        let meta = upsert_table(meta, "metadata");
142        let meta = upsert_table(meta, "system-deps");
143
144        let ns = env.namespaces.main();
145        if let Some(lib_name) = ns.package_names.first() {
146            let meta = upsert_table(meta, nameutil::lib_name_to_toml(lib_name));
147            // Allow both the name and version of a system dep to be overridden by hand
148            meta.entry("name")
149                .or_insert_with(|| Value::String(lib_name.clone()));
150            meta.entry("version")
151                .or_insert_with(|| Value::String(env.config.min_cfg_version.to_string()));
152
153            // Old version API
154            unset(meta, "feature-versions");
155
156            collect_versions(env)
157                .iter()
158                .filter(|(&v, _)| v > env.config.min_cfg_version)
159                .for_each(|(v, lib_version)| {
160                    let version_section = upsert_table(meta, v.to_feature());
161                    // Allow system-deps version for this feature level to be overridden by hand
162                    version_section
163                        .entry("version")
164                        .or_insert_with(|| Value::String(lib_version.to_string()));
165                });
166        }
167    }
168
169    {
170        // Small trick to prevent having double quotes around it since toml doesn't like
171        // having '.'
172        let docs_rs_metadata = upsert_table(root, "package");
173        let docs_rs_metadata = upsert_table(docs_rs_metadata, "metadata");
174        let docs_rs_metadata = upsert_table(docs_rs_metadata, "docs");
175        let docs_rs_metadata = upsert_table(docs_rs_metadata, "rs");
176
177        // Set the rustc and rustdoc args to be able to build the docs on docs.rs without the libraries
178        docs_rs_metadata.entry("rustc-args").or_insert_with(|| {
179            Value::Array(vec![
180                Value::String("--cfg".to_string()),
181                Value::String("docsrs".to_string()),
182            ])
183        });
184        docs_rs_metadata.entry("rustdoc-args").or_insert_with(|| {
185            Value::Array(vec![
186                Value::String("--cfg".to_string()),
187                Value::String("docsrs".to_string()),
188                Value::String("--generate-link-to-definition".to_string()),
189            ])
190        });
191
192        // Generate docs for all features unless a list of features to be activated on docs.rs was specified
193        if let toml::map::Entry::Vacant(_) = docs_rs_metadata.entry("features") {
194            docs_rs_metadata.insert("all-features".to_string(), Value::Boolean(true));
195        }
196    }
197}
198
199fn get_feature_dependencies(
200    version: Version,
201    prev_version: Option<Version>,
202    feature_dependencies: &HashMap<Version, Vec<String>>,
203) -> Vec<String> {
204    let mut vec = Vec::with_capacity(10);
205    if let Some(v) = prev_version {
206        vec.push(v.to_feature());
207    }
208    if let Some(dependencies) = feature_dependencies.get(&version) {
209        vec.extend_from_slice(dependencies);
210    }
211    vec
212}
213
214/// Returns the name of crate being currently generated.
215fn get_crate_name(config: &Config, root: &Table) -> String {
216    if let Some(Value::Table(lib)) = root.get("lib") {
217        if let Some(Value::String(lib_name)) = lib.get("name") {
218            // Converting don't needed as library target names cannot contain hyphens
219            return lib_name.clone();
220        }
221    }
222    if let Some(Value::Table(package)) = root.get("package") {
223        if let Some(Value::String(package_name)) = package.get("name") {
224            return nameutil::crate_name(package_name);
225        }
226    }
227    format!("{}_sys", nameutil::crate_name(&config.library_name))
228}
229
230fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
231    table.insert(name.into(), Value::String(new_value.into()));
232}
233
234fn unset(table: &mut Table, name: &str) {
235    table.remove(name);
236}
237
238fn upsert_table<S: Into<String>>(parent: &mut Table, name: S) -> &mut Table {
239    if let Value::Table(table) = parent
240        .entry(name.into())
241        .or_insert_with(|| Value::Table(toml::map::Map::new()))
242    {
243        table
244    } else {
245        unreachable!()
246    }
247}