Full Code of foxlet/macOS-Simple-KVM for AI

master 527588d5a25c cached
29 files
12.6 MB
15.8k tokens
14 symbols
1 requests
Download .txt
Repository: foxlet/macOS-Simple-KVM
Branch: master
Commit: 527588d5a25c
Files: 29
Total size: 12.6 MB

Directory structure:
gitextract_myseskiw/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .gitmodules
├── ESP.qcow2
├── README.md
├── basic.sh
├── docs/
│   ├── FAQs.md
│   ├── guide-Apple-ID.md
│   ├── guide-networking.md
│   ├── guide-passthrough.md
│   ├── guide-performance.md
│   └── guide-screen-resolution.md
├── firmware/
│   ├── LICENSE
│   ├── OVMF_CODE.fd
│   └── OVMF_VARS-1024x768.fd
├── headless.sh
├── jumpstart.sh
├── make.sh
├── tools/
│   ├── FetchMacOS/
│   │   ├── .idea/
│   │   │   ├── FetchMacOS.iml
│   │   │   ├── misc.xml
│   │   │   ├── modules.xml
│   │   │   └── workspace.xml
│   │   ├── fetch-macos.py
│   │   ├── fetch.sh
│   │   └── requirements.txt
│   ├── debug.sh
│   ├── dmg2img
│   └── template.xml.in
└── virtio.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: foxlet
custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QFXXKKAB2B9MA&item_name=macOS-Simple-KVM', 'https://commerce.coinbase.com/checkout/96dc5777-0abf-437d-a9b5-a78ae2c4c227']


================================================
FILE: .gitignore
================================================
.DS_Store
BaseSystem.img
BaseSystem.dmg
BaseSystem.chunklist


================================================
FILE: .gitmodules
================================================
[submodule "tools/dmg2img-src"]
	path = tools/dmg2img-src
	url = https://github.com/foxlet/dmg2img.git


================================================
FILE: ESP.qcow2
================================================
[File too large to display: 12.6 MB]

================================================
FILE: README.md
================================================
# macOS-Simple-KVM
Documentation to set up a simple macOS VM in QEMU, accelerated by KVM.

