Пример #1
0
  @Test
  public void testIgnorePaths() throws IOException {
    ProjectFilesystem filesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(filesystem.getPathRelativizer())
        .andReturn(Functions.<String>identity())
        .times(2);
    BuildTargetParser parser = EasyMock.createMock(BuildTargetParser.class);
    EasyMock.replay(filesystem, parser);

    Reader reader =
        new StringReader(
            Joiner.on('\n').join("[project]", "ignore = .git, foo, bar/, baz//, a/b/c"));
    BuckConfig config = BuckConfig.createFromReader(reader, filesystem, parser, Platform.detect());

    ImmutableSet<String> ignorePaths = config.getIgnorePaths();
    assertEquals(
        "Should ignore paths, sans trailing slashes",
        ignorePaths,
        ImmutableSet.of(
            BuckConstant.BUCK_OUTPUT_DIRECTORY,
            ".idea",
            System.getProperty(BuckConfig.BUCK_BUCKD_DIR_KEY, ".buckd"),
            config.getCacheDir(),
            ".git",
            "foo",
            "bar",
            "baz",
            "a/b/c"));

    EasyMock.verify(filesystem, parser);
  }
Пример #2
0
  @Test
  public void replaceLocationOfFullyQualifiedBuildTarget() {
    ProjectFilesystem filesystem = EasyMock.createNiceMock(ProjectFilesystem.class);
    EasyMock.expect(filesystem.getPathRelativizer()).andStubReturn(relativeToAbsolutePathFunction);
    EasyMock.replay(filesystem);

    BuildRuleResolver ruleResolver = new BuildRuleResolver();
    JavaBinaryRule javaBinary = createSampleJavaBinaryRule(ruleResolver);

    String originalCmd =
        String.format(
            "$(location :%s) $(location %s) $OUT",
            javaBinary.getBuildTarget().getShortName(),
            javaBinary.getBuildTarget().getFullyQualifiedName());

    String contextBasePath = javaBinary.getBuildTarget().getBasePath();
    Set<? extends BuildRule> deps = ImmutableSet.of(javaBinary);

    Genrule rule = createGenrule(ruleResolver, originalCmd, contextBasePath, deps);
    AbstractGenruleStep genruleStep = rule.createGenruleStep();

    // Interpolate the build target in the genrule cmd string.
    String transformedString = genruleStep.replaceMatches(filesystem, originalCmd);

    // Verify that the correct cmd was created.
    Path pathToOutput =
        getAbsolutePathInBase(GEN_DIR + "/java/com/facebook/util/ManifestGenerator.jar");
    String expectedCmd = String.format("%s %s $OUT", pathToOutput, pathToOutput);
    assertEquals(expectedCmd, transformedString);
    EasyMock.verify(filesystem);
  }
  @Test
  public void testAmendBuilder() throws NoSuchBuildTargetException {
    Map<BuildTarget, BuildRule> buildRuleIndex = Maps.newHashMap();
    BuildRuleResolver ruleResolver = new BuildRuleResolver(buildRuleIndex);

    // Set up mocks.
    ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(projectFilesystem.getAbsolutifier())
        .andReturn(IdentityPathAbsolutifier.getIdentityAbsolutifier());
    BuildTargetParser buildTargetParser =
        new BuildTargetParser(projectFilesystem) {
          @Override
          public BuildTarget parse(String buildTargetName, ParseContext parseContext)
              throws NoSuchBuildTargetException {
            return BuildTargetFactory.newInstance(buildTargetName);
          }
        };
    Map<String, ?> instance =
        ImmutableMap.of(
            "vm_args", ImmutableList.of("-Dbuck.robolectric_dir=javatests/com/facebook/base"),
            "source_under_test", ImmutableList.of("//java/com/facebook/base:base"));
    BuildTarget buildTarget = BuildTargetFactory.newInstance("//javatests/com/facebook/base:base");
    BuildFileTree buildFileTree = EasyMock.createMock(BuildFileTree.class);
    EasyMock.replay(projectFilesystem, buildFileTree);
    BuildRuleFactoryParams params =
        new BuildRuleFactoryParams(
            instance,
            projectFilesystem,
            buildFileTree,
            buildTargetParser,
            buildTarget,
            new FakeRuleKeyBuilderFactory());

    // Create a builder using the factory.
    RobolectricTestBuildRuleFactory factory = new RobolectricTestBuildRuleFactory();
    RobolectricTestRule.Builder builder =
        factory.newBuilder(new FakeAbstractBuildRuleBuilderParams()).setBuildTarget(buildTarget);

    // Invoke the method under test.
    factory.amendBuilder(builder, params);

    // Create a build rule using the builder.
    BuildRule base =
        new FakeJavaLibraryRule(
            BuildRuleType.ANDROID_LIBRARY,
            buildTarget,
            ImmutableSortedSet.<BuildRule>of(),
            ImmutableSet.<BuildTargetPattern>of());
    buildRuleIndex.put(BuildTargetFactory.newInstance("//java/com/facebook/base:base"), base);
    RobolectricTestRule robolectricRule =
        (RobolectricTestRule) ruleResolver.buildAndAddToIndex(builder);

    // Verify the build rule built from the builder.
    assertEquals(
        ImmutableList.of("-Dbuck.robolectric_dir=javatests/com/facebook/base"),
        robolectricRule.getVmArgs());
    assertEquals(ImmutableSet.of(base), robolectricRule.getSourceUnderTest());
    EasyMock.verify(projectFilesystem, buildFileTree);
  }
