<commit_before>package coursier
package cli

import java.io.{ByteArrayInputStream, ByteArrayOutputStream, File, FileInputStream, IOException}
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files
import java.nio.file.attribute.PosixFilePermission
import java.util.Properties
import java.util.zip.{ZipEntry, ZipInputStream, ZipOutputStream}

import caseapp._
import coursier.cli.options.BootstrapOptions
import coursier.cli.util.Zip
import coursier.internal.FileUtil

import scala.collection.JavaConverters._

object Bootstrap extends CaseApp[BootstrapOptions] {

  def run(options: BootstrapOptions, args: RemainingArgs): Unit = {

    val helper = new Helper(
      options.options.common,
      args.all,
      isolated = options.options.isolated,
      warnBaseLoaderNotFound = false
    )

    val output0 = new File(options.options.output)
    if (!options.options.force && output0.exists()) {
      Console.err.println(s"Error: ${options.options.output} already exists, use -f option to force erasing it.")
      sys.exit(1)
    }

    val mainClass =
      if (options.options.mainClass.isEmpty)
        helper.retainedMainClass
      else
        options.options.mainClass

    if (options.options.native) {

      val files = helper.fetch(
        sources = false,
        javadoc = false,
        artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false)
      )

      val log: String => Unit =
        if (options.options.common.verbosityLevel >= 0)
          s => Console.err.println(s)
        else
          _ => ()

      val tmpDir = new File(options.options.target)

      try {
        coursier.extra.Native.create(
          mainClass,
          files,
          output0,
          tmpDir,
          log,
          verbosity = options.options.common.verbosityLevel
        )
      } finally {
        if (!options.options.keepTarget)
          coursier.extra.Native.deleteRecursive(tmpDir)
      }
    } else {

      val (validProperties, wrongProperties) = options.options.property.partition(_.contains("="))
      if (wrongProperties.nonEmpty) {
        Console.err.println(s"Wrong -P / --property option(s):\n${wrongProperties.mkString("\n")}")
        sys.exit(255)
      }

      val properties0 = validProperties.map { s =>
        val idx = s.indexOf('=')
        assert(idx >= 0)
        (s.take(idx), s.drop(idx + 1))
      }

      val bootstrapJar =
        Option(Thread.currentThread().getContextClassLoader.getResourceAsStream("bootstrap.jar")) match {
          case Some(is) => FileUtil.readFully(is)
          case None =>
            Console.err.println(s"Error: bootstrap JAR not found")
            sys.exit(1)
        }

      val isolatedDeps = options.options.isolated.isolatedDeps(options.options.common.scalaVersion)

      val (_, isolatedArtifactFiles) =
        options.options.isolated.targets.foldLeft((Vector.empty[String], Map.empty[String, (Seq[String], Seq[File])])) {
          case ((done, acc), target) =>
            val subRes = helper.res.subset(isolatedDeps.getOrElse(target, Nil).toSet)

            val (done0, subUrls, subFiles) =
              if (options.options.standalone) {
                val subFiles0 = helper.fetch(
                  sources = false,
                  javadoc = false,
                  artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false),
                  subset = isolatedDeps.getOrElse(target, Seq.empty).toSet
                )

                (done, Nil, subFiles0)
              } else {
                val subArtifacts0 = subRes.dependencyArtifacts.map(_._2)
                val artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false)
                val subArtifacts =
                  if (artifactTypes("*"))
                    subArtifacts0
                  else
                    subArtifacts0.filter(a => artifactTypes(a.`type`))
                val filteredSubArtifacts = subArtifacts.map(_.url).diff(done)
                (done ++ filteredSubArtifacts, filteredSubArtifacts, Nil)
              }

            val updatedAcc = acc + (target -> (subUrls, subFiles))

            (done0, updatedAcc)
        }

      val (urls, files) =
        helper.fetchMap(
        sources = false,
        javadoc = false,
        artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false)
      ).toList.foldLeft((List.empty[String], List.empty[File])){
        case ((urls, files), (url, file)) =>
          if (options.options.standalone) (urls, file :: files)
          else if (url.startsWith("file:/")) (urls, file :: files)
          else (url :: urls, files)
      }
