This guide describes a reproducible method for building a portable Windrop environment under Cygwin around the following components:
The goal is to produce a clean, isolated, restartable tree without depending on any implicit session state or an older build.
The approach is straightforward:
Many thanks to Michael Ortmann for the original guide. It provided an excellent foundation and was extremely helpful as a starting point for reproducing a modern portable Windrop build. This HOWTO builds on that work and documents the extra steps, fixes, and packaging details needed to make the process fully reproducible in practice.
https://github.com/michaelortmann/windrop?tab=readme-ov-file
Install at least the following packages:
autoconf2.7
automake1.17
binutils
gcc-core
gcc-g++
make
mingw64-x86_64-gcc-core
pkg-config
git
wget
curl
zip
unzip
tar
xz
patch
perl
python3
python3-devel
rebase
dash
zlib
zlib-devel
Quick verification:
for cmd in gcc g++ make autoconf pkg-config tar wget; do
printf '%-12s ' "$cmd"
if command -v "$cmd" >/dev/null 2>&1; then
"$cmd" --version 2>/dev/null | head -1
else
echo "KO"
fi
done
printf '%-12s ' "nproc"
if command -v nproc >/dev/null 2>&1; then
nproc
else
echo "KO"
fi
Since Eggdrop can build python.mod, also check:
echo "python3: $(command -v python3 2>/dev/null || echo KO)"
echo "python version: $(python3 --version 2>/dev/null || echo KO)"
echo "python3-config: $(readlink -f /usr/bin/python3-config 2>/dev/null || echo KO)"
echo "includes: $(/usr/bin/python3-config --includes 2>/dev/null || echo KO)"
echo "ldflags: $(/usr/bin/python3-config --ldflags 2>/dev/null || echo KO)"
Important point under Cygwin:
$(command -v python3-config) in this guide/usr/bin/python3-config-I//include/... and -L//libThe workflow below also supports the optional integration of three external modules often associated with recent Windrop builds:
botnetop.modgseen.modstats.modThey are retrieved via git clone, which is why git is listed in the prerequisites.
Under Cygwin, builds can be sped up with:
nproc
make -j"$(nproc)"
In this guide:
make -j"$(nproc)" is used for compilationmake install remains single-threadedtcllib intentionally stays more conservativeDefine these variables at the start of the session:
export BUILD_ROOT=/home/eggbox/windrop-build
export WINDROP_ROOT=/home/eggbox/windrop-root
export OPENSSL_PREFIX=$WINDROP_ROOT/openssl
export TCL_PREFIX=$WINDROP_ROOT/tcl
export PATH="$TCL_PREFIX/bin:$OPENSSL_PREFIX/bin:/bin:/usr/bin:$PATH"
export LD_LIBRARY_PATH="$OPENSSL_PREFIX/lib:$OPENSSL_PREFIX/lib64:$TCL_PREFIX/lib"
export TCLLIBPATH="$TCL_PREFIX/lib"
export PKG_CONFIG_PATH="$OPENSSL_PREFIX/lib/pkgconfig:$OPENSSL_PREFIX/lib64/pkgconfig"
Verification:
echo "BUILD_ROOT=$BUILD_ROOT"
echo "WINDROP_ROOT=$WINDROP_ROOT"
echo "OPENSSL_PREFIX=$OPENSSL_PREFIX"
echo "TCL_PREFIX=$TCL_PREFIX"
If you open a new Cygwin session later, simply rerun this block.
Create the working directory tree:
mkdir -p "$BUILD_ROOT"/{src,build} "$WINDROP_ROOT"
Verification:
find "$BUILD_ROOT" -maxdepth 2 -type d | sort
find "$WINDROP_ROOT" -maxdepth 2 -type d | sort
Move into the source directory:
mkdir -p "$BUILD_ROOT/src"
cd "$BUILD_ROOT/src"
Download the archives:
wget -O openssl-3.6.1.tar.gz \
"https://github.com/openssl/openssl/releases/download/openssl-3.6.1/openssl-3.6.1.tar.gz"
wget -O tcl9.0.3-src.tar.gz \
"https://prdownloads.sourceforge.net/tcl/tcl9.0.3-src.tar.gz"
wget -O tcltls-2.0-src.tar.gz \
"https://core.tcl-lang.org/tcltls/uv/tcltls-2.0-src.tar.gz"
wget -O eggdrop-1.10.1.tar.gz \
"https://ftp.eggheads.org/pub/eggdrop/source/1.10/eggdrop-1.10.1.tar.gz"
wget -O tcllib-2.0.tar.gz \
"https://core.tcl-lang.org/tcllib/tarball/tcllib-2.0.tar.gz?r=tcllib-2-0"
These modules are not provided as .tar.gz archives in this guide. They will be fetched later directly into the Eggdrop tree via Git.
Repositories used:
https://github.com/michaelortmann/botnetop.modhttps://github.com/michaelortmann/gseen.modhttps://github.com/michaelortmann/stats.modVerification:
ls -lh "$BUILD_ROOT/src"
.tar.gz archivesTo avoid fragile cd usage, consistently use this method:
cd "$BUILD_ROOT/build"
export ARCHIVE="$BUILD_ROOT/src/monarchive.tar.gz"
export SRC_DIR="$(tar tzf "$ARCHIVE" | head -1 | cut -d/ -f1)"
echo "$SRC_DIR"
tar xzf "$ARCHIVE"
cd "$SRC_DIR"
rm -rf "$OPENSSL_PREFIX"
mkdir -p "$OPENSSL_PREFIX"
cd "$BUILD_ROOT/build"
export ARCHIVE="$BUILD_ROOT/src/openssl-3.6.1.tar.gz"
export SRC_DIR="$(tar tzf "$ARCHIVE" | head -1 | cut -d/ -f1)"
tar xzf "$ARCHIVE"
cd "$SRC_DIR"
./Configure \
--prefix="$OPENSSL_PREFIX" \
shared \
Cygwin-x86_64
Important point: the target must be Cygwin-x86_64 with an uppercase C.
make -j"$(nproc)"
make install_sw
chmod 755 "$OPENSSL_PREFIX/bin/cygcrypto-3.dll" 2>/dev/null || true
chmod 755 "$OPENSSL_PREFIX/bin/cygssl-3.dll" 2>/dev/null || true
"$OPENSSL_PREFIX/bin/openssl" version -a
ls -l "$OPENSSL_PREFIX/bin"/cyg*.dll
rm -rf "$TCL_PREFIX"
mkdir -p "$TCL_PREFIX"
mkdir -p "$TCL_PREFIX/man/mann"
cd "$BUILD_ROOT/build"
export ARCHIVE="$BUILD_ROOT/src/tcl9.0.3-src.tar.gz"
export SRC_DIR="$(tar tzf "$ARCHIVE" | head -1 | cut -d/ -f1)"
tar xzf "$ARCHIVE"
cd "$SRC_DIR/unix"
./configure --prefix="$TCL_PREFIX"
make -j"$(nproc)"
make install
ln -sf "$TCL_PREFIX/bin/tclsh9.0" "$TCL_PREFIX/bin/tclsh"
"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
puts "Tcl=[info patchlevel]"
puts "auto_path=$auto_path"
EOF
Expected result: Tcl=9.0.3
rm -rf "$TCL_PREFIX/lib/tcltls2.0"
cd "$BUILD_ROOT/build"
export ARCHIVE="$BUILD_ROOT/src/tcltls-2.0-src.tar.gz"
export SRC_DIR="$(tar tzf "$ARCHIVE" | head -1 | cut -d/ -f1)"
tar xzf "$ARCHIVE"
cd "$SRC_DIR"
make distclean 2>/dev/null || true
make clean 2>/dev/null || true
./configure \
--prefix="$TCL_PREFIX" \
--with-tcl="$TCL_PREFIX/lib" \
--with-tclinclude="$TCL_PREFIX/include" \
CPPFLAGS="-I$OPENSSL_PREFIX/include -I$TCL_PREFIX/include" \
LDFLAGS="-L$OPENSSL_PREFIX/lib -L$OPENSSL_PREFIX/lib64 -L$TCL_PREFIX/lib"
make -j"$(nproc)"
make install
chmod 755 "$TCL_PREFIX/lib/tcltls2.0/cygtcl9tls2.0.dll" 2>/dev/null || true
chmod 755 "$OPENSSL_PREFIX/bin/cygcrypto-3.dll" 2>/dev/null || true
chmod 755 "$OPENSSL_PREFIX/bin/cygssl-3.dll" 2>/dev/null || true
find "$TCL_PREFIX/lib/tcltls2.0" -type d -exec chmod 755 {} \; 2>/dev/null || true
"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
puts "Tcl=[info patchlevel]"
puts "tls=[package require tls]"
EOF
Expected result:
Tcl=9.0.3tls=2.0cd "$BUILD_ROOT/build"
export ARCHIVE="$BUILD_ROOT/src/tcllib-2.0.tar.gz"
export SRC_DIR="$(tar tzf "$ARCHIVE" | head -1 | cut -d/ -f1)"
tar xzf "$ARCHIVE"
cd "$SRC_DIR"
make clean 2>/dev/null || true
rm -rf "$TCL_PREFIX/lib/tcllib2.0"
rm -rf "$TCL_PREFIX/man"
mkdir -p "$TCL_PREFIX/man/mann"
installer.tclIn this environment, tcllib 2.0 may fail with:
error deleting ".../tcllib2.0/page/plugins": directory not empty
Backup:
cp -a installer.tcl installer.tcl.orig
Perl patch:
perl -0pi -e 's/run file delete \$sub/set __visible [glob -nocomplain [file join \$sub *]]\n set __hidden [glob -nocomplain -types hidden [file join \$sub *]]\n if {![llength [concat \$__visible \$__hidden]]} {\n catch {run file delete -force -- \$sub}\n }/s' installer.tcl
Visual check:
nl -ba installer.tcl | sed -n '129,140p'
./configure --prefix="$TCL_PREFIX"
Do not use plain make here: it also tries to run a critcl step.
Use directly:
make install-tcl
"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
package require fileutil
puts "tcllib OK"
EOF
rm -rf "$WINDROP_ROOT/eggdrop-1.10.1"
cd "$BUILD_ROOT/build"
export ARCHIVE="$BUILD_ROOT/src/eggdrop-1.10.1.tar.gz"
export SRC_DIR="$(tar tzf "$ARCHIVE" | head -1 | cut -d/ -f1)"
tar xzf "$ARCHIVE"
cd "$SRC_DIR"
If the goal is to reproduce a build enriched with botnetop, gseen, and stats, clone the repositories into src/mod before configuration:
cd "$BUILD_ROOT/build/$SRC_DIR/src/mod"
env -i HOME="$HOME" USER="$USER" TERM="$TERM" PATH="/bin:/usr/bin" \
git clone --depth=1 https://github.com/michaelortmann/botnetop.mod.git
env -i HOME="$HOME" USER="$USER" TERM="$TERM" PATH="/bin:/usr/bin" \
git clone --depth=1 https://github.com/michaelortmann/gseen.mod.git
env -i HOME="$HOME" USER="$USER" TERM="$TERM" PATH="/bin:/usr/bin" \
git clone --depth=1 https://github.com/michaelortmann/stats.mod.git
cd "$BUILD_ROOT/build/$SRC_DIR"
This step is optional. If these modules are not needed, go straight to configuration.
Eggdrop auto-detects Tcl correctly in this context.
However, you must explicitly provide:
python3-configexport PYTHON_CONFIG=/usr/bin/python3-config
./configure \
--prefix="$WINDROP_ROOT/eggdrop-1.10.1" \
--with-sslinc="$OPENSSL_PREFIX/include" \
--with-ssllib="$OPENSSL_PREFIX/lib" \
--with-python-config="$PYTHON_CONFIG"
make config
python.mod verificationCheck the generated flags:
grep -n '^PYTHON_CFLAGS\|^PYTHON_LDFLAGS' src/mod/python.mod/Makefile
Expected result:
-I//include/...-L//libIf //include or //lib still appear, stop here and fix Python detection before building.
make -j"$(nproc)"
make install DEST="$WINDROP_ROOT/eggdrop-1.10.1"
cd "$BUILD_ROOT"
export PATH="$TCL_PREFIX/bin:$OPENSSL_PREFIX/bin:/bin:/usr/bin:$PATH"
export LD_LIBRARY_PATH="$OPENSSL_PREFIX/lib:$OPENSSL_PREFIX/lib64:$TCL_PREFIX/lib"
export TCLLIBPATH="$TCL_PREFIX/lib"
"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
puts "Tcl=[info patchlevel]"
puts "tls=[package require tls]"
catch {package require fileutil} r1
puts "fileutil=$r1"
EOF
Only continue if everything responds correctly.
For a portable Windrop bundle to launch directly from Windows, installing Eggdrop is not enough: you also need to copy the required runtime DLLs into the bot root.
Copy the following files into:
$WINDROP_ROOT/eggdrop-1.10.1/
Command:
cd "$WINDROP_ROOT/eggdrop-1.10.1/"
cp -f /bin/cygwin1.dll "$WINDROP_ROOT/eggdrop-1.10.1/"
cp -f /bin/cygz.dll "$WINDROP_ROOT/eggdrop-1.10.1/"
cp -f /bin/cyggcc_s-seh-1.dll "$WINDROP_ROOT/eggdrop-1.10.1/"
cp -f "$OPENSSL_PREFIX/bin/cygcrypto-3.dll" "$WINDROP_ROOT/eggdrop-1.10.1/"
cp -f "$OPENSSL_PREFIX/bin/cygssl-3.dll" "$WINDROP_ROOT/eggdrop-1.10.1/"
cp -f "$TCL_PREFIX/bin/cygtcl9.0.dll" "$WINDROP_ROOT/eggdrop-1.10.1/"
At this point, the bot root must contain at least:
eggdrop.exe-1.10.1cygwin1.dllcygz.dllcyggcc_s-seh-1.dllcygcrypto-3.dllcygssl-3.dllcygtcl9.0.dllWithout this step, a direct launch from cmd.exe may fail with a message saying a DLL is missing.
The portable bundle relies on scripts/autoload_pkgs.tcl to load Tcl packages from the local ./lib tree, without depending on a Tcl environment installed elsewhere on the machine.
This script must therefore be present in:
scripts/autoload_pkgs.tcl
The provided minimal configuration explicitly loads this file before the HTTPS wrapper and before the other Tcl scripts.
Snippet:
# Portable loader from ./lib only
source scripts/autoload_pkgs.tcl
The bundle must therefore include at least the following:
scripts/autoload_pkgs.tcl
lib/tcl9
lib/tcllib2.0
and, if Tcl TLS support is used by the scripts, the portable TclTLS directory matching the build.
If the provided Windrop-portable-1.10.1-minimal.conf is used with the Tcl 9 HTTPS wrapper, you need to include a CA certificate bundle in the bot directory.
The expected location is:
ssl/certs/cacert.pem
Create the directory tree, then download cacert.pem:
mkdir -p "$WINDROP_ROOT/eggdrop-1.10.1/ssl/certs"
wget -O "$WINDROP_ROOT/eggdrop-1.10.1/ssl/certs/cacert.pem" \
"https://curl.se/ca/cacert.pem"
Verify CA bundle presence:
ls -lh "$WINDROP_ROOT/eggdrop-1.10.1/ssl/certs/cacert.pem"
If the configuration file Windrop-portable-1.10.1-minimal.conf is included with the bundle, you can then use:
set ::httpscompat::cafile "ssl/certs/cacert.pem"
and then uncomment the HTTPS wrapper load if needed:
source scripts/https_compat_tcl9.tcl
This step is only useful if Tcl scripts actually use package http, especially with https:// URLs.
An Eggdrop install under Cygwin can leave symlinks in the installed tree, notably:
eggdrop.exe -> eggdrop.exe-1.10.1modules -> modules-1.10.1To get a truly portable directory that is easy to copy from the Windows filesystem, it is better to replace these links with real files and real directories.
Command:
cd "$WINDROP_ROOT/eggdrop-1.10.1"
rm -f eggdrop.exe
cp -f eggdrop.exe-1.10.1 eggdrop.exe
rm -rf modules
cp -a modules-1.10.1 modules
If botnetop.mod, gseen.mod, and stats.mod have been cloned and built, then copy their configuration and language files into the installed tree:
cd "$BUILD_ROOT/build/$SRC_DIR"
cp src/mod/botnetop.mod/botnetop.conf "$WINDROP_ROOT/eggdrop-1.10.1" 2>/dev/null || true
cp src/mod/gseen.mod/gseen.conf "$WINDROP_ROOT/eggdrop-1.10.1" 2>/dev/null || true
cp src/mod/gseen.mod/*.lang "$WINDROP_ROOT/eggdrop-1.10.1/language/" 2>/dev/null || true
cp src/mod/stats.mod/stats.conf "$WINDROP_ROOT/eggdrop-1.10.1" 2>/dev/null || true
cp src/mod/stats.mod/language/* "$WINDROP_ROOT/eggdrop-1.10.1/language/" 2>/dev/null || true
cp -r src/mod/stats.mod/templates "$WINDROP_ROOT/eggdrop-1.10.1" 2>/dev/null || true
These copies are also optional and only make sense if the modules were added to the Eggdrop tree earlier.
find "$WINDROP_ROOT/eggdrop-1.10.1" -maxdepth 3 -ls
The provided Windrop-portable-1.10.1-minimal.conf file serves as the starting point.
Before the first launch, check at least:
username, admin, network, nick, altnick, realname)listen)The Tcl 9 HTTPS wrapper is provided but remains disabled by default:
#source scripts/https_compat_tcl9.tcl
The Tcl 9 / TclTLS 2.0 HTTPS wrapper restores clean compatibility for Tcl scripts using package http, especially with https:// URLs, in a portable Windrop bundle based on Tcl 9, TclTLS 2.0, and tcllib 2.0.
This wrapper must be loaded before the Tcl scripts that use HTTP/HTTPS.
Provided archive:
Example configuration loading:
# Optional overrides before loading:
# set ::httpscompat::debug 1
# set ::httpscompat::cafile "ssl/certs/cacert.pem"
# set ::httpscompat::selftest_host "google.com"
source scripts/https_compat_tcl9.tcl
If this wrapper is enabled, it is also recommended to include a portable CA bundle in:
ssl/certs/cacert.pem
and set the following in the configuration:
set ::httpscompat::cafile "ssl/certs/cacert.pem"
This appendix complements the HOWTO section covering the portable CA bundle and the initial configuration.
In the installed tree, eggdrop.exe may be a symlink or a stub that is not directly usable depending on the Windows context.
The real binary to launch is usually eggdrop.exe-1.10.1.
The simplest approach is therefore to provide an eggdrop.cmd launcher placed in the Eggdrop install directory.
This wrapper:
openssl\bin and tcl\bin paths without overwriting the system PATH;TCL_LIBRARY, TCLLIBPATH, and the certificate-related variables;eggdrop.exe-1.10.1 binary.eggdrop.cmd file@echo off
setlocal EnableExtensions
set "BASE_DIR=%~dp0"
if "%BASE_DIR:~-1%"=="\" set "BASE_DIR=%BASE_DIR:~0,-1%"
set "EGGDIR=%BASE_DIR%"
set "OPENSSL_DIR=%EGGDIR%\..\openssl"
set "TCL_DIR=%EGGDIR%\..\tcl"
set "BINARY=%EGGDIR%\eggdrop.exe-1.10.1"
if not exist "%BINARY%" (
echo [ERROR] Could not find binary : "%BINARY%"
exit /b 1
)
set "PATH=%OPENSSL_DIR%\bin;%TCL_DIR%\bin;%PATH%"
set "TCL_LIBRARY=%TCL_DIR%\lib\tcl9"
set "TCLLIBPATH=%TCL_DIR%\lib"
set "SSL_CERT_DIR=%EGGDIR%\ssl\certs"
set "SSL_CERT_FILE=%EGGDIR%\ssl\certs\cacert.pem"
pushd "%EGGDIR%" >nul 2>&1
if errorlevel 1 (
echo [ERROR] Could not change directory to "%EGGDIR%"
exit /b 1
)
"%BINARY%" %*
set "RC=%ERRORLEVEL%"
popd >nul 2>&1
exit /b %RC%
At this stage of the installation, you should no longer use the Cygwin shell, but a regular cmd.exe instead. The /home/eggbox/windrop-root/eggdrop-1.10.1 directory can be copied anywhere on the Windows filesystem.
Initialize a new bot:
eggdrop.cmd -m bot.conf
Run in the foreground:
eggdrop.cmd -n bot.conf
Normal launch:
eggdrop.cmd bot.conf
bot.conf is given here for illustration only.
In practice, you should start from Windrop-portable-1.10.1-minimal.conf, adapt it, and then use it with this launcher.
Preventive cleanup is part of the normal workflow.
make cleanA make clean or make distclean should never depend on the current directory left by the previous step.
.tar.gz archivesAlways:
ARCHIVESRC_DIR with tar tzf ... | head -1 | cut -d/ -f1tar xzfcd "$SRC_DIR"Reload the environment variable block.
Clean the prefixes and restart the steps from the beginning of the affected component.
The recommended sequence is:
This method deliberately remains conservative. It prioritizes reproducibility, readability, and a clean recovery path when a build fails.
You must be logged in to reply.