Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.synopsys.integration.detectable.detectable.executable.resolver;

import com.synopsys.integration.detectable.ExecutableTarget;
import com.synopsys.integration.detectable.detectable.exception.DetectableException;

public interface MakeResolver {
ExecutableTarget resolveMake() throws DetectableException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.synopsys.integration.detectable.detectables.buildroot;

public enum BuildrootDependencyType {
HOST
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.synopsys.integration.detectable.detectables.buildroot;

import com.synopsys.integration.bdio.graph.builder.MissingExternalIdException;
import com.synopsys.integration.common.util.finder.FileFinder;
import com.synopsys.integration.detectable.Detectable;
import com.synopsys.integration.detectable.DetectableEnvironment;
import com.synopsys.integration.detectable.ExecutableTarget;
import com.synopsys.integration.detectable.detectable.DetectableAccuracyType;
import com.synopsys.integration.detectable.detectable.Requirements;
import com.synopsys.integration.detectable.detectable.annotation.DetectableInfo;
import com.synopsys.integration.detectable.detectable.exception.DetectableException;
import com.synopsys.integration.detectable.detectable.executable.ExecutableFailedException;
import com.synopsys.integration.detectable.detectable.executable.resolver.MakeResolver;
import com.synopsys.integration.detectable.detectable.result.DetectableResult;
import com.synopsys.integration.detectable.extraction.Extraction;
import com.synopsys.integration.detectable.extraction.ExtractionEnvironment;
import com.synopsys.integration.executable.ExecutableRunnerException;

@DetectableInfo(name = "Buildroot", language = "various", forge = "Buildroot", accuracy = DetectableAccuracyType.HIGH, requirementsMarkdown = "Files: .confg, Makefile. Executable: make.")
public class BuildrootDetectable extends Detectable {
public static final String CONFIG_FILENAME = ".config";
public static final String MAKEFILE_FILENAME = "Makefile";

private final FileFinder fileFinder;
private final BuildrootExtractor buildrootExtractor;
private final MakeResolver makeResolver;

private ExecutableTarget makeExe;

public BuildrootDetectable(DetectableEnvironment environment, FileFinder fileFinder, BuildrootExtractor buildrootExtractor, MakeResolver makeResolver) {
super(environment);

this.fileFinder = fileFinder;
this.buildrootExtractor = buildrootExtractor;
this.makeResolver = makeResolver;
}

@Override
public DetectableResult applicable() {
Requirements requirements = new Requirements(fileFinder, environment);
requirements.file(CONFIG_FILENAME);
requirements.file(MAKEFILE_FILENAME);
return requirements.result();
}

@Override
public DetectableResult extractable() throws DetectableException {
Requirements requirements = new Requirements(fileFinder, environment);
makeExe = requirements.executable(makeResolver::resolveMake, "make");
return requirements.result();
}

@Override
public Extraction extract(ExtractionEnvironment extractionEnvironment) throws ExecutableRunnerException, MissingExternalIdException, ExecutableFailedException {
return buildrootExtractor.extract(makeExe, environment.getDirectory());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.synopsys.integration.detectable.detectables.buildroot;

import com.synopsys.integration.detectable.detectable.util.EnumListFilter;

public class BuildrootDetectableOptions {
private final EnumListFilter<BuildrootDependencyType> dependencyTypeFilter;

public BuildrootDetectableOptions(EnumListFilter<BuildrootDependencyType> dependencyTypeFilter) {
this.dependencyTypeFilter = dependencyTypeFilter;
}

public EnumListFilter<BuildrootDependencyType> getDependencyTypeFilter() {
return dependencyTypeFilter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.synopsys.integration.detectable.detectables.buildroot;

import java.io.File;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonParseException;
import com.synopsys.integration.bdio.graph.builder.LazyExternalIdDependencyGraphBuilder;
import com.synopsys.integration.bdio.graph.builder.LazyId;
import com.synopsys.integration.bdio.graph.builder.MissingExternalIdException;
import com.synopsys.integration.bdio.model.Forge;
import com.synopsys.integration.bdio.model.dependency.Dependency;
import com.synopsys.integration.bdio.model.dependency.DependencyFactory;
import com.synopsys.integration.bdio.model.externalid.ExternalIdFactory;
import com.synopsys.integration.detectable.ExecutableTarget;
import com.synopsys.integration.detectable.ExecutableUtils;
import com.synopsys.integration.detectable.detectable.codelocation.CodeLocation;
import com.synopsys.integration.detectable.detectable.executable.DetectableExecutableRunner;
import com.synopsys.integration.detectable.detectable.executable.ExecutableFailedException;
import com.synopsys.integration.detectable.detectables.buildroot.model.Parser;
import com.synopsys.integration.detectable.detectables.buildroot.model.ShowInfoComponent;
import com.synopsys.integration.detectable.extraction.Extraction;
import com.synopsys.integration.detectable.util.ToolVersionLogger;
import com.synopsys.integration.executable.ExecutableRunnerException;

public class BuildrootExtractor {
public static final Forge forge = new Forge("/", "buildroot");

private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final ExternalIdFactory externalIdFactory = new ExternalIdFactory();
private final DependencyFactory dependencyFactory = new DependencyFactory(externalIdFactory);
private final Parser parser = new Parser();
private final BuildrootDetectableOptions options;
private final DetectableExecutableRunner executableRunner;
private final ToolVersionLogger toolVersionLogger;

public BuildrootExtractor(BuildrootDetectableOptions options, DetectableExecutableRunner executableRunner, ToolVersionLogger toolVersionLogger) {
this.options = options;
this.executableRunner = executableRunner;
this.toolVersionLogger = toolVersionLogger;
}

public Extraction extract(ExecutableTarget makeExe, File workingDirectory) throws ExecutableRunnerException, MissingExternalIdException, ExecutableFailedException {
// log version of make
toolVersionLogger.log(workingDirectory, makeExe, "-v");

// log version of buildroot
toolVersionLogger.log(workingDirectory, makeExe, "print-version");

String output = executableRunner.executeSuccessfully(ExecutableUtils.createFromTarget(workingDirectory, makeExe, "show-info")).getStandardOutput();

Map<String, ShowInfoComponent> components;
try {
components = parser.parse(output);
} catch (JsonParseException e) {
return Extraction.failure("Unable to parse make show-info output");
}

LazyExternalIdDependencyGraphBuilder graph = new LazyExternalIdDependencyGraphBuilder();

for (ShowInfoComponent component : components.values()) {
String type = component.getType();
if (type.equals("rootfs") || type.equals("host") && options.getDependencyTypeFilter().shouldExclude(BuildrootDependencyType.HOST)) {
logger.trace("Skipping component of type: " + type);
continue;
}
logger.trace("Processing buildroot component: " + component.getName());

LazyId id = makeLazyId(component);

if (component.getReverseDependencies().size() == 0) {
graph.addChildToRoot(id);
} else {
for (String reverseDependency : component.getReverseDependencies()) {
LazyId reverseDependencyId = makeLazyId(components.get(reverseDependency));
graph.addChildWithParent(id, reverseDependencyId);
}
}

Dependency dependency = dependencyFactory.createNameVersionDependency(forge, component.getName(), component.getVersion());
graph.setDependencyInfo(id, component.getName(), component.getVersion(), dependency.getExternalId());
}

return Extraction.success(new CodeLocation(graph.build()));
}

private LazyId makeLazyId(ShowInfoComponent component) {
return LazyId.fromNameAndVersion(component.getName(), component.getVersion());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.synopsys.integration.detectable.detectables.buildroot.model;

import java.lang.reflect.Type;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class Parser {

public Map<String, ShowInfoComponent> parse(String showInfoOutput) {
Gson gson = new Gson();
Type type = new TypeToken<Map<String, ShowInfoComponent>>() {}.getType();
return gson.fromJson(showInfoOutput, type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.synopsys.integration.detectable.detectables.buildroot.model;

import java.util.List;

import com.google.gson.annotations.SerializedName;
import com.synopsys.integration.util.Stringable;

public class ShowInfoComponent extends Stringable {
private final String type;
private final String name;
private final String version;
private final List<String> dependencies;

@SerializedName("reverse_dependencies")
private final List<String> reverseDependencies;

public ShowInfoComponent(String type, String name, String version, List<String> dependencies, List<String> reverseDependencies) {
this.type = type;
this.name = name;
this.version = version;
this.dependencies = dependencies;
this.reverseDependencies = reverseDependencies;
}

public String getType() {
return type;
}

public String getName() {
return name;
}

public String getVersion() {
return version;
}

public List<String> getDependencies() {
return dependencies;
}

public List<String> getReverseDependencies() {
return reverseDependencies;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.synopsys.integration.detectable.detectable.executable.resolver.GradleResolver;
import com.synopsys.integration.detectable.detectable.executable.resolver.JavaResolver;
import com.synopsys.integration.detectable.detectable.executable.resolver.LernaResolver;
import com.synopsys.integration.detectable.detectable.executable.resolver.MakeResolver;
import com.synopsys.integration.detectable.detectable.executable.resolver.MavenResolver;
import com.synopsys.integration.detectable.detectable.executable.resolver.NpmResolver;
import com.synopsys.integration.detectable.detectable.executable.resolver.PearResolver;
Expand Down Expand Up @@ -59,6 +60,9 @@
import com.synopsys.integration.detectable.detectables.bitbake.parse.PwdOutputParser;
import com.synopsys.integration.detectable.detectables.bitbake.transform.BitbakeDependencyGraphTransformer;
import com.synopsys.integration.detectable.detectables.bitbake.transform.BitbakeGraphTransformer;
import com.synopsys.integration.detectable.detectables.buildroot.BuildrootDetectable;
import com.synopsys.integration.detectable.detectables.buildroot.BuildrootDetectableOptions;
import com.synopsys.integration.detectable.detectables.buildroot.BuildrootExtractor;
import com.synopsys.integration.detectable.detectables.cargo.CargoExtractor;
import com.synopsys.integration.detectable.detectables.cargo.CargoLockDetectable;
import com.synopsys.integration.detectable.detectables.cargo.parse.CargoDependencyLineParser;
Expand Down Expand Up @@ -347,6 +351,11 @@ public BitbakeDetectable createBitbakeDetectable(DetectableEnvironment environme
return new BitbakeDetectable(environment, fileFinder, bitbakeDetectableOptions, bitbakeExtractor, bashResolver);
}

public BuildrootDetectable createBuildrootDetectable(DetectableEnvironment environment, BuildrootDetectableOptions options, MakeResolver makeResolver) {
BuildrootExtractor buildrootExtractor = new BuildrootExtractor(options, executableRunner, toolVersionLogger);
return new BuildrootDetectable(environment, fileFinder, buildrootExtractor, makeResolver);
}

public CargoLockDetectable createCargoDetectable(DetectableEnvironment environment) {
CargoTomlParser cargoTomlParser = new CargoTomlParser();
CargoDependencyLineParser cargoDependencyLineParser = new CargoDependencyLineParser();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.synopsys.integration.detectable.detectables.buildroot.functional;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.IOException;

import org.jetbrains.annotations.NotNull;

import com.synopsys.integration.bdio.model.externalid.ExternalId;
import com.synopsys.integration.bdio.model.externalid.ExternalIdFactory;
import com.synopsys.integration.detectable.Detectable;
import com.synopsys.integration.detectable.DetectableEnvironment;
import com.synopsys.integration.detectable.ExecutableTarget;
import com.synopsys.integration.detectable.detectable.util.EnumListFilter;
import com.synopsys.integration.detectable.detectables.buildroot.BuildrootDependencyType;
import com.synopsys.integration.detectable.detectables.buildroot.BuildrootDetectableOptions;
import com.synopsys.integration.detectable.detectables.buildroot.BuildrootExtractor;
import com.synopsys.integration.detectable.extraction.Extraction;
import com.synopsys.integration.detectable.functional.DetectableFunctionalTest;
import com.synopsys.integration.detectable.util.FunctionalTestFiles;
import com.synopsys.integration.detectable.util.graph.NameVersionGraphAssert;

public class BuildrootDetectableTest extends DetectableFunctionalTest {
public BuildrootDetectableTest() throws IOException {
super("buildroot");
}

@Override
protected void setup() throws IOException {
addFile(".config");
addFile("Makefile");

addExecutableOutput(
createStandardOutput(
FunctionalTestFiles.asString("/buildroot/make-show-info.json")
),
new String[] {
"make",
"show-info"
}
);
}

@Override
public @NotNull Detectable create(@NotNull DetectableEnvironment detectableEnvironment) {
return detectableFactory.createBuildrootDetectable(
detectableEnvironment,
new BuildrootDetectableOptions(
EnumListFilter.fromExcluded(BuildrootDependencyType.HOST)
),
() -> ExecutableTarget.forCommand("make")
);
}

@Override
public void assertExtraction(@NotNull Extraction extraction) {
assertEquals(1, extraction.getCodeLocations().size());

ExternalIdFactory factory = new ExternalIdFactory();
ExternalId busyboxId = factory.createNameVersionExternalId(BuildrootExtractor.forge, "busybox", "1.36.1");
ExternalId gccId = factory.createNameVersionExternalId(BuildrootExtractor.forge, "gcc-final", "12.3.0");

ExternalId glibcId = factory.createNameVersionExternalId(
BuildrootExtractor.forge,
"glibc",
"2.38-44-gd37c2b20a4787463d192b32041c3406c2bd91de0"
);
ExternalId linuxHeadersId = factory.createNameVersionExternalId(BuildrootExtractor.forge, "linux-headers", "6.6.18");

ExternalId libtoolId = factory.createNameVersionExternalId(BuildrootExtractor.forge, "libtool", "2.4.6");


NameVersionGraphAssert graphAssert = new NameVersionGraphAssert(
BuildrootExtractor.forge,
extraction.getCodeLocations().get(0).getDependencyGraph()
);

graphAssert.hasDependency(busyboxId);
graphAssert.hasDependency(gccId);
graphAssert.hasDependency(glibcId);
graphAssert.hasDependency(linuxHeadersId);

graphAssert.hasParentChildRelationship(glibcId, linuxHeadersId);

graphAssert.hasNoDependency(libtoolId);
}

}
Loading