From 5c18900fb7fa65bfb6cdb4da1f3d763700022b8c Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Tue, 17 Sep 2024 18:47:36 +0800 Subject: [PATCH] Use Cygwin to build libiconv on Windows CI (#14999) This uses the official releases and build instructions. To compile code with this patch using a Windows Crystal compiler without this patch, either the new library files (`lib\iconv.lib`, `lib\iconv-dynamic.lib`, `iconv-2.dll`) shall be copied to that existing Crystal installation, or `CRYSTAL_LIBRARY_PATH` shall include the new `lib` directory so that the `@[Link]` annotation will pick up the new `iconv-2.dll` on program builds. Otherwise, compiled programs will continue to look for the old `libiconv.dll`, and silently break if it is not in `%PATH%` (which is hopefully rare since most of the time Crystal itself is also in `%PATH%`). Cygwin's location is currently hardcoded to `C:\cygwin64`, the default installation location for 64-bit Cygwin. Cygwin itself doesn't have native ARM64 support, but cross-compilation should be possible by simply using the x64-to-ARM64 cross tools MSVC developer prompt on an ARM64 machine. --- .github/workflows/win.yml | 20 ++++++++-- .github/workflows/win_build_portable.yml | 7 +++- etc/win-ci/build-iconv.ps1 | 47 +++++------------------- etc/win-ci/cygwin-build-iconv.sh | 32 ++++++++++++++++ src/crystal/lib_iconv.cr | 2 +- 5 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 etc/win-ci/cygwin-build-iconv.sh diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index a256a6806a3f..d4b9316ef1a2 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -21,6 +21,13 @@ jobs: - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 + - name: Set up Cygwin + uses: cygwin/cygwin-install-action@006ad0b0946ca6d0a3ea2d4437677fa767392401 # v4 + with: + packages: make + install-dir: C:\cygwin64 + add-to-path: false + - name: Download Crystal source uses: actions/checkout@v4 @@ -50,7 +57,7 @@ jobs: run: .\etc\win-ci\build-pcre2.ps1 -BuildTree deps\pcre2 -Version 10.43 - name: Build libiconv if: steps.cache-libs.outputs.cache-hit != 'true' - run: .\etc\win-ci\build-iconv.ps1 -BuildTree deps\iconv + run: .\etc\win-ci\build-iconv.ps1 -BuildTree deps\iconv -Version 1.17 - name: Build libffi if: steps.cache-libs.outputs.cache-hit != 'true' run: .\etc\win-ci\build-ffi.ps1 -BuildTree deps\ffi -Version 3.3 @@ -93,6 +100,13 @@ jobs: - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 + - name: Set up Cygwin + uses: cygwin/cygwin-install-action@006ad0b0946ca6d0a3ea2d4437677fa767392401 # v4 + with: + packages: make + install-dir: C:\cygwin64 + add-to-path: false + - name: Download Crystal source uses: actions/checkout@v4 @@ -112,7 +126,7 @@ jobs: libs/xml2-dynamic.lib dlls/pcre.dll dlls/pcre2-8.dll - dlls/libiconv.dll + dlls/iconv-2.dll dlls/gc.dll dlls/libffi.dll dlls/zlib1.dll @@ -131,7 +145,7 @@ jobs: run: .\etc\win-ci\build-pcre2.ps1 -BuildTree deps\pcre2 -Version 10.43 -Dynamic - name: Build libiconv if: steps.cache-dlls.outputs.cache-hit != 'true' - run: .\etc\win-ci\build-iconv.ps1 -BuildTree deps\iconv -Dynamic + run: .\etc\win-ci\build-iconv.ps1 -BuildTree deps\iconv -Version 1.17 -Dynamic - name: Build libffi if: steps.cache-dlls.outputs.cache-hit != 'true' run: .\etc\win-ci\build-ffi.ps1 -BuildTree deps\ffi -Version 3.3 -Dynamic diff --git a/.github/workflows/win_build_portable.yml b/.github/workflows/win_build_portable.yml index 12ee17da9e68..98c428ee5bad 100644 --- a/.github/workflows/win_build_portable.yml +++ b/.github/workflows/win_build_portable.yml @@ -23,6 +23,7 @@ jobs: - name: Install Crystal uses: crystal-lang/install-crystal@v1 + id: install-crystal with: crystal: "1.13.2" @@ -68,7 +69,7 @@ jobs: libs/xml2-dynamic.lib dlls/pcre.dll dlls/pcre2-8.dll - dlls/libiconv.dll + dlls/iconv-2.dll dlls/gc.dll dlls/libffi.dll dlls/zlib1.dll @@ -107,6 +108,10 @@ jobs: run: | echo "CRYSTAL_LIBRARY_PATH=$(pwd)\libs" >> ${env:GITHUB_ENV} echo "LLVM_CONFIG=$(pwd)\llvm\bin\llvm-config.exe" >> ${env:GITHUB_ENV} + # NOTE: the name of the libiconv DLL has changed, so we manually copy + # the new one to the existing Crystal installation; remove after + # updating the base compiler to 1.14 + cp dlls/iconv-2.dll ${{ steps.install-crystal.outputs.path }} - name: Build LLVM extensions run: make -f Makefile.win deps diff --git a/etc/win-ci/build-iconv.ps1 b/etc/win-ci/build-iconv.ps1 index 56d0417bd729..541066c6327f 100644 --- a/etc/win-ci/build-iconv.ps1 +++ b/etc/win-ci/build-iconv.ps1 @@ -1,47 +1,20 @@ param( [Parameter(Mandatory)] [string] $BuildTree, + [Parameter(Mandatory)] [string] $Version, [switch] $Dynamic ) . "$(Split-Path -Parent $MyInvocation.MyCommand.Path)\setup.ps1" [void](New-Item -Name (Split-Path -Parent $BuildTree) -ItemType Directory -Force) -Setup-Git -Path $BuildTree -Url https://github.com/pffang/libiconv-for-Windows.git -Ref 1353455a6c4e15c9db6865fd9c2bf7203b59c0ec # master@{2022-10-11} +Invoke-WebRequest "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-${Version}.tar.gz" -OutFile libiconv.tar.gz +tar -xzf libiconv.tar.gz +mv libiconv-* $BuildTree +rm libiconv.tar.gz Run-InDirectory $BuildTree { - Replace-Text libiconv\include\iconv.h '__declspec (dllimport) ' '' - - echo ' - - $(MsbuildThisFileDirectory)\Override.props - - ' > 'Directory.Build.props' - - echo " - - false - - - - None - false - - - false - - - - - MultiThreadedDLL - - - " > 'Override.props' - - if ($Dynamic) { - MSBuild.exe /p:Platform=x64 /p:Configuration=Release libiconv.vcxproj - } else { - MSBuild.exe /p:Platform=x64 /p:Configuration=ReleaseStatic libiconv.vcxproj - } + $env:CHERE_INVOKING = 1 + & 'C:\cygwin64\bin\bash.exe' --login "$PSScriptRoot\cygwin-build-iconv.sh" "$Version" "$(if ($Dynamic) { 1 })" if (-not $?) { Write-Host "Error: Failed to build libiconv" -ForegroundColor Red Exit 1 @@ -49,8 +22,8 @@ Run-InDirectory $BuildTree { } if ($Dynamic) { - mv -Force $BuildTree\output\x64\Release\libiconv.lib libs\iconv-dynamic.lib - mv -Force $BuildTree\output\x64\Release\libiconv.dll dlls\ + mv -Force $BuildTree\iconv\lib\iconv.dll.lib libs\iconv-dynamic.lib + mv -Force $BuildTree\iconv\bin\iconv-2.dll dlls\ } else { - mv -Force $BuildTree\output\x64\ReleaseStatic\libiconvStatic.lib libs\iconv.lib + mv -Force $BuildTree\iconv\lib\iconv.lib libs\ } diff --git a/etc/win-ci/cygwin-build-iconv.sh b/etc/win-ci/cygwin-build-iconv.sh new file mode 100644 index 000000000000..a8507542e646 --- /dev/null +++ b/etc/win-ci/cygwin-build-iconv.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -eo pipefail + +Version=$1 +Dynamic=$2 + +export PATH="$(pwd)/build-aux:$PATH" +export CC="$(pwd)/build-aux/compile cl -nologo" +export CXX="$(pwd)/build-aux/compile cl -nologo" +export AR="$(pwd)/build-aux/ar-lib lib" +export LD="link" +export NM="dumpbin -symbols" +export STRIP=":" +export RANLIB=":" +if [ -n "$Dynamic" ]; then + export CFLAGS="-MD" + export CXXFLAGS="-MD" + enable_shared=yes + enable_static=no +else + export CFLAGS="-MT" + export CXXFLAGS="-MT" + enable_shared=no + enable_static=yes +fi +export CPPFLAGS="-D_WIN32_WINNT=_WIN32_WINNT_WIN7 -I$(pwd)/iconv/include" +export LDFLAGS="-L$(pwd)/iconv/lib" + +./configure --host=x86_64-w64-mingw32 --prefix="$(pwd)/iconv" --enable-shared="${enable_shared}" --enable-static="${enable_static}" +make +make install diff --git a/src/crystal/lib_iconv.cr b/src/crystal/lib_iconv.cr index 5f1506758454..07100ff9c1dc 100644 --- a/src/crystal/lib_iconv.cr +++ b/src/crystal/lib_iconv.cr @@ -6,7 +6,7 @@ require "c/stddef" @[Link("iconv")] {% if compare_versions(Crystal::VERSION, "1.11.0-dev") >= 0 %} - @[Link(dll: "libiconv.dll")] + @[Link(dll: "iconv-2.dll")] {% end %} lib LibIconv type IconvT = Void*