com.blackducksoftware.integration.hub.detect.detector.bitbake.BitbakeExtractor.java Source code

Java tutorial

Introduction

Here is the source code for com.blackducksoftware.integration.hub.detect.detector.bitbake.BitbakeExtractor.java

Source

/**
 * hub-detect
 *
 * Copyright (C) 2019 Black Duck Software, Inc.
 * http://www.blackducksoftware.com/
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.blackducksoftware.integration.hub.detect.detector.bitbake;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blackducksoftware.integration.hub.detect.detector.ExtractionId;
import com.blackducksoftware.integration.hub.detect.util.executable.Executable;
import com.blackducksoftware.integration.hub.detect.util.executable.ExecutableOutput;
import com.blackducksoftware.integration.hub.detect.util.executable.ExecutableRunner;
import com.blackducksoftware.integration.hub.detect.util.executable.ExecutableRunnerException;
import com.blackducksoftware.integration.hub.detect.workflow.codelocation.DetectCodeLocation;
import com.blackducksoftware.integration.hub.detect.workflow.codelocation.DetectCodeLocationType;
import com.blackducksoftware.integration.hub.detect.workflow.extraction.Extraction;
import com.blackducksoftware.integration.hub.detect.workflow.file.DetectFileFinder;
import com.blackducksoftware.integration.hub.detect.workflow.file.DirectoryManager;
import com.paypal.digraph.parser.GraphParser;
import com.synopsys.integration.bdio.graph.DependencyGraph;
import com.synopsys.integration.bdio.model.Forge;
import com.synopsys.integration.bdio.model.externalid.ExternalId;
import com.synopsys.integration.exception.IntegrationException;

public class BitbakeExtractor {
    public static final String RECIPE_DEPENDS_FILE_NAME = "recipe-depends.dot";

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final ExecutableRunner executableRunner;
    private final DetectFileFinder detectFileFinder;
    private final DirectoryManager directoryManager;
    private final GraphParserTransformer graphParserTransformer;
    private final BitbakeListTasksParser bitbakeListTasksParser;

    public BitbakeExtractor(final ExecutableRunner executableRunner, final DirectoryManager directoryManager,
            final DetectFileFinder detectFileFinder, final GraphParserTransformer graphParserTransformer,
            final BitbakeListTasksParser bitbakeListTasksParser) {
        this.executableRunner = executableRunner;
        this.directoryManager = directoryManager;
        this.detectFileFinder = detectFileFinder;
        this.graphParserTransformer = graphParserTransformer;
        this.bitbakeListTasksParser = bitbakeListTasksParser;
    }

    public Extraction extract(final ExtractionId extractionId, final File buildEnvScript, final File sourcePath,
            String[] packageNames, File bash) {
        final File outputDirectory = directoryManager.getExtractionOutputDirectory(extractionId);
        final File bitbakeBuildDirectory = new File(outputDirectory, "build");

        final List<DetectCodeLocation> detectCodeLocations = new ArrayList<>();
        for (final String packageName : packageNames) {
            final File dependsFile = executeBitbakeForRecipeDependsFile(outputDirectory, bitbakeBuildDirectory,
                    buildEnvScript, packageName, bash);
            final String targetArchitecture = executeBitbakeForTargetArchitecture(outputDirectory, buildEnvScript,
                    packageName, bash);

            try {
                if (dependsFile == null) {
                    throw new IntegrationException(String.format(
                            "Failed to find %s. This may be due to this project being a version of The Yocto Project earlier than 2.3 (Pyro) which is the minimum version for Detect",
                            RECIPE_DEPENDS_FILE_NAME));
                }
                if (StringUtils.isBlank(targetArchitecture)) {
                    throw new IntegrationException("Failed to find a target architecture");
                }

                logger.debug(FileUtils.readFileToString(dependsFile, Charset.defaultCharset()));
                final InputStream recipeDependsInputStream = FileUtils.openInputStream(dependsFile);
                final GraphParser graphParser = new GraphParser(recipeDependsInputStream);
                final DependencyGraph dependencyGraph = graphParserTransformer.transform(graphParser,
                        targetArchitecture);
                final ExternalId externalId = new ExternalId(Forge.YOCTO);
                final DetectCodeLocation detectCodeLocation = new DetectCodeLocation.Builder(
                        DetectCodeLocationType.BITBAKE, sourcePath.getCanonicalPath(), externalId, dependencyGraph)
                                .build();

                detectCodeLocations.add(detectCodeLocation);
            } catch (final IOException | IntegrationException | NullPointerException e) {
                logger.error(String.format(
                        "Failed to extract a Code Location while running Bitbake against package '%s'",
                        packageName));
                logger.debug(e.getMessage(), e);
            }
        }

        final Extraction extraction;

        if (detectCodeLocations.isEmpty()) {
            extraction = new Extraction.Builder().failure("No Code Locations were generated during extraction")
                    .build();
        } else {
            extraction = new Extraction.Builder().success(detectCodeLocations).build();
        }

        return extraction;
    }

    private File executeBitbakeForRecipeDependsFile(final File outputDirectory, final File bitbakeBuildDirectory,
            final File buildEnvScript, final String packageName, File bash) {
        final String bitbakeCommand = "bitbake -g " + packageName;
        final ExecutableOutput executableOutput = runBitbake(outputDirectory, buildEnvScript, bitbakeCommand, bash);
        final int returnCode = executableOutput.getReturnCode();
        File recipeDependsFile = null;

        if (returnCode == 0) {
            recipeDependsFile = detectFileFinder.findFile(bitbakeBuildDirectory, RECIPE_DEPENDS_FILE_NAME);
        } else {
            logger.error(String.format("Executing command '%s' returned a non-zero exit code %s", bitbakeCommand,
                    returnCode));
        }

        return recipeDependsFile;
    }

    private String executeBitbakeForTargetArchitecture(final File outputDirectory, final File buildEnvScript,
            final String packageName, File bash) {
        final String bitbakeCommand = "bitbake -c listtasks " + packageName;
        final ExecutableOutput executableOutput = runBitbake(outputDirectory, buildEnvScript, bitbakeCommand, bash);
        final int returnCode = executableOutput.getReturnCode();
        String targetArchitecture = null;

        if (returnCode == 0) {
            targetArchitecture = bitbakeListTasksParser
                    .parseTargetArchitecture(executableOutput.getStandardOutput()).orElse(null);
        } else {
            logger.error(String.format("Executing command '%s' returned a non-zero exit code %s", bitbakeCommand,
                    returnCode));
        }

        return targetArchitecture;
    }

    private ExecutableOutput runBitbake(final File outputDirectory, final File buildEnvScript,
            final String bitbakeCommand, File bash) {

        final List<String> arguments = new ArrayList<>();
        arguments.add("-c");
        arguments.add(". " + buildEnvScript + "; " + bitbakeCommand);
        final Executable sourceExecutable = new Executable(outputDirectory, bash, arguments);
        ExecutableOutput executableOutput = null;

        try {
            executableOutput = executableRunner.execute(sourceExecutable);
        } catch (final ExecutableRunnerException e) {
            logger.error(
                    String.format("Failed executing command '%s'", sourceExecutable.getExecutableDescription()));
            logger.debug(e.getMessage(), e);
        }

        return executableOutput;
    }
}