Skip to content

Commit 66379ef

Browse files
authored
Merge branch 'main' into polish-localization
2 parents cbe3e94 + 9aa9d90 commit 66379ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+451
-100
lines changed

Configuration/UTMAppleConfigurationDrive.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
4848
} else {
4949
sizeBytes = Int64(sizeMib) * Int64(bytesInMib)
5050
}
51-
return ByteCountFormatter.string(fromByteCount: sizeBytes, countStyle: .file)
51+
return ByteCountFormatter.string(fromByteCount: sizeBytes, countStyle: .binary)
5252
}
5353

5454
init(newSize: Int) {

Documentation/MacDevelopment.md

+28-12
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ Because UTM is a sand-boxed Mac app, there are a few extra steps needed for a pr
55
## Getting the Source
66

77
Make sure you perform a recursive clone to get all the submodules:
8-
```
8+
```sh
99
git clone --recursive https://github.com/utmapp/UTM.git
1010
```
1111

12-
Alternatively, run `git submodule update --init --recursive` after cloning if you did not do a recursive clone.
12+
Alternatively, run the following after cloning if you did not do a recursive clone.
13+
```sh
14+
git submodule update --init --recursive
15+
```
1316

1417
## Dependencies
1518

@@ -21,15 +24,28 @@ If you want to build the dependencies yourself, it is highly recommended that yo
2124

2225
1. Install Xcode command line and [Homebrew][1]
2326
2. Install the following build prerequisites
24-
`brew install bison pkg-config gettext glib-utils libgpg-error nasm meson`
25-
`pip3 install six pyparsing`
26-
Make sure to add `bison` to your `$PATH` environment variable!
27-
`export PATH=/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH`
28-
3. Run `./scripts/build_dependencies.sh -p macos -a ARCH` where `ARCH` is either `arm64` or `x86_64`.
27+
```sh
28+
brew install bison pkg-config gettext glib-utils libgpg-error nasm meson
29+
```
30+
31+
```sh
32+
pip3 install six pyparsing
33+
```
34+
35+
Make sure to add `bison` to your `$PATH` environment variable!
36+
37+
```sh
38+
export PATH=/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH
39+
```
40+
3. Run
41+
```sh
42+
./scripts/build_dependencies.sh -p macos -a ARCH
43+
```
44+
where `ARCH` is either `arm64` or `x86_64`.
2945

3046
If you want to build universal binaries, you need to run `build_dependencies.sh` for both `arm64` and `x86_64` and then run
3147

32-
```
48+
```sh
3349
./scripts/pack_dependencies.sh . macos arm64 x86_64
3450
```
3551

@@ -41,7 +57,7 @@ If you are developing QEMU and wish to pass in a custom path to QEMU, you can us
4157

4258
You can build UTM with the script:
4359

44-
```
60+
```sh
4561
./scripts/build_utm.sh -t TEAMID -p macos -a ARCH -o /path/to/output/directory
4662
```
4763

@@ -55,15 +71,15 @@ Artifacts built with `build_utm.sh` (includes GitHub Actions artifacts) must be
5571

5672
#### Unsigned packages
5773

58-
```
74+
```sh
5975
./scripts/package_mac.sh unsigned /path/to/UTM.xcarchive /path/to/output
6076
```
6177

6278
This builds `UTM.dmg` in `/path/to/output` which can be installed to `/Applications`.
6379

6480
#### Signed packages
6581

66-
```
82+
```sh
6783
./scripts/package_mac.sh developer-id /path/to/UTM.xcarchive /path/to/output TEAM_ID PROFILE_UUID HELPER_PROFILE_UUID LAUNCHER_PROFILE_UUID
6884
```
6985

@@ -73,7 +89,7 @@ Once properly signed, you can ask Apple to notarize the DMG.
7389

7490
#### Mac App Store
7591

76-
```
92+
```sh
7793
./scripts/package_mac.sh app-store /path/to/UTM.xcarchive /path/to/output TEAM_ID PROFILE_UUID HELPER_PROFILE_UUID LAUNCHER_PROFILE_UUID
7894
```
7995

Icons/backtrack.png

-127 KB
Loading

Managers/UTMAppleVirtualMachine.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import Virtualization
4848

4949
@MainActor override var detailsSystemMemoryLabel: String {
5050
let bytesInMib = Int64(1048576)
51-
return ByteCountFormatter.string(fromByteCount: Int64(appleConfig.system.memorySize) * bytesInMib, countStyle: .memory)
51+
return ByteCountFormatter.string(fromByteCount: Int64(appleConfig.system.memorySize) * bytesInMib, countStyle: .binary)
5252
}
5353

5454
override var hasSaveState: Bool {

Managers/UTMPendingVirtualMachine.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,13 @@ import Foundation
9797
lastDownloadSpeedUpdate = Date()
9898
let bytesPerSecond = bytesWrittenSinceLastDownloadSpeedUpdate
9999
bytesWrittenSinceLastDownloadSpeedUpdate = 0
100-
let bytesString = ByteCountFormatter.string(fromByteCount: bytesPerSecond, countStyle: .file)
100+
let bytesString = ByteCountFormatter.string(fromByteCount: bytesPerSecond, countStyle: .binary)
101101
let speedFormat = NSLocalizedString("%@/s",
102102
comment: "Format string for the 'per second' part of a download speed.")
103103
estimatedDownloadSpeed = String.localizedStringWithFormat(speedFormat, bytesString)
104104
/// sizes
105-
downloadedSize = ByteCountFormatter.string(fromByteCount: totalBytesWritten, countStyle: .file)
106-
estimatedDownloadSize = ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite, countStyle: .file)
105+
downloadedSize = ByteCountFormatter.string(fromByteCount: totalBytesWritten, countStyle: .binary)
106+
estimatedDownloadSize = ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite, countStyle: .binary)
107107
}
108108

