EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Web / Build APM บน Windows ตอนที่ 2: ดึง source และ lib ด้วย vcpkg
Web — Build APM บน Windows

Build APM บน Windows ตอนที่ 2: ดึง source และ lib ด้วย vcpkg

โดย Ton-Or ·22 ต.ค. 2026·อ่าน 14 นาที
Build APM บน Windows ตอนที่ 2: ดึง source และ lib ด้วย vcpkg

ก่อนจะคอมไพล์อะไรได้ ต้องมี source ที่ตรวจแล้วว่าไม่ใช่ไฟล์ปลอม กับ lib ที่ Apache ต้องใช้ครบทุกตัว สองงานนี้ใช้เวลารวมเกือบชั่วโมงในรอบแรก แต่ทำครั้งเดียว รอบถัดไปได้ของเดิมจาก cache เกือบทั้งหมด

พอดีผมต้องเขียนขั้นตอนนี้ให้คนที่ไม่เคยคอมไพล์อะไรเลยทำตามได้ เลยเปิดสคริปต์ทั้งสองตัวขึ้นมาอ่านใหม่ทีละบรรทัด แล้วเอาทุกคำสั่งในบทความนี้ไปตรวจกับเครื่องที่ผม build ไว้แล้วอีกรอบ ตอนที่ 2 นี้เป็นตอนที่ผมอยากให้คุณอ่านช้า ๆ ที่สุด เพราะเป็นตอนที่ตัดสินว่าของที่จะถูกคอมไพล์เป็น web server ของคุณมาจากไหน และเชื่อถือได้แค่ไหน ถ้า source ที่ดาวน์โหลดมาถูกสลับไส้ ต่อให้ขั้นต่อ ๆ ไปทำถูกทุกบรรทัด ผลที่ได้ก็คือ Apache ที่มีของแถมโดยที่เราไม่รู้

ตอนนี้ทำสองสคริปต์ คือ 00-fetch.ps1 ดาวน์โหลดและตรวจ hash ของ source กับเครื่องมือทั้งหมด และ 10-deps-vcpkg.ps1 ให้ vcpkg คอมไพล์ lib ที่ Apache ต้องใช้ (OpenSSL, PCRE2, zlib, nghttp2, brotli, expat, libxml2, jansson, curl) จาก source ด้วย MSVC ถ้ายังไม่ได้อ่านตอนที่ 1 ที่ติดตั้ง Build Tools กับแตกชุด kit ไว้ที่ C:\apm-build ให้กลับไปทำก่อน เพราะตอนนี้ถือว่าเครื่องมือครบแล้ว

ที่มาของซีรีส์

PHP ออกแพตช์ช่องโหว่ RCE ระดับวิกฤตสองตัวเมื่อ 2026-10-21 (รายละเอียดอยู่ใน บทความข่าว php2shell) ผมเลย build stack ของตัวเองให้เปลี่ยนเลขเวอร์ชันในไฟล์ versions.json ไฟล์เดียวแล้ว build ใหม่ได้ในวันเดียวกับที่ upstream ปล่อย ตอนที่ 2 คือส่วนที่ทำให้ข้อนั้นเป็นจริง เพราะ hash ของ source ทุกตัวถูกตรวจก่อนใช้

13ไฟล์ที่ดาวน์โหลด
560 MBขนาดรวมไฟล์
3-5 นาทีfetch รอบแรก
45 นาทีvcpkg รอบแรก

1. เตรียมตัว: เช็กว่าตอนที่ 1 เสร็จจริง

ทุกขั้นในตอนนี้และตอนต่อ ๆ ไปรันด้วย PowerShell 7 (pwsh) ในสิทธิ์ user ปกติ ไม่ต้องเปิดเป็น Administrator สคริปต์ build โหลด environment ของ MSVC ให้เอง เลยไม่ต้องเปิด Developer Prompt ส่วนข้อ 1 นี้เป็นการเช็กก่อนลงมือ ทำทีละข้อตามลำดับ

ขั้นที่ 1.1 เปิด pwsh

ที่ไหน: กด Start พิมพ์ pwsh แล้วเปิด PowerShell 7 ธรรมดา (ไม่ใช่ Run as administrator) หัวหน้าต่างควรเขียนว่า PowerShell 7 ไม่ใช่ Windows PowerShell

$PSVersionTable.PSVersion.ToString()

ควรเห็นเลขรุ่น 7.5 ขึ้นไป (เครื่องผมได้ 7.6.7) ถ้าได้ 5.1 แปลว่าเปิดผิดตัว สคริปต์ 00-fetch.ps1 ใช้ ConvertFrom-Json -AsHashtable ซึ่งมีแค่ใน PowerShell 6 ขึ้นไป รัน 5.1 จะล้มทันที

ขั้นที่ 1.2 เข้าโฟลเดอร์ของ kit

Set-Location C:\apm-build
Get-ChildItem -Name

ควรเห็นรายการนี้ (ลำดับอาจต่างเล็กน้อย)

deps
ports-overlay
scripts
templates
tools
.gitignore
build.cmd
deploy.cmd
README.txt
versions.json
versions.lock.json

ถ้าไม่เห็น build.cmd ตรงนี้ แปลว่าแตก zip ซ้อนโฟลเดอร์ (ได้ C:\apm-build\apm-build-kit\build.cmd) ให้ย้ายทุกอย่างขึ้นมาหนึ่งชั้นตามที่ตอนที่ 1 บอกไว้ ก่อนไปต่อ

ขั้นที่ 1.3 เช็กว่าเครื่องมือหลักตอบ

git --version; cmake --version | Select-Object -First 1; ninja --version; pwsh --version
git version 2.54.0.windows.1
cmake version 4.1.2
1.13.1
PowerShell 7.6.1

เลขรุ่นของคุณอาจใหม่กว่านี้ได้ ขอแค่ทุกตัวตอบกลับมา ถ้าตัวไหนขึ้น is not recognized ให้เปิด pwsh ใหม่ (PATH เพิ่งถูกเพิ่มหลัง winget) แล้วถ้ายังไม่ได้ให้กลับไปตอนที่ 1

ขั้นที่ 1.4 เช็กพื้นที่ดิสก์

[math]::Round((Get-PSDrive C).Free / 1GB, 1)

ต้องได้เลขไม่ต่ำกว่า 60 (หน่วย GB) เพราะ build ทั้งซีรีส์กินที่ประมาณ 45 GB ตอนที่ 2 ใช้ไม่มาก แต่ถ้าดิสก์เต็มกลางทางตอนที่ 5 หรือ 6 จะเสียเวลากว่าตอนนี้เยอะ

ตอนที่ 2 ทำอะไรversions.jsonล็อกเวอร์ชันและ hash->00-fetch.ps1โหลด ตรวจ แตกไฟล์->10-deps-vcpkg.ps1build lib 9 ตัว->build\vcpkg_installedส่งต่อตอนที่ 3
แถวบน: ดาวน์โหลด ตรวจ hash แตกไฟล์ (00-fetch.ps1)versions.jsonเวอร์ชัน + hash00-fetch.ps1curl + tardownloads\13 ไฟล์ ประมาณ 560 MBtoolchain\ + src\แตกไฟล์พร้อมใช้แถวล่าง: lib ของ Apache (10-deps-vcpkg.ps1)deps\apache\vcpkg.jsonports-overlay\openssl\10-deps-vcpkg.ps1vcpkg installbuild\vcpkg_installed\x64-windowslib\ bin\ include\ DLL 18 ตัวคลัง vcpkg ส่วนตัวถูก clone ไปที่ toolchain\vcpkg (ไม่แตะ C:\vcpkg)ผลของสองแถวนี้คืออินพุตของตอนที่ 3 (20-apache.ps1)
สองสคริปต์ของตอนนี้ แถวบนได้ source และเครื่องมือ แถวล่างได้ lib ที่ build จาก source ด้วย MSVC ทั้งหมด