By [@FoxletFox](https://twitter.com/foxletfox), and the help of many others. Find this useful? You can donate [on Coinbase](https://commerce.coinbase.com/checkout/96dc5777-0abf-437d-a9b5-a78ae2c4c227) or [Paypal!](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QFXXKKAB2B9MA&item_name=macOS-Simple-KVM).

New to macOS and KVM? Check [the FAQs.](docs/FAQs.md)

## Getting Started
You'll need a Linux system with `qemu` (3.1 or later), `python3`, `pip` and the KVM modules enabled. A Mac is **not** required. Some examples for different distributions:

```
sudo apt-get install qemu-system qemu-utils python3 python3-pip  # for Ubuntu, Debian, Mint, and PopOS.
sudo pacman -S qemu python python-pip python-wheel  # for Arch.
sudo xbps-install -Su qemu python3 python3-pip   # for Void Linux.
sudo zypper in qemu-tools qemu-kvm qemu-x86 qemu-audio-pa python3-pip  # for openSUSE Tumbleweed
sudo dnf install qemu qemu-img python3 python3-pip # for Fedora
sudo emerge -a qemu python:3.4 pip # for Gentoo
```

## Step 1
Run `jumpstart.sh` to download installation media for macOS (internet required). The default installation uses Catalina, but you can choose which version to get by adding either `--high-sierra`, `--mojave`, or `--catalina`. For example:
```
./jumpstart.sh --mojave
```
> Note: You can skip this if you already have `BaseSystem.img` downloaded. If you have `BaseSystem.dmg`, you will need to convert it with the `dmg2img` tool.

## Step 2
Create an empty hard disk using `qemu-img`, changing the name and size to preference:
```
qemu-img create -f qcow2 MyDisk.qcow2 64G
```

and add it to the end of `basic.sh`:
```
    -drive id=SystemDisk,if=none,file=MyDisk.qcow2 \
    -device ide-hd,bus=sata.4,drive=SystemDisk \
```
> Note: If you're running on a headless system (such as on Cloud providers), you will need `-nographic` and `-vnc :0 -k en-us` for VNC support.

Then run `basic.sh` to start the machine and install macOS. Remember to partition in Disk Utility first!

## Step 2a (Virtual Machine Manager)
1. If instead of QEMU, you'd like to import the setup into Virt-Manager for further configuration, just run `sudo ./make.sh --add`.
2. After running the above command, add `MyDisk.qcow2` as storage in the properties of the newly added entry for VM.

## Step 2b (Headless Systems)
If you're using a cloud-based/headless system, you can use `headless.sh` to set up a quick VNC instance. Settings are defined through variables as seen in the following example. VNC will start on port `5900` by default.
```
HEADLESS=1 MEM=1G CPUS=2 SYSTEM_DISK=MyDisk.qcow2 ./headless.sh
```

## Step 3

You're done!

To fine-tune the system and improve performance, look in the `docs` folder for more information on [adding memory](docs/guide-performance.md), setting up [bridged networking](docs/guide-networking.md), adding [passthrough hardware (for GPUs)](docs/guide-passthrough.md), tweaking [screen resolution](docs/guide-screen-resolution.md), and enabling sound features.


================================================
FILE: basic.sh
================================================
#!/bin/bash

OSK="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc"
VMDIR=$PWD
OVMF=$VMDIR/firmware
#export QEMU_AUDIO_DRV=pa
#QEMU_AUDIO_DRV=pa

qemu-system-x86_64 \
    -enable-kvm \
    -m 2G \
    -machine q35,accel=kvm \
    -smp 4,cores=2 \
    -cpu Penryn,vendor=GenuineIntel,kvm=on,+sse3,+sse4.2,+aes,+xsave,+avx,+xsaveopt,+xsavec,+xgetbv1,+avx2,+bmi2,+smep,+bmi1,+fma,+movbe,+invtsc \
    -device isa-applesmc,osk="$OSK" \
    -smbios type=2 \
    -drive if=pflash,format=raw,readonly,file="$OVMF/OVMF_CODE.fd" \
    -drive if=pflash,format=raw,file="$OVMF/OVMF_VARS-1024x768.fd" \
    -vga qxl \
    -device ich9-intel-hda -device hda-output \
    -usb -device usb-kbd -device usb-mouse \
    -netdev user,id=net0 \
    -device e1000-82545em,netdev=net0,id=net0,mac=52:54:00:c9:18:27 \
    -device ich9-ahci,id=sata \
    -drive id=ESP,if=none,format=qcow2,file=ESP.qcow2 \
    -device ide-hd,bus=sata.2,drive=ESP \
    -drive id=InstallMedia,format=raw,if=none,file=BaseSystem.img \
    -device ide-hd,bus=sata.3,drive=InstallMedia \


================================================
FILE: docs/FAQs.md
================================================
# FAQs

## Q: Does this work on any CPU?
A: There is a minimum CPU requirement for macOS itself. Both Intel and AMD CPUs are supported, but the recommendations are Ivy Bridge (or later) Core and Xeon processors, or Ryzen and Threadripper processors.

## Q: How much disk space do I need?
A: The jumpstart download is ~500MB compressed (2GB uncompressed), the installation files are uncompressed and measure 6.5GB. Bare minimum virtual disk size would be around 20GB, but you'll find it hard to get any apps installed (like Xcode, which is at least 8GB compressed).

## Q: Does this work on DigitalOcean/ScaleWay/Azure/GCS?
A: If the cloud provider supports nested KVM as well as the necessary CPU instructions, yes.
   In some cases only certain tiers will work as the CPU need to be supported.
   
   For DigitalOcean, this means a `General Purpose` or `CPU Optimized` machine is required. `Ubuntu 19.04` or newer is recommended.


================================================
FILE: docs/guide-Apple-ID.md
================================================
1. Use a random multicast [MAC address generator](https://www.hellion.org.uk/cgi-bin/randmac.pl) and paste it into the template generated by `make.sh`. 
 - While I was editing the template, I also used `uuidgen` and pasted that in there for good measure.
 - Then import the template with `virsh define whatever.xml`.
2. Inside macOS, download [Clover Configurator](https://mackie100projects.altervista.org/download-clover-configurator/) and use it to mount the EFI partition, "Load Configuration" (bottom left most button), then find the `config.plist` file in the EFI volume in Finder and drag it into the Clover file picker. (macOs.... :-/)
 - Then use Clover Configurator as shown in the screenshot below to select an an Apple hardware model, and hit all the "Generate New" buttons you can find there.
 - In the "Rt Variables" section set the MLB value to blank.
 - Then the second button on the bottom left to save the configuration.

![Screenshot_20190622_091035](https://user-images.githubusercontent.com/2692138/59964986-1d76a780-94ce-11e9-9404-21da23530b87.png)


================================================
FILE: docs/guide-networking.md
================================================
Guide to Bridged Networking
===========================

*Note*: you don't need to set up bridged networking just to get internet access. With `basic.sh` you should be able to access the internet from MacOS automatically. *However*, the ICMP protocol (used for `ping`) is not supported with the default networking solution. 

To set up bridged networking for the macOS VM, use one of the following methods:


## Using /etc/network/interfaces

It is possible to create the bridge and tun/tap interfaces by adding the following lines to `/etc/network/interfaces`. Replace `DEVICENAME` with your ethernet card's device name, and `MYUSERNAME` with the user that is starting the VM.

```
auto br0
iface br0 inet dhcp
  bridge_ports DEVICENAME tap0

auto tap0
iface tap0 inet dhcp
  pre-up tunctl -u MYUSERNAME -t tap0
```

## Using NetworkManager
You can use NetworkManager to control the bridge and tun/tap interfaces, by creating them with the following commands. Replace `DEVICENAME` with your ethernet card's device name.

### Make the Bridge
```
nmcli connection add type bridge \
    ifname br1 con-name mybridge
```

### Attach Bridge to Ethernet
```
nmcli connection add type bridge-slave \
    ifname DEVICENAME con-name mynetwork master br1
```

### Make the Tun/Tap
```
nmcli connection add type tun \
    ifname tap0 con-name mytap \
    mode tap owner `id -u`
```

### Attach Tun/Tap to Bridge
```
nmcli connection mod mytap connection.slave-type bridge \
    connection.master br1
```

## Attach Bridge to QEMU
Once you have set up the bridge and tun/tap on the host, you'll have to add the following line to `basic.sh`, replacing `-netdev user,id=net0`. Change `tap0` to your corresponding device name.

```
    -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
```
You can optionally use the `vmxnet3` driver for higher performance compared to the default e1000. Note that replacing it requires macOS El Capitan or higher.
```
    -device vmxnet3,netdev=net0,id=net0,mac=52:54:00:c9:18:27 \
```

## Using Netctl
You can also use netctl and the qemu bridge helper to control the bridge and tun/tap interfaces. Replace `DEVICENAME` with your ethernet card's device name.

### Create netctl configuration file in /etc/netctl (f.e. /etc/netctl/kvm-bridge)
```
Description="Bridge Interface br10 : DEVICENAME"
Interface=br10
Connection=bridge
BindsToInterfaces=(DEVICENAME)
IP=dhcp
# If you want also for DHCPv6,uncomment below line
#IP6=dhcp
```

### Activate netctl bridge handler with system boot
```
sudo netctl enable kvm-bridge
```

### Create bridge whitelist file for qemu (/etc/qemu/bridge.conf)
```
allow br10
```

## Attach Bridge to QEMU
Now you'll have to add the following line to `basic.sh`, replacing `-netdev user,id=net0`. Change `br10` to your corresponding device name.

```
    -netdev bridge,br=br10,id=net0 \
```


================================================
FILE: docs/guide-passthrough.md
================================================
Guide to PCIe Passthrough
=========================

## Enable BIOS features
To use PCIe Passthrough, you will need a compatible motherboard and CPU with support for iommu. Look up your motherboard manual on how to enable these features, but they are commonly named `VT-d` or `AMD Vi`.

## Get Some Information
To pass through a card, you'll need to know some value pertaining to the card itself: The Device IDs, and BDF IDs. The following command will give a list of relevant devices to use in the next steps.

```
lspci -nn | grep "VGA\|Audio"
```

### Example
An example output might look like this:

```
26:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Curacao XT / Trinidad XT [Radeon R7 370 / R9 270X/370X] [1002:6810]
26:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Oland/Hainan/Cape Verde/Pitcairn HDMI Audio [Radeon HD 7000 Series] [1002:aab0]
```

The first value (`26:00.0`) is the BDF ID, and the last (`1002:6810`) is the Device ID. Cards with a built-in audio controller have to be passed together, so note the IDs for both subdevices.

## Load the vfio-pci module
The `vfio-pci` module is not included in the kernel on all systems, you may need for load it as part of initramfs. Look up your distro's documentation on how to do this.

## Add Kernel Flags
The `iommu` kernel module is not enabled by default, but you can enable it on boot by passing the following flags to the kernel. Replace the Device IDs with your corresponding card.

### AMD
```
iommu=pt amd_iommu=on vfio-pci.ids=1002:66af,1002:ab20
```

### Intel
```
iommu=pt intel_iommu=on vfio-pci.ids=1002:66af,1002:ab20
```

To do this permanently, you can add it to your bootloader. If you're using GRUB, for example, edit `/etc/default/grub` and add the previous lines to the `GRUB_CMDLINE_LINUX_DEFAULT` section, then run `sudo update-grub` (or `sudo grub-mkconfig` on some systems) and reboot.

## Attach card to QEMU
You will need to attach the cards using the BDF IDs for the audio and video controller. The following example shows the config for a card with two devices. The romfile parameter is optional. 

**Note:** You may need to run `basic.sh` as sudo for the card to work.

```
    -vga none \
    -device pcie-root-port,bus=pcie.0,multifunction=on,port=1,chassis=1,id=port.1 \
    -device vfio-pci,host=26:00.0,bus=port.1,multifunction=on,romfile=/path/to/card.rom \
    -device vfio-pci,host=26:00.1,bus=port.1 \
```


================================================
FILE: docs/guide-performance.md
================================================
Guide to Performance Tuning
===========================
The default macOS VM configuration (`basic.sh`) assumes some sane defaults, however this may not reflect the actual topology or peformance capabilities of the underlaying machine.

Here's some tunable parameters to get the most of out QEMU:

## Memory
The following line controls memory allocation. The default is 2GB of RAM.

Increase this as needed (limited to the real amount of memory in your machine).
```
    -m 2G \
```

## Cores
The default configuration enables 4 threads, divided into CPUs with 2 cores each.
```
    -smp 4,cores=2 \
```

The following example describes all possible topology flags:
```
    -smp cores=4,threads=4,sockets=1 \
```


================================================
FILE: docs/guide-screen-resolution.md
================================================
## How to increase screen resolution for macOS-Simple-KVM

_(Thanks to [passthroughpo.st](https://passthroughpo.st/new-and-improved-mac-os-tutorial-part-1-the-basics/) and [urcomputertechnics.com](http://urcomputertechnics.com/how-to-mount-efi-partition-on-macos-mojave/) for the tips.)_

1. In the macOS Finder, look for **EFI** in the left bar under **Volumes**. If it isn't visible you will have to mount it:
 - Open the macOS Terminal and type `diskutil list` and look for the disk/partition location of the EFI. (There may be more than one.)
 - Type `sudo diskutil mount diskYsZ`, using the disk/partition location name where you see EFI.
 - The **EFI** partition will appear in the left Finder bar under **Volumes**.
 - If you don't see anything in that volume after browsing to it, try the other ones that you found in `diskutil`.
2. In the **EFI** volume, go into the `Clover` directory and open the `config.plist` file in the macOS text editor.
3. There should be a section of the file that looks like this:

```````````````````
<key>ScreenResolution</key>
<string>1280x720</string>
```````````````````

 - Edit that to your preferred screen resolution.
 - Some odd/intermediate resolutions like 1366×768 may not work well. Try to stick to more common 16:9, 16:10, and 4:3 form factors.

2. Shut down the VM, relaunch it using `basic.sh` script and follow the following steps:
 - Press `Escape` key as soon as the window comes up.
 - In the interface that comes up, select `Device Manager`->`OVMF Platform Configuration`->`Change Preferred` and select the correct resolution.
 - Press `F10` to save the changes.
 - Press `Escape` multiple times to come back to main menu, and then select `Continue` on it.


================================================
FILE: firmware/LICENSE
================================================
Copyright (c) 2019, Intel Corporation.  All rights reserved.

SPDX-License-Identifier: BSD-2-Clause-Patent


================================================
FILE: headless.sh
================================================
#!/bin/bash

OSK="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc"
VMDIR=$PWD
OVMF=$VMDIR/firmware

[[ -z "$MEM" ]] && {
	MEM="1G"
}

[[ -z "$CPUS" ]] && {
	CPUS=1
}

[[ -z "$SYSTEM_DISK" ]] && {
    echo "Please set the SYSTEM_DISK environment variable"
    exit 1
}

[[ -r "$SYSTEM_DISK" ]] || {
    echo "Can't read system disk image: $SYSTEM_DISK"
    exit 1
}

MOREARGS=()

[[ "$HEADLESS" = "1" ]] && {
    MOREARGS+=(-nographic -vnc :0 -k en-us)
}

qemu-system-x86_64 \
    -enable-kvm \
    -m $MEM \
    -machine q35,accel=kvm \
    -smp $CPUS \
    -cpu Penryn,vendor=GenuineIntel,kvm=on,+sse3,+sse4.2,+aes,+xsave,+avx,+xsaveopt,+xsavec,+xgetbv1,+avx2,+bmi2,+smep,+bmi1,+fma,+movbe,+invtsc \
    -device isa-applesmc,osk="$OSK" \
    -smbios type=2 \
    -drive if=pflash,format=raw,readonly,file="$OVMF/OVMF_CODE.fd" \
    -drive if=pflash,format=raw,file="$OVMF/OVMF_VARS-1024x768.fd" \
    -vga qxl \
    -usb -device usb-kbd -device usb-tablet \
    -netdev user,id=net0 \
    -device e1000-82545em,netdev=net0,id=net0,mac=52:54:00:0e:0d:20 \
    -device ich9-ahci,id=sata \
    -drive id=ESP,if=none,format=qcow2,file=ESP.qcow2 \
    -device ide-hd,bus=sata.2,drive=ESP \
    -drive id=InstallMedia,format=raw,if=none,file=BaseSystem.img \
    -device ide-hd,bus=sata.3,drive=InstallMedia \
    -drive id=SystemDisk,if=none,file="${SYSTEM_DISK}" \
    -device ide-hd,bus=sata.4,drive=SystemDisk \
    "${MOREARGS[@]}"


================================================
FILE: jumpstart.sh
================================================
#!/bin/bash

# jumpstart.sh: Fetches BaseSystem and converts it to a viable format.
# by Foxlet <foxlet@furcode.co>

TOOLS=$PWD/tools

print_usage() {
    echo
    echo "Usage: $0"
    echo
    echo " -s, --high-sierra   Fetch High Sierra media."
    echo " -m, --mojave        Fetch Mojave media."
    echo " -c, --catalina      Fetch Catalina media."
    echo
}

error() {
    local error_message="$*"
    echo "${error_message}" 1>&2;
}

argument="$1"
case $argument in
    -h|--help)
        print_usage
        ;;
    -s|--high-sierra)
        "$TOOLS/FetchMacOS/fetch.sh" -v 10.13 || exit 1;
        ;;
    -m|--mojave)
        "$TOOLS/FetchMacOS/fetch.sh" -v 10.14 || exit 1;
        ;;
    -c|--catalina|*)
        "$TOOLS/FetchMacOS/fetch.sh" -v 10.15 || exit 1;
        ;;
esac

"$TOOLS/dmg2img" "$TOOLS/FetchMacOS/BaseSystem/BaseSystem.dmg" "$PWD/BaseSystem.img"


================================================
FILE: make.sh
================================================
#!/bin/bash

# make.sh: Generate customized libvirt XML.
# by Foxlet <foxlet@furcode.co>

VMDIR=$PWD
MACHINE="$(qemu-system-x86_64 --machine help | grep q35 | cut -d" " -f1 | grep -Eoe ".*-[0-9.]+" | sort -rV | head -1)"
OUT="template.xml"

print_usage() {
    echo
    echo "Usage: $0"
    echo
    echo " -a, --add   Add XML to virsh (uses sudo)."
    echo
}

error() {
    local error_message="$*"
    echo "${error_message}" 1>&2;
}

generate(){
    sed -e "s|VMDIR|$VMDIR|g" -e "s|MACHINE|$MACHINE|g" tools/template.xml.in > $OUT
    echo "$OUT has been generated in $VMDIR"
}

generate

argument="$1"
case $argument in
    -a|--add)
        sudo virsh define $OUT
        ;;
    -h|--help)
        print_usage
        ;;
