-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
repository_ctx: Allow renaming archive entries during extraction. #16052
Changes from 1 commit
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 |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException; | ||
import com.google.devtools.build.lib.vfs.Path; | ||
import com.google.errorprone.annotations.CanIgnoreReturnValue; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import javax.annotation.Nullable; | ||
|
||
|
@@ -33,17 +34,21 @@ public class DecompressorDescriptor { | |
private final Path repositoryPath; | ||
private final Optional<String> prefix; | ||
private final boolean executable; | ||
private final Map<String, String> renameFiles; | ||
private final Decompressor decompressor; | ||
|
||
private DecompressorDescriptor( | ||
String targetKind, String targetName, Path archivePath, Path repositoryPath, | ||
@Nullable String prefix, boolean executable, Decompressor decompressor) { | ||
@Nullable String prefix, boolean executable, | ||
@Nullable Map<String, String> renameFiles, | ||
Decompressor decompressor) { | ||
this.targetKind = targetKind; | ||
this.targetName = targetName; | ||
this.archivePath = archivePath; | ||
this.repositoryPath = repositoryPath; | ||
this.prefix = Optional.fromNullable(prefix); | ||
this.executable = executable; | ||
this.renameFiles = renameFiles; | ||
this.decompressor = decompressor; | ||
} | ||
|
||
|
@@ -71,6 +76,10 @@ public boolean executable() { | |
return executable; | ||
} | ||
|
||
public Map<String, String> renameFiles() { | ||
return renameFiles; | ||
} | ||
|
||
public Decompressor getDecompressor() { | ||
return decompressor; | ||
} | ||
|
@@ -92,12 +101,13 @@ public boolean equals(Object other) { | |
&& Objects.equals(repositoryPath, descriptor.repositoryPath) | ||
&& Objects.equals(prefix, descriptor.prefix) | ||
&& Objects.equals(executable, descriptor.executable) | ||
&& Objects.equals(renameFiles, descriptor.renameFiles) | ||
&& decompressor == descriptor.decompressor; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(targetKind, targetName, archivePath, repositoryPath, prefix); | ||
return Objects.hash(targetKind, targetName, archivePath, repositoryPath, prefix, renameFiles); | ||
} | ||
|
||
public static Builder builder() { | ||
|
@@ -115,6 +125,7 @@ public static class Builder { | |
private Path repositoryPath; | ||
private String prefix; | ||
private boolean executable; | ||
private Map<String, String> renameFiles; | ||
private Decompressor decompressor; | ||
|
||
private Builder() { | ||
|
@@ -125,7 +136,7 @@ public DecompressorDescriptor build() throws RepositoryFunctionException { | |
decompressor = DecompressorValue.getDecompressor(archivePath); | ||
} | ||
return new DecompressorDescriptor( | ||
targetKind, targetName, archivePath, repositoryPath, prefix, executable, decompressor); | ||
targetKind, targetName, archivePath, repositoryPath, prefix, executable, renameFiles, decompressor); | ||
} | ||
|
||
@CanIgnoreReturnValue | ||
|
@@ -164,6 +175,12 @@ public Builder setExecutable(boolean executable) { | |
return this; | ||
} | ||
|
||
@CanIgnoreReturnValue | ||
public Builder setRenameFiles(Map<String, String> renameFiles) { | ||
this.renameFiles = renameFiles; | ||
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. defensively copy here (ImmutableMap.copyOf) 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. Done. |
||
return this; | ||
} | ||
|
||
@CanIgnoreReturnValue | ||
public Builder setDecompressor(Decompressor decompressor) { | ||
this.decompressor = decompressor; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -749,8 +749,22 @@ public StructImpl download( | |
+ " archive. Instead of needing to specify this prefix over and over in the" | ||
+ " <code>build_file</code>, this field can be used to strip it from extracted" | ||
+ " files."), | ||
@Param( | ||
name = "renameFiles", | ||
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. This is the first time I actually noticed that 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. Done. |
||
defaultValue = "{}", | ||
named = true, | ||
positional = false, | ||
doc = | ||
"An optional dict specifying files to rename during the extraction. Archive entries" | ||
+ " with names exactly matching a key will be renamed to the value, prior to" | ||
+ " any directory prefix adjustment."), | ||
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. could you add more information on how this parameter can be used, like you did in the PR comments? or maybe even link to this PR 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. Done. |
||
}) | ||
public void extract(Object archive, Object output, String stripPrefix, StarlarkThread thread) | ||
public void extract( | ||
Object archive, | ||
Object output, | ||
String stripPrefix, | ||
Dict<?, ?> renameFiles, // <String, String> expected | ||
StarlarkThread thread) | ||
throws RepositoryFunctionException, InterruptedException, EvalException { | ||
StarlarkPath archivePath = getPath("extract()", archive); | ||
|
||
|
@@ -762,11 +776,15 @@ public void extract(Object archive, Object output, String stripPrefix, StarlarkT | |
StarlarkPath outputPath = getPath("extract()", output); | ||
checkInOutputDirectory("write", outputPath); | ||
|
||
Map<String, String> renameFilesMap = | ||
Dict.cast(renameFiles, String.class, String.class, "renameFiles"); | ||
|
||
WorkspaceRuleEvent w = | ||
WorkspaceRuleEvent.newExtractEvent( | ||
archive.toString(), | ||
output.toString(), | ||
stripPrefix, | ||
renameFilesMap, | ||
rule.getLabel().toString(), | ||
thread.getCallerLocation()); | ||
env.getListener().post(w); | ||
|
@@ -782,6 +800,7 @@ public void extract(Object archive, Object output, String stripPrefix, StarlarkT | |
.setArchivePath(archivePath.getPath()) | ||
.setRepositoryPath(outputPath.getPath()) | ||
.setPrefix(stripPrefix) | ||
.setRenameFiles(renameFilesMap) | ||
.build()); | ||
env.getListener().post(new ExtractProgress(outputPath.getPath().toString())); | ||
} | ||
|
@@ -881,6 +900,15 @@ public void extract(Object archive, Object output, String stripPrefix, StarlarkT | |
+ " risk to omit the checksum as remote files can change. At best omitting this" | ||
+ " field will make your build non-hermetic. It is optional to make development" | ||
+ " easier but should be set before shipping."), | ||
@Param( | ||
name = "renameFiles", | ||
defaultValue = "{}", | ||
named = true, | ||
positional = false, | ||
doc = | ||
"An optional dict specifying files to rename during the extraction. Archive entries" | ||
+ " with names exactly matching a key will be renamed to the value, prior to" | ||
+ " any directory prefix adjustment."), | ||
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 here 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. Done. |
||
}) | ||
public StructImpl downloadAndExtract( | ||
Object url, | ||
|
@@ -892,6 +920,7 @@ public StructImpl downloadAndExtract( | |
String canonicalId, | ||
Dict<?, ?> auth, // <String, Dict> expected | ||
String integrity, | ||
Dict<?, ?> renameFiles, // <String, String> expected | ||
StarlarkThread thread) | ||
throws RepositoryFunctionException, InterruptedException, EvalException { | ||
Map<URI, Map<String, String>> authHeaders = getAuthHeaders(getAuthContents(auth, "auth")); | ||
|
@@ -911,6 +940,9 @@ public StructImpl downloadAndExtract( | |
checksumValidation = e; | ||
} | ||
|
||
Map<String, String> renameFilesMap = | ||
Dict.cast(renameFiles, String.class, String.class, "renameFiles"); | ||
|
||
WorkspaceRuleEvent w = | ||
WorkspaceRuleEvent.newDownloadAndExtractEvent( | ||
urls, | ||
|
@@ -919,6 +951,7 @@ public StructImpl downloadAndExtract( | |
integrity, | ||
type, | ||
stripPrefix, | ||
renameFilesMap, | ||
rule.getLabel().toString(), | ||
thread.getCallerLocation()); | ||
|
||
|
@@ -977,6 +1010,7 @@ public StructImpl downloadAndExtract( | |
.setArchivePath(downloadedPath) | ||
.setRepositoryPath(outputPath.getPath()) | ||
.setPrefix(stripPrefix) | ||
.setRenameFiles(renameFilesMap) | ||
.build()); | ||
env.getListener().post(new ExtractProgress(outputPath.getPath().toString())); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,13 +14,15 @@ | |
|
||
package com.google.devtools.build.lib.bazel.repository; | ||
|
||
import static com.google.common.truth.Truth.assertThat; | ||
import static com.google.devtools.build.lib.bazel.repository.TestArchiveDescriptor.INNER_FOLDER_NAME; | ||
import static com.google.devtools.build.lib.bazel.repository.TestArchiveDescriptor.ROOT_FOLDER_NAME; | ||
|
||
import com.google.devtools.build.lib.vfs.Path; | ||
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.HashMap; | ||
import java.util.zip.GZIPInputStream; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
@@ -64,6 +66,26 @@ public void testDecompressWithPrefix() throws Exception { | |
archiveDescriptor.assertOutputFiles(outputDir, INNER_FOLDER_NAME); | ||
} | ||
|
||
/** | ||
* Test decompressing a tar.gz file, with some entries being renamed during the | ||
* extraction process. | ||
*/ | ||
@Test | ||
public void testDecompressWithRenamedFiles() throws Exception { | ||
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. can we add a test case where strip_prefix is also present, to test that rename_files happens before strip_prefix? 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. Done. |
||
String innerDirName = ROOT_FOLDER_NAME + "/" + INNER_FOLDER_NAME; | ||
|
||
HashMap<String, String> renameFiles = new HashMap<>(); | ||
renameFiles.put( | ||
innerDirName + "/hardLinkFile", | ||
innerDirName + "/renamedFile"); | ||
DecompressorDescriptor.Builder descriptorBuilder = | ||
archiveDescriptor.createDescriptorBuilder().setRenameFiles(renameFiles); | ||
Path outputDir = decompress(descriptorBuilder); | ||
|
||
Path innerDir = outputDir.getRelative(ROOT_FOLDER_NAME).getRelative(INNER_FOLDER_NAME); | ||
assertThat(innerDir.getRelative("renamedFile").exists()).isTrue(); | ||
} | ||
|
||
private Path decompress(DecompressorDescriptor.Builder descriptorBuilder) throws Exception { | ||
descriptorBuilder.setDecompressor(TarGzFunction.INSTANCE); | ||
return new CompressedTarFunction() { | ||
|
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.
this doesn't need to be nullable (it's never null anyway, and null has the same semantics as empty). also remove all the null checks
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.