2. 00-fetch.ps1: ดาวน์โหลดแล้วตรวจ hash ก่อนแตกไฟล์

สคริปต์ตัวนี้ทำหน้าที่เดียวคือ อ่าน versions.json แล้วดึงทุกรายการที่มี url ลงโฟลเดอร์ downloads\ ตรวจ sha256 แล้วแตกไฟล์ไปไว้ใน toolchain\ (เครื่องมือ) หรือ src\ (source) นี่คือตัวสคริปต์เต็ม อ่านมันก่อนสักรอบแล้วค่อยดูคำอธิบายทีละช่วงข้างล่าง

# 00-fetch.ps1 - download + verify + extract everything pinned in versions.json
# Usage: pwsh scripts\00-fetch.ps1 [-Only php,httpd] [-Force]
param([string[]]$Only, [switch]$Force)
$ErrorActionPreference = 'Stop'
# `pwsh -File ... -Only php,httpd` passes ONE string "php,httpd" (no array parsing in -File mode)
$Only = @($Only | ForEach-Object { $_ -split ',' } | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$Root = Split-Path $PSScriptRoot -Parent
$V    = Get-Content "$Root\versions.json" -Raw | ConvertFrom-Json
$LockFile = "$Root\versions.lock.json"
$Lock = if (Test-Path $LockFile) { Get-Content $LockFile -Raw | ConvertFrom-Json -AsHashtable } else { @{} }
$Dl   = "$Root\downloads"; New-Item -ItemType Directory -Force $Dl, "$Root\src" | Out-Null

function Get-One($name, $item, $destDir, $strip) {
    if ($Only -and $name -notin $Only) { return }
    if (-not $item.url) { return }   # git-based sources (imagemagick) are fetched by their own build script
    $file = Join-Path $Dl ([IO.Path]::GetFileName(([uri]$item.url).AbsolutePath))
    if (-not (Test-Path $file) -or $Force) {
        Write-Host "[GET] $name $($item.version)"
        curl.exe -fsSL --retry 3 -o "$file.part" $item.url
        if ($LASTEXITCODE) { throw "download failed: $($item.url)" }
        Move-Item -Force "$file.part" $file
    }
    $sha = (Get-FileHash $file -Algorithm SHA256).Hash.ToLower()
    $want = if ($item.sha256) { $item.sha256 } elseif ($Lock[$name]) { $Lock[$name] } else { $null }
    if ($want -and $sha -ne $want) { throw "[ERR] sha256 mismatch $name`n  got  $sha`n  want $want" }
    if ($item.sha1) {
        $s1 = (Get-FileHash $file -Algorithm SHA1).Hash.ToLower()
        if ($s1 -ne $item.sha1) { throw "[ERR] sha1 mismatch $name" }
    }
    if (-not $want) { Write-Host "[TOFU] $name sha256=$sha" }
    $Lock[$name] = $sha

    if ((Test-Path $destDir) -and -not $Force) { Write-Host "[OK] $name (already extracted)"; return }
    if (Test-Path $destDir) { Remove-Item $destDir -Recurse -Force }
    New-Item -ItemType Directory -Force $destDir | Out-Null
    tar.exe -xf $file -C $destDir --strip-components=$strip
    if ($LASTEXITCODE) { throw "extract failed: $file" }
    Write-Host "[OK] $name -> $destDir"
}

foreach ($p in $V.toolchain.PSObject.Properties) {
    Get-One $p.Name $p.Value (Join-Path $Root $p.Value.dest) $p.Value.strip
}
foreach ($p in $V.sources.PSObject.Properties) {
    Get-One $p.Name $p.Value (Join-Path "$Root\src" "$($p.Name)-$($p.Value.version)") 1
}
$Lock | ConvertTo-Json | Set-Content $LockFile -Encoding utf8

ไฟล์ versions.json ที่สคริปต์อ่าน

ผมย่อ hash ให้สั้นลงเพื่อให้อ่านง่าย ไฟล์จริงในชุด kit มี hash เต็ม 64 ตัวอักษร

{
  "apm_version": "1.0.0",
  "toolchain": {
    "php-sdk": { "version": "2.8.4", "url": "https://github.com/php/php-sdk-binary-tools/archive/refs/tags/php-sdk-2.8.4.zip", "sha256": null, "dest": "toolchain/php-sdk", "strip": 1 },
    "nasm":    { "version": "3.01", "url": "https://www.nasm.us/pub/nasm/releasebuilds/3.01/win64/nasm-3.01-win64.zip", "sha256": null, "dest": "toolchain/nasm", "strip": 1 }
  },
  "sources": {
    "php":       { "version": "8.4.27", "url": "https://www.php.net/distributions/php-8.4.27.tar.xz", "sha256": "dcf93923b0e38e0d…" },
    "httpd":     { "version": "2.4.69", "url": "https://downloads.apache.org/httpd/httpd-2.4.69.tar.bz2", "sha256": "c5e6ebc66e349b87…" },
    "mod_fcgid": { "version": "2.3.9",  "url": "https://archive.apache.org/dist/httpd/mod_fcgid/mod_fcgid-2.3.9.tar.bz2", "sha256": null, "sha1": "f0b6d87dfcfe18b3…" },
    "imagemagick": { "version": "7.1.2-34", "url": null, "sha256": null }
  }
}

ในไฟล์จริงมี toolchain 4 ตัว (php-sdk, strawberry, nasm, winflexbison) กับ sources 10 ตัว (php, httpd, apr, apr-util, mod_fcgid, mariadb, pecl-redis, pecl-igbinary, pecl-imagick, imagemagick) ข้อสังเกตสามอย่างที่ต้องรู้ก่อนอ่านสคริปต์

  • ค่า "sha256": null ไม่ได้แปลว่าไม่ตรวจ มันแปลว่าต้นทางไม่ได้ประกาศ hash ไว้ ให้สคริปต์ไปใช้ versions.lock.json แทน (อธิบายในหัวข้อถัดไป)
  • strip คือจำนวนชั้นโฟลเดอร์ที่ตัดทิ้งตอนแตกไฟล์ tarball ส่วนใหญ่มีโฟลเดอร์ชั้นนอกชื่อ httpd-2.4.69\ อยู่ข้างใน เราตัดมันทิ้ง (strip เป็น 1) เพื่อให้ได้ src\httpd-2.4.69\CMakeLists.txt ตรง ๆ ไม่ซ้อนสองชั้น
  • imagemagick มี url เป็น null เพราะไม่ใช่ไฟล์ให้โหลด แต่เป็น git tag ที่ตอนที่ 5 ไป clone เอง สคริปต์เจอ url ว่างก็ข้ามเงียบ ๆ ไม่พิมพ์อะไร

อ่านฟังก์ชัน Get-One ตามลำดับที่มันรัน

  1. กรอง -Only ถ้าสั่ง -Only php ทุกชื่อที่ไม่ใช่ php จะถูกข้ามทันที (ก่อนถึงตรงนี้ บรรทัด $Only = @(...) ด้านบนแตกข้อความ php,httpd ให้เป็นสองชื่อแล้ว)
  2. ตั้งชื่อไฟล์จาก URL เอาท้าย path ของ URL มาเป็นชื่อไฟล์ใน downloads\ เช่น httpd-2.4.69.tar.bz2
  3. ดาวน์โหลดเมื่อยังไม่มีไฟล์ หรือสั่ง -Force ใช้ curl.exe ที่มากับ Windows (-f ล้มเมื่อ HTTP error, -sS เงียบแต่โชว์ error, -L ตาม redirect, --retry 3 ลองใหม่สามครั้ง) ดาวน์โหลดลงไฟล์ชื่อ .part ก่อน แล้วค่อย Move-Item เปลี่ยนชื่อเมื่อโหลดจบ ถ้าเน็ตหลุดกลางทางจะเหลือแต่ .part ไม่มีไฟล์ครึ่งเดียวที่ดูเหมือนสมบูรณ์
  4. คำนวณ sha256 ของไฟล์เสมอ แม้ไฟล์จะมีอยู่ก่อนแล้ว เพื่อให้ไฟล์ที่ค้างจากรอบก่อนถูกตรวจซ้ำทุกครั้ง
  5. เลือกค่าที่จะใช้เทียบ ($want) ถ้า versions.json มี sha256 ก็ใช้ค่านั้น ถ้าเป็น null ก็ไปดูใน versions.lock.json ถ้าไม่มีทั้งสองที่ ก็ไม่มีอะไรให้เทียบ
  6. ไม่ตรง = หยุดทันที throw พร้อมพิมพ์ค่าที่ได้ (got) กับค่าที่ควรเป็น (want) สคริปต์ไม่แตกไฟล์ที่ hash ไม่ตรงเด็ดขาด
  7. ตรวจ sha1 ซ้ำ เฉพาะรายการที่ระบุ sha1 ไว้ ซึ่งมีตัวเดียวคือ mod_fcgid เพราะโครงการ Apache ประกาศ sha1 ไว้ให้ตัวนี้
  8. ถ้าไม่มีอะไรให้เทียบเลยจะพิมพ์ [TOFU] แล้วจด hash ที่ได้ลง $Lock
  9. ข้ามการแตกไฟล์ถ้าโฟลเดอร์ปลายทางมีอยู่แล้ว (พิมพ์ already extracted) เว้นแต่สั่ง -Force ซึ่งจะลบโฟลเดอร์แล้วแตกใหม่ ข้อนี้มีกับดัก อธิบายในหัวข้อ "ถ้าพังตรงนี้"
  10. แตกไฟล์ด้วย tar.exe ที่มากับ Windows 10 ขึ้นไป (bsdtar) อ่าน .zip .tar.bz2 .tar.xz .tgz ได้หมด ไม่ต้องติดตั้ง 7-Zip
  11. บรรทัดสุดท้ายของสคริปต์ เขียน $Lock กลับลง versions.lock.json เป็นรอบสุดท้าย

3. sha256 ที่ต้นทางประกาศ กับ TOFU ต่างกันยังไง

สองคำนี้เป็นหัวใจของความน่าเชื่อถือของทั้งซีรีส์ ผมเลยแยกอธิบายให้ชัด

แบบที่ 1: ต้นทางประกาศ hash ไว้เอง PHP, httpd, APR, APR-util, MariaDB ต้นทางเผยแพร่ sha256 ไว้ในหน้าที่ปล่อยเวอร์ชัน (php.net มี JSON, Apache มีไฟล์ .sha256 ข้าง tarball, MariaDB มี REST API) ผมคัดค่านั้นมาใส่ใน versions.json เองแล้ว ถ้าใครสลับไฟล์ระหว่างทางหรือที่ mirror hash ย่อมไม่ตรง และสคริปต์หยุด

แบบที่ 2: TOFU (Trust On First Use) ของบางตัวต้นทางไม่ได้ประกาศ hash ไว้ เช่น release ของ GitHub (php-sdk, winflexbison, Strawberry Perl) และแพ็กเกจ PECL ทั้งสามตัว วิธีที่ทำได้คือ ดาวน์โหลดครั้งแรกแล้วจด hash ไว้ ครั้งต่อไปค่อยเทียบกับที่จดไว้ มันป้องกันไม่ได้ว่าไฟล์ครั้งแรกถูกสลับหรือเปล่า แต่ป้องกันได้ว่าไฟล์ที่เคยดีอยู่เกิดเปลี่ยนไปทีหลัง

เงื่อนไขของรายการสิ่งที่สคริปต์ทำผลลัพธ์มี sha256 ใน versions.jsonphp httpd apr apr-util mariadbเทียบกับค่าในไฟล์นั้นต้นทางประกาศ hash ไว้เองตรง = ไปขั้นแตกไฟล์ไม่ตรง = หยุดทันทีsha256 เป็น nullแต่มีชื่อนี้ใน versions.lock.jsonเทียบกับค่าในล็อกค่าที่ผมจดไว้ในชุดที่แจกตรง = ไปขั้นแตกไฟล์ไม่ตรง = หยุดทันทีsha256 เป็น nullและยังไม่มีชื่อนี้ในล็อก[TOFU] คำนวณ sha256แล้วจดลง versions.lock.jsonผ่านเสมอ (ครั้งแรก)ครั้งต่อไปถูกเทียบกับค่านี้รายการที่มี sha1 (mod_fcgid) ถูกตรวจ sha1 ซ้ำอีกชั้นเสมอ
สามกรณีของการตรวจไฟล์ที่ดาวน์โหลด ทุกกรณีที่เทียบแล้วไม่ตรงจะหยุดก่อนแตกไฟล์

ในชุด kit ที่คุณโหลดมามีไฟล์ versions.lock.json ติดมาด้วย ผมจด hash ของแต่ละตัวไว้จากการดาวน์โหลดครั้งแรกบนเครื่องผม ผลคือเครื่องคุณจะไม่เห็นบรรทัด [TOFU] ในการรันครั้งแรก เพราะทุกตัวมีค่าในล็อกให้เทียบอยู่แล้ว และ hash ของ php-sdk, Strawberry Perl, NASM, winflexbison, mod_fcgid กับ PECL ทั้งสามจะถูกเทียบกับของที่ผมดาวน์โหลดไว้ ไม่ใช่เชื่อไฟล์แรกที่เจอ ถ้าอยากเห็นกลไก TOFU ทำงานจริง ให้ลบไฟล์ versions.lock.json แล้วรันใหม่ จะเห็น [TOFU] <ชื่อ> sha256=<64 ตัวอักษร> ขึ้นมาแทน

ข้อควรรู้เรื่อง TOFU

การเทียบกับล็อกบอกได้แค่ว่าไฟล์ตรงกับที่ผมเคยได้ ไม่ได้รับรองว่าต้นทางของไฟล์นั้นปลอดภัย ถ้าคุณจะเปลี่ยนเวอร์ชันของตัวที่เป็น TOFU (ตอนที่ 7 อธิบายขั้นตอน) ควรเปิดหน้า release ของต้นทางดูก่อนว่า tag และไฟล์ตรงกับที่ประกาศ แล้วค่อยให้สคริปต์จด hash ใหม่

4. รัน fetch ทีละขั้น

ขั้นที่ 4.1 สั่งรัน

ที่ไหน: pwsh (user ปกติ) โฟลเดอร์ C:\apm-build ใช้รูปแบบ pwsh -File แบบเดียวกับที่ build.cmd ใช้ ซึ่งข้ามเรื่อง ExecutionPolicy ของไฟล์ที่ดาวน์โหลดจากเน็ต (ไฟล์ใน zip มักติดเครื่องหมาย "มาจากอินเทอร์เน็ต" ทำให้นโยบาย RemoteSigned ปฏิเสธไม่ให้รัน)

pwsh -NoProfile -ExecutionPolicy Bypass -File scripts\00-fetch.ps1

ขั้นที่ 4.2 ดูผลระหว่างรัน

รอบแรกใช้เวลา 3-5 นาที ขึ้นกับความเร็วเน็ต ไฟล์ที่ใหญ่ที่สุดคือ Strawberry Perl (ประมาณ 290 MB) กับ MariaDB (ประมาณ 211 MB) จะเห็นบรรทัดเป็นคู่ [GET] แล้ว [OK] ต่อรายการ ผมย่อบางรายการออก

[GET] php-sdk 2.8.4
[OK] php-sdk -> C:\apm-build\toolchain\php-sdk
[GET] strawberry 5.42.3.1
[OK] strawberry -> C:\apm-build\toolchain\strawberry
...
[GET] php 8.4.27
[OK] php -> C:\apm-build\src\php-8.4.27
[GET] httpd 2.4.69
[OK] httpd -> C:\apm-build\src\httpd-2.4.69
...
[OK] pecl-imagick -> C:\apm-build\src\pecl-imagick-3.8.1

บรรทัดสุดท้ายที่ควรเห็นคือ pecl-imagick ไม่มีบรรทัดของ imagemagick เพราะถูกข้ามตามที่อธิบายไป ถ้ามี [ERR] ให้ข้ามไปหัวข้อ "ถ้าพังตรงนี้"

ขั้นที่ 4.3 ตรวจว่าไฟล์ครบ 13 ไฟล์

Get-ChildItem C:\apm-build\downloads | Select-Object Name, @{n='MB';e={[math]::Round($_.Length/1MB,1)}}

ควรได้ 13 แถว หน้าตาประมาณนี้ (ขนาดจากเครื่องผม)

Name                                          MB
----                                          --
apr-1.7.6.tar.bz2                            0.9
apr-util-1.6.5.tar.bz2                       0.4
httpd-2.4.69.tar.bz2                         8.1
igbinary-3.2.16.tgz                          0.1
imagick-3.8.1.tgz                            0.3
mariadb-11.8.9.tar.gz                      211.4
mod_fcgid-2.3.9.tar.bz2                      0.1
nasm-3.01-win64.zip                          0.6
php-8.4.27.tar.xz                           13.2
php-sdk-2.8.4.zip                           34.4
redis-6.3.0.tgz                              0.4
strawberry-perl-5.42.3.1-64bit-portable.zip 290.6
win_flex_bison-2.5.25.zip                    1.0

ขั้นที่ 4.4 ตรวจว่า source ถูกแตกไฟล์

Get-ChildItem C:\apm-build\src -Name
Get-ChildItem C:\apm-build\toolchain -Name
apr-1.7.6
apr-util-1.6.5
httpd-2.4.69
mariadb-11.8.9
mod_fcgid-2.3.9
pecl-igbinary-3.2.16
pecl-imagick-3.8.1
pecl-redis-6.3.0
php-8.4.27
nasm
php-sdk
strawberry
winflexbison

ฝั่ง src ได้ 9 โฟลเดอร์ ฝั่ง toolchain ได้ 4 (ยังไม่มี vcpkg ตอนนี้ มันจะมาในหัวข้อ 7 และ imagemagick-windows จะมาในตอนที่ 5)

ขั้นที่ 4.5 ตรวจว่าแตกไฟล์ถูกชั้น

นี่คือการตรวจ strip ว่าได้ผล ถ้าแตกซ้อนสองชั้นไฟล์พวกนี้จะหาไม่เจอ และตอนที่ 3 จะล้มด้วย error ที่งง

Test-Path C:\apm-build\src\httpd-2.4.69\CMakeLists.txt
Test-Path C:\apm-build\src\apr-1.7.6\CMakeLists.txt
Test-Path C:\apm-build\src\apr-util-1.6.5\CMakeLists.txt
Test-Path C:\apm-build\src\mod_fcgid-2.3.9\modules\fcgid\CMakeLists.txt

ต้องได้ True ครบสี่บรรทัด

ขั้นที่ 4.6 ตรวจเครื่องมือที่โหลดมาว่ารันได้

ที่ตรวจสองตัวนี้เพราะเป็นตัวที่ vcpkg และ OpenSSL พึ่งใน build ภายหลัง และเป็นตัวที่ผิดตัวแล้วเจ็บที่สุด

C:\apm-build\toolchain\strawberry\perl\bin\perl.exe -e 'print "$^V $^O\n"'
C:\apm-build\toolchain\nasm\nasm.exe -v
v5.42.3 MSWin32
NASM version 3.01 compiled on Oct 10 2025

คำว่า MSWin32 สำคัญมาก เพราะ perl ที่มากับ Git for Windows เป็นแบบ cygwin (จะขึ้น cygwin แทน) สร้าง path แบบ Unix แล้วทำให้ build OpenSSL พัง เลยต้องใช้ Strawberry Perl ที่โหลดมานี้เท่านั้น

ขั้นที่ 4.7 รันซ้ำเพื่อดูว่าข้ามได้

pwsh -NoProfile -ExecutionPolicy Bypass -File scripts\00-fetch.ps1

ครั้งนี้เสร็จในไม่กี่วินาที (เวลาส่วนใหญ่หมดไปกับการคำนวณ hash ซ้ำ) เพราะไฟล์อยู่ครบ สคริปต์แค่คำนวณ hash ซ้ำแล้วพิมพ์ว่าแตกไฟล์แล้ว ผลที่ได้คือ (จาก log จริงของผม)

[OK] php-sdk (already extracted)
[OK] strawberry (already extracted)
[OK] nasm (already extracted)
[OK] winflexbison (already extracted)
[OK] php (already extracted)
...
[OK] pecl-imagick (already extracted)

ขั้นที่ 4.8 (ไม่บังคับ) ตรวจ hash ด้วยมือเทียบกับต้นทาง

ถ้าคุณอยากไม่เชื่อสคริปต์ผม ให้เทียบเอง ตัวอย่างกับ httpd

Get-FileHash C:\apm-build\downloads\httpd-2.4.69.tar.bz2 -Algorithm SHA256 | Select-Object -ExpandProperty Hash

แล้วเปิดไฟล์ https://downloads.apache.org/httpd/httpd-2.4.69.tar.bz2.sha256 ในเบราว์เซอร์เทียบ ค่าต้องตรงกัน (PowerShell พิมพ์ตัวพิมพ์ใหญ่ ส่วนไฟล์ของ Apache เป็นตัวพิมพ์เล็ก ไม่ต้องสนใจต่างกันตรงนี้) ของผมขึ้นต้นด้วย C5E6EBC66E34

ใช้ -Only กับ -Force เมื่อต้องการโหลดเฉพาะบางตัว

ตอนที่ 7 จะใช้ตัวเลือกพวกนี้ตอนอัปเดตเวอร์ชัน สั่งหลายชื่อพร้อมกันได้ตรง ๆ ด้วย -File คั่นด้วยจุลภาค

pwsh -NoProfile -ExecutionPolicy Bypass -File scripts�-fetch.ps1 -Only php,httpd

มันทำงานได้เพราะบรรทัดที่เพิ่มเข้ามาใต้ $ErrorActionPreference (บรรทัดที่ขึ้นต้น $Only = @(...)) เรื่องคือ pwsh -File ไม่แปลง php,httpd เป็นอาร์เรย์ ให้ส่งเข้าไปเป็นข้อความก้อนเดียว "php,httpd" ใน $Only เลยมีสมาชิกตัวเดียว และชื่อ httpd ไม่ตรงกับอะไร สคริปต์ก็ข้ามทุกรายการเงียบ ๆ โดยไม่มี error สคริปต์รุ่นใหม่เลยแตกข้อความเองก่อนใช้ ไล่ทุกค่าใน $Only ผ่าน -split ',' ตัดช่องว่างหัวท้ายด้วย .Trim() ทิ้งค่าว่างด้วย Where-Object { $_ } แล้วครอบด้วย @(...) ให้เป็นอาร์เรย์เสมอ ผลคือ -Only php, -Only php,httpd และ -Only "php, httpd" ได้ผลเหมือนกัน

ถ้าอยากเช็กว่าแตกถูก ให้รันแล้วดูว่ามีบรรทัด [OK] หรือ [GET] ของทั้งสองชื่อ (ถ้าไฟล์กับโฟลเดอร์มีอยู่แล้วจะขึ้น [OK] php (already extracted) กับ [OK] httpd (already extracted)) ใครที่ใช้ kit รุ่นเก่าเคยต้องสั่งผ่าน -Command "& .\scripts�-fetch.ps1 -Only php,httpd" แทน เพราะรุ่นนั้นยังไม่แตกข้อความเอง รุ่นปัจจุบันไม่ต้องแล้ว

ส่วน -Force ใส่ท้ายคำสั่งได้เลย จะโหลดไฟล์ใหม่ทับและลบโฟลเดอร์ปลายทางแตกใหม่

5. vcpkg กับ lib ที่ Apache ต้องใช้

Apache httpd ตัวมันเองมีแต่โค้ด C ของมัน ส่วนที่ทำให้มันคุย HTTPS, HTTP/2, บีบอัดแบบ brotli ได้ มาจาก lib ภายนอก ถ้าคุณเคยลง Apache บน Linux ด้วย apt install คำสั่งเดียวได้ lib พวกนี้ครบ แต่บน Windows ไม่มีใครดูแลให้ เราเลยใช้ vcpkg ซึ่งเป็นตัวจัดการ package ของ C/C++ จาก Microsoft มันดาวน์โหลด source ของแต่ละ lib แล้วคอมไพล์ด้วย MSVC ในเครื่องเราให้ ABI ตรงกับ Apache ที่เราจะ build

manifest: บอกว่าต้องการอะไร

vcpkg มีสองโหมด โหมดที่ผมใช้เรียกว่า manifest คือเขียนรายการ lib ที่ต้องการไว้ในไฟล์ vcpkg.json ของโปรเจกต์ แล้วสั่ง vcpkg install ไฟล์นี้อยู่ที่ deps\apache\vcpkg.json (ผมย่อ hash ของ baseline)

{
  "name": "apm-apache-deps",
  "version-string": "1",
  "builtin-baseline": "bd8298ae21…",
  "dependencies": [
    "openssl", "pcre2", "zlib", "nghttp2", "brotli",
    "expat", "libxml2", "jansson", "curl"
  ]
}

builtin-baseline คือ commit ของคลัง vcpkg ที่ล็อกเวอร์ชันของ lib ทุกตัวให้ตายตัว ถ้าไม่ล็อก ทุกครั้งที่ vcpkg อัปเดตเราจะได้ lib คนละรุ่นโดยไม่รู้ตัว ผลคือ build รอบเดียวกันสองเครื่องออกมาไม่เหมือนกัน ในไฟล์จริง baseline เป็น commit id 40 ตัวอักษร ขึ้นต้นด้วย bd8298ae21

ทั้ง 9 ตัวใช้ทำอะไร สรุปตามที่ configure ของ httpd ตรวจเจอใน log ของผม (ส่วน Summary of feature detection) กับที่ dumpbin ในตอนที่ 3 ยืนยัน

lib ใช้กับ หมายเหตุ
openssl mod_ssl, mod_md, apr_crypto_openssl HTTPS/TLS ทั้งหมด (เป็นเรื่องของหัวข้อ 6)
pcre2 libhttpd.dll regex ของ mod_rewrite, <LocationMatch> ฯลฯ
zlib mod_deflate, libcurl, libxml2 บีบอัด gzip/deflate
nghttp2 mod_http2 HTTP/2
brotli mod_brotli บีบอัด brotli
expat libaprutil อ่าน XML ให้ APR-util
libxml2 mod_xml2enc จัดการ encoding ในเอกสาร XML/HTML ที่ proxy ผ่าน
jansson mod_md อ่าน/เขียน JSON ของ ACME
curl mod_md ให้ mod_md คุยกับ Let's Encrypt

vcpkg ยังลากของที่ lib เหล่านี้ต้องใช้ตอน build มาให้เองด้วย เช่น libiconv (ตัวที่ libxml2 ต้องการ) กับ vcpkg-cmake รวมแล้วบรรทัด * ... ใน log เป็นของที่ถูกติดตั้งในฐานะ dependency เครื่องมือที่ vcpkg ใช้ (CMake, Ninja, Perl, NASM, msys2) มันดาวน์โหลดมาเก็บเองใน toolchain\vcpkg\downloads\tools\ ไม่ไปยุ่งกับของที่คุณติดตั้งในเครื่อง

6. OpenSSL 3.5 LTS ด้วย overlay port

นี่คือส่วนที่ผมตัดสินใจเองและต้องอธิบายเหตุผลให้ได้ vcpkg รุ่นที่ baseline ชี้อยู่ให้ OpenSSL 3.6.5 ซึ่งเป็นรุ่น non-LTS และหมดระยะซัพพอร์ตภายในเดือนพฤศจิกายน 2026 ซึ่งก็คือเดือนหน้า ส่วน OpenSSL 3.5 เป็นรุ่น LTS ซัพพอร์ตถึงเมษายน 2030 สำหรับ web server ที่ตั้งใจให้ตามแพตช์ความปลอดภัยทุกรอบ ผมเลือก LTS ไม่ต้องย้ายรุ่นใหญ่อีกในเร็ว ๆ นี้

vcpkg มีกลไก overlay port รองรับพอดี คือบอกให้ใช้ port (สูตร build ของ lib หนึ่งตัว) จากโฟลเดอร์ของเราแทนตัวที่อยู่ในคลัง vcpkg โฟลเดอร์ของเราคือ ports-overlay\openssl\ ใน kit ข้างในเป็นสูตร OpenSSL ฉบับเต็ม (portfile, patch และ vcpkg.json) ที่ตั้งเป็นรุ่น 3.5.9 สองจุดที่ต้องแตะทุกครั้งที่เปลี่ยนรุ่นคือเลขเวอร์ชันใน vcpkg.json กับ SHA512 ของ tarball ใน portfile.cmake

{
  "name": "openssl",
  "version": "3.5.9",
  ...
}
vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO openssl/openssl
    REF "openssl-${VERSION}"
    SHA512 ab29a6ed40dd8c74…
    PATCHES
        cmake-config.patch
        command-line-length.patch
        script-prefix.patch
        windows/install-layout.patch
        windows/install-pdbs.patch
        windows/install-programs.diff
        unix/android-cc.patch
        unix/move-openssldir.patch
        unix/no-empty-dirs.patch
        unix/no-static-libs-for-shared.patch
)

