Skip to content

Commit 8e6a522

Browse files
committed
ios-support (although serial devices are not supported by apple ios devices because of sandboxing)
1 parent 5e1b1d1 commit 8e6a522

File tree

4 files changed

+243
-2
lines changed

4 files changed

+243
-2
lines changed

Cargo.lock

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+12-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ homepage = "https://github.com/hacknus/serial-monitor-rust"
99

1010
[dependencies]
1111
csv = "1.3"
12-
eframe = { version = "0.31", features = ["persistence", "wayland", "x11"] }
1312
egui_plot = "0.31"
1413
egui_extras = { version = "0.31", features = ["all_loaders"] }
1514
egui-phosphor = { version = "0.9" }
1615
egui-theme-switch = { git = "https://github.com/hacknus/egui-theme-switch" }
1716
egui_logger = { git = "https://github.com/hacknus/egui_logger" }
1817
egui-file-dialog = { git = "https://github.com/hacknus/egui-file-dialog", branch = "sort_by_metadata", features = ["information_view"] }
1918
image = { version = "0.25", default-features = false, features = ["png"] }
20-
keepawake = { version = "0.5.1" }
2119
preferences = { version = "2.0.0" }
2220
regex = "1"
2321
serde = { version = "1.0", features = ["derive"] }
@@ -29,6 +27,18 @@ reqwest = { version = "0.12", default-features = false, features = ["blocking",
2927
semver = { version = "1.0.24", optional = true }
3028
crossbeam-channel = "0.5.14"
3129

30+
[target.'cfg(not(target_os = "ios"))'.dependencies]
31+
eframe = { version = "0.31", features = ["persistence", "wayland", "x11"] }
32+
keepawake = { version = "0.5.1" }
33+
# ios:
34+
[target.'cfg(target_os = "ios")'.dependencies]
35+
eframe = { version = "0.31", default-features = false, features = [
36+
"accesskit",
37+
"default_fonts",
38+
"wgpu", # Use the wgpu rendering backend on iOS.
39+
"persistence",
40+
] }
41+
3242
[features]
3343
self_update = ["dep:self_update", "tempfile", "reqwest", "semver"]
3444

ios-cargo

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import subprocess
4+
import tomllib
5+
import os
6+
import shutil
7+
import zipfile
8+
import tempfile
9+
import json
10+
11+
def parse_cargo_toml():
12+
with open('Cargo.toml', 'rb') as f:
13+
cargo_toml = tomllib.load(f)
14+
app_name = cargo_toml['package']['metadata']['bundle']['name']
15+
app_id = cargo_toml['package']['metadata']['bundle']['identifier']
16+
return app_name, app_id
17+
18+
def ipa(args):
19+
print("Releasing the build...")
20+
args.release = True
21+
build(args)
22+
23+
app_name, _ = parse_cargo_toml()
24+
build_type = 'release'
25+
target = get_target(args)
26+
cargo_target_dir = os.getenv('CARGO_TARGET_DIR')
27+
28+
if cargo_target_dir:
29+
base_target_dir = cargo_target_dir
30+
else:
31+
base_target_dir = 'target'
32+
app_path = os.path.join(base_target_dir, target, build_type, 'bundle', 'ios', f'{app_name}.app')
33+
temp_dir = tempfile.mkdtemp()
34+
35+
payload_dir = os.path.join(temp_dir, "Payload")
36+
os.makedirs(payload_dir)
37+
shutil.copytree(app_path, os.path.join(payload_dir, f'{app_name}.app'))
38+
39+
ipa_path = f'{app_name}.ipa'
40+
with zipfile.ZipFile(ipa_path, 'w', zipfile.ZIP_DEFLATED) as ipa_file:
41+
for root, dirs, files in os.walk(payload_dir):
42+
for file in files:
43+
file_path = os.path.join(root, file)
44+
ipa_file.write(file_path, os.path.relpath(file_path, os.path.dirname(payload_dir)))
45+
46+
shutil.rmtree(payload_dir)
47+
print(f"Created {ipa_path}")
48+
49+
def post_process_info_plist(plist_path, ipad):
50+
try:
51+
with open('insert.plist', 'r') as insert_file:
52+
insert_content = insert_file.read()
53+
except:
54+
insert_content = ""
55+
with open(plist_path, 'r') as plist_file:
56+
plist_content = plist_file.read()
57+
58+
if ipad:
59+
ipad_content = """
60+
<key>UIDeviceFamily</key>
61+
<array>
62+
<integer>1</integer> <!-- iPhone -->
63+
<integer>2</integer> <!-- iPad -->
64+
</array>
65+
<key>UISupportedInterfaceOrientations</key>
66+
<array>
67+
<string>UIInterfaceOrientationPortrait</string>
68+
<string>UIInterfaceOrientationLandscapeLeft</string>
69+
<string>UIInterfaceOrientationLandscapeRight</string>
70+
</array>
71+
<key>UISupportedInterfaceOrientations~ipad</key>
72+
<array>
73+
<string>UIInterfaceOrientationPortrait</string>
74+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
75+
<string>UIInterfaceOrientationLandscapeLeft</string>
76+
<string>UIInterfaceOrientationLandscapeRight</string>
77+
</array>
78+
"""
79+
insert_content += ipad_content
80+
81+
modified_content = plist_content.replace('</dict>', f'{insert_content}\n</dict>')
82+
83+
with open(plist_path, 'w') as plist_file:
84+
plist_file.write(modified_content)
85+
86+
def get_target(args):
87+
target = 'aarch64-apple-ios'
88+
if args.x86:
89+
target = 'x86_64-apple-ios'
90+
elif args.sim:
91+
target = 'aarch64-apple-ios-sim'
92+
if args.target:
93+
target = args.target
94+
return target
95+
96+
def build(args):
97+
target = get_target(args)
98+
command = ['cargo', 'bundle', '--target', target]
99+
if args.release:
100+
command.append('--release')
101+
102+
print(f"Running command: {' '.join(command)}")
103+
subprocess.run(command, check=True)
104+
app_name, _ = parse_cargo_toml()
105+
build_type = 'release' if args.release else 'debug'
106+
cargo_target_dir = os.getenv('CARGO_TARGET_DIR')
107+
if cargo_target_dir:
108+
base_target_dir = cargo_target_dir
109+
else:
110+
base_target_dir = 'target'
111+
plist_path = os.path.join(base_target_dir, target, build_type, 'bundle', 'ios', f'{app_name}.app', 'Info.plist')
112+
post_process_info_plist(plist_path, args.ipad)
113+
114+
def get_booted_device():
115+
result = subprocess.run(['xcrun', 'simctl', 'list', 'devices', '--json'], capture_output=True, text=True, check=True)
116+
devices = json.loads(result.stdout)
117+
for runtime in devices['devices']:
118+
for dev in devices['devices'][runtime]:
119+
if dev['state'] == 'Booted':
120+
return dev['udid']
121+
return None
122+
123+
def boot_device(device):
124+
print(f"Booting device {device}...")
125+
subprocess.run(['xcrun', 'simctl', 'boot', device], check=True)
126+
print(f"Device {device} booted.")
127+
128+
def get_newest_iphone_udid():
129+
result = subprocess.run(['xcrun', 'simctl', 'list', 'devices', '--json'], capture_output=True, text=True, check=True)
130+
devices = json.loads(result.stdout)
131+
132+
for runtime in devices['devices']:
133+
for dev in devices['devices'][runtime]:
134+
if 'iPhone' in dev['name'] and dev['isAvailable'] and 'SE' not in dev['name']:
135+
return dev['udid']
136+
137+
raise Exception("No available iPhone simulators found")
138+
139+
def run_build(args):
140+
app_name, app_id = parse_cargo_toml()
141+
build_type = 'release' if args.release else 'debug'
142+
target = get_target(args)
143+
144+
cargo_target_dir = os.getenv('CARGO_TARGET_DIR')
145+
if cargo_target_dir:
146+
base_target_dir = cargo_target_dir
147+
else:
148+
base_target_dir = 'target'
149+
app_path = os.path.join(base_target_dir, target, build_type, 'bundle', 'ios', f'{app_name}.app')
150+
151+
if args.device == "booted":
152+
if not get_booted_device():
153+
specific_device_udid = get_newest_iphone_udid()
154+
boot_device(specific_device_udid)
155+
args.device = specific_device_udid
156+
157+
install_command = [
158+
'xcrun', 'simctl', 'install', args.device, app_path
159+
]
160+
161+
launch_command = ['xcrun', 'simctl', 'launch', '--console', args.device, app_id]
162+
163+
print(f"Running command: {' '.join(install_command)}")
164+
subprocess.run(install_command, check=True)
165+
166+
print(f"Running command: {' '.join(launch_command)}")
167+
subprocess.run(launch_command, check=True)
168+
169+
def run(args):
170+
print("Running the build process...")
171+
build(args)
172+
print("Running the build...")
173+
run_build(args)
174+
175+
def main():
176+
parser = argparse.ArgumentParser(description='A script with build, run, run-build, and release subcommands.')
177+
subparsers = parser.add_subparsers(dest='command', required=True)
178+
179+
build_parser = subparsers.add_parser('build', help='Build the project')
180+
build_parser.add_argument('--x86', action='store_true', help='Use x86 target')
181+
build_parser.add_argument('--sim', action='store_true', help='Use simulator target')
182+
build_parser.add_argument('--target', type=str, help='Specify custom target')
183+
build_parser.add_argument('--release', '-r', action='store_true', help='Build for release')
184+
build_parser.add_argument('--ipad', action='store_true', help='Include iPad-specific Info.plist entries')
185+
build_parser.set_defaults(func=build)
186+
187+
run_parser = subparsers.add_parser('run', help='Build and run the project')
188+
run_parser.add_argument('--x86', action='store_true', help='Use x86 target')
189+
run_parser.add_argument('--sim', action='store_true', help='Use simulator target')
190+
run_parser.add_argument('--target', type=str, help='Specify custom target')
191+
run_parser.add_argument('--release', '-r', action='store_true', help='Build for release')
192+
run_parser.add_argument('--ipad', action='store_true', help='Include iPad-specific Info.plist entries')
193+
run_parser.add_argument('--device', type=str, default='booted', help='Specify the target device')
194+
195+
run_parser.set_defaults(func=run)
196+
197+
run_build_parser = subparsers.add_parser('run-build', help='Runs already built project')
198+
run_build_parser.add_argument('--x86', action='store_true', help='Use x86 target')
199+
run_build_parser.add_argument('--sim', action='store_true', help='Use simulator target')
200+
run_build_parser.add_argument('--target', type=str, help='Specify custom target')
201+
run_build_parser.add_argument('--release', '-r', action='store_true', help='Build for release')
202+
run_build_parser.add_argument('--device', type=str, default='booted', help='Specify the target device')
203+
run_build_parser.add_argument('--ipad', action='store_true', help='Include iPad-specific Info.plist entries')
204+
205+
run_build_parser.set_defaults(func=run_build)
206+
207+
release_parser = subparsers.add_parser('ipa', help='Creates a ipa')
208+
release_parser.add_argument('--x86', action='store_true', help='Use x86 target')
209+
release_parser.add_argument('--sim', action='store_true', help='Use simulator target')
210+
release_parser.add_argument('--target', type=str, help='Specify custom target')
211+
release_parser.add_argument('--release', '-r', action='store_true', help='Build for release')
212+
release_parser.add_argument('--ipad', action='store_true', help='Include iPad-specific Info.plist entries')
213+
release_parser.set_defaults(func=ipa)
214+
215+
args = parser.parse_args()
216+
args.func(args)
217+
218+
if __name__ == '__main__':
219+
main()

src/serial.rs

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub fn serial_thread(
114114
let mut last_connected_device = Device::default();
115115

116116
loop {
117+
#[cfg(not(target_os = "ios"))]
117118
let _not_awake = keepawake::Builder::default()
118119
.display(false)
119120
.reason("Serial Connection")
@@ -155,6 +156,7 @@ pub fn serial_thread(
155156

156157
let t_zero = Instant::now();
157158

159+
#[cfg(not(target_os = "ios"))]
158160
let _awake = keepawake::Builder::default()
159161
.display(true)
160162
.reason("Serial Connection")

0 commit comments

Comments
 (0)