All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.android.compiler.tools.AndroidApt Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition android-common library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2012 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jetbrains.android.compiler.tools;

import com.android.SdkConstants;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.build.SymbolLoader;
import com.android.sdklib.internal.build.SymbolWriter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.execution.ParametersListUtil;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidCompilerMessageKind;
import org.jetbrains.android.util.AndroidExecutionUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.*;

/**
 * AndroidApt decorator.
 *
 * @author Alexey Efimov
 */
public final class AndroidApt {
  private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.tools.AndroidApt");

  @NonNls private static final String COMMAND_CRUNCH = "crunch";
  @NonNls private static final String COMMAND_PACKAGE = "package";

  private static final FileFilter PNG_FILES_FILTER = new FileFilter() {
    @Override
    public boolean accept(File file) {
      return file.isDirectory() || FileUtilRt.extensionEquals(file.getName(), AndroidCommonUtils.PNG_EXTENSION);
    }
  };

  private AndroidApt() {
  }

  public static Map> compile(@NotNull IAndroidTarget target,
                                                                      int platformToolsRevision,
                                                                      @NotNull String manifestFileOsPath,
                                                                      @NotNull String aPackage,
                                                                      @NotNull String outDirOsPath,
                                                                      @NotNull String[] resourceDirsOsPaths,
                                                                      @NotNull String[] extraPackages,
                                                                      boolean nonConstantFields,
                                                                      @Nullable String proguardCfgOutputFileOsPath) throws IOException {
    final List> libRTxtFilesAndPackages = new ArrayList>();

    for (String extraPackage : extraPackages) {
      libRTxtFilesAndPackages.add(Pair.create((String)null, extraPackage));
    }
    return compile(target, platformToolsRevision, manifestFileOsPath, aPackage, outDirOsPath, resourceDirsOsPaths,
                   libRTxtFilesAndPackages, nonConstantFields, proguardCfgOutputFileOsPath, null, false);
  }

  public static Map> compile(@NotNull IAndroidTarget target,
                                                                      int platformToolsRevision,
                                                                      @NotNull String manifestFileOsPath,
                                                                      @NotNull String aPackage,
                                                                      @NotNull String outDirOsPath,
                                                                      @NotNull String[] resourceDirsOsPaths,
                                                                      @NotNull List> libRTxtFilesAndPackages,
                                                                      boolean nonConstantFields,
                                                                      @Nullable String proguardCfgOutputFileOsPath,
                                                                      @Nullable String rTxtOutDirOsPath,
                                                                      boolean optimizeRFile) throws IOException {
    final Map> messages = new HashMap>();
    messages.put(AndroidCompilerMessageKind.ERROR, new ArrayList());
    messages.put(AndroidCompilerMessageKind.INFORMATION, new ArrayList());

    final File outOsDir = new File(outDirOsPath);
    if (!outOsDir.exists()) {
      if (!outOsDir.mkdirs()) {
        messages.get(AndroidCompilerMessageKind.ERROR).add("Unable to create directory " + outDirOsPath);
      }
    }

    final String packageFolderOsPath = FileUtil.toSystemDependentName(outDirOsPath + '/' + aPackage.replace('.', '/'));

    /* We actually need to delete the manifest.java as it may become empty and
    in this case aapt doesn't generate an empty one, but instead doesn't
    touch it */
    final File manifestJavaFile = new File(packageFolderOsPath + File.separatorChar + AndroidCommonUtils.MANIFEST_JAVA_FILE_NAME);
    if (manifestJavaFile.exists()) {
      if (!FileUtil.delete(manifestJavaFile)) {
        messages.get(AndroidCompilerMessageKind.ERROR).add("Unable to delete " + manifestJavaFile.getPath());
      }
    }

    final File rJavaFile = new File(packageFolderOsPath + File.separatorChar + AndroidCommonUtils.R_JAVA_FILENAME);
    if (rJavaFile.exists()) {
      if (!FileUtil.delete(rJavaFile)) {
        messages.get(AndroidCompilerMessageKind.ERROR).add("Unable to delete " + rJavaFile.getPath());
      }
    }

    final File[] extraRJavaFiles = new File[libRTxtFilesAndPackages.size()];

    for (int i = 0, n = libRTxtFilesAndPackages.size(); i < n; i++) {
      final String libPackage = libRTxtFilesAndPackages.get(i).getSecond();
      final String libPackageFolderOsPath = FileUtil.toSystemDependentName(outDirOsPath + '/' + libPackage.replace('.', '/'));
      extraRJavaFiles[i] = new File(libPackageFolderOsPath + File.separatorChar + AndroidCommonUtils.R_JAVA_FILENAME);
    }

    for (File extraRJavaFile : extraRJavaFiles) {
      if (extraRJavaFile.exists()) {
        if (!FileUtil.delete(extraRJavaFile)) {
          messages.get(AndroidCompilerMessageKind.ERROR).add("Unable to delete " + extraRJavaFile.getPath());
        }
      }
    }

    if (platformToolsRevision < 0 || platformToolsRevision > 7) {
      Map> map =
        doCompile(target, manifestFileOsPath, outDirOsPath, resourceDirsOsPaths, libRTxtFilesAndPackages,
                  null, nonConstantFields, proguardCfgOutputFileOsPath, rTxtOutDirOsPath, optimizeRFile);

      if (map.get(AndroidCompilerMessageKind.ERROR).isEmpty()) {
        makeFieldsNotFinal(extraRJavaFiles);
      }

      AndroidExecutionUtil.addMessages(messages, map);
      return messages;
    }
    else {
      messages.get(AndroidCompilerMessageKind.ERROR).add(
        "'Platform Tools' package is out of date. Please update it through Android SDK manager");
      return messages;
    }
  }

