1use crate::*;
18
19use leo_ast::DiGraph;
20use leo_errors::Result;
21use leo_span::Symbol;
22
23use indexmap::{IndexMap, map::Entry};
24use snarkvm::prelude::anyhow;
25use std::path::{Path, PathBuf};
26
27#[derive(Clone, Debug)]
30pub enum ProgramData {
31 Bytecode(String),
32 SourcePath {
35 directory: PathBuf,
36 source: PathBuf,
37 },
38}
39
40#[derive(Clone, Debug)]
42pub struct Package {
43 pub base_directory: PathBuf,
45
46 pub workspace_root: Option<PathBuf>,
54
55 pub compilation_units: Vec<CompilationUnit>,
62
63 pub manifest: Manifest,
65
66 pub dep_graph: DiGraph<Symbol>,
68}
69
70impl Package {
71 pub fn build_directory(&self) -> PathBuf {
79 self.workspace_root.as_deref().unwrap_or(&self.base_directory).join(BUILD_DIRECTORY)
80 }
81
82 pub fn primary_unit(&self) -> Option<&CompilationUnit> {
85 let primary = bare_unit_name(&self.manifest.program);
86 self.compilation_units.iter().find(|u| !u.kind.is_test() && bare_unit_name(&u.name.to_string()) == primary)
87 }
88
89 pub fn unit_build_directory(&self, name: &str) -> PathBuf {
93 self.build_directory().join(bare_unit_name(name))
94 }
95
96 pub fn unit_bytecode_path(&self, name: &str) -> PathBuf {
99 let bare = bare_unit_name(name);
100 self.unit_build_directory(name).join(format!("{bare}.aleo"))
101 }
102
103 pub fn unit_abi_path(&self, name: &str) -> PathBuf {
105 self.unit_build_directory(name).join(ABI_FILENAME)
106 }
107
108 pub fn unit_interfaces_directory(&self, name: &str) -> PathBuf {
111 self.unit_build_directory(name).join(INTERFACES_DIRNAME)
112 }
113
114 pub fn unit_snapshots_directory(&self, name: &str) -> PathBuf {
118 self.unit_build_directory(name).join(SNAPSHOTS_DIRNAME)
119 }
120
121 pub fn source_directory(&self) -> PathBuf {
122 self.base_directory.join(SOURCE_DIRECTORY)
123 }
124
125 pub fn tests_directory(&self) -> PathBuf {
126 self.base_directory.join(TESTS_DIRECTORY)
127 }
128
129 pub fn initialize<P: AsRef<Path>>(package_name: &str, path: P, is_library: bool) -> Result<PathBuf> {
131 Self::initialize_impl(package_name, path.as_ref(), is_library)
132 }
133
134 fn initialize_impl(package_name: &str, path: &Path, is_library: bool) -> Result<PathBuf> {
135 let package_name = if is_library {
136 if !crate::is_valid_library_name(package_name) {
137 return Err(crate::errors::cli_invalid_package_name("library", package_name).into());
138 }
139
140 package_name.to_string()
141 } else {
142 let program_name =
143 if package_name.ends_with(".aleo") { package_name.to_string() } else { format!("{package_name}.aleo") };
144
145 if !crate::is_valid_program_name(&program_name) {
146 return Err(crate::errors::cli_invalid_package_name("program", &program_name).into());
147 }
148
149 program_name
150 };
151
152 let path = path.canonicalize().map_err(|e| crate::errors::failed_path(path.display(), e))?;
153 let full_path = path.join(package_name.strip_suffix(".aleo").unwrap_or(&package_name));
154
155 if full_path.exists() {
157 return Err(
158 crate::errors::failed_to_initialize_package(package_name, &path, "Directory already exists").into()
159 );
160 }
161
162 std::fs::create_dir(&full_path)
164 .map_err(|e| crate::errors::failed_to_initialize_package(&package_name, &full_path, e))?;
165
166 std::env::set_current_dir(&full_path)
168 .map_err(|e| crate::errors::failed_to_initialize_package(&package_name, &full_path, e))?;
169
170 const GITIGNORE_TEMPLATE: &str = ".env\n*.avm\n*.prover\n*.verifier\nbuild/\n";
172 const GITIGNORE_FILENAME: &str = ".gitignore";
173
174 let gitignore_path = full_path.join(GITIGNORE_FILENAME);
175 std::fs::write(gitignore_path, GITIGNORE_TEMPLATE).map_err(crate::errors::io_error_gitignore_file)?;
176
177 let manifest = Manifest {
179 program: package_name.clone(),
180 version: "0.1.0".to_string(),
181 description: String::new(),
182 license: "MIT".to_string(),
183 leo: env!("CARGO_PKG_VERSION").to_string(),
184 dependencies: None,
185 dev_dependencies: None,
186 no_std: false,
187 };
188
189 let manifest_path = full_path.join(MANIFEST_FILENAME);
190 manifest.write_to_file(manifest_path)?;
191
192 let source_path = full_path.join(SOURCE_DIRECTORY);
194
195 std::fs::create_dir(&source_path)
196 .map_err(|e| crate::errors::failed_to_create_source_directory(source_path.display(), e))?;
197
198 let name_no_aleo = package_name.strip_suffix(".aleo").unwrap_or(&package_name);
199
200 if is_library {
201 let lib_path = source_path.join("lib.leo");
203
204 std::fs::write(&lib_path, lib_template(name_no_aleo)).map_err(|e| {
205 crate::errors::util_file_io_error(format_args!("Failed to write `{}`", lib_path.display()), e)
206 })?;
207
208 let tests_path = full_path.join(TESTS_DIRECTORY);
210
211 std::fs::create_dir(&tests_path)
212 .map_err(|e| crate::errors::failed_to_create_source_directory(tests_path.display(), e))?;
213
214 let test_file_path = tests_path.join(format!("test_{name_no_aleo}.leo"));
215
216 std::fs::write(&test_file_path, lib_test_template(name_no_aleo)).map_err(|e| {
217 crate::errors::util_file_io_error(format_args!("Failed to write `{}`", test_file_path.display()), e)
218 })?;
219 } else {
220 let main_path = source_path.join(MAIN_FILENAME);
222
223 std::fs::write(&main_path, main_template(name_no_aleo)).map_err(|e| {
224 crate::errors::util_file_io_error(format_args!("Failed to write `{}`", main_path.display()), e)
225 })?;
226
227 let tests_path = full_path.join(TESTS_DIRECTORY);
229
230 std::fs::create_dir(&tests_path)
231 .map_err(|e| crate::errors::failed_to_create_source_directory(tests_path.display(), e))?;
232
233 let test_file_path = tests_path.join(format!("test_{name_no_aleo}.leo"));
234
235 std::fs::write(&test_file_path, test_template(name_no_aleo)).map_err(|e| {
236 crate::errors::util_file_io_error(format_args!("Failed to write `{}`", test_file_path.display()), e)
237 })?;
238 }
239
240 Ok(full_path)
241 }
242
243 pub fn from_directory_no_graph<P: AsRef<Path>, Q: AsRef<Path>>(
247 path: P,
248 home_path: Q,
249 network: Option<NetworkName>,
250 endpoint: Option<&str>,
251 network_retries: u32,
252 ) -> Result<Self> {
253 Self::from_directory_impl(
254 path.as_ref(),
255 home_path.as_ref(),
256 false,
257 false,
258 false,
259 false,
260 false,
261 network,
262 endpoint,
263 network_retries,
264 )
265 }
266
267 #[allow(clippy::too_many_arguments)]
270 pub fn from_directory<P: AsRef<Path>, Q: AsRef<Path>>(
271 path: P,
272 home_path: Q,
273 no_cache: bool,
274 no_local: bool,
275 offline: bool,
276 network: Option<NetworkName>,
277 endpoint: Option<&str>,
278 network_retries: u32,
279 ) -> Result<Self> {
280 Self::from_directory_impl(
281 path.as_ref(),
282 home_path.as_ref(),
283 true,
284 false,
285 no_cache,
286 no_local,
287 offline,
288 network,
289 endpoint,
290 network_retries,
291 )
292 }
293
294 #[allow(clippy::too_many_arguments)]
297 pub fn from_directory_with_tests<P: AsRef<Path>, Q: AsRef<Path>>(
298 path: P,
299 home_path: Q,
300 no_cache: bool,
301 no_local: bool,
302 offline: bool,
303 network: Option<NetworkName>,
304 endpoint: Option<&str>,
305 network_retries: u32,
306 ) -> Result<Self> {
307 Self::from_directory_impl(
308 path.as_ref(),
309 home_path.as_ref(),
310 true,
311 true,
312 no_cache,
313 no_local,
314 offline,
315 network,
316 endpoint,
317 network_retries,
318 )
319 }
320
321 pub fn test_files(&self) -> impl Iterator<Item = PathBuf> {
322 let path = self.tests_directory();
323 let data: Vec<PathBuf> = Self::files_with_extension(&path, "leo").collect();
326 data.into_iter()
327 }
328
329 fn files_with_extension(path: &Path, extension: &'static str) -> impl Iterator<Item = PathBuf> {
330 path.read_dir()
331 .ok()
332 .into_iter()
333 .flatten()
334 .flat_map(|maybe_filename| maybe_filename.ok())
335 .filter(|entry| entry.file_type().ok().map(|filetype| filetype.is_file()).unwrap_or(false))
336 .flat_map(move |entry| {
337 let path = entry.path();
338 if path.extension().is_some_and(|e| e == extension) { Some(path) } else { None }
339 })
340 }
341
342 #[allow(clippy::too_many_arguments)]
343 fn from_directory_impl(
344 path: &Path,
345 home_path: &Path,
346 build_graph: bool,
347 with_tests: bool,
348 no_cache: bool,
349 no_local: bool,
350 offline: bool,
351 network: Option<NetworkName>,
352 endpoint: Option<&str>,
353 network_retries: u32,
354 ) -> Result<Self> {
355 let map_err = |path: &Path, err| {
356 crate::errors::util_file_io_error(format_args!("Trying to find path at {}", path.display()), err)
357 };
358
359 let path = path.canonicalize().map_err(|err| map_err(path, err))?;
360
361 let workspace_root = Workspace::discover_root(&path)?;
365
366 let manifest = Manifest::read_from_file(path.join(MANIFEST_FILENAME))?;
367
368 let (compilation_units, digraph) = if build_graph {
369 let home_path = home_path.canonicalize().map_err(|err| map_err(home_path, err))?;
370
371 let mut map: IndexMap<Symbol, (Dependency, CompilationUnit)> = IndexMap::new();
372
373 let mut digraph = DiGraph::<Symbol>::new(Default::default());
374
375 let declared_deps = collect_declared_deps(&path, &manifest, with_tests)?;
378
379 let lock_dir = workspace_root.as_deref().unwrap_or(&path).to_path_buf();
381 let old_lock = Lock::read(&lock_dir);
383 let mut new_lock = Lock::default();
384
385 let first_dependency = Dependency {
386 name: manifest.program.clone(),
387 location: Location::Local,
388 path: Some(path.clone()),
389 edition: None,
390 ..Default::default()
391 };
392
393 let test_dependencies: Vec<Dependency> = if with_tests {
394 let tests_directory = path.join(TESTS_DIRECTORY);
395 let mut test_dependencies: Vec<Dependency> = Self::files_with_extension(&tests_directory, "leo")
396 .map(|path| Dependency {
397 name: format!("{}.aleo", crate::filename_no_leo_extension(&path).unwrap()),
399 edition: None,
400 location: Location::Test,
401 path: Some(path.to_path_buf()),
402 ..Default::default()
403 })
404 .collect();
405 if let Some(deps) = manifest.dev_dependencies.as_ref() {
406 for dep in deps {
409 let dep = canonicalize_dependency_path_relative_to(&path, dep.clone())?;
410 let dep = if dep.location == Location::Workspace {
411 resolve_workspace_dependency(&path, dep)?
412 } else {
413 dep
414 };
415 test_dependencies.push(dep);
416 }
417 }
418 test_dependencies
419 } else {
420 Vec::new()
421 };
422
423 for dependency in test_dependencies.into_iter().chain(std::iter::once(first_dependency.clone())) {
424 Self::graph_build(
425 &home_path,
426 network,
427 endpoint,
428 &first_dependency,
429 dependency,
430 &mut map,
431 &mut digraph,
432 no_cache,
433 no_local,
434 network_retries,
435 &declared_deps,
436 &old_lock,
437 &mut new_lock,
438 offline,
439 )?;
440 }
441
442 if workspace_root.is_some() {
445 new_lock.carry_over(&old_lock, |_| true);
446 } else {
447 let dev_git_names: Vec<&str> = if with_tests {
448 Vec::new()
449 } else {
450 manifest
451 .dev_dependencies
452 .iter()
453 .flatten()
454 .filter(|dep| dep.location == Location::Git)
455 .map(|dep| dep.name.as_str())
456 .collect()
457 };
458 new_lock.carry_over(&old_lock, |entry| dev_git_names.contains(&entry.name.as_str()));
459 }
460 new_lock.write(&lock_dir)?;
462
463 let ordered_dependency_symbols =
464 digraph.post_order().map_err(|_| crate::errors::circular_dependency_error())?;
465
466 (
467 ordered_dependency_symbols.into_iter().map(|symbol| map.swap_remove(&symbol).unwrap().1).collect(),
468 digraph,
469 )
470 } else {
471 (Vec::new(), DiGraph::default())
472 };
473
474 Ok(Package { base_directory: path, workspace_root, compilation_units, manifest, dep_graph: digraph })
475 }
476
477 #[allow(clippy::too_many_arguments)]
478 fn graph_build(
479 home_path: &Path,
480 network: Option<NetworkName>,
481 endpoint: Option<&str>,
482 main_program: &Dependency,
483 new: Dependency,
484 map: &mut IndexMap<Symbol, (Dependency, CompilationUnit)>,
485 graph: &mut DiGraph<Symbol>,
486 no_cache: bool,
487 no_local: bool,
488 network_retries: u32,
489 declared_deps: &IndexMap<Symbol, Dependency>,
490 old_lock: &Lock,
491 new_lock: &mut Lock,
492 offline: bool,
493 ) -> Result<()> {
494 let name_symbol = symbol(&new.name)?;
495
496 let unit = match map.entry(name_symbol) {
497 Entry::Occupied(occupied) => {
498 let existing_dep = &occupied.get().0;
501 assert_eq!(new.name, existing_dep.name);
502 if new.location != existing_dep.location
503 || new.path != existing_dep.path
504 || new.edition != existing_dep.edition
505 || new.git != existing_dep.git
506 {
507 return Err(crate::errors::conflicting_dependency(existing_dep, new).into());
508 }
509 return Ok(());
510 }
511 Entry::Vacant(vacant) => {
512 let unit = match (new.path.as_ref(), new.location) {
513 (Some(path), Location::Local) if !no_local => {
514 if path.extension().and_then(|p| p.to_str()) == Some("aleo") && path.is_file() {
516 CompilationUnit::from_aleo_path(name_symbol, path, declared_deps)?
517 } else {
518 CompilationUnit::from_package_path(name_symbol, path)?
519 }
520 }
521 (Some(path), Location::Test) => {
522 CompilationUnit::from_test_path(path, main_program.clone())?
525 }
526 (_, Location::Network) | (Some(_), Location::Local) => {
527 let Some(endpoint) = endpoint else {
529 return Err(anyhow!("An endpoint must be provided to fetch network dependencies.").into());
530 };
531 let Some(network) = network else {
532 return Err(anyhow!("A network must be provided to fetch network dependencies.").into());
533 };
534 CompilationUnit::fetch(
535 name_symbol,
536 new.edition,
537 home_path,
538 network,
539 endpoint,
540 no_cache,
541 network_retries,
542 )?
543 }
544 (_, Location::Git) => CompilationUnit::from_git(
545 name_symbol,
546 &new,
547 home_path,
548 old_lock,
549 new_lock,
550 offline,
551 declared_deps,
552 )?,
553 (_, Location::Workspace) => {
554 return Err(anyhow!(
555 "Workspace dependency `{}` was not resolved before graph building. This is a compiler bug.",
556 new.name
557 )
558 .into());
559 }
560 _ => return Err(anyhow!("Invalid dependency data for {} (path must be given).", new.name).into()),
561 };
562
563 vacant.insert((new, unit.clone()));
564
565 unit
566 }
567 };
568
569 graph.add_node(name_symbol);
570
571 let checkouts_root = crate::git::checkouts_root(home_path);
574 if let ProgramData::SourcePath { directory, .. } = &unit.data
575 && directory.starts_with(&checkouts_root)
576 {
577 let checkout = directory
579 .strip_prefix(&checkouts_root)
580 .ok()
581 .and_then(|rel| {
582 let mut components = rel.components();
583 Some((components.next()?, components.next()?))
584 })
585 .map(|(key, commit)| checkouts_root.join(key).join(commit));
586 for dependency in unit.dependencies.iter() {
587 if let Some(path) = &dependency.path
588 && !checkout.as_ref().is_some_and(|checkout| path.starts_with(checkout))
589 {
590 return Err(crate::errors::invalid_manifest_dependency(
591 &dependency.name,
592 "a git dependency may only reference paths inside its own repository checkout",
593 )
594 .into());
595 }
596 }
597 }
598
599 for dependency in unit.dependencies.iter() {
600 let dependency_symbol = symbol(&dependency.name)?;
601 graph.add_edge(name_symbol, dependency_symbol);
602 Self::graph_build(
603 home_path,
604 network,
605 endpoint,
606 main_program,
607 dependency.clone(),
608 map,
609 graph,
610 no_cache,
611 no_local,
612 network_retries,
613 declared_deps,
614 old_lock,
615 new_lock,
616 offline,
617 )?;
618 }
619
620 Ok(())
621 }
622}
623
624fn main_template(name: &str) -> String {
625 format!(
626 r#"// The '{name}' program.
627program {name}.aleo {{
628 // This is the constructor for the program.
629 // The constructor allows you to manage program upgrades.
630 // It is called when the program is deployed or upgraded.
631 // It is currently configured to **prevent** upgrades.
632 // Other configurations include:
633 // - @admin(address="aleo1...")
634 // - @checksum(mapping="credits.aleo/fixme", key="0field")
635 // - @custom
636 // For more information, please refer to the documentation: `https://docs.leo-lang.org/guides/upgradability`
637 @noupgrade
638 constructor() {{}}
639
640 fn main(public a: u32, b: u32) -> u32 {{
641 let c: u32 = a + b;
642 return c;
643 }}
644}}
645"#
646 )
647}
648
649fn test_template(name: &str) -> String {
650 format!(
651 r#"// The 'test_{name}' test program.
652import {name}.aleo;
653program test_{name}.aleo {{
654 @test
655 @should_fail
656 fn test_main_fails() {{
657 let result: u32 = {name}.aleo::main(2u32, 3u32);
658 assert_eq(result, 3u32);
659 }}
660
661 @noupgrade
662 constructor() {{}}
663}}
664"#
665 )
666}
667
668fn lib_template(name: &str) -> String {
669 format!(
670 r#"// The '{name}' library.
671
672// Returns the identity of x.
673fn example(x: u32) -> u32 {{
674 return x;
675}}
676"#
677 )
678}
679
680fn lib_test_template(name: &str) -> String {
681 format!(
682 r#"// The 'test_{name}' test program.
683program test_{name}.aleo {{
684 @test
685 fn test_example() {{
686 assert_eq({name}::example(42u32), 42u32);
687 }}
688
689 @noupgrade
690 constructor() {{}}
691}}
692"#
693 )
694}
695
696fn collect_declared_deps(
703 root_path: &Path,
704 manifest: &Manifest,
705 with_tests: bool,
706) -> Result<IndexMap<Symbol, Dependency>> {
707 let mut declared = IndexMap::new();
708 collect_declared_deps_recursive(root_path, manifest, with_tests, &mut declared)?;
709 Ok(declared)
710}
711
712fn collect_declared_deps_recursive(
713 base_path: &Path,
714 manifest: &Manifest,
715 include_dev: bool,
716 declared: &mut IndexMap<Symbol, Dependency>,
717) -> Result<()> {
718 let deps = manifest.dependencies.iter().flatten();
719 let dev: Vec<&Dependency> =
720 if include_dev { manifest.dev_dependencies.iter().flatten().collect() } else { Vec::new() };
721 for dep in deps.chain(dev) {
722 let dep = canonicalize_dependency_path_relative_to(base_path, dep.clone())?;
723 let dep = if dep.location == Location::Workspace { resolve_workspace_dependency(base_path, dep)? } else { dep };
725 let sym = symbol(&dep.name)?;
726 let Entry::Vacant(e) = declared.entry(sym) else {
730 continue;
731 };
732 e.insert(dep.clone());
733 if dep.location == Location::Local
734 && let Some(path) = &dep.path
735 {
736 let manifest_path = path.join(MANIFEST_FILENAME);
737 if path.is_dir() && manifest_path.exists() {
738 let child = Manifest::read_from_file(manifest_path)?;
739 collect_declared_deps_recursive(path, &child, false, declared)?;
741 }
742 }
743 }
744 Ok(())
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750
751 fn dummy_package(base: &str) -> Package {
752 dummy_package_with(base, None)
753 }
754
755 fn dummy_package_with(base: &str, workspace_root: Option<PathBuf>) -> Package {
756 Package {
757 base_directory: PathBuf::from(base),
758 workspace_root,
759 compilation_units: Vec::new(),
760 manifest: Manifest {
761 program: "demo.aleo".to_string(),
762 version: "0.1.0".to_string(),
763 description: String::new(),
764 license: "MIT".to_string(),
765 leo: "0.0.0".to_string(),
766 dependencies: None,
767 dev_dependencies: None,
768 no_std: false,
769 },
770 dep_graph: DiGraph::default(),
771 }
772 }
773
774 #[test]
775 fn bare_unit_name_strips_aleo_suffix() {
776 assert_eq!(crate::bare_unit_name("token.aleo"), "token");
777 assert_eq!(crate::bare_unit_name("token"), "token");
778 assert_eq!(crate::bare_unit_name("credits.aleo"), "credits");
779 }
780
781 #[test]
782 fn unit_paths_are_keyed_by_bare_name() {
783 let pkg = dummy_package("/tmp/demo");
784 assert_eq!(pkg.unit_build_directory("token.aleo"), PathBuf::from("/tmp/demo/build/token"));
787 assert_eq!(pkg.unit_build_directory("token"), PathBuf::from("/tmp/demo/build/token"));
788 assert_eq!(pkg.unit_bytecode_path("token.aleo"), PathBuf::from("/tmp/demo/build/token/token.aleo"));
789 assert_eq!(pkg.unit_abi_path("token"), PathBuf::from("/tmp/demo/build/token/abi.json"));
790 assert_eq!(pkg.unit_interfaces_directory("token"), PathBuf::from("/tmp/demo/build/token/interfaces"));
791 assert_eq!(pkg.unit_snapshots_directory("token"), PathBuf::from("/tmp/demo/build/token/snapshots"));
792 }
793
794 #[test]
795 fn libraries_are_keyed_like_programs() {
796 let pkg = dummy_package("/tmp/demo");
799 assert_eq!(pkg.unit_build_directory("my_lib"), PathBuf::from("/tmp/demo/build/my_lib"));
800 assert_eq!(pkg.unit_interfaces_directory("my_lib"), PathBuf::from("/tmp/demo/build/my_lib/interfaces"));
801 }
802
803 #[test]
804 fn build_directory_is_the_single_root() {
805 let pkg = dummy_package("/tmp/demo");
806 assert_eq!(pkg.build_directory(), PathBuf::from("/tmp/demo/build"));
807 assert!(pkg.unit_bytecode_path("x").starts_with(pkg.build_directory()));
809 assert!(pkg.unit_interfaces_directory("credits.aleo").starts_with(pkg.build_directory()));
810 }
811
812 #[test]
813 fn workspace_root_routes_build_directory_to_shared() {
814 let pkg = dummy_package_with("/tmp/ws/members/token", Some(PathBuf::from("/tmp/ws")));
819 assert_eq!(pkg.build_directory(), PathBuf::from("/tmp/ws/build"));
820 assert_eq!(pkg.unit_build_directory("token"), PathBuf::from("/tmp/ws/build/token"));
821 assert_eq!(pkg.unit_bytecode_path("token"), PathBuf::from("/tmp/ws/build/token/token.aleo"));
822 let dep = dummy_package_with("/tmp/ws/members/swap", Some(PathBuf::from("/tmp/ws")));
826 assert_eq!(pkg.unit_bytecode_path("token"), dep.unit_bytecode_path("token"));
827 }
828
829 #[test]
830 fn standalone_package_keeps_per_base_build_directory() {
831 let pkg = dummy_package_with("/tmp/standalone", None);
834 assert_eq!(pkg.build_directory(), PathBuf::from("/tmp/standalone/build"));
835 assert_eq!(pkg.unit_build_directory("demo"), PathBuf::from("/tmp/standalone/build/demo"));
836 }
837}