love5an: (Default)
Dmitry Ignatiev ([personal profile] love5an) wrote2012-02-15 09:38 pm

Скрипт для сборки SBCL под MinGW+MSYS

Использовать например так:
build-sbcl.sh --with-git=/c/Git

Работает в любой директории, но если не указаны sbcl-srcdir и win32-srcdir, то скрипту требуется Git, для скачивания последних версий исходников из соответствующих репозиториев.
Также, Git нужен для определения версии SBCL.
Git для Windows можно взять тут: http://code.google.com/p/msysgit/

Кроме того, для сборки требуются более-менее новые GCC и SBCL.

Естественно, так как скрипт работает под MSYS, ему нужна более-менее полная среда MSYS.

Для поддержки SBCL core compression(SBCL умеет сжимать образы лиспа, т.е. ".core"-файлы) также нужна zlib, установленная там, где GCC сможет ее найти, т.е. например в c:/mingw. Причем, в этом билд-скрипте используется статическая версия, т.е. gcc должен суметь найти libz.a

Для сборки .MSI-пакета также необходим Windows Installer XML



#!/bin/bash

show_help()
{
    echo "Usage: $0 [OPTIONS]"
    echo "Options:"
    echo "  --help"
    echo "      Print this message and exit."
    echo "  --sbcl-srcdir=[DIR]"
    echo "      Source directory for upstream SBCL"
    echo "  --win32-srcdir=[DIR]"
    echo "      Source directory for sbcl-win32-threads fork"
    echo "  --build-dir=[DIR]"
    echo "      Build directory"
    echo "  --enable-runtime-optimizations[=yes/no] (default=no)"
    echo "      Enables small config fixes for optimization."
    echo "      Possibly makes runtime executable non-portable."
    echo "  --enable-core-compression[=yes/no] (default=yes)"
    echo "      Enables support for SBCL core compression."
    echo "      Requires zlib data compression library."
    echo "  --enable-latest[=yes/no] (default=yes)"
    echo "      Enables fetching latest sources from git repositories."
    echo "  --enable-tests[=yes/no] (default=no)"
    echo "      Run included tests after building SBCL"
    echo "  --with-git=[DIR]"
    echo "      Search for Git in DIR."
    echo "  --without-git"
    echo "      Disable Git support."
    echo "  --with-wix=[DIR]"
    echo "      Search for WIX in DIR."
    echo "  --without-wix"
    echo "      Do not build windows installer package."
    echo "  SBCL_VERSION=[VERSION]"    
    echo "      Unless Git support is enabled, assume sbcl version is VERSION."
    exit 0
}

error()
{
    echo "$1" >&2
    exit 1
}

arch=
case `uname -m` in
    *86) arch=x86 ;;
    i86pc) arch=x86 ;;
    *x86_64) arch=x86-64 ;;
    amd64) arch=x86-64 ;;
    *) error "Unknown machine type" ;;
esac

root_dir=`pwd -W`
git_prefix=
no_git=
wix_prefix=
no_wix=
sbcl_win32_threads_git=git://github.com/akovalenko/sbcl-win32-threads.git
sbcl_git=git://sbcl.git.sourceforge.net/gitroot/sbcl/sbcl.git
srcdir="${root_dir}/sbcl-win32-threads"
orig_srcdir="$root_dir/sbcl"
build_dir="${root_dir}/sbcl-win32-threads-build"
optimize=
compress=yes
latest=yes
tests=