  private static void makeFieldsNotFinal(@NotNull File[] libRJavaFiles) throws IOException {
    for (File file : libRJavaFiles) {
      if (file.isFile()) {
        final String fileContent = AndroidCommonUtils.readFile(file);
        FileUtil.writeToFile(file, fileContent.replace("public static final int ", "public static int "));
      }
    }
  }

  private static Map> doCompile(@NotNull IAndroidTarget target,
                                                                         @NotNull String manifestFileOsPath,
                                                                         @NotNull String outDirOsPath,
                                                                         @NotNull String[] resourceDirsOsPaths,
                                                                         @NotNull List> libRTxtFilesAndPackages,
                                                                         @Nullable String customPackage,
                                                                         boolean nonConstantIds,
                                                                         @Nullable String proguardCfgOutputFileOsPath,
                                                                         @Nullable String rTxtOutDirOsPath,
                                                                         boolean optimizeRFile)
    throws IOException {
    final List args = new ArrayList();

    BuildToolInfo buildToolInfo = target.getBuildToolInfo();
    if (buildToolInfo == null) {
      return Collections.singletonMap(AndroidCompilerMessageKind.ERROR, Collections.singletonList("No Build Tools in the Android SDK."));
    }

    args.add(buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));
    args.add("package");
    args.add("-m");

    if (nonConstantIds) {
      args.add("--non-constant-id");
    }

    if (resourceDirsOsPaths.length > 1) {
      args.add("--auto-add-overlay");
    }
    final Set extraPackages = new HashSet();

    for (Pair pair : libRTxtFilesAndPackages) {
      extraPackages.add(pair.getSecond());
    }
    if (extraPackages.size() > 0) {
      args.add("--extra-packages");
      args.add(toPackagesString(ArrayUtil.toStringArray(extraPackages)));
    }

    if (customPackage != null) {
      args.add("--custom-package");
      args.add(customPackage);
    }

    if (rTxtOutDirOsPath != null) {
      args.add("--output-text-symbols");
      args.add(rTxtOutDirOsPath);
    }
    args.add("-J");
    args.add(outDirOsPath);
    args.add("-M");
    args.add(manifestFileOsPath);

