com.facebook.buck.cxx.CxxInferCapture.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.cxx.CxxInferCapture.java

Source

/*
 * Copyright 2015-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.cxx;

import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildRuleWithResolver;
import com.facebook.buck.rules.AddToRuleKey;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.RuleKeyAppendable;
import com.facebook.buck.rules.RuleKeyObjectSink;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.keys.SupportsDependencyFileRuleKey;
import com.facebook.buck.shell.DefaultShellStep;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.StepExecutionResult;
import com.facebook.buck.step.fs.MkdirStep;
import com.facebook.buck.util.Escaper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;

/**
 * Generate the CFG for a source file
 */
public class CxxInferCapture extends AbstractBuildRuleWithResolver
        implements RuleKeyAppendable, SupportsDependencyFileRuleKey {

    @AddToRuleKey
    private final InferBuckConfig inferConfig;
    private final CxxToolFlags preprocessorFlags;
    private final CxxToolFlags compilerFlags;
    @AddToRuleKey
    private final SourcePath input;
    private final CxxSource.Type inputType;
    @AddToRuleKey(stringify = true)
    private final Path output;
    @AddToRuleKey
    private final PreprocessorDelegate preprocessorDelegate;

    private final Path resultsDir;
    private final DebugPathSanitizer sanitizer;

    CxxInferCapture(BuildRuleParams buildRuleParams, SourcePathResolver pathResolver,
            CxxToolFlags preprocessorFlags, CxxToolFlags compilerFlags, SourcePath input,
            AbstractCxxSource.Type inputType, Path output, PreprocessorDelegate preprocessorDelegate,
            InferBuckConfig inferConfig, DebugPathSanitizer sanitizer) {
        super(buildRuleParams, pathResolver);
        this.preprocessorFlags = preprocessorFlags;
        this.compilerFlags = compilerFlags;
        this.input = input;
        this.inputType = inputType;
        this.output = output;
        this.preprocessorDelegate = preprocessorDelegate;
        this.inferConfig = inferConfig;
        this.resultsDir = BuildTargets.getGenPath(getProjectFilesystem(), this.getBuildTarget(), "infer-out-%s");
        this.sanitizer = sanitizer;
    }

    private CxxToolFlags getSearchPathFlags() {
        return preprocessorDelegate.getFlagsWithSearchPaths(/* no pch */ Optional.empty());
    }

    private ImmutableList<String> getFrontendCommand() {
        ImmutableList.Builder<String> commandBuilder = ImmutableList.builder();
        return commandBuilder.add(this.inferConfig.getInferTopLevel().toString()).add("-a", "capture")
                .add("--project_root", getProjectFilesystem().getRootPath().toString())
                .add("--out", resultsDir.toString()).add("--").add("clang").add("@" + getArgfile()).build();
    }

    @Override
    public ImmutableList<Step> getBuildSteps(BuildContext context, BuildableContext buildableContext) {
        ImmutableList<String> frontendCommand = getFrontendCommand();

        buildableContext.recordArtifact(this.getPathToOutput());

        Path inputRelativePath = context.getSourcePathResolver().getRelativePath(input);
        return ImmutableList.<Step>builder().add(new MkdirStep(getProjectFilesystem(), resultsDir))
                .add(new MkdirStep(getProjectFilesystem(), output.getParent()))
                .add(new WriteArgFileStep(inputRelativePath))
                .add(new DefaultShellStep(getProjectFilesystem().getRootPath(), frontendCommand, ImmutableMap.of()))
                .add(new ParseAndWriteBuckCompatibleDepfileStep(getTempDepFilePath(), getDepFilePath(),
                        inputRelativePath))
                .build();
    }

    @Override
    public Path getPathToOutput() {
        return this.resultsDir;
    }

    public Path getAbsolutePathToOutput() {
        return getProjectFilesystem().resolve(resultsDir);
    }

    @Override
    public void appendToRuleKey(RuleKeyObjectSink sink) {
        // Sanitize any relevant paths in the flags we pass to the preprocessor, to prevent them
        // from contributing to the rule key.
        sink.setReflectively("platformPreprocessorFlags",
                sanitizer.sanitizeFlags(preprocessorFlags.getPlatformFlags()))
                .setReflectively("rulePreprocessorFlags", sanitizer.sanitizeFlags(preprocessorFlags.getRuleFlags()))
                .setReflectively("platformCompilerFlags", sanitizer.sanitizeFlags(compilerFlags.getPlatformFlags()))
                .setReflectively("ruleCompilerFlags", sanitizer.sanitizeFlags(compilerFlags.getRuleFlags()));
    }

    @Override
    public boolean useDependencyFileRuleKeys() {
        return true;
    }

    @Override
    public Optional<ImmutableSet<SourcePath>> getPossibleInputSourcePaths() {
        return preprocessorDelegate.getPossibleInputSourcePaths();
    }

    @Override
    public ImmutableList<SourcePath> getInputsAfterBuildingLocally() throws IOException {
        ImmutableList.Builder<SourcePath> inputs = ImmutableList.builder();

        // include all inputs coming from the preprocessor tool.
        inputs.addAll(preprocessorDelegate.getInputsAfterBuildingLocally(readDepFileLines()));

        // Add the input.
        inputs.add(input);

        return inputs.build();
    }

    private Path getArgfile() {
        return output.getFileSystem().getPath(output.getParent().resolve("infer-capture.argsfile").toString());
    }

    private Path getDepFilePath() {
        return output.getFileSystem().getPath(output.toString() + ".dep");
    }

    private Path getTempDepFilePath() {
        return output.getFileSystem().getPath(getDepFilePath().toString() + ".tmp.dep");
    }

    private ImmutableList<String> readDepFileLines() throws IOException {
        return ImmutableList.copyOf(getProjectFilesystem().readLines(getDepFilePath()));
    }

    private class ParseAndWriteBuckCompatibleDepfileStep implements Step {

        private Path sourceDepfile;
        private Path destDepfile;
        private Path inputRelativePath;

        public ParseAndWriteBuckCompatibleDepfileStep(Path sourceDepfile, Path destDepfile,
                Path inputRelativePath) {
            this.sourceDepfile = sourceDepfile;
            this.destDepfile = destDepfile;
            this.inputRelativePath = inputRelativePath;
        }

        @Override
        public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException {
            Depfiles.parseAndWriteBuckCompatibleDepfile(context, getProjectFilesystem(),
                    preprocessorDelegate.getHeaderPathNormalizer(), preprocessorDelegate.getHeaderVerification(),
                    sourceDepfile, destDepfile, inputRelativePath, output);
            return StepExecutionResult.SUCCESS;
        }

        @Override
        public String getShortName() {
            return "depfile-parse";
        }

        @Override
        public String getDescription(ExecutionContext context) {
            return "Parse depfiles and write them in a Buck compatible format";
        }
    }

    private class WriteArgFileStep implements Step {

        private final Path inputRelativePath;

        public WriteArgFileStep(Path inputRelativePath) {
            this.inputRelativePath = inputRelativePath;
        }

        @Override
        public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException {
            getProjectFilesystem().writeLinesToPath(Iterables.transform(getCompilerArgs(), Escaper.ARGFILE_ESCAPER),
                    getArgfile());
            return StepExecutionResult.SUCCESS;
        }

        @Override
        public String getShortName() {
            return "write-argfile";
        }

        @Override
        public String getDescription(ExecutionContext context) {
            return "Write argfile for clang";
        }

        private ImmutableList<String> getCompilerArgs() {
            ImmutableList.Builder<String> commandBuilder = ImmutableList.builder();
            return commandBuilder.add("-MD", "-MF", getTempDepFilePath().toString())
                    .addAll(CxxToolFlags.concat(preprocessorFlags, getSearchPathFlags(), compilerFlags)
                            .getAllFlags())
                    .add("-x", inputType.getLanguage()).add("-o", output.toString()) // TODO(martinoluca): Use -fsyntax-only for better perf
                    .add("-c").add(inputRelativePath.toString()).build();
        }
    }
}