Forum teuk.org

Building a Portable Windrop 1.10.1 Bundle with Tcl 9 and OpenSSL 3.6.1

in Eggdrop · started by TeuK · 23h ago

TeuK · 23h ago

Building a portable Windrop environment under Cygwin

Overview

This guide describes a reproducible method for building a portable Windrop environment under Cygwin around the following components:

  • OpenSSL 3.6.1
  • Tcl 9.0.3
  • TclTLS 2.0
  • tcllib 2.0
  • Eggdrop 1.10.1
  • optional additional modules: botnetop, gseen, stats

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:

  • work with stable environment variables
  • clearly separate sources, build, and installation
  • clean before each major component
  • validate each step before moving on

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


Cygwin prerequisites

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

Python verification

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:

  • do not use $(command -v python3-config) in this guide
  • force /usr/bin/python3-config
  • this avoids broken paths such as -I//include/... and -L//lib

Optional additional modules

The workflow below also supports the optional integration of three external modules often associated with recent Windrop builds:

  • botnetop.mod
  • gseen.mod
  • stats.mod

They are retrieved via git clone, which is why git is listed in the prerequisites.

Parallel builds

Under Cygwin, builds can be sped up with:

nproc
make -j"$(nproc)"

In this guide:

  • make -j"$(nproc)" is used for compilation
  • make install remains single-threaded
  • tcllib intentionally stays more conservative

Environment variables

Define 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.


Directory setup

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

Downloading the sources

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"

Sources for optional modules

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.mod
  • https://github.com/michaelortmann/gseen.mod
  • https://github.com/michaelortmann/stats.mod

Verification:

ls -lh "$BUILD_ROOT/src"

General rule for .tar.gz archives

To 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"

1. Build OpenSSL 3.6.1

Preventive cleanup

rm -rf "$OPENSSL_PREFIX"
mkdir -p "$OPENSSL_PREFIX"

Extraction

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"

Configuration

./Configure \
  --prefix="$OPENSSL_PREFIX" \
  shared \
  Cygwin-x86_64

Important point: the target must be Cygwin-x86_64 with an uppercase C.

Build and install

make -j"$(nproc)"
make install_sw

DLL permissions

chmod 755 "$OPENSSL_PREFIX/bin/cygcrypto-3.dll" 2>/dev/null || true
chmod 755 "$OPENSSL_PREFIX/bin/cygssl-3.dll" 2>/dev/null || true

Check openssl version

"$OPENSSL_PREFIX/bin/openssl" version -a
ls -l "$OPENSSL_PREFIX/bin"/cyg*.dll

2. Build Tcl 9.0.3

Preventive cleanup

rm -rf "$TCL_PREFIX"
mkdir -p "$TCL_PREFIX"
mkdir -p "$TCL_PREFIX/man/mann"

Extraction

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, build, install

./configure --prefix="$TCL_PREFIX"
make -j"$(nproc)"
make install
ln -sf "$TCL_PREFIX/bin/tclsh9.0" "$TCL_PREFIX/bin/tclsh"

Check tcl version

"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
puts "Tcl=[info patchlevel]"
puts "auto_path=$auto_path"
EOF

Expected result: Tcl=9.0.3


3. Build TclTLS 2.0

Preventive cleanup

rm -rf "$TCL_PREFIX/lib/tcltls2.0"

Extraction

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"

Build cleanup

make distclean 2>/dev/null || true
make clean 2>/dev/null || true

Configuration

./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"

Build and install

make -j"$(nproc)"
make install

DLL permissions

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

Check TclTLS version

"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
puts "Tcl=[info patchlevel]"
puts "tls=[package require tls]"
EOF

Expected result:

  • Tcl=9.0.3
  • tls=2.0

4. Install tcllib 2.0

Extraction

cd "$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"

Preventive cleanup