Пример #4
0
  /**
   * If the source paths specified contains one source path to a non-generated file then we should
   * return the correct source tmp corresponding to that non-generated source path. Especially when
   * the generated file comes first in the ordered set.
   */
  @Test
  public void testMixedSourceFile() {
    String pathToGenFile = (GEN_DIR + "/com/facebook/GeneratedFile.java");
    String pathToNonGenFile1 = ("package/src/SourceFile1.java");
    String pathToNonGenFile2 = ("package/src-gen/SourceFile2.java");

    ImmutableSortedSet<String> javaSrcs =
        ImmutableSortedSet.of(pathToGenFile, pathToNonGenFile1, pathToNonGenFile2);

    File parentFile1 = createMock(File.class);
    expect(parentFile1.getName()).andReturn("src");
    expect(parentFile1.getPath()).andReturn("package/src");

    File sourceFile1 = createMock(File.class);
    expect(sourceFile1.getParentFile()).andReturn(parentFile1);

    File parentFile2 = createMock(File.class);
    expect(parentFile2.getName()).andReturn("src");
    expect(parentFile2.getPath()).andReturn("package/src-gen");

    File sourceFile2 = createMock(File.class);
    expect(sourceFile2.getParentFile()).andReturn(parentFile2);

    DefaultJavaPackageFinder defaultJavaPackageFinder = createMock(DefaultJavaPackageFinder.class);
    expect(defaultJavaPackageFinder.getPathsFromRoot()).andReturn(pathsFromRoot).times(2);
    expect(defaultJavaPackageFinder.getPathElements()).andReturn(pathElements).times(2);

    ProjectFilesystem projectFilesystem = createMock(ProjectFilesystem.class);
    expect(projectFilesystem.getFileForRelativePath(pathToNonGenFile1)).andReturn(sourceFile1);
    expect(projectFilesystem.getFileForRelativePath(pathToNonGenFile2)).andReturn(sourceFile2);

    JavaLibraryRule javaLibraryRule =
        new FakeJavaLibraryRule(new BuildTarget("//foo", "bar")).setJavaSrcs(javaSrcs);

    Object[] mocks =
        new Object[] {
          parentFile1,
          sourceFile1,
          parentFile2,
          sourceFile2,
          defaultJavaPackageFinder,
          projectFilesystem
        };
    replay(mocks);

    ImmutableSet<String> result =
        TestCommand.getPathToSourceFolders(
            javaLibraryRule, Optional.of(defaultJavaPackageFinder), projectFilesystem);

    assertEquals(
        "The non-generated source files are under two different source folders.",
        ImmutableSet.of("package/src-gen/", "package/src/"),
        result);

    verify(mocks);
  }
Пример #5
0
 /**
  * Finds the build file responsible for the given {@link Path} and invalidates all of the cached
  * rules dependent on it.
  *
  * @param path A {@link Path} "contained" within the build file to find and invalidate.
  */
 private void invalidateContainingBuildFile(Path path) throws IOException {
   String packageBuildFilePath =
       buildFileTreeCache
           .getInput()
           .getBasePathOfAncestorTarget(
               projectFilesystem.getProjectRoot().toPath().relativize(path).toString());
   invalidateDependents(
       projectFilesystem
           .getFileForRelativePath(packageBuildFilePath + '/' + BuckConstant.BUILD_RULES_FILE_NAME)
           .toPath());
 }
Пример #6
0
  @Test
  public void testIgnorePathsWithRelativeCacheDir() throws IOException {
    ProjectFilesystem filesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(filesystem.getPathRelativizer()).andReturn(Functions.<String>identity());
    BuildTargetParser parser = EasyMock.createMock(BuildTargetParser.class);
    EasyMock.replay(filesystem, parser);

    Reader reader = new StringReader(Joiner.on('\n').join("[cache]", "dir = cache_dir"));
    BuckConfig config = BuckConfig.createFromReader(reader, filesystem, parser, Platform.detect());

    ImmutableSet<String> ignorePaths = config.getIgnorePaths();
    assertTrue(
        "Relative cache directory should be in set of ignored paths",
        ignorePaths.contains("cache_dir"));

    EasyMock.verify(filesystem, parser);
  }
Пример #7
0
 @Before
 public void newFakeFilesystem() {
   fakeFilesystem = EasyMock.createNiceMock(ProjectFilesystem.class);
   EasyMock.expect(fakeFilesystem.getPathRelativizer())
       .andReturn(relativeToAbsolutePathFunction)
       .times(0, 1);
   EasyMock.replay(fakeFilesystem);
 }