esac


================================================
FILE: tools/FetchMacOS/.idea/FetchMacOS.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$" />
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
  <component name="TestRunnerService">
    <option name="PROJECT_TEST_RUNNER" value="Unittests" />
  </component>
</module>

================================================
FILE: tools/FetchMacOS/.idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.10 (/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7)" project-jdk-type="Python SDK" />
</project>

================================================
FILE: tools/FetchMacOS/.idea/modules.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/.idea/FetchMacOS.iml" filepath="$PROJECT_DIR$/.idea/FetchMacOS.iml" />
    </modules>
  </component>
</project>

================================================
FILE: tools/FetchMacOS/.idea/workspace.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ChangeListManager">
    <list default="true" id="9235d6ad-9f56-4dc9-b3f5-2978117b4de2" name="Default" comment="" />
    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
    <option name="TRACKING_ENABLED" value="true" />
    <option name="SHOW_DIALOG" value="false" />
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
    <option name="LAST_RESOLUTION" value="IGNORE" />
  </component>
  <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
  <component name="FileEditorManager">
    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
      <file leaf-file-name="fetch-macos.py" pinned="false" current-in-tab="true">
        <entry file="file://$PROJECT_DIR$/fetch-macos.py">
          <provider selected="true" editor-type-id="text-editor">
            <state relative-caret-position="389">
              <caret line="106" column="9" lean-forward="true" selection-start-line="106" selection-start-column="9" selection-end-line="106" selection-end-column="9" />
              <folding>
                <element signature="e#102#116#0" expanded="true" />
              </folding>
            </state>
          </provider>
        </entry>
      </file>
      <file leaf-file-name="requirements.txt" pinned="false" current-in-tab="false">
        <entry file="file://$PROJECT_DIR$/requirements.txt">
          <provider selected="true" editor-type-id="text-editor">
            <state relative-caret-position="15">
              <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
              <folding />
            </state>
          </provider>
        </entry>
      </file>
    </leaf>
  </component>
  <component name="FileTemplateManagerImpl">
    <option name="RECENT_TEMPLATES">
      <list>
        <option value="Python Script" />
      </list>
    </option>
  </component>
  <component name="IdeDocumentHistory">
    <option name="CHANGED_PATHS">
      <list>
        <option value="$PROJECT_DIR$/requirements.txt" />
        <option value="$PROJECT_DIR$/fetch-macos.py" />
      </list>
    </option>
  </component>
  <component name="ProjectFrameBounds">
    <option name="y" value="23" />
    <option name="width" value="1236" />
    <option name="height" value="985" />
  </component>
  <component name="ProjectView">
    <navigator currentView="ProjectPane" proportions="" version="1">
      <flattenPackages />
      <showMembers />
      <showModules />
      <showLibraryContents />
      <hideEmptyPackages />
      <abbreviatePackageNames />
      <autoscrollToSource />
      <autoscrollFromSource />
      <sortByType />
      <manualOrder />
      <foldersAlwaysOnTop value="true" />
    </navigator>
    <panes>
      <pane id="ProjectPane">
        <subPane>
          <PATH>
            <PATH_ELEMENT>
              <option name="myItemId" value="FetchMacOS" />
              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
            </PATH_ELEMENT>
            <PATH_ELEMENT>
              <option name="myItemId" value="FetchMacOS" />
              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
            </PATH_ELEMENT>
          </PATH>
        </subPane>
      </pane>
      <pane id="Scratches" />
      <pane id="Scope" />
    </panes>
  </component>
  <component name="PropertiesComponent">
    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
  </component>
  <component name="RunDashboard">
    <option name="ruleStates">
      <list>
        <RuleState>
          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
        </RuleState>
        <RuleState>
          <option name="name" value="StatusDashboardGroupingRule" />
        </RuleState>
      </list>
    </option>
  </component>
  <component name="RunManager" selected="Python.fetch-macos">
    <configuration default="false" name="fetch-macos" type="PythonConfigurationType" factoryName="Python" temporary="true">
      <option name="INTERPRETER_OPTIONS" value="" />
      <option name="PARENT_ENVS" value="true" />
      <envs>
        <env name="PYTHONUNBUFFERED" value="1" />
      </envs>
      <option name="SDK_HOME" value="" />
      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
      <option name="IS_MODULE_SDK" value="true" />
      <option name="ADD_CONTENT_ROOTS" value="true" />
      <option name="ADD_SOURCE_ROOTS" value="true" />
      <module name="FetchMacOS" />
      <option name="SCRIPT_NAME" value="$PROJECT_DIR$/fetch-macos.py" />
      <option name="PARAMETERS" value="" />
      <option name="SHOW_COMMAND_LINE" value="false" />
      <option name="EMULATE_TERMINAL" value="false" />
      <method />
    </configuration>
    <configuration default="true" type="PythonConfigurationType" factoryName="Python">
      <option name="INTERPRETER_OPTIONS" value="" />
      <option name="PARENT_ENVS" value="true" />
      <envs>
        <env name="PYTHONUNBUFFERED" value="1" />
      </envs>
      <option name="SDK_HOME" value="" />
      <option name="WORKING_DIRECTORY" value="" />
      <option name="IS_MODULE_SDK" value="false" />
      <option name="ADD_CONTENT_ROOTS" value="true" />
      <option name="ADD_SOURCE_ROOTS" value="true" />
      <module name="FetchMacOS" />
      <option name="SCRIPT_NAME" value="" />
      <option name="PARAMETERS" value="" />
      <option name="SHOW_COMMAND_LINE" value="false" />
      <option name="EMULATE_TERMINAL" value="false" />
      <method />
    </configuration>
    <configuration default="true" type="Tox" factoryName="Tox">
      <option name="INTERPRETER_OPTIONS" value="" />
      <option name="PARENT_ENVS" value="true" />
      <envs />
      <option name="SDK_HOME" value="" />
      <option name="WORKING_DIRECTORY" value="" />
      <option name="IS_MODULE_SDK" value="false" />
      <option name="ADD_CONTENT_ROOTS" value="true" />
      <option name="ADD_SOURCE_ROOTS" value="true" />
      <module name="FetchMacOS" />
      <method />
    </configuration>
    <configuration default="true" type="tests" factoryName="Doctests">
      <option name="INTERPRETER_OPTIONS" value="" />
      <option name="PARENT_ENVS" value="true" />
      <envs />
      <option name="SDK_HOME" value="" />
      <option name="WORKING_DIRECTORY" value="" />
      <option name="IS_MODULE_SDK" value="false" />
      <option name="ADD_CONTENT_ROOTS" value="true" />
      <option name="ADD_SOURCE_ROOTS" value="true" />
      <module name="FetchMacOS" />
      <option name="SCRIPT_NAME" value="" />
      <option name="CLASS_NAME" value="" />
      <option name="METHOD_NAME" value="" />
      <option name="FOLDER_NAME" value="" />
      <option name="TEST_TYPE" value="TEST_SCRIPT" />
      <option name="PATTERN" value="" />
      <option name="USE_PATTERN" value="false" />
      <method />
    </configuration>
    <configuration default="true" type="tests" factoryName="Unittests">
      <option name="INTERPRETER_OPTIONS" value="" />
      <option name="PARENT_ENVS" value="true" />
      <envs />
      <option name="SDK_HOME" value="" />
      <option name="WORKING_DIRECTORY" value="" />
      <option name="IS_MODULE_SDK" value="false" />
      <option name="ADD_CONTENT_ROOTS" value="true" />
      <option name="ADD_SOURCE_ROOTS" value="true" />
      <module name="FetchMacOS" />
      <option name="_new_additionalArguments" value="&quot;&quot;" />
      <option name="_new_target" value="&quot;.&quot;" />
      <option name="_new_targetType" value="&quot;PATH&quot;" />
      <method />
    </configuration>
    <list size="1">
      <item index="0" class="java.lang.String" itemvalue="Python.fetch-macos" />
    </list>
    <recent_temporary>
      <list size="1">
        <item index="0" class="java.lang.String" itemvalue="Python.fetch-macos" />
      </list>
    </recent_temporary>
  </component>
  <component name="ShelveChangesManager" show_recycled="false">
    <option name="remove_strategy" value="false" />
  </component>
  <component name="SvnConfiguration">
    <configuration />
  </component>
  <component name="TaskManager">
    <task active="true" id="Default" summary="Default task">
      <changelist id="9235d6ad-9f56-4dc9-b3f5-2978117b4de2" name="Default" comment="" />
      <created>1499116578112</created>
      <option name="number" value="Default" />
      <option name="presentableId" value="Default" />
      <updated>1499116578112</updated>
    </task>
    <servers />
  </component>
  <component name="ToolWindowManager">
    <frame x="0" y="23" width="1236" height="985" extended-state="0" />
    <editor active="true" />
    <layout>
      <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.29126215" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.30349344" sideWeight="0.2920712" order="7" side_tool="true" content_ui="tabs" />
      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.30349344" sideWeight="0.7079288" order="2" side_tool="false" content_ui="tabs" />
      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
      <window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3297062" sideWeight="0.4969088" order="7" side_tool="false" content_ui="tabs" />
      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32969433" sideWeight="0.46521035" order="7" side_tool="false" content_ui="tabs" />
      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
      <window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
    </layout>
  </component>
  <component name="VcsContentAnnotationSettings">
    <option name="myLimit" value="2678400000" />
  </component>
  <component name="XDebuggerManager">
    <breakpoint-manager>
      <option name="time" value="1" />
    </breakpoint-manager>
    <watches-manager />
  </component>
  <component name="editorHistoryManager">
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="30">
          <caret line="2" column="0" lean-forward="true" selection-start-line="2" selection-start-column="0" selection-end-line="2" selection-end-column="0" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="0">
          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/requirements.txt">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="15">
          <caret line="1" column="5" lean-forward="false" selection-start-line="1" selection-start-column="5" selection-end-line="1" selection-end-column="5" />
          <folding />
        </state>
      </provider>
    </entry>
    <entry file="file://$PROJECT_DIR$/fetch-macos.py">
      <provider selected="true" editor-type-id="text-editor">
        <state relative-caret-position="389">
          <caret line="106" column="9" lean-forward="true" selection-start-line="106" selection-start-column="9" selection-end-line="106" selection-end-column="9" />
          <folding>
            <element signature="e#102#116#0" expanded="true" />
          </folding>
        </state>
      </provider>
    </entry>
  </component>
