Android Gradle Plugin 源码解析之 externalNativeBuild
在Android Studio 2.2開始的Android Gradle Plugin版本中,Google集成了對cmake的完美支持,而原先的ndkBuild的方式支持也變得更加良好。這篇文章就來說說Android Gradle Plugin與交叉編譯之間的一些事,即externalNativeBuild相關的task,主要是解讀一下gradle構建系統相關的源碼。
前言
如果你在gradle中使用過cmake,你會發現在gradle執行sync操作后,項目的module目錄下就會生成一個叫.externalNativeBuild的文件夾,該文件夾用來進行C/C++代碼的編譯,當然,如果你用的是ndkBuild的方式,該文件夾下的文件會發生變化,文件較cmake會少很多。
cmake方式產生的文件列表如下:
而ndkBuild方式產生的文件列表如下:
他們的共同點是都有一個叫android_gradle_build.json的文件,這個文件用來被Android Gradle Plugin中的externalNativeBuild任務解析,將構建命令解析出來,然后編譯C/C++代碼,最后產生目標so文件。除此之外,還有x_build_command.txt和x_build_output.txt兩個文件,其中x表示構建方式,使用cmake的話x就等于cmake,使用ndkBuild的話x就等于ndkBuild。x_build_command.txt文件承載著構建命令,android_gradle_build.json的生成依賴它,而x_build_output.txt文件是執行x_build_command.txt中的構建命令后控制臺輸出的內容。
cmake
通過查看android gradle plugin的源碼,可以發現生成cmake_build_command.txt文件生成的方式其實很簡單,就是一個不斷拼接參數的過程,其源碼在CmakeExternalNativeJsonGenerator中的getProcessBuilder,如下:
| 1234567891011121314151617181920212223242526272829303132333435363738394041424344 | @NonNull@OverrideProcessInfoBuilder getProcessBuilder(@NonNull String abi, int abiPlatformVersion, @NonNull File outputJson) {checkConfiguration();ProcessInfoBuilder builder = new ProcessInfoBuilder(); // CMake requires a folder. Trim the filename off.File cmakeListsFolder = getMakefile().getParentFile();builder.setExecutable(getCmakeExecutable());builder.addArgs(String.format("-H%s", cmakeListsFolder));builder.addArgs(String.format("-B%s", outputJson.getParentFile())); // TODO: possibly remove the Android Gradle part. // Depends on how upstream CMake accepts our JSON patch.builder.addArgs("-GAndroid Gradle - Ninja");builder.addArgs(String.format("-DANDROID_ABI=%s", abi));builder.addArgs(String.format("-DANDROID_NDK=%s", getNdkFolder()));builder.addArgs( String.format("-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=%s", new File(getObjFolder(), abi)));builder.addArgs( String.format("-DCMAKE_BUILD_TYPE=%s", isDebuggable() ? "Debug" : "Release"));builder.addArgs(String.format("-DCMAKE_MAKE_PROGRAM=%s",getNinjaExecutable().getAbsolutePath()));builder.addArgs(String.format("-DCMAKE_TOOLCHAIN_FILE=%s",getToolChainFile().getAbsolutePath()));builder.addArgs(String.format("-DANDROID_PLATFORM=android-%s", abiPlatformVersion)); if (!getcFlags().isEmpty()) {builder.addArgs(String.format("-DCMAKE_C_FLAGS=%s", Joiner.on(" ").join(getcFlags())));} if (!getCppFlags().isEmpty()) {builder.addArgs(String.format("-DCMAKE_CXX_FLAGS=%s",Joiner.on(" ").join(getCppFlags())));} for (String argument : getBuildArguments()) {builder.addArgs(argument);} return builder;} |
該函數的返回值為ProcessInfoBuilder,該對象專門用于攜帶可執行文件以及可執行文件執行時需要的參數,傳遞給project.exec執行。
- 設置可執行文件為cmake,調用getCmakeExecutable方法,獲取cmake可執行文件,調用setExecutable方法設置它
- 拼接-H參數,其值為CMakeList.txt文件所在目錄
- 拼接-B參數,其值為cmake產生的中間產物,一般就是cmake_build_command.txt所在目錄的父目錄,cmake構建產生的中間產物全都位于此目錄。
- 拼接-G參數,其值為Android Gradle - Ninja,告訴cmake生成Android Gradle需要的項目文件,并且使用ninja構建,值得注意的是,該值在標準的cmake中是不支持的,也就是說,as使用的cmake是google修改過的,通過查看其注釋?possibly remove the Android Gradle part. Depends on how upstream CMake accepts our JSON patch.?也可以看出,google可能會移除該值中Android Gradle部分,但是還是要取決于cmake如果接受google的patch。
- 設置ANDROID_ABI參數,其值為armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64中的一個。
- 設置ANDROID_NDK參數,其值為ndk的路徑。
- 設置CMAKE_LIBRARY_OUTPUT_DIRECTORY參數,其值為so的輸出路徑,一般其值為項目的build路徑下的intermediates/cmake/debug/obj/$ANDROID_ABI
- 設置CMAKE_BUILD_TYPE參數,是否是debug,其值為Debug或者Release中的一個,debug含符號信息,so很大,便于調試,Release移除了debug信息,小很多。其值來源于build.gradle中的debuggable值。
- 設置CMAKE_MAKE_PROGRAM參數。其值為ninja路徑,因為生成的是ninja構建的項目,所以需要指定其路徑。該值gradle會根據sdk和ndk的路徑,自動推斷出。
- 設置CMAKE_TOOLCHAIN_FILE參數,該參數是cmake用于交叉編譯時設置的必要參數,主要設置一些交叉編譯需要的參數,如CC,CXX,AR,AS,CFLAGS,CXXFLAGS等,可以查看NDK 交叉編譯常用變量,android.toolchain.cmake文件的代碼在android.toolchain.cmake,兼容android-cmake,該值gradle會根據sdk和ndk的路徑,自動推斷出。
- 設置ANDROID_PLATFORM參數,一般設成和項目的最小api版本一樣即可,gradle會通過它和minSdk查找出合適的值
- 設置可選項CMAKE_C_FLAGS參數,如果不為空,則設置,其值為編譯C時的一些參數
- 設置可選項CMAKE_CXX_FLAGS參數,如果不為空,則設置,其值為編譯C++時的一些參數
- 設置可選項arguments,其值為gradle傳進來的arguments參數。
更多參數說明見CMake
然后將該返回值轉為字符串輸出到文件中,該文件即cmake_build_command.txt
其內容大致如下:
| 12345678910111213141516 | Executable : /Users/lizhangqu/AndroidSDK/cmake/3.6.3155560/bin/cmakearguments : -Hpath/to/CMakeFiles Parent Dir-Bpath/to/moduleDir/.externalNativeBuild/cmake/debug/armeabi-v7a-GAndroid Gradle - Ninja-DANDROID_ABI=armeabi-v7a-DANDROID_NDK=/Users/lizhangqu/AndroidNDK/android-ndk-r14b-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=path/to/moduleDir/build/intermediates/cmake/debug/obj/armeabi-v7a-DCMAKE_BUILD_TYPE=Debug-DCMAKE_MAKE_PROGRAM=/Users/lizhangqu/AndroidSDK/cmake/3.6.3155560/bin/ninja-DCMAKE_TOOLCHAIN_FILE=/Users/lizhangqu/AndroidNDK/android-ndk-r14b/build/cmake/android.toolchain.cmake-DANDROID_PLATFORM=android-14-DCMAKE_C_FLAGS=-fpic -fexceptions -frtti-DCMAKE_CXX_FLAGS=-fpic -fexceptions -frtti-DANDROID_STL=c++_staticjvmArgs : |
當然我們可以直接在命令行調用之,生成cmake項目結構,如下
| 1234567891011121314 | /Users/lizhangqu/AndroidSDK/cmake/3.6.3155560/bin/cmake \-H"path/to/CMakeFiles Parent Dir" \-B"path/to/moduleDir/.externalNativeBuild/cmake/debug/armeabi-v7a" \-G"Android Gradle - Ninja" \-DANDROID_ABI="armeabi-v7a" \-DANDROID_NDK="/Users/lizhangqu/AndroidNDK/android-ndk-r14b" \-DCMAKE_LIBRARY_OUTPUT_DIRECTORY="path/to/moduleDir/build/intermediates/cmake/debug/obj/armeabi-v7a" \-DCMAKE_BUILD_TYPE="Debug" \-DCMAKE_MAKE_PROGRAM="/Users/lizhangqu/AndroidSDK/cmake/3.6.3155560/bin/ninja" \-DCMAKE_TOOLCHAIN_FILE="/Users/lizhangqu/AndroidNDK/android-ndk-r14b/build/cmake/android.toolchain.cmake" \-DANDROID_PLATFORM="android-14" \-DCMAKE_C_FLAGS="-fpic -fexceptions -frtti" \-DCMAKE_CXX_FLAGS="-fpic -fexceptions -frtti" \-DANDROID_STL="c++_static" |
其對應的gradle調用代碼大致如下
| 12345 | // See whether the current build command matches a previously written build command.String currentBuildCommand = processBuilder.toString();boolean rebuildDueToMissingPreviousCommand = false;File commandFile = new File(expectedJson.getParentFile(), String.format("%s_build_command.txt", getNativeBuildSystem().getName())); |
就是將processBuilder對象中攜帶的參數,調用project.exec執行即可。
| 12345678 | String buildOutput = executeProcess(processBuilder);// Write the captured process output to a file for diagnostic purposes.File outputTextFile = new File(expectedJson.getParentFile(), String.format("%s_build_output.txt", getNativeBuildSystem().getName()));diagnostic("write build output %s", outputTextFile.getAbsolutePath());Files.write(buildOutput, outputTextFile, Charsets.UTF_8); |
executeProcess執行完之后,就會產生cmake_build_output.txt文件,該文件就是執行cmake_build_command.txt中的命令之后控制臺輸出的內容。大致如下:
| 123456789101112131415 | -- Check for working C compiler: /Users/lizhangqu/AndroidNDK/android-ndk-r14b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang-- Check for working C compiler: /Users/lizhangqu/AndroidNDK/android-ndk-r14b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang -- works-- Detecting C compiler ABI info-- Detecting C compiler ABI info - done-- Detecting C compile features-- Detecting C compile features - done-- Check for working CXX compiler: /Users/lizhangqu/AndroidNDK/android-ndk-r14b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++-- Check for working CXX compiler: /Users/lizhangqu/AndroidNDK/android-ndk-r14b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ -- works-- Detecting CXX compiler ABI info-- Detecting CXX compiler ABI info - done-- Detecting CXX compile features-- Detecting CXX compile features - done-- Configuring done-- Generating done-- Build files have been written to: path/to/moduleDir/.externalNativeBuild/cmake/debug/armeabi-v7a |
此外,其他文件也被一并產生,如android_gradle_build.json,build.ninja,cmake_insatll.cmake,CMakeCache.txt,rules.ninja,CMakefiles文件夾等等。
而executeProcess方法,最終調用的是GradleProcessExecutor的execute方法,然后通過其內部類ExecAction的execute方法構造ExecSpec對象,調用project.exec方法執行之。其大致源碼如下:
| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 | public ProcessResult execute( ProcessInfo processInfo, ProcessOutputHandler processOutputHandler) {ProcessOutput output = processOutputHandler.createOutput();ExecResult result; try {result = project.exec(new ExecAction(processInfo, output));} finally { try {output.close();} catch (IOException e) {project.getLogger().warn("Exception while closing sub process streams", e);}} try {processOutputHandler.handleOutput(output);} catch (final ProcessException e) { return new OutputHandlerFailedGradleProcessResult(e);} return new GradleProcessResult(result, processInfo);}private static class ExecAction implements Action<ExecSpec> { private final ProcessInfo processInfo; private final ProcessOutput processOutput;ExecAction( final ProcessInfo processInfo, final ProcessOutput processOutput) { this.processInfo = processInfo; this.processOutput = processOutput;} public void execute(ExecSpec execSpec) { /* * Gradle doesn't work correctly when there are empty args. */List<String> args =processInfo.getArgs().stream().map(a -> a.isEmpty()? "\"\"" : a).collect(Collectors.toList());execSpec.setExecutable(processInfo.getExecutable());execSpec.args(args);execSpec.environment(processInfo.getEnvironment());execSpec.setStandardOutput(processOutput.getStandardOutput());execSpec.setErrorOutput(processOutput.getErrorOutput()); // we want the caller to be able to do its own thing.execSpec.setIgnoreExitValue(true);}} |
json文件生成了,之后就是生產so文件了,生成so文件由ExternalNativeBuildTask負責,其主要職責就是解析出android_gradle_build.json文件中libraries的各項中的的artifactName和buildCommand,傳入對應的executeProcessBatch函數,執行buildCommand中的值,執行的方式也是通過GradleProcessExecutor的execute方法,最終產生so。
我們來看看android_gradle_build.json的大致內容:
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 | { "buildFiles" : [ "path/to/CMakeLists.txt"], "cleanCommands" : [ "/Users/lizhangqu/AndroidSDK/cmake/3.6.3155560/bin/cmake --build path/to/.externalNativeBuild/cmake/debug/armeabi-v7a --target clean"], "cppFileExtensions" : ["cpp" ], "libraries" : { "so名字-Debug-armeabi-v7a" : { "abi" : "armeabi-v7a", "artifactName" : "so名字", "buildCommand" : "/Users/lizhangqu/AndroidSDK/cmake/3.6.3155560/bin/cmake --build path/to/.externalNativeBuild/cmake/debug/armeabi-v7a --target so名字", "buildType" : "debug", "files" : [ { "flags" : "內容", "src" : "內容", "workingDirectory" : "path/to/.externalNativeBuild/cmake/debug/armeabi-v7a"},{ "flags" : "內容", "src" : "內容", "workingDirectory" : "path/to/.externalNativeBuild/cmake/debug/armeabi-v7a"},{ "flags" : "內容", "src" : "內容", "workingDirectory" : "path/to/.externalNativeBuild/cmake/debug/armeabi-v7a"}], "output" : "path/to/build/intermediates/cmake/debug/obj/armeabi-v7a/so名字.so", "toolchain" : "12644252315582812689"}}, "toolchains" : { "12644252315582812689" : { "cCompilerExecutable" : "/Users/lizhangqu/AndroidNDK/android-ndk-r14b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang", "cppCompilerExecutable" : "/Users/lizhangqu/AndroidNDK/android-ndk-r14b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++"}}} |
沒錯解析的就是libraries下”so名字-Debug-armeabi-v7a”下的artifactName和buildCommand,我們可以試試直接將buildCommand中的命令復制到命令行執行,可以看到so就會編譯產生。
除了直接復制buildCommand中的命令,也可以進入到cmake生成的文件目錄,即-B指定的目錄,調用ninja進行構建。如:
| 123 | /UsersAndroidSDK3.6.3155560ninja clean/UsersAndroidSDK3.6.3155560ninja |
ninja是chromium的核心構建工具,可以參考Ninja - chromium核心構建工具學習下相關的內容。
而clean操作,則由ExternalNativeCleanTask負責,其主要職責就是解析出android_gradle_build.json中的cleanCommands命令,然后執行。
ndkBuild
和cmake類似,首先就是ndkBuild_build_command.txt的生成,其生成所需的關鍵參數由NdkBuildExternalNativeJsonGenerator中的getProcessBuilder函數和getBaseArgs函數負責,其代碼如下:
| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 | @NonNull@OverrideProcessInfoBuilder getProcessBuilder(@NonNull String abi, int abiPlatformVersion,@NonNull File outputJson) {checkConfiguration(); // Discover Application.mk if one exists next to Android.mk // If there is an Application.mk file next to Android.mk then pick it up.File applicationMk = new File(getMakeFile().getParent(), "Application.mk");ProcessInfoBuilder builder = new ProcessInfoBuilder();builder.setExecutable(getNdkBuild()).addArgs(getBaseArgs(abi, abiPlatformVersion, applicationMk)) // Disable response files so we can parse the command line..addArgs("APP_SHORT_COMMANDS=false").addArgs("LOCAL_SHORT_COMMANDS=false").addArgs("-B") // Build as if clean.addArgs("-n"); return builder;}/** * Get the base list of arguments for invoking ndk-build. */@NonNullprivate List<String> getBaseArgs(@NonNull String abi, int abiPlatformVersion,@NonNull File applicationMk) {List<String> result = Lists.newArrayList();result.add("NDK_PROJECT_PATH=null");result.add("APP_BUILD_SCRIPT=" + getMakeFile()); if (applicationMk.exists()) { // NDK_APPLICATION_MK specifies the Application.mk file.result.add("NDK_APPLICATION_MK=" + applicationMk.getAbsolutePath());} // APP_ABI and NDK_ALL_ABIS work together. APP_ABI is the specific ABI for this build. // NDK_ALL_ABIS is the universe of all ABIs for this build. NDK_ALL_ABIS is set to just the // current ABI. If we don't do this, then ndk-build will erase build artifacts for all abis // aside from the current.result.add("APP_ABI=" + abi);result.add("NDK_ALL_ABIS=" + abi); if (isDebuggable()) {result.add("NDK_DEBUG=1");} else {result.add("NDK_DEBUG=0");}result.add("APP_PLATFORM=android-" + abiPlatformVersion); // getObjFolder is set to the "local" subfolder in the user specified directory, therefore, // NDK_OUT should be set to getObjFolder().getParent() instead of getObjFolder(). String ndkOut = getObjFolder().getParent(); if (CURRENT_PLATFORM == PLATFORM_WINDOWS) { // Due to b.android.com/219225, NDK_OUT on Windows requires forward slashes. // ndk-build.cmd is supposed to escape the back-slashes but it doesn't happen. // Workaround here by replacing back slash with forward. // ndk-build will have a fix for this bug in r14 but this gradle fix will make it // work back to r13, r12, r11, and r10.ndkOut = ndkOut.replace('\\', '/');}result.add("NDK_OUT=" + ndkOut);result.add("NDK_LIBS_OUT=" + getSoFolder().getAbsolutePath()); for (String flag : getcFlags()) {result.add(String.format("APP_CFLAGS+=\"%s\"", flag));} for (String flag : getCppFlags()) {result.add(String.format("APP_CPPFLAGS+=\"%s\"", flag));} for (String argument : getBuildArguments()) {result.add(argument);} return result;} |
- 設置可執行文件為ndk-Build
- 設置NDK_PROJECT_PATH=null
- 設置APP_BUILD_SCRIPT參數,其值指向Android.mk文件
- 設置NDK_APPLICATION_MK參數,其值指向Application.mk文件(如果存在的話,不存在就不會設置該參數)
- 設置APP_ABI參數,其值為armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64中的一個。
- 設置NDK_ALL_ABIS參數,其值等同于APP_ABI
- 設置NDK_DEBUG參數,表示十分是debug構建,debug含符號信息,so很大,便于調試,release移除了debug信息,小很多。其值來源于build.gradle中的debuggable值。
- 設置APP_PLATFORM參數,一般設成和項目的最小api版本一樣即可,gradle會通過它和minSdk查找出合適的值
- 設置NDK_OUT參數,其值為obj文件產生目錄,一般指向項目的build路徑下的intermediates/ndkBuild/$buildType/obj目錄
- 設置NDK_LIBS_OUT參數,其值為libs參數目錄,用于so的存儲,一般指向項目的build路徑下的intermediates/ndkBuild/$buildType/lib目錄
- 設置可選項APP_CFLAGS參數,如果不為空,則設置,其值為編譯C時的一些參數
- 設置可選項APP_CPPFLAGS參數,如果不為空,則設置,其值為編譯C++時的一些參數
- 設置可選項arguments,其值為gradle傳進來的arguments參數。
- 設置APP_SHORT_COMMANDS=false
- 設置LOCAL_SHORT_COMMANDS=false
- 添加-B參數
- 添加-n參數
最終生成的文件大致內容如下:
| 123456789101112131415161718 | Executable : /Users/lizhangqu/AndroidSDK/ndk-bundle/ndk-buildarguments : NDK_PROJECT_PATH=nullAPP_BUILD_SCRIPT=path/to/Android.mkNDK_APPLICATION_MK=path/to/Application.mkAPP_ABI=armeabiNDK_ALL_ABIS=armeabiNDK_DEBUG=1APP_PLATFORM=android-14NDK_OUT=path/to/moduleDir/build/intermediates/ndkBuild/debug/objNDK_LIBS_OUT=path/to/moduleDir/build/intermediates/ndkBuild/debug/libAPP_CFLAGS+="-fpic -fexceptions -frtti"APP_CPPFLAGS+="-fpic -fexceptions -frtti"APP_SHORT_COMMANDS=falseLOCAL_SHORT_COMMANDS=false-B-njvmArgs : |
同理我們也可以直接在命令行調用他們執行
| 12345678910111213141516 | /Users/lizhangqu/AndroidSDK/ndk-bundle/ndk-build \NDK_PROJECT_PATH=null \APP_BUILD_SCRIPT=path/to/Android.mk \NDK_APPLICATION_MK=path/to/Application.mk \APP_ABI=armeabi \NDK_ALL_ABIS=armeabi \NDK_DEBUG=1 \APP_PLATFORM=android-14 \NDK_OUT=path/to/moduleDir/build/intermediates/ndkBuild/debug/obj \NDK_LIBS_OUT=path/to/moduleDir/build/intermediates/ndkBuild/debug/lib \APP_CFLAGS+="-fpic -fexceptions -frtti" \APP_CPPFLAGS+="-fpic -fexceptions -frtti" \APP_SHORT_COMMANDS=false \LOCAL_SHORT_COMMANDS=false \-B \-n \ |
其代碼調用過程同cmake,最終生成ndkBuild_build_output.txt,該文件內容就是調用ndkBuild_build_command.txt中的命令后控制臺輸出的內容。
而同cmake不同的是,android_gradle_build.json的文件,不再是由cmake構建系統產生,而是gradle解析ndkBuild_build_command.txt產生的。
其代碼大致如下
| 12345678910111213141516171819202122232425262728293031323334 | NativeBuildConfigValue buildConfig = new NativeBuildConfigValueBuilder(getMakeFile(),projectDir).addCommands(getBuildCommand(abi, abiPlatformVersion, applicationMk),variantName,buildOutput,isWindows()).build();if (applicationMk.exists()) {diagnostic("found application make file %s", applicationMk.getAbsolutePath());Preconditions.checkNotNull(buildConfig.buildFiles);buildConfig.buildFiles.add(applicationMk);}String actualResult = new GsonBuilder().registerTypeAdapter(File.class, new PlainFileGsonTypeAdaptor()).setPrettyPrinting().create().toJson(buildConfig);// Write the captured ndk-build output to JSON fileFile expectedJson = ExternalNativeBuildTaskUtils.getOutputJson(getJsonFolder(), abi);Files.write(actualResult, expectedJson, Charsets.UTF_8);/** * ExternalNativeBuildTaskUtils.getOutputJson * Utility function that gets the name of the output JSON for a particular ABI. */@NonNullpublic static File getOutputJson(@NonNull File jsonFolder, @NonNull String abi) { return new File(getOutputFolder(jsonFolder, abi), "android_gradle_build.json");} |
NativeBuildConfigValue的build方法如下,總而言之就是調用各個方法,獲取對應的值。
| 1234567891011121314151617181920 | /** * Builds the {@link NativeBuildConfigValue} from the given information. */@NonNullpublic NativeBuildConfigValue build() {findLibraryNames();findToolchainNames();findToolChainCompilers();NativeBuildConfigValue config = new NativeBuildConfigValue(); // Sort by library name so that output is stableCollections.sort(outputs, (o1, o2) -> o1.libraryName.compareTo(o2.libraryName)); config.cleanCommands = generateCleanCommands(); config.buildFiles = Lists.newArrayList(androidMk); config.libraries = generateLibraries(); config.toolchains = generateToolchains(); config.cFileExtensions = generateExtensions(cFileExtensions); config.cppFileExtensions = generateExtensions(cppFileExtensions); return config;} |
生成的json文件內容就不貼了,同cmake。
http://fucknmb.com/2017/06/24/Android-Gradle-Plugin%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B9%8BexternalNativeBuild/ 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android Gradle Plugin 源码解析之 externalNativeBuild的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NDK 交叉编译常用变量
- 下一篇: 又掌握了一项新技能 - 断点调试 Gra