Пример #8
0
  public int createIntellijProject(
      File jsonTempFile,
      ProcessExecutor processExecutor,
      boolean generateMinimalProject,
      PrintStream stdOut,
      PrintStream stdErr)
      throws IOException {
    List<Module> modules = createModulesForProjectConfigs();
    writeJsonConfig(jsonTempFile, modules);

    List<String> modifiedFiles = Lists.newArrayList();

    // Process the JSON config to generate the .xml and .iml files for IntelliJ.
    ExitCodeAndOutput result = processJsonConfig(jsonTempFile, generateMinimalProject);
    if (result.exitCode != 0) {
      return result.exitCode;
    } else {
      // intellij.py writes the list of modified files to stdout, so parse stdout and add the
      // resulting file paths to the modifiedFiles list.
      Iterable<String> paths =
          Splitter.on('\n').trimResults().omitEmptyStrings().split(result.stdOut);
      Iterables.addAll(modifiedFiles, paths);
    }

    // Write out the project.properties files.
    List<String> modifiedPropertiesFiles = generateProjectDotPropertiesFiles(modules);
    modifiedFiles.addAll(modifiedPropertiesFiles);

    // Write out the .idea/compiler.xml file (the .idea/ directory is guaranteed to exist).
    CompilerXml compilerXml = new CompilerXml(modules);
    final String pathToCompilerXml = ".idea/compiler.xml";
    File compilerXmlFile = projectFilesystem.getFileForRelativePath(pathToCompilerXml);
    if (compilerXml.write(compilerXmlFile)) {
      modifiedFiles.add(pathToCompilerXml);
    }

    // If the user specified a post-processing script, then run it.
    if (pathToPostProcessScript.isPresent()) {
      String pathToScript = pathToPostProcessScript.get();
      Process process = Runtime.getRuntime().exec(new String[] {pathToScript});
      ProcessExecutor.Result postProcessResult = processExecutor.execute(process);
      int postProcessExitCode = postProcessResult.getExitCode();
      if (postProcessExitCode != 0) {
        return postProcessExitCode;
      }
    }

    // If any files have been modified by `buck project`, then list them for the user.
    if (!modifiedFiles.isEmpty()) {
      SortedSet<String> modifiedFilesInSortedForder = Sets.newTreeSet(modifiedFiles);
      stdOut.printf("MODIFIED FILES:\n%s\n", Joiner.on('\n').join(modifiedFilesInSortedForder));
    }
    // Blit stderr from intellij.py to parent stderr.
    stdErr.print(result.stdErr);

    return 0;
  }
Пример #9
0
  @Override
  public String getExecutableCommand(ProjectFilesystem projectFilesystem) {
    Preconditions.checkState(
        mainClass != null,
        "Must specify a main class for %s in order to to run it.",
        getBuildTarget().getFullyQualifiedName());

    return String.format(
        "java -jar %s", projectFilesystem.getPathRelativizer().apply(getOutputFile()));
  }
Пример #10
0
 /**
  * Populates the collection of known build targets that this Parser will use to construct a
  * dependency graph using all build files inside the given project root and returns an optionally
  * filtered set of build targets.
  *
  * @param filesystem The project filesystem.
  * @param includes A list of files that should be included by each build file.
  * @param filter if specified, applied to each rule in rules. All matching rules will be included
  *     in the List returned by this method. If filter is null, then this method returns null.
  * @return The build targets in the project filtered by the given filter.
  */
 public synchronized List<BuildTarget> filterAllTargetsInProject(
     ProjectFilesystem filesystem, Iterable<String> includes, @Nullable RawRulePredicate filter)
     throws BuildFileParseException, BuildTargetException, IOException {
   Preconditions.checkNotNull(filesystem);
   Preconditions.checkNotNull(includes);
   if (!projectFilesystem.getProjectRoot().equals(filesystem.getProjectRoot())) {
     throw new HumanReadableException(
         String.format(
             "Unsupported root path change from %s to %s",
             projectFilesystem.getProjectRoot(), filesystem.getProjectRoot()));
   }
   if (!isCacheComplete(includes)) {
     knownBuildTargets.clear();
     parsedBuildFiles.clear();
     parseRawRulesInternal(
         ProjectBuildFileParser.getAllRulesInProject(buildFileParserFactory, includes));
     allBuildFilesParsed = true;
   }
   return filterTargets(filter);
 }
Пример #11
0
  @Test
  public void testConstructorThrowsNonExistentBasePath() throws IOException {
    Reader reader =
        new StringReader(Joiner.on('\n').join("[alias]", "katana = //java/com/example:fb4a"));
    ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(projectFilesystem.exists("java/com/example")).andReturn(false);
    EasyMock.replay(projectFilesystem);

    try {
      BuildTargetParser parser = new BuildTargetParser(projectFilesystem);
      createWithDefaultFilesystem(reader, parser);
      fail("Should have thrown HumanReadableException.");
    } catch (HumanReadableException e) {
      assertEquals(
          "No directory java/com/example when resolving target //java/com/example:fb4a "
              + "in context FULLY_QUALIFIED",
          e.getHumanReadableErrorMessage());
    }

    EasyMock.verify(projectFilesystem);
  }