</project>

================================================
FILE: tools/FetchMacOS/fetch-macos.py
================================================
#!/usr/bin/python

"""fetch-macos.py: Fetches macOS products from Apple's SoftwareUpdate service."""

import logging
import plistlib
import os
import errno
import click
import requests
import sys

__author__ = "Foxlet"
__copyright__ = "Copyright 2019, FurCode Project"
__license__ = "GPLv3"
__version__ = "1.4"

logging.basicConfig(format='%(asctime)-15s %(message)s', level=logging.INFO)
logger = logging.getLogger('webactivity')


class ClientMeta:
    # Client used to connect to the Software CDN
    osinstall = {"User-Agent":"osinstallersetupplaind (unknown version) CFNetwork/720.5.7 Darwin/14.5.0 (x86_64)"}
    # Client used to connect to the Software Distribution service
    swupdate = {"User-Agent":"Software%20Update (unknown version) CFNetwork/807.0.1 Darwin/16.0.0 (x86_64)"}


class Filesystem:
    @staticmethod
    def download_file(url, size, path):
        label = url.split('/')[-1]
        filename = os.path.join(path, label)
        # Set to stream mode for large files
        remote = requests.get(url, stream=True, headers=ClientMeta.osinstall)

        with open(filename, 'wb') as f:
            with click.progressbar(remote.iter_content(1024), length=size/1024, label="Fetching {} ...".format(filename)) as stream:
                for data in stream:
                    f.write(data)
        return filename

    @staticmethod
    def check_directory(path):
        try:
            os.makedirs(path)
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

    @staticmethod
    def fetch_plist(url):
        logging.info("Network Request: %s", "Fetching {}".format(url))
        plist_raw = requests.get(url, headers=ClientMeta.swupdate)
        plist_data = plist_raw.text.encode('UTF-8')
        return plist_data
    
    @staticmethod
    def parse_plist(catalog_data):
        if sys.version_info > (3, 0):
            root = plistlib.loads(catalog_data)
        else:
            root = plistlib.readPlistFromString(catalog_data)
        return root

