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());
}