Пример #12
0
  @Test
  public void testNonObfuscatedBuild() throws IOException {
    Path proguardConfigFile = Paths.get("the/configuration.txt");
    Path proguardMappingFile = Paths.get("the/mapping.txt");
    SplitZipStep splitZipStep =
        new SplitZipStep(
            /* inputPathsToSplit */ ImmutableSet.<Path>of(),
            /* secondaryJarMetaPath */ Paths.get(""),
            /* primaryJarPath */ Paths.get(""),
            /* secondaryJarDir */ Paths.get(""),
            /* secondaryJarPattern */ "",
            /* proguardFullConfigFile */ Optional.of(proguardConfigFile),
            /* proguardMappingFile */ Optional.of(proguardMappingFile),
            /* primaryDexPatterns */ ImmutableSet.<String>of("primary"),
            /* primaryDexClassesFile */ Optional.<Path>absent(),
            ZipSplitter.DexSplitStrategy.MAXIMIZE_PRIMARY_DEX_SIZE,
            DexStore.JAR,
            /* pathToReportDir */ Paths.get(""),
            /* useLinearAllocSplitDex */ true,
            /* linearAllocHardLimit */ 4 * 1024 * 1024);

    ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(projectFilesystem.readLines(proguardConfigFile))
        .andReturn(ImmutableList.<String>of("-dontobfuscate"));
    ExecutionContext context = EasyMock.createMock(ExecutionContext.class);
    EasyMock.expect(context.getProjectFilesystem()).andReturn(projectFilesystem).anyTimes();
    EasyMock.replay(projectFilesystem, context);

    Predicate<String> requiredInPrimaryZipPredicate =
        splitZipStep.createRequiredInPrimaryZipPredicate(context);
    assertTrue(
        "Primary class should be in primary.",
        requiredInPrimaryZipPredicate.apply("primary.class"));
    assertFalse(
        "Secondary class should be in secondary.",
        requiredInPrimaryZipPredicate.apply("secondary.class"));

    EasyMock.verify(projectFilesystem, context);
  }
Пример #13
0
  /**
   * Called when file change events are posted to the file change EventBus to invalidate cached
   * build rules if required.
   */
  @Subscribe
  public synchronized void onFileSystemChange(WatchEvent<?> event) throws IOException {
    if (console.getVerbosity() == Verbosity.ALL) {
      console
          .getStdErr()
          .printf(
              "Parser watched event %s %s\n",
              event.kind(), projectFilesystem.createContextString(event));
    }

    if (projectFilesystem.isPathChangeEvent(event)) {
      Path path = (Path) event.context();

      if (isPathCreateOrDeleteEvent(event)) {

        if (path.endsWith(BuckConstant.BUILD_RULES_FILE_NAME)) {

          // If a build file has been added or removed, reconstruct the build file tree.
          buildFileTreeCache.invalidate();
        }

        // Added or removed files can affect globs, so invalidate the package build file
        // "containing" {@code path} unless its filename matches a temp file pattern.
        if (!isTempFile(path)) {
          invalidateContainingBuildFile(path);
        }
      }

      // Invalidate the raw rules and targets dependent on this file.
      invalidateDependents(path);

    } else {

      // Non-path change event, likely an overflow due to many change events: invalidate everything.
      buildFileTreeCache.invalidate();
      invalidateCache();
    }
  }
Пример #14
0
  /**
   * If the source paths specified are all for non-generated files then we should return the correct
   * source tmp corresponding to a non-generated source path.
   */
  @Test
  public void testNonGeneratedSourceFile() {
    String pathToNonGenFile = "package/src/SourceFile1.java";
    assertFalse(JavaTestRule.isGeneratedFile(pathToNonGenFile));

    ImmutableSortedSet<String> javaSrcs = ImmutableSortedSet.of(pathToNonGenFile);
    JavaLibraryRule javaLibraryRule =
        new FakeJavaLibraryRule(new BuildTarget("//foo", "bar")).setJavaSrcs(javaSrcs);

    File parentFile = createMock(File.class);
    expect(parentFile.getName()).andReturn("src");
    expect(parentFile.getPath()).andReturn("package/src");

    File sourceFile = createMock(File.class);
    expect(sourceFile.getParentFile()).andReturn(parentFile);

    DefaultJavaPackageFinder defaultJavaPackageFinder = createMock(DefaultJavaPackageFinder.class);
    expect(defaultJavaPackageFinder.getPathsFromRoot()).andReturn(pathsFromRoot);
    expect(defaultJavaPackageFinder.getPathElements()).andReturn(pathElements);

    ProjectFilesystem projectFilesystem = createMock(ProjectFilesystem.class);
    expect(projectFilesystem.getFileForRelativePath(pathToNonGenFile)).andReturn(sourceFile);

    Object[] mocks =
        new Object[] {parentFile, sourceFile, defaultJavaPackageFinder, projectFilesystem};
    replay(mocks);

    ImmutableSet<String> result =
        TestCommand.getPathToSourceFolders(
            javaLibraryRule, Optional.of(defaultJavaPackageFinder), projectFilesystem);

    assertEquals(
        "All non-generated source files are under one source tmp.",
        ImmutableSet.of("package/src/"),
        result);

    verify(mocks);
  }