class SoftwareService:
    # macOS 10.15 is available in 4 different catalogs from SoftwareScan
    catalogs = {
                "10.15": {
                    "CustomerSeed":"https://swscan.apple.com/content/catalogs/others/index-10.15customerseed-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
                    "DeveloperSeed":"https://swscan.apple.com/content/catalogs/others/index-10.15seed-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
                    "PublicSeed":"https://swscan.apple.com/content/catalogs/others/index-10.15beta-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
                    "PublicRelease":"https://swscan.apple.com/content/catalogs/others/index-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
                        },
                "10.14": {
                    "PublicRelease":"https://swscan.apple.com/content/catalogs/others/index-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
                        },
                "10.13": {
                    "PublicRelease":"https://swscan.apple.com/content/catalogs/others/index-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
                        }
                }

    def __init__(self, version, catalog_id):
        self.version = version
        self.catalog_url = self.catalogs.get(version).get(catalog_id)
        self.catalog_data = ""

    def getcatalog(self):
        self.catalog_data = Filesystem.fetch_plist(self.catalog_url)
        return self.catalog_data

    def getosinstall(self):
        # Load catalogs based on Py3/2 lib
        root = Filesystem.parse_plist(self.catalog_data)

        # Iterate to find valid OSInstall packages
        ospackages = []
        products = root['Products']
        for product in products:
            if products.get(product, {}).get('ExtendedMetaInfo', {}).get('InstallAssistantPackageIdentifiers', {}).get('OSInstall', {}) == 'com.apple.mpkg.OSInstall':
                ospackages.append(product)
                
        # Iterate for an specific version
        candidates = []
        for product in ospackages:
            meta_url = products.get(product, {}).get('ServerMetadataURL', {})
            if self.version in Filesystem.parse_plist(Filesystem.fetch_plist(meta_url)).get('CFBundleShortVersionString', {}):
                candidates.append(product)
        
        return candidates