บรรทัด REF "openssl-${VERSION}" ดึงเลขจาก vcpkg.json เลยแก้เวอร์ชันที่เดียวก็ตามไปทั้งสองไฟล์ ส่วน SHA512 คือ hash ของไฟล์ https://github.com/openssl/openssl/archive/openssl-3.5.9.tar.gz ถ้าไฟล์ที่ vcpkg โหลดมา hash ไม่ตรง มันหยุดเหมือนที่ 00-fetch.ps1 ทำ ส่วน 10 patch ในรายการ เป็นของ vcpkg เองทั้งหมด ผมลองแล้วใช้กับ 3.5.9 ได้โดยไม่ต้องแก้ แต่รุ่นอื่นต้องลองเองเสมอ

vcpkg install --overlay-ports=ports-overlaymanifest ขอ opensslregistry ของ vcpkg (baseline)openssl 3.6.5non-LTS หมดซัพพอร์ต พ.ย. 2026ports-overlay\openssl (ใช้ตัวนี้)openssl 3.5.9 LTSซัพพอร์ตถึง เม.ย. 2030ถูกข้ามoverlay ชนะไม่ถูก build ในเครื่องนี้libssl-3-x64.dll libcrypto-3-x64.dllProductVersion 3.5.9
overlay port ถูกค้นก่อนคลังของ vcpkg ผลคือได้ OpenSSL รุ่น LTS แทนรุ่น 3.6 ที่ baseline ชี้ไว้
ทำไมไม่ใช้ OpenSSL ของ PHP team

