/** * Looks through filtered drawables for files not of the target density and replaces them with * scaled versions. * * <p>Any drawables found by this step didn't have equivalents in the target density. If they are * of a higher density, we can replicate what Android does and downscale them at compile-time. */ private void scaleUnmatchedDrawables(ExecutionContext context) throws IOException, InterruptedException { ResourceFilters.Density targetDensity = ResourceFilters.Density.ORDERING.max(targetDensities); // Go over all the images that remain after filtering. Preconditions.checkNotNull(drawableFinder); Collection<Path> drawables = drawableFinder.findDrawables(inResDirToOutResDirMap.values(), filesystem); for (Path drawable : drawables) { if (drawable.toString().endsWith(".9.png")) { // Skip nine-patch for now. continue; } ResourceFilters.Qualifiers qualifiers = new ResourceFilters.Qualifiers(drawable); ResourceFilters.Density density = qualifiers.density; // If the image has a qualifier but it's not the right one. Preconditions.checkNotNull(targetDensities); if (!targetDensities.contains(density)) { // Replace density qualifier with target density using regular expression to match // the qualifier in the context of a path to a drawable. String fromDensity = (density == ResourceFilters.Density.NO_QUALIFIER ? "" : "-") + density.toString(); Path destination = Paths.get( MorePaths.pathWithUnixSeparators(drawable) .replaceFirst( "((?:^|/)drawable[^/]*)" + Pattern.quote(fromDensity) + "(-|$|/)", "$1-" + targetDensity + "$2")); double factor = targetDensity.value() / density.value(); if (factor >= 1.0) { // There is no point in up-scaling, or converting between drawable and drawable-mdpi. continue; } // Make sure destination folder exists and perform downscaling. filesystem.createParentDirs(destination); Preconditions.checkNotNull(imageScaler); imageScaler.scale(factor, drawable, destination, context); // Delete source file. filesystem.deleteFileAtPath(drawable); // Delete newly-empty directories to prevent missing resources errors in apkbuilder. Path parent = drawable.getParent(); if (filesystem.listFiles(parent).length == 0) { filesystem.deleteFileAtPath(parent); } } } }
private int handleGet(Request baseRequest, HttpServletResponse response) throws IOException { if (!artifactCache.isPresent()) { response.getWriter().write("Serving local cache is disabled for this instance."); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } String path = baseRequest.getUri().getPath(); String[] pathElements = path.split("/"); if (pathElements.length != 4 || !pathElements[2].equals("key")) { response.getWriter().write("Incorrect url format."); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } RuleKey ruleKey = RuleKey.TO_RULE_KEY.apply(pathElements[3]); Path temp = null; try { temp = projectFilesystem.createTempFile(BuckConstant.SCRATCH_PATH, "outgoing_rulekey", ".tmp"); projectFilesystem.createParentDirs(temp); CacheResult fetchResult; try { fetchResult = artifactCache.get().fetch(ruleKey, temp); } catch (InterruptedException e) { LOG.error(e, "Interrupted when fetching from local cache."); e.printStackTrace(response.getWriter()); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } if (!fetchResult.getType().isSuccess()) { return HttpServletResponse.SC_NOT_FOUND; } final Path tempFinal = temp; HttpArtifactCacheBinaryProtocol.FetchResponse fetchResponse = new HttpArtifactCacheBinaryProtocol.FetchResponse( ImmutableSet.of(ruleKey), fetchResult.getMetadata(), new ByteSource() { @Override public InputStream openStream() throws IOException { return projectFilesystem.newFileInputStream(tempFinal); } }); fetchResponse.write(response.getOutputStream()); response.setContentLengthLong(fetchResponse.getContentLength()); return HttpServletResponse.SC_OK; } finally { if (temp != null) { projectFilesystem.deleteFileAtPathIfExists(temp); } } }
@Test public void testDeleteFiles() throws IOException { ProjectFilesystem projectFilesystem = new ProjectFilesystem(tmpDir.getRoot().toPath()); String tracePath = String.format("%s/build.trace", BuckConstant.BUCK_TRACE_DIR); File traceFile = new File(tmpDir.getRoot(), tracePath); projectFilesystem.createParentDirs(tracePath); traceFile.createNewFile(); traceFile.setLastModified(0); for (int i = 0; i < 10; ++i) { File oldResult = new File( tmpDir.getRoot(), String.format("%s/build.100%d.trace", BuckConstant.BUCK_TRACE_DIR, i)); oldResult.createNewFile(); oldResult.setLastModified(TimeUnit.SECONDS.toMillis(i)); } ChromeTraceBuildListener listener = new ChromeTraceBuildListener( projectFilesystem, new FakeClock(1409702151000000000L), new ObjectMapper(), Locale.US, TimeZone.getTimeZone("America/Los_Angeles"), /* tracesToKeep */ 3, false); listener.deleteOldTraces(); ImmutableList<String> files = FluentIterable.from(Arrays.asList(projectFilesystem.listFiles(BuckConstant.BUCK_TRACE_DIR))) .transform( new Function<File, String>() { @Override public String apply(File input) { return input.getName(); } }) .toList(); assertEquals(4, files.size()); assertEquals( ImmutableSortedSet.of( "build.trace", "build.1009.trace", "build.1008.trace", "build.1007.trace"), ImmutableSortedSet.copyOf(files)); }
private CacheResult tryToFetchArtifactFromBuildCacheAndOverlayOnTopOfProjectFilesystem( BuildRule rule, RuleKey ruleKey, BuildInfoRecorder buildInfoRecorder, ArtifactCache artifactCache, ProjectFilesystem filesystem, BuildContext buildContext) throws InterruptedException { // Create a temp file whose extension must be ".zip" for Filesystems.newFileSystem() to infer // that we are creating a zip-based FileSystem. Path zipFile; try { zipFile = Files.createTempFile( "buck_artifact_" + MoreFiles.sanitize(rule.getBuildTarget().getShortName()), ".zip"); } catch (IOException e) { throw new RuntimeException(e); } // TODO(mbolin): Change ArtifactCache.fetch() so that it returns a File instead of takes one. // Then we could download directly from the remote cache into the on-disk cache and unzip it // from there. CacheResult cacheResult = buildInfoRecorder.fetchArtifactForBuildable(ruleKey, zipFile, artifactCache); if (!cacheResult.getType().isSuccess()) { try { Files.delete(zipFile); } catch (IOException e) { LOG.warn(e, "failed to delete %s", zipFile); } return cacheResult; } // We unzip the file in the root of the project directory. // Ideally, the following would work: // // Path pathToZip = Paths.get(zipFile.getAbsolutePath()); // FileSystem fs = FileSystems.newFileSystem(pathToZip, /* loader */ null); // Path root = Iterables.getOnlyElement(fs.getRootDirectories()); // MoreFiles.copyRecursively(root, projectRoot); // // Unfortunately, this does not appear to work, in practice, because MoreFiles fails when trying // to resolve a Path for a zip entry against a file Path on disk. ArtifactCacheEvent.Started started = ArtifactCacheEvent.started( ArtifactCacheEvent.Operation.DECOMPRESS, ImmutableSet.of(ruleKey)); buildContext.getEventBus().post(started); try { Unzip.extractZipFile( zipFile.toAbsolutePath(), filesystem, Unzip.ExistingFileMode.OVERWRITE_AND_CLEAN_DIRECTORIES); // We only delete the ZIP file when it has been unzipped successfully. Otherwise, we leave it // around for debugging purposes. Files.delete(zipFile); if (cacheResult.getType() == CacheResult.Type.HIT) { // If we have a hit, also write out the build metadata. Path metadataDir = BuildInfo.getPathToMetadataDirectory(rule.getBuildTarget()); for (Map.Entry<String, String> ent : cacheResult.getMetadata().entrySet()) { Path dest = metadataDir.resolve(ent.getKey()); filesystem.createParentDirs(dest); filesystem.writeContentsToPath(ent.getValue(), dest); } } } catch (IOException e) { // In the wild, we have seen some inexplicable failures during this step. For now, we try to // give the user as much information as we can to debug the issue, but return CacheResult.MISS // so that Buck will fall back on doing a local build. buildContext .getEventBus() .post( ConsoleEvent.warning( "Failed to unzip the artifact for %s at %s.\n" + "The rule will be built locally, " + "but here is the stacktrace of the failed unzip call:\n" + rule.getBuildTarget(), zipFile.toAbsolutePath(), Throwables.getStackTraceAsString(e))); return CacheResult.miss(); } finally { buildContext.getEventBus().post(ArtifactCacheEvent.finished(started)); } return cacheResult; }