EST. 2026 — Notes from real server work SYSADMIN · SECURITY · AI · DIRECTADMIN Issue 01
THE SYSADMIN ALMANAC
Home / Web / Build APM on Windows, Part 5: Compile ImageMagick 7 for PHP imagick
Web — Build APM บน Windows

Build APM on Windows, Part 5: Compile ImageMagick 7 for PHP imagick

by Ton-Or ·22 Oct 2026·14 min read
Build APM on Windows, Part 5: Compile ImageMagick 7 for PHP imagick

This part produces the two sets of files PHP needs in the next part—DLLs for runtime, and headers and libs for compiling the imagick extension—and covers the one patch needed in the official toolchain, without which the build fails with an error that gives no instructions for fixing it

The extensions I need in APM to match my production machine include imagick, and imagick is not an extension you can compile on its own. It needs ImageMagick 7 already built on the machine, including headers, .lib files for linking, and DLLs for runtime. This part therefore builds ImageMagick first; Part 6 will bring it together with PHP. I will describe every step in enough detail to follow, specifying which terminal to use, which folder to work in, and what results to expect each time.

If you are joining the series here, you can start with the series contents. Parts 1 through 4 prepared the tools, downloaded the source, and built Apache and MariaDB. Every path in this article assumes the scripts are in C:\apm-build.

What this part does and how long it takes

ImageMagick on Windows does not build with CMake as in the previous parts. Its team maintains a separate toolchain at github.com/ImageMagick/Windows: a set of scripts that clones ImageMagick and the libraries it uses (called delegates, such as jpeg, png, webp, heif, tiff, freetype, and lcms), around 40 repos in total, then generates a Visual Studio solution to build everything with MSBuild. My scripts\25-imagemagick.ps1 script wraps this process and adds two missing pieces: patching the channel mask value and arranging the output folders for imagick.

40-60 minutesFirst run
3.3 minutesRepeat run
2.8 GBGrowth in src
175 DLLIn Artifacts\bin

These times were measured on my machine (Windows 11 x64, i5-12500H, RAM 32 GB). The first run includes cloning around 40 repos and building around 170 DLLs. The 3.3-minute repeat run was measured on 22 October 2026 with the cache still present. The 2.8 GB is the entire src\imagemagick-windows folder after the build. The 175 is the number of .dll files I counted in Artifacts\bin after the build (31 of them are CORE_RL_*).

25-imagemagick.ps1: 6 steps, 40-60 minutes on the first run1 cloneat tag 7.1.2-342 patch Options.cppmask 64 to 323 build Configuremsbuild v1434 run Configure.exegenerate .sln5 msbuild full slnImageMagick + delegate6 arrange im-devheader + CORE_RL libcontinue belowStep 5 takes nearly all the time (clone log: 45 Cloning lines)Repeat run with cache: 3.3 minutes (measured 2026-10-22)
Step 2 is the only place I change the official toolchain source. Everything else is used as provided.

This part does not need vcpkg or the results from Apache and MariaDB. It needs the following five things. I will walk through checking each one in the next section.

  1. Visual Studio 2022 Build Tools with the ATL and ATLMFC components (Part 1)
  2. A standard installation of Git for Windows, because the script calls Git's bash.exe
  3. NASM in C:\apm-build\toolchain\nasm (Part 2)
  4. An internet connection, because around 40 repos must be cloned from GitHub
  5. At least 10 GB of free disk space for this part alone (the whole series requires 60 GB, as stated in Part 1)

Check the prerequisites before starting

Step 1: Open PowerShell 7 as a regular user

Open Start, type pwsh, and select PowerShell 7. Do not use Run as administrator. The entire build requires no admin privileges.

Step 2: Go to the scripts folder

Set-Location C:\apm-build

This command produces no output when successful. Check with Get-ChildItem -Name; you should see build.cmd and the scripts folder.

Step 3: Check that the tag in versions.json is 7.1.2-34

(Get-Content C:\apm-build\versions.json -Raw | ConvertFrom-Json).sources.imagemagick.version

Expected output:

7.1.2-34

This value is the ImageMagick git tag, not a download URL (its url field in the file is null, with a note that 25-imagemagick.ps1 handles cloning).

Step 4: Check that Git bash is available on the machine