    for (String libResFolderOsPath : resourceDirsOsPaths) {
      args.add("-S");
      args.add(libResFolderOsPath);
    }

    args.add("-I");
    args.add(target.getPath(IAndroidTarget.ANDROID_JAR));

    if (proguardCfgOutputFileOsPath != null) {
      args.add("-G");
      args.add(proguardCfgOutputFileOsPath);
    }
    final Map> messages = AndroidExecutionUtil.doExecute(ArrayUtil.toStringArray(args));
    LOG.info(AndroidCommonUtils.command2string(args));

    if (messages.get(AndroidCompilerMessageKind.ERROR).size() > 0) {
      return messages;
    }

    if (optimizeRFile && !libRTxtFilesAndPackages.isEmpty() && rTxtOutDirOsPath != null) {
      final File rFile = new File(rTxtOutDirOsPath, SdkConstants.FN_RESOURCE_TEXT);
      // if the project has no resources the file could not exist.
      if (rFile.isFile()) {
        final SymbolLoader fullSymbolValues = new SymbolLoader(rFile);
        fullSymbolValues.load();
        final MultiMap libMap = new MultiMap();

        for (Pair pair : libRTxtFilesAndPackages) {
          final File rTextFile = new File(pair.getFirst());
          final String libPackage = pair.getSecond();

          if (rTextFile.isFile()) {
            final SymbolLoader libSymbols = new SymbolLoader(rTextFile);
            libSymbols.load();
            libMap.putValue(libPackage, libSymbols);
          }
        }

        for (Map.Entry> entry : libMap.entrySet()) {
          final String libPackage = entry.getKey();
          final Collection symbols = entry.getValue();
          final SymbolWriter writer = new SymbolWriter(outDirOsPath, libPackage, fullSymbolValues);

          for (SymbolLoader symbolLoader : symbols) {
            writer.addSymbolsToWrite(symbolLoader);
          }
          writer.write();
        }
      }
    }
    return messages;
  }

  @NotNull
  private static String toPackagesString(@NotNull String[] packages) {
    final StringBuilder builder = new StringBuilder();
    for (int i = 0, n = packages.length; i < n; i++) {
      if (i > 0) {
        builder.append(':');
      }
      builder.append(packages[i]);
    }
    return builder.toString();
  }

  public static Map> crunch(@NotNull IAndroidTarget target,
                                                                     @NotNull List resPaths,
                                                                     @NotNull String outputPath) throws IOException {
    BuildToolInfo buildToolInfo = target.getBuildToolInfo();
    if (buildToolInfo == null) {
      return Collections.singletonMap(AndroidCompilerMessageKind.ERROR, Collections.singletonList("No Build Tools in the Android SDK."));
    }

    final ArrayList args = new ArrayList();

    args.add(buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));

    args.add(COMMAND_CRUNCH);
    File tempDir = null;
    try {
      if (resPaths.size() > 0) {
        if (resPaths.size() == 1) {
          args.add("-S");
          args.add(resPaths.get(0));
        }
        else {
          tempDir = FileUtil.createTempDirectory("android_combined_resources", "tmp");

          for (int i = resPaths.size() - 1; i >= 0; i--) {
            final String resDirPath = resPaths.get(i);
            final File resDir = new File(resDirPath);

            if (resDir.exists()) {
              FileUtil.copyDir(resDir, tempDir, PNG_FILES_FILTER);
            }
          }
          args.add("-S");
          args.add(tempDir.getPath());
        }
      }

      args.add("-C");
      args.add(outputPath);

      LOG.info(AndroidCommonUtils.command2string(args));
      return AndroidExecutionUtil.doExecute(ArrayUtil.toStringArray(args));
    }
    finally {
      if (tempDir != null) {
        FileUtil.delete(tempDir);
      }
    }
  }

  public static Map> packageResources(@NotNull IAndroidTarget target,
                                                                               int platformToolsRevision,
                                                                               @NotNull String manifestPath,
                                                                               @NotNull String[] resPaths,
                                                                               @NotNull String[] osAssetDirPaths,
                                                                               @NotNull String outputPath,
                                                                               @Nullable String configFilter,
                                                                               boolean debugMode,
                                                                               int versionCode,
                                                                               @Nullable String customManifestPackage,
                                                                               @Nullable String additionalParameters,
                                                                               FileFilter assetsFilter) throws IOException {
    for (String resDirPath : resPaths) {
      if (FileUtil.isAncestor(resDirPath, outputPath, false)) {
        throw new IOException("Resource directory " +
                              FileUtil.toSystemDependentName(resDirPath) +
                              " contains output " +
                              FileUtil.toSystemDependentName(outputPath));
      }
    }

    for (String assetsDirPath : osAssetDirPaths) {
      if (FileUtil.isAncestor(assetsDirPath, outputPath, false)) {
        throw new IOException("Assets directory " +
                              FileUtil.toSystemDependentName(assetsDirPath) +
                              " contains output " +
                              FileUtil.toSystemDependentName(outputPath));
      }
    }

    BuildToolInfo buildToolInfo = target.getBuildToolInfo();
    if (buildToolInfo == null) {
      return Collections.singletonMap(AndroidCompilerMessageKind.ERROR, Collections.singletonList("No Build Tools in the Android SDK."));
    }

    final ArrayList args = new ArrayList();

    args.add(buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));

    args.add(COMMAND_PACKAGE);

    for (String path : resPaths) {
      args.add("-S");
      args.add(path);
    }

    args.add("-f");

    if (platformToolsRevision < 0 || platformToolsRevision > 7) {
      args.add("--no-crunch");
    }

    if (resPaths.length > 1) {
      args.add("--auto-add-overlay");
    }

    if (debugMode) {
      args.add("--debug-mode");
    }

    if (versionCode > 0) {
      args.add("--version-code");
      args.add(Integer.toString(versionCode));
    }

    if (configFilter != null) {
      args.add("-c");
      args.add(configFilter);
    }
    args.add("-M");
    args.add(manifestPath);

    File tempDir = null;
    try {
      if (osAssetDirPaths.length > 0) {
        final String[] nonEmptyAssetDirs = getNonEmptyExistingDirs(osAssetDirPaths);

        if (nonEmptyAssetDirs.length > 0) {
          if (nonEmptyAssetDirs.length == 1) {
            args.add("-A");
            args.add(nonEmptyAssetDirs[0]);
          }
          else {
            tempDir = FileUtil.createTempDirectory("android_combined_assets", "tmp");
            for (int i = nonEmptyAssetDirs.length - 1; i >= 0; i--) {
              final String assetDir = nonEmptyAssetDirs[i];
              FileUtil.copyDir(new File(assetDir), tempDir, assetsFilter);
            }
            args.add("-A");
            args.add(tempDir.getPath());
          }
        }
      }

      args.add("-I");
      args.add(target.getPath(IAndroidTarget.ANDROID_JAR));

      if (customManifestPackage != null) {
        args.add("--rename-manifest-package");
        args.add(customManifestPackage);
      }
      if (additionalParameters != null && additionalParameters.length() > 0) {
        args.addAll(ParametersListUtil.parse(additionalParameters));
      }
      args.add("-F");
      args.add(outputPath);
      LOG.info(AndroidCommonUtils.command2string(args));
      return AndroidExecutionUtil.doExecute(ArrayUtil.toStringArray(args));
    }
    finally {
      if (tempDir != null) {
        FileUtil.delete(tempDir);
      }
    }
  }

  @NotNull
  private static String[] getNonEmptyExistingDirs(@NotNull String[] dirs) {
    final List result = new ArrayList();
    for (String dirPath : dirs) {
      final File dir = new File(dirPath);

      if (dir.isDirectory()) {
        final File[] children = dir.listFiles();

        if (children != null && children.length > 0) {
          result.add(dirPath);
        }
      }
    }
    return ArrayUtil.toStringArray(result);
  }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy