Java tutorial
/* * Copyright (C) 2015 The Android Open Source Project * * 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.android.build.gradle.external.gnumake; import com.android.utils.SparseArray; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Analyze flow of inputs and outputs between commands (where ordering is important). * Result is a mapping from output file to input file(s). */ class FlowAnalyzer { /** * Build the flow analysis for the given set of classifications. * This tracks library files back through the {@link BuildStepInfo} call chain and * attributes source input files (.c and .cpp) to resulting library files (.so) * * @return ListMultimap where keys are the library names and values are a list of * {@link BuildStepInfo} of the source files used to create the library. */ static ListMultimap<String, List<BuildStepInfo>> analyze(String commands, boolean isWin32) { List<BuildStepInfo> commandSummaries = CommandClassifier.classify(commands, isWin32); // For each filename, record the last command that created it. Map<String, Integer> outputToCommand = new HashMap<>(); // For each command, the set of terminal inputs. ArrayList<Set<BuildStepInfo>> outputToTerminals = new ArrayList<>(); // For each command, the set of outputs that was consumed. SparseArray<Set<String>> commandOutputsConsumed = new SparseArray<>(); for (int i = 0; i < commandSummaries.size(); ++i) { BuildStepInfo current = commandSummaries.get(i); if (current.inputsAreSourceFiles()) { if (current.getInputs().size() != 1) { throw new RuntimeException(String.format( "GNUMAKE: Expected exactly one source file in compile step:" + " %s\nbut received: \n%s", current, Joiner.on("\n").join(current.getInputs()))); } } commandOutputsConsumed.put(i, new HashSet<>()); // For each input, find the line that created it or null if this is a terminal input. Set<BuildStepInfo> terminals = new HashSet<>(); for (String input : current.getInputs()) { if (outputToCommand.containsKey(input)) { int inputCommandIndex = outputToCommand.get(input); terminals.addAll(outputToTerminals.get(inputCommandIndex)); // Record this a consumed output. commandOutputsConsumed.get(inputCommandIndex).add(input); continue; } if (current.inputsAreSourceFiles()) { terminals.add(current); } } outputToTerminals.add(terminals); // Record the files output by this command for (String output : current.getOutputs()) { outputToCommand.put(output, i); } } // Emit the outputs that are never consumed. ListMultimap<String, List<BuildStepInfo>> result = ArrayListMultimap.create(); for (int i = 0; i < commandSummaries.size(); ++i) { BuildStepInfo current = commandSummaries.get(i); Set<String> outputsConsumed = commandOutputsConsumed.get(i); for (String output : current.getOutputs()) { if (!outputsConsumed.contains(output) || !current.inputsAreSourceFiles()) { // Sort the inputs List<BuildStepInfo> ordered = new ArrayList<>(); ordered.addAll(outputToTerminals.get(i)); Collections.sort(ordered, (o1, o2) -> o1.getOnlyInput().compareTo(o2.getOnlyInput())); result.put(output, ordered); } } } return result; } }