Skip to content

Commit f3c6ad2

Browse files
committed
feat: more splitting and renaming of code
1 parent 682ac69 commit f3c6ad2

File tree

11 files changed

+448
-598
lines changed

11 files changed

+448
-598
lines changed

crates/pixi-build/src/bin/pixi-build-cmake/cmake.rs

Lines changed: 13 additions & 376 deletions
Large diffs are not rendered by default.

crates/pixi-build/src/bin/pixi-build-cmake/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
mod build_script;
22
mod cmake;
33
mod config;
4+
mod protocol;
45
mod stub;
56

6-
use cmake::CMakeBuildBackend;
7+
use protocol::CMakeBuildBackendInstantiator;
78

89
#[tokio::main]
910
pub async fn main() {
10-
if let Err(err) = pixi_build_backend::cli::main(CMakeBuildBackend::factory).await {
11+
if let Err(err) = pixi_build_backend::cli::main(CMakeBuildBackendInstantiator::new).await {
1112
eprintln!("{err:?}");
1213
std::process::exit(1);
1314
}
Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
use std::{str::FromStr, sync::Arc};
2+
3+
use miette::{Context, IntoDiagnostic};
4+
use pixi_build_backend::{
5+
protocol::{Protocol, ProtocolInstantiator},
6+
utils::TemporaryRenderedRecipe,
7+
};
8+
use pixi_build_types::{
9+
procedures::{
10+
conda_build::{
11+
CondaBuildParams, CondaBuildResult, CondaBuiltPackage, CondaOutputIdentifier,
12+
},
13+
conda_metadata::{CondaMetadataParams, CondaMetadataResult},
14+
initialize::{InitializeParams, InitializeResult},
15+
negotiate_capabilities::{NegotiateCapabilitiesParams, NegotiateCapabilitiesResult},
16+
},
17+
CondaPackageMetadata, PlatformAndVirtualPackages,
18+
};
19+
use rattler_build::{
20+
build::run_build,
21+
console_utils::LoggingOutputHandler,
22+
hash::HashInfo,
23+
metadata::{Directories, Output},
24+
recipe::{parser::BuildString, Jinja},
25+
render::resolved_dependencies::DependencyInfo,
26+
tool_configuration::Configuration,
27+
};
28+
use rattler_conda_types::{ChannelConfig, MatchSpec, PackageName, Platform};
29+
30+
use crate::{cmake::CMakeBuildBackend, config::CMakeBackendConfig};
31+
32+
fn input_globs() -> Vec<String> {
33+
[
34+
// Source files
35+
"**/*.{c,cc,cxx,cpp,h,hpp,hxx}",
36+
// CMake files
37+
"**/*.{cmake,cmake.in}",
38+
"**/CMakeFiles.txt",
39+
]
40+
.iter()
41+
.map(|s| s.to_string())
42+
.collect()
43+
}
44+
45+
pub struct CMakeBuildBackendInstantiator {
46+
logging_output_handler: LoggingOutputHandler,
47+
}
48+
49+
impl CMakeBuildBackendInstantiator {
50+
pub fn new(logging_output_handler: LoggingOutputHandler) -> Self {
51+
Self {
52+
logging_output_handler,
53+
}
54+
}
55+
}
56+
#[async_trait::async_trait]
57+
impl Protocol for CMakeBuildBackend {
58+
async fn get_conda_metadata(
59+
&self,
60+
params: CondaMetadataParams,
61+
) -> miette::Result<CondaMetadataResult> {
62+
let channel_config = ChannelConfig {
63+
channel_alias: params.channel_configuration.base_url,
64+
root_dir: self.manifest_root.to_path_buf(),
65+
};
66+
let channels = params.channel_base_urls.unwrap_or_default();
67+
68+
let host_platform = params
69+
.host_platform
70+
.as_ref()
71+
.map(|p| p.platform)
72+
.unwrap_or(Platform::current());
73+
74+
// Build the tool configuration
75+
let tool_config = Arc::new(
76+
Configuration::builder()
77+
.with_opt_cache_dir(self.cache_dir.clone())
78+
.with_logging_output_handler(self.logging_output_handler.clone())
79+
.with_channel_config(channel_config.clone())
80+
.with_testing(false)
81+
.with_keep_build(true)
82+
.finish(),
83+
);
84+
85+
let package_name = PackageName::from_str(&self.project_model.name)
86+
.into_diagnostic()
87+
.context("`{name}` is not a valid package name")?;
88+
89+
let directories = Directories::setup(
90+
package_name.as_normalized(),
91+
&self.manifest_path,
92+
&params.work_directory,
93+
true,
94+
&chrono::Utc::now(),
95+
)
96+
.into_diagnostic()
97+
.context("failed to setup build directories")?;
98+
99+
// Create a variant config from the variant configuration in the parameters.
100+
let variant_combinations =
101+
self.compute_variants(params.variant_configuration, host_platform)?;
102+
103+
// Construct the different outputs
104+
let mut packages = Vec::new();
105+
for variant in variant_combinations {
106+
// TODO: Determine how and if we can determine this from the manifest.
107+
let recipe = self.recipe(host_platform, &channel_config, &variant)?;
108+
let output = Output {
109+
build_configuration: self.build_configuration(
110+
&recipe,
111+
channels.clone(),
112+
params.build_platform.clone(),
113+
params.host_platform.clone(),
114+
variant,
115+
directories.clone(),
116+
)?,
117+
recipe,
118+
finalized_dependencies: None,
119+
finalized_cache_dependencies: None,
120+
finalized_cache_sources: None,
121+
finalized_sources: None,
122+
build_summary: Arc::default(),
123+
system_tools: Default::default(),
124+
extra_meta: None,
125+
};
126+
127+
let temp_recipe = TemporaryRenderedRecipe::from_output(&output)?;
128+
let tool_config = tool_config.clone();
129+
let output = temp_recipe
130+
.within_context_async(move || async move {
131+
output
132+
.resolve_dependencies(&tool_config)
133+
.await
134+
.into_diagnostic()
135+
})
136+
.await?;
137+
138+
let selector_config = output.build_configuration.selector_config();
139+
140+
let jinja = Jinja::new(selector_config.clone()).with_context(&output.recipe.context);
141+
142+
let hash = HashInfo::from_variant(output.variant(), output.recipe.build().noarch());
143+
let build_string = output.recipe.build().string().resolve(
144+
&hash,
145+
output.recipe.build().number(),
146+
&jinja,
147+
);
148+
149+
let finalized_deps = &output
150+
.finalized_dependencies
151+
.as_ref()
152+
.expect("dependencies should be resolved at this point")
153+
.run;
154+
155+
packages.push(CondaPackageMetadata {
156+
name: output.name().clone(),
157+
version: output.version().clone().into(),
158+
build: build_string.to_string(),
159+
build_number: output.recipe.build.number,
160+
subdir: output.build_configuration.target_platform,
161+
depends: finalized_deps
162+
.depends
163+
.iter()
164+
.map(DependencyInfo::spec)
165+
.map(MatchSpec::to_string)
166+
.collect(),
167+
constraints: finalized_deps
168+
.constraints
169+
.iter()
170+
.map(DependencyInfo::spec)
171+
.map(MatchSpec::to_string)
172+
.collect(),
173+
license: output.recipe.about.license.map(|l| l.to_string()),
174+
license_family: output.recipe.about.license_family,
175+
noarch: output.recipe.build.noarch,
176+
});
177+
}
178+
179+
Ok(CondaMetadataResult {
180+
packages,
181+
input_globs: None,
182+
})
183+
}
184+
185+
async fn build_conda(&self, params: CondaBuildParams) -> miette::Result<CondaBuildResult> {
186+
let channel_config = ChannelConfig {
187+
channel_alias: params.channel_configuration.base_url,
188+
root_dir: self.manifest_root.to_path_buf(),
189+
};
190+
let channels = params.channel_base_urls.unwrap_or_default();
191+
let host_platform = params
192+
.host_platform
193+
.as_ref()
194+
.map(|p| p.platform)
195+
.unwrap_or_else(Platform::current);
196+
197+
let package_name = PackageName::from_str(&self.project_model.name)
198+
.into_diagnostic()
199+
.context("`{name}` is not a valid package name")?;
200+
201+
let directories = Directories::setup(
202+
package_name.as_normalized(),
203+
&self.manifest_path,
204+
&params.work_directory,
205+
true,
206+
&chrono::Utc::now(),
207+
)
208+
.into_diagnostic()
209+
.context("failed to setup build directories")?;
210+
211+
// Recompute all the variant combinations
212+
let variant_combinations =
213+
self.compute_variants(params.variant_configuration, host_platform)?;
214+
215+
// Compute outputs for each variant
216+
let mut outputs = Vec::with_capacity(variant_combinations.len());
217+
for variant in variant_combinations {
218+
let recipe = self.recipe(host_platform, &channel_config, &variant)?;
219+
let build_configuration = self.build_configuration(
220+
&recipe,
221+
channels.clone(),
222+
params.host_platform.clone(),
223+
Some(PlatformAndVirtualPackages {
224+
platform: host_platform,
225+
virtual_packages: params.build_platform_virtual_packages.clone(),
226+
}),
227+
variant,
228+
directories.clone(),
229+
)?;
230+
231+
let mut output = Output {
232+
build_configuration,
233+
recipe,
234+
finalized_dependencies: None,
235+
finalized_cache_dependencies: None,
236+
finalized_cache_sources: None,
237+
finalized_sources: None,
238+
build_summary: Arc::default(),
239+
system_tools: Default::default(),
240+
extra_meta: None,
241+
};
242+
243+
// Resolve the build string
244+
let selector_config = output.build_configuration.selector_config();
245+
let jinja = Jinja::new(selector_config.clone()).with_context(&output.recipe.context);
246+
let hash = HashInfo::from_variant(output.variant(), output.recipe.build().noarch());
247+
let build_string = output
248+
.recipe
249+
.build()
250+
.string()
251+
.resolve(&hash, output.recipe.build().number(), &jinja)
252+
.into_owned();
253+
output.recipe.build.string = BuildString::Resolved(build_string);
254+
255+
outputs.push(output);
256+
}
257+
258+
// Setup tool configuration
259+
let tool_config = Arc::new(
260+
Configuration::builder()
261+
.with_opt_cache_dir(self.cache_dir.clone())
262+
.with_logging_output_handler(self.logging_output_handler.clone())
263+
.with_channel_config(channel_config.clone())
264+
.with_testing(false)
265+
.with_keep_build(true)
266+
.finish(),
267+
);
268+
269+
// Determine the outputs to build
270+
let selected_outputs = if let Some(output_identifiers) = params.outputs {
271+
output_identifiers
272+
.into_iter()
273+
.filter_map(|iden| {
274+
let pos = outputs.iter().position(|output| {
275+
let CondaOutputIdentifier {
276+
name,
277+
version,
278+
build,
279+
subdir,
280+
} = &iden;
281+
name.as_ref()
282+
.map_or(true, |n| output.name().as_normalized() == n)
283+
&& version
284+
.as_ref()
285+
.map_or(true, |v| output.version().to_string() == *v)
286+
&& build
287+
.as_ref()
288+
.map_or(true, |b| output.build_string() == b.as_str())
289+
&& subdir
290+
.as_ref()
291+
.map_or(true, |s| output.target_platform().as_str() == s)
292+
})?;
293+
Some(outputs.remove(pos))
294+
})
295+
.collect()
296+
} else {
297+
outputs
298+
};
299+
300+
let mut packages = Vec::with_capacity(selected_outputs.len());
301+
for output in selected_outputs {
302+
let temp_recipe = TemporaryRenderedRecipe::from_output(&output)?;
303+
let build_string = output
304+
.recipe
305+
.build
306+
.string
307+
.as_resolved()
308+
.expect("build string must have already been resolved")
309+
.to_string();
310+
let tool_config = tool_config.clone();
311+
let (output, package) = temp_recipe
312+
.within_context_async(move || async move { run_build(output, &tool_config).await })
313+
.await?;
314+
let built_package = CondaBuiltPackage {
315+
output_file: package,
316+
input_globs: input_globs(),
317+
name: output.name().as_normalized().to_string(),
318+
version: output.version().to_string(),
319+
build: build_string.to_string(),
320+
subdir: output.target_platform().to_string(),
321+
};
322+
packages.push(built_package);
323+
}
324+
325+
Ok(CondaBuildResult { packages })
326+
}
327+
}
328+
329+
#[async_trait::async_trait]
330+
impl ProtocolInstantiator for CMakeBuildBackendInstantiator {
331+
type ProtocolEndpoint = CMakeBuildBackend;
332+
333+
async fn initialize(
334+
&self,
335+
params: InitializeParams,
336+
) -> miette::Result<(Self::ProtocolEndpoint, InitializeResult)> {
337+
let project_model = params
338+
.project_model
339+
.ok_or_else(|| miette::miette!("project model is required"))?;
340+
341+
let project_model = project_model
342+
.into_v1()
343+
.ok_or_else(|| miette::miette!("project model v1 is required"))?;
344+
345+
let config = if let Some(config) = params.configuration {
346+
serde_json::from_value(config)
347+
.into_diagnostic()
348+
.context("failed to parse configuration")?
349+
} else {
350+
CMakeBackendConfig::default()
351+
};
352+
353+
let instance = CMakeBuildBackend::new(
354+
params.manifest_path.as_path(),
355+
project_model,
356+
config,
357+
self.logging_output_handler.clone(),
358+
params.cache_directory,
359+
)?;
360+
361+
Ok((instance, InitializeResult {}))
362+
}
363+
364+
async fn negotiate_capabilities(
365+
params: NegotiateCapabilitiesParams,
366+
) -> miette::Result<NegotiateCapabilitiesResult> {
367+
let capabilities = Self::ProtocolEndpoint::capabilities(&params.capabilities);
368+
Ok(NegotiateCapabilitiesResult { capabilities })
369+
}
370+
}

0 commit comments

Comments
 (0)