1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4use clap::{Parser, ValueEnum};
5use i_slint_compiler::diagnostics::BuildDiagnostics;
6use i_slint_compiler::*;
7use itertools::Itertools;
8use std::io::{BufWriter, Write};
9
10#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
11enum Embedding {
12 /// Embed resources using absolute paths on the build system (alias: false)
13 #[value(alias = "false")]
14 AsAbsolutePath,
15 /// Embed contents of resource files (alias: true)
16 #[value(alias = "true")]
17 EmbedFiles,
18 /// Embed in a format optimized for the software renderer. This
19 /// option falls back to `embed-files` if the software-renderer is not
20 /// used
21 #[cfg(feature = "software-renderer")]
22 EmbedForSoftwareRenderer,
23}
24
25#[derive(Parser)]
26#[command(author, version, about, long_about = None)]
27struct Cli {
28 /// Set output format
29 #[arg(short = 'f', long = "format", default_value = "cpp", action)]
30 format: generator::OutputFormat,
31
32 /// Include path for other .slint files
33 #[arg(short = 'I', name = "include path", number_of_values = 1, action)]
34 include_paths: Vec<std::path::PathBuf>,
35
36 /// The argument should be in the format `<library>=<path>` specifying the
37 /// name of the library and the path to the library directory or a .slint
38 /// entry-point file.
39 #[arg(short = 'L', name = "library path", number_of_values = 1, action)]
40 library_paths: Vec<String>,
41
42 /// Path to .slint file ('-' for stdin)
43 #[arg(name = "file", action)]
44 path: std::path::PathBuf,
45
46 /// The style name ('native' or 'fluent')
47 #[arg(long, name = "style name", action)]
48 style: Option<String>,
49
50 /// Generate a dependency file
51 #[arg(name = "dependency file", long = "depfile", number_of_values = 1, action)]
52 depfile: Option<std::path::PathBuf>,
53
54 /// Declare which resources to embed
55 #[arg(long, name = "value", value_enum)]
56 embed_resources: Option<Embedding>,
57
58 /// Sets the output file ('-' for stdout)
59 #[arg(name = "file to generate", short = 'o', default_value = "-", action)]
60 output: std::path::PathBuf,
61
62 /// Translation domain
63 #[arg(long = "translation-domain", action)]
64 translation_domain: Option<String>,
65
66 /// C++ namespace
67 #[arg(long = "cpp-namespace", name = "C++ namespace")]
68 cpp_namespace: Option<String>,
69}
70
71fn main() -> std::io::Result<()> {
72 proc_macro2::fallback::force(); // avoid a abort if panic=abort is set
73 let args = Cli::parse();
74 let mut diag = BuildDiagnostics::default();
75 let syntax_node = parser::parse_file(&args.path, &mut diag);
76 //println!("{:#?}", syntax_node);
77 if diag.has_error() {
78 diag.print();
79 std::process::exit(-1);
80 }
81
82 let mut format = args.format.clone();
83
84 if args.cpp_namespace.is_some() {
85 if !matches!(format, generator::OutputFormat::Cpp(..)) {
86 eprintln!("C++ namespace option was set. Output format will be C++.");
87 }
88 format =
89 generator::OutputFormat::Cpp(generator::cpp::Config { namespace: args.cpp_namespace });
90 }
91
92 let mut compiler_config = CompilerConfiguration::new(format.clone());
93 compiler_config.translation_domain = args.translation_domain;
94
95 // Override defaults from command line:
96 if let Some(embed) = args.embed_resources {
97 compiler_config.embed_resources = match embed {
98 Embedding::AsAbsolutePath => EmbedResourcesKind::OnlyBuiltinResources,
99 Embedding::EmbedFiles => EmbedResourcesKind::EmbedAllResources,
100 #[cfg(feature = "software-renderer")]
101 Embedding::EmbedForSoftwareRenderer => EmbedResourcesKind::EmbedTextures,
102 };
103 }
104
105 compiler_config.include_paths = args.include_paths;
106 compiler_config.library_paths = args
107 .library_paths
108 .iter()
109 .filter_map(|entry| entry.split('=').collect_tuple().map(|(k, v)| (k.into(), v.into())))
110 .collect();
111 if let Some(style) = args.style {
112 compiler_config.style = Some(style);
113 }
114 let syntax_node = syntax_node.expect("diags contained no compilation errors");
115 let (doc, diag, _) = spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config));
116
117 let diag = diag.check_and_exit_on_error();
118
119 if args.output == std::path::Path::new("-") {
120 generator::generate(format, &mut std::io::stdout(), &doc)?;
121 } else {
122 generator::generate(
123 format,
124 &mut BufWriter::new(std::fs::File::create(&args.output)?),
125 &doc,
126 )?;
127 }
128
129 if let Some(depfile) = args.depfile {
130 let mut f = BufWriter::new(std::fs::File::create(depfile)?);
131 write!(f, "{}: {}", args.output.display(), args.path.display())?;
132 for x in &diag.all_loaded_files {
133 if x.is_absolute() {
134 write!(f, " {}", x.display())?;
135 }
136 }
137 for resource in doc.root_component.embedded_file_resources.borrow().keys() {
138 if !fileaccess::load_file(std::path::Path::new(resource))
139 .map_or(false, |f| f.is_builtin())
140 {
141 write!(f, " {}", resource)?;
142 }
143 }
144
145 writeln!(f)?;
146 }
147 diag.print_warnings_and_exit_on_error();
148 Ok(())
149}
150