Java tutorial
/* * Copyright 2013-present Facebook, Inc. * * 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.facebook.buck.java; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetPattern; import com.facebook.buck.rules.AbiRule; import com.facebook.buck.rules.AbstractBuildRuleBuilderParams; import com.facebook.buck.rules.AbstractBuildable; import com.facebook.buck.rules.BuildContext; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildRuleType; import com.facebook.buck.rules.Buildable; import com.facebook.buck.rules.BuildableContext; import com.facebook.buck.rules.OnDiskBuildInfo; import com.facebook.buck.rules.Sha1HashCode; import com.facebook.buck.step.AbstractExecutionStep; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.Step; import com.facebook.buck.step.fs.MkdirStep; import com.facebook.buck.step.fs.RmStep; import com.facebook.buck.util.BuckConstant; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.hash.HashCode; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** * {@link Buildable} that writes the list of {@code .class} files found in a zip or directory to a * file. */ public class AccumulateClassNames extends AbstractBuildable { private static final Splitter CLASS_NAME_AND_HASH_SPLITTER = Splitter .on(AccumulateClassNamesStep.CLASS_NAME_HASH_CODE_SEPARATOR); private final JavaLibraryRule javaLibraryRule; private final Path pathToOutputFile; /** * This is not set until this {@link Buildable} is built. */ @Nullable private Sha1HashCode abiKey; /** * This will contain the classes info discovered in the course of building this {@link Buildable}. * This {@link Supplier} will be defined and determined after this buildable is built. */ @VisibleForTesting @Nullable Supplier<ImmutableSortedMap<String, HashCode>> classNames; @VisibleForTesting AccumulateClassNames(BuildTarget buildTarget, JavaLibraryRule javaLibraryRule) { Preconditions.checkNotNull(buildTarget); this.javaLibraryRule = Preconditions.checkNotNull(javaLibraryRule); this.pathToOutputFile = Paths.get(BuckConstant.GEN_DIR, buildTarget.getBasePath(), buildTarget.getShortName() + ".classes.txt"); } @Override public Iterable<String> getInputsToCompareToOutput() { // The deps of this rule already capture all of the inputs that should affect the cache key. return ImmutableSortedSet.of(); } @Override public String getPathToOutputFile() { return pathToOutputFile.toString(); } public JavaLibraryRule getJavaLibraryRule() { return javaLibraryRule; } @Override public List<Step> getBuildSteps(BuildContext context, final BuildableContext buildableContext) throws IOException { ImmutableList.Builder<Step> steps = ImmutableList.builder(); steps.add(new RmStep(getPathToOutputFile(), /* shouldForceDeletion */ true)); // Make sure that the output directory exists for the output file. steps.add(new MkdirStep(pathToOutputFile.getParent())); AccumulateClassNamesStep accumulateClassNamesStep = new AccumulateClassNamesStep( Paths.get(javaLibraryRule.getPathToOutputFile()), Paths.get(getPathToOutputFile())); classNames = accumulateClassNamesStep; steps.add(accumulateClassNamesStep); AbstractExecutionStep recordAbiStep = new AbstractExecutionStep("record_abi") { @Override public int execute(ExecutionContext context) { AccumulateClassNames.this.abiKey = computeAbiKey(classNames); buildableContext.addMetadata(AbiRule.ABI_KEY_ON_DISK_METADATA, abiKey.toString()); return 0; } }; steps.add(recordAbiStep); return steps.build(); } /** * Sets both {@link #classNames} and {@link #abiKey}. */ @Override protected void initializeFromDisk(OnDiskBuildInfo onDiskBuildInfo) { // Read the output file, which should now be in place because this rule was downloaded from // cache. List<String> lines; try { lines = onDiskBuildInfo.getOutputFileContentsByLine(this); } catch (IOException e) { throw new RuntimeException(e); } // Use the contents of the file to create the ImmutableSortedMap<String, HashCode>. ImmutableSortedMap.Builder<String, HashCode> classNamesBuilder = ImmutableSortedMap.naturalOrder(); for (String line : lines) { List<String> parts = CLASS_NAME_AND_HASH_SPLITTER.splitToList(line); Preconditions.checkState(parts.size() == 2); String key = parts.get(0); HashCode value = HashCode.fromString(parts.get(1)); classNamesBuilder.put(key, value); } this.classNames = Suppliers.ofInstance(classNamesBuilder.build()); this.abiKey = onDiskBuildInfo.getHash(AbiRule.ABI_KEY_ON_DISK_METADATA).get(); } public ImmutableSortedMap<String, HashCode> getClassNames() { // TODO(mbolin): Assert that this Buildable has been built. Currently, there is no way to do // that from a Buildable (but there is from an AbstractCachingBuildRule). Preconditions.checkNotNull(classNames); return classNames.get(); } public Sha1HashCode getAbiKey() { Preconditions.checkNotNull(abiKey); return abiKey; } @VisibleForTesting static Sha1HashCode computeAbiKey(Supplier<ImmutableSortedMap<String, HashCode>> classNames) { Hasher hasher = Hashing.sha1().newHasher(); for (Map.Entry<String, HashCode> entry : classNames.get().entrySet()) { hasher.putUnencodedChars(entry.getKey()); hasher.putByte((byte) 0); hasher.putUnencodedChars(entry.getValue().toString()); hasher.putByte((byte) 0); } return new Sha1HashCode(hasher.hash().toString()); } public static Builder newAccumulateClassNamesBuilder(AbstractBuildRuleBuilderParams params) { return new Builder(params); } public static class Builder extends AbstractBuildable.Builder { private JavaLibraryRule javaLibraryRule; private Builder(AbstractBuildRuleBuilderParams params) { super(params); } @Override protected BuildRuleType getType() { return BuildRuleType._CLASS_NAMES; } @Override protected AccumulateClassNames newBuildable(BuildRuleParams params, BuildRuleResolver resolver) { return new AccumulateClassNames(params.getBuildTarget(), javaLibraryRule); } public Builder setJavaLibraryToDex(JavaLibraryRule javaLibraryRule) { this.javaLibraryRule = javaLibraryRule; this.addDep(javaLibraryRule.getBuildTarget()); return this; } @Override public Builder setBuildTarget(BuildTarget buildTarget) { super.setBuildTarget(buildTarget); return this; } @Override public Builder addDep(BuildTarget buildTarget) { super.addDep(buildTarget); return this; } @Override public Builder addVisibilityPattern(BuildTargetPattern visibilityPattern) { super.addVisibilityPattern(visibilityPattern); return this; } } }