com.google.devtools.build.benchmark.BazelBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.benchmark.BazelBuilder.java

Source

// Copyright 2017 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.benchmark;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.shell.Command;
import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.shell.CommandResult;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** Class that provides all needed feature of Bazel for benchmark. */
class BazelBuilder implements Builder {

    private static final Logger logger = Logger.getLogger(BazelBuilder.class.getName());
    private static final FileSystem fileSystem = new JavaIoFileSystem();

    private static final String BAZEL_BINARY_PATH = "bazel-bin/src/bazel";
    private static final Pattern ELAPSED_TIME_PATTERN = Pattern.compile("(?<=Elapsed time: )[0-9.]+");
    private static final String DEFAULT_GIT_REPO = "https://github.com/bazelbuild/bazel.git";

    private final Path generatedCodeDir;
    private final Path builderDir;
    private Path buildBinary = null;
    private String currentCodeVersion = "";

    BazelBuilder(Path generatedCodeDir, Path builderDir) {
        this.generatedCodeDir = generatedCodeDir;
        this.builderDir = builderDir;
    }

    @Override
    public Path getBuildBinary(String codeVersion) throws IOException, CommandException {
        if (buildBinary != null && currentCodeVersion.equals(codeVersion)) {
            return buildBinary;
        }

        // git checkout codeVersion
        String[] checkoutCommand = { "git", "checkout", codeVersion };
        Command cmd = new Command(checkoutCommand, null, builderDir.toFile());
        cmd.execute();

        // bazel build src:bazel
        String[] buildBazelCommand = { "bazel", "build", "src:bazel" };
        cmd = new Command(buildBazelCommand, null, builderDir.toFile());
        CommandResult result = cmd.execute();

        // Get binary path, bazel output is in stderr
        String output = new String(result.getStderr(), UTF_8).trim();
        if (!output.contains(BAZEL_BINARY_PATH)) {
            throw new IOException("Bazel binary " + BAZEL_BINARY_PATH + " is not in output of build.");
        }
        buildBinary = builderDir.resolve(BAZEL_BINARY_PATH);
        currentCodeVersion = codeVersion;
        return buildBinary;
    }

    @Override
    public ImmutableList<String> getCommandFromConfig(BuildTargetConfig targetConfig, BuildEnvConfig envConfig) {
        return ImmutableList.<String>builder().add("build").add(targetConfig.getBuildTarget())
                .addAll(envConfig.getBuildArgsList()).build();
    }

    @Override
    public double buildAndGetElapsedTime(Path buildBinary, ImmutableList<String> args) throws CommandException {
        List<String> cmdList = new ArrayList<>();
        cmdList.add(buildBinary.toString());
        cmdList.addAll(args);
        String[] cmdArr = new String[cmdList.size()];
        cmdArr = cmdList.toArray(cmdArr);

        // Run build command
        Command cmd = new Command(cmdArr, null, generatedCodeDir.toFile());
        CommandResult result = cmd.execute();

        // Get elapsed time from output
        String output = new String(result.getStderr(), UTF_8).trim();
        Matcher m = ELAPSED_TIME_PATTERN.matcher(output);

        if (m.find()) {
            try {
                return (Double.parseDouble(m.group(0)));
            } catch (NumberFormatException e) {
                // Should not be here since we look for [0-9.]+
                logger.log(Level.SEVERE, "Cannot parse " + m.group(0));
            }
        }
        throw new CommandException(cmd, "Command didn't provide parsable output.");
    }

    @Override
    public void clean() throws CommandException {
        String[] cleanCommand = { "bazel", "clean", "--expunge" };
        Command cmd = new Command(cleanCommand, null, generatedCodeDir.toFile());
        cmd.execute();
    }

    @Override
    public void prepare() throws IOException, CommandException {
        prepareFromGitRepo(DEFAULT_GIT_REPO);
    }

    @Override
    public ImmutableList<String> getCodeVersionsBetweenVersions(VersionFilter versionFilter)
            throws CommandException {
        return getListOfOutputFromCommand("git", "log", versionFilter.getFrom() + ".." + versionFilter.getTo(),
                "--pretty=format:%H", "--reverse");
    }

    @Override
    public ImmutableList<String> getCodeVersionsBetweenDates(DateFilter dateFilter) throws CommandException {
        return getListOfOutputFromCommand("git", "log", "--after", dateFilter.getFromString(), "--before",
                dateFilter.getToString(), "--pretty=format:%H", "--reverse");
    }

    @Override
    public ImmutableList<String> getDatetimeForCodeVersions(ImmutableList<String> codeVersions)
            throws CommandException {
        return getListOfOutputFromCommandWithAdditionalParam(codeVersions, "git", "show", "-s", "--date=iso",
                "--pretty=format:%cd", "--date=format:%Y-%m-%d %H:%M:%S");
    }

    void prepareFromGitRepo(String gitRepo) throws IOException, CommandException {
        // Try to pull git repo first, delete directory if failed.
        if (builderDir.toFile().isDirectory()) {
            try {
                pullGitRepo();
            } catch (CommandException e) {
                FileSystemUtils.deleteTree(fileSystem.getPath(builderDir.toString()));
            }
        }

        if (Files.notExists(builderDir)) {
            try {
                Files.createDirectories(builderDir);
            } catch (IOException e) {
                throw new IOException("Failed to create directory for bazel", e);
            }

            String[] gitCloneCommand = { "git", "clone", gitRepo, "." };
            Command cmd = new Command(gitCloneCommand, null, builderDir.toFile());
            cmd.execute();
        }
        // Assume the directory is what we need if not empty
    }

    private void pullGitRepo() throws CommandException {
        String[] gitCloneCommand = { "git", "pull" };
        Command cmd = new Command(gitCloneCommand, null, builderDir.toFile());
        cmd.execute();
    }

    private ImmutableList<String> getListOfOutputFromCommand(String... command) throws CommandException {
        Command cmd = new Command(command, null, builderDir.toFile());
        CommandResult result = cmd.execute();
        String output = new String(result.getStdout(), UTF_8).trim();
        return ImmutableList.copyOf(output.split("\n"));
    }

    private ImmutableList<String> getListOfOutputFromCommandWithAdditionalParam(
            ImmutableList<String> additionalParam, String... command) throws CommandException {
        ImmutableList<String> commandList = ImmutableList.<String>builder().add(command).addAll(additionalParam)
                .build();
        String[] finalCommand = commandList.toArray(new String[0]);

        return getListOfOutputFromCommand(finalCommand);
    }
}