1#![doc = include_str!("README.md")]
6#![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![deny(unsafe_code)]
10
11#[cfg(feature = "proc_macro_span")]
12extern crate proc_macro;
13
14use core::future::Future;
15use core::pin::Pin;
16use std::cell::RefCell;
17use std::collections::HashMap;
18use std::rc::Rc;
19
20mod builtin_elements;
21pub mod builtin_macros;
22pub mod data_uri;
23pub mod diagnostics;
24pub mod embedded_resources;
25pub mod expression_tree;
26pub mod fileaccess;
27pub mod generator;
28pub mod langtype;
29pub mod layout;
30pub mod lexer;
31pub mod literals;
32pub mod llr;
33pub mod lookup;
34pub mod namedreference;
35pub mod object_tree;
36pub mod parser;
37pub mod pathutils;
38pub mod symbol_counters;
39#[cfg(feature = "bundle-translations")]
40pub mod translations;
41pub mod typeloader;
42pub mod typeregister;
43
44pub mod passes;
45
46use crate::generator::OutputFormat;
47use std::path::Path;
48
49#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub enum EmbedResourcesKind {
52 Nothing,
54 OnlyBuiltinResources,
58 ListAllResources,
62 EmbedAllResources,
65 #[cfg(feature = "renderer-software")]
66 EmbedTextures,
72}
73
74#[derive(Clone, Debug, Eq, PartialEq)]
77#[non_exhaustive]
78pub enum DefaultTranslationContext {
79 ComponentName,
83 None,
88}
89
90#[derive(Clone, Debug, Eq, PartialEq, Default)]
91#[non_exhaustive]
92pub enum ComponentSelection {
93 #[default]
99 ExportedWindows,
100
101 LastExported,
106
107 Named(String),
109}
110
111pub type OpenImportCallback =
121 Rc<dyn Fn(String) -> Pin<Box<dyn Future<Output = Option<std::io::Result<String>>>>>>;
122pub type ResourceUrlMapper =
123 Rc<dyn Fn(&url::Url) -> Pin<Box<dyn Future<Output = Option<url::Url>>>>>;
124
125#[derive(Clone)]
127pub struct CompilerConfiguration {
128 pub embed_resources: EmbedResourcesKind,
131 #[cfg(all(feature = "renderer-software", feature = "sdf-fonts"))]
133 pub use_sdf_fonts: bool,
134 pub include_paths: Vec<std::path::PathBuf>,
136 pub library_paths: HashMap<String, std::path::PathBuf>,
138 pub style: Option<String>,
140
141 pub open_import_callback: Option<OpenImportCallback>,
146 pub resource_url_mapper: Option<ResourceUrlMapper>,
150
151 pub inline_all_elements: bool,
156
157 pub const_scale_factor: Option<f32>,
160
161 pub const_image_sizes: bool,
166
167 pub accessibility: bool,
169
170 pub enable_experimental: bool,
172
173 pub translation_domain: Option<String>,
175 #[cfg(feature = "bundle-translations")]
177 pub translation_path_bundle: Option<std::path::PathBuf>,
178 pub default_translation_context: DefaultTranslationContext,
180
181 pub no_native_menu: bool,
183
184 pub cpp_namespace: Option<String>,
186
187 pub error_on_binding_loop_with_window_layout: bool,
190
191 pub debug_info: bool,
193
194 pub coverage: bool,
198
199 pub debug_hooks: Option<std::hash::RandomState>,
201
202 pub components_to_generate: ComponentSelection,
203
204 pub library_name: Option<String>,
206
207 pub rust_module: Option<String>,
209
210 #[cfg(feature = "slint-sc")]
214 pub(crate) slint_sc: bool,
215
216 pub is_preview: bool,
220}
221
222impl CompilerConfiguration {
223 pub fn new(output_format: OutputFormat) -> Self {
224 let embed_resources = if std::env::var_os("SLINT_EMBED_TEXTURES").is_some()
225 || std::env::var_os("DEP_MCU_BOARD_SUPPORT_MCU_EMBED_TEXTURES").is_some()
226 {
227 #[cfg(not(feature = "renderer-software"))]
228 panic!(
229 "the renderer-software feature must be enabled in i-slint-compiler when embedding textures"
230 );
231 #[cfg(feature = "renderer-software")]
232 EmbedResourcesKind::EmbedTextures
233 } else if let Ok(var) = std::env::var("SLINT_EMBED_RESOURCES") {
234 let var = var.parse::<bool>().unwrap_or_else(|_|{
235 panic!("SLINT_EMBED_RESOURCES has incorrect value. Must be either unset, 'true' or 'false'")
236 });
237 match var {
238 true => EmbedResourcesKind::EmbedAllResources,
239 false => EmbedResourcesKind::OnlyBuiltinResources,
240 }
241 } else {
242 match output_format {
243 #[cfg(feature = "rust")]
244 OutputFormat::Rust => EmbedResourcesKind::EmbedAllResources,
245 OutputFormat::Interpreter => EmbedResourcesKind::Nothing,
246 _ => EmbedResourcesKind::OnlyBuiltinResources,
247 }
248 };
249
250 let inline_all_elements = match std::env::var("SLINT_INLINING") {
251 Ok(var) => var.parse::<bool>().unwrap_or_else(|_| {
252 panic!(
253 "SLINT_INLINING has incorrect value. Must be either unset, 'true' or 'false'"
254 )
255 }),
256 Err(_) => output_format == OutputFormat::Interpreter,
258 };
259
260 #[cfg(feature = "slint-sc")]
264 let inline_all_elements =
265 inline_all_elements || matches!(output_format, OutputFormat::SlintSc);
266
267 let const_scale_factor = std::env::var("SLINT_SCALE_FACTOR")
268 .ok()
269 .and_then(|x| x.parse::<f32>().ok())
270 .filter(|f| *f > 0.);
271
272 let const_image_sizes = match std::env::var("CARGO_CFG_TARGET_FAMILY") {
273 Ok(target_family) => !target_family.split(',').any(|f| f == "wasm"),
275 Err(_) => output_format == OutputFormat::Interpreter && !cfg!(target_family = "wasm"),
278 };
279
280 let enable_experimental = std::env::var_os("SLINT_ENABLE_EXPERIMENTAL_FEATURES").is_some();
281
282 let debug_info = std::env::var_os("SLINT_EMIT_DEBUG_INFO").is_some();
283
284 #[cfg(feature = "slint-sc")]
285 let slint_sc = matches!(output_format, OutputFormat::SlintSc);
286
287 let cpp_namespace = match output_format {
288 #[cfg(feature = "cpp")]
289 OutputFormat::Cpp(config) => match config.namespace {
290 Some(namespace) => Some(namespace),
291 None => std::env::var("SLINT_CPP_NAMESPACE").ok(),
292 },
293 _ => None,
294 };
295
296 let style = std::env::var("SLINT_STYLE").ok();
297
298 Self {
299 embed_resources,
300 include_paths: Default::default(),
301 library_paths: Default::default(),
302 style,
303 open_import_callback: None,
304 resource_url_mapper: None,
305 inline_all_elements,
306 const_scale_factor,
307 const_image_sizes,
308 accessibility: true,
309 enable_experimental,
310 translation_domain: None,
311 default_translation_context: DefaultTranslationContext::ComponentName,
312 no_native_menu: false,
313 cpp_namespace,
314 error_on_binding_loop_with_window_layout: false,
315 debug_info,
316 coverage: false,
317 debug_hooks: None,
318 components_to_generate: ComponentSelection::ExportedWindows,
319 #[cfg(all(feature = "renderer-software", feature = "sdf-fonts"))]
320 use_sdf_fonts: false,
321 #[cfg(feature = "bundle-translations")]
322 translation_path_bundle: std::env::var("SLINT_BUNDLE_TRANSLATIONS")
323 .ok()
324 .map(|x| x.into()),
325 library_name: None,
326 rust_module: None,
327 #[cfg(feature = "slint-sc")]
328 slint_sc,
329 is_preview: false,
330 }
331 }
332}
333
334fn prepare_for_compile(
338 diagnostics: &mut diagnostics::BuildDiagnostics,
339 #[allow(unused_mut)] mut compiler_config: CompilerConfiguration,
340) -> typeloader::TypeLoader {
341 #[cfg(feature = "renderer-software")]
342 if compiler_config.embed_resources == EmbedResourcesKind::EmbedTextures {
343 compiler_config.accessibility = false;
346 }
347
348 diagnostics.enable_experimental = compiler_config.enable_experimental;
349 #[cfg(feature = "slint-sc")]
350 {
351 diagnostics.slint_sc = compiler_config.slint_sc;
352 }
353
354 typeloader::TypeLoader::new(compiler_config, diagnostics)
355}
356
357pub async fn compile_syntax_node(
358 doc_node: parser::SyntaxNode,
359 mut diagnostics: diagnostics::BuildDiagnostics,
360 #[allow(unused_mut)] mut compiler_config: CompilerConfiguration,
361) -> (object_tree::Document, diagnostics::BuildDiagnostics, typeloader::TypeLoader) {
362 let mut loader = prepare_for_compile(&mut diagnostics, compiler_config);
363
364 let doc_node: parser::syntax_nodes::Document = doc_node.into();
365
366 let type_registry =
367 Rc::new(RefCell::new(typeregister::TypeRegister::new(&loader.global_type_registry)));
368 let (foreign_imports, reexports) =
369 loader.load_dependencies_recursively(&doc_node, &mut diagnostics, &type_registry).await;
370
371 let ignore_missing_font_files = loader.compiler_config.resource_url_mapper.is_some();
372 let mut doc = crate::object_tree::Document::from_node(
373 doc_node,
374 foreign_imports,
375 reexports,
376 &mut diagnostics,
377 &type_registry,
378 ignore_missing_font_files,
379 &loader.symbol_counters,
380 );
381
382 if !diagnostics.has_errors() {
383 passes::run_passes(&mut doc, &mut loader, false, &mut diagnostics).await;
384 } else {
385 passes::run_import_passes(&doc, &loader, &mut diagnostics);
387 }
388 (doc, diagnostics, loader)
389}
390
391pub async fn load_root_file(
397 path: &Path,
398 source_path: &Path,
399 source_code: String,
400 mut diagnostics: diagnostics::BuildDiagnostics,
401 #[allow(unused_mut)] mut compiler_config: CompilerConfiguration,
402) -> (std::path::PathBuf, diagnostics::BuildDiagnostics, typeloader::TypeLoader) {
403 let mut loader = prepare_for_compile(&mut diagnostics, compiler_config);
404
405 let (path, _) =
406 loader.load_root_file(path, source_path, source_code, false, &mut diagnostics).await;
407
408 (path, diagnostics, loader)
409}
410
411pub async fn load_root_file_with_raw_type_loader(
418 path: &Path,
419 source_path: &Path,
420 source_code: String,
421 mut diagnostics: diagnostics::BuildDiagnostics,
422 #[allow(unused_mut)] mut compiler_config: CompilerConfiguration,
423) -> (
424 std::path::PathBuf,
425 diagnostics::BuildDiagnostics,
426 typeloader::TypeLoader,
427 Option<typeloader::TypeLoader>,
428) {
429 let mut loader = prepare_for_compile(&mut diagnostics, compiler_config);
430
431 let (path, raw_type_loader) =
432 loader.load_root_file(path, source_path, source_code, true, &mut diagnostics).await;
433
434 (path, diagnostics, loader, raw_type_loader)
435}
436
437fn reject_experimental_feature(
442 diagnostics: &mut diagnostics::BuildDiagnostics,
443 type_register: &typeregister::TypeRegister,
444 feature: &str,
445 source: &dyn diagnostics::Spanned,
446) -> bool {
447 if !diagnostics.enable_experimental && !type_register.expose_internal_types {
448 diagnostics.push_error(format!("'{feature}' is an experimental feature"), source);
449 true
450 } else {
451 false
452 }
453}