/**
   * Tests an android_binary with zero dependent android_library rules that contains an assets
   * directory.
   */
  @Test
  public void testCreateAllAssetsDirectoryWithZeroAssetsDirectories() throws IOException {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, neither with an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    // Build up the parameters needed to invoke createAllAssetsDirectory().
    Set<String> assetsDirectories = ImmutableSet.of();
    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    DirectoryTraverser traverser =
        new DirectoryTraverser() {
          @Override
          public void traverse(DirectoryTraversal traversal) {
            throw new RuntimeException("Unexpected: no assets directories to traverse!");
          }
        };

    // Invoke createAllAssetsDirectory(), the method under test.
    Optional<String> allAssetsDirectory =
        androidBinary.createAllAssetsDirectory(
            assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser);

    // Verify that no assets/ directory is used.
    assertFalse(
        "There should not be an assets/ directory to pass to aapt.",
        allAssetsDirectory.isPresent());
    assertTrue(
        "There should not be any commands to build up an assets/ directory.",
        commands.build().isEmpty());
  }
Exemple #2
0
  private JavaBinaryRule createSampleJavaBinaryRule(BuildRuleResolver ruleResolver) {
    // Create a java_binary that depends on a java_library so it is possible to create a
    // java_binary rule with a classpath entry and a main class.
    JavaLibraryRule javaLibrary =
        ruleResolver.buildAndAddToIndex(
            DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(
                    new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(BuildTargetFactory.newInstance("//java/com/facebook/util:util"))
                .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)
                .addSrc("java/com/facebook/util/ManifestGenerator.java"));

    JavaBinaryRule javaBinary =
        ruleResolver.buildAndAddToIndex(
            JavaBinaryRule.newJavaBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(
                    BuildTargetFactory.newInstance("//java/com/facebook/util:ManifestGenerator"))
                .setMainClass("com.facebook.util.ManifestGenerator")
                .addDep(javaLibrary.getBuildTarget()));

    return javaBinary;
  }
  @Test
  public void testAndroidBinaryNoDx() {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, neither with an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .addBuildRuleToExcludeFromDex(
                    BuildTargetFactory.newInstance("//java/src/com/facebook/base:libraryTwo"))
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    DependencyGraph graph = RuleMap.createGraphFromBuildRules(ruleResolver);
    AndroidTransitiveDependencies transitiveDependencies =
        androidBinary.findTransitiveDependencies(graph);
    AndroidDexTransitiveDependencies dexTransitiveDependencies =
        androidBinary.findDexTransitiveDependencies(graph);
    ImmutableList.Builder<Step> commands = ImmutableList.builder();

    BuildContext context = createMock(BuildContext.class);
    replay(context);
    androidBinary.addProguardCommands(
        context,
        dexTransitiveDependencies.classpathEntriesToDex,
        transitiveDependencies.proguardConfigs,
        commands,
        ImmutableSet.<String>of());
    verify(context);

    MakeCleanDirectoryStep expectedClean =
        new MakeCleanDirectoryStep("buck-out/gen/java/src/com/facebook/base/.proguard/apk");

    GenProGuardConfigStep expectedGenProguard =
        new GenProGuardConfigStep(
            "buck-out/bin/java/src/com/facebook/base/__manifest_apk__/AndroidManifest.xml",
            ImmutableSet.<String>of(),
            "buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt");

    ProGuardObfuscateStep expectedObfuscation =
        new ProGuardObfuscateStep(
            "buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt",
            ImmutableSet.<String>of(),
            false,
            ImmutableMap.of(
                "buck-out/gen/java/src/com/facebook/base/lib__libraryOne__output/libraryOne.jar",
                "buck-out/gen/java/src/com/facebook/base/.proguard/apk/buck-out/gen/java/src/com/"
                    + "facebook/base/lib__libraryOne__output/libraryOne-obfuscated.jar"),
            ImmutableSet.of(
                "buck-out/gen/java/src/com/facebook/base/lib__libraryTwo__output/libraryTwo.jar"),
            "buck-out/gen/java/src/com/facebook/base/.proguard/apk");

    assertEquals(
        ImmutableList.of(expectedClean, expectedGenProguard, expectedObfuscation),
        commands.build());
  }
  /**
   * Tests an android_binary with multiple dependent android_library rules, each with its own assets
   * directory.
   */
  @Test
  public void testCreateAllAssetsDirectoryWithMultipleAssetsDirectories() throws IOException {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, each with an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            "java/src/com/facebook/base/assets1",
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            "java/src/com/facebook/base/assets2",
            null /* nativeLibsDirectory */);

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    AndroidResourceRule resourceOne =
        (AndroidResourceRule)
            ruleResolver.get(
                BuildTargetFactory.newInstance(
                    "//java/src/com/facebook/base:libraryOne_resources"));
    AndroidResourceRule resourceTwo =
        (AndroidResourceRule)
            ruleResolver.get(
                BuildTargetFactory.newInstance(
                    "//java/src/com/facebook/base:libraryTwo_resources"));

    // Build up the parameters needed to invoke createAllAssetsDirectory().
    Set<String> assetsDirectories =
        ImmutableSet.of(resourceOne.getAssets(), resourceTwo.getAssets());
    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    DirectoryTraverser traverser =
        new DirectoryTraverser() {
          @Override
          public void traverse(DirectoryTraversal traversal) throws IOException {
            String rootPath = Paths.normalizePathSeparator(traversal.getRoot().getPath());
            if ("java/src/com/facebook/base/assets1".equals(rootPath)) {
              traversal.visit(
                  new File("java/src/com/facebook/base/assets1", "guava-10.0.1-fork.dex.1.jar"),
                  "guava-10.0.1-fork.dex.1.jar");
            } else if ("java/src/com/facebook/base/assets2".equals(rootPath)) {
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Medium.otf"),
                  "fonts/Theinhardt-Medium.otf");
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Regular.otf"),
                  "fonts/Theinhardt-Regular.otf");
            } else {
              throw new RuntimeException("Unexpected path: " + rootPath);
            }
          }
        };

    // Invoke createAllAssetsDirectory(), the method under test.
    Optional<String> allAssetsDirectory =
        androidBinary.createAllAssetsDirectory(
            assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser);

    // Verify that an assets/ directory will be created and passed to aapt.
    assertTrue(allAssetsDirectory.isPresent());
    assertEquals(BIN_DIR + "/java/src/com/facebook/base/__assets_apk__", allAssetsDirectory.get());
    List<? extends Step> expectedCommands =
        ImmutableList.of(
            new MakeCleanDirectoryStep(BIN_DIR + "/java/src/com/facebook/base/__assets_apk__"),
            new MkdirAndSymlinkFileStep(
                "java/src/com/facebook/base/assets1/guava-10.0.1-fork.dex.1.jar",
                BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/guava-10.0.1-fork.dex.1.jar"),
            new MkdirAndSymlinkFileStep(
                "java/src/com/facebook/base/assets2/fonts/Theinhardt-Medium.otf",
                BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/fonts/Theinhardt-Medium.otf"),
            new MkdirAndSymlinkFileStep(
                "java/src/com/facebook/base/assets2/fonts/Theinhardt-Regular.otf",
                BIN_DIR
                    + "/java/src/com/facebook/base/__assets_apk__/fonts/Theinhardt-Regular.otf"));
    MoreAsserts.assertListEquals(expectedCommands, commands.build());
  }
  /**
   * Tests an android_binary with one dependent android_library rule that contains an assets
   * directory.
   */
  @Test
  public void testCreateAllAssetsDirectoryWithOneAssetsDirectory() throws IOException {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, one of which has an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            "java/src/com/facebook/base/assets2",
            null /* nativeLibsDirectory */);

    AndroidResourceRule resourceOne =
        (AndroidResourceRule)
            ruleResolver.get(
                BuildTargetFactory.newInstance(
                    "//java/src/com/facebook/base:libraryTwo_resources"));

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    // Build up the parameters needed to invoke createAllAssetsDirectory().
    Set<String> assetsDirectories = ImmutableSet.of(resourceOne.getAssets());
    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    DirectoryTraverser traverser =
        new DirectoryTraverser() {
          @Override
          public void traverse(DirectoryTraversal traversal) throws IOException {
            String rootPath = Paths.normalizePathSeparator(traversal.getRoot().getPath());
            if ("java/src/com/facebook/base/assets2".equals(rootPath)) {
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Medium.otf"),
                  "fonts/Theinhardt-Medium.otf");
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Regular.otf"),
                  "fonts/Theinhardt-Regular.otf");
            } else {
              throw new RuntimeException("Unexpected path: " + rootPath);
            }
          }
        };

    // Invoke createAllAssetsDirectory(), the method under test.
    Optional<String> allAssetsDirectory =
        androidBinary.createAllAssetsDirectory(
            assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser);

    // Verify that the existing assets/ directory will be passed to aapt.
    assertTrue(allAssetsDirectory.isPresent());
    assertEquals(
        "Even though there is only one assets directory, the one in "
            + BIN_DIR
            + " should be used.",
        androidBinary.getPathToAllAssetsDirectory(),
        allAssetsDirectory.get());
  }
