WinPythonを、WiX4で配置する。

この記事は「[Python実行環境を配布して、.NETアプリから利用する。](https://qiita.com/hiro_t/items/a2d67e183fbca0b8df3b)」への追記です。

# 目標
[WiX Toolset](https://wixtoolset.org/)で、[WinPython](https://winpython.github.io/)を、指定したフォルダに配置するセットアップファイルを作成

# 方法

`MSI Package`を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/9578c9f8-025e-95c8-ff9d-bce42bdcfbc8.png)

`NuGet`で、`WixToolset.Heat`をインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/ca9b7b22-85aa-7f02-b580-84fc37b5a832.png)

## プロジェクトのプロパティ
```xml
<Project Sdk="WixToolset.Sdk/4.0.3">
  <PropertyGroup>
    <!--
    _HarvestedComponents_dir.wxsで、
    error WIX0231: The component '*******' has a key file with path 'TARGETDIR\.\python\python3.12\idle (python gui).exe'. Since this path is not rooted in one of the standard directories (like ProgramFilesFolder), this component does not fit the criteria for having an automatically generated guid. (This error may also occur if a path contains a likely standard directory such as nesting a directory with name "Common Files" under ProgramFilesFolder.)
    のエラーを回避するために、Guidの生成をAutoにせず、すぐに作成する。
    -->
    <HarvestDirectoryAutogenerateGuids>false</HarvestDirectoryAutogenerateGuids>
    <HarvestDirectoryGenerateGuidsNow>true</HarvestDirectoryGenerateGuidsNow>
  </PropertyGroup>
  <ItemGroup>
    <!--WinPythonのフォルダを指定-->
    <HarvestDirectory Include="C:\HOGE\FUGA\Python3.12">
      <ComponentGroupName>HarvestedComponents</ComponentGroupName>
      <SuppressRootDirectory>true</SuppressRootDirectory>
      <DirectoryRefId>INSTALLFOLDER</DirectoryRefId>
    </HarvestDirectory>
    <!--WinPythonのフォルダを指定-->
    <BindPath Include="C:\HOGE\FUGA\Python3.12" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="WixToolset.Heat" Version="4.0.3" />
  </ItemGroup>
</Project>
```
### 収集するWinPythonを指定
収集する`WinPython`のフォルダを、2カ所、設定します。このフォルダのファイルが、`Heat`で収集(Harvest)されます。

### GUID
```xml
HarvestDirectoryAutogenerateGuids
HarvestDirectoryGenerateGuidsNow
```
を上記のように設定することで、
自動的に作成される`_HarvestedComponents_dir.wxs`が、GUIDが作成された状態で、作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/6786135f-e032-85e3-abfd-e00eabd665ee.png)

## Folders.wxs
このファイルは使わないので、`<Fragment>`部分をコメントアウトしておきます。ファイル自体を削除してもいいかもしれません。
```xml
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
  <!--<Fragment>
    <StandardDirectory Id="ProgramFiles6432Folder">
      <Directory Id="INSTALLFOLDER" Name="!(bind.Property.Manufacturer) !(bind.Property.ProductName)" />
    </StandardDirectory>
  </Fragment>-->
</Wix>
```

## Package.wxs
次のように設定します。

```xml
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
  <Package Name="PythonSetup" Manufacturer="TODO Manufacturer" Version="1.0.0.0" UpgradeCode="PUT GUID">
    <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
    
    <!--cabファイルをmsiに含める-->
    <MediaTemplate EmbedCab="yes" />
    
    <Feature Id="Main">
      <!--ExampleComponentsは利用していない-->
      <!--<ComponentGroupRef Id="ExampleComponents" />-->
      <ComponentGroupRef Id="HarvestedComponents" />
    </Feature>
  </Package>
  
  <Fragment>
    <!--Windowsのあるドライブ＞Python>Pthon3.12にインストールする-->
    <SetDirectory Id="WINDOWSVOLUME" Value="[WindowsVolume]"/>
    <Directory Id="WINDOWSVOLUME">
      <Directory Id="WindowsVolume_PythonDIR" Name="Python">
        <Directory Id="INSTALLFOLDER" Name="Python3.12" />
      </Directory>
    </Directory>
  </Fragment>
</Wix>
```

- `MediaTemplate`で、セットアップファイルを`cabファイル`に分離せず、`msiファイル`にまとめます。
- `ExampleComponents`は利用していないので、コメントアウトします。ファイルを削除しても良いかもしれません。
- `<Fragment>`で、`Windowsのあるドライブ＞Python>Pthon3.12`をインストールフォルダに指定しています。
Windowsのあるドライブとは、おおむねCドライブです。

## ビルド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/f2cdc577-cda0-de28-2404-168f74b23bf4.png)

ビルドすると、`msiファイル`が作成されます。

この`msiファイル`は、最小限のものなので、実行すると、すぐにインストールが始まります。

## アンインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/35e149c9-503d-a1d0-90da-35bbd5302919.png)

アンインストールした時、インストールしたフォルダには、設定や`pycache`が残ります。
WiXで、フォルダごと削除する設定もできるようです（未検証）。

# WinPythonをMSIファイルで配置する利点
- WinPythonのフォルダに、**運用に必要なPythonのライブラリを含めて**、配布できる。
- 展開先を、指定できる。
- アンインストール時も、`Windows標準のアプリ＞インストールされているアプリ`から削除できる。