Пример #15
0
  public int createIntellijProject(File jsonTempFile, PrintStream stdOut) throws IOException {
    List<Module> modules = createModulesForProjectConfigs();
    writeJsonConfig(jsonTempFile, modules);

    List<String> modifiedFiles = Lists.newArrayList();

    // Process the JSON config to generate the .xml and .iml files for IntelliJ.
    ExitCodeAndStdOut result = processJsonConfig(jsonTempFile);
    if (result.exitCode != 0) {
      return result.exitCode;
    } else {
      // intellij.py writes the list of modified files to stdout, so parse stdout and add the
      // resulting file paths to the modifiedFiles list.
      Iterable<String> paths =
          Splitter.on('\n').trimResults().omitEmptyStrings().split(result.stdOut);
      Iterables.addAll(modifiedFiles, paths);
    }

    // Write out the project.properties files.
    List<String> modifiedPropertiesFiles = generateProjectDotPropertiesFiles(modules);
    modifiedFiles.addAll(modifiedPropertiesFiles);

    // Write out the .idea/compiler.xml file (the .idea/ directory is guaranteed to exist).
    CompilerXml compilerXml = new CompilerXml(modules);
    final String pathToCompilerXml = ".idea/compiler.xml";
    File compilerXmlFile = projectFilesystem.getFileForRelativePath(pathToCompilerXml);
    if (compilerXml.write(compilerXmlFile)) {
      modifiedFiles.add(pathToCompilerXml);
    }

    // If any files have been modified by `buck project`, then list them for the user.
    if (!modifiedFiles.isEmpty()) {
      SortedSet<String> modifiedFilesInSortedForder = Sets.newTreeSet(modifiedFiles);
      stdOut.printf("MODIFIED FILES:\n%s\n", Joiner.on('\n').join(modifiedFilesInSortedForder));
    }

    return 0;
  }
Пример #16
0
 @VisibleForTesting
 static void addRootExcludes(
     Module module, BuildRule buildRule, ProjectFilesystem projectFilesystem) {
   // If in the root of the project, specify ignored paths.
   if (buildRule != null && buildRule.getBuildTarget().getBasePathWithSlash().isEmpty()) {
     for (Path path : projectFilesystem.getIgnorePaths()) {
       // It turns out that ignoring all of buck-out causes problems in IntelliJ: it forces an
       // extra "modules" folder to appear at the top of the navigation pane that competes with the
       // ordinary file tree, making navigation a real pain. The hypothesis is that this is because
       // there are files in buck-out/gen and buck-out/android that IntelliJ freaks out about if it
       // cannot find them. Therefore, if "buck-out" is listed in the default list of paths to
       // ignore (which makes sense for other parts of Buck, such as Watchman), then we will ignore
       // only the appropriate subfolders of buck-out instead.
       if (BuckConstant.BUCK_OUTPUT_PATH.equals(path)) {
         addRootExclude(module, BuckConstant.BIN_PATH);
         addRootExclude(module, BuckConstant.LOG_PATH);
       } else {
         addRootExclude(module, path);
       }
     }
     module.isRootModule = true;
   }
 }
Пример #17
0
  @Test
  public void testRequiredInPrimaryZipPredicate() throws IOException {
    Path primaryDexClassesFile = Paths.get("the/manifest.txt");
    SplitZipStep splitZipStep =
        new SplitZipStep(
            /* inputPathsToSplit */ ImmutableSet.<Path>of(),
            /* secondaryJarMetaPath */ Paths.get(""),
            /* primaryJarPath */ Paths.get(""),
            /* secondaryJarDir */ Paths.get(""),
            /* secondaryJarPattern */ "",
            /* proguardFullConfigFile */ Optional.<Path>absent(),
            /* proguardMappingFile */ Optional.<Path>absent(),
            /* primaryDexPatterns */ ImmutableSet.of("List"),
            Optional.of(primaryDexClassesFile),
            ZipSplitter.DexSplitStrategy.MAXIMIZE_PRIMARY_DEX_SIZE,
            DexStore.JAR,
            /* pathToReportDir */ Paths.get(""),
            /* useLinearAllocSplitDex */ true,
            /* linearAllocHardLimit */ 4 * 1024 * 1024);
    List<String> linesInManifestFile =
        ImmutableList.of(
            "com/google/common/collect/ImmutableSortedSet",
            "  com/google/common/collect/ImmutableSet",
            "# com/google/common/collect/ImmutableMap");

    ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(projectFilesystem.readLines(primaryDexClassesFile))
        .andReturn(linesInManifestFile);
    ExecutionContext context = EasyMock.createMock(ExecutionContext.class);
    EasyMock.expect(context.getProjectFilesystem()).andReturn(projectFilesystem);
    EasyMock.replay(projectFilesystem, context);

    Predicate<String> requiredInPrimaryZipPredicate =
        splitZipStep.createRequiredInPrimaryZipPredicate(context);
    assertTrue(
        "All non-.class files should be accepted.",
        requiredInPrimaryZipPredicate.apply("apples.txt"));
    assertTrue(
        "com/google/common/collect/ImmutableSortedSet.class is listed in the manifest verbatim.",
        requiredInPrimaryZipPredicate.apply("com/google/common/collect/ImmutableSortedSet.class"));
    assertTrue(
        "com/google/common/collect/ImmutableSet.class is in the manifest with whitespace.",
        requiredInPrimaryZipPredicate.apply("com/google/common/collect/ImmutableSet.class"));
    assertFalse(
        "com/google/common/collect/ImmutableSet.class cannot have whitespace as param.",
        requiredInPrimaryZipPredicate.apply("  com/google/common/collect/ImmutableSet.class"));
    assertFalse(
        "com/google/common/collect/ImmutableMap.class is commented out.",
        requiredInPrimaryZipPredicate.apply("com/google/common/collect/ImmutableMap.class"));
    assertFalse(
        "com/google/common/collect/Iterables.class is not even mentioned.",
        requiredInPrimaryZipPredicate.apply("com/google/common/collect/Iterables.class"));
    assertTrue(
        "java/awt/List.class matches the substring 'List'.",
        requiredInPrimaryZipPredicate.apply("java/awt/List.class"));
    assertFalse(
        "Substring matching is case-sensitive.",
        requiredInPrimaryZipPredicate.apply("shiny/Glistener.class"));

    EasyMock.verify(projectFilesystem, context);
  }