arg_arg=
for arg in ${@}
do
    case "$arg" in
        *=*) arg_arg=`expr "$arg" : '[^=]*=\(.*\)'` ;;
        *) arg_arg=yes ;;
    esac
    case $arg in
        --help) show_help ;;
        --sbcl-srcdir=*) orig_srcdir=`dirname "${arg_arg}/."` ;;
        --win32-srcdir=*) srcdir=`dirname "${arg_arg}/."` ;;
        --build-dir=*) build_dir=`dirname "${arg_arg}/."` ;;
        --enable-runtime-optimizations) optimize=yes ;;
        --enable-runtime-optimizations=*)
            optimize=`echo "$arg_arg" | tr '[:upper:]' '[:lower:]'`
            case "$optimize" in
                y*) optimize=yes ;;
                n*) optimize="" ;;
            esac
            ;;
        --disable-runtime-optimizations) optimize="" ;;
        --enable-core-compression) compress=yes ;;
        --enable-core-compression=*)
            compress=`echo "$arg_arg" | tr '[:upper:]' '[:lower:]'`
            case "$compress" in
                y*) compress=yes ;;
                n*) compress="" ;;
            esac
            ;;
        --disable-core-compression) compress="" ;;
        --enable-latest) latest=yes ;;
        --enable-latest=*)
            latest=`echo "$arg_arg" | tr '[:upper:]' '[:lower:]'`
            case "$latest" in
                y*) latest=yes ;;
                n*) latest="" ;;
            esac
            ;;
        --disable-latest) latest="" ;;
        --enable-tests) tests=yes ;;
        --enable-tests=*)
            tests=`echo "$arg_arg" | tr '[:upper]' '[:lower]'`
            case "$tests" in
                y*) tests=yes ;;
                n*) tests="" ;;
            esac
            ;;
        --disable-tests) tests="" ;;
        --with-git=*) git_prefix="$arg_arg" ;;
        --without-git) no_git="yes" ;;
        --with-wix=*) wix_prefix="$arg_arg" ;;
        --without-wix) no_wix="yes" ;;
        SBCL_VERSION=*) SBCL_VERSION="$arg_arg" ;;
        *) error "Unkown option: $arg" ;;           
    esac   
done

case "$build_dir" in
    "$orig_srcdir")
        error "Unable to build sbcl-win32-threads in original sbcl source directory"
        ;;
    "$srcdir")
        error "Unable to build sbcl-win32-threads in source directory"
        ;;
esac

case "$srcdir" in
    "$orig_srcdir")
        error "Upstream sbcl and sbcl-win32-threads sources must exist in different directories"
        ;;
esac

if [ "$git_prefix" ]; then
    cd "$git_prefix" || error "Unable to enter Git install directory"
    git_prefix="/`pwd -W | sed -e 's/\://'`"
    PATH="${PATH}:${git_prefix}/bin"
fi

if [ -z "$no_git" ]; then
    no_git=`git --version &> /dev/null || echo "yes"`
    if [ "$no_git" ]; then
        echo "Warning: unable to find working Git" >&2
    fi
fi

if [ -z "$no_git" ]; then
    if [ "$latest" ]; then
        echo "Fetching latest sbcl sources"
        if [ -d "$orig_srcdir" ]; then
            cd "$orig_srcdir"
            orig_srcdir=`pwd -W`
            git pull || no_git=yes
            if [ "$no_git" ]; then
                echo "Warning: unable to pull updates from upstream sbcl repo" >&2
            fi
        else
            git clone "$sbcl_git" "$orig_srcdir" \
                || error "Unable to clone upstream sbcl repo"
        fi
        cd "$root_dir"
        if [ -z "$no_git" ]; then
            echo "Fetching latest sbcl-win32-threads sources"
            if [ -d "$srcdir" ]; then
                cd "$srcdir"
                srcdir=`pwd -W`
                git pull || no_git=yes
                if [ "$no_git" ]; then
                    echo "Warning: unable to pull updates from sbcl-win32-threads repo" >&2
                fi
            else
                git clone "$sbcl_win32_threads_git" "$srcdir" || \
                    error "Unable to clone sbcl-win32-threads repo."
            fi
        fi
    else
        cd "$srcdir" || \
            error "Unable to enter sbcl-win32-threads source directory"
        srcdir=`pwd -W`
        cd "$orig_srcdir" || \
            error "Unable to enter original sbcl source directory"
        orig_srcdir=`pwd -W`
    fi
fi

cd "$root_dir"

echo Copying sources into "$build_dir"

if [ -d "$build_dir" ]; then
    echo "Warning: build directory already exists" >&2
fi
mkdir -p "$build_dir" || error "Unable to create build directory"
cd "$build_dir"
build_dir=`pwd -W`
for f in "${srcdir}/*"; do cp -r $f "${build_dir}/"; done