class MacOSProduct:
    def __init__(self, catalog, product_id):
        root = Filesystem.parse_plist(catalog)
        products = root['Products']
        self.date = root['IndexDate']
        self.product = products[product_id]

    def fetchpackages(self, path, keyword=None):
        Filesystem.check_directory(path)
        packages = self.product['Packages']
        if keyword:
            for item in packages:
                if keyword in item.get("URL"):
                    Filesystem.download_file(item.get("URL"), item.get("Size"), path)
        else:
            for item in packages:
                Filesystem.download_file(item.get("URL"), item.get("Size"), path)

@click.command()
@click.option('-o', '--output-dir', default="BaseSystem/", help="Target directory for package output.")
@click.option('-v', '--catalog-version', default="10.15", help="Version of catalog.")
@click.option('-c', '--catalog-id', default="PublicRelease", help="Name of catalog.")
@click.option('-p', '--product-id', default="", help="Product ID (as seen in SoftwareUpdate).")
def fetchmacos(output_dir="BaseSystem/", catalog_version="10.15", catalog_id="PublicRelease", product_id=""):
    # Get the remote catalog data
    remote = SoftwareService(catalog_version, catalog_id)
    catalog = remote.getcatalog()

    # If no product is given, find the latest OSInstall product
    if product_id == "":
        product_id = remote.getosinstall()[0]

    # Fetch the given Product ID
    try:
        product = MacOSProduct(catalog, product_id)
    except KeyError:
        print("Product ID {} could not be found.".format(product_id))
        exit(1)
        
    logging.info("Selected macOS Product: {}".format(product_id))

    # Download package to disk
    product.fetchpackages(output_dir, keyword="BaseSystem")

if __name__ == "__main__":
    fetchmacos()


================================================
FILE: tools/FetchMacOS/fetch.sh
================================================
#!/bin/bash

# fetch.sh: Run fetch-macos.py with safety checks
# by Foxlet <foxlet@furcode.co>

set +x;
SCRIPTDIR="$(dirname "$0")";
cd "$SCRIPTDIR"

initpip() {
    if [ -x "$(command -v easy_install)" ]; then
        sudo easy_install pip
    else
        echo "Please install python3-pip or easy_install before continuing."
        exit 1;
    fi
    pip install -r requirements.txt --user
}

getpip(){
    if [ -x "$(command -v pip3)" ]; then
        pip3 install -r requirements.txt --user
    elif [ -x "$(command -v pip)" ]; then
        pip install -r requirements.txt --user
    else
        echo "pip will be installed..." >&2
        initpip
    fi
}

getpython(){
    if [ -x "$(command -v python3)" ]; then
        PYTHONBIN=python3
    elif [ -x "$(command -v python)" ]; then
        PYTHONBIN=python
    elif [ -x "$(command -v python2)" ]; then
        PYTHONBIN=python2
    else
        echo "Please install Python 3 before continuing." >&2
        exit 1;
    fi
}

getpip
getpython
$PYTHONBIN fetch-macos.py "$@"

exit;


