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 | |
4 | use clap::{Parser, ValueEnum}; |
5 | use i_slint_compiler::diagnostics::BuildDiagnostics; |
6 | use i_slint_compiler::*; |
7 | use itertools::Itertools; |
8 | use std::io::{BufWriter, Write}; |
9 | |
10 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] |
11 | enum 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)] |
27 | struct 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 | |
71 | fn 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 | |