From 02dfa5bac32443206f64c05329d19e32f9eceefa Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 7 Nov 2024 00:27:02 +0100 Subject: [PATCH] Add OpenLDAP MSYS2 builder This does a full client build of OpenLDAP, using upstream's autotools based build chain on MSYS2, but using MSVC build tools. This requires wrappers for the MSVC build tools; ar-lib and compile are taken unmodified from automake; windres is a minimalist self-made wrapper. We also need POSIX regex for the client tools (such as ldapsearch); we use rxspencer[1], which we build on the fly and link in statically. Using upstream's autotools based toolchain has the obvious drawback that building now requires MSYS2 instead of "native" Windows tools only. On the other hand, instead of needing hand-made configuration and Visual Studio solutions, what is volatile regarding updates of the library, we only need a manageable patch set. Plus, we get the client tools basically for free. If we want to distribute them is another question. [1] --- .github/workflows/openldap_msys.yml | 103 ++++++++ autotools/ar-lib | 279 ++++++++++++++++++++++ autotools/compile | 351 ++++++++++++++++++++++++++++ autotools/windres | 49 ++++ 4 files changed, 782 insertions(+) create mode 100644 .github/workflows/openldap_msys.yml create mode 100755 autotools/ar-lib create mode 100755 autotools/compile create mode 100755 autotools/windres diff --git a/.github/workflows/openldap_msys.yml b/.github/workflows/openldap_msys.yml new file mode 100644 index 0000000..77dda3b --- /dev/null +++ b/.github/workflows/openldap_msys.yml @@ -0,0 +1,103 @@ +name: Build openldap_msys +on: + workflow_dispatch: + inputs: + version: + description: openldap tag to build + required: true + php: + description: PHP version to build for + required: true + stability: + description: the series stability + required: false + default: 'staging' +defaults: + run: + shell: cmd +jobs: + build: + strategy: + matrix: + arch: [x64, x86] + runs-on: windows-2022 + steps: + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - name: Checkout winlib-builder + uses: actions/checkout@v4 + with: + path: winlib-builder + - name: Checkout openldap + uses: actions/checkout@v4 + with: + path: openldap + repository: winlibs/openldap + ref: ${{github.event.inputs.version}} + - name: Compute virtual inputs + id: virtuals + run: powershell winlib-builder/scripts/compute-virtuals -version ${{github.event.inputs.php}} -arch ${{matrix.arch}} + - name: Fetch dependencies + run: powershell winlib-builder/scripts/fetch-deps -lib openldap -version ${{github.event.inputs.php}} -vs ${{steps.virtuals.outputs.vs}} -arch ${{matrix.arch}} -stability ${{github.event.inputs.stability}} + - name: Checkout rxspencer + uses: actions/checkout@v4 + with: + path: rxspencer + repository: garyhouston/rxspencer + ref: v3.9.0 + - name: Setup MSVC development environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{matrix.arch}} + toolset: ${{steps.virtuals.outputs.toolset}} + - name: Build and install rxspencer + run: | + cd rxspencer + cmake -G "Visual Studio 17 2022" -A ${{steps.virtuals.outputs.msarch}} -T ${{steps.virtuals.outputs.msts}} -DCMAKE_SYSTEM_VERSION=${{steps.virtuals.outputs.winsdk}} -Drxshared=0 -DBUILD_TESTING=OFF -DINSTALL_DOCS=OFF . + cmake --build . --config Release + cmake --install . --config Release --prefix ..\deps + move ..\deps\include\rxspencer\regex.h ..\deps\include\regex.h + rmdir ..\deps\include\rxspencer + ren ..\deps\lib\rxspencer.lib libregex.lib + tree /a /f ..\deps + - name: Create build script + run: | + cd openldap + set WORKSPACE=${{github.workspace}} + set WORKSPACE=%WORKSPACE:\=/% + set WORKSPACE=/%WORKSPACE::=% + > winlibs.sh echo export PATH=/usr/local/bin:/usr/bin:/bin:/opt/bin:$PATH + >>winlibs.sh echo pacman -S --noconfirm make groff + >>winlibs.sh echo ./configure --host=x86_64-w64-mingw32 --disable-slapd --disable-shared --prefix %WORKSPACE%/install --includedir=%WORKSPACE%/install/include/openldap \ + >>winlibs.sh echo CC='%WORKSPACE%/winlib-builder/autotools/compile cl -nologo' \ + >>winlibs.sh echo AR='%WORKSPACE%/winlib-builder/autotools/ar-lib lib' \ + >>winlibs.sh echo NM='dumpbin -symbols' \ + >>winlibs.sh echo STRIP=':' \ + >>winlibs.sh echo RANLIB=':' \ + >>winlibs.sh echo WINDRES='%WORKSPACE%/winlib-builder/autotools/windres' \ + >>winlibs.sh echo RC='%WORKSPACE%/winlib-builder/autotools/windres' \ + >>winlibs.sh echo CFLAGS='-MD -O2 -Ob1 -Gy -Zc:inline -GF' \ + >>winlibs.sh echo LIBS='-lWs2_32' + >>winlibs.sh echo make depend + >>winlibs.sh echo make + >>winlibs.sh echo make install + - name: Build openldap + run: | + cd openldap + set CHERE_INVOKING=yes + set MSYS2_PATH_TYPE=inherit + set MSYS2_ARG_CONV_EXCL=* + set INCLUDE=${{github.workspace}}\deps\include;%INCLUDE% + set LIB=${{github.workspace}}\deps\lib;%LIB% + C:\msys64\usr\bin\bash.exe -lc ./winlibs.sh + - name: Patch installation + run: | + xcopy openldap\libraries\liblber\olber32_a.pdb install\lib\* + xcopy openldap\libraries\libldap\oldap32_a.pdb install\lib\* + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{github.event.inputs.version}}-${{steps.virtuals.outputs.vs}}-${{matrix.arch}} + path: install diff --git a/autotools/ar-lib b/autotools/ar-lib new file mode 100755 index 0000000..1521987 --- /dev/null +++ b/autotools/ar-lib @@ -0,0 +1,279 @@ +#! /bin/sh +# Wrapper for Microsoft lib.exe + +me=ar-lib +scriptversion=2024-06-19.01; # UTC + +# Copyright (C) 2010-2024 Free Software Foundation, Inc. +# Written by Peter Rosin . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + + +# func_error message +func_error () +{ + echo "$me: $1" 1>&2 + exit 1 +} + +file_conv= + +# func_file_conv build_file +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv in + mingw) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin | msys) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_at_file at_file operation archive +# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE +# for each of them. +# When interpreting the content of the @FILE, do NOT use func_file_conv, +# since the user would need to supply preconverted file names to +# binutils ar, at least for MinGW. +func_at_file () +{ + operation=$2 + archive=$3 + at_file_contents=`cat "$1"` + eval set x "$at_file_contents" + shift + + for member + do + $AR -NOLOGO $operation:"$member" "$archive" || exit $? + done +} + +case $1 in + '') + func_error "no command. Try '$0 --help' for more information." + ;; + -h | --h*) + cat <. +GNU Automake home page: . +General help using GNU software: . +EOF + exit $? + ;; + -v | --v*) + echo "$me (GNU Automake) $scriptversion" + exit $? + ;; +esac + +if test $# -lt 3; then + func_error "you must specify a program, an action and an archive" +fi + +AR=$1 +shift +while : +do + if test $# -lt 2; then + func_error "you must specify a program, an action and an archive" + fi + case $1 in + -lib | -LIB \ + | -ltcg | -LTCG \ + | -machine* | -MACHINE* \ + | -subsystem* | -SUBSYSTEM* \ + | -verbose | -VERBOSE \ + | -wx* | -WX* ) + AR="$AR $1" + shift + ;; + -nologo | -NOLOGO) + # We always invoke AR with -nologo, so don't need to add it again. + shift + ;; + *) + action=$1 + shift + break + ;; + esac +done +orig_archive=$1 +shift +func_file_conv "$orig_archive" +archive=$file + +# strip leading dash in $action +action=${action#-} + +delete= +extract= +list= +quick= +replace= +index= +create= + +while test -n "$action" +do + case $action in + d*) delete=yes ;; + x*) extract=yes ;; + t*) list=yes ;; + q*) quick=yes ;; + r*) replace=yes ;; + s*) index=yes ;; + S*) ;; # the index is always updated implicitly + c*) create=yes ;; + u*) ;; # TODO: don't ignore the update modifier + v*) ;; # TODO: don't ignore the verbose modifier + *) + func_error "unknown action specified" + ;; + esac + action=${action#?} +done + +case $delete$extract$list$quick$replace,$index in + yes,* | ,yes) + ;; + yesyes*) + func_error "more than one action specified" + ;; + *) + func_error "no action specified" + ;; +esac + +if test -n "$delete"; then + if test ! -f "$orig_archive"; then + func_error "archive not found" + fi + for member + do + case $1 in + @*) + func_at_file "${1#@}" -REMOVE "$archive" + ;; + *) + func_file_conv "$1" + $AR -NOLOGO -REMOVE:"$file" "$archive" || exit $? + ;; + esac + done + +elif test -n "$extract"; then + if test ! -f "$orig_archive"; then + func_error "archive not found" + fi + if test $# -gt 0; then + for member + do + case $1 in + @*) + func_at_file "${1#@}" -EXTRACT "$archive" + ;; + *) + func_file_conv "$1" + $AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $? + ;; + esac + done + else + $AR -NOLOGO -LIST "$archive" | tr -d '\r' | sed -e 's/\\/\\\\/g' \ + | while read member + do + $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $? + done + fi + +elif test -n "$quick$replace"; then + if test ! -f "$orig_archive"; then + if test -z "$create"; then + echo "$me: creating $orig_archive" + fi + orig_archive= + else + orig_archive=$archive + fi + + for member + do + case $1 in + @*) + func_file_conv "${1#@}" + set x "$@" "@$file" + ;; + *) + func_file_conv "$1" + set x "$@" "$file" + ;; + esac + shift + shift + done + + if test -n "$orig_archive"; then + $AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $? + else + $AR -NOLOGO -OUT:"$archive" "$@" || exit $? + fi + +elif test -n "$list"; then + if test ! -f "$orig_archive"; then + func_error "archive not found" + fi + $AR -NOLOGO -LIST "$archive" || exit $? +fi diff --git a/autotools/compile b/autotools/compile new file mode 100755 index 0000000..49b3d05 --- /dev/null +++ b/autotools/compile @@ -0,0 +1,351 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2024-06-19.01; # UTC + +# Copyright (C) 1999-2024 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.lo | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +GNU Automake home page: . +General help using GNU software: . +EOF + exit $? + ;; + -v | --v*) + echo "compile (GNU Automake) $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + clang-cl | *[/\\]clang-cl | clang-cl.exe | *[/\\]clang-cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/autotools/windres b/autotools/windres new file mode 100755 index 0000000..78bf5a5 --- /dev/null +++ b/autotools/windres @@ -0,0 +1,49 @@ +#! /bin/sh + +DEFINES=() + +while [[ $# -gt 0 ]]; do + case $1 in + -i) + INPUT="`cygpath -w $2`" + shift + shift + ;; + --output-format=*) + if test "${1#*=}" != coff; then + echo "Unsupported output format ${1#*=}" + exit 1 + fi + shift + ;; + -O) + if test "$2" != coff; then + echo "Unsupported output format $2" + exit 1 + fi + shift + shift + ;; + -o) + OUTPUT="`cygpath -w $2`" + shift + shift + ;; + -D*) + DEFINES+=("$1") + shift + ;; + -*) + echo "Unsupported option $1" + exit 1 + ;; + *) + INPUT="`cygpath -w $1`" + shift + ;; + esac +done + +rc.exe ${DEFINES[@]} -fo ${OUTPUT}.res ${INPUT} +cvtres.exe -machine:${Platform} -out:${OUTPUT} ${OUTPUT}.res +rm ${OUTPUT}.res