Exemple #6
0
  private Module createModuleForProjectConfig(ProjectConfigRule projectConfig) throws IOException {
    BuildRule projectRule = projectConfig.getProjectRule();
    Preconditions.checkState(
        projectRule instanceof JavaLibraryRule
            || projectRule instanceof AndroidLibraryRule
            || projectRule instanceof AndroidResourceRule
            || projectRule instanceof AndroidBinaryRule
            || projectRule instanceof NdkLibraryRule,
        "project_config() does not know how to process a src_target of type %s.",
        projectRule.getType().getName());

    LinkedHashSet<DependentModule> dependencies = Sets.newLinkedHashSet();
    final BuildTarget target = projectConfig.getBuildTarget();
    Module module = new Module(projectRule, target);
    module.name = getIntellijNameForRule(projectRule);
    module.isIntelliJPlugin = projectConfig.getIsIntelliJPlugin();

    String relativePath = projectConfig.getBuildTarget().getBasePathWithSlash();
    module.pathToImlFile = String.format("%s%s.iml", relativePath, module.name);

    // List the module source as the first dependency.
    boolean includeSourceFolder = true;

    // Do the tests before the sources so they appear earlier in the classpath. When tests are run,
    // their classpath entries may be deliberately shadowing production classpath entries.

    // tests folder
    boolean hasSourceFoldersForTestRule =
        addSourceFolders(
            module,
            projectConfig.getTestRule(),
            projectConfig.getTestsSourceRoots(),
            true /* isTestSource */);

    // test dependencies
    BuildRule testRule = projectConfig.getTestRule();
    if (testRule != null) {
      walkRuleAndAdd(testRule, true /* isForTests */, dependencies, projectConfig.getSrcRule());
    }

    // src folder
    boolean hasSourceFoldersForSrcRule =
        addSourceFolders(
            module,
            projectConfig.getSrcRule(),
            projectConfig.getSourceRoots(),
            false /* isTestSource */);

    // At least one of src or tests should contribute a source folder unless this is an
    // non-library Android project with no source roots specified.
    if (!hasSourceFoldersForTestRule && !hasSourceFoldersForSrcRule) {
      includeSourceFolder = false;
    }

    // IntelliJ expects all Android projects to have a gen/ folder, even if there is no src/
    // directory specified.
    boolean isAndroidRule = projectRule.isAndroidRule();
    if (isAndroidRule) {
      boolean hasSourceFolders = !module.sourceFolders.isEmpty();
      module.sourceFolders.add(SourceFolder.GEN);
      if (!hasSourceFolders) {
        includeSourceFolder = true;
      }
    }

    // src dependencies
    // Note that isForTests is false even if projectRule is the project_config's test_target.
    walkRuleAndAdd(projectRule, false /* isForTests */, dependencies, projectConfig.getSrcRule());

    String basePathWithSlash = projectConfig.getBuildTarget().getBasePathWithSlash();

    // Specify another path for intellij to generate gen/ for each android module,
    // so that it will not disturb our glob() rules.
    // To specify the location of gen, Intellij requires the relative path from
    // the base path of current build target.
    module.moduleGenPath = generateRelativeGenPath(basePathWithSlash);

    DependentModule jdkDependency;
    if (isAndroidRule) {
      // android details
      if (projectRule instanceof NdkLibraryRule) {
        NdkLibraryRule ndkLibraryRule = (NdkLibraryRule) projectRule;
        module.isAndroidLibraryProject = true;
        module.keystorePath = null;
        module.nativeLibs =
            Paths.computeRelativePath(relativePath, ndkLibraryRule.getLibraryPath());
      } else if (projectRule instanceof AndroidResourceRule) {
        AndroidResourceRule androidResourceRule = (AndroidResourceRule) projectRule;
        module.resFolder = createRelativePath(androidResourceRule.getRes(), target);
        module.isAndroidLibraryProject = true;
        module.keystorePath = null;
      } else if (projectRule instanceof AndroidBinaryRule) {
        AndroidBinaryRule androidBinaryRule = (AndroidBinaryRule) projectRule;
        module.resFolder = null;
        module.isAndroidLibraryProject = false;
        KeystoreProperties keystoreProperties =
            KeystoreProperties.createFromPropertiesFile(
                androidBinaryRule.getPathToKeystoreProperties(), projectFilesystem);

        // getKeystore() returns a path relative to the project root, but an IntelliJ module
        // expects the path to the keystore to be relative to the module root, so we strip off the
        // module path.
        String keystorePathRelativeToProjectRoot = keystoreProperties.getKeystore();
        String keystorePath = keystorePathRelativeToProjectRoot.substring(relativePath.length());
        module.keystorePath = keystorePath;
      } else {
        module.isAndroidLibraryProject = true;
        module.keystorePath = null;
      }

      module.hasAndroidFacet = true;
      module.proguardConfigPath = null;

      // If there is a default AndroidManifest.xml specified in .buckconfig, use it if
      // AndroidManifest.xml is not present in the root of the [Android] IntelliJ module.
      if (pathToDefaultAndroidManifest.isPresent()) {
        String androidManifest = basePathWithSlash + "AndroidManifest.xml";
        if (!projectFilesystem.exists(androidManifest)) {
          String manifestPath = this.pathToDefaultAndroidManifest.get();
          String rootPrefix = "//";
          Preconditions.checkState(
              manifestPath.startsWith(rootPrefix),
              "Currently, we expect this option to start with '%s', "
                  + "indicating that it is relative to the root of the repository.",
              rootPrefix);
          manifestPath = manifestPath.substring(rootPrefix.length());
          String relativePathToManifest =
              Paths.computeRelativePath(basePathWithSlash, manifestPath);
          // IntelliJ requires that the path start with a slash to indicate that it is relative to
          // the module.
          module.androidManifest = "/" + relativePathToManifest;
        }
      }

      // List this last so that classes from modules can shadow classes in the JDK.
      jdkDependency = DependentModule.newInheritedJdk();
    } else {
      module.hasAndroidFacet = false;

      if (module.isIntelliJPlugin()) {
        jdkDependency = DependentModule.newIntelliJPluginJdk();
      } else {
        jdkDependency = DependentModule.newStandardJdk();
      }
    }

    // Assign the dependencies.
    module.dependencies =
        createDependenciesInOrder(includeSourceFolder, dependencies, jdkDependency);

    // Annotation processing generates sources for IntelliJ to consume, but does so outside
    // the module directory to avoid messing up globbing.
    if (projectRule instanceof JavaLibraryRule) {
      JavaLibraryRule javaLibraryRule = (JavaLibraryRule) projectRule;
      AnnotationProcessingData processingData = javaLibraryRule.getAnnotationProcessingData();

      String annotationGenSrc = processingData.getGeneratedSourceFolderName();
      if (annotationGenSrc != null) {
        module.annotationGenPath =
            "/" + Paths.computeRelativePath(basePathWithSlash, annotationGenSrc);
        module.annotationGenIsForTest = !hasSourceFoldersForSrcRule;
      }
    }

    return module;
  }