ตอนที่ 6 ที่ build PHP จะได้ DLL ของ OpenSSL อีกชุดจากกลุ่ม deps ของ PHP team (ผมวัดได้รุ่น 3.0.22 ในตอนนั้น) สองชุดนี้แยกกัน ฝั่ง Apache ใช้ 3.5.9 ที่เรา build เอง ฝั่ง PHP ใช้ของ PHP team เพื่อให้ ABI ตรงกับ PHP official รายละเอียดอยู่ในตอนที่ 6

7. 10-deps-vcpkg.ps1 ทีละบรรทัด และรันจริง

นี่คือสคริปต์เต็ม

# 10-deps-vcpkg.ps1 - build Apache's third-party libs from source with vcpkg (manifest mode)
# OpenSSL comes from ports-overlay\openssl (3.5 LTS) instead of the vcpkg default.
$ErrorActionPreference = 'Stop'
$Root  = Split-Path $PSScriptRoot -Parent
$Vcpkg = "$Root\toolchain\vcpkg\vcpkg.exe"
$Out   = "$Root\build\vcpkg_installed"
New-Item -ItemType Directory -Force "$Root\build\logs" | Out-Null

# private vcpkg checkout (never the shared C:\vcpkg), at least as new as the manifest baseline
if (-not (Test-Path $Vcpkg)) {
    $baseline = (Get-Content "$Root\deps\apache\vcpkg.json" -Raw | ConvertFrom-Json).'builtin-baseline'
    git clone --quiet https://github.com/microsoft/vcpkg.git "$Root\toolchain\vcpkg"
    git -C "$Root\toolchain\vcpkg" checkout --quiet $baseline
    & "$Root\toolchain\vcpkg\bootstrap-vcpkg.bat" -disableMetrics
    if (-not (Test-Path $Vcpkg)) { throw '[ERR] vcpkg bootstrap failed' }
}

