Статическая сборка ICU4С 69.1

Что хочу? Получить статическую сборку icu, без внешних зависимостей от библиотек и файлов данных. В качестве подопытной буду использовать программу, которая должна правильно посчитать длину строки в “китайских буквах” 😉

#include <iostream>
#include <fstream>
 
#include <unicode/unistr.h>
#include <unicode/locid.h>
#include <unicode/brkiter.h>
 
int main() {
    const icu::UnicodeString str(L"नमस्ते");
 
    UErrorCode err = U_ZERO_ERROR;
    std::unique_ptr<icu::BreakIterator> iter(icu::BreakIterator::createCharacterInstance(icu::Locale::getDefault(), err));
    if (U_FAILURE(err)) {
        std::cout << u_errorName(err) << std::endl;
        return -1;
    }
    iter->setText(str);
 
    int count = 0;
    while (iter->next() != icu::BreakIterator::DONE) {
        ++count;
    }
    std::cout << std::string((3==count)?"GREAT SUCCESS":"EPIC FAIL") << std::endl;
    return 0;
}

Прежде всего нам понадобятся:

  1. MinGW
  2. MSYS2
  3. Python

Приступаем к сборке.

  1. Переходим по ссылке, выбираем нужную версию и скачиваем архив с исходным кодом (icu4c-69_1-src.zip)
  2. Распаковываем, например в корень T:\
  3. В icu\source\common\unicode\uconfig.h сразу после header guards добавляем строчку
    #define U_DISABLE_RENAMING 1
    чтобы получилось примерно так:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*  
**********************************************************************
*   Copyright (C) 2002-2016, International Business Machines
*   Corporation and others.  All Rights Reserved.
**********************************************************************
*   file name:  uconfig.h
*   encoding:   UTF-8
*   tab size:   8 (not used)
*   indentation:4
*
*   created on: 2002sep19
*   created by: Markus W. Scherer
*/

#ifndef __UCONFIG_H__
#define __UCONFIG_H__

#define U_DISABLE_RENAMING 1

<...>
  1. Запускаем MSYS2 и переходим в /t/icu/source
  2. Настраиваем окружение так, чтобы использовался наш MinGW и Python:
export PATH=/c/dev/8.1.0/x64/mingw/bin:/c/dev/tools/python39:$PATH
  1. Настраиваем флаги компилятора для правильной статической сборки:
export CFLAGS="-DU_STATIC_IMPLEMENTATION=1 -DU_DISABLE_RENAMING=1 -static"
export CXXFLAGS="-DU_STATIC_IMPLEMENTATION=1 -static-libstdc++ -static -std=c++11"
  1. Конфигурируем ICU:
./runConfigureICU MinGW prefix=/c/dev/8.1.0/x64/icu4c-69_1/static -disable-shared -enable-static --disable-renaming --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --with-data-packaging=static
  1. Пытаемся собрать:
mingw32-make

У меня сборка сломается на makeconv из-за того, что имена статических библиотек icu отличаются от ожидаемых

x86_64-w64-mingw32-g++ -O3 -DU_STATIC_IMPLEMENTATION=1 -static-libstdc++ -static -std=c++11 -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long -mthreads    -o ../../bin/makeconv.exe gencnvex.o genmbcs.o makeconv.o ucnvstat.o -L../../lib "-licutu" -L../../lib "-licuin" -L../../lib "-licuuc" -L../../stubdata "-licudt" -lpthread -lm
C:/dev/8.1.0/x64/mingw/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -licutu
C:/dev/8.1.0/x64/mingw/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -licuin
C:/dev/8.1.0/x64/mingw/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -licuuc
C:/dev/8.1.0/x64/mingw/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -licudt
collect2.exe: error: ld returned 1 exit status
mingw32-make[2]: *** [Makefile:81: ../../bin/makeconv.exe] Error 1
mingw32-make[2]: Leaving directory 'T:/icu/source/tools/makeconv'
mingw32-make[1]: *** [Makefile:47: all-recursive] Error 2
mingw32-make[1]: Leaving directory 'T:/icu/source/tools'
mingw32-make: *** [Makefile:153: all-recursive] Error 2

поэтому…

  1. Переименовываем библиотеки:
