Android Studio中带原生库的模块A-B-C依赖链配置方法问询
Hey there! Let's walk through how to set up the dependency chain (A → B → C) for your native shared libraries—so building A automatically links B, and building B automatically pulls in C. I'll cover the most common build systems since you didn't specify which one you're using.
CMake (Cross-Platform & Widely Used)
CMake makes dependency handling straightforward with target-based configuration. Here's how to set it up:
Module C Setup
First, configure your CMakeLists.txt for module C to build its shared library:
# Module C's CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(C) # Build shared library for C add_library(C SHARED src/c_core.cpp src/c_utils.cpp ) # Export headers so dependent modules can include them target_include_directories(C PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include )
Module B Setup
Link B directly to C, and include C's directory so B can use its headers. If C is a subdirectory of your project, use add_subdirectory to pull it in first:
# Module B's CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(B) # Include Module C's build configuration add_subdirectory(path/to/module/C) # Build shared library for B add_library(B SHARED src/b_logic.cpp ) # Link B to C—CMake will automatically handle linking C when building B target_link_libraries(B PRIVATE C) # Export B's headers target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include )
Module A Setup
Repeat the pattern: include B's build, then link A to B. CMake will automatically propagate the dependency on C to A, so linking A will pull in both B and C:
# Module A's CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(A) # Include Module B's build configuration add_subdirectory(path/to/module/B) # Build shared library for A add_library(A SHARED src/a_entry.cpp ) # Link A to B—CMake handles the C dependency automatically target_link_libraries(A PRIVATE B)
Gradle + Android NDK (For Android Apps)
If you're building for Android with the NDK, use Gradle's module dependency system alongside CMake:
Module C (Android Library Module)
Set up your build.gradle to build the C shared library, and mark it as an Android Library:
// Module C's build.gradle (:C) plugins { id 'com.android.library' } android { namespace "com.yourdomain.c" compileSdk 34 defaultConfig { minSdk 24 targetSdk 34 externalNativeBuild { cmake { arguments "-DANDROID_STL=c++_shared" targets "C" // Match the library name from C's CMakeLists.txt } } } externalNativeBuild { cmake { path file('src/main/cpp/CMakeLists.txt') version '3.22.1' } } }
Module B (Android Library Module)
Add a dependency on Module C in Gradle, then link B to C in its CMakeLists.txt:
// Module B's build.gradle (:B) plugins { id 'com.android.library' } android { // ... similar config to Module C } dependencies { // Add dependency on Module C implementation project(':C') }
In B's CMakeLists.txt, link to C just like in the cross-platform example:
target_link_libraries(B PRIVATE C)
Module A & Your App
Add a dependency on Module B to your app's build.gradle:
// App module's build.gradle (:app) dependencies { implementation project(':A') }
Gradle will automatically package all three shared libraries (A, B, C) into your APK, and linking will happen without extra manual steps.
Makefile (Traditional Native Builds)
Makefiles require a bit more manual setup, but you can use variables to simplify dependency propagation:
Module C's Makefile
# Module C's Makefile CC = gcc CFLAGS = -fPIC -Wall -Wextra INCLUDES = -I./include LIB_OUTPUT = libC.so all: $(LIB_OUTPUT) $(LIB_OUTPUT): c_core.o c_utils.o $(CC) -shared -o $@ $^ c_core.o: src/c_core.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ c_utils.o: src/c_utils.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ clean: rm -f *.o $(LIB_OUTPUT)
Module B's Makefile
Include C's build and link against its library. Use variables to store C's paths so they're easy to reference:
# Module B's Makefile CC = gcc CFLAGS = -fPIC -Wall -Wextra INCLUDES = -I./include -I../C/include LIB_C_PATH = ../C LIB_C = -L$(LIB_C_PATH) -lC LIB_OUTPUT = libB.so # Build Module C first all: $(LIB_C_PATH)/libC.so $(LIB_OUTPUT) $(LIB_C_PATH)/libC.so: cd $(LIB_C_PATH) && make $(LIB_OUTPUT): b_logic.o $(CC) -shared -o $@ $^ $(LIB_C) b_logic.o: src/b_logic.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ clean: rm -f *.o $(LIB_OUTPUT) cd $(LIB_C_PATH) && make clean
Module A's Makefile
Repeat the pattern, using B's variables to pull in both B and C dependencies:
# Module A's Makefile CC = gcc CFLAGS = -fPIC -Wall -Wextra INCLUDES = -I./include -I../B/include -I../C/include LIB_B_PATH = ../B LIB_B = -L$(LIB_B_PATH) -lB LIB_C = -L../C -lC LIB_OUTPUT = libA.so all: $(LIB_B_PATH)/libB.so $(LIB_OUTPUT) $(LIB_B_PATH)/libB.so: cd $(LIB_B_PATH) && make $(LIB_OUTPUT): a_entry.o $(CC) -shared -o $@ $^ $(LIB_B) $(LIB_C) a_entry.o: src/a_entry.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ clean: rm -f *.o $(LIB_OUTPUT) cd $(LIB_B_PATH) && make clean
Key Takeaway
No matter which build system you use, the core idea is the same: explicitly declare each module's direct dependencies. Modern build systems like CMake and Gradle will automatically handle dependency propagation, so you don't have to manually link all transitive dependencies (like C when building A). For Makefiles, use variables and pre-build steps to ensure dependent libraries are built and linked correctly.
内容的提问来源于stack exchange,提问作者Kamille