config="${build_dir}/src/runtime/Config.${arch}-win32"

# This adds some extra optimizations. Optional step.
if [ "$optimize" ]; then
    echo Fixing configs...
    
    cat "$config" | \
    sed -e 's/-march=[^ ]*/-march=native/' | \
    sed -e 's/-mtune=[^ ]*/-mtune=native/' | \
    sed -e 's/\(LINKFLAGS[^\r\n]*\)/\1 -s/' > "${config}.tmp" \
    || error "Unable to fix config files"
    mv "${config}.tmp" "$config" || error "Unable to install new config files"
fi

if [ "$compress" ]; then
    cat "$config" | \
    sed -e 's/-lz/-Wl,-Bstatic,-lz/' > "${config}.tmp" \
    || error "Unable to fix config files"
    mv "${config}.tmp" "$config" || error "Unable to install new config files"
    echo "(lambda (l) (pushnew :sb-core-compression l) l)" \
        > "${build_dir}/customize-target-features.lisp"
fi

echo Resolving SBCL version...

src_commits="${build_dir}/sbcl-win32-threads.commits.tmp"
orig_src_commits="${build_dir}/sbcl.commits.tmp"

if [ -z "$no_git" ]; then
    cd "$srcdir"
    git log -n1 &> /dev/null || no_git="yes"
    if [ "$no_git" ]; then
        echo "Warning: $srcdir is not a Git repository. Disabling Git-based version resolving" >&2
    else
        cd "$orig_srcdir"
        git log -n1 &> /dev/null || no_git="yes"
        if [ "$no_git" ]; then
            echo "Warning: $orig_srcdir is not a Git repository. Disabling Git-based version resolving" >&2
        fi
    fi
fi

if [ "$no_git" ]; then
    version="${SBCL_VERSION:-"unknown-version"}"
else
    cd "$srcdir"
    git log --pretty=format:%H > "$src_commits" || error "$srcdir is not a git repo"
    version_head=`git rev-parse HEAD`
    version_head_short=`git rev-parse --short HEAD`
    cd "$orig_srcdir"
    git log --pretty=format:%H > "$orig_src_commits" || error "$orig_srcdir is not a git repo"
    version_base=`grep -Fxf "$orig_src_commits" "$src_commits" | head -n1`
    version_tag=`git describe --tags --match 'sbcl*' --abbrev=0 $version_base`
    version_release=`echo $version_tag | sed -e 's/sbcl[_-]//' | sed -e 's/_/\./g'`
    version_n_root=`git rev-list $version_base --not $version_tag | wc -l`
    cd "$srcdir"
    version_n_branch=`git rev-list $version_head --not $version_base | wc -l`
    version_branch=`git branch --no-color | sed -e 's/* //'`
    version=`printf "%s.%s.%s.%s-%s" \
        "$version_release" \
        "$version_n_root" \
        "$version_branch" \
        "$version_n_branch" \
        "$version_head_short"`
fi

version_file="${build_dir}/version.lisp-expr"

echo "\"$version\"" > "$version_file"

cd "$root_dir"

if [ -z "$no_wix" ]; then
    echo "Searching for WIX"
    if [ -z "$wix_prefix" ]; then
        if [ -z "$WIX" ]; then
            echo "Warning: unable to find working WIX" >&2
            no_wix=yes
        else
            "$WIX/bin/candle" --version &> /dev/null || no_wix=yes
            if [ "$no_wix" ]; then
                echo "Warning: unable to find working WIX" >&2
            else
                cd "${WIX}/bin"
                export WIX_PATH=`pwd -W`
            fi
        fi
    else
        cd "$wix_prefix" || error "Unable to enter WIX install directory"
        "bin/candle" --version &> /dev/null || no_wix=yes
        if [ "$no_wix" ]; then
            echo "Warning: unable to find WIX at $wix_prefix" >&2
        else
            cd bin
            export WIX_PATH=`pwd -W`
        fi
    fi
fi

echo "Building sbcl-$version"
echo "    in $build_dir"