Пример #18
0
 public File getProjectRoot() {
   return projectFilesystem.getProjectRoot();
 }
Пример #19
0
  @Test
  public void testRequiredInPrimaryZipPredicateWithProguard() throws IOException {
    Path proguardConfigFile = Paths.get("the/configuration.txt");
    Path proguardMappingFile = Paths.get("the/mapping.txt");
    Path primaryDexClassesFile = Paths.get("the/manifest.txt");
    SplitZipStep splitZipStep =
        new SplitZipStep(
            /* inputPathsToSplit */ ImmutableSet.<Path>of(),
            /* secondaryJarMetaPath */ Paths.get(""),
            /* primaryJarPath */ Paths.get(""),
            /* secondaryJarDir */ Paths.get(""),
            /* secondaryJarPattern */ "",
            /* proguardFullConfigFile */ Optional.of(proguardConfigFile),
            /* proguardMappingFile */ Optional.of(proguardMappingFile),
            /* primaryDexPatterns */ ImmutableSet.of("/primary/", "x/"),
            Optional.of(primaryDexClassesFile),
            ZipSplitter.DexSplitStrategy.MAXIMIZE_PRIMARY_DEX_SIZE,
            DexStore.JAR,
            /* pathToReportDir */ Paths.get(""),
            /* useLinearAllocSplitDex */ true,
            /* linearAllocHardLimit */ 4 * 1024 * 1024);
    List<String> linesInMappingFile =
        ImmutableList.of(
            "foo.bar.MappedPrimary -> foo.bar.a:",
            "foo.bar.MappedSecondary -> foo.bar.b:",
            "foo.bar.UnmappedPrimary -> foo.bar.UnmappedPrimary:",
            "foo.bar.UnmappedSecondary -> foo.bar.UnmappedSecondary:",
            "foo.primary.MappedPackage -> x.a:",
            "foo.secondary.MappedPackage -> x.b:",
            "foo.primary.UnmappedPackage -> foo.primary.UnmappedPackage:");
    List<String> linesInManifestFile =
        ImmutableList.of(
            // Actual primary dex classes.
            "foo/bar/MappedPrimary",
            "foo/bar/UnmappedPrimary",
            // Red herrings!
            "foo/bar/b",
            "x/b");

    ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class);
    EasyMock.expect(projectFilesystem.readLines(primaryDexClassesFile))
        .andReturn(linesInManifestFile);
    EasyMock.expect(projectFilesystem.readLines(proguardConfigFile))
        .andReturn(ImmutableList.<String>of());
    EasyMock.expect(projectFilesystem.readLines(proguardMappingFile)).andReturn(linesInMappingFile);
    ExecutionContext context = EasyMock.createMock(ExecutionContext.class);
    EasyMock.expect(context.getProjectFilesystem()).andReturn(projectFilesystem).anyTimes();
    EasyMock.replay(projectFilesystem, context);

    Predicate<String> requiredInPrimaryZipPredicate =
        splitZipStep.createRequiredInPrimaryZipPredicate(context);
    assertTrue(
        "Mapped class from primary list should be in primary.",
        requiredInPrimaryZipPredicate.apply("foo/bar/a.class"));
    assertTrue(
        "Unmapped class from primary list should be in primary.",
        requiredInPrimaryZipPredicate.apply("foo/bar/UnmappedPrimary.class"));
    assertTrue(
        "Mapped class from substring should be in primary.",
        requiredInPrimaryZipPredicate.apply("x/a.class"));
    assertTrue(
        "Unmapped class from substring should be in primary.",
        requiredInPrimaryZipPredicate.apply("foo/primary/UnmappedPackage.class"));
    assertFalse(
        "Mapped class with obfuscated name match should not be in primary.",
        requiredInPrimaryZipPredicate.apply("foo/bar/b.class"));
    assertFalse(
        "Unmapped class name should not randomly be in primary.",
        requiredInPrimaryZipPredicate.apply("foo/bar/UnmappedSecondary.class"));
    assertFalse(
        "Map class with obfuscated name substring should not be in primary.",
        requiredInPrimaryZipPredicate.apply("x/b.class"));

    EasyMock.verify(projectFilesystem, context);
  }
