package org.smoothbuild.builtin.android; import static org.smoothbuild.builtin.android.AndroidSdk.AIDL_BINARY; import static org.smoothbuild.builtin.util.Exceptions.stackTraceToString; import static org.smoothbuild.io.fs.base.Path.path; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import org.smoothbuild.io.util.TempDir; import org.smoothbuild.lang.message.ErrorMessage; import org.smoothbuild.lang.plugin.Container; import org.smoothbuild.lang.plugin.SmoothFunction; import org.smoothbuild.lang.value.Array; import org.smoothbuild.lang.value.SFile; import org.smoothbuild.lang.value.SString; import org.smoothbuild.util.CommandExecutor; public class AidlFunction { @SmoothFunction public static SFile aidl(Container container, SString apiLevel, SString buildToolsVersion, SFile interfaceFile) throws InterruptedException { return execute(container, buildToolsVersion.value(), apiLevel.value(), interfaceFile); } private static SFile execute(Container container, String buildToolsVersion, String apiLevel, SFile interfaceFile) throws InterruptedException { String aidlBinary = AndroidSdk.getAidlBinaryPath(buildToolsVersion).toString(); String frameworkAidl = AndroidSdk.getFrameworkAidl(apiLevel).toString(); TempDir inputFilesDir = container.createTempDir(); inputFilesDir.writeFile(interfaceFile); TempDir outputFilesDir = container.createTempDir(); List<String> command = new ArrayList<>(); command.add(aidlBinary); command.add("-p" + frameworkAidl); command.add("-o" + outputFilesDir.rootOsPath()); command.add(inputFilesDir.asOsPath(path(interfaceFile.path().value()))); executeCommand(command); return onlyElement(outputFilesDir.readFiles()); } private static SFile onlyElement(Array<SFile> outputFiles) { Iterator<SFile> iterator = outputFiles.iterator(); if (!iterator.hasNext()) { throw new ErrorMessage(AIDL_BINARY + " binary should return exactly one file but returned zero."); } SFile result = iterator.next(); if (iterator.hasNext()) { StringBuilder builder = new StringBuilder(); builder.append(AIDL_BINARY); builder.append("binary should return exactly one file but it returned following files:\n"); for (SFile file : outputFiles) { builder.append(file.path().value()); builder.append("\n"); } throw new ErrorMessage(builder.toString()); } return result; } private static void executeCommand(List<String> command) throws InterruptedException { try { int exitValue = CommandExecutor.execute(command); if (exitValue != 0) { throw new ErrorMessage(AIDL_BINARY + " binary returned non zero exit value = " + exitValue); } } catch (IOException e) { throw new ErrorMessage(binaryFailedMessage(command, e)); } } private static String binaryFailedMessage(List<String> command, IOException e) { return "Following command line failed:\n" + join(command) + "\n" + "stack trace is:\n" + stackTraceToString(e); } private static String join(List<String> command) { return command.stream().collect(Collectors.joining(" ")); } // Documentation copy/pasted from aidl command line tool: // // usage: aidl OPTIONS INPUT [OUTPUT] // aidl --preprocess OUTPUT INPUT... // // OPTIONS: // -I<DIR> search path for import statements. // -d<FILE> generate dependency file. // -a generate dependency file next to the output file with the name based on // the input file. // -p<FILE> file created by --preprocess to import. // -o<FOLDER> base output folder for generated files. // -b fail when trying to compile a parcelable. // // INPUT: // An aidl interface file. // // OUTPUT: // The generated interface files. // If omitted and the -o option is not used, the input filename is used, with // the .aidl extension changed to a .java extension. // If the -o option is used, the generated files will be placed in the base // output folder, under their package folder }