================================================
FILE: tools/FetchMacOS/requirements.txt
================================================
requests
click


================================================
FILE: tools/debug.sh
================================================
#!/bin/bash

# debug.sh: Checks common virtualization programs and modules.
# by Foxlet <foxlet@furcode.co>

echo "== Distro Info ==" >&2
lsb_release -a

echo "== Loaded Modules ==" >&2
lsmod | grep kvm
lsmod | grep amd_iommu
lsmod | grep intel_iommu

echo "== Installed Binaries ==" >&2
if [ -x "$(command -v qemu-system-x86_64)" ]; then
    qemu-system-x86_64 --version
else
    echo "qemu is not installed." >&2
fi

if [ -x "$(command -v virt-manager)" ]; then
    echo "virt-manager version $(virt-manager --version)"
else
    echo "virt-manager is not installed." >&2
fi

if [ -x "$(command -v python)" ]; then
    python --version
else
    echo "python is not installed." >&2
fi

if [ -x "$(command -v pip)" ]; then
    pip --version
else
    echo "pip is not installed." >&2
fi


================================================
FILE: tools/template.xml.in
================================================
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  <name>macOS-Simple-KVM</name>
  <uuid>d06d502a-904a-4b34-847d-debf1a3d76c7</uuid>
  <memory unit='KiB'>2097152</memory>
  <currentMemory unit='KiB'>2097152</currentMemory>
  <vcpu placement='static'>4</vcpu>
  <os>
    <type arch='x86_64' machine='MACHINE'>hvm</type>
    <loader readonly='yes' type='pflash'>VMDIR/firmware/OVMF_CODE.fd</loader>
    <nvram>VMDIR/firmware/OVMF_VARS-1024x768.fd</nvram>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
    <apic/>
    <vmport state='off'/>
  </features>
  <cpu>
    <topology sockets='1' cores='4' threads='1'/>
  </cpu>
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>
  </pm>
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='VMDIR/ESP.qcow2'/>
      <target dev='sda' bus='sata'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw'/>
      <source file='VMDIR/BaseSystem.img'/>
      <target dev='sdb' bus='sata'/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>
    <controller type='usb' index='0' model='ich9-ehci1'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x7'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci1'>
      <master startport='0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x0' multifunction='on'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci2'>
      <master startport='2'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x1'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci3'>
      <master startport='4'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x2'/>
    </controller>
    <controller type='sata' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
    </controller>
    <interface type='network'>
      <mac address='52:54:00:92:d4:7b'/>
      <source network='default'/>
      <model type='e1000-82545em'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </interface>
    <serial type='pty'>
      <target type='isa-serial' port='0'>
        <model name='isa-serial'/>
      </target>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <input type='mouse' bus='usb'>
      <address type='usb' bus='0' port='1'/>
    </input>
    <input type='keyboard' bus='usb'>
      <address type='usb' bus='0' port='2'/>
    </input>
    <graphics type='spice' autoport='yes'>
      <listen type='address'/>
      <image compression='off'/>
    </graphics>
    <sound model='ich9'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
    </sound>
    <video>
      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
    </video>
    <redirdev bus='usb' type='spicevmc'>
      <address type='usb' bus='0' port='3'/>
    </redirdev>
    <redirdev bus='usb' type='spicevmc'>
      <address type='usb' bus='0' port='4'/>
    </redirdev>
    <memballoon model='none'/>
  </devices>
  <qemu:commandline>
    <qemu:arg value='-cpu'/>
    <qemu:arg value='Penryn,kvm=on,vendor=GenuineIntel,+invtsc,vmware-cpuid-freq=on,+pcid,+ssse3,+sse4.2,+popcnt,+avx,+aes,+xsave,+xsaveopt,check'/>
    <qemu:arg value='-device'/>
    <qemu:arg value='isa-applesmc,osk=ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc'/>
    <qemu:arg value='-smbios'/>
    <qemu:arg value='type=2'/>
  </qemu:commandline>
</domain>



================================================
FILE: virtio.sh
================================================
#!/bin/bash

OSK="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc"
VMDIR=$PWD
OVMF=$VMDIR/firmware
#export QEMU_AUDIO_DRV=pa
#QEMU_AUDIO_DRV=pa

qemu-system-x86_64 \
    -nodefaults \
    -enable-kvm \
    -m 2G \
    -machine q35,accel=kvm \
    -smp 4,cores=2 \
    -cpu Penryn,vendor=GenuineIntel,kvm=on,+sse3,+sse4.2,+aes,+xsave,+avx,+xsaveopt,+xsavec,+xgetbv1,+avx2,+bmi2,+smep,+bmi1,+fma,+movbe,+invtsc \
    -device isa-applesmc,osk="$OSK" \
    -smbios type=2 \
    -drive if=pflash,format=raw,readonly,file="$OVMF/OVMF_CODE.fd" \
    -drive if=pflash,format=raw,file="$OVMF/OVMF_VARS-1024x768.fd" \
    -vga std \
    -device ich9-intel-hda -device hda-output \
    -usb -device usb-kbd -device usb-mouse \
    -netdev user,id=net0 \
    -device vmxnet3,netdev=net0,id=net0,mac=52:54:00:09:49:17 \
    -drive id=ESP,if=virtio,format=qcow2,file=ESP.qcow2 \
    -drive id=MyDisk,if=virtio,format=qcow2,file=MyDisk.qcow2 \
Download .txt
gitextract_myseskiw/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .gitmodules
├── ESP.qcow2
├── README.md
├── basic.sh
├── docs/
│   ├── FAQs.md
│   ├── guide-Apple-ID.md
│   ├── guide-networking.md
│   ├── guide-passthrough.md
│   ├── guide-performance.md
│   └── guide-screen-resolution.md
├── firmware/
│   ├── LICENSE
│   ├── OVMF_CODE.fd
│   └── OVMF_VARS-1024x768.fd
├── headless.sh
├── jumpstart.sh
├── make.sh
├── tools/
│   ├── FetchMacOS/
│   │   ├── .idea/
│   │   │   ├── FetchMacOS.iml
│   │   │   ├── misc.xml
│   │   │   ├── modules.xml
│   │   │   └── workspace.xml
│   │   ├── fetch-macos.py
│   │   ├── fetch.sh
│   │   └── requirements.txt
│   ├── debug.sh
│   ├── dmg2img
│   └── template.xml.in
└── virtio.sh
Download .txt
SYMBOL INDEX (14 symbols across 1 files)