Пример #20
0
  /**
   * @param buildTargetName either a fully-qualified name or relative to the {@link ParseContext}.
   *     For example, inside {@code first-party/orca/orcaapp/BUILD}, which can be obtained by
   *     calling {@code ParseContext.forBaseName("first-party/orca/orcaapp")}, {@code
   *     //first-party/orca/orcaapp:assets} and {@code :assets} refer to the same target. However,
   *     from the command line the context is obtained by calling {@link
   *     ParseContext#fullyQualified()} and relative names are not recognized.
   * @param parseContext how targets should be interpreted, such in the context of a specific build
   *     file or only as fully-qualified names (as is the case for targets from the command line).
   */
  public BuildTarget parse(String buildTargetName, ParseContext parseContext)
      throws NoSuchBuildTargetException {
    Preconditions.checkNotNull(buildTargetName);
    Preconditions.checkNotNull(parseContext);

    for (String invalidSubstring : INVALID_BUILD_RULE_SUBSTRINGS) {
      if (buildTargetName.contains(invalidSubstring)) {
        throw new BuildTargetParseException(
            String.format("%s cannot contain %s", buildTargetName, invalidSubstring));
      }
    }

    if (buildTargetName.endsWith(BUILD_RULE_SEPARATOR)
        && parseContext.getType() != ParseContext.Type.VISIBILITY) {
      throw new BuildTargetParseException(
          String.format("%s cannot end with a colon", buildTargetName));
    }

    List<String> parts = ImmutableList.copyOf(BUILD_RULE_SEPARATOR_SPLITTER.split(buildTargetName));
    if (parts.size() != 2) {
      throw new BuildTargetParseException(
          String.format(
              "%s must contain exactly one colon (found %d)", buildTargetName, parts.size() - 1));
    }

    String baseName = parts.get(0).isEmpty() ? parseContext.getBaseName() : parts.get(0);
    String shortName = parts.get(1);

    String fullyQualifiedName = String.format("%s:%s", baseName, shortName);
    if (!fullyQualifiedName.startsWith(BUILD_RULE_PREFIX)) {
      throw new BuildTargetParseException(
          String.format("%s must start with %s", fullyQualifiedName, BUILD_RULE_PREFIX));
    }

    // Make sure the directory that contains the build file exists.
    String buildFileDirectory = baseName.substring(BUILD_RULE_PREFIX.length());
    String buildFilePath =
        (buildFileDirectory.isEmpty() ? "" : buildFileDirectory + "/") + BUILD_RULES_FILE_NAME;
    if (!projectFilesystem.exists(buildFileDirectory)) {
      if (parseContext.getType() == ParseContext.Type.BUILD_FILE
          && baseName.equals(parseContext.getBaseName())) {
        throw new BuildTargetParseException(
            String.format(
                "Internal error: Parsing in the context of %s, but %s does not exist",
                buildFilePath, buildFileDirectory));
      } else {
        throw NoSuchBuildTargetException.createForMissingDirectory(
            buildFileDirectory, buildTargetName, parseContext);
      }
    }

    // Make sure the build file exists.
    if (!projectFilesystem.exists(buildFilePath)) {
      if (parseContext.getType() == ParseContext.Type.BUILD_FILE
          && baseName.equals(parseContext.getBaseName())) {
        throw new BuildTargetParseException(
            String.format(
                "Internal error: Parsing in the context of %s, but %s does not exist",
                buildFilePath, buildFilePath));
      } else {
        throw NoSuchBuildTargetException.createForMissingBuildFile(
            buildFilePath, buildTargetName, parseContext);
      }
    }

    return new BuildTarget(baseName, shortName);
  }
