com.google.devtools.build.android.RClassGeneratorAction.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.android.RClassGeneratorAction.java

Source

// Copyright 2016 The Bazel Authors. All rights reserved.
//
// 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 com.google.devtools.build.android;

import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.devtools.build.android.Converters.DependencySymbolFileProviderListConverter;
import com.google.devtools.build.android.Converters.PathConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;

import com.android.builder.core.VariantConfiguration;
import com.android.builder.dependency.SymbolFileProvider;
import com.android.builder.internal.SymbolLoader;
import com.android.utils.StdLogger;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

/**
 * Provides an entry point for the compiling resource classes using a custom compiler (simply parse
 * R.txt and make a jar, which is simpler than parsing R.java and running errorprone, etc.).
 *
 * For now, we assume this is only worthwhile for android_binary and not libraries.
 *
 * <pre>
 * Example Usage:
 *   java/com/google/build/android/RClassGeneratorAction\
 *      --primaryRTxt path/to/R.txt\
 *      --primaryManifest path/to/AndroidManifest.xml\
 *      --libraries p/t/1/AndroidManifest.txt:p/t/1/R.txt,\
 *                  p/t/2/AndroidManifest.txt:p/t/2/R.txt\
 *      --classJarOutput path/to/write/archive_resources.jar
 * </pre>
 */
public class RClassGeneratorAction {

    private static final StdLogger STD_LOGGER = new StdLogger(StdLogger.Level.WARNING);

    private static final Logger LOGGER = Logger.getLogger(RClassGeneratorAction.class.getName());

    /**
     * Flag specifications for this action.
     */
    public static final class Options extends OptionsBase {

        @Option(name = "primaryRTxt", defaultValue = "null", converter = PathConverter.class, category = "input", help = "The path to the binary's R.txt file")
        public Path primaryRTxt;

        @Option(name = "primaryManifest", defaultValue = "null", converter = PathConverter.class, category = "input", help = "The path to the binary's AndroidManifest.xml file. This helps provide the package.")
        public Path primaryManifest;

        @Option(name = "packageForR", defaultValue = "null", category = "config", help = "Custom java package to generate the R class files.")
        public String packageForR;

        @Option(name = "libraries", defaultValue = "", converter = DependencySymbolFileProviderListConverter.class, category = "input", help = "R.txt and manifests for the libraries in this binary's deps. We will write "
                + "class files for the libraries as well. Expected format: lib1/R.txt[:lib2/R.txt]")
        public List<DependencySymbolFileProvider> libraries;

        @Option(name = "classJarOutput", defaultValue = "null", converter = PathConverter.class, category = "output", help = "Path for the generated jar of R.class files.")
        public Path classJarOutput;

    }

    public static void main(String[] args) throws Exception {
        final Stopwatch timer = Stopwatch.createStarted();
        OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
        if (args.length == 1 && args[0].startsWith("@")) {
            args = Files.readAllLines(Paths.get(args[0].substring(1)), StandardCharsets.UTF_8)
                    .toArray(new String[0]);
        }

        optionsParser.parseAndExitUponError(args);
        Options options = optionsParser.getOptions(Options.class);
        Preconditions.checkNotNull(options.classJarOutput);
        final AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(STD_LOGGER);
        try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("android_res_compile_tmp")) {
            Path tmp = scopedTmp.getPath();
            Path classOutPath = tmp.resolve("compiled_classes");

            LOGGER.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
            List<SymbolFileProvider> libraries = new ArrayList<>();
            for (DependencySymbolFileProvider library : options.libraries) {
                libraries.add(library);
            }
            // Note that we need to write the R class for the main binary (so proceed even if there
            // are no libraries).
            if (options.primaryRTxt != null) {
                String appPackageName = options.packageForR;
                if (appPackageName == null) {
                    appPackageName = VariantConfiguration.getManifestPackage(options.primaryManifest.toFile());
                }
                Multimap<String, SymbolLoader> libSymbolMap = ArrayListMultimap.create();
                SymbolLoader fullSymbolValues = resourceProcessor.loadResourceSymbolTable(libraries, appPackageName,
                        options.primaryRTxt, libSymbolMap);
                LOGGER.fine(String.format("Load symbols finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
                // For now, assuming not used for libraries and setting final access for fields.
                if (fullSymbolValues != null) {
                    resourceProcessor.writePackageRClasses(libSymbolMap, fullSymbolValues, appPackageName,
                            classOutPath, true /* finalFields */);
                    LOGGER.fine(String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
                }
            } else {
                Files.createDirectories(classOutPath);
            }
            // We write .class files to temp, then jar them up after (we create a dummy jar, even if
            // there are no class files).
            resourceProcessor.createClassJar(classOutPath, options.classJarOutput);
            LOGGER.fine(String.format("createClassJar finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
        } finally {
            resourceProcessor.shutdown();
        }
        LOGGER.fine(String.format("Compile action done in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
    }
}