public BuildRule createBuildRule()

in src/com/facebook/buck/features/python/PythonTestDescription.java [242:475]


  public BuildRule createBuildRule(
      BuildRuleCreationContextWithTargetGraph context,
      BuildTarget buildTarget,
      BuildRuleParams params,
      PythonTestDescriptionArg args) {

    FlavorDomain<PythonPlatform> pythonPlatforms =
        toolchainProvider
            .getByName(
                PythonPlatformsProvider.DEFAULT_NAME,
                buildTarget.getTargetConfiguration(),
                PythonPlatformsProvider.class)
            .getPythonPlatforms();

    ActionGraphBuilder graphBuilder = context.getActionGraphBuilder();
    PythonPlatform pythonPlatform =
        pythonPlatforms
            .getValue(buildTarget)
            .orElse(
                pythonPlatforms.getValue(
                    args.getPlatform()
                        .<Flavor>map(InternalFlavor::of)
                        .orElse(pythonPlatforms.getFlavors().iterator().next())));
    CxxPlatform cxxPlatform =
        getCxxPlatform(buildTarget, args)
            .resolve(graphBuilder, buildTarget.getTargetConfiguration());
    Optional<ImmutableMap<BuildTarget, Version>> selectedVersions =
        context.getTargetGraph().get(buildTarget).getSelectedVersions();

    ImmutableMap<Path, SourcePath> srcs =
        PythonUtil.parseModules(
            buildTarget, graphBuilder, pythonPlatform, cxxPlatform, selectedVersions, args);

    ImmutableMap<Path, SourcePath> resources =
        PythonUtil.parseResources(
            buildTarget, graphBuilder, pythonPlatform, cxxPlatform, selectedVersions, args);

    // Convert the passed in module paths into test module names.
    ImmutableSet.Builder<String> testModulesBuilder = ImmutableSet.builder();
    for (Path name : srcs.keySet()) {
      testModulesBuilder.add(PythonUtil.toModuleName(buildTarget, name.toString()));
    }
    ImmutableSet<String> testModules = testModulesBuilder.build();

    ProjectFilesystem projectFilesystem = context.getProjectFilesystem();

    // Construct a build rule to generate the test modules list source file and
    // add it to the build.
    BuildRule testModulesBuildRule =
        createTestModulesSourceBuildRule(
            buildTarget,
            projectFilesystem,
            getTestModulesListPath(buildTarget, projectFilesystem),
            testModules);
    graphBuilder.addToIndex(testModulesBuildRule);

    Optional<PythonTestRunner> testRunner = maybeGetTestRunner(args, graphBuilder);
    Path testMainName = getTestMainPath(graphBuilder.getSourcePathResolver(), testRunner);
    String mainModule =
        testRunner
            .map(runner -> runner.getMainModule())
            .orElse(
                args.getMainModule()
                    .orElseGet(
                        () -> PythonUtil.toModuleName(buildTarget, testMainName.toString())));

    ImmutableSortedMap<Path, SourcePath> modules =
        ImmutableSortedMap.<Path, SourcePath>naturalOrder()
            .put(getTestModulesListName(), testModulesBuildRule.getSourcePathToOutput())
            .put(
                testMainName,
                testRunner
                    .map(runner -> runner.getSrc())
                    .orElseGet(() -> requireTestMain(buildTarget, projectFilesystem, graphBuilder)))
            .putAll(srcs)
            .build();

    ImmutableList<BuildRule> deps =
        RichStream.from(
                PythonUtil.getDeps(
                    pythonPlatform, cxxPlatform, args.getDeps(), args.getPlatformDeps()))
            .concat(args.getNeededCoverage().stream().map(NeededCoverageSpec::getBuildTarget))
            .map(graphBuilder::getRule)
            .collect(ImmutableList.toImmutableList());

    // Build up the list of everything going into the python test.
    PythonPackagable root =
        ImmutablePythonBinaryPackagable.of(
            buildTarget,
            projectFilesystem,
            deps,
            Optional.of(PythonMappedComponents.of(modules)),
            Optional.of(PythonMappedComponents.of(ImmutableSortedMap.copyOf(resources))),
            args.getZipSafe());

    CellPathResolver cellRoots = context.getCellPathResolver();
    StringWithMacrosConverter macrosConverter =
        StringWithMacrosConverter.of(
            buildTarget,
            cellRoots.getCellNameResolver(),
            graphBuilder,
            PythonUtil.macroExpanders(context.getTargetGraph()));
    PythonPackageComponents allComponents =
        PythonUtil.getAllComponents(
            cellRoots,
            buildTarget,
            projectFilesystem,
            params,
            graphBuilder,
            root,
            pythonPlatform,
            cxxBuckConfig,
            cxxPlatform,
            args.getLinkerFlags().stream()
                .map(macrosConverter::convert)
                .collect(ImmutableList.toImmutableList()),
            pythonBuckConfig.getNativeLinkStrategy(),
            args.getPreloadDeps(),
            args.getCompile().orElse(false));

    // Build the PEX using a python binary rule with the minimum dependencies.
    buildTarget.assertUnflavored();
    PythonBinary binary =
        binaryDescription.createPackageRule(
            cellRoots,
            buildTarget.withAppendedFlavors(BINARY_FLAVOR),
            projectFilesystem,
            params,
            graphBuilder,
            pythonPlatform,
            cxxPlatform,
            mainModule,
            args.getExtension(),
            allComponents,
            args.getBuildArgs(),
            args.getPackageStyle().orElse(pythonBuckConfig.getPackageStyle()),
            PythonUtil.getPreloadNames(graphBuilder, cxxPlatform, args.getPreloadDeps()));
    graphBuilder.addToIndex(binary);

    if (testRunner.isPresent()) {
      Preconditions.checkState(
          args.getSpecs().isPresent(), "Specs must be present when runner is present.");
      return PythonTestX.from(
          buildTarget,
          projectFilesystem,
          params,
          binary,
          args.getLabels(),
          args.getContacts(),
          TestRunnerSpecCoercer.coerce(args.getSpecs().get(), macrosConverter));
    }

    ImmutableList.Builder<Pair<Float, ImmutableSet<Path>>> neededCoverageBuilder =
        ImmutableList.builder();
    for (NeededCoverageSpec coverageSpec : args.getNeededCoverage()) {
      BuildRule buildRule = graphBuilder.getRule(coverageSpec.getBuildTarget());
      if (deps.contains(buildRule) && buildRule instanceof PythonLibrary) {
        PythonLibrary pythonLibrary = (PythonLibrary) buildRule;
        ImmutableSortedSet<Path> paths;
        if (coverageSpec.getPathName().isPresent()) {
          Path path =
              coverageSpec
                  .getBuildTarget()
                  .getCellRelativeBasePath()
                  .getPath()
                  .toPath(projectFilesystem.getFileSystem())
                  .resolve(coverageSpec.getPathName().get());
          if (!pythonLibrary
              .getPythonModules(pythonPlatform, cxxPlatform, graphBuilder)
              .map(PythonMappedComponents::getComponents)
              .map(Map::keySet)
              .orElseGet(ImmutableSet::of)
              .contains(path)) {
            throw new HumanReadableException(
                "%s: path %s specified in needed_coverage not found in target %s",
                buildTarget, path, buildRule.getBuildTarget());
          }
          paths = ImmutableSortedSet.of(path);
        } else {
          paths =
              pythonLibrary
                  .getPythonModules(pythonPlatform, cxxPlatform, graphBuilder)
                  .map(PythonMappedComponents::getComponents)
                  .map(ImmutableSortedMap::keySet)
                  .orElseGet(ImmutableSortedSet::of);
        }
        neededCoverageBuilder.add(
            new Pair<>(coverageSpec.getNeededCoverageRatioPercentage() / 100.f, paths));
      } else {
        throw new HumanReadableException(
            "%s: needed_coverage requires a python library dependency. Found %s instead",
            buildTarget, buildRule);
      }
    }

    Function<BuildRuleResolver, ImmutableMap<String, Arg>> testEnv =
        (ruleResolverInner) ->
            ImmutableMap.copyOf(Maps.transformValues(args.getEnv(), macrosConverter::convert));

    // Additional CXX Targets used to generate CXX coverage.
    ImmutableSet<UnflavoredBuildTarget> additionalCoverageTargets =
        RichStream.from(args.getAdditionalCoverageTargets())
            .map(BuildTarget::getUnflavoredBuildTarget)
            .collect(ImmutableSet.toImmutableSet());
    ImmutableSortedSet<SourcePath> additionalCoverageSourcePaths =
        additionalCoverageTargets.isEmpty()
            ? ImmutableSortedSet.of()
            : binary
                .getRuntimeDeps(graphBuilder)
                .filter(
                    target -> additionalCoverageTargets.contains(target.getUnflavoredBuildTarget()))
                .map(DefaultBuildTargetSourcePath::of)
                .collect(ImmutableSortedSet.toImmutableSortedSet(Ordering.natural()));

    // Generate and return the python test rule, which depends on the python binary rule above.
    return PythonTest.from(
        buildTarget,
        projectFilesystem,
        params,
        graphBuilder,
        testEnv,
        binary,
        args.getLabels(),
        neededCoverageBuilder.build(),
        additionalCoverageSourcePaths,
        args.getTestRuleTimeoutMs()
            .map(Optional::of)
            .orElse(
                cxxBuckConfig
                    .getDelegate()
                    .getView(TestBuckConfig.class)
                    .getDefaultTestRuleTimeoutMs()),
        args.getContacts());
  }