Пример #21
0
  private boolean addSourceFolders(
      Module module,
      @Nullable BuildRule buildRule,
      @Nullable ImmutableList<SourceRoot> sourceRoots,
      boolean isTestSource) {
    if (buildRule == null || sourceRoots == null) {
      return false;
    }

    if (buildRule instanceof AndroidBinaryRule && sourceRoots.isEmpty()) {
      return false;
    }

    if (sourceRoots.isEmpty()) {
      // When there is a src_target, but no src_roots were specified, then the current directory is
      // treated as the SourceRoot. This is the common case when a project contains one folder of
      // Java source code with a build file for each Java package. For example, if the project's
      // only source folder were named "java/" and a build file in java/com/example/base/ contained
      // the an extremely simple set of build rules:
      //
      // java_library(
      //   name = 'base',
      //   srcs = glob(['*.java']),
      // }
      //
      // project_config(
      //   src_target = ':base',
      // )
      //
      // then the corresponding .iml file (in the same directory) should contain:
      //
      // <content url="file://$MODULE_DIR$">
      //   <sourceFolder url="file://$MODULE_DIR$" isTestSource="false"
      // packagePrefix="com.example.base" />
      //   <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
      //
      //   <!-- It will have an <excludeFolder> for every "subpackage" of com.example.base. -->
      //   <excludeFolder url="file://$MODULE_DIR$/util" />
      // </content>
      //
      // Note to prevent the <excludeFolder> elements from being included, the project_config()
      // rule should be:
      //
      // project_config(
      //   src_target = ':base',
      //   src_root_includes_subdirectories = True,
      // )
      //
      // Because developers who organize their code this way will have many build files, the default
      // values of project_config() assume this approach to help minimize the tedium in writing all
      // of those project_config() rules.
      String url = "file://$MODULE_DIR$";
      String packagePrefix = javaPackageFinder.findJavaPackageForPath(module.pathToImlFile);
      SourceFolder sourceFolder = new SourceFolder(url, isTestSource, packagePrefix);
      module.sourceFolders.add(sourceFolder);
    } else {
      for (SourceRoot sourceRoot : sourceRoots) {
        SourceFolder sourceFolder =
            new SourceFolder(
                String.format("file://$MODULE_DIR$/%s", sourceRoot.getName()), isTestSource);
        module.sourceFolders.add(sourceFolder);
      }
    }

    // Include <excludeFolder> elements, as appropriate.
    for (String relativePath : this.buildFileTree.getChildPaths(buildRule.getBuildTarget())) {
      String excludeFolderUrl = "file://$MODULE_DIR$/" + relativePath;
      SourceFolder excludeFolder = new SourceFolder(excludeFolderUrl, /* isTestSource */ false);
      module.excludeFolders.add(excludeFolder);
    }

    // If in the root of the project, specify ignored paths.
    if ("".equals(buildRule.getBuildTarget().getBasePathWithSlash())) {
      for (String path : projectFilesystem.getIgnorePaths()) {
        module.excludeFolders.add(
            new SourceFolder(String.format("file://$MODULE_DIR$/%s", path), false));
      }
    }

    return true;
  }
Пример #22
0
  private Module createModuleForProjectConfig(ProjectConfig projectConfig) throws IOException {
    BuildRule projectRule = projectConfig.getProjectRule();
    Buildable buildable = projectRule.getBuildable();
    Preconditions.checkState(
        projectRule instanceof JavaLibrary
            || buildable instanceof JavaLibrary
            || buildable instanceof JavaBinary
            || buildable instanceof AndroidLibrary
            || buildable instanceof AndroidResource
            || buildable instanceof AndroidBinary
            || buildable instanceof NdkLibrary,
        "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 */);

    addRootExcludes(module, projectConfig.getSrcRule(), projectFilesystem);

    // 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.getProperties().is(ANDROID);
    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).toString();

    DependentModule jdkDependency;
    if (isAndroidRule) {
      // android details
      if (projectRule.getBuildable() instanceof NdkLibrary) {
        NdkLibrary ndkLibrary = (NdkLibrary) projectRule.getBuildable();
        module.isAndroidLibraryProject = true;
        module.keystorePath = null;
        module.nativeLibs =
            Paths.get(relativePath).relativize(ndkLibrary.getLibraryPath()).toString();
      } else if (projectRule.getBuildable() instanceof AndroidResource) {
        AndroidResource androidResource = (AndroidResource) projectRule.getBuildable();
        module.resFolder = createRelativePath(androidResource.getRes(), target);
        module.isAndroidLibraryProject = true;
        module.keystorePath = null;
      } else if (projectRule.getBuildable() instanceof AndroidBinary) {
        AndroidBinary androidBinary = (AndroidBinary) projectRule.getBuildable();
        module.resFolder = null;
        module.isAndroidLibraryProject = false;
        KeystoreProperties keystoreProperties =
            KeystoreProperties.createFromPropertiesFile(
                androidBinary.getKeystore().getPathToStore(),
                androidBinary.getKeystore().getPathToPropertiesFile(),
                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.
        module.keystorePath =
            Paths.get(relativePath).relativize(keystoreProperties.getKeystore()).toString();
      } 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.get(basePathWithSlash).relativize(Paths.get(manifestPath)).toString();
          // 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.
    JavaLibrary javaLibrary = null;
    if (projectRule.getBuildable() instanceof JavaLibrary) {
      javaLibrary = (JavaLibrary) projectRule.getBuildable();
    } else if (projectRule instanceof JavaLibrary) {
      javaLibrary = (JavaLibrary) projectRule;
    }
    if (javaLibrary != null) {
      AnnotationProcessingData processingData = javaLibrary.getAnnotationProcessingData();

      Path annotationGenSrc = processingData.getGeneratedSourceFolderName();
      if (annotationGenSrc != null) {
        module.annotationGenPath =
            "/" + Paths.get(basePathWithSlash).relativize(annotationGenSrc).toString();
        module.annotationGenIsForTest = !hasSourceFoldersForSrcRule;
      }
    }

    return module;
  }