Export: trimming enhancements - #3530
Conversation
| state *core.BuildState | ||
| targetDir string | ||
|
|
||
| exportedTargets map[*core.Package]map[core.BuildLabel]bool |
There was a problem hiding this comment.
Using a pointer as a key always makes me uncomfortable; can we avoid this?
More generally, could we avoid the nested map? core.BuildLabel includes the package name anyway, right?
There was a problem hiding this comment.
We can probably avoid using the pointer has key by using the package label, but we will have to look up in the graph each time. I was using the pointer directly assuming some consistency of no repeated package instances. Should I use the string instead and lookup in the graph each time we want to use it?
The nested map is useful for looping though the exported target per each package (and for efficient verification of visited targets). What's your opinion, should I try to unnest?
There was a problem hiding this comment.
I think using the pointer is a bug waiting to happen, and I don't think fetching the package itself will be too expensive (it's a map lookup protected by an RWLock). We should probably export packageKey and tuse that.
The nesting makes sense to keep, but add a comment?
There was a problem hiding this comment.
This is comment refers to outdate code. I forgot to mention here but I did change this after your initial comment. It currently uses a build label directly and is not nested.
// exportedTargets maintains a record of the targets that have been exported so far.
exportedTargets map[core.BuildLabel]boolThis integrated well with the visited check and I've added an explicit visitedPackages map for the implementation that requires it. I'll assume this aligns with your opinion and will not action on the comment.
f6fbf1a to
7152109
Compare
toastwaffle
left a comment
There was a problem hiding this comment.
Nearly there!
As ever, comments phrased as questions probably imply a need for code comments
peterebden
left a comment
There was a problem hiding this comment.
I have some worries about crossing package responsibilities here - I get there's a lot more information we need to store to support this, and we can work through a bunch of that, but I think some of these changes like wanting to request parses from code post the actual build is a line we shouldn't cross (and I think maybe we don't have to).
setting subincludes at package level instead of at target level
- register subinclude statements in the package metadata - filter subincludes label - export all non build_target related statements
this is no longer relevant for the symbol tracking since we mirror the implicit logic for the actual scope symbols.
…arser alive Replaces the fragile background-daemon "KeepParserRunning" parsing logic with a synchronous upfront-parsing design. When "ForceParseEntirePackage" is enabled, we queue and parse all other targets in a visited package exactly once at parse time.
3f140a1 to
7552be7
Compare
|
Comments addressed and ready for another review. |
| // Give the waiters time to block on the wait channel | ||
| time.Sleep(10 * time.Millisecond) |
There was a problem hiding this comment.
this doesn't guarantee that they do start blocking
There was a problem hiding this comment.
I've added some synchronisation to hopefully improve this, but I don't think we can avoid having a sleep. If you have any ideas, or prefer I remove the test, let me know.
| d.printLines(targets) | ||
| for _, line := range cli.CurrentBackend.Output() { | ||
| d.printf("${ERASE_AFTER}%s\n", line) | ||
| logs := cli.CurrentBackend.Output() |
There was a problem hiding this comment.
Maybe it's a bit late now but it would have been really nice to have changes like this split from the bulk of the export change here; it'd be easier to reason about a set of logging changes in isolation. I'm not really clear at the moment why you need to do this; I don't especially object to it but it doesn't seem like plz export has any particularly unique output requirements.
| // to its StatementMetadata. Refer to [StatementMetadata] for more details but this single | ||
| // mapping tracks the targets produced by the statement, the subincluded labels required for its | ||
| // interpretation, and other information. | ||
| statements *cmap.Map[BuildStatement, *StatementMetadata] |
There was a problem hiding this comment.
do you really need these to be cmaps? They are explicitly optimised for high concurrency but not low overhead, I didn't anticipate generating large numbers of them (in this case two per package).
Would a map-and-mutex not be adequate here? Or, from the comment, I'm a little unclear if the mutex is even required - they can only be written once and I assume you'd just be reading them later during the export operation?
There was a problem hiding this comment.
We initially favoured the thread-safe type instead of a shared mutex. I eventually understood that the write phase is single threaded. We maintained the cmaps for consistency but I agree that it is a waste since it results in creating 8 maps + 8 mutexes per package and likely to include few objects in each map.
I believe it would be correct to have no locking whatsoever but I've refactored to include a RWlock for consistency and to be resistant to any future changes (or multi-threaded export). It's better than having cmaps and hopefully with minimal overhead for our current single threaded logic.
| // The intention is to finds all the subincluded labels required by the package but not used to | ||
| // generate targets. An example could be a variable declaration that depends on a subincluded value. | ||
| // We range over all interpreted statements that require any subincluded target. From those, we | ||
| // filter out the statements that generate targets and any explicit subinclude() statement calls. |
There was a problem hiding this comment.
Curious why you need this? I'm struggling a bit to see how this links to what's required for export to work
There was a problem hiding this comment.
This is required to support the following examples (snippets from tests):
subinclude("//build_defs:versions_build_def")
for version, name in VERSIONS.items():
pass # Trimmed during exportfor file in glob(["file*.in"]):
genrule(
name = "target_" + file.removesuffix(".in"),
srcs = [file],
outs = [file.removesuffix(".in") + ".out"],
cmd = "cp $SRCS $OUT",
)Since we are not trimming variables or for headers, we need to determine what else is required by the BUILD file but doesn't necessarily generate a build target.
in the current implementation the mutex is not required but for consistence (and because the overhead should be minimal) we added the support for a shared mutex
…for query metadata
this fixes an issue with running our //src/core:core export test on our CI runners that don't include the python binary
Enhancements to plz export, moving from a basic target-level trimming (using gc.RewriteFile) to build statement-level trimming, including only the required build rules and subincludes.
For consistency, we format all the exported BUILD files.
Changelog:
src/export/export.goto enforce better separation of the DefaultExporter (for trimming) and NoTrimExporter.