<commit_msg>Simplify bootstrap artifacts handling a bit (#810)<commit_after>package coursier
package cli

import java.io.{ByteArrayInputStream, ByteArrayOutputStream, File, FileInputStream, IOException}
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files
import java.nio.file.attribute.PosixFilePermission
import java.util.Properties
import java.util.zip.{ZipEntry, ZipInputStream, ZipOutputStream}

import caseapp._
import coursier.cli.options.BootstrapOptions
import coursier.cli.util.Zip
import coursier.internal.FileUtil

import scala.collection.JavaConverters._

object Bootstrap extends CaseApp[BootstrapOptions] {

  def run(options: BootstrapOptions, args: RemainingArgs): Unit = {

    val helper = new Helper(
      options.options.common,
      args.all,
      isolated = options.options.isolated,
      warnBaseLoaderNotFound = false
    )

    val output0 = new File(options.options.output)
    if (!options.options.force && output0.exists()) {
      Console.err.println(s"Error: ${options.options.output} already exists, use -f option to force erasing it.")
      sys.exit(1)
    }

    val mainClass =
      if (options.options.mainClass.isEmpty)
        helper.retainedMainClass
      else
        options.options.mainClass

    if (options.options.native) {

      val files = helper.fetch(
        sources = false,
        javadoc = false,
        artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false)
      )

      val log: String => Unit =
        if (options.options.common.verbosityLevel >= 0)
          s => Console.err.println(s)
        else
          _ => ()

      val tmpDir = new File(options.options.target)

      try {
        coursier.extra.Native.create(
          mainClass,
          files,
          output0,
          tmpDir,
          log,
          verbosity = options.options.common.verbosityLevel
        )
      } finally {
        if (!options.options.keepTarget)
          coursier.extra.Native.deleteRecursive(tmpDir)
      }
    } else {

      val (validProperties, wrongProperties) = options.options.property.partition(_.contains("="))
      if (wrongProperties.nonEmpty) {
        Console.err.println(s"Wrong -P / --property option(s):\n${wrongProperties.mkString("\n")}")
        sys.exit(255)
      }

      val properties0 = validProperties.map { s =>
        val idx = s.indexOf('=')
        assert(idx >= 0)
        (s.take(idx), s.drop(idx + 1))
      }

      val bootstrapJar =
        Option(Thread.currentThread().getContextClassLoader.getResourceAsStream("bootstrap.jar")) match {
          case Some(is) => FileUtil.readFully(is)
          case None =>
            Console.err.println(s"Error: bootstrap JAR not found")
            sys.exit(1)
        }

      val isolatedDeps = options.options.isolated.isolatedDeps(options.options.common.scalaVersion)

      val (_, isolatedArtifactFiles) =
        options.options.isolated.targets.foldLeft((Vector.empty[String], Map.empty[String, (Seq[String], Seq[File])])) {
          case ((done, acc), target) =>

            // TODO Add non regression test checking that optional artifacts indeed land in the isolated loader URLs

            val m = helper.fetchMap(
              sources = false,
              javadoc = false,
              artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false),
              subset = isolatedDeps.getOrElse(target, Seq.empty).toSet
            )

            val (done0, subUrls, subFiles) =
              if (options.options.standalone) {
                val subFiles0 = m.values.toSeq
                (done, Nil, subFiles0)
              } else {
                val filteredSubArtifacts = m.keys.toSeq.diff(done)
                (done ++ filteredSubArtifacts, filteredSubArtifacts, Nil)
              }

            val updatedAcc = acc + (target -> (subUrls, subFiles))

            (done0, updatedAcc)
        }

      val (urls, files) =
        helper.fetchMap(
        sources = false,
        javadoc = false,
        artifactTypes = options.artifactOptions.artifactTypes(sources = false, javadoc = false)
      ).toList.foldLeft((List.empty[String], List.empty[File])){
        case ((urls, files), (url, file)) =>
          if (options.options.standalone) (urls, file :: files)
          else if (url.startsWith("file:/")) (urls, file :: files)
          else (url :: urls, files)
      }

{'fraction_non_alphanumeric': 0.11031017911751857, 'fraction_numerical': 0.004477937964176496, 'mean_word_length': 2.3033910533910533, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3744271', 'n_tokens_mistral': 2688, 'n_tokens_neox': 2540, 'n_words': 538}