$env:Path = ($env:Path -split ';' | Where-Object { $_ -notmatch '(?i)xampp' }) -join ';'
$env:VCPKG_DISABLE_METRICS = '1'
& $Vcpkg install --triplet x64-windows `
    --x-manifest-root="$Root\deps\apache" `
    --x-install-root="$Out" `
    --overlay-ports="$Root\ports-overlay" `
    --clean-after-build 2>&1 | Tee-Object "$Root\build\logs\vcpkg-apache.log" | Select-Object -Last 25
if ($LASTEXITCODE) { throw "[ERR] vcpkg install failed (log: build\logs\vcpkg-apache.log)" }
Write-Host "[OK] deps -> $Out\x64-windows"

อ่านตามลำดับที่รัน

  1. บรรทัด 4-7 กำหนด path ของ vcpkg.exe ผลลัพธ์ (build\vcpkg_installed) และสร้างโฟลเดอร์ build\logs
  2. บล็อก if (-not (Test-Path $Vcpkg)) ทำเฉพาะครั้งแรกที่ยังไม่มี vcpkg อ่าน commit id จาก builtin-baseline ใน manifest, git clone คลัง vcpkg ลง toolchain\vcpkg, git checkout ไปที่ commit นั้น แล้วรัน bootstrap-vcpkg.bat ซึ่งโหลด vcpkg.exe ให้ ตั้งใจให้เป็น checkout ส่วนตัวในโฟลเดอร์ของโปรเจกต์ ไม่ใช้ C:\vcpkg ที่คุณอาจมีใช้ร่วมกับงานอื่น เพื่อไม่ให้สองงานเหยียบกัน
  3. บรรทัด $env:Path = ... ถอด path ที่มีคำว่า xampp ออกจาก PATH เฉพาะใน process นี้ กัน perl, php ฯลฯ ของ XAMPP หลุดเข้ามาแทนตัวที่ควรใช้ ถ้าไม่มี XAMPP บรรทัดนี้ไม่ทำอะไร
  4. VCPKG_DISABLE_METRICS ปิดการส่งสถิติการใช้งานของ vcpkg ไปยัง Microsoft
  5. คำสั่ง vcpkg install มี 5 ตัวเลือก
    • --triplet x64-windows สถาปัตยกรรม x64 แบบ DLL (ไม่ใช่ static) ได้ไฟล์ .dll ข้างๆ .lib ซึ่งเป็นแบบที่ Apache ต้องการ
    • --x-manifest-root ชี้ไปโฟลเดอร์ที่มี vcpkg.json
    • --x-install-root ติดตั้งผลลัพธ์ลง build\vcpkg_installed ในโปรเจกต์ ไม่ใช่ที่กลางของ vcpkg
    • --overlay-ports ชี้ไป ports-overlay เพื่อให้ OpenSSL 3.5.9 ชนะ (หัวข้อ 6)
    • --clean-after-build ลบโฟลเดอร์ต้นไม้ build ชั่วคราวของแต่ละ lib ทันทีที่เสร็จ ประหยัดดิสก์หลาย GB
  6. ท่อ 2>&1 | Tee-Object ... | Select-Object -Last 25 รวม error เข้า output, เขียน log เต็มลง build\logs\vcpkg-apache.log และโชว์บนจอเฉพาะ 25 บรรทัดสุดท้าย ที่มันสั้นเพราะ vcpkg พ่นข้อความยาวมาก
  7. $LASTEXITCODE ถ้า vcpkg คืนค่าไม่ใช่ 0 สคริปต์ throw พร้อมบอกที่อยู่ของ log

