compose-rust 가이드

문제 해결

만날 수 있는 실패와 알아보는 법

이 페이지의 문제는 대부분 셋 중 하나입니다. 렌더러가 링커가 기대하는 곳에 없거나, JDK가 틀렸거나, 건드리면 안 되는 텍스트 경로를 무언가가 건드렸습니다.

빌드가 렌더러를 찾지 못합니다

이 저장소 밖에서는 빌드가 렌더러를 내려받으므로, 여기서의 실패에는 무엇을 시도했고 무엇을 해야 하는지 말하는 메시지가 따라옵니다. 읽어 보십시오. 네트워크가 없는 경우와 릴리스에 대상이 없는 경우를 구별하고, 오프라인이라면 둘 파일과 둘 디렉터리를 알려 줍니다. COMPOSE_RUST_RENDERER_DIR 은 이미 가진 렌더러를 빌드에 알려 줍니다.

체크아웃 안에서는 빌드가 내려받을 수 있는 어떤 것보다 renderer/build/native-image/dist/lib 의 네이티브 이미지를 먼저 씁니다. 그 디렉터리가 있는데 비어 있다면 렌더러를 빌드하지 않은 것입니다.

shell, macOS
ls renderer/build/native-image/dist/lib
# expect libcompose_rust_renderer.dylib with Skia and AWT libraries beside it

./renderer/desktop/scripts/build-native.sh

그 파일들을 떼어 놓지 마십시오. 네이티브 이미지는 실행 중에 자기 위치를 찾고, Skia와 AWT가 어디 있는지를 자기 디렉터리에서 끌어냅니다. 라이브러리만 따로 다른 곳에 복사하면 로드는 되지만 그것들을 찾지 못합니다.

Kotlin/Native 정적 라이브러리는 DXC_MACOS_NATIVE_LIB 나 DXC_LINUX_NATIVE_LIB 가 libcompose_rust_renderer.a 가 있는 디렉터리를 가리킬 때만 링크됩니다. 빌드해 두었는데도 바이너리가 네이티브 이미지를 불러온다면 그 변수를 확인하십시오.

빌드되고 실행되는데 창이 뜨지 않습니다

default-features = false 로 빌드한 바이너리는 이것을 조용히 하지 않습니다. 무엇이 빠졌는지 출력하고 0이 아닌 값으로 끝납니다. mock-renderer 기능으로 빌드한 것은 창 없는 Host를 요청한 것이므로 일부러 조용합니다.

그 밖이라면 두 반쪽을 떼어 봅니다. 스모크 테스트는 작은 C 호스트를 라이브러리에 링크해 run 을 직접 부릅니다.

shell
./renderer/desktop/scripts/smoke-test.sh

거기서는 창이 뜨고 Rust에서는 뜨지 않는다면 바이너리의 링크를 보십시오. 거기서도 뜨지 않는다면 렌더러 라이브러리 자체가 잘못된 것이고, 보통 다음 문제입니다.

잘못된 JDK: Darwin의 AWT 함정

macOS의 네이티브 이미지에 해당합니다. 좋은 경우는 빌드 스크립트가 no Liberica NIK 25 Full found, and GRAALVM_HOME is not set 으로 멈추는 것입니다. 문제를 대신 잡아 준 것입니다.

