@VisibleForTesting protected Step getVerifyStep() { return new AbstractExecutionStep("verify_symlink_tree") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { for (ImmutableMap.Entry<Path, SourcePath> entry : getLinks().entrySet()) { for (Path pathPart : entry.getKey()) { if (pathPart.toString().equals("..")) { context .getBuckEventBus() .post( ConsoleEvent.create( Level.SEVERE, String.format( "Path '%s' should not contain '%s'.", entry.getKey(), pathPart))); return StepExecutionResults.ERROR; } } } return StepExecutionResults.SUCCESS; } }; }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { return ImmutableList.of( new AbstractExecutionStep("install_apk") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { trigger.verify(context); boolean result = context .getAndroidDevicesHelper() .get() .installApk(buildContext.getSourcePathResolver(), apk, false, true, null); return result ? StepExecutionResults.SUCCESS : StepExecutionResults.ERROR; } }); }
static Step createMetadataStep( ProjectFilesystem filesystem, Path pathToMetadataTxt, Path pathToAllLibsDir) { return new AbstractExecutionStep("hash_native_libs") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { ImmutableList.Builder<String> metadataLines = ImmutableList.builder(); for (Path nativeLib : filesystem.getFilesUnderPath(pathToAllLibsDir)) { Sha1HashCode filesha1 = filesystem.computeSha1(nativeLib); Path relativePath = pathToAllLibsDir.relativize(nativeLib); metadataLines.add(String.format("%s %s", relativePath, filesha1)); } filesystem.writeLinesToPath(metadataLines.build(), pathToMetadataTxt); return StepExecutionResults.SUCCESS; } }; }
private AbstractExecutionStep createRenameAssetLibrariesStep( APKModule module, ImmutableSortedSet.Builder<Path> inputAssetLibrariesBuilder, ImmutableList.Builder<Path> outputAssetLibrariesBuilder) { return new AbstractExecutionStep("rename_asset_libraries_as_temp_files_" + module.getName()) { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { ProjectFilesystem filesystem = getProjectFilesystem(); for (Path libPath : inputAssetLibrariesBuilder.build()) { Path tempPath = libPath.resolveSibling(libPath.getFileName() + "~"); filesystem.move(libPath, tempPath); outputAssetLibrariesBuilder.add(tempPath); } return StepExecutionResults.SUCCESS; } }; }
@Override public ImmutableList<Step> getBuildSteps( BuildContext buildContext, ProjectFilesystem filesystem, OutputPathResolver outputPathResolver, BuildCellRelativePathFactory buildCellPathFactory) { return ImmutableList.of( new AbstractExecutionStep("writing_file_hash") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { filesystem.writeContentsToPath( filesystem .computeSha1(buildContext.getSourcePathResolver().getAbsolutePath(inputPath)) .getHash(), outputPathResolver.resolvePath(outputPath)); return StepExecutionResults.SUCCESS; } }); }
/** * Assuming the build has completed successfully, the ABI should have been computed, and it should * be stored for subsequent builds. */ private void addStepsToRecordAbiToDisk(ImmutableList.Builder<Step> commands, final Supplier<Sha1HashCode> abiKeySupplier, final BuildableContext buildableContext) { // Note that the parent directories for all of the files written by these steps should already // have been created by a previous step. Therefore, there is no reason to add a MkdirStep here. commands.add(new AbstractExecutionStep("recording ABI metadata") { @Override public int execute(ExecutionContext context) { Sha1HashCode abiKey = abiKeySupplier.get(); buildableContext.addMetadata(ABI_KEY_ON_DISK_METADATA, abiKey.getHash()); return 0; } }); }
@Test(expected = NullPointerException.class) public void testConstructorThrowsNullPointerExceptionWhenNoDescriptionGiven() { new AbstractExecutionStep(null) { @Override public int execute(ExecutionContext context) { return 0; } }; }
Supplier<ImmutableMap<String, HashCode>> addAccumulateClassNamesStep( final ImmutableSet<Path> classPathEntriesToDex, ImmutableList.Builder<Step> steps) { final ImmutableMap.Builder<String, HashCode> builder = ImmutableMap.builder(); steps.add( new AbstractExecutionStep("collect_all_class_names") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { Map<String, Path> classesToSources = new HashMap<>(); for (Path path : classPathEntriesToDex) { Optional<ImmutableSortedMap<String, HashCode>> hashes = AccumulateClassNamesStep.calculateClassHashes( context, getProjectFilesystem(), path); if (!hashes.isPresent()) { return StepExecutionResults.ERROR; } builder.putAll(hashes.get()); for (String className : hashes.get().keySet()) { if (classesToSources.containsKey(className)) { throw new IllegalArgumentException( String.format( "Duplicate class: %s was found in both %s and %s.", className, classesToSources.get(className), path)); } classesToSources.put(className, path); } } return StepExecutionResults.SUCCESS; } }); return MoreSuppliers.memoize(builder::build); }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { buildableContext.recordArtifact(getOutputPath()); return ImmutableList.<Step>builder() .addAll( MakeCleanDirectoryStep.of( BuildCellRelativePath.fromCellRelativePath( buildContext.getBuildCellRootPath(), getProjectFilesystem(), getOutputPath().getParent()))) .add( new AbstractExecutionStep("write_native_libs_paths") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { List<String> lines = inputs .stream() .map( sp -> getProjectFilesystem() .relativize( buildContext.getSourcePathResolver().getAbsolutePath(sp))) .map(Object::toString) .collect(Collectors.toList()); getProjectFilesystem().writeLinesToPath(lines, getOutputPath()); return StepExecutionResults.SUCCESS; } }) .build(); }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { ImmutableList.Builder<Step> steps = ImmutableList.builder(); // Make sure we have a clean output directory Path outputDirPath = getPathForStringResourcesDirectory(); steps.addAll( MakeCleanDirectoryStep.of( BuildCellRelativePath.fromCellRelativePath( buildContext.getBuildCellRootPath(), getProjectFilesystem(), outputDirPath))); // Copy `values/strings.xml` files from resource directories to hex-enumerated resource // directories under output directory, retaining the input order steps.add( new AbstractExecutionStep("copy_string_resources") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { ProjectFilesystem fileSystem = getProjectFilesystem(); int i = 0; for (Path resDir : filteredResourcesProvider.getRelativeResDirectories( fileSystem, buildContext.getSourcePathResolver())) { Path stringsFilePath = resDir.resolve(VALUES).resolve(STRINGS_XML); if (fileSystem.exists(stringsFilePath)) { // create <output_dir>/<new_res_dir>/values Path newStringsFileDir = outputDirPath.resolve(String.format(NEW_RES_DIR_FORMAT, i++)).resolve(VALUES); fileSystem.mkdirs(newStringsFileDir); // copy <res_dir>/values/strings.xml -> // <output_dir>/<new_res_dir>/values/strings.xml fileSystem.copyFile(stringsFilePath, newStringsFileDir.resolve(STRINGS_XML)); } } return StepExecutionResults.SUCCESS; } }); // Cache the outputDirPath with all the required string resources buildableContext.recordArtifact(outputDirPath); return steps.build(); }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { return ImmutableList.of( new AbstractExecutionStep("installing_exo_files") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { trigger.verify(context); String packageName = AdbHelper.tryToExtractPackageNameFromManifest( buildContext.getSourcePathResolver().getAbsolutePath(manifestPath)); ImmutableSortedMap<String, ImmutableSortedSet<Path>> contents = ExopackageDeviceDirectoryLister.deserializeDirectoryContentsForPackage( getProjectFilesystem(), buildContext.getSourcePathResolver().getRelativePath(deviceExoContents), packageName); context .getAndroidDevicesHelper() .get() .adbCallOrThrow( "installing_exo_files", device -> { ImmutableSortedSet<Path> presentFiles = Preconditions.checkNotNull(contents.get(device.getSerialNumber())); new ExopackageInstaller( buildContext.getSourcePathResolver(), context, getProjectFilesystem(), packageName, device) .installMissingExopackageFiles(presentFiles, exopackageInfo); return true; }, true); return StepExecutionResults.SUCCESS; } }); }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { return ImmutableList.of( new AbstractExecutionStep("listing_exo_contents") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { trigger.verify(context); ConcurrentHashMap<String, SortedSet<String>> contents = new ConcurrentHashMap<>(); context .getAndroidDevicesHelper() .get() .adbCallOrThrow( "listing_exo_contents_for_device", (device) -> { device.mkDirP(ExopackageInstaller.EXOPACKAGE_INSTALL_ROOT.toString()); contents.put( device.getSerialNumber(), device .listDirRecursive(ExopackageInstaller.EXOPACKAGE_INSTALL_ROOT) .stream() .map(Path::toString) .collect( ImmutableSortedSet.toImmutableSortedSet(Ordering.natural()))); return true; }, true); getProjectFilesystem().mkdirs(outputPath.getParent()); getProjectFilesystem() .writeContentsToPath( serializeDirectoryContents(ImmutableSortedMap.copyOf(contents)), outputPath); buildableContext.recordArtifact(outputPath); return StepExecutionResults.SUCCESS; } }); }
@Test public void failedRuntimeDepsArePropagated() throws Exception { final String description = "failing step"; Step failingStep = new AbstractExecutionStep(description) { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { return StepExecutionResults.ERROR; } }; BuildRule ruleToTest = createRule( filesystem, resolver, /* deps */ ImmutableSortedSet.of(), /* buildSteps */ ImmutableList.of(failingStep), /* postBuildSteps */ ImmutableList.of(), /* pathToOutputFile */ null, ImmutableList.of()); resolver.addToIndex(ruleToTest); FakeBuildRule withRuntimeDep = new FakeHasRuntimeDeps( BuildTargetFactory.newInstance("//:with_runtime_dep"), filesystem, ruleToTest); try (CachingBuildEngine cachingBuildEngine = cachingBuildEngineFactory().build()) { BuildResult result = cachingBuildEngine .build(buildContext, TestExecutionContext.newInstance(), withRuntimeDep) .getResult() .get(); assertThat(result.getStatus(), equalTo(BuildRuleStatus.CANCELED)); assertThat(result.getFailure(), instanceOf(BuckUncheckedExecutionException.class)); Throwable cause = result.getFailure().getCause(); assertThat(cause, instanceOf(StepFailedException.class)); assertThat(((StepFailedException) cause).getStep().getShortName(), equalTo(description)); } }
@Test public void failedRuntimeDepsAreNotPropagatedWithKeepGoing() throws Exception { buildContext = this.buildContext.withKeepGoing(true); final String description = "failing step"; Step failingStep = new AbstractExecutionStep(description) { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { return StepExecutionResults.ERROR; } }; BuildRule ruleToTest = createRule( filesystem, resolver, /* deps */ ImmutableSortedSet.of(), /* buildSteps */ ImmutableList.of(failingStep), /* postBuildSteps */ ImmutableList.of(), /* pathToOutputFile */ null, ImmutableList.of()); resolver.addToIndex(ruleToTest); FakeBuildRule withRuntimeDep = new FakeHasRuntimeDeps( BuildTargetFactory.newInstance("//:with_runtime_dep"), filesystem, ruleToTest); try (CachingBuildEngine cachingBuildEngine = cachingBuildEngineFactory().build()) { BuildResult result = cachingBuildEngine .build(buildContext, TestExecutionContext.newInstance(), withRuntimeDep) .getResult() .get(); assertThat(result.getStatus(), equalTo(BuildRuleStatus.SUCCESS)); } }
@Test public void matchingRuleKeyDoesNotRunPostBuildSteps() throws Exception { // Add a post build step so we can verify that it's steps are executed. Step failingStep = new AbstractExecutionStep("test") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { return StepExecutionResults.ERROR; } }; BuildRule ruleToTest = createRule( filesystem, resolver, /* deps */ ImmutableSortedSet.of(), /* buildSteps */ ImmutableList.of(), /* postBuildSteps */ ImmutableList.of(failingStep), /* pathToOutputFile */ null, ImmutableList.of()); BuildInfoRecorder recorder = createBuildInfoRecorder(ruleToTest.getBuildTarget()); recorder.addBuildMetadata( BuildInfo.MetadataKey.RULE_KEY, defaultRuleKeyFactory.build(ruleToTest).toString()); recorder.addMetadata(BuildInfo.MetadataKey.RECORDED_PATHS, ImmutableList.of()); recorder.writeMetadataToDisk(true); // Create the build engine. try (CachingBuildEngine cachingBuildEngine = cachingBuildEngineFactory().build()) { // Run the build. BuildResult result = cachingBuildEngine .build(buildContext, TestExecutionContext.newInstance(), ruleToTest) .getResult() .get(); assertEquals(BuildRuleSuccessType.MATCHING_RULE_KEY, result.getSuccess()); } }
/** * @see com.facebook.buck.dalvik.CanaryFactory#create(int) */ private DexWithClasses createCanary(final int index, ImmutableList.Builder<Step> steps) { final FileLike fileLike = CanaryFactory.create(index); final String canaryDirName = "canary_" + String.valueOf(index); final Path scratchDirectoryForCanaryClass = scratchDirectory.resolve(canaryDirName); // Strip the .class suffix to get the class name for the DexWithClasses object. final String relativePathToClassFile = fileLike.getRelativePath(); Preconditions.checkState(relativePathToClassFile.endsWith(".class")); final String className = relativePathToClassFile.replaceFirst("\\.class$", ""); // Write out the .class file. steps.add(new AbstractExecutionStep("write_canary_class") { @Override public int execute(ExecutionContext context) { Path classFile = scratchDirectoryForCanaryClass.resolve(relativePathToClassFile); ProjectFilesystem projectFilesystem = context.getProjectFilesystem(); try (InputStream inputStream = fileLike.getInput()) { projectFilesystem.createParentDirs(classFile); projectFilesystem.copyToPath(inputStream, classFile); } catch (IOException e) { context.logError(e, "Error writing canary class file: %s.", classFile.toString()); return 1; } return 0; } }); return new DexWithClasses() { @Override public int getSizeEstimate() { // Because we do not know the units being used for DEX size estimation and the canary should // be very small, assume the size is zero. return 0; } @Override public Path getPathToDexFile() { return scratchDirectoryForCanaryClass; } @Override public ImmutableSet<String> getClassNames() { return ImmutableSet.of(className); } @Override public Sha1HashCode getClassesHash() { // The only thing unique to canary classes is the index, which is captured by canaryDirName. Hasher hasher = Hashing.sha1().newHasher(); hasher.putString(canaryDirName, Charsets.UTF_8); return new Sha1HashCode(hasher.hash().toString()); } }; }
@Test public void testArtifactFetchedFromCache() throws InterruptedException, ExecutionException, IOException { Step step = new AbstractExecutionStep("exploding step") { @Override public int execute(ExecutionContext context) { throw new UnsupportedOperationException("build step should not be executed"); } }; BuildRule buildRule = createRule( /* deps */ ImmutableSet.<BuildRule>of(), ImmutableList.<Path>of(), ImmutableList.of(step), /* pathToOutputFile */ null); StepRunner stepRunner = createSameThreadStepRunner(); // Mock out all of the disk I/O. ProjectFilesystem projectFilesystem = createMock(ProjectFilesystem.class); expect(projectFilesystem .readFileIfItExists( Paths.get("buck-out/bin/src/com/facebook/orca/.orca/metadata/RULE_KEY"))) .andReturn(Optional.<String>absent()); expect(projectFilesystem.getRootPath()).andReturn(tmp.getRoot().toPath()); // Simulate successfully fetching the output file from the ArtifactCache. ArtifactCache artifactCache = createMock(ArtifactCache.class); Map<String, String> desiredZipEntries = ImmutableMap.of( "buck-out/gen/src/com/facebook/orca/orca.jar", "Imagine this is the contents of a valid JAR file."); expect( artifactCache.fetch( eq(buildRule.getRuleKey()), capture(new CaptureThatWritesAZipFile(desiredZipEntries)))) .andReturn(CacheResult.DIR_HIT); BuckEventBus buckEventBus = BuckEventBusFactory.newInstance(); BuildContext buildContext = BuildContext.builder() .setActionGraph(RuleMap.createGraphFromSingleRule(buildRule)) .setStepRunner(stepRunner) .setProjectFilesystem(projectFilesystem) .setArtifactCache(artifactCache) .setJavaPackageFinder(createMock(JavaPackageFinder.class)) .setEventBus(buckEventBus) .build(); // Build the rule! replayAll(); CachingBuildEngine cachingBuildEngine = new CachingBuildEngine(); ListenableFuture<BuildRuleSuccess> result = cachingBuildEngine.build(buildContext, buildRule); buckEventBus.post(CommandEvent.finished("build", ImmutableList.<String>of(), false, 0)); verifyAll(); assertTrue( "We expect build() to be synchronous in this case, " + "so the future should already be resolved.", MoreFutures.isSuccess(result)); BuildRuleSuccess success = result.get(); assertEquals(BuildRuleSuccess.Type.FETCHED_FROM_CACHE, success.getType()); assertTrue( ((BuildableAbstractCachingBuildRule) buildRule).isInitializedFromDisk()); assertTrue( "The entries in the zip should be extracted as a result of building the rule.", new File(tmp.getRoot(), "buck-out/gen/src/com/facebook/orca/orca.jar").isFile()); }
@Override public ImmutableList<Step> getBuildSteps( BuildContext context, BuildableContext buildableContext) { buildableContext.recordArtifact(specsDir); buildableContext.recordArtifact( context.getSourcePathResolver().getRelativePath(getSourcePathToOutput())); return ImmutableList.<Step>builder() .add( MkdirStep.of( BuildCellRelativePath.fromCellRelativePath( context.getBuildCellRootPath(), getProjectFilesystem(), specsDir))) .add( new SymCopyStep( getProjectFilesystem(), captureRules .stream() .map(CxxInferCapture::getSourcePathToOutput) .map(context.getSourcePathResolver()::getRelativePath) .collect(ImmutableList.toImmutableList()), resultsDir)) .add( new AbstractExecutionStep("write_specs_path_list") { @Override public StepExecutionResult execute(ExecutionContext executionContext) throws IOException, InterruptedException { ImmutableList<String> specsDirsWithAbsolutePath = getSpecsOfAllDeps() .stream() .map( input -> context.getSourcePathResolver().getAbsolutePath(input).toString()) .collect(ImmutableList.toImmutableList()); getProjectFilesystem().writeLinesToPath(specsDirsWithAbsolutePath, specsPathList); return StepExecutionResults.SUCCESS; } }) .add( new DefaultShellStep( getBuildTarget(), getProjectFilesystem().getRootPath(), getAnalyzeCommand(), ImmutableMap.of())) .build(); }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { return ImmutableList.of( new AbstractExecutionStep("installing_exo_resource_files") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { trigger.verify(context); SourcePathResolver resolver = buildContext.getSourcePathResolver(); String packageName = AdbHelper.tryToExtractPackageNameFromManifest( resolver.getAbsolutePath(manifestPath)); ImmutableSortedMap<String, ImmutableSortedSet<Path>> contents = ExopackageDeviceDirectoryLister.deserializeDirectoryContentsForPackage( getProjectFilesystem(), resolver.getRelativePath(deviceExoContents), packageName); context .getAndroidDevicesHelper() .get() .adbCallOrThrow( "installing_exo_resource_files", device -> { ImmutableSortedSet<Path> presentFiles = Preconditions.checkNotNull(contents.get(device.getSerialNumber())); new ExopackageInstaller( resolver, context, getProjectFilesystem(), packageName, device) .installMissingFiles( presentFiles, ResourcesExoHelper.getFilesToInstall( ResourcesExoHelper.getResourceFilesByHash( resolver, getProjectFilesystem(), paths.stream())), ExopackageInstaller.RESOURCES_TYPE); return true; }, true); return StepExecutionResults.SUCCESS; } }); }
private void addMetadataWriteStep( final PreDexedFilesSorter.Result result, final ImmutableList.Builder<Step> steps, final Path metadataFilePath) { StringBuilder nameBuilder = new StringBuilder(30); final boolean isRootModule = result.apkModule.equals(apkModuleGraph.getRootAPKModule()); final String storeId = result.apkModule.getName(); nameBuilder.append("write_"); if (!isRootModule) { nameBuilder.append(storeId); nameBuilder.append("_"); } nameBuilder.append("metadata_txt"); steps.add( new AbstractExecutionStep(nameBuilder.toString()) { @Override public StepExecutionResult execute(ExecutionContext executionContext) throws IOException, InterruptedException { Map<Path, DexWithClasses> metadataTxtEntries = result.metadataTxtDexEntries; List<String> lines = Lists.newArrayListWithCapacity(metadataTxtEntries.size()); lines.add(".id " + storeId); if (isRootModule) { if (dexSplitMode.getDexStore() == DexStore.RAW) { lines.add(".root_relative"); } } else { for (APKModule dependency : apkModuleGraph.getGraph().getOutgoingNodesFor(result.apkModule)) { lines.add(".requires " + dependency.getName()); } } for (Map.Entry<Path, DexWithClasses> entry : metadataTxtEntries.entrySet()) { Path pathToSecondaryDex = entry.getKey(); String containedClass = Iterables.get(entry.getValue().getClassNames(), 0); containedClass = containedClass.replace('/', '.'); Sha1HashCode hash = getProjectFilesystem().computeSha1(pathToSecondaryDex); lines.add( String.format( "%s %s %s", pathToSecondaryDex.getFileName(), hash, containedClass)); } getProjectFilesystem().writeLinesToPath(lines, metadataFilePath); return StepExecutionResults.SUCCESS; } }); }
@Override public ImmutableList<? extends Step> getBuildSteps( BuildContext buildContext, BuildableContext buildableContext) { return ImmutableList.of( new AbstractExecutionStep("finishing_apk_installation") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { trigger.verify(context); ExopackageInfo exoInfo = apkInfo.getExopackageInfo().get(); String packageName = AdbHelper.tryToExtractPackageNameFromManifest( buildContext.getSourcePathResolver().getAbsolutePath(manifestPath)); ImmutableSortedMap<String, ImmutableSortedSet<Path>> contents = ExopackageDeviceDirectoryLister.deserializeDirectoryContentsForPackage( getProjectFilesystem(), buildContext.getSourcePathResolver().getRelativePath(deviceExoContents), packageName); context .getAndroidDevicesHelper() .get() .adbCallOrThrow( "finishing_apk_installation_call", device -> { ExopackageInstaller installer = new ExopackageInstaller( buildContext.getSourcePathResolver(), context, getProjectFilesystem(), packageName, device); installer.finishExoFileInstallation( ImmutableSortedSet.copyOf(contents.get(device.getSerialNumber())), exoInfo); installer.installApkIfNecessary(apkInfo); installer.killApp(apkInfo, null); return true; }, true); return StepExecutionResults.SUCCESS; } }); }
private Step createMergedThirdPartyJarsStep( ImmutableSet<Path> thirdPartyJars, Path absoluteMergedPath) { return new AbstractExecutionStep("merging_third_party_jar_resources") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { try (ResourcesZipBuilder builder = new ResourcesZipBuilder(absoluteMergedPath)) { for (Path jar : thirdPartyJars) { try (ZipFile base = new ZipFile(jar.toFile())) { for (ZipEntry inputEntry : Collections.list(base.entries())) { if (inputEntry.isDirectory()) { continue; } String name = inputEntry.getName(); String ext = Files.getFileExtension(name); String filename = Paths.get(name).getFileName().toString(); // Android's ApkBuilder filters out a lot of files from Java resources. Try to // match its behavior. // See // https://android.googlesource.com/platform/sdk/+/jb-release/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java if (name.startsWith(".") || name.endsWith("~") || name.startsWith("META-INF") || "aidl".equalsIgnoreCase(ext) || "rs".equalsIgnoreCase(ext) || "rsh".equalsIgnoreCase(ext) || "d".equalsIgnoreCase(ext) || "java".equalsIgnoreCase(ext) || "scala".equalsIgnoreCase(ext) || "class".equalsIgnoreCase(ext) || "scc".equalsIgnoreCase(ext) || "swp".equalsIgnoreCase(ext) || "thumbs.db".equalsIgnoreCase(filename) || "picasa.ini".equalsIgnoreCase(filename) || "package.html".equalsIgnoreCase(filename) || "overview.html".equalsIgnoreCase(filename)) { continue; } try (InputStream inputStream = base.getInputStream(inputEntry)) { builder.addEntry( inputStream, inputEntry.getSize(), inputEntry.getCrc(), name, Deflater.NO_COMPRESSION, false); } } } } } return StepExecutionResults.SUCCESS; } }; }
private AbstractExecutionStep createAssetLibrariesMetadataStep( Path libSubdirectory, String metadataFilename, APKModule module, ImmutableSortedSet.Builder<Path> inputAssetLibrariesBuilder) { return new AbstractExecutionStep("write_metadata_for_asset_libraries_" + module.getName()) { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { ProjectFilesystem filesystem = getProjectFilesystem(); // Walk file tree to find libraries filesystem.walkRelativeFileTree( libSubdirectory, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!file.toString().endsWith(".so")) { throw new IOException("unexpected file in lib directory"); } inputAssetLibrariesBuilder.add(file); return FileVisitResult.CONTINUE; } }); // Write a metadata ImmutableList.Builder<String> metadataLines = ImmutableList.builder(); Path metadataOutput = libSubdirectory.resolve(metadataFilename); for (Path libPath : inputAssetLibrariesBuilder.build()) { // Should return something like x86/libfoo.so Path relativeLibPath = libSubdirectory.relativize(libPath); long filesize = filesystem.getFileSize(libPath); String desiredOutput = relativeLibPath.toString(); String checksum = filesystem.computeSha256(libPath); metadataLines.add(desiredOutput + ' ' + filesize + ' ' + checksum); } ImmutableList<String> metadata = metadataLines.build(); if (!metadata.isEmpty()) { filesystem.writeLinesToPath(metadata, metadataOutput); } return StepExecutionResults.SUCCESS; } }; }
/** @see com.facebook.buck.android.dalvik.CanaryFactory#create(String, int) */ private DexWithClasses createCanary( final ProjectFilesystem filesystem, String storeName, final int index, ImmutableList.Builder<Step> steps) { final FileLike fileLike = CanaryFactory.create(storeName, index); final String canaryDirName = "canary_" + storeName + "_" + String.valueOf(index); final Path scratchDirectoryForCanaryClass = scratchDirectory.resolve(canaryDirName); // Strip the .class suffix to get the class name for the DexWithClasses object. final String relativePathToClassFile = fileLike.getRelativePath(); Preconditions.checkState(relativePathToClassFile.endsWith(".class")); final String className = relativePathToClassFile.replaceFirst("\\.class$", ""); // Write out the .class file. steps.add( new AbstractExecutionStep("write_canary_class") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { Path classFile = scratchDirectoryForCanaryClass.resolve(relativePathToClassFile); try (InputStream inputStream = fileLike.getInput()) { filesystem.createParentDirs(classFile); filesystem.copyToPath(inputStream, classFile); } return StepExecutionResults.SUCCESS; } }); return new DexWithClasses() { @Override public int getWeightEstimate() { // Because we do not know the units being used for DEX size estimation and the canary // should be very small, assume the size is zero. return 0; } @Override public Path getPathToDexFile() { return scratchDirectoryForCanaryClass; } @Override public ImmutableSet<String> getClassNames() { return ImmutableSet.of(className); } @Override public Sha1HashCode getClassesHash() { // The only thing unique to canary classes is the index, // which is captured by canaryDirName. Hasher hasher = Hashing.sha1().newHasher(); hasher.putString(canaryDirName, Charsets.UTF_8); return Sha1HashCode.fromHashCode(hasher.hash()); } }; }
/** * @return a {@link Step} which removes outputs which don't correspond to this rule's modules from * the given output dir, as the module-derived outputs themselves will be controlled by the * haskell compiler. */ private Step prepareOutputDir(String name, Path root, String suffix) { return new AbstractExecutionStep(String.format("preparing %s output dir", name)) { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { getProjectFilesystem().mkdirs(root); getProjectFilesystem() .walkRelativeFileTree( root, new SimpleFileVisitor<Path>() { // Only leave paths which would be overwritten when invoking the compiler. private final Set<Path> allowedPaths = RichStream.from(sources.getModuleNames()) .map(s -> root.resolve(s.replace('.', File.separatorChar) + "." + suffix)) .toImmutableSet(); @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Preconditions.checkState(!file.isAbsolute()); if (!allowedPaths.contains(file)) { LOG.verbose("cleaning " + file); getProjectFilesystem().deleteFileAtPath(file); } return super.visitFile(file, attrs); } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Preconditions.checkState(!dir.isAbsolute()); if (!dir.equals(root) && getProjectFilesystem().getDirectoryContents(dir).isEmpty()) { LOG.verbose("cleaning " + dir); getProjectFilesystem().deleteFileAtPath(dir); } return super.postVisitDirectory(dir, exc); } }); return StepExecutionResults.SUCCESS; } }; }
@Test public void multipleTopLevelRulesDontBlockEachOther() throws Exception { Exchanger<Boolean> exchanger = new Exchanger<>(); Step exchangerStep = new AbstractExecutionStep("interleaved_step") { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { try { // Forces both rules to wait for the other at this point. exchanger.exchange(true, 6, TimeUnit.SECONDS); } catch (TimeoutException e) { throw new RuntimeException(e); } return StepExecutionResults.SUCCESS; } }; BuildRule interleavedRuleOne = createRule( filesystem, resolver, /* deps */ ImmutableSortedSet.of(), /* buildSteps */ ImmutableList.of(exchangerStep), /* postBuildSteps */ ImmutableList.of(), /* pathToOutputFile */ null, ImmutableList.of(InternalFlavor.of("interleaved-1"))); resolver.addToIndex(interleavedRuleOne); BuildRule interleavedRuleTwo = createRule( filesystem, resolver, /* deps */ ImmutableSortedSet.of(), /* buildSteps */ ImmutableList.of(exchangerStep), /* postBuildSteps */ ImmutableList.of(), /* pathToOutputFile */ null, ImmutableList.of(InternalFlavor.of("interleaved-2"))); resolver.addToIndex(interleavedRuleTwo); // The engine needs a couple of threads to ensure that it can schedule multiple steps at the // same time. ListeningExecutorService executorService = listeningDecorator(Executors.newFixedThreadPool(4)); try (CachingBuildEngine cachingBuildEngine = cachingBuildEngineFactory().setExecutorService(executorService).build()) { BuildEngineResult engineResultOne = cachingBuildEngine.build( buildContext, TestExecutionContext.newInstance(), interleavedRuleOne); BuildEngineResult engineResultTwo = cachingBuildEngine.build( buildContext, TestExecutionContext.newInstance(), interleavedRuleTwo); assertThat(engineResultOne.getResult().get().getStatus(), equalTo(BuildRuleStatus.SUCCESS)); assertThat(engineResultTwo.getResult().get().getStatus(), equalTo(BuildRuleStatus.SUCCESS)); } executorService.shutdown(); }
@Test public void pendingWorkIsCancelledOnFailures() throws Exception { final String description = "failing step"; AtomicInteger failedSteps = new AtomicInteger(0); Step failingStep = new AbstractExecutionStep(description) { @Override public StepExecutionResult execute(ExecutionContext context) throws IOException, InterruptedException { System.out.println("Failing"); failedSteps.incrementAndGet(); return StepExecutionResults.ERROR; } }; ImmutableSortedSet.Builder<BuildRule> depsBuilder = ImmutableSortedSet.naturalOrder(); for (int i = 0; i < 20; i++) { BuildRule failingDep = createRule( filesystem, resolver, /* deps */ ImmutableSortedSet.of(), /* buildSteps */ ImmutableList.of(failingStep), /* postBuildSteps */ ImmutableList.of(), /* pathToOutputFile */ null, ImmutableList.of(InternalFlavor.of("failing-" + i))); resolver.addToIndex(failingDep); depsBuilder.add(failingDep); } FakeBuildRule withFailingDeps = new FakeBuildRule( BuildTargetFactory.newInstance("//:with_failing_deps"), depsBuilder.build()); // Use a CommandThreadManager to closely match the real-world CachingBuildEngine experience. // Limit it to 1 thread so that we don't start multiple deps at the same time. try (CommandThreadManager threadManager = new CommandThreadManager( "cachingBuildEngingTest", new ConcurrencyLimit( 1, ResourceAllocationFairness.FAIR, 1, ResourceAmounts.of(100, 100, 100, 100), ResourceAmounts.of(0, 0, 0, 0)))) { CachingBuildEngine cachingBuildEngine = cachingBuildEngineFactory() .setExecutorService(threadManager.getWeightedListeningExecutorService()) .build(); BuildResult result = cachingBuildEngine .build(buildContext, TestExecutionContext.newInstance(), withFailingDeps) .getResult() .get(); assertThat(result.getStatus(), equalTo(BuildRuleStatus.CANCELED)); assertThat(result.getFailure(), instanceOf(BuckUncheckedExecutionException.class)); Throwable cause = result.getFailure().getCause(); assertThat(cause, instanceOf(StepFailedException.class)); assertThat(failedSteps.get(), equalTo(1)); assertThat(((StepFailedException) cause).getStep().getShortName(), equalTo(description)); } }