make clean 2>/dev/null || true
rm -rf "$TCL_PREFIX/lib/tcllib2.0"
rm -rf "$TCL_PREFIX/man"
mkdir -p "$TCL_PREFIX/man/mann"

Local patch for installer.tcl

In 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'

Configuration

./configure --prefix="$TCL_PREFIX"

Pure Tcl installation

Do not use plain make here: it also tries to run a critcl step.

Use directly:

make install-tcl

Check tcllib health

"$TCL_PREFIX/bin/tclsh9.0" <<'EOF'
package require fileutil
puts "tcllib OK"
EOF

5. Build Eggdrop 1.10.1

Preventive cleanup

rm -rf "$WINDROP_ROOT/eggdrop-1.10.1"

Extraction

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"

Optional additional modules

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.

Configuration

Eggdrop auto-detects Tcl correctly in this context.
However, you must explicitly provide:

  • the path to the OpenSSL headers
  • the path to the OpenSSL libraries
  • the stable path to python3-config
export 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"

Build

make config

python.mod verification

Check the generated flags:

grep -n '^PYTHON_CFLAGS\|^PYTHON_LDFLAGS' src/mod/python.mod/Makefile

Expected result:

  • consistent Python paths
  • no -I//include/...
  • no -L//lib

If //include or //lib still appear, stop here and fix Python detection before building.

make -j"$(nproc)"

Installation

make install DEST="$WINDROP_ROOT/eggdrop-1.10.1"

Global Tcl environment verification

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.


Copy the required DLLs into the bot root

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.1
  • cygwin1.dll
  • cygz.dll
  • cyggcc_s-seh-1.dll
  • cygcrypto-3.dll
  • cygssl-3.dll
  • cygtcl9.0.dll

Without this step, a direct launch from cmd.exe may fail with a message saying a DLL is missing.

Portable Tcl package loader

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.

Add the portable CA bundle for HTTPS

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.

Replace symlinks for a real Windows bundle

An Eggdrop install under Cygwin can leave symlinks in the installed tree, notably:

  • eggdrop.exe -> eggdrop.exe-1.10.1
  • modules -> modules-1.10.1

To 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

Copy files for optional 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.

Verify the installed tree

find "$WINDROP_ROOT/eggdrop-1.10.1" -maxdepth 3 -ls

Initial configuration

The provided Windrop-portable-1.10.1-minimal.conf file serves as the starting point.
Before the first launch, check at least:

  • the bot identity (username, admin, network, nick, altnick, realname)
  • the listening port (listen)
  • the owner
  • the IRC servers
  • the channels to join
  • the modules you actually want

The Tcl 9 HTTPS wrapper is provided but remains disabled by default:

#source scripts/https_compat_tcl9.tcl

Appendix – Tcl 9 / TclTLS 2.0 HTTPS wrapper

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.

Launching Eggdrop with a portable CMD wrapper

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:

  • uses its own location as the base;
  • adds the openssl\bin and tcl\bin paths without overwriting the system PATH;
  • sets TCL_LIBRARY, TCLLIBPATH, and the certificate-related variables;
  • launches the real 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%

Usage

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.


Practical reminders

Always clean before major components

Preventive cleanup is part of the normal workflow.

Always reposition before running make clean

A make clean or make distclean should never depend on the current directory left by the previous step.

For .tar.gz archives

Always:

  • define ARCHIVE
  • determine SRC_DIR with tar tzf ... | head -1 | cut -d/ -f1
  • use tar xzf
  • run cd "$SRC_DIR"

If a variable has disappeared

Reload the environment variable block.

If the environment becomes questionable

Clean the prefixes and restart the steps from the beginning of the affected component.


Summary

The recommended sequence is:

  1. prepare the environment variables
  2. download the sources
  3. build OpenSSL
  4. build Tcl
  5. build TclTLS
  6. install tcllib
  7. build Eggdrop
  8. optionally add the extra modules and their associated files
  9. validate the whole setup

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.