1#![doc = include_str!("../README.md")]
2
3pub mod build;
4pub mod buildpack_dependency_graph;
5pub mod buildpack_kind;
6pub mod cargo;
7pub mod cross_compile;
8pub mod dependency_graph;
9pub mod output;
10pub mod package;
11pub mod package_descriptor;
12pub mod util;
13
14use crate::build::BuildpackBinaries;
15use std::fs;
16use std::path::{Path, PathBuf};
17use std::process::Command;
18
19#[derive(Debug, Copy, Clone, Eq, PartialEq)]
23pub enum CargoProfile {
24 Dev,
26 Release,
28}
29
30fn assemble_buildpack_directory(
43 destination_path: impl AsRef<Path>,
44 buildpack_descriptor_path: impl AsRef<Path>,
45 buildpack_binaries: &BuildpackBinaries,
46) -> std::io::Result<()> {
47 fs::create_dir_all(destination_path.as_ref())?;
48
49 fs::copy(
50 buildpack_descriptor_path.as_ref(),
51 destination_path.as_ref().join("buildpack.toml"),
52 )?;
53
54 let bin_path = destination_path.as_ref().join("bin");
55 fs::create_dir_all(&bin_path)?;
56
57 fs::copy(
58 &buildpack_binaries.buildpack_target_binary_path,
59 bin_path.join("build"),
60 )?;
61
62 create_file_symlink("build", bin_path.join("detect"))?;
63
64 if !buildpack_binaries.additional_target_binary_paths.is_empty() {
65 let additional_binaries_dir = destination_path
66 .as_ref()
67 .join(".libcnb-cargo")
68 .join("additional-bin");
69
70 fs::create_dir_all(&additional_binaries_dir)?;
71
72 for (binary_target_name, binary_path) in &buildpack_binaries.additional_target_binary_paths
73 {
74 fs::copy(
75 binary_path,
76 additional_binaries_dir.join(binary_target_name),
77 )?;
78 }
79 }
80
81 Ok(())
82}
83
84#[cfg(target_family = "unix")]
85fn create_file_symlink<P: AsRef<Path>, Q: AsRef<Path>>(
86 original: P,
87 link: Q,
88) -> std::io::Result<()> {
89 std::os::unix::fs::symlink(original.as_ref(), link.as_ref())
90}
91
92#[cfg(target_family = "windows")]
93fn create_file_symlink<P: AsRef<Path>, Q: AsRef<Path>>(
94 original: P,
95 link: Q,
96) -> std::io::Result<()> {
97 std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref())
98}
99
100pub fn find_buildpack_dirs(start_dir: &Path) -> Result<Vec<PathBuf>, ignore::Error> {
108 ignore::Walk::new(start_dir)
109 .collect::<Result<Vec<_>, _>>()
110 .map(|entries| {
111 entries
112 .iter()
113 .filter_map(|entry| {
114 if entry.path().is_dir() && entry.path().join("buildpack.toml").exists() {
115 Some(entry.path().to_path_buf())
116 } else {
117 None
118 }
119 })
120 .collect()
121 })
122}
123
124pub fn find_cargo_workspace_root_dir(
134 dir_in_workspace: &Path,
135) -> Result<PathBuf, FindCargoWorkspaceRootError> {
136 let cargo_bin = std::env::var("CARGO")
137 .map(PathBuf::from)
138 .map_err(FindCargoWorkspaceRootError::GetCargoEnv)?;
139
140 let output = Command::new(cargo_bin)
141 .args(["locate-project", "--workspace", "--message-format", "plain"])
142 .current_dir(dir_in_workspace)
143 .output()
144 .map_err(FindCargoWorkspaceRootError::SpawnCommand)?;
145
146 let status = output.status;
147
148 output
149 .status
150 .success()
151 .then_some(output)
152 .ok_or(FindCargoWorkspaceRootError::CommandFailure(status))
153 .and_then(|output| {
154 let root_cargo_toml = PathBuf::from(String::from_utf8_lossy(&output.stdout).trim());
156 root_cargo_toml.parent().map(Path::to_path_buf).ok_or(
157 FindCargoWorkspaceRootError::GetParentDirectory(root_cargo_toml),
158 )
159 })
160}
161
162#[derive(thiserror::Error, Debug)]
163pub enum FindCargoWorkspaceRootError {
164 #[error("Couldn't get value of CARGO environment variable: {0}")]
165 GetCargoEnv(#[source] std::env::VarError),
166 #[error("Error while spawning Cargo process: {0}")]
167 SpawnCommand(#[source] std::io::Error),
168 #[error("Unexpected Cargo exit status ({}) while attempting to read workspace root", exit_code_or_unknown(*.0))]
169 CommandFailure(std::process::ExitStatus),
170 #[error("Couldn't locate a Cargo workspace within {0} or its parent directories")]
171 GetParentDirectory(PathBuf),
172}
173
174fn exit_code_or_unknown(exit_status: std::process::ExitStatus) -> String {
175 exit_status
176 .code()
177 .map_or_else(|| String::from("<unknown>"), |code| code.to_string())
178}