mv lib/lib{s,}icuin.a
mv lib/lib{s,}icuio.a
mv lib/lib{s,}icutu.a
mv lib/lib{s,}icuuc.a
mv stubdata/lib{s,}icudt.a
  1. Продолжаем сборку:
mingw32-make

Сборка должна завершиться успешно. На данный момент мы имеем статически собранный ICU, без внешних зависимостей от библиотек, но с заглушкой вместо библиотеки данных. Исправим это.

  1. Копируем библиотеку данных на место заглушки:
cp lib/libsicudt.a stubdata/libsicudt.a
cp lib/libsicudt.a stubdata/libicudt.a
  1. Удаляем собранные исполнимые файлы, поскольку они собраны с заглушкой:
rm ./bin/*
  1. Повторно собираем исполнимые файлы и, наконец, устанавливаем библиотеку:
mingw32-make all install

Всё. Можно скомпилировать и запустить нашу программу.

$ g++ main.cpp -o main -static -L/c/dev/8.1.0/x64/icu4c-69_1/static/lib/ -I/c/dev/8.1.0/x64/icu4c-69_1/static/include -lsicuuc -lsicudt
$ ./main.exe
GREAT SUCCESS
$

Если используется система сборки qmake, в pro файле библиотека подключается так:

LIBS += -LC:/dev/8.1.0/x64/icu4c-69_1/static/lib/
LIBS += -lsicuin
LIBS += -lsicutest
LIBS += -lsicutu
LIBS += -lsicuio
LIBS += -lsicuuc
LIBS += -lsicudt

INCLUDEPATH += C:/dev/8.1.0/x64/icu4c-69_1/static/include
DEPENDPATH += C:/dev/8.1.0/x64/icu4c-69_1/static/include

MinGW

MinGW я не собираю, но и не устанавливаю традиционным способом 🙂

  1. Проходим по ссылке и выбираем инструменты для сборки (Toolchains targetting) нужной разрядности (например x64)
  2. Выбираем версию (я выбрал MinGW-W64 GCC-8.1.0)
  3. Выбираем вариант модели потоков (win/posix) и обработки исключений (seh/sjlj). Я выбрал x86_64-posix-sjlj
  4. Скачиваем архив (в моём случае – x86_64-8.1.0-release-posix-sjlj-rt_v6-rev0.7z)
  5. Распаковываем его.

Я распаковал его в c:\dev\8.1.0\x64\mingw соответственно.

Проверяю

Моё окружение разработчика и как его собрать

В основном в разработке у меня используются следующие инструменты:

  1. MinGW
  2. Qt
  3. git
  4. Разные библиотеки

Все инструменты у меня располагаются в C:\dev\, вынутри которой есть одна поддиректория ‘tools’ (с полезными инструментами) и несколько директорий с названиями версий MinGW (на данный момент 7.3.0 и 8.1.0), внутри которых находятся директории с инструментами разной разрядности (x32 и x64).

Для наглядности приведу пример структуры:

├───7.3.0
│   ├───x32
│   │   ├───mingw
│   │   └───Qt
│   │       ├───5.12.3
│   │       │   ├───shared
│   │       │   └───static
│   │       ├───5.13.1
│   │       │   ├───shared
│   │       │   └───static
│   │       └───5.5.1
│   │           ├───shared
│   │           └───static
│   └───x64
│       ├───boost
│       │   ├───1.71.0
│       │   │   └───static
│       │   └───1.72.0
│       │       ├───shared
│       │       └───static
│       ├───icu4c-57.2
│       │   ├───shared
│       │   └───static
│       ├───libcurl
│       ├───mingw
│       ├───postgresql-11.1
│       ├───sqlite_3200100
│       └───zlib-1.2.11
├───8.1.0
│   └───x64
│       ├───mingw
│       ├───openssl-1.1.1k
│       └───Qt
│           └───5.15.2
│               ├───shared
│               └───static
└───tools
    ├───cmake-3.14.0-rc2-win64-x64
    ├───dbeaver
    ├───DrMemory-Windows-2.1.0-1
    ├───git-2.20.1-64
    ├───Python39
    ├───QtCreator-4.13.0
    ├───QtCreator-4.15.0
    └───SQLiteStudio 3.2.1

Дальше планирую записывать как получить тот или иной инструмент в окружении:

  1. MinGW