WIP 86 Android: 7-Zip 25.01 을 공식 소스에서 NDK 로 빌드 (lib7zz.so) - Android 에서도 7z · RAR 풀기 (검토 대기)
- 관리자 지시 4: 7z2501-src.7z (SHA-256 2aed39b8…0181 고정, 고치지 않음) 의 CPP/7zip/Bundles/Alone2 (7zz) 를 NDK clang (aarch64-linux-android24) 으로 빌드 - tool/build_7zz_android.sh. 어셈블리 끔, Windows 에서 빌드할 때 makefile 이 MinGW 로 여기지 않게 SystemDrive · SYSTEMDRIVE 를 비움, 새 clang 경고로 실패하지 않게 경고 묶음 끔. libc++ 는 정적 (libc · libm · libdl 만 씀). 결과 third_party/7zip/android/arm64-v8a/lib7zz.so (2.8MB) + License.txt. - APK: jniLibs 에 third_party/7zip/android, third_party_libs.txt · check_apk 목록에 lib7zz.so (check_apk 통과 - 실행 파일 5개). - 앱: bundledSevenZip 이 Android 에서는 네이티브 라이브러리 폴더의 lib7zz.so (rsync 와 같은 방식). 있으면 "다른 앱으로 열기" 안내 대신 실제 풀기 - 같은 검사 (zip slip · 링크 · 이름 · 공간 · 비밀번호 표준 입력) 그대로. 없으면 예전처럼 안내. - 고지: THIRD_PARTY_NOTICES_ANDROID 에 7-Zip 25.01 (LGPL 2.1+ · BSD · unRAR 제한, 소스 주소 · 빌드 방법 · 대응 소스 = 태그). - 시험: licenses_test (Android 고지 · 빌드 스크립트 해시 · APK 목록). Android 기기에서 실제로 푸는 것은 15 의 기기 확인 필요. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음6
parent
47144981d0
commit
e0466d6168
|
|
@ -65,9 +65,11 @@ android {
|
||||||
sourceSets["main"].jniLibs.srcDir("../../third_party/sdcpp/android")
|
sourceSets["main"].jniLibs.srcDir("../../third_party/sdcpp/android")
|
||||||
// Tor (libtor.so 라는 이름의 실행 파일): 공식 Tor Expert Bundle (Android aarch64) 의 libTor.so, tool/fetch_tor_android.ps1 (판 · SHA-256 고정)
|
// Tor (libtor.so 라는 이름의 실행 파일): 공식 Tor Expert Bundle (Android aarch64) 의 libTor.so, tool/fetch_tor_android.ps1 (판 · SHA-256 고정)
|
||||||
sourceSets["main"].jniLibs.srcDir("../../third_party/tor/android")
|
sourceSets["main"].jniLibs.srcDir("../../third_party/tor/android")
|
||||||
|
// 86: 7-Zip 25.01 (lib7zz.so 라는 이름의 실행 파일 - 7z · RAR 풀기): tool/build_7zz_android.sh (공식 소스, SHA-256 고정)
|
||||||
|
sourceSets["main"].jniLibs.srcDir("../../third_party/7zip/android")
|
||||||
// 195: third_party 의 실행 파일은 third_party_libs.txt 에 적은 것만 (공유 폴더에 다른 판이 있어도 섞이지 않게)
|
// 195: third_party 의 실행 파일은 third_party_libs.txt 에 적은 것만 (공유 폴더에 다른 판이 있어도 섞이지 않게)
|
||||||
val thirdPartyLibs = file("third_party_libs.txt").readLines().map { it.trim() }.filter { it.isNotEmpty() && !it.startsWith("#") }.toSet()
|
val thirdPartyLibs = file("third_party_libs.txt").readLines().map { it.trim() }.filter { it.isNotEmpty() && !it.startsWith("#") }.toSet()
|
||||||
listOf("../../third_party/rsync/android", "../../third_party/sdcpp/android", "../../third_party/tor/android").forEach { dir ->
|
listOf("../../third_party/rsync/android", "../../third_party/sdcpp/android", "../../third_party/tor/android", "../../third_party/7zip/android").forEach { dir ->
|
||||||
fileTree(dir).matching { include("**/*.so") }.forEach { so ->
|
fileTree(dir).matching { include("**/*.so") }.forEach { so ->
|
||||||
if (so.name !in thirdPartyLibs) packaging.jniLibs.excludes += "**/${so.name}"
|
if (so.name !in thirdPartyLibs) packaging.jniLibs.excludes += "**/${so.name}"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ librsync.so
|
||||||
libsdcli.so
|
libsdcli.so
|
||||||
libsdcli_v80.so
|
libsdcli_v80.so
|
||||||
libtor.so
|
libtor.so
|
||||||
|
lib7zz.so
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@ JJ_MKVMaker (Android) - 포함된 오픈 소스 구성 요소 및 라이선스
|
||||||
- rsync 3.4.1 (librsync.so 라는 이름의 실행 파일) GPL-3.0-or-later https://rsync.samba.org
|
- rsync 3.4.1 (librsync.so 라는 이름의 실행 파일) GPL-3.0-or-later https://rsync.samba.org
|
||||||
공식 소스 (https://download.samba.org/pub/rsync/src/rsync-3.4.1.tar.gz) 를 고치지 않고 Android NDK 로 빌드
|
공식 소스 (https://download.samba.org/pub/rsync/src/rsync-3.4.1.tar.gz) 를 고치지 않고 Android NDK 로 빌드
|
||||||
(빌드 방법: 소스의 tool/build_rsync_android.sh)
|
(빌드 방법: 소스의 tool/build_rsync_android.sh)
|
||||||
|
- 7-Zip 25.01 (lib7zz.so 라는 이름의 실행 파일) LGPL-2.1-or-later · BSD-3-Clause · BSD-2-Clause · unRAR 라이선스 제한
|
||||||
|
Copyright (C) 1999-2025 Igor Pavlov. https://www.7-zip.org
|
||||||
|
7z · RAR 압축을 푸는 데만 씁니다. RAR 을 푸는 코드는 unRAR 라이선스 제한을 따르며 RAR 을 만드는 데 쓰지 않습니다
|
||||||
|
(이 앱은 RAR 을 만들지 않습니다). 공식 소스 (https://www.7-zip.org/a/7z2501-src.7z) 를 고치지 않고 Android NDK 로 빌드
|
||||||
|
(빌드 방법: 소스의 tool/build_7zz_android.sh). 따로 실행하는 프로그램이며, 대응 소스는 위 주소와 이 앱 판의 태그에 있습니다.
|
||||||
|
원문: 앱 안 "오픈 소스 라이선스" 의 7-Zip (7ZIP_LICENSE.txt) · GNU LGPL 2.1
|
||||||
- stable-diffusion.cpp · ggml (libsdcli.so 라는 이름의 실행 파일) MIT https://github.com/leejet/stable-diffusion.cpp
|
- stable-diffusion.cpp · ggml (libsdcli.so 라는 이름의 실행 파일) MIT https://github.com/leejet/stable-diffusion.cpp
|
||||||
커밋 228c707 을 고치지 않고 Android NDK 로 빌드 (AI 그림 · 해상도 올리기, 빌드 방법: 소스의 tool/build_sdcpp_android.sh)
|
커밋 228c707 을 고치지 않고 Android NDK 로 빌드 (AI 그림 · 해상도 올리기, 빌드 방법: 소스의 tool/build_sdcpp_android.sh)
|
||||||
함께 묶인 구성 요소: ggml (MIT) · libwebp · libwebm (BSD-3) · Oniguruma · Darts-clone (BSD-2) · utf8proc · nlohmann/json ·
|
함께 묶인 구성 요소: ggml (MIT) · libwebp · libwebm (BSD-3) · Oniguruma · Darts-clone (BSD-2) · utf8proc · nlohmann/json ·
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart' show MethodChannel;
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
import '../app/app_controller.dart';
|
import '../app/app_controller.dart';
|
||||||
|
|
@ -12,8 +13,17 @@ import '../core/vfs.dart';
|
||||||
import '../l10n/tr.dart';
|
import '../l10n/tr.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
/// 86: 동봉한 7-Zip (Windows: 실행 파일 옆 7zip\7z.exe). 개발 중에는 third_party 의 것. 없으면 null (Android - 아직 없음)
|
/// 86: 동봉한 7-Zip. 없으면 null
|
||||||
String? bundledSevenZip() {
|
/// - Windows: 실행 파일 옆 7zip\7z.exe (개발 중에는 third_party 의 것)
|
||||||
|
/// - Android: 네이티브 라이브러리 폴더의 lib7zz.so (tool/build_7zz_android.sh, APK 의 jniLibs - rsync 와 같은 방식)
|
||||||
|
Future<String?> bundledSevenZip() async {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
try {
|
||||||
|
final dir = await const MethodChannel('jj_mkvmaker/android').invokeMethod<String>('nativeLibDir');
|
||||||
|
if (dir != null && File(p.join(dir, 'lib7zz.so')).existsSync()) return p.join(dir, 'lib7zz.so');
|
||||||
|
} catch (_) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!Platform.isWindows) return null;
|
if (!Platform.isWindows) return null;
|
||||||
for (final f in [
|
for (final f in [
|
||||||
p.join(p.dirname(Platform.resolvedExecutable), '7zip', '7z.exe'),
|
p.join(p.dirname(Platform.resolvedExecutable), '7zip', '7z.exe'),
|
||||||
|
|
@ -98,7 +108,8 @@ Future<String?> extractArchiveFlow(BuildContext context, AppController c, String
|
||||||
final local = isDav(path) ? await vLocalCopy(path, await c.services.storage.tempDirectory()) : path;
|
final local = isDav(path) ? await vLocalCopy(path, await c.services.storage.tempDirectory()) : path;
|
||||||
if (!context.mounted) return null;
|
if (!context.mounted) return null;
|
||||||
// 7z · RAR: 동봉한 7-Zip 이 있어야 (Android 는 아직 - 막지 않고 다른 앱으로 안내)
|
// 7z · RAR: 동봉한 7-Zip 이 있어야 (Android 는 아직 - 막지 않고 다른 앱으로 안내)
|
||||||
final exe = kind == ArchiveKind.zip ? null : bundledSevenZip();
|
final exe = kind == ArchiveKind.zip ? null : await bundledSevenZip();
|
||||||
|
if (!context.mounted) return null;
|
||||||
if (kind != ArchiveKind.zip && exe == null) {
|
if (kind != ArchiveKind.zip && exe == null) {
|
||||||
final open = await showDialog<bool>(
|
final open = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ void main() {
|
||||||
await tester.tap(find.text('취소'));
|
await tester.tap(find.text('취소'));
|
||||||
await wait(tester, () => finished);
|
await wait(tester, () => finished);
|
||||||
expect(Directory(p.join(tmp.path, 'a')).existsSync(), isFalse);
|
expect(Directory(p.join(tmp.path, 'a')).existsSync(), isFalse);
|
||||||
if (bundledSevenZip() == null) {
|
if (await tester.runAsync(bundledSevenZip) == null) {
|
||||||
final sz = File(p.join(tmp.path, 'x.7z'))..writeAsStringSync('7z');
|
final sz = File(p.join(tmp.path, 'x.7z'))..writeAsStringSync('7z');
|
||||||
finished = false;
|
finished = false;
|
||||||
run((ctx) => extractArchiveFlow(ctx, c, sz.path)).then((_) => finished = true);
|
run((ctx) => extractArchiveFlow(ctx, c, sz.path)).then((_) => finished = true);
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,13 @@ void main() {
|
||||||
expect(cmake, contains('third_party/7zip/windows'));
|
expect(cmake, contains('third_party/7zip/windows'));
|
||||||
expect(cmake, contains('License.txt'));
|
expect(cmake, contains('License.txt'));
|
||||||
expect(File('tool/fetch_7zip.ps1').readAsStringSync(), contains('e7eb0b7ed5efa4e087b7b17f191797f7af5b7f442d1290c66f3a21777005ef57'));
|
expect(File('tool/fetch_7zip.ps1').readAsStringSync(), contains('e7eb0b7ed5efa4e087b7b17f191797f7af5b7f442d1290c66f3a21777005ef57'));
|
||||||
|
// Android 판 (lib7zz.so - 공식 소스를 NDK 로 빌드): 고지 · 빌드 스크립트의 소스 해시 · APK 목록
|
||||||
|
final android = File('assets/licenses/THIRD_PARTY_NOTICES_ANDROID.txt').readAsStringSync();
|
||||||
|
for (final s in ['7-Zip 25.01 (lib7zz.so', '7z2501-src.7z', 'build_7zz_android.sh', 'RAR 을 만들지 않습니다']) {
|
||||||
|
expect(android, contains(s), reason: s);
|
||||||
|
}
|
||||||
|
expect(File('tool/build_7zz_android.sh').readAsStringSync(), contains('2aed39b8f1238464475e9de7dda169a5e873a1dc8bbf4f664b943eaba5620181'));
|
||||||
|
expect(File('android/app/third_party_libs.txt').readAsLinesSync(), contains('lib7zz.so'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Windows 설치: 실행 파일 옆 · tools · rsync 에 원문을 둔다 (CMake)', () {
|
test('Windows 설치: 실행 파일 옆 · tools · rsync 에 원문을 둔다 (CMake)', () {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# 86 · 관리자 지시 4: Android (arm64) 용 7-Zip (7zz - 7z · RAR 풀기) 을 공식 소스에서 빌드해
|
||||||
|
# third_party/7zip/android/arm64-v8a/lib7zz.so 로 둔다 (rsync 와 같은 방식 - APK 의 jniLibs 로 들어가 앱의 네이티브 라이브러리
|
||||||
|
# 폴더에서 실행된다. Android 의 libc · libm · libdl 만 쓰고 libc++ 는 정적으로).
|
||||||
|
# 소스: https://www.7-zip.org/a/7z2501-src.7z (7-Zip 25.01, SHA-256 고정, 고치지 않음)
|
||||||
|
# 라이선스: GNU LGPL 2.1+ · BSD 3/2-clause · unRAR 라이선스 제한 (RAR 은 풀기만 - 앱은 RAR 을 만들지 않는다)
|
||||||
|
# 빌드: CPP/7zip/Bundles/Alone2 (7zz) 를 NDK clang 으로 (cmpl_clang_arm64.mak). 어셈블리는 끄고 (USE_ASM=),
|
||||||
|
# Windows 에서 빌드할 때 makefile 이 SystemDrive 로 MinGW 빌드라고 여기지 않게 SystemDrive · SYSTEMDRIVE 를 비운다.
|
||||||
|
# 새 NDK clang 의 새 경고로 실패하지 않게 경고 묶음 (-Weverything -Werror) 은 끈다 (코드는 그대로).
|
||||||
|
# 사용법 (Git Bash): sh tool/build_7zz_android.sh - Android NDK 가 필요 (ANDROID_NDK 또는 tools/android-sdk/ndk/*)
|
||||||
|
# .7z 소스를 풀 7-Zip 이 필요 (Windows: third_party/7zip/windows/7z.exe - tool/fetch_7zip.ps1, 아니면 PATH 의 7z · 7zz)
|
||||||
|
set -e
|
||||||
|
APP=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
VER=2501
|
||||||
|
SHA=2aed39b8f1238464475e9de7dda169a5e873a1dc8bbf4f664b943eaba5620181
|
||||||
|
NDK_ROOT=${ANDROID_NDK:-$(ls -d "$APP"/../tools/android-sdk/ndk/* | sort | tail -1)}
|
||||||
|
case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) HOST=windows-x86_64; EXE=.exe ;; Darwin) HOST=darwin-x86_64; EXE= ;; *) HOST=linux-x86_64; EXE= ;; esac
|
||||||
|
TC=$NDK_ROOT/toolchains/llvm/prebuilt/$HOST/bin
|
||||||
|
MAKE=$NDK_ROOT/prebuilt/$HOST/bin/make$EXE
|
||||||
|
[ -x "$MAKE" ] || MAKE=make
|
||||||
|
SZ="$APP/third_party/7zip/windows/7z.exe"
|
||||||
|
[ -x "$SZ" ] || SZ=$(command -v 7zz || command -v 7z)
|
||||||
|
WORK=${TMPDIR:-/tmp}/jj_7zz_android
|
||||||
|
rm -rf "$WORK" && mkdir -p "$WORK" && cd "$WORK"
|
||||||
|
curl -sSLf -o src.7z "https://www.7-zip.org/a/7z$VER-src.7z"
|
||||||
|
echo "$SHA src.7z" | sha256sum -c -
|
||||||
|
"$SZ" x -y -osrc src.7z > /dev/null
|
||||||
|
cd src/CPP/7zip/Bundles/Alone2
|
||||||
|
mkdir -p b/c_arm64
|
||||||
|
"$MAKE" -j8 -f ../../cmpl_clang_arm64.mak SHELL=/bin/sh SystemDrive= SYSTEMDRIVE= MSYSTEM= \
|
||||||
|
CC="$TC/clang$EXE --target=aarch64-linux-android24" CXX="$TC/clang++$EXE --target=aarch64-linux-android24" \
|
||||||
|
USE_ASM= LIB2=-ldl LDFLAGS_STATIC_3=-static-libstdc++ CFLAGS_WARN= CFLAGS_WARN_WALL= > make.log 2>&1 || { tail -30 make.log; exit 1; }
|
||||||
|
OUT="$APP/third_party/7zip/android/arm64-v8a"
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
"$TC/llvm-strip$EXE" -o "$OUT/lib7zz.so" b/c_arm64/7zz
|
||||||
|
cp ../../../../DOC/License.txt "$OUT/../License.txt"
|
||||||
|
ls -l "$OUT/lib7zz.so"
|
||||||
|
|
@ -39,7 +39,7 @@ if (-not $Libs) { $Libs = Join-Path $app 'android\app\third_party_libs.txt' }
|
||||||
$listed = @(Get-Content $Libs | ForEach-Object { $_.Trim() } | Where-Object { $_ -and -not $_.StartsWith('#') })
|
$listed = @(Get-Content $Libs | ForEach-Object { $_.Trim() } | Where-Object { $_ -and -not $_.StartsWith('#') })
|
||||||
# third_party 에 있는 파일 (목록 밖이면 APK 에 없어야 함)
|
# third_party 에 있는 파일 (목록 밖이면 APK 에 없어야 함)
|
||||||
$tp = @()
|
$tp = @()
|
||||||
foreach ($d in 'third_party\rsync\android', 'third_party\sdcpp\android', 'third_party\tor\android') {
|
foreach ($d in 'third_party\rsync\android', 'third_party\sdcpp\android', 'third_party\tor\android', 'third_party\7zip\android') {
|
||||||
$dir = Join-Path $app $d
|
$dir = Join-Path $app $d
|
||||||
if (Test-Path $dir) { $tp += @(Get-ChildItem $dir -Recurse -Filter *.so | ForEach-Object { $_.Name }) }
|
if (Test-Path $dir) { $tp += @(Get-ChildItem $dir -Recurse -Filter *.so | ForEach-Object { $_.Name }) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue