build.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. use std::{
  2. collections::HashMap,
  3. fs::File,
  4. io::{self, BufWriter, Write},
  5. path::PathBuf,
  6. };
  7. use failure::Fail;
  8. use rbx_dom_weak::RbxInstanceProperties;
  9. use crate::{
  10. imfs::new::{FsError, Imfs, RealFetcher, WatchMode},
  11. snapshot::{apply_patch_set, compute_patch_set, InstancePropertiesWithMeta, RojoTree},
  12. snapshot_middleware::snapshot_from_imfs,
  13. };
  14. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  15. pub enum OutputKind {
  16. Rbxmx,
  17. Rbxlx,
  18. Rbxm,
  19. Rbxl,
  20. }
  21. fn detect_output_kind(options: &BuildOptions) -> Option<OutputKind> {
  22. let extension = options.output_file.extension()?.to_str()?;
  23. match extension {
  24. "rbxlx" => Some(OutputKind::Rbxlx),
  25. "rbxmx" => Some(OutputKind::Rbxmx),
  26. "rbxl" => Some(OutputKind::Rbxl),
  27. "rbxm" => Some(OutputKind::Rbxm),
  28. _ => None,
  29. }
  30. }
  31. #[derive(Debug)]
  32. pub struct BuildOptions {
  33. pub fuzzy_project_path: PathBuf,
  34. pub output_file: PathBuf,
  35. pub output_kind: Option<OutputKind>,
  36. }
  37. #[derive(Debug, Fail)]
  38. pub enum BuildError {
  39. #[fail(display = "Could not detect what kind of file to create")]
  40. UnknownOutputKind,
  41. #[fail(display = "IO error: {}", _0)]
  42. IoError(#[fail(cause)] io::Error),
  43. #[fail(display = "XML model file error")]
  44. XmlModelEncodeError(rbx_xml::EncodeError),
  45. #[fail(display = "Binary model file error")]
  46. BinaryModelEncodeError(rbx_binary::EncodeError),
  47. #[fail(display = "{}", _0)]
  48. FsError(#[fail(cause)] FsError),
  49. }
  50. impl_from!(BuildError {
  51. io::Error => IoError,
  52. rbx_xml::EncodeError => XmlModelEncodeError,
  53. rbx_binary::EncodeError => BinaryModelEncodeError,
  54. FsError => FsError,
  55. });
  56. fn xml_encode_config() -> rbx_xml::EncodeOptions {
  57. rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
  58. }
  59. pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
  60. let output_kind = options
  61. .output_kind
  62. .or_else(|| detect_output_kind(options))
  63. .ok_or(BuildError::UnknownOutputKind)?;
  64. log::info!("Hoping to generate file of type {:?}", output_kind);
  65. let mut tree = RojoTree::new(InstancePropertiesWithMeta {
  66. properties: RbxInstanceProperties {
  67. name: "ROOT".to_owned(),
  68. class_name: "Folder".to_owned(),
  69. properties: HashMap::new(),
  70. },
  71. metadata: Default::default(),
  72. });
  73. let root_id = tree.get_root_id();
  74. log::trace!("Constructing in-memory filesystem");
  75. let mut imfs = Imfs::new(RealFetcher::new(WatchMode::Disabled));
  76. log::trace!("Reading project root");
  77. let entry = imfs
  78. .get(&options.fuzzy_project_path)
  79. .expect("could not get project path");
  80. log::trace!("Generating snapshot of instances from IMFS");
  81. let snapshot = snapshot_from_imfs(&mut imfs, &entry)
  82. .expect("snapshot failed")
  83. .expect("snapshot did not return an instance");
  84. log::trace!("Computing patch set");
  85. let patch_set = compute_patch_set(&snapshot, &tree, root_id);
  86. log::trace!("Applying patch set");
  87. apply_patch_set(&mut tree, &patch_set);
  88. log::trace!("Opening output file for write");
  89. let mut file = BufWriter::new(File::create(&options.output_file)?);
  90. match output_kind {
  91. OutputKind::Rbxmx => {
  92. // Model files include the root instance of the tree and all its
  93. // descendants.
  94. rbx_xml::to_writer(&mut file, tree.inner(), &[root_id], xml_encode_config())?;
  95. }
  96. OutputKind::Rbxlx => {
  97. // Place files don't contain an entry for the DataModel, but our
  98. // RbxTree representation does.
  99. let root_instance = tree.get_instance(root_id).unwrap();
  100. let top_level_ids = root_instance.children();
  101. rbx_xml::to_writer(&mut file, tree.inner(), top_level_ids, xml_encode_config())?;
  102. }
  103. OutputKind::Rbxm => {
  104. rbx_binary::encode(tree.inner(), &[root_id], &mut file)?;
  105. }
  106. OutputKind::Rbxl => {
  107. log::warn!("Support for building binary places (rbxl) is still experimental.");
  108. log::warn!("Using the XML place format (rbxlx) is recommended instead.");
  109. log::warn!("For more info, see https://github.com/LPGhatguy/rojo/issues/180");
  110. let root_instance = tree.get_instance(root_id).unwrap();
  111. let top_level_ids = root_instance.children();
  112. rbx_binary::encode(tree.inner(), top_level_ids, &mut file)?;
  113. }
  114. }
  115. file.flush()?;
  116. log::trace!("Done!");
  117. Ok(())
  118. }