-
Notifications
You must be signed in to change notification settings - Fork 79
Sb bitbake multi layer recipes #516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 24 commits
51e054b
a0878c9
4eba072
bfeb7ef
05ab2f7
55b9e90
ab53844
e100f81
351d58c
c490705
e732b9e
b458c44
2cef78d
5c16222
b3f8cf6
c94d1a0
073d430
aae22f0
bc30893
d9eb3fa
e6fd2e5
5ef067e
44eae26
ecfb60c
3f5c33e
94a213e
7311737
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
||
|
|
@@ -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; | ||
|
|
@@ -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; | ||
|
|
@@ -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()); | ||
| 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())); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing, you could just bubble these up. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
|
|
@@ -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()) { | ||
|
|
@@ -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 |
|---|---|---|
| @@ -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 |
|---|---|---|
| @@ -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; | ||
|
|
||
|
|
@@ -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)) { | ||
|
|
@@ -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); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -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; | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 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; | ||
|
|
@@ -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; | ||
|
|
@@ -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()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we not want to add the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
|
|
||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done