@BeforeClass
  public static void createParser() {
    ProjectFilesystem filesystem = new FakeProjectFilesystem();
    FakeBuckConfig buckConfig = new FakeBuckConfig();
    ParserConfig parserConfig = new ParserConfig(buckConfig);
    PythonBuckConfig pythonBuckConfig = new PythonBuckConfig(buckConfig, new ExecutableFinder());

    ImmutableSet<Description<?>> descriptions =
        ImmutableSet.of(
            new RemoteFileDescription(new ExplodingDownloader()), new PrebuiltJarDescription());

    DefaultProjectBuildFileParserFactory parserFactory =
        new DefaultProjectBuildFileParserFactory(
            filesystem.getRootPath(),
            pythonBuckConfig.getPythonInterpreter(),
            parserConfig.getAllowEmptyGlobs(),
            parserConfig.getBuildFileName(),
            parserConfig.getDefaultIncludes(),
            descriptions);
    buildFileParser =
        parserFactory.createParser(
            new TestConsole(),
            ImmutableMap.<String, String>of(),
            BuckEventBusFactory.newInstance());
  }
  @VisibleForTesting
  static Builder createBuilder(
      BuckConfig config,
      ProcessExecutor processExecutor,
      AndroidDirectoryResolver androidDirectoryResolver,
      Optional<Path> testTempDirOverride)
      throws InterruptedException, IOException {

    Platform platform = Platform.detect();

    AndroidBuckConfig androidConfig = new AndroidBuckConfig(config, platform);
    Optional<String> ndkVersion = androidConfig.getNdkVersion();
    // If a NDK version isn't specified, we've got to reach into the runtime environment to find
    // out which one we will end up using.
    if (!ndkVersion.isPresent()) {
      ndkVersion = androidDirectoryResolver.getNdkVersion();
    }

    AppleConfig appleConfig = new AppleConfig(config);
    final AppleBundle.DebugInfoFormat defaultDebugInfoFormat = appleConfig.getDebugInfoFormat();

    ImmutableMap.Builder<Flavor, AppleCxxPlatform> platformFlavorsToAppleCxxPlatformsBuilder =
        ImmutableMap.builder();
    buildAppleCxxPlatforms(
        appleConfig.getAppleDeveloperDirectorySupplier(processExecutor),
        appleConfig.getExtraToolchainPaths(),
        appleConfig.getExtraPlatformPaths(),
        config,
        appleConfig,
        platformFlavorsToAppleCxxPlatformsBuilder);
    ImmutableMap<Flavor, AppleCxxPlatform> platformFlavorsToAppleCxxPlatforms =
        platformFlavorsToAppleCxxPlatformsBuilder.build();

    // Setup the NDK C/C++ platforms.
    Optional<Path> ndkRoot = androidDirectoryResolver.findAndroidNdkDir();
    ImmutableMap.Builder<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> ndkCxxPlatformsBuilder =
        ImmutableMap.builder();
    if (ndkRoot.isPresent()) {
      NdkCxxPlatforms.Compiler.Type compilerType =
          androidConfig.getNdkCompiler().or(NdkCxxPlatforms.DEFAULT_COMPILER_TYPE);
      String gccVersion = androidConfig.getNdkGccVersion().or(NdkCxxPlatforms.DEFAULT_GCC_VERSION);
      NdkCxxPlatforms.Compiler compiler =
          ImmutableNdkCxxPlatforms.Compiler.builder()
              .setType(compilerType)
              .setVersion(
                  compilerType == NdkCxxPlatforms.Compiler.Type.GCC
                      ? gccVersion
                      : androidConfig
                          .getNdkClangVersion()
                          .or(NdkCxxPlatforms.DEFAULT_CLANG_VERSION))
              .setGccVersion(gccVersion)
              .build();
      ndkCxxPlatformsBuilder.putAll(
          NdkCxxPlatforms.getPlatforms(
              new ProjectFilesystem(ndkRoot.get()),
              compiler,
              androidConfig.getNdkCxxRuntime().or(NdkCxxPlatforms.DEFAULT_CXX_RUNTIME),
              androidConfig.getNdkAppPlatform().or(NdkCxxPlatforms.DEFAULT_TARGET_APP_PLATFORM),
              androidConfig.getNdkCpuAbis().or(NdkCxxPlatforms.DEFAULT_CPU_ABIS),
              platform));
    }
    ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> ndkCxxPlatforms =
        ndkCxxPlatformsBuilder.build();

    // Construct the C/C++ config wrapping the buck config.
    CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(config);
    ImmutableMap.Builder<Flavor, CxxPlatform> cxxPlatformsBuilder = ImmutableMap.builder();

    // If an Android NDK is present, add platforms for that.  This is mostly useful for
    // testing our Android NDK support for right now.
    for (NdkCxxPlatform ndkCxxPlatform : ndkCxxPlatforms.values()) {
      cxxPlatformsBuilder.put(
          ndkCxxPlatform.getCxxPlatform().getFlavor(), ndkCxxPlatform.getCxxPlatform());
    }

    for (Map.Entry<Flavor, AppleCxxPlatform> entry :
        platformFlavorsToAppleCxxPlatforms.entrySet()) {
      cxxPlatformsBuilder.put(entry.getKey(), entry.getValue().getCxxPlatform());
    }

    // Add the default, config-defined C/C++ platform.
    CxxPlatform systemDefaultCxxPlatform = DefaultCxxPlatforms.build(platform, cxxBuckConfig);
    cxxPlatformsBuilder.put(systemDefaultCxxPlatform.getFlavor(), systemDefaultCxxPlatform);
    ImmutableMap<Flavor, CxxPlatform> cxxPlatformsMap = cxxPlatformsBuilder.build();

    // Get the default platform from config.
    CxxPlatform defaultCxxPlatform =
        CxxPlatforms.getConfigDefaultCxxPlatform(
            cxxBuckConfig, cxxPlatformsMap, systemDefaultCxxPlatform);

    // Add platforms for each cxx flavor obtained from the buck config files
    // from sections of the form cxx#{flavor name}
    ImmutableSet<Flavor> cxxFlavors = CxxBuckConfig.getCxxFlavors(config);
    for (Flavor flavor : cxxFlavors) {
      CxxBuckConfig flavoredCxxBuckConfig = new CxxBuckConfig(config, flavor);
      CxxPlatform defaultPlatformForFlavor =
          CxxPlatforms.getConfigDefaultCxxPlatform(
              flavoredCxxBuckConfig, cxxPlatformsMap, systemDefaultCxxPlatform);
      cxxPlatformsBuilder.put(
          flavor,
          CxxPlatforms.copyPlatformWithFlavorAndConfig(
              defaultPlatformForFlavor, flavoredCxxBuckConfig, flavor));
    }

    cxxPlatformsMap = cxxPlatformsBuilder.build();

    // Build up the final list of C/C++ platforms.
    FlavorDomain<CxxPlatform> cxxPlatforms = new FlavorDomain<>("C/C++ platform", cxxPlatformsMap);

    DBuckConfig dBuckConfig = new DBuckConfig(config);

    ReactNativeBuckConfig reactNativeBuckConfig = new ReactNativeBuckConfig(config);

    RustBuckConfig rustBuckConfig = new RustBuckConfig(config);

    GoBuckConfig goBuckConfig = new GoBuckConfig(config, processExecutor);

    HalideBuckConfig halideBuckConfig = new HalideBuckConfig(config);

    ProGuardConfig proGuardConfig = new ProGuardConfig(config);

    PythonBuckConfig pyConfig = new PythonBuckConfig(config, new ExecutableFinder());
    ImmutableList<PythonPlatform> pythonPlatformsList =
        pyConfig.getPythonPlatforms(processExecutor);
    ImmutableMap.Builder<Flavor, PythonPlatform> pythonPlatformsMapBuilder = ImmutableMap.builder();
    for (PythonPlatform pythonPlatform : pythonPlatformsList) {
      pythonPlatformsMapBuilder.put(pythonPlatform.getFlavor(), pythonPlatform);
    }
    ImmutableMap<Flavor, PythonPlatform> pythonPlatformsMap = pythonPlatformsMapBuilder.build();
    FlavorDomain<PythonPlatform> pythonPlatforms =
        new FlavorDomain<>("Python Platform", pythonPlatformsMap);
    PythonBinaryDescription pythonBinaryDescription =
        new PythonBinaryDescription(pyConfig, pythonPlatforms, defaultCxxPlatform, cxxPlatforms);

    // Look up the timeout to apply to entire test rules.
    Optional<Long> defaultTestRuleTimeoutMs = config.getLong("test", "rule_timeout");

    // Prepare the downloader if we're allowing mid-build downloads
    Downloader downloader;
    DownloadConfig downloadConfig = new DownloadConfig(config);
    if (downloadConfig.isDownloadAtRuntimeOk()) {
      downloader =
          StackedDownloader.createFromConfig(
              config, androidDirectoryResolver.findAndroidSdkDirSafe());
    } else {
      // Or just set one that blows up
      downloader = new ExplodingDownloader();
    }

    Builder builder = builder();

    JavaBuckConfig javaConfig = new JavaBuckConfig(config);
    JavacOptions defaultJavacOptions = javaConfig.getDefaultJavacOptions();

    InferBuckConfig inferBuckConfig = new InferBuckConfig(config);

    CxxBinaryDescription cxxBinaryDescription =
        new CxxBinaryDescription(
            inferBuckConfig, defaultCxxPlatform, cxxPlatforms, cxxBuckConfig.getPreprocessMode());

    CxxLibraryDescription cxxLibraryDescription =
        new CxxLibraryDescription(
            cxxBuckConfig, inferBuckConfig, cxxPlatforms, cxxBuckConfig.getPreprocessMode());

    CodeSignIdentityStore codeSignIdentityStore = CodeSignIdentityStore.fromSystem(processExecutor);
    ProvisioningProfileStore provisioningProfileStore =
        ProvisioningProfileStore.fromSearchPath(appleConfig.getProvisioningProfileSearchPath());

    AppleLibraryDescription appleLibraryDescription =
        new AppleLibraryDescription(
            cxxLibraryDescription,
            cxxPlatforms,
            platformFlavorsToAppleCxxPlatforms,
            defaultCxxPlatform,
            codeSignIdentityStore,
            provisioningProfileStore,
            defaultDebugInfoFormat);
    builder.register(appleLibraryDescription);

    AppleBinaryDescription appleBinaryDescription =
        new AppleBinaryDescription(
            cxxBinaryDescription,
            cxxPlatforms,
            platformFlavorsToAppleCxxPlatforms,
            defaultCxxPlatform,
            codeSignIdentityStore,
            provisioningProfileStore,
            defaultDebugInfoFormat);
    builder.register(appleBinaryDescription);

    // Create an executor service exclusively for the smart dexing step.
    ListeningExecutorService dxExecutorService =
        MoreExecutors.listeningDecorator(
            Executors.newFixedThreadPool(
                SmartDexingStep.determineOptimalThreadCount(),
                new CommandThreadFactory("SmartDexing")));

    builder.register(new AndroidAarDescription(new AndroidManifestDescription(), ndkCxxPlatforms));
    builder.register(
        new AndroidBinaryDescription(
            defaultJavacOptions, proGuardConfig, ndkCxxPlatforms, dxExecutorService));
    builder.register(new AndroidBuildConfigDescription(defaultJavacOptions));
    builder.register(
        new AndroidInstrumentationApkDescription(
            proGuardConfig, defaultJavacOptions, ndkCxxPlatforms, dxExecutorService));
    builder.register(new AndroidInstrumentationTestDescription(defaultTestRuleTimeoutMs));
    builder.register(new AndroidLibraryDescription(defaultJavacOptions));
    builder.register(new AndroidManifestDescription());
    builder.register(new AndroidPrebuiltAarDescription(defaultJavacOptions));
    builder.register(new AndroidReactNativeLibraryDescription(reactNativeBuckConfig));
    builder.register(new AndroidResourceDescription());
    builder.register(new ApkGenruleDescription());
    builder.register(new AppleAssetCatalogDescription());
    builder.register(new ApplePackageDescription());
    AppleBundleDescription appleBundleDescription =
        new AppleBundleDescription(
            appleBinaryDescription,
            appleLibraryDescription,
            cxxPlatforms,
            platformFlavorsToAppleCxxPlatforms,
            defaultCxxPlatform,
            codeSignIdentityStore,
            provisioningProfileStore,
            defaultDebugInfoFormat);
    builder.register(appleBundleDescription);
    builder.register(new AppleResourceDescription());
    builder.register(
        new AppleTestDescription(
            appleConfig,
            appleBundleDescription,
            appleLibraryDescription,
            cxxPlatforms,
            platformFlavorsToAppleCxxPlatforms,
            defaultCxxPlatform,
            codeSignIdentityStore,
            provisioningProfileStore,
            appleConfig.getAppleDeveloperDirectorySupplier(processExecutor)));
    builder.register(new CoreDataModelDescription());
    builder.register(new CSharpLibraryDescription());
    builder.register(cxxBinaryDescription);
    builder.register(cxxLibraryDescription);
    builder.register(
        new CxxPythonExtensionDescription(pythonPlatforms, cxxBuckConfig, cxxPlatforms));
    builder.register(
        new CxxTestDescription(
            cxxBuckConfig, defaultCxxPlatform, cxxPlatforms, defaultTestRuleTimeoutMs));
    builder.register(new DBinaryDescription(dBuckConfig, defaultCxxPlatform));
    builder.register(new DLibraryDescription(dBuckConfig));
    builder.register(
        new DTestDescription(dBuckConfig, defaultTestRuleTimeoutMs, defaultCxxPlatform));
    builder.register(new ExportFileDescription());
    builder.register(new GenruleDescription());
    builder.register(new GenAidlDescription());
    builder.register(new GoBinaryDescription(goBuckConfig, defaultCxxPlatform));
    builder.register(new GoLibraryDescription(goBuckConfig));
    builder.register(
        new GoTestDescription(goBuckConfig, defaultTestRuleTimeoutMs, defaultCxxPlatform));
    builder.register(new GwtBinaryDescription());
    builder.register(
        new HalideLibraryDescription(
            cxxPlatforms, cxxBuckConfig.getPreprocessMode(), halideBuckConfig));
    builder.register(new IosReactNativeLibraryDescription(reactNativeBuckConfig));
    builder.register(new JavaBinaryDescription(defaultJavacOptions, defaultCxxPlatform));
    builder.register(new JavaLibraryDescription(defaultJavacOptions));
    builder.register(
        new JavaTestDescription(
            defaultJavacOptions,
            defaultTestRuleTimeoutMs,
            defaultCxxPlatform,
            testTempDirOverride));
    builder.register(new KeystoreDescription());
    builder.register(new NdkLibraryDescription(ndkVersion, ndkCxxPlatforms));
    OCamlBuckConfig ocamlBuckConfig = new OCamlBuckConfig(platform, config);
    builder.register(new OCamlBinaryDescription(ocamlBuckConfig));
    builder.register(new OCamlLibraryDescription(ocamlBuckConfig));
    builder.register(new PrebuiltCxxLibraryDescription(cxxPlatforms));
    builder.register(new PrebuiltDotNetLibraryDescription());
    builder.register(new PrebuiltJarDescription());
    builder.register(new PrebuiltNativeLibraryDescription());
    builder.register(new PrebuiltOCamlLibraryDescription());
    builder.register(new PrebuiltPythonLibraryDescription());
    builder.register(new ProjectConfigDescription());
    builder.register(pythonBinaryDescription);
    builder.register(new PythonLibraryDescription());
    builder.register(
        new PythonTestDescription(
            pythonBinaryDescription,
            pyConfig,
            pythonPlatforms,
            defaultCxxPlatform,
            defaultTestRuleTimeoutMs,
            cxxPlatforms));
    builder.register(new RemoteFileDescription(downloader));
    builder.register(
        new RobolectricTestDescription(
            defaultJavacOptions,
            defaultTestRuleTimeoutMs,
            defaultCxxPlatform,
            testTempDirOverride));
    builder.register(new RustBinaryDescription(rustBuckConfig));
    builder.register(new RustLibraryDescription(rustBuckConfig));
    builder.register(new ShBinaryDescription());
    builder.register(new ShTestDescription());
    ThriftBuckConfig thriftBuckConfig = new ThriftBuckConfig(config);
    builder.register(
        new ThriftLibraryDescription(
            thriftBuckConfig,
            ImmutableList.of(
                new ThriftJavaEnhancer(thriftBuckConfig, defaultJavacOptions),
                new ThriftCxxEnhancer(thriftBuckConfig, cxxLibraryDescription, /* cpp2 */ false),
                new ThriftCxxEnhancer(thriftBuckConfig, cxxLibraryDescription, /* cpp2 */ true),
                new ThriftPythonEnhancer(thriftBuckConfig, ThriftPythonEnhancer.Type.NORMAL),
                new ThriftPythonEnhancer(thriftBuckConfig, ThriftPythonEnhancer.Type.TWISTED))));
    builder.register(new XcodePostbuildScriptDescription());
    builder.register(new XcodePrebuildScriptDescription());
    builder.register(new XcodeWorkspaceConfigDescription());
    builder.register(new ZipDescription());

    builder.setCxxPlatforms(cxxPlatforms);
    builder.setDefaultCxxPlatform(defaultCxxPlatform);

    return builder;
  }