Test-Path "$env:ProgramFiles\Git\bin\bash.exe"

The result must be True. The script calls this exact path to run clone-repositories.sh. If it returns False, Git is installed elsewhere or has not been installed. Go back to Part 1.

Step 5: Check that NASM is present

Test-Path C:\apm-build\toolchain\nasm\nasm.exe

The result must be True. If it returns False, run pwsh -NoProfile -File scripts\00-fetch.ps1 -Only nasm. NASM assembles assembly code used by some delegates (in my log, I saw the script assemble lib ffi's win64.asm file).

Step 6: Check that Visual Studio has ATL/MFC

$vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -version '[17.0,18.0)' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
Get-ChildItem "$vs\VC\Tools\MSVC\*\atlmfc" -Directory -Name

You should see at least one line showing the name atlmfc. If nothing appears, open Visual Studio Installer, click Modify for the Visual Studio 2022 you installed (Build Tools or any other edition), and select ATL and ATLMFC as described in Part 1. The command above uses vswhere to find the install folder, so it is not tied to the BuildTools path. If vswhere cannot find a Visual Studio 2022 with the C++ x64 tools, the build script also stops with [ERR] Visual Studio 2022 with C++ x64 tools not found; go back to Part 1. ATL/MFC is required because ImageMagick's Configure.exe is an MFC application.

(My machine has several MSVC versions installed side by side, and atlmfc is present in version 14.44, which the script uses. If your machine has only one version, you will see one line.)

Step 7: Check free disk space

(Get-PSDrive C).Free / 1GB

The result is the number of free gigabytes. It should be greater than 10. My machine had several hundred GB free, so this was not a problem.

The full script, explained section by section

This is the complete scripts\25-imagemagick.ps1 file from the downloaded package.

# 25-imagemagick.ps1 - build ImageMagick 7 (+ all delegate libs) from source with the official
# ImageMagick/Windows toolchain, then lay out headers/libs for the PHP imagick extension.
# Version comes from versions.json -> sources.imagemagick.version (an ImageMagick git tag).
param([switch]$SkipClone)
$ErrorActionPreference = 'Stop'
$Root = Split-Path $PSScriptRoot -Parent
. "$PSScriptRoot\lib.ps1"
$Tag  = (Get-Content "$Root\versions.json" -Raw | ConvertFrom-Json).sources.imagemagick.version
$W    = "$Root\src\imagemagick-windows"
$Dev  = "$Root\build\im-dev"
$Log  = "$Root\build\logs\imagemagick.log"
New-Item -ItemType Directory -Force "$Root\build\logs" | Out-Null

# 1. sources: ImageMagick at $Tag + the Configure/Dependencies versions that tag pins
if (-not $SkipClone) {
    if (-not (Test-Path $W)) { git clone --quiet https://github.com/ImageMagick/Windows.git $W }
    Push-Location $W
    & "$env:ProgramFiles\Git\bin\bash.exe" -c "./clone-repositories.sh --imagemagick7 --commit $Tag" *> "$Root\build\logs\im-clone.log"
    if ($LASTEXITCODE) { Pop-Location; throw "[ERR] clone failed (build\logs\im-clone.log)" }
    Pop-Location
}

Enter-VsEnv
$env:Path = "$Root\toolchain\nasm;$env:Path"

# 2. Configure.exe (project targets v145/VS2026; this box has v143/VS2022)
Push-Location "$W\Configure"
# Windows Configure hardcodes a 64-bit channel mask on x64, which makes MagickCore headers C++-only
# (#error in magick-baseconfig.h). imagick is C -> use 32 like the Linux default.
$opt = 'src\Options.cpp'
$src = Get-Content $opt -Raw
$patched = $src -replace '(?s)(Options::channelMaskDepth\(\) const.*?if \(architecture != Architecture::x86\)\s*return\(L")64("\);)', '${1}32$2'
if ($patched -ne $src) { Set-Content $opt $patched -NoNewline -Encoding utf8; Write-Host '[OK] Configure patched: channel mask 32' }
elseif ($src -notmatch 'channelMaskDepth') { throw '[ERR] Options.cpp layout changed - re-check the channel mask patch' }
Invoke-Step 'IM Configure.exe build' { msbuild Configure.sln /m /t:Rebuild /p:Configuration=Release,Platform=x64 /p:PlatformToolset=v143 /v:minimal /nologo } $Log
Copy-Item Artifacts\Release\x64\bin\Configure.Release.x64.exe . -Force
# 3. generate IM7.Dynamic.x64.sln: VS2022 toolset, x64, DLL build, Q16 + HDRI (IM7 default)
Invoke-Step 'IM configure' { .\Configure.Release.x64.exe /noWizard /VS2022 /x64 /dynamic /Q16 /hdri } $Log
Pop-Location

# 4. build everything (ImageMagick + delegates: jpeg, png, webp, heif, tiff, freetype, xml, zlib ...)
Push-Location $W
Invoke-Step 'IM build' { msbuild IM7.Dynamic.x64.sln /m /t:Build /p:Configuration=Release,Platform=x64 /v:minimal /nologo } $Log
Pop-Location

# 5. dev layout for imagick's config.w32: <dir>\MagickCore\*.h, <dir>\MagickWand\*.h, <dir>\CORE_RL_*.lib
if (Test-Path $Dev) { Remove-Item $Dev -Recurse -Force }
New-Item -ItemType Directory -Force "$Dev\MagickCore", "$Dev\MagickWand" | Out-Null
Copy-Item "$W\ImageMagick\MagickCore\*.h" "$Dev\MagickCore"
Copy-Item "$W\ImageMagick\MagickWand\*.h" "$Dev\MagickWand"
Copy-Item "$W\Artifacts\lib\CORE_RL_Magick*_.lib" $Dev
Write-Host "[OK] ImageMagick $Tag -> $W\Artifacts\bin ; dev layout -> $Dev"

Section 1: Variables and the -SkipClone switch

$Tag is read from versions.json, as in every script in the package (currently 7.1.2-34). $W is C:\apm-build\src\imagemagick-windows, where the entire toolchain is stored. $Dev is C:\apm-build\build\im-dev, the destination for the files PHP will use. $Log is C:\apm-build\build\logs\imagemagick.log.

The -SkipClone parameter skips all cloning steps. Use it when all the source is already present and you just want to rebuild without waiting on the network. One difference from the MariaDB script in Part 4 is that this script does not delete imagemagick.log at startup. Each run keeps appending to the log. When looking for errors, focus on the end of the file.

Section 2: Clone at the required tag (script step 1)

If the $W folder does not exist yet, the script first uses git clone to fetch the ImageMagick/Windows toolchain. It then enters that folder and calls Git's bash.exe to run ./clone-repositories.sh --imagemagick7 --commit 7.1.2-34. This .sh script belongs to the ImageMagick team. It clones ImageMagick itself, checks out the tag I passed, then clones the Configure and Dependencies repos and each delegate repo, setting them to the versions specified by that tag (the HOWTO records that Configure and Dependencies versions are pinned automatically according to the tag).

None of the output appears on screen, because *> redirects it to build\logs\im-clone.log. These are examples of actual lines in that file:

Cloning ImageMagick
HEAD is now at a0c4bf729 release

Cloning Configure
HEAD is now at 08ea96d Bump _WIN32_WINNT to Windows 10 to fix issues with the cairo build.

Cloning Dependencies
HEAD is now at ab03aca Bump actions/create-github-app-token from 3.2.0 to 3.3.0 (#47)

Cloning aom
HEAD is now at 949b208 Move part of the header to a separate file to reduce the binary size.
Cloning fontconfig
HEAD is now at 943048d Updated fontconfig to 2.18.3

The value a0c4bf729 is the ImageMagick commit that tag 7.1.2-34 points to. The same identifier appears in the magick -version output at the end of this part (a0c4bf729:20261014), confirming that the binary came from the source I requested.

In the same file, I saw the line grep: ImageMagick/.github/build/windows/download-dependencies.sh: No such file or directory after the Configure cloning step. This line did not stop the script, and my build completed successfully. I did not investigate what it affects, so all I can say is that it appeared in my log but did not cause a failure. My clone log contains 45 lines with Cloning.

If cloning fails, the script checks $LASTEXITCODE and throws [ERR] clone failed (build\logs\im-clone.log). Open that file and look at the end. The usual cause is a network connection dropping midway. You can simply run the script again. When I reran it, I saw Already up to date. in the log, meaning repos that had already been cloned were not downloaded again.

Section 3: Prepare the environment

Enter-VsEnv loads MSVC into this PowerShell session (as in Part 4), then adds toolchain\nasm to the front of PATH so MSBuild can find nasm.exe.

Section 4: Patch the channel mask (script step 2)

This is the most important section of this part. I explain it separately in the next section.

Section 5: Build Configure.exe with toolset v143

Configure.exe is ImageMagick's program that reads options and generates the solution (.sln) for the actual build. The program itself must be built first, using this command:

msbuild Configure.sln /m /t:Rebuild /p:Configuration=Release,Platform=x64 /p:PlatformToolset=v143 /v:minimal /nologo
Option Meaning
/m Build multiple projects in parallel according to the core count
/t:Rebuild Rebuild everything on every run without reusing previous results (because its source has just been changed)
/p:Configuration=Release,Platform=x64 Build configuration and architecture
/p:PlatformToolset=v143 Force the use of the MSVC v143 compiler from VS 2022
/v:minimal /nologo Reduce the amount of output

I need to force v143 because the Configure project specifies toolset v145, which belongs to Visual Studio 2026 (the script comment says the same thing). My machine uses VS 2022, so v145 is unavailable. Without this option, the build fails because it cannot find the toolset.

After the build, the program is at Configure\Artifacts\Release\x64\bin\Configure.Release.x64.exe. The script uses Copy-Item to place it in the Configure folder so it can be called with a short name.

Section 6: Have Configure.exe generate the .sln file (script step 3)

.\Configure.Release.x64.exe /noWizard /VS2022 /x64 /dynamic /Q16 /hdri

I use the .\ prefix because some machines have NoDefaultCurrentDirectoryInExePath=1 set, which prevents cmd and PowerShell from searching for files in the current folder (I ran into the same issue when building PHP). Each option means the following:

Option Meaning
/noWizard Do not open a selection window; accept options only from the command line (important, otherwise the script waits for user input)
/VS2022 Generate a solution for Visual Studio 2022
/x64 64-bit architecture
/dynamic Build DLLs (the CORE_RL_*.dll files), rather than static libraries
/Q16 Quantum color depth of 16 bits per channel (the ImageMagick 7 default)
/hdri Enable HDRI to support color values outside the normal range (the ImageMagick 7 default)

The result is IM7.Dynamic.x64.sln at the root of src\imagemagick-windows, the only file the next step needs.

Section 7: Build everything (script step 4)

msbuild IM7.Dynamic.x64.sln /m /t:Build /p:Configuration=Release,Platform=x64 /v:minimal /nologo

This is the part that takes nearly all the time. It compiles ImageMagick and all delegate libraries specified by ImageMagick/Windows. Notice that this time it uses /t:Build, rather than Rebuild, so it can continue from previous results. That is why a repeat run takes only 3.3 minutes.

These lines appear in imagemagick.log during the build (I changed the path to C:\apm-build):

  Assembling C:\apm-build\src\imagemagick-windows\Dependencies\Dependencies\ffi\src\x86\win64.asm...
  aom_codec.c
  APP_magick.vcxproj -> C:\apm-build\src\imagemagick-windows\Artifacts\bin\magick.exe
  APP_compare.vcxproj -> C:\apm-build\src\imagemagick-windows\Artifacts\bin\compare.exe

This solution also builds fuzz programs (names starting with FUZZ_) and other ImageMagick tools that I do not use, but I leave them as provided and do not change the solution.

Section 8: Arrange the im-dev folder (script step 5)

The script deletes build\im-dev if it exists, then recreates it. Inside are three parts: MagickCore headers, MagickWand headers, and three import libraries named CORE_RL_Magick*_.lib. This is because imagick's configuration file (config.w32) looks for exactly this layout: <โฟลเดอร์>\MagickCore\*.h, <โฟลเดอร์>\MagickWand\*.h, and <โฟลเดอร์>\CORE_RL_*.lib. In Part 6, I will pass this folder through --with-imagick=shared,build\im-dev.

The last line of the script prints [OK] ImageMagick 7.1.2-34 -> ...\Artifacts\bin ; dev layout -> ...\build\im-dev.

Two sets of output, used at different timesArtifacts\bin175 DLL (31 are CORE_RL)+ magick.exe for checksbuild\im-devMagickCore + MagickWand+ 3 CORE_RL_*.lib files (2.7 MB)Copy beside php.exePart 6 does this automaticallyWhen building imagick--with-imagick=shared,build\im-devDLLs are needed at runtime; headers and .lib files only at compile time
If im-dev is missing, PHP cannot build imagick. If the DLLs are missing, PHP cannot load imagick.

The one required patch: channel mask from 64 to 32

I encountered this error on my first imagick build, and its cause is inside ImageMagick, not PHP, so it needs to be fixed when building ImageMagick.

Symptoms

When building the imagick extension in Part 6, this error appears:

ImageMagick was build with a 64 channel bit mask and that requires a C++ compiler

It comes from ImageMagick's own MagickCore\magick-baseconfig.h header. I opened the actual file, and lines 266 through 270 contain:

#if MAGICKCORE_CHANNEL_MASK_DEPTH == 64
#  if !defined(__cplusplus) && !defined(c_plusplus)
#    error ImageMagick was build with a 64 channel bit mask and that requires a C++ compiler
#  endif
#endif

Cause

MAGICKCORE_CHANNEL_MASK_DEPTH is set when Configure.exe runs. On x64, this program always selects 64 automatically, making the generated header unusable with C code. Since imagick is written in C, it runs into this restriction. On Linux, the default is 32, which also matches my production machine, so I chose to change it to 32.

Before patch: build failsDefault Configuremask 64 on x64magick-baseconfig.h#error unless C++imagick (C)build failsAfter patch: build passesOptions.cppboth branches return 32magick-baseconfig.hCHANNEL_MASK_DEPTH 32imagick (C)passes32 is the Linux default and matches the production machine
The error message is ImageMagick was build with a 64 channel bit mask and that requires a C++ compiler

How the script fixes it

This value is returned by Options::channelMaskDepth() in Configure\src\Options.cpp. The script patches that file before building Configure, using one line of regex:

$patched = $src -replace '(?s)(Options::channelMaskDepth\(\) const.*?if \(architecture != Architecture::x86\)\s*return\(L")64("\);)', '${1}32$2'

Here is what each part does:

  • (?s) makes the dot . match newlines too, because the function spans multiple lines.
  • (Options::channelMaskDepth\(\) const.*?if \(architecture != Architecture::x86\)\s*return\(L") matches from the start of the function through return(L" under the "not x86" condition (group 1). The .*? matches as little as possible so it does not extend into other functions.
  • 64 is the number to replace.
  • ("\);) is group 2, the closing ");.
  • The replacement ${1}32$2 puts it back together as group 1 + 32 + group 2.

After the patch, the function in my file reads as follows (both branches return 32 because the x86 branch was already 32):

const wstring Options::channelMaskDepth() const
{
  if (!isImageMagick7)
    return(L"");

  if (architecture != Architecture::x86)
    return(L"32");
  else
    return(L"32");
}

The script has two safeguards. If it makes the change, it prints [OK] Configure patched: channel mask 32. If nothing changes (because it was patched on a previous run), it stays silent. If the file does not contain channelMaskDepth at all (meaning the ImageMagick team has changed the file structure), the script throws [ERR] Options.cpp layout changed - re-check the channel mask patch instead of continuing the build with 64 and failing later when building PHP.

This number must match production

I chose 32 because I want images processed through imagick to match the results on my production machine, which uses this value. I have not tested the 64-bit case. This series uses only 32.

Run it step by step

Step 1: Run the script

In the same terminal (PowerShell 7 as a regular user, in C:\apm-build), run:

pwsh -NoProfile -File scripts\25-imagemagick.ps1

The screen immediately goes quiet for a while because cloning is in progress and output is going to a file. No messages appear until the clone step finishes. If you are using build.cmd and it fails at this step, use build.cmd -From imagemagick to resume (it will continue with the php step afterward).

Step 2: Open a second terminal to watch cloning progress (optional)

Get-Content C:\apm-build\build\logs\im-clone.log -Tail 5 -Wait

You will see Cloning ... lines appear, one repo at a time. When they stop, press Ctrl+C to stop watching (this does not affect the main script).

Step 3: Watch for the Configure.exe build to start

When cloning finishes, the first window shows the following (on the first run only, the patch line appears first):

[OK] Configure patched: channel mask 32
[..] IM Configure.exe build
[OK] IM Configure.exe build
[..] IM configure
[OK] IM configure
[..] IM build

The longest quiet period is between [..] IM build and [OK] IM build. Watch the actual activity with:

Get-Content C:\apm-build\build\logs\imagemagick.log -Tail 5 -Wait

Step 4: Wait for the whole solution to build

This takes most of the 40-60 minutes. The CPU will be busy across all cores. Do not close the window or let the machine sleep. If it stops midway, simply repeat Step 1 (you can use -SkipClone if cloning has finished). MSBuild will continue from the previous results.

Step 5: Check the script's final line

[OK] IM build
[OK] ImageMagick 7.1.2-34 -> C:\apm-build\src\imagemagick-windows\Artifacts\bin ; dev layout -> C:\apm-build\build\im-dev

Step 6: Check that magick.exe works

C:\apm-build\src\imagemagick-windows\Artifacts\bin\magick.exe -version

Expected output (this is the actual output from my machine):

Version: ImageMagick 7.1.2-34 Q16-HDRI x64 a0c4bf729:20261014 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/license/
Features: Cipher DPC HDRI Modules OpenCL OpenMP(2.0)
Delegates (built-in): bzlib cairo freetype gslib heic jng jp2 jpeg jxl lcms lqr lzma openexr pangocairo png ps raqm raw rsvg tiff webp xml zip zlib zstd
Compiler: Visual Studio 2022 (194435217)

Check three things: Q16-HDRI x64 matches the /Q16 /hdri /x64 options passed to Configure, a0c4bf729 matches the cloned commit, and Delegates (built-in) lists all the libraries built in.

Step 7: Check that the channel mask is 32

Select-String -Path C:\apm-build\src\imagemagick-windows\ImageMagick\MagickCore\magick-baseconfig.h -Pattern 'define MAGICKCORE_CHANNEL_MASK_DEPTH'

Expected output (the line ends with 32; the filename and line number will appear at the beginning):

#define MAGICKCORE_CHANNEL_MASK_DEPTH 32

If you get 64, the patch was not applied. Go to "If something fails here".

Step 8: Check the im-dev folder

Get-ChildItem C:\apm-build\build\im-dev -Name

Expected output:

MagickCore
MagickWand
CORE_RL_Magick++_.lib
CORE_RL_MagickCore_.lib
CORE_RL_MagickWand_.lib

(The display order may differ, but all five entries must be present.) The whole folder is around 2.7 MB on my machine.

Step 9: Try an actual image conversion

This is an extra test I ran outside the script to confirm that the delegates actually work, rather than merely appearing in the list. Create a solid-color 320x200 image and save it as webp.

C:\apm-build\src\imagemagick-windows\Artifacts\bin\magick.exe -size 320x200 xc:'#3366cc' $env:TEMP\apm-test.webp

There is no output on success. Check that the file exists with (Get-Item $env:TEMP\apm-test.webp).Length. My result was 240 bytes. You can also try changing the extension to .avif, .png, or .jpg. I got 318, 315, and 1038 bytes respectively (the size depends on the encoder version, so a small difference is fine as long as the file is created and there are no error messages). Delete the test files afterward with Remove-Item $env:TEMP\apm-test.*.

A limitation to know: HEIC files cannot be written

Try writing HEIC the same way as in Step 9.

C:\apm-build\src\imagemagick-windows\Artifacts\bin\magick.exe -size 320x200 xc:'#3366cc' $env:TEMP\apm-test.heic

I got this message (the actual message):

magick.exe: no encode delegate for this image format `HEIC' @ warning/constitute.c/WriteImage/1473.
magick.exe: no encode delegate for this image format `HEIC' @ error/constitute.c/WriteImage/1482.

Check the supported format list.

C:\apm-build\src\imagemagick-windows\Artifacts\bin\magick.exe -list format | Select-String 'HEIC'
     AVIF  HEIC      rw+   AV1 Image File Format (1.23.6)
     HEIC  HEIC      r--   High Efficiency Image Format (1.23.6)

Read the permissions column: r-- for HEIC means read-only; it cannot write. The rw+ for AVIF means it can read and write. According to the HOWTO, this is because the HEIC encoder (x265) is not included for licensing reasons. HEIC images from phones can still be read and converted to jpeg or webp normally, but cannot be written as HEIC. webp, avif, png, and jpeg can all be read and written.

Read and write

webp, avif, png, jpeg

I tested writing all four formats through magick.exe with a 320x200 image and got a file in every format. They can be used as output formats for an image resizing system.

Read-only

heic

Images from phones can be accepted, but cannot be saved as heic. They must always be converted to another format first.

If something fails here

This table collects symptoms recorded in the HOWTO from actual builds. The last two rows are marked as causes identified from reading the script, rather than ones I encountered myself.

Symptom Cause Fix Source
ImageMagick was build with a 64 channel bit mask and that requires a C++ compiler (when building imagick in Part 6) Configure sets mask 64 on x64 Make sure the Options.cpp patch is applied (Step 7 above must return 32), then rebuild ImageMagick Encountered directly
Configure fails to build because toolset v145 cannot be found The project is set up for VS 2026 Add /p:PlatformToolset=v143 and run Configure with /VS2022 (the script already does this) Encountered directly
Class "Imagick" not found when running PHP extension = imagick is missing from php.ini It has been added to the template file; if you deployed earlier, run deploy.cmd -ResetConfig (Part 7) Encountered directly
[ERR] Options.cpp layout changed - re-check the channel mask patch The ImageMagick team changed the file structure, so the regex cannot find the function Open Configure\src\Options.cpp, inspect the function that now returns the channel mask, and adjust the regex in the script Message from the script; I did not encounter it myself
[ERR] clone failed (build\logs\im-clone.log) The network connection dropped, or Git's bash.exe is not at the path the script calls Check the end of im-clone.log, fix the network or install Git at the expected path, then rerun Message from the script; I did not encounter it myself
Configure.sln fails to build with an ATL/MFC-related error ATL or ATLMFC components are missing Visual Studio Installer, Modify, select ATL and ATLMFC The HOWTO states that Configure is an MFC application (I did not save the actual error message)

When the IM build step fails, the error message gets buried in a four-thousand-line log. The script prints the last 30 lines, but the actual error line is often further up. Use this command to find errors in the format MSBuild writes:

Select-String -Path C:\apm-build\build\logs\imagemagick.log -Pattern ': error ' | Select-Object -Last 10
Do not use Git's perl with this package

If you previously changed PATH to put Git for Windows' perl (the cygwin version) first, remove that entry before building the whole package. The toolchain requires only Strawberry Perl in toolchain\strawberry. I encountered this myself when building OpenSSL in the same package.

Checklist before moving to Part 6

  1. The script ends with [OK] ImageMagick 7.1.2-34 -> ...
  2. magick.exe -version shows ImageMagick 7.1.2-34 Q16-HDRI x64 and commit a0c4bf729
  3. The MAGICKCORE_CHANNEL_MASK_DEPTH line in magick-baseconfig.h is 32
  4. C:\apm-build\build\im-dev contains MagickCore, MagickWand, and three CORE_RL_*.lib files
  5. magick.exe can write webp, avif, png, and jpg files (HEIC cannot be written, as expected)
Don't want to build it yourself

If you just want to use APM without compiling anything, the finished build is at github.com/ChonLaWan/APM-Server. Download APM-1.0.0-win64.zip (about 140 MB) from Releases, compare its sha256 with the attached .sha256 file, unzip it anywhere and run manager\APMManager.exe. The Manager picks free ports, writes the config, issues the certificate and creates the database for you. Its Check for updates button downloads a new release, verifies the sha256 and swaps the binaries without touching your config, databases or sites.

Build APM on Windows series

Contents · Previous: Part 4: Compile MariaDB · Next: Part 6: Compile PHP 8.4

The next part covers PHP itself. I will use build\im-dev and the DLLs from this part to build the imagick extension, along with more than fifty other extensions. For things I have not tried in this part, such as enabling the HEIC encoder myself, I do not yet have verified test results. I will come back and add them later.

Want us to set this up?

Get help with server setup, configuration, migration, or troubleshooting. Save time for the work that matters.

Get a quote

Hit something like this? Stuck somewhere?

Come trade notes on the LinuxThai board

→ Continue on the board

Comments 0

No comments yet — be the first