109109
public func setDownloadProgress(new newBytesWritten: Int64, currentTotal totalBytesWritten: Int64, estimatedTotal totalBytesExpectedToWrite: Int64) {

Managers/UTMQemuVirtualMachine.h

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ NS_ASSUME_NONNULL_BEGIN
2828
/// This property is observable and must only be accessed on the main thread.
2929
@property (nonatomic) BOOL isGuestToolsInstallRequested;
3030

31+
/// Sends power off request to the guest
32+
- (void)requestGuestPowerDown;
33+
3134
@end
3235

3336
NS_ASSUME_NONNULL_END

Managers/UTMQemuVirtualMachine.m

+10
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,16 @@ - (void)vmResumeWithCompletion:(void (^)(NSError * _Nullable))completion {
567567
});
568568
}
569569

570+
- (void)requestGuestPowerDown {
571+
dispatch_async(self.vmOperations, ^{
572+
[self.qemu qemuPowerDownWithCompletion:^(NSError *err) {
573+
if (err) {
574+
UTMLog(@"Error requesting power down: %@", err.localizedDescription);
575+
}
576+
}];
577+
});
578+
}
579+
570580
#pragma mark - Qemu manager delegate
571581

572582
- (void)qemuHasWakeup:(UTMQemuManager *)manager {

Managers/UTMQemuVirtualMachine.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public extension UTMQemuVirtualMachine {
4444

4545
@MainActor override var detailsSystemMemoryLabel: String {
4646
let bytesInMib = Int64(1048576)
47-
return ByteCountFormatter.string(fromByteCount: Int64(qemuConfig.system.memorySize) * bytesInMib, countStyle: .memory)
47+
return ByteCountFormatter.string(fromByteCount: Int64(qemuConfig.system.memorySize) * bytesInMib, countStyle: .binary)
4848
}
4949

5050
/// Check if a QEMU target is supported

Managers/UTMVirtualMachine.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ NS_ASSUME_NONNULL_BEGIN
106106
/// `-saveUTMWithCompletion:` should be called to save to disk.
107107
/// @param configuration VM configuration
108108
/// @param packageURL Location of the VM
109-
+ (UTMVirtualMachine *)virtualMachineWithConfiguration:(UTMConfigurationWrapper *)configuration packageURL:(NSURL *)packageURL;
109+
+ (UTMVirtualMachine *)virtualMachineWithConfigurationWrapper:(UTMConfigurationWrapper *)configuration packageURL:(NSURL *)packageURL;
110110

111111
/// Discard any changes to configuration by reloading from disk
112112
/// @param err Error thrown

Managers/UTMVirtualMachine.m

+2-2
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ + (nullable UTMVirtualMachine *)virtualMachineWithURL:(NSURL *)url {
143143
UTMConfigurationWrapper *config = [[UTMConfigurationWrapper alloc] initFrom:url];
144144
[url stopAccessingSecurityScopedResource];
145145
if (config) {
146-
UTMVirtualMachine *vm = [UTMVirtualMachine virtualMachineWithConfiguration:config packageURL:url];
146+
UTMVirtualMachine *vm = [UTMVirtualMachine virtualMachineWithConfigurationWrapper:config packageURL:url];
147147
dispatch_async(dispatch_get_main_queue(), ^{
148148
[vm updateConfigFromRegistry];
149149
});
@@ -153,7 +153,7 @@ + (nullable UTMVirtualMachine *)virtualMachineWithURL:(NSURL *)url {
153153
}
154154
}
155155

156-
+ (UTMVirtualMachine *)virtualMachineWithConfiguration:(UTMConfigurationWrapper *)configuration packageURL:(nonnull NSURL *)packageURL {
156+
+ (UTMVirtualMachine *)virtualMachineWithConfigurationWrapper:(UTMConfigurationWrapper *)configuration packageURL:(nonnull NSURL *)packageURL {
157157
#if TARGET_OS_OSX
158158
if (@available(macOS 11, *)) {
159159
if (configuration.isAppleVirtualization) {

Managers/UTMVirtualMachine.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extension UTMVirtualMachine: ObservableObject {
5858
@nonobjc convenience init<Config: UTMConfiguration>(newConfig: Config, destinationURL: URL) {
5959
let packageURL = UTMVirtualMachine.virtualMachinePath(newConfig.information.name, inParentURL: destinationURL)
6060
let configuration = UTMConfigurationWrapper(wrapping: newConfig)
61-
self.init(configuration: configuration, packageURL: packageURL)
61+
self.init(configurationWrapper: configuration, packageURL: packageURL)
6262
}
6363
}
6464

Platform/Main.swift

+4-8
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ class Main {
3434
static var jitAvailable = true
3535

3636
static func main() {
37-
#if os(iOS)
38-
#if !WITH_QEMU_TCI
37+
#if os(iOS) && !WITH_QEMU_TCI
3938
// check if we have jailbreak
4039
if jb_spawn_ptrace_child(CommandLine.argc, CommandLine.unsafeArgv) {
4140
logger.info("JIT: ptrace() child spawn trick")
@@ -56,15 +55,12 @@ class Main {
5655
logger.info("MEM: successfully removed memory limits")
5756
}
5857
#endif
59-
// UIViewController patches
60-
UTMViewControllerPatches.patchAll()
58+
// do patches
59+
UTMPatches.patchAll()
60+
#if os(iOS)
6161
// register defaults
6262
registerDefaultsFromSettingsBundle()
6363
#endif
64-
#if os(macOS)
65-
// SwiftUI bug: works around crash due to "already had more Update Constraints in Window passes than there are views in the window" exception
66-
UserDefaults.standard.set(false, forKey: "NSWindowAssertWhenDisplayCycleLimitReached")
67-
#endif
6864
UTMApp.main()
6965
}
7066

Platform/Shared/DetailedSection.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ struct DetailedSection<Content>: View where Content: View {
4444

4545
struct DetailedSection_Previews: PreviewProvider {
4646
static var previews: some View {
47-
DetailedSection("Section", description: "Description") {
48-
EmptyView()
47+
Form {
48+
DetailedSection("Section", description: "Description") {
49+
EmptyView()
50+
}
4951
}
52+
.frame(width: 200)
5053
}
5154
}

Platform/Shared/SizeTextField.swift

+7-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ struct SizeTextField: View {
3838
.multilineTextAlignment(.trailing)
3939
.help("The amount of storage to allocate for this image. Ignored if importing an image. If this is a raw image, then an empty file of this size will be stored with the VM. Otherwise, the disk image will dynamically expand up to this size.")
4040
Button(action: { isGiB.toggle() }, label: {
41-
Text(isGiB ? "GB" : "MB")
42-
.foregroundColor(.blue)
41+
Group {
42+
if isGiB {
43+
Text("GB")
44+
} else {
45+
Text("MB")
46+
}
47+
}.foregroundColor(.blue)
4348
}).buttonStyle(.plain)
4449
}
4550
}

Platform/Shared/UTMDownloadTask.swift

+16-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
2525
private var taskContinuation: CheckedContinuation<UTMVirtualMachine?, Error>?
2626
@MainActor private(set) lazy var pendingVM: UTMPendingVirtualMachine = createPendingVM()
2727

28+
private let kMaxRetries = 5
29+
private var retries = 0
30+
2831
var fileManager: FileManager {
2932
FileManager.default
3033
}
@@ -82,6 +85,7 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
8285
sessionTask.cancel()
8386
return
8487
}
88+
retries = 0 // reset retry counter on success
8589
Task {
8690
await pendingVM.setDownloadProgress(new: bytesWritten,
8791
currentTotal: totalBytesWritten,
@@ -91,12 +95,22 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
9195

9296
/// when the session ends with an error, it could be cancelled or an actual error
9397
internal func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
98+
let error = error as? NSError
99+
if let resumeData = error?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
100+
retries += 1
101+
guard retries > kMaxRetries else {
102+
logger.warning("Retrying download due to connection error...")
103+
let task = session.downloadTask(withResumeData: resumeData)
104+
task.resume()
105+
return
106+
}
107+
}
94108
guard let taskContinuation = taskContinuation else {
95109
return
96110
}
97111
self.taskContinuation = nil
112+
self.retries = 0 // reset retry counter
98113
if let error = error {
99-
let error = error as NSError
100114
if error.code == NSURLErrorCancelled {
101115
/// download was cancelled normally
102116
taskContinuation.resume(returning: nil)
@@ -135,7 +149,7 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
135149
/// - Returns: Completed download or nil if canceled
136150
func download() async throws -> UTMVirtualMachine? {
137151
/// begin the download
138-
let session = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: nil)
152+
let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
139153
downloadTask = Task.detached { [self] in
140154
let sessionDownload = session.downloadTask(with: url)
141155
await pendingVM.setDownloadStarting()

Platform/Shared/VMConfigDisplayConsoleView.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ struct VMConfigDisplayConsoleView_Previews: PreviewProvider {
7171
@State static private var config = UTMConfigurationTerminal()
7272

7373
static var previews: some View {
74-
VMConfigDisplayConsoleView(config: $config)
74+
Form {
75+
VMConfigDisplayConsoleView(config: $config)
76+
}
77+
#if os(macOS)
78+
.scrollable()
79+
#endif
7580
}
7681
}

Platform/Shared/VMConfigDriveDetailsView.swift

+10-10
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ struct VMConfigDriveDetailsView: View {
3535
}
3636

3737
@Binding var config: UTMQemuConfigurationDrive
38-
let onDelete: (() -> Void)?
38+
@Binding var requestDriveDelete: UTMQemuConfigurationDrive?
3939

4040
@EnvironmentObject private var data: UTMData
4141
@State private var isImporterPresented: Bool = false
@@ -78,19 +78,19 @@ struct VMConfigDriveDetailsView: View {
7878
}
7979

8080
if let imageUrl = config.imageURL, let fileSize = data.computeSize(for: imageUrl) {
81-
DefaultTextField("Size", text: .constant(ByteCountFormatter.string(fromByteCount: fileSize, countStyle: .file))).disabled(true)
81+
DefaultTextField("Size", text: .constant(ByteCountFormatter.string(fromByteCount: fileSize, countStyle: .binary))).disabled(true)
8282
} else if config.sizeMib > 0 {
83-
DefaultTextField("Size", text: .constant(ByteCountFormatter.string(fromByteCount: Int64(config.sizeMib) * bytesInMib, countStyle: .file))).disabled(true)
83+
DefaultTextField("Size", text: .constant(ByteCountFormatter.string(fromByteCount: Int64(config.sizeMib) * bytesInMib, countStyle: .binary))).disabled(true)
8484
}
8585

8686
#if os(macOS)
8787
HStack {
88-
if let onDelete = onDelete {
89-
Button(action: onDelete) {
90-
Label("Delete Drive", systemImage: "externaldrive.badge.minus")
91-
.foregroundColor(.red)
92-
}.help("Delete this drive.")
93-
}
88+
Button {
89+
requestDriveDelete = config
90+
} label: {
91+
Label("Delete Drive", systemImage: "externaldrive.badge.minus")
92+
.foregroundColor(.red)
93+
}.help("Delete this drive.")
9494

9595
if let imageUrl = config.imageURL, FileManager.default.fileExists(atPath: imageUrl.path) {
9696
Button {
@@ -160,7 +160,7 @@ private struct ResizePopoverView: View {
160160

161161
private var sizeString: String? {
162162
if let currentSize = currentSize {
163-
return ByteCountFormatter.string(fromByteCount: currentSize, countStyle: .file)
163+
return ByteCountFormatter.string(fromByteCount: currentSize, countStyle: .binary)
164164
} else {
165165
return nil
166166
}

Platform/Shared/VMConfigInfoView.swift

+3
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ struct VMConfigInfoView_Previews: PreviewProvider {
267267
static var previews: some View {
268268
Group {
269269
VMConfigInfoView(config: $config)
270+
#if os(macOS)
271+
.scrollable()
272+
#endif
270273
IconSelect() { _ in
271274

272275
}

Platform/Shared/VMConfigInputView.swift

+3
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,8 @@ struct VMConfigInputView_Previews: PreviewProvider {
100100

101101
static var previews: some View {
102102
VMConfigInputView(config: $config)
103+
#if os(macOS)
104+
.scrollable()
105+
#endif
103106
}
104107
}

Platform/Shared/VMConfigQEMUView.swift

+1
Original file line numberDiff line numberDiff line change
@@ -210,5 +210,6 @@ struct VMConfigQEMUView_Previews: PreviewProvider {
210210

211211
static var previews: some View {
212212
VMConfigQEMUView(config: $config, system: $system, fetchFixedArguments: { [] })
213+
.frame(minHeight: 500)
213214
}
214215
}

Platform/Shared/VMConfigSerialView.swift

+3
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,8 @@ struct VMConfigSerialView_Previews: PreviewProvider {
9999

100100
static var previews: some View {
101101
VMConfigSerialView(config: $config, system: $system)
102+
#if os(macOS)
103+
.scrollable()
104+
#endif
102105
}
103106
}

0 commit comments

Comments
 (0)