Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
51e054b
refactor: preserve the full recipe layer list a bit longer
stevebillings Jan 18, 2022
a0878c9
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 18, 2022
4eba072
feat: collect recipe layers only once
stevebillings Jan 18, 2022
bfeb7ef
refactor: ShowRecipesResult object
stevebillings Jan 18, 2022
05ab2f7
feat: parse dependency recipe layer from task-depends.dot
stevebillings Jan 18, 2022
55b9e90
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 18, 2022
ab53844
feat: prefer dependency recipe layer parsed from task-depends.dot
stevebillings Jan 18, 2022
e100f81
style: added log msg
stevebillings Jan 18, 2022
351d58c
refactor: cleanup
stevebillings Jan 18, 2022
c490705
refactor: when parsing show-layers, directly generate the map ultimat…
stevebillings Jan 18, 2022
e732b9e
refactor: cleanup
stevebillings Jan 18, 2022
b458c44
refactor: cleanup
stevebillings Jan 18, 2022
2cef78d
style: added a todo based on QA's experience with 7.9.0 reading an ol…
stevebillings Jan 20, 2022
5c16222
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 24, 2022
b3f8cf6
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 24, 2022
c94d1a0
refactor: cleaning up the extractor a little bit
stevebillings Jan 24, 2022
073d430
refactor: cleanup
stevebillings Jan 24, 2022
aae22f0
test: test layer parsing
stevebillings Jan 24, 2022
bc30893
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 25, 2022
d9eb3fa
refactor: minor simplification
stevebillings Jan 25, 2022
e6fd2e5
style(doc): bitbake fix doc and release note
stevebillings Jan 25, 2022
5ef067e
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 27, 2022
44eae26
Merge branch 'sb_bitbakeMultiLayerRecipes' into docForIDETECT-2925
stevebillings Jan 27, 2022
ecfb60c
Merge pull request #517 from blackducksoftware/docForIDETECT-2925
stevebillings Jan 27, 2022
3f5c33e
chore: extractor throw, rather than catch, IO/Execute exceptions
stevebillings Jan 28, 2022
94a213e
refactor: added GraphNodeLabelParser and GraphNodeLabelDetails
stevebillings Jan 28, 2022
7311737
Merge branch 'master' into sb_bitbakeMultiLayerRecipes
stevebillings Jan 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Optional;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.NotImplementedException;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -25,7 +27,6 @@
import com.synopsys.integration.detectable.detectable.util.EnumListFilter;
import com.synopsys.integration.detectable.detectables.bitbake.model.BitbakeEnvironment;
import com.synopsys.integration.detectable.detectables.bitbake.model.BitbakeGraph;
import com.synopsys.integration.detectable.detectables.bitbake.model.BitbakeRecipe;
import com.synopsys.integration.detectable.detectables.bitbake.parse.BitbakeEnvironmentParser;
import com.synopsys.integration.detectable.detectables.bitbake.parse.BitbakeGraphTransformer;
import com.synopsys.integration.detectable.detectables.bitbake.parse.BitbakeRecipesParser;
Expand All @@ -42,20 +43,18 @@ public class BitbakeExtractor {
private final GraphParserTransformer graphParserTransformer;
private final BitbakeGraphTransformer bitbakeGraphTransformer;
private final BitbakeRecipesParser bitbakeRecipesParser;
private final BitbakeRecipesToLayerMapConverter bitbakeRecipesToLayerMap;
private final ToolVersionLogger toolVersionLogger;
private final BuildFileFinder buildFileFinder;
private final LicenseManifestParser licenseManifestParser;
private final BitbakeEnvironmentParser bitbakeEnvironmentParser;

public BitbakeExtractor(DetectableExecutableRunner executableRunner, GraphParserTransformer graphParserTransformer, BitbakeGraphTransformer bitbakeGraphTransformer,
BitbakeRecipesParser bitbakeRecipesParser, BitbakeRecipesToLayerMapConverter bitbakeRecipesToLayerMap, ToolVersionLogger toolVersionLogger, BuildFileFinder buildFileFinder,
BitbakeRecipesParser bitbakeRecipesParser, ToolVersionLogger toolVersionLogger, BuildFileFinder buildFileFinder,
LicenseManifestParser licenseManifestParser, BitbakeEnvironmentParser bitbakeEnvironmentParser) {
this.executableRunner = executableRunner;
this.graphParserTransformer = graphParserTransformer;
this.bitbakeGraphTransformer = bitbakeGraphTransformer;
this.bitbakeRecipesParser = bitbakeRecipesParser;
this.bitbakeRecipesToLayerMap = bitbakeRecipesToLayerMap;
this.toolVersionLogger = toolVersionLogger;
this.buildFileFinder = buildFileFinder;
this.licenseManifestParser = licenseManifestParser;
Expand All @@ -73,34 +72,27 @@ public Extraction extract(
ExecutableTarget bash
) {
List<CodeLocation> codeLocations = new ArrayList<>();

BitbakeSession bitbakeSession = new BitbakeSession(executableRunner, bitbakeRecipesParser, sourceDirectory, buildEnvScript, sourceArguments, bash, toolVersionLogger, buildFileFinder, bitbakeEnvironmentParser);
bitbakeSession.logBitbakeVersion();
File buildDir = bitbakeSession.determineBuildDir();
BitbakeEnvironment bitbakeEnvironment = bitbakeSession.executeBitbakeForEnvironment();
for (String packageName : packageNames) {
Map<String, String> imageRecipes = null;
ShowRecipesResults showRecipesResults;
try {
showRecipesResults = bitbakeSession.executeBitbakeForRecipeLayerCatalog();
} catch (IOException | ExecutableFailedException e) {
String msg = String.format("Error collecting recipe layer information from show-recipes command: %s", e.getMessage());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just bubble these up - if this message helpful for debugging though, looks good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

logger.error(msg);
return new Extraction.Builder().failure(msg).build();
}
for (String targetImage : packageNames) {
try {
if (dependencyTypeFilter.shouldExclude(BitbakeDependencyType.BUILD)) {
imageRecipes = readImageRecipes(buildDir, packageName, bitbakeEnvironment, followSymLinks, searchDepth);
}
BitbakeGraph bitbakeGraph = generateBitbakeGraph(bitbakeSession, buildDir, packageName, followSymLinks, searchDepth);
List<BitbakeRecipe> bitbakeRecipes = bitbakeSession.executeBitbakeForRecipeLayerCatalog();
Map<String, String> recipeNameToLayersMap = bitbakeRecipesToLayerMap.convert(bitbakeRecipes);

DependencyGraph dependencyGraph = bitbakeGraphTransformer.transform(bitbakeGraph, recipeNameToLayersMap, imageRecipes);
CodeLocation codeLocation = new CodeLocation(dependencyGraph);

codeLocations.add(codeLocation);

codeLocations.add(generateCodeLocationForTargetImage(followSymLinks, searchDepth, dependencyTypeFilter, bitbakeSession, buildDir, bitbakeEnvironment, showRecipesResults, targetImage));
} catch (IOException | IntegrationException | NotImplementedException | ExecutableFailedException e) {
logger.error(String.format("Failed to extract a Code Location while running Bitbake against package '%s': %s", packageName, e.getMessage()));
logger.error(String.format("Failed to extract a Code Location while running Bitbake against package '%s': %s", targetImage, e.getMessage()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing, you could just bubble these up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we want to just log and continue on to the next target image / codelocation

logger.debug(e.getMessage(), e);
}
}

Extraction extraction;

if (codeLocations.isEmpty()) {
extraction = new Extraction.Builder()
.failure("No Code Locations were generated during extraction")
Expand All @@ -111,10 +103,21 @@ public Extraction extract(
.success(codeLocations)
.build();
}

return extraction;
}

@NotNull
private CodeLocation generateCodeLocationForTargetImage(final boolean followSymLinks, final Integer searchDepth, final EnumListFilter<BitbakeDependencyType> dependencyTypeFilter, final BitbakeSession bitbakeSession, final File buildDir,
final BitbakeEnvironment bitbakeEnvironment, final ShowRecipesResults showRecipesResults, final String packageName) throws IntegrationException, IOException, ExecutableFailedException {
Map<String, String> imageRecipes = null;
if (dependencyTypeFilter.shouldExclude(BitbakeDependencyType.BUILD)) {
imageRecipes = readImageRecipes(buildDir, packageName, bitbakeEnvironment, followSymLinks, searchDepth);
}
BitbakeGraph bitbakeGraph = generateBitbakeGraph(bitbakeSession, buildDir, packageName, showRecipesResults.getLayerNames(), followSymLinks, searchDepth);
DependencyGraph dependencyGraph = bitbakeGraphTransformer.transform(bitbakeGraph, showRecipesResults.getRecipesWithLayers(), imageRecipes);
return new CodeLocation(dependencyGraph);
}

private Map<String, String> readImageRecipes(File buildDir, String targetImageName, BitbakeEnvironment bitbakeEnvironment, boolean followSymLinks, int searchDepth) throws IntegrationException, IOException {
Optional<File> licenseManifestFile = buildFileFinder.findLicenseManifestFile(buildDir, targetImageName, bitbakeEnvironment, followSymLinks, searchDepth);
if (licenseManifestFile.isPresent()) {
Expand All @@ -127,17 +130,18 @@ private Map<String, String> readImageRecipes(File buildDir, String targetImageNa
}

private BitbakeGraph generateBitbakeGraph(BitbakeSession bitbakeSession,
File buildDir,
String packageName,
boolean followSymLinks,
Integer searchDepth
File buildDir,
String packageName,
Set<String> knownLayers,
boolean followSymLinks,
Integer searchDepth
) throws IOException, IntegrationException, ExecutableFailedException {
File taskDependsFile = bitbakeSession.executeBitbakeForDependencies(buildDir, packageName, followSymLinks, searchDepth);
if (logger.isTraceEnabled()) {
logger.trace(FileUtils.readFileToString(taskDependsFile, Charset.defaultCharset()));
}
InputStream dependsFileInputStream = FileUtils.openInputStream(taskDependsFile);
GraphParser graphParser = new GraphParser(dependsFileInputStream);
return graphParserTransformer.transform(graphParser);
return graphParserTransformer.transform(graphParser, knownLayers);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.synopsys.integration.detectable.detectable.executable.DetectableExecutableRunner;
import com.synopsys.integration.detectable.detectable.executable.ExecutableFailedException;
import com.synopsys.integration.detectable.detectables.bitbake.model.BitbakeEnvironment;
import com.synopsys.integration.detectable.detectables.bitbake.model.BitbakeRecipe;
import com.synopsys.integration.detectable.detectables.bitbake.parse.BitbakeEnvironmentParser;
import com.synopsys.integration.detectable.detectables.bitbake.parse.BitbakeRecipesParser;
import com.synopsys.integration.detectable.util.ToolVersionLogger;
Expand Down Expand Up @@ -96,7 +95,7 @@ public BitbakeEnvironment executeBitbakeForEnvironment() {
}
}

public List<BitbakeRecipe> executeBitbakeForRecipeLayerCatalog() throws IOException, ExecutableFailedException {
public ShowRecipesResults executeBitbakeForRecipeLayerCatalog() throws IOException, ExecutableFailedException {
ExecutableOutput executableOutput = runBitbake(BITBAKE_LAYERS_SHOW_RECIPES_COMMAND);
return bitbakeRecipesParser.parseShowRecipes(executableOutput.getStandardOutputAsList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ public Optional<File> findLicenseManifestFile(File buildDir, String targetImageN

@NotNull
private List<File> generateListOfFiles(final File licensesDir) {
// TODO surely there's a single-line way to do this (via nio or apache FileUtils)
File[] licensesDirContentsArray = licensesDir.listFiles();
if (licensesDirContentsArray == null) {
return new ArrayList<>(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.synopsys.integration.detectable.detectables.bitbake;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class ShowRecipesResults {
private final Set<String> layerNames;
private final Map<String, List<String>> recipesWithLayers;

public ShowRecipesResults(final Set<String> layerNames, Map<String, List<String>> recipesWithLayers) {
this.layerNames = layerNames;
this.recipesWithLayers = recipesWithLayers;
}

public Set<String> getLayerNames() {
return layerNames;
}

public Map<String, List<String>> getRecipesWithLayers() {
return recipesWithLayers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ private BitbakeNode getOrCreate(String name) {
return newNode;
}

public void addNode(String name, @Nullable String version) {
getOrCreate(name).setVersion(version);
public void addNode(String name, @Nullable String version, @Nullable String layer) {
BitbakeNode node = getOrCreate(name);
node.setVersion(version);
node.setLayer(layer);
}

public void addChild(String parent, String child) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class BitbakeNode {
private final String name;
private String version = null;
private String layer = null;
private final Set<String> children = new HashSet<>();

public BitbakeNode(String name) {this.name = name;}
Expand All @@ -19,6 +20,10 @@ public void setVersion(String version) {
this.version = version;
}

public void setLayer(String layer) {
this.layer = layer;
}

public String getName() {
return name;
}
Expand All @@ -27,6 +32,10 @@ public Optional<String> getVersion() {
return Optional.ofNullable(version);
}

public Optional<String> getLayer() {
return Optional.ofNullable(layer);
}

public Set<String> getChildren() {
return children;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.synopsys.integration.detectable.detectables.bitbake.parse;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -33,21 +35,22 @@ public BitbakeGraphTransformer(ExternalIdFactory externalIdFactory, EnumListFilt
this.dependencyTypeFilter = dependencyTypeFilter;
}

public DependencyGraph transform(BitbakeGraph bitbakeGraph, Map<String, String> recipeLayerMap, Map<String, String> imageRecipes) {
public DependencyGraph transform(BitbakeGraph bitbakeGraph, Map<String, List<String>> recipeLayerMap, Map<String, String> imageRecipes) {
Map<String, Dependency> namesToExternalIds = generateExternalIds(bitbakeGraph, recipeLayerMap, imageRecipes);
return buildGraph(bitbakeGraph, namesToExternalIds);
}

@NotNull
private Map<String, Dependency> generateExternalIds(BitbakeGraph bitbakeGraph, Map<String, String> recipeLayerMap, Map<String, String> imageRecipes) {
private Map<String, Dependency> generateExternalIds(BitbakeGraph bitbakeGraph, Map<String, List<String>> recipeLayerMap, Map<String, String> imageRecipes) {
Map<String, Dependency> namesToExternalIds = new HashMap<>();
for (BitbakeNode bitbakeNode : bitbakeGraph.getNodes()) {
String name = bitbakeNode.getName();

if (bitbakeNode.getVersion().isPresent()) {
String version = bitbakeNode.getVersion().get();
Optional<String> actualLayer = bitbakeNode.getLayer();
if (dependencyTypeFilter.shouldInclude(BitbakeDependencyType.BUILD) || !isBuildDependency(imageRecipes, name, version)) {
Optional<Dependency> dependency = generateExternalId(name, version, recipeLayerMap).map(Dependency::new);
Optional<Dependency> dependency = generateExternalId(name, version, actualLayer.orElse(null), recipeLayerMap).map(Dependency::new);
dependency.ifPresent(value -> namesToExternalIds.put(bitbakeNode.getName(), value));
}
} else if (name.startsWith(VIRTUAL_PREFIX)) {
Expand Down Expand Up @@ -117,20 +120,20 @@ private String removeEpochPrefix(String recipeVersion) {
return epochlessRecipeVersion;
}

private Optional<ExternalId> generateExternalId(String dependencyName, String dependencyVersion, Map<String, String> recipeLayerMap) {
String priorityLayerName = recipeLayerMap.get(dependencyName);
private Optional<ExternalId> generateExternalId(String dependencyName, String dependencyVersion, @Nullable String dependencyLayer, Map<String, List<String>> recipeLayerMap) {
List<String> recipeLayerNames = recipeLayerMap.get(dependencyName);
ExternalId externalId = null;

if (priorityLayerName != null) {
externalId = externalIdFactory.createYoctoExternalId(priorityLayerName, dependencyName, dependencyVersion);
if (recipeLayerNames != null) {
dependencyLayer = chooseRecipeLayer(dependencyName, dependencyLayer, recipeLayerNames);
externalId = externalIdFactory.createYoctoExternalId(dependencyLayer, dependencyName, dependencyVersion);
} else {
logger.debug("Failed to find component '{}' in component layer map.", dependencyName);
logger.debug("Failed to find component '{}' in component layer map. [dependencyVersion: {}; dependencyLayer: {}", dependencyName, dependencyVersion, dependencyLayer);
if (dependencyName.endsWith(NATIVE_SUFFIX)) {
String alternativeName = dependencyName.replace(NATIVE_SUFFIX, "");
logger.debug("Generating alternative component name '{}' for '{}=={}'", alternativeName, dependencyName, dependencyVersion);
externalId = generateExternalId(alternativeName, dependencyVersion, recipeLayerMap).orElse(null);
externalId = generateExternalId(alternativeName, dependencyVersion, dependencyLayer, recipeLayerMap).orElse(null);
} else {
logger.debug("'{}=={}' is not an actual component. Excluding from graph.", dependencyName, dependencyVersion);
logger.debug("'{}:{}' is not an actual component. Excluding from graph.", dependencyName, dependencyVersion);
}
}

Expand All @@ -140,4 +143,14 @@ private Optional<ExternalId> generateExternalId(String dependencyName, String de

return Optional.ofNullable(externalId);
}

private String chooseRecipeLayer(final String dependencyName, @Nullable String dependencyLayer, final List<String> recipeLayerNames) {
if (dependencyLayer == null) {
logger.warn("Did not parse a layer for dependency {} from task-depends.dot; falling back to layer {} (first from show-recipes output)", dependencyName, recipeLayerNames.get(0));
dependencyLayer = recipeLayerNames.get(0);
} else {
logger.trace("For dependency recipe {}: using layer {} parsed from task-depends.dot", dependencyName, dependencyLayer);
}
return dependencyLayer;
}
}
Copy link
Contributor Author

@stevebillings stevebillings Jan 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm half tempted to remove this fallback-to-first-in-list. I'm hoping it's never used. If we did remove it, we could execute bitbake-layers show-layers instead of bitbake-layers show-recipes, and do a whole lot less parsing. I'm hoping that after reading a few customer logs it'll become more clear that we don't need the fallback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm perfectly fine with defaulting to the easier approach and waiting for real customer examples to come through via support. For example, assuming something doesn't happen, but throw exception if we detect it and wait for a diagnostic zip with real world example.

Copy link
Contributor Author

@stevebillings stevebillings Jan 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JakeMathews I'd love your opinion on this. The bitbake code does not assume it'll find the recipe version in the task-depends.dot label (GraphParseTransformer.getVersionFromNode() has returned an Optional even before I started mucking with things). Given the vast experience you have now, do you believe that caution makes sense? And should it apply to finding the layer there as well? (I realize this is largely guesswork, but I trust your guesses more than mine.)

Looking more closely: the detector does not assume that the label attribute is present, but if the label attribute is present, it assumes it will contain the version. My current plan is to do the same for layer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the label is present its pretty safe to assume the layer is in the path. I always prefer to air on the side of caution especially when understanding is murky at best.
It would be great if we could phone home suspected discrepancies rather than failing or silently use a default.

i think what you have now will likely be the safest approach, but we won’t know if its right until we can collect more data.

Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.synopsys.integration.detectable.detectables.bitbake.parse;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;

import com.synopsys.integration.detectable.detectables.bitbake.ShowRecipesResults;
import com.synopsys.integration.detectable.detectables.bitbake.model.BitbakeRecipe;
import com.synopsys.integration.log.IntLogger;
import com.synopsys.integration.log.Slf4jIntLogger;
Expand All @@ -17,8 +22,9 @@ public class BitbakeRecipesParser {
* @param showRecipeLines is the executable output.
* @return Recipe names mapped to a recipe's the layer names.
*/
public List<BitbakeRecipe> parseShowRecipes(List<String> showRecipeLines) {
List<BitbakeRecipe> bitbakeRecipes = new ArrayList<>();
public ShowRecipesResults parseShowRecipes(List<String> showRecipeLines) {
Map<String, List<String>> bitbakeRecipes = new HashMap<>();
Set<String> layerNames = new HashSet<>();

boolean started = false;
BitbakeRecipe currentRecipe = null;
Expand All @@ -35,17 +41,20 @@ public List<BitbakeRecipe> parseShowRecipes(List<String> showRecipeLines) {
}

if (currentRecipe != null) {
bitbakeRecipes.add(currentRecipe);
bitbakeRecipes.put(currentRecipe.getName(), currentRecipe.getLayerNames());
if (currentRecipe.getLayerNames() != null) {
layerNames.addAll(currentRecipe.getLayerNames());
}
}

return bitbakeRecipes;
return new ShowRecipesResults(layerNames, bitbakeRecipes);
}

private BitbakeRecipe parseLine(String line, BitbakeRecipe currentRecipe, List<BitbakeRecipe> bitbakeRecipes) {
private BitbakeRecipe parseLine(String line, BitbakeRecipe currentRecipe, Map<String, List<String>> bitbakeRecipes) {
if (line.contains(":") && !line.startsWith(" ")) {
// Parse beginning of new component
if (currentRecipe != null) {
bitbakeRecipes.add(currentRecipe);
bitbakeRecipes.put(currentRecipe.getName(), currentRecipe.getLayerNames());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we not want to add the currentRecipe to the bitbakeRecipes map in the other else-if statements in this method?

Copy link
Contributor Author

@stevebillings stevebillings Jan 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know... all I did in that method was change that list to a map (so I could delete BitbakeRecipesToLayerMapConverter). It's been that way for a while and I was unaware of a reason to question it.

}

String recipeName = line.replace(":", "").trim();
Expand Down
Loading