cd "$build_dir"

./make.sh || error "Build failed"

if [ "$tests" ]; then
    cd tests && ./run-tests.sh
fi

if [ -z "$no_wix" ]; then
    cd "$build_dir"
    ./make-windows-installer.sh
    cp output/*.msi ./
    echo "MSI package: "`pwd -W`"/"`stat -c %n *.msi`
fi

echo "Build finished"


[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-15 07:16 pm (UTC)(link)
это все круто, но есть ли возможность использовать msvc вместо упячки mingw ?

[identity profile] love5an.livejournal.com 2012-02-15 07:38 pm (UTC)(link)
Ну во-первых, по-любому нужен юникс-подобный шелл, типа msys - там куча скриптов для сборки, которым нужна unix-like среда.

Во-вторых, там точно нужен GNU Assembler, т.к. есть куски кода на нем(хотя м.б. и на другой ассемблер перевести не проблема).

В третьих, насколько я понимаю, нужен компилятор, поддерживающий C99 (MSVC в режиме Си, насколько я знаю, поддерживает только C89).

А так, я не уверен, используются ли там какие-то специфические расширения GCC - насчет этого надо будет консультироваться у разработчиков(у Антона Коваленко, например, и других).
Если используются, то нужна будет намаленькая работа в плане для портирования кода.

Ну а вообще - какой смысл переводить код на MSVC?

[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-15 07:43 pm (UTC)(link)
> какой смысл переводить код на MSVC?
например потому что это лучшая из известных науке сред по эффективности генерируемого кода, а также по удобности отладки - не ?
вот ecl например на msvc вполне собирается

[identity profile] love5an.livejournal.com 2012-02-15 07:57 pm (UTC)(link)
ecl то собирается, но некоторые библиотеки CL - нет, у меня лично часто возникает ошибка на почве слишком длинных для MSVC строковых литералов(это как минимум с cl-unicode например было)

Потом - про отладку, наверное, соглашусь, а вот по вопросу эффективности генерируемого кода:
я бы поставил на ICC. А GCC и MSVC одинаково неплохой код генерируют; у меня даже на некоторых примерах GCC выигрывал.

Вообще, рантайм SBCL играет не такую уж большую роль в вопросе производительности программ на лиспе. Кроме GC, возможно, но это скорее вопрос не крутости компилятора, а алгоритма(там немало еще пилить в этом плане). SBCL компилирует в машкод, и соответственно, производительность программ, работающих под SBCL зависит на 99% от крутости компилятора SBCL.

Плюс, действительно неплохая причина переводить код на MSVC была бы в случае кусков кода на C++. Это как минимум для совместимости с другими плюсовыми программами и библиотеками под винду. Но в рантайме SBCL только Си и ассемблер. И вообще, код там слишком низкоуровневый для того чтобы C++ зачем-то был нужен.


[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-15 08:09 pm (UTC)(link)
думаю icc просто слишком распиарен - его адовая эффективность на практике не подтверждается: http://redplait.blogspot.com/2011/09/intel-c-ompozer-xe-2011.html
для sbcl есть средства для создания удобных биндингов к c/c++ ? в swig модуля для sbcl нет например, что повергает в уныние

[identity profile] love5an.livejournal.com 2012-02-15 08:33 pm (UTC)(link)
По поводу средства для создания удобных бидингов у меня такое мнение, что вменяемые биндинги всегда стоит писать вручную.

А так, в SBCL, естественно, есть FFI. Как и у каждой современной реализации CL.
И плюс, для лиспа есть compatibility layers, чтобы для устранить различия между разными FFI разных реализаций.
Типа, вот, CFFI: http://common-lisp.net/project/cffi/
Или мой Virgil: http://github.com/Lovesan/Virgil
Плюс, я вот в своей библиотеке Doors сделал интерфейс к COM(правда пока к чистому COM, без automation, т.е. IDispatch и пр. пока нет) https://github.com/Lovesan/doors

C С++ вопрос сложнее - у C++, как известно, стандартизированного ABI нет, поэтому в случае C++ных библиотек придется сначала писать DLL-обертку с сишным интерфейсом.


Плюс, если идти еще дальше, то за счет наличия в лиспе макросов, биндинги пишутся легко и непринужденно - буквально расставлением скобок вокруг описаний функций из документации.

Вот см. например как у меня в Doors:
https://github.com/Lovesan/doors/blob/master/system/console.lisp
Или вот в моем LDX, как у меня описываются COM-интерфейсы - это практически IDL, только со скобками:
https://github.com/Lovesan/LDX/blob/master/dxgi/interfaces.lisp

[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-15 08:39 pm (UTC)(link)
я надеюсь ты в курсе что cffi вносит нехилый такой overhead ?
и что биндинг к тому же Qt на нем написать практически нереально ?

[identity profile] love5an.livejournal.com 2012-02-15 09:07 pm (UTC)(link)
cffi не вносит _никакого_ оверхеда.
Вообще.
Потому что это просто макро-обертка. Когда foreign-типы известны при компиляции, все раскрывается в нативное FFI прямо при раскрытии макросов, а потом компилятором переводится в машкоды.

Оверхед на вызов Си из лиспа, естественно, есть(надо сохранять некоторые регистры, плюс помечать участки стека, используемые "неуправляемым" кодом), но именно CFFI/etc. никакого оверхеда не вносит.

Биндинг к Qt вообще мало на чем реально написать. Потому что C++(а у C++ _нет_ _ABI_)
(хотя есть CommonQt(я не проверял, но, говорят, работает))
Ну да и нафиг он нужен. Если мне так припрет использовать стороннее GUI из лиспа, я лучше возьму вызову что-нибудь через COM, RDNZL(Windows.Forms, WPF), да или даже просто какое-нибудь WinAPI через Си.

[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-15 10:49 pm (UTC)(link)
ога
а маршаллинг mutable буферов (строк например или массивов) типа бесплатен

[identity profile] love5an.livejournal.com 2012-02-16 05:45 am (UTC)(link)
1) кто мешает хранить заранее выделенные буферы?
2) затраты на маршалинг строк мизерные.

[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-15 08:46 pm (UTC)(link)
и да - например даже perl умеет юзать com
весьма прискорбно что lisp до сих пор (21век на дворе давно) не

[identity profile] love5an.livejournal.com 2012-02-15 09:02 pm (UTC)(link)
Почему не умеет? Умеет. Я же вон сделал.
А у коммерческих реализаций CL поддержка COM с 90х есть.

А perl кстати не умеет, как и прочая скриптота. Ну, automation только. Покажи-ка мне вызов D3D11 из перла(ага, и, только без "модулей на Си").

[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-17 08:47 am (UTC)(link)
> Virgil - моя библиотека-FFI для маршалинга лисповых данных в неуправляемую память(кстати, я таки сподобился написать для нее документацию, скоро опубликую)
чо-то я следов не нашел той документации
цитата взята отсюда есличо: http://love5an.livejournal.com/365354.html

[identity profile] love5an.livejournal.com 2012-02-18 11:09 pm (UTC)(link)
у меня валяется где-то на задворках винта полудописанный мануал. я его выкладывал в juick
могу еще выложить, но там мало что есть пока.

хотя надо дописать да

[identity profile] redplait.blogspot.com (from livejournal.com) 2012-02-19 03:05 pm (UTC)(link)
> я его выкладывал в juick
чо-та я так и не смог найти :-(

[identity profile] love5an.livejournal.com 2012-02-15 08:36 pm (UTC)(link)
плюс, см. у меня по тегам в ЖЖ
http://love5an.livejournal.com/tag/doors
http://love5an.livejournal.com/tag/LDX
http://love5an.livejournal.com/tag/Virgil

[identity profile] love5an.livejournal.com 2012-02-15 08:39 pm (UTC)(link)
кстати, SWIG-модуль есть для упомянутого выше CFFI

[identity profile] maxim.livejournal.com 2012-02-15 08:55 pm (UTC)(link)
Ох я бы потестировал. Давно хотел SBCL под Windows. Но к сожалению я сейчас в Linux.