Skip to content

Commit

Permalink
Merge branch 'main' into polish-localization
Browse files Browse the repository at this point in the history
  • Loading branch information
osy authored Dec 4, 2022
2 parents cbe3e94 + 9aa9d90 commit 66379ef
Show file tree
Hide file tree
Showing 45 changed files with 451 additions and 100 deletions.
2 changes: 1 addition & 1 deletion Configuration/UTMAppleConfigurationDrive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
} else {
sizeBytes = Int64(sizeMib) * Int64(bytesInMib)
}
return ByteCountFormatter.string(fromByteCount: sizeBytes, countStyle: .file)
return ByteCountFormatter.string(fromByteCount: sizeBytes, countStyle: .binary)
}

init(newSize: Int) {
Expand Down
40 changes: 28 additions & 12 deletions Documentation/MacDevelopment.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ Because UTM is a sand-boxed Mac app, there are a few extra steps needed for a pr
## Getting the Source

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

Alternatively, run `git submodule update --init --recursive` after cloning if you did not do a recursive clone.
Alternatively, run the following after cloning if you did not do a recursive clone.
```sh
git submodule update --init --recursive
```

## Dependencies

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

1. Install Xcode command line and [Homebrew][1]
2. Install the following build prerequisites
`brew install bison pkg-config gettext glib-utils libgpg-error nasm meson`
`pip3 install six pyparsing`
Make sure to add `bison` to your `$PATH` environment variable!
`export PATH=/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH`
3. Run `./scripts/build_dependencies.sh -p macos -a ARCH` where `ARCH` is either `arm64` or `x86_64`.
```sh
brew install bison pkg-config gettext glib-utils libgpg-error nasm meson
```

```sh
pip3 install six pyparsing
```

Make sure to add `bison` to your `$PATH` environment variable!

```sh
export PATH=/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH
```
3. Run
```sh
./scripts/build_dependencies.sh -p macos -a ARCH
```
where `ARCH` is either `arm64` or `x86_64`.

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

```
```sh
./scripts/pack_dependencies.sh . macos arm64 x86_64
```

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

You can build UTM with the script:

```
```sh
./scripts/build_utm.sh -t TEAMID -p macos -a ARCH -o /path/to/output/directory
```

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

#### Unsigned packages

```
```sh
./scripts/package_mac.sh unsigned /path/to/UTM.xcarchive /path/to/output
```

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

#### Signed packages

```
```sh
./scripts/package_mac.sh developer-id /path/to/UTM.xcarchive /path/to/output TEAM_ID PROFILE_UUID HELPER_PROFILE_UUID LAUNCHER_PROFILE_UUID
```

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

#### Mac App Store

```
```sh
./scripts/package_mac.sh app-store /path/to/UTM.xcarchive /path/to/output TEAM_ID PROFILE_UUID HELPER_PROFILE_UUID LAUNCHER_PROFILE_UUID
```

Expand Down
Binary file modified Icons/backtrack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Managers/UTMAppleVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import Virtualization

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

override var hasSaveState: Bool {
Expand Down
6 changes: 3 additions & 3 deletions Managers/UTMPendingVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ import Foundation
lastDownloadSpeedUpdate = Date()
let bytesPerSecond = bytesWrittenSinceLastDownloadSpeedUpdate
bytesWrittenSinceLastDownloadSpeedUpdate = 0
let bytesString = ByteCountFormatter.string(fromByteCount: bytesPerSecond, countStyle: .file)
let bytesString = ByteCountFormatter.string(fromByteCount: bytesPerSecond, countStyle: .binary)
let speedFormat = NSLocalizedString("%@/s",
comment: "Format string for the 'per second' part of a download speed.")
estimatedDownloadSpeed = String.localizedStringWithFormat(speedFormat, bytesString)
/// sizes
downloadedSize = ByteCountFormatter.string(fromByteCount: totalBytesWritten, countStyle: .file)
estimatedDownloadSize = ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite, countStyle: .file)
downloadedSize = ByteCountFormatter.string(fromByteCount: totalBytesWritten, countStyle: .binary)
estimatedDownloadSize = ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite, countStyle: .binary)
}

public func setDownloadProgress(new newBytesWritten: Int64, currentTotal totalBytesWritten: Int64, estimatedTotal totalBytesExpectedToWrite: Int64) {
Expand Down
3 changes: 3 additions & 0 deletions Managers/UTMQemuVirtualMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ NS_ASSUME_NONNULL_BEGIN
/// This property is observable and must only be accessed on the main thread.
@property (nonatomic) BOOL isGuestToolsInstallRequested;

/// Sends power off request to the guest
- (void)requestGuestPowerDown;

@end

NS_ASSUME_NONNULL_END
10 changes: 10 additions & 0 deletions Managers/UTMQemuVirtualMachine.m
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,16 @@ - (void)vmResumeWithCompletion:(void (^)(NSError * _Nullable))completion {
});
}

- (void)requestGuestPowerDown {
dispatch_async(self.vmOperations, ^{
[self.qemu qemuPowerDownWithCompletion:^(NSError *err) {
if (err) {
UTMLog(@"Error requesting power down: %@", err.localizedDescription);
}
}];
});
}

#pragma mark - Qemu manager delegate

- (void)qemuHasWakeup:(UTMQemuManager *)manager {
Expand Down
2 changes: 1 addition & 1 deletion Managers/UTMQemuVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public extension UTMQemuVirtualMachine {

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

/// Check if a QEMU target is supported
Expand Down
2 changes: 1 addition & 1 deletion Managers/UTMVirtualMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ NS_ASSUME_NONNULL_BEGIN
/// `-saveUTMWithCompletion:` should be called to save to disk.
/// @param configuration VM configuration
/// @param packageURL Location of the VM
+ (UTMVirtualMachine *)virtualMachineWithConfiguration:(UTMConfigurationWrapper *)configuration packageURL:(NSURL *)packageURL;
+ (UTMVirtualMachine *)virtualMachineWithConfigurationWrapper:(UTMConfigurationWrapper *)configuration packageURL:(NSURL *)packageURL;

/// Discard any changes to configuration by reloading from disk
/// @param err Error thrown
Expand Down
4 changes: 2 additions & 2 deletions Managers/UTMVirtualMachine.m
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ + (nullable UTMVirtualMachine *)virtualMachineWithURL:(NSURL *)url {
UTMConfigurationWrapper *config = [[UTMConfigurationWrapper alloc] initFrom:url];
[url stopAccessingSecurityScopedResource];
if (config) {
UTMVirtualMachine *vm = [UTMVirtualMachine virtualMachineWithConfiguration:config packageURL:url];
UTMVirtualMachine *vm = [UTMVirtualMachine virtualMachineWithConfigurationWrapper:config packageURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
[vm updateConfigFromRegistry];
});
Expand All @@ -153,7 +153,7 @@ + (nullable UTMVirtualMachine *)virtualMachineWithURL:(NSURL *)url {
}
}

+ (UTMVirtualMachine *)virtualMachineWithConfiguration:(UTMConfigurationWrapper *)configuration packageURL:(nonnull NSURL *)packageURL {
+ (UTMVirtualMachine *)virtualMachineWithConfigurationWrapper:(UTMConfigurationWrapper *)configuration packageURL:(nonnull NSURL *)packageURL {
#if TARGET_OS_OSX
if (@available(macOS 11, *)) {
if (configuration.isAppleVirtualization) {
Expand Down
2 changes: 1 addition & 1 deletion Managers/UTMVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extension UTMVirtualMachine: ObservableObject {
@nonobjc convenience init<Config: UTMConfiguration>(newConfig: Config, destinationURL: URL) {
let packageURL = UTMVirtualMachine.virtualMachinePath(newConfig.information.name, inParentURL: destinationURL)
let configuration = UTMConfigurationWrapper(wrapping: newConfig)
self.init(configuration: configuration, packageURL: packageURL)
self.init(configurationWrapper: configuration, packageURL: packageURL)
}
}

Expand Down
12 changes: 4 additions & 8 deletions Platform/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ class Main {
static var jitAvailable = true

static func main() {
#if os(iOS)
#if !WITH_QEMU_TCI
#if os(iOS) && !WITH_QEMU_TCI
// check if we have jailbreak
if jb_spawn_ptrace_child(CommandLine.argc, CommandLine.unsafeArgv) {
logger.info("JIT: ptrace() child spawn trick")
Expand All @@ -56,15 +55,12 @@ class Main {
logger.info("MEM: successfully removed memory limits")
}
#endif
// UIViewController patches
UTMViewControllerPatches.patchAll()
// do patches
UTMPatches.patchAll()
#if os(iOS)
// register defaults
registerDefaultsFromSettingsBundle()
#endif
#if os(macOS)
// SwiftUI bug: works around crash due to "already had more Update Constraints in Window passes than there are views in the window" exception
UserDefaults.standard.set(false, forKey: "NSWindowAssertWhenDisplayCycleLimitReached")
#endif
UTMApp.main()
}

Expand Down
7 changes: 5 additions & 2 deletions Platform/Shared/DetailedSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ struct DetailedSection<Content>: View where Content: View {

struct DetailedSection_Previews: PreviewProvider {
static var previews: some View {
DetailedSection("Section", description: "Description") {
EmptyView()
Form {
DetailedSection("Section", description: "Description") {
EmptyView()
}
}
.frame(width: 200)
}
}
9 changes: 7 additions & 2 deletions Platform/Shared/SizeTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ struct SizeTextField: View {
.multilineTextAlignment(.trailing)
.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.")
Button(action: { isGiB.toggle() }, label: {
Text(isGiB ? "GB" : "MB")
.foregroundColor(.blue)
Group {
if isGiB {
Text("GB")
} else {
Text("MB")
}
}.foregroundColor(.blue)
}).buttonStyle(.plain)
}
}
Expand Down
18 changes: 16 additions & 2 deletions Platform/Shared/UTMDownloadTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
private var taskContinuation: CheckedContinuation<UTMVirtualMachine?, Error>?
@MainActor private(set) lazy var pendingVM: UTMPendingVirtualMachine = createPendingVM()

private let kMaxRetries = 5
private var retries = 0

var fileManager: FileManager {
FileManager.default
}
Expand Down Expand Up @@ -82,6 +85,7 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
sessionTask.cancel()
return
}
retries = 0 // reset retry counter on success
Task {
await pendingVM.setDownloadProgress(new: bytesWritten,
currentTotal: totalBytesWritten,
Expand All @@ -91,12 +95,22 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate

/// when the session ends with an error, it could be cancelled or an actual error
internal func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let error = error as? NSError
if let resumeData = error?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
retries += 1
guard retries > kMaxRetries else {
logger.warning("Retrying download due to connection error...")
let task = session.downloadTask(withResumeData: resumeData)
task.resume()
return
}
}
guard let taskContinuation = taskContinuation else {
return
}
self.taskContinuation = nil
self.retries = 0 // reset retry counter
if let error = error {
let error = error as NSError
if error.code == NSURLErrorCancelled {
/// download was cancelled normally
taskContinuation.resume(returning: nil)
Expand Down Expand Up @@ -135,7 +149,7 @@ class UTMDownloadTask: NSObject, URLSessionDelegate, URLSessionDownloadDelegate
/// - Returns: Completed download or nil if canceled
func download() async throws -> UTMVirtualMachine? {
/// begin the download
let session = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: nil)
let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
downloadTask = Task.detached { [self] in
let sessionDownload = session.downloadTask(with: url)
await pendingVM.setDownloadStarting()
Expand Down
7 changes: 6 additions & 1 deletion Platform/Shared/VMConfigDisplayConsoleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ struct VMConfigDisplayConsoleView_Previews: PreviewProvider {
@State static private var config = UTMConfigurationTerminal()

static var previews: some View {
VMConfigDisplayConsoleView(config: $config)
Form {
VMConfigDisplayConsoleView(config: $config)
}
#if os(macOS)
.scrollable()
#endif
}
}
20 changes: 10 additions & 10 deletions Platform/Shared/VMConfigDriveDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct VMConfigDriveDetailsView: View {
}

@Binding var config: UTMQemuConfigurationDrive
let onDelete: (() -> Void)?
@Binding var requestDriveDelete: UTMQemuConfigurationDrive?

@EnvironmentObject private var data: UTMData
@State private var isImporterPresented: Bool = false
Expand Down Expand Up @@ -78,19 +78,19 @@ struct VMConfigDriveDetailsView: View {
}

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

#if os(macOS)
HStack {
if let onDelete = onDelete {
Button(action: onDelete) {
Label("Delete Drive", systemImage: "externaldrive.badge.minus")
.foregroundColor(.red)
}.help("Delete this drive.")
}
Button {
requestDriveDelete = config
} label: {
Label("Delete Drive", systemImage: "externaldrive.badge.minus")
.foregroundColor(.red)
}.help("Delete this drive.")

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

private var sizeString: String? {
if let currentSize = currentSize {
return ByteCountFormatter.string(fromByteCount: currentSize, countStyle: .file)
return ByteCountFormatter.string(fromByteCount: currentSize, countStyle: .binary)
} else {
return nil
}
Expand Down
3 changes: 3 additions & 0 deletions Platform/Shared/VMConfigInfoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ struct VMConfigInfoView_Previews: PreviewProvider {
static var previews: some View {
Group {
VMConfigInfoView(config: $config)
#if os(macOS)
.scrollable()
#endif
IconSelect() { _ in

}
Expand Down
3 changes: 3 additions & 0 deletions Platform/Shared/VMConfigInputView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,8 @@ struct VMConfigInputView_Previews: PreviewProvider {

static var previews: some View {
VMConfigInputView(config: $config)
#if os(macOS)
.scrollable()
#endif
}
}
1 change: 1 addition & 0 deletions Platform/Shared/VMConfigQEMUView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,5 +210,6 @@ struct VMConfigQEMUView_Previews: PreviewProvider {

static var previews: some View {
VMConfigQEMUView(config: $config, system: $system, fetchFixedArguments: { [] })
.frame(minHeight: 500)
}
}
3 changes: 3 additions & 0 deletions Platform/Shared/VMConfigSerialView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,8 @@ struct VMConfigSerialView_Previews: PreviewProvider {

static var previews: some View {
VMConfigSerialView(config: $config, system: $system)
#if os(macOS)
.scrollable()
#endif
}
}
Loading

0 comments on commit 66379ef

Please sign in to comment.