Skip to content

Commit 0912b10

Browse files
committed
Enhance native library handling in the StringCare plugin: add a new task to synchronize native libraries from the stringcare-jni/dist directory into the plugin's internal structure. Update build.gradle.kts for improved task dependencies and streamline native library loading. Modify README.md to include new debugging options for native library loading. Adjust app/build.gradle.kts to enable debug mode for stringcare. Clean up JNI files by removing obsolete binaries.
1 parent 39f0a5c commit 0912b10

17 files changed

Lines changed: 439 additions & 188 deletions

README.md

Lines changed: 107 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,107 @@
1-
[![Maven Central](https://img.shields.io/maven-central/v/dev.vyp.stringcare/library.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22dev.vyp.stringcare%22%20AND%20a:%22library%22)
2-
3-
**StringCare Android** v5.0.0 — Compile-time obfuscation for strings and assets; runtime reveal in your app. GroupId: `dev.vyp.stringcare`.
4-
5-
Full documentation is in the [docs/](docs/) directory (getting started, configuration, API, publishing, troubleshooting).
6-
7-
<p align="center"><img width="10%" vspace="10" src="https://github.com/StringCare/AndroidLibrary/raw/develop/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png"></p>
8-
9-
<h3 align="center" style="margin-bottom:30px" vspace="20">StringCare Android Library</h3>
10-
11-
<p align="center"><img width="10%" vspace="20" src="https://github.com/StringCare/AndroidLibrary/raw/develop/white.png"></p>
12-
13-
### Installation (Kotlin DSL)
14-
15-
**1. Plugin** — In the project root `settings.gradle.kts`:
16-
17-
```kotlin
18-
pluginManagement {
19-
repositories {
20-
gradlePluginPortal()
21-
mavenCentral()
22-
google()
23-
}
24-
}
25-
// If publishing locally: includeBuild("../path/to/stringcare-android/plugin")
26-
```
27-
28-
In the app (or module) `build.gradle.kts`:
29-
30-
```kotlin
31-
plugins {
32-
id("com.android.application")
33-
id("org.jetbrains.kotlin.android")
34-
id("dev.vyp.stringcare.plugin")
35-
}
36-
37-
stringcare {
38-
debug = false
39-
skip = false
40-
assetsFiles = mutableListOf("*.json")
41-
stringFiles = mutableListOf("strings.xml")
42-
srcFolders = mutableListOf("src/main")
43-
}
44-
```
45-
46-
**2. Library** — In the same module:
47-
48-
```kotlin
49-
dependencies {
50-
implementation("dev.vyp.stringcare:library:5.0.0")
51-
}
52-
```
53-
54-
For **Groovy** use `buildscript` / `apply plugin: 'dev.vyp.stringcare.plugin'` and `implementation 'dev.vyp.stringcare:library:5.0.0'`. See [Migration from 4.x](MIGRATION.md) if upgrading.
55-
56-
#### [Flutter Support](https://github.com/StringCare/stringcare)
57-
58-
#### [What Is StringCare](https://github.com/StringCare/AndroidLibrary/wiki/What-is-StringCare)
59-
60-
#### [Migration from 4.x to 5.0](MIGRATION.md)
61-
62-
#### [Implementation](https://github.com/StringCare/AndroidLibrary/wiki/Implementation)
63-
64-
#### [Strings Usage](https://github.com/StringCare/AndroidLibrary/wiki/Strings-Usage)
65-
66-
#### [Assets Usage](https://github.com/StringCare/AndroidLibrary/wiki/Assets-Usage)
67-
68-
#### [Configuration](https://github.com/StringCare/AndroidLibrary/wiki/Configuration)
69-
70-
#### [Publish APK](https://github.com/StringCare/AndroidLibrary/wiki/Publish-APK)
71-
72-
#### [Limitations](https://github.com/StringCare/AndroidLibrary/wiki/Limitations)
73-
74-
#### [Compatibility](https://github.com/StringCare/AndroidLibrary/wiki/Compatibility)
75-
76-
#### [Tasks](https://github.com/StringCare/AndroidLibrary/wiki/Tasks)
77-
78-
#### [Resource Tips](https://github.com/StringCare/AndroidLibrary/wiki/Resource-Tips)
79-
80-
**Build / CI:** This project uses the JNI native library as a Git submodule (`stringcare-jni`, e.g. stringcare-android-c). Clone with submodules: `git clone --recurse-submodules ...` or run `git submodule update --init --recursive` after clone. CI workflows must use `checkout` with `submodules: true`.
81-
82-
**Plugin host natives:** With submodule `stringcare-jni` and `dist/` built, the plugin JAR picks up **`dist/{macos,linux,windows}/`** automatically on each **`preparePluginNativeLibraries`** (runs before `processResources`). Use **`./gradlew :plugin:syncPluginNativeLibraries`** only if you want to commit those binaries under `plugin/.../jni/`. macOS: universal `libsignKey.dylib`; Linux/Windows: x64 + arm64 `*.so` / `*.dll`.
83-
84-
**Publishing (release workflow):** See [CONTRIBUTING.md](CONTRIBUTING.md) for required secrets: `NEXUS_USERNAME`, `NEXUS_PASSWORD`, `GPG_KEY_ID`, `GPG_PASSPHRASE`, `PAT`. When dispatching the release workflow, set **Publish Maven** to `true` to run the publish job (publishes both `dev.vyp.stringcare:library` and `dev.vyp.stringcare:plugin`).
85-
86-
<p align="center"><img width="10%" vspace="20" src="https://github.com/StringCare/AndroidLibrary/raw/develop/white.png"></p>
87-
88-
<p align="center"><img width="40%" vspace="20" src="https://raw.githubusercontent.com/efraespada/AndroidStringObfuscator/develop/sample.png"> <img width="40%" vspace="20" src="https://raw.githubusercontent.com/efraespada/AndroidStringObfuscator/develop/sample2.png"></p>
89-
90-
91-
<p align="center"><img width="10%" vspace="20" src="https://github.com/StringCare/AndroidLibrary/raw/develop/white.png"></p>
92-
93-
License
94-
-------
95-
96-
Licensed under the Apache License, Version 2.0 (the "License");
97-
you may not use this file except in compliance with the License.
98-
You may obtain a copy of the License at
99-
100-
http://www.apache.org/licenses/LICENSE-2.0
101-
102-
Unless required by applicable law or agreed to in writing, software
103-
distributed under the License is distributed on an "AS IS" BASIS,
104-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105-
See the License for the specific language governing permissions and
106-
limitations under the License.
107-
1+
[![Maven Central](https://img.shields.io/maven-central/v/dev.vyp.stringcare/library.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22dev.vyp.stringcare%22%20AND%20a:%22library%22)
2+
3+
**StringCare Android** v5.0.0 — Compile-time obfuscation for strings and assets; runtime reveal in your app. GroupId: `dev.vyp.stringcare`.
4+
5+
Full documentation is in the [docs/](docs/) directory (getting started, configuration, API, publishing, troubleshooting).
6+
7+
<p align="center"><img width="10%" vspace="10" src="https://github.com/StringCare/AndroidLibrary/raw/develop/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png"></p>
8+
9+
<h3 align="center" style="margin-bottom:30px" vspace="20">StringCare Android Library</h3>
10+
11+
<p align="center"><img width="10%" vspace="20" src="https://github.com/StringCare/AndroidLibrary/raw/develop/white.png"></p>
12+
13+
### Installation (Kotlin DSL)
14+
15+
**1. Plugin** — In the project root `settings.gradle.kts`:
16+
17+
```kotlin
18+
pluginManagement {
19+
repositories {
20+
gradlePluginPortal()
21+
mavenCentral()
22+
google()
23+
}
24+
}
25+
// If publishing locally: includeBuild("../path/to/stringcare-android/plugin")
26+
```
27+
28+
In the app (or module) `build.gradle.kts`:
29+
30+
```kotlin
31+
plugins {
32+
id("com.android.application")
33+
id("org.jetbrains.kotlin.android")
34+
id("dev.vyp.stringcare.plugin")
35+
}
36+
37+
stringcare {
38+
debug = false
39+
skip = false
40+
assetsFiles = mutableListOf("*.json")
41+
stringFiles = mutableListOf("strings.xml")
42+
srcFolders = mutableListOf("src/main")
43+
}
44+
```
45+
46+
**2. Library** — In the same module:
47+
48+
```kotlin
49+
dependencies {
50+
implementation("dev.vyp.stringcare:library:5.0.0")
51+
}
52+
```
53+
54+
For **Groovy** use `buildscript` / `apply plugin: 'dev.vyp.stringcare.plugin'` and `implementation 'dev.vyp.stringcare:library:5.0.0'`. See [Migration from 4.x](MIGRATION.md) if upgrading.
55+
56+
#### [Flutter Support](https://github.com/StringCare/stringcare)
57+
58+
#### [What Is StringCare](https://github.com/StringCare/AndroidLibrary/wiki/What-is-StringCare)
59+
60+
#### [Migration from 4.x to 5.0](MIGRATION.md)
61+
62+
#### [Implementation](https://github.com/StringCare/AndroidLibrary/wiki/Implementation)
63+
64+
#### [Strings Usage](https://github.com/StringCare/AndroidLibrary/wiki/Strings-Usage)
65+
66+
#### [Assets Usage](https://github.com/StringCare/AndroidLibrary/wiki/Assets-Usage)
67+
68+
#### [Configuration](https://github.com/StringCare/AndroidLibrary/wiki/Configuration)
69+
70+
#### [Publish APK](https://github.com/StringCare/AndroidLibrary/wiki/Publish-APK)
71+
72+
#### [Limitations](https://github.com/StringCare/AndroidLibrary/wiki/Limitations)
73+
74+
#### [Compatibility](https://github.com/StringCare/AndroidLibrary/wiki/Compatibility)
75+
76+
#### [Tasks](https://github.com/StringCare/AndroidLibrary/wiki/Tasks)
77+
78+
#### [Resource Tips](https://github.com/StringCare/AndroidLibrary/wiki/Resource-Tips)
79+
80+
**Build / CI:** This project uses the JNI native library as a Git submodule (`stringcare-jni`, e.g. stringcare-android-c). Clone with submodules: `git clone --recurse-submodules ...` or run `git submodule update --init --recursive` after clone. CI workflows must use `checkout` with `submodules: true`.
81+
82+
**Plugin host natives:** With submodule `stringcare-jni` and `dist/` built, the plugin mirrors the whole **`dist/`** tree (e.g. **`macos/`**, **`linux/`**, **`windows/`**) into the JAR on each **`preparePluginNativeLibraries`**. To sync that tree into **`plugin/.../internal/jni/`**, run **`./gradlew syncPluginNativesFromDist`** (root) or **`./gradlew -p plugin syncDistNativesToPluginJni`**. macOS: universal `libsignKey.dylib`; Linux/Windows: x64 + arm64 `*.so` / `*.dll`.
83+
84+
**Publishing (release workflow):** See [CONTRIBUTING.md](CONTRIBUTING.md) for required secrets: `NEXUS_USERNAME`, `NEXUS_PASSWORD`, `GPG_KEY_ID`, `GPG_PASSPHRASE`, `PAT`. When dispatching the release workflow, set **Publish Maven** to `true` to run the publish job (publishes both `dev.vyp.stringcare:library` and `dev.vyp.stringcare:plugin`).
85+
86+
<p align="center"><img width="10%" vspace="20" src="https://github.com/StringCare/AndroidLibrary/raw/develop/white.png"></p>
87+
88+
<p align="center"><img width="40%" vspace="20" src="https://raw.githubusercontent.com/efraespada/AndroidStringObfuscator/develop/sample.png"> <img width="40%" vspace="20" src="https://raw.githubusercontent.com/efraespada/AndroidStringObfuscator/develop/sample2.png"></p>
89+
90+
91+
<p align="center"><img width="10%" vspace="20" src="https://github.com/StringCare/AndroidLibrary/raw/develop/white.png"></p>
92+
93+
License
94+
-------
95+
96+
Licensed under the Apache License, Version 2.0 (the "License");
97+
you may not use this file except in compliance with the License.
98+
You may obtain a copy of the License at
99+
100+
http://www.apache.org/licenses/LICENSE-2.0
101+
102+
Unless required by applicable law or agreed to in writing, software
103+
distributed under the License is distributed on an "AS IS" BASIS,
104+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105+
See the License for the specific language governing permissions and
106+
limitations under the License.
107+

app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
}
66

77
stringcare {
8-
debug = false
8+
debug = true
99
skip = false // Obfuscation runs when native lib is available (x86_64 or arm64); otherwise tasks skip automatically
1010
assetsFiles = mutableListOf("*.json")
1111
stringFiles = mutableListOf("strings.xml")

build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@ plugins {
88
tasks.register<Delete>("clean") {
99
delete(rootProject.layout.buildDirectory)
1010
}
11+
12+
/** Dev: copies `stringcare-jni/dist` into the plugin’s `internal/jni` (included build `plugin`). */
13+
tasks.register("syncPluginNativesFromDist") {
14+
group = "stringcare"
15+
description =
16+
"Copy stringcare-jni/dist → plugin internal/jni. Run after building natives in stringcare-android-c."
17+
dependsOn(gradle.includedBuild("plugin").task(":syncDistNativesToPluginJni"))
18+
}

docs/troubleshooting.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,5 @@ See [Build and CI](build-and-ci.md).
6161
- The **library** requires **minSdk 21** and a valid NDK/CMake setup. The **stringcare-jni** submodule must be present (see “Submodule not loaded” above).
6262
- **ABI filters:** The library builds for the default ABIs (e.g. armeabi-v7a, arm64-v8a, x86, x86_64). If you restrict ABIs in your app, ensure the library is compatible.
6363
- The **plugin** ships JNI for the **host** (Gradle runs on macOS, Windows, or Linux). Prebuilts live in **stringcare-jni** (`dist/macos`, `dist/linux`, `dist/windows`). The plugin build **automatically** packs `dist/` into the JAR via **`preparePluginNativeLibraries`** (as long as `stringcare-jni/dist` exists after submodule init + native build). Optional: **`./gradlew :plugin:syncPluginNativeLibraries`** copies `dist/` into `plugin/.../jni/` for Git. If your OS/arch binary is still missing from `dist/`, set **`skip = true`** or complete the native build. See [Build and CI](build-and-ci.md) and [Configuration](configuration.md).
64+
65+
- **`Skipping … (native library not available for this architecture)`** even though the JAR lists `libsignKey.*`: the native loader runs on first **`isNativeLibLoaded()`** (task **execution**), not during Gradle configuration — earlier versions loaded in a static initializer when the task class was loaded, which was **too early** for classpath resources. If you still see this on **Linux**, ensure **`libsignKey.so`** / **`libsignKey-arm64.so`** are inside the plugin JAR (`jar tf … | grep signKey`). On **Apple Silicon**, the macOS **`.dylib`** must include **arm64** (universal or arm64-only); an x86_64-only dylib will fail `System.load`.

docs/verify-obfuscation.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,20 @@ Skipping <variant> (native library not available for this architecture)
1818

1919
Tras actualizar `dist/` en el submódulo, basta con **`./gradlew :plugin:jar`** o **`./gradlew :app:assemble...`** para volver a empaquetar las nativas.
2020

21-
## 2. Tareas útiles
21+
## 2. Depurar carga de librería nativa (plugin)
22+
23+
Si ves *Skipping … native library not available*, activa trazas en **stderr** con cualquiera de:
24+
25+
1. **`stringcare { debug = true }`** en el módulo `app` (ya propagado a las tareas de ofuscación).
26+
2. **Propiedad JVM** al invocar Gradle:
27+
```bash
28+
./gradlew :app:assembleDevDebug -Dstringcare.debug.native=true --info
29+
```
30+
3. **Variable de entorno:** `STRINGCARE_DEBUG_NATIVE=1` o `STRINGCARE_DEBUG_NATIVE=true`.
31+
32+
Verás líneas `[StringCare native] …`: OS, arquitectura, orden de ficheros probados, si se encontró el recurso en classpath / JAR / `resources/main`, tamaño del fichero temporal y errores de `System.load` (p. ej. `UnsatisfiedLinkError` si el binario no coincide con la CPU).
33+
34+
## 3. Tareas útiles
2235

2336
Listar tareas del plugin en el app:
2437

@@ -37,7 +50,7 @@ Tareas típicas (por variante; ejemplo **prodDebug**):
3750
| `stringcarePreview` | Vista previa / diagnóstico |
3851
| `stringcareTestObfuscate` | Prueba de ofuscación (tests del plugin) |
3952

40-
## 3. Comprobar que no se salta la ofuscación
53+
## 4. Comprobar que no se salta la ofuscación
4154

4255
Con salida detallada:
4356

@@ -53,15 +66,15 @@ Comprobar también assets:
5366
./gradlew :app:stringcareBeforeMergeAssetsProdDebug --rerun-tasks --info 2>&1 | tee -a stringcare-verify.log
5467
```
5568

56-
## 4. Build completa
69+
## 5. Build completa
5770

5871
```bash
5972
./gradlew :app:clean :app:assembleProdDebug --rerun-tasks --info 2>&1 | tee stringcare-assemble.log
6073
```
6174

6275
Durante el merge, los recursos en disco pueden estar ofuscados de forma temporal; al final **`stringcareAfterMerge*`** restaura los fuentes del módulo. La comprobación fiable es el **log** de las tareas `BeforeMerge` (y que exista huella vía `signingReport` o configuración del plugin), no solo el `values.xml` final empaquetado.
6376

64-
## 5. Huella de firma (SHA1)
77+
## 6. Huella de firma (SHA1)
6578

6679
Si no hay `mockedFingerprint` en el bloque `stringcare { }`, el plugin usa `./gradlew signingReport` para la variante. Sin SHA1 válido, puede no ofuscar aunque la nativa cargue. Para depurar:
6780

plugin/build.gradle.kts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,55 +95,61 @@ if (project.hasProperty("signing.gnupg.keyName")) {
9595
}
9696

9797
/**
98-
* Prebuilt natives from stringcare-android-c (submodule `stringcare-jni`).
99-
* NOTE: With `includeBuild("plugin")`, this project's `rootProject` is the plugin dir, not stringcare-android —
100-
* use a path relative to this project (`../stringcare-jni/dist`).
98+
* Prebuilts from stringcare-android-c (Git submodule path in this repo: `stringcare-jni`).
99+
* Relative to `plugin/`: `../stringcare-jni/dist` (same tree as `dist/` in stringcare-android-c: `macos/`, `linux/`, `windows/`, …).
101100
*/
102-
val stringcareJniDist = layout.projectDirectory.dir("../stringcare-jni/dist")
101+
val stringcareJniDist = layout.projectDirectory.dir("../../stringcare-android-c/dist")
103102
val pluginJniSource = layout.projectDirectory.dir("src/main/kotlin/dev/vyp/stringcare/plugin/internal/jni")
104103
val pluginNativesGenerated = layout.buildDirectory.dir("generated/stringcare-plugin-natives")
105104

106105
/**
107-
* Packs all host natives into the plugin JAR on every `processResources` / `jar`.
108-
* - If `../stringcare-jni/dist/{macos,linux,windows}/` exists (after building in stringcare-android-c), copies from there.
109-
* - Otherwise falls back to checked-in files under `internal/jni/`.
106+
* Mirrors `dist/` (or checked-in `internal/jni/`) into the build dir so `processResources` packs the same layout into the JAR.
110107
*/
111108
tasks.register<Sync>("preparePluginNativeLibraries") {
112109
into(pluginNativesGenerated)
113110
if (stringcareJniDist.asFile.exists()) {
114-
from(stringcareJniDist.dir("macos")) { include("*.dylib") }
115-
from(stringcareJniDist.dir("linux")) { include("*.so") }
116-
from(stringcareJniDist.dir("windows")) { include("*.dll") }
111+
from(stringcareJniDist)
117112
} else {
118-
from(pluginJniSource) {
119-
include("*.dylib", "*.dll", "*.so")
120-
}
113+
from(pluginJniSource)
121114
}
122115
}
123116

124117
tasks.processResources {
125118
duplicatesStrategy = DuplicatesStrategy.INCLUDE
126119
dependsOn("preparePluginNativeLibraries")
127120
from(pluginNativesGenerated) {
128-
include("*.dylib", "*.dll", "*.so")
121+
include("**/*.dylib", "**/*.dll", "**/*.so")
129122
}
130123
}
131124

125+
// If you run `:plugin:clean :plugin:jar` in one invocation, Gradle may run `jar` (UP-TO-DATE) before `clean` and delete the JAR. Force correct order.
126+
tasks.named("jar") {
127+
mustRunAfter(tasks.named("clean"))
128+
}
129+
132130
/**
133-
* Optional: copy dist natives into `src/.../jni/` for committing prebuilts to Git (e.g. Maven publish without submodule on CI).
131+
* Dev-only: mirror `../stringcare-jni/dist/` into `internal/jni/` (same folder layout: `macos/`, `linux/`, `windows/`, …).
132+
*
133+
* From repo root: `./gradlew syncPluginNativesFromDist`. Or: `./gradlew -p plugin syncDistNativesToPluginJni`
134134
*/
135-
tasks.register<Copy>("syncPluginNativeLibraries") {
136-
val dist = stringcareJniDist
135+
tasks.register<Sync>("syncDistNativesToPluginJni") {
136+
group = "stringcare"
137+
description = "Dev: sync stringcare-jni/dist → plugin internal/jni (entire tree)."
137138
into(pluginJniSource)
138-
from(dist.dir("macos")) { include("*.dylib") }
139-
from(dist.dir("linux")) { include("*.so") }
140-
from(dist.dir("windows")) { include("*.dll") }
139+
from(stringcareJniDist)
141140
doFirst {
142-
if (!dist.asFile.exists()) {
141+
if (!stringcareJniDist.asFile.exists()) {
143142
throw GradleException(
144143
"stringcare-jni/dist not found. Init submodule: git submodule update --init --recursive " +
145144
"then build natives in stringcare-android-c (see that repo's docs)."
146145
)
147146
}
148147
}
149148
}
149+
150+
// Legacy name; prefer syncDistNativesToPluginJni.
151+
tasks.register("syncPluginNativeLibraries") {
152+
group = "stringcare"
153+
description = "Alias for syncDistNativesToPluginJni."
154+
dependsOn("syncDistNativesToPluginJni")
155+
}

0 commit comments

Comments
 (0)