diff --git a/AndroidProjectTooling.sh b/AndroidProjectTooling.sh index 5dd17b2..9571983 100755 --- a/AndroidProjectTooling.sh +++ b/AndroidProjectTooling.sh @@ -450,7 +450,7 @@ build_android_git_for_abi() { local output_dir="$ANDROID_JNI_DIR/$abi" local output_binary="$output_dir/libgit.so" local stamp="$output_dir/.source-fingerprint" - local fingerprint="$3:android:$abi:$cc:$ANDROID_API" + local fingerprint="$3:android:$abi:$cc:$ANDROID_API:githug-embedded-main" require_path "$ANDROID_TOOLBIN/$cc" @@ -470,6 +470,7 @@ build_android_git_for_abi() { NO_PTHREADS=YesPlease \ NO_LIBGEN_H=YesPlease \ HAVE_DEV_TTY=YesPlease \ + CFLAGS_APPEND=-DGITHUG_EMBEDDED_MAIN \ CC="$cc" \ AR=llvm-ar \ RANLIB=llvm-ranlib \ diff --git a/README.md b/README.md index 6f1f482..fccacd0 100644 --- a/README.md +++ b/README.md @@ -155,13 +155,13 @@ Git build outputs are stamped with a Git source fingerprint. Re-running `--test` Git manpage assets are also refreshed from the checked-out Git source whenever Git is compiled or an app artifact is built. -Git source patches can live under `patches/git/` and are applied by `AndroidProjectTooling.sh` after the Git source checkout is cloned or reused. These patches are part of the source fingerprint, so changing a patch forces the host and Android Git binaries to rebuild. The current runtime does not require a Git source patch: it resolves Git's existing exported `init_git` and `cmd_main` symbols through JNI and calls them with Git-style `argc`/`argv`. +Git source patches live under `patches/git/` and are applied by `AndroidProjectTooling.sh` after the Git source checkout is cloned or reused. These patches are part of the source fingerprint, so changing a patch forces Git binaries to rebuild. The Android Git build enables a small embedded-main patch that exports `githug_git_main(argc, argv)`. That wrapper still delegates command dispatch and parsing to Git, but routes Git's process-exit path through `_exit()` in the forked child so Android does not run libc/JVM inherited exit handlers after native Git completes. ## Runtime Architecture The command engine has one app-facing runtime: -- **Native Git path**: the packaged Git binary for the device ABI is loaded by the JNI bridge and every `git ...` command invokes Git's existing `init_git(argv)` and `cmd_main(argc, argv)` path in a real per-level repository sandbox in app-private storage. +- **Native Git path**: the packaged Git binary for the device ABI is loaded by the JNI bridge and every `git ...` command invokes Git's patched embedded entry point, `githug_git_main(argc, argv)`, in a forked child process inside a real per-level repository sandbox in app-private storage. The runtime exposes a `RepoState` surface to validators. In addition to files, commits, branches, tags, remotes, and config, the model tracks learning-relevant effects such as stashes, fetched remote refs, pushed branches/tags, submodules, and repository maintenance actions. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef4dd2f..9603ede 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,8 +20,8 @@ android { applicationId = "solutions.tretter.githugandroid" minSdk = 26 targetSdk = 35 - versionCode = 177 - versionName = "0.1.176" + versionCode = 178 + versionName = "0.1.177" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/cpp/githug_runtime.c b/app/src/main/cpp/githug_runtime.c index f6e656b..7691c85 100644 --- a/app/src/main/cpp/githug_runtime.c +++ b/app/src/main/cpp/githug_runtime.c @@ -16,6 +16,7 @@ #define GIT_COMMAND_TIMEOUT_MS 30000 typedef int (*git_main_fn)(int argc, const char **argv); +typedef int (*githug_git_main_fn)(int argc, const char **argv); typedef void (*git_init_fn)(const char **argv); struct output_buffer { @@ -27,6 +28,7 @@ struct output_buffer { static pthread_mutex_t git_mutex = PTHREAD_MUTEX_INITIALIZER; static void *git_handle = NULL; static git_main_fn git_main = NULL; +static githug_git_main_fn githug_git_main = NULL; static git_init_fn git_init = NULL; static int append_output(struct output_buffer *buffer, const char *data, size_t length) { @@ -116,6 +118,7 @@ static int load_git(const char *library_path) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "dlsym init_git failed: %s", dlerror()); return -1; } + githug_git_main = (githug_git_main_fn)dlsym(git_handle, "githug_git_main"); git_main = (git_main_fn)dlsym(git_handle, "cmd_main"); if (git_main == NULL) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "dlsym cmd_main failed: %s", dlerror()); @@ -274,8 +277,13 @@ Java_solutions_tretter_githugandroid_NativeGitBridge_runGitMainNative( (void)saved_env; chdir(working_directory_chars); - git_init((const char **)argv); - int child_exit_code = git_main(argc, (const char **)argv); + int child_exit_code; + if (githug_git_main != NULL) { + child_exit_code = githug_git_main(argc, (const char **)argv); + } else { + git_init((const char **)argv); + child_exit_code = git_main(argc, (const char **)argv); + } fflush(stdout); fflush(stderr); _exit(child_exit_code); diff --git a/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt b/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt index e564bf2..4a6b89d 100644 --- a/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt +++ b/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt @@ -196,7 +196,7 @@ class GitRepositoryRuntime private constructor( addAll(GitSandboxEngine.commandReferenceLines()) add("Native Git runtime:") add(" binary path: nativeLibraryDir/libgit.so") - add(" invocation: init_git(argv), then cmd_main(argc, argv)") + add(" invocation: githug_git_main(argc, argv)") add(" selected ABI: ${Build.SUPPORTED_ABIS.firstOrNull() ?: "unknown"}") add(" helper commands: ls/dir, pwd, cat, sh