1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tradefed.build; 17 18 import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey; 19 import com.android.tradefed.build.proto.BuildInformation; 20 import com.android.tradefed.build.proto.BuildInformation.BuildFile; 21 import com.android.tradefed.build.proto.BuildInformation.KeyBuildFilePair; 22 import com.android.tradefed.config.DynamicRemoteFileResolver; 23 import com.android.tradefed.device.ITestDevice; 24 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 25 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.util.FileUtil; 28 import com.android.tradefed.util.MultiMap; 29 import com.android.tradefed.util.UniqueMultiMap; 30 31 import com.google.common.base.MoreObjects; 32 import com.google.common.base.Objects; 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.io.ObjectInputStream; 37 import java.io.ObjectOutputStream; 38 import java.lang.reflect.InvocationTargetException; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collection; 42 import java.util.HashSet; 43 import java.util.Hashtable; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 48 /** 49 * Generic implementation of a {@link IBuildInfo} that should be associated 50 * with a {@link ITestDevice}. 51 */ 52 public class BuildInfo implements IBuildInfo { 53 private static final long serialVersionUID = BuildSerializedVersion.VERSION; 54 private static final String BUILD_ALIAS_KEY = "build_alias"; 55 56 private String mBuildId = UNKNOWN_BUILD_ID; 57 private String mTestTag = "stub"; 58 private String mBuildTargetName = "stub"; 59 private final UniqueMultiMap<String, String> mBuildAttributes = 60 new UniqueMultiMap<String, String>(); 61 // TODO: once deployed make non-transient 62 private Map<String, VersionedFile> mVersionedFileMap; 63 private transient MultiMap<String, VersionedFile> mVersionedFileMultiMap; 64 private String mBuildFlavor = null; 65 private String mBuildBranch = null; 66 private String mDeviceSerial = null; 67 68 /** File handling properties: Some files of the BuildInfo might requires special handling */ 69 private final Set<BuildInfoProperties> mProperties = new HashSet<>(); 70 71 private static final String[] FILE_NOT_TO_CLONE = 72 new String[] { 73 BuildInfoFileKey.TESTDIR_IMAGE.getFileKey(), 74 BuildInfoFileKey.HOST_LINKED_DIR.getFileKey(), 75 BuildInfoFileKey.TARGET_LINKED_DIR.getFileKey(), 76 }; 77 78 /** 79 * Creates a {@link BuildInfo} using default attribute values. 80 */ BuildInfo()81 public BuildInfo() { 82 mVersionedFileMap = new Hashtable<String, VersionedFile>(); 83 mVersionedFileMultiMap = new MultiMap<String, VersionedFile>(); 84 } 85 86 /** 87 * Creates a {@link BuildInfo} 88 * 89 * @param buildId the build id 90 * @param buildTargetName the build target name 91 */ BuildInfo(String buildId, String buildTargetName)92 public BuildInfo(String buildId, String buildTargetName) { 93 this(); 94 mBuildId = buildId; 95 mBuildTargetName = buildTargetName; 96 } 97 98 /** 99 * Creates a {@link BuildInfo}, populated with attributes given in another build. 100 * 101 * @param buildToCopy 102 */ BuildInfo(BuildInfo buildToCopy)103 BuildInfo(BuildInfo buildToCopy) { 104 this(buildToCopy.getBuildId(), buildToCopy.getBuildTargetName()); 105 addAllBuildAttributes(buildToCopy); 106 try { 107 addAllFiles(buildToCopy); 108 } catch (IOException e) { 109 throw new RuntimeException(e); 110 } 111 } 112 113 /** 114 * {@inheritDoc} 115 */ 116 @Override getBuildId()117 public String getBuildId() { 118 return mBuildId; 119 } 120 121 /** 122 * {@inheritDoc} 123 */ 124 @Override setBuildId(String buildId)125 public void setBuildId(String buildId) { 126 mBuildId = buildId; 127 } 128 129 /** 130 * {@inheritDoc} 131 */ 132 @Override setTestTag(String testTag)133 public void setTestTag(String testTag) { 134 mTestTag = testTag; 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override getTestTag()141 public String getTestTag() { 142 return mTestTag; 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override getDeviceSerial()149 public String getDeviceSerial() { 150 return mDeviceSerial; 151 } 152 153 /** 154 * {@inheritDoc} 155 */ 156 @Override getBuildAttributes()157 public Map<String, String> getBuildAttributes() { 158 return mBuildAttributes.getUniqueMap(); 159 } 160 161 /** {@inheritDoc} */ 162 @Override setProperties(BuildInfoProperties... properties)163 public void setProperties(BuildInfoProperties... properties) { 164 mProperties.clear(); 165 mProperties.addAll(Arrays.asList(properties)); 166 } 167 168 /** {@inheritDoc} */ 169 @Override getProperties()170 public Set<BuildInfoProperties> getProperties() { 171 return new HashSet<>(mProperties); 172 } 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override getBuildTargetName()178 public String getBuildTargetName() { 179 return mBuildTargetName; 180 } 181 182 /** 183 * {@inheritDoc} 184 */ 185 @Override addBuildAttribute(String attributeName, String attributeValue)186 public void addBuildAttribute(String attributeName, String attributeValue) { 187 mBuildAttributes.put(attributeName, attributeValue); 188 } 189 190 /** {@inheritDoc} */ 191 @Override addBuildAttributes(Map<String, String> buildAttributes)192 public void addBuildAttributes(Map<String, String> buildAttributes) { 193 mBuildAttributes.putAll(buildAttributes); 194 } 195 196 /** 197 * Helper method to copy build attributes, branch, and flavor from other build. 198 */ addAllBuildAttributes(BuildInfo build)199 protected void addAllBuildAttributes(BuildInfo build) { 200 mBuildAttributes.putAll(build.getAttributesMultiMap()); 201 setBuildFlavor(build.getBuildFlavor()); 202 setBuildBranch(build.getBuildBranch()); 203 setTestTag(build.getTestTag()); 204 } 205 getAttributesMultiMap()206 protected MultiMap<String, String> getAttributesMultiMap() { 207 return mBuildAttributes; 208 } 209 210 /** 211 * Helper method to copy all files from the other build. 212 * 213 * <p>Creates new hardlinks to the files so that each build will have a unique file path to the 214 * file. 215 * 216 * @throws IOException if an exception is thrown when creating the hardlink. 217 */ addAllFiles(BuildInfo build)218 protected void addAllFiles(BuildInfo build) throws IOException { 219 for (Map.Entry<String, VersionedFile> fileEntry : build.getVersionedFileMap().entrySet()) { 220 File origFile = fileEntry.getValue().getFile(); 221 if (applyBuildProperties(fileEntry.getValue(), build, this)) { 222 continue; 223 } 224 File copyFile; 225 if (origFile.isDirectory()) { 226 copyFile = FileUtil.createTempDir(fileEntry.getKey()); 227 FileUtil.recursiveHardlink(origFile, copyFile); 228 } else { 229 // Only using createTempFile to create a unique dest filename 230 copyFile = FileUtil.createTempFile(fileEntry.getKey(), 231 FileUtil.getExtension(origFile.getName())); 232 copyFile.delete(); 233 FileUtil.hardlinkFile(origFile, copyFile); 234 } 235 setFile(fileEntry.getKey(), copyFile, fileEntry.getValue().getVersion()); 236 } 237 } 238 239 /** 240 * Allow to apply some of the {@link com.android.tradefed.build.IBuildInfo.BuildInfoProperties} 241 * and possibly do a different handling. 242 * 243 * @param origFileConsidered The currently looked at {@link VersionedFile}. 244 * @param build the original build being cloned 245 * @param receiver the build receiving the information. 246 * @return True if we applied the properties and further handling should be skipped. False 247 * otherwise. 248 */ applyBuildProperties( VersionedFile origFileConsidered, IBuildInfo build, IBuildInfo receiver)249 protected boolean applyBuildProperties( 250 VersionedFile origFileConsidered, IBuildInfo build, IBuildInfo receiver) { 251 // If the no copy on sharding is set, that means the tests dir will be shared and should 252 // not be copied. 253 if (getProperties().contains(BuildInfoProperties.DO_NOT_COPY_ON_SHARDING)) { 254 for (String name : FILE_NOT_TO_CLONE) { 255 if (origFileConsidered.getFile().equals(build.getFile(name))) { 256 receiver.setFile( 257 name, origFileConsidered.getFile(), origFileConsidered.getVersion()); 258 return true; 259 } 260 } 261 } 262 if (getProperties().contains(BuildInfoProperties.DO_NOT_COPY_IMAGE_FILE)) { 263 if (origFileConsidered.equals(build.getVersionedFile(BuildInfoFileKey.DEVICE_IMAGE))) { 264 CLog.d("Skip copying of device_image."); 265 return true; 266 } 267 } 268 return false; 269 } 270 getVersionedFileMap()271 protected Map<String, VersionedFile> getVersionedFileMap() { 272 return mVersionedFileMultiMap.getUniqueMap(); 273 } 274 getVersionedFileMapFull()275 protected MultiMap<String, VersionedFile> getVersionedFileMapFull() { 276 return new MultiMap<>(mVersionedFileMultiMap); 277 } 278 279 /** {@inheritDoc} */ 280 @Override getVersionedFileKeys()281 public Set<String> getVersionedFileKeys() { 282 return mVersionedFileMultiMap.keySet(); 283 } 284 285 /** 286 * {@inheritDoc} 287 */ 288 @Override getFile(String name)289 public File getFile(String name) { 290 List<VersionedFile> fileRecords = mVersionedFileMultiMap.get(name); 291 if (fileRecords == null || fileRecords.isEmpty()) { 292 return null; 293 } 294 return fileRecords.get(0).getFile(); 295 } 296 297 /** {@inheritDoc} */ 298 @Override getFile(BuildInfoFileKey key)299 public File getFile(BuildInfoFileKey key) { 300 return getFile(key.getFileKey()); 301 } 302 303 /** {@inheritDoc} */ 304 @Override getVersionedFile(String name)305 public final VersionedFile getVersionedFile(String name) { 306 List<VersionedFile> fileRecords = mVersionedFileMultiMap.get(name); 307 if (fileRecords == null || fileRecords.isEmpty()) { 308 return null; 309 } 310 return fileRecords.get(0); 311 } 312 313 /** {@inheritDoc} */ 314 @Override getVersionedFile(BuildInfoFileKey key)315 public VersionedFile getVersionedFile(BuildInfoFileKey key) { 316 return getVersionedFile(key.getFileKey()); 317 } 318 319 /** {@inheritDoc} */ 320 @Override getVersionedFiles(BuildInfoFileKey key)321 public final List<VersionedFile> getVersionedFiles(BuildInfoFileKey key) { 322 if (!key.isList()) { 323 throw new UnsupportedOperationException( 324 String.format("Key %s does not support list of files.", key.getFileKey())); 325 } 326 return mVersionedFileMultiMap.get(key.getFileKey()); 327 } 328 329 /** 330 * {@inheritDoc} 331 */ 332 @Override getFiles()333 public Collection<VersionedFile> getFiles() { 334 return mVersionedFileMultiMap.values(); 335 } 336 337 /** 338 * {@inheritDoc} 339 */ 340 @Override getVersion(String name)341 public String getVersion(String name) { 342 List<VersionedFile> fileRecords = mVersionedFileMultiMap.get(name); 343 if (fileRecords == null || fileRecords.isEmpty()) { 344 return null; 345 } 346 return fileRecords.get(0).getVersion(); 347 } 348 349 /** {@inheritDoc} */ 350 @Override getVersion(BuildInfoFileKey key)351 public String getVersion(BuildInfoFileKey key) { 352 return getVersion(key.getFileKey()); 353 } 354 355 /** 356 * {@inheritDoc} 357 */ 358 @Override setFile(String name, File file, String version)359 public void setFile(String name, File file, String version) { 360 if (!mVersionedFileMap.containsKey(name)) { 361 mVersionedFileMap.put(name, new VersionedFile(file, version)); 362 } 363 if (mVersionedFileMultiMap.containsKey(name)) { 364 BuildInfoFileKey key = BuildInfoFileKey.fromString(name); 365 // If the key is a list, we will add it to the map. 366 if (key == null || !key.isList()) { 367 CLog.e( 368 "Device build already contains a file for %s in thread %s", 369 name, Thread.currentThread().getName()); 370 return; 371 } 372 } 373 mVersionedFileMultiMap.put(name, new VersionedFile(file, version)); 374 } 375 376 /** {@inheritDoc} */ 377 @Override setFile(BuildInfoFileKey key, File file, String version)378 public void setFile(BuildInfoFileKey key, File file, String version) { 379 setFile(key.getFileKey(), file, version); 380 } 381 382 /** {@inheritDoc} */ 383 @Override getAppPackageFiles()384 public List<VersionedFile> getAppPackageFiles() { 385 List<VersionedFile> origList = getVersionedFiles(BuildInfoFileKey.PACKAGE_FILES); 386 List<VersionedFile> listCopy = new ArrayList<VersionedFile>(); 387 if (origList != null) { 388 listCopy.addAll(origList); 389 } 390 return listCopy; 391 } 392 393 /** {@inheritDoc} */ 394 @Override addAppPackageFile(File appPackageFile, String version)395 public void addAppPackageFile(File appPackageFile, String version) { 396 setFile(BuildInfoFileKey.PACKAGE_FILES, appPackageFile, version); 397 } 398 399 /** 400 * {@inheritDoc} 401 */ 402 @Override cleanUp()403 public void cleanUp() { 404 for (VersionedFile fileRecord : mVersionedFileMultiMap.values()) { 405 FileUtil.recursiveDelete(fileRecord.getFile()); 406 } 407 mVersionedFileMultiMap.clear(); 408 } 409 410 /** {@inheritDoc} */ 411 @Override cleanUp(List<File> doNotClean)412 public void cleanUp(List<File> doNotClean) { 413 if (doNotClean == null) { 414 cleanUp(); 415 } 416 for (VersionedFile fileRecord : mVersionedFileMultiMap.values()) { 417 if (!doNotClean.contains(fileRecord.getFile())) { 418 FileUtil.recursiveDelete(fileRecord.getFile()); 419 } 420 } 421 refreshVersionedFiles(); 422 } 423 424 /** 425 * Run through all the {@link VersionedFile} and remove from the map the one that do not exists. 426 */ refreshVersionedFiles()427 private void refreshVersionedFiles() { 428 Set<String> keys = new HashSet<>(mVersionedFileMultiMap.keySet()); 429 for (String key : keys) { 430 for (VersionedFile file : mVersionedFileMultiMap.get(key)) { 431 if (!file.getFile().exists()) { 432 mVersionedFileMultiMap.remove(key); 433 } 434 } 435 } 436 } 437 438 /** 439 * {@inheritDoc} 440 */ 441 @Override clone()442 public IBuildInfo clone() { 443 BuildInfo copy = null; 444 try { 445 copy = 446 this.getClass() 447 .getDeclaredConstructor(String.class, String.class) 448 .newInstance(getBuildId(), getBuildTargetName()); 449 } catch (InstantiationException 450 | IllegalAccessException 451 | IllegalArgumentException 452 | InvocationTargetException 453 | NoSuchMethodException 454 | SecurityException e) { 455 CLog.e("Failed to clone the build info."); 456 throw new RuntimeException(e); 457 } 458 copy.addAllBuildAttributes(this); 459 copy.setProperties(this.getProperties().toArray(new BuildInfoProperties[0])); 460 try { 461 copy.addAllFiles(this); 462 } catch (IOException e) { 463 throw new RuntimeException(e); 464 } 465 copy.setBuildBranch(mBuildBranch); 466 copy.setBuildFlavor(mBuildFlavor); 467 copy.setDeviceSerial(mDeviceSerial); 468 469 return copy; 470 } 471 472 /** 473 * {@inheritDoc} 474 */ 475 @Override getBuildFlavor()476 public String getBuildFlavor() { 477 return mBuildFlavor; 478 } 479 480 /** 481 * {@inheritDoc} 482 */ 483 @Override setBuildFlavor(String buildFlavor)484 public void setBuildFlavor(String buildFlavor) { 485 mBuildFlavor = buildFlavor; 486 } 487 488 /** 489 * {@inheritDoc} 490 */ 491 @Override getBuildBranch()492 public String getBuildBranch() { 493 return mBuildBranch; 494 } 495 496 /** 497 * {@inheritDoc} 498 */ 499 @Override setBuildBranch(String branch)500 public void setBuildBranch(String branch) { 501 mBuildBranch = branch; 502 } 503 504 /** 505 * {@inheritDoc} 506 */ 507 @Override setDeviceSerial(String serial)508 public void setDeviceSerial(String serial) { 509 mDeviceSerial = serial; 510 } 511 512 /** 513 * {@inheritDoc} 514 */ 515 @Override hashCode()516 public int hashCode() { 517 return Objects.hashCode(mBuildAttributes, mBuildBranch, mBuildFlavor, mBuildId, 518 mBuildTargetName, mTestTag, mDeviceSerial); 519 } 520 521 /** 522 * {@inheritDoc} 523 */ 524 @Override equals(Object obj)525 public boolean equals(Object obj) { 526 if (this == obj) { 527 return true; 528 } 529 if (obj == null) { 530 return false; 531 } 532 if (getClass() != obj.getClass()) { 533 return false; 534 } 535 BuildInfo other = (BuildInfo) obj; 536 return Objects.equal(mBuildAttributes, other.mBuildAttributes) 537 && Objects.equal(mBuildBranch, other.mBuildBranch) 538 && Objects.equal(mBuildFlavor, other.mBuildFlavor) 539 && Objects.equal(mBuildId, other.mBuildId) 540 && Objects.equal(mBuildTargetName, other.mBuildTargetName) 541 && Objects.equal(mTestTag, other.mTestTag) 542 && Objects.equal(mDeviceSerial, other.mDeviceSerial); 543 } 544 545 /** 546 * {@inheritDoc} 547 */ 548 @Override toString()549 public String toString() { 550 return MoreObjects.toStringHelper(this.getClass()) 551 .omitNullValues() 552 .add("build_alias", getBuildAttributes().get(BUILD_ALIAS_KEY)) 553 .add("bid", mBuildId) 554 .add("target", mBuildTargetName) 555 .add("build_flavor", mBuildFlavor) 556 .add("branch", mBuildBranch) 557 .add("serial", mDeviceSerial) 558 .toString(); 559 } 560 561 /** {@inheritDoc} */ 562 @Override toProto()563 public BuildInformation.BuildInfo toProto() { 564 BuildInformation.BuildInfo.Builder protoBuilder = BuildInformation.BuildInfo.newBuilder(); 565 if (getBuildId() != null) { 566 protoBuilder.setBuildId(getBuildId()); 567 } 568 if (getBuildFlavor() != null) { 569 protoBuilder.setBuildFlavor(getBuildFlavor()); 570 } 571 if (getBuildBranch() != null) { 572 protoBuilder.setBranch(getBuildBranch()); 573 } 574 // Attributes 575 protoBuilder.putAllAttributes(getBuildAttributes()); 576 // Populate the versioned file 577 for (String fileKey : mVersionedFileMultiMap.keySet()) { 578 KeyBuildFilePair.Builder buildFile = KeyBuildFilePair.newBuilder(); 579 buildFile.setBuildFileKey(fileKey); 580 for (VersionedFile vFile : mVersionedFileMultiMap.get(fileKey)) { 581 BuildFile.Builder fileInformation = BuildFile.newBuilder(); 582 fileInformation.setVersion(vFile.getVersion()); 583 if (fileKey.startsWith(IBuildInfo.REMOTE_FILE_PREFIX)) { 584 // Remote file doesn't exist on local cache, so don't save absolute path. 585 fileInformation.setLocalPath(vFile.getFile().toString()); 586 } else { 587 fileInformation.setLocalPath(vFile.getFile().getAbsolutePath()); 588 } 589 buildFile.addFile(fileInformation); 590 } 591 protoBuilder.addVersionedFile(buildFile); 592 } 593 protoBuilder.setBuildInfoClass(this.getClass().getCanonicalName()); 594 return protoBuilder.build(); 595 } 596 597 /** Copy all the {@link VersionedFile} from a given build to this one. */ copyAllFileFrom(BuildInfo build)598 public final void copyAllFileFrom(BuildInfo build) { 599 MultiMap<String, VersionedFile> versionedMap = build.getVersionedFileMapFull(); 600 for (String versionedFile : versionedMap.keySet()) { 601 for (VersionedFile vFile : versionedMap.get(versionedFile)) { 602 setFile(versionedFile, vFile.getFile(), vFile.getVersion()); 603 } 604 } 605 } 606 607 /** Special serialization to handle the new underlying type. */ writeObject(ObjectOutputStream outputStream)608 private void writeObject(ObjectOutputStream outputStream) throws IOException { 609 outputStream.defaultWriteObject(); 610 outputStream.writeObject(mVersionedFileMultiMap); 611 } 612 613 /** Special java method that allows for custom deserialization. */ readObject(ObjectInputStream in)614 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 615 in.defaultReadObject(); 616 try { 617 mVersionedFileMultiMap = (MultiMap<String, VersionedFile>) in.readObject(); 618 } catch (IOException | ClassNotFoundException e) { 619 mVersionedFileMultiMap = new MultiMap<>(); 620 } 621 } 622 623 /** Inverse operation to {@link #toProto()} to get the instance back. */ fromProto(BuildInformation.BuildInfo protoBuild)624 public static IBuildInfo fromProto(BuildInformation.BuildInfo protoBuild) { 625 IBuildInfo buildInfo; 626 String buildClass = protoBuild.getBuildInfoClass(); 627 if (buildClass.isEmpty()) { 628 buildInfo = new BuildInfo(); 629 } else { 630 // Restore the original type of build info. 631 try { 632 buildInfo = 633 (BuildInfo) 634 Class.forName(buildClass).getDeclaredConstructor().newInstance(); 635 } catch (InstantiationException 636 | IllegalAccessException 637 | ClassNotFoundException 638 | InvocationTargetException 639 | NoSuchMethodException e) { 640 throw new RuntimeException(e); 641 } 642 } 643 // Build id 644 if (!protoBuild.getBuildId().isEmpty()) { 645 buildInfo.setBuildId(protoBuild.getBuildId()); 646 } 647 // Build Flavor 648 if (!protoBuild.getBuildFlavor().isEmpty()) { 649 buildInfo.setBuildFlavor(protoBuild.getBuildFlavor()); 650 } 651 // Build Branch 652 if (!protoBuild.getBranch().isEmpty()) { 653 buildInfo.setBuildBranch(protoBuild.getBranch()); 654 } 655 // Attributes 656 for (String key : protoBuild.getAttributes().keySet()) { 657 buildInfo.addBuildAttribute(key, protoBuild.getAttributes().get(key)); 658 } 659 // Versioned File 660 for (KeyBuildFilePair filePair : protoBuild.getVersionedFileList()) { 661 for (BuildFile buildFile : filePair.getFileList()) { 662 buildInfo.setFile( 663 filePair.getBuildFileKey(), 664 new File(buildFile.getLocalPath()), 665 buildFile.getVersion()); 666 } 667 } 668 return buildInfo; 669 } 670 671 /** {@inheritDoc} */ 672 @Override getRemoteFiles()673 public Set<File> getRemoteFiles() { 674 Set<File> remoteFiles = new HashSet<>(); 675 for (String fileKey : mVersionedFileMultiMap.keySet()) { 676 if (fileKey.startsWith(IBuildInfo.REMOTE_FILE_PREFIX)) { 677 // Remote file is not versioned, there should be only one entry. 678 remoteFiles.add(mVersionedFileMultiMap.get(fileKey).get(0).getFile()); 679 } 680 } 681 return remoteFiles; 682 } 683 684 /** {@inheritDoc} */ 685 @Override stageRemoteFile(String fileName, File workingDir)686 public File stageRemoteFile(String fileName, File workingDir) { 687 InvocationMetricLogger.addInvocationMetrics( 688 InvocationMetricKey.STAGE_TESTS_INDIVIDUAL_DOWNLOADS, fileName); 689 List<String> includeFilters = Arrays.asList(String.format("/%s$", fileName)); 690 for (File file : getRemoteFiles()) { 691 try { 692 new DynamicRemoteFileResolver() 693 .resolvePartialDownloadZip( 694 workingDir, file.toString(), includeFilters, null); 695 } catch (BuildRetrievalError e) { 696 throw new RuntimeException(e); 697 } 698 699 File stagedFile = FileUtil.findFile(workingDir, fileName); 700 if (stagedFile != null) { 701 return stagedFile; 702 } 703 } 704 return null; 705 } 706 } 707