나쁜 경우는 PATH 에 업스트림 GraalVM의 native-image 가 있는 것입니다. 빌드는 되지만, 실행하면 AWT 클래스나 심볼이 없다며 실패하거나 창을 아예 열지 않습니다. 업스트림 GraalVM은 Darwin에서 AWT를 빼고 (oracle/graal#13272), Liberica NIK Full은 그것을 정적으로 링크합니다. 실제로 무엇을 가리키는지 확인하십시오.

shell
echo "$GRAALVM_HOME"
"$GRAALVM_HOME"/bin/native-image --version   # expect a Liberica NIK 25 build

./scripts/install-nik.sh 가 맞는 것을 설치합니다. 메타데이터 수집과 네이티브 빌드도 같은 JDK를 써야 하며, 스크립트는 GRAALVM_HOME 으로 JAVA_HOME 을 정해 이것을 강제합니다. 그렇지 않으면 도달성 메타데이터가 컴파일하는 것과 다른 JDK를 기술합니다.

앱이 시작하자마자 돌아옵니다

compose_rust_renderer_run 은 프로세스의 메인 스레드에서 불러야 합니다. macOS에서 다른 곳에서 부르면 아무것도 하지 않고 RUN_NOT_MAIN_THREAD(-4)를 돌려줍니다. 네이티브 이미지의 다른 음수 코드도 알아 둘 만합니다.

코드뜻
-1GraalVM isolate를 만들 수 없었습니다.
-2run 을 두 번 불렀습니다.
-3라이브러리가 자기 경로를 알아내지 못해 Skia와 AWT를 찾을 수 없습니다.
-4메인 스레드가 아닙니다.
-5렌더러 스레드를 시작할 수 없었습니다.

Linux의 Kotlin/Native 렌더러도 -2 와 -4 를 같은 뜻으로 씁니다.

IME 문제

서로 다른 실패 셋이 있고, 헷갈리기 쉽습니다.

Enter가 전송하면서 음절을 먹습니다

한글을 치고 음절을 확정하려고 Enter 를 눌렀는데, 그 음절이 빠진 채 메시지가 전송됩니다. 조합 중에 키 이벤트가 Host에 닿은 것입니다. 렌더러는 조합 중에 키 이벤트를 넘기면 안 됩니다. 조합 중의 Enter 는 전송이 아니라 확정이기 때문입니다. 이것을 보았다면 버그는 핸들러가 아니라 렌더러 쪽 키 경로에 있습니다.

치는 동안 조합이 초기화됩니다

음절을 조립하는 동안 글자가 사라지거나 순서가 바뀝니다. 텍스트가 Host를 왕복한 흔적입니다. 조합 중에 무언가가 Rust에서 입력창의 텍스트를 바꾼 것입니다. TextField 가 비제어인 것이 이 때문이고, SetText 는 조합이 끝날 때까지 기다립니다.

네이티브 이미지에서 조합이 아예 되지 않습니다

렌더러를 JVM에서 돌리면 한글 입력이 되는데 네이티브 이미지에서는 되지 않습니다. 이것은 설정 문제로 다룹니다. 미리 컴파일한다고 실행되는 코드가 바뀌지는 않기 때문입니다. InputMethodDescriptor ServiceLoader 리소스, JNI와 리플렉션 메타데이터, 로캘과 문자셋 리소스, CJK 글꼴 대체를 차례로 확인하십시오.

한글을 치면서 메타데이터를 수집하십시오

추적 에이전트는 실제로 실행된 코드 경로만 기록합니다. 앱을 열었다 닫기만 하면 IME에 대해서는 아무것도 기록되지 않습니다. collect-metadata.sh 를 실행하고, 창에 한글을 치고, 이것저것 누른 뒤, 창이 스스로 닫히게 두십시오. 그렇게 하지 않고 수집한 메타데이터로 만든 네이티브 이미지는 텍스트 입력이 정확히 이렇게 망가집니다.

ProtocolError 이벤트

렌더러가 모르는 위젯, 속성, Modifier는 아무것도 충돌시키지 않습니다. 렌더러는 코드와 메시지를 담은 ProtocolError 이벤트를 보냅니다. 이것이 보이면 두 쪽이 스키마에 대해 의견이 다른 것이고, 보통은 실행 중인 Rust 크레이트와 다른 소스로 빌드한 렌더러입니다. 렌더러를 다시 빌드하십시오.

완전한 불일치는 더 일찍 잡힙니다. 빌드가 렌더러의 스키마 해시를 크레이트의 것과 비교하고, 핸드셰이크가 시작할 때 다시 비교해 다르면 초기화를 거부합니다.

테스트가 생성된 Kotlin에서 실패합니다

생성한 Kotlin과 체크인된 파일을 비교하는 테스트가 실패한다면, Rust 스키마를 바꾸고 다시 생성하지 않은 것입니다. 의도한 경보입니다. 스키마가 유일한 원천이고, 갱신되지 않은 Kotlin 쪽은 런타임에 엇나가기보다 빌드에서 실패해야 합니다. cargo run -p compose-rust --bin codegen 을 실행하고 결과를 커밋하십시오.