FILE: tools/FetchMacOS/fetch-macos.py
  class ClientMeta (line 22) | class ClientMeta:
  class Filesystem (line 29) | class Filesystem:
    method download_file (line 31) | def download_file(url, size, path):
    method check_directory (line 44) | def check_directory(path):
    method fetch_plist (line 52) | def fetch_plist(url):
    method parse_plist (line 59) | def parse_plist(catalog_data):
  class SoftwareService (line 66) | class SoftwareService:
    method __init__ (line 83) | def __init__(self, version, catalog_id):
    method getcatalog (line 88) | def getcatalog(self):
    method getosinstall (line 92) | def getosinstall(self):
  class MacOSProduct (line 113) | class MacOSProduct:
    method __init__ (line 114) | def __init__(self, catalog, product_id):
    method fetchpackages (line 120) | def fetchpackages(self, path, keyword=None):
  function fetchmacos (line 136) | def fetchmacos(output_dir="BaseSystem/", catalog_version="10.15", catalo...
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (58K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 209,
    "preview": "github: foxlet\ncustom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QFXXKKAB2B9MA&item_name=macOS-Sim"
  },
  {
    "path": ".gitignore",
    "chars": 61,
    "preview": ".DS_Store\nBaseSystem.img\nBaseSystem.dmg\nBaseSystem.chunklist\n"
  },
  {
    "path": ".gitmodules",
    "chars": 103,
    "preview": "[submodule \"tools/dmg2img-src\"]\n\tpath = tools/dmg2img-src\n\turl = https://github.com/foxlet/dmg2img.git\n"
  },
  {
    "path": "README.md",
    "chars": 3091,
    "preview": "# macOS-Simple-KVM\nDocumentation to set up a simple macOS VM in QEMU, accelerated by KVM.\n\nBy [@FoxletFox](https://twitt"
  },
  {
    "path": "basic.sh",
    "chars": 1061,
    "preview": "#!/bin/bash\n\nOSK=\"ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc\"\nVMDIR=$PWD\nOVMF=$VMDIR/firmware\n#exp"
  },
  {
    "path": "docs/FAQs.md",
    "chars": 931,
    "preview": "# FAQs\n\n## Q: Does this work on any CPU?\nA: There is a minimum CPU requirement for macOS itself. Both Intel and AMD CPUs"
  },
  {
    "path": "docs/guide-Apple-ID.md",
    "chars": 1070,
    "preview": "1. Use a random multicast [MAC address generator](https://www.hellion.org.uk/cgi-bin/randmac.pl) and paste it into the t"
  },
  {
    "path": "docs/guide-networking.md",
    "chars": 2850,
    "preview": "Guide to Bridged Networking\n===========================\n\n*Note*: you don't need to set up bridged networking just to get"
  },
  {
    "path": "docs/guide-passthrough.md",
    "chars": 2460,
    "preview": "Guide to PCIe Passthrough\n=========================\n\n## Enable BIOS features\nTo use PCIe Passthrough, you will need a co"
  },
  {
    "path": "docs/guide-performance.md",
    "chars": 713,
    "preview": "Guide to Performance Tuning\n===========================\nThe default macOS VM configuration (`basic.sh`) assumes some san"
  },
  {
    "path": "docs/guide-screen-resolution.md",
    "chars": 1715,
    "preview": "## How to increase screen resolution for macOS-Simple-KVM\n\n_(Thanks to [passthroughpo.st](https://passthroughpo.st/new-a"
  },
  {
    "path": "firmware/LICENSE",
    "chars": 107,
    "preview": "Copyright (c) 2019, Intel Corporation.  All rights reserved.\n\nSPDX-License-Identifier: BSD-2-Clause-Patent\n"
  },
  {
    "path": "headless.sh",
    "chars": 1449,
    "preview": "#!/bin/bash\n\nOSK=\"ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc\"\nVMDIR=$PWD\nOVMF=$VMDIR/firmware\n\n[[ "
  },
  {
    "path": "jumpstart.sh",
    "chars": 874,
    "preview": "#!/bin/bash\n\n# jumpstart.sh: Fetches BaseSystem and converts it to a viable format.\n# by Foxlet <foxlet@furcode.co>\n\nTOO"
  },
  {
    "path": "make.sh",
    "chars": 732,
    "preview": "#!/bin/bash\n\n# make.sh: Generate customized libvirt XML.\n# by Foxlet <foxlet@furcode.co>\n\nVMDIR=$PWD\nMACHINE=\"$(qemu-sys"
  },
  {
    "path": "tools/FetchMacOS/.idea/FetchMacOS.iml",
    "chars": 398,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"PYTHON_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager"
  },
  {
    "path": "tools/FetchMacOS/.idea/misc.xml",
    "chars": 254,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectRootManager\" version=\"2\" project-"
  },
  {
    "path": "tools/FetchMacOS/.idea/modules.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": "tools/FetchMacOS/.idea/workspace.xml",
    "chars": 21551,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ChangeListManager\">\n    <list default=\"t"
  },
  {
    "path": "tools/FetchMacOS/fetch-macos.py",
    "chars": 6550,
    "preview": "#!/usr/bin/python\n\n\"\"\"fetch-macos.py: Fetches macOS products from Apple's SoftwareUpdate service.\"\"\"\n\nimport logging\nimp"
  },
  {
    "path": "tools/FetchMacOS/fetch.sh",
    "chars": 1040,
    "preview": "#!/bin/bash\n\n# fetch.sh: Run fetch-macos.py with safety checks\n# by Foxlet <foxlet@furcode.co>\n\nset +x;\nSCRIPTDIR=\"$(dir"
  },
  {
    "path": "tools/FetchMacOS/requirements.txt",
    "chars": 15,
    "preview": "requests\nclick\n"
  },
  {
    "path": "tools/debug.sh",
    "chars": 785,
    "preview": "#!/bin/bash\n\n# debug.sh: Checks common virtualization programs and modules.\n# by Foxlet <foxlet@furcode.co>\n\necho \"== Di"
  },
  {
    "path": "tools/template.xml.in",
    "chars": 4183,
    "preview": "<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>\n  <name>macOS-Simple-KVM</name>\n  <uuid>d06d"
  },
  {
    "path": "virtio.sh",
    "chars": 946,
    "preview": "#!/bin/bash\n\nOSK=\"ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc\"\nVMDIR=$PWD\nOVMF=$VMDIR/firmware\n#exp"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the foxlet/macOS-Simple-KVM GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (12.6 MB), approximately 15.8k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!