ขั้นที่ 7.1 สั่งรัน

ที่ไหน: pwsh (user ปกติ) โฟลเดอร์ C:\apm-build เตรียมใจว่ารอบแรกนาน ประมาณ 45 นาทีบนเครื่องผม (i5-12500H) โดย OpenSSL กับ libiconv ช้าที่สุด จอจะนิ่งไปนานช่วงนั้น ไม่ได้ค้าง ถ้าอยากรู้ว่ายังทำงานอยู่ ให้เปิด Task Manager ดูว่ามีโปรเซส cl.exe หรือ nmake กินซีพียูอยู่

pwsh -NoProfile -ExecutionPolicy Bypass -File scripts\10-deps-vcpkg.ps1

ขั้นที่ 7.2 ดูผลตอนจบ

จอจะแสดงแค่ 25 บรรทัดสุดท้าย ท้ายสุดจะลงท้ายด้วยบรรทัดสรุปของ vcpkg กับ [OK] ของสคริปต์ ด้านล่างเป็นของจริงจาก log ของผมตอนรันซ้ำหลัง cache พร้อมแล้ว (เลยขึ้นว่า already installed) รอบแรกของคุณจะเป็นบรรทัดของ "will be built and installed" แทน แต่ 3 บรรทัดท้ายหน้าตาเหมือนกัน ต่างแค่เวลา

