Skip to content

Commit ec50619

Browse files
committed
App Packing
1 parent 9f80637 commit ec50619

File tree

10 files changed

+306
-32
lines changed

10 files changed

+306
-32
lines changed

src-tauri/Cargo.lock

Lines changed: 63 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ tar = "0.4.44"
3737
reqwest = "0.12.20"
3838
flate2 = "1.1.2"
3939
regex = "1"
40+
toml = "0.9.2"
4041

4142
[features]
4243
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!

src-tauri/src/builder/config.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use std::{path::PathBuf, process::Command};
2+
3+
use serde::{Deserialize, Serialize};
4+
5+
use crate::builder::swift::swift_bin;
6+
7+
pub const FORMAT_VERSION: u32 = 1;
8+
9+
pub struct BuildSettings {
10+
pub debug: bool,
11+
}
12+
13+
// TODO: Min ios version, etc.
14+
pub struct ProjectConfig {
15+
pub product: String,
16+
pub version_num: String,
17+
pub version_string: String,
18+
pub bundle_id: String,
19+
pub project_path: PathBuf,
20+
}
21+
22+
#[derive(Deserialize, Serialize)]
23+
struct TomlConfig {
24+
pub format_version: u32,
25+
pub project: ProjectTomlConfig,
26+
}
27+
28+
#[derive(Deserialize, Serialize)]
29+
struct ProjectTomlConfig {
30+
pub version_num: String,
31+
pub version_string: String,
32+
pub bundle_id: String,
33+
}
34+
35+
// TODO: Check platforms
36+
#[derive(Deserialize)]
37+
struct SwiftPackageDump {
38+
name: String,
39+
targets: Vec<SwiftPackageTarget>,
40+
}
41+
42+
// TODO: Resources
43+
#[derive(Deserialize)]
44+
struct SwiftPackageTarget {
45+
name: String,
46+
}
47+
48+
impl ProjectConfig {
49+
pub fn load(project_path: PathBuf, toolchain_path: &str) -> Result<Self, String> {
50+
let toml_config = TomlConfig::load_or_default(project_path.clone())?;
51+
let swift = swift_bin(toolchain_path)?;
52+
let raw_package = Command::new(swift)
53+
.arg("package")
54+
.arg("dump-package")
55+
.current_dir(&project_path)
56+
.output()
57+
.map_err(|e| format!("Failed to execute swift command: {}", e))?;
58+
if !raw_package.status.success() {
59+
return Err(format!(
60+
"Failed to dump package: {}",
61+
String::from_utf8_lossy(&raw_package.stderr)
62+
));
63+
}
64+
let package: SwiftPackageDump = serde_json::from_slice(&raw_package.stdout)
65+
.map_err(|e| format!("Failed to parse package dump: {}", e))?;
66+
67+
Ok(ProjectConfig {
68+
product: package.name,
69+
version_num: toml_config.project.version_num,
70+
version_string: toml_config.project.version_string,
71+
bundle_id: toml_config.project.bundle_id,
72+
project_path,
73+
})
74+
}
75+
}
76+
77+
impl TomlConfig {
78+
pub fn default(bundle_id: &str) -> Self {
79+
TomlConfig {
80+
format_version: FORMAT_VERSION,
81+
project: ProjectTomlConfig {
82+
version_num: "1".to_string(),
83+
version_string: "1.0.0".to_string(),
84+
bundle_id: bundle_id.to_string(),
85+
},
86+
}
87+
}
88+
89+
pub fn load_or_default(project_path: PathBuf) -> Result<Self, String> {
90+
if project_path.exists() {
91+
Self::load(project_path)
92+
} else {
93+
let config = Self::default("com.example.myapp");
94+
config.save(project_path)?;
95+
Ok(config)
96+
}
97+
}
98+
99+
fn load(project_path: PathBuf) -> Result<Self, String> {
100+
let content =
101+
std::fs::read_to_string(project_path.join("ycode.toml")).map_err(|e| e.to_string())?;
102+
let config: TomlConfig = toml::from_str(&content).map_err(|e| e.to_string())?;
103+
if config.format_version != FORMAT_VERSION {
104+
return Err(format!(
105+
"Unsupported format version: {}, expected: {}",
106+
config.format_version, FORMAT_VERSION
107+
));
108+
}
109+
Ok(config)
110+
}
111+
112+
pub fn save(&self, project_path: PathBuf) -> Result<(), String> {
113+
let content = toml::to_string(self).map_err(|e| e.to_string())?;
114+
std::fs::write(project_path.join("ycode.toml"), content).map_err(|e| e.to_string())?;
115+
Ok(())
116+
}
117+
}

src-tauri/src/builder/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
pub mod config;
2+
pub mod packer;
13
pub mod sdk;
24
pub mod swift;

src-tauri/src/builder/packer.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use std::{fs, path::PathBuf};
2+
3+
use dircpy::CopyBuilder;
4+
5+
use crate::builder::config::{BuildSettings, ProjectConfig};
6+
7+
pub fn pack(
8+
project_path: PathBuf,
9+
config: &ProjectConfig,
10+
build_settings: &BuildSettings,
11+
) -> Result<(), String> {
12+
let workdir = project_path.join(".ycode");
13+
if !workdir.exists() {
14+
std::fs::create_dir_all(&workdir)
15+
.map_err(|e| format!("Failed to create work directory: {}", e))?;
16+
}
17+
let app_path = workdir.join(format!("{}.app", config.product));
18+
if app_path.exists() {
19+
std::fs::remove_dir_all(&app_path)
20+
.map_err(|e| format!("Failed to remove existing app directory: {}", e))?;
21+
}
22+
std::fs::create_dir_all(&app_path)
23+
.map_err(|e| format!("Failed to create app directory: {}", e))?;
24+
25+
let exec = project_path
26+
.join(".build")
27+
.join("arm64-apple-ios")
28+
.join(if build_settings.debug {
29+
"debug"
30+
} else {
31+
"release"
32+
})
33+
.join(&config.product);
34+
35+
if !exec.exists() {
36+
return Err(format!("Executable not found at: {}", exec.display()));
37+
}
38+
39+
fs::copy(exec, app_path.join(&config.product))
40+
.map_err(|e| format!("Failed to copy executable: {}", e))?;
41+
42+
// TODO: Create default Info.plist if it doesn't exist
43+
let info_plist = project_path.join("Info.plist");
44+
if !info_plist.exists() {
45+
return Err(format!("Info.plist not found at: {}", info_plist.display()));
46+
}
47+
48+
let info_content = fs::read_to_string(&info_plist)
49+
.map_err(|e| format!("Failed to read Info.plist: {}", e))?
50+
.replace("[[bundle_id]]", &config.bundle_id)
51+
.replace("[[product]]", &config.product)
52+
.replace("[[version_num]]", &config.version_num)
53+
.replace("[[version_string]]", &config.version_string);
54+
fs::write(&app_path.join("Info.plist"), info_content)
55+
.map_err(|e| format!("Failed to write Info.plist: {}", e))?;
56+
57+
let resources = project_path.join("Resources");
58+
59+
if !resources.exists() {
60+
std::fs::create_dir_all(&resources)
61+
.map_err(|e| format!("Failed to create Resources directory: {}", e))?;
62+
}
63+
64+
CopyBuilder::new(&resources, &app_path)
65+
.run()
66+
.map_err(|e| format!("Failed to copy resources: {}", e))?;
67+
68+
Ok(())
69+
}

0 commit comments

Comments
 (0)