Чиним IntelliJ IDEA под *BSD
В один прекрасный летний день 2023го, разработчики этой замечательной IDE выкатили обновление, в составе которого появилась нативная неотключаемая библиотека. Результат можете наблюдать на скриншоте ниже.
Breaking News
Пока я все это чинил и писал, появился официальный способ решения данной проблемы:
Please use VM property idea.ui.icons.svg.disk.cache=false
as a workaround. Help -> Edit Custom VM options... -> add new line
-Didea.ui.icons.svg.disk.cache=false
Так что весь описанный ниже цирк вам больше не нужен. Прогресс это хорошо, даже если кривой и с п#здюлями.
Баг
Вот полный текстовый трейс, взятый из лога среды разработки:
2023-08-13 09:32:54,512 [ 101] INFO - STDERR - java.lang.AssertionError: Cannot create SvgCacheManager 2023-08-13 09:32:54,513 [ 102] INFO - STDERR - at com.intellij.openapi.diagnostic.DefaultLogger.error(DefaultLogger.java:54) 2023-08-13 09:32:54,513 [ 102] INFO - STDERR - at com.intellij.openapi.diagnostic.Logger.error(Logger.java:419) 2023-08-13 09:32:54,514 [ 103] INFO - STDERR - at com.intellij.ui.svg.SvgCacheManagerKt.createSvgCacheManager(SvgCacheManager.kt:62) 2023-08-13 09:32:54,515 [ 104] INFO - STDERR - at com.intellij.ui.svg.SvgCacheManagerKt$createSvgCacheManager$1.invokeSuspend(SvgCacheManager.kt) 2023-08-13 09:32:54,515 [ 104] INFO - STDERR - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 2023-08-13 09:32:54,516 [ 105] INFO - STDERR - at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:32) 2023-08-13 09:32:54,517 [ 106] INFO - STDERR - at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102) 2023-08-13 09:32:54,517 [ 106] INFO - STDERR - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) 2023-08-13 09:32:54,518 [ 107] INFO - STDERR - at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) 2023-08-13 09:32:54,518 [ 107] INFO - STDERR - at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) 2023-08-13 09:32:54,519 [ 108] INFO - STDERR - at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793) 2023-08-13 09:32:54,519 [ 108] INFO - STDERR - at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697) 2023-08-13 09:32:54,520 [ 109] INFO - STDERR - at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684) 2023-08-13 09:32:54,520 [ 109] INFO - STDERR - Caused by: java.lang.UnsatisfiedLinkError: /opt/app/idea-IC-232.8660.185/lib/native/linux-x86_64/libsqliteij.so: /opt/app/idea-IC-232.8660.185/lib/native/linux-x86_64/libsqliteij.so: wrong number of segments (4 != 2) 2023-08-13 09:32:54,521 [ 110] INFO - STDERR - at java.base/jdk.internal.loader.NativeLibraries.load(Native Method) 2023-08-13 09:32:54,522 [ 111] INFO - STDERR - at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:388) 2023-08-13 09:32:54,522 [ 111] INFO - STDERR - at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:232) 2023-08-13 09:32:54,523 [ 112] INFO - STDERR - at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:174) 2023-08-13 09:32:54,523 [ 112] INFO - STDERR - at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2389) 2023-08-13 09:32:54,524 [ 113] INFO - STDERR - at java.base/java.lang.Runtime.load0(Runtime.java:755) 2023-08-13 09:32:54,524 [ 113] INFO - STDERR - at java.base/java.lang.System.load(System.java:1953) 2023-08-13 09:32:54,525 [ 114] INFO - STDERR - at org.jetbrains.sqlite.SqliteLibLoaderKt.loadSqliteNativeLibrary(sqliteLibLoader.kt:42) 2023-08-13 09:32:54,525 [ 114] INFO - STDERR - at org.jetbrains.sqlite.SqliteLibLoaderKt.loadNativeDb(sqliteLibLoader.kt:30) 2023-08-13 09:32:54,526 [ 115] INFO - STDERR - at org.jetbrains.sqlite.SqliteConnection.<init>(SqliteConnection.kt:32) 2023-08-13 09:32:54,526 [ 115] INFO - STDERR - at org.jetbrains.sqlite.SqliteConnection.<init>(SqliteConnection.kt:21) 2023-08-13 09:32:54,527 [ 116] INFO - STDERR - at com.intellij.ui.svg.SvgCacheManagerKt.connectToSvgCache(SvgCacheManager.kt:76) 2023-08-13 09:32:54,527 [ 116] INFO - STDERR - at com.intellij.ui.svg.SvgCacheManagerKt.access$connectToSvgCache(SvgCacheManager.kt:1) 2023-08-13 09:32:54,528 [ 117] INFO - STDERR - at com.intellij.ui.svg.SvgCacheManagerKt$createSvgCacheManager$2$1.invokeSuspend(SvgCacheManager.kt:53) 2023-08-13 09:32:54,528 [ 117] INFO - STDERR - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 2023-08-13 09:32:54,528 [ 117] INFO - STDERR - at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) 2023-08-13 09:32:54,529 [ 118] INFO - STDERR - at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115) 2023-08-13 09:32:54,529 [ 118] INFO - STDERR - at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100) 2023-08-13 09:32:54,530 [ 119] INFO - STDERR - ... 4 more 2023-08-13 09:32:54,530 [ 119] INFO - STDERR -
Багофича
Конечно же я такой не один и сразу после этого релиза вылезло много людей, использующих продукт Intellij в самых экзотических ОС.
Официально *BSD не поддерживаются, но Idea вполне неплохо на них работала. Устаревшие версии этой IDE еще присутствуют в портах, но лишь в виде уже готовой сборки.
Но полная сборка среды ни под одной из BSD не поддерживается.
Вообще в Idea достаточно много нативных бибилотек, но до последнего патча это не мешало — просто валились ошибки о неподдерживаемой ОС при запуске среды.
К сожалению разработчики решили поступиться кроссплатформенностью ради производительности и влепили кастомную нативную библиотеку sqlite прямо в ядро своей среды разработки, без возможности отключения.
Поскольку Intellij выкатывают релизы чаще чем кошки плодятся, поддерживать сборку под *BSD у них явно нет лишних рук.
Моих двух рук к сожалению тоже недостаточно чтобы полноценно поддерживать полную сборку под FreeBSD, поэтому ниже решение «на сегодняшний день», которое может сломаться или стать неактуальным в скором времени.
Как чинить
На наше девелоперское счастье, Idea - открытый проект, исходники которого публично доступны в Github, поэтому решение заключается в выкачивании исходников, сборки под FreeBSD этой конкретной библиотеки и подкладывании ее вручную в каталог с установленной Intellij Idea.
git clone --depth 1 https://github.com/JetBrains/intellij-community.git
Исходников много, поэтому выкачиваем лишь одну ветку, без истории изменений.
Нужный нам проект находится в каталоге:
platform/sqlite
Полностью его мы собирать не будем, поскольку помимо нативной части он содержит еще и биндинги на Java, с которыми все хорошо.
Скриптов сборки два: build.sh — стартовый, содержит обертку для запуска в докере, make.sh — сама сборка нативной библиотеки.
Вот make.sh нам и нужен, его и будем править.
elif [ "$OS" == "freebsd" ]; then
Переопределить пути до LLVM, который используется для сборки:
export LDFLAGS="-L/usr/local/llvm15/lib" export CPPFLAGS="-I/usr/local/llvm15/include"
Поскольку при сборке на MacOS использовалась 15я версия LLVM — я использовал ее же на FreeBSD.
Добавить путь к системным библиотекам:
cFlags+=" -isystem /usr/local/include"
Ниже полный исходник поправленного скрипта сборки, с добавлением поддержки FreeBSD:
#!/usr/bin/env bash # Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. set -ex outDir="target/sqlite/$OS-$ARCH" rm -rf "${outDir:?}/*" mkdir -p "$outDir" # brew install llvm # use latest CLang 15 instead of 14 for a smaller binaries export PATH="/opt/homebrew/opt/llvm/bin:$PATH" export LDFLAGS="-L/opt/homebrew/opt/llvm/lib" export CPPFLAGS="-I/opt/homebrew/opt/llvm/include" cFlags="-O3 -fPIC -Isqlite -fvisibility=hidden -Wno-implicit-function-declaration" linkFlags="-Wl,-S,-x" libFilename="so" if [ "$OS" == "mac" ]; then cFlags+=" -mmacosx-version-min=10.14" linkFlags="-dynamiclib -fuse-ld=lld " libFilename="libsqliteij.jnilib" if [ "$ARCH" == "x86_64" ]; then cFlags+=" --target=x86_64-apple-darwin18.7.0" fi # our FreeBSD support elif [ "$OS" == "freebsd" ]; then export LDFLAGS="-L/usr/local/llvm15/lib" export CPPFLAGS="-I/usr/local/llvm15/include" libFilename="libsqliteij.so" linkFlags+=" -shared" cFlags+=" -isystem /usr/local/include" elif [ "$OS" == "linux" ]; then libFilename="libsqliteij.so" # cannot compile arm - unable to find library -lgcc, so, use dock cross if [ "$ARCH" == "aarch64" ]; then linkFlags+=" -shared" else cFlags+=" --target=$ARCH-unknown-linux-gnu --sysroot=target/linux-$ARCH" linkFlags+=" -shared -fuse-ld=lld" fi elif [ "$OS" == "win" ]; then linkFlags="-Wl,--kill-at -shared -static-libgcc" libFilename="sqliteij.dll" fi CC="${CC:-clang}" #linkFlags+=" -fuse-ld=lld" "${CROSS_PREFIX}${CC}" -o "$outDir/sqlite3.o" -c $cFlags \ -DSQLITE_DQS=1 \ -DSQLITE_THREADSAFE=1 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_USE_ALLOCA \ -DSQLITE_OMIT_AUTOINIT \ -DSQLITE_HAVE_ISNAN \ -DHAVE_USLEEP=1 \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_DEFAULT_CACHE_SIZE=2000 \ -DSQLITE_CORE \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STAT4 \ -DSQLITE_MAX_MMAP_SIZE=1099511627776 \ \ sqlite/sqlite3.c "${CROSS_PREFIX}${CC}" -o "$outDir/NativeDB.o" -c $cFlags -I"sqlite/$OS" sqlite/NativeDB.c libFile="$outDir/$libFilename" "${CROSS_PREFIX}${CC}" $cFlags -o "$libFile" "$outDir/NativeDB.o" "$outDir/sqlite3.o" $linkFlags shasum -a 256 "$libFile" | head -c 64 >"$libFile.sha256" unlink "$outDir/sqlite3.o" unlink "$outDir/NativeDB.o"
Также нужно будет создать каталог с "вычисляемым" заголовком JNI:
mkdir sqlite/freebsd && cp sqlite/linux/jni_md.h sqlite/freebsd
Запускается скрипт вот так (необходимо использовать bash):
OS=freebsd ARCH=x86_64 ./make.sh
Результат сборки выглядит вот так:
Готовая нативная библиотека будет в каталоге:
target/sqlite/freebsd-x86_64
Ее необходимо скопировать в каталог в котором установлена (распакована среда разработки):
cp target/sqlite/freebsd-x86_64/libsqliteij.so /opt/app/idea-IC-232.8660.185/lib/native/linux-x86_64/
И продолжать наслаждаться этой замечательной средой разработки.
Unable to save plugin settings
При первом запуске и открытии либо создании нового проекта у вас будут сыпаться вот такие ошибки:
Виновник этого празника — плагин «Code With Me», который нужен для реальной работы примерно как пятая нога.
Казалось бы и хер с ним, но из-за этих ошибок среда разработки не сохраняет настройки и сбивает состояние на начальное — вместо последнего открытого проекта у вас постоянно будет открываться стартовая страница среды разработки.
Поэтому это чудо инженерной мысли нужн отключить:
Добавление про OpenBSD
Точно также вылезла эта проблема, шаги для исправления все те же самые, но настройка чуть отличается:
elif [ "$OS" == "openbsd" ]; then libFilename="libsqliteij.so" linkFlags+=" -shared" cFlags+=" -isystem /usr/local/include"
OS=openbsd ARCH=x86_64 ./make.sh
Дальше готовая библиотека все также копируется в дистрибьютив Idea и она начинает запускаться:
Добавление для NetBSD
Шаги для исправления полностью аналогичны описанному выше методу для OpenBSD, в make.sh добавляем:
elif [ "$OS" == "netbsd" ]; then libFilename="libsqliteij.so" linkFlags+=" -shared" cFlags+=" -isystem /usr/local/include"
OS=netbsd ARCH=x86_64 ./make.sh
Точно также копируем в заменой в папку lib/native/linux-x86_64/ в распакованном дистрибьютиве Intellij Idea и запускаем.
Вот так оно выглядит в работе:
Таким образом удалось починить и продолжить использование свежих версий Intellij Idea под всеми тремя вариациями BSD и никто при этом даже не умер.