pcre2 provides pkg-config modules:

  # Posix compatible interface to libpcre2-8
  libpcre2-posix

All requested installations completed successfully in: 323 us
[OK] deps -> C:\apm-build\build\vcpkg_installed\x64-windows

323 us ในรอบของผมแปลว่า vcpkg ไม่ได้ทำอะไรเลย รอบแรกของคุณตรงนี้จะเป็นเวลาหลักนาที ถ้าอยากเห็นรายการ package ที่ติดตั้ง ให้ค้นใน log เต็ม

Select-String -Path C:\apm-build\build\logs\vcpkg-apache.log -Pattern 'x64-windows@' | ForEach-Object Line

ผลในเครื่องผม (รอบที่ใช้ cache) แสดงเลขรุ่นของ lib ทุกตัว

    brotli:[email protected]
    curl[core,sspi,ssl]:[email protected]
    expat:[email protected]
    jansson:[email protected]
  * libiconv:[email protected]#1
    libxml2[core,zlib,iconv]:[email protected]
    nghttp2:[email protected]
    openssl:[email protected]
    pcre2[core,platform-default-features,jit]:[email protected]
  * vcpkg-cmake:x64-windows@2025-08-07
  * vcpkg-cmake-config:x64-windows@2026-07-21
  * vcpkg-cmake-get-vars:x64-windows@2025-05-29
  * vcpkg-make:x64-windows@2026-07-09#2
    zlib:[email protected]#2

ให้ดูบรรทัด openssl:[email protected] เป็นตัวชี้ว่า overlay ทำงาน ถ้าเห็น 3.6.5 แปลว่า overlay ไม่ถูกใช้ ให้กลับไปเช็กว่าโฟลเดอร์ ports-overlay\openssl มีอยู่จริง

ขั้นที่ 7.3 ตรวจไฟล์ .lib ที่ Apache จะลิงก์

Get-ChildItem C:\apm-build\build\vcpkg_installed\x64-windows\lib -File -Name
brotlicommon.lib
brotlidec.lib
brotlienc.lib
charset.lib
iconv.lib
jansson.lib
libcrypto.lib
libcurl.lib
libexpat.lib
libssl.lib
libxml2.lib
nghttp2.lib
pcre2-16.lib
pcre2-32.lib
pcre2-8.lib
pcre2-posix.lib
z.lib

ต้องมี 17 ไฟล์ ถ้าขาด libssl.lib หรือ libcrypto.lib แปลว่า OpenSSL ไม่ผ่าน ให้ดูหัวข้อ "ถ้าพังตรงนี้"

ขั้นที่ 7.4 ตรวจเลขรุ่นของ DLL ที่ build ออกมาจริง

ขั้นนี้ยืนยันว่าไฟล์ที่จะถูกคัดลอกไปข้าง httpd.exe ในตอนที่ 3 เป็นรุ่นที่เราตั้งใจ ไม่ใช่ที่ไหนก็ไม่รู้

$b = 'C:\apm-build\build\vcpkg_installed\x64-windows\bin'
foreach ($n in 'libcrypto-3-x64','libssl-3-x64','libcurl','libxml2','z') { "{0,-18} {1}" -f $n, (Get-Item "$b\$n.dll").VersionInfo.ProductVersion }
libcrypto-3-x64    3.5.9
libssl-3-x64       3.5.9
libcurl            8.23.0
libxml2            2.15.4
z                  1.3.2

ชื่อไฟล์ libcrypto-3-x64.dll ยังมีเลข 3 อยู่ แม้ตัวเนื้อเป็น 3.5.9 เพราะเลขในชื่อคือรุ่นใหญ่ (major) ของ ABI ของ OpenSSL ไม่ใช่เลขรุ่นเต็ม

ขั้นที่ 7.5 ตรวจว่า vcpkg ชี้ commit ถูกตัว

git -C C:\apm-build\toolchain\vcpkg log -1 --format=%h
bd8298ae21

ต้องขึ้นต้นด้วยตัวเดียวกับ builtin-baseline ในไฟล์ manifest

ขั้นที่ 7.6 ย้ำว่าสคริปต์รันซ้ำได้

รันขั้น 7.1 อีกรอบ จะเสร็จภายในไม่กี่วินาที เพราะ vcpkg เก็บ binary cache ของแต่ละ package ไว้ในเครื่อง (โดยปริยายอยู่ที่ %LOCALAPPDATA%\vcpkg\archives) ผลคือเปลี่ยนเวอร์ชัน lib ตัวเดียว ตัวอื่นไม่ถูกคอมไพล์ใหม่ ทั้ง build.cmd รันซ้ำรวมกันที่ผมวัดได้ ขั้น vcpkg ใช้ 0.1 นาที

ผลลัพธ์ที่ได้

มีโฟลเดอร์ C:\apm-build\build\vcpkg_installed\x64-windows ที่ข้างในมี include\, lib\ (17 ไฟล์ .lib) และ bin\ (DLL 18 ตัว) นี่คือของที่ตอนที่ 3 จะดึงไปใช้ทั้งหมด

8. ถ้าพังตรงนี้

ตารางนี้รวมอาการที่เจอจริงหรือที่โค้ดของสคริปต์ออกแบบให้ดักไว้ ของที่ผมไม่เคยเจอเองผมระบุไว้ว่าอ่านจากโค้ด

