1use std::{error::Error, path::PathBuf, process::Command};
2
3use cargo_metadata::{Message, MetadataCommand, Package, TargetKind};
4use std::process::Stdio;
5
6fn package_contains_dependency_on_steel(packages: &[Package]) -> Option<&Package> {
7 packages.iter().find(|x| x.name.as_ref() == "steel-core")
8}
9
10pub fn steel_home() -> Option<PathBuf> {
17 std::env::var("STEEL_HOME")
18 .ok()
19 .map(PathBuf::from)
20 .or_else(|| {
21 let home = env_home::env_home_dir().map(|x| x.join(".steel"));
22
23 if let Some(home) = home {
24 if home.exists() {
25 return Some(home);
26 }
27
28 #[cfg(target_os = "windows")]
29 {
30 if let Err(e) = std::fs::create_dir(&home) {
31 eprintln!("Unable to create steel home directory {:?}: {}", home, e)
32 }
33
34 return Some(home);
35 }
36 }
37 #[cfg(not(target_os = "windows"))]
38 {
39 let bd = xdg::BaseDirectories::new();
40 let home = bd.data_home;
41
42 home.map(|mut x: PathBuf| {
43 x.push("steel");
44
45 if !x.exists() {
50 if let Err(e) = std::fs::create_dir(&x) {
51 eprintln!("Unable to create steel home directory {:?}: {}", x, e)
52 }
53 }
54
55 x
56 })
57 }
58
59 #[cfg(target_os = "windows")]
60 None
61 })
62}
63
64pub fn run(args: Vec<String>, env_vars: Vec<(String, String)>) -> Result<bool, Box<dyn Error>> {
65 let mut steel_home = steel_home().expect("Unable to find STEEL_HOME");
66
67 steel_home.push("native");
68
69 if !steel_home.exists() {
70 std::fs::create_dir(&steel_home)?;
71 }
72
73 let mut metadata_command = MetadataCommand::new();
75
76 for pair in args.chunks(2) {
77 match &pair {
78 &[arg, path] if arg == "--manifest-path" => {
79 metadata_command.manifest_path(path);
80 }
81 _ => {}
82 }
83 }
84
85 let metadata = metadata_command.exec()?;
86
87 let package = match metadata.root_package() {
88 Some(p) => p,
89 None => return Err("cargo steel-lib must be run from within a crate".into()),
90 };
91
92 println!("Attempting to install: {:#?}", package.name);
93
94 if package_contains_dependency_on_steel(&metadata.packages).is_none() {
95 return Err(
96 "Cannot install package as a steel dylib - does not contain a dependency on steel!"
97 .into(),
98 );
99 }
100
101 let mut command = Command::new("cargo")
102 .args([
103 "build",
104 "--release",
105 "--message-format=json-render-diagnostics",
106 ])
107 .args(args)
108 .envs(env_vars)
109 .stdout(Stdio::piped())
110 .spawn()
111 .unwrap();
112
113 let reader = std::io::BufReader::new(command.stdout.take().unwrap());
114
115 let artifacts = cargo_metadata::Message::parse_stream(reader).filter_map(|x| {
116 if let Ok(Message::CompilerArtifact(artifact)) = x {
117 Some(artifact)
118 } else {
119 None
120 }
121 });
122
123 for last in artifacts {
124 if last.target.kind.contains(&TargetKind::CDyLib) {
125 for file in last.filenames {
126 if file.extension() == Some(std::env::consts::DLL_EXTENSION) {
127 println!("Found a cdylib!");
128 let prefix = if cfg!(target_os = "windows") {
129 "lib"
130 } else {
131 ""
132 };
133 let filename =
136 format!("{}{}", prefix, file.file_name().unwrap()).replace("-", "_");
137
138 steel_home.push(filename);
140
141 println!("Copying {} to {}", file, &steel_home.to_str().unwrap());
142
143 if steel_home.exists() {
144 std::fs::remove_file(&steel_home)
145 .expect("Unable to delete the existing dylib");
146 }
147
148 std::fs::copy(file, &steel_home).unwrap();
149
150 steel_home.pop();
151 break;
152 }
153 }
154 }
155 }
156
157 println!("Done!");
158
159 let exit_status = command.wait().expect("Couldn't get cargo's exit status");
160
161 Ok(exit_status.success())
162}