อาการ สาเหตุ วิธีแก้
[ERR] sha256 mismatch <ชื่อ> พร้อม got กับ want ไฟล์ที่โหลดมาไม่ตรง hash ที่ล็อกไว้ อาจโหลดไม่สมบูรณ์ mirror ส่งไฟล์ผิด หรือไฟล์ต้นทางถูกเปลี่ยน ลบไฟล์นั้นใน downloads\ แล้วรันใหม่ถ้ายังเหมือนเดิม ห้ามแก้ hash ให้ผ่านเฉย ๆ ให้ไปเทียบกับหน้าต้นทางก่อน (ขั้น 4.8)
download failed: <url> เน็ตหลุด, proxy ขององค์กรบล็อก, URL ของต้นทางเปลี่ยน ลองเปิด URL นั้นในเบราว์เซอร์ ถ้าเปิดได้แต่ curl.exe ไม่ได้ ให้เช็ก proxy ไฟล์ .part ที่ค้างลบทิ้งได้
รันแล้วเงียบ ไม่มี [OK] สักบรรทัด ชื่อใน -Only สะกดไม่ตรงกับชื่อใน versions.json (เช่น apache แทน httpd) ทุกรายการเลยถูกข้าม หรือใช้ kit รุ่นเก่าที่ยังไม่แตกข้อความ a,b เอง เทียบชื่อกับ key ใน versions.json ถ้าเป็น kit เก่าให้โหลด kit ใหม่ หรือสั่งผ่าน -Command "& .\scripts�-fetch.ps1 -Only a,b"
ขึ้น [OK] <ชื่อ> (already extracted) แต่ไฟล์ข้างในไม่ครบ แตกไฟล์ค้างกลางทาง (ดิสก์เต็ม) โฟลเดอร์ปลายทางถูกสร้างไปแล้ว รอบหน้าสคริปต์เห็นโฟลเดอร์ก็ข้าม (อ่านจากโค้ด) ลบโฟลเดอร์นั้นใน src\ หรือ toolchain\ ทิ้ง แล้วรันใหม่ หรือใส่ -Force
extract failed: <ไฟล์> tar.exe อ่านไฟล์ไม่ได้ ไฟล์เสีย หรือ path ยาวเกิน ลบไฟล์ใน downloads\ โหลดใหม่ ถ้ายังเป็น ให้เช็กว่าโฟลเดอร์โปรเจกต์ไม่ลึกเกินไป ใช้ C:\apm-build ตามที่บอก
A parameter cannot be found that matches parameter name 'AsHashtable' รันด้วย Windows PowerShell 5.1 ไม่ใช่ pwsh 7 เปิด pwsh (ขั้น 1.1)
ไม่มีไฟล์ .lib ของ OpenSSL / [ERR] vcpkg install failed build ของ lib ใด lib หนึ่งล้ม เปิด build\logs\vcpkg-apache.log เลื่อนหาข้อความ error ตัวแรก vcpkg มักบอกชื่อ log ย่อยของ package นั้นไว้ในบรรทัดท้าย ๆ
OpenSSL ล้มด้วย path แปลก ๆ แบบ Unix perl ของ Git (cygwin) แทรกเข้ามาใน PATH ทำตามขั้น 4.6 ให้ได้ MSWin32 และเอาโฟลเดอร์ perl ของ Git ออกจาก PATH ของ session นั้นถ้าตั้งไว้
warning: ... ignoring mismatched VCPKG_ROOT environment value C:\vcpkg มีตัวแปร VCPKG_ROOT ของ vcpkg ตัวอื่นในเครื่อง ไม่ใช่ปัญหา เป็นแค่คำเตือน vcpkg ใช้ตัวในโปรเจกต์ตามที่ตั้งใจ (เจอใน log จริงของผม)
ช้ามากเป็นสองเท่า Defender สแกนไฟล์ .obj นับหมื่นไฟล์ ตั้ง exclusion ตามตอนที่ 1 (ต้องใช้ admin)
สั่งซ้ำแล้ว vcpkg ยังคอมไพล์ OpenSSL ใหม่ทั้งที่ไม่ได้แก้อะไร ลบ binary cache ใน %LOCALAPPDATA%\vcpkg\archives หรือเปลี่ยน compiler (อัปเดต MSVC ทำให้ hash ของ compiler เปลี่ยน) อ่านจากพฤติกรรมของ vcpkg ที่คิด hash ตาม compiler ปล่อยให้มันรันจบ รอบหน้าจะกลับมาเร็ว

ถ้าเจออาการนอกตาราง ให้เปิด log ก่อนเสมอ ข้อความบน console ถูกตัดเหลือ 25 บรรทัดสุดท้าย แต่ log เต็มอยู่ที่ C:\apm-build\build\logs\vcpkg-apache.log และข้อความ error ตัวแรกที่บอกต้นเหตุจริงมักอยู่เหนือ 25 บรรทัดนั้นขึ้นไป

9. เช็กลิสต์ก่อนไปตอนที่ 3

ทุกข้อต้องจริงก่อนไปต่อ ถ้าข้อไหนไม่ได้ ให้กลับไปขั้นที่อ้างถึง

  1. C:\apm-build\downloads มี 13 ไฟล์ และ 00-fetch.ps1 รันซ้ำแล้วขึ้น already extracted ครบทุกบรรทัด (ขั้น 4.3 และ 4.7)
  2. C:\apm-build\src มี 9 โฟลเดอร์ และ Test-Path ของ CMakeLists.txt สี่ตัวได้ True (ขั้น 4.4 และ 4.5)
  3. perl.exe ของ Strawberry ตอบ MSWin32 (ขั้น 4.6)
  4. C:\apm-build\build\vcpkg_installed\x64-windows\lib มี 17 ไฟล์ .lib รวม libssl.lib กับ libcrypto.lib (ขั้น 7.3)
  5. libcrypto-3-x64.dll มี ProductVersion เป็น 3.5.9 (ขั้น 7.4)
  6. C:\apm-build\build\logs\vcpkg-apache.log ลงท้ายด้วย All requested installations completed successfully (ขั้น 7.2)
ไม่อยาก build เอง

ใครอยากได้ APM ไปใช้เลยโดยไม่ต้องคอมไพล์ ผมปล่อยตัวที่ build เสร็จแล้วไว้ที่ github.com/ChonLaWan/APM-Server โหลด APM-1.0.0-win64.zip (ราว 140 MB) จากหน้า Releases เทียบ sha256 กับไฟล์ .sha256 ที่แนบไว้ แตกไว้ที่ไหนก็ได้ แล้วเปิด manager\APMManager.exe ตัว Manager จะเลือกพอร์ต เขียน config ออก cert และสร้างฐานข้อมูลให้เอง มีปุ่ม Check for updates ที่โหลดรุ่นใหม่จาก Releases มาตรวจ sha256 แล้วแทน binary ให้ โดยไม่แตะ config ฐานข้อมูล และเว็บของเรา

ซีรีส์ Build APM บน Windows

สารบัญ · ตอนก่อนหน้า: ตอนที่ 1 เตรียมเครื่องมือ · ตอนถัดไป: ตอนที่ 3 build Apache httpd

ตอนที่ 3 จะเอา source ใน src\ กับ lib ใน build\vcpkg_installed มาประกอบเป็น Apache httpd กับ mod_fcgid และจะได้เห็นว่าสคริปต์ไล่หา DLL ที่ต้องวางข้าง httpd.exe ยังไง ถ้าคุณทำตามมาถึงตรงนี้ ส่วนที่นานที่สุดของ Apache ผ่านไปแล้ว เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย