[
  {
    "path": ".dockerignore",
    "content": "./out\n.bloop\n.bsp\n.metals\n.scala-build\n.scala\ngifs\nwebsite\n.github\nDockerfile\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Scala Steward: Reformat with scalafmt 3.7.3\n34ae72e8cf5878dccb44ac3f864cbf4892f18354\n\n# Scala Steward: Reformat with scalafmt 3.8.2\n6d2639650f6e0b941840b995cc30b7de7afff5a0\n\n# Scala Steward: Reformat with scalafmt 3.8.3\n52b913a12d8abdff1b340db668ebe38c59b423e4\n\n# Scala Steward: Reformat with scalafmt 3.8.5\n74f069ccdaa91872cb77dc1f902752221d588db1\n\n# Scala Steward: Reformat with scalafmt 3.10.7\nf45699aa27d21bfe09e087a6d355ab6abf1ff0e6\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Version(s)**\nPlease provide the version(s) of Scala CLI that is affected by this bug\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nPlease provide us with steps on how to reproduce the bug. Code snippets (or link to the used codebase) and used commands are especially useful.\n\n**Expected behaviour**\nA clear and concise description of what you expected to happen.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/new_release.md",
    "content": "---\nname: Plan a release\nabout: Plan releasing a new version\ntitle: \"Release v[VERSION]\"\nlabels: internal\nassignees: ''\n\n---\n\n**What version number is to be released?**\nv[VERSION]\n\n**What is the estimated date for this release?**\n[RELEASE DATE]\n\n**Release procedure**\nPlease refer to the [Release Procedure doc](https://github.com/VirtusLab/scala-cli/blob/main/.github/release/release-procedure.md).\n\n**Release notes**\nPlease remember to create a pull request with a draft of the release notes in the [Release Notes History doc](https://github.com/VirtusLab/scala-cli/blob/main/website/docs/release_notes.md).\nPlease make sure the notes render correctly on [the website](https://scala-cli.virtuslab.org/docs/release_notes).\nThat includes swapping out GitHub-idiomatic @mentions of users, links to PRs, issues, etc. \nYou can do that using the [regexes provided in this doc](https://github.com/VirtusLab/scala-cli/blob/main/.github/release/release-notes-regexes.md)"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.md",
    "content": "---\nname: Other\nabout: Request a change that is neither a new feature nor related to a bug.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Version(s)**\nPlease provide the version(s) of Scala CLI for which the proposed change is necessary (if at all relevant).\n\n**Describe what needs to be done and why**\nA clear and concise description of what you want to happen - and why.\n\n**Is your feature request related to a past ticket or discussion?**\nPlease mention any other issues or discussions relevant to this one.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions you've considered.\n\n**Additional context**\nAdd any other context or screenshots that might be relevant in this section.\n"
  },
  {
    "path": ".github/actions/windows-reg-import/action.yml",
    "content": "name: windows-reg-import\ndescription: Import a .reg file and verify those registry values (best-effort)\ninputs:\n  reg-file:\n    description: \"Path to the .reg file\"\n    required: true\nruns:\n  using: \"composite\"\n  steps:\n    - name: Attempt to import custom registry (best-effort)\n      shell: pwsh\n      run: |\n        try {\n          $regFile = Join-Path $env:GITHUB_WORKSPACE \"${{ inputs.reg-file }}\"\n          if (-not (Test-Path $regFile)) {\n            Write-Warning \"Registry file not found (skipping): $regFile\"\n          } else {\n            Write-Host \"Importing registry from $regFile (attempting, non-fatal)\"\n            reg import $regFile 2>&1 | ForEach-Object { Write-Host $_ }\n          }\n        } catch {\n          Write-Warning \"Registry import failed (ignored): $_\"\n        }\n\n    - name: Attempt to verify registry values (best-effort)\n      shell: pwsh\n      run: |\n        try {\n          $acp = (Get-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage' -Name ACP -ErrorAction Stop).ACP\n          Write-Host \"ACP = $acp\"\n        } catch {\n          Write-Warning \"Failed to read ACP (ignored): $_\"\n        }\n        try {\n          $eb = (Get-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Nls\\MUILanguagePreferences' -Name EnableBetaUnicode -ErrorAction Stop).EnableBetaUnicode\n          Write-Host \"EnableBetaUnicode = $eb\"\n        } catch {\n          Write-Warning \"Failed to read EnableBetaUnicode (ignored): $_\"\n        }\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github-actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: \"npm\"\n    directory: \"/website\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      npm-dependencies:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!-- Fixes #XYZ (where XYZ is the issue number from the issue tracker) -->\n\n<!-- TODO description of the change -->\n\n<!-- if the PR is still a WIP, create it as a draft PR (or convert it into one) -->\n\n## Checklist\n- [ ] tested the solution locally and it works \n- [ ] ran the code formatter (`scala-cli fmt .`)\n- [ ] ran `scalafix` (`./mill -i __.fix`)\n- [ ] ran reference docs auto-generation (`./mill -i 'generate-reference-doc[]'.run`)\n\n## How much have your relied on LLM-based tools in this contribution?\n\n<!-- \n  State clearly in the pull request description, \n  whether LLM-based tools were used and to what extent \n\n  (extensively/moderately/minimally/not at all)\n-->\n\n<!--\n  Refer to our [LLM usage policy](https://github.com/scala/scala3/blob/main/LLM_POLICY.md) for rules and guidelines\n  regarding usage of LLM-based tools in contributions.\n-->\n\n## How was the solution tested?\n\n<!-- \n  If automated tests are included, mention it.\n  If they are not, explain why and how the solution was tested.\n-->\n\n## Additional notes\n\n<!-- Placeholder for any extra context regarding this contribution. -->\n\n<!-- When in doubt, check[our contribution guide](https://github.com/VirtusLab/scala-cli/blob/main/CONTRIBUTING.md) -->\n"
  },
  {
    "path": ".github/release/release-notes-regexes.md",
    "content": "# Regexes for preparing Scala CLI release notes\n\nWhen auto-generating release notes from the GitHub UI, the notes will contain GitHub-idiomatic @mentions of users, \nlinks to PRs, issues, etc. Those have to then be swapped out for the corresponding Markdown syntax to be rendered \ncorrectly on our documentation website.\nWhat's worse, the GitHub syntax has to be preserved in the GitHub release description, as using the full Markdown \nsyntax required by our website breaks the GitHub mouse hover magic.\n\nThis has since been automated in the [process_release_notes.sc](../scripts/process_release_notes.sc) script.\n\n```bash\n# Check if the release notes need processing\n.github/scripts/process_release_notes.sc check website/docs/release_notes.md\n# Error: File ~/scala-cli/website/docs/release_notes.md contains patterns that need transformation\n# The following patterns were found that should be transformed:\n#   - Pattern: by @(.*?) in(?!.*\\]\\()\n#   - Pattern: (?<!\\[)@(.*?) made(?!.*\\]\\()\n# Run: .github/scripts/process_release_notes.sc apply <file>\n\n# Apply the regexes to fix the release notes\n.github/scripts/process_release_notes.sc apply website/docs/release_notes.md \n# Applied regexes to: ~/scala-cli/website/docs/release_notes.md\n\n# Verify that the release notes are now properly formatted\n# This is the check we run on the CI, as well\n.github/scripts/process_release_notes.sc verify website/docs/release_notes.md                  \n# File /~/scala-cli/website/docs/release_notes.md is properly formatted\n```\n\nIf you ever need to manually fix the release notes, you can use the old regexes below.\n\nDo keep in mind that IDEA IntelliJ allows to automatically apply regexes when replacing text, so you can use that to\nfix the release notes on the fly.\n\n![image](img/apply-regexes-on-release-notes-in-intellij.png)\n\n## PR link\nFind: `in https\\:\\/\\/github\\.com\\/VirtusLab\\/scala\\-cli\\/pull\\/(.*?)$` </br>\nReplace: `in [#$1](https://github.com/VirtusLab/scala-cli/pull/$1)`\n\n## Contributor link\nFind: `by @(.*?) in` </br>\nReplace: `by [@$1](https://github.com/$1) in`\n\n## New contributor link\nFind: `@(.*?) made` </br>\nReplace: `[@$1](https://github.com/$1) made`\n\n## No GH contributor link\nFind: `by \\[@(.*?).\\(.*\\) in` </br>\nReplace: `by @$1 in`"
  },
  {
    "path": ".github/release/release-procedure.md",
    "content": "# Release procedure reference\n\n- [ ] Draft release notes using the `Draft new release` button in the `Releases` section of `scala-cli` GitHub page.\n    - [ ] Create a tag for the new release.\n    - [ ] Use the `Auto-generate release notes` feature to pre-populate the document with pull requests included in the\n      release.\n    - [ ] Fill in the remaining sections, as in previous releases (features worth mentioning, notable changes, etc).\n    - [ ] Don't publish, save as draft instead\n- [ ] Add the release notes on top\n  of [the release notes doc](https://github.com/VirtusLab/scala-cli/blob/main/website/docs/release_notes.md) and\n  create a PR.\n    - [ ] Make sure the notes render correctly on [the website](https://scala-cli.virtuslab.org/docs/release_notes) - that\n      includes swapping out GitHub-idiomatic @mentions of users, links to PRs, issues, etc.\n      - This is automated with the [process_release_notes.sc](../scripts/process_release_notes.sc) script.\n      - When using IntelliJ you can do that using the regexes in [release-notes-regexes.md](release-notes-regexes.md).\n    - [ ] Copy any fixes over to the draft after getting the PR reviewed and merged.\n- [ ] Mark the release draft as `pre-release` and then `Publish Release`\n- [ ] Wait for a green release CI build with all the updated versions.\n  - [ ] Double check if none of the steps failed, including individual distribution channels in \n    the `update-packages` and `windows-packages` jobs.\n- [ ] ScalaCLI Setup\n    - [ ] Merge pull request with updated Scala CLI version\n      in [scala-cli-setup](https://github.com/VirtusLab/scala-cli-setup) repository. Pull request should be opened\n      automatically after release.\n    - [ ] Wait for the `Update dist` PR to be automatically created after the previous one has been merged, and then\n      proceed to merge it.\n    - [ ] Make a release with the updated Scala CLI version.\n    - [ ] Update the `v1` & `v1.12` tags to the latest release commit.\n      ```bash\n      git fetch --all\n      git checkout origin v1.12.x\n      git tag -d v1.12\n      git tag v1.12\n      git push origin v1.12 -f \n      git tag -d v1\n      git tag v1\n      git push origin v1 -f\n      ```\n- [ ] Submit Scala CLI MSI installer `scala-cli-x86_64-pc-win32.msi` for malware analysis. The Msi file must be uploaded\n  using this [service](https://www.microsoft.com/en-us/wdsi/filesubmission). For more information on this process, refer\n  [here](windows-antimalware-analysis.md).\n- [ ] Unmark release as `pre-release`.\n- [ ] Announce the new release\n    - [ ] announce on Twitter\n    - [ ] announce on Discord\n    - [ ] announce on Reddit if the release contains any noteworthy changes\n- [ ] Create a ticket for the next release using the `Plan a release` template and assign it to the person responsible.\n"
  },
  {
    "path": ".github/release/windows-antimalware-analysis.md",
    "content": "# Microsoft anti-malware analysis\n\nAs new Scala CLI are (wrongly) assumed to be PUA (potentially unwanted applications) by Microsoft Defender SmartScreen \non Windows, we need to submit them for analysis to Microsoft after release.\n\nNote: the analysis may take time, and the results may not be immediately available. \nSometimes it's days, sometimes it's weeks.\nIt may even occur that the new release gets analysed while the previous one is still in the pipeline \ndue to reasons unknown to us. \nAs those eventually do pull through, we can't do much about it.\n\n## Submitting a file for analysis\n\nAfter going through the [release procedure](release-procedure.md), we need to submit the MSI installer \nand the EXE launcher for analysis:\n  - [ ] Download the `scala-cli-x86_64-pc-win32.msi` and upload it using [this service](https://www.microsoft.com/en-us/wdsi/filesubmission).\n  - [ ] Download the `scala-cli-x86_64-pc-win32.zip`, extract it and upload `scala-cli.exe` using [the same service](https://www.microsoft.com/en-us/wdsi/filesubmission).\nYou will need to log in using your company account authorised by the VirtusLab IT division. \nIf you don't have one or if the one you do have doesn't have the right permissions\n(even though you are a maintainer of the Scala CLI repository), be sure to reach out to IT.\n\n## Submission form\n\nWhen reaching https://www.microsoft.com/en-us/wdsi/filesubmission, you will be presented with a form to fill out.\n\n![image](img/submit-for-malware-analysis-1.png)\n\nSubmit file as a `Software Developer` and click continue.\n\n![image](img/submit-for-malware-analysis-2.png)\n\nMake sure to grant your team members access to the submission by adding their emails \nin the `Give additional user s access to the submission` section.\nYou can find the current Scala CLI team listed in the [Scala CLI publish module definition](../../project/publish.sc)\nYou might also want to add the `scala-cli@virtuslab.com` group email address.\n\nSelect `Windows Server Antimalware` as the security product used to scan the file.\nFill in `VirtusLab` as the `Company Name`.\n\n![image](img/submit-for-malware-analysis-3.png)\n\n#### What do you believe this file is?\nSelect `Incorrectly detected as PUA (potentially unwanted application)`.\n\n#### Detection name \nMicrosoft Defender SmartScreen prevented an unrecognised app from starting.\n\n#### Definition version\nThe version number for the Scala CLI release.\n\n#### Additional information\nWhen uploading the installer (`*.msi`), paste the following, fixing swapping out the version number and release link accordingly.\n```text\nThis is the Scala CLI v<version number> installer for Microsoft Windows. \nScala CLI is the official runner of the Scala programming language. \nFor more information check https://github.com/VirtusLab/scala-cli/releases/tag/v<version number>\n```\n\nFor the launcher (`*.exe`), use the (almost identical) following text:\n```text\nThis is the Scala CLI v<version number> launcher for Microsoft Windows.\nScala CLI is the official runner of the Scala programming language.\nFor more information check https://github.com/VirtusLab/scala-cli/releases/tag/v<version number>\n```\n\nClick continue.\n\n![image](img/submit-for-malware-analysis-4.png)\n\nYou might have to verify that you're a human, after which the submission should proceed.\n\n![image](img/submit-for-malware-analysis-5.png)\n\nDouble-check the submission details are correct, ending the process.\n"
  },
  {
    "path": ".github/scripts/build-website.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nyarn --cwd website install\nyarn --cwd website build\n"
  },
  {
    "path": ".github/scripts/check-cross-version-deps.sc",
    "content": "#!/usr/bin/env -S scala-cli shebang\n//> using scala 3\n//> using toolkit default\n//> using options -Werror -Wunused:all\n\nval modules =\n  os.proc(os.pwd / \"mill\", \"-i\", \"resolve\", \"__[]\")\n    .call(cwd = os.pwd)\n    .out\n    .lines()\n\nfor { module <- modules } {\n  println(s\"Checking for $module...\")\n  val depRegex            = \"[│└├─\\\\S\\\\s]+\\\\s([\\\\w.-]+):([\\\\w.-]+):([\\\\w\\\\s\\\\S.-]+)\".r\n  val scalaDepSuffixRegex = \"^(.+?)(_[23](?:\\\\.\\\\d{2})?)?$\".r\n  val deps                = os.proc(os.pwd / \"mill\", \"-i\", s\"$module.showMvnDepsTree\")\n    .call(cwd = os.pwd)\n    .out\n    .lines()\n    .filter(_.count(_ == ':') == 2)\n    .map { case depRegex(org, name, depVersion) => (org, name, depVersion) }\n  val invalidOrgAndName         = \"invalid:invalid\"\n  val scalaVersionsByOrgAndName = deps\n    .groupBy {\n      case (org, scalaDepSuffixRegex(nameWithoutSuffix, _), _) => s\"$org:$nameWithoutSuffix\"\n      case _                                                   => invalidOrgAndName\n    }\n    .collect {\n      case (key, entries) if key != invalidOrgAndName =>\n        key ->\n          entries\n            .collect { case (_, scalaDepSuffixRegex(_, scalaVersion), _) => scalaVersion }\n            .distinct\n    }\n    .filter { case (_, scalaVersions) => scalaVersions.head != null } // filter out non-Scala deps\n  println(\"Checking for clashing dependency Scala versions...\")\n  val conflictEntries: Map[String, Vector[String]] =\n    scalaVersionsByOrgAndName\n      .filter { case (key, scalaVersions) =>\n        if scalaVersions.length == 1 then\n          println(s\"[info] $key${scalaVersions.head} (OK)\")\n          false\n        else\n          println(\n            s\"[${Console.RED}error${Console.RESET}] $key: multiple conflicting Scala versions: ${scalaVersions.mkString(\", \")}\"\n          )\n          true\n      }\n  if conflictEntries.nonEmpty then\n    println(s\"${Console.RED}ERROR: Found ${conflictEntries.size} conflicting entries for $module:\")\n    conflictEntries.foreach {\n      case (key, scalaVersions) =>\n        println(s\"  $key: multiple conflicting Scala versions: ${scalaVersions.mkString(\", \")}\")\n    }\n    println(Console.RESET)\n    sys.exit(1)\n  else println(s\"[info] $module OK\")\n}\n\nprintln(\"Checks completed for:\")\nmodules.foreach(m => println(s\"  $m\"))\nprintln(\"No conflicts detected.\")\nsys.exit(0)\n"
  },
  {
    "path": ".github/scripts/check-override-keywords.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\n# Checks the PR body for [test_*] override keywords.\n# Inputs (env vars): EVENT_NAME, PR_BODY\n# Outputs: writes override=true/false pairs to $GITHUB_OUTPUT and a summary table to $GITHUB_STEP_SUMMARY\n\nif [[ \"$EVENT_NAME\" != \"pull_request\" ]]; then\n  echo \"Non-PR event, setting all overrides to true\"\n  for override in test_all test_native test_integration test_docs test_format; do\n    echo \"$override=true\" >> \"$GITHUB_OUTPUT\"\n  done\n  exit 0\nfi\n\nTEST_ALL=false; TEST_NATIVE=false; TEST_INTEGRATION=false; TEST_DOCS=false; TEST_FORMAT=false\n\ncheck_override() {\n  local keyword=\"$1\"\n  local var_name=\"$2\"\n  if printf '%s' \"$PR_BODY\" | grep -qF \"$keyword\"; then\n    eval \"$var_name=true\"\n    echo \"Override $keyword found\"\n  fi\n}\n\ncheck_override \"[test_all]\" \"TEST_ALL\"\ncheck_override \"[test_native]\" \"TEST_NATIVE\"\ncheck_override \"[test_integration]\" \"TEST_INTEGRATION\"\ncheck_override \"[test_docs]\" \"TEST_DOCS\"\ncheck_override \"[test_format]\" \"TEST_FORMAT\"\n\necho \"Override keywords:\"\necho \"  test_all=$TEST_ALL\"\necho \"  test_native=$TEST_NATIVE\"\necho \"  test_integration=$TEST_INTEGRATION\"\necho \"  test_docs=$TEST_DOCS\"\necho \"  test_format=$TEST_FORMAT\"\n\necho \"test_all=$TEST_ALL\" >> \"$GITHUB_OUTPUT\"\necho \"test_native=$TEST_NATIVE\" >> \"$GITHUB_OUTPUT\"\necho \"test_integration=$TEST_INTEGRATION\" >> \"$GITHUB_OUTPUT\"\necho \"test_docs=$TEST_DOCS\" >> \"$GITHUB_OUTPUT\"\necho \"test_format=$TEST_FORMAT\" >> \"$GITHUB_OUTPUT\"\n\necho \"## Override keywords\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| Keyword | Active |\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"|---------|--------|\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| [test_all] | $TEST_ALL |\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| [test_native] | $TEST_NATIVE |\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| [test_integration] | $TEST_INTEGRATION |\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| [test_docs] | $TEST_DOCS |\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| [test_format] | $TEST_FORMAT |\" >> \"$GITHUB_STEP_SUMMARY\"\n"
  },
  {
    "path": ".github/scripts/choco/scala-cli.nuspec",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd\">\n  <metadata>\n    <id>scala-cli</id>\n    <version>@LAUNCHER_VERSION@</version>\n    <title>Scala CLI</title>\n    <authors>virtuslab-chocolatey</authors>\n    <owners>virtuslab-chocolatey</owners>\n    <tags>scala-cli</tags>\n    <summary>Scala CLI</summary>\n    <description>Scala CLI is a command-line tool to interact with the Scala language. It lets you compile, run, test, and package your Scala code (and more!)</description>\n    <packageSourceUrl>https://github.com/VirtusLab/scala-cli/tree/main/.github/scripts/choco</packageSourceUrl>\n    <projectSourceUrl>https://github.com/VirtusLab/scala-cli</projectSourceUrl>\n    <projectUrl>https://scala-cli.virtuslab.org/</projectUrl>\n    <bugTrackerUrl>https://github.com/VirtusLab/scala-cli/issues</bugTrackerUrl>\n    <copyright>© 2021-2022 VirtusLab Sp. z. o. o.</copyright>\n    <iconUrl>https://cdn.jsdelivr.net/gh/VirtusLab/scala-cli@e4c0eb72276ae77e689c61a83230ec16324791e8/.github/scripts/choco/logo.ico</iconUrl>\n    <licenseUrl>https://github.com/VirtusLab/scala-cli/blob/main/LICENSE</licenseUrl>\n    <requireLicenseAcceptance>true</requireLicenseAcceptance>\n    <releaseNotes>https://github.com/VirtusLab/scala-cli/releases</releaseNotes>    \n  </metadata>\n  <files>\n    <file src=\"tools\\**\" target=\"tools\" />\n  </files>\n</package>\n"
  },
  {
    "path": ".github/scripts/choco/tools/chocolateyinstall.ps1",
    "content": "$ErrorActionPreference = 'Stop';\n$url64      = '@LAUNCHER_URL@'\n$packageArgs = @{\n  packageName   = 'scala-cli'\n  fileType      = 'MSI'\n  url64bit      = $url64\n\n  softwareName  = 'Scala CLI' \n  checksum64    = '@LAUNCHER_SHA256@'\n  checksumType64= 'sha256'\n\n  silentArgs    = \"/qn /norestart\"\n  validExitCodes= @(0)\n}\n\nInstall-ChocolateyPackage @packageArgs"
  },
  {
    "path": ".github/scripts/classify-changes.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\n# Classifies changed files into categories for CI job filtering.\n# Inputs (env vars): EVENT_NAME, BASE_REF\n# Outputs: writes category=true/false pairs to $GITHUB_OUTPUT and a summary table to $GITHUB_STEP_SUMMARY\n\nif [[ \"$EVENT_NAME\" != \"pull_request\" ]]; then\n  echo \"Non-PR event ($EVENT_NAME), setting all categories to true\"\n  for cat in code docs ci format_config benchmark gifs mill_wrapper; do\n    echo \"$cat=true\" >> \"$GITHUB_OUTPUT\"\n  done\n  exit 0\nfi\n\nCHANGED_FILES=$(git diff --name-only \"origin/$BASE_REF...HEAD\" || echo \"DIFF_FAILED\")\nif [[ \"$CHANGED_FILES\" == \"DIFF_FAILED\" ]]; then\n  echo \"::warning::Failed to compute diff, running all jobs\"\n  for cat in code docs ci format_config benchmark gifs mill_wrapper; do\n    echo \"$cat=true\" >> \"$GITHUB_OUTPUT\"\n  done\n  exit 0\nfi\n\nCODE=false; DOCS=false; CI=false; FORMAT_CONFIG=false; BENCHMARK=false; GIFS=false; MILL_WRAPPER=false\n\nwhile IFS= read -r file; do\n  case \"$file\" in\n    modules/*|build.mill|project/*) CODE=true ;;\n    website/*) DOCS=true ;;\n    .github/*) CI=true ;;\n    .scalafmt.conf|.scalafix.conf) FORMAT_CONFIG=true ;;\n    gcbenchmark/*) BENCHMARK=true ;;\n    gifs/*) GIFS=true ;;\n    mill|mill.bat) MILL_WRAPPER=true ;;\n  esac\ndone <<< \"$CHANGED_FILES\"\n\necho \"Change categories:\"\necho \"  code=$CODE\"\necho \"  docs=$DOCS\"\necho \"  ci=$CI\"\necho \"  format_config=$FORMAT_CONFIG\"\necho \"  benchmark=$BENCHMARK\"\necho \"  gifs=$GIFS\"\necho \"  mill_wrapper=$MILL_WRAPPER\"\n\necho \"code=$CODE\" >> \"$GITHUB_OUTPUT\"\necho \"docs=$DOCS\" >> \"$GITHUB_OUTPUT\"\necho \"ci=$CI\" >> \"$GITHUB_OUTPUT\"\necho \"format_config=$FORMAT_CONFIG\" >> \"$GITHUB_OUTPUT\"\necho \"benchmark=$BENCHMARK\" >> \"$GITHUB_OUTPUT\"\necho \"gifs=$GIFS\" >> \"$GITHUB_OUTPUT\"\necho \"mill_wrapper=$MILL_WRAPPER\" >> \"$GITHUB_OUTPUT\"\n\necho \"## Change categories\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"| Category | Changed |\" >> \"$GITHUB_STEP_SUMMARY\"\necho \"|----------|---------|\" >> \"$GITHUB_STEP_SUMMARY\"\nfor cat in code docs ci format_config benchmark gifs mill_wrapper; do\n  val=$(eval echo \\$$( echo $cat | tr 'a-z' 'A-Z'))\n  echo \"| $cat | $val |\" >> \"$GITHUB_STEP_SUMMARY\"\ndone\n"
  },
  {
    "path": ".github/scripts/docker/ScalaCliDockerFile",
    "content": "FROM debian:stable-slim\nRUN apt update && apt install build-essential libz-dev clang procps -y\nADD scala-cli /usr/bin/\nRUN \\\n echo \"println(1)\" | scala-cli -S 3 - -v -v -v && \\\n echo \"println(1)\" | scala-cli -S 2.13 - -v -v -v && \\\n echo \"println(1)\" | scala-cli -S 2.12 - -v -v -v\nRUN \\\n echo \"println(1)\" | scala-cli --power package --native _.sc --force && \\\n echo \"println(1)\" | scala-cli --power package --native-image _.sc --force\nENTRYPOINT [\"scala-cli\"]\n"
  },
  {
    "path": ".github/scripts/docker/ScalaCliSlimDockerFile",
    "content": "FROM debian:stable-slim AS build-env\n\nFROM gcr.io/distroless/base-debian12\nADD scala-cli /usr/local/bin/scala-cli\nCOPY --from=build-env /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1\nENTRYPOINT [\"/usr/local/bin/scala-cli\"]\n"
  },
  {
    "path": ".github/scripts/generate-docker-image.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nROOT=\"$(cd \"$(dirname \"$0\")/../..\" && pwd)\"\nWORKDIR=\"$ROOT/out/docker-workdir\"\n\nmkdir -p \"$WORKDIR\"\n./mill -i copyTo --task 'cli[]'.nativeImageStatic --dest \"$WORKDIR/scala-cli\" 1>&2\n\ncd \"$WORKDIR\"\ndocker build -t scala-cli -f \"$ROOT/.github/scripts/docker/ScalaCliDockerFile\" .\n"
  },
  {
    "path": ".github/scripts/generate-junit-reports.sc",
    "content": "#!/usr/bin/env -S scala-cli shebang\n//> using scala 3\n//> using toolkit default\n//> using dep org.scala-lang.modules::scala-xml:2.4.0\n//> using options -Werror -Wunused:all\n// adapted from https://github.com/vic/mill-test-junit-report\nimport java.io.File\nimport scala.collection.mutable.ArrayBuffer\nimport scala.annotation.tailrec\nimport java.nio.file.Paths\nimport scala.util.Try\n\ncase class Trace(declaringClass: String, methodName: String, fileName: String, lineNumber: Int) {\n  override def toString: String = s\"$declaringClass.$methodName($fileName:$lineNumber)\"\n}\n\ncase class Failure(name: String, message: String, trace: Seq[Trace])\n\ncase class Test(\n  fullyQualifiedName: String,\n  selector: String,\n  duration: Double,\n  failure: Option[Failure]\n)\n\n@tailrec\ndef findFiles(paths: Seq[os.Path], result: Seq[os.Path] = Nil): Seq[os.Path] =\n  paths match\n    case Nil          => result\n    case head :: tail =>\n      val newFiles =\n        if head.segments.contains(\"test\") && head.last.endsWith(\".dest\") && os.isDir(head) then\n          os.list(head).filter(f => f.last == \"out.json\").toList\n        else Seq.empty\n      val newDirs = os.list(head).filter(p => os.isDir(p)).toList\n      findFiles(tail ++ newDirs, result ++ newFiles)\n\nextension (s: String)\n  def toNormalisedPath: os.Path = if Paths.get(s).isAbsolute then os.Path(s) else os.Path(s, os.pwd)\n\ndef printUsageMessage(): Unit = println(\"Usage: generate-junit-reports <id> <name> <into> <path>\")\nif args.length != 4 then {\n  println(s\"Error: provided too few arguments: ${args.length}\")\n  printUsageMessage()\n  System.exit(1)\n}\n\nval id: String   = args(0)\nval name: String = args(1)\n\nif new File(args(2)).exists() then {\n  println(s\"Error: specified output path already exists: ${args(2)}\")\n  System.exit(1)\n}\nval into = args(2).toNormalisedPath\n\nval pathArg           = args(3)\nval rootPath: os.Path =\n  if Paths.get(pathArg).isAbsolute then os.Path(pathArg) else os.Path(pathArg, os.pwd)\nif !os.isDir(rootPath) then {\n  println(s\"The path provided is not a directory: $pathArg\")\n  System.exit(1)\n}\nval reports: Seq[os.Path] = findFiles(Seq(rootPath))\nprintln(s\"Found ${reports.length} mill json reports:\")\nprintln(reports.mkString(\"\\n\"))\nif reports.isEmpty then println(\"Warn: no reports found!\")\nprintln(\"Reading reports...\")\nval tests: Seq[Test] = reports.map(x => ujson.read(x.toNIO)).flatMap { json =>\n  json(1).value.asInstanceOf[ArrayBuffer[ujson.Obj]].map { test =>\n    Test(\n      fullyQualifiedName = test(\"fullyQualifiedName\").str,\n      selector = test(\"selector\").str,\n      duration = test(\"duration\").num / 1000.0,\n      failure = test(\"status\").str match {\n        case \"Failure\" => Some(Failure(\n            name = test(\"exceptionName\")(0).str,\n            message = test(\"exceptionMsg\")(0).str,\n            trace = test(\"exceptionTrace\")(0).arr.map { st =>\n              val declaringClass = st(\"declaringClass\").str\n              val methodName     = st(\"methodName\").str\n              val fileName       = st(\"fileName\")(0).str\n              val lineNumber     = st(\"lineNumber\").num.toInt\n              Trace(declaringClass, methodName, fileName, lineNumber)\n            }.toList\n          ))\n        case _ => None\n      }\n    )\n  }\n}\nprintln(s\"Found ${tests.length} tests.\")\nif tests.isEmpty then println(\"Warn: no tests found!\")\nprintln(\"Generating JUnit XML report...\")\nval suites = tests.groupBy(_.fullyQualifiedName).map { case (suit, tests) =>\n  val testcases = tests.map { test =>\n    <testcase id={test.selector} classname={test.fullyQualifiedName} name={\n      test.selector.substring(test.fullyQualifiedName.length)\n    } time={test.duration.toString}>\n        {\n      test.failure.map { failure =>\n        val maybeTrace = Try(failure.trace(1)).toOption\n        val fileName   = maybeTrace.map(_.fileName).getOrElse(\"unknown\")\n        val lineNumber = maybeTrace.map(_.lineNumber).getOrElse(-1)\n        <failure message={failure.message} type=\"ERROR\">\n            ERROR: {failure.message}\n            Category: {failure.name}\n            File: {fileName}\n            Line: {lineNumber}\n          </failure>\n      }.orNull\n    }\n        {\n      test.failure.map { failure =>\n        <system-err>{\n          failure.trace.mkString(s\"${failure.name}: ${failure.message}\", \"\\n    at \", \"\")\n        }</system-err>\n      }.orNull\n    }\n      </testcase>\n  }\n\n  <testsuite id={suit} name={suit} tests={tests.length.toString} failures={\n    tests.count(_.failure.isDefined).toString\n  } time={tests.map(_.duration).sum.toString}>\n      {testcases}\n    </testsuite>\n}\n\nval n = <testsuites id={id} name={name} tests={tests.length.toString} failures={\n  tests.count(_.failure.isDefined).toString\n} time={tests.map(_.duration).sum.toString}>\n  {suites}\n</testsuites>\nval prettyXmlPrinter = new scala.xml.PrettyPrinter(80, 2)\nval xmlToSave        = scala.xml.XML.loadString(prettyXmlPrinter.format(n))\nscala.xml.XML.save(filename = into.toString(), node = xmlToSave, xmlDecl = true)\nprintln(s\"Generated report at: $into\")\n"
  },
  {
    "path": ".github/scripts/generate-native-image.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nCOMMAND=\"cli[].base-image.writeDefaultNativeImageScript\"\n\n# temporary, until we pass JPMS options to native-image,\n# see https://www.graalvm.org/release-notes/22_2/#native-image\nexport USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM=false\n\n# Using 'mill -i' so that the Mill process doesn't outlive this invocation\nif [[ \"$OSTYPE\" == \"msys\" || \"$OSTYPE\" == \"cygwin\" ]]; then\n  ./mill.bat -i ci.copyJvm --dest jvm\n  export JAVA_HOME=\"$(pwd -W | sed 's,/,\\\\,g')\\\\jvm\"\n  export GRAALVM_HOME=\"$JAVA_HOME\"\n  export PATH=\"$(pwd)/bin:$PATH\"\n  echo \"PATH=$PATH\"\n\n  # this part runs into connection problems on Windows, so we retry up to 5 times\n  MAX_RETRIES=5\n  RETRY_COUNT=0\n  while (( RETRY_COUNT < MAX_RETRIES )); do\n      ./mill.bat -i \"$COMMAND\" --scriptDest generate-native-image.bat\n\n      if [[ $? -ne 0 ]]; then\n          echo \"Error occurred during 'mill.bat -i $COMMAND generate-native-image.bat' command. Retrying... ($((RETRY_COUNT + 1))/$MAX_RETRIES)\"\n          (( RETRY_COUNT++ ))\n          sleep 2\n      else\n          ./generate-native-image.bat\n          if [[ $? -ne 0 ]]; then\n              echo \"Error occurred during 'generate-native-image.bat'. Retrying... ($((RETRY_COUNT + 1))/$MAX_RETRIES)\"\n              (( RETRY_COUNT++ ))\n              sleep 2\n          else\n              echo \"'generate-native-image.bat' succeeded with $RETRY_COUNT retries.\"\n              break\n          fi\n      fi\n  done\n\n  if (( RETRY_COUNT == MAX_RETRIES )); then\n      echo \"Exceeded maximum retry attempts. Exiting with error.\"\n      exit 1\n  fi\nelse\n  if [ $# == \"0\" ]; then\n    if [[ \"$OSTYPE\" == \"linux-gnu\" ]]; then\n      if [[ \"$(uname -m)\" == \"aarch64\" ]]; then\n        COMMAND=\"cli[].base-image.writeDefaultNativeImageScript\"\n        CLEANUP=(\"true\")\n      else\n        COMMAND=\"cli[].linux-docker-image.writeDefaultNativeImageScript\"\n        CLEANUP=(\"sudo\" \"rm\" \"-rf\" \"out/cli/linux-docker-image/nativeImageDockerWorkingDir\")\n      fi\n    else\n      CLEANUP=(\"true\")\n    fi\n  else\n    case \"$1\" in\n      \"static\")\n        COMMAND=\"cli[].static-image.writeDefaultNativeImageScript\"\n        CLEANUP=(\"sudo\" \"rm\" \"-rf\" \"out/cli/static-image/nativeImageDockerWorkingDir\")\n        ;;\n      \"mostly-static\")\n        COMMAND=\"cli[].mostly-static-image.writeDefaultNativeImageScript\"\n        CLEANUP=(\"sudo\" \"rm\" \"-rf\" \"out/cli/mostly-static-image/nativeImageDockerWorkingDir\")\n        ;;\n      *)\n        echo \"Invalid image name: $1\" 1>&2\n        exit 1\n        ;;\n    esac\n  fi\n\n  ./mill -i \"$COMMAND\" --scriptDest generate-native-image.sh\n  bash ./generate-native-image.sh\n  \"${CLEANUP[@]}\"\nfi\n"
  },
  {
    "path": ".github/scripts/generate-os-packages.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nARCHITECTURE=\"x86_64\" # Set the default architecture\nif [[ \"$OSTYPE\" == \"linux-gnu\"* ]] || [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n  # When running on macOS and Linux lets determine the architecture\n  ARCHITECTURE=$(uname -m)\nfi\nif [[ $# -eq 1 ]]; then\n  # architecture gets overridden by command line param\n  ARCHITECTURE=$1\nfi\n\nARTIFACTS_DIR=\"artifacts/\"\nmkdir -p \"$ARTIFACTS_DIR\"\n\nif [[ -z \"$OSTYPE\" ]]; then\n  mill=\"./mill.bat\"\nelse\n  mill=\"./mill\"\nfi\n\npackager() {\n  \"$mill\" -i packager.run \"$@\"\n}\n\nlauncher() {\n  local launcherMillCommand=\"cli[].nativeImage\"\n  local launcherName\n\n  if [[ \"${OS-}\" == \"Windows_NT\" ]]; then\n    launcherName=\"scala.exe\"\n  else\n    launcherName=\"scala\"\n  fi\n\n  \"$mill\" -i copyTo --task \"$launcherMillCommand\" --dest \"$launcherName\" 1>&2\n  echo \"$launcherName\"\n}\n\nversion() {\n  \"$mill\" -i writePackageVersionTo --dest scala-cli-version 1>&2\n  cat scala-cli-version\n}\n\nshortVersion() {\n  \"$mill\" -i writeShortPackageVersionTo --dest scala-cli-short-version 1>&2\n  cat scala-cli-short-version\n}\n\ngenerate_deb() {\n  packager \\\n    --deb \\\n    --version \"$(version)\" \\\n    --source-app-path \"$(launcher)\" \\\n    --output \"$ARTIFACTS_DIR/scala-cli.deb\" \\\n    --description \"Scala CLI\" \\\n    --maintainer \"scala-cli@virtuslab.com\" \\\n    --launcher-app \"scala-cli\" \\\n    --priority \"optional\" \\\n    --section \"devel\"\n  mv \"$ARTIFACTS_DIR/scala-cli.deb\" \"$ARTIFACTS_DIR/scala-cli-x86_64-pc-linux.deb\"\n}\n\ngenerate_rpm() {\n  packager \\\n    --rpm \\\n    --version \"$(shortVersion)\" \\\n    --source-app-path \"$(launcher)\" \\\n    --output \"$ARTIFACTS_DIR/scala-cli-x86_64-pc-linux.rpm\" \\\n    --description \"Scala CLI\" \\\n    --maintainer \"scala-cli@virtuslab.com\" \\\n    --license \"ASL 2.0\" \\\n    --launcher-app \"scala-cli\"\n}\n\ngenerate_pkg() {\n  arch=$1\n  packager \\\n    --pkg \\\n    --version \"$(version)\" \\\n    --source-app-path \"$(launcher)\" \\\n    --output \"$ARTIFACTS_DIR/scala-cli-$arch-apple-darwin.pkg\" \\\n    --identifier \"scala-cli\" \\\n    --launcher-app \"scala-cli\"\n}\n\ngenerate_msi() {\n\n  # Having the MSI automatically install Visual C++ redistributable when needed,\n  # see https://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/install_vcredist.html\n  \"$mill\" -i ci.writeWixConfigExtra --dest wix-visual-cpp-redist.xml\n\n  packager \\\n    --msi \\\n    --version \"$(shortVersion)\" \\\n    --source-app-path \"$(launcher)\" \\\n    --output \"$ARTIFACTS_DIR/scala-cli-x86_64-pc-win32.msi\" \\\n    --product-name \"Scala CLI\" \\\n    --maintainer \"scala-cli@virtuslab.com\" \\\n    --launcher-app \"scala-cli\" \\\n    --license-path \"./LICENSE\" \\\n    --exit-dialog \"To run Scala CLI, open a Command window, and type scala-cli + Enter. If scala-cli cannot be found, ensure that the Command window was opened after Scala CLI was installed.\" \\\n    --logo-path \"./logo.png\" \\\n    --suppress-validation \\\n    --extra-configs wix-visual-cpp-redist.xml \\\n    --wix-upgrade-code-guid \"C74FC9A1-9381-40A6-882F-9044C603ABD9\"\n  rm -f \"$ARTIFACTS_DIR/\"*.wixpdb || true\n}\n\ngenerate_sdk() {\n  local sdkDirectory\n  local binName\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\"* ]]; then\n    if [[ \"$ARCHITECTURE\" == \"aarch64\" ]] || [[ \"$ARCHITECTURE\" == \"x86_64\" ]]; then\n      sdkDirectory=\"scala-cli-$ARCHITECTURE-pc-linux-static-sdk\"\n    else\n      echo \"scala-cli is not supported on $ARCHITECTURE\"\n      exit 2\n    fi\n    binName=\"scala-cli\"\n  elif [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n    if [[ \"$ARCHITECTURE\" == \"arm64\" ]]; then\n      sdkDirectory=\"scala-cli-aarch64-apple-darwin-sdk\"\n    else\n      sdkDirectory=\"scala-cli-x86_64-apple-darwin-sdk\"\n    fi\n    binName=\"scala-cli\"\n  elif [[ \"$OSTYPE\" == \"msys\" || \"$OSTYPE\" == \"cygwin\" ]]; then\n    sdkDirectory=\"scala-cli-x86_64-pc-win32-sdk\"\n    binName=\"scala-cli.exe\"\n  else\n    echo \"Unrecognized operating system: $OSTYPE\" 1>&2\n    exit 1\n  fi\n\n  mkdir -p \"$sdkDirectory\"/bin\n  cp \"$(launcher)\" \"$sdkDirectory\"/bin/\"$binName\"\n\n  if [[ \"$OSTYPE\" == \"msys\" || \"$OSTYPE\" == \"cygwin\" ]]; then\n    7z a \"$sdkDirectory\".zip \"$sdkDirectory\"\n  else\n    zip -r \"$sdkDirectory\".zip \"$sdkDirectory\"\n  fi\n\n  mv \"$sdkDirectory\".zip \"$ARTIFACTS_DIR/\"/\"$sdkDirectory\".zip\n}\n\nif [[ \"$OSTYPE\" == \"linux-gnu\"* ]]; then\n  if [ \"$ARCHITECTURE\" == \"x86_64\" ]; then\n    generate_deb\n    generate_rpm\n  fi\n  generate_sdk\nelif [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n  if [ \"$ARCHITECTURE\" == \"arm64\" ]; then\n    generate_pkg \"aarch64\"\n  else\n    generate_pkg \"x86_64\"\n  fi\n  generate_sdk\nelif [[ \"$OSTYPE\" == \"msys\" || \"$OSTYPE\" == \"cygwin\" ]]; then\n  generate_msi\n  generate_sdk\nelse\n  echo \"Unrecognized operating system: $OSTYPE\" 1>&2\n  exit 1\nfi\n"
  },
  {
    "path": ".github/scripts/generate-slim-docker-image.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nROOT=\"$(cd \"$(dirname \"$0\")/../..\" && pwd)\"\nWORKDIR=\"$ROOT/out/docker-slim-workdir\"\n\nmkdir -p \"$WORKDIR\"\n./mill -i copyTo --task 'cli[]'.nativeImageMostlyStatic --dest \"$WORKDIR/scala-cli\" 1>&2\n\ncd \"$WORKDIR\"\ndocker build -t scala-cli-slim -f \"$ROOT/.github/scripts/docker/ScalaCliSlimDockerFile\" .\n"
  },
  {
    "path": ".github/scripts/get-latest-cs.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nCS_VERSION=\"2.1.25-M24\"\n\nDIR=\"$(cs get --archive \"https://github.com/coursier/coursier/releases/download/v$CS_VERSION/cs-x86_64-pc-win32.zip\")\"\n\ncp \"$DIR/\"*.exe cs.exe\n"
  },
  {
    "path": ".github/scripts/gpg-setup.sh",
    "content": "#!/usr/bin/env sh\n\n# from https://github.com/coursier/apps/blob/f1d2bf568bf466a98569a85c3f23c5f3a8eb5360/.github/scripts/gpg-setup.sh\n\necho \"$PGP_SECRET\" | base64 --decode | gpg --import --no-tty --batch --yes\n\necho \"allow-loopback-pinentry\" >>~/.gnupg/gpg-agent.conf\necho \"pinentry-mode loopback\" >>~/.gnupg/gpg.conf\n\ngpg-connect-agent reloadagent /bye\n"
  },
  {
    "path": ".github/scripts/process_release_notes.sc",
    "content": "#!/usr/bin/env -S scala-cli shebang\n//> using scala 3\n//> using toolkit default\n//> using options -Werror -Wunused:all\n\ncase class Replacement(find: String, replace: String)\n\n// Hardcoded regex replacements for processing release notes\n// These transform GitHub-idiomatic syntax to website-compatible Markdown\nval replacements: Seq[Replacement] = Seq(\n  // 1. Contributor link: Transform \"by @user in\" to \"by [@user](https://github.com/user) in\"\n  // Excludes bots: dependabot, github-actions, scala-steward\n  // Only matches if not already a link (negative lookahead to avoid matching already-formatted entries)\n  // Must come BEFORE PR link pattern to avoid interference\n  // The pattern stops at the first \" in\" and doesn't match if there's a formatted link before it\n  Replacement(\n    find =\n      \"by @(?!dependabot\\\\[bot\\\\]|github-actions\\\\[bot\\\\]|scala-steward)([^\\\\[\\\\]]+?) in(?!.*\\\\[@.*?\\\\]\\\\()\",\n    replace = \"by [@$1](https://github.com/$1) in\"\n  ),\n  // 2. New contributor link: Transform \"@user made\" to \"[@user](https://github.com/user) made\"\n  // Excludes bots: dependabot, github-actions, scala-steward\n  // Only matches if not already a link (negative lookbehind and lookahead to avoid matching already-formatted entries)\n  Replacement(\n    find =\n      \"(?<!\\\\[)@(?!dependabot\\\\[bot\\\\]|github-actions\\\\[bot\\\\]|scala-steward)(.*?) made(?!.*\\\\]\\\\()\",\n    replace = \"[@$1](https://github.com/$1) made\"\n  ),\n  // 3. PR link: Transform \"in https://github.com/VirtusLab/scala-cli/pull/123\" to \"in [#123](https://github.com/VirtusLab/scala-cli/pull/123)\"\n  // Only matches if not already formatted as a markdown link\n  Replacement(\n    find = \"in https://github\\\\.com/VirtusLab/scala-cli/pull/(.*?)$(?!.*\\\\]\\\\()\",\n    replace = \"in [#$1](https://github.com/VirtusLab/scala-cli/pull/$1)\"\n  )\n)\n\ndef applyReplacements(text: String, replacements: Seq[Replacement]): String = {\n  // Process line by line to handle $ end-of-line anchors correctly\n  val lines          = text.linesIterator.toSeq\n  val processedLines = lines.map { line =>\n    replacements.foldLeft(line) { (current, replacement) =>\n      try\n        val regex = replacement.find.r\n        // Manually handle replacement to avoid issues with $ in replacement strings\n        var result  = current\n        val matches =\n          regex.findAllMatchIn(current).toSeq.reverse // Process from end to avoid offset issues\n        for matchData <- matches do\n          // Build replacement string by substituting $1, $2, etc. with actual group values\n          var replacementText = replacement.replace\n          // Replace $1, $2, etc. with actual group values (in reverse order to avoid replacing $11 when we mean $1)\n          for i <- matchData.groupCount to 1 by -1 do\n            val groupValue = if matchData.group(i) != null then matchData.group(i) else \"\"\n            replacementText = replacementText.replace(s\"$$$i\", groupValue)\n          // Replace the matched portion\n          result = result.substring(0, matchData.start) + replacementText +\n            result.substring(matchData.end)\n        result\n      catch\n        case e: Exception =>\n          System.err.println(\n            s\"Warning: Failed to apply regex '${replacement.find}': ${e.getMessage}\"\n          )\n          if System.getenv(\"DEBUG\") == \"true\" then e.printStackTrace()\n          current\n    }\n  }\n  // Rejoin lines with newlines, preserving original line endings\n  val lineEnding = if text.contains(\"\\r\\n\") then \"\\r\\n\"\n  else if text.contains(\"\\n\") then \"\\n\" else System.lineSeparator()\n  processedLines.mkString(lineEnding) +\n    (if text.endsWith(\"\\n\") || text.endsWith(\"\\r\\n\") then lineEnding else \"\")\n}\n\ndef printUsageMessage(): Unit = {\n  println(\"Usage: process_release_notes.sc <command> <file>\")\n  println(\"Commands:\")\n  println(\"  apply    - Apply regexes to the file (modifies in place)\")\n  println(\"  check    - Check if file needs regexes applied (exits with error if needed)\")\n  println(\"  verify   - Verify file has regexes applied (exits with error if not)\")\n}\n\nif args.length < 2 then\n  println(s\"Error: too few arguments: ${args.length}\")\n  printUsageMessage()\n  sys.exit(1)\n\nval command  = args(0)\nval filePath = os.Path(args(1), os.pwd)\n\nif !os.exists(filePath) then\n  println(s\"Error: file does not exist: $filePath\")\n  sys.exit(1)\n\nif System.getenv(\"DEBUG\") == \"true\" then\n  println(s\"Loaded ${replacements.length} replacement patterns\")\n  replacements.zipWithIndex.foreach { case (r, i) =>\n    println(s\"  Pattern ${i + 1}: Find='${r.find}', Replace='${r.replace}'\")\n  }\n\nval originalContent    = os.read(filePath)\nval transformedContent = applyReplacements(originalContent, replacements)\n\ncommand match\n  case \"apply\" =>\n    os.write.over(filePath, transformedContent)\n    println(s\"Applied regexes to: $filePath\")\n\n  case \"check\" =>\n    if originalContent != transformedContent then\n      println(s\"Error: File $filePath needs regexes applied\")\n      println(\"Run: .github/scripts/process_release_notes.sc apply <file>\")\n      sys.exit(1)\n    else\n      println(s\"File $filePath is already processed correctly\")\n      sys.exit(0)\n\n  case \"verify\" =>\n    // Check for patterns that should have been transformed\n    // All patterns have negative lookaheads to avoid matching already-formatted entries\n    val patternsToCheck     = replacements\n    val needsTransformation =\n      patternsToCheck.exists { replacement =>\n        try\n          val regex = replacement.find.r\n          regex.findFirstIn(originalContent).isDefined\n        catch\n          case _: Exception => false\n      }\n\n    if needsTransformation then\n      println(s\"Error: File $filePath contains patterns that need transformation\")\n      println(\"The following patterns were found that should be transformed:\")\n      patternsToCheck.foreach { replacement =>\n        try\n          val regex = replacement.find.r\n          if regex.findFirstIn(originalContent).isDefined then\n            println(s\"  - Pattern: ${replacement.find}\")\n        catch\n          case _: Exception => ()\n      }\n      println(\"Run: .github/scripts/process_release_notes.sc apply <file>\")\n      sys.exit(1)\n    else\n      println(s\"File $filePath is properly formatted\")\n      sys.exit(0)\n\n  case _ =>\n    println(s\"Error: unknown command: $command\")\n    printUsageMessage()\n    sys.exit(1)\n"
  },
  {
    "path": ".github/scripts/publish-docker-images.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nRAW_VERSION=\"$(./mill -i ci.publishVersion)\"\nSCALA_CLI_VERSION=\"${RAW_VERSION##* }\"\n\ndocker tag scala-cli virtuslab/scala-cli:latest\ndocker tag scala-cli virtuslab/scala-cli:\"$SCALA_CLI_VERSION\"\ndocker push virtuslab/scala-cli:latest\ndocker push virtuslab/scala-cli:\"$SCALA_CLI_VERSION\"\n"
  },
  {
    "path": ".github/scripts/publish-sdkman.sh",
    "content": "#!/usr/bin/env bash\n\n# from https://github.com/lampepfl/dotty/blob/37e997abc2bf4d42321492acaf7f7832ee7ce146/.github/workflows/scripts/publish-sdkman.sh\n# This is script for publishing Scala CLI on SDKMAN.\n# It's releasing and announcing the release of Scala CLI on SDKMAN.\n#\n# Requirement:\n#   - the latest stable version of Scala CLI should be available in github artifacts\n\nset -eu\n\nversion() {\n  \"./mill\" -i writePackageVersionTo --dest scala-cli-version 1>&2\n  cat scala-cli-version\n}\n\nSCALA_CLI_VERSION=\"$(version)\"\nARCHS=(\"x86_64\" \"aarch64\" \"x86_64\" \"aarch64\" \"x86_64\")\nUNAMES=(\"pc-linux-static-sdk\" \"pc-linux-static-sdk\" \"apple-darwin-sdk\" \"apple-darwin-sdk\" \"pc-win32-sdk\")\nPLATFORMS=(\"LINUX_64\" \"LINUX_ARM64\" \"MAC_OSX\" \"MAC_ARM64\" \"WINDOWS_64\")\n\nfor i in \"${!PLATFORMS[@]}\"; do\n\n    SCALA_CLI_URL=\"https://github.com/VirtusLab/scala-cli/releases/download/v$SCALA_CLI_VERSION/scala-cli-${ARCHS[i]}-${UNAMES[i]}.zip\"\n\n    # Release a new Candidate Version\n    curl --silent --show-error --fail \\\n        -X POST \\\n        -H \"Consumer-Key: $SDKMAN_KEY\" \\\n        -H \"Consumer-Token: $SDKMAN_TOKEN\" \\\n        -H \"Content-Type: application/json\" \\\n        -H \"Accept: application/json\" \\\n        -d '{\"candidate\": \"scalacli\", \"version\": \"'\"$SCALA_CLI_VERSION\"'\", \"url\": \"'\"$SCALA_CLI_URL\"'\", \"platform\": \"'\"${PLATFORMS[i]}\"'\" }' \\\n        https://vendors.sdkman.io/release\n\n    if [[ $? -ne 0 ]]; then\n    echo \"Fail sending POST request to releasing Scala CLI on SDKMAN on platform: ${PLATFORMS[i]}.\"\n    exit 1\n    fi\n\ndone\n\n# Set SCALA_CLI_VERSION as Default for Candidate\ncurl --silent --show-error --fail \\\n    -X PUT \\\n    -H \"Consumer-Key: $SDKMAN_KEY\" \\\n    -H \"Consumer-Token: $SDKMAN_TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    -H \"Accept: application/json\" \\\n    -d '{\"candidate\": \"scalacli\", \"version\": \"'\"$SCALA_CLI_VERSION\"'\" }' \\\n    https://vendors.sdkman.io/default\n\nif [[ $? -ne 0 ]]; then\n  echo \"Fail sending PUT request to announcing the release of Scala CLI on SDKMAN.\"\n  exit 1\nfi"
  },
  {
    "path": ".github/scripts/publish-slim-docker-images.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nRAW_VERSION=\"$(./mill -i ci.publishVersion)\"\nSCALA_CLI_VERSION=\"${RAW_VERSION##* }\"\n\ndocker tag scala-cli-slim virtuslab/scala-cli-slim:latest\ndocker tag scala-cli-slim virtuslab/scala-cli-slim:\"$SCALA_CLI_VERSION\"\ndocker push virtuslab/scala-cli-slim:latest\ndocker push virtuslab/scala-cli-slim:\"$SCALA_CLI_VERSION\"\n"
  },
  {
    "path": ".github/scripts/scala-cli.rb.template",
    "content": "# typed: false\n# frozen_string_literal: true\n\n# ScalaCli Formula\nclass ScalaCli < Formula\n  desc \"Launcher for ScalaCli\"\n  homepage \"https://virtuslab.github.io/scala-cli/\"\n  url (RUBY_PLATFORM.include? \"arm64\") ?\n    \"@ARM64_LAUNCHER_URL@\" :\n    \"@X86_LAUNCHER_URL@\"\n  version \"@LAUNCHER_VERSION@\"\n  sha256 (RUBY_PLATFORM.include? \"arm64\") ?\n    \"@ARM64_LAUNCHER_SHA256@\" :\n    \"@X86_LAUNCHER_SHA256@\"\n  license \"Apache-2.0\"\n\n  def install\n    if (RUBY_PLATFORM.include? \"arm64\")\n      bin.install \"scala-cli-aarch64-apple-darwin\" => \"scala-cli\"\n    else\n      bin.install \"scala-cli-x86_64-apple-darwin\" => \"scala-cli\"\n    end\n  end\n\n  test do\n    (testpath / \"Hello.scala\").write \"object Hello {\n                                def main(args: Array[String]): Unit =\n                                  println(\\\"Hello from Scala\\\")\n                              }\"\n    output = shell_output(\"#{bin}/scala-cli Hello.scala\")\n    assert_equal [\"Hello from Scala\\n\"], output.lines\n  end\nend\n"
  },
  {
    "path": ".github/scripts/scala.rb.template",
    "content": "# typed: false\n# frozen_string_literal: true\n\n# Experimental Scala Formula\nclass Scala < Formula\n  desc \"Experimental launcher for Scala\"\n  homepage \"https://virtuslab.github.io/scala-cli/\"\n  url (RUBY_PLATFORM.include? \"arm64\") ?\n    \"@ARM64_LAUNCHER_URL@\" :\n    \"@X86_LAUNCHER_URL@\"\n  version \"@LAUNCHER_VERSION@\"\n  sha256 (RUBY_PLATFORM.include? \"arm64\") ?\n    \"@ARM64_LAUNCHER_SHA256@\" :\n    \"@X86_LAUNCHER_SHA256@\"\n  license \"Apache-2.0\"\n\n  def install\n    if (RUBY_PLATFORM.include? \"arm64\")\n      bin.install \"scala-cli-aarch64-apple-darwin\" => \"scala-cli\"\n    else\n      bin.install \"scala-cli-x86_64-apple-darwin\" => \"scala-cli\"\n    end\n    bin.install_symlink \"scala-cli\" => \"scala\"\n  end\n\n  test do\n    (testpath / \"Hello.scala\").write \"object Hello {\n                                def main(args: Array[String]): Unit =\n                                  println(\\\"Hello from Scala\\\")\n                              }\"\n    output = shell_output(\"#{bin}/scala-cli Hello.scala\")\n    assert_equal [\"Hello from Scala\\n\"], output.lines\n  end\nend\n"
  },
  {
    "path": ".github/scripts/update-website.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\ngit config --global user.name \"gh-actions\"\ngit config --global user.email \"actions@github.com\"\n\ncd website\nyarn install\nyarn build\nyarn deploy\n"
  },
  {
    "path": ".github/scripts/verify_old_cpus.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\n# Verifies that the native launcher runs on older x86_64 CPUs (without AVX/AVX2/FMA).\n# Uses QEMU user-mode emulation with a Westmere CPU model (2010, SSE4.2 but no AVX).\n\nLAUNCHER_GZ=\"${1:?Usage: $0 <launcher.gz>}\"\n\nsudo apt-get update -qq && sudo apt-get install -y -qq qemu-user > /dev/null\n\nLAUNCHER=\"/tmp/scala-cli-compat-test\"\ngunzip -c \"$LAUNCHER_GZ\" > \"$LAUNCHER\"\nchmod +x \"$LAUNCHER\"\n\necho \"Running native launcher under QEMU with Westmere CPU (no AVX/AVX2/FMA)...\"\nqemu-x86_64 -cpu Westmere \"$LAUNCHER\" version\necho \"CPU compatibility check passed.\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non:\n  push:\n    branches:\n    - main\n    tags:\n    - \"v*\"\n  pull_request:\n  workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}\n\njobs:\n  changes:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 5\n    outputs:\n      code: ${{ steps.classify.outputs.code }}\n      docs: ${{ steps.classify.outputs.docs }}\n      ci: ${{ steps.classify.outputs.ci }}\n      format_config: ${{ steps.classify.outputs.format_config }}\n      benchmark: ${{ steps.classify.outputs.benchmark }}\n      gifs: ${{ steps.classify.outputs.gifs }}\n      mill_wrapper: ${{ steps.classify.outputs.mill_wrapper }}\n      test_all: ${{ steps.overrides.outputs.test_all }}\n      test_native: ${{ steps.overrides.outputs.test_native }}\n      test_integration: ${{ steps.overrides.outputs.test_integration }}\n      test_docs: ${{ steps.overrides.outputs.test_docs }}\n      test_format: ${{ steps.overrides.outputs.test_format }}\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n    - name: Classify changes\n      id: classify\n      env:\n        EVENT_NAME: ${{ github.event_name }}\n        BASE_REF: ${{ github.event.pull_request.base.ref }}\n      run: .github/scripts/classify-changes.sh\n    - name: Check override keywords\n      id: overrides\n      env:\n        EVENT_NAME: ${{ github.event_name }}\n        PR_BODY: ${{ github.event.pull_request.body }}\n      run: .github/scripts/check-override-keywords.sh\n\n  unit-tests:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.mill_wrapper == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping unit tests -- changes do not affect compiled code, CI, or mill wrapper.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Copy launcher\n      run: ./mill -i copyJvmLauncher --directory artifacts/\n      if: runner.os == 'Linux' && env.SHOULD_RUN == 'true'\n    - name: Copy bootstrapped launcher\n      run: ./mill -i copyJvmBootstrappedLauncher --directory artifacts/\n      if: runner.os == 'Linux' && env.SHOULD_RUN == 'true'\n    - uses: actions/upload-artifact@v7\n      if: runner.os == 'Linux' && env.SHOULD_RUN == 'true'\n      with:\n        name: jvm-launchers\n        path: artifacts/\n        if-no-files-found: error\n        retention-days: 2\n    - name: Cross compile everything\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i '__[_].compile'\n    - name: Build macros negative compilation tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i build-macros[_].test.testNegativeCompilation\n    - name: Unit tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i unitTests\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc unit-tests 'Scala CLI Unit Tests' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-unit-tests\n        path: test-report.xml\n\n  test-fish-shell:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.mill_wrapper == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping fish shell test -- changes do not affect compiled code, CI, or mill wrapper.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Install fish\n      if: env.SHOULD_RUN == 'true'\n      run: |\n        sudo apt-add-repository ppa:fish-shell/release-3\n        sudo apt update\n        sudo apt install fish\n    - name: Test mill script in fish shell\n      if: env.SHOULD_RUN == 'true'\n      run: |\n        fish -c './mill __.compile'\n\n  jvm-bootstrapped-tests-default:\n    needs: [changes]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_integration == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping JVM bootstrapped integration tests -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: JVM integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i integration.test.jvmBootstrapped\n        env:\n          SCALA_CLI_IT_GROUP: 1\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc jvm-bootstrapped-tests-default 'Scala CLI JVM Bootstrapped Tests' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-jvm-bootstrapped-tests-default\n          path: test-report.xml\n\n  jvm-tests-default:\n    needs: [changes]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_integration == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping JVM integration tests (default) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: JVM integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i integration.test.jvm\n      env:\n        SCALA_CLI_IT_GROUP: 1\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc jvm-tests-default 'Scala CLI JVM Tests (default)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-jvm-tests-default\n        path: test-report.xml\n\n  jvm-tests-scala-2-13:\n    needs: [changes]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_integration == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping JVM integration tests (Scala 2.13) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: JVM integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i integration.test.jvm\n      env:\n        SCALA_CLI_IT_GROUP: 2\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc jvm-tests-scala-2-13 'Scala CLI JVM Tests (Scala 2.13)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-jvm-tests-scala-2-13\n        path: test-report.xml\n\n  jvm-tests-scala-2-12:\n    needs: [changes]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_integration == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping JVM integration tests (Scala 2.12) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: JVM integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i integration.test.jvm\n      env:\n        SCALA_CLI_IT_GROUP: 3\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc jvm-tests-scala-2-12 'Scala CLI JVM Tests (Scala 2.12)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-jvm-tests-scala-2-12\n        path: test-report.xml\n\n  jvm-tests-lts:\n    needs: [changes]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_integration == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping JVM integration tests (Scala 3 LTS) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: JVM integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i integration.test.jvm\n        env:\n          SCALA_CLI_IT_GROUP: 4\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc jvm-tests-lts 'Scala CLI JVM Tests (Scala 3 LTS)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-jvm-tests-lts\n          path: test-report.xml\n\n  jvm-tests-rc:\n    needs: [changes]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_integration == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping JVM integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: JVM integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i integration.test.jvm\n        env:\n          SCALA_CLI_IT_GROUP: 5\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc jvm-tests-rc 'Scala CLI JVM Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-jvm-tests-rc\n          path: test-report.xml\n\n  generate-linux-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping Linux native launcher generation -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Generate native launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-native-image.sh\n    - run: ./mill -i ci.setShouldPublish\n      if: env.SHOULD_RUN == 'true'\n    - name: Build OS packages\n      if: env.SHOULD_PUBLISH == 'true' && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-os-packages.sh\n    - name: Copy artifacts\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i copyDefaultLauncher --directory artifacts/\n    - name: Verify native launcher CPU compatibility\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/verify_old_cpus.sh artifacts/scala-cli-x86_64-pc-linux.gz\n    - uses: actions/upload-artifact@v7\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: linux-launchers\n        path: artifacts/\n        if-no-files-found: error\n        retention-days: 2\n\n  native-linux-tests-default:\n    needs: [changes, generate-linux-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native Linux integration tests (default) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: linux-launchers\n        path: artifacts/\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i nativeIntegrationTests\n      env:\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 1\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc linux-tests-default 'Scala CLI Linux Tests (default)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-linux-tests-default\n        path: test-report.xml\n\n  native-linux-tests-scala-2-13:\n    needs: [changes, generate-linux-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native Linux integration tests (Scala 2.13) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: linux-launchers\n        path: artifacts/\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i nativeIntegrationTests\n      env:\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 2\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc linux-tests-scala-2-13 'Scala CLI Linux Tests (Scala 2.13)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-linux-tests-scala-2-13\n        path: test-report.xml\n\n  native-linux-tests-scala-2-12:\n    needs: [changes, generate-linux-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native Linux integration tests (Scala 2.12) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: linux-launchers\n        path: artifacts/\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i nativeIntegrationTests\n      env:\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 3\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc linux-tests-scala-2-12 'Scala CLI Linux Tests (Scala 2.12)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-linux-tests-scala-2-12\n        path: test-report.xml\n\n  native-linux-tests-lts:\n    needs: [changes, generate-linux-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native Linux integration tests (Scala 3 LTS) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: linux-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 4\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc linux-tests-lts 'Scala CLI Linux Tests (Scala 3 LTS)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-linux-tests-lts\n          path: test-report.xml\n\n  native-linux-tests-rc:\n    needs: [changes, generate-linux-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native Linux integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: linux-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc linux-tests-rc 'Scala CLI Linux Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-linux-tests-rc\n          path: test-report.xml\n\n  generate-linux-arm64-native-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04-arm\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping Linux ARM64 native launcher generation -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: Install build dependencies\n        if: env.SHOULD_RUN == 'true'\n        run: |\n          sudo apt-get update -q -y\n          sudo apt-get install -q -y build-essential libz-dev zlib1g-dev python3-pip\n      - name: Generate native launcher\n        if: env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-native-image.sh\n      - run: ./mill -i ci.setShouldPublish\n        if: env.SHOULD_RUN == 'true'\n      - name: Build OS packages\n        if: env.SHOULD_PUBLISH == 'true' && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-os-packages.sh\n      - name: Copy artifacts\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i copyDefaultLauncher --directory artifacts/\n      - uses: actions/upload-artifact@v7\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: linux-aarch64-launchers\n          path: artifacts/\n          if-no-files-found: error\n          retention-days: 2\n\n  native-linux-arm64-tests-default:\n    needs: [changes, generate-linux-arm64-native-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04-arm\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native Linux ARM64 integration tests (default) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: linux-aarch64-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 1\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc linux-tests-default 'Scala CLI Linux ARM 64 Tests (default)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-linux-arm64-tests-default\n          path: test-report.xml\n\n  native-linux-arm64-tests-rc:\n    needs: [changes, generate-linux-arm64-native-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04-arm\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native Linux ARM64 integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: linux-aarch64-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc linux-tests-rc 'Scala CLI Linux ARM64 Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-linux-arm64-tests-rc\n          path: test-report.xml\n\n  generate-macos-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: \"macOS-15-intel\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping macOS native launcher generation -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Ensure it's not running on aarch64\n      if: env.SHOULD_RUN == 'true'\n      run: scala-cli -e 'assert(System.getProperty(\"os.arch\") != \"aarch64\")'\n    - name: Generate native launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-native-image.sh\n    - run: ./mill -i ci.setShouldPublish\n      if: env.SHOULD_RUN == 'true'\n    - name: Build OS packages\n      if: env.SHOULD_PUBLISH == 'true' && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-os-packages.sh\n    - name: Copy artifacts\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i copyDefaultLauncher --directory artifacts/\n    - uses: actions/upload-artifact@v7\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: macos-launchers\n        path: artifacts/\n        if-no-files-found: error\n        retention-days: 2\n\n  native-macos-tests-default:\n    needs: [changes, generate-macos-launcher]\n    timeout-minutes: 150\n    runs-on: \"macOS-15-intel\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native macOS integration tests (default) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Ensure it's not running on aarch64\n      if: env.SHOULD_RUN == 'true'\n      run: scala-cli -e 'assert(System.getProperty(\"os.arch\") != \"aarch64\")'\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: macos-launchers\n        path: artifacts/\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i nativeIntegrationTests\n      env:\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 1\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc macos-tests-default 'Scala CLI MacOS Tests (default)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-macos-tests-default\n        path: test-report.xml\n\n  native-macos-tests-rc:\n    needs: [changes, generate-macos-launcher]\n    timeout-minutes: 150\n    runs-on: \"macOS-15-intel\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native macOS integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: Ensure it's not running on aarch64\n        if: env.SHOULD_RUN == 'true'\n        run: scala-cli -e 'assert(System.getProperty(\"os.arch\") != \"aarch64\")'\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: macos-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc macos-tests-rc 'Scala CLI MacOS Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-macos-tests-rc\n          path: test-report.xml\n\n  generate-macos-arm64-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: \"macOS-15\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping macOS ARM64 native launcher generation -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-darwin-aarch64-22.2.0.tar.gz\"\n      - name: Ensure it's running on aarch64\n        if: env.SHOULD_RUN == 'true'\n        run: scala-cli -e 'assert(System.getProperty(\"os.arch\") == \"aarch64\")'\n      - name: Generate native launcher\n        if: env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-native-image.sh\n      - run: ./mill -i ci.setShouldPublish\n        if: env.SHOULD_RUN == 'true'\n      - name: Build OS packages\n        if: env.SHOULD_PUBLISH == 'true' && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-os-packages.sh\n      - name: Copy artifacts\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i copyDefaultLauncher --directory artifacts/\n      - uses: actions/upload-artifact@v7\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: macos-arm64-launchers\n          path: artifacts/\n          if-no-files-found: error\n          retention-days: 2\n\n  native-macos-arm64-tests-default:\n    needs: [changes, generate-macos-arm64-launcher]\n    timeout-minutes: 150\n    runs-on: \"macOS-15\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native macOS ARM64 integration tests (default) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-darwin-aarch64-22.2.0.tar.gz\"\n      - name: Ensure it's running on aarch64\n        if: env.SHOULD_RUN == 'true'\n        run: scala-cli -e 'assert(System.getProperty(\"os.arch\") == \"aarch64\")'\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: macos-arm64-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 1\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc macos-arm64-tests-default 'Scala CLI MacOS ARM64 Tests (default)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-macos-arm64-tests-default\n          path: test-report.xml\n\n  native-macos-arm64-tests-lts:\n    needs: [changes, generate-macos-arm64-launcher]\n    timeout-minutes: 150\n    runs-on: \"macOS-15\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native macOS ARM64 integration tests (Scala 3 LTS) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-darwin-aarch64-22.2.0.tar.gz\"\n      - name: Ensure it's running on aarch64\n        if: env.SHOULD_RUN == 'true'\n        run: scala-cli -e 'assert(System.getProperty(\"os.arch\") == \"aarch64\")'\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: macos-arm64-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 4\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc macos-arm64-tests-lts 'Scala CLI MacOS ARM64 Tests (Scala 3 LTS)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-macos-arm64-tests-lts\n          path: test-report.xml\n\n  native-macos-arm64-tests-rc:\n    needs: [changes, generate-macos-arm64-launcher]\n    timeout-minutes: 150\n    runs-on: \"macOS-15\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native macOS ARM64 integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-darwin-aarch64-22.2.0.tar.gz\"\n      - name: Ensure it's running on aarch64\n        if: env.SHOULD_RUN == 'true'\n        run: scala-cli -e 'assert(System.getProperty(\"os.arch\") == \"aarch64\")'\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: macos-arm64-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc macos-arm64-tests-rc 'Scala CLI MacOS ARM64 Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-macos-arm64-tests-rc\n          path: test-report.xml\n\n  generate-windows-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: \"windows-2025\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping Windows native launcher generation -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - name: Import custom registry and verify\n      if: env.SHOULD_RUN == 'true'\n      uses: ./.github/actions/windows-reg-import\n      with:\n        reg-file: .github/ci/windows/custom.reg\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Get latest coursier launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/get-latest-cs.sh\n      shell: bash\n    - name: Generate native launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-native-image.sh\n      shell: bash\n    - run: ./mill -i ci.setShouldPublish\n      if: env.SHOULD_RUN == 'true'\n    - name: Build OS packages\n      if: env.SHOULD_PUBLISH == 'true' && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-os-packages.sh\n      shell: bash\n    - name: Copy artifacts\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i copyDefaultLauncher --directory artifacts/\n    - uses: actions/upload-artifact@v7\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: windows-launchers\n        path: artifacts/\n        if-no-files-found: error\n        retention-days: 2\n\n  native-windows-tests-default:\n    needs: [changes, generate-windows-launcher]\n    timeout-minutes: 150\n    runs-on: \"windows-2025\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native Windows integration tests (default) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - name: Import custom registry and verify\n      if: env.SHOULD_RUN == 'true'\n      uses: ./.github/actions/windows-reg-import\n      with:\n        reg-file: .github/ci/windows/custom.reg\n    - name: Set up Python\n      if: env.SHOULD_RUN == 'true'\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Get latest coursier launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/get-latest-cs.sh\n      shell: bash\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: windows-launchers\n        path: artifacts/\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i nativeIntegrationTests\n      env:\n        COURSIER_JNI: force\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 1\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run:  scala-cli shebang .github/scripts/generate-junit-reports.sc windows-tests-default 'Scala CLI Windows Tests (default)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-windows-tests-default\n        path: test-report.xml\n\n  native-windows-tests-lts:\n    needs: [changes, generate-windows-launcher]\n    timeout-minutes: 150\n    runs-on: \"windows-2025\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native Windows integration tests (Scala 3 LTS) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - name: Import custom registry and verify\n        if: env.SHOULD_RUN == 'true'\n        uses: ./.github/actions/windows-reg-import\n        with:\n          reg-file: .github/ci/windows/custom.reg\n      - name: Set up Python\n        if: env.SHOULD_RUN == 'true'\n        uses: actions/setup-python@v6\n        with:\n          python-version: \"3.10\"\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: Get latest coursier launcher\n        if: env.SHOULD_RUN == 'true'\n        run: .github/scripts/get-latest-cs.sh\n        shell: bash\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: windows-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          COURSIER_JNI: force\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 4\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run:  scala-cli shebang .github/scripts/generate-junit-reports.sc windows-tests-lts 'Scala CLI Windows Tests (Scala 3 LTS)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-windows-tests-lts\n          path: test-report.xml\n\n  native-windows-tests-rc:\n    needs: [changes, generate-windows-launcher]\n    timeout-minutes: 150\n    runs-on: \"windows-2025\"\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native Windows integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - name: Import custom registry and verify\n        if: env.SHOULD_RUN == 'true'\n        uses: ./.github/actions/windows-reg-import\n        with:\n          reg-file: .github/ci/windows/custom.reg\n      - name: Set up Python\n        if: env.SHOULD_RUN == 'true'\n        uses: actions/setup-python@v6\n        with:\n          python-version: \"3.10\"\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - name: Get latest coursier launcher\n        if: env.SHOULD_RUN == 'true'\n        run: .github/scripts/get-latest-cs.sh\n        shell: bash\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: windows-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i nativeIntegrationTests\n        env:\n          COURSIER_JNI: force\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run:  scala-cli shebang .github/scripts/generate-junit-reports.sc windows-tests-rc 'Scala CLI Windows Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-windows-tests-rc\n          path: test-report.xml\n\n  generate-mostly-static-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping mostly-static native launcher generation -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Generate native launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-native-image.sh mostly-static\n      shell: bash\n    - name: Copy artifacts\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i copyMostlyStaticLauncher --directory artifacts/\n    - uses: actions/upload-artifact@v7\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: mostly-static-launchers\n        path: artifacts/\n        if-no-files-found: error\n        retention-days: 2\n\n  native-mostly-static-tests-default:\n    needs: [changes, generate-mostly-static-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native mostly-static integration tests (default) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: mostly-static-launchers\n        path: artifacts/\n    - name: Build slim docker image\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-slim-docker-image.sh\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i integration.test.nativeMostlyStatic\n      env:\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 1\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Docker integration tests\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: ./mill integration.docker-slim.test\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc native-mostly-static-tests-default 'Scala CLI Native Mostly Static Tests (default)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-native-mostly-static-tests-default\n        path: test-report.xml\n    - name: Login to GitHub Container Registry\n      if: startsWith(github.ref, 'refs/tags/v') && env.SHOULD_RUN == 'true'\n      uses: docker/login-action@v4\n      with:\n        username: ${{ secrets.DOCKERHUB_USERNAME }}\n        password: ${{ secrets.DOCKERHUB_TOKEN }}\n    - name: Push slim scala-cli image to github container registry\n      if: startsWith(github.ref, 'refs/tags/v') && env.SHOULD_RUN == 'true'\n      run: .github/scripts/publish-slim-docker-images.sh\n\n  native-mostly-static-tests-rc:\n    needs: [changes, generate-mostly-static-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native mostly-static integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: mostly-static-launchers\n          path: artifacts/\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i integration.test.nativeMostlyStatic\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc native-mostly-static-tests-rc 'Scala CLI Native Mostly Static Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-native-mostly-static-tests-rc\n          path: test-report.xml\n\n\n  generate-static-launcher:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping static native launcher generation -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Generate native launcher\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-native-image.sh static\n      shell: bash\n    - name: Copy artifacts\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i copyStaticLauncher --directory artifacts/\n    - uses: actions/upload-artifact@v7\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: static-launchers\n        path: artifacts/\n        if-no-files-found: error\n        retention-days: 2\n\n  native-static-tests-default:\n    needs: [changes, generate-static-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping native static integration tests (default) -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: static-launchers\n        path: artifacts/\n    - name: Build docker image\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-docker-image.sh\n    - name: Native integration tests\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i integration.test.nativeStatic\n      env:\n        UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n        SCALA_CLI_IT_GROUP: 1\n        SCALA_CLI_SODIUM_JNI_ALLOW: false\n    - name: Docker integration tests\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: ./mill integration.docker.test\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc native-static-tests-default 'Scala CLI Native Static Tests (default)' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-native-static-tests-default\n        path: test-report.xml\n    - name: Login to GitHub Container Registry\n      if: startsWith(github.ref, 'refs/tags/v') && env.SHOULD_RUN == 'true'\n      uses: docker/login-action@v4\n      with:\n        username: ${{ secrets.DOCKERHUB_USERNAME }}\n        password: ${{ secrets.DOCKERHUB_TOKEN }}\n    - name: Push scala-cli to github container registry\n      if: startsWith(github.ref, 'refs/tags/v') && env.SHOULD_RUN == 'true'\n      run: .github/scripts/publish-docker-images.sh\n\n  native-static-tests-rc:\n    needs: [changes, generate-static-launcher]\n    timeout-minutes: 150\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_native == 'true' }}\n    steps:\n      - name: Log skip reason\n        if: env.SHOULD_RUN != 'true'\n        run: echo \"Skipping native static integration tests (RC) -- changes do not affect compiled code or CI.\"\n      - uses: actions/checkout@v6\n        if: env.SHOULD_RUN == 'true'\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        if: env.SHOULD_RUN == 'true'\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        if: env.SHOULD_RUN == 'true'\n        with:\n          name: static-launchers\n          path: artifacts/\n      - name: Build docker image\n        if: env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-docker-image.sh\n      - name: Native integration tests\n        if: env.SHOULD_RUN == 'true'\n        run: ./mill -i integration.test.nativeStatic\n        env:\n          UPDATE_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY: artifacts/\n          SCALA_CLI_IT_GROUP: 5\n          SCALA_CLI_SODIUM_JNI_ALLOW: false\n      - name: Convert Mill test reports to JUnit XML format\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        run: .github/scripts/generate-junit-reports.sc native-static-tests-rc 'Scala CLI Native Static Tests (5)' test-report.xml out/\n      - name: Upload test report\n        uses: actions/upload-artifact@v7\n        if: (success() || failure()) && env.SHOULD_RUN == 'true'\n        with:\n          name: test-results-native-static-tests-rc\n          path: test-report.xml\n\n  docs-tests:\n    needs: [changes]\n    # for now, let's run those tests only on ubuntu\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.docs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.gifs == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_docs == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping docs tests -- changes do not affect code, docs, CI, or gifs.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"zulu:17\"\n    - uses: actions/setup-node@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        node-version: 24\n    - name: Build documentation\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/build-website.sh\n    - name: Verify release notes formatting\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/process_release_notes.sc verify website/docs/release_notes.md\n    - name: Test documentation\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i 'docs-tests[]'.test\n    - name: Convert Mill test reports to JUnit XML format\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      run: .github/scripts/generate-junit-reports.sc docs-tests 'Scala CLI Docs Tests' test-report.xml out/\n    - name: Upload test report\n      uses: actions/upload-artifact@v7\n      if: (success() || failure()) && env.SHOULD_RUN == 'true'\n      with:\n        name: test-results-docs-tests\n        path: test-report.xml\n\n  checks:\n    needs: [changes]\n    timeout-minutes: 60\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.docs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.format_config == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping checks -- changes do not affect code, docs, CI, or format config.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Check Scala / Scala.js versions in doc\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i ci.checkScalaVersions\n    - name: Check native-image config format\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i __.checkNativeImageConfFormat\n    - name: Check Ammonite availability\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill -i 'dummy.amm[_].resolvedRunMvnDeps'\n    - name: Check for cross Scala version conflicts\n      if: env.SHOULD_RUN == 'true'\n      run: .github/scripts/check-cross-version-deps.sc\n    - name: Scalafix check\n      if: env.SHOULD_RUN == 'true'\n      run: |\n        ./mill -i __.fix --check || (\n          echo \"To remove unused import run\"\n          echo \"  ./mill -i __.fix\"\n          exit 1\n        )\n\n  format:\n    needs: [changes]\n    timeout-minutes: 15\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.docs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.format_config == 'true' || needs.changes.outputs.test_all == 'true' || needs.changes.outputs.test_format == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping format check -- changes do not affect code, docs, CI, or format config.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n    - run: scala-cli fmt . --check\n      if: env.SHOULD_RUN == 'true'\n\n  reference-doc:\n    needs: [changes]\n    timeout-minutes: 15\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.docs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping reference doc check -- changes do not affect code, docs, or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Check that reference doc is up-to-date\n      if: env.SHOULD_RUN == 'true'\n      run: |\n        ./mill -i 'generate-reference-doc[]'.run --check || (\n          echo \"Reference doc is not up-to-date. Run\"\n          echo \"  ./mill -i 'generate-reference-doc[]'.run\"\n          echo \"to update it, then commit the result.\"\n          exit 1\n        )\n\n  bloop-memory-footprint:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.benchmark == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping bloop memory footprint benchmark -- changes do not affect code, CI, or benchmarks.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Java Version\n      if: env.SHOULD_RUN == 'true'\n      run: java -version\n    - name: Java Home\n      if: env.SHOULD_RUN == 'true'\n      run: echo \"$JAVA_HOME\"\n    - name: Build Scala CLI\n      if: env.SHOULD_RUN == 'true'\n      run: ./mill copyJvmLauncher --directory build\n    - name: Build Benchmark\n      if: env.SHOULD_RUN == 'true'\n      run: java -jar ./build/scala-cli --power package --standalone gcbenchmark/gcbenchmark.scala -o gc\n    - name: Run Benchmark\n      if: env.SHOULD_RUN == 'true'\n      run: ./gc $(realpath ./build/scala-cli)\n\n  test-hypothetical-sbt-export:\n    needs: [changes]\n    timeout-minutes: 120\n    runs-on: ubuntu-24.04\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping sbt export test -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - name: Try to export to SBT\n      if: env.SHOULD_RUN == 'true'\n      run: scala-cli --power export --sbt .\n\n  vc-redist:\n    needs: [changes]\n    timeout-minutes: 15\n    runs-on: \"windows-2025\"\n    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == 'Virtuslab/scala-cli'\n    env:\n      SHOULD_RUN: ${{ needs.changes.outputs.code == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.test_all == 'true' }}\n    steps:\n    - name: Log skip reason\n      if: env.SHOULD_RUN != 'true'\n      run: echo \"Skipping vc-redist -- changes do not affect compiled code or CI.\"\n    - uses: actions/checkout@v6\n      if: env.SHOULD_RUN == 'true'\n      with:\n        fetch-depth: 0\n        submodules: true\n    - name: Import custom registry and verify\n      if: env.SHOULD_RUN == 'true'\n      uses: ./.github/actions/windows-reg-import\n      with:\n        reg-file: .github/ci/windows/custom.reg\n    - uses: coursier/cache-action@v8\n      if: env.SHOULD_RUN == 'true'\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      if: env.SHOULD_RUN == 'true'\n      with:\n        jvm: \"temurin:17\"\n    - run: ./mill -i ci.copyVcRedist\n      if: env.SHOULD_RUN == 'true'\n    - uses: actions/upload-artifact@v7\n      if: env.SHOULD_RUN == 'true'\n      with:\n        name: vc-redist-launchers\n        path: artifacts/\n        if-no-files-found: warn\n        retention-days: 2\n\n  publish:\n    needs:\n      - changes\n      - unit-tests\n      - jvm-bootstrapped-tests-default\n      - jvm-tests-default\n      - jvm-tests-scala-2-13\n      - jvm-tests-scala-2-12\n      - jvm-tests-lts\n      - jvm-tests-rc\n      - native-linux-tests-default\n      - native-linux-tests-scala-2-13\n      - native-linux-tests-scala-2-12\n      - native-linux-tests-lts\n      - native-linux-tests-rc\n      - native-linux-arm64-tests-default\n      - native-linux-arm64-tests-rc\n      - native-macos-tests-default\n      - native-macos-tests-rc\n      - native-macos-arm64-tests-default\n      - native-macos-arm64-tests-lts\n      - native-macos-arm64-tests-rc\n      - native-windows-tests-default\n      - native-windows-tests-lts\n      - native-windows-tests-rc\n      - native-mostly-static-tests-default\n      - native-mostly-static-tests-rc\n      - native-static-tests-default\n      - native-static-tests-rc\n      - vc-redist\n      - format\n      - checks\n      - test-fish-shell\n      - test-hypothetical-sbt-export\n      - bloop-memory-footprint\n      - reference-doc\n      - docs-tests\n    if: github.event_name == 'push' && github.repository == 'VirtusLab/scala-cli'\n    runs-on: ubuntu-24.04\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n        submodules: true\n        ssh-key: ${{ secrets.SSH_PRIVATE_KEY_SCALA_CLI }}\n    - uses: coursier/cache-action@v8\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      with:\n        jvm: \"temurin:17\"\n    - name: GPG setup\n      run: .github/scripts/gpg-setup.sh\n      env:\n        PGP_SECRET: ${{ secrets.PGP_SECRET }}\n    - name: Set publish flag\n      run: ./mill -i ci.setShouldPublish\n    - name: Print version\n      run: ./mill -i show project.publish.finalPublishVersion\n    - name: Publish to Sonatype Central\n      run: ./mill mill.scalalib.SonatypeCentralPublishModule/ --publishArtifacts '{__[],_}.publishArtifacts'\n      if: env.SHOULD_PUBLISH == 'true'\n      env:\n        MILL_PGP_SECRET_BASE64: ${{ secrets.PGP_SECRET }}\n        MILL_PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}\n        MILL_SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}\n        MILL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n    - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555\n      with:\n        ssh-private-key: |\n          ${{ secrets.SSH_PRIVATE_KEY_SCALA_CLI }}\n    - name: Update stable branch\n      if: env.SHOULD_PUBLISH == 'true' && startsWith(github.ref, 'refs/tags/v')\n      run: |\n        git config user.name gh-actions\n        git config user.email actions@github.com\n        git checkout stable\n        git merge origin/main -m \"Back port of documentation changes to stable\"\n        git push origin stable\n\n  launchers:\n    timeout-minutes: 20\n    needs:\n      - changes\n      - unit-tests\n      - jvm-bootstrapped-tests-default\n      - jvm-tests-default\n      - jvm-tests-scala-2-13\n      - jvm-tests-scala-2-12\n      - jvm-tests-lts\n      - jvm-tests-rc\n      - native-linux-tests-default\n      - native-linux-tests-scala-2-13\n      - native-linux-tests-scala-2-12\n      - native-linux-tests-lts\n      - native-linux-tests-rc\n      - native-linux-arm64-tests-default\n      - native-linux-arm64-tests-rc\n      - native-macos-tests-default\n      - native-macos-tests-rc\n      - native-macos-arm64-tests-default\n      - native-macos-arm64-tests-lts\n      - native-macos-arm64-tests-rc\n      - native-windows-tests-default\n      - native-windows-tests-lts\n      - native-windows-tests-rc\n      - native-mostly-static-tests-default\n      - native-mostly-static-tests-rc\n      - native-static-tests-default\n      - native-static-tests-rc\n      - vc-redist\n      - format\n      - checks\n      - test-fish-shell\n      - test-hypothetical-sbt-export\n      - bloop-memory-footprint\n      - reference-doc\n      - generate-linux-arm64-native-launcher\n      - publish\n    if: github.event_name == 'push'\n    runs-on: ubuntu-24.04\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: coursier/cache-action@v8\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      with:\n        jvm: \"temurin:17\"\n    - run: ./mill -i ci.setShouldPublish\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: linux-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: linux-aarch64-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: macos-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: macos-arm64-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: windows-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: mostly-static-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: static-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: jvm-launchers\n        path: artifacts/\n    - uses: actions/download-artifact@v8\n      if: env.SHOULD_PUBLISH == 'true'\n      with:\n        name: vc-redist-launchers\n        path: artifacts/\n    - run: ./mill -i uploadLaunchers --directory artifacts/\n      if: env.SHOULD_PUBLISH == 'true'\n      env:\n        UPLOAD_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n  update-packages:\n    name: Update packages\n    needs:\n      - changes\n      - launchers\n      - publish\n    runs-on: ubuntu-24.04\n    if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'VirtusLab/scala-cli'\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n          submodules: true\n      - uses: coursier/cache-action@v8\n        with:\n          ignoreJob: true\n      - uses: VirtusLab/scala-cli-setup@v1\n        with:\n          jvm: \"temurin:17\"\n      - uses: actions/download-artifact@v8\n        with:\n          name: linux-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: linux-aarch64-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: macos-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: macos-arm64-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: windows-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: mostly-static-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: static-launchers\n          path: artifacts/\n      - uses: actions/download-artifact@v8\n        with:\n          name: jvm-launchers\n          path: artifacts/\n      - name: Display structure of downloaded files\n        run: ls -R\n        working-directory: artifacts/\n      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555\n        with:\n          ssh-private-key: |\n            ${{ secrets.SCALA_CLI_PACKAGES_KEY }}\n            ${{ secrets.HOMEBREW_SCALA_CLI_KEY }}\n            ${{ secrets.HOMEBREW_SCALA_EXPERIMENTAL_KEY }}\n            ${{ secrets.SCALA_CLI_SETUP_KEY }}\n      - run: ./mill -i ci.updateInstallationScript\n        continue-on-error: true\n      - run: ./mill -i ci.updateScalaCliBrewFormula\n        continue-on-error: true\n      - name: GPG setup\n        run: .github/scripts/gpg-setup.sh\n        env:\n          PGP_SECRET: ${{ secrets.PGP_SECRET }}\n      - run: ./mill -i ci.updateDebianPackages\n        continue-on-error: true\n        env:\n          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}\n          GPG_EMAIL: ${{ secrets.GPG_EMAIL }}\n      - run: ./mill -i ci.updateCentOsPackages\n        continue-on-error: true\n        env:\n          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}\n          KEYGRIP: ${{ secrets.KEYGRIP }}\n          PGP_SECRET: ${{ secrets.PGP_SECRET }}\n          GPG_EMAIL: ${{ secrets.GPG_EMAIL }}\n      - run: ./mill -i ci.updateStandaloneLauncher\n        continue-on-error: true\n        env:\n          UPLOAD_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: Publish to SDKMAN\n        continue-on-error: true\n        run: .github/scripts/publish-sdkman.sh\n        shell: bash\n        env:\n          SDKMAN_KEY: ${{ secrets.SDKMAN_KEY }}\n          SDKMAN_TOKEN: ${{ secrets.SDKMAN_TOKEN }}\n      - run: ./mill -i ci.updateScalaCliSetup\n        continue-on-error: true\n      - run: ./mill -i ci.updateScalaExperimentalBrewFormula\n\n  update-windows-packages:\n    name: Update Windows packages\n    needs:\n      - changes\n      - launchers\n      - publish\n    runs-on: \"windows-2025\"\n    if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'VirtusLab/scala-cli'\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n        submodules: true\n    - name: Import custom registry and verify\n      uses: ./.github/actions/windows-reg-import\n      with:\n        reg-file: .github/ci/windows/custom.reg\n    - uses: coursier/cache-action@v8\n      with:\n        ignoreJob: true\n    - uses: VirtusLab/scala-cli-setup@v1\n      with:\n        jvm: \"temurin:17\"\n    - uses: actions/download-artifact@v8\n      with:\n        name: windows-launchers\n        path: artifacts/\n    - name: Publish to chocolatey\n      run: ./mill -i ci.updateChocolateyPackage\n      continue-on-error: true\n      env:\n        CHOCO_SECRET: ${{ secrets.CHOCO_SECRET_KEY }}\n    - uses: vedantmgoyal9/winget-releaser@main\n      with:\n        identifier: VirtusLab.ScalaCLI\n        installers-regex: '\\.msi$'\n        fork-user: scala-steward\n        token: ${{ secrets.STEWARD_WINGET_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/publish-docker.yml",
    "content": "name: Create and publish a Docker image\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n    branches: [\"main\"]\n    tags: [\"v*\"]\n\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: virtuslab/scala-cli\n  DOCKERFILE: ./Dockerfile\n  REGISTRY_LOGIN: ${{ github.actor }}\n  REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}\n\njobs:\n  docker_build:\n    strategy:\n      fail-fast: true\n      matrix:\n        os: [\"ubuntu-24.04\", \"ubuntu-24.04-arm\"]\n    runs-on: ${{ matrix.os }}\n    # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.\n    permissions:\n      contents: read\n      packages: write\n      attestations: write\n      id-token: write\n      #\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      - name: Log in to the Container registry\n        uses: docker/login-action@v4\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ env.REGISTRY_LOGIN }}\n          password: ${{ env.REGISTRY_PASSWORD }}\n\n      # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` \"meta\" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.\n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@v6\n        with:\n          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n\n      # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.\n      # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see \"[Usage](https://github.com/docker/build-push-action#usage)\" in the README of the `docker/build-push-action` repository.\n      # It uses the `tags` and `labels` parameters to tag and label the image with the output from the \"meta\" step.\n      - name: Build and push Docker image\n        id: push\n        uses: docker/build-push-action@v7\n        with:\n          context: .\n          file: ${{ env.DOCKERFILE }}\n          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ matrix.os }}\n          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ matrix.os }},mode=max\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n\n      # This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see \"[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds).\"\n      - name: Generate artifact attestation\n        uses: actions/attest-build-provenance@v4\n        with:\n          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}\n          subject-digest: ${{ steps.push.outputs.digest }}\n          push-to-registry: true\n\n      - name: Export digest\n        run: |\n          mkdir -p /tmp/digests\n          digest=\"${{ steps.push.outputs.digest }}\"\n          touch \"/tmp/digests/${digest#sha256:}\"\n\n      - name: Upload digest\n        uses: actions/upload-artifact@v7\n        with:\n          name: digests-${{ matrix.os }}\n          path: /tmp/digests/*\n          if-no-files-found: error\n          retention-days: 1\n\n  docker_release_merge:\n    runs-on: ubuntu-24.04\n    permissions:\n      contents: read\n      packages: write\n      attestations: write\n      id-token: write\n    needs: [docker_build]\n    if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')\n    steps:\n      - name: Download digests\n        uses: actions/download-artifact@v8\n        with:\n          pattern: digests-*\n          path: /tmp/digests\n          merge-multiple: true\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n\n      - name: Log in to the Container registry\n        uses: docker/login-action@v4\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ env.REGISTRY_LOGIN }}\n          password: ${{ env.REGISTRY_PASSWORD }}\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v6\n        with:\n          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n\n      - name: Create manifest list and push\n        working-directory: /tmp/digests\n        run: |\n          docker buildx imagetools create $(jq -cr '.tags | map(\"-t \" + .) | join(\" \")' <<< \"$DOCKER_METADATA_OUTPUT_JSON\") \\\n            $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)\n\n      - name: Inspect image\n        run: |\n          docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}\n"
  },
  {
    "path": ".github/workflows/test-report.yml",
    "content": "name: 'Test Report'\non:\n  workflow_run:\n    workflows: ['CI']\n    types:\n      - completed\npermissions:\n  statuses: write\n  checks: write\n  contents: write\n  pull-requests: write\n  actions: write\njobs:\n  report:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: dorny/test-reporter@v3\n        with:\n          artifact: /test-results-(.*)/\n          name: 'Test report $1'\n          path: '*.xml'\n          reporter: java-junit"
  },
  {
    "path": ".github/workflows/website.yaml",
    "content": "name: Website deploy\non:\n  push:\n    branches:\n    - stable\n\njobs:\n  update-website:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n        submodules: true\n    - uses: actions/setup-node@v6\n      with:\n        node-version: 24\n    - run: .github/scripts/update-website.sh\n      env:\n        GIT_USER: Virtuslab\n        DEPLOYMENT_BRANCH: gh-pages\n        GIT_PASS: ${{ secrets.GITHUB_TOKEN }}\n    # after the release the PR should be empty\n    - name: Open PR with changes back to main\n      uses: repo-sync/pull-request@v2\n      with:\n        destination_branch: \"main\"\n        github_token: ${{ secrets.GITHUB_TOKEN }}\n        pr_title: \"Back port of documentation changes to main\"\n        name: Update stable branch\n        branch: backport/stable"
  },
  {
    "path": ".gitignore",
    "content": "out/\n.bloop/\n.metals/\n.vscode/\n.idea/\n.cursor/\n\n.bsp\n.scala-build\ndest/\ntarget/\n\n*/scoverage.coverage\n\n# ignore vim backup files\n*.sw[op]\n.DS_Store\n\n"
  },
  {
    "path": ".mill-jvm-opts",
    "content": "-Xmx2048m\n-Xms128m\n-Xss8m\n-Dxsbt.skip.cp.lookup=true\n"
  },
  {
    "path": ".mill-version",
    "content": "1.1.5\n"
  },
  {
    "path": ".scala-steward.conf",
    "content": "postUpdateHooks = [{\n  command = [\"./mill\", \"-i\", \"generate-reference-doc[].run\"],\n  commitMessage = \"Generate the reference doc\"\n}]\n"
  },
  {
    "path": ".scalafix.conf",
    "content": "rules  = [\n  DisableSyntax,\n  RemoveUnused,\n  OrganizeImports,\n  NoValInForComprehension,\n  ProcedureSyntax\n]\nDisableSyntax.noFinalize = true\nDisableSyntax.noIsInstanceOf = true\nDisableSyntax.noReturns = true\n\n// `rules` on compilation\ntriggered.rules = [\n  DisableSyntax\n]\n\nOrganizeImports {\n  coalesceToWildcardImportThreshold = 6\n  expandRelative = true\n  groups = [\"*\", \"re:javax?\\\\.\", \"scala.\"]\n  groupedImports = AggressiveMerge\n}\n"
  },
  {
    "path": ".scalafix3.conf",
    "content": "rules  = [\n  DisableSyntax,\n  RemoveUnused,\n  OrganizeImports,\n  NoValInForComprehension,\n  # ProcedureSyntax\n]\nDisableSyntax.noFinalize = true\nDisableSyntax.noIsInstanceOf = true\nDisableSyntax.noReturns = true\n\n// `rules` on compilation\ntriggered.rules = [\n  DisableSyntax\n]\n\nOrganizeImports {\n  coalesceToWildcardImportThreshold = 6\n  expandRelative = true\n  groups = [\"*\", \"re:javax?\\\\.\", \"scala.\"]\n  groupedImports = AggressiveMerge\n  targetDialect = Scala3\n}\n"
  },
  {
    "path": ".scalafmt.conf",
    "content": "version = \"3.10.7\"\n\nalign.preset = more\nmaxColumn = 100\nassumeStandardLibraryStripMargin = true\nindent.defnSite = 2\nindentOperator.topLevelOnly = false\nalign.preset = more\nalign.openParenCallSite = false\nnewlines.source = keep\nnewlines.beforeMultiline = keep\nnewlines.afterCurlyLambdaParams = keep\nnewlines.alwaysBeforeElseAfterCurlyIf = true\n\nrunner.dialect = scala3\n\nrewrite.rules = [\n  RedundantBraces\n  RedundantParens\n  SortModifiers\n]\n\nrewrite.redundantBraces {\n  ifElseExpressions = true\n  includeUnitMethods = false\n  stringInterpolation = true\n}\n\nrewrite.sortModifiers.order = [\n  \"private\", \"final\", \"override\", \"protected\",\n  \"implicit\", \"sealed\", \"abstract\", \"lazy\"\n]\n\nproject.excludeFilters = [\n  \".bloop\"\n  \".metals\"\n  \".scala-build\"\n  \"examples\" # Scala 3 scripts and using directives not supported yet\n  \"out\"\n  \"scala-version.scala\"\n]\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# AGENTS.md — Guidance for AI agents contributing to Scala CLI\n\nShort reference for AI agents. For task-specific guidance (directives, integration tests), load skills from *\n*[agentskills/](agentskills/)** when relevant.\n\n> **LLM Policy**: All AI-assisted contributions must comply with the\n> [LLM usage policy](https://github.com/scala/scala3/blob/HEAD/LLM_POLICY.md). The contributor (human) is responsible\n> for every line. State LLM usage in the PR description. See [LLM_POLICY.md](LLM_POLICY.md).\n\n## Human-facing docs\n\n- **[DEV.md](DEV.md)**                   — Setup, run from source, tests, launchers, GraalVM.\n- **[CONTRIBUTING.md](CONTRIBUTING.md)** — PR workflow, formatting, reference doc generation.\n- **[INTERNALS.md](INTERNALS.md)**       — Modules, `Inputs → Sources → Build`, preprocessing.\n\n## Build system\n\nThe project uses [Mill](https://mill-build.org/). Mill launchers ship with the repo (`./mill`). JVM 17 required.\nCross-compilation: default `Scala.defaultInternal`; `[]` = default version, `[_]` = all.\n\n### Key build files\n\n| File                            | Purpose                                                                                  |\n|---------------------------------|------------------------------------------------------------------------------------------|\n| `build.mill`                    | Root build definition: all module declarations, CI helper tasks, integration test wiring |\n| `project/deps/package.mill`     | Dependency versions and definitions (`Deps`, `Scala`, `Java` objects)                    |\n| `project/settings/package.mill` | Shared traits, utils (`HasTests`, `CliLaunchers`, `FormatNativeImageConf`, etc.)         |\n| `project/publish/package.mill`  | Publishing settings                                                                      |\n| `project/website/package.mill`  | Website-related build tasks                                                              |\n\n### Essential commands\n\n```bash\n./mill -i clean                                                           # Clean Mill context\n./mill -i scala …args…                                                    # Run Scala CLI from source\n./mill -i __.compile                                                      # Compile everything\n./mill -i unitTests                                                       # All unit tests\n./mill -i 'build-module[].test'                                           # Unit tests for a specific module\n./mill -i 'build-module[].test' 'scala.build.tests.BuildTestsScalac.*'    # Filter by suite\n./mill -i 'build-module[].test' 'scala.build.tests.BuildTests.simple'     # Single test by name\n./mill -i integration.test.jvm                                            # Integration tests (JVM launcher)\n./mill -i integration.test.jvm 'scala.cli.integration.RunTestsDefault.*'  # Integration: filter by suite\n./mill -i 'generate-reference-doc[]'.run                                  # Regenerate reference docs\n./mill -i __.fix                                                          # Fix import ordering (scalafix)\nscala-cli fmt .                                                           # Format all code (scalafmt)\n```\n\n## Project modules\n\nModules live under `modules/`. The dependency graph flows roughly as:\n\n```\nspecification-level → config → core → options → directives → build-module → cli\n```\n\n### Module overview\n\nThe list below may not be exhaustive — check `modules/` and `build.mill` for the current set.\n\n| Module                                        | Purpose                                                                                                          |\n|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------|\n| `specification-level`                         | Defines `SpecificationLevel` (MUST / SHOULD / IMPLEMENTATION / RESTRICTED / EXPERIMENTAL) for SIP-46 compliance. |\n| `config`                                      | Scala CLI configuration keys and persistence.                                                                    |\n| `build-macros`                                | Compile-time macros (e.g. `EitherCps`).                                                                          |\n| `core`                                        | Core types: `Inputs`, `Sources`, build constants, Bloop integration, JVM/JS/Native tooling.                      |\n| `options`                                     | `BuildOptions`, `SharedOptions`, and all option types.                                                           |\n| `directives`                                  | Using directive handlers — the bridge between `//> using` directives and `BuildOptions`.                         |\n| `build-module` (aliased from `build` in mill) | The main build pipeline: preprocessing, compilation, post-processing. Most business logic lives here.            |\n| `cli`                                         | Command definitions, argument parsing (CaseApp), the `ScalaCli` entry point. Packaged as the native image.       |\n| `runner`                                      | Lightweight app that runs a main class and pretty-prints exceptions. Fetched at runtime.                         |\n| `test-runner`                                 | Discovers and runs test frameworks/suites. Fetched at runtime.                                                   |\n| `tasty-lib`                                   | Edits file names in `.tasty` files for source mapping.                                                           |\n| `scala-cli-bsp`                               | BSP protocol types.                                                                                              |\n| `integration`                                 | Integration tests (see dedicated section below).                                                                 |\n| `docs-tests`                                  | Tests that validate documentation (`Sclicheck`).                                                                 |\n| `generate-reference-doc`                      | Generates reference documentation from CLI option/directive metadata.                                            |\n\n## Specification levels\n\nEvery command, CLI option, and using directive has a `SpecificationLevel`. This is central to how features are exposed.\n\n| Level            | In the Scala Runner spec? | Available without `--power`? | Stability                       |\n|------------------|---------------------------|------------------------------|---------------------------------|\n| `MUST`           | Yes                       | Yes                          | Stable                          |\n| `SHOULD`         | Yes                       | Yes                          | Stable                          |\n| `IMPLEMENTATION` | No                        | Yes                          | Stable                          |\n| `RESTRICTED`     | No                        | No (requires `--power`)      | Stable                          |\n| `EXPERIMENTAL`   | No                        | No (requires `--power`)      | Unstable — may change/disappear |\n\n**New features contributed by agents should generally be marked `EXPERIMENTAL`** unless the maintainers explicitly\nrequest otherwise. This applies to new sub-commands, options, and directives alike.\n\nThe specification level is set via:\n\n- **Directives**: `@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)` annotation on the directive case class.\n- **CLI options**: `@Tag(tags.experimental)` annotation on option fields.\n- **Commands**: Override `scalaSpecificationLevel` in the command class.\n\n## Using directives\n\nUsing directives are in-source configuration comments:\n\n```scala\n//> using scala 3\n//> using dep com.lihaoyi::os-lib:0.11.4\n//> using test.dep org.scalameta::munit::1.1.1\n```\n\nDirectives are parsed by `using_directives`, then `ExtractedDirectives` → `DirectivesPreprocessor` → `BuildOptions`/\n`BuildRequirements`. **CLI options override directive values.** To add a new directive,\nsee [agentskills/adding-directives/](agentskills/adding-directives/SKILL.md).\n\n## Testing\n\n> **Every contribution that changes logic must include automated tests.** A PR without tests for\n> new or changed behavior will not be accepted. If testing is truly infeasible, explain why in the\n> PR description — but this should be exceptional.\n\n> **Unit tests are always preferred over integration tests.** Unit tests are faster, more reliable,\n> easier to debug, and cheaper to run on CI. Only add integration tests when the behavior cannot be\n> adequately verified at the unit level (e.g. end-to-end CLI invocation, launcher-specific behavior,\n> cross-process interactions).\n\n> **Always re-run and verify tests locally before submitting.** After any logic change, run the\n> relevant test suites on your machine and confirm they pass. Do not rely on CI to catch failures —\n> CI resources are shared, and broken PRs waste maintainer time.\n\n**Unit tests**: munit, in each module’s `test` submodule. Run commands above; add tests in `modules/build/.../tests/` or\n`modules/cli/src/test/scala/`. Prefer unit over integration.\n\n**Integration tests**: `modules/integration/`; they run the CLI as a subprocess.\nSee [agentskills/integration-tests/](agentskills/integration-tests/SKILL.md) for structure and how to add tests.\n\n## Pre-PR checklist\n\n1. Code compiles: `./mill -i __.compile`\n2. Tests added and passing locally (unit tests first, integration if needed)\n3. Code formatted: `scala-cli fmt .`\n4. Imports ordered: `./mill -i __.fix`\n5. Reference docs regenerated (if options/directives changed): `./mill -i 'generate-reference-doc[]'.run`\n6. PR template filled, LLM usage stated\n\n## Code style\n\nCode style is enforced.\n\n**Scala 3**: Prefer `if … then … else`, `for … do`/`yield`, `enum`, `extension`, `given`/`using`, braceless blocks,\ntop-level defs. Use union/intersection types when they simplify signatures. Always favor Scala 3 idiomatic syntax.\n\n**Functional**: Prefer `val`, immutable collections, `case class`.copy(). Prefer expressions over statements; prefer\n`map`/`flatMap`/`fold`/`for`-comprehensions over loops. Use `@tailrec` for tail recursion. Avoid `null`; use `Option`/\n`Either`/`EitherCps` (build-macros). Keep functions small; extract helpers.\n\n**No duplication**: Extract repeated logic into shared traits or utils (`*Options` traits, companion helpers,\n`CommandHelpers`, `TestUtil`). Check for existing abstractions before copying.\n\n**Logging**: Use the project `Logger` only — never `System.err` or `System.out`. Logger respects verbosity (`-v`, `-q`).\nUse `logger.message(msg)` (default), `logger.log(msg)` (verbose), `logger.debug(msg)` (debug), `logger.error(msg)` (\nalways). In commands: `options.shared.logging.logger`; in build code it is passed in; in tests use `TestLogger`.\n\n**Mutability**: OK in hot paths or when a Java API requires it; keep scope minimal.\n\n## Further reference\n\n[DEV.md](DEV.md), [CONTRIBUTING.md](CONTRIBUTING.md), [INTERNALS.md](INTERNALS.md).\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "Scala CLI uses the [Scala Code of Conduct](https://scala-lang.org/conduct/) for all communication and discussion. This includes both GitHub, Discord and other more direct lines of communication such as email.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Thanks for contributing to Scala CLI!\n\nThis doc is meant as a guide on how best to contribute to Scala CLI.\n\n## Creating issues\n\nWhenever you happen upon something that needs improvement, be sure to come back to us and create an issue. Please make\nuse of the available templates and answer all the included questions, so that the maintenance team can understand your\nproblem easier.\n\n## Pull requests\n\n### Fork-Pull\n\nWe accept external pull requests according to\nthe [standard GitHub fork-pull flow](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork).\nCreate a fork of this repository, commit your changes there and create a pull request. We try to review those as often\nas possible.\n\n### Main & stable branches\n\n#### `main`\n\nAll code changes should branch from [main](https://github.com/VirtusLab/scala-cli/tree/main) (which is also the default\nbranch).\n\n#### `stable` and documentation changes\n\nHowever, documentation changes which don't depend on any not-yet-released code changes should branch\nfrom [stable](https://github.com/VirtusLab/scala-cli/tree/stable). This allows the CI to immediately update the website.\nA subsequent PR from `stable` back to `main` is created automatically.\n\n### Rules for a well-formed PR\n\nWhenever reasonable, we try to follow the following set of rules when merging code to the repository. Following those\nwill save you from getting a load of comments and speed up the code review.\n\n- If you are using LLM-based tools to assist you in your contribution, state that clearly in the PR description \n  and refer to our [LLM usage policy](LLM_POLICY.md) for rules and guidelines regarding usage of LLM-based tools \n  in contributions.\n- If the PR is meant to be merged as a single commit (`squash & merge`), please make sure that you modify only one\n  thing.\n    - This means such a PR shouldn't include code clean-up, a secondary feature or bug fix, just the single thing\n      mentioned in the title.\n    - If it's not obvious, please mention it in the PR description or a comment.\n- Otherwise, make sure you keep all the commits nice and tidy:\n    - all side-refactors, nitpick changes, formatting fixes and other side-changes should be extracted to separate\n      commits with the `NIT` prefix in the commit message;\n      - similarly, code review comments regarding such changes should be marked with the same prefix;\n    - ensure everything compiles at every commit (`./mill -i __.compile`);\n    - ensure everything is well formatted at every commit (`scala-cli fmt .` or `scalafmt`);\n    - ensure imports are well-ordered at every commit (`./mill -i __.fix`);\n    - ensure reference docs are up-to date at every commit (`./mill -i 'generate-reference-doc[]'.run`);\n    - ensure all tests pass at every commit (refer to the [dev docs](DEV.md) on how to run tests);\n        - nobody expects you to run all the unit and integration tests for all platforms locally, that'd take too long;\n        - just make sure the test suites relevant to your changes pass on your local machine.\n\nOther notes:\n\n- fill the pull request template;\n- make sure to add tests wherever possible;\n    - favor unit tests over integration tests where applicable;\n- try to add scaladocs for key classes and functions;\n- try to add comments where your code isn't self-explanatory;\n- if you're changing the app behaviour or adding a new feature, make sure to add docs on the website (or note in the PR\n  that you'll do it separately)."
  },
  {
    "path": "DEV.md",
    "content": "## Developer docs\n\n### Requirements\n\nBuilding Scala CLI requires JVM 17 to work properly.\n\nIn theory, our build is able to download and install for its own needs JVM 17 on some OSes however it may not work in\nIntellij / Metals out of the box.\n\nThe Scala CLI sources ship with Mill launchers, so that Mill itself doesn't need to be installed on your system.\n\n### Common commands\n\n#### Running the CLI from sources\n\nRun the `scala` target with Mill:\n```bash\n./mill -i scala …arguments…\n```\n\nThis is the equivalent of running the `cli` task with the default Scala version:\n```bash\n./mill -i 'cli[]'.run …arguments…\n```\n\n#### Debugging the CLI from sources\n\n```bash\n./mill -i debug debug-port …arguments…\n```\n\nwhich is short for:\n```bash\n./mill -i 'cli[]'.debug debug-port …arguments…\n```\nE.g:\n```bash\n./mill -i 'cli[]'.debug 5050 ~/Main.scala -S 3.3.0\n```\n\n#### Run unit tests\n\nThis command runs the unit tests from the `build-module` module.\n\n```bash\n./mill 'build-module.test'\n```\n\nIf you want to run unit tests for another module, set `module_name` to the name of the module from which you want to run\nthe unit tests:\n\n```bash\n./mill 'module_name.test'\n```\n\nTo can filter unit test suites:\n\n```bash\n./mill 'build-module[].test' 'scala.build.tests.BuildTestsScalac.*'\n./mill 'build-module[].test' 'scala.build.tests.BuildTestsScalac.simple'\n```\n\n#### Run integration tests with the JVM launcher\n\n```bash\n./mill integration.test.jvm\n```\n\nFilter test suites with\n\n```bash\n./mill integration.test.jvm 'scala.cli.integration.RunTestsDefault.*'\n./mill integration.test.jvm 'scala.cli.integration.RunTestsDefault.Multiple scripts'\n```\n\nYou can pass the `--debug` option to debug Scala CLI when running integration tests. Note that this allows to debug the\nScala CLI launcher (the app) and not the integration test code itself. The debugger is being run in the `attach` mode.\n\n```bash\n./mill integration.test.jvm 'scala.cli.integration.RunTestsDefault.*' --debug\n```\n\nThe debug option uses 5005 port by default. It is possible to change it as follows:\n\n```bash\n./mill integration.test.jvm 'scala.cli.integration.RunTestsDefault.*' --debug:5006\n```\n\n#### Run integration tests with the native launcher\n\n(generating the launcher can take several minutes)\n\n```bash\n./mill integration.test.native\n./mill integration.test.native 'scala.cli.integration.RunTestsDefault.*'\n```\n\n#### Generate JUnit test reports\n\nAs running tests with mill generates output in a non-standard JSON format, we have a script for converting it to the \nmore well known JUnit XML test report format which we can then process and view on the CI.\nIn case you want to generate a test report locally, you can run the following command:\n\n```bash\n.github/scripts/generate-junit-reports.sc <test suite title> <test report title> <output-path out/\n```\n\nThe test should fail when no test reports were found or if no tests were actually run.\n\n#### Generate native packages\n\nBuild native packagers:\n\n* `deb` for linux\n* `msi` for windows\n* `dmg` and `pkg` for macOS\n\n(generating native packager for specified format)\n\n```bash\n./mill -i scala package ..arguments... --deb --output 'path.deb'\n./mill -i scala package ..arguments... --dmg --output 'path.dmg'\n./mill -i scala package ..arguments... --pkg --output 'path.pkg'\n```\n\n#### IDE Import\n\nThe Scala CLI repository should work when imported automatically from Mill to IDEA IntelliJ and Metals.\nPlease raise an issue if you run into any problems.\n\nWhen working with IntelliJ make sure that the project's Java is set correctly.\nTo confirm, check under `File -> Project Structure` that:\n\n- in `Project Settings/Project` `SDK` and `Language level` is set to **17**\n- in `Project Settings/Modules` all the modules have `Language level` set to **17**\n- in `Platform Settings/SDKs` only **Java 17** is visible\n\nOtherwise, some IDE features may not work correctly, i.e. the debugger might crash upon connection.\n\n#### Generate a native launcher\n\n```bash\n./mill -i show 'cli[]'.nativeImage\n```\n\nThis prints the path to the generated native image.\nThe file named `scala` at the root of the project should also\nbe a link to it. (Note that the link is committed and is always there,\nwhether the files it points at exists or not.)\n\n#### Generate a JVM launcher\n\n```bash\n./mill -i show 'cli[]'.launcher\n```\n\nThis prints the path to the generated launcher. This launcher is a JAR,\nthat directly re-uses the class directories of the modules of the project\n(so that cleaning up those classes will break the launcher). If this is a\nproblem (if you wish to run the launcher on another machine or from a\nDocker image for example), use a native launcher (see above) or a standalone\nJVM one (see below).\n\n#### Generate a standalone JVM launcher\n\n```bash\n./mill -i show 'cli[]'.standaloneLauncher\n```\n\nThis prints the path to the generated launcher. This launcher is a JAR,\nthat embeds JARs of the scala-cli modules, and downloads their dependencies\nfrom Maven Central upon first launch (using the coursier cache, just like\na coursier bootstrap).\n\n### Helper projects\n\nA number of features of Scala CLI are managed from external projects, living under\nthe [`scala-cli`](https://github.com/scala-cli) and [`VirtusLab`](https://github.com/VirtusLab) organizations on GitHub. These\nprojects can be used by Scala CLI as libraries pulled before it's compiled, but also\nas binaries. In the latter case, Scala CLI downloads on-the-fly binaries from these\nrepositories' GitHub release assets, and runs them as external processes.\n\nHere's some of the more important external projects used by Scala CLI:\n\n- [scala-js-cli-native-image](https://github.com/VirtusLab/scala-js-cli): provides a binary running the\n  Scala.js linker\n- [scala-cli-signing](https://github.com/VirtusLab/scala-cli-signing): provides both libraries and binaries to handle\n  PGP concerns in Scala CLI\n- [scala-packager](https://github.com/VirtusLab/scala-packager): provides a library to package applications \n  in native formats\n- [libsodiumjni](https://github.com/VirtusLab/libsodiumjni): provides minimal JNI bindings for\n  [libsodium](https://github.com/jedisct1/libsodium), that is used by Scala CLI to encrypt secrets\n  uploaded as GitHub repository secrets in the `publish setup` sub-command\n- [scala-cli-setup](https://github.com/VirtusLab/scala-cli-setup): a GitHub Action to install Scala CLI.\n- [bloop-core](https://github.com/scala-cli/bloop-core): a fork of [bloop](https://github.com/scalacenter/bloop)\n  stripped up of its benchmark infrastructure and build integrations.\n- [no-crc32-zip-input-stream](https://github.com/VirtusLab/no-crc32-zip-input-stream): A copy of `ZipInputStream` \n  from OpenJDK, with CRC32 calculations disabled.\n- [lightweight-spark-distrib](https://github.com/VirtusLab/lightweight-spark-distrib): a small application allowing\n  to make Spark distributions more lightweight.\n- [java-class-name](https://github.com/VirtusLab/java-class-name): a small library to extract class names\n  from Java sources.\n\nLegacy projects:\n- [scalafmt-native-image](https://github.com/VirtusLab/scalafmt-native-image): GraalVM native-image launchers\n  for `scalafmt` (used for `scalafmt` versions < 3.9.1, no longer maintained)\n\nThe use of external binaries allows to make the Scala CLI binary slimmer and faster\nto generate, but also allow to lower memory requirements to generate it (allowing to\ngenerate these binaries on the GitHub-provided GitHub actions hosts).\n\n### Website\n\nThe Scala CLI website is built with [Docusaurus](https://v1.docusaurus.io/en/) and\nuses [Infima](https://infima.dev/docs/layout/spacing) for styling.\n\nEnsure you are using Node >= 16.14.2.\n\n#### Generate the website once\n\n```bash\ncd website\nyarn\nyarn build\nnpm run serve\n```\n\n#### Generate the website continuously\n\n```bash\ncd website\nyarn\nyarn run start\n```\n\n### Verifying the documentation\n\nWe have a built-in tool to validate `.md` files called [Sclicheck](/sclicheck/Readme.md).\nAll `Sclicheck` tests can be run with `Mill` + `munit`: (and this is what we run on the CI, too)\n\n```bash\n./mill -i 'docs-tests[]'.test\n```\n\nThe former also includes testing gifs and `Sclicheck` itself.\nTo just check the documents, run:\n\n```bash\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.*'\n```\n\nYou can also check all root docs, commands, reference docs, guides or cookbooks:\n\n```bash\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.root*'\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.guide*'\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.command*'\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.cookbook*'\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.reference*'\n```\n\nSimilarly, you can check single files:\n\n```bash\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.<category> <doc-name>'\n```\n\nFor example, to run the check on `compile.md`\n\n```bash\n./mill -i 'docs-tests[]'.test 'sclicheck.DocTests.command compile'\n```\n\n## Scala CLI logos\n\nPackage with various logos for scala-cli can be found\non [google drive](https://drive.google.com/drive/u/1/folders/1M6JeQXmO4DTBeRBKAFJ5HH2p_hbfQnqS)\n\n## Launcher script\n\nThere is a script `scala-cli-src` in the repository root that is intended to work exactly like released scala-cli, but\nusing a binary compiled the worktree.\nJust add it to your PATH to get the already-released-scala-cli experience.\n\n## CI change detection\n\nOn pull requests, the CI workflow detects which files changed and skips jobs that are not relevant.\nPushes to `main`, `v*` tags, and manual dispatches always run everything.\n\n### Override keywords\n\nYou can force specific job groups to run regardless of which files changed by including\nthese keywords anywhere in the PR body (description):\n\n| Keyword | Effect |\n|---------|--------|\n| `[test_all]` | Run **all** CI jobs, no skipping |\n| `[test_native]` | Force native launcher builds and native integration tests |\n| `[test_integration]` | Force JVM integration tests |\n| `[test_docs]` | Force documentation tests |\n| `[test_format]` | Force format and scalafix checks |\n\nFor example, if your PR only touches documentation, but you want to verify native\nlaunchers still build, add `[test_native]` to the PR description.\n\n## Releases\n\nInstructions on how to\nrelease - [Release Procedure](https://github.com/VirtusLab/scala-cli/blob/main/.github/release/release-procedure.md)\n\n## Debugging BSP server\n\nThe easiest way to debug BSP sever is using `scala-cli-src` script with `--bsp-debug-port 5050` flag (the port should be\nunique to the workspace where BSP will be debugged). In such case BSP will be launched using local source and will run\non JVM. It will also expects a debugger running in the listen mode using provided port (so the initialization of the\nconnection can be debugged). In such case we recommend to have option to auto rerun debugging session off (so there is\nalways a debugger instance ready to be used).\n\n## GraalVM reflection configuration\n\nAs Scala CLI is using GraalVM native image, it requires a configuration file for reflection.\nThe configuration for the `cli` module is located\nin [the reflect-config.json](modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json)\nfile.\n\nWhen adding new functionalities or updating dependencies, it might turn out the reflection configuration for some class\nmay be missing. The relevant error message when running `integration.test.native` may be misleading,\nusually with a `ClassNotFoundException` or even with a functionality seemingly being skipped.\nThis is because logic referring to classes with missing reflection configuration may be skipped for the used native\nimage.\n\nTo generate the relevant configuration automatically, you can run:\n\n```bash\n./mill -i 'cli[]'.runWithAssistedConfig <scala-cli-sub-command> <args> <options>\n```\n\nJust make sure to run it exactly the same as the native image would have been run, as the configuration is generated for\na particular invocation path. The run has to succeed as well, as the configuration will only be fully generated after an\nexit code 0.\n\n```text\nConfig generated in out/cli/<scalaVersion>/runWithAssistedConfig.dest/config\n```\n\nAs a result, you should get the path to the generated configuration file. It might contain some unnecessary entries, so\nmake sure to only copy what you truly need.\nAs the formatting of the `reflect-config.json` is verified on the CI, make sure to run the following command to adjust\nit accordingly before committing:\n\n```bash\n./mill -i __.formatNativeImageConf\n```\n\nFor more info about reflection configuration in GraalVM,\ncheck [the relevant GraalVM Reflection docs](https://www.graalvm.org/latest/reference-manual/native-image/dynamic-features/Reflection/).\n\n## Overriding Scala versions in Scala CLI builds\n\nIt's possible to override the internal Scala version used to build Scala CLI,\nas well as the default version used by the CLI itself with Java props.\n- `scala.version.internal` - overrides the internal Scala version used to build Scala CLI\n- `scala.version.user` - overrides the default Scala version used by the CLI itself\n\nNOTE: remember to run `./mill clean` to make sure the Scala versions aren't being cached anywhere.\n\n```bash\n./mill -i clean\n./mill -i --define scala.version.internal=3.4.0-RC1-bin-20231012-242ba21-NIGHTLY --define scala.version.user=3.4.0-RC1-bin-20231012-242ba21-NIGHTLY scala version --offline\n# Scala CLI version: 1.x.x-SNAPSHOT\n# Scala version (default): 3.4.0-RC1-bin-20231012-242ba21-NIGHTLY\n```\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM eclipse-temurin:17 as build\nRUN apt update && apt install build-essential libz-dev clang procps git -y\nWORKDIR /workdir\nCOPY . .\nRUN ./mill -i copyTo --task 'cli[].base-image.nativeImage' --dest \"./docker-out/scala-cli\" 1>&2\n\nFROM debian:stable-slim\nRUN apt update && apt install build-essential libz-dev clang procps -y\nCOPY --from=build /workdir/docker-out/scala-cli /usr/bin/scala-cli\nRUN \\\n echo \"println(1)\" | scala-cli -S 3 - -v -v -v && \\\n echo \"println(1)\" | scala-cli -S 2.13 - -v -v -v && \\\n echo \"println(1)\" | scala-cli -S 2.12 - -v -v -v\nRUN \\\n echo \"println(1)\" | scala-cli --power package --native _.sc --force && \\\n echo \"println(1)\" | scala-cli --power package --native-image _.sc --force\n\nENTRYPOINT [\"scala-cli\"]\n"
  },
  {
    "path": "INTERNALS.md",
    "content": "# Internals overview\n\n## Modules\n\nModules live under `modules/`. Each sub-directory there has a\ncorresponding mill module definition in `build.sc` (but for `integration`).\n\nMost of the code currently lives in the `build` module.\n\nThe `cli` module depends on `build`, gets\npackaged as a native-image executable, and distributed as `scala-cli` binary.\n\nThe other modules are either:\n- integration tests\n- utility modules, that `build` either:\n  - depends on\n  - fetches at run-time.\n\n## Utility modules\n\nThese are:\n- `runner`: simple app that starts a main class, catches any exception it throws and pretty-prints it.\n- `test-runner`: finds test frameworks, test suites, and runs them\n- `tasty-lib`: edits file names in `.tasty` files\n\n## Tests\n\nThe tests live either in:\n- `build`: unit tests\n- `integration`: integration tests\n\nRun unit tests with\n```bash\n./mill 'build[_].test'\n```\n\nRun integration tests with a JVM-based `scala-cli` with\n```bash\n./mill integration.test.jvm\n```\n\nRun integration tests with a native-image-based `scala-cli` with\n```bash\n./mill integration.test.native\n```\n\n## General workflow in most `scala-cli` commands\n\nWe roughly go from user inputs to byte code through 3 classes:\n- `Inputs`: ADT for input files / directories.\n- `Sources`: processed sources, ready to be passed to scalac\n- `Build`: compilation result: success or failure.\n\nMost commands\n- take the arguments passed on the command-line: we have an `Array[String]`\n- check whether each of them is a `.scala` file, an `.sc` file, a directory, …: we get an `Inputs` instance\n- reads the directories, the `.scala` / `.sc` files: we get a `Sources` instance\n- compile those sources: we get a `Build` instance\n- do something with the build output (run it, run tests, package it, …)\n\nIn watch mode, we loop over the last 3 steps (`Inputs` is computed only once, the rest is re-computed upon file change).\n\n## Source pre-processing\n\nSome input files cannot be passed as is to scalac, if they are scripts (`.sc` files), which contain top-level statements\n\nScripts get wrapped. If the script `a/b/foo.sc` contains\n```scala\nval n = 2\n```\nwe compile it as\n```scala\npackage a.b\nobject foo {\nval n = 2\ndef main(args: Array[String]): Unit = ()\n}\n```\nBasically,\n- its directory dictates its package\n- we put its sources as is in an object\n- we add a `main` method\n\n## Build outputs post-processing\n\nThe source generation changes:\n- file names, which now correspond to the directory where we write generated sources\n- positions, when we wrap code (for `.sc` files)\n\nAs a consequence, some build outputs contains wrong paths or positions:\n- diagnostics (warning and error messages) contain file paths and positions, used in reporting\n- byte code contains file names and line numbers, that are used in stack traces\n- semantic DBs contain relative file paths and positions, used by IDEs\n- TASTy files contain relative file paths, used in pretty stack traces\n\nWe post-process those build outputs, to adjust positions and file paths of the generated sources:\nvarious \"mappings\" are computed out of the generated sources list, and are used to adjust:\n- diagnostics: done in memory, right before printing diagnostics\n- byte code: done using the ASM library\n- semantic DBs: we parse the semantic DBs, edit them in memory, and write them back on disk\n- TASTy files: we partly parse them in memory, edit names that contain source file paths, and write them back on disk\n\n## Publishing scalajs-cli\n\n### Maven Publishing\n\n- Version Synchronization: `scalajs-cli` will be published with the same version as Scala.js version, for\n  example `1.13.0`.\n- Updates & Fixes: For any subsequent fixes or patches in `scalajs-cli`, we will append a numeric value to the end,\n  like `1.13.0.1`.\n- GitHub Uploads\n    - Native Launchers: With the patch release of `scalajs-cli`, native launchers are automatically uploaded to both\n      versions, for\n      example `1.13.0.1` and `1.13.0` tags on GitHub.\n    - For instance: For release `1.13.0.2`, the launchers are uploaded to tags `1.13.0.2` and `1.13.0`.\n- ScalaCli dependency to `scalajs-cli`:\n    - For Coursier to retrieve the most recent scalajs-cli for a specific Scala.js version, the version is set\n      as `org.virtuslab:scalajscli_2.13:{Scala.js version}+`. For example `org.virtuslab:scalajscli_2.13:1.13.0+`.\n    - Native Version Download:\n        - The native version is downloaded from the Scala.js version tag. If there are updates or fixes to the\n          native `scalajs-cli` launchers, the updated launchers are uploaded to the `1.13.0` tag during the `1.13.0.1`\n          publishing.\n\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "LLM_POLICY.md",
    "content": "# Policy regarding LLM-generated code in contributions to Scala CLI\n\nScala CLI accepts contributions containing code produced with AI assistance. This means that using LLM-based\ntooling aiding software development (like Cursor, Claude Code, Copilot or whatever else) is allowed.\n\nAll such contributions are regulated by the policy defined in the Scala 3 compiler repository, which can be found at:\nhttps://github.com/scala/scala3/blob/main/LLM_POLICY.md"
  },
  {
    "path": "README.md",
    "content": "# scala-cli\n\n[![Build status](https://github.com/VirtusLab/scala-cli/workflows/CI/badge.svg)](https://github.com/VirtusLab/scala-ci/actions?query=workflow%3ACI)\n[![Maven Central](https://img.shields.io/maven-central/v/org.virtuslab.scala-cli/cli_3.svg)](https://maven-badges.herokuapp.com/maven-central/org.virtuslab.scala-cli/cli_3)\n[![Discord](https://img.shields.io/discord/632277896739946517.svg?label=&logo=discord&logoColor=ffffff&color=404244&labelColor=6A7EC2)](https://discord.gg/KzQdYkZZza)\n\nScala CLI is a command-line tool to interact with the Scala language. It lets you compile, run, test, and package your\nScala code. (and more!) It shares some similarities with build tools, but it doesn't aim at supporting multi-module\nprojects, nor to be extended via a task system.\n\nAs of Scala 3.5.0, Scala CLI has become the official `scala` runner of the language (for more information \nrefer to [SIP-46](https://github.com/scala/improvement-proposals/pull/46)). For more details on using Scala CLI\nvia the `scala` command, refer to [this doc](https://scala-cli.virtuslab.org/docs/reference/scala-command/).\n\n## Docs\n- user-facing documentation: [scala-cli.virtuslab.org](https://scala-cli.virtuslab.org/)\n- [contributing guide](CONTRIBUTING.md)\n- [developer docs](DEV.md)\n- [app internals](INTERNALS.md)\n- [docs website readme](website/README.md)\n- [docs gifs readme](gifs/README.md)\n- [sclicheck readme](modules/docs-tests/README.md)\n- [gcbenchmark readme](gcbenchmark/README.md)\n- [release procedure](.github/release/release-procedure.md)\n- [code of conduct](CODE_OF_CONDUCT.md)\n"
  },
  {
    "path": "agentskills/README.md",
    "content": "# Agent skills (Scala CLI)\n\nThis directory holds **agent skills** — task-specific guidance loaded on demand by AI coding agents. The layout is tool-agnostic; Cursor, Claude Code, Codex, and other tools that support a standard skill directory can use this (e.g. by configuring or symlinking to `.agents/skills/` if required).\n\nEach subdirectory contains a `SKILL.md` with frontmatter and instructions. See [agentskills/agentskills](https://github.com/agentskills/agentskills) for the open standard.\n"
  },
  {
    "path": "agentskills/adding-directives/SKILL.md",
    "content": "---\nname: scala-cli-adding-directives\ndescription: Add or change using directives in Scala CLI. Use when adding a new //> using directive, registering a directive handler, or editing directive preprocessing.\n---\n\n# Adding a new directive (Scala CLI)\n\n1. **Create a case class** in `modules/directives/src/main/scala/scala/build/preprocessing/directives/` extending one of:\n   - `HasBuildOptions` — produces `BuildOptions` directly\n   - `HasBuildOptionsWithRequirements` — produces `BuildOptions` with scoped requirements (e.g. `test.dep`)\n   - `HasBuildRequirements` — produces `BuildRequirements` (for `//> require`)\n\n2. **Annotate**: `@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)`, `@DirectiveDescription(\"…\")`, `@DirectiveUsage(\"…\")`, `@DirectiveExamples(\"…\")`, `@DirectiveName(\"key\")` on fields.\n\n3. **Companion**: `val handler: DirectiveHandler[YourDirective] = DirectiveHandler.derive`\n\n4. **Register** in `modules/build/.../DirectivesPreprocessingUtils.scala` in the right list: `usingDirectiveHandlers`, `usingDirectiveWithReqsHandlers`, or `requireDirectiveHandlers`.\n\n5. **Regenerate reference docs**: `./mill -i 'generate-reference-doc[]'.run`\n\nCLI options always override directive values when both set the same thing.\n"
  },
  {
    "path": "agentskills/integration-tests/SKILL.md",
    "content": "---\nname: scala-cli-integration-tests\ndescription: Add or run Scala CLI integration tests. Use when adding integration tests, debugging RunTests/CompileTests/etc., or working in modules/integration.\n---\n\n# Integration tests (Scala CLI)\n\n**Location**: `modules/integration/`. Tests invoke the CLI as an external process.\n\n**Run**: `./mill -i integration.test.jvm` (all). Filter: `./mill -i integration.test.jvm 'scala.cli.integration.RunTestsDefault.*'` or by test name. Native: `./mill -i integration.test.native`.\n\n**Structure**: `*TestDefinitions.scala` (abstract, holds test logic) → `*TestsDefault`, `*Tests213`, etc. (concrete, Scala version trait). Traits: `TestDefault`, `Test212`, `Test213`, `Test3Lts`, `Test3NextRc`.\n\n**Adding a test**:\n1. Open the right `*TestDefinitions` (e.g. `RunTestDefinitions` for `run`).\n2. Add `test(\"description\") { … }` using `TestInputs(os.rel / \"Main.scala\" -> \"…\").fromRoot { root => … }` and `os.proc(TestUtil.cli, \"run\", …).call(cwd = root)`.\n3. Assert on stdout/stderr.\n\n**Helpers**: `TestInputs(...).fromRoot`, `TestUtil.cli`. Test groups (CI): `SCALA_CLI_IT_GROUP=1..5`; see `modules/integration/` for group mapping.\n"
  },
  {
    "path": "build.mill",
    "content": "//| mill-jvm-version: system|17\n//| mvnDeps:\n//| - io.github.alexarchambault.mill::mill-native-image::0.2.4\n//| - io.github.alexarchambault.mill::mill-native-image-upload:0.2.4\n//| - com.goyeau::mill-scalafix::0.6.0\n//| - com.lumidion::sonatype-central-client-requests:0.6.0\n//| - io.get-coursier:coursier-launcher_2.13:2.1.25-M24\n//| - org.eclipse.jgit:org.eclipse.jgit:7.5.0.202512021534-r\npackage build\n\nimport build.ci.publishVersion\nimport build.project.deps\nimport deps.{Cli, Deps, Docker, Java, Scala, TestDeps}\nimport build.project.publish\nimport publish.{ScalaCliPublishModule, finalPublishVersion, ghName, ghOrg, organization}\nimport build.project.settings\nimport settings.{\n  CliLaunchers,\n  FormatNativeImageConf,\n  HasTests,\n  LocalRepo,\n  LocatedInModules,\n  PublishLocalNoFluff,\n  ScalaCliCrossSbtModule,\n  ScalaCliScalafixModule,\n  isCI,\n  jvmPropertiesFileName,\n  localRepoResourcePath,\n  platformExecutableJarExtension,\n  projectFileName,\n  workspaceDirName\n}\nimport deps.customRepositories\nimport deps.alpineVersion\nimport build.project.website\nimport coursier.Repository\n\nimport java.io.File\nimport java.net.URL\nimport java.nio.charset.Charset\nimport java.util.Locale\nimport io.github.alexarchambault.millnativeimage.upload.Upload\nimport mill.*\nimport mill.api.{BuildCtx, BuildInfo, ModuleCtx, Task}\nimport mill.scalalib.*\nimport scalalib.{publish as _, *}\nimport mill.javalib.testrunner.TestResult\nimport mill.util.{Tasks, VcsVersion}\n\nimport _root_.scala.util.{Properties, Using}\nimport _root_.scala.util.{Properties, Using}\n\nobject cli extends Cross[Cli](Scala.scala3MainVersions) with CrossScalaDefaultToInternal\n\ntrait CrossScalaDefault { self: Cross[?] =>\n  def crossScalaDefaultVersion: String\n  override def defaultCrossSegments: Seq[String] = Seq(crossScalaDefaultVersion)\n}\n\ntrait CrossScalaDefaultToInternal extends CrossScalaDefault { self: Cross[?] =>\n  override def crossScalaDefaultVersion: String = Scala.defaultInternal\n}\n\ntrait CrossScalaDefaultToRunner extends CrossScalaDefault { self: Cross[?] =>\n  override def crossScalaDefaultVersion: String = Scala.runnerScala3\n}\n\n// Publish a bootstrapped, executable jar for a restricted environments\nobject cliBootstrapped extends ScalaCliPublishModule {\n  override def unmanagedClasspath: T[Seq[PathRef]] =\n    Task(cli(Scala.defaultInternal).nativeImageClassPath().filter(ref => os.exists(ref.path)))\n  override def jar: T[PathRef] = assembly()\n\n  import mill.scalalib.Assembly\n\n  override def prependShellScript: T[String] = Task(\"\")\n\n  override def mainClass: T[Option[String]] = Some(\"scala.cli.ScalaCli\")\n\n  override def assemblyRules: Seq[Assembly.Rule] = Seq(\n    Assembly.Rule.ExcludePattern(\".*\\\\.tasty\"),\n    Assembly.Rule.ExcludePattern(\".*\\\\.semanticdb\")\n  ) ++ super.assemblyRules\n\n  override def resources: T[Seq[PathRef]] = super.resources() ++ Seq(propertiesFilesResources())\n\n  def propertiesFilesResources: T[PathRef] = Task(persistent = true) {\n    val dir = Task.dest / \"resources\"\n\n    val dest    = dir / \"java-properties\" / \"scala-cli-properties\"\n    val content = \"scala-cli.kind=jvm.bootstrapped\"\n    os.write.over(dest, content, createFolders = true)\n    PathRef(dir)\n  }\n}\n\nobject `specification-level` extends Cross[SpecificationLevel](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject `build-macros` extends Cross[BuildMacros](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject config extends Cross[Config](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject options extends Cross[Options](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject directives extends Cross[Directives](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject core extends Cross[Core](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject `build-module` extends Cross[Build](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\nobject runner extends Cross[Runner](Scala.runnerScalaVersions)\n    with CrossScalaDefaultToRunner\nobject `test-runner` extends Cross[TestRunner](Scala.runnerScalaVersions)\n    with CrossScalaDefaultToRunner\nobject `java-test-runner` extends JavaTestRunner\n    with LocatedInModules\nobject `tasty-lib` extends Cross[TastyLib](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\n\nobject `scala-cli-bsp` extends JavaModule\n    with ScalaCliPublishModule\n    with LocatedInModules {\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.bsp4j\n  )\n}\nobject integration extends CliIntegration {\n  object test extends IntegrationScalaTests {\n    override def testParallelism: T[Boolean]           = !isCI\n    override def testForkGrouping: T[Seq[Seq[String]]] =\n      if isCI then discoveredTestClasses().grouped(1).toSeq else super.testForkGrouping()\n    override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n      Deps.coursierArchiveCache,\n      Deps.jgit,\n      Deps.jsoup\n    )\n  }\n  object docker extends CliIntegrationDocker {\n    object test extends ScalaCliTests {\n      override def sources: T[Seq[PathRef]] = super.sources() ++ integration.sources()\n      def tmpDirBase: T[PathRef]            = Task(persistent = true) {\n        PathRef(Task.dest / \"working-dir\")\n      }\n      override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq(\n        \"SCALA_CLI_TMP\"                -> tmpDirBase().path.toString,\n        \"SCALA_CLI_IMAGE\"              -> \"scala-cli\",\n        \"SCALA_CLI_PRINT_STACK_TRACES\" -> \"1\"\n      )\n    }\n  }\n\n  object `docker-slim` extends CliIntegrationDocker {\n    object test extends ScalaCliTests {\n      override def sources: T[Seq[PathRef]] = integration.docker.test.sources()\n      def tmpDirBase: T[PathRef]            = Task(persistent = true) {\n        PathRef(Task.dest / \"working-dir\")\n      }\n      override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq(\n        \"SCALA_CLI_TMP\"                -> tmpDirBase().path.toString,\n        \"SCALA_CLI_IMAGE\"              -> \"scala-cli-slim\",\n        \"SCALA_CLI_PRINT_STACK_TRACES\" -> \"1\"\n      )\n    }\n  }\n}\n\nobject `docs-tests` extends Cross[DocsTests](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\n\ntrait DocsTests extends CrossSbtModule with ScalaCliScalafixModule with LocatedInModules\n    with HasTests { main =>\n  override def mvnDeps: T[Seq[Dep]] = Seq(\n    Deps.fansi,\n    Deps.osLib,\n    Deps.pprint\n  )\n  def tmpDirBase: T[PathRef] = Task(persistent = true) {\n    PathRef(Task.dest / \"working-dir\")\n  }\n  def extraEnv: T[Seq[(String, String)]] = Task {\n    Seq(\n      \"SCLICHECK_SCALA_CLI\" -> cli(crossScalaVersion).standaloneLauncher().path.toString,\n      \"SCALA_CLI_CONFIG\"    -> (tmpDirBase().path / \"config\" / \"config.json\").toString\n    )\n  }\n  override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ extraEnv()\n\n  def constantsFile: T[PathRef] = Task(persistent = true) {\n    val dir  = Task.dest / \"constants\"\n    val dest = dir / \"Constants.scala\"\n    val code =\n      s\"\"\"package sclicheck\n         |\n         |/** Build-time constants. Generated by mill. */\n         |object Constants {\n         |  def coursierOrg = \"${Deps.coursier.dep.module.organization.value}\"\n         |  def coursierCliModule = \"${Deps.coursierCli.dep.module.name.value}\"\n         |  def coursierCliVersion = \"${Deps.Versions.coursierCli}\"\n         |  def defaultScalaVersion = \"${Scala.defaultUser}\"\n         |  def scalaLegacyRunnerVersion = \"${Scala.scalaLegacyRunnerVersion}\"\n         |  def alpineVersion = \"$alpineVersion\"\n         |}\n         |\"\"\".stripMargin\n    if (!os.isFile(dest) || os.read(dest) != code)\n      os.write.over(dest, code, createFolders = true)\n    PathRef(dir)\n  }\n  override def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile())\n\n  object test extends ScalaCliTests with ScalaCliScalafixModule {\n    override def testParallelism: T[Boolean]           = !isCI\n    override def testForkGrouping: T[Seq[Seq[String]]] =\n      if isCI then discoveredTestClasses().grouped(1).toSeq else super.testForkGrouping()\n    override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ extraEnv() ++ Seq(\n      \"SCALA_CLI_EXAMPLES\"      -> (BuildCtx.workspaceRoot / \"examples\").toString,\n      \"SCALA_CLI_GIF_SCENARIOS\" -> (BuildCtx.workspaceRoot / \"gifs\" / \"scenarios\").toString,\n      \"SCALA_CLI_WEBSITE_IMG\"   -> (BuildCtx.workspaceRoot / \"website\" / \"static\" / \"img\").toString,\n      \"SCALA_CLI_GIF_RENDERER_DOCKER_DIR\" -> (BuildCtx.workspaceRoot / \"gifs\").toString,\n      \"SCALA_CLI_SVG_RENDERER_DOCKER_DIR\" ->\n        (BuildCtx.workspaceRoot / \"gifs\" / \"svg_render\").toString\n    )\n    private def customResources: T[Seq[PathRef]] = {\n      val customPaths: Seq[os.Path] = Seq(\n        BuildCtx.workspaceRoot / \"website\" / \"docs\" / \"commands\",\n        BuildCtx.workspaceRoot / \"website\" / \"docs\" / \"cookbooks\"\n      )\n      Task.Sources(customPaths*)\n    }\n    override def resources: T[Seq[PathRef]] =\n      // Adding markdown directories here, so that they're watched for changes in watch mode\n      super.resources() ++ customResources()\n  }\n}\n\nobject packager extends ScalaModule {\n  override def scalaVersion: T[String] = Scala.scala3Lts\n  override def mvnDeps: T[Seq[Dep]]    = Seq(\n    Deps.scalaPackagerCli\n  )\n  override def mainClass: T[Option[String]] = Some(\"packager.cli.PackagerCli\")\n}\n\nobject `generate-reference-doc` extends Cross[GenerateReferenceDoc](Scala.scala3MainVersions)\n    with CrossScalaDefaultToInternal\n\ntrait GenerateReferenceDoc extends CrossSbtModule\n    with LocatedInModules\n    with ScalaCliScalafixModule {\n  override def moduleDeps: Seq[JavaModule] = Seq(\n    cli(crossScalaVersion)\n  )\n  override def repositoriesTask: Task[Seq[Repository]] =\n    Task.Anon(super.repositoriesTask() ++ customRepositories)\n  override def mvnDeps: T[Seq[Dep]] = Seq(\n    Deps.argonautShapeless,\n    Deps.caseApp,\n    Deps.munit\n  )\n  override def mainClass: T[Option[String]] = Some(\"scala.cli.doc.GenerateReferenceDoc\")\n\n  override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq(\n    \"SCALA_CLI_POWER\" -> \"true\"\n  )\n}\n\nobject dummy extends LocatedInModules {\n  // dummy projects to get scala steward updates for Ammonite and scalafmt, whose\n  // versions are used in the fmt and repl commands, and ensure Ammonite is available\n  // for all Scala versions we support.\n  object amm extends Cross[Amm](Scala.listMaxAmmoniteScalaVersion)\n  trait Amm  extends Cross.Module[String] with CrossScalaModule {\n    override def crossScalaVersion: String = crossValue\n    override def mvnDeps: T[Seq[Dep]]      = {\n      val ammoniteDep =\n        if (crossValue == Scala.scala3Lts) Deps.ammoniteForScala3Lts\n        else Deps.ammonite\n      Seq(ammoniteDep)\n    }\n  }\n  object scalafmt extends ScalaModule {\n    override def scalaVersion: T[String] = Scala.defaultInternal\n    override def mvnDeps: T[Seq[Dep]]    = Seq(\n      Deps.scalafmtCli\n    )\n  }\n  object pythonInterface extends JavaModule {\n    override def mvnDeps: T[Seq[Dep]] = Seq(\n      Deps.pythonInterface\n    )\n  }\n  object scalaPy extends ScalaModule {\n    override def scalaVersion: T[String] = Scala.defaultInternal\n    override def mvnDeps: T[Seq[Dep]]    = Seq(\n      Deps.scalaPy\n    )\n  }\n  object scalafix extends ScalaModule {\n    override def scalaVersion: T[String] = Scala.defaultInternal\n    override def mvnDeps: T[Seq[Dep]]    = Seq(\n      Deps.scalafixInterfaces\n    )\n  }\n}\n\ntrait BuildMacros extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with ScalaCliScalafixModule\n    with HasTests\n    with LocatedInModules {\n  override def crossScalaVersion: String   = crossValue\n  override def compileMvnDeps: T[Seq[Dep]] = Task {\n    if (crossScalaVersion.startsWith(\"3\")) super.compileMvnDeps()\n    else super.compileMvnDeps() ++ Seq(Deps.scalaReflect(crossScalaVersion))\n  }\n\n  object test extends ScalaCliTests with ScalaCliScalafixModule {\n    override def scalacOptions: T[Seq[String]] = Task {\n      super.scalacOptions() ++ Seq(\"-deprecation\")\n    }\n\n    def testNegativeCompilation(): Command[Unit] = Task.Command(exclusive = true) {\n      val base          = BuildCtx.workspaceRoot / \"modules\" / \"build-macros\" / \"src\"\n      val negativeTests = Seq(\n        \"MismatchedLeft.scala\" -> Seq(\n          \"Found: +EE1\".r,\n          \"Found: +EE2\".r,\n          \"Required: +E2\".r\n        )\n      )\n\n      val cpsSource       = base / \"main\" / \"scala\" / \"scala\" / \"build\" / \"EitherCps.scala\"\n      val cpsSourceExists = os.exists(cpsSource)\n      if (!cpsSourceExists) System.err.println(s\"Expected source file $cpsSource does not exist\")\n      else System.err.println(s\"Found source file $cpsSource\")\n      assert(cpsSourceExists)\n\n      val sv                                                = scalaVersion()\n      def compile(extraSources: os.Path*): os.CommandResult =\n        os.proc(\n          \"scala-cli\",\n          \"--cli-default-scala-version\",\n          sv,\n          \"compile\",\n          cpsSource,\n          extraSources\n        ).call(\n          check = false,\n          mergeErrIntoOut = true,\n          cwd = BuildCtx.workspaceRoot\n        )\n      val compileResult = compile()\n      if (compileResult.exitCode != 0) {\n        System.err.println(s\"Compilation failed: $cpsSource\")\n        System.err.println(compileResult.out.text())\n      }\n      else\n        System.err.println(s\"Compiled $cpsSource successfully\")\n      assert(0 == compileResult.exitCode)\n\n      val notPassed = negativeTests.filter { case (testName, expectedErrors) =>\n        val testFile = base / \"negative-tests\" / testName\n        val res      = compile(testFile)\n        println(s\"Compiling $testName:\")\n        println(res.out.text())\n        val name = testFile.last\n        if (res.exitCode != 0) {\n          println(s\"Test case $name failed to compile as expected\")\n          val lines = res.out.lines()\n          println(lines)\n          expectedErrors.forall { expected =>\n            if (lines.exists(expected.findFirstIn(_).nonEmpty)) false\n            else {\n              println(s\"ERROR: regex `$expected` not found in compilation output for $testName\")\n              true\n            }\n          }\n        }\n        else {\n          println(s\"[ERROR] $name compiled successfully but it should not!\")\n          true\n        }\n\n      }\n      assert(notPassed.isEmpty)\n    }\n  }\n}\n\ndef asyncScalacOptions(scalaVersion: String) =\n  if (scalaVersion.startsWith(\"3\")) Nil else Seq(\"-Xasync\")\n\ntrait ProtoBuildModule extends ScalaCliPublishModule with HasTests\n    with ScalaCliScalafixModule\n\ntrait Core extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with HasTests\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def crossScalaVersion: String = crossValue\n\n  override def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(\n    config(crossScalaVersion)\n  )\n  override def compileModuleDeps: Seq[JavaModule] = Seq(\n    `build-macros`(crossScalaVersion)\n  )\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion)\n  }\n\n  override def repositoriesTask: Task[Seq[Repository]] =\n    Task.Anon(super.repositoriesTask() ++ deps.customRepositories)\n\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.bloopRifle.exclude((\"org.scala-lang.modules\", \"scala-collection-compat_2.13\")),\n    Deps.collectionCompat,\n    Deps.coursierJvm\n      // scalaJsEnvNodeJs brings a guava version that conflicts with this\n      .exclude((\"com.google.collections\", \"google-collections\"))\n      // Coursier is not cross-compiled and pulls jsoniter-scala-macros in 2.13\n      .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-macros\"))\n      // Let's favor our config module rather than the one coursier pulls\n      .exclude((organization, \"config_2.13\"))\n      .exclude((organization, \"config_3\"))\n      .exclude((\"org.scala-lang.modules\", \"scala-collection-compat_2.13\")),\n    Deps.dependency,\n    Deps.guava, // for coursierJvm / scalaJsEnvNodeJs, see above\n    Deps.jgit,\n    Deps.nativeTools, // Used only for discovery methods. For linking, look for scala-native-cli\n    Deps.osLib,\n    Deps.pprint,\n    Deps.scalaJsEnvJsdomNodejs,\n    Deps.scalaJsLogging,\n    Deps.swoval\n  )\n  override def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq(\n    Deps.jsoniterMacros\n  )\n\n  private def vcsState: T[String] = Task(persistent = true) {\n    val isCI  = System.getenv(\"CI\") != null\n    val state = VcsVersion.vcsState().format()\n    if (isCI) state\n    else state + \"-maybe-stale\"\n  }\n\n  def constantsFile: T[PathRef] = Task(persistent = true) {\n    val dir                 = Task.dest / \"constants\"\n    val dest                = dir / \"Constants.scala\"\n    val testRunnerMainClass = `test-runner`(crossScalaVersion)\n      .mainClass()\n      .getOrElse(sys.error(\"No main class defined for test-runner\"))\n    val runnerMainClass = build.runner(crossScalaVersion)\n      .mainClass()\n      .getOrElse(sys.error(\"No main class defined for runner\"))\n    val javaTestRunnerMainClass = `java-test-runner`\n      .mainClass()\n      .getOrElse(sys.error(\"No main class defined for java-test-runner\"))\n    val detailedVersionValue =\n      if (`local-repo`.developingOnStubModules) s\"\"\"Some(\"${vcsState()}\")\"\"\"\n      else \"None\"\n    val testRunnerOrganization = `test-runner`(crossScalaVersion)\n      .pomSettings()\n      .organization\n    val javaTestRunnerOrganization = `java-test-runner`\n      .pomSettings()\n      .organization\n    val code =\n      s\"\"\"package scala.build.internal\n         |\n         |/** Build-time constants. Generated by mill. */\n         |object Constants {\n         |  def version = \"${publishVersion()}\"\n         |  def detailedVersion: Option[String] = $detailedVersionValue\n         |  def ghOrg = \"$ghOrg\"\n         |  def ghName = \"$ghName\"\n         |\n         |  def scalaJsVersion = \"${Scala.scalaJs}\"\n         |  def scalaJsCliVersion = \"${Scala.scalaJsCli}\"\n         |  def scalajsEnvJsdomNodejsVersion = \"${Deps.scalaJsEnvJsdomNodejs.dep.versionConstraint.asString}\"\n         |  def scalaNativeVersion04 = \"${Deps.Versions.scalaNative04}\"\n         |  def scalaNativeVersion = \"${Deps.Versions.scalaNative}\"\n         |\n         |  def testRunnerOrganization = \"$testRunnerOrganization\"\n         |  def testRunnerModuleName = \"${`test-runner`(crossScalaVersion).artifactName()}\"\n         |  def testRunnerVersion = \"${`test-runner`(crossScalaVersion).publishVersion()}\"\n         |  def testRunnerMainClass = \"$testRunnerMainClass\"\n         |\n         |  def javaTestRunnerOrganization = \"$javaTestRunnerOrganization\"\n         |  def javaTestRunnerModuleName = \"${`java-test-runner`.artifactName()}\"\n         |  def javaTestRunnerVersion = \"${`java-test-runner`.publishVersion()}\"\n         |  def javaTestRunnerMainClass = \"$javaTestRunnerMainClass\"\n         |\n         |  def runnerOrganization = \"${build.runner(crossScalaVersion).pomSettings().organization}\"\n         |  def runnerModuleName = \"${build.runner(crossScalaVersion).artifactName()}\"\n         |  def runnerVersion = \"${build.runner(crossScalaVersion).publishVersion()}\"\n         |  def runnerScala30LegacyVersion = \"${Cli.runnerScala30LegacyVersion}\"\n         |  def runnerScala2LegacyVersion = \"${Cli.runnerScala2LegacyVersion}\"\n         |  def runnerMainClass = \"$runnerMainClass\"\n         |\n         |  def semanticDbPluginOrganization = \"${Deps.semanticDbScalac.dep.module.organization\n          .value}\"\n         |  def semanticDbPluginModuleName = \"${Deps.semanticDbScalac.dep.module.name.value}\"\n         |  def semanticDbPluginVersion = \"${Deps.semanticDbScalac.dep.versionConstraint.asString}\"\n         |\n         |  def semanticDbJavacPluginOrganization = \"${Deps.semanticDbJavac.dep.module.organization\n          .value}\"\n         |  def semanticDbJavacPluginModuleName = \"${Deps.semanticDbJavac.dep.module.name.value}\"\n         |  def semanticDbJavacPluginVersion = \"${Deps.semanticDbJavac.dep.versionConstraint.asString}\"\n         |\n         |  def localRepoResourcePath = \"$localRepoResourcePath\"\n         |\n         |  def jmhVersion = \"${Deps.Versions.jmh}\"\n         |  def jmhOrg = \"${Deps.jmhCore.dep.module.organization.value}\"\n         |  def jmhCoreModule = \"${Deps.jmhCore.dep.module.name.value}\"\n         |  def jmhGeneratorBytecodeModule = \"${Deps.jmhGeneratorBytecode.dep.module.name.value}\"\n         |\n         |  def ammoniteVersion = \"${Deps.Versions.ammonite}\"\n         |  def ammoniteVersionForScala3Lts = \"${Deps.Versions.ammoniteForScala3Lts}\"\n         |  def millVersion = \"${BuildInfo.millVersion}\"\n         |  def maxScalaNativeForMillExport = \"${Deps.Versions.maxScalaNativeForMillExport}\"\n         |\n         |  def scalafmtOrganization = \"${Deps.scalafmtCli.dep.module.organization.value}\"\n         |  def scalafmtName = \"${Deps.scalafmtCli.dep.module.name.value}\"\n         |  def defaultScalafmtVersion = \"${Deps.scalafmtCli.dep.versionConstraint.asString}\"\n         |\n         |  def toolkitOrganization = \"${Deps.toolkit.dep.module.organization.value}\"\n         |  def toolkitName = \"${Deps.toolkit.dep.module.name.value}\"\n         |  def toolkitTestName = \"${Deps.toolkitTest.dep.module.name.value}\"\n         |  def toolkitDefaultVersion = \"${Deps.toolkitVersion}\"\n         |  def toolkitMaxScalaNative = \"${Deps.Versions.maxScalaNativeForToolkit}\"\n         |  def toolkitVersionForNative04 = \"${Deps.toolkitVersionForNative04}\"\n         |  def toolkitVersionForNative05 = \"${Deps.toolkitVersionForNative05}\"\n         |\n         |  def typelevelOrganization = \"${Deps.typelevelToolkit.dep.module.organization.value}\"\n         |  def typelevelToolkitDefaultVersion = \"${Deps.typelevelToolkitVersion}\"\n         |  def typelevelToolkitMaxScalaNative = \"${Deps.Versions.maxScalaNativeForTypelevelToolkit}\"\n         |\n         |  def minimumLauncherJavaVersion = ${Java.minimumJavaLauncherJava}\n         |  def minimumBloopJavaVersion = ${Java.minimumBloopJava}\n         |  def minimumInternalJavaVersion = ${Java.minimumInternalJava}\n         |  def defaultJavaVersion = ${Java.defaultJava}\n         |  def mainJavaVersions = Seq(${Java.mainJavaVersions.sorted.mkString(\", \")})\n         |\n         |  def defaultScalaVersion = \"${Scala.defaultUser}\"\n         |  def defaultScala212Version = \"${Scala.scala212}\"\n         |  def defaultScala213Version = \"${Scala.scala213}\"\n         |  def scala3NextRcVersion = \"${Scala.scala3NextRc}\"\n         |  def scala3NextPrefix = \"${Scala.scala3NextPrefix}\"\n         |  def scala3LtsPrefix = \"${Scala.scala3LtsPrefix}\"\n         |  def scala3Lts       = \"${Scala.scala3Lts}\"\n         |  \n         |  def scala38Versions = Seq(${Scala.scala38Versions\n          .sorted\n          .map(s => s\"\\\"$s\\\"\")\n          .mkString(\", \")})\n         |  def scala38MinJavaVersion = ${Java.minimumScala38Java}\n         |\n         |  def workspaceDirName = \"$workspaceDirName\"\n         |  def projectFileName = \"$projectFileName\"\n         |  def jvmPropertiesFileName = \"$jvmPropertiesFileName\"\n         |  def scalacArgumentsFileName = \"scalac.args.txt\"\n         |  def maxScalacArgumentsCount = 5000\n         |\n         |  def defaultGraalVMJavaVersion = ${deps.graalVmJavaVersion}\n         |  def defaultGraalVMVersion = \"${deps.graalVmCommunityVersion}\"\n         |\n         |  def scalaCliSigningOrganization = \"${Deps.signingCli.dep.module.organization.value}\"\n         |  def scalaCliSigningName = \"${Deps.signingCli.dep.module.name.value}\"\n         |  def scalaCliSigningVersion = \"${Deps.signingCli.dep.versionConstraint.asString}\"\n         |  def javaClassNameOrganization = \"${Deps.javaClassName.dep.module.organization.value}\"\n         |  def javaClassNameName = \"${Deps.javaClassName.dep.module.name.value}\"\n         |  def javaClassNameVersion = \"${Deps.javaClassName.dep.versionConstraint.asString}\"\n         |\n         |  def signingCliJvmVersion = ${Deps.Versions.signingCliJvmVersion}\n         |\n         |  def libsodiumVersion = \"${deps.libsodiumVersion}\"\n         |  def libsodiumjniVersion = \"${Deps.libsodiumjni.dep.versionConstraint.asString}\"\n         |  def alpineLibsodiumVersion = \"${deps.alpineLibsodiumVersion}\"\n         |  def condaLibsodiumVersion = \"${deps.condaLibsodiumVersion}\"\n         |\n         |  def scalaPyVersion = \"${Deps.scalaPy.dep.versionConstraint.asString}\"\n         |  def scalaPyMaxScalaNative = \"${Deps.Versions.maxScalaNativeForScalaPy}\"\n         |\n         |  def giter8Organization = \"${Deps.giter8.dep.module.organization.value}\"\n         |  def giter8Name = \"${Deps.giter8.dep.module.name.value}\"\n         |  def giter8Version = \"${Deps.giter8.dep.versionConstraint.asString}\"\n         |  \n         |  def sbtVersion = \"${Deps.Versions.sbtVersion}\"\n         |\n         |  def mavenVersion = \"${Deps.Versions.mavenVersion}\"\n         |  def mavenScalaCompilerPluginVersion = \"${Deps.Versions.mavenScalaCompilerPluginVersion}\"\n         |  def mavenExecPluginVersion = \"${Deps.Versions.mavenExecPluginVersion}\"\n         |  def mavenAppArtifactId = \"${Deps.Versions.mavenAppArtifactId}\"\n         |  def mavenAppGroupId = \"${Deps.Versions.mavenAppGroupId}\"\n         |  def mavenAppVersion = \"${Deps.Versions.mavenAppVersion}\"\n         |\n         |  def scalafixVersion = \"${Deps.Versions.scalafix}\"\n         |  \n         |  def alpineVersion = \"$alpineVersion\"\n         |}\n         |\"\"\".stripMargin\n    if (!os.isFile(dest) || os.read(dest) != code)\n      os.write.over(dest, code, createFolders = true)\n    PathRef(dir)\n  }\n  override def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile())\n}\n\ntrait Directives extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with HasTests\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def crossScalaVersion: String                     = crossValue\n  override def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(\n    options(crossScalaVersion),\n    core(crossScalaVersion),\n    `build-macros`(crossScalaVersion),\n    `specification-level`(crossScalaVersion)\n  )\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion)\n  }\n\n  override def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq(\n    Deps.jsoniterMacros,\n    Deps.svm\n  )\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    // Deps.asm,\n    Deps.bloopConfig,\n    Deps.jsoniterCore,\n    Deps.pprint,\n    Deps.usingDirectives\n  )\n\n  override def repositoriesTask: Task[Seq[Repository]] =\n    Task.Anon(super.repositoriesTask() ++ deps.customRepositories)\n\n  object test extends ScalaCliTests {\n    override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n      Deps.pprint\n    )\n    override def runClasspath: T[Seq[PathRef]] = Task {\n      super.runClasspath() ++ Seq(`local-repo`.localRepoJar())\n    }\n\n    override def generatedSources: T[Seq[PathRef]] =\n      super.generatedSources() ++ Seq(constantsFile())\n\n    def constantsFile: T[PathRef] = Task(persistent = true) {\n      val dir  = Task.dest / \"constants\"\n      val dest = dir / \"Constants2.scala\"\n      val code =\n        s\"\"\"package scala.build.tests\n           |\n           |/** Build-time constants. Generated by mill. */\n           |object Constants {\n           |  def cs = \"${settings.cs().replace(\"\\\\\", \"\\\\\\\\\")}\"\n           |}\n           |\"\"\".stripMargin\n      if (!os.isFile(dest) || os.read(dest) != code)\n        os.write.over(dest, code, createFolders = true)\n      PathRef(dir)\n    }\n\n    // uncomment below to debug tests in attach mode on 5005 port\n    // def forkArgs = Task {\n    //   super.forkArgs() ++ Seq(\"-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=y\")\n    // }\n  }\n}\n\ntrait Config extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def crossScalaVersion: String                     = crossValue\n  override def moduleDeps: Seq[SonatypeCentralPublishModule] =\n    Seq(`specification-level`(crossScalaVersion))\n  override def mvnDeps: T[Seq[Dep]]          = super.mvnDeps() ++ Seq(Deps.jsoniterCore)\n  override def compileMvnDeps: T[Seq[Dep]]   = super.compileMvnDeps() ++ Seq(Deps.jsoniterMacros)\n  override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ Seq(\"-deprecation\")\n}\n\ntrait Options extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with HasTests\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def crossScalaVersion: String                     = crossValue\n  override def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(\n    core(crossScalaVersion)\n  )\n  override def compileModuleDeps: Seq[JavaModule] = Seq(\n    `build-macros`(crossScalaVersion)\n  )\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion)\n  }\n\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.bloopConfig,\n    Deps.signingCliShared\n  )\n  override def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq(\n    Deps.jsoniterMacros\n  )\n\n  override def repositoriesTask: Task[Seq[Repository]] =\n    Task.Anon(super.repositoriesTask() ++ deps.customRepositories)\n\n  object test extends ScalaCliTests with ScalaCliScalafixModule {\n    override def scalacOptions = super.scalacOptions() ++ Seq(\"-deprecation\")\n    // uncomment below to debug tests in attach mode on 5005 port\n    // def forkArgs = Task {\n    //   super.forkArgs() ++ Seq(\"-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=y\")\n    // }\n  }\n}\n\ntrait Build extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with HasTests\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def crossScalaVersion: String                     = crossValue\n  override def moduleDir: os.Path                            = super.moduleDir / os.up / \"build\"\n  override def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(\n    options(crossScalaVersion),\n    directives(crossScalaVersion),\n    `scala-cli-bsp`,\n    `test-runner`(crossScalaVersion),\n    `tasty-lib`(crossScalaVersion)\n  )\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion)\n  }\n\n  override def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq(\n    Deps.jsoniterMacros,\n    Deps.svm\n  )\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.asm,\n    Deps.collectionCompat,\n    Deps.javaClassName,\n    Deps.jsoniterCore,\n    Deps.scalametaSemanticDbShared,\n    Deps.nativeTestRunner,\n    Deps.osLib,\n    Deps.pprint,\n    Deps.scalaJsEnvNodeJs,\n    Deps.scalaJsTestAdapter,\n    Deps.swoval,\n    Deps.zipInputStream\n  )\n\n  override def repositoriesTask: Task[Seq[Repository]] =\n    Task.Anon(super.repositoriesTask() ++ deps.customRepositories)\n\n  object test extends ScalaCliTests with ScalaCliScalafixModule {\n    override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ Seq(\"-deprecation\")\n    override def mvnDeps: T[Seq[Dep]]          = super.mvnDeps() ++ Seq(\n      Deps.pprint,\n      Deps.slf4jNop\n    )\n    override def runClasspath: T[Seq[PathRef]] = Task {\n      super.runClasspath() ++ Seq(`local-repo`.localRepoJar())\n    }\n\n    override def generatedSources: T[Seq[PathRef]] =\n      super.generatedSources() ++ Seq(constantsFile())\n\n    def constantsFile: T[PathRef] = Task(persistent = true) {\n      val dir  = Task.dest / \"constants\"\n      val dest = dir / \"Constants2.scala\"\n      val code =\n        s\"\"\"package scala.build.tests\n           |\n           |/** Build-time constants. Generated by mill. */\n           |object Constants {\n           |  def cs = \"${settings.cs().replace(\"\\\\\", \"\\\\\\\\\")}\"\n           |  def toolkitOrganization = \"${Deps.toolkit.dep.module.organization.value}\"\n           |  def toolkitName = \"${Deps.toolkit.dep.module.name.value}\"\n           |  def toolkitTestName = \"${Deps.toolkitTest.dep.module.name.value}\"\n           |  def toolkitVersion = \"${Deps.toolkitTest.dep.versionConstraint.asString}\"\n           |  def typelevelToolkitOrganization = \"${Deps.typelevelToolkit.dep.module.organization\n            .value}\"\n           |  def typelevelToolkitVersion = \"${Deps.typelevelToolkit.dep.versionConstraint.asString}\"\n           |\n           |  def defaultScalaVersion = \"${Scala.defaultUser}\"\n           |  def defaultScala212Version = \"${Scala.scala212}\"\n           |  def defaultScala213Version = \"${Scala.scala213}\"\n           |}\n           |\"\"\".stripMargin\n      if (!os.isFile(dest) || os.read(dest) != code)\n        os.write.over(dest, code, createFolders = true)\n      PathRef(dir)\n    }\n\n    // uncomment below to debug tests in attach mode on 5005 port\n    // def forkArgs = Task {\n    //   super.forkArgs() ++ Seq(\"-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=y\")\n    // }\n  }\n}\n\ntrait SpecificationLevel extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with LocatedInModules {\n  override def crossScalaVersion: String = crossValue\n}\n\ntrait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers\n    with FormatNativeImageConf\n    with LocatedInModules {\n  // Copied from Mill: https://github.com/com-lihaoyi/mill/blob/ea367c09bd31a30464ca901cb29863edde5340be/scalalib/src/mill/scalalib/JavaModule.scala#L792\n  def debug(port: Int, args: Task[Args] = Task.Anon(Args())): Command[Unit] = Task.Command {\n    try mill.api.Result.Success(\n        mill.util.Jvm.callProcess(\n          mainClass = finalMainClass(),\n          classPath = runClasspath().map(_.path),\n          jvmArgs = forkArgs() ++ Seq(\n            s\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$port,quiet=y\"\n          ),\n          mainArgs = args().value\n        )\n      )\n    catch {\n      case _: Exception =>\n        mill.api.Result.Failure(\"subprocess failed\")\n    }\n  }\n\n  def constantsFile: T[PathRef] = Task(persistent = true) {\n    val dir  = Task.dest / \"constants\"\n    val dest = dir / \"Constants.scala\"\n    val code =\n      s\"\"\"package scala.cli.internal\n         |\n         |/** Build-time constants. Generated by mill. */\n         |object Constants {\n         |  def pythonInterfaceOrg          = \"${Deps.pythonInterface.dep.module.organization.value}\"\n         |  def pythonInterfaceName         = \"${Deps.pythonInterface.dep.module.name.value}\"\n         |  def pythonInterfaceVersion      = \"${Deps.pythonInterface.dep.versionConstraint.asString}\"\n         |  def launcherTypeResourcePath    = \"${launcherTypeResourcePath.toString}\"\n         |  def defaultFilesResourcePath    = \"$defaultFilesResourcePath\"\n         |  def maxAmmoniteScala3Version    = \"${Scala.maxAmmoniteScala3Version}\"\n         |  def maxAmmoniteScala3LtsVersion = \"${Scala.maxAmmoniteScala3LtsVersion}\"\n         |}\n         |\"\"\".stripMargin\n    if (!os.isFile(dest) || os.read(dest) != code)\n      os.write.over(dest, code, createFolders = true)\n    PathRef(dir)\n  }\n  def optionsConstantsFile: T[PathRef] = Task(persistent = true) {\n    val dir  = Task.dest / \"constants\"\n    val dest = dir / \"Constants.scala\"\n    val code =\n      s\"\"\"package scala.cli.commands\n         |\n         |/** Build-time constants. Generated by mill. */\n         |object Constants {\n         |  def defaultScalaVersion = \"${Scala.defaultUser}\"\n         |  def defaultJavaVersion = ${Java.defaultJava}\n         |  def minimumLauncherJavaVersion = ${Java.minimumJavaLauncherJava}\n         |  def minimumBloopJavaVersion = ${Java.minimumBloopJava}\n         |  def scalaJsVersion = \"${Scala.scalaJs}\"\n         |  def scalaJsCliVersion = \"${Scala.scalaJsCli}\"\n         |  def scalaNativeVersion = \"${Deps.nativeTools.dep.versionConstraint.asString}\"\n         |  def ammoniteVersion = \"${Deps.Versions.ammonite}\"\n         |  def ammoniteVersionForScala3Lts = \"${Deps.Versions.ammoniteForScala3Lts}\"\n         |  def defaultScalafmtVersion = \"${Deps.scalafmtCli.dep.versionConstraint.asString}\"\n         |  def defaultGraalVMJavaVersion = \"${deps.graalVmJavaVersion}\"\n         |  def defaultGraalVMVersion = \"${deps.graalVmCommunityVersion}\"\n         |  def scalaPyVersion = \"${Deps.scalaPy.dep.versionConstraint.asString}\"\n         |  def signingCliJvmVersion = ${Deps.Versions.signingCliJvmVersion}\n         |  def defaultMillVersion = \"${BuildInfo.millVersion}\"\n         |  def mill012Version = \"${Deps.Versions.mill012Version}\"\n         |  def mill1Version = \"${Deps.Versions.mill1Version}\"\n         |  def defaultSbtVersion = \"${Deps.Versions.sbtVersion}\"\n         |  def defaultMavenVersion = \"${Deps.Versions.mavenVersion}\"\n         |  def defaultMavenScalaCompilerPluginVersion = \"${Deps.Versions.mavenScalaCompilerPluginVersion}\"\n         |  def defaultMavenExecPluginVersion = \"${Deps.Versions.mavenExecPluginVersion}\"\n         |}\n         |\"\"\".stripMargin\n    if (!os.isFile(dest) || os.read(dest) != code)\n      os.write.over(dest, code, createFolders = true)\n    PathRef(dir)\n  }\n  override def generatedSources: T[Seq[PathRef]] =\n    super.generatedSources() ++ Seq(constantsFile(), optionsConstantsFile())\n\n  def defaultFilesResources: T[PathRef] = Task(persistent = true) {\n    val dir                                                  = Task.dest / \"resources\"\n    def transformWorkflow(content: Array[Byte]): Array[Byte] =\n      new String(content, \"UTF-8\")\n        .replaceAll(\" ./scala-cli\", \" scala-cli\")\n        .getBytes(\"UTF-8\")\n    val resources = Seq[(String, os.SubPath, Array[Byte] => Array[Byte])](\n      (\n        \"https://raw.githubusercontent.com/scala-cli/default-workflow/main/.github/workflows/ci.yml\",\n        os.sub / \"workflows\" / \"default.yml\",\n        transformWorkflow\n      ),\n      (\n        \"https://raw.githubusercontent.com/scala-cli/default-workflow/main/.gitignore\",\n        os.sub / \"gitignore\",\n        identity\n      )\n    )\n    for ((srcUrl, destRelPath, transform) <- resources) {\n      val dest = dir / defaultFilesResourcePath / destRelPath\n      if (!os.isFile(dest)) {\n        val content = Using.resource(new URL(srcUrl).openStream())(_.readAllBytes())\n        os.write(dest, transform(content), createFolders = true)\n      }\n    }\n    PathRef(dir)\n  }\n  override def resources: T[Seq[PathRef]] = super.resources() ++ Seq(defaultFilesResources())\n\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion) ++ Seq(\"-deprecation\")\n  }\n  override def javacOptions: T[Seq[String]] = Task {\n    super.javacOptions() ++ Seq(\"--release\", \"16\")\n  }\n  def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(\n    `build-module`(crossScalaVersion),\n    config(crossScalaVersion),\n    `specification-level`(crossScalaVersion)\n  )\n\n  override def repositoriesTask: Task[Seq[Repository]] =\n    Task.Anon(super.repositoriesTask() ++ customRepositories)\n\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.caseApp,\n    Deps.coursierLauncher,\n    Deps.coursierProxySetup,\n    Deps.coursierPublish.exclude((organization, \"config_2.13\")).exclude((organization, \"config_3\")),\n    Deps.jimfs, // scalaJsEnvNodeJs pulls jimfs:1.1, whose class path seems borked (bin compat issue with the guava version it depends on)\n    Deps.jniUtils,\n    Deps.jsoniterCore,\n    Deps.libsodiumjni,\n    Deps.metaconfigTypesafe,\n    Deps.pythonNativeLibs,\n    Deps.scalaPackager.exclude(\"com.lihaoyi\" -> \"os-lib_2.13\"),\n    Deps.signingCli.exclude((organization, \"config_2.13\")).exclude((organization, \"config_3\")),\n    Deps.slf4jNop, // to silence jgit\n    Deps.sttp,\n    Deps.scalafixInterfaces,\n    Deps.scala3Graal,         // TODO: drop this if we ever bump internal JDK to 24+\n    Deps.scala3GraalProcessor // TODO: drop this if we ever bump internal JDK to 24+\n  )\n  override def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq(\n    Deps.jsoniterMacros,\n    Deps.svm\n  )\n  override def mainClass: T[Option[String]] = Some(\"scala.cli.ScalaCli\")\n\n  private def scala3GraalProcessorClassPath: T[Seq[PathRef]] = Task {\n    defaultResolver().classpath {\n      val bind = bindDependency()\n      Seq(Deps.scala3GraalProcessor).map(bind)\n    }\n  }\n\n  override def nativeImageClassPath: T[Seq[PathRef]] = Task {\n    val classpath = super.nativeImageClassPath().map(_.path).mkString(File.pathSeparator)\n    val cache     = Task.dest / \"native-cp\"\n    // `scala3-graal-processor`.run() does not give me output and I cannot pass dynamically computed values like classpath\n    // TODO: drop this if we ever bump internal JDK to 24+\n    val res = mill.util.Jvm.callProcess(\n      mainClass = \"scala.cli.graal.CoursierCacheProcessor\",\n      classPath = scala3GraalProcessorClassPath().map(_.path).toList,\n      mainArgs = Seq(cache.toNIO.toString, classpath)\n    )\n    val cp = res.out.trim()\n    cp.split(File.pathSeparator).toSeq.map(p => PathRef(os.Path(p)))\n  }\n\n  override def localRepoJar: T[PathRef] = `local-repo`.localRepoJar()\n\n  object test extends ScalaCliTests with ScalaCliScalafixModule {\n    override def moduleDeps: Seq[JavaModule] = super.moduleDeps ++ Seq(\n      `build-module`(crossScalaVersion).test\n    )\n    override def runClasspath: T[Seq[PathRef]] = Task {\n      super.runClasspath() ++ Seq(localRepoJar())\n    }\n\n    override def compileMvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n      Deps.jsoniterMacros\n    )\n\n    // Required by the reflection usage in modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala\n    override def forkArgs: T[Seq[String]] = Task {\n      super.forkArgs() ++ Seq(\"--add-opens=java.base/java.util=ALL-UNNAMED\")\n    }\n  }\n}\n\ntrait CliIntegration extends SbtModule\n    with ScalaCliPublishModule\n    with HasTests\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def scalaVersion: T[String] = sv\n\n  def sv: String = Scala.scala3Lts\n\n  def tmpDirBase: T[PathRef] = Task(persistent = true) {\n    PathRef(Task.dest / \"working-dir\")\n  }\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ Seq(\"-deprecation\")\n  }\n\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.osLib\n  )\n\n  trait IntegrationScalaTests extends super.ScalaCliTests with ScalaCliScalafixModule {\n    override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n      Deps.bsp4j,\n      Deps.coursier\n        .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-macros\")),\n      Deps.dockerClient,\n      Deps.jsoniterCore,\n      Deps.libsodiumjni,\n      Deps.pprint,\n      Deps.slf4jNop,\n      Deps.usingDirectives\n    )\n    override def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq(\n      Deps.jsoniterMacros\n    )\n    override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq(\n      \"SCALA_CLI_TMP\"                -> tmpDirBase().path.toString,\n      \"SCALA_CLI_PRINT_STACK_TRACES\" -> \"1\",\n      \"SCALA_CLI_CONFIG\"             -> (tmpDirBase().path / \"config\" / \"config.json\").toString\n    )\n\n    def constantsFile: T[PathRef] = Task(persistent = true) {\n      val dir                    = Task.dest / \"constants\"\n      val dest                   = dir / \"Constants.scala\"\n      val mostlyStaticDockerfile =\n        BuildCtx.workspaceRoot / \".github\" / \"scripts\" / \"docker\" / \"ScalaCliSlimDockerFile\"\n      assert(\n        os.exists(mostlyStaticDockerfile),\n        s\"Error: $mostlyStaticDockerfile not found\"\n      )\n      val code =\n        s\"\"\"package scala.cli.integration\n           |\n           |/** Build-time constants. Generated by mill. */\n           |object Constants {\n           |  def cliVersion                   = \"${publishVersion()}\"\n           |  def allJavaVersions              = Seq(${Java.allJavaVersions.sorted.mkString(\", \")})\n           |  def bspVersion                   = \"${Deps.bsp4j.dep.versionConstraint.asString}\"\n           |  def minimumLauncherJavaVersion   = ${Java.minimumJavaLauncherJava}\n           |  def bloopMinimumJvmVersion       = ${Java.minimumBloopJava}\n           |  def minimumInternalJvmVersion    = ${Java.minimumInternalJava}\n           |  def defaultJvmVersion            = ${Java.defaultJava}\n           |  def scala212                     = \"${Scala.scala212}\"\n           |  def scala213                     = \"${Scala.scala213}\"\n           |  def scala3LtsPrefix              = \"${Scala.scala3LtsPrefix}\"\n           |  def scala3Lts                    = \"${Scala.scala3Lts}\"\n           |  def scala3NextPrefix             = \"${Scala.scala3NextPrefix}\"\n           |  def scala3NextRc                 = \"${Scala.scala3NextRc}\"\n           |  def scala3NextRcAnnounced        = \"${Scala.scala3NextRcAnnounced}\"\n           |  def scala3Next                   = \"${Scala.scala3Next}\"\n           |  def scala3NextAnnounced          = \"${Scala.scala3NextAnnounced}\"\n           |  def defaultScala                 = \"${Scala.defaultUser}\"\n           |  def scala38Versions              = Seq(${Scala.scala38Versions\n            .sorted\n            .map(s => s\"\\\"$s\\\"\")\n            .mkString(\", \")})\n           |  def scala38MinJavaVersion        = ${Java.minimumScala38Java}\n           |  def defaultScalafmtVersion       = \"${Deps.scalafmtCli.dep.versionConstraint.asString}\"\n           |  def maxAmmoniteScala212Version   = \"${Scala.maxAmmoniteScala212Version}\"\n           |  def maxAmmoniteScala213Version   = \"${Scala.maxAmmoniteScala213Version}\"\n           |  def maxAmmoniteScala3Version     = \"${Scala.maxAmmoniteScala3Version}\"\n           |  def maxAmmoniteScala3LtsVersion  = \"${Scala.maxAmmoniteScala3LtsVersion}\"\n           |  def legacyScala3Versions         = Seq(${Scala.legacyScala3Versions.map(p =>\n            s\"\\\"$p\\\"\"\n          ).mkString(\", \")})\n           |  def scalaJsVersion               = \"${Scala.scalaJs}\"\n           |  def scalaJsCliVersion            = \"${Scala.scalaJsCli}\"\n           |  def scalaNativeVersion           = \"${Deps.Versions.scalaNative}\"\n           |  def scalaNativeVersion04         = \"${Deps.Versions.scalaNative04}\"\n           |  def scalaNativeVersion05         = \"${Deps.Versions.scalaNative05}\"\n           |  def semanticDbJavacPluginVersion = \"${Deps.semanticDbJavac.dep.versionConstraint.asString}\"\n           |  def ammoniteVersion              = \"${Deps.ammonite.dep.versionConstraint.asString}\"\n           |  def defaultGraalVMJavaVersion    = \"${deps.graalVmJavaVersion}\"\n           |  def defaultGraalVMVersion        = \"${deps.graalVmCommunityVersion}\"\n           |  def runnerScala30LegacyVersion   = \"${Cli.runnerScala30LegacyVersion}\"\n           |  def runnerScala2LegacyVersion    = \"${Cli.runnerScala2LegacyVersion}\"\n           |  def scalaPyVersion               = \"${Deps.scalaPy.dep.versionConstraint.asString}\"\n           |  def scalaPyMaxScalaNative        = \"${Deps.Versions.maxScalaNativeForScalaPy}\"\n           |  def bloopVersion                 = \"${Deps.bloopRifle.dep.versionConstraint.asString}\"\n           |  def pprintVersion                = \"${TestDeps.pprint.dep.versionConstraint.asString}\"\n           |  def munitVersion                 = \"${TestDeps.munit.dep.versionConstraint.asString}\"\n           |  def dockerTestImage              = \"${Docker.testImage}\"\n           |  def dockerAlpineTestImage        = \"${Docker.alpineTestImage}\"\n           |  def authProxyTestImage           = \"${Docker.authProxyTestImage}\"\n           |  def mostlyStaticDockerfile       = \"${mostlyStaticDockerfile.toString.replace(\n            \"\\\\\",\n            \"\\\\\\\\\"\n          )}\"\n           |  def cs                           = \"${settings.cs().replace(\"\\\\\", \"\\\\\\\\\")}\"\n           |  def workspaceDirName             = \"$workspaceDirName\"\n           |  def libsodiumVersion             = \"${deps.libsodiumVersion}\"\n           |  def alpineLibsodiumVersion       = \"${deps.alpineLibsodiumVersion}\"\n           |  def condaLibsodiumVersion        = \"${deps.condaLibsodiumVersion}\"\n           |  def dockerArchLinuxImage         = \"${TestDeps.archLinuxImage}\"\n           |  \n           |  def toolkitVersion                 = \"${Deps.toolkitVersion}\"\n           |  def toolkitVersionForNative04      = \"${Deps.toolkitVersionForNative04}\"\n           |  def toolkitVersionForNative05      = \"${Deps.toolkitVersionForNative05}\"\n           |  def toolkiMaxScalaNative           = \"${Deps.Versions.maxScalaNativeForToolkit}\"\n           |  def typelevelToolkitVersion        = \"${Deps.typelevelToolkitVersion}\"\n           |  def typelevelToolkitMaxScalaNative = \"${Deps.Versions\n            .maxScalaNativeForTypelevelToolkit}\"\n           |\n           |  def ghOrg  = \"$ghOrg\"\n           |  def ghName = \"$ghName\"\n           |\n           |  def jmhVersion = \"${Deps.Versions.jmh}\"\n           |  def jmhOrg = \"${Deps.jmhCore.dep.module.organization.value}\"\n           |  def jmhCoreModule = \"${Deps.jmhCore.dep.module.name.value}\"\n           |  def jmhGeneratorBytecodeModule = \"${Deps.jmhGeneratorBytecode.dep.module.name.value}\"\n           |  def defaultMillVersion = \"${BuildInfo.millVersion}\"\n           |  def mill012Version = \"${Deps.Versions.mill012Version}\"\n           |  def mill1Version = \"${Deps.Versions.mill1Version}\"\n           |}\n           |\"\"\".stripMargin\n      if (!os.isFile(dest) || os.read(dest) != code)\n        os.write.over(dest, code, createFolders = true)\n      PathRef(dir)\n    }\n    override def generatedSources: T[Seq[PathRef]] =\n      super.generatedSources() ++ Seq(constantsFile())\n\n    override def testForked(args: String*): Command[(msg: String, results: Seq[TestResult])] =\n      jvm(args*)\n\n    def forcedLauncher: T[PathRef] = Task(persistent = true) {\n      val ext      = if (Properties.isWin) \".exe\" else \"\"\n      val launcher = Task.dest / s\"scala-cli$ext\"\n      if !os.exists(launcher) then\n        BuildCtx.withFilesystemCheckerDisabled {\n          val dir = Option(System.getenv(\"SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY\")).getOrElse {\n            sys.error(\"SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY not set\")\n          }\n          System.err.println(s\"SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY was set to $dir\")\n          val content = importedLauncher(dir, BuildCtx.workspaceRoot)\n          System.err.println(s\"writing launcher to $launcher\")\n          os.write(\n            launcher,\n            content,\n            createFolders = true,\n            perms = if (Properties.isWin) null else \"rwxr-xr-x\"\n          )\n        }\n      PathRef(launcher)\n    }\n\n    def forcedStaticLauncher: T[PathRef] = Task(persistent = true) {\n      val launcher = Task.dest / \"scala-cli\"\n      if !os.exists(launcher) then\n        BuildCtx.withFilesystemCheckerDisabled {\n          val dir =\n            Option(System.getenv(\"SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY\")).getOrElse {\n              sys.error(\"SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY not set\")\n            }\n          System.err.println(s\"SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY was set to $dir\")\n          val content = importedLauncher(dir, BuildCtx.workspaceRoot)\n          System.err.println(s\"writing launcher to $launcher\")\n          os.write(launcher, content, createFolders = true)\n        }\n      PathRef(launcher)\n    }\n\n    def forcedMostlyStaticLauncher: T[PathRef] = Task(persistent = true) {\n      val launcher = Task.dest / \"scala-cli\"\n      if !os.exists(launcher) then\n        BuildCtx.withFilesystemCheckerDisabled {\n          val dir =\n            Option(System.getenv(\"SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY\")).getOrElse {\n              sys.error(\"SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY not set\")\n            }\n          System.err.println(\n            s\"SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY was set to $dir\"\n          )\n          val content = importedLauncher(dir, BuildCtx.workspaceRoot)\n          System.err.println(s\"writing launcher to $launcher\")\n          os.write(launcher, content, createFolders = true)\n        }\n      PathRef(launcher)\n    }\n\n    private object Launchers {\n      def jvm: T[PathRef] = cli(Scala.defaultInternal).standaloneLauncher\n\n      def jvmBootstrapped: T[PathRef] = cliBootstrapped.jar\n\n      def native: T[PathRef] =\n        Option(System.getenv(\"SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY\")) match {\n          case Some(_) => forcedLauncher\n          case None    => cli(Scala.defaultInternal).nativeImage\n        }\n\n      def nativeStatic: T[PathRef] =\n        Option(System.getenv(\"SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY\")) match {\n          case Some(_) => forcedStaticLauncher\n          case None    => cli(Scala.defaultInternal).nativeImageStatic\n        }\n\n      def nativeMostlyStatic: T[PathRef] =\n        Option(System.getenv(\"SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY\")) match {\n          case Some(_) => forcedMostlyStaticLauncher\n          case None    => cli(Scala.defaultInternal).nativeImageMostlyStatic\n        }\n    }\n\n    private def extraTestArgs(launcher: os.Path, cliKind: String): Seq[String] =\n      Seq(\n        s\"-Dtest.scala-cli.path=$launcher\",\n        s\"-Dtest.scala-cli.kind=$cliKind\"\n      )\n\n    private def debugTestArgs(args: Seq[String]): Seq[String] = {\n      val debugReg     = \"^--debug$|^--debug:([0-9]+)$\".r\n      val debugPortOpt = args.find(debugReg.matches).flatMap {\n        case debugReg(port) => Option(port).orElse(Some(\"5005\"))\n        case _              => None\n      }\n      debugPortOpt match {\n        case Some(port) =>\n          System.err.println(\n            s\"--debug option has been passed. Listening for transport dt_socket at address: $port\"\n          )\n          Seq(s\"-Dtest.scala-cli.debug.port=$port\")\n        case _ => Seq.empty\n      }\n    }\n\n    private def testArgs(args: Seq[String], launcher: os.Path, cliKind: String): Seq[String] =\n      extraTestArgs(launcher, cliKind) ++ debugTestArgs(args)\n\n    def jvm(args: String*): Command[(msg: String, results: Seq[TestResult])] = Task.Command {\n      testTask(\n        Task.Anon(args ++ testArgs(args, Launchers.jvm().path, \"jvm\")),\n        Task.Anon(Seq.empty[String])\n      )()\n    }\n\n    def jvmBootstrapped(args: String*): Command[(msg: String, results: Seq[TestResult])] =\n      Task.Command {\n        testTask(\n          Task.Anon(args ++ testArgs(args, Launchers.jvmBootstrapped().path, \"jvmBootstrapped\")),\n          Task.Anon(Seq.empty[String])\n        )()\n      }\n\n    def native(args: String*): Command[(msg: String, results: Seq[TestResult])] = Task.Command {\n      testTask(\n        Task.Anon(args ++ testArgs(args, Launchers.native().path, \"native\")),\n        Task.Anon(Seq.empty[String])\n      )()\n    }\n\n    def nativeStatic(args: String*): Command[(msg: String, results: Seq[TestResult])] =\n      Task.Command {\n        testTask(\n          Task.Anon(args ++ testArgs(args, Launchers.nativeStatic().path, \"native-static\")),\n          Task.Anon(Seq.empty[String])\n        )()\n      }\n\n    def nativeMostlyStatic(args: String*): Command[(msg: String, results: Seq[TestResult])] =\n      Task.Command {\n        testTask(\n          Task.Anon(args ++ testArgs(\n            args,\n            Launchers.nativeMostlyStatic().path,\n            \"native-mostly-static\"\n          )),\n          Task.Anon(Seq.empty[String])\n        )()\n      }\n  }\n}\n\ntrait CliIntegrationDocker extends SbtModule with ScalaCliPublishModule with HasTests {\n  override def scalaVersion: T[String] = Scala.scala3Lts\n  override def mvnDeps: T[Seq[Dep]]    = super.mvnDeps() ++ Seq(\n    Deps.osLib\n  )\n}\n\ntrait Runner extends CrossSbtModule\n    with ScalaCliPublishModule\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ Seq(\"-deprecation\")\n  }\n  override def mainClass: T[Option[String]] = Some(\"scala.cli.runner.Runner\")\n  override def sources: T[Seq[PathRef]]     = {\n    val scala3DirName =\n      if (crossScalaVersion.contains(\"-RC\")) \"scala-3-unstable\" else \"scala-3-stable\"\n    val extraDirs = Seq(PathRef(moduleDir / \"src\" / \"main\" / scala3DirName))\n    super.sources() ++ extraDirs\n  }\n}\n\ntrait TestRunner extends CrossSbtModule\n    with ScalaCliPublishModule\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def scalacOptions: T[Seq[String]] = Task {\n    super.scalacOptions() ++ Seq(\"-deprecation\")\n  }\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.asm,\n    Deps.collectionCompat,\n    Deps.testInterface\n  )\n  override def mainClass: T[Option[String]] = Some(\"scala.build.testrunner.DynamicTestRunner\")\n}\n\ntrait JavaTestRunner extends JavaModule\n    with ScalaCliPublishModule\n    with LocatedInModules {\n  override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n    Deps.asm,\n    Deps.testInterface\n  )\n  override def mainClass: T[Option[String]] = Some(\"scala.build.testrunner.JavaDynamicTestRunner\")\n}\n\ntrait TastyLib extends ScalaCliCrossSbtModule\n    with ScalaCliPublishModule\n    with ScalaCliScalafixModule\n    with LocatedInModules {\n  override def crossScalaVersion: String = crossValue\n  def constantsFile: T[PathRef]          = Task(persistent = true) {\n    val dir  = Task.dest / \"constants\"\n    val dest = dir / \"Constants.scala\"\n    val code =\n      s\"\"\"package scala.build.tastylib.internal\n         |\n         |/** Build-time constants. Generated by mill. */\n         |object Constants {\n         |  def defaultScalaVersion = \"${Scala.defaultUser}\"\n         |}\n         |\"\"\".stripMargin\n    if (!os.isFile(dest) || os.read(dest) != code)\n      os.write.over(dest, code, createFolders = true)\n    PathRef(dir)\n  }\n\n  override def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile())\n}\n\nobject `local-repo` extends LocalRepo {\n\n  /*\n   * If you are developing locally on any of the stub modules (stubs, runner, test-runner),\n   * set this to true, so that Mill's watch mode takes into account changes in those modules\n   * when embedding their JARs in the Scala CLI launcher.\n   */\n  def developingOnStubModules = false\n\n  override def stubsModules: Seq[PublishLocalNoFluff] =\n    Seq(runner(Scala.runnerScala3), `test-runner`(Scala.runnerScala3), `java-test-runner`)\n\n  override def version: T[String] = runner(Scala.runnerScala3).publishVersion()\n}\n\n// Helper CI commands\ndef publishSonatype(tasks: Tasks[PublishModule.PublishData]) = Task.Command {\n  val taskNames = tasks.value.map(_.toString())\n  System.err.println(\n    s\"\"\"Tasks producing artifacts to be included in the bundle:\n       |  ${taskNames.mkString(\"\\n  \")}\"\"\".stripMargin\n  )\n  val pv = finalPublishVersion()\n  System.err.println(s\"Publish version: $pv\")\n  val bundleName = s\"$organization-$ghName-$pv\"\n  System.err.println(s\"Publishing bundle: $bundleName\")\n  publish.publishSonatype(\n    data = Task.sequence(tasks.value)(),\n    log = Task.ctx().log,\n    workspace = BuildCtx.workspaceRoot,\n    env = Task.env,\n    bundleName = bundleName\n  )\n}\n\ndef copyTo(task: Tasks[PathRef], dest: String): Command[Unit] = Task.Command {\n  val destPath = os.Path(dest, BuildCtx.workspaceRoot)\n  if (task.value.length > 1)\n    sys.error(\"Expected a single task\")\n  val ref = task.value.head()\n  os.makeDir.all(destPath / os.up)\n  os.copy.over(ref.path, destPath)\n}\n\ndef writePackageVersionTo(dest: String): Command[Unit] = Task.Command {\n  val destPath   = os.Path(dest, BuildCtx.workspaceRoot)\n  val rawVersion = cli(Scala.defaultInternal).publishVersion()\n  val version    =\n    if (rawVersion.contains(\"+\")) rawVersion.stripSuffix(\"-SNAPSHOT\")\n    else rawVersion\n  os.write.over(destPath, version)\n}\n\ndef writeShortPackageVersionTo(dest: String): Command[Unit] = Task.Command {\n  val destPath   = os.Path(dest, BuildCtx.workspaceRoot)\n  val rawVersion = cli(Scala.defaultInternal).publishVersion()\n  val version    = rawVersion.takeWhile(c => c != '-' && c != '+')\n  os.write.over(destPath, version)\n}\n\ndef importedLauncher(directory: String = \"artifacts\", workspace: os.Path): Array[Byte] = {\n  val ext  = if (Properties.isWin) \".zip\" else \".gz\"\n  val from = os.Path(directory, workspace) / s\"scala-cli-${Upload.platformSuffix}$ext\"\n  System.err.println(s\"Importing launcher from $from\")\n  if (!os.exists(from))\n    sys.error(s\"$from not found\")\n\n  if (Properties.isWin) {\n    import java.util.zip.ZipFile\n    Using.resource(new ZipFile(from.toIO)) { zf =>\n      val ent = zf.getEntry(\"scala-cli.exe\")\n      Using.resource(zf.getInputStream(ent)) { is =>\n        is.readAllBytes()\n      }\n    }\n  }\n  else {\n    import java.io.ByteArrayInputStream\n    import java.util.zip.GZIPInputStream\n\n    val compressed = os.read.bytes(from)\n    val bais       = new ByteArrayInputStream(compressed)\n    Using.resource(new GZIPInputStream(bais)) { gzis =>\n      gzis.readAllBytes()\n    }\n  }\n}\n\ndef copyLauncher(directory: String = \"artifacts\"): Command[os.Path] = Task.Command {\n  val nativeLauncher = cli(Scala.defaultInternal).nativeImage().path\n  Upload.copyLauncher0(\n    nativeLauncher = nativeLauncher,\n    directory = directory,\n    name = \"scala-cli\",\n    compress = true,\n    workspace = BuildCtx.workspaceRoot\n  )\n}\n\ndef copyJvmLauncher(directory: String = \"artifacts\"): Command[Unit] = Task.Command {\n  val launcher = cli(Scala.defaultInternal).standaloneLauncher().path\n  os.copy(\n    launcher,\n    os.Path(directory, BuildCtx.workspaceRoot) / s\"scala-cli$platformExecutableJarExtension\",\n    createFolders = true,\n    replaceExisting = true\n  )\n}\ndef copyJvmBootstrappedLauncher(directory: String = \"artifacts\"): Command[Unit] = Task.Command {\n  val launcher = cliBootstrapped.jar().path\n  os.copy(\n    launcher,\n    os.Path(directory, BuildCtx.workspaceRoot) / s\"scala-cli.jar\",\n    createFolders = true,\n    replaceExisting = true\n  )\n}\n\ndef uploadLaunchers(directory: String = \"artifacts\"): Command[Unit] = Task.Command {\n  val version = cli(Scala.defaultInternal).publishVersion()\n\n  val path      = os.Path(directory, BuildCtx.workspaceRoot)\n  val launchers = os.list(path).filter(os.isFile(_)).map { path =>\n    path -> path.last\n  }\n  val (tag, overwriteAssets) =\n    if (version.endsWith(\"-SNAPSHOT\")) (\"nightly\", true)\n    else (\"v\" + version, false)\n  System.err.println(s\"Uploading to tag $tag (overwrite assets: $overwriteAssets)\")\n  Upload.upload(ghOrg, ghName, ghToken(), tag, dryRun = false, overwrite = overwriteAssets)(\n    launchers*\n  )\n}\n\ndef unitTests(): Command[(msg: String, results: Seq[TestResult])] = Task.Command {\n  `build-module`(Scala.defaultInternal).test.testForked()()\n  `build-macros`(Scala.defaultInternal).test.testForked()()\n  cli(Scala.defaultInternal).test.testForked()()\n  directives(Scala.defaultInternal).test.testForked()()\n  options(Scala.defaultInternal).test.testForked()()\n}\n\ndef scala(args: Task[Args] = Task.Anon(Args())) = Task.Command {\n  cli(Scala.defaultInternal).run(args)()\n}\n\ndef debug(port: Int, args: Task[Args] = Task.Anon(Args())) = Task.Command {\n  cli(Scala.defaultInternal).debug(port, args)()\n}\n\ndef defaultNativeImage(): Command[PathRef] =\n  Task.Command {\n    cli(Scala.defaultInternal).nativeImage()\n  }\n\ndef nativeIntegrationTests(): Command[(msg: String, results: Seq[TestResult])] =\n  integration.test.native()\n\ndef copyDefaultLauncher(directory: String = \"artifacts\"): Command[os.Path] =\n  Task.Command {\n    copyLauncher(directory)()\n  }\n\ndef copyMostlyStaticLauncher(directory: String = \"artifacts\"): Command[os.Path] = Task.Command {\n  val nativeLauncher = cli(Scala.defaultInternal).nativeImageMostlyStatic().path\n  Upload.copyLauncher0(\n    nativeLauncher = nativeLauncher,\n    directory = directory,\n    name = \"scala-cli\",\n    compress = true,\n    workspace = BuildCtx.workspaceRoot,\n    suffix = \"-mostly-static\"\n  )\n}\n\ndef copyStaticLauncher(directory: String = \"artifacts\"): Command[os.Path] = Task.Command {\n  val nativeLauncher = cli(Scala.defaultInternal).nativeImageStatic().path\n  Upload.copyLauncher0(\n    nativeLauncher = nativeLauncher,\n    directory = directory,\n    name = \"scala-cli\",\n    compress = true,\n    workspace = BuildCtx.workspaceRoot,\n    suffix = \"-static\"\n  )\n}\nprivate def ghToken(): String = Option(System.getenv(\"UPLOAD_GH_TOKEN\")).getOrElse {\n  sys.error(\"UPLOAD_GH_TOKEN not set\")\n}\nprivate def gitClone(repo: String, branch: String, workDir: os.Path): os.CommandResult =\n  os.proc(\"git\", \"clone\", repo, \"-q\", \"-b\", branch).call(cwd = workDir)\nprivate def setupGithubRepo(repoDir: os.Path): os.CommandResult = {\n  val gitUserName = \"gh-actions\"\n  val gitEmail    = \"actions@github.com\"\n\n  os.proc(\"git\", \"config\", \"user.name\", gitUserName).call(cwd = repoDir)\n  os.proc(\"git\", \"config\", \"user.email\", gitEmail).call(cwd = repoDir)\n}\n\nprivate def commitChanges(\n  name: String,\n  branch: String,\n  repoDir: os.Path,\n  force: Boolean = false\n): Unit = {\n  if (os.proc(\"git\", \"status\").call(cwd = repoDir).out.trim().contains(\"nothing to commit\"))\n    println(\"Nothing Changes\")\n  else {\n    os.proc(\"git\", \"add\", \"-A\").call(cwd = repoDir)\n    os.proc(\"git\", \"commit\", \"-am\", name).call(cwd = repoDir)\n    println(s\"Trying to push on $branch branch\")\n    val pushExtraOptions = if (force) Seq(\"--force\") else Seq.empty\n    os.proc(\"git\", \"push\", \"origin\", branch, pushExtraOptions).call(cwd = repoDir)\n    println(s\"Push successfully on $branch branch\")\n  }\n}\n\n// TODO Move most CI-specific tasks there\nobject ci extends Module {\n  def publishVersion(): Command[Unit] = Task.Command {\n    println(cli(Scala.defaultInternal).publishVersion())\n  }\n  def updateScalaCliSetup(): Command[Unit] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir       = BuildCtx.workspaceRoot / \"target-scala-cli-setup\"\n    val mainDir         = targetDir / \"scala-cli-setup\"\n    val setupScriptPath = mainDir / \"src\" / \"main.ts\"\n\n    // clean target directory\n    if (os.exists(targetDir)) os.remove.all(targetDir)\n\n    os.makeDir.all(targetDir)\n\n    val branch       = \"main\"\n    val targetBranch = s\"update-scala-cli-setup\"\n    val repo         = \"git@github.com:VirtusLab/scala-cli-setup.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(mainDir)\n\n    val setupScript            = os.read(setupScriptPath)\n    val scalaCliVersionRegex   = \"const scalaCLIVersion = '.*'\".r\n    val updatedSetupScriptPath =\n      scalaCliVersionRegex.replaceFirstIn(setupScript, s\"const scalaCLIVersion = '$version'\")\n    os.write.over(setupScriptPath, updatedSetupScriptPath)\n\n    os.proc(\"git\", \"switch\", \"-c\", targetBranch).call(cwd = mainDir)\n    commitChanges(s\"Update scala-cli version to $version\", targetBranch, mainDir, force = true)\n  }\n  def updateStandaloneLauncher(): Command[os.CommandResult] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir                     = BuildCtx.workspaceRoot / \"target\"\n    val scalaCliDir                   = targetDir / \"scala-cli\"\n    val standaloneLauncherPath        = scalaCliDir / \"scala-cli.sh\"\n    val standaloneWindowsLauncherPath = scalaCliDir / \"scala-cli.bat\"\n\n    // clean scala-cli directory\n    if (os.exists(scalaCliDir)) os.remove.all(scalaCliDir)\n    if (!os.exists(targetDir)) os.makeDir.all(targetDir)\n\n    val branch       = \"main\"\n    val targetBranch = s\"update-standalone-launcher-$version\"\n    val repo         = s\"https://oauth2:${ghToken()}@github.com/$ghOrg/$ghName.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(scalaCliDir)\n\n    val launcherScript        = os.read(standaloneLauncherPath)\n    val scalaCliVersionRegex  = \"SCALA_CLI_VERSION=\\\".*\\\"\".r\n    val updatedLauncherScript =\n      scalaCliVersionRegex.replaceFirstIn(launcherScript, s\"\"\"SCALA_CLI_VERSION=\"$version\"\"\"\")\n    os.write.over(standaloneLauncherPath, updatedLauncherScript)\n\n    val launcherWindowsScript        = os.read(standaloneWindowsLauncherPath)\n    val scalaCliWindowsVersionRegex  = \"SCALA_CLI_VERSION=.*\\\"\".r\n    val updatedWindowsLauncherScript =\n      scalaCliWindowsVersionRegex.replaceFirstIn(\n        launcherWindowsScript,\n        s\"\"\"SCALA_CLI_VERSION=$version\"\"\"\"\n      )\n    os.write.over(standaloneWindowsLauncherPath, updatedWindowsLauncherScript)\n\n    os.proc(\"git\", \"switch\", \"-c\", targetBranch).call(cwd = scalaCliDir)\n    commitChanges(s\"Update scala-cli.sh launcher for $version\", targetBranch, scalaCliDir)\n    os.proc(\"gh\", \"auth\", \"login\", \"--with-token\").call(cwd = scalaCliDir, stdin = ghToken())\n    os.proc(\"gh\", \"pr\", \"create\", \"--fill\", \"--base\", \"main\", \"--head\", targetBranch)\n      .call(cwd = scalaCliDir)\n  }\n  def brewLaunchersSha(\n    x86LauncherPath: os.Path,\n    arm64LauncherPath: os.Path,\n    targetDir: os.Path\n  ): (String, String) = {\n    val x86BinarySha256 = os.proc(\"openssl\", \"dgst\", \"-sha256\", \"-binary\")\n      .call(\n        cwd = targetDir,\n        stdin = os.read.stream(x86LauncherPath)\n      ).out.bytes\n    val arm64BinarySha256 = os.proc(\"openssl\", \"dgst\", \"-sha256\", \"-binary\")\n      .call(\n        cwd = targetDir,\n        stdin = os.read.stream(arm64LauncherPath)\n      ).out.bytes\n    val x86Sha256 = os.proc(\"xxd\", \"-p\", \"-c\", \"256\")\n      .call(\n        cwd = targetDir,\n        stdin = x86BinarySha256\n      ).out.trim()\n    val arm64Sha256 = os.proc(\"xxd\", \"-p\", \"-c\", \"256\")\n      .call(\n        cwd = targetDir,\n        stdin = arm64BinarySha256\n      ).out.trim()\n\n    (x86Sha256, arm64Sha256)\n  }\n  def updateScalaCliBrewFormula(): Command[Unit] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir          = BuildCtx.workspaceRoot / \"target\"\n    val homebrewFormulaDir = targetDir / \"homebrew-scala-cli\"\n\n    // clean target directory\n    if (os.exists(targetDir)) os.remove.all(targetDir)\n\n    os.makeDir.all(targetDir)\n\n    val branch = \"main\"\n    val repo   = s\"git@github.com:Virtuslab/homebrew-scala-cli.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(homebrewFormulaDir)\n\n    val x86LauncherURL =\n      s\"https://github.com/Virtuslab/scala-cli/releases/download/v$version/scala-cli-x86_64-apple-darwin.gz\"\n    val arm64LauncherURL =\n      s\"https://github.com/Virtuslab/scala-cli/releases/download/v$version/scala-cli-aarch64-apple-darwin.gz\"\n\n    val x86LauncherPath =\n      os.Path(\"artifacts\", BuildCtx.workspaceRoot) / \"scala-cli-x86_64-apple-darwin.gz\"\n    val arm64LauncherPath =\n      os.Path(\"artifacts\", BuildCtx.workspaceRoot) / \"scala-cli-aarch64-apple-darwin.gz\"\n    val (x86Sha256, arm64Sha256) = brewLaunchersSha(x86LauncherPath, arm64LauncherPath, targetDir)\n\n    val templateFormulaPath =\n      BuildCtx.workspaceRoot / \".github\" / \"scripts\" / \"scala-cli.rb.template\"\n    val template = os.read(templateFormulaPath)\n\n    val updatedFormula = template\n      .replace(\"@X86_LAUNCHER_URL@\", x86LauncherURL)\n      .replace(\"@ARM64_LAUNCHER_URL@\", arm64LauncherURL)\n      .replace(\"@X86_LAUNCHER_SHA256@\", x86Sha256)\n      .replace(\"@ARM64_LAUNCHER_SHA256@\", arm64Sha256)\n      .replace(\"@LAUNCHER_VERSION@\", version)\n\n    val formulaPath = homebrewFormulaDir / \"scala-cli.rb\"\n    os.write.over(formulaPath, updatedFormula)\n\n    commitChanges(s\"Update for $version\", branch, homebrewFormulaDir)\n  }\n  def updateScalaExperimentalBrewFormula(): Command[Unit] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir          = BuildCtx.workspaceRoot / \"target\"\n    val homebrewFormulaDir = targetDir / \"homebrew-scala-experimental\"\n\n    // clean homebrew-scala-experimental directory\n    if (os.exists(homebrewFormulaDir)) os.remove.all(homebrewFormulaDir)\n\n    os.makeDir.all(targetDir)\n\n    val branch = \"main\"\n    val repo   = s\"git@github.com:VirtusLab/homebrew-scala-experimental.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(homebrewFormulaDir)\n\n    val x86LauncherURL =\n      s\"https://github.com/Virtuslab/scala-cli/releases/download/v$version/scala-cli-x86_64-apple-darwin.gz\"\n    val arm64LauncherURL =\n      s\"https://github.com/Virtuslab/scala-cli/releases/download/v$version/scala-cli-aarch64-apple-darwin.gz\"\n\n    val x86LauncherPath =\n      os.Path(\"artifacts\", BuildCtx.workspaceRoot) / \"scala-cli-x86_64-apple-darwin.gz\"\n    val arm64LauncherPath =\n      os.Path(\"artifacts\", BuildCtx.workspaceRoot) / \"scala-cli-aarch64-apple-darwin.gz\"\n    val (x86Sha256, arm64Sha256) = brewLaunchersSha(x86LauncherPath, arm64LauncherPath, targetDir)\n\n    val templateFormulaPath = BuildCtx.workspaceRoot / \".github\" / \"scripts\" / \"scala.rb.template\"\n    val template            = os.read(templateFormulaPath)\n\n    val updatedFormula = template\n      .replace(\"@X86_LAUNCHER_URL@\", x86LauncherURL)\n      .replace(\"@ARM64_LAUNCHER_URL@\", arm64LauncherURL)\n      .replace(\"@X86_LAUNCHER_SHA256@\", x86Sha256)\n      .replace(\"@ARM64_LAUNCHER_SHA256@\", arm64Sha256)\n      .replace(\"@LAUNCHER_VERSION@\", version)\n\n    val formulaPath = homebrewFormulaDir / \"scala.rb\"\n    os.write.over(formulaPath, updatedFormula)\n\n    commitChanges(s\"Update for $version\", branch, homebrewFormulaDir)\n  }\n  def updateInstallationScript(): Command[Unit] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir              = BuildCtx.workspaceRoot / \"target\"\n    val packagesDir            = targetDir / \"scala-cli-packages\"\n    val installationScriptPath = packagesDir / \"scala-setup.sh\"\n\n    // clean target directory\n    if (os.exists(targetDir)) os.remove.all(targetDir)\n\n    os.makeDir.all(targetDir)\n\n    val branch = \"main\"\n    val repo   = s\"git@github.com:Virtuslab/scala-cli-packages.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(packagesDir)\n\n    val installationScript        = os.read(installationScriptPath)\n    val scalaCliVersionRegex      = \"SCALA_CLI_VERSION=\\\".*\\\"\".r\n    val updatedInstallationScript =\n      scalaCliVersionRegex.replaceFirstIn(installationScript, s\"\"\"SCALA_CLI_VERSION=\"$version\"\"\"\")\n    os.write.over(installationScriptPath, updatedInstallationScript)\n\n    commitChanges(s\"Update installation script for $version\", branch, packagesDir)\n  }\n  def updateDebianPackages(): Command[Unit] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir   = BuildCtx.workspaceRoot / \"target\"\n    val packagesDir = targetDir / \"scala-cli-packages\"\n    val debianDir   = packagesDir / \"debian\"\n\n    // clean target directory\n    if (os.exists(targetDir)) os.remove.all(targetDir)\n\n    os.makeDir.all(targetDir)\n\n    val branch = \"main\"\n    val repo   = s\"git@github.com:Virtuslab/scala-cli-packages.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(packagesDir)\n\n    // copy deb package to repository\n    os.copy(\n      os.Path(\"artifacts\", BuildCtx.workspaceRoot) / \"scala-cli-x86_64-pc-linux.deb\",\n      debianDir / s\"scala-cli_$version.deb\"\n    )\n\n    val packagesPath = debianDir / \"Packages\"\n    os.proc(\"dpkg-scanpackages\", \"--multiversion\", \".\").call(cwd = debianDir, stdout = packagesPath)\n    os.proc(\"gzip\", \"-k\", \"-f\", \"Packages\").call(cwd = debianDir)\n\n    val releasePath = debianDir / \"Release\"\n    os.proc(\"apt-ftparchive\", \"release\", \".\").call(cwd = debianDir, stdout = releasePath)\n\n    val pgpPassphrase =\n      Option(System.getenv(\"PGP_PASSPHRASE\")).getOrElse(sys.error(\"PGP_PASSPHRASE not set\"))\n    val keyName = Option(System.getenv(\"GPG_EMAIL\")).getOrElse(sys.error(\"GPG_EMAIL not set\"))\n    val releaseGpgPath = debianDir / \"Release.gpg\"\n    val inReleasePath  = debianDir / \"InRelease\"\n    os.proc(\n      \"gpg\",\n      \"--batch\",\n      \"--yes\",\n      \"--passphrase-fd\",\n      \"0\",\n      \"--default-key\",\n      keyName,\n      \"-abs\",\n      \"-o\",\n      \"-\",\n      \"Release\"\n    )\n      .call(cwd = debianDir, stdin = pgpPassphrase, stdout = releaseGpgPath)\n    os.proc(\n      \"gpg\",\n      \"--batch\",\n      \"--yes\",\n      \"--passphrase-fd\",\n      \"0\",\n      \"--default-key\",\n      keyName,\n      \"--clearsign\",\n      \"-o\",\n      \"-\",\n      \"Release\"\n    )\n      .call(cwd = debianDir, stdin = pgpPassphrase, stdout = inReleasePath)\n\n    // Export the public key as a binary (non-armored) keyring at the repo root so that\n    // users can reference it via `signed-by` in their APT sources.\n    // Binary format is required per https://wiki.debian.org/DebianRepository/UseThirdParty\n    val keyringPath = packagesDir / \"scala-cli-archive-keyring.gpg\"\n    os.proc(\"gpg\", \"--batch\", \"--yes\", \"--export\", keyName)\n      .call(stdout = keyringPath)\n\n    // Update the .list file to include the signed-by option pointing at the keyring.\n    // This scopes the key to this repository only, preventing the globally-trusted key\n    // security issue described in the Debian wiki.\n    os.write.over(\n      debianDir / \"scala_cli_packages.list\",\n      \"deb [signed-by=/etc/apt/keyrings/scala-cli-archive-keyring.gpg] https://virtuslab.github.io/scala-cli-packages/debian ./\\n\"\n    )\n\n    // Also provide a DEB822 .sources file for users on modern Debian (apt modernize-sources).\n    // No Components field: this is a flat repository (Suites: ./).\n    // No Architectures field: avoids breaking when aarch64 packages are added later.\n    os.write.over(\n      debianDir / \"scala_cli_packages.sources\",\n      \"\"\"Types: deb\n        |URIs: https://virtuslab.github.io/scala-cli-packages/debian\n        |Suites: ./\n        |Signed-By: /etc/apt/keyrings/scala-cli-archive-keyring.gpg\n        |\"\"\".stripMargin\n    )\n\n    commitChanges(s\"Update Debian packages for $version\", branch, packagesDir)\n  }\n  def updateChocolateyPackage(): Command[os.CommandResult] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val packagesDir = BuildCtx.workspaceRoot / \"target\" / \"scala-cli-packages\"\n    val chocoDir    = BuildCtx.workspaceRoot / \".github\" / \"scripts\" / \"choco\"\n\n    val msiPackagePath = packagesDir / s\"scala-cli_$version.msi\"\n    os.copy(\n      BuildCtx.workspaceRoot / \"artifacts\" / \"scala-cli-x86_64-pc-win32.msi\",\n      msiPackagePath,\n      createFolders = true\n    )\n\n    // prepare ps1 file\n    val ps1Path = chocoDir / \"tools\" / \"chocolateyinstall.ps1\"\n\n    val launcherURL =\n      s\"https://github.com/VirtusLab/scala-cli/releases/download/v$version/scala-cli-x86_64-pc-win32.msi\"\n    val bytes  = os.read.stream(msiPackagePath)\n    val sha256 = os.proc(\"sha256sum\").call(cwd = packagesDir, stdin = bytes).out.trim().take(64)\n    val ps1UpdatedContent = os.read(ps1Path)\n      .replace(\"@LAUNCHER_URL@\", launcherURL)\n      .replace(\"@LAUNCHER_SHA256@\", sha256.toUpperCase)\n\n    os.write.over(ps1Path, ps1UpdatedContent)\n\n    // prepare nuspec file\n    val nuspecPath           = chocoDir / \"scala-cli.nuspec\"\n    val nuspecUpdatedContent = os.read(nuspecPath).replace(\"@LAUNCHER_VERSION@\", version)\n    os.write.over(nuspecPath, nuspecUpdatedContent)\n\n    os.proc(\"choco\", \"pack\", nuspecPath, \"--out\", chocoDir).call(cwd = chocoDir)\n    val chocoKey =\n      Option(System.getenv(\"CHOCO_SECRET\")).getOrElse(sys.error(\"CHOCO_SECRET not set\"))\n    os.proc(\n      \"choco\",\n      \"push\",\n      chocoDir / s\"scala-cli.$version.nupkg\",\n      \"-s\",\n      \"https://push.chocolatey.org/\",\n      \"-k\",\n      chocoKey\n    ).call(cwd = chocoDir)\n  }\n  def updateCentOsPackages(): Command[Unit] = Task.Command {\n    val version = cli(Scala.defaultInternal).publishVersion()\n\n    val targetDir   = BuildCtx.workspaceRoot / \"target\"\n    val packagesDir = targetDir / \"scala-cli-packages\"\n    val centOsDir   = packagesDir / \"CentOS\"\n\n    // clean target directory\n    if (os.exists(targetDir)) os.remove.all(targetDir)\n\n    os.makeDir.all(targetDir)\n\n    val branch = \"main\"\n    val repo   = s\"git@github.com:Virtuslab/scala-cli-packages.git\"\n\n    // Cloning\n    gitClone(repo, branch, targetDir)\n    setupGithubRepo(packagesDir)\n\n    // copy rpm package to repository\n    os.copy(\n      os.Path(\"artifacts\", BuildCtx.workspaceRoot) / \"scala-cli-x86_64-pc-linux.rpm\",\n      centOsDir / \"Packages\" / s\"scala-cli_$version.rpm\"\n    )\n\n    val cmd = Seq[os.Shellable](\n      \"docker\",\n      \"run\",\n      \"-v\",\n      s\"$packagesDir:/packages\",\n      \"-w\",\n      \"/packages\",\n      \"--env\",\n      \"PGP_SECRET\",\n      \"--env\",\n      \"PGP_PASSPHRASE\",\n      \"--env\",\n      \"GPG_EMAIL\",\n      \"--env\",\n      \"KEYGRIP\",\n      \"--privileged\",\n      \"fedora:40\",\n      \"sh\",\n      \"updateCentOsPackages.sh\"\n    )\n\n    os.proc(cmd).call(cwd = packagesDir)\n\n    commitChanges(s\"Update CentOS packages for $version\", branch, packagesDir)\n  }\n  private def vsBasePaths: Seq[os.Path] = Seq(\n    os.Path(\"C:\\\\Program Files\\\\Microsoft Visual Studio\"),\n    os.Path(\"C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\")\n  )\n  def copyVcRedist(\n    directory: String = \"artifacts\",\n    distName: String = \"vc_redist.x64.exe\"\n  ): Command[Unit] =\n    Task.Command {\n      def vcVersions        = Seq(\"2022\", \"2019\", \"2017\")\n      def vcEditions        = Seq(\"Enterprise\", \"Community\", \"BuildTools\")\n      def candidateBaseDirs =\n        for {\n          vsBasePath <- vsBasePaths\n          year       <- vcVersions\n          edition    <- vcEditions\n        } yield vsBasePath / year / edition / \"VC\" / \"Redist\" / \"MSVC\"\n      val baseDirs = candidateBaseDirs.filter(os.isDir(_))\n      if (baseDirs.isEmpty)\n        sys.error(\n          s\"No Visual Studio installation found, tried:\" + System.lineSeparator() +\n            candidateBaseDirs\n              .map(\"  \" + _)\n              .mkString(System.lineSeparator())\n        )\n      val orig = baseDirs\n        .iterator\n        .flatMap(os.list(_).iterator)\n        .filter(os.isDir(_))\n        .map(_ / distName)\n        .filter(os.isFile(_))\n        .take(1)\n        .toList\n        .headOption\n        .getOrElse {\n          sys.error(\n            s\"Error: $distName not found under any of:\" + System.lineSeparator() +\n              baseDirs\n                .map(\"  \" + _)\n                .mkString(System.lineSeparator())\n          )\n        }\n      val destDir = os.Path(directory, BuildCtx.workspaceRoot)\n      os.copy(orig, destDir / distName, createFolders = true, replaceExisting = true)\n    }\n  def writeWixConfigExtra(dest: String = \"wix-visual-cpp-redist.xml\"): Command[Unit] =\n    Task.Command {\n      val msmPath = {\n\n        val vcVersions = Seq(\"2022\", \"2019\", \"2017\")\n        val vcEditions = Seq(\"Enterprise\", \"Community\", \"BuildTools\")\n        val vsDirs     = Seq(\n          os.Path(\"\"\"C:\\Program Files\\Microsoft Visual Studio\"\"\"),\n          os.Path(\"\"\"C:\\Program Files (x86)\\Microsoft Visual Studio\"\"\")\n        )\n        val fileNamePrefix                  = \"Microsoft_VC\".toLowerCase(Locale.ROOT)\n        val fileNameSuffix                  = \"_CRT_x64.msm\".toLowerCase(Locale.ROOT)\n        def candidatesIt: Iterator[os.Path] =\n          for {\n            vsDir   <- vsDirs.iterator\n            version <- vcVersions.iterator\n            edition <- vcEditions.iterator\n            dir = vsDir / version / edition\n            if os.isDir(dir)\n            path <- os.walk.stream(dir)\n              .filter { p =>\n                p.last.toLowerCase(Locale.ROOT).startsWith(fileNamePrefix) &&\n                p.last.toLowerCase(Locale.ROOT).endsWith(fileNameSuffix)\n              }\n              .filter(os.isFile(_))\n              .toVector\n              .iterator\n          } yield path\n\n        candidatesIt.take(1).toList.headOption.getOrElse {\n          sys.error(s\"$fileNamePrefix*$fileNameSuffix not found\")\n        }\n      }\n      val content =\n        s\"\"\"<DirectoryRef Id=\"TARGETDIR\">\n           |  <Merge Id=\"VCRedist\" SourceFile=\"$msmPath\" DiskId=\"1\" Language=\"0\"/>\n           |</DirectoryRef>\n           |<Feature Id=\"VCRedist\" Title=\"Visual C++ Redistributable\" AllowAdvertise=\"no\" Display=\"hidden\" Level=\"1\">\n           |  <MergeRef Id=\"VCRedist\"/>\n           |</Feature>\n           |\"\"\".stripMargin\n      val dest0 = os.Path(dest, BuildCtx.workspaceRoot)\n      os.write.over(dest0, content.getBytes(Charset.defaultCharset()), createFolders = true)\n    }\n  def setShouldPublish(): Command[Unit] = publish.setShouldPublish()\n  def shouldPublish(): Command[Unit]    = Task.Command {\n    println(publish.shouldPublish())\n  }\n  def copyJvm(jvm: String = deps.graalVmJvmId, dest: String = \"jvm\"): Command[os.Path] =\n    Task.Command {\n      import sys.process._\n      val command = Seq(\n        settings.cs(),\n        \"java-home\",\n        \"--jvm\",\n        jvm,\n        \"--update\",\n        \"--ttl\",\n        \"0\"\n      )\n      val baseJavaHome = os.Path(command.!!.trim, BuildCtx.workspaceRoot)\n      System.err.println(s\"Initial Java home $baseJavaHome\")\n      val destJavaHome = os.Path(dest, BuildCtx.workspaceRoot)\n      os.copy(baseJavaHome, destJavaHome, createFolders = true)\n      System.err.println(s\"New Java home $destJavaHome\")\n      destJavaHome\n    }\n\n  def checkScalaVersions(): Command[Unit] = Task.Command {\n    website.checkMainScalaVersions(\n      BuildCtx.workspaceRoot / \"website\" / \"docs\" / \"reference\" / \"scala-versions.md\"\n    )\n    website.checkScalaJsVersions(\n      BuildCtx.workspaceRoot / \"website\" / \"docs\" / \"guides\" / \"advanced\" / \"scala-js.md\"\n    )\n  }\n}\n"
  },
  {
    "path": "gcbenchmark/.gitignore",
    "content": "tmp-*\n"
  },
  {
    "path": "gcbenchmark/README.md",
    "content": "Simple tool to analyze memory usage of bloop running with certain JVM options\n"
  },
  {
    "path": "gcbenchmark/gcbenchmark.scala",
    "content": "//> using dep com.lihaoyi::os-lib:0.9.1\n//> using dep com.lihaoyi::pprint:0.9.6\n//> using scala 2.13\n\n// Usage: scala-cli gcbenchmark.scala -- <path_to_scala_cli_executable>\n\nimport scala.concurrent.duration._\nimport scala.collection.JavaConverters._\n\ncase class Result(\n  env: Map[String, String],\n  maxTime: Duration,\n  avgTime: Duration,\n  maxMemoryFootprintMb: Int,\n  idleMemoryFootprintMb: Int\n)\n\nobject Main {\n  val workspace =\n    os.temp.dir(os.pwd, \"tmp-\", deleteOnExit = true) // where the temporary files are stored\n  val projectSize =\n    200 // Number of files in a generated project used in benchmark\n  val numberOfBuilds = 10 // How many times run build for each setup\n  val idleWait       =\n    90 // In seconds. Wait after builds are done, to measure how much memory JVM returns to OS\n\n  val setups = Seq(\n    Map(\"BLOOP_JAVA_OPTS\" -> \"-XX:+UseParallelGC\"),\n    Map(\n      \"BLOOP_JAVA_OPTS\" ->\n        \"-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahUncommitDelay=30000\"\n    ),\n    Map.empty[String, String]\n  )\n\n  def bloopMemory(bloopPid: Int) =\n    os\n      .proc(\"ps\", \"-o\", \"rss\", bloopPid)\n      .call()\n      .out\n      .text()\n      .linesIterator\n      .toList(1)\n      .toInt / 1024\n\n  def bloopPid: Option[Int] = {\n    val processes = os.proc(\"jps\", \"-l\").call().out.text()\n    \"(\\\\d+) bloop[.]BloopServer\".r\n      .findFirstMatchIn(processes)\n      .map(_.group(1).toInt)\n  }\n  def scalaFile(objectName: String, rand: Int) = s\"\"\"\n                                                    |object $objectName {\n                                                    |def donothing(i:Int) = {}\n                                                    |  donothing($rand)\n                                                    |${\"  donothing(1+1)\\n\" * 1000}\n                                                    |}\n                                                    |\"\"\".stripMargin\n\n  def build(scalaCli: String, rand: Int, env: Map[String, String]): Duration = {\n    val classes = (1 to projectSize).map(i => s\"Bench$i\")\n    for { c <- classes } os.write.over(\n      workspace / s\"$c.scala\",\n      scalaFile(s\"$c\", 1000 + rand)\n    )\n    val start = System.nanoTime()\n    os.proc(\n      \"java\",\n      \"-jar\",\n      scalaCli,\n      \"compile\",\n      workspace\n    ).call(cwd = workspace, env = env, stdout = os.Inherit)\n    val stop    = System.nanoTime()\n    val elapsed = 1.0 * (stop - start) / 1000000000\n    elapsed.seconds\n  }\n\n  def main(args: Array[String]): Unit = {\n    val scalaCli = pprint.log(args(0))\n    os.proc(\"java\", \"-version\").call(stdout = os.Inherit)\n    val results = for { env <- setups } yield {\n      pprint.log(env)\n      bloopPid.foreach(p => os.proc(\"kill\", p).call(stderr = os.Inherit))\n      Thread.sleep(3000)\n      println(\"=\" * 80)\n      build(\n        scalaCli,\n        0,\n        env ++ System.getenv.asScala\n      )\n      val buildResults = (1 to numberOfBuilds).map { i =>\n        val elapsed = build(scalaCli, i, env ++ System.getenv.asScala)\n        val memory  = bloopMemory(bloopPid.get)\n        pprint.log(f\"$memory MB, $elapsed\")\n        (memory, elapsed)\n      }\n      val idleResults = for { i <- 1 to idleWait } yield {\n        Thread.sleep(1000)\n        val memory = bloopMemory(bloopPid.get)\n        pprint.log(s\"$memory MB\")\n        memory\n      }\n      val res = Result(\n        env = env,\n        maxTime = buildResults.map(_._2).max.toSeconds.seconds,\n        avgTime =\n          (buildResults\n            .map(_._2)\n            .fold(0.seconds)(_ + _) / buildResults.size).toSeconds.seconds,\n        maxMemoryFootprintMb = buildResults.map(_._1).max,\n        idleMemoryFootprintMb = idleResults.min\n      )\n      res\n    }\n    println(\"=\" * 80)\n    pprint.log(results)\n  }\n}\n"
  },
  {
    "path": "gifs/Dockerfile",
    "content": "FROM ubuntu:24.04\n\nRUN curl -fsSL https://deb.nodesource.com/setup_12.x | bash -\nRUN apt-get update\nRUN apt-get install ca-certificates-java openjdk-17-jdk openjdk-17-jre -y\nRUN DEBIAN_FRONTEND=noninteractive apt-get install -y pv curl clang rubygems nodejs python3-pip &&\\\n    rm -rf /var/lib/apt/lists/* &&\\\n    gem install rouge &&\\\n    pip3 install --break-system-packages asciinema==2.1.0\n\nRUN mkdir /data\nWORKDIR /data\n\nCOPY scala-cli-jvm /usr/bin/scala-cli\n# Preload scala-cli\nRUN cd /data\nRUN echo 'def a = 123' > a.scala\nRUN scala-cli compile a.scala || echo \"Problems with bloop\"\n\nCOPY *.sh /data/\nCOPY scenarios /data/scenarios\n\nENTRYPOINT ./run_scenario.sh \"$1\""
  },
  {
    "path": "gifs/README.md",
    "content": "# Tooling to generate nice gifs used in documentation\n\nRecordings are possible using https://github.com/paxtonhare/demo-magic licensed under MIT Licence\n\n## How does it work?\n\nOur animated svgs are compose of scenarios built using [demo-magic](https://github.com/paxtonhare/demo-magic) and then recorded using [asciinema](https://asciinema.org/) to .json files to be finally rendered to animated svg files using [svg_rendrer_cli](https://github.com/marionebl/svg-term-cli)\n\n## How to (re)create new gif\n\n1. Copy example.sh into scenarios directory (e.g. `better-error.sh`)\n2. Edit its content based on included tips.\n3. Run `./mill -i docs-tests.test \"sclicheck.GifTests.better-error\"` to to (re)render svgs based on `better-error.sh` scenario\n\nGifs will be saved in `website/static/img/gifs` and `website/static/img/dark/gifs` directories based on name of the scenario (so `foo.sh` becomes `foo.svg`)\n"
  },
  {
    "path": "gifs/create_missing.sc",
    "content": "#!/usr/bin/env scala-cli\n\n//> using lib com.lihaoyi::os-lib:0.7.8\n\n/** Small and handy script to generate stubs for .svg files with nice TODO\n  */\n\nval content                = os.read(os.pwd / \"website\" / \"src\" / \"components\" / \"features.js\")\nval Image                  = \"\"\".*image=\"([^ ]+)\" +(title=\"(.+)\")?.*\"\"\".r\ndef needStub(name: String) =\n  !os.exists(os.pwd / \"website\" / \"static\" / \"img\" / name) && name.endsWith(\".svg\")\n\n// Look for missing .svg files in out feature list\nval stubs = content.linesIterator.collect {\n  case Image(image, _, title) if needStub(image) =>\n    image -> s\"showing $title\"\n  case Image(image) if needStub(image) =>\n    image -> \"\"\n}.toList\n\n// or provide data manually\n// val stubs = Seq(\n//   (\"education\", \"Scala CLI within scope of learning Scala\"),\n//   (\"scripting\", \"scripting with Scala CLI\"),\n//   (\"prototyping\", \"Scala CLI used to prototype, experiment and debug\"),\n//   (\"projects\", \"Scala CLI to manage single-module projects\"),\n//   (\"demo\", \"general demo of Scala CLI\"),\n// )\n\nif stubs.nonEmpty then\n  val scriptBase = os.read(os.pwd / \"gifs\" / \"example.sh\")\n  stubs.foreach { case (imageName, desc) =>\n    val scriptName = imageName.stripSuffix(\".svg\") + \".sh\"\n    val dest       = os.pwd / \"gifs\" / \"scenarios\" / scriptName\n    val fullDescr  = s\"TODO: turn gifs/scenarios/$scriptName into proper scenario $desc\"\n    os.write.over(dest, scriptBase.replace(\"<description>\", fullDescr))\n    os.perms.set(dest, \"rwxr-xr-x\")\n    println(s\"Wrote: $dest\")\n  }\n  val names = stubs.map(_._1.stripSuffix(\".svg\")).mkString(\" \")\n  println(s\"To generate svg files run: 'gifs/generate_gif.sh $names'\")\n"
  },
  {
    "path": "gifs/demo-magic.sh",
    "content": "#!/usr/bin/env bash\n\n###############################################################################\n#\n# demo-magic.sh\n#\n# Copyright (c) 2015 Paxton Hare\n#\n# This script lets you script demos in bash. It runs through your demo script when you press\n# ENTER. It simulates typing and runs commands.\n#\n###############################################################################\n\n# the speed to \"type\" the text\nTYPE_SPEED=20\n\n# no wait after \"p\" or \"pe\"\nNO_WAIT=false\n\n# if > 0, will pause for this amount of seconds before automatically proceeding with any p or pe\nPROMPT_TIMEOUT=0\n\n# don't show command number unless user specifies it\nSHOW_CMD_NUMS=false\n\n\n# handy color vars for pretty prompts\nBLACK=\"\\033[0;30m\"\nBLUE=\"\\033[0;34m\"\nGREEN=\"\\033[0;32m\"\nGREY=\"\\033[0;90m\"\nCYAN=\"\\033[0;36m\"\nRED=\"\\033[0;31m\"\nPURPLE=\"\\033[0;35m\"\nBROWN=\"\\033[0;33m\"\nWHITE=\"\\033[1;37m\"\nCOLOR_RESET=\"\\033[0m\"\n\nC_NUM=0\n\n# prompt and command color which can be overriden\nDEMO_PROMPT=\"$ \"\nDEMO_CMD_COLOR=$WHITE\nDEMO_COMMENT_COLOR=$GREY\n\n##\n# prints the script usage\n##\nfunction usage() {\n  echo -e \"\"\n  echo -e \"Usage: $0 [options]\"\n  echo -e \"\"\n  echo -e \"\\tWhere options is one or more of:\"\n  echo -e \"\\t-h\\tPrints Help text\"\n  echo -e \"\\t-d\\tDebug mode. Disables simulated typing\"\n  echo -e \"\\t-n\\tNo wait\"\n  echo -e \"\\t-w\\tWaits max the given amount of seconds before proceeding with demo (e.g. '-w5')\"\n  echo -e \"\"\n}\n\n##\n# wait for user to press ENTER\n# if $PROMPT_TIMEOUT > 0 this will be used as the max time for proceeding automatically\n##\nfunction wait() {\n  if [[ \"$PROMPT_TIMEOUT\" == \"0\" ]]; then\n    read -rs\n  else\n    read -rst \"$PROMPT_TIMEOUT\"\n  fi\n}\n\n##\n# print command only. Useful for when you want to pretend to run a command\n#\n# takes 1 param - the string command to print\n#\n# usage: p \"ls -l\"\n#\n##\nfunction p() {\n  if [[ ${1:0:1} == \"#\" ]]; then\n    cmd=$DEMO_COMMENT_COLOR$1$COLOR_RESET\n  else\n    cmd=$DEMO_CMD_COLOR$1$COLOR_RESET\n  fi\n\n  # render the prompt\n  x=$(PS1=\"$DEMO_PROMPT\" \"$BASH\" --norc -i </dev/null 2>&1 | sed -n '${s/^\\(.*\\)exit$/\\1/p;}')\n\n  # show command number is selected\n  if $SHOW_CMD_NUMS; then\n   printf \"[$((++C_NUM))] $x\"\n  else\n   printf \"$x\"\n  fi\n\n  # wait for the user to press a key before typing the command\n  if [ $NO_WAIT = false ]; then\n    wait\n  fi\n\n  if [[ -z $TYPE_SPEED ]]; then\n    echo -en \"$cmd\"\n  else\n    echo -en \"$cmd\" | pv -qL $[$TYPE_SPEED+(-2 + RANDOM%5)];\n  fi\n\n  # wait for the user to press a key before moving on\n  if [ $NO_WAIT = false ]; then\n    wait\n  fi\n  echo \"\"\n}\n\n##\n# Prints and executes a command\n#\n# takes 1 parameter - the string command to run\n#\n# usage: pe \"ls -l\"\n#\n##\nfunction pe() {\n  # print the command\n  p \"$@\"\n  run_cmd \"$@\"\n}\n\n##\n# print and executes a command immediately\n#\n# takes 1 parameter - the string command to run\n#\n# usage: pei \"ls -l\"\n#\n##\nfunction pei {\n  NO_WAIT=true pe \"$@\"\n}\n\n##\n# Enters script into interactive mode\n#\n# and allows newly typed commands to be executed within the script\n#\n# usage : cmd\n#\n##\nfunction cmd() {\n  # render the prompt\n  x=$(PS1=\"$DEMO_PROMPT\" \"$BASH\" --norc -i </dev/null 2>&1 | sed -n '${s/^\\(.*\\)exit$/\\1/p;}')\n  printf \"$x\\033[0m\"\n  read command\n  run_cmd \"${command}\"\n}\n\nfunction run_cmd() {\n  function handle_cancel() {\n    printf \"\"\n  }\n\n  trap handle_cancel SIGINT\n  stty -echoctl\n  eval \"$@\"\n  stty echoctl\n  trap - SIGINT\n}\n\n\nfunction check_pv() {\n  command -v pv >/dev/null 2>&1 || {\n\n    echo \"\"\n    echo -e \"${RED}##############################################################\"\n    echo \"# HOLD IT!! I require pv but it's not installed.  Aborting.\" >&2;\n    echo -e \"${RED}##############################################################\"\n    echo \"\"\n    echo -e \"${COLOR_RESET}Installing pv:\"\n    echo \"\"\n    echo -e \"${BLUE}Mac:${COLOR_RESET} $ brew install pv\"\n    echo \"\"\n    echo -e \"${BLUE}Other:${COLOR_RESET} http://www.ivarch.com/programs/pv.shtml\"\n    echo -e \"${COLOR_RESET}\"\n    exit 1;\n  }\n}\n\nfunction updateFile(){\n  rm -f $1\n  if [ $# -eq 1 ]; then\n    while IFS= read -r data; do echo \"$data\" >> $1 ; done;\n  else\n    echo $2 > $1\n  fi\n\n  p \"cat $1\"\n  rougify --theme tulip $1\n  \n  sleep 1\n}\n\nfunction clearConsole(){\n  clear\n}\n\nfunction doSleep(){\n  sleep $1\n}\n\ncheck_pv\n#\n# handle some default params\n# -h for help\n# -d for disabling simulated typing\n#\nwhile getopts \":dhncw:\" opt; do\n  case $opt in\n    h)\n      usage\n      exit 1\n      ;;\n    d)\n      unset TYPE_SPEED\n      ;;\n    n)\n      NO_WAIT=true\n      ;;\n    c)\n      SHOW_CMD_NUMS=true\n      ;;\n    w)\n      PROMPT_TIMEOUT=$OPTARG\n      ;;\n  esac\ndone\n\n"
  },
  {
    "path": "gifs/demo-no-magic.sh",
    "content": "# Mock of demo magic, for running on CI\n\nfunction p() {\n  echo \"running: $@\"\n}\n\nfunction pe() {\n  p \"$@\"\n  run_cmd \"$@\"\n}\n\nfunction pei {\n  NO_WAIT=true pe \"$@\"\n}\n\nfunction cmd() {\n  run_cmd \"${command}\"\n}\n\nfunction run_cmd() {\n  eval \"$@\"\n}\n\nfunction updateFile(){\n  rm -f $1\n  if [ $# -eq 1 ]; then\n    while IFS= read -r data; do echo \"$data\" >> $1 ; done;\n  else\n    echo $2 > $1\n  fi\n\n  p \"cat $1\"\n  rougify --theme tulip $1\n  \n  doSleep 1\n}\n\nfunction clearConsole(){\n  echo clear \n}\n\nfunction doSleep(){\n  echo sleep $1\n}"
  },
  {
    "path": "gifs/example.sh",
    "content": "#!/bin/bash\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Code here will be run before the recording session\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # hide the evidence\n  clear\n\n  # Put your stuff here\n  pe \"echo 'println(\\\"<description>\\\")' | scala-cli -\"\n\n  # Wait a bit to read output of last command\n  sleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/run_scenario.sh",
    "content": "#!/usr/bin/env bash\n\nset -euxo pipefail\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [ \"$#\" != \"1\" ]; then\n  echo \"Please provide one scenario file!\"\n  exit 1\nfi\n\n\nfileName=$(basename $1)\nname=${fileName%%.sh}\nscript=$SCRIPT_DIR/scenarios/$name.sh \n\nls $script\n\n#warmup\n$script -n\necho \"Done with $?\"\n\ntest -f status.txt && rm status.txt\n\n#do recording\n( # do recording with tty\n  tty &&\n  asciinema rec --overwrite --command=\"$script -n\" $SCRIPT_DIR/out/$name.cast\n) || ( # without tty just run the command \n  export ASCIINEMA_REC=true &&\n  # remove magic from demo...\n  cp $SCRIPT_DIR/demo-no-magic.sh $SCRIPT_DIR/demo-magic.sh &&\n  $script -n\n)\n\ntest -f status.txt || (\n  echo \"Scenario $script failed.\" &&\n  echo \"In case logs show that is should succeed check if it creates a status.txt file at the end\" &&\n  exit 1\n)"
  },
  {
    "path": "gifs/scenarios/complete-install.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  rm /usr/bin/scala-cli || true # remove scala-cliu from PATH\n  rm /usr/bin/java || true # remove java from PATH\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  pe scala-cli || true\n  \n  pe java || true\n\n  doSleep 2\n  \n  pe \"curl -sSLf https://scala-cli.virtuslab.org/get | sh\"\n  pe 'source ~/.profile'\n  pe \"echo 'println(\\\"Hello from scala-cli\\\")' | scala-cli -\"\n\n\n  # Wait a bit to read output of last command\n  doSleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/defaults.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n    echo \"println(1)\" | scala-cli -\n    scala-cli fmt .\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n    cat <<EOF | updateFile Main.scala\n@main def hello() = {println(\"Hello \"+\"world\")}\nEOF\n  doSleep 2\n  pe 'scala-cli .'\n  pe 'scala-cli fmt .'\n  p \"cat Main.scala\"\n  rougify --theme tulip Main.scala\n  pe 'scala-cli --power package .'\n  doSleep 4\n  echo \" \" && echo \"ok\" > status.txt\nfi\n"
  },
  {
    "path": "gifs/scenarios/demo.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  cat <<EOF > demo.test.scala |\n//> using dep org.scalameta::munit:0.7.29\nEOF\n  scala-cli test demo.test.scala\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  cat <<EOF | updateFile demo.sc\nprintln(s\"Hello \\${args.mkString}\")\nEOF\n\n  pe \"scala-cli run demo.sc -- World\" || true\n\n  doSleep 2\n\n  clearConsole\n\n  cat <<EOF | updateFile demo.scala\n@main def demo(args: String *) = \n  println(args.mkStrink) // Oops, a typo!\nEOF\n\n  pe \"scala-cli compile demo.scala\" || true\n\n    doSleep 2\n\n  clearConsole\n\n  cat <<EOF | updateFile demo.scala\ndef niceArgs(args: String*): String = \n  args.map(_.capitalize).mkString(\"Hello: \", \", \", \"!\")\n\n@main def demo(args: String*) = println(niceArgs(args*))\nEOF\n\n  pe \"scala-cli demo.scala -- Ala jake Mike\" || echo \"FAILED!\"\n  \n  doSleep 5\n\n  clearConsole\n\n  cat <<EOF | updateFile demo.test.scala\n//> using dep org.scalameta::munit:0.7.29\n\nclass demoTest extends munit.FunSuite {\n  test(\"test nice args\") {\n    assert(clue(niceArgs(\"a\", \"b\")) == \"Hello: A, B!\")\n  }\n  test(\"test empty arguments\") {\n    assert(clue(niceArgs()) == \"Hello!\")\n  }\n}\nEOF\n\n  pe \"scala-cli test demo.scala demo.test.scala\" || true\n\n  # Wait a bit to read output of last command\n  doSleep 5\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/education.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n    cat <<EOF | updateFile HelloWorld.scala\nobject HelloWorld {\n    def main(args: Array[String]) = {\n        println(\"Hello world from Scala CLI\")\n    }\n}\nEOF\n\n  # Put your stuff here\n  pe \"scala-cli HelloWorld.scala\"\n\n  # Wait a bit to read output of last command\n  doSleep 5\n  clearConsole\n      cat <<EOF | updateFile HelloWorld.sc\nprintln(\"Hello world from script\")\nEOF\n\n  pe \"scala-cli HelloWorld.sc\"\n\n  doSleep 5\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/embeddable_scripts.sh",
    "content": "#!/bin/bash\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n    cat <<EOF | updateFile count_lines.sc\n#!/usr/bin/env scala-cli\n//> using scala 3.0.2\nimport scala.io.StdIn.readLine\nimport LazyList.continually\n\nprintln(continually(readLine).takeWhile(_ != null).length)\nEOF\n  doSleep 2\n  pe \"chmod +x count_lines.sc\"\n  pe 'echo -e \"abc\\ndef\" | ./count_lines.sc'\n  pe 'echo -e \"abc\\ndef\\nghi\" | ./count_lines.sc'\n  doSleep 4\n  echo \" \" && echo \"ok\" > status.txt\nfi\n"
  },
  {
    "path": "gifs/scenarios/fast-scripts.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  pe \"echo 'println(\\\"TODO: turn gifs/scenarios/fast-scripts.sh into proper scenario showing Fast Scripts\" key=\"fast-scripts\" scripting=\"true\\\")' | scala-cli -\"\n\n  # Wait a bit to read output of last command\n  doSleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/learning_curve.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -S 3.0.2 -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n   cat <<EOF | updateFile Hello.scala\n//> using scala 3.0.2\n\n@main def hello() = println(\"Hello world from Scala CLI\")\nEOF\n\n  pe \"scala-cli Hello.scala\"\n\n  # Wait a bit to read output of last command\n  doSleep 2\n  clearConsole\n\n cat <<EOF | updateFile Hello.scala\n//> using scala 2.13.6\n\nobject Hello extends App {\n println(\"Hello world from Scala CLI\")\n}\nEOF\n\n  pe \"scala-cli Hello.scala\"\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/powerful_scripts.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n    echo '//> using dep com.lihaoyi::os-lib:0.9.1' | scala-cli -\n    echo '//> using dep com.lihaoyi::pprint:0.8.1' | scala-cli -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  cat <<EOF | updateFile stat.sc\n//> using dep com.lihaoyi::os-lib:0.9.1\n//> using dep com.lihaoyi::pprint:0.8.1\nimport pprint._\nimport os._\n\nval path = Path(args(0), pwd)\npprintln(os.stat(path))\n\nEOF\n  doSleep 3\n  pe \"chmod +x stat.sc\"\n  pe 'echo \"Hello\" > my_file'\n  pe \"scala-cli ./stat.sc -- my_file\"\n  # Wait a bit to read output of last command\n  doSleep 4\n  echo \" \" && echo \"ok\" > status.txt\nfi\n"
  },
  {
    "path": "gifs/scenarios/projects.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -S 2.13.6 -\n  echo \"//> using dep com.softwaremill.sttp.client3::core:3.8.13\" | scala-cli -S 2.13.6  -\n  scala-cli config suppress-warning.outdated-dependencies-files true\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  cat <<EOF | updateFile Post.scala\n//> using dep com.softwaremill.sttp.client3::core:3.3.18\nimport sttp.client3._\n\n// https://sttp.softwaremill.com/en/latest/quickstart.html\n\nobject Post extends App {\n  val backend = HttpURLConnectionBackend()\n  val response = basicRequest\n    .body(\"Hello, world!\")  \n    .post(uri\"https://httpbin.org/post?hello=world\").send(backend)\n\n  println(response.body)  \n}\nEOF\n\n  doSleep 2\n\n  pe \"scala-cli Post.scala -S 2.13.6\"\n\n  # Wait a bit to read output of last command\n  doSleep 5\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/prototyping.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -S 3.0.2 -\n  echo \"println(1)\" | scala-cli -S 2.13.6 -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  cat <<EOF | updateFile Enum.scala\nobject Enum extends App {\n  enum Color:\n    case Red, Green, Blue\n    \n  val values = Color.values.mkString(\",\")\n  println(values)\n}\nEOF\n\n  pe \"scala-cli Enum.scala -S 3.0.2\"\n\n  doSleep 2\n\n  pe \"scala-cli Enum.scala -S 2.13.6\" || true\n  \n\n  # Wait a bit to read output of last command\n  doSleep 5\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/scripting.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  echo \"//> using dep \\\"com.lihaoyi::os-lib::0.9.1\\\"\" | scala-cli  -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  cat <<EOF | updateFile file\nHello World from file\nEOF\n\n  cat <<EOF | updateFile script.sc\n//> using dep com.lihaoyi::os-lib::0.9.1\n\nval filePath = os.pwd / \"file\"\nval fileContent = os.read(filePath)\nprintln(fileContent)\nEOF\n\n  pe \"scala-cli script.sc\"\n\n  doSleep 5\n\n  clearConsole\n  \n  cat <<EOF | updateFile hello.sc\nval message = \"Hello from Scala script\"\nprintln(message)\nEOF\n\n  pe \"scala-cli hello.sc\"\n\n  doSleep 5\n  clearConsole\n  \n  cat <<EOF | updateFile scala-script.sc\n#!/usr/bin/env scala-cli\n\nprintln(\"Hello world\")\nEOF\n\n  pe \"chmod +x scala-script.sc\"\n  pe \"./scala-script.sc\"\n\n  echo \" \"\n  # Wait a bit to read output of last command\n  doSleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/self-contained-examples.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n  pe \"scala-cli https://gist.github.com/alexarchambault/7b4ec20c4033690dd750ffd601e540ec\"\n\n  doSleep 3\n  clearConsole \n\n  pe \"scala-cli https://gist.github.com/lwronski/99bb89d1962d2c5e21da01f1ad60e92f\" || true\n\n  doSleep 2\n  \n  pe \"scala-cli https://gist.github.com/lwronski/99bb89d1962d2c5e21da01f1ad60e92f -M ScalaCli\"\n\n  # Wait a bit to read output of last command\n  doSleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/scenarios/todo.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n\n  pe \"scala-cli version\"\n  pe \"echo 'println(\\\"TODO\\\")' | scala-cli -\"\n\n  # Wait a bit to read output of last command\n  doSleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi\n\n\n\n\n"
  },
  {
    "path": "gifs/scenarios/universal_tool.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  echo \"println(1)\" | scala-cli -\n  echo \"println(1)\" | scala-cli --js - &&\n    echo \"println(1)\" | scala-cli --native -\n   \n  # or do other preparation (e.g. create code)\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  cat <<EOF | updateFile js.scala\n@main def jsMain =\n  import scala.scalajs.js.Dynamic.global\n  println(s\"Node js version: \\${global.process.version}\")\nEOF\n\n  pe \"scala-cli --js js.scala\"\n  doSleep 3\n  clearConsole\n\n  cat <<EOF | updateFile native.scala\nimport scala.scalanative.posix.limits\n@main def native() = println(s\"Max path length in this OS is \\${limits.PATH_MAX}\")\nEOF\n\n  pe \"scala-cli --native native.scala\"\n  doSleep 3\n\n  echo \" \" && echo \"ok\" > status.txt\nfi\n"
  },
  {
    "path": "gifs/scenarios/versions.sh",
    "content": "#!/bin/bash\n\nset -e\n\n########################\n# include the magic\n########################\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\nif [[ -z \"${ASCIINEMA_REC}\" ]]; then\n  # Warm up scala-cli\n  cat <<EOF > versions.scala\nobject ScalaVersion extends App {\n  def props(url: java.net.URL): java.util.Properties = {\n    val properties = new java.util.Properties()\n    val is = url.openStream()\n    try {\n      properties.load(is)\n      properties\n    } finally is.close()    \n  }\n\n  def scala2Version: String = \n    props(getClass.getResource(\"/library.properties\")).getProperty(\"version.number\")\n    \n  def checkScala3(res: java.util.Enumeration[java.net.URL]): String = \n    if (!res.hasMoreElements) scala2Version else {\n      val manifest = props(res.nextElement)\n      manifest.getProperty(\"Specification-Title\") match {\n        case \"scala3-library-bootstrapped\" =>\n          manifest.getProperty(\"Implementation-Version\")\n        case _ => checkScala3(res)\n      }\n    }\n  val manifests = getClass.getClassLoader.getResources(\"META-INF/MANIFEST.MF\")\n    \n  val scalaVersion = checkScala3(manifests)\n  val javaVersion = System.getProperty(\"java.version\")\n\n  println(s\"Scala: \\$scalaVersion Java: \\$javaVersion\")\n}\nEOF\n\n  cat <<EOF > classpath.scala\nobject Main extends App {\n  val classpath = System.getProperty(\"java.class.path\").split(java.io.File.pathSeparator)\n  val ignoreIf = Seq(\"scala-cli\", \"scala-lang\", \"jline\", \"scala-sbt\", \"pretty-stacktraces\", \"java/dev/jna\", \"protobuf-java\")\n  println(classpath.toList\n    .filter(l => !ignoreIf.exists(l.contains))\n    .filter(_.endsWith(\".jar\"))\n    .map(_.split(\"/\").last)\n    .sorted\n    .mkString(\"Jars: \", \", \", \"\")\n  )\n}\nEOF\n\n  scala-cli versions.scala\n  scala-cli --scala 2.13.18 versions.scala\n  scala-cli --scala 2.12.21 versions.scala\n  scala-cli --jvm 17 versions.scala\n  scala-cli --jvm zulu:21 versions.scala\n  scala-cli --scala 2.13.18 --dep org.typelevel::cats-core:2.3.0 classpath.scala\n  scala-cli --dep org.scalameta::munit:0.7.29 classpath.scala\n\nelse\n  . $SCRIPT_DIR/../demo-magic.sh\n  # # hide the evidence\n  clearConsole\n\n  # Put your stuff here\n\n\n  pe \"scala-cli versions.scala\"\n  pe \"scala-cli --scala 2.13.18 versions.scala\"\n  pe \"scala-cli --scala 2.12.21 versions.scala\"\n  doSleep 2\n  clearConsole\n  pe \"scala-cli --jvm 17 versions.scala\"\n  pe \"scala-cli --jvm zulu:21 versions.scala\"\n  doSleep 2\n  clearConsole\n  pe \"scala-cli --dep org.scalameta::munit:0.7.29 classpath.scala\"\n  pe \"scala-cli --scala 2.13.18 --dep org.typelevel::cats-core:2.3.0 classpath.scala\"\n\n  # Wait a bit to read output of last command\n  doSleep 2\n  echo \" \" && echo \"ok\" > status.txt\nfi"
  },
  {
    "path": "gifs/svg_render/Dockerfile",
    "content": "FROM node:12.18.1\n\nRUN npm install -g svg-term-cli\n\nRUN mkdir /profiles\n\n# Terminal themes for light and dark mode of the site\n# Based on https://iterm2colorschemes.com/\nRUN curl -fLo /profiles/dark https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/schemes/Chester.itermcolors\nRUN curl -fLo /profiles/light https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/schemes/Jackie%20Brown.itermcolors\n\n\nENTRYPOINT svg-term $*"
  },
  {
    "path": "gifs/svg_render/README.md",
    "content": "This is docker image that is used to generate our scripts.\n\nThe image is based on amazing svg-term-cli project by marionebl: https://github.com/marionebl/svg-term-cli\n\nThe themes are based on https://iterm2colorschemes.com/."
  },
  {
    "path": "mill",
    "content": "#!/usr/bin/env bash\n\n# Adapted from\n\ncoursier_version=\"2.1.25-M24\"\nCOMMAND=$@\n\n# necessary for Windows various shell environments\nIS_WINDOWS=$(uname | grep -E 'CYG*|MSYS*|MING*|UCRT*|ClANG*|GIT*')\n\n# https://stackoverflow.com/questions/3466166/how-to-check-if-running-in-cygwin-mac-or-linux/17072017#17072017\nif [ \"$(expr substr $(uname -s) 1 5 2>/dev/null)\" == \"Linux\" ]; then\n  if [ \"$(uname -m)\" == \"aarch64\" ]; then\n    cs_url=\"https://github.com/coursier/coursier/releases/download/v$coursier_version/cs-aarch64-pc-linux.gz\"\n  else\n    cs_url=\"https://github.com/coursier/coursier/releases/download/v$coursier_version/cs-x86_64-pc-linux.gz\"\n  fi\n  cache_base=\"$HOME/.cache/coursier/v1\"\nelif [ \"$(uname)\" == \"Darwin\" ]; then\n  if [ \"$(uname -p)\" == \"arm\" ]; then \n    cs_url=\"https://github.com/coursier/coursier/releases/download/v$coursier_version/cs-aarch64-apple-darwin.gz\"\n  else \n    cs_url=\"https://github.com/coursier/coursier/releases/download/v$coursier_version/cs-x86_64-apple-darwin.gz\"\n  fi\n  cache_base=\"$HOME/Library/Caches/Coursier/v1\"\nelse\n  # assuming Windows…\n  cs_url=\"https://github.com/coursier/coursier/releases/download/v$coursier_version/cs-x86_64-pc-win32.zip\"\n  cache_base=\"$LOCALAPPDATA/Coursier/v1\" # TODO Check that\n  ext=\".exe\"\n  do_chmod=\"0\"\nfi\n\ncache_dest=\"$cache_base/$(echo \"$cs_url\" | sed 's@://@/@')\"\n\nif [ ! -f \"$cache_dest\" ]; then\n  mkdir -p \"$(dirname \"$cache_dest\")\"\n  tmp_dest=\"$cache_dest.tmp-setup\"\n  echo \"Downloading $cs_url\"\n  curl -fLo \"$tmp_dest\" \"$cs_url\"\n  mv \"$tmp_dest\" \"$cache_dest\"\nfi\n\nif [[ \"$cache_dest\" == *.gz ]]; then\n  cs=\"$(dirname \"$cache_dest\")/$(basename \"$cache_dest\" .gz)\"\n  if [ ! -f \"$cs\" ]; then\n    gzip -d < \"$cache_dest\" > \"$cs\"\n  fi\n  if [ ! -x \"$cs\" ]; then\n    chmod +x \"$cs\"\n  fi\nelif [[ \"$cache_dest\" == *.zip ]]; then\n  cs=\"$(dirname \"$cache_dest\")/$(basename \"$cache_dest\" .zip).exe\"\n  if [ ! -f \"$cs\" ]; then\n    unzip -p \"$cache_dest\" \"$(basename \"$cache_dest\" .zip).exe\" > \"$cs\"\n  fi\nfi\n\n# If cs is not a valid file, fall back to the `cs` command in PATH\nif [ ! -f \"$cs\" ]; then\n  cs=\"$(command -v cs)\"\nfi\n\nfunction to_bash_syntax {\n  local S\n  for S in \"$@\" ; do\n    echo \"$S\" | sed -E -e 's#^set #export #' -e 's#%([A-Z_][A-Z_0-9]*)%#${\\1}#g' | tr '\\\\' '/'\n  done\n}\nif [[ $IS_WINDOWS ]]; then\n  # needed for coursier version < 2.1.8, harmless otherwise\n  IFS=$'\\n'\n  # temurin:17 (build 17+35) doesn't support utf8 filenames, although some later 17 versions do\n  eval \"$(to_bash_syntax `\"$cs\" java --env --jvm zulu:17` || to_bash_syntax `\"$cs\" java --env --jvm openjdk:1.17.0`)\"\n  unset IFS\nelse\n  eval \"$(\"$cs\" java --env --jvm temurin:17 || \"$cs\" java --env --jvm openjdk:1.17.0)\"\nfi\n\n# temporary, until we pass JPMS options to native-image,\n# see https://www.graalvm.org/release-notes/22_2/#native-image\nexport USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM=false\n\nDIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\"\n\nif [[ $IS_WINDOWS ]]; then\n  exec \"$DIR/mill.bat\" \"$@\"\nelse\n  exec \"$DIR/millw\" $COMMAND\nfi\n"
  },
  {
    "path": "mill.bat",
    "content": "@echo off\n\nsetlocal enabledelayedexpansion\n\nif [!DEFAULT_MILL_VERSION!]==[] ( set \"DEFAULT_MILL_VERSION=1.1.5\" )\n\nif [!MILL_GITHUB_RELEASE_CDN!]==[] ( set \"MILL_GITHUB_RELEASE_CDN=\" )\n\nif [!MILL_MAIN_CLI!]==[] ( set \"MILL_MAIN_CLI=%~f0\" )\n\nset \"MILL_REPO_URL=https://github.com/com-lihaoyi/mill\"\n\nSET MILL_BUILD_SCRIPT=\n\nif exist \"build.mill\" (\n  set MILL_BUILD_SCRIPT=build.mill\n) else (\n    if exist \"build.mill.scala\" (\n      set MILL_BUILD_SCRIPT=build.mill.scala\n    ) else (\n        if exist \"build.sc\" (\n          set MILL_BUILD_SCRIPT=build.sc\n        ) else (\n            rem no-op\n        )\n    )\n)\n\nif [!MILL_VERSION!]==[] (\n  if exist .mill-version (\n    set /p MILL_VERSION=<.mill-version\n  ) else (\n    if exist .config\\mill-version (\n      set /p MILL_VERSION=<.config\\mill-version\n    ) else (\n      rem Determine which config file to use for version extraction\n      set \"MILL_VERSION_CONFIG_FILE=\"\n      set \"MILL_VERSION_SEARCH_PATTERN=\"\n\n      if exist build.mill.yaml (\n        set \"MILL_VERSION_CONFIG_FILE=build.mill.yaml\"\n        set \"MILL_VERSION_SEARCH_PATTERN=mill-version:\"\n      ) else (\n        if not \"%MILL_BUILD_SCRIPT%\"==\"\" (\n          set \"MILL_VERSION_CONFIG_FILE=%MILL_BUILD_SCRIPT%\"\n          set \"MILL_VERSION_SEARCH_PATTERN=//\\|.*mill-version\"\n        )\n      )\n\n      rem Process the config file if found\n      if not \"!MILL_VERSION_CONFIG_FILE!\"==\"\" (\n        rem Find the line and process it\n        for /f \"tokens=*\" %%a in ('findstr /R /C:\"!MILL_VERSION_SEARCH_PATTERN!\" \"!MILL_VERSION_CONFIG_FILE!\"') do (\n            set \"line=%%a\"\n\n            rem --- 1. Replicate sed 's/.*://' ---\n            rem This removes everything up to and including the first colon\n            set \"line=!line:*:=!\"\n\n            rem --- 2. Replicate sed 's/#.*//' ---\n            rem Split on '#' and keep the first part\n            for /f \"tokens=1 delims=#\" %%b in (\"!line!\") do (\n                set \"line=%%b\"\n            )\n\n            rem --- 3. Replicate sed 's/['\"]//g' ---\n            rem Remove all quotes\n            set \"line=!line:'=!\"\n            set \"line=!line:\"=!\"\n\n            rem --- 4. Replicate sed's trim/space removal ---\n            rem Remove all space characters from the result. This is more robust.\n            set \"MILL_VERSION=!line: =!\"\n\n            rem We found the version, so we can exit the loop\n            goto :version_found\n        )\n\n        :version_found\n        rem no-op\n      )\n    )\n  )\n)\n\nif [!MILL_VERSION!]==[] (\n    set MILL_VERSION=%DEFAULT_MILL_VERSION%\n)\n\nif [!MILL_FINAL_DOWNLOAD_FOLDER!]==[] set MILL_FINAL_DOWNLOAD_FOLDER=%USERPROFILE%\\.cache\\mill\\download\n\nrem without bat file extension, cmd doesn't seem to be able to run it\n\nset \"MILL_NATIVE_SUFFIX=-native\"\nset \"MILL_JVM_SUFFIX=-jvm\"\nset \"MILL_FULL_VERSION=%MILL_VERSION%\"\nset \"MILL_DOWNLOAD_EXT=.bat\"\nset \"ARTIFACT_SUFFIX=\"\nREM Check if MILL_VERSION contains MILL_NATIVE_SUFFIX\necho !MILL_VERSION! | findstr /C:\"%MILL_NATIVE_SUFFIX%\" >nul\nif !errorlevel! equ 0 (\n    set \"MILL_VERSION=%MILL_VERSION:-native=%\"\n    REM -native images compiled with graal do not support windows-arm\n    REM https://github.com/oracle/graal/issues/9215\n    IF /I NOT \"%PROCESSOR_ARCHITECTURE%\"==\"ARM64\" (\n        set \"ARTIFACT_SUFFIX=-native-windows-amd64\"\n        set \"MILL_DOWNLOAD_EXT=.exe\"\n    ) else (\n        rem no-op\n    )\n) else (\n    echo !MILL_VERSION! | findstr /C:\"%MILL_JVM_SUFFIX%\" >nul\n    if !errorlevel! equ 0 (\n        set \"MILL_VERSION=%MILL_VERSION:-jvm=%\"\n    ) else (\n        set \"SKIP_VERSION=false\"\n        set \"MILL_PREFIX=%MILL_VERSION:~0,4%\"\n        if \"!MILL_PREFIX!\"==\"0.1.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.2.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.3.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.4.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.5.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.6.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.7.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.8.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.9.\" set \"SKIP_VERSION=true\"\n        set \"MILL_PREFIX=%MILL_VERSION:~0,5%\"\n        if \"!MILL_PREFIX!\"==\"0.10.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.11.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.12.\" set \"SKIP_VERSION=true\"\n\n        if \"!SKIP_VERSION!\"==\"false\" (\n            IF /I NOT \"%PROCESSOR_ARCHITECTURE%\"==\"ARM64\" (\n                set \"ARTIFACT_SUFFIX=-native-windows-amd64\"\n                set \"MILL_DOWNLOAD_EXT=.exe\"\n            )\n        ) else (\n            rem no-op\n        )\n    )\n)\n\nset MILL=%MILL_FINAL_DOWNLOAD_FOLDER%\\!MILL_FULL_VERSION!!MILL_DOWNLOAD_EXT!\n\nset MILL_RESOLVE_DOWNLOAD=\n\nif not exist \"%MILL%\" (\n  set MILL_RESOLVE_DOWNLOAD=true\n) else (\n    if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT (\n        set MILL_RESOLVE_DOWNLOAD=true\n    ) else (\n        rem no-op\n    )\n)\n\n\nif [!MILL_RESOLVE_DOWNLOAD!]==[true] (\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,4%\n    set MILL_SHORT_VERSION_PREFIX=%MILL_VERSION:~0,2%\n    rem Since 0.5.0\n    set MILL_DOWNLOAD_SUFFIX=-assembly\n    rem Since 0.11.0\n    set MILL_DOWNLOAD_FROM_MAVEN=1\n    if [!MILL_VERSION_PREFIX!]==[0.0.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.1.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.2.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.3.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.4.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.5.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.6.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.7.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.8.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.9.] set MILL_DOWNLOAD_FROM_MAVEN=0\n\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5%\n    if [!MILL_VERSION_PREFIX!]==[0.10.] set MILL_DOWNLOAD_FROM_MAVEN=0\n\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,8%\n    if [!MILL_VERSION_PREFIX!]==[0.11.0-M] set MILL_DOWNLOAD_FROM_MAVEN=0\n\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5%\n    set DOWNLOAD_EXT=exe\n    if [!MILL_SHORT_VERSION_PREFIX!]==[0.] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION_PREFIX!]==[0.12.] set DOWNLOAD_EXT=exe\n    if [!MILL_VERSION!]==[0.12.0] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.1] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.2] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.3] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.4] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.5] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.6] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.7] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.8] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.9] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.10] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.11] set DOWNLOAD_EXT=jar\n\n    set MILL_VERSION_PREFIX=\n    set MILL_SHORT_VERSION_PREFIX=\n\n    for /F \"delims=- tokens=1\" %%A in (\"!MILL_VERSION!\") do set MILL_VERSION_BASE=%%A\n    set MILL_VERSION_MILESTONE=\n    for /F \"delims=- tokens=2\" %%A in (\"!MILL_VERSION!\") do set MILL_VERSION_MILESTONE=%%A\n    set MILL_VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1!\n    if [!MILL_VERSION_MILESTONE_START!]==[M] (\n        set MILL_VERSION_TAG=!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!\n    ) else (\n        set MILL_VERSION_TAG=!MILL_VERSION_BASE!\n    )\n    if [!MILL_DOWNLOAD_FROM_MAVEN!]==[1] (\n        set MILL_DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist!ARTIFACT_SUFFIX!/!MILL_VERSION!/mill-dist!ARTIFACT_SUFFIX!-!MILL_VERSION!.!DOWNLOAD_EXT!\n    ) else (\n        set MILL_DOWNLOAD_URL=!MILL_GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!MILL_DOWNLOAD_SUFFIX!\n    )\n\n    if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT (\n        echo !MILL_DOWNLOAD_URL!\n        echo !MILL!\n        exit /b 0\n    )\n\n    rem there seems to be no way to generate a unique temporary file path (on native Windows)\n    if defined MILL_OUTPUT_DIR (\n        set MILL_TEMP_DOWNLOAD_FILE=%MILL_OUTPUT_DIR%\\mill-temp-download\n        if not exist \"%MILL_OUTPUT_DIR%\" mkdir \"%MILL_OUTPUT_DIR%\"\n    ) else (\n        set MILL_TEMP_DOWNLOAD_FILE=out\\mill-bootstrap-download\n        if not exist \"out\" mkdir \"out\"\n    )\n\n    echo Downloading mill !MILL_VERSION! from !MILL_DOWNLOAD_URL! ... 1>&2\n\n    curl -f -L \"!MILL_DOWNLOAD_URL!\" -o \"!MILL_TEMP_DOWNLOAD_FILE!\"\n\n    if not exist \"%MILL_FINAL_DOWNLOAD_FOLDER%\" mkdir \"%MILL_FINAL_DOWNLOAD_FOLDER%\"\n    move /y \"!MILL_TEMP_DOWNLOAD_FILE!\" \"%MILL%\"\n\n    set MILL_TEMP_DOWNLOAD_FILE=\n    set MILL_DOWNLOAD_SUFFIX=\n)\n\nset MILL_FINAL_DOWNLOAD_FOLDER=\nset MILL_VERSION=\nset MILL_REPO_URL=\n\nrem Need to preserve the first position of those listed options\nset MILL_FIRST_ARG=\nif [%~1%]==[--bsp] (\n  set MILL_FIRST_ARG=%1%\n) else (\n  if [%~1%]==[-i] (\n    set MILL_FIRST_ARG=%1%\n  ) else (\n    if [%~1%]==[--interactive] (\n      set MILL_FIRST_ARG=%1%\n    ) else (\n      if [%~1%]==[--no-server] (\n        set MILL_FIRST_ARG=%1%\n      ) else (\n        if [%~1%]==[--no-daemon] (\n          set MILL_FIRST_ARG=%1%\n        ) else (\n          if [%~1%]==[--help] (\n            set MILL_FIRST_ARG=%1%\n          )\n        )\n      )\n    )\n  )\n)\nset \"MILL_PARAMS=%*%\"\n\nif not [!MILL_FIRST_ARG!]==[] (\n  for /f \"tokens=1*\" %%a in (\"%*\") do (\n    set \"MILL_PARAMS=%%b\"\n  )\n)\n\nrem -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2\n\"%MILL%\" %MILL_FIRST_ARG% -D \"mill.main.cli=%MILL_MAIN_CLI%\" %MILL_PARAMS%\n"
  },
  {
    "path": "millw",
    "content": "#!/usr/bin/env sh\n\nset -e\n\nif [ -z \"${DEFAULT_MILL_VERSION}\" ] ; then DEFAULT_MILL_VERSION=\"1.1.5\"; fi\n\nif [ -z \"${GITHUB_RELEASE_CDN}\" ] ; then GITHUB_RELEASE_CDN=\"\"; fi\n\nif [ -z \"$MILL_MAIN_CLI\" ] ; then MILL_MAIN_CLI=\"${0}\"; fi\n\nMILL_REPO_URL=\"https://github.com/com-lihaoyi/mill\"\n\nMILL_BUILD_SCRIPT=\"\"\n\nif [ -f \"build.mill\" ] ; then\n  MILL_BUILD_SCRIPT=\"build.mill\"\nelif [ -f \"build.mill.scala\" ] ; then\n  MILL_BUILD_SCRIPT=\"build.mill.scala\"\nelif [ -f \"build.sc\" ] ; then\n  MILL_BUILD_SCRIPT=\"build.sc\"\nfi\n\n# `s/.*://`:\n#   This is a greedy match that removes everything from the beginning of the line up to (and including) the last\n#   colon (:). This effectively isolates the value part of the declaration.\n#\n#  `s/#.*//`:\n#    This removes any comments at the end of the line.\n#\n#  `s/['\\\"]//g`:\n#    This removes all single and double quotes from the string, wherever they appear (g is for \"global\").\n#\n#  `s/^[[:space:]]*//; s/[[:space:]]*$//`:\n#    These two expressions trim any leading or trailing whitespace ([[:space:]] matches spaces and tabs).\nTRIM_VALUE_SED=\"s/.*://; s/#.*//; s/['\\\"]//g; s/^[[:space:]]*//; s/[[:space:]]*$//\"\n\nif [ -z \"${MILL_VERSION}\" ] ; then\n  if [ -f \".mill-version\" ] ; then\n    MILL_VERSION=\"$(tr '\\r' '\\n' < .mill-version | head -n 1 2> /dev/null)\"\n  elif [ -f \".config/mill-version\" ] ; then\n    MILL_VERSION=\"$(tr '\\r' '\\n' < .config/mill-version | head -n 1 2> /dev/null)\"\n  elif [ -f \"build.mill.yaml\" ] ; then\n    MILL_VERSION=\"$(grep -E \"mill-version:\" \"build.mill.yaml\" | sed -E \"$TRIM_VALUE_SED\")\"\n  elif [ -n \"${MILL_BUILD_SCRIPT}\" ] ; then\n    MILL_VERSION=\"$(grep -E \"//\\|.*mill-version\" \"${MILL_BUILD_SCRIPT}\" | sed -E \"$TRIM_VALUE_SED\")\"\n  fi\nfi\n\nif [ -z \"${MILL_VERSION}\" ] ; then MILL_VERSION=\"${DEFAULT_MILL_VERSION}\"; fi\n\nMILL_USER_CACHE_DIR=\"${XDG_CACHE_HOME:-${HOME}/.cache}/mill\"\n\nif [ -z \"${MILL_FINAL_DOWNLOAD_FOLDER}\" ] ; then MILL_FINAL_DOWNLOAD_FOLDER=\"${MILL_USER_CACHE_DIR}/download\"; fi\n\nMILL_NATIVE_SUFFIX=\"-native\"\nMILL_JVM_SUFFIX=\"-jvm\"\nARTIFACT_SUFFIX=\"\"\n\n# Check if GLIBC version is at least the required version\n# Returns 0 (true) if GLIBC >= required version, 1 (false) otherwise\ncheck_glibc_version() {\n  required_version=\"2.39\"\n  required_major=$(echo \"$required_version\" | cut -d. -f1)\n  required_minor=$(echo \"$required_version\" | cut -d. -f2)\n  # Get GLIBC version from ldd --version (first line contains version like \"ldd (GNU libc) 2.31\")\n  glibc_version=$(ldd --version 2>/dev/null | head -n 1 | grep -oE '[0-9]+\\.[0-9]+$' || echo \"\")\n  if [ -z \"$glibc_version\" ]; then\n    # If we can't determine GLIBC version, assume it's too old\n    return 1\n  fi\n  glibc_major=$(echo \"$glibc_version\" | cut -d. -f1)\n  glibc_minor=$(echo \"$glibc_version\" | cut -d. -f2)\n  if [ \"$glibc_major\" -gt \"$required_major\" ]; then\n    return 0\n  elif [ \"$glibc_major\" -eq \"$required_major\" ] && [ \"$glibc_minor\" -ge \"$required_minor\" ]; then\n    return 0\n  else\n    return 1\n  fi\n}\n\nset_artifact_suffix() {\n  if [ \"$(uname -s 2>/dev/null | cut -c 1-5)\" = \"Linux\" ]; then\n    # Native binaries require new enough GLIBC; fall back to JVM launcher if older\n    if ! check_glibc_version; then\n      return\n    fi\n    if [ \"$(uname -m)\" = \"aarch64\" ]; then ARTIFACT_SUFFIX=\"-native-linux-aarch64\"\n    else ARTIFACT_SUFFIX=\"-native-linux-amd64\"; fi\n  elif [ \"$(uname)\" = \"Darwin\" ]; then\n    if [ \"$(uname -m)\" = \"arm64\" ]; then ARTIFACT_SUFFIX=\"-native-mac-aarch64\"\n    else ARTIFACT_SUFFIX=\"-native-mac-amd64\"; fi\n  else\n    echo \"This native mill launcher supports only Linux and macOS.\" 1>&2\n    exit 1\n  fi\n}\n\ncase \"$MILL_VERSION\" in\n  *\"$MILL_NATIVE_SUFFIX\")\n    MILL_VERSION=${MILL_VERSION%\"$MILL_NATIVE_SUFFIX\"}\n    set_artifact_suffix\n    ;;\n\n  *\"$MILL_JVM_SUFFIX\")\n    MILL_VERSION=${MILL_VERSION%\"$MILL_JVM_SUFFIX\"}\n    ;;\n\n  *)\n    case \"$MILL_VERSION\" in\n      0.1.* | 0.2.* | 0.3.* | 0.4.* | 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.* | 0.12.*)\n        ;;\n      *)\n        set_artifact_suffix\n        ;;\n    esac\n    ;;\nesac\n\nMILL=\"${MILL_FINAL_DOWNLOAD_FOLDER}/$MILL_VERSION$ARTIFACT_SUFFIX\"\n\n# If not already downloaded, download it\nif [ ! -s \"${MILL}\" ] || [ \"$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT\" = \"1\" ] ; then\n  case $MILL_VERSION in\n    0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.*)\n      MILL_DOWNLOAD_SUFFIX=\"\"\n      MILL_DOWNLOAD_FROM_MAVEN=0\n      ;;\n    0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M*)\n      MILL_DOWNLOAD_SUFFIX=\"-assembly\"\n      MILL_DOWNLOAD_FROM_MAVEN=0\n      ;;\n    *)\n      MILL_DOWNLOAD_SUFFIX=\"-assembly\"\n      MILL_DOWNLOAD_FROM_MAVEN=1\n      ;;\n  esac\n  case $MILL_VERSION in\n    0.12.0 | 0.12.1 | 0.12.2 | 0.12.3 | 0.12.4 | 0.12.5 | 0.12.6 | 0.12.7 | 0.12.8 | 0.12.9 | 0.12.10 | 0.12.11)\n      MILL_DOWNLOAD_EXT=\"jar\"\n      ;;\n    0.12.*)\n      MILL_DOWNLOAD_EXT=\"exe\"\n      ;;\n    0.*)\n      MILL_DOWNLOAD_EXT=\"jar\"\n      ;;\n    *)\n      MILL_DOWNLOAD_EXT=\"exe\"\n      ;;\n  esac\n\n  MILL_TEMP_DOWNLOAD_FILE=\"${MILL_OUTPUT_DIR:-out}/mill-temp-download\"\n  mkdir -p \"$(dirname \"${MILL_TEMP_DOWNLOAD_FILE}\")\"\n\n  if [ \"$MILL_DOWNLOAD_FROM_MAVEN\" = \"1\" ] ; then\n    MILL_DOWNLOAD_URL=\"https://repo1.maven.org/maven2/com/lihaoyi/mill-dist${ARTIFACT_SUFFIX}/${MILL_VERSION}/mill-dist${ARTIFACT_SUFFIX}-${MILL_VERSION}.${MILL_DOWNLOAD_EXT}\"\n  else\n    MILL_VERSION_TAG=$(echo \"$MILL_VERSION\" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\\1\\2/')\n    MILL_DOWNLOAD_URL=\"${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${MILL_DOWNLOAD_SUFFIX}\"\n    unset MILL_VERSION_TAG\n  fi\n\n\n  if [ \"$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT\" = \"1\" ] ; then\n    echo \"$MILL_DOWNLOAD_URL\"\n    echo \"$MILL\"\n    exit 0\n  fi\n\n  echo \"Downloading mill ${MILL_VERSION} from ${MILL_DOWNLOAD_URL} ...\" 1>&2\n  curl -f -L -o \"${MILL_TEMP_DOWNLOAD_FILE}\" \"${MILL_DOWNLOAD_URL}\"\n\n  chmod +x \"${MILL_TEMP_DOWNLOAD_FILE}\"\n\n  mkdir -p \"${MILL_FINAL_DOWNLOAD_FOLDER}\"\n  mv \"${MILL_TEMP_DOWNLOAD_FILE}\" \"${MILL}\"\n\n  unset MILL_TEMP_DOWNLOAD_FILE\n  unset MILL_DOWNLOAD_SUFFIX\nfi\n\nMILL_FIRST_ARG=\"\"\nif [ \"$1\" = \"--bsp\" ] || [ \"${1#\"-i\"}\" != \"$1\" ] || [ \"$1\" = \"--interactive\" ] || [ \"$1\" = \"--no-server\" ] || [ \"$1\" = \"--no-daemon\" ] || [ \"$1\" = \"--help\" ] ; then\n  # Need to preserve the first position of those listed options\n  MILL_FIRST_ARG=$1\n  shift\nfi\n\nunset MILL_FINAL_DOWNLOAD_FOLDER\nunset MILL_OLD_DOWNLOAD_PATH\nunset OLD_MILL\nunset MILL_VERSION\nunset MILL_REPO_URL\n\n# -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2\n# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes\n# shellcheck disable=SC2086\nexec \"${MILL}\" $MILL_FIRST_ARG -D \"mill.main.cli=${MILL_MAIN_CLI}\" \"$@\"\n"
  },
  {
    "path": "modules/build/src/main/java/scala/build/internal/Chdir.java",
    "content": "package scala.build.internal;\n\nimport coursier.exec.ErrnoException;\n\npublic final class Chdir {\n\n  public static boolean available() {\n    return false;\n  }\n\n  public static void chdir(String path) throws ErrnoException {\n    // Not supported on the JVM, returning immediately\n  }\n\n}"
  },
  {
    "path": "modules/build/src/main/java/scala/build/internal/ChdirGraalvm.java",
    "content": "package scala.build.internal;\n\nimport java.io.FileNotFoundException;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport com.oracle.svm.core.headers.LibC;\nimport coursier.exec.ErrnoException;\nimport coursier.exec.GraalvmErrnoExtras;\nimport org.graalvm.nativeimage.c.type.CTypeConversion;\nimport org.graalvm.nativeimage.Platform;\nimport org.graalvm.nativeimage.Platforms;\n\n@TargetClass(className = \"scala.build.internal.Chdir\")\n@Platforms({Platform.LINUX.class, Platform.DARWIN.class})\nfinal class ChdirGraalvm {\n\n  @Substitute\n  public static boolean available() {\n    return true;\n  }\n\n  @Substitute\n  public static void chdir(String path) throws ErrnoException {\n    CTypeConversion.CCharPointerHolder path0 = CTypeConversion.toCString(path);\n    int ret = GraalvmUnistdExtras.chdir(path0.get());\n\n    if (ret != 0) {\n      int n = LibC.errno();\n      Throwable cause = null;\n      if (n == GraalvmErrnoExtras.ENOENT() || n == GraalvmErrnoExtras.ENOTDIR())\n        cause = new FileNotFoundException(path);\n      throw new ErrnoException(n, cause);\n    }\n  }\n\n}"
  },
  {
    "path": "modules/build/src/main/java/scala/build/internal/GraalvmUnistdExtras.java",
    "content": "package scala.build.internal;\n\nimport com.oracle.svm.core.posix.headers.PosixDirectives;\nimport org.graalvm.nativeimage.Platform;\nimport org.graalvm.nativeimage.Platforms;\nimport org.graalvm.nativeimage.c.CContext;\nimport org.graalvm.nativeimage.c.function.CFunction;\nimport org.graalvm.nativeimage.c.type.CCharPointer;\nimport org.graalvm.nativeimage.c.type.CCharPointerPointer;\n\n@CContext(PosixDirectives.class)\n@Platforms({Platform.LINUX.class, Platform.DARWIN.class})\npublic class GraalvmUnistdExtras {\n\n    @CFunction\n    public static native int chdir(CCharPointer path);\n\n}\n"
  },
  {
    "path": "modules/build/src/main/java/scala/build/internal/JavaParserProxyMakerSubst.java",
    "content": "package scala.build.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\n\nimport java.util.function.Supplier;\n\n/**\n * This makes [[JavaParserProxyMaker.get]] provide a [[JavaParserProxyBinary]]\n * rather than a [[JavaParserProxyJvm]], from native launchers.\n *\n * See [[JavaParserProxyMaker]] for more details.\n */\n@TargetClass(className = \"scala.build.internal.JavaParserProxyMaker\")\npublic final class JavaParserProxyMakerSubst {\n  @Substitute\n  public JavaParserProxy get(\n    Object archiveCache,\n    scala.Option<String> javaClassNameVersionOpt,\n    scala.build.Logger logger,\n    Supplier<String> javaCommand\n  ) {\n    return new JavaParserProxyBinary(archiveCache, logger, javaClassNameVersionOpt, javaCommand);\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/Bloop.scala",
    "content": "package scala.build\n\nimport bloop.rifle.{BloopRifleConfig, BuildServer}\nimport ch.epfl.scala.bsp4j\nimport coursier.cache.FileCache\nimport coursier.util.Task\nimport dependency.parser.ModuleParser\nimport dependency.{AnyDependency, DependencyLike, ScalaParameters, ScalaVersion}\n\nimport java.io.{File, IOException}\n\nimport scala.annotation.tailrec\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.{BuildException, ModuleFormatError}\nimport scala.build.internal.CsLoggerUtil.*\nimport scala.concurrent.ExecutionException\nimport scala.concurrent.duration.FiniteDuration\nimport scala.jdk.CollectionConverters.*\n\nobject Bloop {\n\n  private object BrokenPipeInCauses {\n    @tailrec\n    def unapply(ex: Throwable): Option[IOException] =\n      ex match {\n        case null                                                           => None\n        case ex: IOException if ex.getMessage == \"Broken pipe\"              => Some(ex)\n        case ex: IOException if ex.getMessage == \"Connection reset by peer\" => Some(ex)\n        case _                                                              => unapply(ex.getCause)\n      }\n  }\n\n  def compile(\n    projectName: String,\n    buildServer: BuildServer,\n    logger: Logger,\n    buildTargetsTimeout: FiniteDuration\n  ): Either[Throwable, Boolean] =\n    try retry()(logger) {\n        logger.debug(\"Listing BSP build targets\")\n        val results = buildServer.workspaceBuildTargets()\n          .get(buildTargetsTimeout.length, buildTargetsTimeout.unit)\n        val buildTargetOpt = results.getTargets.asScala.find(_.getDisplayName == projectName)\n\n        val buildTarget = buildTargetOpt.getOrElse {\n          throw new Exception(\n            s\"Expected to find project '$projectName' in build targets (only got ${results.getTargets\n                .asScala.map(\"'\" + _.getDisplayName + \"'\").mkString(\", \")})\"\n          )\n        }\n\n        logger.debug(s\"Compiling $projectName with Bloop\")\n        val compileRes = buildServer.buildTargetCompile(\n          new bsp4j.CompileParams(List(buildTarget.getId).asJava)\n        ).get()\n\n        val success = compileRes.getStatusCode == bsp4j.StatusCode.OK\n        logger.debug(if (success) \"Compilation succeeded\" else \"Compilation failed\")\n        Right(success)\n      }\n    catch {\n      case ex @ BrokenPipeInCauses(_) =>\n        logger.debug(s\"Caught $ex while exchanging with Bloop server, assuming Bloop server exited\")\n        Left(ex)\n      case ex: ExecutionException =>\n        logger.debug(\n          s\"Caught $ex while exchanging with Bloop server, you may consider restarting the build server\"\n        )\n        Left(ex)\n    }\n  def bloopClassPath(\n    dep: AnyDependency,\n    params: ScalaParameters,\n    logger: Logger,\n    cache: FileCache[Task]\n  ): Either[BuildException, Seq[File]] =\n    either {\n      val res = value {\n        Artifacts.artifacts(\n          Seq(Positioned.none(dep)),\n          Seq(\n            coursier.Repositories.centralMavenSnapshots,\n            RepositoryUtils.snapshotsRepository,\n            RepositoryUtils.scala3NightlyRepository\n          ),\n          Some(params),\n          logger,\n          cache.withMessage(s\"Downloading compilation server ${dep.version}\")\n        )\n      }\n      res.map(_._2.toIO)\n    }\n\n  def bloopClassPath(\n    logger: Logger,\n    cache: FileCache[Task]\n  ): Either[BuildException, Seq[File]] =\n    bloopClassPath(logger, cache, BloopRifleConfig.defaultVersion)\n\n  def bloopClassPath(\n    logger: Logger,\n    cache: FileCache[Task],\n    bloopVersion: String\n  ): Either[BuildException, Seq[File]] = either {\n    val moduleStr = BloopRifleConfig.defaultModule\n    val mod       = value {\n      ModuleParser.parse(moduleStr)\n        .left.map(err => new ModuleFormatError(moduleStr, err, Some(\"Bloop\")))\n    }\n    val dep    = DependencyLike(mod, bloopVersion)\n    val sv     = BloopRifleConfig.defaultScalaVersion\n    val sbv    = ScalaVersion.binary(sv)\n    val params = ScalaParameters(sv, sbv)\n    value(bloopClassPath(dep, params, logger, cache))\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/BloopBuildClient.scala",
    "content": "package scala.build\n\nimport ch.epfl.scala.bsp4j\n\nimport scala.build.options.Scope\n\ntrait BloopBuildClient extends bsp4j.BuildClient {\n  def setProjectParams(newParams: Seq[String]): Unit\n  def setGeneratedSources(scope: Scope, newGeneratedSources: Seq[GeneratedSource]): Unit\n  def diagnostics: Option[Seq[(Either[String, os.Path], bsp4j.Diagnostic)]]\n  def clear(): Unit\n}\n\nobject BloopBuildClient {\n  def create(\n    logger: Logger,\n    keepDiagnostics: Boolean\n  ): BloopBuildClient =\n    new ConsoleBloopBuildClient(\n      logger,\n      keepDiagnostics\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/Build.scala",
    "content": "package scala.build\n\nimport ch.epfl.scala.bsp4j\nimport com.swoval.files.FileTreeViews.Observer\nimport com.swoval.files.{PathWatcher, PathWatchers}\nimport dependency.ScalaParameters\n\nimport java.io.File\nimport java.nio.file.FileSystemException\nimport java.util.concurrent.{ScheduledExecutorService, ScheduledFuture}\n\nimport scala.annotation.tailrec\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.compiler.{ScalaCompiler, ScalaCompilerMaker}\nimport scala.build.errors.*\nimport scala.build.input.*\nimport scala.build.internal.resource.ResourceMapper\nimport scala.build.internal.{Constants, MainClass, Name, Util}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.build.options.*\nimport scala.build.options.validation.ValidationException\nimport scala.build.postprocessing.*\nimport scala.build.postprocessing.LineConversion.scalaLineToScLineShift\nimport scala.collection.mutable.ListBuffer\nimport scala.compiletime.uninitialized\nimport scala.concurrent.duration.DurationInt\nimport scala.util.Properties\nimport scala.util.control.NonFatal\n\ntrait Build {\n  def inputs: Inputs\n  def options: BuildOptions\n  def scope: Scope\n  def outputOpt: Option[os.Path]\n  def success: Boolean\n  def cancelled: Boolean\n  def diagnostics: Option[Seq[(Either[String, os.Path], bsp4j.Diagnostic)]]\n\n  def successfulOpt: Option[Build.Successful]\n}\n\nobject Build {\n\n  final case class Successful(\n    inputs: Inputs,\n    options: BuildOptions,\n    scalaParams: Option[ScalaParameters],\n    scope: Scope,\n    sources: Sources,\n    artifacts: Artifacts,\n    project: Project,\n    output: os.Path,\n    diagnostics: Option[Seq[(Either[String, os.Path], bsp4j.Diagnostic)]],\n    generatedSources: Seq[GeneratedSource],\n    isPartial: Boolean,\n    logger: Logger\n  ) extends Build {\n    def success: Boolean                         = true\n    def cancelled: Boolean                       = false\n    def successfulOpt: Some[this.type]           = Some(this)\n    def outputOpt: Some[os.Path]                 = Some(output)\n    def dependencyClassPath: Seq[os.Path]        = sources.resourceDirs ++ artifacts.classPath\n    def dependencyCompileClassPath: Seq[os.Path] =\n      sources.resourceDirs ++ artifacts.compileClassPath\n    def fullClassPath: Seq[os.Path]        = Seq(output) ++ dependencyClassPath\n    def fullCompileClassPath: Seq[os.Path] = fullClassPath ++ dependencyCompileClassPath\n    private lazy val mainClassesFoundInProject: Seq[String] = MainClass.find(output, logger).sorted\n    private lazy val mainClassesFoundOnExtraClasspath: Seq[String] =\n      options.classPathOptions.extraClassPath.flatMap(MainClass.find(_, logger)).sorted\n    private lazy val mainClassesFoundInUserExtraDependencies: Seq[String] =\n      artifacts.jarsForUserExtraDependencies.flatMap(MainClass.findInDependency).sorted\n    def foundMainClasses(): Seq[String] = {\n      val found = mainClassesFoundInProject ++ mainClassesFoundOnExtraClasspath\n      if inputs.isEmpty && found.isEmpty then mainClassesFoundInUserExtraDependencies else found\n    }\n    def retainedMainClass(\n      mainClasses: Seq[String],\n      commandString: String,\n      logger: Logger\n    ): Either[BuildException, String] = {\n      val defaultMainClassOpt = sources.defaultMainClass\n        .filter(name => mainClasses.contains(name))\n      def foundMainClass: Either[BuildException, String] =\n        mainClasses match {\n          case Seq()          => Left(new NoMainClassFoundError)\n          case Seq(mainClass) => Right(mainClass)\n          case _              =>\n            inferredMainClass(mainClasses, logger)\n              .left.flatMap { mainClasses =>\n                // decode the names to present them to the user,\n                // but keep the link to each original name to account for package prefixes:\n                // \"pack.Main$minus1\" decodes to \"pack.Main-1\", which encodes back to \"pack$u002EMain$minus1\"\n                //  ^^^^^^^^^^^^^^^^----------------NOT THE SAME-----------------------^^^^^^^^^^^^^^^^^^^^^\n                val decodedToEncoded = mainClasses.map(mc => Name.decoded(mc) -> mc).toMap\n\n                options.interactive.flatMap { interactive =>\n                  interactive\n                    .chooseOne(\n                      \"Found several main classes. Which would you like to run?\",\n                      decodedToEncoded.keys.toList\n                    )\n                    .map(decodedToEncoded(_)) // encode back the name of the chosen class\n                    .toRight {\n                      SeveralMainClassesFoundError(\n                        mainClasses = ::(mainClasses.head, mainClasses.tail.toList),\n                        commandString = commandString,\n                        positions = Nil\n                      )\n                    }\n                }\n              }\n        }\n\n      defaultMainClassOpt match {\n        case Some(cls) => Right(cls)\n        case None      => foundMainClass\n      }\n    }\n    private def inferredMainClass(\n      mainClasses: Seq[String],\n      logger: Logger\n    ): Either[Seq[String], String] = {\n      val scriptInferredMainClasses =\n        sources.inMemory.collect {\n          case Sources.InMemory(_, _, _, Some(wrapperParams)) =>\n            wrapperParams.mainClass\n        }\n          .filter(mainClasses.contains(_))\n      val rawInputInferredMainClasses =\n        mainClasses\n          .filterNot(scriptInferredMainClasses.contains(_))\n          .filterNot(mainClassesFoundOnExtraClasspath.contains(_))\n          .filterNot(mainClassesFoundInUserExtraDependencies.contains(_))\n      val extraClasspathInferredMainClasses =\n        mainClassesFoundOnExtraClasspath.filter(mainClasses.contains(_))\n      val userExtraDependenciesInferredMainClasses =\n        mainClassesFoundInUserExtraDependencies.filter(mainClasses.contains(_))\n\n      def logMessageOnLesserPriorityMainClasses(\n        pickedMainClass: String,\n        mainClassDescriptor: String,\n        lesserPriorityMainClasses: Seq[String]\n      ): Unit =\n        if lesserPriorityMainClasses.nonEmpty then {\n          val first          = lesserPriorityMainClasses.head\n          val completeString = lesserPriorityMainClasses.mkString(\", \")\n          logger.message(\n            s\"\"\"Running $pickedMainClass. Also detected $mainClassDescriptor: $completeString\n               |You can run any one of them by passing option --main-class, i.e. --main-class $first\n               |All available main classes can always be listed by passing option --list-main-classes\"\"\".stripMargin\n          )\n        }\n\n      (\n        rawInputInferredMainClasses,\n        scriptInferredMainClasses,\n        extraClasspathInferredMainClasses,\n        userExtraDependenciesInferredMainClasses\n      ) match {\n        case (Seq(pickedMainClass), scriptInferredMainClasses, _, _) =>\n          logMessageOnLesserPriorityMainClasses(\n            pickedMainClass = pickedMainClass,\n            mainClassDescriptor = \"script main classes\",\n            lesserPriorityMainClasses = scriptInferredMainClasses\n          )\n          Right(pickedMainClass)\n        case (rawMcs, scriptMcs, extraCpMcs, userExtraDepsMcs) if rawMcs.length > 1 =>\n          Left(rawMcs ++ scriptMcs ++ extraCpMcs ++ userExtraDepsMcs)\n        case (Nil, Seq(pickedMainClass), _, _) => Right(pickedMainClass)\n        case (Nil, scriptMcs, extraCpMcs, userExtraDepsMcs) if scriptMcs.length > 1 =>\n          Left(scriptMcs ++ extraCpMcs ++ userExtraDepsMcs)\n        case (Nil, Nil, Seq(pickedMainClass), userExtraDepsMcs) =>\n          logMessageOnLesserPriorityMainClasses(\n            pickedMainClass = pickedMainClass,\n            mainClassDescriptor = \"other main classes in dependencies\",\n            lesserPriorityMainClasses = userExtraDepsMcs\n          )\n          Right(pickedMainClass)\n        case (Nil, Nil, extraCpMcs, userExtraDepsMcs) if extraCpMcs.length > 1 =>\n          Left(extraCpMcs ++ userExtraDepsMcs)\n        case (Nil, Nil, Nil, Seq(pickedMainClass)) => Right(pickedMainClass)\n        case (Nil, Nil, Nil, userExtraDepsMcs) if userExtraDepsMcs.length > 1 =>\n          Left(userExtraDepsMcs)\n        case (rawMcs, scriptMcs, extraCpMcs, userExtraDepsMcs) =>\n          Left(rawMcs ++ scriptMcs ++ extraCpMcs ++ userExtraDepsMcs)\n      }\n    }\n    def retainedMainClassOpt(\n      mainClasses: Seq[String],\n      logger: Logger\n    ): Option[String] = {\n      val defaultMainClassOpt = sources.defaultMainClass\n        .filter(name => mainClasses.contains(name))\n      def foundMainClass =\n        mainClasses match {\n          case Seq()          => None\n          case Seq(mainClass) => Some(mainClass)\n          case _              => inferredMainClass(mainClasses, logger).toOption\n        }\n\n      defaultMainClassOpt.orElse(foundMainClass)\n    }\n\n    def crossKey: CrossKey = {\n      val optKey = scalaParams.map { params =>\n        BuildOptions.CrossKey(\n          params.scalaVersion,\n          options.platform.value\n        )\n      }\n      CrossKey(optKey, scope)\n    }\n  }\n\n  final case class Failed(\n    inputs: Inputs,\n    options: BuildOptions,\n    scope: Scope,\n    sources: Sources,\n    artifacts: Artifacts,\n    project: Project,\n    diagnostics: Option[Seq[(Either[String, os.Path], bsp4j.Diagnostic)]]\n  ) extends Build {\n    def success: Boolean = false\n\n    override def cancelled: Boolean = false\n    def successfulOpt: None.type    = None\n    def outputOpt: None.type        = None\n  }\n\n  final case class Cancelled(\n    inputs: Inputs,\n    options: BuildOptions,\n    scope: Scope,\n    reason: String\n  ) extends Build {\n    def success: Boolean         = false\n    def cancelled: Boolean       = true\n    def successfulOpt: None.type = None\n    def outputOpt: None.type     = None\n    def diagnostics: None.type   = None\n  }\n\n  /** If some options are manually overridden, append a hash of the options to the project name\n    * Using only the command-line options not the ones from the sources.\n    */\n  def updateInputs(\n    inputs: Inputs,\n    options: BuildOptions\n  ): Inputs = {\n\n    // If some options are manually overridden, append a hash of the options to the project name\n    // Using options, not options0 - only the command-line options are taken into account. No hash is\n    // appended for options from the sources.\n    val optionsHash = options.hash\n\n    inputs.copy(baseProjectName = inputs.baseProjectName + optionsHash.fold(\"\")(\"_\" + _))\n  }\n\n  private def allInputs(\n    inputs: Inputs,\n    options: BuildOptions,\n    logger: Logger\n  )(using ScalaCliInvokeData) =\n    CrossSources.forInputs(\n      inputs,\n      Sources.defaultPreprocessors(\n        archiveCache = options.archiveCache,\n        javaClassNameVersionOpt = options.internal.javaClassNameVersionOpt,\n        javaCommand = () => options.javaHome().value.javaCommand\n      ),\n      logger,\n      options.suppressWarningOptions,\n      options.internal.exclude,\n      download = options.downloader\n    )\n\n  private def build(\n    inputs: Inputs,\n    crossSources: CrossSources,\n    options: BuildOptions,\n    logger: Logger,\n    buildClient: BloopBuildClient,\n    compiler: ScalaCompiler,\n    docCompilerOpt: Option[ScalaCompiler],\n    crossBuilds: Boolean,\n    buildTests: Boolean,\n    partial: Option[Boolean],\n    actionableDiagnostics: Option[Boolean]\n  )(using ScalaCliInvokeData): Either[BuildException, Builds] = either {\n    val sharedOptions = crossSources.sharedOptions(options)\n    val crossOptions  = sharedOptions.crossOptions\n\n    def doPostProcess(build: Build, inputs: Inputs, scope: Scope): Unit = build match {\n      case build: Build.Successful =>\n        for (sv <- build.project.scalaCompiler.map(_.scalaVersion))\n          postProcess(\n            generatedSources = build.generatedSources,\n            generatedSrcRoot = inputs.generatedSrcRoot(scope),\n            classesDir = build.output,\n            logger = logger,\n            workspace = inputs.workspace,\n            updateSemanticDbs = true,\n            scalaVersion = sv,\n            buildOptions = build.options\n          ).left.foreach(_.foreach(logger.message(_)))\n      case _ =>\n    }\n\n    final case class NonCrossBuilds(\n      main: Build,\n      testOpt: Option[Build],\n      docOpt: Option[Build],\n      testDocOpt: Option[Build]\n    )\n\n    def doBuild(overrideOptions: BuildOptions): Either[BuildException, NonCrossBuilds] = either {\n\n      val inputs0 = updateInputs(\n        inputs,\n        overrideOptions.orElse(options) // update hash in inputs with options coming from the CLI or cross-building, not from the sources\n      )\n\n      val baseOptions = overrideOptions.orElse(sharedOptions)\n\n      val scopedSources: ScopedSources = value(crossSources.scopedSources(baseOptions))\n\n      val mainSources: Sources =\n        value(scopedSources.sources(Scope.Main, baseOptions, inputs.workspace, logger))\n      val mainOptions = mainSources.buildOptions\n\n      val testSources: Sources =\n        value(scopedSources.sources(Scope.Test, baseOptions, inputs.workspace, logger))\n      val testOptions = testSources.buildOptions\n\n      def doBuildScope(\n        options: BuildOptions,\n        sources: Sources,\n        scope: Scope,\n        actualCompiler: ScalaCompiler = compiler\n      ): Either[BuildException, Build] =\n        either {\n          val sources0 = sources.withVirtualDir(inputs0, scope, options)\n\n          val generatedSources = sources0.generateSources(inputs0.generatedSrcRoot(scope))\n\n          val res = build(\n            inputs = inputs0,\n            sources = sources0,\n            generatedSources = generatedSources,\n            options = options,\n            scope = scope,\n            logger = logger,\n            buildClient = buildClient,\n            compiler = actualCompiler,\n            buildTests = buildTests,\n            partial = partial,\n            actionableDiagnostics = actionableDiagnostics\n          )\n\n          value(res)\n        }\n\n      val mainBuild       = value(doBuildScope(mainOptions, mainSources, Scope.Main))\n      val mainDocBuildOpt = docCompilerOpt match {\n        case None              => None\n        case Some(docCompiler) =>\n          Some(value(doBuildScope(\n            options = mainOptions,\n            sources = mainSources,\n            scope = Scope.Main,\n            actualCompiler = docCompiler\n          )))\n      }\n\n      def testBuildOpt(doc: Boolean = false): Either[BuildException, Option[Build]] = either {\n        if buildTests then {\n          val actualCompilerOpt = if doc then docCompilerOpt else Some(compiler)\n          actualCompilerOpt match {\n            case None                 => None\n            case Some(actualCompiler) =>\n              val testBuild = value {\n                mainBuild match {\n                  case s: Build.Successful =>\n                    val extraTestOptions = BuildOptions(\n                      classPathOptions = ClassPathOptions(\n                        extraClassPath = Seq(s.output)\n                      )\n                    )\n                    val testOptions0 = {\n                      val testOrExtra = extraTestOptions.orElse(testOptions)\n                      testOrExtra\n                        .copy(scalaOptions =\n                          // Scala options between scopes need to be compatible\n                          mainOptions.scalaOptions.orElse(testOrExtra.scalaOptions)\n                        )\n                    }\n                    val isScala2 =\n                      value(testOptions0.scalaParams).exists(_.scalaVersion.startsWith(\"2.\"))\n                    val finalSources = if doc && isScala2 then\n                      testSources.withExtraSources(mainSources)\n                    else testSources\n                    doBuildScope(\n                      options = testOptions0,\n                      sources = finalSources,\n                      scope = Scope.Test,\n                      actualCompiler = actualCompiler\n                    )\n                  case _ =>\n                    Right(Build.Cancelled(\n                      inputs,\n                      sharedOptions,\n                      Scope.Test,\n                      \"Parent build failed or cancelled\"\n                    ))\n                }\n              }\n              Some(testBuild)\n          }\n        }\n        else None\n      }\n\n      val testBuildOpt0 = value(testBuildOpt())\n      doPostProcess(mainBuild, inputs0, Scope.Main)\n      for (testBuild <- testBuildOpt0)\n        doPostProcess(testBuild, inputs0, Scope.Test)\n\n      val docTestBuildOpt0 = value(testBuildOpt(doc = true))\n\n      NonCrossBuilds(mainBuild, testBuildOpt0, mainDocBuildOpt, docTestBuildOpt0)\n    }\n\n    def buildScopes(): Either[BuildException, Builds] =\n      either {\n        val nonCrossBuilds = value(doBuild(BuildOptions()))\n\n        val (extraMainBuilds, extraTestBuilds, extraDocBuilds, extraDocTestBuilds) =\n          if crossBuilds then {\n            val extraBuilds = value {\n              val maybeBuilds = crossOptions.map(doBuild)\n              maybeBuilds\n                .sequence\n                .left.map(CompositeBuildException(_))\n            }\n            (\n              extraBuilds.map(_.main),\n              extraBuilds.flatMap(_.testOpt),\n              extraBuilds.flatMap(_.docOpt),\n              extraBuilds.flatMap(_.testDocOpt)\n            )\n          }\n          else {\n            if crossOptions.nonEmpty then {\n              val crossBuildParams: Seq[CrossBuildParams] = crossOptions.map(CrossBuildParams(_))\n              logger.message(\n                s\"\"\"Cross-building is disabled, ignoring ${crossOptions.length} builds:\n                   |  ${crossBuildParams.map(_.asString).mkString(\"\\n  \")}\n                   |Cross builds are only available when the --cross option is passed.\n                   |Defaulting to ${CrossBuildParams(options).asString}\"\"\".stripMargin\n              )\n            }\n            (Nil, Nil, Nil, Nil)\n          }\n\n        Builds(\n          builds = Seq(nonCrossBuilds.main) ++ nonCrossBuilds.testOpt.toSeq,\n          crossBuilds = Seq(extraMainBuilds, extraTestBuilds),\n          docBuilds = nonCrossBuilds.docOpt.toSeq ++ nonCrossBuilds.testDocOpt.toSeq,\n          docCrossBuilds = Seq(extraDocBuilds, extraDocTestBuilds)\n        )\n      }\n\n    val builds = value(buildScopes())\n\n    ResourceMapper.copyResourceToClassesDir(builds.main)\n    for (testBuild <- builds.get(Scope.Test))\n      ResourceMapper.copyResourceToClassesDir(testBuild)\n\n    if actionableDiagnostics.getOrElse(true) then {\n      val projectOptions = builds.get(Scope.Test).getOrElse(builds.main).options\n      projectOptions.logActionableDiagnostics(logger)\n    }\n\n    builds\n  }\n\n  private def build(\n    inputs: Inputs,\n    sources: Sources,\n    generatedSources: Seq[GeneratedSource],\n    options: BuildOptions,\n    scope: Scope,\n    logger: Logger,\n    buildClient: BloopBuildClient,\n    compiler: ScalaCompiler,\n    buildTests: Boolean,\n    partial: Option[Boolean],\n    actionableDiagnostics: Option[Boolean]\n  )(using ScalaCliInvokeData): Either[BuildException, Build] = either {\n\n    val build0 = value {\n      buildOnce(\n        inputs = inputs,\n        sources = sources,\n        generatedSources = generatedSources,\n        options = options,\n        scope = scope,\n        logger = logger,\n        buildClient = buildClient,\n        compiler = compiler,\n        partialOpt = partial\n      )\n    }\n\n    build0 match {\n      case successful: Successful =>\n        if options.jmhOptions.canRunJmh && scope == Scope.Main then\n          value {\n            val res = jmhBuild(\n              inputs = inputs,\n              build = successful,\n              logger = logger,\n              successful.options.javaHome().value.javaCommand,\n              buildClient = buildClient,\n              compiler = compiler,\n              buildTests = buildTests,\n              actionableDiagnostics = actionableDiagnostics\n            )\n            res.flatMap {\n              case Some(b) => Right(b)\n              case None    => Left(new JmhBuildFailedError)\n            }\n          }\n        else\n          build0\n      case _ => build0\n    }\n  }\n\n  def projectRootDir(root: os.Path, projectName: String): os.Path =\n    root / Constants.workspaceDirName / projectName\n  def classesRootDir(root: os.Path, projectName: String): os.Path =\n    projectRootDir(root, projectName) / \"classes\"\n  def classesDir(root: os.Path, projectName: String, scope: Scope, suffix: String = \"\"): os.Path =\n    classesRootDir(root, projectName) / s\"${scope.name}$suffix\"\n\n  def resourcesRegistry(\n    root: os.Path,\n    projectName: String,\n    scope: Scope\n  ): os.Path =\n    root / Constants.workspaceDirName / projectName / s\"resources-${scope.name}\"\n\n  def scalaNativeSupported(\n    options: BuildOptions,\n    inputs: Inputs,\n    logger: Logger\n  ): Either[BuildException, Option[ScalaNativeCompatibilityError]] =\n    either {\n      val scalaParamsOpt = value(options.scalaParams)\n      scalaParamsOpt.flatMap { scalaParams =>\n        val scalaVersion       = scalaParams.scalaVersion\n        val nativeVersionMaybe = options.scalaNativeOptions.numeralVersion\n        def snCompatError      =\n          Left(\n            new ScalaNativeCompatibilityError(\n              scalaVersion,\n              options.scalaNativeOptions.finalVersion\n            )\n          )\n        def warnIncompatibleNativeOptions(numeralVersion: SNNumeralVersion) =\n          if numeralVersion < SNNumeralVersion(0, 4, 4)\n            && options.scalaNativeOptions.embedResources.isDefined\n          then\n            logger.diagnostic(\n              \"This Scala Version cannot embed resources, regardless of the options used.\"\n            )\n\n        val numeralOrError: Either[ScalaNativeCompatibilityError, SNNumeralVersion] =\n          nativeVersionMaybe match {\n            case Some(snNumeralVer) =>\n              if snNumeralVer < SNNumeralVersion(0, 4, 1) && Properties.isWin then snCompatError\n              else if scalaVersion.startsWith(\"3.0\") then snCompatError\n              else if scalaVersion.startsWith(\"3\") then\n                if snNumeralVer >= SNNumeralVersion(0, 4, 3)\n                then Right(snNumeralVer)\n                else snCompatError\n              else if scalaVersion.startsWith(\"2.13\") then Right(snNumeralVer)\n              else if scalaVersion.startsWith(\"2.12\") then\n                if inputs.sourceFiles().forall {\n                    case _: AnyScript => snNumeralVer >= SNNumeralVersion(0, 4, 3)\n                    case _            => true\n                  }\n                then Right(snNumeralVer)\n                else snCompatError\n              else snCompatError\n            case None => snCompatError\n          }\n\n        numeralOrError match {\n          case Left(compatError)       => Some(compatError)\n          case Right(snNumeralVersion) =>\n            warnIncompatibleNativeOptions(snNumeralVersion)\n            None\n        }\n      }\n    }\n\n  def build(\n    inputs: Inputs,\n    options: BuildOptions,\n    compilerMaker: ScalaCompilerMaker,\n    docCompilerMakerOpt: Option[ScalaCompilerMaker],\n    logger: Logger,\n    crossBuilds: Boolean,\n    buildTests: Boolean,\n    partial: Option[Boolean],\n    actionableDiagnostics: Option[Boolean]\n  )(using ScalaCliInvokeData): Either[BuildException, Builds] = either {\n    val buildClient = BloopBuildClient.create(\n      logger = logger,\n      keepDiagnostics = options.internal.keepDiagnostics\n    )\n    val classesDir0                           = classesRootDir(inputs.workspace, inputs.projectName)\n    val (crossSources: CrossSources, inputs0) = value(allInputs(inputs, options, logger))\n    val buildOptions                          = crossSources.sharedOptions(options)\n    if !buildOptions.suppressWarningOptions.suppressDeprecatedFeatureWarning.getOrElse(false) &&\n      buildOptions.scalaParams.exists(_.exists(_.scalaVersion == \"2.12.4\") &&\n      !buildOptions.useBuildServer.contains(false))\n    then\n      logger.message(\n        s\"\"\"[${Console.YELLOW}warn${Console.RESET}] Scala 2.12.4 has been deprecated for use with Bloop.\n           |[${Console.YELLOW}warn${Console.RESET}] It may lead to infinite compilation.\n           |[${Console.YELLOW}warn${Console.RESET}] To disable the build server, pass ${Console.BOLD}--server=false${Console.RESET}.\n           |[${Console.YELLOW}warn${Console.RESET}] Refer to https://github.com/VirtusLab/scala-cli/issues/1382 and https://github.com/sbt/zinc/issues/1010\"\"\".stripMargin\n      )\n    value {\n      compilerMaker.withCompiler(\n        workspace = inputs0.workspace / Constants.workspaceDirName,\n        classesDir = classesDir0,\n        buildClient = buildClient,\n        logger = logger,\n        buildOptions = buildOptions\n      ) { compiler =>\n        docCompilerMakerOpt match {\n          case None =>\n            logger.debug(\"No doc compiler provided, skipping\")\n            build(\n              inputs = inputs0,\n              crossSources = crossSources,\n              options = options,\n              logger = logger,\n              buildClient = buildClient,\n              compiler = compiler,\n              docCompilerOpt = None,\n              crossBuilds = crossBuilds,\n              buildTests = buildTests,\n              partial = partial,\n              actionableDiagnostics = actionableDiagnostics\n            )\n          case Some(docCompilerMaker) =>\n            docCompilerMaker.withCompiler(\n              workspace = inputs0.workspace / Constants.workspaceDirName,\n              classesDir = classesDir0, // ???\n              buildClient = buildClient,\n              logger = logger,\n              buildOptions = buildOptions\n            ) { docCompiler =>\n              build(\n                inputs = inputs0,\n                crossSources = crossSources,\n                options = options,\n                logger = logger,\n                buildClient = buildClient,\n                compiler = compiler,\n                docCompilerOpt = Some(docCompiler),\n                crossBuilds = crossBuilds,\n                buildTests = buildTests,\n                partial = partial,\n                actionableDiagnostics = actionableDiagnostics\n              )\n            }\n        }\n      }\n    }\n  }\n\n  def validate(\n    logger: Logger,\n    options: BuildOptions\n  ): Either[BuildException, Unit] = {\n    val (errors, otherDiagnostics) = options.validate.partition(_.severity == Severity.Error)\n    logger.log(otherDiagnostics)\n    if errors.nonEmpty then Left(CompositeBuildException(errors.map(new ValidationException(_))))\n    else Right(())\n  }\n\n  def watch(\n    inputs: Inputs,\n    options: BuildOptions,\n    compilerMaker: ScalaCompilerMaker,\n    docCompilerMakerOpt: Option[ScalaCompilerMaker],\n    logger: Logger,\n    crossBuilds: Boolean,\n    buildTests: Boolean,\n    partial: Option[Boolean],\n    actionableDiagnostics: Option[Boolean],\n    postAction: () => Unit = () => ()\n  )(action: Either[BuildException, Builds] => Unit)(using ScalaCliInvokeData): Watcher = {\n\n    val buildClient = BloopBuildClient.create(\n      logger,\n      keepDiagnostics = options.internal.keepDiagnostics\n    )\n    val threads     = BuildThreads.create()\n    val classesDir0 = classesRootDir(inputs.workspace, inputs.projectName)\n\n    lazy val compilers: Either[BuildException, (ScalaCompiler, Option[ScalaCompiler])] =\n      either {\n        val (crossSources: CrossSources, inputs0: Inputs) =\n          value(allInputs(inputs, options, logger))\n        val sharedOptions = crossSources.sharedOptions(options)\n        val compiler      = value {\n          compilerMaker.create(\n            workspace = inputs0.workspace / Constants.workspaceDirName,\n            classesDir = classesDir0,\n            buildClient = buildClient,\n            logger = logger,\n            buildOptions = sharedOptions\n          )\n        }\n        val docCompilerOpt = docCompilerMakerOpt.map(_.create(\n          workspace = inputs0.workspace / Constants.workspaceDirName,\n          classesDir = classesDir0,\n          buildClient = buildClient,\n          logger = logger,\n          buildOptions = sharedOptions\n        )).map(value)\n        compiler -> docCompilerOpt\n      }\n\n    def info: Either[BuildException, (ScalaCompiler, Option[ScalaCompiler], CrossSources, Inputs)] =\n      either {\n        val (crossSources: CrossSources, inputs0: Inputs) =\n          value(allInputs(inputs, options, logger))\n        val (compiler, docCompilerOpt) = value(compilers)\n        (compiler, docCompilerOpt, crossSources, inputs0)\n      }\n\n    var res: Either[BuildException, Builds] = null\n\n    def run(): Unit = {\n      try {\n        res =\n          info.flatMap {\n            case (\n                  compiler: ScalaCompiler,\n                  docCompilerOpt: Option[ScalaCompiler],\n                  crossSources: CrossSources,\n                  inputs: Inputs\n                ) =>\n              build(\n                inputs = inputs,\n                crossSources = crossSources,\n                options = options,\n                logger = logger,\n                buildClient = buildClient,\n                compiler = compiler,\n                docCompilerOpt = docCompilerOpt,\n                crossBuilds = crossBuilds,\n                buildTests = buildTests,\n                partial = partial,\n                actionableDiagnostics = actionableDiagnostics\n              )\n          }\n        action(res)\n      }\n      catch {\n        case NonFatal(e) =>\n          Util.printException(e)\n      }\n      postAction()\n    }\n\n    run()\n\n    val watcher =\n      new Watcher(ListBuffer(), threads.fileWatcher, run(), info.foreach(_._1.shutdown()))\n\n    def doWatch(): Unit = either {\n      val (crossSources: CrossSources, inputs0: Inputs) =\n        value(allInputs(inputs, options, logger))\n      val mergedOptions          = crossSources.sharedOptions(options)\n      val elements: Seq[Element] =\n        if res == null then inputs0.elements\n        else\n          res\n            .map { builds =>\n              val allResourceDirectories =\n                crossSources.resourceDirs.map(rd => ResourceDirectory(rd.value))\n              val mainElems = builds.main.inputs.elements\n              val testElems = builds.get(Scope.Test).map(_.inputs.elements).getOrElse(Nil)\n              (mainElems ++ testElems ++ allResourceDirectories).distinct\n            }\n            .getOrElse(inputs.elements)\n      for (elem <- elements) {\n        val depth = elem match {\n          case _: SingleFile => -1\n          case _             => Int.MaxValue\n        }\n        val eventFilter: PathWatchers.Event => Boolean = elem match {\n          case d: Directory =>\n            // Filtering event for directories, to ignore those related to the .bloop directory in particular\n            event =>\n              val p           = os.Path(event.getTypedPath.getPath.toAbsolutePath)\n              val relPath     = p.relativeTo(d.path)\n              val isHidden    = relPath.segments.exists(_.startsWith(\".\"))\n              val pathLast    = relPath.lastOpt.orElse(p.lastOpt).getOrElse(\"\")\n              def isScalaFile = pathLast.endsWith(\".sc\") || pathLast.endsWith(\".scala\")\n              def isJavaFile  = pathLast.endsWith(\".java\")\n              !isHidden && (isScalaFile || isJavaFile)\n          case _ => _ => true\n        }\n\n        val watcher0 = watcher.newWatcher()\n        elem match {\n          case d: OnDisk =>\n            watcher0.register(d.path.toNIO, depth)\n          case _: Virtual =>\n        }\n        watcher0.addObserver {\n          onChangeBufferedObserver(event => if eventFilter(event) then watcher.schedule())\n        }\n      }\n\n      val artifacts = res\n        .map { builds =>\n          def artifacts(build: Build): Seq[os.Path] =\n            build.successfulOpt.toSeq.flatMap(_.artifacts.classPath)\n          val main               = artifacts(builds.main)\n          val test               = builds.get(Scope.Test).map(artifacts).getOrElse(Nil)\n          val allScopesArtifacts = (main ++ test).distinct\n\n          allScopesArtifacts\n            .filterNot(_.segments.contains(Constants.workspaceDirName))\n        }\n        .getOrElse(Nil)\n      for (artifact <- artifacts) {\n        val depth    = if os.isFile(artifact) then -1 else Int.MaxValue\n        val watcher0 = watcher.newWatcher()\n        watcher0.register(artifact.toNIO, depth)\n        watcher0.addObserver(onChangeBufferedObserver(_ => watcher.schedule()))\n      }\n\n      val extraWatchPaths = mergedOptions.watchOptions.extraWatchPaths.distinct\n      for (extraPath <- extraWatchPaths)\n        if os.exists(extraPath) then {\n          val depth    = if os.isFile(extraPath) then -1 else Int.MaxValue\n          val watcher0 = watcher.newWatcher()\n          watcher0.register(extraPath.toNIO, depth)\n          watcher0.addObserver(onChangeBufferedObserver(_ => watcher.schedule()))\n        }\n        else\n          logger.message(s\"$warnPrefix provided watched path doesn't exist: $extraPath\")\n    }\n\n    try doWatch()\n    catch {\n      case NonFatal(e) =>\n        watcher.dispose()\n        throw e\n    }\n\n    watcher\n  }\n\n  def releaseFlag(\n    options: BuildOptions,\n    compilerJvmVersionOpt: Option[Positioned[Int]],\n    logger: Logger\n  ): Option[Int] = {\n    lazy val javaHome = options.javaHome()\n    if compilerJvmVersionOpt.exists(javaHome.value.version > _.value) then {\n      logger.log(List(Diagnostic(\n        Diagnostic.Messages.bloopTooOld,\n        Severity.Warning,\n        javaHome.positions ++ compilerJvmVersionOpt.map(_.positions).getOrElse(Nil)\n      )))\n      None\n    }\n    else if compilerJvmVersionOpt.exists(_.value == 8) then None\n    else if options.scalaOptions.scalacOptions.values.exists(opt =>\n        opt.headOption.exists(_.value.value.startsWith(\"-release\")) ||\n        opt.headOption.exists(_.value.value.startsWith(\"-java-output-version\"))\n      )\n    then None\n    else if compilerJvmVersionOpt.isEmpty && javaHome.value.version == 8 then None\n    else Some(javaHome.value.version)\n  }\n\n  /** Builds a Bloop project.\n    *\n    * @param inputs\n    *   inputs to be included in the project\n    * @param sources\n    *   sources to be included in the project\n    * @param generatedSources\n    *   sources generated by Scala CLI as part of the build\n    * @param options\n    *   build options\n    * @param compilerJvmVersionOpt\n    *   compiler JVM version (optional)\n    * @param scope\n    *   build scope for which the project is to be created\n    * @param logger\n    *   logger\n    * @param maybeRecoverOnError\n    *   a function handling [[BuildException]] instances, possibly recovering them; returns None on\n    *   recovery, Some(e: BuildException) otherwise\n    * @return\n    *   a bloop [[Project]]\n    */\n  def buildProject(\n    inputs: Inputs,\n    sources: Sources,\n    generatedSources: Seq[GeneratedSource],\n    options: BuildOptions,\n    scope: Scope,\n    logger: Logger,\n    artifacts: Artifacts,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)\n  ): Either[BuildException, Project] = either {\n\n    val allSources = sources.paths.map(_._1) ++ generatedSources.map(_.generated)\n\n    val classesDir0 = classesDir(inputs.workspace, inputs.projectName, scope)\n    val scaladocDir = classesDir(inputs.workspace, inputs.projectName, scope, suffix = \"-doc\")\n\n    val generateSemanticDbs =\n      options.scalaOptions.semanticDbOptions.generateSemanticDbs.getOrElse(false)\n    val semanticDbTargetRoot = options.scalaOptions.semanticDbOptions.semanticDbTargetRoot\n    val semanticDbSourceRoot =\n      options.scalaOptions.semanticDbOptions.semanticDbSourceRoot.getOrElse(inputs.workspace)\n\n    val scalaCompilerParamsOpt = artifacts.scalaOpt match {\n      case Some(scalaArtifacts) =>\n        val params = value {\n          options.scalaParams match {\n            case Left(buildException) if maybeRecoverOnError(buildException).isEmpty =>\n              Right(None) // this will effectively try to fall back to a pure Java build\n            case otherwise => otherwise\n          }\n        }.getOrElse {\n          sys.error(\n            \"Should not happen (inconsistency between Scala parameters in BuildOptions and ScalaArtifacts)\"\n          )\n        }\n\n        val pluginScalacOptions = scalaArtifacts.compilerPlugins.map {\n          case (_, _, path) =>\n            ScalacOpt(s\"-Xplugin:$path\")\n        }.distinct\n\n        val semanticDbTargetRootOptions: Seq[ScalacOpt] =\n          (semanticDbTargetRoot match\n            case Some(targetRoot) if params.scalaVersion.startsWith(\"2.\") =>\n              Seq(s\"-P:semanticdb:targetroot:$targetRoot\")\n            case Some(targetRoot) => Seq(\"-semanticdb-target\", targetRoot.toString)\n            case None             => Nil\n          ).map(ScalacOpt(_))\n        val semanticDbScalacOptions: Seq[ScalacOpt] =\n          if generateSemanticDbs then\n            semanticDbTargetRootOptions ++ (\n              if params.scalaVersion.startsWith(\"2.\") then\n                Seq(\n                  \"-Yrangepos\",\n                  \"-P:semanticdb:failures:warning\",\n                  \"-P:semanticdb:synthetics:on\",\n                  s\"-P:semanticdb:sourceroot:$semanticDbSourceRoot\"\n                )\n              else Seq(\"-Xsemanticdb\", \"-sourceroot\", semanticDbSourceRoot.toString)\n            ).map(ScalacOpt(_))\n          else Nil\n\n        val sourceRootScalacOptions =\n          if params.scalaVersion.startsWith(\"2.\")\n          then Nil\n          else Seq(\"-sourceroot\", inputs.workspace.toString).map(ScalacOpt(_))\n\n        val scalaJsScalacOptions =\n          if options.platform.value == Platform.JS && !params.scalaVersion.startsWith(\"2.\")\n          then Seq(ScalacOpt(\"-scalajs\"))\n          else Nil\n\n        val scalapyOptions =\n          if params.scalaVersion.startsWith(\"2.13.\") &&\n            options.notForBloopOptions.python.getOrElse(false)\n          then Seq(ScalacOpt(\"-Yimports:java.lang,scala,scala.Predef,me.shadaj.scalapy\"))\n          else Nil\n\n        val scalacOptions =\n          options.scalaOptions.scalacOptions.map(_.value) ++\n            pluginScalacOptions ++\n            semanticDbScalacOptions ++\n            sourceRootScalacOptions ++\n            scalaJsScalacOptions ++\n            scalapyOptions\n\n        val compilerParams = ScalaCompilerParams(\n          scalaVersion = params.scalaVersion,\n          scalaBinaryVersion = params.scalaBinaryVersion,\n          scalacOptions = scalacOptions.toSeq.map(_.value),\n          compilerClassPath = scalaArtifacts.compilerClassPath,\n          bridgeJarsOpt = scalaArtifacts.bridgeJarsOpt.map(_.headOption.toSeq)\n        )\n        Some(compilerParams)\n\n      case None =>\n        None\n    }\n\n    val javacOptions = {\n\n      val semanticDbJavacOptions =\n        // FIXME Should this be in scalaOptions, now that we use it for javac stuff too?\n        if generateSemanticDbs then {\n          // from https://github.com/scalameta/metals/blob/04405c0401121b372ea1971c361e05108fb36193/metals/src/main/scala/scala/meta/internal/metals/JavaInteractiveSemanticdb.scala#L137-L146\n          val compilerPackages = Seq(\n            \"com.sun.tools.javac.api\",\n            \"com.sun.tools.javac.code\",\n            \"com.sun.tools.javac.model\",\n            \"com.sun.tools.javac.tree\",\n            \"com.sun.tools.javac.util\"\n          )\n          val exports = compilerPackages.flatMap { pkg =>\n            Seq(\"-J--add-exports\", s\"-Jjdk.compiler/$pkg=ALL-UNNAMED\")\n          }\n\n          val javacTargetRoot = semanticDbTargetRoot.getOrElse(\"javac-classes-directory\")\n          Seq(\n            // does the path need to be escaped somehow?\n            s\"-Xplugin:semanticdb -sourceroot:$semanticDbSourceRoot -targetroot:$javacTargetRoot\"\n          ) ++ exports\n        }\n        else\n          Nil\n\n      semanticDbJavacOptions ++ options.javaOptions.javacOptions.map(_.value)\n    }\n\n    // `test` scope should contains class path to main scope\n    val mainClassesPath =\n      if scope == Scope.Test\n      then List(classesDir(inputs.workspace, inputs.projectName, Scope.Main))\n      else Nil\n\n    value(validate(logger, options))\n\n    val fullClassPath = artifacts.compileClassPath ++\n      mainClassesPath ++\n      artifacts.javacPluginDependencies.map(_._3) ++\n      artifacts.extraJavacPlugins\n\n    val project = Project(\n      directory = inputs.workspace / Constants.workspaceDirName,\n      argsFilePath =\n        projectRootDir(inputs.workspace, inputs.projectName) / Constants.scalacArgumentsFileName,\n      workspace = inputs.workspace,\n      classesDir = classesDir0,\n      scaladocDir = scaladocDir,\n      scalaCompiler = scalaCompilerParamsOpt,\n      scalaJsOptions =\n        if options.platform.value == Platform.JS\n        then Some(value(options.scalaJsOptions.config(logger, maybeRecoverOnError)))\n        else None,\n      scalaNativeOptions =\n        if options.platform.value == Platform.Native\n        then Some(options.scalaNativeOptions.bloopConfig())\n        else None,\n      projectName = inputs.scopeProjectName(scope),\n      classPath = fullClassPath,\n      resolution = Some(Project.resolution(artifacts.detailedArtifacts)),\n      sources = allSources,\n      resourceDirs = sources.resourceDirs,\n      scope = scope,\n      javaHomeOpt = Option(options.javaHomeLocation().value),\n      javacOptions = javacOptions.toList\n    )\n    project\n  }\n\n  def prepareBuild(\n    inputs: Inputs,\n    sources: Sources,\n    generatedSources: Seq[GeneratedSource],\n    options: BuildOptions,\n    scope: Scope,\n    compiler: ScalaCompiler,\n    logger: Logger,\n    buildClient: BloopBuildClient,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)\n  ): Either[BuildException, (os.Path, Option[ScalaParameters], Artifacts, Project, Boolean)] =\n    either {\n\n      val options0 =\n        if sources.hasJava && !sources.hasScala\n        then\n          options.copy(\n            scalaOptions = options.scalaOptions.copy(\n              scalaVersion = options.scalaOptions.scalaVersion.orElse {\n                Some(MaybeScalaVersion.none)\n              }\n            )\n          )\n        else options\n      val params = value(options0.scalaParams)\n\n      val scopeParams = if scope == Scope.Main then Nil else Seq(scope.name)\n\n      buildClient.setProjectParams(scopeParams ++ value(options0.projectParams))\n\n      val classesDir0 = classesDir(inputs.workspace, inputs.projectName, scope)\n\n      val artifacts = value(options0.artifacts(logger, scope, maybeRecoverOnError))\n\n      value(validate(logger, options0))\n\n      val project = value {\n        buildProject(\n          inputs = inputs,\n          sources = sources,\n          generatedSources = generatedSources,\n          options = options0,\n          scope = scope,\n          logger = logger,\n          artifacts = artifacts,\n          maybeRecoverOnError = maybeRecoverOnError\n        )\n      }\n\n      val projectChanged = compiler.prepareProject(project, logger)\n\n      if projectChanged then {\n        if compiler.usesClassDir && os.isDir(classesDir0) then {\n          logger.debug(s\"Clearing $classesDir0\")\n          os.list(classesDir0).foreach { p =>\n            logger.debug(s\"Removing $p\")\n            try os.remove.all(p)\n            catch {\n              case ex: FileSystemException =>\n                logger.debug(s\"Ignoring $ex while cleaning up $p\")\n            }\n          }\n        }\n        if os.exists(project.argsFilePath) then {\n          logger.debug(s\"Removing ${project.argsFilePath}\")\n          try os.remove(project.argsFilePath)\n          catch {\n            case ex: FileSystemException =>\n              logger.debug(s\"Ignoring $ex while cleaning up ${project.argsFilePath}\")\n          }\n        }\n      }\n\n      (classesDir0, params, artifacts, project, projectChanged)\n    }\n\n  def buildOnce(\n    inputs: Inputs,\n    sources: Sources,\n    generatedSources: Seq[GeneratedSource],\n    options: BuildOptions,\n    scope: Scope,\n    logger: Logger,\n    buildClient: BloopBuildClient,\n    compiler: ScalaCompiler,\n    partialOpt: Option[Boolean]\n  ): Either[BuildException, Build] = either {\n\n    if options.platform.value == Platform.Native then\n      value(scalaNativeSupported(options, inputs, logger)) match {\n        case None        =>\n        case Some(error) => value(Left(error))\n      }\n\n    val (classesDir0, scalaParams, artifacts, project, _) = value {\n      prepareBuild(\n        inputs = inputs,\n        sources = sources,\n        generatedSources = generatedSources,\n        options = options,\n        scope = scope,\n        compiler = compiler,\n        logger = logger,\n        buildClient = buildClient\n      )\n    }\n\n    if sources.hasJava && sources.hasScala && options.useBuildServer.contains(false) then {\n      val javaPaths = sources.paths\n        .filter(_._1.last.endsWith(\".java\"))\n        .map(_._1.toString) ++\n        sources.inMemory\n          .filter(_.generatedRelPath.last.endsWith(\".java\"))\n          .map(_.originalPath.fold(identity, _._2.toString))\n      val javaPathsList =\n        javaPaths.map(p => s\"  $p\").mkString(System.lineSeparator())\n      logger.message(\n        s\"\"\"$warnPrefix With ${Console.BOLD}--server=false${Console.RESET}, .java files are not compiled to .class files.\n           |scalac parses .java sources for type information (cross-compilation), but without the build server (Bloop/Zinc) nothing compiles them to bytecode.\n           |Affected .java files:\n           |$javaPathsList\n           |Remove --server=false or compile Java files separately to avoid runtime NoClassDefFoundError.\"\"\".stripMargin\n      )\n    }\n\n    buildClient.clear()\n    buildClient.setGeneratedSources(scope, generatedSources)\n\n    val partial = partialOpt.getOrElse {\n      options.notForBloopOptions.packageOptions.packageTypeOpt.exists(_.sourceBased)\n    }\n\n    val success = partial || compiler.compile(project, logger)\n\n    if success then\n      Successful(\n        inputs = inputs,\n        options = options,\n        scalaParams,\n        scope = scope,\n        sources = sources,\n        artifacts = artifacts,\n        project = project,\n        output = classesDir0,\n        diagnostics = buildClient.diagnostics,\n        generatedSources = generatedSources,\n        isPartial = partial,\n        logger = logger\n      )\n    else\n      Failed(\n        inputs = inputs,\n        options = options,\n        scope = scope,\n        sources = sources,\n        artifacts = artifacts,\n        project = project,\n        diagnostics = buildClient.diagnostics\n      )\n  }\n\n  def postProcess(\n    generatedSources: Seq[GeneratedSource],\n    generatedSrcRoot: os.Path,\n    classesDir: os.Path,\n    logger: Logger,\n    workspace: os.Path,\n    updateSemanticDbs: Boolean,\n    scalaVersion: String,\n    buildOptions: BuildOptions\n  ): Either[Seq[String], Unit] =\n    if os.exists(classesDir) then {\n\n      // TODO Write classes to a separate directory during post-processing\n      logger.debug(\"Post-processing class files of pre-processed sources\")\n      val mappings = generatedSources\n        .map { source =>\n          val relPath       = source.generated.relativeTo(generatedSrcRoot).toString\n          val reportingPath = source.reportingPath.fold(s => s, _.last)\n          (relPath, (reportingPath, scalaLineToScLineShift(source.wrapperParamsOpt)))\n        }\n        .toMap\n\n      val postProcessors =\n        Seq(ByteCodePostProcessor) ++\n          (if updateSemanticDbs then Seq(SemanticDbPostProcessor) else Nil) ++\n          Seq(TastyPostProcessor)\n\n      val failures = postProcessors.flatMap(\n        _.postProcess(\n          generatedSources = generatedSources,\n          mappings = mappings,\n          workspace = workspace,\n          output = classesDir,\n          logger = logger,\n          scalaVersion = scalaVersion,\n          buildOptions = buildOptions\n        )\n          .fold(e => Seq(e), _ => Nil)\n      )\n      if failures.isEmpty then Right(()) else Left(failures)\n    }\n    else\n      Right(())\n\n  def onChangeBufferedObserver(onEvent: PathWatchers.Event => Unit): Observer[PathWatchers.Event] =\n    new Observer[PathWatchers.Event] {\n      def onError(t: Throwable): Unit = {\n        // TODO Log that properly\n        System.err.println(\"got error:\")\n        @tailrec\n        def printEx(t: Throwable): Unit =\n          if t != null then {\n            System.err.println(t)\n            System.err.println(\n              t.getStackTrace.iterator.map(\"  \" + _ + System.lineSeparator()).mkString\n            )\n            printEx(t.getCause)\n          }\n        printEx(t)\n      }\n\n      def onNext(event: PathWatchers.Event): Unit =\n        onEvent(event)\n    }\n\n  final class Watcher(\n    val watchers: ListBuffer[PathWatcher[PathWatchers.Event]],\n    val scheduler: ScheduledExecutorService,\n    onChange: => Unit,\n    onDispose: => Unit\n  ) {\n    def newWatcher(): PathWatcher[PathWatchers.Event] = {\n      val w = PathWatchers.get(true)\n      watchers += w\n      w\n    }\n    def dispose(): Unit = {\n      onDispose\n      watchers.foreach(_.close())\n      scheduler.shutdown()\n    }\n\n    private val lock                  = new Object\n    private var f: ScheduledFuture[?] = uninitialized\n    private val waitFor               = 50.millis\n    private val runnable: Runnable    = { () =>\n      lock.synchronized {\n        f = null\n      }\n      onChange // FIXME Log exceptions\n    }\n    def schedule(): Unit =\n      if f == null then\n        lock.synchronized {\n          if f == null then f = scheduler.schedule(runnable, waitFor.length, waitFor.unit)\n        }\n  }\n\n  private def printable(path: os.Path): String =\n    if path.startsWith(os.pwd) then path.relativeTo(os.pwd).toString\n    else path.toString\n\n  private def jmhBuild(\n    inputs: Inputs,\n    build: Build.Successful,\n    logger: Logger,\n    javaCommand: String,\n    buildClient: BloopBuildClient,\n    compiler: ScalaCompiler,\n    buildTests: Boolean,\n    actionableDiagnostics: Option[Boolean]\n  )(using ScalaCliInvokeData): Either[BuildException, Option[Build]] = either {\n    val jmhProjectName = inputs.projectName + \"_jmh\"\n    val jmhOutputDir   = inputs.workspace / Constants.workspaceDirName / jmhProjectName\n    os.remove.all(jmhOutputDir)\n    val jmhSourceDir   = jmhOutputDir / \"sources\"\n    val jmhResourceDir = jmhOutputDir / \"resources\"\n\n    val retCode = run(\n      javaCommand,\n      build.fullClassPath.map(_.toIO),\n      \"org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator\",\n      Seq(printable(build.output), printable(jmhSourceDir), printable(jmhResourceDir), \"default\"),\n      logger\n    )\n    if retCode != 0 then {\n      val red      = Console.RED\n      val lightRed = \"\\u001b[91m\"\n      val reset    = Console.RESET\n      System.err.println(\n        s\"${red}jmh bytecode generator exited with return code $lightRed$retCode$red.$reset\"\n      )\n    }\n\n    if retCode == 0 then {\n      val jmhInputs = inputs.copy(\n        baseProjectName = jmhProjectName,\n        // hash of the underlying project if needed is already in jmhProjectName\n        mayAppendHash = false,\n        elements = inputs.elements ++ Seq(\n          Directory(jmhSourceDir),\n          ResourceDirectory(jmhResourceDir)\n        )\n      )\n      val updatedOptions = build.options.copy(\n        jmhOptions = build.options.jmhOptions.copy(\n          runJmh = build.options.jmhOptions.runJmh.map(_ => false)\n        )\n      )\n      val (crossSources, inputs0) = value(allInputs(jmhInputs, updatedOptions, logger))\n      val jmhBuilds               = value {\n        Build.build(\n          inputs0,\n          crossSources,\n          updatedOptions,\n          logger,\n          buildClient,\n          compiler,\n          None,\n          crossBuilds = false,\n          buildTests = buildTests,\n          partial = None,\n          actionableDiagnostics = actionableDiagnostics\n        )\n      }\n      Some(jmhBuilds.main)\n    }\n    else None\n  }\n\n  private def run(\n    javaCommand: String,\n    classPath: Seq[File],\n    mainClass: String,\n    args: Seq[String],\n    logger: Logger\n  ): Int = {\n\n    val command =\n      Seq(javaCommand) ++\n        Seq(\n          \"-cp\",\n          classPath.iterator.map(_.getAbsolutePath).mkString(File.pathSeparator),\n          mainClass\n        ) ++\n        args\n\n    logger.log(\n      s\"Running ${command.mkString(\" \")}\",\n      \"  Running\" + System.lineSeparator() +\n        command.iterator.map(_ + System.lineSeparator()).mkString\n    )\n\n    new ProcessBuilder(command*)\n      .inheritIO()\n      .start()\n      .waitFor()\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/BuildThreads.scala",
    "content": "package scala.build\n\nimport java.util.concurrent.{Executors, ScheduledExecutorService}\n\nimport scala.build.internal.Util\n\nfinal case class BuildThreads(\n  bloop: _root_.bloop.rifle.BloopThreads,\n  fileWatcher: ScheduledExecutorService\n) {\n  def shutdown(): Unit = {\n    bloop.shutdown()\n    fileWatcher.shutdown()\n  }\n}\n\nobject BuildThreads {\n  def create(): BuildThreads = {\n    val bloop       = _root_.bloop.rifle.BloopThreads.create()\n    val fileWatcher = Executors.newSingleThreadScheduledExecutor(\n      Util.daemonThreadFactory(\"scala-cli-file-watcher\")\n    )\n    BuildThreads(bloop, fileWatcher)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/Builds.scala",
    "content": "package scala.build\n\nimport scala.build.options.Scope\n\nfinal case class Builds(\n  builds: Seq[Build],\n  crossBuilds: Seq[Seq[Build]],\n  docBuilds: Seq[Build],\n  docCrossBuilds: Seq[Seq[Build]]\n) {\n  def main: Build =\n    get(Scope.Main).getOrElse {\n      sys.error(\"No main build found\")\n    }\n  def get(scope: Scope): Option[Build] =\n    builds.find(_.scope == scope)\n  def anyFailed: Boolean = !all.forall(_.success)\n\n  def all: Seq[Build] =\n    builds ++ crossBuilds.flatten\n  lazy val map: Map[CrossKey, Build.Successful] =\n    all\n      .collect {\n        case s: Build.Successful => s\n      }\n      .map(b => b.crossKey -> b)\n      .toMap\n\n  def allDoc: Seq[Build] =\n    docBuilds ++ docCrossBuilds.flatten\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/CollectionOps.scala",
    "content": "package scala.build\nimport scala.collection.mutable\nobject CollectionOps {\n  extension [T](items: Seq[T]) {\n\n    /** Works the same standard lib's `distinct`, but only differentiates based on the key extracted\n      * by the passed function. If more than one value exists for the same key, only the first one\n      * is kept, the rest is filtered out.\n      *\n      * @param f\n      *   function to extract the key used for distinction\n      * @tparam K\n      *   type of the key used for distinction\n      * @return\n      *   the sequence of items with distinct [[items]].map(f)\n      */\n    def distinctBy[K](f: T => K): Seq[T] =\n      if items.length == 1 then items\n      else\n        val seen = mutable.HashSet.empty[K]\n        items.filter { item =>\n          val key = f(item)\n          if seen(key) then false\n          else\n            seen += key\n            true\n        }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/ConsoleBloopBuildClient.scala",
    "content": "package scala.build\n\nimport ch.epfl.scala.bsp4j\n\nimport java.io.File\nimport java.net.URI\nimport java.nio.file.{NoSuchFileException, Paths}\n\nimport scala.build.errors.Severity\nimport scala.build.internal.WrapperParams\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\nimport scala.build.options.Scope\nimport scala.build.postprocessing.LineConversion.scalaLineToScLine\nimport scala.collection.mutable\nimport scala.jdk.CollectionConverters.*\n\nclass ConsoleBloopBuildClient(\n  logger: Logger,\n  keepDiagnostics: Boolean = false,\n  generatedSources: mutable.Map[Scope, Seq[GeneratedSource]] = mutable.Map()\n) extends BloopBuildClient {\n  import ConsoleBloopBuildClient._\n  private var projectParams = Seq.empty[String]\n\n  private def projectNameSuffix =\n    if (projectParams.isEmpty) \"\"\n    else \" (\" + projectParams.mkString(\", \") + \")\"\n\n  private def projectName = \"project\" + projectNameSuffix\n\n  private var printedStart = false\n\n  private val diagnostics0 = new mutable.ListBuffer[(Either[String, os.Path], bsp4j.Diagnostic)]\n\n  def setGeneratedSources(scope: Scope, newGeneratedSources: Seq[GeneratedSource]) =\n    generatedSources(scope) = newGeneratedSources\n  def setProjectParams(newParams: Seq[String]): Unit = {\n    projectParams = newParams\n  }\n  def diagnostics: Option[Seq[(Either[String, os.Path], bsp4j.Diagnostic)]] =\n    if (keepDiagnostics) Some(diagnostics0.result())\n    else None\n\n  private def postProcessDiagnostic(\n    path: os.Path,\n    diag: bsp4j.Diagnostic,\n    diagnosticMappings: Map[os.Path, (Either[String, os.Path], Option[WrapperParams])]\n  ): Option[(Either[String, os.Path], bsp4j.Diagnostic)] =\n    diagnosticMappings.get(path).map { case (originalPath, wrapperParamsOpt) =>\n      (\n        originalPath,\n        scalaLineToScLine(diag.getRange.getStart.getLine, wrapperParamsOpt),\n        scalaLineToScLine(diag.getRange.getStart.getLine, wrapperParamsOpt)\n      )\n    }.collect { case (originalPath, Some(scLineStart), Some(scLineEnd)) =>\n      val start = new bsp4j.Position(scLineStart, diag.getRange.getStart.getCharacter)\n      val end   = new bsp4j.Position(scLineEnd, diag.getRange.getEnd.getCharacter)\n      val range = new bsp4j.Range(start, end)\n\n      val updatedDiag = new bsp4j.Diagnostic(range, diag.getMessage)\n      updatedDiag.setCode(diag.getCode)\n      updatedDiag.setRelatedInformation(diag.getRelatedInformation)\n      updatedDiag.setSeverity(diag.getSeverity)\n      updatedDiag.setSource(diag.getSource)\n      updatedDiag.setData(diag.getData)\n\n      (originalPath, updatedDiag)\n    }\n\n  override def onBuildPublishDiagnostics(params: bsp4j.PublishDiagnosticsParams): Unit = {\n    logger.debug(\"Received onBuildPublishDiagnostics from bloop: \" + params)\n    for (diag <- params.getDiagnostics.asScala) {\n\n      val diagnosticMappings = generatedSources.valuesIterator\n        .flatMap(_.iterator)\n        .map { source =>\n          source.generated -> (source.reportingPath, source.wrapperParamsOpt)\n        }\n        .toMap\n\n      val path = os.Path(Paths.get(new URI(params.getTextDocument.getUri)).toAbsolutePath)\n      val (updatedPath, updatedDiag) = postProcessDiagnostic(path, diag, diagnosticMappings)\n        .getOrElse((Right(path), diag))\n      if (keepDiagnostics)\n        diagnostics0 += updatedPath -> updatedDiag\n      ConsoleBloopBuildClient.printFileDiagnostic(logger, updatedPath, updatedDiag)\n    }\n  }\n\n  override def onBuildLogMessage(params: bsp4j.LogMessageParams): Unit = {\n    logger.debug(\"Received onBuildLogMessage from bloop: \" + params)\n    val prefix = params.getType match {\n      case bsp4j.MessageType.ERROR   => \"Error: \"\n      case bsp4j.MessageType.WARNING => \"Warning: \"\n      case bsp4j.MessageType.INFO    => \"\"\n      case bsp4j.MessageType.LOG     => \"\" // discard those by default?\n    }\n    logger.message(prefix + params.getMessage)\n  }\n\n  override def onBuildShowMessage(params: bsp4j.ShowMessageParams): Unit =\n    logger.debug(\"Received onBuildShowMessage from bloop: \" + params)\n\n  override def onBuildTargetDidChange(params: bsp4j.DidChangeBuildTarget): Unit =\n    logger.debug(\"Received onBuildTargetDidChange from bloop: \" + params)\n\n  override def onBuildTaskStart(params: bsp4j.TaskStartParams): Unit = {\n    logger.debug(\"Received onBuildTaskStart from bloop: \" + params)\n    for (msg <- Option(params.getMessage) if !msg.contains(\" no-op compilation\")) {\n      printedStart = true\n      val msg0 =\n        if (params.getDataKind == \"compile-task\") s\"Compiling $projectName\"\n        else msg\n      logger.message(gray + msg0 + reset)\n    }\n  }\n\n  override def onBuildTaskProgress(params: bsp4j.TaskProgressParams): Unit =\n    logger.debug(\"Received onBuildTaskProgress from bloop: \" + params)\n\n  override def onBuildTaskFinish(params: bsp4j.TaskFinishParams): Unit = {\n    logger.debug(\"Received onBuildTaskFinish from bloop: \" + params)\n    if (printedStart)\n      for (msg <- Option(params.getMessage)) {\n        val msg0 =\n          if (params.getDataKind == \"compile-report\")\n            params.getStatus match {\n              case bsp4j.StatusCode.OK        => s\"Compiled $projectName\"\n              case bsp4j.StatusCode.ERROR     => s\"Error compiling $projectName\"\n              case bsp4j.StatusCode.CANCELLED => s\"Compilation cancelled$projectNameSuffix\"\n            }\n          else msg\n        logger.message(gray + msg0 + reset)\n      }\n  }\n\n  def clear(): Unit = {\n    generatedSources.clear()\n    diagnostics0.clear()\n    printedStart = false\n  }\n}\n\nobject ConsoleBloopBuildClient {\n  private val gray   = ScalaCliConsole.GRAY\n  private val reset  = Console.RESET\n  private val red    = Console.RED\n  private val yellow = Console.YELLOW\n\n  def diagnosticPrefix(severity: bsp4j.DiagnosticSeverity): String =\n    severity match {\n      case bsp4j.DiagnosticSeverity.ERROR       => s\"[${red}error$reset] \"\n      case bsp4j.DiagnosticSeverity.WARNING     => s\"[${yellow}warn$reset] \"\n      case bsp4j.DiagnosticSeverity.INFORMATION => \"[info] \"\n      case bsp4j.DiagnosticSeverity.HINT        => s\"[${yellow}hint$reset] \"\n    }\n\n  def diagnosticPrefix(severity: Severity): String = diagnosticPrefix(severity.toBsp4j)\n\n  def printFileDiagnostic(\n    logger: Logger,\n    path: Either[String, os.Path],\n    diag: bsp4j.Diagnostic\n  ): Unit = {\n    val prefix = diagnosticPrefix(diag.getSeverity)\n\n    val line  = (diag.getRange.getStart.getLine + 1).toString + \":\"\n    val col   = (diag.getRange.getStart.getCharacter + 1).toString\n    val msgIt = diag.getMessage.linesIterator\n\n    val path0 = path match {\n      case Left(source)                     => source\n      case Right(p) if p.startsWith(Os.pwd) =>\n        \".\" + File.separator + p.relativeTo(Os.pwd).toString\n      case Right(p) => p.toString\n    }\n    logger.error(s\"$prefix$path0:$line$col\")\n    for (line <- msgIt)\n      logger.error(prefix + line)\n    val codeOpt = {\n      val lineOpt =\n        if (diag.getRange.getStart.getLine == diag.getRange.getEnd.getLine)\n          Option(diag.getRange.getStart.getLine)\n        else None\n      for {\n        line <- lineOpt\n        p    <- path.toOption\n        lines =\n          try\n            os.read.lines(p)\n          catch\n            case e: NoSuchFileException =>\n              logger.message(s\"File not found: $p\")\n              logger.error(e.getMessage)\n              Nil\n        line <- lines.lift(line)\n      } yield line\n    }\n    for (code <- codeOpt)\n      code.linesIterator.map(prefix + _).foreach(logger.error)\n    val canPrintUnderline = diag.getRange.getStart.getLine == diag.getRange.getEnd.getLine &&\n      diag.getRange.getStart.getCharacter != null &&\n      diag.getRange.getEnd.getCharacter != null &&\n      codeOpt.nonEmpty\n    if (canPrintUnderline) {\n      val len =\n        math.max(1, diag.getRange.getEnd.getCharacter - diag.getRange.getStart.getCharacter)\n      logger.error(\n        prefix + \" \" * diag.getRange.getStart.getCharacter + \"^\" * len\n      )\n    }\n  }\n\n  def printOtherDiagnostic(\n    logger: Logger,\n    message: String,\n    severity: Severity,\n    positions: Seq[Position]\n  ): Unit = {\n    val isWarningOrError = true\n    if (isWarningOrError) {\n      val msgIt  = message.linesIterator\n      val prefix = diagnosticPrefix(severity)\n      logger.message(prefix + (if (msgIt.hasNext) \" \" + msgIt.next() else \"\"))\n      msgIt.foreach(line => logger.message(prefix + line))\n\n      positions.foreach {\n        case Position.Bloop(bloopJavaPath) =>\n          val bloopOutputPrefix = s\"[current bloop jvm] \"\n          logger.message(prefix + bloopOutputPrefix + bloopJavaPath)\n          logger.message(prefix + \" \" * bloopOutputPrefix.length + \"^\" * bloopJavaPath.length())\n        case pos => logger.message(prefix + pos.render())\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/CrossBuildParams.scala",
    "content": "package scala.build\n\nimport dependency.ScalaParameters\n\nimport scala.build.internal.Constants\nimport scala.build.options.BuildOptions\n\ncase class CrossBuildParams(scalaVersion: String, platform: String) {\n  def asString: String = s\"Scala $scalaVersion, $platform\"\n}\n\nobject CrossBuildParams {\n  def apply(buildOptions: BuildOptions): CrossBuildParams = new CrossBuildParams(\n    scalaVersion = buildOptions.scalaOptions.scalaVersion\n      .map(_.asString)\n      .getOrElse(Constants.defaultScalaVersion),\n    platform = buildOptions.platform.value.repr\n  )\n\n  def apply(scalaParams: Option[ScalaParameters], buildOptions: BuildOptions): CrossBuildParams =\n    new CrossBuildParams(\n      scalaVersion = scalaParams.map(_.scalaVersion)\n        .orElse(buildOptions.scalaOptions.scalaVersion.map(_.asString))\n        .getOrElse(Constants.defaultScalaVersion),\n      platform = buildOptions.platform.value.repr\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/CrossKey.scala",
    "content": "package scala.build\n\nimport scala.build.options.{BuildOptions, MaybeScalaVersion, Platform, Scope}\n\nfinal case class CrossKey(\n  optionsKey: Option[BuildOptions.CrossKey],\n  scope: Scope\n) {\n  def scalaVersion: MaybeScalaVersion =\n    optionsKey\n      .map(k => MaybeScalaVersion(k.scalaVersion))\n      .getOrElse(MaybeScalaVersion.none)\n  def platform: Option[Platform] =\n    optionsKey.map(_.platform)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/CrossSources.scala",
    "content": "package scala.build\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  ExcludeDefinitionError,\n  MalformedDirectiveError,\n  Severity,\n  UsingFileFromUriError\n}\nimport scala.build.input.*\nimport scala.build.input.ElementsUtils.*\nimport scala.build.internal.Constants\nimport scala.build.internal.util.{RegexUtils, WarningMessages}\nimport scala.build.options.{\n  BuildOptions,\n  BuildRequirements,\n  MaybeScalaVersion,\n  Scope,\n  SuppressWarningOptions,\n  WithBuildRequirements\n}\nimport scala.build.preprocessing.*\nimport scala.util.Try\nimport scala.util.chaining.*\n\n/** Information gathered from preprocessing command inputs - sources (including unwrapped scripts)\n  * and build options from using directives\n  *\n  * @param paths\n  *   paths and realtive paths to sources on disk, wrapped in their build requirements\n  * @param inMemory\n  *   in memory sources (e.g. snippets) wrapped in their build requirements\n  * @param defaultMainClass\n  * @param resourceDirs\n  * @param buildOptions\n  *   build options from sources\n  * @param unwrappedScripts\n  *   in memory script sources, their code must be wrapped before compiling\n  */\nfinal case class CrossSources(\n  paths: Seq[WithBuildRequirements[(os.Path, os.RelPath)]],\n  inMemory: Seq[WithBuildRequirements[Sources.InMemory]],\n  defaultMainElemPath: Option[os.Path],\n  resourceDirs: Seq[WithBuildRequirements[os.Path]],\n  buildOptions: Seq[WithBuildRequirements[BuildOptions]],\n  unwrappedScripts: Seq[WithBuildRequirements[Sources.UnwrappedScript]]\n) {\n  def sharedOptions(baseOptions: BuildOptions): BuildOptions =\n    buildOptions\n      .filter(_.requirements.isEmpty)\n      .map(_.value)\n      .foldLeft(baseOptions)(_.orElse(_))\n\n  private def needsScalaVersion =\n    paths.exists(_.needsScalaVersion) ||\n    inMemory.exists(_.needsScalaVersion) ||\n    resourceDirs.exists(_.needsScalaVersion) ||\n    buildOptions.exists(_.needsScalaVersion)\n\n  def scopedSources(baseOptions: BuildOptions): Either[BuildException, ScopedSources] = either {\n\n    val sharedOptions0 = sharedOptions(baseOptions)\n\n    // FIXME Not 100% sure the way we compute the intermediate and final BuildOptions\n    // is consistent (we successively filter out / retain options to compute a scala\n    // version and platform, which might not be the version and platform of the final\n    // BuildOptions).\n\n    val crossSources0 =\n      if (needsScalaVersion) {\n\n        val retainedScalaVersion = value(sharedOptions0.scalaParams)\n          .map(p => MaybeScalaVersion(p.scalaVersion))\n          .getOrElse(MaybeScalaVersion.none)\n\n        val buildOptionsWithScalaVersion = buildOptions\n          .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)\n          .filter(_.requirements.isEmpty)\n          .map(_.value)\n          .foldLeft(sharedOptions0)(_.orElse(_))\n\n        val platform = buildOptionsWithScalaVersion.platform\n\n        copy(\n          paths = paths\n            .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          inMemory = inMemory\n            .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          resourceDirs = resourceDirs\n            .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          buildOptions = buildOptions\n            .filter(!_.requirements.isEmpty)\n            .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          unwrappedScripts = unwrappedScripts\n            .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)\n            .flatMap(_.withPlatform(platform.value).toSeq)\n        )\n      }\n      else {\n\n        val platform = sharedOptions0.platform\n\n        copy(\n          paths = paths\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          inMemory = inMemory\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          resourceDirs = resourceDirs\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          buildOptions = buildOptions\n            .filter(!_.requirements.isEmpty)\n            .flatMap(_.withPlatform(platform.value).toSeq),\n          unwrappedScripts = unwrappedScripts\n            .flatMap(_.withPlatform(platform.value).toSeq)\n        )\n      }\n\n    val defaultScope: Scope = Scope.Main\n    ScopedSources(\n      crossSources0.paths.map(_.scopedValue(defaultScope)),\n      crossSources0.inMemory.map(_.scopedValue(defaultScope)),\n      defaultMainElemPath,\n      crossSources0.resourceDirs.map(_.scopedValue(defaultScope)),\n      crossSources0.buildOptions.map(_.scopedValue(defaultScope)),\n      crossSources0.unwrappedScripts.map(_.scopedValue(defaultScope))\n    )\n  }\n}\n\nobject CrossSources {\n\n  private def withinTestSubDirectory(p: ScopePath, inputs: Inputs): Boolean =\n    p.root.exists { path =>\n      val fullPath = path / p.subPath\n      inputs.elements.exists {\n        case Directory(path) =>\n          // Is this file subdirectory of given dir and if we have a subdiretory 'test' on the way\n          fullPath.startsWith(path) &&\n          fullPath.relativeTo(path).segments.contains(\"test\")\n        case _ => false\n      }\n    }\n\n  /** @return\n    *   a CrossSources and Inputs which contains element processed from using directives\n    */\n  def forInputs(\n    inputs: Inputs,\n    preprocessors: Seq[Preprocessor],\n    logger: Logger,\n    suppressWarningOptions: SuppressWarningOptions,\n    exclude: Seq[Positioned[String]] = Nil,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    download: BuildOptions.Download = BuildOptions.Download.notSupported\n  )(using ScalaCliInvokeData): Either[BuildException, (CrossSources, Inputs)] = either {\n\n    def preprocessSources(elems: Seq[SingleElement])\n      : Either[BuildException, Seq[PreprocessedSource]] =\n      elems\n        .map { elem =>\n          preprocessors\n            .iterator\n            .flatMap(p =>\n              p.preprocess(\n                elem,\n                logger,\n                maybeRecoverOnError,\n                inputs.allowRestrictedFeatures,\n                suppressWarningOptions\n              ).iterator\n            )\n            .take(1)\n            .toList\n            .headOption\n            .getOrElse(Right(Nil)) // FIXME Warn about unprocessed stuff?\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n        .map(_.flatten)\n\n    val flattenedInputs = inputs.flattened()\n    val allExclude = { // supports only one exclude directive in one source file, which should be the project file.\n      val projectScalaFileOpt = flattenedInputs.collectFirst {\n        case f: ProjectScalaFile => f\n      }\n      val excludeFromProjectFile =\n        value(preprocessSources(projectScalaFileOpt.toSeq))\n          .flatMap(_.options).flatMap(_.internal.exclude)\n      exclude ++ excludeFromProjectFile\n    }\n\n    val preprocessedInputFromArgs: Seq[PreprocessedSource] =\n      value(\n        preprocessSources(value(excludeSources(flattenedInputs, inputs.workspace, allExclude)))\n      )\n\n    val sourcesFromDirectives =\n      preprocessedInputFromArgs\n        .flatMap(_.options)\n        .flatMap(_.internal.extraSourceFiles)\n        .distinct\n\n    val inputsElemFromDirectives: Seq[SingleElement] =\n      value(resolveInputsFromSources(sourcesFromDirectives, inputs.enableMarkdown, download))\n    val preprocessedSourcesFromDirectives: Seq[PreprocessedSource] =\n      value(preprocessSources(inputsElemFromDirectives.pipe(elements =>\n        value(excludeSources(elements, inputs.workspace, allExclude))\n      )))\n\n    warnAboutChainedUsingFileDirectives(preprocessedSourcesFromDirectives, logger)\n\n    val allInputs = inputs.add(inputsElemFromDirectives).pipe(inputs =>\n      val filteredElements = value(excludeSources(inputs.elements, inputs.workspace, allExclude))\n      inputs.withElements(elements = filteredElements)\n    )\n\n    val preprocessedSources: Seq[PreprocessedSource] =\n      (preprocessedInputFromArgs ++ preprocessedSourcesFromDirectives).distinct\n        .pipe { sources =>\n          val validatedSources: Seq[PreprocessedSource] =\n            value(validateExcludeDirectives(sources, allInputs.workspace))\n          val distinctSources = validatedSources.distinctBy(_.distinctPathOrSource)\n          val diff            = validatedSources.diff(distinctSources)\n          if diff.nonEmpty then\n            val diffString = diff.map(_.distinctPathOrSource).mkString(s\"${System.lineSeparator}  \")\n            logger.message(\n              s\"\"\"[${Console.YELLOW}warn${Console.RESET}] Skipped duplicate sources:\n                 |  $diffString\"\"\".stripMargin\n            )\n          distinctSources\n        }\n\n    logger.flushExperimentalWarnings\n\n    val scopedRequirements       = preprocessedSources.flatMap(_.scopedRequirements)\n    val scopedRequirementsByRoot = scopedRequirements.groupBy(_.path.root)\n    def baseReqs(path: ScopePath): BuildRequirements = {\n      val fromDirectives =\n        scopedRequirementsByRoot\n          .getOrElse(path.root, Nil)\n          .flatMap(_.valueFor(path).toSeq)\n          .foldLeft(BuildRequirements())(_.orElse(_))\n\n      // Scala CLI treats all `.test.scala` files tests as well as\n      // files from within `test` subdirectory from provided input directories\n      // If file has `using target <scope>` directive this take precendeces.\n      if (\n        fromDirectives.scope.isEmpty &&\n        (path.subPath.last.endsWith(\".test.scala\") || withinTestSubDirectory(path, allInputs))\n      )\n        fromDirectives.copy(scope = Some(BuildRequirements.ScopeRequirement(Scope.Test)))\n      else fromDirectives\n    }\n\n    val buildOptions: Seq[WithBuildRequirements[BuildOptions]] = (for {\n      preprocessedSource <- preprocessedSources\n      opts               <- preprocessedSource.options.toSeq\n      if opts != BuildOptions() || preprocessedSource.optionsWithTargetRequirements.nonEmpty\n    } yield {\n      val baseReqs0 = baseReqs(preprocessedSource.scopePath)\n      preprocessedSource.optionsWithTargetRequirements :+ WithBuildRequirements(\n        preprocessedSource.requirements.fold(baseReqs0)(_.orElse(baseReqs0)),\n        opts\n      )\n    }).flatten\n\n    val defaultMainElemPath = for {\n      defaultMainElem <- allInputs.defaultMainClassElement\n    } yield defaultMainElem.path\n\n    val pathsWithDirectivePositions\n      : Seq[(WithBuildRequirements[(os.Path, os.RelPath)], Option[Position.File])] =\n      preprocessedSources.collect {\n        case d: PreprocessedSource.OnDisk =>\n          val baseReqs0 = baseReqs(d.scopePath)\n          WithBuildRequirements(\n            d.requirements.fold(baseReqs0)(_.orElse(baseReqs0)),\n            (d.path, d.path.relativeTo(allInputs.workspace))\n          ) -> d.directivesPositions\n      }\n    val inMemoryWithDirectivePositions\n      : Seq[(WithBuildRequirements[Sources.InMemory], Option[Position.File])] =\n      preprocessedSources.collect {\n        case m: PreprocessedSource.InMemory =>\n          val baseReqs0 = baseReqs(m.scopePath)\n          WithBuildRequirements(\n            m.requirements.fold(baseReqs0)(_.orElse(baseReqs0)),\n            Sources.InMemory(m.originalPath, m.relPath, m.content, m.wrapperParamsOpt)\n          ) -> m.directivesPositions\n      }\n    val unwrappedScriptsWithDirectivePositions\n      : Seq[(WithBuildRequirements[Sources.UnwrappedScript], Option[Position.File])] =\n      preprocessedSources.collect {\n        case m: PreprocessedSource.UnwrappedScript =>\n          val baseReqs0 = baseReqs(m.scopePath)\n          WithBuildRequirements(\n            m.requirements.fold(baseReqs0)(_.orElse(baseReqs0)),\n            Sources.UnwrappedScript(m.originalPath, m.relPath, m.wrapScriptFun)\n          ) -> m.directivesPositions\n      }\n\n    val resourceDirs: Seq[WithBuildRequirements[os.Path]] =\n      resolveResourceDirs(allInputs, preprocessedSources)\n\n    lazy val allPathsWithDirectivesByScope: Map[Scope, Seq[(os.Path, Position.File)]] =\n      (pathsWithDirectivePositions ++ inMemoryWithDirectivePositions ++\n        unwrappedScriptsWithDirectivePositions)\n        .flatMap { (withBuildRequirements, directivesPositions) =>\n          val scope         = withBuildRequirements.scopedValue(Scope.Main).scope\n          val path: os.Path = withBuildRequirements.value match\n            case im: Sources.InMemory =>\n              im.originalPath match\n                case Right((_, p: os.Path)) => p\n                case _                      => inputs.workspace / im.generatedRelPath\n            case us: Sources.UnwrappedScript =>\n              us.originalPath match\n                case Right((_, p: os.Path)) => p\n                case _                      => inputs.workspace / us.generatedRelPath\n            case (p: os.Path, _) => p\n          directivesPositions.map((path, scope, _))\n        }\n        .groupBy((_, scope, _) => scope)\n        .view\n        .mapValues(_.map((path, _, directivesPositions) => path -> directivesPositions))\n        .toMap\n    lazy val anyScopeHasMultipleSourcesWithDirectives =\n      Scope.all.exists(allPathsWithDirectivesByScope.get(_).map(_.length).getOrElse(0) > 1)\n    val shouldSuppressWarning =\n      suppressWarningOptions.suppressDirectivesInMultipleFilesWarning.getOrElse(false)\n    if !shouldSuppressWarning && anyScopeHasMultipleSourcesWithDirectives then {\n      val projectFilePath = inputs.elements.projectSettingsFiles.headOption match\n        case Some(s) => s.path\n        case _       => inputs.workspace / Constants.projectFileName\n      allPathsWithDirectivesByScope\n        .values\n        .flatten\n        .filter((path, _) => ScopePath.fromPath(path) != ScopePath.fromPath(projectFilePath))\n        .pipe { pathsToReport =>\n          val diagnosticMessage = WarningMessages\n            .directivesInMultipleFilesWarning(projectFilePath.toString)\n          val cliFriendlyMessage = WarningMessages.directivesInMultipleFilesWarning(\n            projectFilePath.toString,\n            pathsToReport.map(_._2.render())\n          )\n\n          logger.cliFriendlyDiagnostic(\n            message = diagnosticMessage,\n            cliFriendlyMessage = cliFriendlyMessage,\n            positions = pathsToReport.map(_._2).toSeq\n          )\n        }\n    }\n\n    val paths            = pathsWithDirectivePositions.map(_._1)\n    val inMemory         = inMemoryWithDirectivePositions.map(_._1)\n    val unwrappedScripts = unwrappedScriptsWithDirectivePositions.map(_._1)\n    val crossSources     = CrossSources(\n      paths,\n      inMemory,\n      defaultMainElemPath,\n      resourceDirs,\n      buildOptions,\n      unwrappedScripts\n    )\n    crossSources -> allInputs\n  }\n\n  extension (uri: java.net.URI)\n    def asString: String =\n      java.net.URI(\n        uri.getScheme,\n        uri.getAuthority,\n        uri.getPath,\n        null,\n        uri.getFragment\n      ).toString\n\n  /** @return\n    *   the resource directories that should be added to the classpath\n    */\n  private def resolveResourceDirs(\n    allInputs: Inputs,\n    preprocessedSources: Seq[PreprocessedSource]\n  ): Seq[WithBuildRequirements[os.Path]] = {\n    val fromInputs = allInputs.elements\n      .collect { case r: ResourceDirectory => WithBuildRequirements(BuildRequirements(), r.path) }\n    val fromSources =\n      preprocessedSources.flatMap(_.options)\n        .flatMap(_.classPathOptions.resourcesDir)\n        .map(r => WithBuildRequirements(BuildRequirements(), r))\n    val fromSourcesWithRequirements = preprocessedSources\n      .flatMap(_.optionsWithTargetRequirements)\n      .flatMap(_.map(_.classPathOptions.resourcesDir).flatten)\n    fromInputs ++ fromSources ++ fromSourcesWithRequirements\n  }\n\n  private def downloadFile(download: BuildOptions.Download)(pUri: Positioned[java.net.URI]) =\n    download(pUri.value.toString).left.map(\n      new UsingFileFromUriError(pUri.value, pUri.positions, _)\n    ).map(content =>\n      Seq(Virtual(pUri.value.asString, content))\n    )\n\n  type CodeFile = os.Path | java.net.URI\n\n  private def resolveInputsFromSources(\n    sources: Seq[Positioned[CodeFile]],\n    enableMarkdown: Boolean,\n    download: BuildOptions.Download\n  ) =\n    val links = sources.collect {\n      case Positioned(pos, value: java.net.URI) => Positioned(pos, value)\n    }\n    val paths = sources.collect {\n      case Positioned(pos, value: os.Path) => Positioned(pos, value)\n    }\n\n    (resolveInputsFromPath(paths, enableMarkdown) ++ links.map(downloadFile(download))).sequence\n      .left.map(CompositeBuildException(_))\n      .map(_.flatten)\n\n  private def resolveInputsFromPath(sources: Seq[Positioned[os.Path]], enableMarkdown: Boolean) =\n    sources.map { source =>\n      val sourcePath   = source.value\n      lazy val dir     = sourcePath / os.up\n      lazy val subPath = sourcePath.subRelativeTo(dir)\n      if (os.isDir(sourcePath))\n        Right(Directory(sourcePath).singleFilesFromDirectory(enableMarkdown))\n      else if (sourcePath == os.sub / Constants.projectFileName)\n        Right(Seq(ProjectScalaFile(dir, subPath)))\n      else if (sourcePath.ext == \"scala\") Right(Seq(SourceScalaFile(dir, subPath)))\n      else if (sourcePath.isScript) Right(Seq(Script(dir, subPath, None)))\n      else if (sourcePath.ext == \"java\") Right(Seq(JavaFile(dir, subPath)))\n      else if (sourcePath.ext == \"jar\") Right(Seq(JarFile(dir, subPath)))\n      else if (sourcePath.ext == \"md\") Right(Seq(MarkdownFile(dir, subPath)))\n      else {\n        val msg =\n          if (os.exists(sourcePath))\n            s\"$sourcePath: unrecognized source type (expected .scala, .sc, .java extension or directory) in using directive.\"\n          else s\"$sourcePath: not found path defined in using directive.\"\n        Left(new MalformedDirectiveError(msg, source.positions))\n      }\n    }\n\n  /** Filters out the sources from the input sequence based on the provided 'exclude' patterns. The\n    * exclude patterns can be absolute paths, relative paths, or glob patterns.\n    *\n    * @throws BuildException\n    *   If multiple 'exclude' patterns are defined across the input sources.\n    */\n  private def excludeSources[E <: Element](\n    elements: Seq[E],\n    workspaceDir: os.Path,\n    exclude: Seq[Positioned[String]]\n  ): Either[BuildException, Seq[E]] = either {\n    val excludePatterns = exclude.map(_.value).flatMap { p =>\n      val maybeRelPath = Try(os.RelPath(p)).toOption\n      maybeRelPath match {\n        case Some(relPath) if os.isDir(workspaceDir / relPath) =>\n          // exclude relative directory paths, add * to exclude all files in the directory\n          Seq(p, (workspaceDir / relPath / \"*\").toString)\n        case Some(relPath) =>\n          Seq(p, (workspaceDir / relPath).toString) // exclude relative paths\n        case None => Seq(p)\n      }\n    }\n\n    def isSourceIncluded(path: String, excludePatterns: Seq[String]): Boolean =\n      excludePatterns\n        .forall(pattern => !RegexUtils.globPattern(pattern).matcher(path).matches())\n\n    elements.filter {\n      case e: OnDisk => isSourceIncluded(e.path.toString, excludePatterns)\n      case _         => true\n    }\n  }\n\n  /** Validates that exclude directives are defined only in the one source.\n    */\n  def validateExcludeDirectives(\n    sources: Seq[PreprocessedSource],\n    workspaceDir: os.Path\n  ): Either[BuildException, Seq[PreprocessedSource]] = {\n\n    val excludePositions = for {\n      source   <- sources.flatMap(_.options)\n      exclude  <- source.internal.exclude\n      position <- exclude.positions\n    } yield position\n\n    val expectedProjectFilePath = workspaceDir / Constants.projectFileName\n\n    val singleSourceAtProject = excludePositions.forall {\n      case Position.File(Left(s), _, _, _)  => workspaceDir / s == expectedProjectFilePath\n      case Position.File(Right(p), _, _, _) => p == expectedProjectFilePath\n      case _                                => false\n    }\n    if (singleSourceAtProject) Right(sources)\n    else Left(new ExcludeDefinitionError(excludePositions, expectedProjectFilePath))\n  }\n\n  /** When a source file added by a `using file` directive, itself, contains `using file` directives\n    * there should be a warning printed that transitive `using file` directives are not supported.\n    */\n  def warnAboutChainedUsingFileDirectives(\n    sourcesAddedWithDirectives: Seq[PreprocessedSource],\n    logger: Logger\n  ): Unit = for {\n    additionalSource           <- sourcesAddedWithDirectives\n    buildOptions               <- additionalSource.options\n    transitiveAdditionalSource <- buildOptions.internal.extraSourceFiles\n  } do\n    logger.diagnostic(\n      WarningMessages.chainingUsingFileDirective,\n      Severity.Warning,\n      transitiveAdditionalSource.positions\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/Directories.scala",
    "content": "package scala.build\n\nimport coursier.paths.shaded.dirs.ProjectDirectories\nimport coursier.paths.shaded.dirs.impl.Windows\nimport coursier.paths.shaded.dirs.jni.WindowsJni\n\nimport java.util.function.Supplier\n\nimport scala.build.errors.ConfigDbException\nimport scala.build.internals.EnvVar\nimport scala.cli.config.ConfigDb\nimport scala.util.Properties\n\ntrait Directories {\n  def localRepoDir: os.Path\n  def binRepoDir: os.Path\n  def completionsDir: os.Path\n  def virtualProjectsDir: os.Path\n  def bspSocketDir: os.Path\n  def bloopDaemonDir: os.Path\n  def bloopWorkingDir: os.Path\n  def secretsDir: os.Path\n  def cacheDir: os.Path\n\n  final def dbPath: os.Path =\n    EnvVar.ScalaCli.config.valueOpt\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n      .getOrElse(secretsDir / Directories.defaultDbFileName)\n\n  lazy val configDb = ConfigDb.open(dbPath.toNIO).left.map(ConfigDbException(_))\n}\n\nobject Directories {\n\n  def defaultDbFileName: String =\n    \"config.json\"\n\n  final case class OsLocations(projDirs: ProjectDirectories) extends Directories {\n    lazy val localRepoDir: os.Path =\n      os.Path(projDirs.cacheDir, Os.pwd) / \"local-repo\"\n    lazy val binRepoDir: os.Path =\n      os.Path(localRepoDir, Os.pwd) / \"bin\"\n    lazy val completionsDir: os.Path =\n      os.Path(projDirs.dataLocalDir, Os.pwd) / \"completions\"\n    lazy val virtualProjectsDir: os.Path =\n      os.Path(projDirs.cacheDir, Os.pwd) / \"virtual-projects\"\n    lazy val bspSocketDir: os.Path =\n      // FIXME I would have preferred to use projDirs.dataLocalDir, but it seems named socket\n      // support, or name sockets in general, aren't fine with it.\n      os.Path(projDirs.cacheDir, Os.pwd) / \"bsp-sockets\"\n    lazy val bloopDaemonDir: os.Path =\n      bloopWorkingDir / \"daemon\"\n    lazy val bloopWorkingDir: os.Path = {\n      val baseDir =\n        if (Properties.isMac) projDirs.cacheDir\n        else projDirs.dataLocalDir\n      os.Path(baseDir, Os.pwd) / \"bloop\"\n    }\n    lazy val secretsDir: os.Path =\n      os.Path(projDirs.dataLocalDir, Os.pwd) / \"secrets\"\n\n    lazy val cacheDir: os.Path =\n      os.Path(projDirs.cacheDir, os.pwd)\n  }\n\n  final case class SubDir(dir: os.Path) extends Directories {\n    lazy val localRepoDir: os.Path =\n      dir / \"cache\" / \"local-repo\"\n    lazy val binRepoDir: os.Path =\n      localRepoDir / \"bin\"\n    lazy val completionsDir: os.Path =\n      dir / \"data-local\" / \"completions\"\n    lazy val virtualProjectsDir: os.Path =\n      dir / \"cache\" / \"virtual-projects\"\n    lazy val bspSocketDir: os.Path =\n      dir / \"data-local\" / \"bsp-sockets\"\n    lazy val bloopDaemonDir: os.Path =\n      bloopWorkingDir / \"daemon\"\n    lazy val bloopWorkingDir: os.Path =\n      dir / \"data-local\" / \"bloop\"\n    lazy val secretsDir: os.Path =\n      dir / \"data-local\" / \"secrets\"\n    lazy val cacheDir: os.Path =\n      dir / \"cache\"\n  }\n\n  def default(): Directories = {\n    val windows: Supplier[Windows] =\n      if coursier.paths.Util.useJni() then WindowsJni.getJdkAwareSupplier\n      else Windows.getDefaultSupplier\n    OsLocations(ProjectDirectories.from(null, null, \"ScalaCli\", windows))\n  }\n\n  def under(dir: os.Path): Directories =\n    SubDir(dir)\n\n  lazy val directories: Directories =\n    EnvVar.ScalaCli.home.valueOpt.filter(_.trim.nonEmpty) match {\n      case None =>\n        scala.build.Directories.default()\n      case Some(homeDir) =>\n        val homeDir0 = os.Path(homeDir, Os.pwd)\n        scala.build.Directories.under(homeDir0)\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/GeneratedSource.scala",
    "content": "package scala.build\n\nimport scala.build.internal.WrapperParams\n\n/** Represents a source that's not originally in the user's workspace, yet it's a part of the\n  * project. It can either be synthetically generated by Scala CLI, e.g. BuildInfo or just modified,\n  * e.g. script wrappers\n  *\n  * @param generated\n  *   path to the file created by Scala CLI\n  * @param reportingPath\n  *   the origin of the source:\n  *   - Left(String): there's no path that corresponds to the source it may be a snippet or a gist\n  *     etc.\n  *   - Right(os.Path): this source has been generated based on a file at this path\n  * @param wrapperParamsOpt\n  *   if the generated source is a script wrapper then the params are present here\n  */\nfinal case class GeneratedSource(\n  generated: os.Path,\n  reportingPath: Either[String, os.Path],\n  wrapperParamsOpt: Option[WrapperParams]\n)\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/LocalRepo.scala",
    "content": "package scala.build\nimport coursier.paths.Util\n\nimport java.io.{BufferedInputStream, Closeable}\nimport java.nio.channels.{FileChannel, FileLock}\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.{Path, StandardOpenOption}\n\nimport scala.build.internal.Constants\nimport scala.build.internal.zip.WrappedZipInputStream\nobject LocalRepo {\n\n  private def resourcePath = Constants.localRepoResourcePath\n\n  private def using[S <: Closeable, T](is: => S)(f: S => T): T = {\n    var is0 = Option.empty[S]\n    try {\n      is0 = Some(is)\n      f(is0.get)\n    }\n    finally if (is0.nonEmpty) is0.get.close()\n  }\n\n  private def extractZip(zis: WrappedZipInputStream, dest: os.Path): Unit = {\n    val it = zis.entries()\n    while (it.hasNext) {\n      val ent = it.next()\n      if (!ent.isDirectory) {\n        val content = zis.readAllBytes()\n        zis.closeEntry()\n        os.write(\n          dest / ent.getName.split('/').toSeq,\n          content,\n          createFolders = true\n        )\n      }\n    }\n  }\n\n  private def entryContent(zis: WrappedZipInputStream, entryPath: String): Option[Array[Byte]] = {\n    val it = zis.entries().dropWhile(ent => ent.isDirectory || ent.getName != entryPath)\n    if (it.hasNext) {\n      val ent = it.next()\n      assert(ent.getName == entryPath)\n\n      val content = zis.readAllBytes()\n      zis.closeEntry()\n      Some(content)\n    }\n    else None\n  }\n\n  def localRepo(\n    baseDir: os.Path,\n    logger: Logger,\n    loader: ClassLoader = Thread.currentThread().getContextClassLoader\n  ): Option[String] = {\n    val archiveUrl = loader.getResource(resourcePath)\n    logger.debug(s\"archive url: $archiveUrl\")\n\n    if archiveUrl == null then None\n    else {\n      val version =\n        using(archiveUrl.openStream()) { is =>\n          using(WrappedZipInputStream.create(new BufferedInputStream(is))) { zis =>\n            val b = entryContent(zis, \"version\").getOrElse {\n              sys.error(s\"Malformed local repo JAR $archiveUrl (no version file)\")\n            }\n            new String(b, StandardCharsets.UTF_8)\n          }\n        }\n\n      val repoDir = baseDir / version\n      logger.debug(s\"repo dir: $repoDir\")\n\n      if !os.exists(repoDir) then\n        withLock((repoDir / os.up).toNIO, version) {\n          // Post-lock validation: Recheck repository directory existence to handle\n          // potential race conditions between initial check and lock acquisition\n          if !os.exists(repoDir) then\n            val tmpRepoDir = repoDir / os.up / s\".$version.tmp\"\n            logger.debug(s\"tmp repo dir: $tmpRepoDir\")\n            os.remove.all(tmpRepoDir)\n            using(archiveUrl.openStream()) { is =>\n              using(WrappedZipInputStream.create(new BufferedInputStream(is))) { zis =>\n                extractZip(zis, tmpRepoDir)\n              }\n            }\n            os.move(tmpRepoDir, repoDir)\n        }\n\n      val repo = \"ivy:\" + repoDir.toNIO.toUri.toASCIIString + \"/[defaultPattern]\"\n      logger.debug(s\"local repo (ivy): $repo\")\n      Some(repo)\n    }\n  }\n\n  private val intraProcessLock                               = new Object\n  private def withLock[T](dir: Path, id: String)(f: => T): T =\n    intraProcessLock.synchronized {\n      val lockFile = dir.resolve(s\".lock-$id\");\n      Util.createDirectories(lockFile.getParent)\n      var channel: FileChannel = null\n      var lock: FileLock       = null\n\n      try {\n        channel = FileChannel.open(\n          lockFile,\n          StandardOpenOption.CREATE,\n          StandardOpenOption.WRITE\n        )\n        lock = channel.lock()\n        f\n      }\n      finally {\n        if (lock != null) lock.release()\n        if (channel != null) channel.close()\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/PersistentDiagnosticLogger.scala",
    "content": "package scala.build\n\nimport bloop.rifle.BloopRifleLogger\nimport org.scalajs.logging.Logger as ScalaJsLogger\n\nimport java.io.PrintStream\n\nimport scala.build.errors.{BuildException, Diagnostic}\nimport scala.build.internals.FeatureType\nimport scala.scalanative.build as sn\n\n/** Used to collect and send diagnostics to the build client when operating as a BSP */\nclass PersistentDiagnosticLogger(parent: Logger) extends Logger {\n  private val diagBuilder = List.newBuilder[Diagnostic]\n\n  def diagnostics = diagBuilder.result()\n\n  def error(message: String): Unit = parent.error(message)\n  // TODO Use macros for log and debug calls to have zero cost when verbosity <= 0\n  def message(message: => String): Unit         = parent.message(message)\n  def log(s: => String): Unit                   = parent.log(s)\n  def log(s: => String, debug: => String): Unit = parent.log(s, debug)\n  def debug(s: => String): Unit                 = parent.debug(s)\n\n  def log(diagnostics: Seq[Diagnostic]): Unit = {\n    parent.log(diagnostics)\n    diagBuilder ++= diagnostics\n  }\n\n  def log(ex: BuildException): Unit     = parent.log(ex)\n  def debug(ex: BuildException): Unit   = parent.debug(ex)\n  def exit(ex: BuildException): Nothing = parent.exit(ex)\n\n  def coursierLogger(printBefore: String): coursier.cache.CacheLogger =\n    parent.coursierLogger(printBefore)\n  def bloopRifleLogger: BloopRifleLogger                = parent.bloopRifleLogger\n  def scalaJsLogger: ScalaJsLogger                      = parent.scalaJsLogger\n  def scalaNativeTestLogger: sn.Logger                  = parent.scalaNativeTestLogger\n  def scalaNativeCliInternalLoggerOptions: List[String] = parent.scalaNativeCliInternalLoggerOptions\n\n  def compilerOutputStream: PrintStream = parent.compilerOutputStream\n\n  def verbosity: Int = parent.verbosity\n\n  def experimentalWarning(featureName: String, featureType: FeatureType): Unit =\n    parent.experimentalWarning(featureName, featureType)\n\n  def flushExperimentalWarnings: Unit = parent.flushExperimentalWarnings\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/Project.scala",
    "content": "package scala.build\n\nimport _root_.bloop.config.{Config as BloopConfig, ConfigCodecs as BloopCodecs}\nimport _root_.coursier.{Dependency as CsDependency, core as csCore, util as csUtil}\nimport com.github.plokhotnyuk.jsoniter_scala.core.writeToArray as writeAsJsonToArray\nimport coursier.core.Classifier\n\nimport java.io.ByteArrayOutputStream\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Path\nimport java.util.Arrays\n\nimport scala.build.options.{ScalacOpt, Scope, ShadowingSeq}\n\nfinal case class Project(\n  workspace: os.Path,\n  directory: os.Path,\n  argsFilePath: os.Path,\n  classesDir: os.Path,\n  scaladocDir: os.Path,\n  scalaCompiler: Option[ScalaCompilerParams],\n  scalaJsOptions: Option[BloopConfig.JsConfig],\n  scalaNativeOptions: Option[BloopConfig.NativeConfig],\n  projectName: String,\n  classPath: Seq[os.Path],\n  sources: Seq[os.Path],\n  resolution: Option[BloopConfig.Resolution],\n  resourceDirs: Seq[os.Path],\n  javaHomeOpt: Option[os.Path],\n  scope: Scope,\n  javacOptions: List[String]\n) {\n\n  import Project._\n\n  def bloopProject: BloopConfig.Project = {\n    val platform = (scalaJsOptions, scalaNativeOptions) match {\n      case (None, None) =>\n        val baseJvmConf = bloopJvmPlatform\n        val home        = javaHomeOpt.map(_.toNIO).orElse(baseJvmConf.config.home)\n        baseJvmConf.copy(config = baseJvmConf.config.copy(home = home))\n      case (Some(jsConfig), _)     => BloopConfig.Platform.Js(config = jsConfig, mainClass = None)\n      case (_, Some(nativeConfig)) =>\n        BloopConfig.Platform.Native(config = nativeConfig, mainClass = None)\n    }\n    val scalaConfigOpt = scalaCompiler.map { scalaCompiler0 =>\n      bloopScalaConfig(\"org.scala-lang\", \"scala-compiler\", scalaCompiler0.scalaVersion).copy(\n        options = updateScalacOptions(scalaCompiler0.scalacOptions).map(_.value),\n        jars = scalaCompiler0.compilerClassPath.map(_.toNIO).toList,\n        bridgeJars = scalaCompiler0.bridgeJarsOpt.map(_.map(_.toNIO).toList)\n      )\n    }\n    baseBloopProject(\n      projectName,\n      directory.toNIO,\n      (directory / \".bloop\" / projectName).toNIO,\n      classesDir.toNIO,\n      scope\n    )\n      .copy(\n        workspaceDir = Some(workspace.toNIO),\n        classpath = classPath.map(_.toNIO).toList,\n        sources = sources.iterator.map(_.toNIO).toList,\n        resources = Some(resourceDirs).filter(_.nonEmpty).map(_.iterator.map(_.toNIO).toList),\n        platform = Some(platform),\n        `scala` = scalaConfigOpt,\n        java = Some(BloopConfig.Java(javacOptions)),\n        resolution = resolution\n      )\n  }\n\n  def bloopFile: BloopConfig.File =\n    BloopConfig.File(BloopConfig.File.LatestVersion, bloopProject)\n\n  private def updateScalacOptions(scalacOptions: Seq[String]): List[ScalacOpt] =\n    ShadowingSeq.from(scalacOptions.map(ScalacOpt(_))).values.map { l =>\n      // only look at the head, the tail is only values passed to it\n      l.headOption match {\n        case Some(opt) if opt.value.startsWith(\"-coverage-out:\") =>\n          // actual -coverage-out: option\n          val maybeRelativePath = opt.value.stripPrefix(\"-coverage-out:\")\n          val absolutePath      = os.Path(maybeRelativePath, Os.pwd)\n          ScalacOpt(s\"-coverage-out:$absolutePath\") +: l.tail\n        case _ =>\n          // not a -coverage-out: option\n          l\n      }\n    }.flatten.toList\n\n  private def maybeUpdateInputs(logger: Logger): Boolean = {\n    val dest      = directory / \".bloop\" / s\"$projectName.inputs.txt\"\n    val onDiskOpt =\n      if (os.exists(dest)) Some(os.read.bytes(dest))\n      else None\n    val newContent = {\n      val linesIt =\n        if (sources.forall(_.startsWith(workspace)))\n          sources.iterator.map(_.relativeTo(workspace).toString)\n        else\n          sources.iterator.map(_.toString)\n      val it = linesIt.map(_ + System.lineSeparator()).map(_.getBytes(StandardCharsets.UTF_8))\n      val b  = new ByteArrayOutputStream\n      for (elem <- it)\n        b.write(elem)\n      b.toByteArray()\n    }\n    val doWrite = onDiskOpt.forall(onDisk => !Arrays.equals(onDisk, newContent))\n    if (doWrite) {\n      logger.debug(s\"Writing source file list in $dest\")\n      os.write.over(dest, newContent, createFolders = true)\n    }\n    else\n      logger.debug(s\"Source file list in $dest doesn't need updating\")\n    doWrite\n  }\n\n  def writeBloopFile(strictCheck: Boolean, logger: Logger): Boolean = {\n    lazy val bloopFileContent =\n      writeAsJsonToArray(bloopFile)(using BloopCodecs.codecFile)\n    val dest    = directory / \".bloop\" / s\"$projectName.json\"\n    val doWrite =\n      if (strictCheck)\n        !os.isFile(dest) || {\n          logger.debug(s\"Checking Bloop project in $dest\")\n          val currentContent = os.read.bytes(dest)\n          !Arrays.equals(currentContent, bloopFileContent)\n        }\n      else\n        maybeUpdateInputs(logger) || !os.isFile(dest)\n    if (doWrite) {\n      logger.debug(s\"Writing bloop project in $dest\")\n      os.write.over(dest, bloopFileContent, createFolders = true)\n    }\n    else\n      logger.debug(s\"Bloop project in $dest doesn't need updating\")\n    doWrite\n  }\n}\n\nobject Project {\n\n  def resolution(\n    detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)]\n  ): BloopConfig.Resolution = {\n    val indices = detailedArtifacts\n      .map { case (dep, _, _, _) => dep.moduleVersionConstraint }\n      .map { case (m, vc) => m -> vc.asString }\n      .zipWithIndex.toMap\n    val modules = detailedArtifacts\n      .groupBy(_._1.moduleVersionConstraint)\n      .map {\n        case ((m, vc), artifacts) => m -> vc.asString -> artifacts\n      }\n      .toVector\n      .sortBy { case (modVer, _) => indices.getOrElse(modVer, Int.MaxValue) }\n      .iterator\n      .map {\n        case ((mod, ver), values) =>\n          val artifacts = values.toList.map {\n            case (_, pub, _, f) =>\n              val classifier =\n                if (pub.classifier == Classifier.empty) None\n                else Some(pub.classifier.value)\n              BloopConfig.Artifact(pub.name, classifier, None, f.toNIO)\n          }\n          BloopConfig.Module(mod.organization.value, mod.name.value, ver, None, artifacts)\n      }\n      .toList\n    BloopConfig.Resolution(modules)\n  }\n\n  private def setProjectTestConfig(p: BloopConfig.Project): BloopConfig.Project =\n    p.copy(\n      dependencies = List(p.name.stripSuffix(\"-test\")),\n      test = Some(\n        BloopConfig.Test(\n          frameworks = BloopConfig.TestFramework.DefaultFrameworks,\n          options = BloopConfig.TestOptions.empty\n        )\n      ),\n      tags = Some(List(\"test\"))\n    )\n\n  private def baseBloopProject(\n    name: String,\n    directory: Path,\n    out: Path,\n    classesDir: Path,\n    scope: Scope\n  ): BloopConfig.Project = {\n    val project = BloopConfig.Project(\n      name = name,\n      directory = directory,\n      workspaceDir = None,\n      sources = Nil,\n      sourcesGlobs = None,\n      sourceRoots = None,\n      dependencies = Nil,\n      classpath = Nil,\n      out = out,\n      classesDir = classesDir,\n      resources = None,\n      `scala` = None,\n      java = None,\n      sbt = None,\n      test = None,\n      platform = None,\n      resolution = None,\n      tags = Some(List(\"library\")),\n      sourceGenerators = None\n    )\n    if (scope == Scope.Test)\n      setProjectTestConfig(project)\n    else project\n  }\n\n  private def bloopJvmPlatform: BloopConfig.Platform.Jvm =\n    BloopConfig.Platform.Jvm(\n      config = BloopConfig.JvmConfig(None, Nil),\n      mainClass = None,\n      runtimeConfig = None,\n      classpath = None,\n      resources = None\n    )\n  private def bloopScalaConfig(\n    organization: String,\n    name: String,\n    version: String\n  ): BloopConfig.Scala =\n    BloopConfig.Scala(\n      organization = organization,\n      name = name,\n      version = version,\n      options = Nil,\n      jars = Nil,\n      analysis = None,\n      setup = None,\n      bridgeJars = None\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/ReplArtifacts.scala",
    "content": "package scala.build\n\nimport coursier.cache.FileCache\nimport coursier.core.Repository\nimport coursier.util.Task\nimport dependency.*\n\nimport java.io.File\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.internal.CsLoggerUtil.*\n\nfinal case class ReplArtifacts(\n  replArtifacts: Seq[(String, os.Path)],\n  depArtifacts: Seq[(String, os.Path)],\n  extraClassPath: Seq[os.Path],\n  extraSourceJars: Seq[os.Path],\n  replMainClass: String,\n  replJavaOpts: Seq[String],\n  addSourceJars: Boolean,\n  includeExtraCpOnReplCp: Boolean = false\n) {\n  private lazy val fullExtraClassPath: Seq[os.Path] =\n    if addSourceJars then extraClassPath ++ extraSourceJars else extraClassPath\n  lazy val replClassPath: Seq[os.Path] =\n    (if includeExtraCpOnReplCp then fullExtraClassPath ++ replArtifacts.map(_._2).distinct\n     else replArtifacts.map(_._2))\n      .distinct\n  lazy val depsClassPath: Seq[os.Path] = (fullExtraClassPath ++ depArtifacts.map(_._2)).distinct\n}\n\nobject ReplArtifacts {\n  // TODO In order to isolate more Ammonite dependencies, we'd need to get two class paths:\n  //      - a shared one, with ammonite-repl-api, ammonite-compiler, and dependencies\n  //      - an Ammonite-specific one, with the other ammonite JARs\n  // Then, use the coursier-bootstrap library to generate a launcher creating to class loaders,\n  // with each of those class paths, and run Ammonite with this launcher.\n  // This requires to change this line in Ammonite, https://github.com/com-lihaoyi/Ammonite/blob/0f0d597f04e62e86cbf76d3bd16deb6965331470/amm/src/main/scala/ammonite/Main.scala#L99,\n  // to\n  //     val contextClassLoader = classOf[ammonite.repl.api.ReplAPI].getClassLoader\n  // so that only the first loader is exposed to users in Ammonite.\n  def ammonite(\n    scalaParams: ScalaParameters,\n    ammoniteVersion: String,\n    dependencies: Seq[AnyDependency],\n    extraClassPath: Seq[os.Path],\n    extraSourceJars: Seq[os.Path],\n    extraRepositories: Seq[Repository],\n    logger: Logger,\n    cache: FileCache[Task],\n    addScalapy: Option[String]\n  ): Either[BuildException, ReplArtifacts] = either {\n    val scalapyDeps =\n      addScalapy.map(ver => dep\"${Artifacts.scalaPyOrganization(ver)}::scalapy-core::$ver\").toSeq\n    val allDeps = dependencies ++ Seq(dep\"com.lihaoyi:::ammonite:$ammoniteVersion\") ++ scalapyDeps\n    val replArtifacts = Artifacts.artifacts(\n      allDeps.map(Positioned.none),\n      extraRepositories,\n      Some(scalaParams),\n      logger,\n      cache.withMessage(s\"Downloading Ammonite $ammoniteVersion\")\n    )\n    val replSourceArtifacts = Artifacts.artifacts(\n      allDeps.map(Positioned.none),\n      extraRepositories,\n      Some(scalaParams),\n      logger,\n      cache.withMessage(s\"Downloading Ammonite $ammoniteVersion sources\"),\n      classifiersOpt = Some(Set(\"sources\"))\n    )\n    ReplArtifacts(\n      replArtifacts = value(replArtifacts) ++ value(replSourceArtifacts),\n      depArtifacts =\n        Nil, // amm does not support a -cp option, deps are passed directly to Ammonite cp\n      extraClassPath = extraClassPath,\n      extraSourceJars = extraSourceJars,\n      replMainClass = \"ammonite.Main\",\n      replJavaOpts = Nil,\n      addSourceJars = true,\n      includeExtraCpOnReplCp =\n        true // extra cp & source jars have to be passed directly to Ammonite cp\n    )\n  }\n\n  def default(\n    scalaParams: ScalaParameters,\n    dependencies: Seq[AnyDependency],\n    extraClassPath: Seq[os.Path],\n    logger: Logger,\n    cache: FileCache[Task],\n    repositories: Seq[Repository],\n    addScalapy: Option[String],\n    javaVersion: Int\n  ): Either[BuildException, ReplArtifacts] = either {\n    val isScala2             = scalaParams.scalaVersion.startsWith(\"2.\")\n    val firstNewReplNightly  = \"3.8.0-RC1-bin-20251101-389483e-NIGHTLY\".coursierVersion\n    val firstNewReplRc       = \"3.8.0-RC1\".coursierVersion\n    val firstNewReplStable   = \"3.8.0\".coursierVersion\n    val scalaCoursierVersion = scalaParams.scalaVersion.coursierVersion\n    val shouldUseNewRepl     =\n      !isScala2 &&\n      ((scalaCoursierVersion >= firstNewReplNightly) || (scalaCoursierVersion >= firstNewReplRc) ||\n      scalaCoursierVersion >= firstNewReplStable)\n    val replDeps =\n      if isScala2 then Seq(dep\"org.scala-lang:scala-compiler:${scalaParams.scalaVersion}\")\n      else if shouldUseNewRepl then\n        Seq(\n          dep\"org.scala-lang::scala3-compiler:${scalaParams.scalaVersion}\",\n          dep\"org.scala-lang::scala3-repl:${scalaParams.scalaVersion}\"\n        )\n      else Seq(dep\"org.scala-lang::scala3-compiler:${scalaParams.scalaVersion}\")\n    val scalapyDeps =\n      addScalapy.map(ver => dep\"${Artifacts.scalaPyOrganization(ver)}::scalapy-core::$ver\").toSeq\n    val externalDeps                          = dependencies ++ scalapyDeps\n    val replArtifacts: Seq[(String, os.Path)] = value {\n      Artifacts.artifacts(\n        replDeps.map(Positioned.none),\n        repositories,\n        Some(scalaParams),\n        logger,\n        cache.withMessage(s\"Downloading Scala compiler ${scalaParams.scalaVersion}\")\n      )\n    }\n    val depArtifacts: Seq[(String, os.Path)] = value {\n      Artifacts.artifacts(\n        externalDeps.map(Positioned.none),\n        repositories,\n        Some(scalaParams),\n        logger,\n        cache.withMessage(s\"Downloading REPL dependencies\")\n      )\n    }\n    val mainClass =\n      if isScala2 then \"scala.tools.nsc.MainGenericRunner\"\n      else \"dotty.tools.repl.Main\"\n    val defaultReplJavaOpts = Seq(\"-Dscala.usejavacp=true\")\n    val jlineArtifacts      =\n      replArtifacts\n        .map(_._2.toString)\n        .filter(_.contains(\"jline\"))\n    val jlineJavaOpts: Seq[String] =\n      if javaVersion >= 24 && jlineArtifacts.nonEmpty then {\n        val modulePath    = Seq(\"--module-path\", jlineArtifacts.mkString(File.pathSeparator))\n        val remainingOpts =\n          if isScala2 then\n            Seq(\n              \"--add-modules\",\n              \"org.jline\",\n              \"--enable-native-access=org.jline\"\n            )\n          else\n            Seq(\n              \"--add-modules\",\n              \"org.jline.terminal\",\n              \"--enable-native-access=org.jline.nativ\"\n            )\n        modulePath ++ remainingOpts\n      }\n      else Seq.empty\n    val replJavaOpts = defaultReplJavaOpts ++ jlineJavaOpts\n    ReplArtifacts(\n      replArtifacts = replArtifacts,\n      depArtifacts = depArtifacts,\n      extraClassPath = extraClassPath,\n      extraSourceJars = Nil,\n      replMainClass = mainClass,\n      replJavaOpts = replJavaOpts,\n      addSourceJars = false\n    )\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/ScalaCompilerParams.scala",
    "content": "package scala.build\n\nfinal case class ScalaCompilerParams(\n  scalaVersion: String,\n  scalaBinaryVersion: String,\n  scalacOptions: Seq[String],\n  compilerClassPath: Seq[os.Path],\n  bridgeJarsOpt: Option[Seq[os.Path]]\n)\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/ScalafixArtifacts.scala",
    "content": "package scala.build\n\nimport coursier.cache.FileCache\nimport coursier.core.Repository\nimport coursier.util.Task\nimport dependency.*\nimport org.apache.commons.compress.archivers.zip.ZipFile\n\nimport java.io.{ByteArrayInputStream, IOException}\nimport java.util.Properties\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.{BuildException, ScalafixPropertiesError}\nimport scala.build.internal.Constants\nimport scala.build.internal.CsLoggerUtil.*\n\nfinal case class ScalafixArtifacts(\n  scalafixJars: Seq[os.Path],\n  toolsJars: Seq[os.Path]\n)\n\nobject ScalafixArtifacts {\n\n  def artifacts(\n    scalaVersion: String,\n    externalRulesDeps: Seq[Positioned[AnyDependency]],\n    extraRepositories: Seq[Repository],\n    logger: Logger,\n    cache: FileCache[Task]\n  ): Either[BuildException, ScalafixArtifacts] =\n    either {\n      val scalafixProperties =\n        value(fetchOrLoadScalafixProperties(extraRepositories, logger, cache))\n      val key =\n        value(scalafixPropsKey(scalaVersion))\n      val fetchScalaVersion = scalafixProperties.getProperty(key)\n\n      val scalafixDeps =\n        Seq(dep\"ch.epfl.scala:scalafix-cli_$fetchScalaVersion:${Constants.scalafixVersion}\")\n\n      val scalafix =\n        value(\n          Artifacts.artifacts(\n            scalafixDeps.map(Positioned.none),\n            extraRepositories,\n            None,\n            logger,\n            cache.withMessage(s\"Downloading scalafix-cli ${Constants.scalafixVersion}\")\n          )\n        )\n\n      val scalaParameters =\n        // Scalafix for scala 3 uses 2.13-published community rules\n        // https://github.com/scalacenter/scalafix/issues/2041\n        if (scalaVersion.startsWith(\"3\")) ScalaParameters(Constants.defaultScala213Version)\n        else ScalaParameters(scalaVersion)\n\n      val tools =\n        value(\n          Artifacts.artifacts(\n            externalRulesDeps,\n            extraRepositories,\n            Some(scalaParameters),\n            logger,\n            cache.withMessage(s\"Downloading scalafix.deps\")\n          )\n        )\n\n      ScalafixArtifacts(scalafix.map(_._2), tools.map(_._2))\n    }\n\n  private def fetchOrLoadScalafixProperties(\n    extraRepositories: Seq[Repository],\n    logger: Logger,\n    cache: FileCache[Task]\n  ): Either[BuildException, Properties] =\n    either {\n      val cacheDir  = Directories.directories.cacheDir / \"scalafix-props-cache\"\n      val cachePath = cacheDir / s\"scalafix-interfaces-${Constants.scalafixVersion}.properties\"\n\n      val content =\n        if (!os.exists(cachePath)) {\n          val interfacesJar = value(fetchScalafixInterfaces(extraRepositories, logger, cache))\n          val propsData     = value(readScalafixProperties(interfacesJar))\n          if (!os.exists(cacheDir)) os.makeDir(cacheDir)\n          os.write(cachePath, propsData)\n          propsData\n        }\n        else os.read(cachePath)\n      val props  = new Properties()\n      val stream = new ByteArrayInputStream(content.getBytes())\n      props.load(stream)\n      props\n    }\n\n  private def fetchScalafixInterfaces(\n    extraRepositories: Seq[Repository],\n    logger: Logger,\n    cache: FileCache[Task]\n  ): Either[BuildException, os.Path] =\n    either {\n      val scalafixInterfaces = dep\"ch.epfl.scala:scalafix-interfaces:${Constants.scalafixVersion}\"\n\n      val fetchResult =\n        value(\n          Artifacts.artifacts(\n            List(scalafixInterfaces).map(Positioned.none),\n            extraRepositories,\n            None,\n            logger,\n            cache.withMessage(s\"Downloading scalafix-interfaces ${scalafixInterfaces.version}\")\n          )\n        )\n\n      val expectedJarName = s\"scalafix-interfaces-${Constants.scalafixVersion}.jar\"\n      val interfacesJar   = fetchResult.collectFirst {\n        case (_, path) if path.last == expectedJarName => path\n      }\n\n      value(\n        interfacesJar.toRight(new BuildException(\"Failed to found scalafix-interfaces jar\") {})\n      )\n    }\n\n  private def readScalafixProperties(jar: os.Path): Either[BuildException, String] =\n    try {\n      import scala.jdk.CollectionConverters.*\n      val zipFile = ZipFile.builder().setPath(jar.toNIO).get()\n      val entry   = zipFile.getEntries().asScala.find(entry =>\n        entry.getName == \"scalafix-interfaces.properties\"\n      )\n      val out =\n        entry.toRight(new ScalafixPropertiesError(path = jar))\n          .map { entry =>\n            val stream = zipFile.getInputStream(entry)\n            val bytes  = stream.readAllBytes()\n            new String(bytes)\n          }\n      zipFile.close()\n      out\n    }\n    catch {\n      case e: IOException => Left(new ScalafixPropertiesError(path = jar, cause = Some(e)))\n    }\n\n  private def scalafixPropsKey(scalaVersion: String): Either[BuildException, String] = {\n    val regex = \"(\\\\d)\\\\.(\\\\d+).+\".r\n    scalaVersion match {\n      case regex(\"2\", \"12\")              => Right(\"scala212\")\n      case regex(\"2\", \"13\")              => Right(\"scala213\")\n      case regex(\"3\", x) if x.toInt <= 3 => Right(\"scala3LTS\")\n      case regex(\"3\", _)                 => Right(\"scala3Next\")\n      case _                             =>\n        Left(new BuildException(s\"Scalafix is not supported for Scala version: $scalaVersion\") {})\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/ScopedSources.scala",
    "content": "package scala.build\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.info.{BuildInfo, ScopedBuildInfo}\nimport scala.build.internal.AppCodeWrapper\nimport scala.build.internal.util.WarningMessages\nimport scala.build.options.{BuildOptions, HasScope, Scope}\nimport scala.build.preprocessing.ScriptPreprocessor\n\n/** Information gathered from preprocessing command inputs - sources (including unwrapped scripts)\n  * and build options from using directives. Only scope requirements remain in this object after\n  * resolving them in [[CrossSources.scopedSources]]\n  *\n  * @param paths\n  *   paths and relative paths to sources on disk with the scope they belong to\n  * @param inMemory\n  *   in memory sources (e.g. snippets) with the scope they belong to\n  * @param defaultMainClass\n  * @param resourceDirs\n  * @param buildOptions\n  *   build options sources with the scope they belong to\n  * @param unwrappedScripts\n  *   in memory script sources with the scope they belong to, their code must be wrapped before\n  *   compiling\n  */\n\nfinal case class ScopedSources(\n  paths: Seq[HasScope[(os.Path, os.RelPath)]],\n  inMemory: Seq[HasScope[Sources.InMemory]],\n  defaultMainElemPath: Option[os.Path],\n  resourceDirs: Seq[HasScope[os.Path]],\n  buildOptions: Seq[HasScope[BuildOptions]],\n  unwrappedScripts: Seq[HasScope[Sources.UnwrappedScript]]\n) {\n  def buildOptionsFor(scope: Scope): Seq[BuildOptions] =\n    scope match {\n      case Scope.Test => buildOptions.flatMap(_.valueFor(Scope.Test).toSeq) ++\n          buildOptions.flatMap(_.valueFor(Scope.Main).toSeq)\n      case _ => buildOptions.flatMap(_.valueFor(scope).toSeq)\n    }\n\n  /** Resolve scope requirements and create a Sources instance\n    * @param scope\n    *   scope to be resolved\n    * @param baseOptions\n    *   options that have already been collected for this build, they should consist of:\n    *   - options from the console\n    *   - options from using directives from the sources\n    *   - options from resolved using directives that had Scala version and platform requirements\n    *     that fit the current build\n    * @return\n    *   [[Sources]] instance that belong to specified scope\n    */\n  def sources(\n    scope: Scope,\n    baseOptions: BuildOptions,\n    workspace: os.Path,\n    logger: Logger\n  ): Either[BuildException, Sources] = either {\n    val combinedOptions = combinedBuildOptions(scope, baseOptions)\n\n    val codeWrapper = ScriptPreprocessor.getScriptWrapper(combinedOptions, logger)\n\n    val wrappedScripts = unwrappedScripts\n      .flatMap(_.valueFor(scope).toSeq)\n      .map(_.wrap(codeWrapper))\n\n    codeWrapper match {\n      case _: AppCodeWrapper if wrappedScripts.size > 1 =>\n        wrappedScripts.find(_.originalPath.exists(_._1.toString == \"main.sc\"))\n          .foreach(_ => logger.diagnostic(WarningMessages.mainScriptNameClashesWithAppWrapper))\n      case _ => ()\n    }\n\n    val defaultMainClass = defaultMainElemPath.flatMap { mainElemPath =>\n      wrappedScripts.collectFirst {\n        case Sources.InMemory(Right((_, path)), _, _, Some(wrapperParams))\n            if mainElemPath == path =>\n          wrapperParams.mainClass\n      }\n    }\n\n    val needsBuildInfo = combinedOptions.sourceGeneratorOptions.useBuildInfo.getOrElse(false)\n\n    val maybeBuildInfoSource = if (needsBuildInfo && scope == Scope.Main)\n      Seq(\n        Sources.InMemory(\n          Left(\"build-info\"),\n          os.rel / \"BuildInfo.scala\",\n          value(buildInfo(combinedOptions, workspace)).generateContents().getBytes(\n            StandardCharsets.UTF_8\n          ),\n          None\n        )\n      )\n    else Nil\n\n    Sources(\n      paths.flatMap(_.valueFor(scope).toSeq),\n      inMemory.flatMap(_.valueFor(scope).toSeq) ++ wrappedScripts ++ maybeBuildInfoSource,\n      defaultMainClass,\n      resourceDirs.flatMap(_.valueFor(scope).toSeq),\n      combinedOptions\n    )\n  }\n\n  /** Combine build options that had no requirements (console and using directives) or their\n    * requirements have been resolved (e.g. target using directives) with build options that require\n    * the specified scope\n    *\n    * @param scope\n    *   scope to be resolved\n    * @param baseOptions\n    *   options that have already been collected for this build (had no requirements or they have\n    *   been resolved)\n    * @return\n    *   Combined BuildOptions, baseOptions' values take precedence\n    */\n  def combinedBuildOptions(scope: Scope, baseOptions: BuildOptions): BuildOptions =\n    buildOptionsFor(scope)\n      .foldRight(baseOptions)(_.orElse(_))\n\n  def buildInfo(baseOptions: BuildOptions, workspace: os.Path): Either[BuildException, BuildInfo] =\n    either {\n      def getScopedBuildInfo(scope: Scope): ScopedBuildInfo =\n        val combinedOptions = combinedBuildOptions(scope, baseOptions)\n        val sourcePaths     = paths.flatMap(_.valueFor(scope).toSeq).map(_._1.toString)\n        val inMemoryPaths   =\n          (inMemory.flatMap(_.valueFor(scope).toSeq).flatMap(_.originalPath.toOption) ++\n            unwrappedScripts.flatMap(_.valueFor(scope).toSeq).flatMap(_.originalPath.toOption))\n            .map(_._2.toString)\n\n        ScopedBuildInfo(combinedOptions, sourcePaths ++ inMemoryPaths)\n\n      val baseBuildInfo = value(BuildInfo(combinedBuildOptions(Scope.Main, baseOptions), workspace))\n\n      val mainBuildInfo = getScopedBuildInfo(Scope.Main)\n      val testBuildInfo = getScopedBuildInfo(Scope.Test)\n\n      baseBuildInfo\n        .withScope(Scope.Main.name, mainBuildInfo)\n        .withScope(Scope.Test.name, testBuildInfo)\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/Sources.scala",
    "content": "package scala.build\n\nimport coursier.cache.ArchiveCache\nimport coursier.util.Task\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.input.Inputs\nimport scala.build.internal.{CodeWrapper, WrapperParams}\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.build.preprocessing.*\n\nfinal case class Sources(\n  paths: Seq[(os.Path, os.RelPath)],\n  inMemory: Seq[Sources.InMemory],\n  defaultMainClass: Option[String],\n  resourceDirs: Seq[os.Path],\n  buildOptions: BuildOptions\n) {\n\n  def withExtraSources(other: Sources): Sources =\n    copy(\n      paths = paths ++ other.paths,\n      inMemory = inMemory ++ other.inMemory,\n      resourceDirs = resourceDirs ++ other.resourceDirs\n    )\n\n  def withVirtualDir(inputs: Inputs, scope: Scope, options: BuildOptions): Sources = {\n\n    val srcRootPath   = inputs.generatedSrcRoot(scope)\n    val resourceDirs0 = options.classPathOptions.resourcesVirtualDir.map { path =>\n      srcRootPath / path\n    }\n\n    copy(\n      resourceDirs = resourceDirs ++ resourceDirs0\n    )\n  }\n\n  /** Write all in-memory sources to disk.\n    *\n    * @param generatedSrcRoot\n    *   the root directory where the sources should be written\n    */\n  def generateSources(generatedSrcRoot: os.Path): Seq[GeneratedSource] = {\n    val generated =\n      for (inMemSource <- inMemory) yield {\n        os.write.over(\n          generatedSrcRoot / inMemSource.generatedRelPath,\n          inMemSource.content,\n          createFolders = true\n        )\n        (\n          inMemSource.originalPath.map(_._2),\n          inMemSource.generatedRelPath,\n          inMemSource.wrapperParamsOpt\n        )\n      }\n\n    val generatedSet = generated.map(_._2).toSet\n    if (os.isDir(generatedSrcRoot))\n      os.walk(generatedSrcRoot)\n        .filter(os.isFile(_))\n        .filter(p => !generatedSet(p.relativeTo(generatedSrcRoot)))\n        .foreach(os.remove(_))\n\n    generated.map {\n      case (reportingPath, path, wrapperParamsOpt) =>\n        GeneratedSource(generatedSrcRoot / path, reportingPath, wrapperParamsOpt)\n    }\n  }\n\n  lazy val hasJava =\n    (paths.iterator.map(_._1.last) ++ inMemory.iterator.map(_.generatedRelPath.last))\n      .exists(_.endsWith(\".java\"))\n  lazy val hasScala =\n    (paths.iterator.map(_._1.last) ++ inMemory.iterator.map(_.generatedRelPath.last))\n      .exists(_.endsWith(\".scala\"))\n}\n\nobject Sources {\n\n  final case class InMemory(\n    originalPath: Either[String, (os.SubPath, os.Path)],\n    generatedRelPath: os.RelPath,\n    content: Array[Byte],\n    wrapperParamsOpt: Option[WrapperParams]\n  )\n\n  final case class UnwrappedScript(\n    originalPath: Either[String, (os.SubPath, os.Path)],\n    generatedRelPath: os.RelPath,\n    wrapScriptFun: CodeWrapper => (String, WrapperParams)\n  ) {\n    def wrap(wrapper: CodeWrapper): InMemory = {\n      val (content, wrapperParams) = wrapScriptFun(wrapper)\n      InMemory(\n        originalPath,\n        generatedRelPath,\n        content.getBytes(StandardCharsets.UTF_8),\n        Some(wrapperParams)\n      )\n    }\n  }\n\n  /** The default preprocessor list.\n    *\n    * @param archiveCache\n    *   used from native launchers by the Java preprocessor, to download a java-class-name binary,\n    *   used to infer the class name of unnamed Java sources (like stdin)\n    * @param javaClassNameVersionOpt\n    *   if using a java-class-name binary, the version we should download. If empty, the default\n    *   version is downloaded.\n    * @return\n    */\n  def defaultPreprocessors(\n    archiveCache: ArchiveCache[Task],\n    javaClassNameVersionOpt: Option[String],\n    javaCommand: () => String\n  ): Seq[Preprocessor] =\n    Seq(\n      ScriptPreprocessor,\n      MarkdownPreprocessor,\n      JavaPreprocessor(archiveCache, javaClassNameVersionOpt, javaCommand),\n      ScalaPreprocessor,\n      DataPreprocessor,\n      JarPreprocessor\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BloopSession.scala",
    "content": "package scala.build.bsp\n\nimport com.swoval.files.PathWatchers\n\nimport java.util.concurrent.atomic.AtomicReference\n\nimport scala.build.Build\nimport scala.build.compiler.BloopCompiler\nimport scala.build.input.{Inputs, OnDisk, SingleFile, Virtual}\n\nfinal class BloopSession(\n  val inputs: Inputs,\n  val inputsHash: String,\n  val remoteServer: BloopCompiler,\n  val bspServer: BspServer,\n  val watcher: Build.Watcher\n) {\n  def resetDiagnostics(localClient: BspClient): Unit =\n    for (targetId <- bspServer.targetIds)\n      inputs.flattened().foreach {\n        case f: SingleFile =>\n          localClient.resetDiagnostics(f.path, targetId)\n        case _: Virtual =>\n      }\n  def dispose(): Unit = {\n    watcher.dispose()\n    remoteServer.shutdown()\n  }\n\n  def registerWatchInputs(): Unit =\n    inputs.elements.foreach {\n      case elem: OnDisk =>\n        val eventFilter: PathWatchers.Event => Boolean = { event =>\n          val newOrDeletedFile =\n            event.getKind == PathWatchers.Event.Kind.Create ||\n            event.getKind == PathWatchers.Event.Kind.Delete\n          lazy val p        = os.Path(event.getTypedPath.getPath.toAbsolutePath)\n          lazy val relPath  = p.relativeTo(elem.path)\n          lazy val isHidden = relPath.segments.exists(_.startsWith(\".\"))\n          def isScalaFile   = relPath.last.endsWith(\".sc\") || relPath.last.endsWith(\".scala\")\n          def isJavaFile    = relPath.last.endsWith(\".java\")\n          newOrDeletedFile && !isHidden && (isScalaFile || isJavaFile)\n        }\n        val watcher0 = watcher.newWatcher()\n        watcher0.register(elem.path.toNIO, Int.MaxValue)\n        watcher0.addObserver {\n          Build.onChangeBufferedObserver { event =>\n            if (eventFilter(event))\n              watcher.schedule()\n          }\n        }\n      case _ =>\n    }\n}\n\nobject BloopSession {\n\n  def apply(\n    inputs: Inputs,\n    remoteServer: BloopCompiler,\n    bspServer: BspServer,\n    watcher: Build.Watcher\n  ): BloopSession = new BloopSession(inputs, inputs.sourceHash(), remoteServer, bspServer, watcher)\n\n  final class Reference {\n    private val ref         = new AtomicReference[BloopSession](null)\n    def get(): BloopSession = {\n      val session = ref.get()\n      if (session == null)\n        sys.error(\"BSP server not initialized yet\")\n      session\n    }\n    def getAndNullify(): Option[BloopSession] =\n      Option(ref.getAndSet(null))\n    def update(former: BloopSession, newer: BloopSession, ifError: String): Unit =\n      if (!ref.compareAndSet(former, newer))\n        sys.error(ifError)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/Bsp.scala",
    "content": "package scala.build.bsp\n\nimport java.io.{InputStream, OutputStream}\n\nimport scala.build.errors.BuildException\nimport scala.build.input.{Inputs, ScalaCliInvokeData}\nimport scala.concurrent.Future\n\ntrait Bsp {\n  def run(initialInputs: Inputs, initialBspOptions: BspReloadableOptions): Future[Unit]\n  def shutdown(): Unit\n}\n\nobject Bsp {\n  def create(\n    argsToInputs: Seq[String] => Either[BuildException, Inputs],\n    bspReloadableOptionsReference: BspReloadableOptions.Reference,\n    threads: BspThreads,\n    in: InputStream,\n    out: OutputStream,\n    actionableDiagnostics: Option[Boolean]\n  )(using ScalaCliInvokeData): Bsp =\n    new BspImpl(\n      argsToInputs,\n      bspReloadableOptionsReference,\n      threads,\n      in,\n      out,\n      actionableDiagnostics\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BspClient.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.{ScalaAction, ScalaDiagnostic, ScalaTextEdit, ScalaWorkspaceEdit}\nimport com.google.gson.{Gson, JsonElement}\n\nimport java.lang.Boolean as JBoolean\nimport java.net.URI\nimport java.nio.file.Paths\nimport java.util.concurrent.ConcurrentHashMap\n\nimport scala.build.Position.File\nimport scala.build.errors.{BuildException, CompositeBuildException, Diagnostic}\nimport scala.build.internal.util.WarningMessages\nimport scala.build.postprocessing.LineConversion.scalaLineToScLine\nimport scala.build.{BloopBuildClient, GeneratedSource, Logger}\nimport scala.jdk.CollectionConverters.*\n\nclass BspClient(\n  @volatile var logger: Logger,\n  var forwardToOpt: Option[b.BuildClient] = None\n) extends b.BuildClient with BuildClientForwardStubs with BloopBuildClient\n    with HasGeneratedSourcesImpl {\n\n  private def updatedPublishDiagnosticsParams(\n    params: b.PublishDiagnosticsParams,\n    genSource: GeneratedSource\n  ): b.PublishDiagnosticsParams = {\n    val updatedUri = genSource.reportingPath.fold(\n      _ => params.getTextDocument.getUri,\n      _.toNIO.toUri.toASCIIString\n    )\n    val updatedDiagnostics =\n      if (genSource.wrapperParamsOpt.isEmpty)\n        params.getDiagnostics\n      else\n        val updateLine = scalaLine => scalaLineToScLine(scalaLine, genSource.wrapperParamsOpt)\n        params.getDiagnostics.asScala.toSeq\n          .map { diag =>\n            val updatedDiagOpt = for {\n              startLine <- updateLine(diag.getRange.getStart.getLine)\n              endLine   <- updateLine(diag.getRange.getEnd.getLine)\n            } yield {\n              val diag0 = diag.duplicate()\n              diag0.getRange.getStart.setLine(startLine)\n              diag0.getRange.getEnd.setLine(endLine)\n\n              val scalaDiagnostic = new Gson().fromJson[b.ScalaDiagnostic](\n                diag0.getData().asInstanceOf[JsonElement],\n                classOf[b.ScalaDiagnostic]\n              )\n\n              scalaDiagnostic.getActions().asScala.foreach { action =>\n                for {\n                  change    <- action.getEdit().getChanges().asScala\n                  startLine <- updateLine(change.getRange.getStart.getLine)\n                  endLine   <- updateLine(change.getRange.getEnd.getLine)\n                } yield {\n                  change.getRange().getStart.setLine(startLine)\n                  change.getRange().getEnd.setLine(endLine)\n                }\n              }\n\n              diag0.setData(scalaDiagnostic)\n\n              if (\n                diag0.getMessage.contains(\n                  \"cannot be a main method since it cannot be accessed statically\"\n                )\n              )\n                diag0.setMessage(\n                  WarningMessages.mainAnnotationNotSupported( /* annotationIgnored */ false)\n                )\n\n              diag0\n            }\n            updatedDiagOpt.getOrElse(diag)\n          }\n          .asJava\n\n    val updatedTextDoc = new b.TextDocumentIdentifier(updatedUri)\n    val updatedParams  = new b.PublishDiagnosticsParams(\n      updatedTextDoc,\n      params.getBuildTarget,\n      updatedDiagnostics,\n      params.getReset\n    )\n    updatedParams.setOriginId(params.getOriginId)\n    updatedParams\n  }\n\n  override def onBuildPublishDiagnostics(params: b.PublishDiagnosticsParams): Unit = {\n    val path = os.Path(Paths.get(new URI(params.getTextDocument.getUri)))\n    buildExceptionDiagnosticsDocs.remove((path, params.getBuildTarget))\n\n    actualBuildPublishDiagnostics(params)\n  }\n\n  private def actualBuildPublishDiagnostics(params: b.PublishDiagnosticsParams): Unit = {\n    val updatedParamsOpt = targetScopeOpt(params.getBuildTarget).flatMap { scope =>\n      generatedSources.getOrElse(scope, HasGeneratedSources.GeneratedSources(Nil))\n        .uriMap\n        .get(params.getTextDocument.getUri)\n        .map { genSource =>\n          updatedPublishDiagnosticsParams(params, genSource)\n        }\n    }\n\n    updatedParamsOpt match {\n      case None =>\n        super.onBuildPublishDiagnostics(params)\n      case Some(updatedParams) =>\n        super.onBuildPublishDiagnostics(updatedParams)\n    }\n  }\n\n  def setProjectParams(newParams: Seq[String]): Unit                    = {}\n  def diagnostics: Option[Seq[(Either[String, os.Path], b.Diagnostic)]] = None\n  def clear(): Unit                                                     = {}\n\n  private val buildExceptionDiagnosticsDocs =\n    new ConcurrentHashMap[(os.Path, b.BuildTargetIdentifier), JBoolean]\n\n  def resetDiagnostics(path: os.Path, targetId: b.BuildTargetIdentifier): Unit = {\n    val id     = new b.TextDocumentIdentifier(path.toNIO.toUri.toASCIIString)\n    val params = new b.PublishDiagnosticsParams(\n      id,\n      targetId,\n      List.empty[b.Diagnostic].asJava,\n      true\n    )\n    actualBuildPublishDiagnostics(params)\n  }\n\n  def resetBuildExceptionDiagnostics(targetId: b.BuildTargetIdentifier): Unit =\n    for {\n      (key @ (path, elemTargetId), _) <- buildExceptionDiagnosticsDocs.asScala.toVector\n      if elemTargetId == targetId\n    } {\n      val removedValue = buildExceptionDiagnosticsDocs.remove(key)\n      if (removedValue != null) {\n        val id     = new b.TextDocumentIdentifier(path.toNIO.toUri.toASCIIString)\n        val params = new b.PublishDiagnosticsParams(\n          id,\n          targetId,\n          List.empty[b.Diagnostic].asJava,\n          true\n        )\n        actualBuildPublishDiagnostics(params)\n      }\n    }\n\n  def reportBuildException(\n    targetIdOpt: Option[b.BuildTargetIdentifier],\n    ex: BuildException,\n    reset: Boolean = true\n  ): Unit =\n    targetIdOpt match {\n      case None =>\n        logger.debug(s\"Not reporting $ex to users (no build target id)\")\n      case Some(targetId) =>\n        val touchedFiles = (ex match {\n          case c: CompositeBuildException =>\n            reportDiagnosticsForFiles(targetId, c.exceptions, reset = reset)\n          case _ => reportDiagnosticsForFiles(targetId, Seq(ex), reset = reset)\n        }).toSet\n\n        // Small chance of us wiping some Bloop diagnostics, if these happen\n        // between the call to remove and the call to actualBuildPublishDiagnostics.\n        for {\n          (key @ (path, elemTargetId), _) <- buildExceptionDiagnosticsDocs.asScala.toVector\n          if elemTargetId == targetId && !touchedFiles(path)\n        } {\n          val removedValue = buildExceptionDiagnosticsDocs.remove(key)\n          if (removedValue != null) {\n            val id     = new b.TextDocumentIdentifier(path.toNIO.toUri.toASCIIString)\n            val params = new b.PublishDiagnosticsParams(\n              id,\n              targetId,\n              List.empty[b.Diagnostic].asJava,\n              true\n            )\n            actualBuildPublishDiagnostics(params)\n          }\n        }\n    }\n\n  def reportDiagnosticsForFiles(\n    targetId: b.BuildTargetIdentifier,\n    diags: Seq[Diagnostic],\n    reset: Boolean = true\n  ): Seq[os.Path] =\n    if reset then // send diagnostic with reset only once for every file path\n      diags.flatMap { diag =>\n        diag.positions.map { position =>\n          Diagnostic(diag.message, diag.severity, Seq(position), diag.textEdit)\n        }\n      }\n        .groupBy(_.positions.headOption match\n          case Some(File(Right(path), _, _, _)) => Some(path)\n          case _                                => None)\n        .filter(_._1.isDefined)\n        .values\n        .toSeq\n        .flatMap {\n          case head :: tail =>\n            reportDiagnosticForFiles(targetId, reset = reset)(head)\n              ++ tail.flatMap(reportDiagnosticForFiles(targetId))\n          case _ => Nil\n        }\n    else\n      diags.flatMap(reportDiagnosticForFiles(targetId))\n\n  private def reportDiagnosticForFiles(\n    targetId: b.BuildTargetIdentifier,\n    reset: Boolean = false\n  )(diag: Diagnostic): Seq[os.Path] =\n    diag.positions.flatMap {\n      case File(Right(path), (startLine, startC), (endL, endC), _) =>\n        val id       = new b.TextDocumentIdentifier(path.toNIO.toUri.toASCIIString)\n        val startPos = new b.Position(startLine, startC)\n        val endPos   = new b.Position(endL, endC)\n        val range    = new b.Range(startPos, endPos)\n        val bDiag    =\n          new b.Diagnostic(range, diag.message)\n\n        diag.textEdit.foreach { textEdit =>\n          val bScalaTextEdit      = new ScalaTextEdit(range, textEdit.newText)\n          val bScalaWorkspaceEdit = new ScalaWorkspaceEdit(List(bScalaTextEdit).asJava)\n          val bAction             = new ScalaAction(textEdit.title)\n          bAction.setEdit(bScalaWorkspaceEdit)\n          val bScalaDiagnostic = new ScalaDiagnostic\n          bScalaDiagnostic.setActions(List(bAction).asJava)\n          bDiag.setDataKind(\"scala\")\n          bDiag.setData(new Gson().toJsonTree(bScalaDiagnostic))\n        }\n\n        bDiag.setSeverity(diag.severity.toBsp4j)\n        bDiag.setSource(\"scala-cli\")\n        val params = new b.PublishDiagnosticsParams(\n          id,\n          targetId,\n          List(bDiag).asJava,\n          reset\n        )\n        buildExceptionDiagnosticsDocs.put((path, targetId), JBoolean.TRUE)\n        actualBuildPublishDiagnostics(params)\n        Seq(path)\n      case _ => Nil\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BspImpl.scala",
    "content": "package scala.build.bsp\n\nimport bloop.rifle.BloopServer\nimport ch.epfl.scala.bsp4j as b\nimport com.github.plokhotnyuk.jsoniter_scala.core.{JsonReaderException, readFromArray}\nimport dependency.ScalaParameters\nimport org.eclipse.lsp4j.jsonrpc\nimport org.eclipse.lsp4j.jsonrpc.messages.ResponseError\n\nimport java.io.{InputStream, OutputStream}\nimport java.util.UUID\nimport java.util.concurrent.{CompletableFuture, Executor}\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.compiler.BloopCompiler\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  Diagnostic,\n  ParsingInputsException\n}\nimport scala.build.input.{Inputs, ScalaCliInvokeData}\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.collection.mutable.ListBuffer\nimport scala.compiletime.uninitialized\nimport scala.concurrent.duration.DurationInt\nimport scala.concurrent.{ExecutionContext, Future, Promise}\nimport scala.jdk.CollectionConverters.*\nimport scala.util.{Failure, Success}\n\n/** The implementation for [[Bsp]] command.\n  *\n  * @param argsToInputs\n  *   a function transforming terminal args to [[Inputs]]\n  * @param bspReloadableOptionsReference\n  *   reference to the current instance of [[BspReloadableOptions]]\n  * @param threads\n  *   BSP threads\n  * @param in\n  *   the input stream of bytes\n  * @param out\n  *   the output stream of bytes\n  */\nfinal class BspImpl(\n  argsToInputs: Seq[String] => Either[BuildException, Inputs],\n  bspReloadableOptionsReference: BspReloadableOptions.Reference,\n  threads: BspThreads,\n  in: InputStream,\n  out: OutputStream,\n  actionableDiagnostics: Option[Boolean]\n)(using ScalaCliInvokeData) extends Bsp {\n\n  import BspImpl.{PreBuildData, PreBuildProject, buildTargetIdToEvent, responseError}\n\n  private val shownGlobalMessages =\n    new java.util.concurrent.ConcurrentHashMap[String, Unit]()\n  private var actualLocalClient: BspClient                  = uninitialized\n  private var localClient: b.BuildClient & BloopBuildClient = uninitialized\n  private val bloopSession                                  = new BloopSession.Reference\n\n  /** Sends the buildTarget/didChange BSP notification to the BSP client, indicating that the build\n    * targets defined in the current session have changed.\n    *\n    * @param currentBloopSession\n    *   the current Bloop session\n    */\n  private def notifyBuildChange(currentBloopSession: BloopSession): Unit = {\n    val events =\n      for (targetId <- currentBloopSession.bspServer.targetIds)\n        yield {\n          val event = new b.BuildTargetEvent(targetId)\n          event.setKind(b.BuildTargetEventKind.CHANGED)\n          event\n        }\n    val params = new b.DidChangeBuildTarget(events.asJava)\n    actualLocalClient.onBuildTargetDidChange(params)\n  }\n\n  /** Initial setup for the Bloop project.\n    *\n    * @param currentBloopSession\n    *   the current Bloop session\n    * @param reloadableOptions\n    *   options which may be reloaded on a bsp workspace/reload request\n    * @param maybeRecoverOnError\n    *   a function handling [[BuildException]] instances based on [[Scope]], possibly recovering\n    *   them; returns None on recovery, Some(e: BuildException) otherwise\n    */\n  private def prepareBuild(\n    currentBloopSession: BloopSession,\n    reloadableOptions: BspReloadableOptions,\n    maybeRecoverOnError: Scope => BuildException => Option[BuildException] = _ => e => Some(e)\n  ): Either[(BuildException, Scope), PreBuildProject] = either[(BuildException, Scope)] {\n    val logger       = reloadableOptions.logger\n    val buildOptions = reloadableOptions.buildOptions\n    val verbosity    = reloadableOptions.verbosity\n    logger.log(\"Preparing build\")\n\n    val persistentLogger = new PersistentDiagnosticLogger(logger)\n    val bspServer        = currentBloopSession.bspServer\n    val inputs           = currentBloopSession.inputs\n\n    // allInputs contains elements from using directives\n    val (crossSources, allInputs) = value {\n      CrossSources.forInputs(\n        inputs = inputs,\n        preprocessors = Sources.defaultPreprocessors(\n          buildOptions.archiveCache,\n          buildOptions.internal.javaClassNameVersionOpt,\n          () => buildOptions.javaHome().value.javaCommand\n        ),\n        logger = persistentLogger,\n        suppressWarningOptions = buildOptions.suppressWarningOptions,\n        exclude = buildOptions.internal.exclude,\n        maybeRecoverOnError = maybeRecoverOnError(Scope.Main),\n        download = buildOptions.downloader\n      ).left.map((_, Scope.Main))\n    }\n\n    val sharedOptions = crossSources.sharedOptions(buildOptions)\n\n    if (verbosity >= 3)\n      pprint.err.log(crossSources)\n\n    val scopedSources =\n      value(crossSources.scopedSources(buildOptions).left.map((_, Scope.Main)))\n\n    if (verbosity >= 3)\n      pprint.err.log(scopedSources)\n\n    val sourcesMain = value {\n      scopedSources.sources(Scope.Main, sharedOptions, allInputs.workspace, persistentLogger)\n        .left.map((_, Scope.Main))\n    }\n\n    val sourcesTest = value {\n      scopedSources.sources(Scope.Test, sharedOptions, allInputs.workspace, persistentLogger)\n        .left.map((_, Scope.Test))\n    }\n\n    if (verbosity >= 3)\n      pprint.err.log(sourcesMain)\n\n    val options0Main = sourcesMain.buildOptions\n    val options0Test = sourcesTest.buildOptions.orElse(options0Main)\n\n    val generatedSourcesMain = sourcesMain.generateSources(allInputs.generatedSrcRoot(Scope.Main))\n    val generatedSourcesTest = sourcesTest.generateSources(allInputs.generatedSrcRoot(Scope.Test))\n\n    bspServer.setExtraDependencySources(options0Main.classPathOptions.extraSourceJars)\n    bspServer.setExtraTestDependencySources(options0Test.classPathOptions.extraSourceJars)\n    bspServer.setGeneratedSources(Scope.Main, generatedSourcesMain)\n    bspServer.setGeneratedSources(Scope.Test, generatedSourcesTest)\n\n    val (classesDir0Main, scalaParamsMain, artifactsMain, projectMain, buildChangedMain) = value {\n      val res = Build.prepareBuild(\n        inputs = allInputs,\n        sources = sourcesMain,\n        generatedSources = generatedSourcesMain,\n        options = options0Main,\n        scope = Scope.Main,\n        compiler = currentBloopSession.remoteServer,\n        logger = persistentLogger,\n        buildClient = localClient,\n        maybeRecoverOnError = maybeRecoverOnError(Scope.Main)\n      )\n      res.left.map((_, Scope.Main))\n    }\n\n    val (classesDir0Test, scalaParamsTest, artifactsTest, projectTest, buildChangedTest) = value {\n      val res = Build.prepareBuild(\n        inputs = allInputs,\n        sources = sourcesTest,\n        generatedSources = generatedSourcesTest,\n        options = options0Test,\n        scope = Scope.Test,\n        compiler = currentBloopSession.remoteServer,\n        logger = persistentLogger,\n        buildClient = localClient,\n        maybeRecoverOnError = maybeRecoverOnError(Scope.Test)\n      )\n      res.left.map((_, Scope.Test))\n    }\n\n    localClient.setGeneratedSources(Scope.Main, generatedSourcesMain)\n    localClient.setGeneratedSources(Scope.Test, generatedSourcesTest)\n\n    val mainScope = PreBuildData(\n      sourcesMain,\n      options0Main,\n      classesDir0Main,\n      scalaParamsMain,\n      artifactsMain,\n      projectMain,\n      generatedSourcesMain,\n      buildChangedMain\n    )\n\n    val testScope = PreBuildData(\n      sourcesTest,\n      options0Test,\n      classesDir0Test,\n      scalaParamsTest,\n      artifactsTest,\n      projectTest,\n      generatedSourcesTest,\n      buildChangedTest\n    )\n\n    if (actionableDiagnostics.getOrElse(true)) {\n      val projectOptions = options0Test.orElse(options0Main)\n      projectOptions.logActionableDiagnostics(persistentLogger)\n    }\n\n    PreBuildProject(mainScope, testScope, persistentLogger.diagnostics)\n  }\n\n  private def buildE(\n    currentBloopSession: BloopSession,\n    notifyChanges: Boolean,\n    reloadableOptions: BspReloadableOptions\n  ): Either[(BuildException, Scope), Unit] = {\n    def doBuildOnce(data: PreBuildData, scope: Scope): Either[(BuildException, Scope), Build] =\n      Build.buildOnce(\n        currentBloopSession.inputs,\n        data.sources,\n        data.generatedSources,\n        data.buildOptions,\n        scope,\n        reloadableOptions.logger,\n        actualLocalClient,\n        currentBloopSession.remoteServer,\n        partialOpt = None\n      ).left.map(_ -> scope)\n\n    either[(BuildException, Scope)] {\n      val preBuild = value(prepareBuild(currentBloopSession, reloadableOptions))\n      if (notifyChanges && (preBuild.mainScope.buildChanged || preBuild.testScope.buildChanged))\n        notifyBuildChange(currentBloopSession)\n      value(doBuildOnce(preBuild.mainScope, Scope.Main))\n      value(doBuildOnce(preBuild.testScope, Scope.Test))\n      ()\n    }\n  }\n\n  private def build(\n    currentBloopSession: BloopSession,\n    client: BspClient,\n    notifyChanges: Boolean,\n    reloadableOptions: BspReloadableOptions\n  ): Unit =\n    buildE(currentBloopSession, notifyChanges, reloadableOptions) match {\n      case Left((ex, scope)) =>\n        client.reportBuildException(\n          currentBloopSession.bspServer.targetScopeIdOpt(scope),\n          ex\n        )\n        reloadableOptions.logger.debug(s\"Caught $ex during BSP build, ignoring it\")\n      case Right(()) =>\n        for (targetId <- currentBloopSession.bspServer.targetIds)\n          client.resetBuildExceptionDiagnostics(targetId)\n    }\n\n  private def showGlobalWarningOnce(msg: String): Unit =\n    shownGlobalMessages.computeIfAbsent(\n      msg,\n      _ => {\n        val params = new b.ShowMessageParams(b.MessageType.WARNING, msg)\n        actualLocalClient.onBuildShowMessage(params)\n      }\n    )\n\n  /** Compilation logic, to be called on a buildTarget/compile BSP request.\n    *\n    * @param currentBloopSession\n    *   the current Bloop session\n    * @param executor\n    *   executor\n    * @param reloadableOptions\n    *   options which may be reloaded on a bsp workspace/reload request\n    * @param doCompile\n    *   (self-)reference to calling the compilation logic\n    * @return\n    *   a future of [[b.CompileResult]]\n    */\n  private def compile(\n    currentBloopSession: BloopSession,\n    executor: Executor,\n    reloadableOptions: BspReloadableOptions,\n    doCompile: () => CompletableFuture[b.CompileResult]\n  ): CompletableFuture[b.CompileResult] = {\n    val preBuild = CompletableFuture.supplyAsync(\n      () =>\n        prepareBuild(currentBloopSession, reloadableOptions) match {\n          case Right(preBuild) =>\n            if (preBuild.mainScope.buildChanged || preBuild.testScope.buildChanged)\n              notifyBuildChange(currentBloopSession)\n            Right(preBuild)\n          case Left((ex, scope)) =>\n            Left((ex, scope))\n        },\n      executor\n    )\n\n    preBuild.thenCompose {\n      case Left((ex, scope)) =>\n        val taskId = new b.TaskId(UUID.randomUUID().toString)\n\n        for targetId <- currentBloopSession.bspServer.targetScopeIdOpt(scope) do {\n          val target = targetId.getUri match {\n            case s\"$_?id=$targetId\" => targetId\n            case targetIdUri        => targetIdUri\n          }\n\n          val taskStartParams = new b.TaskStartParams(taskId)\n          taskStartParams.setEventTime(System.currentTimeMillis())\n          taskStartParams.setMessage(s\"Preprocessing '$target'\")\n          taskStartParams.setDataKind(b.TaskStartDataKind.COMPILE_TASK)\n          taskStartParams.setData(new b.CompileTask(targetId))\n\n          actualLocalClient.onBuildTaskStart(taskStartParams)\n\n          actualLocalClient.reportBuildException(\n            Some(targetId),\n            ex\n          )\n\n          val taskFinishParams = new b.TaskFinishParams(taskId, b.StatusCode.ERROR)\n          taskFinishParams.setEventTime(System.currentTimeMillis())\n          taskFinishParams.setMessage(s\"Preprocessed '$target'\")\n          taskFinishParams.setDataKind(b.TaskFinishDataKind.COMPILE_REPORT)\n\n          val errorSize = ex match {\n            case c: CompositeBuildException => c.exceptions.size\n            case _                          => 1\n          }\n\n          taskFinishParams.setData(new b.CompileReport(targetId, errorSize, 0))\n\n          actualLocalClient.onBuildTaskFinish(taskFinishParams)\n        }\n\n        CompletableFuture.completedFuture(\n          new b.CompileResult(b.StatusCode.ERROR)\n        )\n      case Right(params) =>\n        for (targetId <- currentBloopSession.bspServer.targetIds)\n          actualLocalClient.resetBuildExceptionDiagnostics(targetId)\n\n        val targetId = currentBloopSession.bspServer.targetIds.head\n        actualLocalClient.reportDiagnosticsForFiles(targetId, params.diagnostics, reset = false)\n\n        doCompile().thenCompose { res =>\n          def doPostProcess(data: PreBuildData, scope: Scope): Unit =\n            for (sv <- data.project.scalaCompiler.map(_.scalaVersion))\n              Build.postProcess(\n                data.generatedSources,\n                currentBloopSession.inputs.generatedSrcRoot(scope),\n                data.classesDir,\n                reloadableOptions.logger,\n                currentBloopSession.inputs.workspace,\n                updateSemanticDbs = true,\n                scalaVersion = sv,\n                buildOptions = data.buildOptions\n              ).left.foreach(_.foreach(showGlobalWarningOnce))\n\n          if (res.getStatusCode == b.StatusCode.OK)\n            CompletableFuture.supplyAsync(\n              () => {\n                doPostProcess(params.mainScope, Scope.Main)\n                doPostProcess(params.testScope, Scope.Test)\n                res\n              },\n              executor\n            )\n          else\n            CompletableFuture.completedFuture(res)\n        }\n    }\n  }\n\n  /** Returns a reference to the [[BspClient]], respecting the given verbosity\n    * @param verbosity\n    *   verbosity to be passed to the resulting [[BspImpl.LoggingBspClient]]\n    * @return\n    *   BSP client\n    */\n  private def getLocalClient(verbosity: Int): b.BuildClient & BloopBuildClient =\n    if (verbosity >= 3)\n      new BspImpl.LoggingBspClient(actualLocalClient)\n    else\n      actualLocalClient\n\n  /** Creates a fresh Bloop session\n    * @param inputs\n    *   all the inputs to be included in the session's context\n    * @param reloadableOptions\n    *   options which may be reloaded on a bsp workspace/reload request\n    * @param presetIntelliJ\n    *   a flag marking if this is in context of a BSP connection with IntelliJ (allowing to pass\n    *   this setting from a past session)\n    * @return\n    *   a new [[BloopSession]]\n    */\n  private def newBloopSession(\n    inputs: Inputs,\n    reloadableOptions: BspReloadableOptions,\n    presetIntelliJ: Boolean = false\n  ): BloopSession = {\n    val logger            = reloadableOptions.logger\n    val buildOptions      = reloadableOptions.buildOptions\n    val createBloopServer =\n      () =>\n        BloopServer.buildServer(\n          reloadableOptions.bloopRifleConfig,\n          \"scala-cli\",\n          Constants.version,\n          (inputs.workspace / Constants.workspaceDirName).toNIO,\n          Build.classesRootDir(inputs.workspace, inputs.projectName).toNIO,\n          localClient,\n          threads.buildThreads.bloop,\n          logger.bloopRifleLogger\n        )\n    val remoteServer = new BloopCompiler(\n      createBloopServer,\n      20.seconds,\n      strictBloopJsonCheck = buildOptions.internal.strictBloopJsonCheckOrDefault\n    )\n    lazy val bspServer = new BspServer(\n      remoteServer.bloopServer.server,\n      doCompile =>\n        compile(bloopSession0, threads.prepareBuildExecutor, reloadableOptions, doCompile),\n      logger,\n      presetIntelliJ\n    )\n\n    lazy val watcher = new Build.Watcher(\n      ListBuffer(),\n      threads.buildThreads.fileWatcher,\n      build(bloopSession0, actualLocalClient, notifyChanges = true, reloadableOptions),\n      ()\n    )\n    lazy val bloopSession0: BloopSession = BloopSession(inputs, remoteServer, bspServer, watcher)\n\n    bloopSession0.registerWatchInputs()\n    bspServer.newInputs(inputs)\n\n    bloopSession0\n  }\n\n  /** The logic for the actual running of the `bsp` command, initializing the BSP connection.\n    * @param initialInputs\n    *   the initial input sources passed upon initializing the BSP connection (which are subject to\n    *   change on subsequent workspace/reload requests)\n    */\n  override def run(initialInputs: Inputs, initialBspOptions: BspReloadableOptions): Future[Unit] = {\n    val logger    = initialBspOptions.logger\n    val verbosity = initialBspOptions.verbosity\n\n    actualLocalClient = new BspClient(logger)\n    localClient = getLocalClient(verbosity)\n\n    val currentBloopSession = newBloopSession(initialInputs, initialBspOptions)\n    bloopSession.update(null, currentBloopSession, \"BSP server already initialized\")\n\n    val actualLocalServer: b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer\n      & ScalaScriptBuildServer & HasGeneratedSources =\n      new BuildServerProxy(\n        () => bloopSession.get().bspServer,\n        () => onReload()\n      )\n\n    val localServer: b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer\n      & ScalaScriptBuildServer =\n      if verbosity >= 3\n      then new LoggingBuildServerAll(actualLocalServer)\n      else actualLocalServer\n\n    val launcher = new jsonrpc.Launcher.Builder[b.BuildClient]()\n      .setExecutorService(threads.buildThreads.bloop.jsonrpc) // FIXME No\n      .setInput(in)\n      .setOutput(out)\n      .setRemoteInterface(classOf[b.BuildClient])\n      .setLocalService(localServer)\n      .create()\n    val remoteClient = launcher.getRemoteProxy\n    actualLocalClient.forwardToOpt = Some(remoteClient)\n\n    actualLocalClient.newInputs(initialInputs)\n    currentBloopSession.resetDiagnostics(actualLocalClient)\n\n    val recoverOnError: Scope => BuildException => Option[BuildException] = scope =>\n      e => {\n        actualLocalClient.reportBuildException(actualLocalServer.targetScopeIdOpt(scope), e)\n        logger.log(e)\n        None\n      }\n\n    prepareBuild(\n      currentBloopSession,\n      initialBspOptions,\n      maybeRecoverOnError = recoverOnError\n    ) match {\n      case Left((ex, scope)) => recoverOnError(scope)(ex)\n      case Right(_)          =>\n    }\n\n    logger.log {\n      val hasConsole = System.console() != null\n      if (hasConsole)\n        \"Listening to incoming JSONRPC BSP requests, press Ctrl+D to exit.\"\n      else\n        \"Listening to incoming JSONRPC BSP requests.\"\n    }\n    val f = launcher.startListening()\n\n    val initiateFirstBuild: Runnable = { () =>\n      try build(currentBloopSession, actualLocalClient, notifyChanges = false, initialBspOptions)\n      catch {\n        case t: Throwable =>\n          logger.debug(s\"Caught $t during initial BSP build, ignoring it\")\n      }\n    }\n    threads.prepareBuildExecutor.submit(initiateFirstBuild)\n\n    val es      = ExecutionContext.fromExecutorService(threads.buildThreads.bloop.jsonrpc)\n    val futures = Seq(\n      BspImpl.naiveJavaFutureToScalaFuture(f).map(_ => ())(using es),\n      currentBloopSession.bspServer.initiateShutdown\n    )\n    Future.firstCompletedOf(futures)(using es)\n  }\n\n  /** Shuts down the current Bloop session */\n  override def shutdown(): Unit =\n    for (currentBloopSession <- bloopSession.getAndNullify())\n      currentBloopSession.dispose()\n\n  /** BSP reload logic, to be used on a workspace/reload BSP request\n    *\n    * @param currentBloopSession\n    *   the current Bloop session\n    * @param previousInputs\n    *   all the input sources present in the context before the reload\n    * @param newInputs\n    *   all the input sources to be included in the new context after the reload\n    * @param reloadableOptions\n    *   options which may be reloaded on a bsp workspace/reload request\n    * @return\n    *   a future containing a valid workspace/reload response\n    */\n  private def reloadBsp(\n    currentBloopSession: BloopSession,\n    previousInputs: Inputs,\n    newInputs: Inputs,\n    reloadableOptions: BspReloadableOptions\n  ): CompletableFuture[AnyRef] = {\n    val previousTargetIds = currentBloopSession.bspServer.targetIds\n    val wasIntelliJ       = currentBloopSession.bspServer.isIntelliJ\n\n    currentBloopSession.dispose()\n    val newBloopSession0 = newBloopSession(newInputs, reloadableOptions, wasIntelliJ)\n    bloopSession.update(currentBloopSession, newBloopSession0, \"Concurrent reload of workspace\")\n    actualLocalClient.newInputs(newInputs)\n\n    newBloopSession0.resetDiagnostics(actualLocalClient)\n    prepareBuild(newBloopSession0, reloadableOptions) match {\n      case Left((buildException, scope)) =>\n        CompletableFuture.completedFuture(\n          responseError(\n            s\"Can't reload workspace, build failed for scope ${scope.name}: ${buildException.message}\"\n          )\n        )\n      case Right(preBuildProject) =>\n        lazy val projectJavaHome = preBuildProject.mainScope.buildOptions\n          .javaHome()\n          .value\n\n        val finalBloopSession =\n          if (\n            bloopSession.get().remoteServer.jvmVersion.exists(_.value < projectJavaHome.version)\n          ) {\n            reloadableOptions.logger.log(\n              s\"Bloop JVM version too low, current ${bloopSession.get().remoteServer.jvmVersion.get\n                  .value} expected ${projectJavaHome.version}, restarting server\"\n            )\n            // RelodableOptions don't take into account buildOptions from sources\n            val updatedReloadableOptions = reloadableOptions.copy(\n              buildOptions =\n                reloadableOptions.buildOptions.orElse(preBuildProject.mainScope.buildOptions),\n              bloopRifleConfig = reloadableOptions.bloopRifleConfig.copy(\n                javaPath = projectJavaHome.javaCommand,\n                minimumBloopJvm = projectJavaHome.version\n              )\n            )\n\n            newBloopSession0.dispose()\n            val bloopSessionWithJvmOkay =\n              newBloopSession(newInputs, updatedReloadableOptions, wasIntelliJ)\n            bloopSession.update(\n              newBloopSession0,\n              bloopSessionWithJvmOkay,\n              \"Concurrent reload of workspace\"\n            )\n            bloopSessionWithJvmOkay\n          }\n          else newBloopSession0\n\n        if (previousInputs.projectName != preBuildProject.mainScope.project.projectName)\n          for (client <- finalBloopSession.bspServer.clientOpt) {\n            val newTargetIds = finalBloopSession.bspServer.targetIds\n            val events       =\n              newTargetIds.map(buildTargetIdToEvent(_, b.BuildTargetEventKind.CREATED)) ++\n                previousTargetIds.map(buildTargetIdToEvent(_, b.BuildTargetEventKind.DELETED))\n            val didChangeBuildTargetParams = new b.DidChangeBuildTarget(events.asJava)\n            client.onBuildTargetDidChange(didChangeBuildTargetParams)\n          }\n        CompletableFuture.completedFuture(new Object())\n    }\n  }\n\n  /** All the logic surrounding a workspace/reload (establishing the new inputs, settings and\n    * refreshing all the relevant variables), including the actual BSP workspace reloading.\n    *\n    * @return\n    *   a future containing a valid workspace/reload response\n    */\n  private def onReload(): CompletableFuture[AnyRef] = {\n    val currentBloopSession = bloopSession.get()\n    bspReloadableOptionsReference.reload()\n    val reloadableOptions = bspReloadableOptionsReference.get\n    val logger            = reloadableOptions.logger\n    val verbosity         = reloadableOptions.verbosity\n    actualLocalClient.logger = logger\n    localClient = getLocalClient(verbosity)\n    val ideInputsJsonPath =\n      currentBloopSession.inputs.workspace / Constants.workspaceDirName / \"ide-inputs.json\"\n    if (os.isFile(ideInputsJsonPath)) {\n      val maybeResponse = either[BuildException] {\n        val ideInputs = value {\n          try Right(readFromArray(os.read.bytes(ideInputsJsonPath))(using IdeInputs.codec))\n          catch {\n            case e: JsonReaderException =>\n              logger.debug(s\"Caught $e while decoding $ideInputsJsonPath\")\n              Left(new ParsingInputsException(e.getMessage, e))\n          }\n        }\n        val newInputs      = value(argsToInputs(ideInputs.args))\n        val newHash        = newInputs.sourceHash()\n        val previousInputs = currentBloopSession.inputs\n        val previousHash   = currentBloopSession.inputsHash\n        if newInputs == previousInputs && newHash == previousHash then\n          CompletableFuture.completedFuture(new Object)\n        else reloadBsp(currentBloopSession, previousInputs, newInputs, reloadableOptions)\n      }\n      maybeResponse match {\n        case Left(errorMessage) =>\n          CompletableFuture.completedFuture(\n            responseError(s\"Workspace reload failed, couldn't load sources: $errorMessage\")\n          )\n        case Right(r) => r\n      }\n    }\n    else\n      CompletableFuture.completedFuture(\n        responseError(\n          s\"Workspace reload failed, inputs file missing from workspace directory: ${ideInputsJsonPath.toString()}\"\n        )\n      )\n  }\n}\n\nobject BspImpl {\n\n  private def buildTargetIdToEvent(\n    targetId: b.BuildTargetIdentifier,\n    eventKind: b.BuildTargetEventKind\n  ): b.BuildTargetEvent = {\n    val event = new b.BuildTargetEvent(targetId)\n    event.setKind(eventKind)\n    event\n  }\n\n  private def responseError(\n    message: String,\n    errorCode: Int = JsonRpcErrorCodes.InternalError\n  ): ResponseError =\n    new ResponseError(errorCode, message, new Object())\n\n  // from https://github.com/com-lihaoyi/Ammonite/blob/7eb58c58ec8c252dc5bd1591b041fcae01cccf90/amm/interp/src/main/scala/ammonite/interp/script/AmmoniteBuildServer.scala#L550-L565\n  private def naiveJavaFutureToScalaFuture[T](\n    f: java.util.concurrent.Future[T]\n  ): Future[T] = {\n    val p = Promise[T]()\n    val t = new Thread {\n      setDaemon(true)\n      setName(\"bsp-wait-for-exit\")\n      override def run(): Unit =\n        p.complete {\n          try Success(f.get())\n          catch { case t: Throwable => Failure(t) }\n        }\n    }\n    t.start()\n    p.future\n  }\n\n  private final class LoggingBspClient(actualLocalClient: BspClient) extends LoggingBuildClient\n      with BloopBuildClient {\n    // in Scala 3 type of the method needs to be explicitly overridden\n    def underlying: scala.build.bsp.BspClient    = actualLocalClient\n    def clear()                                  = underlying.clear()\n    def diagnostics                              = underlying.diagnostics\n    def setProjectParams(newParams: Seq[String]) =\n      underlying.setProjectParams(newParams)\n    def setGeneratedSources(scope: Scope, newGeneratedSources: Seq[GeneratedSource]) =\n      underlying.setGeneratedSources(scope, newGeneratedSources)\n  }\n\n  private final case class PreBuildData(\n    sources: Sources,\n    buildOptions: BuildOptions,\n    classesDir: os.Path,\n    scalaParams: Option[ScalaParameters],\n    artifacts: Artifacts,\n    project: Project,\n    generatedSources: Seq[GeneratedSource],\n    buildChanged: Boolean\n  )\n\n  private final case class PreBuildProject(\n    mainScope: PreBuildData,\n    testScope: PreBuildData,\n    diagnostics: Seq[Diagnostic]\n  )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BspReloadableOptions.scala",
    "content": "package scala.build.bsp\n\nimport bloop.rifle.BloopRifleConfig\n\nimport scala.build.Logger\nimport scala.build.options.BuildOptions\n\n/** The options and configurations that may be picked up on a bsp workspace/reload request.\n  *\n  * @param buildOptions\n  *   passed options for building sources\n  * @param bloopRifleConfig\n  *   configuration for bloop-rifle\n  * @param logger\n  *   logger\n  * @param verbosity\n  *   the verbosity of logs\n  */\ncase class BspReloadableOptions(\n  buildOptions: BuildOptions,\n  bloopRifleConfig: BloopRifleConfig,\n  logger: Logger,\n  verbosity: Int\n)\n\nobject BspReloadableOptions {\n  class Reference(getReloaded: () => BspReloadableOptions) {\n    @volatile private var ref: BspReloadableOptions = getReloaded()\n    def get: BspReloadableOptions                   = ref\n    def reload(): Unit                              = ref = getReloaded()\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BspServer.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.{BuildClient, LogMessageParams, MessageType}\n\nimport java.io.{File, PrintWriter, StringWriter}\nimport java.net.URI\nimport java.util as ju\nimport java.util.concurrent.{CompletableFuture, TimeUnit}\n\nimport scala.build.Logger\nimport scala.build.internal.Constants\nimport scala.build.options.Scope\nimport scala.concurrent.{Future, Promise}\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Random\n\nclass BspServer(\n  bloopServer: b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer,\n  compile: (() => CompletableFuture[b.CompileResult]) => CompletableFuture[b.CompileResult],\n  logger: Logger,\n  presetIntelliJ: Boolean = false\n) extends BuildServerForwardStubs\n    with ScalaScriptBuildServer\n    with ScalaBuildServerForwardStubs\n    with JavaBuildServerForwardStubs\n    with JvmBuildServerForwardStubs\n    with HasGeneratedSourcesImpl {\n  private val client: Option[BuildClient] = None\n\n  @volatile private var intelliJ: Boolean = presetIntelliJ\n  def isIntelliJ: Boolean                 = intelliJ\n\n  def clientOpt: Option[BuildClient] = client\n\n  @volatile private var extraDependencySources: Seq[os.Path]    = Nil\n  def setExtraDependencySources(sourceJars: Seq[os.Path]): Unit = {\n    extraDependencySources = sourceJars\n  }\n\n  @volatile private var extraTestDependencySources: Seq[os.Path]    = Nil\n  def setExtraTestDependencySources(sourceJars: Seq[os.Path]): Unit = {\n    extraTestDependencySources = sourceJars\n  }\n\n  // Can we accept some errors in some circumstances?\n  override protected def onFatalError(throwable: Throwable, context: String): Unit = {\n    val sw = new StringWriter()\n    throwable.printStackTrace(new PrintWriter(sw))\n    val message =\n      s\"Fatal error has occured within $context. Shutting down the server:\\n ${sw.toString}\"\n    System.err.println(message)\n    client.foreach(_.onBuildLogMessage(new LogMessageParams(MessageType.ERROR, message)))\n\n    // wait random bit before shutting down server to reduce risk of multiple scala-cli instances starting bloop at the same time\n    val timeout = Random.nextInt(400)\n    TimeUnit.MILLISECONDS.sleep(100 + timeout)\n    sys.exit(1)\n  }\n\n  private def maybeUpdateProjectTargetUri(res: b.WorkspaceBuildTargetsResult): Unit =\n    for {\n      (_, n) <- projectNames.iterator\n      if n.targetUriOpt.isEmpty\n      target <- res.getTargets.asScala.iterator.find(_.getDisplayName == n.name)\n    } n.targetUriOpt = Some(target.getId.getUri)\n\n  private def stripInvalidTargets(params: b.WorkspaceBuildTargetsResult): Unit = {\n    val updatedTargets = params\n      .getTargets\n      .asScala\n      .filter(target => validTarget(target.getId))\n      .asJava\n    params.setTargets(updatedTargets)\n  }\n\n  private def check(params: b.CleanCacheParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in CleanCache request: $target\")\n    params\n  }\n  private def check(params: b.CompileParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in Compile request: $target\")\n    params\n  }\n  private def check(params: b.DependencySourcesParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in DependencySources request: $target\")\n    params\n  }\n  private def check(params: b.ResourcesParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in Resources request: $target\")\n    params\n  }\n  private def check(params: b.SourcesParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in Sources request: $target\")\n    params\n  }\n  private def check(params: b.TestParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in Test request: $target\")\n    params\n  }\n  private def check(params: b.DebugSessionParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in Test request: $target\")\n    params\n  }\n  private def check(params: b.OutputPathsParams): params.type = {\n    val invalidTargets = params.getTargets.asScala.filter(!validTarget(_))\n    for (target <- invalidTargets)\n      logger.debug(s\"invalid target in buildTargetOutputPaths request: $target\")\n    params\n  }\n  private def mapGeneratedSources(res: b.SourcesResult): Unit = {\n    val gen = generatedSources.values.toVector\n    for {\n      item <- res.getItems.asScala\n      if validTarget(item.getTarget)\n      sourceItem <- item.getSources.asScala\n      genSource  <- gen.iterator.flatMap(_.uriMap.get(sourceItem.getUri).iterator).take(1)\n      updatedUri <- genSource.reportingPath.toOption.map(_.toNIO.toUri.toASCIIString)\n    } {\n      sourceItem.setUri(updatedUri)\n      sourceItem.setGenerated(false)\n    }\n\n    // GeneratedSources not corresponding to files that exist on disk (unlike script wrappers)\n    val sourcesWithReportingPathString = generatedSources.values.flatMap(_.sources)\n      .filter(_.reportingPath.isLeft)\n\n    for {\n      item <- res.getItems.asScala\n      if validTarget(item.getTarget)\n      sourceItem <- item.getSources.asScala\n      if sourcesWithReportingPathString.exists(\n        _.generated.toNIO.toUri.toASCIIString == sourceItem.getUri\n      )\n    } sourceItem.setGenerated(true)\n  }\n\n  protected def forwardTo\n    : b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer = bloopServer\n\n  private val supportedLanguages: ju.List[String] = List(\n    \"scala\",\n    \"java\",\n    // This makes Metals requests \"wrapped sources\" stuff, that makes it handle .sc files better.\n    \"scala-sc\"\n  ).asJava\n\n  private def capabilities: b.BuildServerCapabilities = {\n    val capabilities = new b.BuildServerCapabilities\n    capabilities.setCompileProvider(new b.CompileProvider(supportedLanguages))\n    capabilities.setTestProvider(new b.TestProvider(supportedLanguages))\n    capabilities.setRunProvider(new b.RunProvider(supportedLanguages))\n    capabilities.setDebugProvider(new b.DebugProvider(supportedLanguages))\n    capabilities.setInverseSourcesProvider(true)\n    capabilities.setDependencySourcesProvider(true)\n    capabilities.setResourcesProvider(true)\n    capabilities.setBuildTargetChangedProvider(true)\n    capabilities.setJvmRunEnvironmentProvider(true)\n    capabilities.setJvmTestEnvironmentProvider(true)\n    capabilities.setCanReload(true)\n    capabilities.setDependencyModulesProvider(true)\n    capabilities.setOutputPathsProvider(true)\n    capabilities\n  }\n\n  override def buildInitialize(\n    params: b.InitializeBuildParams\n  ): CompletableFuture[b.InitializeBuildResult] = {\n    val res = new b.InitializeBuildResult(\n      \"scala-cli\",\n      Constants.version,\n      bloop.rifle.internal.BuildInfo.bspVersion,\n      capabilities\n    )\n    val buildComesFromIntelliJ = params.getDisplayName.toLowerCase.contains(\"intellij\")\n    intelliJ = buildComesFromIntelliJ\n    logger.debug(s\"IntelliJ build: $buildComesFromIntelliJ\")\n    CompletableFuture.completedFuture(res)\n  }\n\n  override def onBuildInitialized(): Unit = ()\n\n  override def buildTargetCleanCache(\n    params: b.CleanCacheParams\n  ): CompletableFuture[b.CleanCacheResult] =\n    super.buildTargetCleanCache(check(params))\n\n  override def buildTargetCompile(params: b.CompileParams): CompletableFuture[b.CompileResult] =\n    compile(() => super.buildTargetCompile(check(params.withVerbosity(logger.verbosity > 0))))\n\n  override def buildTargetDependencySources(\n    params: b.DependencySourcesParams\n  ): CompletableFuture[b.DependencySourcesResult] =\n    super.buildTargetDependencySources(check(params)).thenApply { res =>\n      val updatedItems = res.getItems.asScala.map {\n        case item if validTarget(item.getTarget) =>\n          val isTestTarget                = item.getTarget.getUri.endsWith(\"-test\")\n          val validExtraDependencySources =\n            if isTestTarget then (extraDependencySources ++ extraTestDependencySources).distinct\n            else extraDependencySources\n          val updatedSources = item.getSources.asScala ++ validExtraDependencySources.map {\n            sourceJar =>\n              sourceJar.toNIO.toUri.toASCIIString\n          }\n          new b.DependencySourcesItem(item.getTarget, updatedSources.asJava)\n        case other => other\n      }\n\n      new b.DependencySourcesResult(updatedItems.asJava)\n    }\n\n  override def buildTargetResources(\n    params: b.ResourcesParams\n  ): CompletableFuture[b.ResourcesResult] =\n    super.buildTargetResources(check(params))\n\n  override def buildTargetRun(params: b.RunParams): CompletableFuture[b.RunResult] = {\n    val target = params.getTarget\n    if (!validTarget(target))\n      logger.debug(\n        s\"Got invalid target in Run request: ${target.getUri} (expected ${targetScopeIdOpt(Scope.Main).orNull})\"\n      )\n    super.buildTargetRun(params)\n  }\n\n  override def buildTargetSources(params: b.SourcesParams): CompletableFuture[b.SourcesResult] =\n    super.buildTargetSources(check(params)).thenApply { res =>\n      val res0 = res.duplicate()\n      mapGeneratedSources(res0)\n      res0\n    }\n\n  override def buildTargetTest(params: b.TestParams): CompletableFuture[b.TestResult] =\n    super.buildTargetTest(check(params))\n\n  override def debugSessionStart(params: b.DebugSessionParams)\n    : CompletableFuture[b.DebugSessionAddress] =\n    super.debugSessionStart(check(params))\n\n  override def buildTargetOutputPaths(params: b.OutputPathsParams)\n    : CompletableFuture[b.OutputPathsResult] = {\n    check(params)\n    val targets         = params.getTargets.asScala.filter(validTarget)\n    val outputPathsItem =\n      targets\n        .map(buildTargetId => (buildTargetId, targetWorkspaceDirOpt(buildTargetId)))\n        .collect { case (buildTargetId, Some(targetUri)) => (buildTargetId, targetUri) }\n        .map {\n          case (buildTargetId, targetUri) =>\n            new b.OutputPathsItem(\n              buildTargetId,\n              List(b.OutputPathItem(targetUri, b.OutputPathItemKind.DIRECTORY)).asJava\n            )\n        }\n\n    CompletableFuture.completedFuture(new b.OutputPathsResult(outputPathsItem.asJava))\n  }\n\n  override def workspaceBuildTargets(): CompletableFuture[b.WorkspaceBuildTargetsResult] =\n    super.workspaceBuildTargets().thenApply { res =>\n      maybeUpdateProjectTargetUri(res)\n      val res0 = res.duplicate()\n      stripInvalidTargets(res0)\n      for (target <- res0.getTargets.asScala) {\n        val capabilities = target.getCapabilities\n        capabilities.setCanDebug(true)\n        val baseDirectory = new File(new URI(target.getBaseDirectory))\n        if (\n          isIntelliJ && baseDirectory.getName == Constants.workspaceDirName &&\n          baseDirectory\n            .getParentFile != null\n        ) {\n          val newBaseDirectory = baseDirectory.getParentFile.toPath.toUri.toASCIIString\n          target.setBaseDirectory(newBaseDirectory)\n        }\n      }\n      res0\n    }\n\n  def buildTargetWrappedSources(params: WrappedSourcesParams)\n    : CompletableFuture[WrappedSourcesResult] = {\n    def sourcesItemOpt(scope: Scope) = targetScopeIdOpt(scope).map { id =>\n      val items = generatedSources\n        .getOrElse(scope, HasGeneratedSources.GeneratedSources(Nil))\n        .sources\n        .flatMap { s =>\n          s.reportingPath.toSeq.map(_.toNIO.toUri.toASCIIString).map { uri =>\n            val item    = new WrappedSourceItem(uri, s.generated.toNIO.toUri.toASCIIString)\n            val content = os.read(s.generated)\n            item.setTopWrapper(\n              content\n                .linesIterator\n                .take(s.wrapperParamsOpt.map(_.topWrapperLineCount).getOrElse(0))\n                .mkString(\"\", System.lineSeparator(), System.lineSeparator())\n            )\n            item.setBottomWrapper(\"}\") // meh\n            item\n          }\n        }\n      new WrappedSourcesItem(id, items.asJava)\n    }\n    val sourceItems = Seq(Scope.Main, Scope.Test).flatMap(sourcesItemOpt(_).toSeq)\n    val res         = new WrappedSourcesResult(sourceItems.asJava)\n    CompletableFuture.completedFuture(res)\n  }\n\n  private val shutdownPromise                             = Promise[Unit]()\n  override def buildShutdown(): CompletableFuture[Object] = {\n    if (!shutdownPromise.isCompleted)\n      shutdownPromise.success(())\n    CompletableFuture.completedFuture(null)\n  }\n\n  override def onBuildExit(): Unit = ()\n\n  def initiateShutdown: Future[Unit] =\n    shutdownPromise.future\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BspThreads.scala",
    "content": "package scala.build.bsp\n\nimport java.util.concurrent.{ExecutorService, Executors}\n\nimport scala.build.BuildThreads\nimport scala.build.internal.Util\n\nfinal case class BspThreads(\n  buildThreads: BuildThreads,\n  prepareBuildExecutor: ExecutorService\n) {\n  def shutdown(): Unit = {\n    buildThreads.shutdown()\n    prepareBuildExecutor.shutdown()\n  }\n}\n\nobject BspThreads {\n  def withThreads[T](f: BspThreads => T): T = {\n    var threads: BspThreads = null\n    try {\n      threads = create()\n      f(threads)\n    }\n    finally if (threads != null) threads.shutdown()\n  }\n  def create(): BspThreads =\n    BspThreads(\n      BuildThreads.create(),\n      Executors.newSingleThreadExecutor(\n        Util.daemonThreadFactory(\"scala-cli-bsp-prepare-build-thread\")\n      )\n    )\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BuildClientForwardStubs.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\ntrait BuildClientForwardStubs extends b.BuildClient {\n  protected def forwardToOpt: Option[b.BuildClient]\n  override def onBuildLogMessage(params: b.LogMessageParams): Unit =\n    forwardToOpt.foreach(_.onBuildLogMessage(params))\n  override def onBuildPublishDiagnostics(params: b.PublishDiagnosticsParams): Unit =\n    forwardToOpt.foreach(_.onBuildPublishDiagnostics(params))\n  override def onBuildShowMessage(params: b.ShowMessageParams): Unit =\n    forwardToOpt.foreach(_.onBuildShowMessage(params))\n  override def onBuildTargetDidChange(params: b.DidChangeBuildTarget): Unit =\n    forwardToOpt.foreach(_.onBuildTargetDidChange(params))\n  override def onBuildTaskFinish(params: b.TaskFinishParams): Unit =\n    forwardToOpt.foreach(_.onBuildTaskFinish(params))\n  override def onBuildTaskProgress(params: b.TaskProgressParams): Unit =\n    forwardToOpt.foreach(_.onBuildTaskProgress(params))\n  override def onBuildTaskStart(params: b.TaskStartParams): Unit =\n    forwardToOpt.foreach(_.onBuildTaskStart(params))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BuildServerForwardStubs.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.{DependencyModulesParams, DependencyModulesResult}\n\nimport java.util.concurrent.CompletableFuture\nimport java.util.function.BiFunction\n\ntrait BuildServerForwardStubs extends b.BuildServer {\n  protected def forwardTo: b.BuildServer\n\n  protected def onFatalError(throwable: Throwable, context: String): Unit\n\n  def fatalExceptionHandler[T](methodName: String, params: Any*) = new BiFunction[T, Throwable, T] {\n    override def apply(maybeValue: T, maybeException: Throwable): T =\n      maybeException match {\n        case null =>\n          maybeValue\n        case error =>\n          val methodContext = s\"bloop bsp server, method: $methodName\"\n          val context       =\n            if (params.isEmpty) methodContext\n            else\n              params.mkString(s\"$methodContext, with params: \", \", \", \"\")\n          onFatalError(error, context)\n          throw error\n      }\n  }\n\n  override def buildShutdown(): CompletableFuture[Object] =\n    forwardTo.buildShutdown().handle(fatalExceptionHandler(\"buildShutdown\"))\n\n  override def buildTargetCleanCache(\n    params: b.CleanCacheParams\n  ): CompletableFuture[b.CleanCacheResult] =\n    forwardTo.buildTargetCleanCache(params)\n      .handle(fatalExceptionHandler(\"buildTargetCleanCache\", params))\n\n  override def buildTargetCompile(params: b.CompileParams): CompletableFuture[b.CompileResult] =\n    forwardTo.buildTargetCompile(params)\n      .handle(fatalExceptionHandler(\"buildTargetCompile\", params))\n\n  override def buildTargetDependencySources(\n    params: b.DependencySourcesParams\n  ): CompletableFuture[b.DependencySourcesResult] =\n    forwardTo.buildTargetDependencySources(params)\n      .handle(fatalExceptionHandler(\"buildTargetDependencySources\", params))\n\n  override def buildTargetInverseSources(\n    params: b.InverseSourcesParams\n  ): CompletableFuture[b.InverseSourcesResult] =\n    forwardTo.buildTargetInverseSources(params)\n      .handle(fatalExceptionHandler(\"buildTargetInverseSources\", params))\n\n  override def buildTargetResources(\n    params: b.ResourcesParams\n  ): CompletableFuture[b.ResourcesResult] =\n    forwardTo.buildTargetResources(params)\n      .handle(fatalExceptionHandler(\"buildTargetResources\", params))\n\n  override def buildTargetRun(params: b.RunParams): CompletableFuture[b.RunResult] =\n    forwardTo.buildTargetRun(params)\n      .handle(fatalExceptionHandler(\"buildTargetRun\", params))\n\n  override def buildTargetSources(params: b.SourcesParams): CompletableFuture[b.SourcesResult] =\n    forwardTo.buildTargetSources(params)\n      .handle(fatalExceptionHandler(\"buildTargetSources\", params))\n\n  override def buildTargetTest(params: b.TestParams): CompletableFuture[b.TestResult] =\n    forwardTo.buildTargetTest(params)\n      .handle(fatalExceptionHandler(\"buildTargetTest\", params))\n\n  override def debugSessionStart(params: b.DebugSessionParams)\n    : CompletableFuture[b.DebugSessionAddress] =\n    forwardTo.debugSessionStart(params)\n      .handle(fatalExceptionHandler(\"debugSessionStart\", params))\n\n  override def workspaceBuildTargets(): CompletableFuture[b.WorkspaceBuildTargetsResult] =\n    forwardTo.workspaceBuildTargets()\n      .handle(fatalExceptionHandler(\"workspaceBuildTargets\"))\n\n  /** This implementation should never be called and is merely a placeholder. As Bloop doesn't\n    * support reloading its workspace, Scala CLI has to reload Bloop instead. And so,\n    * [[BuildServerProxy]].workspaceReload() is responsible for the actual reload.\n    */\n  override def workspaceReload(): CompletableFuture[Object] =\n    CompletableFuture.completedFuture(new Object) // should never be called, as per scaladoc\n\n  override def buildTargetDependencyModules(params: DependencyModulesParams)\n    : CompletableFuture[DependencyModulesResult] =\n    forwardTo.buildTargetDependencyModules(params)\n      .handle(fatalExceptionHandler(\"buildTargetDependencyModules\", params))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/BuildServerProxy.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\nimport scala.build.GeneratedSource\nimport scala.build.input.Inputs\nimport scala.build.options.Scope\n\n/** A wrapper for [[BspServer]], allowing to reload the workspace on the fly.\n  * @param bspServer\n  *   the underlying BSP server relying on Bloop\n  * @param onReload\n  *   the actual `workspace/reload` function\n  */\nclass BuildServerProxy(\n  bspServer: () => BspServer,\n  onReload: () => CompletableFuture[Object]\n) extends b.BuildServer with b.ScalaBuildServer with b.JavaBuildServer with b.JvmBuildServer\n    with ScalaScriptBuildServer with HasGeneratedSources {\n  override def buildInitialize(params: b.InitializeBuildParams)\n    : CompletableFuture[b.InitializeBuildResult] = bspServer().buildInitialize(params)\n\n  override def onBuildInitialized(): Unit = bspServer().onBuildInitialized()\n\n  override def buildShutdown(): CompletableFuture[AnyRef] = bspServer().buildShutdown()\n\n  override def onBuildExit(): Unit = bspServer().onBuildExit()\n\n  override def workspaceBuildTargets(): CompletableFuture[b.WorkspaceBuildTargetsResult] =\n    bspServer().workspaceBuildTargets()\n\n  override def buildTargetSources(params: b.SourcesParams): CompletableFuture[b.SourcesResult] =\n    bspServer().buildTargetSources(params)\n\n  override def buildTargetInverseSources(params: b.InverseSourcesParams)\n    : CompletableFuture[b.InverseSourcesResult] = bspServer().buildTargetInverseSources(params)\n\n  override def buildTargetDependencySources(params: b.DependencySourcesParams)\n    : CompletableFuture[b.DependencySourcesResult] =\n    bspServer().buildTargetDependencySources(params)\n\n  override def buildTargetResources(params: b.ResourcesParams)\n    : CompletableFuture[b.ResourcesResult] = bspServer().buildTargetResources(params)\n\n  override def buildTargetCompile(params: b.CompileParams): CompletableFuture[b.CompileResult] =\n    bspServer().buildTargetCompile(params)\n\n  override def buildTargetTest(params: b.TestParams): CompletableFuture[b.TestResult] =\n    bspServer().buildTargetTest(params)\n\n  override def buildTargetRun(params: b.RunParams): CompletableFuture[b.RunResult] =\n    bspServer().buildTargetRun(params)\n\n  override def buildTargetCleanCache(params: b.CleanCacheParams)\n    : CompletableFuture[b.CleanCacheResult] = bspServer().buildTargetCleanCache(params)\n\n  override def buildTargetDependencyModules(params: b.DependencyModulesParams)\n    : CompletableFuture[b.DependencyModulesResult] =\n    bspServer().buildTargetDependencyModules(params)\n\n  override def buildTargetScalacOptions(params: b.ScalacOptionsParams)\n    : CompletableFuture[b.ScalacOptionsResult] = bspServer().buildTargetScalacOptions(params)\n\n  @deprecated\n  override def buildTargetScalaTestClasses(params: b.ScalaTestClassesParams)\n    : CompletableFuture[b.ScalaTestClassesResult] =\n    bspServer().buildTargetScalaTestClasses(params)\n\n  @deprecated\n  override def buildTargetScalaMainClasses(params: b.ScalaMainClassesParams)\n    : CompletableFuture[b.ScalaMainClassesResult] =\n    bspServer().buildTargetScalaMainClasses(params)\n\n  override def buildTargetJavacOptions(params: b.JavacOptionsParams)\n    : CompletableFuture[b.JavacOptionsResult] = bspServer().buildTargetJavacOptions(params)\n  override def debugSessionStart(params: b.DebugSessionParams)\n    : CompletableFuture[b.DebugSessionAddress] =\n    bspServer().debugSessionStart(params)\n\n  override def buildTargetWrappedSources(params: WrappedSourcesParams)\n    : CompletableFuture[WrappedSourcesResult] = bspServer().buildTargetWrappedSources(params)\n\n  override def buildTargetOutputPaths(params: b.OutputPathsParams)\n    : CompletableFuture[b.OutputPathsResult] =\n    bspServer().buildTargetOutputPaths(params)\n\n  /** As Bloop doesn't support `workspace/reload` requests and we have to reload it on Scala CLI's\n    * end, this is used instead of [[BspServer]]'s [[BuildServerForwardStubs]].workspaceReload().\n    */\n  override def workspaceReload(): CompletableFuture[AnyRef] =\n    onReload()\n\n  override def buildTargetJvmRunEnvironment(params: b.JvmRunEnvironmentParams)\n    : CompletableFuture[b.JvmRunEnvironmentResult] =\n    bspServer().buildTargetJvmRunEnvironment(params)\n\n  override def buildTargetJvmTestEnvironment(params: b.JvmTestEnvironmentParams)\n    : CompletableFuture[b.JvmTestEnvironmentResult] =\n    bspServer().buildTargetJvmTestEnvironment(params)\n\n  def targetIds: List[b.BuildTargetIdentifier]                        = bspServer().targetIds\n  def targetScopeIdOpt(scope: Scope): Option[b.BuildTargetIdentifier] =\n    bspServer().targetScopeIdOpt(scope)\n  def setGeneratedSources(scope: Scope, sources: Seq[GeneratedSource]): Unit =\n    bspServer().setGeneratedSources(scope, sources)\n  def setProjectName(workspace: os.Path, name: String, scope: Scope): Unit =\n    bspServer().setProjectName(workspace, name, scope)\n  def resetProjectNames(): Unit =\n    bspServer().resetProjectNames()\n  def newInputs(inputs: Inputs): Unit =\n    bspServer().newInputs(inputs)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/HasGeneratedSources.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport scala.build.GeneratedSource\nimport scala.build.input.Inputs\nimport scala.build.internal.Constants\nimport scala.build.options.Scope\n\ntrait HasGeneratedSources {\n  def targetIds: List[b.BuildTargetIdentifier]\n  def targetScopeIdOpt(scope: Scope): Option[b.BuildTargetIdentifier]\n  def setProjectName(workspace: os.Path, name: String, scope: Scope): Unit\n  def resetProjectNames(): Unit\n  def newInputs(inputs: Inputs): Unit\n  def setGeneratedSources(scope: Scope, sources: Seq[GeneratedSource]): Unit\n}\n\nobject HasGeneratedSources {\n  final case class GeneratedSources(\n    sources: Seq[GeneratedSource]\n  ) {\n\n    lazy val uriMap: Map[String, GeneratedSource] =\n      sources\n        .flatMap { g =>\n          g.reportingPath match {\n            case Left(_)  => Nil\n            case Right(_) => Seq(g.generated.toNIO.toUri.toASCIIString -> g)\n          }\n        }\n        .toMap\n  }\n\n  final case class ProjectName(\n    bloopWorkspace: os.Path,\n    name: String,\n    var targetUriOpt: Option[String] = None\n  ) {\n    targetUriOpt =\n      Some(\n        (bloopWorkspace / Constants.workspaceDirName)\n          .toIO\n          .toURI\n          .toASCIIString\n          .stripSuffix(\"/\") +\n          \"/?id=\" + name\n      )\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/HasGeneratedSourcesImpl.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport scala.build.GeneratedSource\nimport scala.build.input.Inputs\nimport scala.build.internal.Constants\nimport scala.build.options.Scope\nimport scala.collection.mutable\n\ntrait HasGeneratedSourcesImpl extends HasGeneratedSources {\n\n  import HasGeneratedSources._\n\n  protected val projectNames     = mutable.Map[Scope, ProjectName]()\n  protected val generatedSources = mutable.Map[Scope, GeneratedSources]()\n\n  def targetIds: List[b.BuildTargetIdentifier] =\n    projectNames\n      .toList\n      .sortBy(_._1)\n      .map(_._2)\n      .flatMap(_.targetUriOpt)\n      .map(uri => new b.BuildTargetIdentifier(uri))\n\n  def targetScopeIdOpt(scope: Scope): Option[b.BuildTargetIdentifier] =\n    projectNames\n      .get(scope)\n      .flatMap(_.targetUriOpt)\n      .map(uri => new b.BuildTargetIdentifier(uri))\n\n  def resetProjectNames(): Unit =\n    projectNames.clear()\n  def setProjectName(workspace: os.Path, name: String, scope: Scope): Unit =\n    if (!projectNames.contains(scope))\n      projectNames(scope) = ProjectName(workspace, name)\n\n  def newInputs(inputs: Inputs): Unit = {\n    resetProjectNames()\n    setProjectName(inputs.workspace, inputs.projectName, Scope.Main)\n    setProjectName(inputs.workspace, inputs.scopeProjectName(Scope.Test), Scope.Test)\n  }\n\n  def setGeneratedSources(scope: Scope, sources: Seq[GeneratedSource]): Unit = {\n    generatedSources(scope) = GeneratedSources(sources)\n  }\n\n  protected def targetWorkspaceDirOpt(id: b.BuildTargetIdentifier): Option[String] =\n    projectNames.collectFirst {\n      case (_, projName) if projName.targetUriOpt.contains(id.getUri) =>\n        (projName.bloopWorkspace / Constants.workspaceDirName).toIO.toURI.toASCIIString\n    }\n  protected def targetScopeOpt(id: b.BuildTargetIdentifier): Option[Scope] =\n    projectNames.collectFirst {\n      case (scope, projName) if projName.targetUriOpt.contains(id.getUri) =>\n        scope\n    }\n  protected def validTarget(id: b.BuildTargetIdentifier): Boolean =\n    targetScopeOpt(id).nonEmpty\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/IdeInputs.scala",
    "content": "package scala.build.bsp\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\n\nfinal case class IdeInputs(args: Seq[String])\n\nobject IdeInputs {\n  implicit lazy val codec: JsonValueCodec[IdeInputs] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/JavaBuildServerForwardStubs.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.{JavacOptionsParams, JavacOptionsResult}\n\nimport java.util.concurrent.CompletableFuture\n\ntrait JavaBuildServerForwardStubs extends b.JavaBuildServer {\n  protected def forwardTo: b.JavaBuildServer\n\n  override def buildTargetJavacOptions(\n    params: JavacOptionsParams\n  ): CompletableFuture[JavacOptionsResult] =\n    forwardTo.buildTargetJavacOptions(params)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/JsonRpcErrorCodes.scala",
    "content": "package scala.build.bsp\n\n/** Response error codes as defined in JSON RPC.\n  * [[https://www.jsonrpc.org/specification#error_object]]\n  */\nobject JsonRpcErrorCodes {\n  val ParseError: Int     = -32700 // Invalid JSON was received by the server.\n  val InvalidRequest: Int = -32600 // The JSON sent is not a valid Request object.\n  val MethodNotFound: Int = -32601 // The method does not exist / is not available.\n  val InvalidParams: Int  = -32602 // Invalid method parameter(s).\n  val InternalError: Int  = -32603 // Internal JSON-RPC error.\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/JvmBuildServerForwardStubs.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\ntrait JvmBuildServerForwardStubs extends b.JvmBuildServer {\n  protected def forwardTo: b.JvmBuildServer\n\n  override def buildTargetJvmRunEnvironment(params: b.JvmRunEnvironmentParams)\n    : CompletableFuture[b.JvmRunEnvironmentResult] =\n    forwardTo.buildTargetJvmRunEnvironment(params)\n\n  override def buildTargetJvmTestEnvironment(params: b.JvmTestEnvironmentParams)\n    : CompletableFuture[b.JvmTestEnvironmentResult] =\n    forwardTo.buildTargetJvmTestEnvironment(params)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/LoggingBuildClient.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\ntrait LoggingBuildClient extends b.BuildClient {\n  protected def underlying: b.BuildClient\n  override def onBuildLogMessage(params: b.LogMessageParams): Unit =\n    underlying.onBuildLogMessage(pprint.err.log(params))\n  override def onBuildPublishDiagnostics(params: b.PublishDiagnosticsParams): Unit =\n    underlying.onBuildPublishDiagnostics(pprint.err.log(params))\n  override def onBuildShowMessage(params: b.ShowMessageParams): Unit =\n    underlying.onBuildShowMessage(pprint.err.log(params))\n  override def onBuildTargetDidChange(params: b.DidChangeBuildTarget): Unit =\n    underlying.onBuildTargetDidChange(pprint.err.log(params))\n  override def onBuildTaskFinish(params: b.TaskFinishParams): Unit =\n    underlying.onBuildTaskFinish(pprint.err.log(params))\n  override def onBuildTaskProgress(params: b.TaskProgressParams): Unit =\n    underlying.onBuildTaskProgress(pprint.err.log(params))\n  override def onBuildTaskStart(params: b.TaskStartParams): Unit =\n    underlying.onBuildTaskStart(pprint.err.log(params))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/LoggingBuildServer.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.{DependencyModulesParams, DependencyModulesResult}\n\nimport java.util.concurrent.CompletableFuture\n\ntrait LoggingBuildServer extends b.BuildServer {\n  protected def underlying: b.BuildServer\n  override def buildInitialize(\n    params: b.InitializeBuildParams\n  ): CompletableFuture[b.InitializeBuildResult] =\n    underlying.buildInitialize(pprint.err.log(params)).logF\n  override def onBuildExit(): Unit =\n    underlying.onBuildExit()\n  override def onBuildInitialized(): Unit =\n    underlying.onBuildInitialized()\n  override def buildShutdown(): CompletableFuture[Object] =\n    underlying.buildShutdown().logF\n  override def buildTargetCleanCache(\n    params: b.CleanCacheParams\n  ): CompletableFuture[b.CleanCacheResult] =\n    underlying.buildTargetCleanCache(pprint.err.log(params)).logF\n  override def buildTargetCompile(params: b.CompileParams): CompletableFuture[b.CompileResult] =\n    underlying.buildTargetCompile(pprint.err.log(params)).logF\n  override def buildTargetDependencySources(\n    params: b.DependencySourcesParams\n  ): CompletableFuture[b.DependencySourcesResult] =\n    underlying.buildTargetDependencySources(pprint.err.log(params)).logF\n  override def buildTargetInverseSources(\n    params: b.InverseSourcesParams\n  ): CompletableFuture[b.InverseSourcesResult] =\n    underlying.buildTargetInverseSources(pprint.err.log(params)).logF\n  override def buildTargetResources(\n    params: b.ResourcesParams\n  ): CompletableFuture[b.ResourcesResult] =\n    underlying.buildTargetResources(pprint.err.log(params)).logF\n  override def buildTargetRun(params: b.RunParams): CompletableFuture[b.RunResult] =\n    underlying.buildTargetRun(pprint.err.log(params)).logF\n  override def buildTargetSources(params: b.SourcesParams): CompletableFuture[b.SourcesResult] =\n    underlying.buildTargetSources(pprint.err.log(params)).logF\n  override def buildTargetTest(params: b.TestParams): CompletableFuture[b.TestResult] =\n    underlying.buildTargetTest(pprint.err.log(params)).logF\n  override def debugSessionStart(params: b.DebugSessionParams)\n    : CompletableFuture[b.DebugSessionAddress] =\n    underlying.debugSessionStart(pprint.err.log(params)).logF\n  override def workspaceBuildTargets(): CompletableFuture[b.WorkspaceBuildTargetsResult] =\n    underlying.workspaceBuildTargets().logF\n  override def workspaceReload(): CompletableFuture[Object] =\n    underlying.workspaceReload().logF\n  override def buildTargetDependencyModules(params: DependencyModulesParams)\n    : CompletableFuture[DependencyModulesResult] =\n    underlying.buildTargetDependencyModules(pprint.err.log(params)).logF\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/LoggingBuildServerAll.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\nclass LoggingBuildServerAll(\n  val underlying: b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer &\n    ScalaScriptBuildServer\n) extends LoggingBuildServer with LoggingScalaBuildServer with LoggingJavaBuildServer\n    with LoggingJvmBuildServer\n    with ScalaScriptBuildServer {\n\n  def buildTargetWrappedSources(params: WrappedSourcesParams)\n    : CompletableFuture[WrappedSourcesResult] =\n    underlying.buildTargetWrappedSources(pprint.err.log(params)).logF\n\n  override def buildTargetOutputPaths(params: b.OutputPathsParams)\n    : CompletableFuture[b.OutputPathsResult] =\n    underlying.buildTargetOutputPaths(pprint.err.log(params)).logF\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/LoggingJavaBuildServer.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\ntrait LoggingJavaBuildServer extends b.JavaBuildServer {\n  protected def underlying: b.JavaBuildServer\n  override def buildTargetJavacOptions(\n    params: b.JavacOptionsParams\n  ): CompletableFuture[b.JavacOptionsResult] =\n    underlying.buildTargetJavacOptions(pprint.err.log(params)).logF\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/LoggingJvmBuildServer.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\ntrait LoggingJvmBuildServer extends b.JvmBuildServer {\n  protected def underlying: b.JvmBuildServer\n\n  override def buildTargetJvmRunEnvironment(params: b.JvmRunEnvironmentParams)\n    : CompletableFuture[b.JvmRunEnvironmentResult] =\n    underlying.buildTargetJvmRunEnvironment(pprint.err.log(params)).logF\n\n  override def buildTargetJvmTestEnvironment(params: b.JvmTestEnvironmentParams)\n    : CompletableFuture[b.JvmTestEnvironmentResult] =\n    underlying.buildTargetJvmTestEnvironment(pprint.err.log(params)).logF\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/LoggingScalaBuildServer.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\ntrait LoggingScalaBuildServer extends b.ScalaBuildServer {\n  protected def underlying: b.ScalaBuildServer\n  @deprecated\n  override def buildTargetScalaMainClasses(\n    params: b.ScalaMainClassesParams\n  ): CompletableFuture[b.ScalaMainClassesResult] =\n    underlying.buildTargetScalaMainClasses(pprint.err.log(params)).logF\n  @deprecated\n  override def buildTargetScalaTestClasses(\n    params: b.ScalaTestClassesParams\n  ): CompletableFuture[b.ScalaTestClassesResult] =\n    underlying.buildTargetScalaTestClasses(pprint.err.log(params)).logF\n  override def buildTargetScalacOptions(\n    params: b.ScalacOptionsParams\n  ): CompletableFuture[b.ScalacOptionsResult] =\n    underlying.buildTargetScalacOptions(pprint.err.log(params)).logF\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/ScalaBuildServerForwardStubs.scala",
    "content": "package scala.build.bsp\n\nimport ch.epfl.scala.bsp4j as b\n\nimport java.util.concurrent.CompletableFuture\n\ntrait ScalaBuildServerForwardStubs extends b.ScalaBuildServer {\n  protected def forwardTo: b.ScalaBuildServer\n  @deprecated\n  override def buildTargetScalaMainClasses(\n    params: b.ScalaMainClassesParams\n  ): CompletableFuture[b.ScalaMainClassesResult] =\n    forwardTo.buildTargetScalaMainClasses(params)\n\n  @deprecated\n  override def buildTargetScalaTestClasses(\n    params: b.ScalaTestClassesParams\n  ): CompletableFuture[b.ScalaTestClassesResult] =\n    forwardTo.buildTargetScalaTestClasses(params)\n  override def buildTargetScalacOptions(\n    params: b.ScalacOptionsParams\n  ): CompletableFuture[b.ScalacOptionsResult] =\n    forwardTo.buildTargetScalacOptions(params)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/package.scala",
    "content": "package scala.build\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.SourcesItem\n\nimport java.util.concurrent.CompletableFuture\n\nimport scala.jdk.CollectionConverters.*\n\npackage object bsp {\n\n  extension (compileParams: b.CompileParams) {\n    def duplicate(): b.CompileParams = {\n      val params = new b.CompileParams(compileParams.getTargets)\n      params.setOriginId(compileParams.getOriginId)\n      params.setArguments(compileParams.getArguments)\n      params\n    }\n\n    def withExtraArgs(extraArgs: List[String]): b.CompileParams = {\n      val params            = compileParams.duplicate()\n      val previousArguments = Option(params.getArguments).toList.flatMap(_.asScala)\n      val newArguments      = (previousArguments ++ extraArgs).asJava\n      params.setArguments(newArguments)\n      params\n    }\n\n    def withVerbosity(verbose: Boolean): b.CompileParams = {\n      val verboseArg                 = \"--verbose\"\n      val argumentsContainVerboseArg =\n        Option(compileParams.getArguments).exists(_.contains(verboseArg))\n      if verbose && !argumentsContainVerboseArg then compileParams.withExtraArgs(List(verboseArg))\n      else compileParams\n    }\n  }\n\n  implicit class Ext[T](private val f: CompletableFuture[T]) extends AnyVal {\n    def logF: CompletableFuture[T] =\n      f.handle { (res, err) =>\n        if (err == null)\n          pprint.err.log(res)\n        else\n          throw pprint.err.log(err)\n      }\n  }\n\n  implicit class BuildTargetIdentifierExt(\n    private val item: b.BuildTargetIdentifier\n  ) extends AnyVal {\n    def duplicate(): b.BuildTargetIdentifier =\n      new b.BuildTargetIdentifier(item.getUri)\n  }\n\n  implicit class SourceItemExt(private val item: b.SourceItem) extends AnyVal {\n    def duplicate(): b.SourceItem =\n      new b.SourceItem(item.getUri, item.getKind, item.getGenerated)\n  }\n\n  implicit class SourcesItemExt(private val item: b.SourcesItem) extends AnyVal {\n    def duplicate(): b.SourcesItem = {\n      val other = new b.SourcesItem(\n        item.getTarget,\n        item.getSources.asScala.map(_.duplicate()).asJava\n      )\n      for (roots <- Option(item.getRoots))\n        other.setRoots(roots.asScala.toList.asJava)\n      other\n    }\n  }\n\n  implicit class SourcesResultExt(private val res: b.SourcesResult) extends AnyVal {\n    def duplicate(): b.SourcesResult =\n      new b.SourcesResult(\n        res.getItems().asScala.toList\n          .map((item: SourcesItem) => item.duplicate()).asJava\n      )\n  }\n\n  implicit class BuildTargetCapabilitiesExt(\n    private val capabilities: b.BuildTargetCapabilities\n  ) extends AnyVal {\n    def duplicate(): b.BuildTargetCapabilities =\n      val buildTarget = new b.BuildTargetCapabilities()\n      buildTarget.setCanCompile(capabilities.getCanCompile)\n      buildTarget.setCanTest(capabilities.getCanTest)\n      buildTarget.setCanRun(capabilities.getCanRun)\n      buildTarget\n  }\n  implicit class BuildTargetExt(private val target: b.BuildTarget) extends AnyVal {\n    def duplicate(): b.BuildTarget = {\n      val target0 = new b.BuildTarget(\n        target.getId.duplicate(),\n        target.getTags.asScala.toList.asJava,\n        target.getLanguageIds.asScala.toList.asJava,\n        target.getDependencies.asScala.toList.map(_.duplicate()).asJava,\n        target.getCapabilities.duplicate()\n      )\n      target0.setBaseDirectory(target.getBaseDirectory)\n      target0.setData(target.getData) // FIXME Duplicate this when we can too?\n      target0.setDataKind(target.getDataKind)\n      target0.setDisplayName(target.getDisplayName)\n      target0\n    }\n  }\n  implicit class WorkspaceBuildTargetsResultExt(\n    private val res: b.WorkspaceBuildTargetsResult\n  ) extends AnyVal {\n    def duplicate(): b.WorkspaceBuildTargetsResult =\n      new b.WorkspaceBuildTargetsResult(res.getTargets.asScala.toList.map(_.duplicate()).asJava)\n  }\n\n  implicit class LocationExt(private val loc: b.Location) extends AnyVal {\n    def duplicate(): b.Location =\n      new b.Location(loc.getUri, loc.getRange.duplicate())\n  }\n  implicit class DiagnosticRelatedInformationExt(\n    private val info: b.DiagnosticRelatedInformation\n  ) extends AnyVal {\n    def duplicate(): b.DiagnosticRelatedInformation =\n      new b.DiagnosticRelatedInformation(info.getLocation.duplicate(), info.getMessage)\n  }\n  implicit class PositionExt(private val pos: b.Position) extends AnyVal {\n    def duplicate(): b.Position =\n      new b.Position(pos.getLine, pos.getCharacter)\n  }\n  implicit class RangeExt(private val range: b.Range) extends AnyVal {\n    def duplicate(): b.Range =\n      new b.Range(range.getStart.duplicate(), range.getEnd.duplicate())\n  }\n  implicit class DiagnosticExt(private val diag: b.Diagnostic) extends AnyVal {\n    def duplicate(): b.Diagnostic = {\n      val diag0 = new b.Diagnostic(diag.getRange.duplicate(), diag.getMessage)\n      diag0.setCode(diag.getCode)\n      diag0.setRelatedInformation(\n        Option(diag.getRelatedInformation).map(_.asScala.map(_.duplicate()).asJava).orNull\n      )\n      diag0.setSeverity(diag.getSeverity)\n      diag0.setSource(diag.getSource)\n      diag0.setData(diag.getData)\n      diag0\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/bsp/protocol/TextEdit.scala",
    "content": "package scala.build.bsp.protocol\n\nimport ch.epfl.scala.bsp4j as b\nimport com.google.gson.Gson\n\ncase class TextEdit(range: b.Range, newText: String) {\n  def toJsonTree() = new Gson().toJsonTree(this)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/BloopCompiler.scala",
    "content": "package scala.build.compiler\nimport scala.annotation.tailrec\nimport scala.build.{Bloop, Logger, Position, Positioned, Project}\nimport scala.concurrent.duration.FiniteDuration\n\nfinal class BloopCompiler(\n  createServer: () => bloop.rifle.BloopServer,\n  buildTargetsTimeout: FiniteDuration,\n  strictBloopJsonCheck: Boolean\n) extends ScalaCompiler {\n  private var currentBloopServer: bloop.rifle.BloopServer =\n    createServer()\n  def bloopServer: bloop.rifle.BloopServer =\n    currentBloopServer\n\n  def jvmVersion: Option[Positioned[Int]] =\n    Some(\n      Positioned(\n        List(Position.Bloop(bloopServer.bloopInfo.javaHome)),\n        bloopServer.bloopInfo.jvmVersion\n      )\n    )\n\n  def prepareProject(\n    project: Project,\n    logger: Logger\n  ): Boolean =\n    project.writeBloopFile(strictBloopJsonCheck, logger)\n\n  def compile(\n    project: Project,\n    logger: Logger\n  ): Boolean = {\n    @tailrec\n    def helper(remainingAttempts: Int): Boolean =\n      Bloop.compile(project.projectName, bloopServer.server, logger, buildTargetsTimeout) match {\n        case Right(res) => res\n        case Left(ex)   =>\n          if (remainingAttempts > 1) {\n            logger.debug(s\"Seems Bloop server exited (got $ex), trying to restart one\")\n            currentBloopServer = createServer()\n            helper(remainingAttempts - 1)\n          }\n          else\n            throw new Exception(\n              \"Seems compilation server exited, and wasn't able to restart one\",\n              ex\n            )\n      }\n\n    helper(2)\n  }\n\n  def shutdown(): Unit =\n    bloopServer.shutdown()\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/BloopCompilerMaker.scala",
    "content": "package scala.build.compiler\n\nimport bloop.rifle.{BloopRifleConfig, BloopServer, BloopThreads}\nimport ch.epfl.scala.bsp4j.BuildClient\n\nimport scala.build.errors.{BuildException, FetchingDependenciesError, Severity}\nimport scala.build.internal.Constants\nimport scala.build.internal.util.WarningMessages\nimport scala.build.options.BuildOptions\nimport scala.build.{Logger, retry}\nimport scala.concurrent.duration.DurationInt\nimport scala.util.Try\n\nfinal class BloopCompilerMaker(\n  getConfig: BuildOptions => Either[BuildException, BloopRifleConfig],\n  threads: BloopThreads,\n  strictBloopJsonCheck: Boolean,\n  offline: Boolean\n) extends ScalaCompilerMaker {\n  def create(\n    workspace: os.Path,\n    classesDir: os.Path,\n    buildClient: BuildClient,\n    logger: Logger,\n    buildOptions: BuildOptions\n  ): Either[BuildException, ScalaCompiler] =\n    getConfig(buildOptions) match\n      case Left(_) if offline =>\n        logger.diagnostic(WarningMessages.offlineModeBloopJvmNotFound, Severity.Warning)\n        SimpleScalaCompilerMaker(\"java\", Nil).create(\n          workspace,\n          classesDir,\n          buildClient,\n          logger,\n          buildOptions\n        )\n      case Right(config) =>\n        val createBuildServer =\n          () =>\n            // retrying here in case a number of Scala CLI processes are started at the same time\n            // and they all try to connect to the server / spawn a new server\n            // otherwise, users may run into one of:\n            //   - libdaemonjvm.client.ConnectError$ZombieFound\n            //   - Caught java.lang.RuntimeException: Fatal error, could not spawn Bloop: not running\n            //   - java.lang.RuntimeException: Bloop BSP connection in (...) was unexpectedly closed or bloop didn't start.\n            // if a sufficiently large number of processes was started, this may happen anyway, of course\n            retry(if offline then 1 else 3)(logger) {\n              BloopServer.buildServer(\n                config,\n                \"scala-cli\",\n                Constants.version,\n                workspace.toNIO,\n                classesDir.toNIO,\n                buildClient,\n                threads,\n                logger.bloopRifleLogger\n              )\n            }\n\n        val res = Try(new BloopCompiler(createBuildServer, 20.seconds, strictBloopJsonCheck))\n          .toEither\n          .left.flatMap {\n            case e if offline =>\n              e.getCause match\n                case _: FetchingDependenciesError =>\n                  logger.diagnostic(\n                    WarningMessages.offlineModeBloopNotFound,\n                    Severity.Warning\n                  )\n                  SimpleScalaCompilerMaker(\"java\", Nil)\n                    .create(workspace, classesDir, buildClient, logger, buildOptions)\n                case _ => Left(e)\n            case e => Left(e)\n          }.fold(t => throw t, identity)\n        Right(res)\n      case Left(ex) => Left(ex)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/ScalaCompiler.scala",
    "content": "package scala.build.compiler\n\nimport scala.build.{Logger, Positioned, Project}\n\ntrait ScalaCompiler {\n  def jvmVersion: Option[Positioned[Int]]\n  def prepareProject(\n    project: Project,\n    logger: Logger\n  ): Boolean\n  def compile(\n    project: Project,\n    logger: Logger\n  ): Boolean\n  def shutdown(): Unit\n\n  def usesClassDir: Boolean = true\n}\n\nobject ScalaCompiler {\n  final case class IgnoreScala2(compiler: ScalaCompiler) extends ScalaCompiler {\n    private def ignore(\n      project: Project,\n      logger: Logger\n    ): Boolean =\n      project.scalaCompiler.exists { scalaCompiler0 =>\n        val scalaVer = scalaCompiler0.scalaVersion\n        val isScala2 = scalaVer.startsWith(\"2.\")\n        logger.debug(s\"Ignoring compilation for Scala version $scalaVer\")\n        isScala2\n      }\n\n    def jvmVersion: Option[Positioned[Int]] =\n      compiler.jvmVersion\n    def prepareProject(\n      project: Project,\n      logger: Logger\n    ): Boolean =\n      ignore(project, logger) ||\n      compiler.prepareProject(project, logger)\n    def compile(\n      project: Project,\n      logger: Logger\n    ): Boolean =\n      ignore(project, logger) ||\n      compiler.compile(project, logger)\n    def shutdown(): Unit =\n      compiler.shutdown()\n    override def usesClassDir: Boolean =\n      compiler.usesClassDir\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/ScalaCompilerMaker.scala",
    "content": "package scala.build.compiler\n\nimport ch.epfl.scala.bsp4j.BuildClient\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildOptions\n\ntrait ScalaCompilerMaker {\n  def create(\n    workspace: os.Path,\n    classesDir: os.Path,\n    buildClient: BuildClient,\n    logger: Logger,\n    buildOptions: BuildOptions\n  ): Either[BuildException, ScalaCompiler]\n\n  final def withCompiler[T](\n    workspace: os.Path,\n    classesDir: os.Path,\n    buildClient: BuildClient,\n    logger: Logger,\n    buildOptions: BuildOptions\n  )(\n    f: ScalaCompiler => Either[BuildException, T]\n  ): Either[BuildException, T] = {\n    var server: ScalaCompiler = null\n    try {\n      val createdServer = create(\n        workspace,\n        classesDir,\n        buildClient,\n        logger,\n        buildOptions\n      )\n      server = createdServer.toOption.getOrElse(null)\n      createdServer.flatMap(f)\n    }\n    // format: off\n    finally {\n      if (server != null)\n        server.shutdown()\n    }\n    // format: on\n  }\n}\n\nobject ScalaCompilerMaker {\n  final case class IgnoreScala2(compilerMaker: ScalaCompilerMaker) extends ScalaCompilerMaker {\n    def create(\n      workspace: os.Path,\n      classesDir: os.Path,\n      buildClient: BuildClient,\n      logger: Logger,\n      buildOptions: BuildOptions\n    ): Either[BuildException, ScalaCompiler] =\n      compilerMaker.create(workspace, classesDir, buildClient, logger, buildOptions).map(\n        ScalaCompiler.IgnoreScala2(_)\n      )\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/SimpleJavaCompiler.scala",
    "content": "package scala.build.compiler\n\nimport java.io.File\n\nimport scala.build.internal.Runner\nimport scala.build.{Logger, Project}\nimport scala.util.Properties\n\n/** A simple Java compiler to handle pure Java projects.\n  *\n  * @param defaultJavaCommand\n  *   the default `java` command to be used\n  * @param defaultJavaOptions\n  *   the default jvm options to be used with the `java` command\n  */\nfinal case class SimpleJavaCompiler(\n  defaultJavaCommand: String,\n  defaultJavaOptions: Seq[String]\n) {\n\n  def compile(\n    project: Project,\n    logger: Logger\n  ): Boolean =\n    project.sources.isEmpty || {\n      val javacCommand =\n        project.javaHomeOpt.map(javaHome => SimpleJavaCompiler.javaCommand(javaHome, \"javac\"))\n          .getOrElse(defaultJavaCommand)\n\n      // Java 8 doesn't automatically create a directory for the classes\n      if (!os.exists(project.classesDir)) os.makeDir.all(project.classesDir)\n\n      val args = project.javacOptions ++\n        Seq(\n          \"-d\",\n          project.classesDir.toString,\n          \"-cp\",\n          project.classPath.map(_.toString).mkString(File.pathSeparator)\n        ) ++\n        project.sources.map(_.toString)\n\n      val proc = Runner.run(\n        Seq(javacCommand) ++ args,\n        logger,\n        cwd = Some(project.workspace)\n      )\n\n      val res = proc.waitFor()\n\n      res == 0\n    }\n}\n\nobject SimpleJavaCompiler {\n\n  def javaCommand(javaHome: os.Path, command: String = \"java\"): String = {\n    val ext  = if (Properties.isWin) \".exe\" else \"\"\n    val path = javaHome / \"bin\" / s\"$command$ext\"\n    path.toString\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala",
    "content": "package scala.build.compiler\n\nimport java.io.File\n\nimport scala.build.internal.{Constants, Runner}\nimport scala.build.{Logger, Positioned, Project}\n\n/** A simple Scala compiler designed to handle scaladocs, Java projects & get `scalac` outputs.\n  *\n  * @param defaultJavaCommand\n  *   the default `java` command to be used\n  * @param defaultJavaOptions\n  *   the default jvm options to be used with the `java` command\n  * @param scaladoc\n  *   a flag for setting whether this compiler will handle scaladocs\n  */\nfinal case class SimpleScalaCompiler(\n  defaultJavaCommand: String,\n  defaultJavaOptions: Seq[String],\n  scaladoc: Boolean\n) extends ScalaCompiler {\n\n  def jvmVersion: Option[Positioned[Int]] =\n    None // ??? TODO\n\n  def prepareProject(\n    project: Project,\n    logger: Logger\n  ): Boolean =\n    // no incremental compilation, always compiling everything every time\n    true\n\n  override def usesClassDir: Boolean =\n    !scaladoc\n\n  /** Run a synthetic (created in runtime) `scalac` as a JVM process with the specified parameters\n    *\n    * @param mainClass\n    *   the main class of the synthetic Scala compiler\n    * @param javaHomeOpt\n    *   Java home path (optional)\n    * @param javacOptions\n    *   options to be passed for the Java compiler\n    * @param scalacOptions\n    *   options to be passed for the Scala compiler\n    * @param classPath\n    *   class path to be passed to `scalac`\n    * @param compilerClassPath\n    *   class path for the Scala compiler itself\n    * @param sources\n    *   sources to be passed when running `scalac` (optional)\n    * @param outputDir\n    *   output directory for the compiler (optional)\n    * @param argsFileDir\n    *   output directory for the args file (optional)\n    * @param cwd\n    *   working directory for running the compiler\n    * @param logger\n    *   logger\n    * @return\n    *   compiler process exit code\n    */\n  private def runScalacLike(\n    mainClass: String,\n    javaHomeOpt: Option[os.Path],\n    javacOptions: Seq[String],\n    scalacOptions: Seq[String],\n    classPath: Seq[os.Path],\n    compilerClassPath: Seq[os.Path],\n    sources: Seq[String],\n    outputDir: Option[os.Path],\n    argsFilePath: Option[os.Path],\n    cwd: os.Path,\n    logger: Logger\n  ): Int = {\n\n    outputDir.foreach(os.makeDir.all(_))\n\n    // initially adapted from https://github.com/VirtusLab/scala-cli/pull/103/files#diff-d13a7e6d602b8f84d9177e3138487872f0341d006accfe425886a561f029a9c3R120 and around\n    val outputDirArgs = outputDir.map(od => Seq(\"-d\", od.toString())).getOrElse(Nil)\n    val classPathArgs =\n      if (classPath.nonEmpty)\n        Seq(\"-cp\", classPath.map(_.toString).mkString(File.pathSeparator))\n      else Nil\n\n    val args = {\n      val freeArgs = scalacOptions ++ outputDirArgs ++ classPathArgs ++ sources\n\n      if (freeArgs.size > Constants.maxScalacArgumentsCount)\n        argsFilePath.fold(freeArgs) { path =>\n          os.write(path, freeArgs.mkString(System.lineSeparator()))\n          Seq(s\"@$path\")\n        }\n      else freeArgs\n    }\n\n    val javaCommand =\n      javaHomeOpt.map(SimpleJavaCompiler.javaCommand(_)).getOrElse(defaultJavaCommand)\n\n    val javaOptions = defaultJavaOptions ++\n      scalacOptions\n        .filter(_.startsWith(\"-J\"))\n        .map(_.stripPrefix(\"-J\")) ++\n      javacOptions\n        .filter(_.startsWith(\"-J\"))\n        .map(_.stripPrefix(\"-J\"))\n\n    Runner.runJvm(\n      javaCommand,\n      javaOptions,\n      compilerClassPath,\n      mainClass,\n      args,\n      logger,\n      cwd = Some(cwd)\n    ).waitFor()\n  }\n\n  /** Run a synthetic (created in runtime) `scalac` as a JVM process for a given\n    * [[scala.build.Project]]\n    *\n    * @param project\n    *   project to be compiled\n    * @param mainClass\n    *   the main class of the synthetic Scala compiler\n    * @param outputDir\n    *   the scala compiler output directory\n    * @param logger\n    *   logger\n    * @return\n    *   true if the process returned no errors, false otherwise\n    */\n  private def runScalacLikeForProject(\n    project: Project,\n    mainClass: String,\n    outputDir: os.Path,\n    logger: Logger\n  ): Boolean = {\n    val res = runScalacLike(\n      mainClass = mainClass,\n      javaHomeOpt = project.javaHomeOpt,\n      javacOptions = project.javacOptions,\n      scalacOptions = project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil),\n      classPath = project.classPath,\n      compilerClassPath = project.scalaCompiler.map(_.compilerClassPath).getOrElse(Nil),\n      sources = project.sources.map(_.toString),\n      outputDir = Some(outputDir),\n      argsFilePath = Some(project.argsFilePath),\n      cwd = project.workspace,\n      logger = logger\n    )\n    res == 0\n  }\n\n  /** Run a synthetic (created in runtime) `scalac` as a JVM process with minimal parameters. (i.e.\n    * to print `scalac` help)\n    *\n    * @param scalaVersion\n    *   Scala version for which `scalac` is to be created\n    * @param javaHomeOpt\n    *   Java home path (optional)\n    * @param javacOptions\n    *   options to be passed for the Java compiler\n    * @param scalacOptions\n    *   options to be passed for the Scala compiler\n    * @param fullClassPath\n    *   classpath to be passed to the compiler (optional)\n    * @param compilerClassPath\n    *   classpath of the compiler itself\n    * @param logger\n    *   logger\n    * @return\n    *   compiler process exit code\n    */\n  def runSimpleScalacLike(\n    scalaVersion: String,\n    javaHomeOpt: Option[os.Path],\n    javacOptions: Seq[String],\n    scalacOptions: Seq[String],\n    fullClassPath: Seq[os.Path],\n    compilerClassPath: Seq[os.Path],\n    logger: Logger\n  ): Int =\n    compilerMainClass(scalaVersion) match {\n      case Some(mainClass) =>\n        runScalacLike(\n          mainClass = mainClass,\n          javaHomeOpt = javaHomeOpt,\n          javacOptions = javacOptions,\n          scalacOptions = scalacOptions,\n          classPath = fullClassPath,\n          compilerClassPath = compilerClassPath,\n          sources = Nil,\n          outputDir = None,\n          argsFilePath = None,\n          cwd = os.pwd,\n          logger = logger\n        )\n      case _ => 1\n    }\n\n  private def compilerMainClass(scalaVersion: String): Option[String] =\n    if (scalaVersion.startsWith(\"2.\"))\n      Some {\n        if (scaladoc) \"scala.tools.nsc.ScalaDoc\"\n        else \"scala.tools.nsc.Main\"\n      }\n    else if (scaladoc) None\n    else Some(\"dotty.tools.dotc.Main\")\n\n  def compile(\n    project: Project,\n    logger: Logger\n  ): Boolean =\n    if (project.sources.isEmpty) true\n    else\n      project.scalaCompiler match {\n        case Some(compiler) =>\n          val isScala2 = compiler.scalaVersion.startsWith(\"2.\")\n          compilerMainClass(compiler.scalaVersion).forall { mainClass =>\n\n            val outputDir =\n              if (isScala2 && scaladoc) project.scaladocDir\n              else project.classesDir\n\n            runScalacLikeForProject(project, mainClass, outputDir, logger)\n          }\n\n        case None =>\n          scaladoc ||\n          SimpleJavaCompiler(defaultJavaCommand, defaultJavaOptions).compile(project, logger)\n      }\n\n  def shutdown(): Unit =\n    ()\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompilerMaker.scala",
    "content": "package scala.build.compiler\n\nimport ch.epfl.scala.bsp4j.BuildClient\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildOptions\n\nfinal case class SimpleScalaCompilerMaker(\n  defaultJavaCommand: String,\n  defaultJavaOptions: Seq[String],\n  scaladoc: Boolean = false\n) extends ScalaCompilerMaker {\n  def create(\n    workspace: os.Path,\n    classesDir: os.Path,\n    buildClient: BuildClient,\n    logger: Logger,\n    buildOptions: BuildOptions\n  ): Either[BuildException, ScalaCompiler] =\n    Right(SimpleScalaCompiler(defaultJavaCommand, defaultJavaOptions, scaladoc))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/input/Element.scala",
    "content": "package scala.build.input\n\nimport scala.build.preprocessing.ScopePath\nimport scala.util.matching.Regex\n\nsealed abstract class Element extends Product with Serializable\n\nsealed trait SingleElement extends Element\n\nsealed trait AnyScript extends Element\n\nsealed abstract class OnDisk extends Element {\n  def path: os.Path\n}\n\nsealed abstract class Virtual extends SingleElement {\n  def content: Array[Byte]\n\n  def source: String\n\n  def subPath: os.SubPath = {\n    val idx = source.lastIndexOf('/')\n    os.sub / source.drop(idx + 1)\n  }\n\n  def scopePath: ScopePath =\n    ScopePath(Left(source), subPath)\n}\n\nobject Virtual {\n  val urlPathWithQueryParamsRegex                        = \"https?://.*/([^/^?]+)(/?.*)?$\".r\n  def apply(path: String, content: Array[Byte]): Virtual = {\n    val filename = path match {\n      case urlPathWithQueryParamsRegex(name, _) => name\n      case _                                    => path.split(\"/\").last\n    }\n\n    val wrapperPath = os.sub / filename\n\n    if filename.endsWith(\".scala\") then VirtualScalaFile(content, path)\n    else if filename.endsWith(\".java\") then VirtualJavaFile(content, path)\n    else if filename.endsWith(\".sc\") then VirtualScript(content, path, wrapperPath)\n    else if filename.endsWith(\".md\") then VirtualMarkdownFile(content, path, wrapperPath)\n    else VirtualData(content, path)\n  }\n}\n\nsealed abstract class VirtualSourceFile extends Virtual {\n  def isStdin: Boolean = source.startsWith(\"<stdin>\")\n\n  def isSnippet: Boolean = source.startsWith(\"<snippet>\")\n\n  protected def generatedSourceFileName(fileSuffix: String): String =\n    if (isStdin) s\"stdin$fileSuffix\"\n    else if (isSnippet) s\"${source.stripPrefix(\"<snippet>-\")}$fileSuffix\"\n    else s\"virtual$fileSuffix\"\n}\n\nsealed trait SingleFile extends OnDisk with SingleElement\n\nsealed trait SourceFile extends SingleFile {\n  def subPath: os.SubPath\n}\n\nsealed trait Compiled extends Element\n\nsealed trait AnyScalaFile    extends Compiled\nsealed trait AnyJavaFile     extends Compiled\nsealed trait AnyMarkdownFile extends Compiled\n\nsealed trait ScalaFile extends AnyScalaFile {\n  def base: os.Path\n\n  def subPath: os.SubPath\n\n  def path: os.Path = base / subPath\n}\n\nfinal case class Script(base: os.Path, subPath: os.SubPath, inputArg: Option[String])\n    extends OnDisk with SourceFile with AnyScalaFile with AnyScript {\n  lazy val path: os.Path = base / subPath\n}\n\nfinal case class SourceScalaFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile with ScalaFile\n\nfinal case class ProjectScalaFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile with ScalaFile\n\nfinal case class JavaFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile with AnyJavaFile {\n  lazy val path: os.Path = base / subPath\n}\n\nfinal case class JarFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile {\n  lazy val path: os.Path = base / subPath\n}\n\nfinal case class CFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile with Compiled {\n  lazy val path: os.Path = base / subPath\n}\n\nfinal case class MarkdownFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile with AnyMarkdownFile {\n  lazy val path: os.Path = base / subPath\n}\n\nfinal case class SbtFile(base: os.Path, subPath: os.SubPath)\n    extends OnDisk with SourceFile {\n  lazy val path: os.Path = base / subPath\n}\n\nfinal case class Directory(path: os.Path) extends OnDisk with Compiled\n\nfinal case class ResourceDirectory(path: os.Path) extends OnDisk\n\nfinal case class VirtualScript(content: Array[Byte], source: String, wrapperPath: os.SubPath)\n    extends VirtualSourceFile with AnyScalaFile with AnyScript\n\nobject VirtualScript {\n  val VirtualScriptNameRegex: Regex = \"(^stdin$|^snippet\\\\d*$)\".r\n}\n\nfinal case class VirtualScalaFile(content: Array[Byte], source: String)\n    extends VirtualSourceFile with AnyScalaFile {\n  def generatedSourceFileName: String = generatedSourceFileName(\".scala\")\n}\n\nfinal case class VirtualJavaFile(content: Array[Byte], source: String)\n    extends VirtualSourceFile with AnyJavaFile {\n  def generatedSourceFileName: String = generatedSourceFileName(\".java\")\n}\n\nfinal case class VirtualMarkdownFile(\n  content: Array[Byte],\n  override val source: String,\n  wrapperPath: os.SubPath\n) extends VirtualSourceFile with AnyMarkdownFile\n\nfinal case class VirtualData(content: Array[Byte], source: String)\n    extends Virtual\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/input/ElementsUtils.scala",
    "content": "package scala.build.input\n\nimport java.math.BigInteger\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\n\nimport scala.build.Directories\nimport scala.build.internal.Constants\n\nobject ElementsUtils {\n  extension (p: os.Path) {\n    def hasShebang: Boolean =\n      os.isFile(p) && !p.toString.startsWith(\"/dev/fd/\") &&\n      String(os.read.bytes(p, offset = 0, count = 2)) == \"#!\"\n    def isScript: Boolean = p.ext == \"sc\" || (p.hasShebang && p.ext.isEmpty)\n  }\n\n  extension (d: Directory) {\n    def singleFilesFromDirectory(enableMarkdown: Boolean): Seq[SingleFile] = {\n      import Ordering.Implicits.seqOrdering\n      os.walk.stream(d.path, skip = _.last.startsWith(\".\"))\n        .filter(os.isFile(_))\n        .collect {\n          case p if p.last.endsWith(\".java\") =>\n            JavaFile(d.path, p.subRelativeTo(d.path))\n          case p if p.last == Constants.projectFileName =>\n            ProjectScalaFile(d.path, p.subRelativeTo(d.path))\n          case p if p.last.endsWith(\".scala\") =>\n            SourceScalaFile(d.path, p.subRelativeTo(d.path))\n          case p if p.last.endsWith(\".c\") || p.last.endsWith(\".h\") =>\n            CFile(d.path, p.subRelativeTo(d.path))\n          case p if p.last.endsWith(\".md\") && enableMarkdown =>\n            MarkdownFile(d.path, p.subRelativeTo(d.path))\n          case p if p.last.endsWith(\".sc\") =>\n            // TODO: hasShebang test without consuming 1st 2 bytes of Stream\n            Script(d.path, p.subRelativeTo(d.path), None)\n          case p if p.last.endsWith(\".sbt\") =>\n            SbtFile(d.path, p.subRelativeTo(d.path))\n        }\n        .toVector\n        .sortBy(_.subPath.segments)\n    }\n\n    def configFile: Seq[ProjectScalaFile] =\n      if (os.exists(d.path / Constants.projectFileName))\n        Seq(ProjectScalaFile(d.path, os.sub / Constants.projectFileName))\n      else Nil\n  }\n\n  extension (elements: Seq[Element]) {\n    def projectSettingsFiles: Seq[ProjectScalaFile] =\n      elements.flatMap {\n        case f: ProjectScalaFile => Seq(f)\n        case d: Directory        => d.configFile\n        case _                   => Nil\n      }.distinct\n\n    def inputsHash: String = {\n      def bytes(s: String): Array[Byte] = s.getBytes(StandardCharsets.UTF_8)\n\n      val it = elements.iterator.flatMap {\n        case elem: OnDisk =>\n          val prefix = elem match {\n            case _: Directory         => \"dir:\"\n            case _: ResourceDirectory => \"resource-dir:\"\n            case _: JavaFile          => \"java:\"\n            case _: ProjectScalaFile  => \"config:\"\n            case _: SourceScalaFile   => \"scala:\"\n            case _: CFile             => \"c:\"\n            case _: Script            => \"sc:\"\n            case _: MarkdownFile      => \"md:\"\n            case _: JarFile           => \"jar:\"\n            case _: SbtFile           => \"sbt:\"\n          }\n          Iterator(prefix, elem.path.toString, \"\\n\").map(bytes)\n        case v: Virtual =>\n          Iterator(bytes(\"virtual:\"), v.content, bytes(v.source), bytes(\"\\n\"))\n      }\n      val md = MessageDigest.getInstance(\"SHA-1\")\n      it.foreach(md.update)\n      val digest        = md.digest()\n      val calculatedSum = new BigInteger(1, digest)\n      String.format(s\"%040x\", calculatedSum).take(10)\n    }\n\n    def homeWorkspace(directories: Directories): os.Path = {\n      val hash0 = elements.inputsHash\n      val dir   = directories.virtualProjectsDir / hash0.take(2) / s\"project-${hash0.drop(2)}\"\n      os.makeDir.all(dir)\n      dir\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/input/Inputs.scala",
    "content": "package scala.build.input\n\nimport java.io.{ByteArrayInputStream, File}\nimport java.math.BigInteger\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\n\nimport scala.annotation.tailrec\nimport scala.build.Directories\nimport scala.build.errors.{BuildException, InputsException, WorkspaceError}\nimport scala.build.input.ElementsUtils.*\nimport scala.build.internal.Constants\nimport scala.build.internal.zip.WrappedZipInputStream\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.build.preprocessing.SheBang.isShebangScript\nimport scala.util.matching.Regex\nimport scala.util.{Properties, Try}\n\nfinal case class Inputs(\n  elements: Seq[Element],\n  defaultMainClassElement: Option[Script],\n  workspace: os.Path,\n  baseProjectName: String,\n  mayAppendHash: Boolean,\n  workspaceOrigin: Option[WorkspaceOrigin],\n  enableMarkdown: Boolean,\n  allowRestrictedFeatures: Boolean\n) {\n\n  def isEmpty: Boolean = elements.isEmpty\n\n  def singleFiles(): Seq[SingleFile] =\n    elements.flatMap {\n      case f: SingleFile        => Seq(f)\n      case d: Directory         => d.singleFilesFromDirectory(enableMarkdown)\n      case _: ResourceDirectory => Nil\n      case _: Virtual           => Nil\n    }\n\n  def sourceFiles(): Seq[SourceFile] =\n    singleFiles().collect {\n      case f: SourceFile => f\n    }\n\n  def flattened(): Seq[SingleElement] =\n    elements.flatMap {\n      case f: SingleFile        => Seq(f)\n      case d: Directory         => d.singleFilesFromDirectory(enableMarkdown)\n      case _: ResourceDirectory => Nil\n      case v: Virtual           => Seq(v)\n    }\n\n  private lazy val inputsHash: String = elements.inputsHash\n  lazy val projectName: String        = {\n    val needsSuffix = mayAppendHash &&\n      (elements match {\n        case Seq(d: Directory) => d.path != workspace\n        case _                 => true\n      })\n    if needsSuffix then s\"$baseProjectName-$inputsHash\" else baseProjectName\n  }\n\n  def scopeProjectName(scope: Scope): String =\n    if scope == Scope.Main then projectName else s\"$projectName-${scope.name}\"\n\n  def add(extraElements: Seq[Element]): Inputs =\n    if elements.isEmpty then this else copy(elements = (elements ++ extraElements).distinct)\n  def withElements(elements: Seq[Element]): Inputs =\n    copy(elements = elements)\n\n  def generatedSrcRoot(scope: Scope): os.Path =\n    workspace / Constants.workspaceDirName / projectName / \"src_generated\" / scope.name\n\n  private def inHomeDir(directories: Directories): Inputs =\n    copy(\n      workspace = elements.homeWorkspace(directories),\n      mayAppendHash = false,\n      workspaceOrigin = Some(WorkspaceOrigin.HomeDir)\n    )\n  def avoid(forbidden: Seq[os.Path], directories: Directories): Inputs =\n    if forbidden.exists(workspace.startsWith) then inHomeDir(directories) else this\n  def checkAttributes(directories: Directories): Inputs = {\n    @tailrec\n    def existingParent(p: os.Path): Option[os.Path] =\n      if (os.exists(p)) Some(p)\n      else if (p.segmentCount <= 0) None\n      else existingParent(p / os.up)\n    def reallyOwnedByUser(p: os.Path): Boolean =\n      if (Properties.isWin)\n        p.toIO.canWrite // Wondering if there's a better way to do that…\n      else {\n        val maybeUserHome = Try(os.owner(os.home)).toOption\n        maybeUserHome.exists(_ == os.owner(p)) && p.toIO.canWrite\n      }\n    val canWrite = existingParent(workspace).exists(reallyOwnedByUser)\n    if canWrite then this else inHomeDir(directories)\n  }\n  def sourceHash(): String = {\n    def bytes(s: String): Array[Byte] = s.getBytes(StandardCharsets.UTF_8)\n    val it                            = elements.iterator.flatMap {\n      case elem: OnDisk =>\n        val content = elem match {\n          case dirInput: Directory =>\n            Seq(\"dir:\") ++ dirInput.singleFilesFromDirectory(enableMarkdown)\n              .map(file => s\"${file.path}:\" + os.read(file.path))\n          case _: ResourceDirectory => Nil\n          case _: SbtFile           => Nil\n          case _                    => Seq(os.read(elem.path))\n        }\n        (Iterator(elem.path.toString) ++ content.iterator ++ Iterator(\"\\n\")).map(bytes)\n      case v: Virtual =>\n        Iterator(v.content, bytes(\"\\n\"))\n    }\n    val md = MessageDigest.getInstance(\"SHA-1\")\n    it.foreach(md.update)\n    val digest        = md.digest()\n    val calculatedSum = new BigInteger(1, digest)\n    String.format(s\"%040x\", calculatedSum)\n  }\n\n  def nativeWorkDir: os.Path =\n    workspace / Constants.workspaceDirName / projectName / \"native\"\n  def nativeImageWorkDir: os.Path =\n    workspace / Constants.workspaceDirName / projectName / \"native-image\"\n  def libraryJarWorkDir: os.Path =\n    workspace / Constants.workspaceDirName / projectName / \"jar\"\n  def docJarWorkDir: os.Path =\n    workspace / Constants.workspaceDirName / projectName / \"doc\"\n\n}\n\nobject Inputs {\n  private def forValidatedElems(\n    validElems: Seq[Element],\n    workspace: os.Path,\n    needsHash: Boolean,\n    workspaceOrigin: WorkspaceOrigin,\n    enableMarkdown: Boolean,\n    allowRestrictedFeatures: Boolean,\n    extraClasspathWasPassed: Boolean\n  ): Inputs = {\n    assert(extraClasspathWasPassed || validElems.nonEmpty)\n    val allDirs      = validElems.collect { case d: Directory => d.path }\n    val updatedElems = validElems.filter {\n      case f: SourceFile =>\n        val isInDir = allDirs.exists(f.path.relativeTo(_).ups == 0)\n        !isInDir\n      case _: Directory         => true\n      case _: ResourceDirectory => true\n      case _: Virtual           => true\n    }\n    // only on-disk scripts need a main class override\n    val defaultMainClassElemOpt = validElems.collectFirst { case script: Script => script }\n    Inputs(\n      updatedElems,\n      defaultMainClassElemOpt,\n      workspace,\n      baseName(workspace),\n      mayAppendHash = needsHash,\n      workspaceOrigin = Some(workspaceOrigin),\n      enableMarkdown = enableMarkdown,\n      allowRestrictedFeatures = allowRestrictedFeatures\n    )\n  }\n\n  private val githubGistsArchiveRegex: Regex =\n    s\"\"\"://gist\\\\.github\\\\.com/[^/]*?/[^/]*$$\"\"\".r\n\n  private def resolveZipArchive(content: Array[Byte], enableMarkdown: Boolean): Seq[Element] = {\n    val zipInputStream = WrappedZipInputStream.create(new ByteArrayInputStream(content))\n    zipInputStream.entries().foldLeft(List.empty[Element]) {\n      (acc, ent) =>\n        if (ent.isDirectory) acc\n        else {\n          val content = zipInputStream.readAllBytes()\n          (Virtual(ent.getName, content) match {\n            case _: AnyMarkdownFile if !enableMarkdown => None\n            case e: Element                            => Some(e)\n          }) map { element => element :: acc } getOrElse acc\n        }\n    }\n  }\n\n  def validateSnippets(\n    scriptSnippetList: List[String] = List.empty,\n    scalaSnippetList: List[String] = List.empty,\n    javaSnippetList: List[String] = List.empty,\n    markdownSnippetList: List[String] = List.empty\n  ): Seq[Either[String, Seq[Element]]] = {\n    def validateSnippet(\n      snippetList: List[String],\n      f: (Array[Byte], String) => Element\n    ): Seq[Either[String, Seq[Element]]] =\n      snippetList.zipWithIndex.map { case (snippet, index) =>\n        val snippetName: String = if (index > 0) s\"snippet$index\" else \"snippet\"\n        if (snippet.nonEmpty) Right(Seq(f(snippet.getBytes(StandardCharsets.UTF_8), snippetName)))\n        else Left(s\"Empty snippet was passed: $snippetName\")\n      }\n\n    Seq(\n      validateSnippet(\n        scriptSnippetList,\n        (content, snippetName) => VirtualScript(content, snippetName, os.sub / s\"$snippetName.sc\")\n      ),\n      validateSnippet(\n        scalaSnippetList,\n        (content, snippetNameSuffix) =>\n          VirtualScalaFile(content, s\"<snippet>-scala-$snippetNameSuffix\")\n      ),\n      validateSnippet(\n        javaSnippetList,\n        (content, snippetNameSuffix) =>\n          VirtualJavaFile(content, s\"<snippet>-java-$snippetNameSuffix\")\n      ),\n      validateSnippet(\n        markdownSnippetList,\n        (content, snippetNameSuffix) =>\n          VirtualMarkdownFile(\n            content,\n            s\"<snippet>-markdown-$snippetNameSuffix\",\n            os.sub / s\"$snippetNameSuffix.md\"\n          )\n      )\n    ).flatten\n  }\n\n  def validateArgs(\n    args: Seq[String],\n    cwd: os.Path,\n    download: BuildOptions.Download,\n    stdinOpt: => Option[Array[Byte]],\n    acceptFds: Boolean,\n    enableMarkdown: Boolean\n  )(using programInvokeData: ScalaCliInvokeData): Seq[Either[String, Seq[Element]]] =\n    args.zipWithIndex.map {\n      case (arg, idx) =>\n        lazy val path            = os.Path(arg, cwd)\n        lazy val dir             = path / os.up\n        lazy val subPath         = path.subRelativeTo(dir)\n        lazy val stdinOpt0       = stdinOpt\n        lazy val content         = os.read.bytes(path)\n        lazy val fullProgramCall = programInvokeData.progName +\n          s\"${\n              if programInvokeData.subCommand == SubCommand.Default then \"\"\n              else s\" ${programInvokeData.subCommandName}\"\n            }\"\n        val unrecognizedSourceErrorMsg =\n          s\"$arg: unrecognized source type (expected .scala or .sc extension, or a directory).\"\n        val missingShebangHeaderErrorMsg =\n          s\"\"\"$unrecognizedSourceErrorMsg\n             |If $arg is meant to be treated as a script, add a shebang header in its top line.\n             |  ${Console.BOLD}#!/usr/bin/env -S ${programInvokeData.progName} shebang${Console.RESET}\n             |When a shebang header is provided, the script can then be run with the 'shebang' sub-command, even if no file extension is present.\n             |  ${Console.BOLD}${programInvokeData.progName} shebang $arg${Console.RESET}\"\"\".stripMargin\n\n        if (arg == \"-.scala\" || arg == \"_\" || arg == \"_.scala\") && stdinOpt0.nonEmpty then\n          Right(Seq(VirtualScalaFile(stdinOpt0.get, \"<stdin>-scala-file\")))\n        else if (arg == \"-.java\" || arg == \"_.java\") && stdinOpt0.nonEmpty then\n          Right(Seq(VirtualJavaFile(stdinOpt0.get, \"<stdin>-java-file\")))\n        else if (arg == \"-\" || arg == \"-.sc\" || arg == \"_.sc\") && stdinOpt0.nonEmpty then\n          Right(Seq(VirtualScript(stdinOpt0.get, \"stdin\", os.sub / \"stdin.sc\")))\n        else if (arg == \"-.md\" || arg == \"_.md\") && stdinOpt0.nonEmpty then\n          Right(Seq(VirtualMarkdownFile(\n            stdinOpt0.get,\n            \"<stdin>-markdown-file\",\n            os.sub / \"stdin.md\"\n          )))\n        else if arg.endsWith(\".zip\") && os.exists(path) then\n          Right(resolveZipArchive(content, enableMarkdown))\n        else if arg.contains(\"://\") then {\n          val isGithubGist = githubGistsArchiveRegex.findFirstMatchIn(arg).nonEmpty\n          val url          = if isGithubGist then s\"$arg/download\" else arg\n          download(url).map { urlContent =>\n            if isGithubGist then resolveZipArchive(urlContent, enableMarkdown)\n            else List(Virtual(url, urlContent))\n          }\n        }\n        else if path.last == Constants.projectFileName then\n          Right(Seq(ProjectScalaFile(dir, subPath)))\n        else if os.isDir(path) then Right(Seq(Directory(path)))\n        else if arg.endsWith(\".sc\") then Right(Seq(Script(dir, subPath, Some(arg))))\n        else if arg.endsWith(\".scala\") then Right(Seq(SourceScalaFile(dir, subPath)))\n        else if arg.endsWith(\".java\") then Right(Seq(JavaFile(dir, subPath)))\n        else if arg.endsWith(\".jar\") then Right(Seq(JarFile(dir, subPath)))\n        else if arg.endsWith(\".c\") || arg.endsWith(\".h\") then Right(Seq(CFile(dir, subPath)))\n        else if arg.endsWith(\".sbt\") then Right(Seq(SbtFile(dir, subPath)))\n        else if arg.endsWith(\".md\") then Right(Seq(MarkdownFile(dir, subPath)))\n        else if acceptFds && arg.startsWith(\"/dev/fd/\") then\n          Right(Seq(VirtualScript(content, arg, os.sub / s\"input-${idx + 1}.sc\")))\n        else if path.ext.isEmpty && os.exists(path) && path.isScript then\n          Right(Seq(Script(dir, subPath, Some(arg))))\n        else if programInvokeData.subCommand == SubCommand.Shebang && os.exists(path) then\n          if isShebangScript(String(content)) then Right(Seq(Script(dir, subPath, Some(arg))))\n          else\n            Left(\n              if programInvokeData.isShebangCapableShell then missingShebangHeaderErrorMsg\n              else unrecognizedSourceErrorMsg\n            )\n        else {\n          val msg =\n            if os.exists(path) then\n              programInvokeData match {\n                case ScalaCliInvokeData(progName, _, _, true)\n                    if isShebangScript(String(content)) =>\n                  s\"\"\"$arg: scripts with no file extension should be run with the 'shebang' sub-command.\n                     |  ${Console.BOLD}$progName shebang $arg${Console.RESET}\"\"\".stripMargin\n                case ScalaCliInvokeData(_, _, _, true) => missingShebangHeaderErrorMsg\n                case _                                 => unrecognizedSourceErrorMsg\n              }\n            else if programInvokeData.subCommand == SubCommand.Default && idx == 0 &&\n              arg.forall(_.isLetterOrDigit)\n            then\n              s\"\"\"$arg is not a ${programInvokeData.progName} sub-command and it is not a valid path to an input file or directory.\n                 |Try viewing the relevant help to see the list of available sub-commands and options.\n                 |  ${Console.BOLD}${programInvokeData.progName} --help${Console.RESET}\"\"\".stripMargin\n            else\n              s\"\"\"$arg: input file not found\n                 |Try viewing the relevant help to see the list of available sub-commands and options.\n                 |  ${Console.BOLD}$fullProgramCall --help${Console.RESET}\"\"\".stripMargin\n          Left(msg)\n        }\n    }\n\n  private def forNonEmptyArgs(\n    args: Seq[String],\n    cwd: os.Path,\n    download: String => Either[String, Array[Byte]],\n    stdinOpt: => Option[Array[Byte]],\n    scriptSnippetList: List[String],\n    scalaSnippetList: List[String],\n    javaSnippetList: List[String],\n    markdownSnippetList: List[String],\n    acceptFds: Boolean,\n    forcedWorkspace: Option[os.Path],\n    enableMarkdown: Boolean,\n    allowRestrictedFeatures: Boolean,\n    extraClasspathWasPassed: Boolean\n  )(using invokeData: ScalaCliInvokeData): Either[BuildException, Inputs] = {\n    val validatedArgs: Seq[Either[String, Seq[Element]]] =\n      validateArgs(\n        args,\n        cwd,\n        download,\n        stdinOpt,\n        acceptFds,\n        enableMarkdown\n      )\n    val validatedSnippets: Seq[Either[String, Seq[Element]]] =\n      validateSnippets(scriptSnippetList, scalaSnippetList, javaSnippetList, markdownSnippetList)\n    val validatedArgsAndSnippets = validatedArgs ++ validatedSnippets\n    val invalid                  = validatedArgsAndSnippets.collect {\n      case Left(msg) => msg\n    }\n    if (invalid.isEmpty) {\n      val validElems = validatedArgsAndSnippets.collect {\n        case Right(elem) => elem\n      }.flatten\n      assert(extraClasspathWasPassed || validElems.nonEmpty)\n      val (inferredWorkspace, inferredNeedsHash, workspaceOrigin) = {\n        val settingsFiles = validElems.projectSettingsFiles\n        val dirsAndFiles  = validElems.collect {\n          case d: Directory  => d\n          case f: SourceFile => f\n        }\n        settingsFiles.headOption.map { s =>\n          if (settingsFiles.length > 1)\n            System.err.println(\n              s\"Warning: more than one ${Constants.projectFileName} file has been found. Setting ${s.base} as the project root directory for this run.\"\n            )\n          (s.base, true, WorkspaceOrigin.SourcePaths)\n        }.orElse {\n          dirsAndFiles.collectFirst {\n            case d: Directory =>\n              if (dirsAndFiles.length > 1)\n                System.err.println(\n                  s\"Warning: setting ${d.path} as the project root directory for this run.\"\n                )\n              (d.path, true, WorkspaceOrigin.SourcePaths)\n            case f: SourceFile =>\n              if (dirsAndFiles.length > 1)\n                System.err.println(\n                  s\"Warning: setting ${f.path / os.up} as the project root directory for this run.\"\n                )\n              (f.path / os.up, true, WorkspaceOrigin.SourcePaths)\n          }\n        }.orElse {\n          validElems.collectFirst {\n            case _: Virtual =>\n              (os.temp.dir(), true, WorkspaceOrigin.VirtualForced)\n          }\n        }.getOrElse((os.pwd, true, WorkspaceOrigin.Forced))\n      }\n      val (workspace, needsHash, workspaceOrigin0) = forcedWorkspace match {\n        case None                   => (inferredWorkspace, inferredNeedsHash, workspaceOrigin)\n        case Some(forcedWorkspace0) =>\n          val needsHash0 = forcedWorkspace0 != inferredWorkspace || inferredNeedsHash\n          (forcedWorkspace0, needsHash0, WorkspaceOrigin.Forced)\n      }\n\n      if workspace.toString.contains(File.pathSeparator) then\n        val prog       = invokeData.invocationString\n        val argsString = args.mkString(\" \")\n        Left(new WorkspaceError(\n          s\"\"\"Invalid workspace path: ${Console.BOLD}$workspace${Console.RESET}\n             |Workspace path cannot contain a ${Console.BOLD}${File.pathSeparator}${Console.RESET}.\n             |Consider moving your project to a different path.\n             |Alternatively, you can force your workspace with the '--workspace' option:\n             |    ${Console.BOLD}$prog --workspace <alternative-workspace-path> $argsString${Console\n              .RESET}\"\"\"\n            .stripMargin\n        ))\n      else\n        Right(forValidatedElems(\n          validElems,\n          workspace,\n          needsHash,\n          workspaceOrigin0,\n          enableMarkdown,\n          allowRestrictedFeatures,\n          extraClasspathWasPassed\n        ))\n    }\n    else\n      Left(new InputsException(invalid.mkString(System.lineSeparator())))\n  }\n\n  def apply(\n    args: Seq[String],\n    cwd: os.Path,\n    defaultInputs: () => Option[Inputs] = () => None,\n    download: BuildOptions.Download = BuildOptions.Download.notSupported,\n    stdinOpt: => Option[Array[Byte]] = None,\n    scriptSnippetList: List[String] = List.empty,\n    scalaSnippetList: List[String] = List.empty,\n    javaSnippetList: List[String] = List.empty,\n    markdownSnippetList: List[String] = List.empty,\n    acceptFds: Boolean = false,\n    forcedWorkspace: Option[os.Path] = None,\n    enableMarkdown: Boolean = false,\n    allowRestrictedFeatures: Boolean,\n    extraClasspathWasPassed: Boolean\n  )(using ScalaCliInvokeData): Either[BuildException, Inputs] =\n    if (\n      args.isEmpty && scriptSnippetList.isEmpty && scalaSnippetList.isEmpty &&\n      javaSnippetList.isEmpty &&\n      markdownSnippetList.isEmpty && !extraClasspathWasPassed\n    )\n      defaultInputs().toRight(new InputsException(\n        \"No inputs provided (expected files with .scala, .sc, .java or .md extensions, and / or directories).\"\n      ))\n    else\n      forNonEmptyArgs(\n        args,\n        cwd,\n        download,\n        stdinOpt,\n        scriptSnippetList,\n        scalaSnippetList,\n        javaSnippetList,\n        markdownSnippetList,\n        acceptFds,\n        forcedWorkspace,\n        enableMarkdown,\n        allowRestrictedFeatures,\n        extraClasspathWasPassed\n      )\n\n  def default(): Option[Inputs] = None\n\n  def empty(workspace: os.Path, enableMarkdown: Boolean): Inputs =\n    Inputs(\n      elements = Nil,\n      defaultMainClassElement = None,\n      workspace = workspace,\n      baseProjectName = baseName(workspace),\n      mayAppendHash = true,\n      workspaceOrigin = None,\n      enableMarkdown = enableMarkdown,\n      allowRestrictedFeatures = false\n    )\n\n  def empty(projectName: String): Inputs =\n    Inputs(Nil, None, os.pwd, projectName, false, None, true, false)\n\n  def baseName(p: os.Path) = if (p == os.root || p.lastOpt.isEmpty) \"\" else p.baseName\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/input/ScalaCliInvokeData.scala",
    "content": "package scala.build.input\n\n/** Stores information about how the program has been evoked\n  *\n  * @param progName\n  *   the actual Scala CLI program name which was run\n  * @param subCommandName\n  *   the name of the sub-command that was invoked by user\n  * @param subCommand\n  *   the type of the sub-command that was invoked by user\n  * @param isShebangCapableShell\n  *   does the host shell support shebang headers\n  */\n\ncase class ScalaCliInvokeData(\n  progName: String,\n  subCommandName: String,\n  subCommand: SubCommand,\n  isShebangCapableShell: Boolean\n) {\n\n  /** [[progName]] with [[subCommandName]] if any */\n  def invocationString: String =\n    subCommand match\n      case SubCommand.Default => progName\n      case _                  => s\"$progName $subCommandName\"\n}\n\nobject ScalaCliInvokeData {\n  def dummy: ScalaCliInvokeData = ScalaCliInvokeData(\"\", \"\", SubCommand.Other, false)\n}\n\nenum SubCommand:\n  case Default extends SubCommand\n  case Shebang extends SubCommand\n  case Other   extends SubCommand\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/input/WorkspaceOrigin.scala",
    "content": "package scala.build.input\n\nsealed abstract class WorkspaceOrigin extends Product with Serializable\n\nobject WorkspaceOrigin {\n  case object Forced extends WorkspaceOrigin\n\n  case object SourcePaths extends WorkspaceOrigin\n\n  case object ResourcePaths extends WorkspaceOrigin\n\n  case object HomeDir       extends WorkspaceOrigin\n  case object VirtualForced extends WorkspaceOrigin\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/AmmUtil.scala",
    "content": "package scala.build.internal\n\n// adapted from https://github.com/com-lihaoyi/Ammonite/blob/9be39debc367abad5f5541ef58f4b986b2a8d045/amm/util/src/main/scala/ammonite/util/Util.scala\n\nobject AmmUtil {\n  val upPathSegment                                                 = \"^\"\n  def pathToPackageWrapper(relPath0: os.SubPath): (Seq[Name], Name) = {\n    val relPath  = relPath0 / os.up\n    val fileName = relPath0.last\n    val pkg      = relPath.segments.map(Name(_))\n    val wrapper  = fileName.lastIndexOf('.') match {\n      case -1 => fileName\n      case i  => fileName.take(i)\n    }\n\n    (pkg, Name(wrapper))\n  }\n\n  def encodeScalaSourcePath(path: Seq[Name]) = path.map(_.backticked).mkString(\".\")\n\n  def normalizeNewlines(s: String): String =\n    s.replace(\"\\r\", \"\")\n\n  lazy val lineSeparator: String = \"\\n\"\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/AppCodeWrapper.scala",
    "content": "package scala.build.internal\n\ncase class AppCodeWrapper(scalaVersion: String, log: String => Unit) extends CodeWrapper {\n  override def mainClassObject(className: Name) = className\n\n  def apply(\n    code: String,\n    pkgName: Seq[Name],\n    indexedWrapperName: Name,\n    extraCode: String,\n    scriptPath: String\n  ) = {\n    val wrapperObjectName = indexedWrapperName.backticked\n\n    val mainObject = WrapperUtils.mainObjectInScript(scalaVersion, code)\n    val invokeMain = mainObject match\n      case WrapperUtils.ScriptMainMethod.Exists(name) => s\"\\n$name.main(args)\"\n      case otherwise                                  =>\n        otherwise.warningMessage.foreach(log)\n        \"\"\n    val packageDirective =\n      if (pkgName.isEmpty) \"\" else s\"package ${AmmUtil.encodeScalaSourcePath(pkgName)}\" + \"\\n\"\n    val top = AmmUtil.normalizeNewlines(\n      s\"\"\"$packageDirective\n         |\n         |object $wrapperObjectName extends App {\n         |val scriptPath = \\\"\\\"\\\"$scriptPath\\\"\\\"\\\"$invokeMain\n         |\"\"\".stripMargin\n    )\n    val bottom = AmmUtil.normalizeNewlines(\n      s\"\"\"\n         |$extraCode\n         |}\n         |\"\"\".stripMargin\n    )\n\n    (top, bottom)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/ClassCodeWrapper.scala",
    "content": "package scala.build.internal\n\n/** Script code wrapper that solves problem of deadlocks when using threads. The code is placed in a\n  * class instance constructor, the created object is kept in 'mainObjectCode'.script to support\n  * running interconnected scripts using Scala CLI <br> <br> Incompatible with Scala 2 - it uses\n  * Scala 3 feature 'export'<br> Incompatible with native JS members - the wrapper is a class\n  */\ncase class ClassCodeWrapper(scalaVersion: String, log: String => Unit) extends CodeWrapper {\n\n  override def mainClassObject(className: Name): Name =\n    Name(className.raw ++ \"_sc\")\n  def apply(\n    code: String,\n    pkgName: Seq[Name],\n    indexedWrapperName: Name,\n    extraCode: String,\n    scriptPath: String\n  ) = {\n\n    val mainObject     = WrapperUtils.mainObjectInScript(scalaVersion, code)\n    val mainInvocation = mainObject match\n      case WrapperUtils.ScriptMainMethod.Exists(name) => s\"script.$name.main(args)\"\n      case otherwise                                  =>\n        otherwise.warningMessage.foreach(log)\n        s\"val _ = script.hashCode()\"\n\n    val name             = mainClassObject(indexedWrapperName).backticked\n    val wrapperClassName = scala.build.internal.Name(indexedWrapperName.raw ++ \"$_\").backticked\n    val mainObjectCode   =\n      AmmUtil.normalizeNewlines(s\"\"\"|object $name {\n                                    |  private var args$$opt0 = Option.empty[Array[String]]\n                                    |  def args$$set(args: Array[String]): Unit = {\n                                    |    args$$opt0 = Some(args)\n                                    |  }\n                                    |  def args$$opt: Option[Array[String]] = args$$opt0\n                                    |  def args$$: Array[String] = args$$opt.getOrElse {\n                                    |    sys.error(\"No arguments passed to this script\")\n                                    |  }\n                                    |\n                                    |  lazy val script = new $wrapperClassName\n                                    |\n                                    |  def main(args: Array[String]): Unit = {\n                                    |    args$$set(args)\n                                    |    $mainInvocation // hashCode to clear scalac warning about pure expression in statement position\n                                    |  }\n                                    |}\n                                    |\n                                    |export $name.script as `${indexedWrapperName.raw}`\n                                    |\"\"\".stripMargin)\n\n    val packageDirective =\n      if (pkgName.isEmpty) \"\" else s\"package ${AmmUtil.encodeScalaSourcePath(pkgName)}\" + \"\\n\"\n\n    val top = AmmUtil.normalizeNewlines(\n      s\"\"\"$packageDirective\n         |\n         |final class $wrapperClassName {\n         |def args = $name.args$$\n         |def scriptPath = \\\"\\\"\\\"$scriptPath\\\"\\\"\\\"\n         |\"\"\".stripMargin\n    )\n    val bottom = AmmUtil.normalizeNewlines(\n      s\"\"\"$extraCode\n         |}\n         |\n         |$mainObjectCode\n         |\"\"\".stripMargin\n    )\n\n    (top, bottom)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/JavaParserProxy.scala",
    "content": "package scala.build.internal\n\nimport scala.build.errors.BuildException\n\n/** Helper to get class names from Java sources\n  *\n  * See [[JavaParserProxyJvm]] for the implementation that runs things in memory using\n  * java-class-name from the class path, and [[JavaParserProxyBinary]] for the implementation that\n  * downloads and runs a java-class-name binary.\n  */\ntrait JavaParserProxy {\n\n  /** Extracts the class name of a Java source, using the dotty Java parser.\n    *\n    * @param content\n    *   the Java source to extract a class name from\n    * @return\n    *   either some class name (if one was found) or none (if none was found), or a\n    *   [[BuildException]]\n    */\n  def className(content: Array[Byte]): Either[BuildException, Option[String]]\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/JavaParserProxyBinary.scala",
    "content": "package scala.build.internal\n\nimport coursier.cache.ArchiveCache\nimport coursier.util.Task\nimport dependency.*\n\nimport java.util.function.Supplier\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.util.Properties\n\n/** Downloads and runs java-class-name as an external binary. */\nclass JavaParserProxyBinary(\n  archiveCache: ArchiveCache[Task],\n  javaClassNameVersionOpt: Option[String],\n  logger: Logger,\n  javaCommand: () => String\n) extends JavaParserProxy {\n\n  /** For internal use only\n    *\n    * Passing archiveCache as an Object, to work around issues with higher-kind type params from\n    * Java code.\n    */\n  def this(\n    archiveCache: Object,\n    logger: Logger,\n    javaClassNameVersionOpt: Option[String],\n    javaCommand0: Supplier[String]\n  ) =\n    this(\n      archiveCache.asInstanceOf[ArchiveCache[Task]],\n      javaClassNameVersionOpt,\n      logger,\n      () => javaCommand0.get()\n    )\n\n  def className(content: Array[Byte]): Either[BuildException, Option[String]] = either {\n\n    val platformSuffix  = FetchExternalBinary.platformSuffix()\n    val version         = javaClassNameVersionOpt.getOrElse(Constants.javaClassNameVersion)\n    val (tag, changing) =\n      if (version == \"latest\") (\"nightly\", true)\n      else (\"v\" + version, false)\n    val ext = if (Properties.isWin) \".zip\" else \".gz\"\n    val url =\n      s\"https://github.com/VirtusLab/java-class-name/releases/download/$tag/java-class-name-$platformSuffix$ext\"\n\n    val params = ExternalBinaryParams(\n      url,\n      changing,\n      \"java-class-name\",\n      Seq(\n        dep\"${Constants.javaClassNameOrganization}:${Constants.javaClassNameName}:${Constants.javaClassNameVersion}\"\n      ),\n      \"scala.cli.javaclassname.JavaClassName\" // FIXME I'd rather not hardcode that, but automatic detection is cumbersome to setup…\n    )\n    val binary =\n      value(FetchExternalBinary.fetch(params, archiveCache, logger, javaCommand))\n\n    val source =\n      os.temp(content, suffix = \".java\", perms = if (Properties.isWin) null else \"rw-------\")\n    val command = binary.command\n    val output  =\n      try {\n        logger.debug(s\"Running $command $source\")\n        val res = os.proc(command, source).call()\n        res.out.trim()\n      }\n      finally os.remove(source)\n    if (output.isEmpty) None\n    else Some(output)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/JavaParserProxyJvm.scala",
    "content": "package scala.build.internal\n\nimport scala.build.errors.BuildException\nimport scala.cli.javaclassname.JavaParser\n\n/** A [[JavaParserProxy]] that relies on java-class-name in the class path, rather than downloading\n  * it and running it as an external binary.\n  *\n  * Should be used from Scala CLI when it's run on the JVM.\n  */\nclass JavaParserProxyJvm extends JavaParserProxy {\n  override def className(content: Array[Byte]): Either[BuildException, Option[String]] =\n    Right(JavaParser.parseRootPublicClassName(content))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/JavaParserProxyMaker.scala",
    "content": "package scala.build.internal\n\nimport java.util.function.Supplier\n\nimport scala.annotation.unused\nimport scala.build.Logger\n\n/** On the JVM, provides [[JavaParserProxyJvm]] as [[JavaParserProxy]] instance.\n  *\n  * From native launchers, [[JavaParserProxyMakerSubst]] takes over this, and gives\n  * [[JavaParserProxyBinary]] instead.\n  *\n  * That way, no reference to [[JavaParserProxyJvm]] remains in the native call graph, and that\n  * class and those it pulls (the java-class-name classes, which includes parts of the dotty parser)\n  * are not embedded the native launcher.\n  *\n  * Note that this is a class and not an object, to make it easier to write substitutions for that\n  * in Java.\n  */\nclass JavaParserProxyMaker {\n  def get(\n    @unused archiveCache: Object, // Actually a ArchiveCache[Task], but having issues with the higher-kind type param from Java…\n    @unused javaClassNameVersionOpt: Option[String],\n    @unused logger: Logger,\n    @unused javaCommand: Supplier[String]\n  ): JavaParserProxy =\n    new JavaParserProxyJvm\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/MainClass.scala",
    "content": "package scala.build.internal\n\nimport org.objectweb.asm\nimport org.objectweb.asm.ClassReader\n\nimport java.io.{ByteArrayInputStream, InputStream}\nimport java.nio.file.NoSuchFileException\nimport java.util.jar.{Attributes, JarFile}\n\nimport scala.build.internal.zip.WrappedZipInputStream\nimport scala.build.{Logger, retry}\n\nobject MainClass {\n\n  private def stringArrayDescriptor = \"([Ljava/lang/String;)V\"\n\n  private class MainMethodChecker extends asm.ClassVisitor(asm.Opcodes.ASM9) {\n    private var foundMainClass = false\n    private var nameOpt        = Option.empty[String]\n    def found: Boolean         = foundMainClass\n    override def visit(\n      version: Int,\n      access: Int,\n      name: String,\n      signature: String,\n      superName: String,\n      interfaces: Array[String]\n    ): Unit = {\n      nameOpt = Some(name.replace('/', '.').replace('\\\\', '.'))\n    }\n    override def visitMethod(\n      access: Int,\n      name: String,\n      descriptor: String,\n      signature: String,\n      exceptions: Array[String]\n    ): asm.MethodVisitor = {\n      def isStatic = (access & asm.Opcodes.ACC_STATIC) != 0\n      if (name == \"main\" && descriptor == stringArrayDescriptor && isStatic)\n        foundMainClass = true\n      null\n    }\n    def mainClassOpt: Option[String] =\n      if (foundMainClass) nameOpt else None\n  }\n\n  private def findInClass(path: os.Path, logger: Logger): Iterator[String] =\n    try {\n      val is = retry()(logger)(os.read.inputStream(path))\n      findInClass(is, logger)\n    }\n    catch {\n      case e: NoSuchFileException =>\n        e.getStackTrace.foreach(ste => logger.debug(ste.toString))\n        logger.log(s\"Class file $path not found: $e\")\n        logger.log(\"Are you trying to run too many builds at once? Trying to recover...\")\n        Iterator.empty\n    }\n  private def findInClass(is: InputStream, logger: Logger): Iterator[String] =\n    try retry()(logger) {\n        val reader  = new ClassReader(is)\n        val checker = new MainMethodChecker\n        reader.accept(checker, 0)\n        checker.mainClassOpt.iterator\n      }\n    catch {\n      case e: ArrayIndexOutOfBoundsException =>\n        e.getStackTrace.foreach(ste => logger.debug(ste.toString))\n        logger.log(s\"Class input stream could not be created: $e\")\n        logger.log(\"Are you trying to run too many builds at once? Trying to recover...\")\n        Iterator.empty\n    }\n    finally is.close()\n\n  private def findInJar(path: os.Path, logger: Logger): Iterator[String] =\n    try retry()(logger) {\n        val content        = os.read.bytes(path)\n        val jarInputStream = WrappedZipInputStream.create(new ByteArrayInputStream(content))\n        jarInputStream.entries().flatMap(ent =>\n          if !ent.isDirectory && ent.getName.endsWith(\".class\") then {\n            val content     = jarInputStream.readAllBytes()\n            val inputStream = new ByteArrayInputStream(content)\n            findInClass(inputStream, logger)\n          }\n          else Iterator.empty\n        )\n      }\n    catch {\n      case e: NoSuchFileException =>\n        logger.debugStackTrace(e)\n        logger.log(s\"JAR file $path not found: $e, trying to recover...\")\n        logger.log(\"Are you trying to run too many builds at once? Trying to recover...\")\n        Iterator.empty\n    }\n\n  def findInDependency(jar: os.Path): Option[String] =\n    jar match {\n      case jar if os.isFile(jar) && jar.last.endsWith(\".jar\") =>\n        for {\n          manifest          <- Option(new JarFile(jar.toIO).getManifest)\n          mainAttributes    <- Option(manifest.getMainAttributes)\n          mainClass: String <- Option(mainAttributes.getValue(Attributes.Name.MAIN_CLASS))\n        } yield mainClass\n      case _ => None\n    }\n\n  def find(output: os.Path, logger: Logger): Seq[String] =\n    output match {\n      case o if os.isFile(o) && o.last.endsWith(\".class\") =>\n        findInClass(o, logger).toVector\n      case o if os.isFile(o) && o.last.endsWith(\".jar\") =>\n        findInJar(o, logger).toVector\n      case o if os.isDir(o) =>\n        os.walk(o)\n          .iterator\n          .filter(os.isFile)\n          .flatMap {\n            case classFilePath if classFilePath.last.endsWith(\".class\") =>\n              findInClass(classFilePath, logger)\n            case _ => Iterator.empty\n          }\n          .toVector\n      case _ => Vector.empty\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/ManifestJar.scala",
    "content": "package scala.build.internal\n\nimport java.io.OutputStream\n\nobject ManifestJar {\n\n  /** Creates a manifest JAR, in a temporary directory or in the passed scratched directory\n    *\n    * @param classPath\n    *   Entries that should be put in the manifest class path\n    * @param wrongSimplePaths\n    *   Write paths slightly differently in manifest, so that tools such as native-image accept them\n    *   (but the manifest JAR can't be passed to 'java -cp' any more on Windows)\n    * @param scratchDirOpt\n    *   an optional scratch directory to write the manifest JAR under\n    */\n  def create(\n    classPath: Seq[os.Path],\n    wrongSimplePaths: Boolean = false,\n    scratchDirOpt: Option[os.Path] = None\n  ): os.Path = {\n    import java.util.jar._\n    val manifest   = new Manifest\n    val attributes = manifest.getMainAttributes\n    attributes.put(Attributes.Name.MANIFEST_VERSION, \"1.0\")\n    attributes.put(\n      Attributes.Name.CLASS_PATH,\n      if (wrongSimplePaths)\n        // For tools, such as native-image, that don't correctly handle paths in manifests…\n        classPath.map(_.toString).mkString(\" \")\n      else\n        // Paths are encoded this weird way in manifest JARs. This matters on Windows in particular,\n        // where paths like \"C:\\…\" don't work fine.\n        classPath.map(_.toNIO.toUri.getRawPath).mkString(\" \")\n    )\n    val jarFile = scratchDirOpt match {\n      case Some(scratchDir) =>\n        os.makeDir.all(scratchDir)\n        os.temp(dir = scratchDir, prefix = \"classpathJar\", suffix = \".jar\", deleteOnExit = false)\n      case None =>\n        os.temp(prefix = \"classpathJar\", suffix = \".jar\")\n    }\n    var os0: OutputStream    = null\n    var jos: JarOutputStream = null\n    try {\n      os0 = os.write.outputStream(jarFile)\n      jos = new JarOutputStream(os0, manifest)\n    }\n    finally {\n      if (jos != null)\n        jos.close()\n      if (os0 != null)\n        os0.close()\n    }\n    jarFile\n  }\n\n  /** Runs a block of code using a manifest JAR.\n    *\n    * See [[create]] for details about the parameters.\n    */\n  def maybeWithManifestClassPath[T](\n    createManifest: Boolean,\n    classPath: Seq[os.Path],\n    wrongSimplePathsInManifest: Boolean = false\n  )(\n    f: Seq[os.Path] => T\n  ): T =\n    if (createManifest) {\n      var toDeleteOpt = Option.empty[os.Path]\n\n      try {\n        val manifestJar = create(classPath, wrongSimplePaths = wrongSimplePathsInManifest)\n        toDeleteOpt = Some(manifestJar)\n        f(Seq(manifestJar))\n      }\n      finally\n        for (toDelete <- toDeleteOpt)\n          os.remove(toDelete)\n    }\n    else\n      f(classPath)\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/ObjectCodeWrapper.scala",
    "content": "package scala.build.internal\n\n/** Script code wrapper compatible with Scala 2 and JS native members <br> <br> When using Scala 3\n  * or/and not using JS native prefer [[ClassCodeWrapper]], since it prevents deadlocks when running\n  * threads from script\n  */\ncase class ObjectCodeWrapper(scalaVersion: String, log: String => Unit) extends CodeWrapper {\n\n  override def mainClassObject(className: Name): Name =\n    Name(className.raw ++ \"_sc\")\n  def apply(\n    code: String,\n    pkgName: Seq[Name],\n    indexedWrapperName: Name,\n    extraCode: String,\n    scriptPath: String\n  ) = {\n    val mainObject         = WrapperUtils.mainObjectInScript(scalaVersion, code)\n    val name               = mainClassObject(indexedWrapperName).backticked\n    val aliasedWrapperName = name + \"$$alias\"\n    val realScript         =\n      if (name == \"main_sc\")\n        s\"$aliasedWrapperName.alias\" // https://github.com/VirtusLab/scala-cli/issues/314\n      else s\"${indexedWrapperName.backticked}\"\n\n    val funHashCodeMethod = mainObject match\n      case WrapperUtils.ScriptMainMethod.Exists(name) => s\"$realScript.$name.main(args)\"\n      case otherwise                                  =>\n        otherwise.warningMessage.foreach(log)\n        s\"val _ = $realScript.hashCode()\"\n    // We need to call hashCode (or any other method so compiler does not report a warning)\n    val mainObjectCode =\n      AmmUtil.normalizeNewlines(s\"\"\"|object $name {\n                                    |  private var args$$opt0 = Option.empty[Array[String]]\n                                    |  def args$$set(args: Array[String]): Unit = {\n                                    |    args$$opt0 = Some(args)\n                                    |  }\n                                    |  def args$$opt: Option[Array[String]] = args$$opt0\n                                    |  def args$$: Array[String] = args$$opt.getOrElse {\n                                    |    sys.error(\"No arguments passed to this script\")\n                                    |  }\n                                    |  def main(args: Array[String]): Unit = {\n                                    |    args$$set(args)\n                                    |    $funHashCodeMethod // hashCode to clear scalac warning about pure expression in statement position\n                                    |  }\n                                    |}\n                                    |\"\"\".stripMargin)\n\n    val packageDirective =\n      if (pkgName.isEmpty) \"\" else s\"package ${AmmUtil.encodeScalaSourcePath(pkgName)}\" + \"\\n\"\n\n    val aliasObject =\n      if (name == \"main_sc\")\n        s\"\"\"object $aliasedWrapperName {\n           |  val alias = ${indexedWrapperName.backticked}\n           |}\"\"\".stripMargin\n      else \"\"\n\n    val top = AmmUtil.normalizeNewlines(\n      s\"\"\"$packageDirective\n         |\n         |object ${indexedWrapperName.backticked} {\n         |def args = $name.args$$\n         |def scriptPath = \\\"\\\"\\\"$scriptPath\\\"\\\"\\\"\n         |\"\"\".stripMargin\n    )\n\n    val bottom = AmmUtil.normalizeNewlines(\n      s\"\"\"$extraCode\n         |}\n         |$aliasObject\n         |$mainObjectCode\n         |\"\"\".stripMargin\n    )\n\n    (top, bottom)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/Runner.scala",
    "content": "package scala.build.internal\n\nimport coursier.jvm.Execve\nimport org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv\nimport org.scalajs.jsenv.nodejs.NodeJSEnv\nimport org.scalajs.jsenv.{Input, JSEnv, RunConfig}\nimport org.scalajs.testing.adapter.TestAdapter as ScalaJsTestAdapter\nimport sbt.testing.{Framework, Status}\n\nimport java.io.File\nimport java.nio.file.{Files, Path, Paths}\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.*\nimport scala.build.internals.EnvVar\nimport scala.build.testrunner.FrameworkUtils.*\nimport scala.build.testrunner.{AsmTestRunner, Logger as TestRunnerLogger, TestRunner}\nimport scala.scalanative.testinterface.adapter.TestAdapter as ScalaNativeTestAdapter\nimport scala.util.{Failure, Properties, Success}\n\nobject Runner {\n\n  private def toTestRunnerLogger(logger: Logger): TestRunnerLogger =\n    TestRunnerLogger(logger.verbosity)\n\n  def maybeExec(\n    commandName: String,\n    command: Seq[String],\n    logger: Logger,\n    cwd: Option[os.Path] = None,\n    extraEnv: Map[String, String] = Map.empty\n  ): Process =\n    run0(\n      commandName,\n      command,\n      logger,\n      allowExecve = true,\n      cwd,\n      extraEnv,\n      inheritStreams = true\n    )\n\n  def run(\n    command: Seq[String],\n    logger: Logger,\n    cwd: Option[os.Path] = None,\n    extraEnv: Map[String, String] = Map.empty,\n    inheritStreams: Boolean = true\n  ): Process =\n    run0(\n      \"unused\",\n      command,\n      logger,\n      allowExecve = false,\n      cwd,\n      extraEnv,\n      inheritStreams\n    )\n\n  def run0(\n    commandName: String,\n    command: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean,\n    cwd: Option[os.Path],\n    extraEnv: Map[String, String],\n    inheritStreams: Boolean\n  ): Process = {\n\n    import logger.{log, debug}\n\n    log(\n      s\"Running ${command.mkString(\" \")}\",\n      \"  Running\" + System.lineSeparator() +\n        command.iterator.map(_ + System.lineSeparator()).mkString\n    )\n\n    if (allowExecve && Execve.available()) {\n      debug(\"execve available\")\n\n      for (dir <- cwd)\n        Chdir.chdir(dir.toString)\n\n      Execve.execve(\n        findInPath(command.head).fold(command.head)(_.toString),\n        commandName +: command.tail.toArray,\n        (sys.env ++ extraEnv).toArray.sorted.map { case (k, v) => s\"$k=$v\" }\n      )\n      sys.error(\"should not happen\")\n    }\n    else {\n      val b = new ProcessBuilder(command*)\n        .inheritIO()\n\n      if (!inheritStreams) {\n        b.redirectInput(ProcessBuilder.Redirect.PIPE)\n        b.redirectOutput(ProcessBuilder.Redirect.PIPE)\n      }\n\n      if (extraEnv.nonEmpty) {\n        val env = b.environment()\n        for ((k, v) <- extraEnv)\n          env.put(k, v)\n      }\n      for (dir <- cwd)\n        b.directory(dir.toIO)\n      val process = b.start()\n      process\n    }\n  }\n\n  def envCommand(env: Map[String, String]): Seq[String] =\n    env.toVector.sortBy(_._1).map {\n      case (k, v) =>\n        s\"$k=$v\"\n    }\n\n  def jvmCommand(\n    javaCommand: String,\n    javaArgs: Seq[String],\n    classPath: Seq[os.Path],\n    mainClass: String,\n    args: Seq[String],\n    extraEnv: Map[String, String] = Map.empty,\n    useManifest: Option[Boolean] = None,\n    scratchDirOpt: Option[os.Path] = None\n  ): Seq[String] = {\n\n    def command(cp: Seq[os.Path]) =\n      envCommand(extraEnv) ++\n        Seq(javaCommand) ++\n        javaArgs ++\n        Seq(\n          \"-cp\",\n          cp.iterator.map(_.toString).mkString(File.pathSeparator),\n          mainClass\n        ) ++\n        args\n\n    val initialCommand = command(classPath)\n\n    val useManifest0 = useManifest.getOrElse {\n      Properties.isWin && {\n        val commandLen = initialCommand.map(_.length).sum + (initialCommand.length - 1)\n        // On Windows, total command lengths have this limit. Note that the same kind\n        // of limit applies the environment, so that we can't sneak in info via env vars to\n        // overcome the command length limit.\n        // See https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553\n        commandLen >= 32767\n      }\n    }\n\n    if (useManifest0) {\n      val manifestJar = ManifestJar.create(classPath, scratchDirOpt = scratchDirOpt)\n      command(Seq(manifestJar))\n    }\n    else initialCommand\n  }\n\n  def runJvm(\n    javaCommand: String,\n    javaArgs: Seq[String],\n    classPath: Seq[os.Path],\n    mainClass: String,\n    args: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean = false,\n    cwd: Option[os.Path] = None,\n    extraEnv: Map[String, String] = Map.empty,\n    useManifest: Option[Boolean] = None,\n    scratchDirOpt: Option[os.Path] = None\n  ): Process = {\n\n    val command = jvmCommand(\n      javaCommand,\n      javaArgs,\n      classPath,\n      mainClass,\n      args,\n      Map.empty,\n      useManifest,\n      scratchDirOpt\n    )\n\n    if (allowExecve)\n      maybeExec(\"java\", command, logger, cwd = cwd, extraEnv = extraEnv)\n    else\n      run(command, logger, cwd = cwd, extraEnv = extraEnv)\n  }\n\n  private def endsWithCaseInsensitive(s: String, suffix: String): Boolean =\n    s.length >= suffix.length &&\n    s.regionMatches(true, s.length - suffix.length, suffix, 0, suffix.length)\n\n  private def findInPath(app: String): Option[Path] = {\n    val asIs = Paths.get(app)\n    if (Paths.get(app).getNameCount >= 2) Some(asIs)\n    else {\n      def pathEntries =\n        EnvVar.Misc.path.valueOpt\n          .iterator\n          .flatMap(_.split(File.pathSeparator).iterator)\n      def pathSep =\n        if (Properties.isWin)\n          EnvVar.Misc.pathExt.valueOpt\n            .iterator\n            .flatMap(_.split(File.pathSeparator).iterator)\n        else Iterator(\"\")\n      def matches = for {\n        dir <- pathEntries\n        ext <- pathSep\n        app0 = if (endsWithCaseInsensitive(app, ext)) app else app + ext\n        path = Paths.get(dir).resolve(app0)\n        if Files.isExecutable(path)\n      } yield path\n      matches.take(1).toList.headOption\n    }\n  }\n\n  def jsCommand(\n    entrypoint: File,\n    args: Seq[String],\n    jsDom: Boolean = false\n  ): Seq[String] = {\n\n    val nodePath = findInPath(\"node\").fold(\"node\")(_.toString)\n    val command  = Seq(nodePath, entrypoint.getAbsolutePath) ++ args\n\n    if (jsDom)\n      // FIXME We'd need to replicate what JSDOMNodeJSEnv does under-the-hood to get the command in that case.\n      // --command is mostly for debugging purposes, so I'm not sure it matters much here…\n      sys.error(\"Cannot get command when JSDOM is enabled.\")\n    else\n      \"node\" +: command.tail\n  }\n\n  def runJs(\n    entrypoint: File,\n    args: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean = false,\n    jsDom: Boolean = false,\n    sourceMap: Boolean = false,\n    esModule: Boolean = false\n  ): Either[BuildException, Process] = either {\n    val nodePath: String =\n      value(findInPath(\"node\")\n        .map(_.toString)\n        .toRight(NodeNotFoundError()))\n    if !jsDom && allowExecve && Execve.available() then {\n      val command = Seq(nodePath, entrypoint.getAbsolutePath) ++ args\n\n      logger.log(\n        s\"Running ${command.mkString(\" \")}\",\n        \"  Running\" + System.lineSeparator() +\n          command.iterator.map(_ + System.lineSeparator()).mkString\n      )\n\n      logger.debug(\"execve available\")\n      Execve.execve(\n        command.head,\n        \"node\" +: command.tail.toArray,\n        sys.env.toArray.sorted.map { case (k, v) => s\"$k=$v\" }\n      )\n      sys.error(\"should not happen\")\n    }\n    else {\n      val nodeArgs =\n        // Scala.js runs apps by piping JS to node.\n        // If we need to pass arguments, we must first make the piped input explicit\n        // with \"-\", and we pass the user's arguments after that.\n        if args.isEmpty then Nil else \"-\" :: args.toList\n      val envJs =\n        if jsDom then\n          new JSDOMNodeJSEnv(\n            JSDOMNodeJSEnv.Config()\n              .withExecutable(nodePath)\n              .withArgs(nodeArgs)\n              .withEnv(Map.empty)\n          )\n        else\n          new NodeJSEnv(\n            NodeJSEnv.Config()\n              .withExecutable(nodePath)\n              .withArgs(nodeArgs)\n              .withEnv(Map.empty)\n              .withSourceMap(sourceMap)\n          )\n\n      val inputs =\n        Seq(if esModule then Input.ESModule(entrypoint.toPath) else Input.Script(entrypoint.toPath))\n\n      val config    = RunConfig().withLogger(logger.scalaJsLogger)\n      val processJs = envJs.start(inputs, config)\n\n      processJs.future.value.foreach {\n        case Failure(t) => throw new Exception(t)\n        case Success(_) =>\n      }\n\n      val processField =\n        processJs.getClass.getDeclaredField(\"org$scalajs$jsenv$ExternalJSRun$$process\")\n      processField.setAccessible(true)\n      val process = processField.get(processJs).asInstanceOf[Process]\n      process\n    }\n  }\n\n  def runNative(\n    launcher: File,\n    args: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean = false,\n    extraEnv: Map[String, String] = Map.empty\n  ): Process = {\n\n    import logger.{log, debug}\n\n    val command = Seq(launcher.getAbsolutePath) ++ args\n\n    log(\n      s\"Running ${command.mkString(\" \")}\",\n      \"  Running\" + System.lineSeparator() +\n        command.iterator.map(_ + System.lineSeparator()).mkString\n    )\n\n    if (allowExecve && Execve.available()) {\n      debug(\"execve available\")\n      Execve.execve(\n        command.head,\n        launcher.getName +: command.tail.toArray,\n        (sys.env ++ extraEnv).toArray.sorted.map { case (k, v) => s\"$k=$v\" }\n      )\n      sys.error(\"should not happen\")\n    }\n    else {\n      val builder = new ProcessBuilder(command*)\n        .inheritIO()\n      val env = builder.environment()\n      for ((k, v) <- extraEnv)\n        env.put(k, v)\n      builder.start()\n    }\n  }\n\n  private def runTests(\n    classPath: Seq[Path],\n    frameworks: Seq[Framework],\n    requireTests: Boolean,\n    args: Seq[String],\n    parentInspector: AsmTestRunner.ParentInspector,\n    logger: Logger\n  ): Either[NoTestsRun, Boolean] = frameworks\n    .flatMap { framework =>\n      val trLogger = toTestRunnerLogger(logger)\n      val taskDefs =\n        AsmTestRunner.taskDefs(\n          classPath,\n          keepJars = false,\n          framework.fingerprints().toIndexedSeq,\n          parentInspector,\n          trLogger\n        ).toArray\n\n      val runner       = framework.runner(args.toArray, Array(), null)\n      val initialTasks = runner.tasks(taskDefs)\n      val events       = TestRunner.runTasks(initialTasks.toIndexedSeq, System.out)\n\n      val doneMsg = runner.done()\n      if doneMsg.nonEmpty then System.out.println(doneMsg)\n      events\n    } match {\n    case events if requireTests && events.isEmpty => Left(new NoTestsRun)\n    case events                                   => Right {\n        !events.exists { ev =>\n          ev.status == Status.Error ||\n          ev.status == Status.Failure ||\n          ev.status == Status.Canceled\n        }\n      }\n  }\n\n  def frameworkNames(\n    classPath: Seq[Path],\n    parentInspector: AsmTestRunner.ParentInspector,\n    logger: Logger\n  ): Either[NoTestFrameworkFoundError, Seq[String]] = {\n    val trLogger = toTestRunnerLogger(logger)\n    logger.debug(\"Looking for test framework services on the classpath...\")\n    val foundFrameworkServices =\n      AsmTestRunner.findFrameworkServices(classPath, trLogger)\n        .map(_.replace('/', '.').replace('\\\\', '.'))\n    logger.debug(s\"Found ${foundFrameworkServices.length} test framework services.\")\n    if foundFrameworkServices.nonEmpty then\n      logger.debug(s\"  - ${foundFrameworkServices.mkString(\"\\n  - \")}\")\n    logger.debug(\"Looking for more test frameworks on the classpath...\")\n    val foundFrameworks =\n      AsmTestRunner.findFrameworks(\n        classPath,\n        TestRunner.commonTestFrameworks,\n        parentInspector,\n        trLogger\n      )\n        .map(_.replace('/', '.').replace('\\\\', '.'))\n    logger.debug(s\"Found ${foundFrameworks.length} additional test frameworks\")\n    if foundFrameworks.nonEmpty then\n      logger.debug(s\"  - ${foundFrameworks.mkString(\"\\n  - \")}\")\n    val frameworks: Seq[String] = foundFrameworkServices ++ foundFrameworks\n    logger.log(s\"Found ${frameworks.length} test frameworks in total\")\n    if frameworks.nonEmpty then\n      logger.debug(s\"  - ${frameworks.mkString(\"\\n  - \")}\")\n    if frameworks.nonEmpty then Right(frameworks) else Left(new NoTestFrameworkFoundError)\n  }\n\n  def testJs(\n    classPath: Seq[Path],\n    entrypoint: File,\n    requireTests: Boolean,\n    args: Seq[String],\n    predefinedTestFrameworks: Seq[String],\n    logger: Logger,\n    jsDom: Boolean,\n    esModule: Boolean\n  ): Either[TestError, Int] = either {\n    import org.scalajs.jsenv.Input\n    import org.scalajs.jsenv.nodejs.NodeJSEnv\n    logger.debug(\"Preparing to run tests with Scala.js...\")\n    logger.debug(s\"Scala.js tests class path: $classPath\")\n    val nodePath = findInPath(\"node\").fold(\"node\")(_.toString)\n    logger.debug(s\"Node found at $nodePath\")\n    val jsEnv: JSEnv =\n      if jsDom then {\n        logger.log(\"Loading JS environment with JS DOM...\")\n        new JSDOMNodeJSEnv(\n          JSDOMNodeJSEnv.Config()\n            .withExecutable(nodePath)\n            .withArgs(Nil)\n            .withEnv(Map.empty)\n        )\n      }\n      else {\n        logger.log(\"Loading JS environment with Node...\")\n        new NodeJSEnv(\n          NodeJSEnv.Config()\n            .withExecutable(nodePath)\n            .withArgs(Nil)\n            .withEnv(Map.empty)\n            .withSourceMap(NodeJSEnv.SourceMap.Disable)\n        )\n      }\n    val adapterConfig = ScalaJsTestAdapter.Config().withLogger(logger.scalaJsLogger)\n    val inputs        =\n      Seq(if esModule then Input.ESModule(entrypoint.toPath) else Input.Script(entrypoint.toPath))\n    var adapter: ScalaJsTestAdapter = null\n\n    logger.debug(s\"JS tests class path: $classPath\")\n\n    val parentInspector = new AsmTestRunner.ParentInspector(classPath, toTestRunnerLogger(logger))\n    val foundFrameworkNames: List[String] = predefinedTestFrameworks match {\n      case f if f.nonEmpty => f.toList\n      case Nil             => value(frameworkNames(classPath, parentInspector, logger)).toList\n    }\n\n    val res =\n      try {\n        adapter = new ScalaJsTestAdapter(jsEnv, inputs, adapterConfig)\n\n        val loadedFrameworks =\n          adapter\n            .loadFrameworks(foundFrameworkNames.map(List(_)))\n            .flatten\n            .distinctBy(_.name())\n\n        val finalTestFrameworks =\n          loadedFrameworks\n            .filter(\n              !_.name().toLowerCase.contains(\"junit\") ||\n              !loadedFrameworks.exists(_.name().toLowerCase.contains(\"munit\"))\n            )\n        if finalTestFrameworks.nonEmpty then\n          logger.log(\n            s\"\"\"Final list of test frameworks found:\n               |  - ${finalTestFrameworks.map(_.description).mkString(\"\\n  - \")}\n               |\"\"\".stripMargin\n          )\n\n        if finalTestFrameworks.isEmpty then Left(new NoFrameworkFoundByBridgeError)\n        else runTests(classPath, finalTestFrameworks, requireTests, args, parentInspector, logger)\n      }\n      finally if adapter != null then adapter.close()\n\n    if value(res) then 0 else 1\n  }\n\n  def testNative(\n    classPath: Seq[Path],\n    launcher: File,\n    predefinedTestFrameworks: Seq[String],\n    requireTests: Boolean,\n    args: Seq[String],\n    logger: Logger\n  ): Either[TestError, Int] = either {\n    logger.debug(\"Preparing to run tests with Scala Native...\")\n    logger.debug(s\"Native tests class path: $classPath\")\n\n    val parentInspector = new AsmTestRunner.ParentInspector(classPath, toTestRunnerLogger(logger))\n    val foundFrameworkNames: List[String] = predefinedTestFrameworks match {\n      case f if f.nonEmpty => f.toList\n      case Nil             => value(frameworkNames(classPath, parentInspector, logger)).toList\n    }\n\n    val config = ScalaNativeTestAdapter.Config()\n      .withBinaryFile(launcher)\n      .withEnvVars(sys.env)\n      .withLogger(logger.scalaNativeTestLogger)\n\n    var adapter: ScalaNativeTestAdapter = null\n\n    val res =\n      try {\n        adapter = new ScalaNativeTestAdapter(config)\n\n        val loadedFrameworks =\n          adapter\n            .loadFrameworks(foundFrameworkNames.map(List(_)))\n            .flatten\n            .distinctBy(_.name())\n\n        val finalTestFrameworks =\n          loadedFrameworks\n            // .filter(\n            //  _.name() != \"Scala Native JUnit test framework\" ||\n            //    !loadedFrameworks.exists(_.name() == \"munit\")\n            // )\n            // TODO: add support for JUnit and then only hardcode filtering it out when passed with munit\n            // https://github.com/VirtusLab/scala-cli/issues/3627\n            .filter(_.name() != \"Scala Native JUnit test framework\")\n        if finalTestFrameworks.nonEmpty then\n          logger.log(\n            s\"\"\"Final list of test frameworks found:\n               |  - ${finalTestFrameworks.map(_.description).mkString(\"\\n  - \")}\n               |\"\"\".stripMargin\n          )\n\n        val skippedFrameworks = loadedFrameworks.diff(finalTestFrameworks)\n        if skippedFrameworks.nonEmpty then\n          logger.log(\n            s\"\"\"The following test frameworks have been filtered out:\n               |  - ${skippedFrameworks.map(_.description).mkString(\"\\n  - \")}\n               |\"\"\".stripMargin\n          )\n\n        if finalTestFrameworks.isEmpty then Left(new NoFrameworkFoundByNativeBridgeError)\n        else runTests(classPath, finalTestFrameworks, requireTests, args, parentInspector, logger)\n      }\n      finally if adapter != null then adapter.close()\n\n    if value(res) then 0 else 1\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/WrapperUtils.scala",
    "content": "package scala.build.internal\n\nimport scala.build.internal.util.WarningMessages\n\nobject WrapperUtils {\n\n  enum ScriptMainMethod:\n    case Exists(name: String)\n    case Multiple(names: Seq[String])\n    case ToplevelStatsPresent\n    case ToplevelStatsWithMultiple(names: Seq[String])\n    case NoMain\n\n    def warningMessage: List[String] =\n      this match\n        case ScriptMainMethod.Multiple(names) =>\n          List(WarningMessages.multipleMainObjectsInScript(names))\n        case ScriptMainMethod.ToplevelStatsPresent => List(\n            WarningMessages.mixedToplvelAndObjectInScript\n          )\n        case ToplevelStatsWithMultiple(names) =>\n          List(\n            WarningMessages.multipleMainObjectsInScript(names),\n            WarningMessages.mixedToplvelAndObjectInScript\n          )\n        case _ => Nil\n\n  def mainObjectInScript(scalaVersion: String, code: String): ScriptMainMethod =\n    import scala.meta.*\n\n    val scriptDialect =\n      if scalaVersion.startsWith(\"3\") then dialects.Scala3Future else dialects.Scala213Source3\n\n    given Dialect  = scriptDialect.withAllowToplevelStatements(true).withAllowToplevelTerms(true)\n    val parsedCode = code.parse[Source] match\n      case Parsed.Success(Source(stats)) => stats\n      case _                             => Nil\n\n    // Check if there is a main function defined inside an object\n    def checkSignature(defn: Defn.Def) =\n      defn.paramClauseGroups match\n        case List(Member.ParamClauseGroup(\n              Type.ParamClause(Nil),\n              List(Term.ParamClause(\n                List(Term.Param(\n                  Nil,\n                  _: Term.Name,\n                  Some(Type.Apply.After_4_6_0(\n                    Type.Name(\"Array\"),\n                    Type.ArgClause(List(Type.Name(\"String\")))\n                  )),\n                  None\n                )),\n                None\n              ))\n            )) => true\n        case _ => false\n\n    def noToplevelStatements = parsedCode.forall {\n      case _: Term => false\n      case _       => true\n    }\n\n    def hasMainSignature(templ: Template) = templ.body.stats.exists {\n      case defn: Defn.Def =>\n        defn.name.value == \"main\" && checkSignature(defn)\n      case _ => false\n    }\n    def extendsApp(templ: Template) = templ.inits match\n      case Init.After_4_6_0(Type.Name(\"App\"), _, Nil) :: Nil => true\n      case _                                                 => false\n    val potentialMains = parsedCode.collect {\n      case Defn.Object(_, objName, templ) if extendsApp(templ) || hasMainSignature(templ) =>\n        Seq(objName.value)\n    }.flatten\n\n    potentialMains match\n      case head :: Nil if noToplevelStatements =>\n        ScriptMainMethod.Exists(head)\n      case head :: Nil =>\n        ScriptMainMethod.ToplevelStatsPresent\n      case Nil                         => ScriptMainMethod.NoMain\n      case seq if noToplevelStatements =>\n        ScriptMainMethod.Multiple(seq)\n      case seq =>\n        ScriptMainMethod.ToplevelStatsWithMultiple(seq)\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/markdown/MarkdownCodeBlock.scala",
    "content": "package scala.build.internal.markdown\n\nimport scala.annotation.tailrec\nimport scala.build.errors.BuildException\nimport scala.jdk.CollectionConverters.*\n\n/** Representation for a (closed) code block contained in Markdown\n  *\n  * @param info\n  *   a list of tags tied to a given code block\n  * @param body\n  *   the code block content\n  * @param startLine\n  *   starting line on which the code block was defined (excluding backticks)\n  * @param endLine\n  *   end line on which the code block was closed (excluding backticks)\n  */\ncase class MarkdownCodeBlock(\n  info: Seq[String],\n  body: String,\n  startLine: Int,\n  endLine: Int\n) {\n\n  /** @return\n    *   `true` if this snippet should be ignored, `false` otherwise\n    */\n  def shouldIgnore: Boolean = info.head != \"scala\" || info.contains(\"ignore\")\n\n  /** @return\n    *   `true` if this snippet should have its scope reset, `false` otherwise\n    */\n  def resetScope: Boolean = info.contains(\"reset\")\n\n  /** @return\n    *   `true` if this snippet is a test snippet, `false` otherwise\n    */\n  def isTest: Boolean = info.contains(\"test\")\n\n  /** @return\n    *   `true` if this snippet is a raw snippet, `false` otherwise\n    */\n  def isRaw: Boolean = info.contains(\"raw\")\n}\n\nobject MarkdownCodeBlock {\n\n  /** Finds all code snippets in given input\n    *\n    * @param subPath\n    *   the project [[os.SubPath]] to the Markdown file\n    * @param md\n    *   Markdown file in a `String` format\n    * @param maybeRecoverOnError\n    *   function potentially recovering on errors\n    * @return\n    *   list of all found snippets\n    */\n  def findCodeBlocks(\n    subPath: os.SubPath,\n    md: String,\n    maybeRecoverOnError: BuildException => Option[BuildException] = Some(_)\n  ): Either[BuildException, Seq[MarkdownCodeBlock]] = {\n    val allLines = md\n      .lines()\n      .toList\n      .asScala\n    @tailrec\n    def findCodeBlocksRec(\n      lines: Seq[String],\n      closedFences: Seq[MarkdownCodeBlock] = Seq.empty,\n      maybeOpenFence: Option[MarkdownOpenFence] = None,\n      currentIndex: Int = 0\n    ): Either[BuildException, Seq[MarkdownCodeBlock]] = lines -> maybeOpenFence match {\n      case (Seq(currentLine, tail*), mof) =>\n        val (newClosedFences, newOpenFence) = mof match {\n          case None => closedFences -> MarkdownOpenFence.maybeFence(currentLine, currentIndex)\n          case Some(openFence) =>\n            val backticksStart = currentLine.indexOf(openFence.backticks)\n            if backticksStart == openFence.indent &&\n              currentLine.forall(c => c == '`' || c.isWhitespace)\n            then (closedFences :+ openFence.closeFence(currentIndex, allLines.toArray)) -> None\n            else closedFences -> Some(openFence)\n        }\n        findCodeBlocksRec(tail, newClosedFences, newOpenFence, currentIndex + 1)\n      case (Nil, Some(openFence)) =>\n        maybeRecoverOnError(openFence.toUnclosedBackticksError(os.pwd / subPath))\n          .map(e => Left(e))\n          .getOrElse(Right(closedFences))\n      case _ => Right(closedFences)\n    }\n    findCodeBlocksRec(allLines.toSeq).map(_.filter(!_.shouldIgnore))\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/markdown/MarkdownCodeWrapper.scala",
    "content": "package scala.build.internal.markdown\n\nimport scala.annotation.tailrec\nimport scala.build.internal.{AmmUtil, Name}\nimport scala.build.preprocessing.{\n  ExtractedDirectives,\n  PreprocessedMarkdown,\n  PreprocessedMarkdownCodeBlocks\n}\n\n/** A util for extraction and wrapping of code blocks in Markdown files.\n  */\nobject MarkdownCodeWrapper {\n\n  case class WrappedMarkdownCode(\n    code: String,\n    directives: ExtractedDirectives = ExtractedDirectives.empty\n  )\n\n  /** Extracts scala code blocks from Markdown snippets, divides them into 3 categories and wraps\n    * when necessary.\n    *\n    * @param subPath\n    *   the project [[os.SubPath]] to the Markdown file\n    * @param markdown\n    *   preprocessed Markdown code blocks\n    * @return\n    *   a tuple of (Option(simple scala code blocks), Option(raw scala snippets code blocks),\n    *   Option(test scala snippets code blocks))\n    */\n  def apply(\n    subPath: os.SubPath,\n    markdown: PreprocessedMarkdown\n  ): (Option[WrappedMarkdownCode], Option[WrappedMarkdownCode], Option[WrappedMarkdownCode]) = {\n    val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(subPath)\n    val maybePkgString =\n      if pkg.isEmpty then None else Some(s\"package ${AmmUtil.encodeScalaSourcePath(pkg)}\")\n    val wrapperName = Name(s\"${wrapper.raw}_md\").backticked\n    (\n      wrapScalaCode(markdown.scriptCodeBlocks, wrapperName, maybePkgString),\n      rawScalaCode(markdown.rawCodeBlocks),\n      rawScalaCode(markdown.testCodeBlocks)\n    )\n  }\n\n  /** Scope object name for a given index\n    * @param index\n    *   scope index\n    * @return\n    *   scope name\n    */\n  private def scopeObjectName(index: Int): String = if index != 0 then s\"Scope$index\" else \"Scope\"\n\n  /** Transforms [[MarkdownCodeBlock]] code blocks into code in the [[Option]] of String format\n    *\n    * @param snippets\n    *   extracted [[MarkdownCodeBlock]] code blocks\n    * @param f\n    *   a function transforming a sequence of code blocks into a single String of code\n    * @return\n    *   an Option of the resulting code String, if any\n    */\n  private def code(\n    snippets: Seq[MarkdownCodeBlock],\n    f: Seq[MarkdownCodeBlock] => String\n  ): Option[String] =\n    if snippets.isEmpty then None else Some(AmmUtil.normalizeNewlines(f(snippets)))\n\n  /** Wraps plain `scala` snippets in relevant scope objects, forming a script-like wrapper.\n    *\n    * @param snippets\n    *   a sequence of code blocks\n    * @param wrapperName\n    *   name for the wrapper object\n    * @param pkg\n    *   package for the wrapper object\n    * @return\n    *   an option of the wrapped code String\n    */\n  def wrapScalaCode(\n    preprocessed: PreprocessedMarkdownCodeBlocks,\n    wrapperName: String,\n    pkg: Option[String]\n  ): Option[WrappedMarkdownCode] =\n    code(\n      preprocessed.codeBlocks,\n      s => {\n        val packageDirective = pkg.map(_ + \"; \").getOrElse(\"\")\n        val noWarnAnnotation = \"\"\"@annotation.nowarn(\"msg=pure expression does nothing\")\"\"\"\n        val firstLine        =\n          s\"\"\"${packageDirective}object $wrapperName { $noWarnAnnotation def main(args: Array[String]): Unit = { \"\"\"\n        s.indices.foldLeft(0 -> firstLine) {\n          case ((nextScopeIndex, sum), index) =>\n            if preprocessed.codeBlocks(index).resetScope || index == 0 then\n              nextScopeIndex + 1 -> (sum :++ s\"${scopeObjectName(nextScopeIndex)}; \")\n            else nextScopeIndex  -> sum // that class hasn't been created\n        }\n          ._2\n          .:++(\"}\")\n          .:++(generateMainScalaLines(s, 0, 0, 0))\n          .:++(\"}\")\n      }\n    ).map(c => WrappedMarkdownCode(c, preprocessed.extractedDirectives))\n\n  @tailrec\n  private def generateMainScalaLines(\n    snippets: Seq[MarkdownCodeBlock],\n    index: Int,\n    scopeIndex: Int,\n    line: Int,\n    acc: String = \"\"\n  ): String =\n    if (index >= snippets.length) s\"$acc}\" // close last class\n    else {\n      val fence: MarkdownCodeBlock = snippets(index)\n      val classOpener: String      =\n        if (index == 0)\n          s\"object ${scopeObjectName(scopeIndex)} {${AmmUtil.lineSeparator}\" // first snippet needs to open a class\n        else if (fence.resetScope)\n          s\"}; object ${scopeObjectName(scopeIndex)} {${AmmUtil.lineSeparator}\" // if scope is being reset, close previous class and open a new one\n        else AmmUtil.lineSeparator\n      val nextScopeIndex = if index == 0 || fence.resetScope then scopeIndex + 1 else scopeIndex\n      val newAcc         = acc + (AmmUtil.lineSeparator * (fence.startLine - line - 1)) // padding\n        .:++(classOpener) // new class opening (if applicable)\n        .:++(fence.body) // snippet body\n        .:++(AmmUtil.lineSeparator) // padding in place of closing backticks\n      generateMainScalaLines(\n        snippets = snippets,\n        index = index + 1,\n        scopeIndex = nextScopeIndex,\n        line = fence.endLine + 1,\n        acc = newAcc\n      )\n    }\n\n  /** Glues raw Scala snippets into a single file.\n    *\n    * @param snippets\n    *   a sequence of code blocks\n    * @return\n    *   an option of the resulting code String\n    */\n  def rawScalaCode(preprocessed: PreprocessedMarkdownCodeBlocks): Option[WrappedMarkdownCode] =\n    code(preprocessed.codeBlocks, generateRawScalaLines(_, 0, 0))\n      .map(c => WrappedMarkdownCode(c, preprocessed.extractedDirectives))\n\n  @tailrec\n  private def generateRawScalaLines(\n    snippets: Seq[MarkdownCodeBlock],\n    index: Int,\n    line: Int,\n    acc: String = \"\"\n  ): String =\n    if index >= snippets.length then acc\n    else {\n      val fence: MarkdownCodeBlock = snippets(index)\n      val newAcc = acc + (AmmUtil.lineSeparator * (fence.startLine - line)) // padding\n        .:++(fence.body) // snippet body\n        .:++(AmmUtil.lineSeparator) // padding in place of closing backticks\n      generateRawScalaLines(snippets, index + 1, fence.endLine + 1, newAcc)\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/markdown/MarkdownOpenFence.scala",
    "content": "package scala.build.internal.markdown\n\nimport scala.build.Position\nimport scala.build.errors.MarkdownUnclosedBackticksError\nimport scala.build.preprocessing.SheBang\n\n/** Representation for an open code block in Markdown. (open meaning the closing backticks haven't\n  * yet been parsed or they aren't at all present)\n  *\n  * @param info\n  *   a list of tags tied to a given code block\n  * @param tickStartLine\n  *   index of the starting line on which the opening backticks were defined\n  * @param backticks\n  *   the backticks string opening the fence\n  * @param indent\n  *   number of spaces of indentation for the fence\n  */\ncase class MarkdownOpenFence(\n  info: String,\n  tickStartLine: Int, // fence start INCLUDING backticks\n  backticks: String,\n  indent: Int\n) {\n\n  /** Closes started code-fence\n    *\n    * @param tickEndLine\n    *   number of the line where closing backticks are\n    * @param lines\n    *   input file sliced into lines\n    * @return\n    *   [[MarkdownCodeBlock]] representing whole closed code-fence\n    */\n  def closeFence(\n    tickEndLine: Int,\n    lines: Array[String]\n  ): MarkdownCodeBlock = {\n    val start: Int               = tickStartLine + 1\n    val bodyLines: Array[String] = lines.slice(start, tickEndLine)\n    val body                     = bodyLines.mkString(\"\\n\")\n\n    val (bodyWithNoSheBang, _, _) = SheBang.ignoreSheBangLines(body)\n    MarkdownCodeBlock(\n      info.split(\"\\\\s+\").toList, // strip info by whitespaces\n      bodyWithNoSheBang,\n      start,          // snippet has to begin in the new line\n      tickEndLine - 1 // ending backticks have to be placed below the snippet\n    )\n  }\n\n  /** Converts the [[MarkdownOpenFence]] into a [[MarkdownUnclosedBackticksError]]\n    *\n    * @param mdPath\n    *   path to the Markdown file\n    * @return\n    *   a [[MarkdownUnclosedBackticksError]]\n    */\n  def toUnclosedBackticksError(mdPath: os.Path): MarkdownUnclosedBackticksError = {\n    val startCoordinates = tickStartLine -> indent\n    val endCoordinates   =\n      tickStartLine -> (indent + backticks.length)\n    val position = Position.File(Right(mdPath), startCoordinates, endCoordinates)\n    MarkdownUnclosedBackticksError(backticks, Seq(position))\n  }\n}\n\nobject MarkdownOpenFence {\n  def maybeFence(line: String, index: Int): Option[MarkdownOpenFence] = {\n    val start: Int = line.indexOf(\"```\")\n    if (start >= 0) {\n      val fence             = line.substring(start)\n      val backticks: String = fence.takeWhile(_ == '`')\n      val info: String      = fence.substring(backticks.length)\n      Some(MarkdownOpenFence(info, index, backticks, start))\n    }\n    else None\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/resource/NativeResourceMapper.scala",
    "content": "package scala.build.internal.resource\n\nimport scala.build.Build\nimport scala.build.input.CFile\n\nobject NativeResourceMapper {\n\n  private def scalaNativeCFileMapping(build: Build.Successful): Map[os.Path, os.RelPath] =\n    build\n      .inputs\n      .flattened()\n      .collect {\n        case cfile: CFile =>\n          val inputPath = cfile.path\n          val destPath  = os.rel / \"scala-native\" / cfile.subPath\n          (inputPath, destPath)\n      }\n      .toMap\n\n  private def resolveProjectCFileRegistryPath(nativeWorkDir: os.Path) =\n    nativeWorkDir / \".native_registry\"\n\n  /** Copies and maps c file resources from their original path to the destination path in build\n    * output, also caching output paths in a file.\n    *\n    * Remembering the mapping this way allows for the resource to be removed if the original file is\n    * renamed/deleted.\n    */\n  def copyCFilesToScalaNativeDir(build: Build.Successful, nativeWorkDir: os.Path): Unit = {\n    val mappingFilePath = resolveProjectCFileRegistryPath(nativeWorkDir)\n    ResourceMapper.copyResourcesToDirWithMapping(\n      build.output,\n      mappingFilePath,\n      scalaNativeCFileMapping(build)\n    )\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/resource/ResourceMapper.scala",
    "content": "package scala.build.internal.resource\n\nimport scala.build.Build\nimport scala.build.internal.Constants\n\nobject ResourceMapper {\n\n  private def resourceMapping(build: Build.Successful): Map[os.Path, os.RelPath] = {\n    val seq = for {\n      resourceDirPath  <- build.sources.resourceDirs.filter(os.exists(_))\n      resourceFilePath <- os.walk(resourceDirPath).filter(os.isFile(_))\n      relativeResourcePath = resourceFilePath.relativeTo(resourceDirPath)\n      // dismiss files generated by scala-cli\n      if !relativeResourcePath.startsWith(os.rel / Constants.workspaceDirName)\n    } yield (resourceFilePath, relativeResourcePath)\n\n    seq.toMap\n  }\n\n  def copyResourcesToDirWithMapping(\n    output: os.Path,\n    registryFilePath: os.Path,\n    newMapping: Map[os.Path, os.RelPath]\n  ): Unit = {\n\n    val oldRegistry =\n      if (os.exists(registryFilePath))\n        os.read(registryFilePath)\n          .linesIterator\n          .filter(_.nonEmpty)\n          .map(os.RelPath(_))\n          .toSet\n      else\n        Set.empty\n    val removedFiles = oldRegistry -- newMapping.values\n\n    for (f <- removedFiles)\n      os.remove(output / f)\n\n    for ((inputPath, outputPath) <- newMapping)\n      os.copy(\n        inputPath,\n        output / outputPath,\n        replaceExisting = true,\n        createFolders = true\n      )\n\n    if (newMapping.isEmpty)\n      os.remove(registryFilePath)\n    else\n      os.write.over(\n        registryFilePath,\n        newMapping.map(_._2.toString).toVector.sorted.mkString(\"\\n\")\n      )\n  }\n\n  /** Copies and maps resources from their original path to the destination path in build output,\n    * also caching output paths in a file.\n    *\n    * Remembering the mapping this way allows for the resource to be removed if the original file is\n    * renamed/deleted.\n    */\n  def copyResourceToClassesDir(build: Build): Unit = build match {\n    case b: Build.Successful =>\n      val fullFilePath = Build.resourcesRegistry(b.inputs.workspace, b.inputs.projectName, b.scope)\n      copyResourcesToDirWithMapping(b.output, fullFilePath, resourceMapping(b))\n    case _ =>\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/util/RegexUtils.scala",
    "content": "package scala.build.internal.util\n\nimport java.util.regex.Pattern\n\nobject RegexUtils {\n\n  /** Based on junit-interface [GlobFilter.\n    * compileGlobPattern](https://github.com/sbt/junit-interface/blob/f8c6372ed01ce86f15393b890323d96afbe6d594/src/main/java/com/novocode/junit/GlobFilter.java#L37)\n    *\n    * @return\n    *   Pattern allows to regex input which contains only *, for example `*foo*` match to\n    *   `MyTests.foo`\n    */\n  def globPattern(expr: String): Pattern = {\n    val a = expr.split(\"\\\\*\", -1)\n    val b = new StringBuilder()\n    for (i <- 0 until a.length) {\n      if (i != 0) b.append(\".*\")\n      if (a(i).nonEmpty) b.append(Pattern.quote(a(i).replaceAll(\"\\n\", \"\\\\n\")))\n    }\n    Pattern.compile(b.toString)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/util/WarningMessages.scala",
    "content": "package scala.build.internal.util\n\nimport scala.build.input.ScalaCliInvokeData\nimport scala.build.internal.Constants\nimport scala.build.internals.FeatureType\nimport scala.build.preprocessing.directives.{DirectiveHandler, ScopedDirective}\nimport scala.cli.commands.SpecificationLevel\nimport scala.cli.config.Key\n\nobject WarningMessages {\n  private val scalaCliGithubUrl = s\"https://github.com/${Constants.ghOrg}/${Constants.ghName}\"\n\n  private val experimentalNote =\n    s\"\"\"Please bear in mind that non-ideal user experience should be expected.\n       |If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at $scalaCliGithubUrl\"\"\".stripMargin\n  def experimentalFeaturesUsed(namesAndFeatureTypes: Seq[(String, FeatureType)]): String = {\n    val message = namesAndFeatureTypes match {\n      case Seq((name, featureType)) => s\"The `$name` $featureType is experimental\"\n      case namesAndTypes            =>\n        val nl                                    = System.lineSeparator()\n        val distinctFeatureTypes                  = namesAndTypes.map(_._2).distinct\n        val (bulletPointList, featureNameToPrint) = if (distinctFeatureTypes.size == 1)\n          (\n            namesAndTypes.map((name, _) => s\" - `$name`\")\n              .mkString(nl),\n            s\"${distinctFeatureTypes.head}s\" // plural form\n          )\n        else\n          (\n            namesAndTypes.map((name, fType) => s\" - `$name` $fType\")\n              .mkString(nl),\n            \"features\"\n          )\n\n        s\"\"\"Some utilized $featureNameToPrint are marked as experimental:\n           |$bulletPointList\"\"\".stripMargin\n    }\n    s\"\"\"$message\n       |$experimentalNote\"\"\".stripMargin\n  }\n\n  def experimentalSubcommandWarning(name: String): String =\n    s\"\"\"The `$name` sub-command is experimental.\n       |$experimentalNote\"\"\".stripMargin\n\n  def rawValueNotWrittenToPublishFile(\n    rawValue: String,\n    valueName: String,\n    directiveName: String\n  ): String =\n    s\"\"\"The value of $valueName ${Console.BOLD}will not${Console.RESET} be written to a potentially public file!\n       |Provide it as an option to the publish subcommand with:\n       | $directiveName value:$rawValue\n       |\"\"\".stripMargin\n\n  /** Using @main is impossible in new [[scala.build.internal.ClassCodeWrapper]] since none of the\n    * definitions can be accessed statically, so those errors are swapped with this text\n    * @param annotationIgnored\n    *   will annotation be ignored (or will compilation fail)\n    */\n  def mainAnnotationNotSupported(annotationIgnored: Boolean): String =\n    val consequencesString = if annotationIgnored then s\", it will be ignored\" else \"\"\n    s\"Annotation @main in .sc scripts is not supported$consequencesString, use .scala format instead\"\n\n  private def powerFeatureUsedInSip(\n    featureName: String,\n    featureType: String,\n    specificationLevel: SpecificationLevel\n  )(using invokeData: ScalaCliInvokeData): String = {\n    val powerType =\n      if specificationLevel == SpecificationLevel.EXPERIMENTAL then \"experimental\" else \"restricted\"\n    s\"\"\"The `$featureName` $featureType is $powerType.\n       |You can run it with the `--power` flag or turn power mode on globally by running:\n       |  ${Console.BOLD}${invokeData.progName} config power true${Console.RESET}\"\"\".stripMargin\n  }\n\n  def powerCommandUsedInSip(commandName: String, specificationLevel: SpecificationLevel)(using\n    ScalaCliInvokeData\n  ): String = powerFeatureUsedInSip(commandName, \"sub-command\", specificationLevel)\n\n  def powerOptionUsedInSip(optionName: String, specificationLevel: SpecificationLevel)(using\n    ScalaCliInvokeData\n  ): String =\n    powerFeatureUsedInSip(optionName, \"option\", specificationLevel)\n\n  def powerConfigKeyUsedInSip(key: Key[?])(using ScalaCliInvokeData): String =\n    powerFeatureUsedInSip(key.fullName, \"configuration key\", key.specificationLevel)\n\n  def powerDirectiveUsedInSip(\n    directive: ScopedDirective,\n    handler: DirectiveHandler[?]\n  )(using ScalaCliInvokeData): String =\n    powerFeatureUsedInSip(\n      directive.directive.toString,\n      \"directive\",\n      handler.scalaSpecificationLevel\n    )\n\n  val chainingUsingFileDirective: String =\n    \"Chaining the 'using file' directive is not supported, the source won't be included in the build.\"\n\n  val offlineModeBloopNotFound =\n    \"Offline mode is ON and Bloop could not be fetched from the local cache, using scalac as fallback\"\n\n  val offlineModeBloopJvmNotFound =\n    \"Offline mode is ON and a JVM for Bloop could not be fetched from the local cache, using scalac as fallback\"\n\n  def multipleMainObjectsInScript(names: Seq[String]) =\n    s\"Only a single main is allowed within scripts. Multiple main classes were found in the script: ${names.mkString(\", \")}\"\n\n  def mixedToplvelAndObjectInScript =\n    \"Script contains objects with main methods and top-level statements, only the latter will be run.\"\n\n  def directivesInMultipleFilesWarning(\n    projectFilePath: String,\n    pathsToReport: Iterable[String] = Nil\n  ) = {\n    val detectedMsg    = \"Using directives detected in multiple files\"\n    val recommendedMsg =\n      s\"It is recommended to keep them centralized in the $projectFilePath file.\"\n    if pathsToReport.isEmpty then\n      s\"$detectedMsg. $recommendedMsg\"\n    else\n      s\"\"\"$detectedMsg:\n         |${pathsToReport.mkString(\"- \", s\"${System.lineSeparator}- \", \"\")}\n         |$recommendedMsg\n         |\"\"\".stripMargin\n  }\n\n  val mainScriptNameClashesWithAppWrapper =\n    \"Script file named 'main.sc' detected, keep in mind that accessing it from other scripts is impossible due to a clash of `main` symbols\"\n\n  def deprecatedWarning(old: String, `new`: String) =\n    s\"Using '$old' is deprecated, use '${`new`}' instead\"\n\n  def deprecatedToolkitLatest(updatedValue: String = \"\") =\n    if updatedValue.isEmpty then\n      \"\"\"Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour\"\"\"\n    else\n      s\"\"\"Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour:\n         | $updatedValue\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/internal/zip/WrappedZipInputStream.scala",
    "content": "package scala.build.internal.zip\n\nimport java.io.{Closeable, InputStream}\nimport java.util.zip.ZipEntry\n\nimport scala.build.internals.EnvVar\n\n/*\n * juz.ZipInputStream is buggy on Arch Linux from native images (CRC32 calculation issues,\n * see oracle/graalvm#4479), so we use a custom ZipInputStream with disabled CRC32 calculation.\n */\nfinal case class WrappedZipInputStream(\n  wrapped: Either[io.github.scala_cli.zip.ZipInputStream, java.util.zip.ZipInputStream]\n) extends Closeable {\n  def entries(): Iterator[ZipEntry] = {\n    val getNextEntry = wrapped match {\n      case Left(zis)  => () => zis.getNextEntry()\n      case Right(zis) => () => zis.getNextEntry()\n    }\n    Iterator.continually(getNextEntry()).takeWhile(_ != null)\n  }\n  def closeEntry(): Unit =\n    wrapped match {\n      case Left(zis)  => zis.closeEntry()\n      case Right(zis) => zis.closeEntry()\n    }\n  def readAllBytes(): Array[Byte] =\n    wrapped.merge.readAllBytes()\n  def close(): Unit =\n    wrapped.merge.close()\n}\n\nobject WrappedZipInputStream {\n  lazy val shouldUseVendoredImplem = {\n    def toBoolean(input: String): Boolean =\n      input match {\n        case \"true\" | \"1\" => true\n        case _            => false\n      }\n    EnvVar.ScalaCli.vendoredZipInputStream.valueOpt.map(toBoolean)\n      .orElse(sys.props.get(\"scala-cli.zis.vendored\").map(toBoolean))\n      .getOrElse(false)\n  }\n  def create(is: InputStream): WrappedZipInputStream =\n    WrappedZipInputStream {\n      if (shouldUseVendoredImplem) Left(new io.github.scala_cli.zip.ZipInputStream(is))\n      else Right(new java.util.zip.ZipInputStream(is))\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/package.scala",
    "content": "package scala.build\n\nimport scala.annotation.tailrec\nimport scala.concurrent.duration.{DurationInt, FiniteDuration}\nimport scala.util.Random\n\ndef retry[T](\n  maxAttempts: Int = 3,\n  waitDuration: FiniteDuration = 1.seconds,\n  variableWaitDelayInMs: Int = 500\n)(logger: Logger)(\n  run: => T\n): T = {\n  @tailrec\n  def helper(count: Int): T =\n    try run\n    catch {\n      case t: Throwable =>\n        if count >= maxAttempts then throw t\n        else\n          logger.debugStackTrace(t)\n          val variableDelay       = Random.between(0, variableWaitDelayInMs + 1).milliseconds\n          val currentWaitDuration = waitDuration + variableDelay\n          logger.log(s\"Caught $t, trying again in $currentWaitDuration…\")\n          Thread.sleep(currentWaitDuration.toMillis)\n          helper(count + 1)\n    }\n\n  helper(1)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/AsmPositionUpdater.scala",
    "content": "package scala.build.postprocessing\n\nimport org.objectweb.asm\n\nimport java.nio.file.{FileAlreadyExistsException, NoSuchFileException}\n\nimport scala.build.{Logger, Os, retry}\n\nobject AsmPositionUpdater {\n\n  private class LineNumberTableMethodVisitor(\n    lineShift: Int,\n    delegate: asm.MethodVisitor\n  ) extends asm.MethodVisitor(asm.Opcodes.ASM9, delegate) {\n    override def visitLineNumber(line: Int, start: asm.Label): Unit =\n      super.visitLineNumber(line + lineShift, start)\n  }\n\n  private class LineNumberTableClassVisitor(\n    mappings: Map[String, (String, Int)],\n    cw: asm.ClassWriter\n  ) extends asm.ClassVisitor(asm.Opcodes.ASM9, cw) {\n    private var lineShiftOpt                                      = Option.empty[Int]\n    def mappedStuff                                               = lineShiftOpt.nonEmpty\n    override def visitSource(source: String, debug: String): Unit =\n      mappings.get(source) match {\n        case None =>\n          super.visitSource(source, debug)\n        case Some((newSource, lineShift)) =>\n          lineShiftOpt = Some(lineShift)\n          super.visitSource(newSource, debug)\n      }\n    override def visitMethod(\n      access: Int,\n      name: String,\n      descriptor: String,\n      signature: String,\n      exceptions: Array[String]\n    ): asm.MethodVisitor = {\n      val main = super.visitMethod(access, name, descriptor, signature, exceptions)\n      lineShiftOpt match {\n        case None            => main\n        case Some(lineShift) => new LineNumberTableMethodVisitor(lineShift, main)\n      }\n    }\n  }\n\n  def postProcess(\n    mappings: Map[String, (String, Int)],\n    output: os.Path,\n    logger: Logger\n  ): Unit = {\n    os.walk(output)\n      .iterator\n      .filter(os.isFile(_))\n      .filter(_.last.endsWith(\".class\"))\n      .foreach { path =>\n        try retry()(logger) {\n            val is                = os.read.inputStream(path)\n            val updateByteCodeOpt =\n              try retry()(logger) {\n                  val reader  = new asm.ClassReader(is)\n                  val writer  = new asm.ClassWriter(reader, 0)\n                  val checker = new LineNumberTableClassVisitor(mappings, writer)\n                  reader.accept(checker, 0)\n                  if checker.mappedStuff then Some(writer.toByteArray) else None\n                }\n              catch {\n                case e: ArrayIndexOutOfBoundsException =>\n                  e.getStackTrace.foreach(ste => logger.debug(ste.toString))\n                  logger.log(s\"Error while processing ${path.relativeTo(Os.pwd)}: $e.\")\n                  logger.log(\"Are you trying to run too many builds at once? Trying to recover...\")\n                  None\n              }\n              finally is.close()\n            for (b <- updateByteCodeOpt) {\n              logger.debug(s\"Overwriting ${path.relativeTo(Os.pwd)}\")\n              os.write.over(path, b)\n            }\n          }\n        catch {\n          case e: (NoSuchFileException | FileAlreadyExistsException |\n                ArrayIndexOutOfBoundsException) =>\n            logger.debugStackTrace(e)\n            logger.log(s\"Error while processing ${path.relativeTo(Os.pwd)}: $e\")\n            logger.log(\"Are you trying to run too many builds at once? Trying to recover...\")\n        }\n      }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/ByteCodePostProcessor.scala",
    "content": "package scala.build.postprocessing\n\nimport scala.build.options.BuildOptions\nimport scala.build.{GeneratedSource, Logger}\nimport scala.util.{Either, Right}\n\ncase object ByteCodePostProcessor extends PostProcessor {\n  def postProcess(\n    generatedSources: Seq[GeneratedSource],\n    mappings: Map[String, (String, Int)],\n    workspace: os.Path,\n    output: os.Path,\n    logger: Logger,\n    scalaVersion: String,\n    buildOptions: BuildOptions\n  ): Either[String, Unit] =\n    Right(AsmPositionUpdater.postProcess(mappings, output, logger))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/LineConversion.scala",
    "content": "package scala.build.postprocessing\n\nimport scala.build.internal.WrapperParams\n\nobject LineConversion {\n  def scalaLineToScLine(lineScala: Int, wrapperParamsOpt: Option[WrapperParams]): Option[Int] =\n    wrapperParamsOpt match {\n      case Some(wrapperParams) =>\n        val lineSc = lineScala - wrapperParams.topWrapperLineCount\n\n        if (lineSc >= 0 && lineSc < wrapperParams.userCodeLineCount) Some(lineSc) else None\n      case _ => None\n    }\n\n  def scalaLineToScLineShift(wrapperParamsOpt: Option[WrapperParams]): Int =\n    wrapperParamsOpt match {\n      case Some(wrapperParams) => wrapperParams.topWrapperLineCount * -1\n      case _                   => 0\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/PostProcessor.scala",
    "content": "package scala.build.postprocessing\n\nimport scala.build.options.BuildOptions\nimport scala.build.{GeneratedSource, Logger}\n\ntrait PostProcessor {\n  def postProcess(\n    generatedSources: Seq[GeneratedSource],\n    mappings: Map[String, (String, Int)],\n    workspace: os.Path,\n    output: os.Path,\n    logger: Logger,\n    scalaVersion: String,\n    buildOptions: BuildOptions\n  ): Either[String, Unit]\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/SemanticDbPostProcessor.scala",
    "content": "package scala.build.postprocessing\n\nimport java.nio.file.FileSystemException\n\nimport scala.annotation.tailrec\nimport scala.build.options.BuildOptions\nimport scala.build.postprocessing.LineConversion.scalaLineToScLine\nimport scala.build.{GeneratedSource, Logger}\nimport scala.util.{Either, Right, Try}\n\ncase object SemanticDbPostProcessor extends PostProcessor {\n  def postProcess(\n    generatedSources: Seq[GeneratedSource],\n    mappings: Map[String, (String, Int)],\n    workspace: os.Path,\n    output: os.Path,\n    logger: Logger,\n    scalaVersion: String,\n    buildOptions: BuildOptions\n  ): Either[String, Unit] = Right {\n    logger.debug(\"Moving semantic DBs around\")\n    val semanticDbOptions = buildOptions.scalaOptions.semanticDbOptions\n    val semDbSourceRoot   = semanticDbOptions.semanticDbSourceRoot.getOrElse(workspace)\n    val semDbTargetRoot   =\n      semanticDbOptions.semanticDbTargetRoot.getOrElse(output) / \"META-INF\" / \"semanticdb\"\n    for (source <- generatedSources; originalSource <- source.reportingPath) {\n      val actual = originalSource.relativeTo(semDbSourceRoot)\n\n      val generatedSourceParent = os.Path(source.generated.toNIO.getParent)\n      val potentialSemDbFile    = generatedSourceParent / (source.generated.last + \".semanticdb\")\n      Some(potentialSemDbFile)\n        .filter(os.exists)\n        .orElse(Some(semDbTargetRoot / potentialSemDbFile.relativeTo(semDbSourceRoot)))\n        .filter(os.exists)\n        .foreach { semDbFile =>\n          val finalSemDbFile = {\n            val dirSegments = actual.segments.dropRight(1)\n            semDbTargetRoot / dirSegments / s\"${actual.last}.semanticdb\"\n          }\n          SemanticdbProcessor.postProcess(\n            os.read(originalSource),\n            originalSource.relativeTo(semDbSourceRoot),\n            scalaLine => scalaLineToScLine(scalaLine, source.wrapperParamsOpt),\n            semDbFile,\n            finalSemDbFile\n          )\n          try os.remove(semDbFile)\n          catch {\n            case ex: FileSystemException =>\n              logger.debug(s\"Ignoring $ex while removing $semDbFile\")\n          }\n          Try(semDbTargetRoot -> semDbFile.relativeTo(semDbTargetRoot).asSubPath).toOption\n            .foreach { (base, subPath) =>\n              deleteSubPathIfEmpty(base, subPath / os.up, logger)\n            }\n        }\n    }\n  }\n\n  @tailrec\n  private def deleteSubPathIfEmpty(base: os.Path, subPath: os.SubPath, logger: Logger): Unit =\n    if (subPath.segments.nonEmpty) {\n      val p = base / subPath\n      if (os.isDir(p) && os.list.stream(p).headOption.isEmpty) {\n        try os.remove(p)\n        catch {\n          case e: FileSystemException =>\n            logger.debug(s\"Ignoring $e while cleaning up $p\")\n        }\n        deleteSubPathIfEmpty(base, subPath / os.up, logger)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/SemanticdbProcessor.scala",
    "content": "// adapted from https://github.com/com-lihaoyi/Ammonite/blob/2da846d2313f1e12e812802babf9c69005f5d44a/amm/interp/src/main/scala/ammonite/interp/script/SemanticdbProcessor.scala\n\npackage scala.build.postprocessing\n\nimport java.math.BigInteger\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\n\nimport scala.collection.mutable\nimport scala.meta.internal.semanticdb.*\n\nobject SemanticdbProcessor {\n\n  def postProcess(\n    originalCode: String,\n    originalPath: os.RelPath,\n    adjust: Int => Option[Int],\n    orig: os.Path,\n    dest: os.Path\n  ): Unit = {\n\n    val mapRange = {\n      (range: scala.meta.internal.semanticdb.Range) =>\n        for {\n          startLine <- adjust(range.startLine)\n          endLine   <- adjust(range.endLine)\n        } yield range\n          .withStartLine(startLine)\n          .withEndLine(endLine)\n    }\n\n    def updateTrees(trees: Seq[Tree]): Option[Seq[Tree]] =\n      trees\n        .foldLeft(Option(new mutable.ListBuffer[Tree])) {\n          (accOpt, t) =>\n            for (acc <- accOpt; t0 <- updateTree(t)) yield acc += t0\n        }\n        .map(_.result())\n\n    def updateTree(tree: Tree): Option[Tree] =\n      tree match {\n        case a: ApplyTree =>\n          for {\n            function <- updateTree(a.function)\n            args     <- updateTrees(a.arguments)\n          } yield a.withFunction(function).withArguments(args)\n        case Tree.Empty      => Some(Tree.Empty)\n        case f: FunctionTree =>\n          for {\n            body <- updateTree(f.body)\n          } yield f.withBody(body)\n        case i: IdTree             => Some(i)\n        case l: LiteralTree        => Some(l)\n        case m: MacroExpansionTree =>\n          for {\n            beforeExp <- updateTree(m.beforeExpansion)\n          } yield m.withBeforeExpansion(beforeExp)\n        case o: OriginalTree =>\n          if (o.range.isEmpty) Some(o)\n          else\n            for {\n              range <- o.range.flatMap(mapRange)\n            } yield o.withRange(range)\n        case s: SelectTree =>\n          for {\n            qual <- updateTree(s.qualifier)\n          } yield s.withQualifier(qual)\n        case t: TypeApplyTree =>\n          for {\n            fun <- updateTree(t.function)\n          } yield t.withFunction(fun)\n      }\n\n    if (os.isFile(orig)) {\n      val docs        = TextDocuments.parseFrom(os.read.bytes(orig))\n      val updatedDocs = docs.withDocuments {\n        docs.documents.map { doc =>\n          doc\n            .withText(originalCode)\n            .withUri(originalPath.toString)\n            .withMd5(md5(originalCode))\n            .withDiagnostics {\n              doc.diagnostics.flatMap { diag =>\n                diag.range.fold(Option(diag)) { range =>\n                  mapRange(range).map(diag.withRange)\n                }\n              }\n            }\n            .withOccurrences {\n              doc.occurrences.flatMap { occurrence =>\n                occurrence.range.fold(Option(occurrence)) { range =>\n                  mapRange(range).map(occurrence.withRange)\n                }\n              }\n            }\n            .withSynthetics {\n              doc.synthetics.flatMap { syn =>\n                val synOpt = syn.range.fold(Option(syn)) { range =>\n                  mapRange(range).map(syn.withRange)\n                }\n                synOpt.flatMap { syn0 =>\n                  updateTree(syn0.tree)\n                    .map(syn0.withTree)\n                }\n              }\n            }\n        }\n      }\n      os.write.over(dest, updatedDocs.toByteArray, createFolders = true)\n    }\n    else\n      System.err.println(s\"Error: $orig not found (for $dest)\")\n  }\n\n  private def md5(content: String): String = {\n    val md     = MessageDigest.getInstance(\"MD5\")\n    val digest = md.digest(content.getBytes(StandardCharsets.UTF_8))\n    val res    = new BigInteger(1, digest).toString(16)\n    if (res.length < 32)\n      (\"0\" * (32 - res.length)) + res\n    else\n      res\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/postprocessing/TastyPostProcessor.scala",
    "content": "package scala.build.postprocessing\n\nimport java.nio.file.{FileAlreadyExistsException, NoSuchFileException}\n\nimport scala.build.internal.Constants\nimport scala.build.options.BuildOptions\nimport scala.build.tastylib.{TastyData, TastyVersions}\nimport scala.build.{GeneratedSource, Logger, retry}\n\ncase object TastyPostProcessor extends PostProcessor {\n\n  def postProcess(\n    generatedSources: Seq[GeneratedSource],\n    mappings: Map[String, (String, Int)],\n    workspace: os.Path,\n    output: os.Path,\n    logger: Logger,\n    scalaVersion: String,\n    buildOptions: BuildOptions\n  ): Either[String, Unit] = {\n\n    def updatedPaths = generatedSources\n      .flatMap { source =>\n        source.reportingPath.toOption.toSeq.map { originalSource =>\n          val fromSourceRoot = source.generated.relativeTo(workspace)\n          val actual         = originalSource.relativeTo(workspace)\n          fromSourceRoot.toString -> actual.toString\n        }\n      }\n      .toMap\n\n    TastyVersions.shouldRunPreprocessor(\n      scalaVersion,\n      Constants.version,\n      buildOptions.scalaOptions.defaultScalaVersion\n    ) match {\n      case Right(false) => Right(())\n      case Left(msg)    => if (updatedPaths.isEmpty) Right(()) else Left(msg)\n      case Right(true)  =>\n        val paths = updatedPaths\n        if (paths.isEmpty) Right(())\n        else Right(\n          os.walk(output)\n            .filter(os.isFile(_))\n            .filter(_.last.endsWith(\".tasty\")) // make that case-insensitive just in case?\n            .foreach(updateTastyFile(logger, paths))\n        )\n    }\n  }\n\n  private def updateTastyFile(\n    logger: Logger,\n    updatedPaths: Map[String, String]\n  )(f: os.Path): Unit = {\n    logger.debug(s\"Reading TASTy file $f\")\n    try retry()(logger) {\n        val content = os.read.bytes(f)\n        TastyData.read(content) match {\n          case Left(ex)    => logger.debug(s\"Ignoring exception during TASty postprocessing: $ex\")\n          case Right(data) =>\n            logger.debug(s\"Parsed TASTy file $f\")\n            var updatedOne  = false\n            val updatedData = data.mapNames { n =>\n              updatedPaths.get(n) match {\n                case Some(newName) =>\n                  updatedOne = true\n                  newName\n                case None =>\n                  n\n              }\n            }\n            if updatedOne then {\n              logger.debug(\n                s\"Overwriting ${if f.startsWith(os.pwd) then f.relativeTo(os.pwd) else f}\"\n              )\n              val updatedContent = TastyData.write(updatedData)\n              os.write.over(f, updatedContent)\n            }\n        }\n      }\n    catch {\n      case e: (NoSuchFileException | FileAlreadyExistsException | ArrayIndexOutOfBoundsException) =>\n        logger.debugStackTrace(e)\n        logger.log(s\"Tasty file $f not found: $e. Are you trying to run too many builds at once\")\n        logger.log(\"Are you trying to run too many builds at once? Trying to recover...\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/CustomDirectivesReporter.scala",
    "content": "package scala.build.preprocessing\n\nimport com.virtuslab.using_directives.custom.utils.Position as DirectivePosition\nimport com.virtuslab.using_directives.reporter.Reporter\n\nimport scala.build.Position\nimport scala.build.errors.{Diagnostic, Severity}\n\nclass CustomDirectivesReporter(path: Either[String, os.Path], onDiagnostic: Diagnostic => Unit)\n    extends Reporter {\n  private var errorCount   = 0\n  private var warningCount = 0\n\n  private def toScalaCliPosition(position: DirectivePosition): Position = {\n    val coords = (position.getLine, position.getColumn)\n    Position.File(path, coords, coords)\n  }\n\n  override def error(msg: String): Unit =\n    onDiagnostic {\n      errorCount += 1\n      Diagnostic(msg, Severity.Error)\n    }\n  override def error(position: DirectivePosition, msg: String): Unit =\n    onDiagnostic {\n      errorCount += 1\n      Diagnostic(msg, Severity.Error, Seq(toScalaCliPosition(position)))\n    }\n  override def warning(msg: String): Unit =\n    onDiagnostic {\n      warningCount += 1\n      Diagnostic(msg, Severity.Warning)\n    }\n  override def warning(position: DirectivePosition, msg: String): Unit =\n    onDiagnostic {\n      warningCount += 1\n      Diagnostic(msg, Severity.Warning, Seq(toScalaCliPosition(position)))\n    }\n\n  override def hasErrors(): Boolean =\n    errorCount != 0\n\n  override def hasWarnings(): Boolean =\n    warningCount != 0\n\n  override def reset(): Unit = {\n    errorCount = 0\n  }\n}\n\nobject CustomDirectivesReporter {\n  def create(path: Either[String, os.Path])(onDiagnostic: Diagnostic => Unit)\n    : CustomDirectivesReporter =\n    new CustomDirectivesReporter(path, onDiagnostic)\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/DataPreprocessor.scala",
    "content": "package scala.build.preprocessing\nimport scala.build.EitherCps.either\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.input.{ScalaCliInvokeData, SingleElement, VirtualData}\nimport scala.build.options.{BuildRequirements, SuppressWarningOptions}\n\ncase object DataPreprocessor extends Preprocessor {\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]] =\n    input match {\n      case file: VirtualData =>\n        val res = either {\n          val inMemory = Seq(\n            PreprocessedSource.InMemory(\n              originalPath = Left(file.source),\n              relPath = file.subPath,\n              content = file.content,\n              wrapperParamsOpt = None,\n              options = None,\n              optionsWithTargetRequirements = Nil,\n              requirements = Some(BuildRequirements()),\n              scopedRequirements = Nil,\n              mainClassOpt = None,\n              scopePath = file.scopePath,\n              directivesPositions = None\n            )\n          )\n          inMemory\n        }\n        Some(res)\n      case _ =>\n        None\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/DeprecatedDirectives.scala",
    "content": "package scala.build.preprocessing\n\nimport scala.build.Logger\nimport scala.build.errors.Diagnostic.TextEdit\nimport scala.build.internal.Constants\nimport scala.build.internal.util.WarningMessages.{deprecatedToolkitLatest, deprecatedWarning}\nimport scala.build.options.SuppressWarningOptions\nimport scala.build.preprocessing.directives.{DirectiveHandler, StrictDirective, Toolkit}\nimport scala.build.warnings.DeprecatedWarning\n\nobject DeprecatedDirectives {\n\n  /** Used to represent a general form of a deprecated directive, and its replacement\n    * @param keys\n    *   representation of deprecated keys\n    * @param values\n    *   representation of deprecated value\n    */\n  private case class DirectiveTemplate(keys: Seq[String], values: Option[Seq[String]]) {\n    def appliesTo(foundKey: String, foundValues: Seq[String]): Boolean =\n      (keys.isEmpty || keys.contains(foundKey)) &&\n      // FIXME values.contains is not perfect, but is enough for now since we don't look for specific multiple values\n      (values.isEmpty || values.contains(foundValues))\n  }\n\n  private type WarningAndReplacement = (String, DirectiveTemplate)\n\n  private def keyReplacement(replacement: String)(warning: String): WarningAndReplacement =\n    (warning, DirectiveTemplate(Seq(replacement), None))\n\n  private def valueReplacement(replacements: String*)(warning: String): WarningAndReplacement =\n    (warning, DirectiveTemplate(Nil, Some(replacements.toSeq)))\n\n  private def allKeysFrom(handler: DirectiveHandler[?]): Seq[String] =\n    handler.keys.flatMap(_.nameAliases)\n\n  private val deprecatedCombinationsAndReplacements = Map[DirectiveTemplate, WarningAndReplacement](\n    DirectiveTemplate(Seq(\"lib\"), None)  -> keyReplacement(\"dep\")(deprecatedWarning(\"lib\", \"dep\")),\n    DirectiveTemplate(Seq(\"libs\"), None) -> keyReplacement(\"dep\")(deprecatedWarning(\n      \"libs\",\n      \"deps\"\n    )),\n    DirectiveTemplate(Seq(\"compileOnly.lib\"), None) -> keyReplacement(\"compileOnly.dep\")(\n      deprecatedWarning(\"compileOnly.lib\", \"compileOnly.dep\")\n    ),\n    DirectiveTemplate(Seq(\"compileOnly.libs\"), None) -> keyReplacement(\"compileOnly.dep\")(\n      deprecatedWarning(\"compileOnly.libs\", \"compileOnly.deps\")\n    ),\n    DirectiveTemplate(\n      allKeysFrom(directives.Toolkit.handler),\n      Some(Seq(\"latest\"))\n    ) -> valueReplacement(\"default\")(deprecatedToolkitLatest()),\n    DirectiveTemplate(\n      allKeysFrom(directives.Toolkit.handler),\n      Some(Seq(s\"${Toolkit.typelevel}:latest\"))\n    ) -> valueReplacement(s\"${Toolkit.typelevel}:default\")(\n      deprecatedToolkitLatest()\n    ),\n    DirectiveTemplate(\n      allKeysFrom(directives.Toolkit.handler),\n      Some(Seq(s\"${Constants.typelevelOrganization}:latest\"))\n    ) -> valueReplacement(s\"${Toolkit.typelevel}:default\")(\n      deprecatedToolkitLatest()\n    )\n  )\n\n  private def warningAndReplacement(directive: StrictDirective): Option[WarningAndReplacement] =\n    deprecatedCombinationsAndReplacements\n      .find(_._1.appliesTo(directive.key, directive.toStringValues))\n      .map(_._2) // grab WarningAndReplacement\n\n  def issueWarnings(\n    path: Either[String, os.Path],\n    directives: Seq[StrictDirective],\n    suppressWarningOptions: SuppressWarningOptions,\n    logger: Logger\n  ): Unit =\n    if !suppressWarningOptions.suppressDeprecatedFeatureWarning.getOrElse(false) then\n      directives.map(d => d -> warningAndReplacement(d))\n        .foreach {\n          case (directive, Some(warning, replacement)) =>\n            val newKey    = replacement.keys.headOption.getOrElse(directive.key)\n            val newValues = replacement.values.getOrElse(directive.toStringValues)\n            val newText   = s\"$newKey ${newValues.mkString(\" \")}\"\n\n            // TODO use key and/or value positions instead of whole directive\n            val position = directive.position(path)\n\n            val diagnostic = DeprecatedWarning(\n              warning,\n              Seq(position),\n              Some(TextEdit(s\"Change to: $newText\", newText))\n            )\n            logger.log(Seq(diagnostic))\n          case _ => ()\n        }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/DirectivesPreprocessor.scala",
    "content": "package scala.build.preprocessing\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException, DirectiveErrors}\nimport scala.build.input.ScalaCliInvokeData\nimport scala.build.internal.util.WarningMessages\nimport scala.build.internals.FeatureType\nimport scala.build.options.{\n  BuildOptions,\n  BuildRequirements,\n  ConfigMonoid,\n  SuppressWarningOptions,\n  WithBuildRequirements\n}\nimport scala.build.preprocessing.directives.*\nimport scala.build.preprocessing.directives.DirectivesPreprocessingUtils.*\n\ncase class DirectivesPreprocessor(\n  path: Either[String, os.Path],\n  cwd: ScopePath,\n  logger: Logger,\n  allowRestrictedFeatures: Boolean,\n  suppressWarningOptions: SuppressWarningOptions,\n  maybeRecoverOnError: BuildException => Option[BuildException]\n)(\n  using ScalaCliInvokeData\n) {\n  def preprocess(content: String): Either[BuildException, PreprocessedDirectives] = for {\n    directives <- ExtractedDirectives.from(\n      content.toCharArray,\n      path,\n      suppressWarningOptions,\n      logger,\n      maybeRecoverOnError\n    )\n    res <- preprocess(directives)\n  } yield res\n\n  def preprocess(extractedDirectives: ExtractedDirectives)\n    : Either[BuildException, PreprocessedDirectives] = either {\n    val ExtractedDirectives(directives, directivesPositions) = extractedDirectives\n\n    val (\n      buildOptionsWithoutRequirements: PartiallyProcessedDirectives[BuildOptions],\n      buildOptionsWithTargetRequirements: PartiallyProcessedDirectives[\n        List[WithBuildRequirements[BuildOptions]]\n      ],\n      scopedBuildRequirements: PartiallyProcessedDirectives[BuildRequirements],\n      unusedDirectives: Seq[StrictDirective]\n    ) = value {\n      for {\n        regularUsingDirectives: PartiallyProcessedDirectives[BuildOptions] <-\n          applyDirectiveHandlers(directives, usingDirectiveHandlers)\n        usingDirectivesWithRequirements: PartiallyProcessedDirectives[\n          List[WithBuildRequirements[BuildOptions]]\n        ] <-\n          applyDirectiveHandlers(\n            regularUsingDirectives.unused,\n            usingDirectiveWithReqsHandlers\n          )\n        targetDirectives: PartiallyProcessedDirectives[BuildRequirements] <-\n          applyDirectiveHandlers(\n            usingDirectivesWithRequirements.unused,\n            requireDirectiveHandlers\n          )\n        remainingDirectives = targetDirectives.unused\n      } yield (\n        regularUsingDirectives,\n        usingDirectivesWithRequirements,\n        targetDirectives,\n        remainingDirectives\n      )\n    }\n\n    val (optionsWithActualRequirements, optionsWithEmptyRequirements) =\n      buildOptionsWithTargetRequirements.global.partition(_.requirements.nonEmpty)\n    val summedOptionsWithNoRequirements =\n      optionsWithEmptyRequirements\n        .map(_.value)\n        .foldLeft(buildOptionsWithoutRequirements.global)((acc, bo) => acc.orElse(bo))\n\n    value {\n      unusedDirectives.toList match {\n        case Nil =>\n          Right {\n            PreprocessedDirectives(\n              scopedBuildRequirements.global,\n              summedOptionsWithNoRequirements,\n              optionsWithActualRequirements,\n              scopedBuildRequirements.scoped,\n              strippedContent = None,\n              directivesPositions\n            )\n          }\n        case unused =>\n          maybeRecoverOnError {\n            CompositeBuildException(\n              exceptions = unused.map(ScopedDirective(_, path, cwd).unusedDirectiveError)\n            )\n          }.toLeft(PreprocessedDirectives.empty)\n      }\n    }\n  }\n\n  private def applyDirectiveHandlers[T: ConfigMonoid](\n    directives: Seq[StrictDirective],\n    handlers: Seq[DirectiveHandler[T]]\n  ): Either[BuildException, PartiallyProcessedDirectives[T]] = {\n    val configMonoidInstance               = implicitly[ConfigMonoid[T]]\n    val shouldSuppressExperimentalFeatures =\n      suppressWarningOptions.suppressExperimentalFeatureWarning.getOrElse(false)\n\n    def handleValues(handler: DirectiveHandler[T])(\n      scopedDirective: ScopedDirective,\n      logger: Logger\n    ): Either[BuildException, ProcessedDirective[T]] =\n      if !allowRestrictedFeatures && (handler.isRestricted || handler.isExperimental) then\n        Left(DirectiveErrors(\n          ::(WarningMessages.powerDirectiveUsedInSip(scopedDirective, handler), Nil),\n          Seq(scopedDirective.directive.position(scopedDirective.maybePath))\n        ))\n      else\n        if handler.isExperimental && !shouldSuppressExperimentalFeatures then\n          logger.experimentalWarning(scopedDirective.directive.toString, FeatureType.Directive)\n        handler.handleValues(scopedDirective, logger)\n\n    val handlersMap = handlers\n      .flatMap { handler =>\n        handler.keys.flatMap(_.nameAliases).map(k => k -> handleValues(handler))\n      }\n      .toMap\n\n    val unused = directives.filter(d => !handlersMap.contains(d.key))\n\n    val res = directives\n      .iterator\n      .flatMap {\n        case d @ StrictDirective(k, _, _, _) =>\n          handlersMap.get(k).iterator.map(_(ScopedDirective(d, path, cwd), logger))\n      }\n      .toVector\n      .flatMap {\n        case Left(e: BuildException) => maybeRecoverOnError(e).toVector.map(Left(_))\n        case r @ Right(_)            => Vector(r)\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map(_.foldLeft((configMonoidInstance.zero, Seq.empty[Scoped[T]])) {\n        case ((globalAcc, scopedAcc), ProcessedDirective(global, scoped)) => (\n            global.fold(globalAcc)(ns => configMonoidInstance.orElse(ns, globalAcc)),\n            scopedAcc ++ scoped\n          )\n      })\n    res.map {\n      case (g, s) => PartiallyProcessedDirectives(g, s, unused)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/ExtractedDirectives.scala",
    "content": "package scala.build.preprocessing\n\nimport com.virtuslab.using_directives.UsingDirectivesProcessor\nimport com.virtuslab.using_directives.custom.model.{BooleanValue, EmptyValue, StringValue, Value}\nimport com.virtuslab.using_directives.custom.utils.ast.*\n\nimport scala.annotation.targetName\nimport scala.build.errors.*\nimport scala.build.options.SuppressWarningOptions\nimport scala.build.preprocessing.UsingDirectivesOps.*\nimport scala.build.preprocessing.directives.StrictDirective\nimport scala.build.{Logger, Position}\nimport scala.collection.mutable\nimport scala.jdk.CollectionConverters.*\n\ncase class ExtractedDirectives(\n  directives: Seq[StrictDirective],\n  position: Option[Position.File]\n) {\n  @targetName(\"append\")\n  def ++(other: ExtractedDirectives): ExtractedDirectives =\n    ExtractedDirectives(directives ++ other.directives, position)\n}\n\nobject ExtractedDirectives {\n\n  def empty: ExtractedDirectives = ExtractedDirectives(Seq.empty, None)\n\n  def from(\n    contentChars: Array[Char],\n    path: Either[String, os.Path],\n    suppressWarningOptions: SuppressWarningOptions,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  ): Either[BuildException, ExtractedDirectives] = {\n    val errors   = new mutable.ListBuffer[Diagnostic]\n    val reporter = CustomDirectivesReporter\n      .create(path) {\n        case diag\n            if diag.severity == Severity.Warning &&\n            diag.message.toLowerCase.contains(\"deprecated\") &&\n            suppressWarningOptions.suppressDeprecatedFeatureWarning.getOrElse(false) =>\n          () // skip deprecated feature warnings if suppressed\n        case diag if diag.severity == Severity.Warning =>\n          logger.log(Seq(diag))\n        case diag => errors += diag\n      }\n    val processor                = new UsingDirectivesProcessor(reporter)\n    val allDirectives            = processor.extract(contentChars).asScala\n    val malformedDirectiveErrors =\n      errors.map(diag => new MalformedDirectiveError(diag.message, diag.positions)).toSeq\n    val maybeCompositeMalformedDirectiveError =\n      if (malformedDirectiveErrors.nonEmpty)\n        maybeRecoverOnError(CompositeBuildException(malformedDirectiveErrors))\n      else None\n    if (malformedDirectiveErrors.isEmpty || maybeCompositeMalformedDirectiveError.isEmpty) {\n\n      val directivesOpt         = allDirectives.headOption\n      val directivesPositionOpt = directivesOpt match {\n        case Some(directives)\n            if directives.containsTargetDirectives ||\n            directives.isEmpty => None\n        case Some(directives) => Some(directives.getPosition(path))\n        case None             => None\n      }\n\n      val strictDirectives = directivesOpt.toSeq.flatMap { directives =>\n        def toStrictValue(value: UsingValue): Seq[Value[?]] = value match {\n          case uvs: UsingValues   => uvs.values.asScala.toSeq.flatMap(toStrictValue)\n          case el: EmptyLiteral   => Seq(EmptyValue(el))\n          case sl: StringLiteral  => Seq(StringValue(sl.getValue(), sl))\n          case bl: BooleanLiteral => Seq(BooleanValue(bl.getValue(), bl))\n        }\n        def toStrictDirective(ud: UsingDef) =\n          StrictDirective(\n            ud.getKey(),\n            toStrictValue(ud.getValue()),\n            ud.getPosition().getColumn(),\n            ud.getPosition().getLine()\n          )\n\n        directives.getAst match\n          case uds: UsingDefs => uds.getUsingDefs.asScala.toSeq.map(toStrictDirective)\n          case ud: UsingDef   => Seq(toStrictDirective(ud))\n          case _ => Nil // There should be nothing else here other than UsingDefs or UsingDef\n      }\n\n      Right(ExtractedDirectives(strictDirectives.reverse, directivesPositionOpt))\n    }\n    else\n      maybeCompositeMalformedDirectiveError match {\n        case Some(e) => Left(e)\n        case None    => Right(ExtractedDirectives.empty)\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/JarPreprocessor.scala",
    "content": "package scala.build.preprocessing\nimport scala.build.EitherCps.either\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.input.{JarFile, ScalaCliInvokeData, SingleElement}\nimport scala.build.options.{\n  BuildOptions,\n  BuildRequirements,\n  ClassPathOptions,\n  SuppressWarningOptions\n}\n\ncase object JarPreprocessor extends Preprocessor {\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]] =\n    input match {\n      case jar: JarFile => Some(either {\n          val buildOptions = BuildOptions().copy(\n            classPathOptions = ClassPathOptions(\n              extraClassPath = Seq(jar.path)\n            )\n          )\n          Seq(PreprocessedSource.OnDisk(\n            path = jar.path,\n            options = Some(buildOptions),\n            optionsWithTargetRequirements = List.empty,\n            requirements = Some(BuildRequirements()),\n            scopedRequirements = Nil,\n            mainClassOpt = None,\n            directivesPositions = None\n          ))\n        })\n      case _ =>\n        None\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/JavaPreprocessor.scala",
    "content": "package scala.build.preprocessing\n\nimport coursier.cache.ArchiveCache\nimport coursier.util.Task\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.input.{JavaFile, ScalaCliInvokeData, SingleElement, VirtualJavaFile}\nimport scala.build.internal.JavaParserProxyMaker\nimport scala.build.options.{BuildRequirements, SuppressWarningOptions}\nimport scala.build.preprocessing.directives.PreprocessedDirectives\n\n/** Java source preprocessor.\n  *\n  * Doesn't modify Java sources. This only extracts using directives from them, and for unnamed\n  * sources (like stdin), tries to infer a class name from the sources themselves.\n  *\n  * @param archiveCache\n  *   when using a java-class-name external binary to infer a class name (see [[JavaParserProxy]]),\n  *   a cache to download that binary with\n  * @param javaClassNameVersionOpt\n  *   when using a java-class-name external binary to infer a class name (see [[JavaParserProxy]]),\n  *   this forces the java-class-name version to download\n  */\nfinal case class JavaPreprocessor(\n  archiveCache: ArchiveCache[Task],\n  javaClassNameVersionOpt: Option[String],\n  javaCommand: () => String\n) extends Preprocessor {\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]] =\n    input match {\n      case j: JavaFile => Some(either {\n          val content: String = value(PreprocessingUtil.maybeRead(j.path))\n          val scopePath       = ScopePath.fromPath(j.path)\n          val preprocessedDirectives: PreprocessedDirectives = value {\n            DirectivesPreprocessor(\n              Right(j.path),\n              scopePath,\n              logger,\n              allowRestrictedFeatures,\n              suppressWarningOptions,\n              maybeRecoverOnError\n            )\n              .preprocess(content)\n          }\n          Seq(PreprocessedSource.OnDisk(\n            path = j.path,\n            options = Some(preprocessedDirectives.globalUsings),\n            optionsWithTargetRequirements = preprocessedDirectives.usingsWithReqs,\n            requirements = Some(BuildRequirements()),\n            scopedRequirements = Nil,\n            mainClassOpt = None,\n            directivesPositions = preprocessedDirectives.directivesPositions\n          ))\n        })\n      case v: VirtualJavaFile =>\n        val res = either {\n          val relPath =\n            if (v.isStdin || v.isSnippet) {\n              val classNameOpt = value {\n                (new JavaParserProxyMaker)\n                  .get(\n                    archiveCache,\n                    javaClassNameVersionOpt,\n                    logger,\n                    () => javaCommand()\n                  )\n                  .className(v.content)\n              }\n              val fileName = classNameOpt\n                .map(_ + \".java\")\n                .getOrElse(v.generatedSourceFileName)\n              os.sub / fileName\n            }\n            else v.subPath\n          val content = new String(v.content, StandardCharsets.UTF_8)\n          val preprocessedDirectives: PreprocessedDirectives = value {\n            DirectivesPreprocessor(\n              Left(relPath.toString),\n              v.scopePath,\n              logger,\n              allowRestrictedFeatures,\n              suppressWarningOptions,\n              maybeRecoverOnError\n            ).preprocess(\n              content\n            )\n          }\n          val s = PreprocessedSource.InMemory(\n            originalPath = Left(v.source),\n            relPath = relPath,\n            content = v.content,\n            wrapperParamsOpt = None,\n            options = Some(preprocessedDirectives.globalUsings),\n            optionsWithTargetRequirements = preprocessedDirectives.usingsWithReqs,\n            requirements = Some(BuildRequirements()),\n            scopedRequirements = Nil,\n            mainClassOpt = None,\n            scopePath = v.scopePath,\n            directivesPositions = preprocessedDirectives.directivesPositions\n          )\n          Seq(s)\n        }\n        Some(res)\n\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/MarkdownCodeBlockProcessor.scala",
    "content": "package scala.build.preprocessing\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.internal.markdown.MarkdownCodeBlock\nimport scala.build.options.SuppressWarningOptions\n\nobject MarkdownCodeBlockProcessor {\n  def process(\n    codeBlocks: Seq[MarkdownCodeBlock],\n    reportingPath: Either[String, os.Path],\n    suppressWarningOptions: SuppressWarningOptions,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  ): Either[BuildException, PreprocessedMarkdown] = either {\n    val (rawCodeBlocks, remaining)         = codeBlocks.partition(_.isRaw)\n    val (testCodeBlocks, scriptCodeBlocks) = remaining.partition(_.isTest)\n    def preprocessCodeBlocks(cbs: Seq[MarkdownCodeBlock])\n      : Either[BuildException, PreprocessedMarkdownCodeBlocks] = either {\n      val mergedDirectives: ExtractedDirectives = cbs\n        .map { cb =>\n          value {\n            ExtractedDirectives.from(\n              contentChars = cb.body.toCharArray,\n              path = reportingPath,\n              suppressWarningOptions = suppressWarningOptions,\n              logger = logger,\n              maybeRecoverOnError = maybeRecoverOnError\n            )\n          }\n        }\n        .fold(ExtractedDirectives.empty)(_ ++ _)\n      PreprocessedMarkdownCodeBlocks(\n        cbs,\n        mergedDirectives\n      )\n    }\n    PreprocessedMarkdown(\n      value(preprocessCodeBlocks(scriptCodeBlocks)),\n      value(preprocessCodeBlocks(rawCodeBlocks)),\n      value(preprocessCodeBlocks(testCodeBlocks))\n    )\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/MarkdownPreprocessor.scala",
    "content": "package scala.build.preprocessing\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.input.{MarkdownFile, ScalaCliInvokeData, SingleElement, VirtualMarkdownFile}\nimport scala.build.internal.markdown.{MarkdownCodeBlock, MarkdownCodeWrapper}\nimport scala.build.options.SuppressWarningOptions\nimport scala.build.preprocessing.ScalaPreprocessor.ProcessingOutput\n\ncase object MarkdownPreprocessor extends Preprocessor {\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException],\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]] =\n    input match {\n      case markdown: MarkdownFile =>\n        val res = either {\n          val content      = value(PreprocessingUtil.maybeRead(markdown.path))\n          val preprocessed = value {\n            MarkdownPreprocessor.preprocess(\n              Right(markdown.path),\n              content,\n              markdown.subPath,\n              ScopePath.fromPath(markdown.path),\n              logger,\n              maybeRecoverOnError,\n              allowRestrictedFeatures,\n              suppressWarningOptions\n            )\n          }\n          preprocessed\n        }\n        Some(res)\n      case markdown: VirtualMarkdownFile =>\n        val content = new String(markdown.content, StandardCharsets.UTF_8)\n        val res     = either {\n          val preprocessed = value {\n            MarkdownPreprocessor.preprocess(\n              Left(markdown.source),\n              content,\n              markdown.wrapperPath,\n              markdown.scopePath,\n              logger,\n              maybeRecoverOnError,\n              allowRestrictedFeatures,\n              suppressWarningOptions\n            )\n          }\n          preprocessed\n        }\n        Some(res)\n      case _ =>\n        None\n    }\n\n  private def preprocess(\n    reportingPath: Either[String, os.Path],\n    content: String,\n    subPath: os.SubPath,\n    scopePath: ScopePath,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException],\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Either[BuildException, List[PreprocessedSource.InMemory]] = either {\n    def preprocessSnippets(\n      maybeWrapper: Option[MarkdownCodeWrapper.WrappedMarkdownCode],\n      generatedSourceNameSuffix: String\n    ): Either[BuildException, Option[PreprocessedSource.InMemory]] =\n      either {\n        maybeWrapper\n          .map { wrappedMarkdown =>\n            val processingOutput: ProcessingOutput =\n              value {\n                ScalaPreprocessor.processSources(\n                  content = wrappedMarkdown.code,\n                  extractedDirectives = wrappedMarkdown.directives,\n                  path = reportingPath,\n                  scopeRoot = scopePath / os.up,\n                  logger = logger,\n                  allowRestrictedFeatures = allowRestrictedFeatures,\n                  suppressWarningOptions = suppressWarningOptions,\n                  maybeRecoverOnError = maybeRecoverOnError\n                )\n              }.getOrElse(ProcessingOutput.empty)\n            val processedCode = processingOutput.updatedContent.getOrElse(wrappedMarkdown.code)\n            PreprocessedSource.InMemory(\n              originalPath = reportingPath.map(subPath -> _),\n              relPath = os.rel / (subPath / os.up) / s\"${subPath.last}$generatedSourceNameSuffix\",\n              processedCode.getBytes(StandardCharsets.UTF_8),\n              wrapperParamsOpt = None,\n              options = Some(processingOutput.opts),\n              optionsWithTargetRequirements = processingOutput.optsWithReqs,\n              requirements = Some(processingOutput.globalReqs),\n              processingOutput.scopedReqs,\n              mainClassOpt = None,\n              scopePath = scopePath,\n              directivesPositions = processingOutput.directivesPositions\n            )\n          }\n      }\n\n    val codeBlocks: Seq[MarkdownCodeBlock] =\n      value(MarkdownCodeBlock.findCodeBlocks(subPath, content, maybeRecoverOnError))\n    val preprocessedMarkdown: PreprocessedMarkdown =\n      value(MarkdownCodeBlockProcessor.process(\n        codeBlocks,\n        reportingPath,\n        suppressWarningOptions,\n        logger,\n        maybeRecoverOnError\n      ))\n\n    val (mainScalaCode, rawScalaCode, testScalaCode) =\n      MarkdownCodeWrapper(subPath, preprocessedMarkdown)\n\n    val maybeMainFile = value(preprocessSnippets(mainScalaCode, \".scala\"))\n    val maybeRawFile  = value(preprocessSnippets(rawScalaCode, \".raw.scala\"))\n    val maybeTestFile = value(preprocessSnippets(testScalaCode, \".test.scala\"))\n\n    maybeMainFile.toList ++ maybeTestFile ++ maybeRawFile\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/PreprocessedMarkdown.scala",
    "content": "package scala.build.preprocessing\n\nimport scala.build.internal.markdown.MarkdownCodeBlock\n\ncase class PreprocessedMarkdownCodeBlocks(\n  codeBlocks: Seq[MarkdownCodeBlock],\n  extractedDirectives: ExtractedDirectives = ExtractedDirectives.empty\n)\n\nobject PreprocessedMarkdownCodeBlocks {\n  def empty: PreprocessedMarkdownCodeBlocks =\n    PreprocessedMarkdownCodeBlocks(Seq.empty, ExtractedDirectives.empty)\n}\n\ncase class PreprocessedMarkdown(\n  scriptCodeBlocks: PreprocessedMarkdownCodeBlocks = PreprocessedMarkdownCodeBlocks.empty,\n  rawCodeBlocks: PreprocessedMarkdownCodeBlocks = PreprocessedMarkdownCodeBlocks.empty,\n  testCodeBlocks: PreprocessedMarkdownCodeBlocks = PreprocessedMarkdownCodeBlocks.empty\n)\n\nobject PreprocessedMarkdown {\n  def empty: PreprocessedMarkdown = PreprocessedMarkdown()\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala",
    "content": "package scala.build.preprocessing\n\nimport scala.build.Position\nimport scala.build.internal.{CodeWrapper, WrapperParams}\nimport scala.build.options.{BuildOptions, BuildRequirements, WithBuildRequirements}\n\nsealed abstract class PreprocessedSource extends Product with Serializable {\n  def options: Option[BuildOptions]\n  def optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]]\n  def requirements: Option[BuildRequirements]\n  def mainClassOpt: Option[String]\n  def scopedRequirements: Seq[Scoped[BuildRequirements]]\n  def scopePath: ScopePath\n  def directivesPositions: Option[Position.File]\n  def distinctPathOrSource: String = this match {\n    case p: PreprocessedSource.OnDisk          => p.path.toString\n    case p: PreprocessedSource.InMemory        => s\"${p.originalPath}; ${p.relPath}\"\n    case p: PreprocessedSource.UnwrappedScript => p.originalPath.toString\n    case p: PreprocessedSource.NoSourceCode    => p.path.toString\n  }\n}\n\nobject PreprocessedSource {\n\n  final case class OnDisk(\n    path: os.Path,\n    options: Option[BuildOptions],\n    optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]],\n    requirements: Option[BuildRequirements],\n    scopedRequirements: Seq[Scoped[BuildRequirements]],\n    mainClassOpt: Option[String],\n    directivesPositions: Option[Position.File]\n  ) extends PreprocessedSource {\n    def scopePath: ScopePath =\n      ScopePath.fromPath(path)\n  }\n  final case class InMemory(\n    originalPath: Either[String, (os.SubPath, os.Path)],\n    relPath: os.RelPath,\n    content: Array[Byte],\n    wrapperParamsOpt: Option[WrapperParams],\n    options: Option[BuildOptions],\n    optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]],\n    requirements: Option[BuildRequirements],\n    scopedRequirements: Seq[Scoped[BuildRequirements]],\n    mainClassOpt: Option[String],\n    scopePath: ScopePath,\n    directivesPositions: Option[Position.File]\n  ) extends PreprocessedSource {\n    def reportingPath: Either[String, os.Path] =\n      originalPath.map(_._2)\n  }\n\n  final case class UnwrappedScript(\n    originalPath: Either[String, (os.SubPath, os.Path)],\n    relPath: os.RelPath,\n    options: Option[BuildOptions],\n    optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]],\n    requirements: Option[BuildRequirements],\n    scopedRequirements: Seq[Scoped[BuildRequirements]],\n    scopePath: ScopePath,\n    directivesPositions: Option[Position.File],\n    wrapScriptFun: CodeWrapper => (String, WrapperParams)\n  ) extends PreprocessedSource {\n    override def mainClassOpt: Option[String] = None\n  }\n\n  final case class NoSourceCode(\n    options: Option[BuildOptions],\n    optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]],\n    requirements: Option[BuildRequirements],\n    scopedRequirements: Seq[Scoped[BuildRequirements]],\n    path: os.Path\n  ) extends PreprocessedSource {\n    def mainClassOpt: None.type = None\n    def scopePath: ScopePath    =\n      ScopePath.fromPath(path)\n    def directivesPositions: None.type = None\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/PreprocessingUtil.scala",
    "content": "package scala.build.preprocessing\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.errors.{BuildException, FileNotFoundException}\n\nobject PreprocessingUtil {\n  private def defaultCharSet                                = StandardCharsets.UTF_8\n  def maybeRead(f: os.Path): Either[BuildException, String] =\n    if (os.isFile(f)) Right(os.read(f, defaultCharSet))\n    else Left(new FileNotFoundException(f))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/Preprocessor.scala",
    "content": "package scala.build.preprocessing\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.input.{ScalaCliInvokeData, SingleElement}\nimport scala.build.options.SuppressWarningOptions\n\ntrait Preprocessor {\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]]\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala",
    "content": "package scala.build.preprocessing\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.*\nimport scala.build.input.{ScalaCliInvokeData, ScalaFile, SingleElement, VirtualScalaFile}\nimport scala.build.options.*\nimport scala.build.preprocessing.directives.PreprocessedDirectives\nimport scala.build.{Logger, Position}\n\ncase object ScalaPreprocessor extends Preprocessor {\n  case class ProcessingOutput(\n    globalReqs: BuildRequirements,\n    scopedReqs: Seq[Scoped[BuildRequirements]],\n    opts: BuildOptions,\n    optsWithReqs: List[WithBuildRequirements[BuildOptions]],\n    updatedContent: Option[String],\n    directivesPositions: Option[Position.File]\n  )\n\n  object ProcessingOutput {\n    def empty: ProcessingOutput = ProcessingOutput(\n      BuildRequirements(),\n      Nil,\n      BuildOptions(),\n      List.empty,\n      None,\n      None\n    )\n  }\n\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]] =\n    input match {\n      case f: ScalaFile =>\n        val res = either {\n          val content   = value(PreprocessingUtil.maybeRead(f.path))\n          val scopePath = ScopePath.fromPath(f.path)\n          val source    =\n            value(\n              process(\n                content,\n                Right(f.path),\n                scopePath / os.up,\n                logger,\n                maybeRecoverOnError,\n                allowRestrictedFeatures,\n                suppressWarningOptions\n              )\n            ) match {\n              case None =>\n                PreprocessedSource.OnDisk(f.path, None, List.empty, None, Nil, None, None)\n              case Some(ProcessingOutput(\n                    requirements,\n                    scopedRequirements,\n                    options,\n                    optionsWithReqs,\n                    Some(updatedCode),\n                    directivesPositions\n                  )) =>\n                PreprocessedSource.InMemory(\n                  originalPath = Right((f.subPath, f.path)),\n                  relPath = f.subPath,\n                  content = updatedCode.getBytes(StandardCharsets.UTF_8),\n                  wrapperParamsOpt = None,\n                  options = Some(options),\n                  optionsWithTargetRequirements = optionsWithReqs,\n                  requirements = Some(requirements),\n                  scopedRequirements = scopedRequirements,\n                  mainClassOpt = None,\n                  scopePath = scopePath,\n                  directivesPositions = directivesPositions\n                )\n              case Some(ProcessingOutput(\n                    requirements,\n                    scopedRequirements,\n                    options,\n                    optionsWithReqs,\n                    None,\n                    directivesPositions\n                  )) =>\n                PreprocessedSource.OnDisk(\n                  path = f.path,\n                  options = Some(options),\n                  optionsWithTargetRequirements = optionsWithReqs,\n                  requirements = Some(requirements),\n                  scopedRequirements = scopedRequirements,\n                  mainClassOpt = None,\n                  directivesPositions = directivesPositions\n                )\n            }\n          Seq(source)\n        }\n        Some(res)\n\n      case v: VirtualScalaFile =>\n        val res = either {\n          val relPath = v match {\n            case v if !v.isStdin && !v.isSnippet => v.subPath\n            case v                               => os.sub / v.generatedSourceFileName\n          }\n          val content = new String(v.content, StandardCharsets.UTF_8)\n          val (\n            requirements: BuildRequirements,\n            scopedRequirements: Seq[Scoped[BuildRequirements]],\n            options: BuildOptions,\n            optionsWithTargetRequirements: List[WithBuildRequirements[BuildOptions]],\n            updatedContentOpt: Option[String],\n            directivesPositions: Option[Position.File]\n          ) =\n            value(\n              process(\n                content,\n                Left(v.source),\n                v.scopePath / os.up,\n                logger,\n                maybeRecoverOnError,\n                allowRestrictedFeatures,\n                suppressWarningOptions\n              )\n            ).map {\n              case ProcessingOutput(\n                    reqs,\n                    scopedReqs,\n                    opts,\n                    optsWithReqs,\n                    updatedContent,\n                    dirsPositions\n                  ) =>\n                (reqs, scopedReqs, opts, optsWithReqs, updatedContent, dirsPositions)\n            }.getOrElse((\n              BuildRequirements(),\n              Nil,\n              BuildOptions(),\n              List(WithBuildRequirements(BuildRequirements(), BuildOptions())),\n              None,\n              None\n            ))\n          val s = PreprocessedSource.InMemory(\n            originalPath = Left(v.source),\n            relPath = relPath,\n            updatedContentOpt.map(_.getBytes(StandardCharsets.UTF_8)).getOrElse(v.content),\n            wrapperParamsOpt = None,\n            options = Some(options),\n            optionsWithTargetRequirements = optionsWithTargetRequirements,\n            requirements = Some(requirements),\n            scopedRequirements,\n            mainClassOpt = None,\n            scopePath = v.scopePath,\n            directivesPositions = directivesPositions\n          )\n          Seq(s)\n        }\n        Some(res)\n\n      case _ =>\n        None\n    }\n\n  def process(\n    content: String,\n    path: Either[String, os.Path],\n    scopeRoot: ScopePath,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException],\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Either[BuildException, Option[ProcessingOutput]] = either {\n    val (contentWithNoShebang, _, _) = SheBang.ignoreSheBangLines(content)\n\n    val extractedDirectives: ExtractedDirectives = value(ExtractedDirectives.from(\n      contentChars = contentWithNoShebang.toCharArray,\n      path = path,\n      suppressWarningOptions = suppressWarningOptions,\n      logger = logger,\n      maybeRecoverOnError = maybeRecoverOnError\n    ))\n    value {\n      processSources(\n        content,\n        extractedDirectives,\n        path,\n        scopeRoot,\n        logger,\n        allowRestrictedFeatures,\n        suppressWarningOptions,\n        maybeRecoverOnError\n      )\n    }\n  }\n\n  def processSources(\n    content: String,\n    extractedDirectives: ExtractedDirectives,\n    path: Either[String, os.Path],\n    scopeRoot: ScopePath,\n    logger: Logger,\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions,\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  )(using ScalaCliInvokeData): Either[BuildException, Option[ProcessingOutput]] = either {\n    DeprecatedDirectives.issueWarnings(\n      path,\n      extractedDirectives.directives,\n      suppressWarningOptions,\n      logger\n    )\n\n    val (content0, isSheBang, _)                       = SheBang.ignoreSheBangLines(content)\n    val preprocessedDirectives: PreprocessedDirectives =\n      value(DirectivesPreprocessor(\n        path,\n        scopeRoot,\n        logger,\n        allowRestrictedFeatures,\n        suppressWarningOptions,\n        maybeRecoverOnError\n      ).preprocess(\n        extractedDirectives\n      ))\n\n    if (preprocessedDirectives.isEmpty) None\n    else {\n      val allRequirements    = Seq(preprocessedDirectives.globalReqs)\n      val summedRequirements = allRequirements.foldLeft(BuildRequirements())(_.orElse(_))\n      val allOptions         = Seq(preprocessedDirectives.globalUsings)\n      val summedOptions      = allOptions.foldLeft(BuildOptions())(_.orElse(_))\n      val lastContentOpt     = preprocessedDirectives.strippedContent\n        .orElse(if (isSheBang) Some(content0) else None)\n      val directivesPositions = preprocessedDirectives.directivesPositions.map { pos =>\n        if (isSheBang) pos.copy(endPos = pos.endPos._1 + 1 -> pos.endPos._2) else pos\n      }\n\n      val scopedRequirements = preprocessedDirectives.scopedReqs\n      Some(ProcessingOutput(\n        summedRequirements,\n        scopedRequirements,\n        summedOptions,\n        preprocessedDirectives.usingsWithReqs,\n        lastContentOpt,\n        directivesPositions\n      ))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/ScriptPreprocessor.scala",
    "content": "package scala.build.preprocessing\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.input.{ScalaCliInvokeData, Script, SingleElement, VirtualScript}\nimport scala.build.internal.*\nimport scala.build.internal.util.WarningMessages\nimport scala.build.options.{BuildOptions, Platform, SuppressWarningOptions}\nimport scala.build.preprocessing.ScalaPreprocessor.ProcessingOutput\n\ncase object ScriptPreprocessor extends Preprocessor {\n  def preprocess(\n    input: SingleElement,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e),\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Option[Either[BuildException, Seq[PreprocessedSource]]] =\n    input match {\n      case script: Script =>\n        val res = either {\n          val content      = value(PreprocessingUtil.maybeRead(script.path))\n          val preprocessed = value {\n            ScriptPreprocessor.preprocess(\n              Right(script.path),\n              content,\n              script.subPath,\n              script.inputArg,\n              ScopePath.fromPath(script.path),\n              logger,\n              maybeRecoverOnError,\n              allowRestrictedFeatures,\n              suppressWarningOptions\n            )\n          }\n          preprocessed\n        }\n        Some(res)\n\n      case script: VirtualScript =>\n        val content = new String(script.content, StandardCharsets.UTF_8)\n\n        val res = either {\n          val preprocessed = value {\n            ScriptPreprocessor.preprocess(\n              Left(script.source),\n              content,\n              script.wrapperPath,\n              None,\n              script.scopePath,\n              logger,\n              maybeRecoverOnError,\n              allowRestrictedFeatures,\n              suppressWarningOptions\n            )\n          }\n          preprocessed\n        }\n        Some(res)\n\n      case _ =>\n        None\n    }\n\n  private def preprocess(\n    reportingPath: Either[String, os.Path],\n    content: String,\n    subPath: os.SubPath,\n    inputArgPath: Option[String],\n    scopePath: ScopePath,\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException],\n    allowRestrictedFeatures: Boolean,\n    suppressWarningOptions: SuppressWarningOptions\n  )(using ScalaCliInvokeData): Either[BuildException, List[PreprocessedSource.UnwrappedScript]] =\n    either {\n      val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(subPath)\n\n      val processingOutput: ProcessingOutput =\n        value(ScalaPreprocessor.process(\n          content,\n          reportingPath,\n          scopePath / os.up,\n          logger,\n          maybeRecoverOnError,\n          allowRestrictedFeatures,\n          suppressWarningOptions\n        ))\n          .getOrElse(ProcessingOutput.empty)\n\n      val scriptCode =\n        processingOutput.updatedContent.getOrElse(SheBang.ignoreSheBangLines(content)._1)\n      // try to match in multiline mode, don't match comment lines starting with '//'\n      val containsMainAnnot = \"(?m)^(?!//).*@main.*\".r.findFirstIn(scriptCode).isDefined\n\n      val wrapScriptFun = getScriptWrappingFunction(\n        logger,\n        containsMainAnnot,\n        pkg,\n        wrapper,\n        scriptCode,\n        inputArgPath.getOrElse(subPath.toString)\n      )\n\n      (pkg :+ wrapper).map(_.raw).mkString(\".\")\n      val relPath = os.rel / (subPath / os.up) / s\"${subPath.last.stripSuffix(\".sc\")}.scala\"\n\n      val file = PreprocessedSource.UnwrappedScript(\n        originalPath = reportingPath.map((subPath, _)),\n        relPath = relPath,\n        options = Some(processingOutput.opts),\n        optionsWithTargetRequirements = processingOutput.optsWithReqs,\n        requirements = Some(processingOutput.globalReqs),\n        scopedRequirements = processingOutput.scopedReqs,\n        scopePath = scopePath,\n        directivesPositions = processingOutput.directivesPositions,\n        wrapScriptFun = wrapScriptFun\n      )\n      List(file)\n    }\n\n  def getScriptWrappingFunction(\n    logger: Logger,\n    containsMainAnnot: Boolean,\n    packageStrings: Seq[Name],\n    wrapperName: Name,\n    scriptCode: String,\n    scriptPath: String\n  ): CodeWrapper => (String, WrapperParams) = {\n    (codeWrapper: CodeWrapper) =>\n      if (containsMainAnnot) logger.diagnostic(\n        codeWrapper match {\n          case _: AppCodeWrapper =>\n            WarningMessages.mainAnnotationNotSupported( /* annotationIgnored */ true)\n          case _ => WarningMessages.mainAnnotationNotSupported( /* annotationIgnored */ false)\n        }\n      )\n\n      val (code, wrapperParams) = codeWrapper.wrapCode(\n        packageStrings,\n        wrapperName,\n        scriptCode,\n        scriptPath\n      )\n      (code, wrapperParams)\n  }\n\n  /** Get correct script wrapper depending on the platform and version of Scala. For Scala 2 or\n    * Platform JS use [[ObjectCodeWrapper]]. Otherwise - for Scala 3 on JVM or Native use\n    * [[ClassCodeWrapper]].\n    * @param buildOptions\n    *   final version of options, build may fail if incompatible wrapper is chosen\n    * @return\n    *   code wrapper compatible with provided BuildOptions\n    */\n  def getScriptWrapper(buildOptions: BuildOptions, logger: Logger): CodeWrapper = {\n    val effectiveScalaVersion =\n      buildOptions.scalaOptions.scalaVersion.flatMap(_.versionOpt)\n        .orElse(buildOptions.scalaOptions.defaultScalaVersion)\n        .getOrElse(Constants.defaultScalaVersion)\n    def logWarning(msg: String) = logger.diagnostic(msg)\n\n    def objectCodeWrapperForScalaVersion =\n      // AppObjectWrapper only introduces the 'main.sc' restriction when used in Scala 3, there's no gain in using it with Scala 3\n      if effectiveScalaVersion.startsWith(\"2\") then\n        AppCodeWrapper(effectiveScalaVersion, logWarning)\n      else ObjectCodeWrapper(effectiveScalaVersion, logWarning)\n\n    buildOptions.scriptOptions.forceObjectWrapper match {\n      case Some(true) => objectCodeWrapperForScalaVersion\n      case _          =>\n        buildOptions.scalaOptions.platform.map(_.value) match {\n          case Some(_: Platform.JS.type)                  => objectCodeWrapperForScalaVersion\n          case _ if effectiveScalaVersion.startsWith(\"2\") =>\n            AppCodeWrapper(effectiveScalaVersion, logWarning)\n          case _ => ClassCodeWrapper(effectiveScalaVersion, logWarning)\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/SheBang.scala",
    "content": "package scala.build.preprocessing\n\nobject SheBang {\n  def isShebangScript(content: String): Boolean = content.startsWith(\"#!\")\n\n  def partitionOnShebangSection(content: String): (String, String, String) =\n    if (content.startsWith(\"#!\")) {\n      val splitIndex = content.indexOf(\"\\n!#\") match {\n        case -1 =>\n          val eolIndex = content.indexOf(\"\\n\")\n          content.drop(eolIndex + 1) match {\n            case s if s.startsWith(\"#!\") =>\n              eolIndex + s.indexOf(\"\\n\") + 1 // skip over #! nix-shell line\n            case _ =>\n              eolIndex + 1\n          }\n        case index =>\n          var i = index + 1\n          while (i < content.length && content(i) != '\\n') i += 1\n          i + 1 // split at start of subsequent line\n      }\n      val newLine: String = content.drop(splitIndex - 2).take(2) match {\n        case CRLF => CRLF\n        case _    => \"\\n\"\n      }\n      val (header, body) = content.splitAt(splitIndex)\n      (header, body, newLine)\n    }\n    else\n      (\"\", content, lineSeparator(content))\n\n  def ignoreSheBangLines(content: String): (String, Boolean, String) =\n    if (content.startsWith(\"#!\"))\n      val (header, body, newLine) = partitionOnShebangSection(content)\n      val blankHeader             = newLine * (header.split(\"\\\\R\", -1).length - 1)\n      (s\"$blankHeader$body\", true, newLine)\n    else\n      (content, false, lineSeparator(content))\n\n  def lineSeparator(content: String): String = content.indexOf(CRLF) match {\n    case -1 => \"\\n\"\n    case _  => CRLF\n  }\n\n  private final val CRLF: String = \"\\r\\n\"\n\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/UsingDirectivesOps.scala",
    "content": "package scala.build.preprocessing\n\nimport com.virtuslab.using_directives.custom.model.UsingDirectives\nimport com.virtuslab.using_directives.custom.utils.ast.*\n\nimport scala.annotation.tailrec\nimport scala.build.Position\nimport scala.jdk.CollectionConverters.*\n\nobject UsingDirectivesOps {\n  extension (ud: UsingDirectives) {\n    def keySet: Set[String] = ud.getFlattenedMap.keySet().asScala.map(_.toString).toSet\n    def containsTargetDirectives: Boolean = ud.keySet.exists(_.startsWith(\"target.\"))\n\n    def getPosition(path: Either[String, os.Path]): Position.File =\n      extension (pos: Positioned) {\n        def getLine   = pos.getPosition.getLine\n        def getColumn = pos.getPosition.getColumn\n      }\n\n      @tailrec\n      def getEndPostion(ast: UsingTree): (Int, Int) = ast match {\n        case uds: UsingDefs => uds.getUsingDefs.asScala match {\n            case _ :+ lastUsingDef => getEndPostion(lastUsingDef)\n            case _                 => (uds.getLine, uds.getColumn)\n          }\n        case ud: UsingDef     => getEndPostion(ud.getValue)\n        case uvs: UsingValues => uvs.getValues.asScala match {\n            case _ :+ lastUsingValue => getEndPostion(lastUsingValue)\n            case _                   => (uvs.getLine, uvs.getColumn)\n          }\n        case sl: StringLiteral => (\n            sl.getLine,\n            sl.getColumn + sl.getValue.length + { if sl.getIsWrappedDoubleQuotes then 2 else 0 }\n          )\n        case bl: BooleanLiteral => (bl.getLine, bl.getColumn + bl.getValue.toString.length)\n        case el: EmptyLiteral   => (el.getLine, el.getColumn)\n      }\n\n      val (line, column) = getEndPostion(ud.getAst)\n\n      Position.File(path, (0, 0), (line, column), ud.getCodeOffset)\n\n    def getDirectives =\n      ud.getAst match {\n        case usingDefs: UsingDefs =>\n          usingDefs.getUsingDefs.asScala.toSeq\n        case _ =>\n          Nil\n      }\n\n    def nonEmpty: Boolean = !isEmpty\n    def isEmpty: Boolean  = ud.getFlattenedMap.isEmpty\n  }\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/directives/DirectivesPreprocessingUtils.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.{\n  HasBuildOptions,\n  HasBuildOptionsWithRequirements,\n  HasBuildRequirements\n}\nimport scala.build.options.{BuildOptions, BuildRequirements, WithBuildRequirements}\nimport scala.build.preprocessing.directives\n\nobject DirectivesPreprocessingUtils {\n  val usingDirectiveHandlers: Seq[DirectiveHandler[BuildOptions]] =\n    Seq[DirectiveHandler[? <: HasBuildOptions]](\n      directives.Benchmarking.handler,\n      directives.BuildInfo.handler,\n      directives.ComputeVersion.handler,\n      directives.Exclude.handler,\n      directives.JavaHome.handler,\n      directives.Jvm.handler,\n      directives.MainClass.handler,\n      directives.ObjectWrapper.handler,\n      directives.Packaging.handler,\n      directives.Platform.handler,\n      directives.Plugin.handler,\n      directives.Publish.handler,\n      directives.PublishContextual.Local.handler,\n      directives.PublishContextual.CI.handler,\n      directives.Python.handler,\n      directives.Repository.handler,\n      directives.ScalaJs.handler,\n      directives.ScalaNative.handler,\n      directives.ScalaVersion.handler,\n      directives.Sources.handler,\n      directives.Watching.handler,\n      directives.Tests.handler\n    ).map(_.mapE(_.buildOptions))\n\n  val usingDirectiveWithReqsHandlers\n    : Seq[DirectiveHandler[List[WithBuildRequirements[BuildOptions]]]] =\n    Seq[DirectiveHandler[? <: HasBuildOptionsWithRequirements]](\n      directives.CustomJar.handler,\n      directives.Dependency.handler,\n      directives.JavaOptions.handler,\n      directives.JavacOptions.handler,\n      directives.JavaProps.handler,\n      directives.Resources.handler,\n      directives.ScalacOptions.handler,\n      directives.Toolkit.handler\n    ).map(_.mapE(_.buildOptionsWithRequirements))\n\n  val requireDirectiveHandlers: Seq[DirectiveHandler[BuildRequirements]] =\n    Seq[DirectiveHandler[? <: HasBuildRequirements]](\n      directives.RequirePlatform.handler,\n      directives.RequireScalaVersion.handler,\n      directives.RequireScalaVersionBounds.handler,\n      directives.RequireScope.handler\n    ).map(_.mapE(_.buildRequirements))\n}\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/directives/PartiallyProcessedDirectives.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.preprocessing.Scoped\n\ncase class PartiallyProcessedDirectives[T](\n  global: T,\n  scoped: Seq[Scoped[T]],\n  unused: Seq[StrictDirective]\n)\n"
  },
  {
    "path": "modules/build/src/main/scala/scala/build/preprocessing/directives/PreprocessedDirectives.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Position\nimport scala.build.options.{BuildOptions, BuildRequirements, WithBuildRequirements}\nimport scala.build.preprocessing.Scoped\n\ncase class PreprocessedDirectives(\n  globalReqs: BuildRequirements,\n  globalUsings: BuildOptions,\n  usingsWithReqs: List[WithBuildRequirements[BuildOptions]],\n  scopedReqs: Seq[Scoped[BuildRequirements]],\n  strippedContent: Option[String],\n  directivesPositions: Option[Position.File]\n) {\n  def isEmpty: Boolean = globalReqs == BuildRequirements.monoid.zero &&\n    globalUsings == BuildOptions.monoid.zero &&\n    scopedReqs.isEmpty &&\n    strippedContent.isEmpty &&\n    usingsWithReqs.isEmpty\n}\n\nobject PreprocessedDirectives {\n  def empty: PreprocessedDirectives =\n    PreprocessedDirectives(\n      globalReqs = BuildRequirements.monoid.zero,\n      globalUsings = BuildOptions.monoid.zero,\n      usingsWithReqs = Nil,\n      scopedReqs = Nil,\n      strippedContent = None,\n      directivesPositions = None\n    )\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/options/publish/ComputeVersionTests.scala",
    "content": "package scala.build.options.publish\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport org.eclipse.jgit.api.Git\nimport org.eclipse.jgit.lib.Constants\n\nimport scala.build.options.ComputeVersion\nimport scala.build.tests.{TestInputs, TestUtil}\n\nclass ComputeVersionTests extends munit.FunSuite {\n  test(\"git tag\") {\n    TestInputs().fromRoot { root =>\n      val ghRepo = \"scala-cli/compute-version-test\"\n      val repo   =\n        if (TestUtil.isCI) s\"https://git@github.com/$ghRepo.git\"\n        else s\"https://github.com/$ghRepo.git\"\n      os.proc(\"git\", \"clone\", repo)\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n      val dir = root / \"compute-version-test\"\n      val cv  = ComputeVersion.GitTag(os.rel, true, Nil, \"0.0.1-SNAPSHOT\")\n\n      val commitExpectedVersions = Seq(\n        \"8ea4e87f202fbcc369bec9615e7ddf2c14b39e9d\" -> \"0.2.0-1-g8ea4e87-SNAPSHOT\",\n        \"v0.2.0\"                                   -> \"0.2.0\",\n        \"698893f0a4cb1e758cbc8f748827daaf6c7b36d0\" -> \"0.0.1-SNAPSHOT\"\n      )\n\n      for ((commit, expectedVersion) <- commitExpectedVersions) {\n        os.proc(\"git\", \"checkout\", commit)\n          .call(cwd = dir, stdin = os.Inherit, stdout = os.Inherit)\n        val version = cv.get(dir)\n          .fold(ex => throw new Exception(ex), identity)\n        expect(version == expectedVersion)\n      }\n    }\n  }\n\n  test(\"git tag on empty repo\") {\n    TestInputs().fromRoot { root =>\n      val git     = Git.init().setDirectory(root.toIO).call()\n      val hasHead = git.getRepository.resolve(Constants.HEAD) != null\n      expect(!hasHead)\n\n      val defaultVersion = \"0.0.2-SNAPSHOT\"\n      val cv             = ComputeVersion.GitTag(os.rel, true, Nil, defaultVersion)\n\n      val version = cv.get(root)\n        .fold(ex => throw new Exception(ex), identity)\n      expect(version == defaultVersion)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/options/publish/VcsParseTest.scala",
    "content": "package scala.build.options.publish\n\nimport scala.build.Positioned\nimport scala.build.errors.{BuildException, MalformedInputError}\n\nclass VcsParseTest extends munit.FunSuite {\n  test(\"valid GitHub\") {\n    val actual = Vcs.parse(Positioned.none(\"github:VirtusLab/scala-cli\"))\n    val expected: Either[BuildException, Vcs] = Right(Vcs(\n      \"https://github.com/VirtusLab/scala-cli.git\",\n      \"scm:git:github.com/VirtusLab/scala-cli.git\",\n      \"scm:git:git@github.com:VirtusLab/scala-cli.git\"\n    ))\n\n    assertEquals(expected, actual)\n  }\n\n  test(\"invalid GitHub: missing /\") {\n    val actual: Either[BuildException, Vcs] = Vcs.parse(Positioned.none(\"github:scala-cli\"))\n    val expected                            =\n      Left(new MalformedInputError(\"github-vcs\", \"github:scala-cli\", \"github:org/project\", Nil))\n    assert {\n      actual match {\n        case Left(_: BuildException) => true\n        case _                       => sys.error(\"incorrect type\")\n      }\n    }\n    assertEquals(expected.toString, actual.toString)\n  }\n\n  test(\"invalid GitHub: too many /\") {\n    val actual: Either[BuildException, Vcs] =\n      Vcs.parse(Positioned.none(\"github:github.com/VirtusLab/scala-cli\"))\n    val expected = Left(new MalformedInputError(\n      \"github-vcs\",\n      \"github:github.com/VirtusLab/scala-cli\",\n      \"github:org/project\",\n      Nil\n    ))\n    assert {\n      actual match {\n        case Left(_: BuildException) => true\n        case _                       => sys.error(\"incorrect type\")\n      }\n    }\n    assertEquals(expected.toString, actual.toString)\n  }\n\n  test(\"valid generic\") {\n    val actual = Vcs.parse(Positioned.none(\n      \"https://github.com/VirtusLab/scala-cli.git|scm:git:github.com/VirtusLab/scala-cli.git|scm:git:git@github.com:VirtusLab/scala-cli.git\"\n    ))\n    val expected: Either[BuildException, Vcs] = Right(Vcs(\n      \"https://github.com/VirtusLab/scala-cli.git\",\n      \"scm:git:github.com/VirtusLab/scala-cli.git\",\n      \"scm:git:git@github.com:VirtusLab/scala-cli.git\"\n    ))\n\n    assertEquals(expected, actual)\n  }\n\n  test(\"invalid generic: missing |\") {\n    val actual: Either[BuildException, Vcs] = Vcs.parse(Positioned.none(\n      \"https://github.com/VirtusLab/scala-cli|scm:git:github.com/VirtusLab/scala-cli.git\"\n    ))\n    val expected = Left(new MalformedInputError(\n      \"vcs\",\n      \"https://github.com/VirtusLab/scala-cli|scm:git:github.com/VirtusLab/scala-cli.git\",\n      \"url|connection|developer-connection\",\n      Nil\n    ))\n\n    assert {\n      actual match {\n        case Left(_: BuildException) => true\n        case _                       => sys.error(\"incorrect type\")\n      }\n    }\n    assertEquals(expected.toString, actual.toString)\n  }\n\n  test(\"invalid generic: extra |\") {\n    val actual: Either[BuildException, Vcs] = Vcs.parse(Positioned.none(\"a|b|c|d\"))\n    val expected                            =\n      Left(new MalformedInputError(\"vcs\", \"a|b|c|d\", \"url|connection|developer-connection\", Nil))\n\n    assert {\n      actual match {\n        case Left(_: BuildException) => true\n        case _                       => sys.error(\"incorrect type\")\n      }\n    }\n    assertEquals(expected.toString, actual.toString)\n  }\n\n  test(\"invalid generic: gibberish\") {\n    val actual: Either[BuildException, Vcs] = Vcs.parse(Positioned.none(\"sfrgt pagdhn\"))\n    val expected                            = Left(new MalformedInputError(\n      \"vcs\",\n      \"sfrgt pagdhn\",\n      \"url|connection|developer-connection\",\n      Nil\n    ))\n\n    assert {\n      actual match {\n        case Left(_: BuildException) => true\n        case _                       => sys.error(\"incorrect type\")\n      }\n    }\n    assertEquals(expected.toString, actual.toString)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/ActionableDiagnosticTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.version.Version\n\nimport scala.build.Ops.*\nimport scala.build.Position.File\nimport scala.build.actionable.ActionableDiagnostic.*\nimport scala.build.actionable.ActionablePreprocessor\nimport scala.build.options.{BuildOptions, InternalOptions, SuppressWarningOptions}\nimport scala.build.{BuildThreads, Directories, LocalRepo}\n\nclass ActionableDiagnosticTests extends TestUtil.ScalaCliBuildSuite {\n\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-actionable-diagnostic-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n  val baseOptions              = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger())\n    )\n  )\n  val buildThreads: BuildThreads = BuildThreads.create()\n\n  def path2url(p: os.Path): String = p.toIO.toURI.toURL.toString\n\n  test(\"using outdated os-lib\") {\n    val dependencyOsLib = \"com.lihaoyi::os-lib:0.7.8\"\n    val testInputs      = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using dep $dependencyOsLib\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, None, actionableDiagnostics = true) {\n      (_, _, maybeBuild) =>\n        val build             = maybeBuild.orThrow\n        val updateDiagnostics =\n          ActionablePreprocessor.generateActionableDiagnostics(build.options).orThrow\n\n        val osLibDiagnosticOpt = updateDiagnostics.collectFirst {\n          case diagnostic: ActionableDependencyUpdateDiagnostic => diagnostic\n        }\n\n        expect(osLibDiagnosticOpt.nonEmpty)\n        val osLibDiagnostic = osLibDiagnosticOpt.get\n\n        expect(Version(osLibDiagnostic.newVersion) > Version(osLibDiagnostic.currentVersion))\n    }\n  }\n\n  test(\"actionable diagnostic report correct position\") {\n    val dependencyOsLib     = \"com.lihaoyi::os-lib:0.7.8\"\n    val dependencyPprintLib = \"com.lihaoyi::pprint:0.6.6\"\n    val testInputs          = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using dep $dependencyOsLib\n           |//> using dep $dependencyPprintLib\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, None, actionableDiagnostics = true) {\n      (root, _, maybeBuild) =>\n        val build             = maybeBuild.orThrow\n        val updateDiagnostics =\n          ActionablePreprocessor.generateActionableDiagnostics(build.options).orThrow\n\n        val actionableDiagnostics = updateDiagnostics.collect {\n          case diagnostic: ActionableDependencyUpdateDiagnostic => diagnostic\n        }\n\n        val osLib = actionableDiagnostics.find(_.suggestion.startsWith(\"com.lihaoyi::os-lib\")).get\n        val pprintLib =\n          actionableDiagnostics.find(_.suggestion.startsWith(\"com.lihaoyi::pprint\")).get\n\n        val path = root / \"Foo.scala\"\n        expect(osLib.positions == Seq(File(Right(path), (0, 14), (0, 39))))\n        expect(pprintLib.positions == Seq(File(Right(path), (1, 14), (1, 39))))\n    }\n  }\n\n  test(\"using outdated dependencies with --suppress-outdated-dependency-warning\") {\n    val dependencyOsLib     = \"com.lihaoyi::os-lib:0.7.8\"\n    val dependencyPprintLib = \"com.lihaoyi::pprint:0.6.6\"\n    val testInputs          = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using dep $dependencyOsLib\n           |//> using dep $dependencyPprintLib\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    val optionsWithSuppress = baseOptions.copy(\n      suppressWarningOptions = SuppressWarningOptions(\n        suppressOutdatedDependencyWarning = Some(true)\n      )\n    )\n\n    testInputs.withBuild(optionsWithSuppress, buildThreads, None, actionableDiagnostics = true) {\n      (_, _, maybeBuild) =>\n        val build             = maybeBuild.orThrow\n        val updateDiagnostics =\n          ActionablePreprocessor.generateActionableDiagnostics(build.options).orThrow\n\n        val updateDepsDiagnostics = updateDiagnostics.collect {\n          case diagnostic: ActionableDependencyUpdateDiagnostic => diagnostic\n        }\n\n        expect(updateDepsDiagnostics.isEmpty)\n    }\n  }\n\n  test(\"actionable actions suggest update only to stable version\") {\n    val testInputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using dep test-org::test-name-1:1.0.6\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    // create fake repository which contains hardcoded versions [1.0.6, 1.0.7, 1.0.7-M1] of test-name-1 library\n    // scala-cli should skip non-stable version 1.0.7-M1 and suggest update 1.0.7\n    val repoTmpDir = os.temp.dir(prefix = \"scala-cli-tests-actionable-diagnostic-repo\")\n    os.write(\n      repoTmpDir / \"test-org\" / \"test-name-1_3\" / \"maven-metadata.xml\",\n      \"\"\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        |<metadata>\n        |  <groupId>test-org</groupId>\n        |  <artifactId>test-name-1_3</artifactId>\n        |  <versioning>\n        |    <latest>1.0.7-M1</latest>\n        |    <release>1.0.7-M1</release>\n        |    <versions>\n        |      <version>1.0.6</version>\n        |      <version>1.0.7</version>\n        |      <version>1.0.7-M1</version>\n        |    </versions>\n        |  </versioning>\n        |</metadata>\n        |\"\"\".stripMargin,\n      createFolders = true\n    )\n    os.write(\n      repoTmpDir / \"test-org\" / \"test-name-1_3\" / \"1.0.6\" / \"test-name-1_3-1.0.6.pom\",\n      \"\"\"<?xml version='1.0' encoding='UTF-8'?>\n        |<project>\n        |    <groupId>test-org</groupId>\n        |    <artifactId>test-name-1_3</artifactId>\n        |    <version>1.0.6</version>\n        |</project>\"\"\".stripMargin,\n      createFolders = true\n    )\n    val withRepoBuildOptions = baseOptions.copy(\n      classPathOptions =\n        baseOptions.classPathOptions.copy(extraRepositories = Seq(path2url(repoTmpDir)))\n    )\n    testInputs.withBuild(withRepoBuildOptions, buildThreads, None, actionableDiagnostics = true) {\n      (_, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n\n        val updateDiagnostics =\n          ActionablePreprocessor.generateActionableDiagnostics(build.options).orThrow\n\n        val testLibDiagnosticOpt = updateDiagnostics.collectFirst {\n          case diagnostic: ActionableDependencyUpdateDiagnostic => diagnostic\n        }\n        expect(testLibDiagnosticOpt.nonEmpty)\n        val testLibDiagnostic = testLibDiagnosticOpt.get\n\n        expect(testLibDiagnostic.newVersion == \"1.0.7\")\n    }\n  }\n\n  test(\"actionable actions should not suggest update to previous version\") {\n    val testInputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using dep test-org::test-name-1:2.0.0-M1\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    // create fake repository which contains hardcoded versions [1.0.0] of test-name-1 library\n    val repoTmpDir = os.temp.dir(prefix = \"scala-cli-tests-actionable-diagnostic-repo\")\n    os.write(\n      repoTmpDir / \"test-org\" / \"test-name-1_3\" / \"maven-metadata.xml\",\n      \"\"\"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        |<metadata>\n        |  <groupId>test-org</groupId>\n        |  <artifactId>test-name-1_3</artifactId>\n        |  <versioning>\n        |    <latest>2.0.0-M</latest>\n        |    <release>2.0.0-M1</release>\n        |    <versions>\n        |      <version>1.0.0</version>\n        |      <version>2.0.0-M1</version>\n        |    </versions>\n        |  </versioning>\n        |</metadata>\n        |\"\"\".stripMargin,\n      createFolders = true\n    )\n    os.write(\n      repoTmpDir / \"test-org\" / \"test-name-1_3\" / \"2.0.0-M1\" / \"test-name-1_3-2.0.0-M1.pom\",\n      \"\"\"<?xml version='1.0' encoding='UTF-8'?>\n        |<project>\n        |    <groupId>test-org</groupId>\n        |    <artifactId>test-name-1_3</artifactId>\n        |    <version>2.0.0-M1</version>\n        |</project>\"\"\".stripMargin,\n      createFolders = true\n    )\n    val withRepoBuildOptions = baseOptions.copy(\n      classPathOptions =\n        baseOptions.classPathOptions.copy(extraRepositories =\n          Seq(path2url(repoTmpDir))\n        )\n    )\n    testInputs.withBuild(withRepoBuildOptions, buildThreads, None, actionableDiagnostics = true) {\n      (_, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n\n        val updateDiagnostics =\n          ActionablePreprocessor.generateActionableDiagnostics(build.options).orThrow\n\n        val testLibDiagnosticOpt = updateDiagnostics.collectFirst {\n          case diagnostic: ActionableDependencyUpdateDiagnostic => diagnostic\n        }\n        expect(testLibDiagnosticOpt.isEmpty)\n    }\n  }\n\n  test(\"actionable actions should not suggest update if uses version: latest\") {\n    val testInputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using toolkit latest\n           |\n           |object Hello extends App {\n           |  os.list(os.pwd).foreach(println)\n           |}\n           |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, None, actionableDiagnostics = true) {\n      (_, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n\n        val updateDiagnostics =\n          ActionablePreprocessor.generateActionableDiagnostics(build.options).orThrow\n\n        val testLibDiagnosticOpt = updateDiagnostics.collectFirst {\n          case diagnostic: ActionableDependencyUpdateDiagnostic => diagnostic\n        }\n        expect(testLibDiagnosticOpt.isEmpty)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/BspServerTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.util.concurrent.TimeUnit\n\nimport scala.build.Ops.*\nimport scala.build.bsp.{\n  BspServer,\n  ScalaScriptBuildServer,\n  WrappedSourceItem,\n  WrappedSourcesItem,\n  WrappedSourcesParams,\n  WrappedSourcesResult\n}\nimport scala.build.options.{BuildOptions, InternalOptions, Scope}\nimport scala.build.{Build, BuildThreads, Directories, GeneratedSource, LocalRepo}\nimport scala.collection.mutable.ArrayBuffer\nimport scala.jdk.CollectionConverters.*\n\nclass BspServerTests extends TestUtil.ScalaCliBuildSuite {\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-bsp-server-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n  val baseOptions              = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger())\n    )\n  )\n  val buildThreads: BuildThreads = BuildThreads.create()\n\n  def getScriptBuildServer(\n    generatedSources: Seq[GeneratedSource],\n    workspace: os.Path,\n    scope: Scope = Scope.Main\n  ): ScalaScriptBuildServer = {\n    val bspServer = new BspServer(null, null, null)\n    bspServer.setGeneratedSources(Scope.Main, generatedSources)\n    bspServer.setProjectName(workspace, \"test\", scope)\n\n    bspServer\n  }\n\n  test(\"correct topWrapper and bottomWrapper for wrapped scripts\") {\n    val testInputs = TestInputs(\n      os.rel / \"script.sc\" ->\n        s\"\"\"def msg: String = \"Hello\"\n           |\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n\n    testInputs.withBuild(baseOptions, buildThreads, None) {\n      (root, _, maybeBuild) =>\n        val build: Build = maybeBuild.orThrow\n\n        build match {\n          case success: Build.Successful =>\n            val generatedSources = success.generatedSources\n            expect(generatedSources.size == 1)\n            val wrappedScript     = generatedSources.head\n            val wrappedScriptCode = os.read(wrappedScript.generated)\n            val topWrapper        = wrappedScriptCode\n              .linesIterator\n              .take(wrappedScript.wrapperParamsOpt.map(_.topWrapperLineCount).getOrElse(0))\n              .mkString(\"\", System.lineSeparator(), System.lineSeparator())\n\n            val bspServer = getScriptBuildServer(generatedSources, root)\n\n            val wrappedSourcesResult: WrappedSourcesResult = bspServer\n              .buildTargetWrappedSources(new WrappedSourcesParams(ArrayBuffer.empty.asJava))\n              .get(10, TimeUnit.SECONDS)\n            val wrappedItems = wrappedSourcesResult.getItems.asScala\n\n            expect(wrappedItems.size == 1)\n            expect(wrappedItems.head.getSources().asScala.size == 1)\n\n            expect(wrappedItems.head.getSources().asScala.head.getTopWrapper == topWrapper)\n\n          case _ => munit.Assertions.fail(\"Build Failed\")\n        }\n\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.assert as expect\nimport coursier.Repositories\nimport coursier.cache.FileCache\nimport coursier.maven.MavenRepository\nimport coursier.version.Version\nimport dependency.ScalaParameters\n\nimport scala.build.Ops.*\nimport scala.build.errors.{\n  InvalidBinaryScalaVersionError,\n  NoValidScalaVersionFoundError,\n  ScalaVersionError,\n  UnsupportedScalaVersionError\n}\nimport scala.build.internal.Constants.*\nimport scala.build.internal.Regexes.{scala2NightlyRegex, scala3LtsRegex}\nimport scala.build.options.*\nimport scala.build.tests.util.BloopServer\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo, Positioned, RepositoryUtils}\nimport scala.concurrent.duration.DurationInt\n\nclass BuildOptionsTests extends TestUtil.ScalaCliBuildSuite {\n  override def munitFlakyOK: Boolean = TestUtil.isCI\n  val extraRepoTmpDir: os.Path       = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories       = Directories.under(extraRepoTmpDir)\n  val buildThreads: BuildThreads     = BuildThreads.create()\n  val baseOptions                    = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n  def bloopConfigOpt            = Some(BloopServer.bloopConfig)\n  override def afterAll(): Unit = {\n    buildThreads.shutdown()\n  }\n\n  test(\"Empty BuildOptions is actually empty\") {\n    val empty = BuildOptions()\n    val zero  = BuildOptions.monoid.zero\n    expect(\n      empty == zero,\n      \"Unexpected Option / Seq / Set / Boolean with a non-empty / non-false default value\"\n    )\n  }\n\n  test(\"-S 3.nightly option works\") {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"3.nightly\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scalaParams.scalaVersion.startsWith(\"3\") && scalaParams.scalaVersion.endsWith(\"-NIGHTLY\"),\n      \"-S 3.nightly argument does not lead to scala3 nightly build option\"\n    )\n  }\n  test(\"-S 3.1.nightly option works\") {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"3.1.nightly\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    expect(\n      scalaParams.scalaVersion.startsWith(\"3.1.\") && scalaParams.scalaVersion.endsWith(\"-NIGHTLY\"),\n      \"-S 3.1.nightly argument does not lead to scala 3.1. nightly build option\"\n    )\n  }\n\n  test(s\"Scala 3.${Int.MaxValue} shows Invalid Binary Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(s\"3.${Int.MaxValue}\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: InvalidBinaryScalaVersionError => true; case _ => false\n      },\n      s\"specifying the 3.${Int.MaxValue} scala version does not lead to the Invalid Binary Scala Version Error\"\n    )\n  }\n\n  test(s\"Scala 2.lts shows Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(s\"3.${Int.MaxValue}\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: ScalaVersionError => true; case _ => false\n      },\n      s\"specifying 2.lts scala version does not lead to Scala Version Error\"\n    )\n  }\n\n  test(\"Scala 2.11.2 shows Unupported Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.11.2\"))\n      )\n    )\n\n    assert(\n      options.projectParams.swap.exists {\n        case _: UnsupportedScalaVersionError => true; case _ => false\n      },\n      \"specifying the 2.11.2 scala version does not lead to the Unsupported Scala Version Error\"\n    )\n  }\n\n  test(\"Scala 2.11 shows Unupported Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.11\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: UnsupportedScalaVersionError => true; case _ => false\n      },\n      \"specifying the 2.11 scala version does not lead to the Unsupported Scala Version Error\"\n    )\n  }\n\n  test(s\"Scala 3.${Int.MaxValue}.3 shows Invalid Binary Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(s\"3.${Int.MaxValue}.3\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: InvalidBinaryScalaVersionError => true; case _ => false\n      },\n      \"specifying the 3.2147483647.3 scala version does not lead to the Invalid Binary Scala Version Error\"\n    )\n  }\n\n  test(\"Scala 3.1.3-RC1-bin-20220213-fd97eee-NIGHTLY shows No Valid Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"3.1.3-RC1-bin-20220213-fd97eee-NIGHTLY\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: NoValidScalaVersionFoundError => true; case _ => false\n      },\n      \"specifying the wrong full scala 3 nightly version does not lead to the No Valid Scala Version Found Error\"\n    )\n  }\n\n  test(\"Scala 3.1.2-RC1 works\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"3.1.2-RC1\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scalaParams.scalaVersion == \"3.1.2-RC1\",\n      \"-S 3.1.2-RC1 argument does not lead to 3.1.2-RC1 build option\"\n    )\n  }\n\n  test(\"Scala 2.12.9-bin-1111111 shows No Valid Scala Version Error\".flaky) {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.12.9-bin-1111111\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: NoValidScalaVersionFoundError => true; case _ => false\n      },\n      \"specifying the wrong full scala 2 nightly version does not lead to the No Valid Scala Version Found Error\"\n    )\n  }\n\n  test(s\"Scala 2.${Int.MaxValue} shows Invalid Binary Scala Version Error\") {\n\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(s\"2.${Int.MaxValue}\"))\n      )\n    )\n    assert(\n      options.projectParams.swap.exists {\n        case _: InvalidBinaryScalaVersionError => true; case _ => false\n      },\n      s\"specifying 2.${Int.MaxValue} as Scala version does not lead to Invalid Binary Scala Version Error\"\n    )\n  }\n\n  test(\"-S 2.nightly option works\") {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.nightly\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined,\n      \"-S 2.nightly argument does not lead to scala2 nightly build option\"\n    )\n  }\n\n  test(\"-S 2.13.nightly option works\") {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.13.nightly\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined,\n      \"-S 2.13.nightly argument does not lead to scala2 nightly build option\"\n    )\n  }\n\n  test(\"-S 3.lts option works\") {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"3.lts\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scala3LtsRegex.unapplySeq(scalaParams.scalaVersion).isDefined,\n      \"-S 3.lts argument does not lead to scala3 LTS\"\n    )\n  }\n\n  test(\"-S 2.12.nightly option works\") {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.12.nightly\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined,\n      \"-S 2.12.nightly argument does not lead to scala2 nightly build option\"\n    )\n  }\n\n  test(\"-S 2.13.9-bin-4505094 option works without repo specification\".flaky) {\n    val options = BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"2.13.9-bin-4505094\"))\n      )\n    )\n    val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n    assert(\n      scalaParams.scalaVersion == \"2.13.9-bin-4505094\",\n      \"-S 2.13.9-bin-4505094 argument does not lead to 2.13.9-bin-4505094 scala version in build option\"\n    )\n  }\n\n  test(\"Empty BuildRequirements is actually empty\") {\n    val empty = BuildRequirements()\n    val zero  = BuildRequirements.monoid.zero\n    expect(\n      empty == zero,\n      \"Unexpected Option / Seq / Set / Boolean with a non-empty / non-false default value\"\n    )\n  }\n\n  val expectedScalaVersions: Seq[(Option[String], String)] = Seq(\n    None           -> defaultScalaVersion,\n    Some(\"2.13.2\") -> \"2.13.2\",\n    Some(\"3.0.1\")  -> \"3.0.1\",\n    Some(\"3.0\")    -> \"3.0.2\"\n  )\n\n  for ((prefix, expectedScalaVersion) <- expectedScalaVersions)\n    test(\n      s\"use scala $expectedScalaVersion for prefix scala version: ${prefix.getOrElse(\"empty\")}\"\n    ) {\n      val options = BuildOptions(\n        scalaOptions = ScalaOptions(\n          scalaVersion = prefix.map(MaybeScalaVersion(_))\n        ),\n        internal = InternalOptions(\n          cache = Some(FileCache().withTtl(0.seconds))\n        )\n      )\n      val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n\n      val expectedScalaParams = ScalaParameters(expectedScalaVersion)\n\n      expect(scalaParams == expectedScalaParams)\n    }\n\n  {\n    val cache        = FileCache().withTtl(0.seconds)\n    val repositories = BuildOptions(\n      internal = InternalOptions(cache = Some(cache)),\n      classPathOptions =\n        ClassPathOptions(\n          extraRepositories = Seq(\n            coursier.Repositories.scalaIntegration.root,\n            RepositoryUtils.scala3NightlyRepository.root\n          )\n        )\n    ).finalRepositories.orThrow\n    val allScalaVersions = ScalaVersionUtil.allMatchingVersions(None, cache, repositories)\n    for {\n      (prefix, defaultMatchingVersion, predefinedDefaultScalaVersion) <- {\n        extension (nightlies: Seq[String])\n          private def latestNightly: Option[String] =\n            if nightlies.nonEmpty then Some(nightlies.maxBy(Version(_))) else None\n        val scala2Nightlies     = allScalaVersions.filter(ScalaVersionUtil.isScala2Nightly)\n        val scala212Nightlies   = scala2Nightlies.filter(_.startsWith(\"2.12\"))\n        val scala213Nightlies   = scala2Nightlies.filter(_.startsWith(\"2.13\"))\n        val scala3Nightlies     = allScalaVersions.filter(ScalaVersionUtil.isScala3Nightly)\n        val scala3NextNightlies = scala3Nightlies.filter(_.startsWith(scala3NextPrefix))\n        Seq(\n          (\"2.12\", defaultScala212Version, None),\n          (\"2.12\", defaultScala212Version, scala212Nightlies.latestNightly),\n          (\"2.13\", defaultScala213Version, None),\n          (\"2.13\", defaultScala213Version, scala213Nightlies.latestNightly),\n          (\"3\", defaultScalaVersion, None),\n          (scala3NextPrefix, defaultScalaVersion, None),\n          (scala3NextPrefix, defaultScalaVersion, scala3NextNightlies.latestNightly)\n        ).distinct\n      }\n      options = BuildOptions(\n        scalaOptions = ScalaOptions(\n          scalaVersion = Some(prefix).map(MaybeScalaVersion(_)),\n          defaultScalaVersion = predefinedDefaultScalaVersion\n        ),\n        internal = InternalOptions(\n          cache = Some(cache)\n        ),\n        classPathOptions = ClassPathOptions(\n          extraRepositories = Seq(coursier.Repositories.scalaIntegration.root)\n        )\n      )\n      latestMatchingVersion = allScalaVersions\n        .filter(ScalaVersionUtil.isStable)\n        .filter(_.startsWith(prefix))\n        .maxBy(Version(_))\n      expectedVersion            = predefinedDefaultScalaVersion.getOrElse(defaultMatchingVersion)\n      expectedVersionDescription =\n        if expectedVersion == defaultMatchingVersion then \"default\" else \"overridden default\"\n      launcherDefaultVersionDescription = if expectedVersion == defaultMatchingVersion then \"\"\n      else s\"or the launcher default ($defaultMatchingVersion)\"\n      testDescription =\n        s\"-S $prefix should choose the $expectedVersionDescription version ($expectedVersion), not necessarily the latest stable ($latestMatchingVersion) $launcherDefaultVersionDescription\"\n    } test(testDescription) {\n      val scalaParams = options.scalaParams.orThrow.getOrElse(sys.error(\"should not happen\"))\n\n      val expectedScalaParams = ScalaParameters(expectedVersion)\n\n      expect(scalaParams == expectedScalaParams, s\"expected $expectedScalaParams, got $scalaParams\")\n    }\n  }\n\n  test(\"User scalac options shadow internal ones\") {\n    val defaultOptions = BuildOptions(\n      internal = InternalOptions(\n        localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger())\n      )\n    )\n\n    val newSourceRoot = os.pwd / \"out\" / \"foo\"\n\n    val extraScalacOpt = Seq(\"-sourceroot\", newSourceRoot.toString)\n    val options        = defaultOptions.copy(\n      scalaOptions = defaultOptions.scalaOptions.copy(\n        scalaVersion = Some(MaybeScalaVersion(\"3.1.1\")),\n        scalacOptions = ShadowingSeq.from(\n          extraScalacOpt\n            .map(ScalacOpt(_))\n            .map(Positioned.none)\n        )\n      )\n    )\n\n    val dummyInputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"object Foo\n          |\"\"\".stripMargin\n    )\n\n    dummyInputs.withLoadedBuild(options, buildThreads, None) {\n      (_, _, build) =>\n\n        val build0 = build match {\n          case s: Build.Successful => s\n          case _                   => sys.error(s\"Unexpected failed or cancelled build $build\")\n        }\n\n        val rawOptions = build0.project.scalaCompiler.toSeq.flatMap(_.scalacOptions)\n        val seq        = ShadowingSeq.from(rawOptions.map(ScalacOpt(_)))\n\n        expect(seq.toSeq.length == rawOptions.length) // no option needs to be shadowed\n\n        pprint.err.log(rawOptions)\n        expect(rawOptions.containsSlice(extraScalacOpt))\n    }\n  }\n\n  test(\"parse snapshots repository\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"//> using repository snapshots\n          |//> using repository central\n          |object Foo extends App {\n          |  println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(BuildOptions(), buildThreads, bloopConfigOpt, buildTests = false) {\n      (_, _, maybeBuild) =>\n        expect(maybeBuild.exists(_.success))\n        val build = maybeBuild\n          .toOption\n          .flatMap(_.successfulOpt)\n          .getOrElse(sys.error(\"cannot happen\"))\n        val repositories = build.options.finalRepositories.orThrow\n\n        expect(repositories.length == 3)\n        expect(repositories.contains(Repositories.central))\n        expect(repositories.contains(RepositoryUtils.snapshotsRepository))\n        expect(repositories.contains(RepositoryUtils.scala3NightlyRepository))\n    }\n  }\n\n  test(\"resolve semanticDB for older scala version\") {\n    val buildOptions = BuildOptions()\n    val scalaVersion = \"2.13.3\"\n\n    val semanticDbVersion = buildOptions.findSemanticDbVersion(scalaVersion, TestLogger())\n    expect(semanticDbVersion == \"4.8.4\")\n  }\n\n  test(\"skip setting release option when -release or -java-output-version is set by user\") {\n    val javaOutputVersionOpt =\n      s\"-java-output-version:${scala.build.internal.Constants.scala38MinJavaVersion}\"\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"//> using option $javaOutputVersionOpt\n           |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(BuildOptions(), buildThreads, bloopConfigOpt, buildTests = false) {\n      (_, _, maybeBuild) =>\n        expect(maybeBuild.exists(_.success))\n        val build = maybeBuild\n          .toOption\n          .flatMap(_.successfulOpt)\n          .getOrElse(sys.error(\"cannot happen\"))\n\n        val scalacOpts = build.project.scalaCompiler.toSeq.flatMap(_.scalacOptions)\n\n        expect(scalacOpts.contains(javaOutputVersionOpt))\n        expect(!scalacOpts.exists(_.startsWith(\"-release\")))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/BuildProjectTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleLogger\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.cache.CacheLogger\nimport org.scalajs.logging.{Logger as ScalaJsLogger, NullLogger}\n\nimport java.io.PrintStream\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, Diagnostic}\nimport scala.build.input.Inputs\nimport scala.build.internals.FeatureType\nimport scala.build.options.{BuildOptions, InternalOptions, Scope}\nimport scala.build.{Build, LocalRepo, Logger, Sources}\n\nclass BuildProjectTests extends TestUtil.ScalaCliBuildSuite {\n\n  class LoggerMock extends Logger {\n    var diagnostics: List[Diagnostic]                      = Nil\n    override def error(message: String): Unit              = sys.error(message)\n    override def message(message: => String): Unit         = System.err.println(message)\n    override def log(s: => String): Unit                   = System.err.println(s)\n    override def log(s: => String, debug: => String): Unit = System.err.println(s)\n    override def debug(s: => String): Unit                 = System.err.println(s)\n    override def log(diagnostics: Seq[Diagnostic]): Unit   = {\n      this.diagnostics = this.diagnostics ++ diagnostics\n    }\n    override def log(ex: BuildException): Unit = {\n      ex.printStackTrace()\n      System.err.println(ex.message)\n    }\n    override def debug(ex: BuildException): Unit = {\n      ex.printStackTrace()\n      System.err.println(ex.message)\n    }\n    override def exit(ex: BuildException): Nothing = {\n      ex.printStackTrace()\n      System.err.println(ex.message)\n      sys.exit(1)\n    }\n    override def coursierLogger(message: String): CacheLogger          = CacheLogger.nop\n    override def bloopRifleLogger: BloopRifleLogger                    = BloopRifleLogger.nop\n    override def scalaJsLogger: ScalaJsLogger                          = NullLogger\n    override def scalaNativeTestLogger: scala.scalanative.build.Logger =\n      scala.scalanative.build.Logger.nullLogger\n    override def scalaNativeCliInternalLoggerOptions: List[String] =\n      List()\n    override def compilerOutputStream: PrintStream = System.out\n    override def verbosity: Int                    = 0\n    override def experimentalWarning(featureName: String, featureType: FeatureType): Unit =\n      System.err.println(s\"experimental: $featureName\")\n    override def flushExperimentalWarnings: Unit = ()\n  }\n\n  test(\"workspace for bsp\") {\n    val options = BuildOptions(\n      internal = InternalOptions(localRepository =\n        LocalRepo.localRepo(scala.build.Directories.default().localRepoDir, TestLogger())\n      )\n    )\n    val inputs    = Inputs.empty(\"project\")\n    val sources   = Sources(Nil, Nil, None, Nil, options)\n    val logger    = new LoggerMock()\n    val artifacts = options.artifacts(logger, Scope.Test).orThrow\n\n    val project =\n      Build.buildProject(inputs, sources, Nil, options, Scope.Main, logger, artifacts).orThrow\n\n    expect(project.workspace == inputs.workspace)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/BuildTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.config.Config.LinkerMode\nimport bloop.rifle.BloopRifleConfig\nimport ch.epfl.scala.bsp4j\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.google.gson.Gson\nimport dependency.parser.DependencyParser\n\nimport java.io.IOException\n\nimport scala.build.Ops.*\nimport scala.build.errors.{\n  DependencyFormatError,\n  InvalidBinaryScalaVersionError,\n  ScalaNativeCompatibilityError\n}\nimport scala.build.options.*\nimport scala.build.tastylib.TastyData\nimport scala.build.tests.TestUtil.*\nimport scala.build.tests.util.BloopServer\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo, Positioned}\nimport scala.jdk.CollectionConverters.*\nimport scala.meta.internal.semanticdb.TextDocuments\nimport scala.util.Properties\n\nabstract class BuildTests(server: Boolean) extends TestUtil.ScalaCliBuildSuite {\n  private def hasDiagnostics = server\n\n  val buildThreads: BuildThreads               = BuildThreads.create()\n  def bloopConfigOpt: Option[BloopRifleConfig] =\n    if server then Some(BloopServer.bloopConfig) else None\n\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n\n  override def afterAll(): Unit = {\n    TestInputs.tryRemoveAll(extraRepoTmpDir)\n    buildThreads.shutdown()\n  }\n\n  val baseOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  def sv2: String                  = Constants.defaultScala213Version\n  val defaultOptions: BuildOptions = baseOptions.copy(\n    scalaOptions = baseOptions.scalaOptions.copy(\n      scalaVersion = Some(MaybeScalaVersion(sv2)),\n      scalaBinaryVersion = None\n    ),\n    scriptOptions = ScriptOptions(Some(true))\n  )\n\n  def sv3: String                        = Constants.defaultScalaVersion\n  val defaultScala3Options: BuildOptions = defaultOptions.copy(\n    scalaOptions = defaultOptions.scalaOptions.copy(\n      scalaVersion = Some(MaybeScalaVersion(sv3)),\n      scalaBinaryVersion = None\n    ),\n    scriptOptions = ScriptOptions(None)\n  )\n\n  def simple(checkResults: Boolean = true): Unit = {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      if (checkResults)\n        maybeBuild.orThrow.assertGeneratedEquals(\n          \"simple.class\",\n          \"simple$.class\",\n          \"simple$delayedInit$body.class\"\n        )\n    }\n  }\n\n  try simple(checkResults = false)\n  catch {\n    case _: IOException => // ignored\n  }\n\n  test(\"simple\") {\n    simple()\n  }\n\n  test(\"scala 3\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultScala3Options, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        maybeBuild.orThrow.assertGeneratedEquals(\n          \"simple$_.class\",\n          \"simple_sc.class\",\n          \"simple_sc.tasty\",\n          \"simple$_.tasty\",\n          \"simple_sc$.class\",\n          \"simple$package$.class\",\n          \"simple$package.class\",\n          \"simple$package.tasty\"\n        )\n        maybeBuild.orThrow.assertNoDiagnostics()\n    }\n  }\n\n  // Test if we do not print any warnings\n  test(\"scala 3 class in .sc file\") {\n    val testInputs = TestInputs(\n      os.rel / \"other.sc\" ->\n        \"class A\"\n    )\n    testInputs.withBuild(defaultScala3Options, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n        build.assertGeneratedEquals(\n          \"other$_$A.class\",\n          \"other$_.tasty\",\n          \"other$_.class\",\n          \"other_sc$.class\",\n          \"other_sc.class\",\n          \"other_sc.tasty\",\n          \"other$package$.class\",\n          \"other$package.class\",\n          \"other$package.tasty\"\n        )\n        maybeBuild.orThrow.assertNoDiagnostics()\n    }\n  }\n\n  test(\"semantic DB\") {\n    import scala.meta.internal.semanticdb.*\n\n    val scriptContents =\n      \"\"\"val n = 2\n        |println(s\"n=$n\")\n        |\"\"\".stripMargin\n\n    val expectedSymbolOccurences = Seq(\n      SymbolOccurrence(\n        Some(Range(0, 4, 0, 5)),\n        \"_empty_/simple.n.\",\n        SymbolOccurrence.Role.DEFINITION\n      ),\n      SymbolOccurrence(\n        Some(Range(1, 8, 1, 9)),\n        \"scala/StringContext#s().\",\n        SymbolOccurrence.Role.REFERENCE\n      ),\n      SymbolOccurrence(\n        Some(Range(1, 0, 1, 7)),\n        \"scala/Predef.println(+1).\",\n        SymbolOccurrence.Role.REFERENCE\n      ),\n      SymbolOccurrence(\n        Some(Range(1, 13, 1, 14)),\n        \"_empty_/simple.n.\",\n        SymbolOccurrence.Role.REFERENCE\n      )\n    )\n\n    val testInputs   = TestInputs(os.rel / \"simple.sc\" -> scriptContents)\n    val buildOptions = defaultOptions.copy(\n      scalaOptions = defaultOptions.scalaOptions.copy(\n        semanticDbOptions = defaultOptions.scalaOptions.semanticDbOptions.copy(\n          generateSemanticDbs = Some(true)\n        )\n      )\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val build = maybeBuild.orThrow\n      build.assertGeneratedEquals(\n        \"simple$delayedInit$body.class\",\n        \"simple$.class\",\n        \"simple.class\",\n        \"META-INF/semanticdb/simple.sc.semanticdb\"\n      )\n      maybeBuild.orThrow.assertNoDiagnostics()\n\n      val outputDir = build.outputOpt.getOrElse(sys.error(\"no build output???\"))\n      val semDb     = os.read.bytes(outputDir / \"META-INF\" / \"semanticdb\" / \"simple.sc.semanticdb\")\n      val doc       = TextDocuments.parseFrom(semDb)\n      val uris      = doc.documents.map(_.uri)\n      expect(uris == Seq(\"simple.sc\"))\n\n      val occurences = doc.documents.flatMap(_.occurrences)\n      expect(occurences.forall(_.range.isDefined))\n\n      val sortedOccurences = doc.documents.flatMap(_.occurrences)\n        .sortBy(s =>\n          s.range.map(r => (r.startLine, r.startCharacter)).getOrElse((Int.MaxValue, Int.MaxValue))\n        )\n      val sortedExpectedOccurences = expectedSymbolOccurences\n        .sortBy(s =>\n          s.range.map(r => (r.startLine, r.startCharacter)).getOrElse((Int.MaxValue, Int.MaxValue))\n        )\n\n      munit.Assertions.assert(\n        sortedOccurences == sortedExpectedOccurences,\n        clue = doc.documents.flatMap(_.occurrences)\n      )\n    }\n  }\n\n  test(\"TASTy\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |\"\"\".stripMargin\n    )\n    val buildOptions = defaultScala3Options.copy(\n      scalaOptions = defaultScala3Options.scalaOptions.copy(\n        semanticDbOptions = defaultScala3Options.scalaOptions.semanticDbOptions.copy(\n          generateSemanticDbs = Some(true)\n        )\n      )\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val build = maybeBuild.orThrow\n      build.assertGeneratedEquals(\n        \"simple$_.class\",\n        \"simple_sc.class\",\n        \"simple_sc.tasty\",\n        \"simple$_.tasty\",\n        \"simple_sc$.class\",\n        \"simple$package$.class\",\n        \"simple$package.class\",\n        \"simple$package.tasty\",\n        \"META-INF/semanticdb/simple.sc.semanticdb\"\n      )\n      maybeBuild.orThrow.assertNoDiagnostics()\n      val outputDir = build.outputOpt.getOrElse(sys.error(\"no build output???\"))\n      val tastyData = TastyData.read(os.read.bytes(outputDir / \"simple$_.tasty\")).orThrow\n      val names     = tastyData.names.simpleNames\n      expect(names.contains(\"simple.sc\"))\n    }\n  }\n\n  test(\"simple JS\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions.enableJs, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        maybeBuild.orThrow.assertGeneratedEquals(\n          \"simple$.class\",\n          \"simple$.sjsir\",\n          \"simple$delayedInit$body.class\",\n          \"simple$delayedInit$body.sjsir\",\n          \"simple.class\",\n          \"simple.sjsir\"\n        )\n        maybeBuild.orThrow.assertNoDiagnostics()\n    }\n  }\n\n  def simpleNativeTest(): Unit = {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions.enableNative, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        maybeBuild.orThrow.assertGeneratedEquals(\n          \"simple$.class\",\n          \"simple$.nir\",\n          \"simple$delayedInit$body.class\",\n          \"simple$delayedInit$body.nir\",\n          \"simple.class\",\n          \"simple.nir\"\n        )\n        maybeBuild.orThrow.assertNoDiagnostics()\n    }\n  }\n  if (!Properties.isWin)\n    test(\"simple native\") {\n      simpleNativeTest()\n    }\n\n  test(\"dependencies - using\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::geny:0.6.5\n          |import geny.Generator\n          |val g = Generator(\"Hel\", \"lo\")\n          |println(g.mkString)\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      maybeBuild.orThrow.assertGeneratedEquals(\n        \"simple.class\",\n        \"simple$.class\",\n        \"simple$delayedInit$body.class\"\n      )\n      maybeBuild.orThrow.assertNoDiagnostics()\n    }\n  }\n\n  test(\"several dependencies - using\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::geny:0.6.5\n          |//> using dep com.lihaoyi::pprint:0.6.6\n          |import geny.Generator\n          |val g = Generator(\"Hel\", \"lo\")\n          |pprint.log(g)\n          |\"\"\".stripMargin,\n      os.rel / \"simple2.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::geny:0.6.5 com.lihaoyi::pprint:0.6.6\n          |import geny.Generator\n          |val g = Generator(\"Hel\", \"lo\")\n          |pprint.log(g)\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      maybeBuild.orThrow.assertGeneratedEquals(\n        \"simple$.class\",\n        \"simple$delayedInit$body.class\",\n        \"simple.class\",\n        \"simple2$.class\",\n        \"simple2$delayedInit$body.class\",\n        \"simple2.class\"\n      )\n      maybeBuild.orThrow.assertNoDiagnostics()\n    }\n  }\n\n  if (hasDiagnostics)\n    test(\"diagnostics\") {\n      diagnosticsTest()\n    }\n  def diagnosticsTest(): Unit = {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |zz\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (root, _, maybeBuild) =>\n      val expectedDiag = {\n        val start = new bsp4j.Position(2, 0)\n        val end   = new bsp4j.Position(2, 2)\n        val range = new bsp4j.Range(start, end)\n        val d     = new bsp4j.Diagnostic(range, \"not found: value zz\")\n        d.setSource(\"bloop\")\n        d.setSeverity(bsp4j.DiagnosticSeverity.ERROR)\n        val bScalaDiagnostic = new bsp4j.ScalaDiagnostic\n        bScalaDiagnostic.setActions(List().asJava)\n        d.setData(new Gson().toJsonTree(bScalaDiagnostic))\n        d\n      }\n      val diagnostics = maybeBuild.orThrow.diagnostics\n      val expected    = Some(Seq(Right(root / \"simple.sc\") -> expectedDiag))\n      expect(diagnostics == expected)\n    }\n  }\n\n  if (hasDiagnostics)\n    test(\"diagnostics Scala 3\") {\n      scala3DiagnosticsTest()\n    }\n  def scala3DiagnosticsTest(): Unit = {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"val n = 2\n          |println(s\"n=$n\")\n          |zz\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultScala3Options, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        val expectedDiag = {\n          val start = new bsp4j.Position(2, 0)\n          val end   = new bsp4j.Position(2, 2)\n          val range = new bsp4j.Range(start, end)\n          val d     = new bsp4j.Diagnostic(range, \"Not found: zz\")\n          d.setSource(\"bloop\")\n          d.setCode(\"6\")\n          d.setSeverity(bsp4j.DiagnosticSeverity.ERROR)\n          val bScalaDiagnostic = new bsp4j.ScalaDiagnostic\n          bScalaDiagnostic.setActions(List().asJava)\n          d.setData(new Gson().toJsonTree(bScalaDiagnostic))\n          d\n        }\n        val diagnostics = maybeBuild.orThrow.diagnostics\n        val expected    = Some(Seq(Right(root / \"simple.sc\") -> expectedDiag))\n        expect(diagnostics == expected)\n    }\n  }\n\n  test(\"ignore files if wrong Scala version requirement\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        \"\"\"object Simple {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Ignored.scala\" ->\n        \"\"\"//> using target.scala.== 2.12\n          |object Ignored {\n          |  def foo = 2\n          |}\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      maybeBuild.orThrow.assertGeneratedEquals(\n        \"Simple.class\",\n        \"Simple$.class\"\n      )\n    }\n  }\n  test(\"ignore files if wrong Scala target requirement\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        \"\"\"object Simple {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Ignored.scala\" ->\n        \"\"\"//> using target.platform scala.js\n          |object Ignored {\n          |  def foo = 2\n          |}\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      maybeBuild.orThrow.assertGeneratedEquals(\n        \"Simple.class\",\n        \"Simple$.class\"\n      )\n    }\n  }\n\n  test(\"ignore files if wrong Scala target requirement - JS\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        \"\"\"object Simple {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Ignored.scala\" ->\n        \"\"\"//> using target.platform jvm\n          |object Ignored {\n          |  def foo = 2\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"IgnoredToo.scala\" ->\n        \"\"\"//> using target.platform native\n          |object IgnoredToo {\n          |  def foo = 2\n          |}\n          |\"\"\".stripMargin\n    )\n    val options = defaultOptions.enableJs\n    testInputs.withBuild(options, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      maybeBuild.orThrow.assertGeneratedEquals(\n        \"Simple.class\",\n        \"Simple$.class\",\n        \"Simple.sjsir\",\n        \"Simple$.sjsir\"\n      )\n    }\n  }\n\n  test(\"Pass files with only commented directives as is to scalac\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::pprint:0.6.6\n          |object Simple {\n          |  def main(args: Array[String]): Unit =\n          |    pprint.log(\"Hello \" + \"from tests\")\n          |}\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val sources = maybeBuild.toOption.get.successfulOpt.get.sources\n      expect(sources.inMemory.isEmpty)\n      expect(sources.paths.lengthCompare(1) == 0)\n    }\n  }\n\n  test(\"Compiler plugins from using directives\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using scala 2.13\n          |//> using plugins com.olegpy::better-monadic-for:0.3.1\n          |\n          |def getCounts: Either[String, (Int, Int)] = ???\n          |\n          |for {\n          |  (x, y) <- getCounts\n          |} yield x + y\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      assert(clue(maybeBuild.orThrow.diagnostics).toSeq.flatten.isEmpty)\n    }\n  }\n\n  test(\"Scala Native working with Scala 3.1\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        \"\"\"//> using platform scala-native\n          |//> using nativeVersion 0.4.3-RC2\n          |//> using scala 3.1.0\n          |def foo(): String = \"foo\"\n          |\"\"\".stripMargin\n    )\n    val buildOptions = defaultOptions.copy(\n      scalaOptions = defaultOptions.scalaOptions.copy(\n        scalaVersion = None\n      )\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      assert(maybeBuild.isRight)\n    }\n  }\n\n  test(\"Scala Native not working with Scala 3.0\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        \"\"\"//> using platform scala-native\n          |//> using nativeVersion 0.4.3-RC2\n          |//> using scala 3.0.2\n          |def foo(): String = \"foo\"\n          |\"\"\".stripMargin\n    )\n    val buildOptions = defaultOptions.copy(\n      scalaOptions = defaultOptions.scalaOptions.copy(\n        scalaVersion = None\n      )\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      assert(maybeBuild.isLeft)\n      assert(\n        maybeBuild.swap.toOption.exists {\n          case _: ScalaNativeCompatibilityError => true\n          case _                                => false\n        }\n      )\n    }\n  }\n\n  test(s\"Scala 3.${Int.MaxValue}.3 makes the build fail with InvalidBinaryScalaVersionError\") {\n    val testInputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        s\"\"\" // using scala \"3.${Int.MaxValue}.3\"\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(\"Hello\")\n           |}\n           |\n           |\"\"\".stripMargin\n    )\n    val buildOptions = baseOptions.copy(\n      scalaOptions = baseOptions.scalaOptions.copy(\n        scalaVersion = Some(MaybeScalaVersion(s\"3.${Int.MaxValue}.3\")),\n        scalaBinaryVersion = None\n      )\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.swap.exists { case _: InvalidBinaryScalaVersionError => true; case _ => false },\n        s\"specifying Scala 3.${Int.MaxValue}.3 as version does not lead to InvalidBinaryScalaVersionError\"\n      )\n    }\n  }\n\n  test(\"cli dependency options shadowing using directives\") {\n    val usingDependency = \"org.scalameta::munit::1.0.0-M1\"\n    val cliDependency   = \"org.scalameta::munit::1.1.1\"\n\n    val inputs = TestInputs(\n      os.rel / \"foo.scala\" ->\n        s\"\"\"//> using dep $usingDependency\n           |def foo = \"bar\"\n           |\"\"\".stripMargin\n    )\n\n    val parsedCliDependency = DependencyParser.parse(cliDependency).getOrElse(\n      throw new DependencyFormatError(cliDependency, \"\", Nil)\n    )\n\n    // Emulates options derived from cli\n    val buildOptions = defaultOptions.copy(\n      classPathOptions = defaultOptions.classPathOptions.copy(\n        extraDependencies = ShadowingSeq.from(Seq(Positioned.none(parsedCliDependency)))\n      )\n    )\n\n    inputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      assert(maybeBuild.isRight)\n      val build     = maybeBuild.toOption.get\n      val artifacts = build.options.classPathOptions.extraDependencies.toSeq\n      assert(artifacts.exists(_.value.toString() == cliDependency))\n    }\n  }\n\n  test(\"cli scalac options shadowing using directives\") {\n    val cliScalacOptions =\n      Seq(\"-Xmaxwarns\", \"4\", \"-g:source\", \"-language:no2AutoTupling\", \"-language\", \"no2AutoTupling\")\n    val usingDirectiveScalacOptions = Seq(\n      \"-nobootcp\",\n      \"-Xmaxwarns\",\n      \"5\",\n      \"-g:none\",\n      \"-language:no2AutoTupling\",\n      \"-language:strictEquality\"\n    )\n\n    val expectedOptions = Seq(\n      \"-Xmaxwarns\",\n      \"4\",\n      \"-g:source\",\n      \"-language:no2AutoTupling\",\n      \"-nobootcp\",\n      \"-language:strictEquality\"\n    )\n\n    val inputs = TestInputs(\n      os.rel / \"foo.scala\" ->\n        s\"\"\"//> using options ${usingDirectiveScalacOptions.mkString(\" \")}\n           |def foo = \"bar\"\n           |\"\"\".stripMargin\n    )\n\n    // Emulates options derived from cli\n    val buildOptions = defaultOptions.copy(\n      scalaOptions = defaultOptions.scalaOptions.copy(\n        scalacOptions = ShadowingSeq.from(\n          cliScalacOptions.map(ScalacOpt(_)).map(Positioned.commandLine)\n        )\n      )\n    )\n\n    inputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      assert(maybeBuild.isRight)\n      val build         = maybeBuild.toOption.get\n      val scalacOptions = build.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)\n      assertEquals(scalacOptions, expectedOptions)\n    }\n  }\n\n  test(\"cli java options shadowing using directives\") {\n    val cliJavaOptions            = Seq(\"-proc:only\", \"-JflagB\", \"-Xmx2G\")\n    val usingDirectiveJavaOptions = Seq(\"-proc:none\", \"-parameters\", \"-JflagA\", \"-Xmx4G\")\n\n    val expectedJavaOptions =\n      Seq(\"-proc:only\", \"-JflagB\", \"-Xmx2G\", \"-parameters\", \"-JflagA\")\n\n    val inputs = TestInputs(\n      os.rel / \"foo.scala\" ->\n        s\"\"\"//> using javaOpt ${usingDirectiveJavaOptions.mkString(\" \")}\n           |def foo = \"bar\"\n           |\"\"\".stripMargin\n    )\n\n    // Emulates options derived from cli\n    val buildOptions = defaultOptions.copy(\n      javaOptions = defaultOptions.javaOptions.copy(\n        javaOpts = ShadowingSeq.from(\n          cliJavaOptions.map(JavaOpt(_)).map(Positioned.commandLine)\n        )\n      )\n    )\n\n    inputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val build       = maybeBuild.orThrow\n      val javaOptions = build.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n      assert(javaOptions == expectedJavaOptions)\n    }\n  }\n\n  test(\"repeated Java options\") {\n    val inputs = TestInputs(\n      os.rel / \"foo.sc\" ->\n        \"\"\"//> using javaOpt --add-opens foo/bar\n          |//> using javaOpt --add-opens other/thing\n          |//> using javaOpt --add-exports foo/bar\n          |//> using javaOpt --add-exports other/thing\n          |//> using javaOpt --add-modules foo/bar\n          |//> using javaOpt --add-modules other/thing\n          |//> using javaOpt --add-reads foo/bar\n          |//> using javaOpt --add-reads other/thing\n          |//> using javaOpt --patch-module foo/bar\n          |//> using javaOpt --patch-module other/thing\n          |\n          |def foo = \"bar\"\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val expectedOptions =\n        // format: off\n        Seq(\n          \"--add-opens\", \"foo/bar\",\n          \"--add-opens\", \"other/thing\",\n          \"--add-exports\", \"foo/bar\",\n          \"--add-exports\", \"other/thing\",\n          \"--add-modules\", \"foo/bar\",\n          \"--add-modules\", \"other/thing\",\n          \"--add-reads\", \"foo/bar\",\n          \"--add-reads\", \"other/thing\",\n          \"--patch-module\", \"foo/bar\",\n          \"--patch-module\", \"other/thing\"\n        )\n        // format: on\n      val javaOptions =\n        maybeBuild.toOption.get.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n      expect(javaOptions == expectedOptions)\n    }\n  }\n\n  // Issue #607\n  test(\"-source:future not internally duplicating\") {\n    val inputs = TestInputs(\n      os.rel / \"foo.scala\" ->\n        \"\"\"//> using option -source:future\n          |def foo = \"bar\"\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val expectedOptions = Seq(\"-source:future\")\n      val scalacOptions   =\n        maybeBuild.orThrow.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)\n      expect(scalacOptions == expectedOptions)\n    }\n\n  }\n\n  // Issue #525\n  test(\"scalac options not spuriously duplicating\") {\n    val inputs = TestInputs(\n      os.rel / \"foo.scala\" ->\n        \"\"\"//> using scala 2.13\n          |//> using options -deprecation -feature -Xmaxwarns 1\n          |//> using option -Xdisable-assertions\n          |\n          |def foo = \"bar\"\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val expectedOptions =\n        Seq(\"-deprecation\", \"-feature\", \"-Xmaxwarns\", \"1\", \"-Xdisable-assertions\")\n      val scalacOptions =\n        maybeBuild.toOption.get.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)\n      expect(scalacOptions == expectedOptions)\n    }\n  }\n\n  test(\"multiple times scalac options with -Xplugin prefix\") {\n    val inputs = TestInputs(\n      os.rel / \"foo.scala\" ->\n        \"\"\"//> using option -Xplugin:/paradise_2.12.15-2.1.1.jar\n          |//> using option -Xplugin:/semanticdb-scalac_2.12.15-4.4.31.jar\n          |\n          |def foo = \"bar\"\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(defaultOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      val expectedOptions =\n        Seq(\n          \"-Xplugin:/paradise_2.12.15-2.1.1.jar\",\n          \"-Xplugin:/semanticdb-scalac_2.12.15-4.4.31.jar\"\n        )\n      val scalacOptions =\n        maybeBuild.toOption.get.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)\n      expect(scalacOptions == expectedOptions)\n    }\n  }\n\n  test(\"Pin Scala 2 artifacts version\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"//> using dep com.lihaoyi:ammonite_2.13.8:2.5.1-6-5fce97fb\n          |//> using scala 2.13.5\n          |\n          |object Foo {\n          |  def main(args: Array[String]): Unit = {\n          |    println(scala.util.Properties.versionNumberString)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      expect(maybeBuild.exists(_.success))\n      val build = maybeBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error(\"cannot happen\"))\n      val cp    = build.artifacts.classPath.map(_.last)\n\n      val scalaLibraryJarNameOpt =\n        cp.find(n => n.startsWith(\"scala-library-\") && n.endsWith(\".jar\"))\n      val scalaCompilerJarNameOpt =\n        cp.find(n => n.startsWith(\"scala-compiler-\") && n.endsWith(\".jar\"))\n      val scalaReflectJarNameOpt =\n        cp.find(n => n.startsWith(\"scala-reflect-\") && n.endsWith(\".jar\"))\n      expect(scalaLibraryJarNameOpt.contains(\"scala-library-2.13.5.jar\"))\n      expect(scalaCompilerJarNameOpt.contains(\"scala-compiler-2.13.5.jar\"))\n      expect(scalaReflectJarNameOpt.contains(\"scala-reflect-2.13.5.jar\"))\n    }\n  }\n\n  test(\"Pin Scala 3 artifacts version\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"//> using dep com.lihaoyi:ammonite_3.1.1:2.5.1-6-5fce97fb\n          |//> using scala 3.1.0\n          |\n          |object Foo {\n          |  def main(args: Array[String]): Unit = {\n          |    println(scala.util.Properties.versionNumberString)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n      expect(maybeBuild.exists(_.success))\n      val build = maybeBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error(\"cannot happen\"))\n      val cp    = build.artifacts.classPath.map(_.last)\n\n      val scalaLibraryJarNameOpt =\n        cp.find(n => n.startsWith(\"scala3-library_3-\") && n.endsWith(\".jar\"))\n      val scalaCompilerJarNameOpt =\n        cp.find(n => n.startsWith(\"scala3-compiler_3-\") && n.endsWith(\".jar\"))\n      expect(scalaLibraryJarNameOpt.contains(\"scala3-library_3-3.1.0.jar\"))\n      expect(scalaCompilerJarNameOpt.contains(\"scala3-compiler_3-3.1.0.jar\"))\n    }\n  }\n\n  test(\"Pure Java\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.java\" ->\n        \"\"\"package foo;\n          |\n          |public class Foo {\n          |  public static void main(String[] args) {\n          |    System.out.println(\"Hello\");\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt, buildTests = false) {\n      (_, _, maybeBuild) =>\n        expect(maybeBuild.exists(_.success))\n        val build = maybeBuild\n          .toOption\n          .flatMap(_.successfulOpt)\n          .getOrElse(sys.error(\"cannot happen\"))\n        val cp = build.fullClassPath\n        expect(cp.length == 1) // no scala-library, only the class directory\n    }\n  }\n\n  test(\"No stubs JAR at runtime\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"package foo\n          |\n          |object Foo {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt, buildTests = false) {\n      (_, _, maybeBuild) =>\n        expect(maybeBuild.exists(_.success))\n        val build = maybeBuild\n          .toOption\n          .flatMap(_.successfulOpt)\n          .getOrElse(sys.error(\"cannot happen\"))\n        val cp     = build.fullClassPath\n        val coreCp = cp.filter { f =>\n          val name = f.last\n          !name.startsWith(\"scala-library\") &&\n          !name.startsWith(\"scala3-library\") &&\n          !name.startsWith(\"runner\")\n        }\n        expect(coreCp.length == 1) // only classes directory, no stubs jar\n    }\n  }\n\n  test(\"declared sources in using directive should be included to count project hash\") {\n    val helloFile = \"Hello.scala\"\n    val inputs    =\n      TestInputs(\n        os.rel / helloFile ->\n          \"\"\"|//> using file Utils.scala\n             |\n             |object Hello extends App {\n             |   println(Utils.hello)\n             |}\"\"\".stripMargin,\n        os.rel / \"Utils.scala\" ->\n          s\"\"\"|object Utils {\n              |  val hello = \"Hello\"\n              |}\"\"\".stripMargin,\n        os.rel / \"Helper.scala\" ->\n          s\"\"\"|object Helper {\n              |  val hello = \"Hello\"\n              |}\"\"\".stripMargin\n      )\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) { (root, _, maybeBuild) =>\n      expect(maybeBuild.exists(_.success))\n      val build = maybeBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error(\"cannot happen\"))\n\n      // updating sources in using directive should change project name\n      val updatedHelloScala =\n        \"\"\"|//> using file Helper.scala\n           |\n           |object Hello extends App {\n           |   println(Helper.hello)\n           |}\"\"\".stripMargin\n      os.write.over(root / helloFile, updatedHelloScala)\n\n      inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) { (_, _, maybeUpdatedBuild) =>\n        expect(maybeUpdatedBuild.exists(_.success))\n        val updatedBuild =\n          maybeUpdatedBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error(\"cannot happen\"))\n        // project name should be change after updating source in using directive\n        expect(build.inputs.projectName != updatedBuild.inputs.projectName)\n      }\n    }\n  }\n\n  for (options <- Seq(defaultOptions, defaultScala3Options))\n    test(s\"compile 12k sources for Scala ${options.scalaOptions.scalaVersion.get.asString}\") {\n      val mainInput = os.rel / \"main.sc\" ->\n        s\"\"\"//> using jvm ${scala.build.internal.Constants.scala38MinJavaVersion}\n           |println(\"Hello from big build\")\n           |\"\"\".stripMargin\n\n      val additionalInputs = 1 to 12000 map { i =>\n        os.rel / s\"Foo$i.scala\" ->\n          s\"\"\"object Foo$i\n             |\"\"\".stripMargin\n      }\n\n      val allInputs  = mainInput +: additionalInputs\n      val testInputs = TestInputs(allInputs*)\n\n      testInputs.withBuild(options, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n        expect(maybeBuild.exists(_.success))\n      }\n    }\n\n  for (dirValue <- Seq(\"default\", \"typelevel:default\"))\n    test(s\"error when toolkit $dirValue is used with Scala 2.12\") {\n      val testInputs = TestInputs(\n        os.rel / \"simple.sc\" ->\n          s\"\"\"//> using toolkit $dirValue\n             |\n             |val n = 2\n             |println(s\"n=$$n\")\n             |\"\"\".stripMargin\n      )\n\n      val scala212Options = baseOptions.copy(\n        scalaOptions = baseOptions.scalaOptions.copy(\n          scalaVersion = Some(MaybeScalaVersion(Constants.defaultScala212Version)),\n          scalaBinaryVersion = None\n        ),\n        scriptOptions = ScriptOptions(Some(true))\n      )\n\n      testInputs.withBuild(scala212Options, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>\n        expect(maybeBuild.left.exists(_.message.startsWith(\"Toolkits do not support Scala 2.12\")))\n      }\n    }\n\n  for {\n    (modeStr, bloopMode) <-\n      Seq(\"fastLinkJs\" -> LinkerMode.Debug, \"fullLinkJs\" -> LinkerMode.Release)\n    if server\n  } do {\n    test(s\"bloop config for $modeStr\") {\n      val testInputs = TestInputs(\n        os.rel / \"Simple.scala\" ->\n          \"\"\"//> using platform js\n            |def foo(): String = \"foo\"\n            |\"\"\".stripMargin\n      )\n      val jsLinkBuildOptions = defaultOptions.copy(\n        scalaOptions = defaultOptions.scalaOptions.copy(\n          scalaVersion = None\n        ),\n        scalaJsOptions = defaultOptions.scalaJsOptions.copy(\n          mode = ScalaJsMode(Some(modeStr))\n        )\n      )\n      testInputs.withBuild(jsLinkBuildOptions, buildThreads, bloopConfigOpt) {\n        (_, _, maybeBuild) =>\n          maybeBuild match {\n            case Right(b: Build.Successful) =>\n              assert(b.project.scalaJsOptions.exists(_.mode == bloopMode))\n            case _ => fail(\"Build failed\")\n          }\n\n      }\n    }\n\n    test(s\"bloop config for noOpt and $modeStr\") {\n      val testInputs = TestInputs(\n        os.rel / \"Simple.scala\" ->\n          \"\"\"//> using platform js\n            |def foo(): String = \"foo\"\n            |\"\"\".stripMargin\n      )\n      val noOptBuildOptions = defaultOptions.copy(\n        scalaOptions = defaultOptions.scalaOptions.copy(\n          scalaVersion = None\n        ),\n        scalaJsOptions = defaultOptions.scalaJsOptions.copy(\n          mode = ScalaJsMode(Some(modeStr)),\n          noOpt = Some(true)\n        )\n      )\n      testInputs.withBuild(noOptBuildOptions, buildThreads, bloopConfigOpt) {\n        (_, _, maybeBuild) =>\n          maybeBuild match {\n            case Right(b: Build.Successful) =>\n              assert(b.project.scalaJsOptions.exists(_.mode == LinkerMode.Debug))\n            case _ => fail(\"Build failed\")\n          }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/BuildTestsBloop.scala",
    "content": "package scala.build.tests\n\nclass BuildTestsBloop extends BuildTests(server = true)\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/BuildTestsScalac.scala",
    "content": "package scala.build.tests\n\nclass BuildTestsScalac extends BuildTests(server = false) {\n\n  test(\"warn about Java files in mixed compilation with --server=false\") {\n    val recordingLogger = new RecordingLogger()\n    val inputs          = TestInputs(\n      os.rel / \"Side.java\" ->\n        \"\"\"public class Side {\n          |    public static String message = \"Hello\";\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Main.scala\" ->\n        \"\"\"@main def main() = println(Side.message)\n          |\"\"\".stripMargin\n    )\n    val options = defaultScala3Options.copy(useBuildServer = Some(false))\n    inputs.withBuild(options, buildThreads, bloopConfigOpt, logger = Some(recordingLogger)) {\n      (_, _, maybeBuild) =>\n        assert(maybeBuild.isRight)\n        val hasWarning = recordingLogger.messages.exists { msg =>\n          msg.contains(\".java files are not compiled to .class files\") &&\n          msg.contains(\"--server=false\") &&\n          msg.contains(\"Affected .java files\")\n        }\n        assert(\n          hasWarning,\n          s\"Expected warning about Java files with --server=false in: ${recordingLogger.messages.mkString(\"\\n\")}\"\n        )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/DirectiveTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.Ops.EitherThrowOps\nimport scala.build.errors.{\n  CompositeBuildException,\n  DependencyFormatError,\n  FetchingDependenciesError,\n  ToolkitDirectiveMissingVersionError\n}\nimport scala.build.options.{\n  BuildOptions,\n  InternalOptions,\n  MaybeScalaVersion,\n  ScalaOptions,\n  ScalacOpt,\n  Scope\n}\nimport scala.build.tests.util.BloopServer\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo, Position, Positioned}\n\nclass DirectiveTests extends TestUtil.ScalaCliBuildSuite {\n  val buildThreads: BuildThreads               = BuildThreads.create()\n  def bloopConfigOpt: Option[BloopRifleConfig] = Some(BloopServer.bloopConfig)\n  val extraRepoTmpDir: os.Path                 = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories                 = Directories.under(extraRepoTmpDir)\n\n  override def afterAll(): Unit = {\n    TestInputs.tryRemoveAll(extraRepoTmpDir)\n    buildThreads.shutdown()\n  }\n\n  val baseOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  test(\"resolving position of dep directive\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::utest:0.7.10\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n        val dep   = build.options.classPathOptions.extraDependencies.toSeq.headOption\n        assert(dep.nonEmpty)\n\n        val position = dep.get.positions.headOption\n        assert(position.nonEmpty)\n\n        val (startPos, endPos) = position.get match {\n          case Position.File(_, startPos, endPos, _) => (startPos, endPos)\n          case _                                     => sys.error(\"cannot happen\")\n        }\n\n        expect(startPos == (0, 14))\n        expect(endPos == (0, 39))\n    }\n  }\n\n  test(\"should parse javac options\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using javacOpt source 1.8 target 1.8\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build    = maybeBuild.orThrow\n        val javacOpt = build.options.javaOptions.javacOptions\n\n        val expectedJavacOpt = Seq(\"source\", \"1.8\", \"target\", \"1.8\")\n        expect(javacOpt.map(_.value) == expectedJavacOpt)\n    }\n  }\n\n  test(\"should parse graalVM args\") {\n    val expectedGraalVMArgs @ Seq(noFallback, enableUrl) =\n      Seq(\"--no-fallback\", \"--enable-url-protocols=http,https\")\n    TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"//> using packaging.graalvmArgs $noFallback $enableUrl\n           |\"\"\".stripMargin\n    ).withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build       = maybeBuild.orThrow\n        val graalvmArgs =\n          build.options.notForBloopOptions.packageOptions.nativeImageOptions.graalvmArgs\n        expect(graalvmArgs.map(_.value) == expectedGraalVMArgs)\n    }\n  }\n\n  test(s\"resolve toolkit & toolkit-test dependency with version passed\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"//> using toolkit latest\n           |\"\"\".stripMargin\n    )\n    testInputs.withBuilds(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuilds) =>\n        val expectedVersion = \"latest.release\"\n        val builds          = maybeBuilds.orThrow\n        val mainBuild       = builds.get(Scope.Main).get\n        val toolkitDep      =\n          mainBuild.options.classPathOptions.extraDependencies.toSeq.headOption.map(_.value).get\n        expect(toolkitDep.organization == Constants.toolkitOrganization)\n        expect(toolkitDep.name == Constants.toolkitName)\n        expect(toolkitDep.version == expectedVersion)\n        val testBuild      = builds.get(Scope.Test).get\n        val toolkitTestDep =\n          testBuild.options.classPathOptions.extraDependencies.toSeq.headOption.map(_.value).get\n        expect(toolkitTestDep.organization == Constants.toolkitOrganization)\n        expect(toolkitTestDep.name == Constants.toolkitTestName)\n        expect(toolkitTestDep.version == expectedVersion)\n    }\n  }\n\n  for (toolkitDirectiveKey <- Seq(\"toolkit\", \"test.toolkit\"))\n    test(s\"missing $toolkitDirectiveKey version produces an informative error message\") {\n      val testInputs = TestInputs(\n        os.rel / \"simple.sc\" ->\n          s\"\"\"//> using $toolkitDirectiveKey\n             |\"\"\".stripMargin\n      )\n      testInputs.withBuilds(baseOptions, buildThreads, bloopConfigOpt) {\n        (_, _, maybeBuilds) =>\n          maybeBuilds match\n            case Left(ToolkitDirectiveMissingVersionError(_, errorKey)) =>\n              expect(errorKey == toolkitDirectiveKey)\n            case _ => sys.error(\"should not happen\")\n      }\n    }\n  for (scope <- Scope.all) {\n    def withProjectFile[T](projectFileContent: String)(f: (Build, Boolean) => T): T = TestInputs(\n      os.rel / \"project.scala\"    -> projectFileContent,\n      os.rel / \"Tests.test.scala\" ->\n        \"\"\"class Tests extends munit.FunSuite {\n          |  test(\"foo\") {\n          |    println(\"foo\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n    ).withBuild(baseOptions, buildThreads, bloopConfigOpt, scope = scope) { (_, _, maybeBuild) =>\n      f(maybeBuild.orThrow, scope == Scope.Test)\n    }\n\n    test(s\"resolve test scope dependencies correctly when building for ${scope.name} scope\") {\n      withProjectFile(projectFileContent = \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n                                             |//> using test.dep org.scalameta::munit::0.7.29\n                                             |\"\"\".stripMargin) { (build, isTestScope) =>\n        val deps = build.options.classPathOptions.extraDependencies.toSeq.map(_.value)\n        expect(deps.nonEmpty)\n        val hasMainDeps = deps.exists(d =>\n          d.organization == \"com.lihaoyi\" && d.name == \"os-lib\" && d.version == \"0.9.1\"\n        )\n        val hasTestDeps = deps.exists(d =>\n          d.organization == \"org.scalameta\" && d.name == \"munit\" && d.version == \"0.7.29\"\n        )\n        expect(hasMainDeps)\n        expect(if isTestScope then hasTestDeps else !hasTestDeps)\n      }\n    }\n    test(s\"resolve test scope javacOpts correctly when building for ${scope.name} scope\") {\n      withProjectFile(projectFileContent =\n        \"\"\"//> using javacOpt source 1.8\n          |//> using test.javacOpt target 1.8\n          |//> using test.dep org.scalameta::munit::0.7.29\n          |\"\"\".stripMargin\n      ) { (build, isTestScope) =>\n        val javacOpts = build.options.javaOptions.javacOptions.map(_.value)\n        expect(javacOpts.contains(\"source\"))\n        val hasTestJavacOpts = javacOpts.contains(\"target\")\n        expect(if isTestScope then hasTestJavacOpts else !hasTestJavacOpts)\n      }\n    }\n    test(s\"resolve test scope scalac opts correctly when building for ${scope.name} scope\") {\n      withProjectFile(projectFileContent =\n        \"\"\"//> using option --explain\n          |//> using test.option -deprecation\n          |//> using test.dep org.scalameta::munit::0.7.29\n          |\"\"\".stripMargin\n      ) { (build, isTestScope) =>\n        val scalacOpts = build.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)\n        expect(scalacOpts.contains(\"--explain\"))\n        val hasTestScalacOpts = scalacOpts.contains(\"-deprecation\")\n        expect(if isTestScope then hasTestScalacOpts else !hasTestScalacOpts)\n      }\n    }\n    test(s\"resolve test scope javaOpts correctly when building for ${scope.name} scope\") {\n      withProjectFile(projectFileContent =\n        \"\"\"//> using javaOpt -Xmx2g\n          |//> using test.javaOpt -Dsomething=a\n          |//> using test.dep org.scalameta::munit::0.7.29\n          |\"\"\".stripMargin\n      ) { (build, isTestScope) =>\n        val javaOpts = build.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n        expect(javaOpts.contains(\"-Xmx2g\"))\n        val hasTestJavaOpts = javaOpts.contains(\"-Dsomething=a\")\n        expect(if isTestScope then hasTestJavaOpts else !hasTestJavaOpts)\n      }\n    }\n    test(s\"resolve test scope javaProps correctly when building for ${scope.name} scope\") {\n      withProjectFile(projectFileContent =\n        \"\"\"//> using javaProp foo=1\n          |//> using test.javaProp bar=2\n          |//> using test.dep org.scalameta::munit::0.7.29\n          |\"\"\".stripMargin\n      ) { (build, isTestScope) =>\n        val javaProps = build.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n        expect(javaProps.contains(\"-Dfoo=1\"))\n        val hasTestJavaProps = javaProps.contains(\"-Dbar=2\")\n        expect(if isTestScope then hasTestJavaProps else !hasTestJavaProps)\n      }\n    }\n    test(s\"resolve test scope resourceDir correctly when building for ${scope.name} scope\") {\n      withProjectFile(projectFileContent =\n        \"\"\"//> using resourceDir ./mainResources\n          |//> using test.resourceDir ./testResources\n          |//> using test.dep org.scalameta::munit::0.7.29\n          |\"\"\".stripMargin\n      ) { (build, isTestScope) =>\n        val resourcesDirs = build.options.classPathOptions.resourcesDir\n        expect(resourcesDirs.exists(_.last == \"mainResources\"))\n        val hasTestResources = resourcesDirs.exists(_.last == \"testResources\")\n        expect(if isTestScope then hasTestResources else !hasTestResources)\n      }\n    }\n    test(s\"resolve test scope toolkit dependency correctly when building for ${scope.name} scope\") {\n      withProjectFile(\n        projectFileContent =\n          s\"\"\"//> using test.toolkit ${Constants.toolkitVersion}\n             |\"\"\".stripMargin\n      ) { (build, isTestScope) =>\n        val deps = build.options.classPathOptions.extraDependencies.toSeq.map(_.value)\n        if isTestScope then expect(deps.nonEmpty)\n        val hasToolkitDep =\n          deps.exists(d =>\n            d.organization == Constants.toolkitOrganization &&\n            d.name == Constants.toolkitName &&\n            d.version == Constants.toolkitVersion\n          )\n        val hasTestToolkitDep =\n          deps.exists(d =>\n            d.organization == Constants.toolkitOrganization &&\n            d.name == Constants.toolkitTestName &&\n            d.version == Constants.toolkitVersion\n          )\n        expect(if isTestScope then hasToolkitDep else !hasToolkitDep)\n        expect(if isTestScope then hasTestToolkitDep else !hasTestToolkitDep)\n      }\n    }\n  }\n\n  test(\"handling special syntax for path\") {\n    val filePath   = os.rel / \"src\" / \"simple.scala\"\n    val testInputs = TestInputs(\n      os.rel / filePath ->\n        \"\"\"//> using options -coverage-out:${.}\"\"\"\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        val build                                        = maybeBuild.orThrow\n        val scalacOptions: Option[Positioned[ScalacOpt]] =\n          build.options.scalaOptions.scalacOptions.toSeq.headOption\n        assert(scalacOptions.nonEmpty)\n\n        val scalacOpt            = scalacOptions.get.value.value\n        val expectedCoveragePath = (root / filePath / os.up).toString\n        expect(scalacOpt == s\"-coverage-out:$expectedCoveragePath\")\n    }\n  }\n\n  test(\"handling special syntax for path with more dollars before\") {\n    val filePath   = os.rel / \"src\" / \"simple.scala\"\n    val testInputs = TestInputs(\n      os.rel / filePath ->\n        \"\"\"//> using options -coverage-out:$$${.}\"\"\"\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        val build                                        = maybeBuild.orThrow\n        val scalacOptions: Option[Positioned[ScalacOpt]] =\n          build.options.scalaOptions.scalacOptions.toSeq.headOption\n        assert(scalacOptions.nonEmpty)\n\n        val scalacOpt            = scalacOptions.get.value.value\n        val expectedCoveragePath = (root / filePath / os.up).toString\n        expect(scalacOpt == s\"-coverage-out:$$$expectedCoveragePath\")\n    }\n  }\n\n  test(\"skip handling special syntax for path when double dollar\") {\n    val filePath   = os.rel / \"src\" / \"simple.scala\"\n    val testInputs = TestInputs(\n      os.rel / filePath ->\n        \"\"\"//> using options -coverage-out:$${.}\"\"\"\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build                                        = maybeBuild.orThrow\n        val scalacOptions: Option[Positioned[ScalacOpt]] =\n          build.options.scalaOptions.scalacOptions.toSeq.headOption\n        assert(scalacOptions.nonEmpty)\n\n        val scalacOpt = scalacOptions.get.value.value\n        expect(scalacOpt == \"\"\"-coverage-out:${.}\"\"\")\n    }\n  }\n\n  test(\"resolve typelevel toolkit dependency\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using toolkit typelevel:latest\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n        val dep   = build.options.classPathOptions.extraDependencies.toSeq.headOption\n        assert(dep.nonEmpty)\n\n        val toolkitDep = dep.get.value\n        expect(toolkitDep.organization == Constants.typelevelToolkitOrganization)\n        expect(toolkitDep.name == Constants.toolkitName)\n        expect(toolkitDep.version == \"latest.release\")\n    }\n  }\n\n  def testSourceJar(getDirectives: (String, String) => String): Unit = {\n    val dummyJar        = \"Dummy.jar\"\n    val dummySourcesJar = \"Dummy-sources.jar\"\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        s\"\"\"${getDirectives(dummyJar, dummySourcesJar)}\n           |object Main extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / dummyJar        -> \"dummy\",\n      os.rel / dummySourcesJar -> \"dummy-sources\"\n    ).withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        val build = maybeBuild.orThrow\n        val jar   = build.options.classPathOptions.extraClassPath.head\n        expect(jar == root / dummyJar)\n        val sourceJar = build.options.classPathOptions.extraSourceJars.head\n        expect(sourceJar == root / dummySourcesJar)\n    }\n  }\n\n  test(\"source jar\") {\n    testSourceJar((dummyJar, dummySourcesJar) =>\n      s\"\"\"//> using jar $dummyJar\n         |//> using sourceJar $dummySourcesJar\"\"\".stripMargin\n    )\n  }\n\n  test(\"assumed source jar\") {\n    testSourceJar((dummyJar, dummySourcesJar) =>\n      s\"//> using jars $dummyJar $dummySourcesJar\"\n    )\n  }\n\n  test(\"include test.resourceDir into sources for test scope\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using test.resourceDir foo\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt, scope = Scope.Test) {\n      (root, _, maybeBuild) =>\n        val build =\n          maybeBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error(\"cannot happen\"))\n        val resourceDirs = build.sources.resourceDirs\n\n        expect(resourceDirs.nonEmpty)\n        expect(resourceDirs.length == 1)\n        val path = root / \"foo\"\n        expect(resourceDirs == Seq(path))\n    }\n  }\n  test(\"do not include test.resourceDir into sources for main scope\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using test.resourceDir foo\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt, scope = Scope.Main) {\n      (_, _, maybeBuild) =>\n        val build =\n          maybeBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error(\"cannot happen\"))\n        val resourceDirs = build.sources.resourceDirs\n\n        expect(resourceDirs.isEmpty)\n    }\n  }\n  test(\"parse boolean for publish.doc\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using publish.doc false\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (_, _, maybeBuild) =>\n        val build            = maybeBuild.orThrow\n        val publishOptionsCI =\n          build.options.notForBloopOptions.publishOptions.contextual(isCi = true)\n        val publishOptionsLocal =\n          build.options.notForBloopOptions.publishOptions.contextual(isCi = false)\n\n        expect(publishOptionsCI.docJar.contains(false))\n        expect(publishOptionsLocal.docJar.contains(false))\n    }\n  }\n\n  test(\"dependency parsing error with position\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using dep not-a-dep\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        expect(maybeBuild.isLeft)\n        val error = maybeBuild.left.toOption.get\n\n        error match {\n          case error: DependencyFormatError =>\n            expect(\n              error.message == \"Error parsing dependency 'not-a-dep': malformed module: not-a-dep\"\n            )\n            expect(error.positions.length == 1)\n            val path = root / \"simple.sc\"\n            expect(error.positions.head == Position.File(\n              Right(path),\n              (0, 14),\n              (0, 23)\n            ))\n          case _ => fail(\"unexpected BuildException type\")\n        }\n    }\n  }\n\n  test(\"separate dependency resolution errors for each dependency\") {\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using dep org.xyz::foo:0.0.1\n          |//> using dep com.lihaoyi::os-lib:0.9.1 org.qwerty::bar:0.0.1\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        expect(maybeBuild.isLeft)\n        val errors = maybeBuild.left.toOption.get\n\n        errors match {\n          case error: CompositeBuildException =>\n            expect(error.exceptions.length == 2)\n            expect(error.exceptions.forall {\n              case _: FetchingDependenciesError => true\n              case _                            => false\n            })\n            expect(error.exceptions.forall(_.positions.length == 1))\n\n            {\n              val xyzError = error.exceptions.find(_.message.contains(\"org.xyz\")).get\n              expect(xyzError.message.startsWith(\"Error downloading org.xyz:foo\"))\n              expect(!xyzError.message.contains(\"com.lihaoyi\"))\n              expect(!xyzError.message.contains(\"org.qwerty\"))\n              val path = root / \"simple.sc\"\n              expect(xyzError.positions.head == Position.File(\n                Right(path),\n                (0, 14),\n                (0, 32)\n              ))\n            }\n\n            {\n              val qwertyError = error.exceptions.find(_.message.contains(\"org.qwerty\")).get\n              expect(qwertyError.message.contains(\"Error downloading org.qwerty:bar\"))\n              expect(!qwertyError.message.contains(\"com.lihaoyi\"))\n              expect(!qwertyError.message.contains(\"org.xyz\"))\n              val path = root / \"simple.sc\"\n              expect(qwertyError.positions.head == Position.File(\n                Right(path),\n                (1, 40),\n                (1, 61)\n              ))\n            }\n          case _ => fail(\"unexpected BuildException type\")\n        }\n    }\n  }\n\n  test(\"main scope dependencies propagate to test scope\") {\n    val Scala322Options = baseOptions.copy(scalaOptions =\n      ScalaOptions(\n        scalaVersion = Some(MaybeScalaVersion(\"3.2.2\"))\n      )\n    )\n\n    val testInputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        \"\"\"//> using target.scala 3.2.2\n          |//> using dep com.lihaoyi::os-lib:0.9.1\n          |\"\"\".stripMargin,\n      os.rel / \"test\" / \"test.sc\" ->\n        \"\"\"println(os.list(os.pwd))\n          |\"\"\".stripMargin\n    )\n    testInputs.withBuild(Scala322Options, buildThreads, bloopConfigOpt, scope = Scope.Test) {\n      (_, _, maybeBuild) => expect(maybeBuild.exists(_.success))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/DistinctByTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\nclass DistinctByTests extends TestUtil.ScalaCliBuildSuite {\n  case class Message(a: String, b: Int)\n  val distinctData: Seq[Message] = Seq(\n    Message(a = \"1\", b = 4),\n    Message(a = \"2\", b = 3),\n    Message(a = \"3\", b = 2),\n    Message(a = \"4\", b = 1)\n  )\n  val repeatingData: Seq[Message] = Seq(\n    Message(a = \"1\", b = 4),\n    Message(a = \"1\", b = 44),\n    Message(a = \"2\", b = 3),\n    Message(a = \"22\", b = 3),\n    Message(a = \"3\", b = 22),\n    Message(a = \"33\", b = 2),\n    Message(a = \"4\", b = 1),\n    Message(a = \"4\", b = 11)\n  )\n\n  test(\"distinctBy where data is already distinct\") {\n    val distinctByA     = distinctData.distinctBy(_.a)\n    val distinctByB     = distinctData.distinctBy(_.b)\n    val generalDistinct = distinctData.distinct\n    expect(distinctData == generalDistinct)\n    expect(distinctData == distinctByA)\n    expect(distinctData == distinctByB)\n  }\n\n  test(\"distinctBy doesn't change data order\") {\n    val expectedData = Seq(\n      Message(a = \"1\", b = 4),\n      Message(a = \"2\", b = 3),\n      Message(a = \"22\", b = 3),\n      Message(a = \"3\", b = 22),\n      Message(a = \"33\", b = 2),\n      Message(a = \"4\", b = 1)\n    )\n    expect(repeatingData.distinctBy(_.a) == expectedData)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/ExcludeTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.cache.Cache.Fetch\nimport coursier.cache.{ArchiveCache, ArtifactError, Cache}\nimport coursier.util.{Artifact, EitherT, Task}\n\nimport java.io.File\n\nimport scala.build.Ops.*\nimport scala.build.errors.ExcludeDefinitionError\nimport scala.build.input.ScalaCliInvokeData\nimport scala.build.options.{BuildOptions, Scope, SuppressWarningOptions}\nimport scala.build.preprocessing.Preprocessor\nimport scala.build.{CrossSources, Sources}\nimport scala.concurrent.ExecutionContext\n\nclass ExcludeTests extends TestUtil.ScalaCliBuildSuite {\n  val preprocessors: Seq[Preprocessor] = Sources.defaultPreprocessors(\n    archiveCache = ArchiveCache().withCache(\n      new Cache[Task] {\n        def fetch: Fetch[Task] = _ => sys.error(\"shouldn't be used\")\n        def file(artifact: Artifact): EitherT[Task, ArtifactError, File] =\n          sys.error(\"shouldn't be used\")\n        def ec: ExecutionContext = sys.error(\"shouldn't be used\")\n      }\n    ),\n    javaClassNameVersionOpt = None,\n    javaCommand = () => sys.error(\"shouldn't be used\")\n  )\n\n  test(\"throw error when exclude found in multiple files\") {\n    val testInputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"//> using exclude *.sc\n          |\"\"\".stripMargin,\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using exclude */test/*\n          |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (_, inputs) =>\n      val crossSources =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy)\n      crossSources match {\n        case Left(_: ExcludeDefinitionError) =>\n        case o                               => fail(\"Exception expected\", clues(o))\n      }\n    }\n  }\n\n  test(\"throw error when exclude found in non top-level project.scala and file\") {\n    val testInputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using exclude */test/*\n          |\"\"\".stripMargin,\n      os.rel / \"src\" / \"project.scala\" ->\n        s\"\"\"//> using exclude *.sc\"\"\"\n    )\n    testInputs.withInputs { (_, inputs) =>\n      val crossSources =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy)\n      crossSources match {\n        case Left(_: ExcludeDefinitionError) =>\n        case o                               => fail(\"Exception expected\", clues(o))\n      }\n    }\n  }\n\n  test(\"multiple excludes\") {\n    val testInputs = TestInputs(\n      os.rel / \"Hello.scala\"   -> \"object Hello\",\n      os.rel / \"World.scala\"   -> \"object World\",\n      os.rel / \"Main.scala\"    -> \"object Main\",\n      os.rel / \"project.scala\" -> s\"\"\"//> using exclude Hello.scala World.scala\"\"\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy).orThrow\n      val scopedSources = crossSources.scopedSources(BuildOptions())\n        .orThrow\n      val sources =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(sources.paths.nonEmpty)\n      expect(sources.paths.length == 2)\n      val paths = Seq(os.rel / \"Main.scala\", os.rel / \"project.scala\")\n      expect(sources.paths.map(_._2) == paths)\n    }\n  }\n\n  test(\"exclude relative paths\") {\n    val testInputs = TestInputs(\n      os.rel / \"Hello.scala\" -> \"object Hello\",\n      os.rel / \"Main.scala\"  ->\n        \"\"\"object Main {\n          |}\"\"\".stripMargin,\n      os.rel / \"project.scala\" ->\n        s\"\"\"//> using exclude Main.scala\"\"\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy).orThrow\n      val scopedSources = crossSources.scopedSources(BuildOptions())\n        .orThrow\n      val sources =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(sources.paths.nonEmpty)\n      expect(sources.paths.length == 2)\n      val paths = Seq(os.rel / \"Hello.scala\", os.rel / \"project.scala\")\n      expect(sources.paths.map(_._2) == paths)\n    }\n  }\n\n  test(\"exclude absolute file paths\") {\n    val testInputs = TestInputs(\n      os.rel / \"Hello.scala\" -> \"object Hello\",\n      os.rel / \"Main.scala\"  ->\n        \"\"\"object Main {\n          |}\"\"\".stripMargin,\n      os.rel / \"project.scala\" ->\n        s\"\"\"//> using exclude $${.}${File.separator}Main.scala\"\"\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy).orThrow\n      val scopedSources = crossSources.scopedSources(BuildOptions())\n        .orThrow\n      val sources =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(sources.paths.nonEmpty)\n      expect(sources.paths.length == 2)\n      val paths = Seq(os.rel / \"Hello.scala\", os.rel / \"project.scala\")\n      expect(sources.paths.map(_._2) == paths)\n    }\n  }\n\n  test(\"exclude relative directory paths\") {\n    val testInputs = TestInputs(\n      os.rel / \"Hello.scala\"                  -> \"object Hello\",\n      os.rel / \"src\" / \"scala\" / \"Main.scala\" ->\n        \"\"\"object Main {\n          |}\"\"\".stripMargin,\n      os.rel / \"project.scala\" ->\n        \"\"\"//> using exclude src/*.scala\"\"\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy).orThrow\n      val scopedSources = crossSources.scopedSources(BuildOptions())\n        .orThrow\n      val sources =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(sources.paths.nonEmpty)\n      expect(sources.paths.length == 2)\n      val paths = Seq(os.rel / \"Hello.scala\", os.rel / \"project.scala\")\n      expect(sources.paths.map(_._2) == paths)\n    }\n  }\n\n  test(\"exclude relative directory paths with glob pattern\") {\n    val testInputs = TestInputs(\n      os.rel / \"Hello.scala\"                  -> \"object Hello\",\n      os.rel / \"src\" / \"scala\" / \"Main.scala\" ->\n        \"\"\"object Main {\n          |}\"\"\".stripMargin,\n      os.rel / \"project.scala\" ->\n        \"\"\"//> using exclude src/*.scala\"\"\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy).orThrow\n      val scopedSources = crossSources.scopedSources(BuildOptions())\n        .orThrow\n      val sources =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(sources.paths.nonEmpty)\n      expect(sources.paths.length == 2)\n      val paths = Seq(os.rel / \"Hello.scala\", os.rel / \"project.scala\")\n      expect(sources.paths.map(_._2) == paths)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/FrameworkDiscoveryTests.scala",
    "content": "package scala.build.tests\n\nimport java.nio.file.Files\n\nimport scala.build.errors.NoFrameworkFoundByNativeBridgeError\nimport scala.build.testrunner.{AsmTestRunner, Logger as TestRunnerLogger}\n\nclass FrameworkDiscoveryTests extends TestUtil.ScalaCliBuildSuite {\n\n  test(\n    \"findFrameworkServices parses Java ServiceLoader format (trim, skip comments and empty lines)\"\n  ) {\n    val dir = Files.createTempDirectory(\"scala-cli-framework-services-\")\n    try {\n      val servicesDir = dir.resolve(\"META-INF\").resolve(\"services\")\n      Files.createDirectories(servicesDir)\n      val serviceFile = servicesDir.resolve(\"sbt.testing.Framework\")\n      // Content with newlines, comments, and surrounding whitespace\n      val content =\n        \"\"\"munit.Framework\n          |# comment line\n          |\n          |  munit.native.Framework  \n          |\n          |\"\"\".stripMargin\n      Files.writeString(serviceFile, content)\n\n      val found = AsmTestRunner.findFrameworkServices(Seq(dir), TestRunnerLogger(0))\n      assertEquals(\n        found.sorted,\n        Seq(\"munit.Framework\", \"munit.native.Framework\"),\n        clue = \"Service file lines should be trimmed; comments and empty lines skipped\"\n      )\n    }\n    finally {\n      def deleteRecursively(p: java.nio.file.Path): Unit = {\n        if Files.isDirectory(p) then Files.list(p).forEach(deleteRecursively)\n        Files.deleteIfExists(p)\n      }\n      deleteRecursively(dir)\n    }\n  }\n\n  test(\"NoFrameworkFoundByNativeBridgeError has Native-specific message (not Scala.js)\") {\n    val err = new NoFrameworkFoundByNativeBridgeError\n    assert(err.getMessage.contains(\"Scala Native\"), clue = \"Message should mention Scala Native\")\n    assert(!err.getMessage.contains(\"Scala.js\"), clue = \"Message should not mention Scala.js\")\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/InputsTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.input.*\nimport scala.build.input.ElementsUtils.*\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.build.tests.util.BloopServer\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo}\n\nclass InputsTests extends TestUtil.ScalaCliBuildSuite {\n  val buildThreads: BuildThreads               = BuildThreads.create()\n  val extraRepoTmpDir: os.Path                 = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories                 = Directories.under(extraRepoTmpDir)\n  def bloopConfigOpt: Option[BloopRifleConfig] = Some(BloopServer.bloopConfig)\n  val buildOptions: BuildOptions               = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  test(\"forced workspace\") {\n    val testInputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"object Foo {\n          |  def main(): Unit = {\n          |    println(\"Hello\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    val forcedWorkspace = os.rel / \"workspace\"\n    testInputs.withCustomInputs(viaDirectory = false, forcedWorkspaceOpt = Some(forcedWorkspace)) {\n      (root, inputs) =>\n        expect(inputs.workspace == root / forcedWorkspace)\n        expect(inputs.baseProjectName == \"workspace\")\n    }\n  }\n\n  test(\"project file\") {\n    val projectFileName = Constants.projectFileName\n    val testInputs      = TestInputs(\n      files = Seq(\n        os.rel / \"custom-dir\" / projectFileName -> \"\",\n        os.rel / projectFileName                -> s\"//> using javaProp \\\"foo=bar\\\"\".stripMargin,\n        os.rel / \"foo.scala\"                    ->\n          s\"\"\"object Foo {\n             |  def main(args: Array[String]): Unit =\n             |    println(\"Foo\")\n             |}\n             |\"\"\".stripMargin\n      ),\n      inputArgs = Seq(\"foo.scala\", \"custom-dir\", projectFileName)\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) {\n      (root, _, buildMaybe) =>\n        val javaOptsCheck = buildMaybe match {\n          case Right(build: Build.Successful) =>\n            build.options.javaOptions.javaOpts.toSeq.head.value.value == \"-Dfoo=bar\"\n          case _ => false\n        }\n        assert(javaOptsCheck)\n        assert(os.exists(root / \"custom-dir\" / Constants.workspaceDirName))\n        assert(!os.exists(root / Constants.workspaceDirName))\n\n        val filesUnderScalaBuild = os.list(root / \"custom-dir\" / Constants.workspaceDirName)\n        assert(filesUnderScalaBuild.exists(_.baseName.startsWith(\"custom-dir\")))\n        assert(!filesUnderScalaBuild.exists(_.baseName.startsWith(\"project\")))\n    }\n  }\n\n  test(\"setting root dir without project settings file\") {\n    val testInputs = TestInputs(\n      files = Seq(\n        os.rel / \"custom-dir\" / \"foo.scala\" ->\n          s\"\"\"object Foo {\n             |  def main(args: Array[String]): Unit =\n             |    println(\"Foo\")\n             |}\n             |\"\"\".stripMargin,\n        os.rel / \"bar.scala\" -> \"\"\n      ),\n      inputArgs = Seq(\"custom-dir\", \"bar.scala\")\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) {\n      (root, _, _) =>\n        assert(os.exists(root / \"custom-dir\" / Constants.workspaceDirName))\n        assert(!os.exists(root / Constants.workspaceDirName))\n\n        val filesUnderScalaBuild = os.list(root / \"custom-dir\" / Constants.workspaceDirName)\n        assert(filesUnderScalaBuild.exists(_.baseName.startsWith(\"custom-dir\")))\n        assert(!filesUnderScalaBuild.exists(_.baseName.startsWith(\"project\")))\n    }\n  }\n\n  test(\"passing project file and its parent directory\") {\n    val projectFileName = Constants.projectFileName\n    val testInputs      = TestInputs(\n      files = Seq(\n        os.rel / \"foo.scala\" ->\n          s\"\"\"object Foo {\n             |  def main(args: Array[String]): Unit =\n             |    println(\"Foo\")\n             |}\n             |\"\"\".stripMargin,\n        os.rel / projectFileName -> \"\"\n      ),\n      inputArgs = Seq(\".\", projectFileName)\n    )\n    testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) {\n      (root, inputs, _) =>\n        assert(os.exists(root / Constants.workspaceDirName))\n        assert(inputs.elements.projectSettingsFiles.length == 1)\n\n        val filesUnderScalaBuild = os.list(root / Constants.workspaceDirName)\n        assert(filesUnderScalaBuild.exists(_.baseName.startsWith(root.baseName)))\n        assert(!filesUnderScalaBuild.exists(_.baseName.startsWith(\"project\")))\n    }\n  }\n\n  test(\"sbt file is recognized as SbtFile when passed explicitly\") {\n    TestInputs(os.rel / \"build.sbt\" -> \"\").fromRoot { root =>\n      val elements = Inputs.validateArgs(\n        Seq((root / \"build.sbt\").toString),\n        root,\n        download = _ => Right(Array.emptyByteArray),\n        stdinOpt = None,\n        acceptFds = false,\n        enableMarkdown = false\n      )(using ScalaCliInvokeData.dummy)\n      elements match {\n        case Seq(Right(Seq(f: SbtFile))) =>\n          assert(f.path == root / \"build.sbt\")\n        case _ => fail(s\"Unexpected elements: $elements\")\n      }\n    }\n  }\n\n  test(\"sbt file is picked up from directory scan\") {\n    TestInputs(os.rel / \"build.sbt\" -> \"\").fromRoot { root =>\n      val dir      = Directory(root)\n      val singles  = dir.singleFilesFromDirectory(enableMarkdown = false)\n      val sbtFiles = singles.collect { case f: SbtFile => f }\n      assert(sbtFiles.nonEmpty)\n      assert(sbtFiles.head.path == root / \"build.sbt\")\n    }\n  }\n\n  test(\"URLs with query parameters\") {\n    val urlBase =\n      \"https://gist.githubusercontent.com/USER/hash/raw/hash\"\n    val urls = Seq(\n      s\"$urlBase/test.sc\",\n      s\"$urlBase/test.sc?foo=bar\",\n      s\"$urlBase/test.sc?foo=endsWith.md\",\n      s\"http://gist.githubusercontent.com/USER/hash/raw/hash/test.sc?foo=bar\",\n      s\"$urlBase/test.scala?foo=endsWith.java\",\n      s\"$urlBase/test.java?token=123456789123456789\",\n      s\"file:///Users/user/content/test.sc\"\n    )\n\n    TestInputs().fromRoot { root =>\n      val elements = Inputs.validateArgs(\n        urls,\n        root,\n        download = _ => Right(Array.emptyByteArray),\n        stdinOpt = None,\n        acceptFds = true,\n        enableMarkdown = true\n      )(using ScalaCliInvokeData.dummy)\n\n      elements match {\n        case Seq(\n              Right(Seq(el1: VirtualScript)),\n              Right(Seq(el2: VirtualScript)),\n              Right(Seq(el3: VirtualScript)),\n              Right(Seq(el4: VirtualScript)),\n              Right(Seq(el5: VirtualScalaFile)),\n              Right(Seq(el6: VirtualJavaFile)),\n              Right(Seq(el7: VirtualScript))\n            ) =>\n          Seq(el1, el2, el3, el4, el5, el6, el7)\n            .zip(urls)\n            .foreach {\n              case (el: VirtualScript, url) =>\n                expect(el.source == url)\n                expect(el.content.isEmpty)\n                val path = os.rel / \"test.sc\"\n                expect(el.wrapperPath.endsWith(path))\n              case (el: VirtualScalaFile, url) =>\n                expect(el.source == url)\n                expect(el.content.isEmpty)\n              case (el: VirtualJavaFile, url) =>\n                expect(el.source == url)\n                expect(el.content.isEmpty)\n              case _ => fail(\"Unexpected elements\")\n            }\n        case _ => fail(\"Unexpected elements\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/JavaTestRunnerTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.assert as expect\n\nimport scala.build.options.*\n\nclass JavaTestRunnerTests extends TestUtil.ScalaCliBuildSuite {\n\n  private def makeOptions(\n    scalaVersionOpt: Option[MaybeScalaVersion],\n    addTestRunner: Boolean\n  ): BuildOptions =\n    BuildOptions(\n      scalaOptions = ScalaOptions(\n        scalaVersion = scalaVersionOpt\n      ),\n      internalDependencies = InternalDependenciesOptions(\n        addTestRunnerDependencyOpt = Some(addTestRunner)\n      )\n    )\n\n  test(\"pure Java build has no scalaParams\") {\n    val opts   = makeOptions(Some(MaybeScalaVersion.none), addTestRunner = false)\n    val params = opts.scalaParams.toOption.flatten\n    expect(params.isEmpty, \"Pure Java build should have no scalaParams\")\n  }\n\n  test(\"Scala build has scalaParams\") {\n    val opts   = makeOptions(None, addTestRunner = false)\n    val params = opts.scalaParams.toOption.flatten\n    expect(params.isDefined, \"Scala build should have scalaParams\")\n  }\n\n  test(\"pure Java test build gets addJvmJavaTestRunner=true in Artifacts params\") {\n    val opts   = makeOptions(Some(MaybeScalaVersion.none), addTestRunner = true)\n    val isJava = opts.scalaParams.toOption.flatten.isEmpty\n    expect(isJava, \"Expected pure Java build to have no scalaParams\")\n  }\n\n  test(\"Scala test build gets addJvmTestRunner=true in Artifacts params\") {\n    val opts   = makeOptions(None, addTestRunner = true)\n    val isJava = opts.scalaParams.toOption.flatten.isEmpty\n    expect(!isJava, \"Expected Scala build to have scalaParams\")\n  }\n\n  test(\"mixed Scala+Java build still gets Scala test runner\") {\n    val opts   = makeOptions(None, addTestRunner = true)\n    val isJava = opts.scalaParams.toOption.flatten.isEmpty\n    expect(!isJava, \"Mixed Scala+Java build should still use Scala test runner\")\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/OfflineTests.scala",
    "content": "package scala.build.tests\nimport coursier.cache.FileCache\n\nimport scala.build.errors.ScalaVersionError\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.build.tests.Constants\nimport scala.build.{BuildThreads, Directories}\n\nclass OfflineTests extends TestUtil.ScalaCliBuildSuite {\n  val extraRepoTmpDir: os.Path  = os.temp.dir(prefix = \"scala-cli-tests-offline-\")\n  val directories: Directories  = Directories.under(extraRepoTmpDir)\n  val baseOptions: BuildOptions = BuildOptions(\n    internal = InternalOptions(\n      cache = Some(FileCache()\n        .withLocation(directories.cacheDir.toString)\n        .withCachePolicies(Seq(coursier.cache.CachePolicy.LocalOnly)))\n    )\n  )\n\n  val buildThreads: BuildThreads = BuildThreads.create()\n\n  for (\n    defaultVersion <- Seq(\n      Constants.defaultScalaVersion,\n      Constants.defaultScala212Version,\n      Constants.defaultScala213Version\n    )\n  )\n    test(s\"Default versions of Scala should pass without validation for $defaultVersion\") {\n      val testInputs = TestInputs(\n        os.rel / \"script.sc\" ->\n          s\"\"\"//> using scala $defaultVersion\n             |def msg: String = \"Hello\"\n             |\n             |println(msg)\n             |\"\"\".stripMargin\n      )\n\n      testInputs.withBuild(baseOptions, buildThreads, None) {\n        (_, _, maybeBuild) =>\n          maybeBuild match {\n            case Left(e: ScalaVersionError) =>\n              munit.Assertions.fail(\n                s\"Validation Failed with:${System.lineSeparator()} ${e.getMessage}\"\n              )\n            case _ => ()\n          }\n      }\n    }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/PackagingUsingDirectiveTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.options.{BuildOptions, InternalOptions, PackageType}\nimport scala.build.tests.util.BloopServer\nimport scala.build.{BuildThreads, Directories, LocalRepo}\n\nclass PackagingUsingDirectiveTests extends TestUtil.ScalaCliBuildSuite {\n  val buildThreads: BuildThreads            = BuildThreads.create()\n  def bloopConfig: Option[BloopRifleConfig] = Some(BloopServer.bloopConfig)\n\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n\n  val buildOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  test(\"package type\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using packaging.packageType graalvm\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      val foundPackageTypeOpt = maybeBuild.options.notForBloopOptions.packageOptions.packageTypeOpt\n      expect(foundPackageTypeOpt.contains(PackageType.GraalVMNativeImage))\n    }\n  }\n\n  test(\"output\") {\n    val output = \"foo\"\n    val inputs = TestInputs(\n      os.rel / \"Bar.scala\" ->\n        s\"\"\"//> using packaging.output $output\n           |def hello() = println(\"hello\")\n           |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      val maybePackageOutput  = maybeBuild.options.notForBloopOptions.packageOptions.output\n      val packageOutputString = maybePackageOutput.getOrElse(\"None\")\n      val index               = packageOutputString.lastIndexOf('/')\n      val packageName         = packageOutputString.drop(index + 1)\n      expect(packageName == output)\n    }\n  }\n\n  test(\"docker options\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using packaging.dockerFrom openjdk:11\n          |//> using packaging.dockerImageTag 1.0.0\n          |//> using packaging.dockerImageRegistry virtuslab\n          |//> using packaging.dockerImageRepository scala-cli\n          |\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      val dockerOpt = maybeBuild.options.notForBloopOptions.packageOptions.dockerOptions\n      expect(dockerOpt.from == Some(\"openjdk:11\"))\n      expect(dockerOpt.imageTag == Some(\"1.0.0\"))\n      expect(dockerOpt.imageRegistry == Some(\"virtuslab\"))\n      expect(dockerOpt.imageRepository == Some(\"scala-cli\"))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/PreprocessingTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.input.{MarkdownFile, ScalaCliInvokeData, Script, SourceScalaFile}\nimport scala.build.options.SuppressWarningOptions\nimport scala.build.preprocessing.{MarkdownPreprocessor, ScalaPreprocessor, ScriptPreprocessor}\n\nclass PreprocessingTests extends TestUtil.ScalaCliBuildSuite {\n  test(\"Report error if scala file not exists\") {\n    val logger    = TestLogger()\n    val scalaFile = SourceScalaFile(os.temp.dir(), os.SubPath(\"NotExists.scala\"))\n\n    val res = ScalaPreprocessor.preprocess(\n      scalaFile,\n      logger,\n      allowRestrictedFeatures = false,\n      suppressWarningOptions = SuppressWarningOptions()\n    )(using ScalaCliInvokeData.dummy)\n    val expectedMessage = s\"File not found: ${scalaFile.path}\"\n\n    assert(res.nonEmpty)\n    assert(res.get.isLeft)\n    expect(res.get.swap.toOption.get.message == expectedMessage)\n  }\n\n  test(\"Report error if scala script not exists\") {\n    val logger      = TestLogger()\n    val scalaScript = Script(os.temp.dir(), os.SubPath(\"NotExists.sc\"), None)\n\n    val res = ScriptPreprocessor.preprocess(\n      scalaScript,\n      logger,\n      allowRestrictedFeatures = false,\n      suppressWarningOptions = SuppressWarningOptions()\n    )(using ScalaCliInvokeData.dummy)\n    val expectedMessage = s\"File not found: ${scalaScript.path}\"\n\n    assert(res.nonEmpty)\n    assert(res.get.isLeft)\n    expect(res.get.swap.toOption.get.message == expectedMessage)\n  }\n\n  test(\"Report error if markdown does not exist\") {\n    val logger       = TestLogger()\n    val markdownFile = MarkdownFile(os.temp.dir(), os.SubPath(\"NotExists.md\"))\n\n    val res = MarkdownPreprocessor.preprocess(\n      markdownFile,\n      logger,\n      allowRestrictedFeatures = false,\n      suppressWarningOptions = SuppressWarningOptions()\n    )(using ScalaCliInvokeData.dummy)\n    val expectedMessage = s\"File not found: ${markdownFile.path}\"\n\n    assert(res.nonEmpty)\n    assert(res.get.isLeft)\n    expect(res.get.swap.toOption.get.message == expectedMessage)\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/ReplArtifactsTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.cache.FileCache\nimport dependency.ScalaParameters\n\nimport scala.build.{Logger, ReplArtifacts}\n\nclass ReplArtifactsTests extends TestUtil.ScalaCliBuildSuite {\n  def scalaPyTest(version: String, usesFormerOrg: Boolean = false): Unit =\n    TestInputs.withTmpDir(\"replartifactstests\") { _ =>\n      val artifacts = ReplArtifacts.ammonite(\n        scalaParams = ScalaParameters(\"2.13.8\"),\n        ammoniteVersion = \"2.5.4\",\n        dependencies = Nil,\n        extraClassPath = Nil,\n        extraSourceJars = Nil,\n        extraRepositories = Nil,\n        logger = Logger.nop,\n        cache = FileCache(),\n        addScalapy = Some(version)\n      ).fold(e => throw new Exception(e), identity)\n\n      val urls           = artifacts.replArtifacts.map(_._1)\n      val meShadajUrls   = urls.filter(_.startsWith(\"https://repo1.maven.org/maven2/me/shadaj/\"))\n      val devScalaPyUrls = urls.filter(_.startsWith(\"https://repo1.maven.org/maven2/dev/scalapy/\"))\n\n      if (usesFormerOrg) {\n        expect(meShadajUrls.nonEmpty)\n        expect(devScalaPyUrls.isEmpty)\n      }\n      else {\n        expect(meShadajUrls.isEmpty)\n        expect(devScalaPyUrls.nonEmpty)\n      }\n    }\n\n  test(\"ScalaPy former organization\") {\n    scalaPyTest(\"0.5.2+5-83f1eb68\", usesFormerOrg = true)\n  }\n  test(\"ScalaPy new organization\") {\n    scalaPyTest(\"0.5.2+9-623f0807\")\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/ScalaNativeUsingDirectiveTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.errors.UsingDirectiveValueNumError\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.build.tests.util.BloopServer\nimport scala.build.{BuildThreads, Directories, LocalRepo}\n\nclass ScalaNativeUsingDirectiveTests extends TestUtil.ScalaCliBuildSuite {\n  val buildThreads: BuildThreads            = BuildThreads.create()\n  def bloopConfig: Option[BloopRifleConfig] = Some(BloopServer.bloopConfig)\n\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n\n  val buildOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  test(\"ScalaNativeOptions for native-gc with no values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using `native-gc`\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      expect(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-gc with multiple values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-gc 78 12\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n\n  }\n\n  test(\"ScalaNativeOptions for native-gc\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-gc 78\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      val gcStr = maybeBuild.options.scalaNativeOptions.gcStr\n      expect(gcStr.contains(\"78\"))\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-mode with no values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using `native-mode`\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      expect(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-mode with multiple values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-mode debug release-full\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-mode\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-mode release-full\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(maybeBuild.options.scalaNativeOptions.modeStr.get == \"release-full\")\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-version with multiple values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-version 0.4.0 0.3.3\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n\n  }\n\n  test(\"ScalaNativeOptions for native-version\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-version 0.4.0\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(maybeBuild.options.scalaNativeOptions.version.get == \"0.4.0\")\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-compile\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-compile compileOption1 compileOption2\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.options.scalaNativeOptions.compileOptions.head == \"compileOption1\"\n      )\n      assert(\n        maybeBuild.options.scalaNativeOptions.compileOptions(1) == \"compileOption2\"\n      )\n    }\n  }\n\n  for { directiveKey <- Seq(\"nativeCCompile\", \"native-c-compile\") }\n    test(s\"ScalaNativeOptions for $directiveKey\") {\n      val expectedOption1 = \"compileOption1\"\n      val expectedOption2 = \"compileOption2\"\n      val inputs          = TestInputs(\n        os.rel / \"p.sc\" ->\n          s\"\"\"//> using $directiveKey $expectedOption1 $expectedOption2\n             |def foo() = println(\"hello foo\")\n             |\"\"\".stripMargin\n      )\n\n      inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n        assert(\n          maybeBuild.options.scalaNativeOptions.cCompileOptions.head == expectedOption1\n        )\n        assert(\n          maybeBuild.options.scalaNativeOptions.cCompileOptions.drop(1).head == expectedOption2\n        )\n      }\n    }\n\n  for { directiveKey <- Seq(\"nativeCppCompile\", \"native-cpp-compile\") }\n    test(s\"ScalaNativeOptions for $directiveKey\") {\n      val expectedOption1 = \"compileOption1\"\n      val expectedOption2 = \"compileOption2\"\n      val inputs          = TestInputs(\n        os.rel / \"p.sc\" ->\n          s\"\"\"//> using $directiveKey $expectedOption1 $expectedOption2\n             |def foo() = println(\"hello foo\")\n             |\"\"\".stripMargin\n      )\n\n      inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n        assert(\n          maybeBuild.options.scalaNativeOptions.cppCompileOptions.head == expectedOption1\n        )\n        assert(\n          maybeBuild.options.scalaNativeOptions.cppCompileOptions.drop(1).head == expectedOption2\n        )\n      }\n    }\n\n  test(\"ScalaNativeOptions for native-linking and no value\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using `native-linking`\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(maybeBuild.exists { build =>\n        build.options.scalaNativeOptions.linkingOptions.isEmpty\n      })\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-linking\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-linking linkingOption1 linkingOption2\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.options.scalaNativeOptions.linkingOptions.head == \"linkingOption1\"\n      )\n      assert(\n        maybeBuild.options.scalaNativeOptions.linkingOptions(1) == \"linkingOption2\"\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-clang\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-clang clang/path\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.options.scalaNativeOptions.clang.get == \"clang/path\"\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-clang and multiple values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-clang path1 path2\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-clang-pp\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-clang-pp clangpp/path\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.options.scalaNativeOptions.clangpp.get == \"clangpp/path\"\n      )\n    }\n  }\n\n  test(\"ScalaNativeOptions for native-clang-pp and multiple values\") {\n    val inputs = TestInputs(\n      os.rel / \"p.sc\" ->\n        \"\"\"//> using native-clang-pp path1 path2\n          |def foo() = println(\"hello foo\")\n          |\"\"\".stripMargin\n    )\n    inputs.withBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n      assert(\n        maybeBuild.left.exists { case _: UsingDirectiveValueNumError => true; case _ => false }\n      )\n    }\n  }\n\n  for { multithreadingDirective <- Seq(\"`native-multithreading`\", \"nativeMultithreading\") }\n    test(s\"ScalaNativeOptions for $multithreadingDirective\") {\n      val inputs = TestInputs(\n        os.rel / \"p.sc\" ->\n          s\"\"\"//> using $multithreadingDirective\n             |def foo() = println(\"hello foo\")\n             |\"\"\".stripMargin\n      )\n      inputs.withLoadedBuild(buildOptions, buildThreads, bloopConfig) { (_, _, maybeBuild) =>\n        assert(maybeBuild.options.scalaNativeOptions.multithreading.get)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/ScalaPreprocessorTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.input.{ScalaCliInvokeData, Script, SourceScalaFile}\nimport scala.build.options.SuppressWarningOptions\nimport scala.build.preprocessing.{ScalaPreprocessor, ScriptPreprocessor}\n\nclass ScalaPreprocessorTests extends TestUtil.ScalaCliBuildSuite {\n\n  test(\"should respect using directives in a .scala file with the shebang line\") {\n    val lastUsingLine =\n      \"//> using dep \\\"com.lihaoyi::os-lib::0.8.1\\\" \\\"com.lihaoyi::os-lib::0.8.1\\\"\"\n    TestInputs(os.rel / \"Main.scala\" ->\n      s\"\"\"#!/usr/bin/env -S scala-cli shebang\n         |//> using jvm 11\n         |$lastUsingLine\n         |\n         |object Main {\n         |  def main(args: Array[String]): Unit = {\n         |    println(os.pwd)\n         |  }\n         |}\"\"\".stripMargin).fromRoot { root =>\n      val scalaFile = SourceScalaFile(root, os.sub / \"Main.scala\")\n      val result    = ScalaPreprocessor.preprocess(\n        scalaFile,\n        logger = TestLogger(),\n        allowRestrictedFeatures = true,\n        suppressWarningOptions = SuppressWarningOptions()\n      )(using ScalaCliInvokeData.dummy).get.getOrElse(sys.error(\"preprocessing failed\"))\n      expect(result.nonEmpty)\n      val directivesPositions = result.head.directivesPositions.get\n      expect(directivesPositions.startPos == 0 -> 0)\n      expect(directivesPositions.endPos == 3   -> lastUsingLine.length)\n    }\n  }\n\n  test(\"should respect using directives in a .sc file with the shebang line\") {\n    val depLine = \"//> using dep com.lihaoyi::os-lib::0.8.1\"\n\n    TestInputs(os.rel / \"sample.sc\" ->\n      s\"\"\"#!/usr/bin/env -S scala-cli shebang\n         |$depLine\n         |println(os.pwd)\n         |\"\"\".stripMargin).fromRoot { root =>\n      val scalaFile = Script(root, os.sub / \"sample.sc\", None)\n      val result    = ScriptPreprocessor.preprocess(\n        scalaFile,\n        logger = TestLogger(),\n        allowRestrictedFeatures = false,\n        suppressWarningOptions = SuppressWarningOptions()\n      )(using ScalaCliInvokeData.dummy).get.getOrElse(sys.error(\"preprocessing failed\"))\n      expect(result.nonEmpty)\n      val directivesPositions = result.head.directivesPositions.get\n      expect(directivesPositions.startPos == 0 -> 0)\n      expect(directivesPositions.endPos == 2   -> depLine.length)\n    }\n  }\n\n  val lastUsingLines: Seq[(String, String)] = Seq(\n    \"//> using dep com.lihaoyi::os-lib::0.8.1 com.lihaoyi::os-lib::0.8.1\" -> \"string literal\",\n    \"//> using scala 2.13.7\"                                              -> \"numerical string\",\n    \"//> using objectWrapper true\"                                        -> \"boolean literal\",\n    \"//> using objectWrapper\"                                             -> \"empty value literal\"\n  )\n\n  for ((lastUsingLine, typeName) <- lastUsingLines) do\n    test(s\"correct directive positions with $typeName\") {\n      TestInputs(os.rel / \"Main.scala\" ->\n        s\"\"\"#!/usr/bin/env -S scala-cli shebang\n           |//> using jvm 11\n           |$lastUsingLine\n           |\n           |object Main {\n           |  def main(args: Array[String]): Unit = {\n           |    println(os.pwd)\n           |  }\n           |}\"\"\".stripMargin).fromRoot { root =>\n        val scalaFile = SourceScalaFile(root, os.sub / \"Main.scala\")\n        val result    = ScalaPreprocessor.preprocess(\n          scalaFile,\n          logger = TestLogger(),\n          allowRestrictedFeatures = true,\n          suppressWarningOptions = SuppressWarningOptions()\n        )(using ScalaCliInvokeData.dummy).get.getOrElse(sys.error(\"preprocessing failed\"))\n        expect(result.nonEmpty)\n        val directivesPositions = result.head.directivesPositions.get\n        expect(directivesPositions.startPos == 0 -> 0)\n        expect(directivesPositions.endPos == 3   -> lastUsingLine.length)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/ScriptWrapperTests.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.Ops.EitherThrowOps\nimport scala.build.options.{\n  BuildOptions,\n  InternalOptions,\n  MaybeScalaVersion,\n  Platform,\n  ScalaOptions,\n  ScriptOptions\n}\nimport scala.build.tests.util.BloopServer\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo, Position, Positioned}\n\nclass ScriptWrapperTests extends TestUtil.ScalaCliBuildSuite {\n\n  def expectAppWrapper(wrapperName: String, path: os.Path): Unit = {\n    val generatedFileContent = os.read(path)\n    assert(\n      generatedFileContent.contains(s\"object $wrapperName extends App {\"),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n    assert(\n      !generatedFileContent.contains(s\"final class $wrapperName$$_\") &&\n      !generatedFileContent.contains(s\"object $wrapperName {\"),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n  }\n\n  def expectObjectWrapper(wrapperName: String, path: os.Path): Unit = {\n    val generatedFileContent = os.read(path)\n    assert(\n      generatedFileContent.contains(s\"object $wrapperName {\"),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n    assert(\n      !generatedFileContent.contains(s\"final class $wrapperName$$_\") &&\n      !generatedFileContent.contains(s\"object $wrapperName extends App {\"),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n  }\n\n  def expectClassWrapper(wrapperName: String, path: os.Path): Unit = {\n    val generatedFileContent = os.read(path)\n    assert(\n      generatedFileContent.contains(s\"final class $wrapperName$$_\"),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n    assert(\n      !generatedFileContent.contains(s\"object $wrapperName extends App {\") &&\n      !generatedFileContent.contains(s\"object $wrapperName {\"),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n  }\n\n  val buildThreads: BuildThreads               = BuildThreads.create()\n  def bloopConfigOpt: Option[BloopRifleConfig] = Some(BloopServer.bloopConfig)\n\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n\n  override def afterAll(): Unit = {\n    TestInputs.tryRemoveAll(extraRepoTmpDir)\n    buildThreads.shutdown()\n  }\n\n  val baseOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  val objectWrapperOptions = BuildOptions(\n    scriptOptions = ScriptOptions(\n      forceObjectWrapper = Some(true)\n    )\n  )\n  val scala213Options = BuildOptions(\n    scalaOptions = ScalaOptions(\n      scalaVersion = Some(MaybeScalaVersion(Some(\"2.13\")))\n    )\n  )\n  val platfromJsOptions = BuildOptions(\n    scalaOptions = ScalaOptions(\n      platform = Some(Positioned(List(Position.CommandLine()), Platform.JS))\n    )\n  )\n\n  test(s\"class wrapper for scala 3\") {\n    val inputs = TestInputs(\n      os.rel / \"script1.sc\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |\n           |def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / \"script2.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |\n          |println(\"Hello\")\n          |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        expect(maybeBuild.orThrow.success)\n        val projectDir = os.list(root / \".scala-build\").filter(\n          _.baseName.startsWith(root.baseName + \"_\")\n        )\n        expect(projectDir.size == 1)\n        expectClassWrapper(\n          \"script1\",\n          projectDir.head / \"src_generated\" / \"main\" / \"script1.scala\"\n        )\n        expectClassWrapper(\n          \"script2\",\n          projectDir.head / \"src_generated\" / \"main\" / \"script2.scala\"\n        )\n    }\n  }\n\n  for {\n    useDirectives                    <- Seq(true, false)\n    (directive, options, optionName) <- Seq(\n      (\"//> using object.wrapper\", objectWrapperOptions, \"--object-wrapper\"),\n      (\"//> using platform js\", platfromJsOptions, \"--js\")\n    )\n  } {\n    val inputs = TestInputs(\n      os.rel / \"script1.sc\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |${if (useDirectives) directive else \"\"}\n           |\n           |def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / \"script2.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |\n          |println(\"Hello\")\n          |\"\"\".stripMargin\n    )\n\n    test(\n      s\"object wrapper forced with ${if (useDirectives) directive else optionName}\"\n    ) {\n      inputs.withBuild(options.orElse(baseOptions), buildThreads, bloopConfigOpt) {\n        (root, _, maybeBuild) =>\n          expect(maybeBuild.orThrow.success)\n          val projectDir = os.list(root / \".scala-build\").filter(\n            _.baseName.startsWith(root.baseName + \"_\")\n          )\n          expect(projectDir.size == 1)\n          expectObjectWrapper(\n            \"script1\",\n            projectDir.head / \"src_generated\" / \"main\" / \"script1.scala\"\n          )\n          expectObjectWrapper(\n            \"script2\",\n            projectDir.head / \"src_generated\" / \"main\" / \"script2.scala\"\n          )\n      }\n    }\n  }\n\n  for {\n    useDirectives                    <- Seq(true, false)\n    (directive, options, optionName) <- Seq(\n      (\"//> using scala 2.13\", scala213Options, \"--scala 2.13\")\n    )\n  } {\n    val inputs = TestInputs(\n      os.rel / \"script1.sc\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |${if (useDirectives) directive else \"\"}\n           |\n           |def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / \"script2.sc\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |\n          |println(\"Hello\")\n          |\"\"\".stripMargin\n    )\n\n    test(\n      s\"App object wrapper forced with ${if (useDirectives) directive else optionName}\"\n    ) {\n      inputs.withBuild(options.orElse(baseOptions), buildThreads, bloopConfigOpt) {\n        (root, _, maybeBuild) =>\n          expect(maybeBuild.orThrow.success)\n          val projectDir = os.list(root / \".scala-build\").filter(\n            _.baseName.startsWith(root.baseName + \"_\")\n          )\n          expect(projectDir.size == 1)\n          expectAppWrapper(\n            \"script1\",\n            projectDir.head / \"src_generated\" / \"main\" / \"script1.scala\"\n          )\n          expectAppWrapper(\n            \"script2\",\n            projectDir.head / \"src_generated\" / \"main\" / \"script2.scala\"\n          )\n      }\n    }\n  }\n\n  for {\n    (targetDirective, enablingDirective) <- Seq(\n      (\"target.scala 3.2.2\", \"scala 3.2.2\"),\n      (\"target.platform scala-native\", \"platform scala-native\")\n    )\n  } {\n    val inputs = TestInputs(\n      os.rel / \"script1.sc\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |//> using $targetDirective\n           |//> using objectWrapper\n           |\n           |def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / \"script2.sc\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |//> using $enablingDirective\n           |\n           |println(\"Hello\")\n           |\"\"\".stripMargin\n    )\n\n    test(\n      s\"object wrapper with $targetDirective\"\n    ) {\n      inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n        (root, _, maybeBuild) =>\n          expect(maybeBuild.orThrow.success)\n          val projectDir = os.list(root / \".scala-build\").filter(\n            _.baseName.startsWith(root.baseName + \"_\")\n          )\n          expect(projectDir.size == 1)\n\n          expectObjectWrapper(\n            \"script1\",\n            projectDir.head / \"src_generated\" / \"main\" / \"script1.scala\"\n          )\n          expectObjectWrapper(\n            \"script2\",\n            projectDir.head / \"src_generated\" / \"main\" / \"script2.scala\"\n          )\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/SourceGeneratorTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.Console.println\nimport scala.build.Ops.EitherThrowOps\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.build.tests.util.BloopServer\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo, Position}\n\nclass SourceGeneratorTests extends TestUtil.ScalaCliBuildSuite {\n\n  val buildThreads = BuildThreads.create()\n\n  def bloopConfigOpt = Some(BloopServer.bloopConfig)\n\n  val extraRepoTmpDir = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories     = Directories.under(extraRepoTmpDir)\n\n  override def afterAll(): Unit = {\n    TestInputs.tryRemoveAll(extraRepoTmpDir)\n    buildThreads.shutdown()\n  }\n\n  val baseOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger()),\n      keepDiagnostics = true\n    )\n  )\n\n  private def normalizeContents(contents: String): String =\n    contents\n      .replaceAll(\n        \"ivy:file:[^\\\"]*scala-cli-tests-extra-repo[^\\\"]*/local-repo[^\\\"]*\",\n        \"ivy:file:.../scala-cli-tests-extra-repo/local-repo/...\"\n      )\n      .replaceAll(\n        \"ivy:file:[^\\\"]*\\\\.ivy2/local[^\\\"]*\",\n        \"ivy:file:.../.ivy2/local/\"\n      )\n      .replaceAll(\n        \"val scalaCliVersion = Some\\\\(\\\"[^\\\"]*\\\"\\\\)\",\n        \"val scalaCliVersion = Some\\\\(\\\"1.1.1-SNAPSHOT\\\"\\\\)\"\n      )\n      .replaceAll(\"\\\\\\\\\", \"\\\\\")\n      .linesWithSeparators\n      .filterNot(_.stripLeading().startsWith(\"/**\"))\n      .mkString\n\n  def initializeGit(\n    cwd: os.Path,\n    tag: String = \"test-inputs\",\n    gitUserName: String = \"testUser\",\n    gitUserEmail: String = \"testUser@scala-cli-tests.com\"\n  ): Unit = {\n    println(s\"Initializing git in $cwd...\")\n    os.proc(\"git\", \"init\").call(cwd = cwd)\n    println(s\"Setting git user.name to $gitUserName\")\n    os.proc(\"git\", \"config\", \"--local\", \"user.name\", gitUserName).call(cwd = cwd)\n    println(s\"Setting git user.email to $gitUserEmail\")\n    os.proc(\"git\", \"config\", \"--local\", \"user.email\", gitUserEmail).call(cwd = cwd)\n    println(s\"Adding $cwd to git...\")\n    os.proc(\"git\", \"add\", \".\").call(cwd = cwd)\n    println(s\"Doing an initial commit...\")\n    os.proc(\"git\", \"commit\", \"-m\", \"git init test inputs\").call(cwd = cwd)\n    println(s\"Tagging as $tag...\")\n    os.proc(\"git\", \"tag\", tag).call(cwd = cwd)\n    println(s\"Git initialized at $cwd\")\n  }\n\n  test(\"BuildInfo source generated\") {\n    val inputs = TestInputs(\n      os.rel / \"main.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |//> using option -Xasync\n          |//> using plugin org.wartremover:::wartremover:3.0.9\n          |//> using scala 3.2.2\n          |//> using jvm 11\n          |//> using mainClass Main\n          |//> using resourceDir ./resources\n          |//> using jar TEST1.jar TEST2.jar\n          |\n          |//> using buildInfo\n          |\n          |import scala.cli.build.BuildInfo\n          |\n          |object Main extends App {\n          |  println(s\"Scala version: ${BuildInfo.scalaVersion}\")\n          |  BuildInfo.Main.customJarsDecls.foreach(println)\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      initializeGit(root, \"v1.0.0\")\n\n      inputs.copy(forceCwd = Some(root))\n        .withBuild(baseOptions, buildThreads, bloopConfigOpt, skipCreatingSources = true) {\n          (root, _, maybeBuild) =>\n            expect(maybeBuild.orThrow.success)\n            val projectDir = os.list(root / \".scala-build\").filter(\n              _.baseName.startsWith(root.baseName + \"_\")\n            )\n            expect(projectDir.size == 1)\n            val buildInfoPath = projectDir.head / \"src_generated\" / \"main\" / \"BuildInfo.scala\"\n            expect(os.isFile(buildInfoPath))\n\n            val buildInfoContent = os.read(buildInfoPath)\n\n            assertNoDiff(\n              normalizeContents(buildInfoContent),\n              s\"\"\"package scala.cli.build\n                 |\n                 |object BuildInfo {\n                 |  val scalaVersion = \"3.2.2\"\n                 |  val platform = \"JVM\"\n                 |  val jvmVersion = Some(\"11\")\n                 |  val scalaJsVersion = None\n                 |  val jsEsVersion = None\n                 |  val scalaNativeVersion = None\n                 |  val mainClass = Some(\"Main\")\n                 |  val projectVersion = Some(\"1.0.0\")\n                 |  val scalaCliVersion = Some(\"1.1.1-SNAPSHOT\")\n                 |\n                 |  object Main {\n                 |    val sources = Seq(\"${root / \"main.scala\"}\")\n                 |    val scalacOptions = Seq(\"-Xasync\")\n                 |    val scalaCompilerPlugins = Seq(\"org.wartremover:wartremover_3.2.2:3.0.9\")\n                 |    val dependencies = Seq(\"com.lihaoyi:os-lib_3:0.9.1\")\n                 |    val resolvers = Seq(\"ivy:file:.../scala-cli-tests-extra-repo/local-repo/...\", \"https://repo1.maven.org/maven2\", \"ivy:file:.../.ivy2/local/\")\n                 |    val resourceDirs = Seq(\"${root / \"resources\"}\")\n                 |    val customJarsDecls = Seq(\"${root / \"TEST1.jar\"}\", \"${root / \"TEST2.jar\"}\")\n                 |  }\n                 |\n                 |  object Test {\n                 |    val sources = Nil\n                 |    val scalacOptions = Nil\n                 |    val scalaCompilerPlugins = Nil\n                 |    val dependencies = Nil\n                 |    val resolvers = Nil\n                 |    val resourceDirs = Nil\n                 |    val customJarsDecls = Nil\n                 |  }\n                 |}\n                 |\"\"\".stripMargin\n            )\n        }\n    }\n\n  }\n\n  test(\"BuildInfo for native\") {\n    val inputs = TestInputs(\n      os.rel / \"main.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |//> using option -Xasync\n           |//> using plugin org.wartremover:::wartremover:3.0.9\n           |//> using scala 3.2.2\n           |//> using jvm 11\n           |//> using mainClass Main\n           |//> using resourceDir ./resources\n           |//> using jar TEST1.jar TEST2.jar\n           |//> using platform scala-native\n           |//> using nativeVersion 0.4.6\n           |\n           |//> using buildInfo\n           |\n           |import scala.cli.build.BuildInfo\n           |\n           |object Main extends App {\n           |  println(s\"Scala version: $${BuildInfo.scalaVersion}\")\n           |  BuildInfo.Main.customJarsDecls.foreach(println)\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        expect(maybeBuild.orThrow.success)\n        val projectDir = os.list(root / \".scala-build\").filter(\n          _.baseName.startsWith(root.baseName + \"_\")\n        )\n        expect(projectDir.size == 1)\n        val buildInfoPath = projectDir.head / \"src_generated\" / \"main\" / \"BuildInfo.scala\"\n        expect(os.isFile(buildInfoPath))\n\n        val buildInfoContent = os.read(buildInfoPath)\n\n        assertNoDiff(\n          normalizeContents(buildInfoContent),\n          s\"\"\"package scala.cli.build\n             |\n             |object BuildInfo {\n             |  val scalaVersion = \"3.2.2\"\n             |  val platform = \"Native\"\n             |  val jvmVersion = None\n             |  val scalaJsVersion = None\n             |  val jsEsVersion = None\n             |  val scalaNativeVersion = Some(\"0.4.6\")\n             |  val mainClass = Some(\"Main\")\n             |  val projectVersion = None\n             |  val scalaCliVersion = Some(\"1.1.1-SNAPSHOT\")\n             |\n             |  object Main {\n             |    val sources = Seq(\"${root / \"main.scala\"}\")\n             |    val scalacOptions = Seq(\"-Xasync\")\n             |    val scalaCompilerPlugins = Seq(\"org.wartremover:wartremover_3.2.2:3.0.9\")\n             |    val dependencies = Seq(\"com.lihaoyi:os-lib_3:0.9.1\")\n             |    val resolvers = Seq(\"ivy:file:.../scala-cli-tests-extra-repo/local-repo/...\", \"https://repo1.maven.org/maven2\", \"ivy:file:.../.ivy2/local/\")\n             |    val resourceDirs = Seq(\"${root / \"resources\"}\")\n             |    val customJarsDecls = Seq(\"${root / \"TEST1.jar\"}\", \"${root / \"TEST2.jar\"}\")\n             |  }\n             |\n             |  object Test {\n             |    val sources = Nil\n             |    val scalacOptions = Nil\n             |    val scalaCompilerPlugins = Nil\n             |    val dependencies = Nil\n             |    val resolvers = Nil\n             |    val resourceDirs = Nil\n             |    val customJarsDecls = Nil\n             |  }\n             |}\n             |\"\"\".stripMargin\n        )\n    }\n  }\n\n  test(\"BuildInfo for js\") {\n    val inputs = TestInputs(\n      os.rel / \"main.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |//> using option -Xasync\n           |//> using plugin org.wartremover:::wartremover:3.0.9\n           |//> using scala 3.2.2\n           |//> using jvm 11\n           |//> using mainClass Main\n           |//> using resourceDir ./resources\n           |//> using jar TEST1.jar TEST2.jar\n           |//> using platform scala-js\n           |//> using jsVersion 1.13.1\n           |//> using jsEsVersionStr es2015\n           |\n           |//> using buildInfo\n           |\n           |import scala.cli.build.BuildInfo\n           |\n           |object Main extends App {\n           |  println(s\"Scala version: $${BuildInfo.scalaVersion}\")\n           |  BuildInfo.Main.customJarsDecls.foreach(println)\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        expect(maybeBuild.orThrow.success)\n        val projectDir = os.list(root / \".scala-build\").filter(\n          _.baseName.startsWith(root.baseName + \"_\")\n        )\n        expect(projectDir.size == 1)\n        val buildInfoPath = projectDir.head / \"src_generated\" / \"main\" / \"BuildInfo.scala\"\n        expect(os.isFile(buildInfoPath))\n\n        val buildInfoContent = os.read(buildInfoPath)\n\n        assertNoDiff(\n          normalizeContents(buildInfoContent),\n          s\"\"\"package scala.cli.build\n             |\n             |object BuildInfo {\n             |  val scalaVersion = \"3.2.2\"\n             |  val platform = \"JS\"\n             |  val jvmVersion = None\n             |  val scalaJsVersion = Some(\"1.13.1\")\n             |  val jsEsVersion = Some(\"es2015\")\n             |  val scalaNativeVersion = None\n             |  val mainClass = Some(\"Main\")\n             |  val projectVersion = None\n             |  val scalaCliVersion = Some(\"1.1.1-SNAPSHOT\")\n             |\n             |  object Main {\n             |    val sources = Seq(\"${root / \"main.scala\"}\")\n             |    val scalacOptions = Seq(\"-Xasync\")\n             |    val scalaCompilerPlugins = Seq(\"org.wartremover:wartremover_3.2.2:3.0.9\")\n             |    val dependencies = Seq(\"com.lihaoyi:os-lib_3:0.9.1\")\n             |    val resolvers = Seq(\"ivy:file:.../scala-cli-tests-extra-repo/local-repo/...\", \"https://repo1.maven.org/maven2\", \"ivy:file:.../.ivy2/local/\")\n             |    val resourceDirs = Seq(\"${root / \"resources\"}\")\n             |    val customJarsDecls = Seq(\"${root / \"TEST1.jar\"}\", \"${root / \"TEST2.jar\"}\")\n             |  }\n             |\n             |  object Test {\n             |    val sources = Nil\n             |    val scalacOptions = Nil\n             |    val scalaCompilerPlugins = Nil\n             |    val dependencies = Nil\n             |    val resolvers = Nil\n             |    val resourceDirs = Nil\n             |    val customJarsDecls = Nil\n             |  }\n             |}\n             |\"\"\".stripMargin\n        )\n    }\n  }\n\n  test(\"BuildInfo for Scala 2\") {\n    val inputs = TestInputs(\n      os.rel / \"main.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |//> using option -Xasync\n           |//> using plugin org.wartremover:::wartremover:3.0.9\n           |//> using scala 2.13.6\n           |//> using jvm 11\n           |//> using mainClass Main\n           |//> using resourceDir ./resources\n           |//> using jar TEST1.jar TEST2.jar\n           |\n           |//> using buildInfo\n           |\n           |import scala.cli.build.BuildInfo\n           |\n           |object Main extends App {\n           |  println(s\"Scala version: $${BuildInfo.scalaVersion}\")\n           |  BuildInfo.Main.customJarsDecls.foreach(println)\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        expect(maybeBuild.orThrow.success)\n        val projectDir = os.list(root / \".scala-build\").filter(\n          _.baseName.startsWith(root.baseName + \"_\")\n        )\n        expect(projectDir.size == 1)\n        val buildInfoPath = projectDir.head / \"src_generated\" / \"main\" / \"BuildInfo.scala\"\n        expect(os.isFile(buildInfoPath))\n\n        val buildInfoContent = os.read(buildInfoPath)\n\n        assertNoDiff(\n          normalizeContents(buildInfoContent),\n          s\"\"\"package scala.cli.build\n             |\n             |object BuildInfo {\n             |  val scalaVersion = \"2.13.6\"\n             |  val platform = \"JVM\"\n             |  val jvmVersion = Some(\"11\")\n             |  val scalaJsVersion = None\n             |  val jsEsVersion = None\n             |  val scalaNativeVersion = None\n             |  val mainClass = Some(\"Main\")\n             |  val projectVersion = None\n             |  val scalaCliVersion = Some(\"1.1.1-SNAPSHOT\")\n             |\n             |  object Main {\n             |    val sources = Seq(\"${root / \"main.scala\"}\")\n             |    val scalacOptions = Seq(\"-Xasync\")\n             |    val scalaCompilerPlugins = Seq(\"org.wartremover:wartremover_2.13.6:3.0.9\")\n             |    val dependencies = Seq(\"com.lihaoyi:os-lib_2.13:0.9.1\")\n             |    val resolvers = Seq(\"ivy:file:.../scala-cli-tests-extra-repo/local-repo/...\", \"https://repo1.maven.org/maven2\", \"ivy:file:.../.ivy2/local/\")\n             |    val resourceDirs = Seq(\"${root / \"resources\"}\")\n             |    val customJarsDecls = Seq(\"${root / \"TEST1.jar\"}\", \"${root / \"TEST2.jar\"}\")\n             |  }\n             |\n             |  object Test {\n             |    val sources = Nil\n             |    val scalacOptions = Nil\n             |    val scalaCompilerPlugins = Nil\n             |    val dependencies = Nil\n             |    val resolvers = Nil\n             |    val resourceDirs = Nil\n             |    val customJarsDecls = Nil\n             |  }\n             |}\n             |\"\"\".stripMargin\n        )\n    }\n  }\n\n  test(\"BuildInfo no git repository error\") {\n    val usingPrefix = \"//> using computeVersion \\\"\"\n    val usingValue  = \"git:tag\"\n    val usingSuffix = \"\\\"\"\n\n    val inputs = TestInputs(\n      os.rel / \"main.scala\" ->\n        s\"\"\"\n           |$usingPrefix$usingValue$usingSuffix\n           |//> using buildInfo\n           |\n           |import scala.cli.build.BuildInfo\n           |\n           |object Main extends App {\n           |  println(s\"Scala version: $${BuildInfo.projectVersion}\")\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {\n      (root, _, maybeBuild) =>\n        maybeBuild match {\n          case Left(buildException) =>\n            expect(buildException.positions.size == 1)\n            val position = buildException.positions.head\n\n            assertEquals(\n              position,\n              scala.build.Position.File(\n                Right(root / \"main.scala\"),\n                (1, usingPrefix.length),\n                (1, (usingPrefix + usingValue).length)\n              )\n            )\n            assertNoDiff(\n              buildException.message,\n              s\"BuildInfo generation error: $root doesn't look like a Git repository\"\n            )\n          case _ => fail(\"Build should fail\")\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/SourcesTests.scala",
    "content": "package scala.build.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.cache.Cache.Fetch\nimport coursier.cache.{ArchiveCache, ArtifactError, Cache}\nimport coursier.util.{Artifact, EitherT, Task}\nimport dependency.*\n\nimport java.io.File\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.Ops.*\nimport scala.build.errors.{UsingDirectiveValueNumError, UsingDirectiveWrongValueTypeError}\nimport scala.build.input.ScalaCliInvokeData\nimport scala.build.internal.ScalaJsLinkerConfig\nimport scala.build.options.{BuildOptions, Scope, SuppressWarningOptions}\nimport scala.build.preprocessing.Preprocessor\nimport scala.build.{CrossSources, Position, Sources}\nimport scala.concurrent.ExecutionContext\n\nclass SourcesTests extends TestUtil.ScalaCliBuildSuite {\n  def scalaVersion: String         = \"2.13.5\"\n  def scalaParams: ScalaParameters = ScalaParameters(scalaVersion)\n  def scalaBinaryVersion: String   = scalaParams.scalaBinaryVersion\n\n  given ScalaCliInvokeData = ScalaCliInvokeData.dummy\n\n  val preprocessors: Seq[Preprocessor] = Sources.defaultPreprocessors(\n    ArchiveCache().withCache(\n      new Cache[Task] {\n        def fetch: Fetch[Task] = _ => sys.error(\"shouldn't be used\")\n        def file(artifact: Artifact): EitherT[Task, ArtifactError, File] =\n          sys.error(\"shouldn't be used\")\n        def ec: ExecutionContext = sys.error(\"shouldn't be used\")\n      }\n    ),\n    None,\n    () => sys.error(\"shouldn't be used\")\n  )\n\n  for (\n    (singularAlias, pluralAlias) <-\n      List((\"lib\", \"libs\"), (\"dep\", \"deps\"), (\"dependency\", \"dependencies\"))\n  )\n    test(s\"dependencies in .scala - using aliases: $pluralAlias and $singularAlias\") {\n      val testInputs = TestInputs(\n        os.rel / \"something.scala\" ->\n          s\"\"\"//> using $pluralAlias org1:name1:1.1 org2::name2:2.2\n             |//> using $singularAlias org3:::name3:3.3\n             |import scala.collection.mutable\n             |\n             |object Something {\n             |  def a = 1\n             |}\n             |\"\"\".stripMargin\n      )\n      val expectedDeps = Seq(\n        dep\"org1:name1:1.1\",\n        dep\"org2::name2:2.2\",\n        dep\"org3:::name3:3.3\"\n      )\n      testInputs.withInputs { (root, inputs) =>\n        val (crossSources, _) =\n          CrossSources.forInputs(\n            inputs,\n            preprocessors,\n            TestLogger(),\n            SuppressWarningOptions()\n          ).orThrow\n\n        val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n        val sources       =\n          scopedSources.sources(\n            Scope.Main,\n            crossSources.sharedOptions(BuildOptions()),\n            root,\n            TestLogger()\n          )\n            .orThrow\n\n        val obtainedDeps = sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(\n          _.value\n        )\n\n        expect(obtainedDeps.sortBy(_.version) == expectedDeps.sortBy(_.version))\n        expect(sources.paths.length == 1)\n        val path = os.rel / \"something.scala\"\n        expect(sources.paths.map(_._2) == Seq(path))\n        expect(sources.inMemory.isEmpty)\n      }\n    }\n\n  test(\"dependencies in .scala - using witin tests\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.test.scala\" ->\n        \"\"\"//> using deps org1:name1:1.1 org2::name2:2.2\n          |//> using dep org3:::name3:3.3\n          |import scala.collection.mutable\n          |\n          |object Something {\n          |  def a = 1\n          |}\n          |\"\"\".stripMargin\n    )\n    val expectedDeps = Nil\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(\n        sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value) == expectedDeps\n      )\n      expect(sources.paths.isEmpty)\n      expect(sources.inMemory.isEmpty)\n    }\n  }\n\n  test(\"dependencies in .scala - using URL with query parameters\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.scala\" ->\n        \"\"\"| //> using file http://github.com/VirtusLab/scala-cli/blob/main/modules/dummy/amm/src/main/scala/AmmDummy.scala?version=3\n           |\n           |object Main {\n           |}\n           |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions(),\n          download = _ => Right(Array.empty[Byte])\n        ).orThrow\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        ).orThrow\n\n      expect(sources.paths.length == 1)\n      expect(sources.inMemory.length == 1)\n      expect(sources.inMemory.head.generatedRelPath.last == \"AmmDummy.scala\")\n    }\n  }\n\n  test(\"dependencies in .test.scala - using\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.test.scala\" ->\n        \"\"\"//> using deps org1:name1:1.1 org2::name2:2.2\n          |//> using dep org3:::name3:3.3\n          |import scala.collection.mutable\n          |\n          |object Something {\n          |  def a = 1\n          |}\n          |\"\"\".stripMargin\n    )\n    val expectedDeps = Nil\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(\n        sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value) == expectedDeps\n      )\n      expect(sources.paths.isEmpty)\n      expect(sources.inMemory.isEmpty)\n    }\n  }\n\n  test(\"dependencies in test/name.scala\") {\n    val files = Seq(\n      os.rel / \"test\" / \"something.scala\" ->\n        \"\"\"//> using deps org1:name1:1.1 org2::name2:2.2\n          |//> using dep org3:::name3:3.3\n          |import scala.collection.mutable\n          |\n          |object Something {\n          |  def a = 1\n          |}\n          |\"\"\".stripMargin\n    )\n    val testInputs = TestInputs(files, Seq(\".\"))\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value).isEmpty)\n      expect(sources.paths.isEmpty)\n      expect(sources.inMemory.isEmpty)\n    }\n  }\n\n  test(\"dependencies in .scala - //> using\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.scala\" ->\n        \"\"\"//> using dep org1:name1:1.1\n          |//> using dep org2::name2:2.2\n          |//> using dep org3:::name3:3.3\n          |import scala.collection.mutable\n          |\n          |object Something {\n          |  def a = 1\n          |}\n          |\"\"\".stripMargin\n    )\n    val expectedDeps = Seq(\n      dep\"org1:name1:1.1\",\n      dep\"org2::name2:2.2\",\n      dep\"org3:::name3:3.3\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(\n        sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value) == expectedDeps\n      )\n      expect(sources.paths.length == 1)\n      val path = os.rel / \"something.scala\"\n      expect(sources.paths.map(_._2) == Seq(path))\n      expect(sources.inMemory.isEmpty)\n    }\n  }\n\n  test(\"dependencies in .java - //> using\") {\n    val testInputs = TestInputs(\n      os.rel / \"Something.java\" ->\n        \"\"\"//> using dep org1:name1:1.1\n          |//> using dep org2::name2:2.2\n          |//> using dep org3:::name3:3.3\n          |\n          |public class Something {\n          |  public Int a = 1;\n          |}\n          |\"\"\".stripMargin\n    )\n    val expectedDeps = Seq(\n      dep\"org1:name1:1.1\",\n      dep\"org2::name2:2.2\",\n      dep\"org3:::name3:3.3\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(\n        sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value) == expectedDeps\n      )\n      expect(sources.paths.length == 1)\n      val path = os.rel / \"Something.java\"\n      expect(sources.paths.map(_._2) == Seq(path))\n      expect(sources.inMemory.isEmpty)\n    }\n  }\n\n  test(\"should skip SheBang in .sc and .scala\") {\n    val testInputs = TestInputs(\n      os.rel / \"something1.sc\" ->\n        \"\"\"#!/usr/bin/env scala-cli\n          |\n          |println(\"Hello World\")\"\"\".stripMargin,\n      os.rel / \"something2.sc\" ->\n        \"\"\"#!/usr/bin/scala-cli\n          |\n          |println(\"Hello World\")\"\"\".stripMargin,\n      os.rel / \"something3.sc\" ->\n        \"\"\"#!/usr/bin/scala-cli\n          |#! nix-shell -i scala-cli\n          |\n          |println(\"Hello World\")\"\"\".stripMargin,\n      os.rel / \"something4.sc\" ->\n        \"\"\"#!/usr/bin/scala-cli\n          |#! nix-shell -i scala-cli\n          |\n          |!#\n          |\n          |println(\"Hello World\")\"\"\".stripMargin,\n      os.rel / \"something5.sc\" ->\n        \"\"\"#!/usr/bin/scala-cli\n          |\n          |println(\"Hello World #!\")\"\"\".stripMargin,\n      os.rel / \"multiline.sc\" ->\n        \"\"\"#!/usr/bin/scala-cli\n          |# comment\n          |VAL=1\n          |!#\n          |\n          |println(\"Hello World #!\")\"\"\".stripMargin,\n      os.rel / \"hasBangHashInComment.sc\" ->\n        \"\"\"#!/usr/bin/scala-cli\n          |\n          |\n          |\n          |\n          |println(\"Hello World !#\")\"\"\".stripMargin\n    )\n    val expectedParsedCodes = Seq(\n      \"\"\"\n        |println(\"Hello World\")\"\"\".stripMargin,\n      \"\"\"\n        |println(\"Hello World\")\"\"\".stripMargin,\n      \"\"\"\n        |\n        |\n        |println(\"Hello World\")\"\"\".stripMargin,\n      \"\"\"\n        |\n        |\n        |\n        |println(\"Hello World\")\"\"\".stripMargin,\n      \"\"\"\n        |\n        |println(\"Hello World #!\")\"\"\".stripMargin,\n      \"\"\"\n        |\n        |\n        |\n        |\n        |println(\"Hello World #!\")\"\"\".stripMargin,\n      \"\"\"\n        |\n        |\n        |\n        |\n        |println(\"Hello World !#\")\"\"\".stripMargin\n    )\n\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      val parsedCodes: Seq[String] =\n        sources.inMemory.map(_.content).map(s => new String(s, StandardCharsets.UTF_8))\n\n      parsedCodes.zip(expectedParsedCodes).foreach { case (parsedCode, expectedCode) =>\n        showDiff(parsedCode, expectedCode)\n        expect(parsedCode.contains(expectedCode))\n      }\n    }\n  }\n  def showDiff(parsed: String, expected: String): Unit = {\n    if (!parsed.contains(expected))\n      for (((p, e), i) <- (parsed zip expected).zipWithIndex) {\n        val ps = TestUtil.c2s(p)\n        val es = TestUtil.c2s(e)\n        if (ps != es)\n          System.err.printf(\"%2d: [%s]!=[%s]\\n\", i, ps, es)\n      }\n  }\n\n  test(\"dependencies in .sc - using\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.sc\" ->\n        \"\"\"//> using deps org1:name1:1.1 org2::name2:2.2 org3:::name3:3.3\n          |import scala.collection.mutable\n          |\n          |def a = 1\n          |\"\"\".stripMargin\n    )\n    val expectedDeps = Seq(\n      dep\"org1:name1:1.1\",\n      dep\"org2::name2:2.2\",\n      dep\"org3:::name3:3.3\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(\n        sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value) == expectedDeps\n      )\n      expect(sources.paths.isEmpty)\n      expect(sources.inMemory.length == 1)\n      val path = os.rel / \"something.scala\"\n      expect(sources.inMemory.map(_.generatedRelPath) == Seq(path))\n    }\n  }\n\n  test(\"dependencies in .sc - //> using\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.sc\" ->\n        \"\"\"//> using dep org1:name1:1.1\n          |//> using dep org2::name2:2.2\n          |//> using dep org3:::name3:3.3\n          |import scala.collection.mutable\n          |\n          |def a = 1\n          |\"\"\".stripMargin\n    )\n    val expectedDeps = Seq(\n      dep\"org1:name1:1.1\",\n      dep\"org2::name2:2.2\",\n      dep\"org3:::name3:3.3\"\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      expect(\n        sources.buildOptions.classPathOptions.extraDependencies.toSeq.map(_.value) == expectedDeps\n      )\n      expect(sources.paths.isEmpty)\n      expect(sources.inMemory.length == 1)\n      val path = os.rel / \"something.scala\"\n      expect(sources.inMemory.map(_.generatedRelPath) == Seq(path))\n    }\n  }\n\n  test(\"java props in using directives\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.sc\" ->\n        \"\"\"//> using javaProp foo1\n          |//> using javaProp foo2=bar2\n          |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n      val javaOpts = sources.buildOptions.javaOptions.javaOpts.toSeq.sortBy(_.toString)\n\n      val path = root / \"something.sc\"\n      expect(\n        javaOpts.head.value.value == \"-Dfoo1\",\n        javaOpts.head.positions == Seq(Position.File(Right(path), (0, 19), (0, 23))),\n        javaOpts(1).value.value == \"-Dfoo2=bar2\",\n        javaOpts(1).positions == Seq(Position.File(Right(path), (1, 19), (1, 28)))\n      )\n    }\n  }\n\n  test(\"java -XX:* options in using directives\") {\n    val (opt1, opt2, opt3) = (\n      \"-XX:+UnlockExperimentalVMOptions\",\n      \"-XX:+AlwaysPreTouch\",\n      \"-XX:+UseParallelGC\"\n    )\n    val scriptPath = os.rel / \"something.sc\"\n    val testInputs = TestInputs(\n      scriptPath ->\n        s\"\"\"//> using javaOpt $opt1 $opt2 $opt3\n           |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n      val javaOpts = sources.buildOptions.javaOptions.javaOpts.toSeq.sortBy(_.toString)\n\n      val scriptAbsolutePath = root / scriptPath\n      val startPosX          = 0\n      val startPosY1         = 18\n      expect(\n        javaOpts.head.value.value == opt1,\n        javaOpts.head.positions == Seq(Position.File(\n          Right(scriptAbsolutePath),\n          (startPosX, startPosY1),\n          (startPosX, startPosY1 + opt1.length)\n        ))\n      )\n      val startPosY2 = startPosY1 + opt1.length + 1\n      expect(\n        javaOpts.drop(1).head.value.value == opt2,\n        javaOpts.drop(1).head.positions == Seq(Position.File(\n          Right(scriptAbsolutePath),\n          (startPosX, startPosY2),\n          (startPosX, startPosY2 + opt2.length)\n        ))\n      )\n      val startPosY3 = startPosY2 + opt2.length + 1\n      expect(\n        javaOpts.drop(2).head.value.value == opt3,\n        javaOpts.drop(2).head.positions == Seq(Position.File(\n          Right(scriptAbsolutePath),\n          (startPosX, startPosY3),\n          (startPosX, startPosY3 + opt3.length)\n        ))\n      )\n    }\n  }\n\n  test(\"js options in using directives\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.sc\" ->\n        \"\"\"//> using jsVersion 1.8.0\n          |//> using jsMode mode\n          |//> using jsNoOpt\n          |//> using jsModuleKind commonjs\n          |//> using jsCheckIr true\n          |//> using jsEmitSourceMaps true\n          |//> using jsDom true\n          |//> using jsHeader \"#!/usr/bin/env node\\n\"\n          |//> using jsAllowBigIntsForLongs true\n          |//> using jsAvoidClasses false\n          |//> using jsAvoidLetsAndConsts false\n          |//> using jsModuleSplitStyleStr smallestmodules\n          |//> using jsEsVersionStr es2017\n          |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (root, inputs) =>\n      val (crossSources, _) =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        ).orThrow\n\n      val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow\n      val sources       =\n        scopedSources.sources(\n          Scope.Main,\n          crossSources.sharedOptions(BuildOptions()),\n          root,\n          TestLogger()\n        )\n          .orThrow\n\n      val jsOptions = sources.buildOptions.scalaJsOptions\n      val jsConfig  = jsOptions.linkerConfig(TestLogger())\n      expect(\n        jsOptions.version.contains(\"1.8.0\"),\n        jsOptions.mode.nameOpt.contains(\"mode\"),\n        jsOptions.moduleKindStr.contains(\"commonjs\"),\n        jsOptions.checkIr.contains(true),\n        jsOptions.emitSourceMaps,\n        jsOptions.dom.contains(true),\n        jsOptions.noOpt.contains(true)\n      )\n      expect(\n        jsConfig.moduleKind == ScalaJsLinkerConfig.ModuleKind.CommonJSModule,\n        jsConfig.checkIR,\n        jsConfig.sourceMap,\n        jsConfig.jsHeader.contains(\"#!/usr/bin/env node\\n\"),\n        jsConfig.esFeatures.allowBigIntsForLongs,\n        !jsConfig.esFeatures.avoidClasses,\n        !jsConfig.esFeatures.avoidLetsAndConsts,\n        jsConfig.esFeatures.esVersion == \"ES2017\",\n        jsConfig.moduleSplitStyle == ScalaJsLinkerConfig.ModuleSplitStyle.SmallestModules\n      )\n    }\n  }\n\n  test(\"js options in using directives failure - multiple values\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.sc\" ->\n        \"\"\"//> using jsVersion 1.8.0 2.3.4\n          |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (_, inputs) =>\n      val crossSources =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )\n      crossSources match {\n        case Left(_: UsingDirectiveValueNumError) =>\n        case o                                    => fail(\"Exception expected\", clues(o))\n      }\n    }\n  }\n\n  test(\"js options in using directives failure - not a boolean\") {\n    val testInputs = TestInputs(\n      os.rel / \"something.sc\" ->\n        \"\"\"//> using jsDom fasle\n          |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (_, inputs) =>\n      val crossSources =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )\n      crossSources match {\n        case Left(_: UsingDirectiveWrongValueTypeError) =>\n        case o                                          => fail(\"Exception expected\", clues(o))\n      }\n    }\n  }\n\n  test(\"CrossSources.forInputs respects the order of inputs passed\") {\n    val inputArgs @ Seq(project, main, abc, message) =\n      Seq(\"project.scala\", \"Main.scala\", \"Abc.scala\", \"Message.scala\")\n    val testInputs = TestInputs(\n      os.rel / project ->\n        \"\"\"//> using dep com.lihaoyi::os-lib::0.8.1\n          |//> using file Message.scala\n          |\"\"\".stripMargin,\n      os.rel / main ->\n        \"\"\"object Main extends App {\n          |  println(Message(Abc.hello))\n          |}\n          |\"\"\".stripMargin,\n      os.rel / abc ->\n        \"\"\"object Abc {\n          |  val hello = \"Hello\"\n          |}\n          |\"\"\".stripMargin,\n      os.rel / message ->\n        \"\"\"case class Message(value: String)\n          |\"\"\".stripMargin\n    )\n    testInputs.withInputs { (_, inputs) =>\n      val crossSourcesResult =\n        CrossSources.forInputs(\n          inputs,\n          preprocessors,\n          TestLogger(),\n          SuppressWarningOptions()\n        )\n      assert(crossSourcesResult.isRight)\n      val CrossSources(onDiskSources, _, _, _, _, _) =\n        crossSourcesResult.map(_._1)\n          .getOrElse(sys.error(\"should not happen\"))\n      val onDiskPaths = onDiskSources.map(_.value._1.last)\n      expect(onDiskPaths == inputArgs)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/TestInputs.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleConfig\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.compiler.{BloopCompilerMaker, SimpleScalaCompilerMaker}\nimport scala.build.errors.BuildException\nimport scala.build.input.{Inputs, ScalaCliInvokeData}\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.build.{Build, BuildThreads, Builds, Logger}\nimport scala.util.Try\nimport scala.util.control.NonFatal\n\nfinal case class TestInputs(\n  files: Seq[(os.RelPath, String)],\n  inputArgs: Seq[String] = Seq.empty,\n  forceCwd: Option[os.Path] = None\n) {\n  def withInputs[T](f: (os.Path, Inputs) => T): T =\n    withCustomInputs(false, None)(f)\n\n  def fromRoot[T](f: os.Path => T, skipCreatingSources: Boolean = false): T =\n    TestInputs.withTmpDir(\"scala-cli-tests-\", forceCwd) { tmpDir =>\n      if skipCreatingSources then f(tmpDir)\n      else {\n        for ((relPath, content) <- files) {\n          val path = tmpDir / relPath\n          os.write(path, content.getBytes(StandardCharsets.UTF_8), createFolders = true)\n        }\n\n        f(tmpDir)\n      }\n    }\n\n  def withCustomInputs[T](\n    viaDirectory: Boolean,\n    forcedWorkspaceOpt: Option[os.FilePath],\n    skipCreatingSources: Boolean = false\n  )(\n    f: (os.Path, Inputs) => T\n  ): T =\n    fromRoot(\n      { tmpDir =>\n        val inputArgs0 =\n          if (viaDirectory) Seq(tmpDir.toString)\n          else if (inputArgs.isEmpty) files.map(_._1.toString)\n          else inputArgs\n        val res = Inputs(\n          inputArgs0,\n          tmpDir,\n          forcedWorkspace = forcedWorkspaceOpt.map(_.resolveFrom(tmpDir)),\n          allowRestrictedFeatures = true,\n          extraClasspathWasPassed = false\n        )(using ScalaCliInvokeData.dummy)\n        res match {\n          case Left(err)     => throw new Exception(err)\n          case Right(inputs) => f(tmpDir, inputs)\n        }\n      },\n      skipCreatingSources\n    )\n\n  def withLoadedBuild[T](\n    options: BuildOptions,\n    buildThreads: BuildThreads, // actually only used when bloopConfigOpt is non-empty\n    bloopConfigOpt: Option[BloopRifleConfig],\n    fromDirectory: Boolean = false\n  )(f: (os.Path, Inputs, Build) => T): T =\n    withBuild(options, buildThreads, bloopConfigOpt, fromDirectory)((p, i, maybeBuild) =>\n      maybeBuild match {\n        case Left(e)  => throw e\n        case Right(b) => f(p, i, b)\n      }\n    )\n\n  def withLoadedBuilds[T](\n    options: BuildOptions,\n    buildThreads: BuildThreads, // actually only used when bloopConfigOpt is non-empty\n    bloopConfigOpt: Option[BloopRifleConfig],\n    fromDirectory: Boolean = false\n  )(f: (os.Path, Inputs, Builds) => T): T =\n    withBuilds(options, buildThreads, bloopConfigOpt, fromDirectory)((p, i, builds) =>\n      builds match {\n        case Left(e)  => throw e\n        case Right(b) => f(p, i, b)\n      }\n    )\n\n  def withBuilds[T](\n    options: BuildOptions,\n    buildThreads: BuildThreads, // actually only used when bloopConfigOpt is non-empty\n    bloopConfigOpt: Option[BloopRifleConfig],\n    fromDirectory: Boolean = false,\n    buildTests: Boolean = true,\n    actionableDiagnostics: Boolean = false,\n    skipCreatingSources: Boolean = false,\n    logger: Option[Logger] = None\n  )(f: (os.Path, Inputs, Either[BuildException, Builds]) => T): T =\n    withCustomInputs(fromDirectory, None, skipCreatingSources) { (root, inputs) =>\n      val compilerMaker = bloopConfigOpt match {\n        case Some(bloopConfig) =>\n          new BloopCompilerMaker(\n            _ => Right(bloopConfig),\n            buildThreads.bloop,\n            strictBloopJsonCheck = true,\n            offline = false\n          )\n        case None =>\n          SimpleScalaCompilerMaker(\"java\", Nil)\n      }\n      val log    = logger.getOrElse(TestLogger())\n      val builds =\n        Build.build(\n          inputs,\n          options,\n          compilerMaker,\n          None,\n          log,\n          crossBuilds = false,\n          buildTests = buildTests,\n          partial = None,\n          actionableDiagnostics = Some(actionableDiagnostics)\n        )(using ScalaCliInvokeData.dummy)\n      f(root, inputs, builds)\n    }\n\n  def withBuild[T](\n    options: BuildOptions,\n    buildThreads: BuildThreads, // actually only used when bloopConfigOpt is non-empty\n    bloopConfigOpt: Option[BloopRifleConfig],\n    fromDirectory: Boolean = false,\n    buildTests: Boolean = true,\n    actionableDiagnostics: Boolean = false,\n    scope: Scope = Scope.Main,\n    skipCreatingSources: Boolean = false,\n    logger: Option[Logger] = None\n  )(f: (os.Path, Inputs, Either[BuildException, Build]) => T): T =\n    withBuilds(\n      options,\n      buildThreads,\n      bloopConfigOpt,\n      fromDirectory,\n      buildTests = buildTests,\n      actionableDiagnostics = actionableDiagnostics,\n      skipCreatingSources = skipCreatingSources,\n      logger = logger\n    ) {\n      (p, i, builds) =>\n        f(\n          p,\n          i,\n          builds.map(_.get(scope).getOrElse(sys.error(s\"No ${scope.name} build found\")))\n        )\n    }\n}\n\nobject TestInputs {\n\n  def apply(files: (os.RelPath, String)*): TestInputs =\n    TestInputs(files, Nil)\n\n  def withTmpDir[T](prefix: String, forceCwd: Option[os.Path] = None)(f: os.Path => T): T =\n    forceCwd match {\n      case Some(path) => f(path)\n      case None       =>\n        val tmpDir = os.temp.dir(prefix = prefix)\n        try f(tmpDir)\n        finally tryRemoveAll(tmpDir)\n    }\n\n  def tryRemoveAll(f: os.Path): Unit =\n    try os.remove.all(f)\n    catch {\n      case ex: java.nio.file.FileSystemException =>\n        System.err.println(s\"Could not remove $f ($ex), will try to remove it upon JVM shutdown.\")\n        System.err.println(s\"find $f = '${Try(os.walk(f))}'\")\n        Runtime.getRuntime.addShutdownHook(\n          new Thread(\"remove-dir-windows\") {\n            setDaemon(true)\n            override def run(): Unit =\n              try os.remove.all(f)\n              catch {\n                case NonFatal(e) =>\n                  System.err.println(s\"Caught $e while trying to remove $f, ignoring it.\")\n              }\n          }\n        )\n    }\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/TestLogger.scala",
    "content": "package scala.build.tests\n\nimport bloop.rifle.BloopRifleLogger\nimport coursier.cache.CacheLogger\nimport coursier.cache.loggers.{FallbackRefreshDisplay, RefreshLogger}\nimport org.scalajs.logging.{Logger as ScalaJsLogger, NullLogger}\n\nimport java.io.PrintStream\n\nimport scala.build.Logger\nimport scala.build.errors.{BuildException, Diagnostic}\nimport scala.build.internals.FeatureType\nimport scala.collection.mutable.ListBuffer\nimport scala.scalanative.build as sn\n\n/** Logger that records all message() and log() calls for test assertions. */\nfinal class RecordingLogger(delegate: Logger = TestLogger()) extends Logger {\n  val messages: ListBuffer[String] = ListBuffer.empty\n\n  override def error(message: String): Unit      = delegate.error(message)\n  override def message(message: => String): Unit = {\n    val msg = message\n    messages += msg\n    delegate.message(msg)\n  }\n  override def log(s: => String): Unit = {\n    val msg = s\n    messages += msg\n    delegate.log(msg)\n  }\n  override def log(s: => String, debug: => String): Unit         = delegate.log(s, debug)\n  override def debug(s: => String): Unit                         = delegate.debug(s)\n  override def log(diagnostics: Seq[Diagnostic]): Unit           = delegate.log(diagnostics)\n  override def log(ex: BuildException): Unit                     = delegate.log(ex)\n  override def debug(ex: BuildException): Unit                   = delegate.debug(ex)\n  override def exit(ex: BuildException): Nothing                 = delegate.exit(ex)\n  override def coursierLogger(message: String): CacheLogger      = delegate.coursierLogger(message)\n  override def bloopRifleLogger: BloopRifleLogger                = delegate.bloopRifleLogger\n  override def scalaJsLogger: ScalaJsLogger                      = delegate.scalaJsLogger\n  override def scalaNativeTestLogger: sn.Logger                  = delegate.scalaNativeTestLogger\n  override def scalaNativeCliInternalLoggerOptions: List[String] =\n    delegate.scalaNativeCliInternalLoggerOptions\n  override def compilerOutputStream: PrintStream = delegate.compilerOutputStream\n  override def verbosity: Int                    = delegate.verbosity\n  override def experimentalWarning(featureName: String, featureType: FeatureType): Unit =\n    delegate.experimentalWarning(featureName, featureType)\n  override def flushExperimentalWarnings: Unit = delegate.flushExperimentalWarnings\n}\n\ncase class TestLogger(info: Boolean = true, debug: Boolean = false) extends Logger {\n  override def log(diagnostics: Seq[Diagnostic]): Unit = {\n    diagnostics.foreach { d =>\n      System.err.println(d.positions.map(_.render()).mkString(\"/\") ++ \": \" ++ d.message)\n    }\n  }\n\n  def error(message: String): Unit =\n    System.err.println(message)\n  def message(message: => String): Unit =\n    if (info)\n      System.err.println(message)\n  def log(s: => String): Unit =\n    if (info)\n      System.err.println(s)\n  def log(s: => String, debug: => String): Unit =\n    if (this.debug)\n      System.err.println(debug)\n    else if (info)\n      System.err.println(s)\n  def debug(s: => String): Unit =\n    if (debug)\n      System.err.println(s)\n\n  def log(ex: BuildException): Unit =\n    System.err.println(ex.getMessage)\n  def debug(ex: BuildException): Unit =\n    debug(ex.getMessage)\n  def exit(ex: BuildException): Nothing =\n    throw new Exception(ex)\n\n  def coursierLogger(message: String): CacheLogger =\n    RefreshLogger.create(new FallbackRefreshDisplay)\n\n  def bloopRifleLogger: BloopRifleLogger =\n    if (debug)\n      new BloopRifleLogger {\n        def bloopBspStderr: Option[java.io.OutputStream] = Some(System.err)\n        def bloopBspStdout: Option[java.io.OutputStream] = Some(System.out)\n        def bloopCliInheritStderr: Boolean               = true\n        def bloopCliInheritStdout: Boolean               = true\n        def debug(msg: => String): Unit                  = {\n          System.err.println(msg)\n        }\n        def debug(msg: => String, ex: Throwable): Unit = {\n          System.err.println(msg)\n          if (ex != null) ex.printStackTrace(System.err)\n        }\n        def error(msg: => String, ex: Throwable): Unit = {\n          System.err.println(msg)\n          if (ex != null) ex.printStackTrace(System.err)\n        }\n        def error(msg: => String): Unit = System.err.println(msg)\n        def info(msg: => String): Unit  =\n          System.err.println(msg)\n      }\n    else\n      BloopRifleLogger.nop\n  def scalaJsLogger: ScalaJsLogger =\n    NullLogger\n  def scalaNativeTestLogger: sn.Logger =\n    sn.Logger.nullLogger\n  def scalaNativeCliInternalLoggerOptions: List[String] =\n    List()\n\n  def compilerOutputStream: PrintStream = System.err\n\n  def verbosity: Int =\n    if debug then 2\n    else if info then 0\n    else -1\n\n  override def experimentalWarning(featureName: String, featureType: FeatureType): Unit =\n    System.err.println(s\"Experimental $featureType `$featureName` used\")\n\n  override def flushExperimentalWarnings: Unit = ()\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/TestUtil.scala",
    "content": "package scala.build.tests\n\nimport munit.AnyFixture\nimport munit.Assertions.assertEquals\n\nimport java.util.concurrent.TimeUnit\n\nimport scala.build.options.{BuildOptions, Platform}\nimport scala.build.{Build, Positioned}\nimport scala.concurrent.duration.FiniteDuration\n\nobject TestUtil {\n  abstract class ScalaCliBuildSuite extends munit.FunSuite {\n    extension (munitContext: BeforeEach | AfterEach) {\n      def locationAbsolutePath: os.Path =\n        os.Path {\n          (munitContext match {\n            case beforeEach: BeforeEach => beforeEach.test\n            case afterEach: AfterEach   => afterEach.test\n          }).location.path\n        }\n    }\n    override def munitTimeout             = new FiniteDuration(120, TimeUnit.SECONDS)\n    val testStartEndLogger: Fixture[Unit] = new Fixture[Unit](\"files\") {\n      def apply(): Unit = ()\n\n      override def beforeEach(context: BeforeEach): Unit = {\n        val fileName = context.locationAbsolutePath.baseName\n        System.err.println(\n          s\">==== ${Console.CYAN}Running '${context.test.name}' from $fileName${Console.RESET}\"\n        )\n      }\n\n      override def afterEach(context: AfterEach): Unit = {\n        val fileName = context.locationAbsolutePath.baseName\n        System.err.println(\n          s\"X==== ${Console.CYAN}Finishing '${context.test.name}' from $fileName${Console.RESET}\"\n        )\n      }\n    }\n    override def munitFixtures: Seq[AnyFixture[?]] = List(testStartEndLogger)\n  }\n\n  val isCI: Boolean = System.getenv(\"CI\") != null\n\n  implicit class TestBuildOps(private val build: Build) extends AnyVal {\n    private def successfulBuild: Build.Successful =\n      build.successfulOpt.getOrElse {\n        sys.error(\"Compilation failed\")\n      }\n    def generated(): Seq[os.RelPath] =\n      os.walk(successfulBuild.output)\n        .filter(os.isFile(_))\n        .map(_.relativeTo(successfulBuild.output))\n    def assertGeneratedEquals(expected: String*): Unit = {\n      val generated0 = generated()\n      assert(\n        generated0.map(_.toString).toSet == expected.toSet, {\n          pprint.log(generated0.map(_.toString).sorted)\n          pprint.log(expected.sorted)\n          \"\"\n        }\n      )\n    }\n\n    def assertNoDiagnostics(): Unit = assertEquals(build.diagnostics.toSeq.flatten, Nil)\n\n  }\n\n  implicit class TestBuildOptionsOps(private val options: BuildOptions) extends AnyVal {\n    def enableJs: BuildOptions =\n      options.copy(\n        scalaOptions = options.scalaOptions.copy(\n          platform = Some(Positioned.none(Platform.JS))\n        )\n      )\n    def enableNative: BuildOptions =\n      options.copy(\n        scalaOptions = options.scalaOptions.copy(\n          platform = Some(Positioned.none(Platform.Native))\n        )\n      )\n  }\n\n  implicit class TestAnyOps[T](private val x: T) extends AnyVal {\n    def is(expected: T): Unit =\n      assert(\n        x == expected, {\n          pprint.log(x)\n          pprint.log(expected)\n          \"Assertion failed\"\n        }\n      )\n  }\n\n  def c2s(c: Char): String = c match {\n    case '\\r' => \"\\\\r\"\n    case '\\n' => \"\\\\n\"\n    case s    => s\"$s\"\n  }\n\n  lazy val cs: String = Constants.cs\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/markdown/MarkdownCodeBlockTests.scala",
    "content": "package scala.build.tests.markdown\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.Position\nimport scala.build.errors.{BuildException, MarkdownUnclosedBackticksError}\nimport scala.build.internal.markdown.MarkdownCodeBlock\nimport scala.build.tests.TestUtil\nimport scala.build.tests.markdown.MarkdownTestUtil.*\n\nclass MarkdownCodeBlockTests extends TestUtil.ScalaCliBuildSuite {\n  test(\"no code blocks are extracted from markdown if none are present\") {\n    val markdown: String =\n      \"\"\"\n        |# Heading\n        |Lorem ipsum dolor sit amet,\n        |consectetur adipiscing elit,\n        |\n        |## Subheading\n        |sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n        |Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n        |Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n        |Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n        |\"\"\".stripMargin\n    val path = os.sub / \"Example.md\"\n    expect(MarkdownCodeBlock.findCodeBlocks(path, markdown) == Right(Seq.empty))\n  }\n\n  test(\"a simple Scala code block is extracted correctly from markdown\") {\n    val code     = \"\"\"println(\"Hello\")\"\"\"\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = PlainScalaInfo,\n        body = code,\n        startLine = 3,\n        endLine = 3\n      )\n    val path         = os.sub / \"Example.md\"\n    val actualResult =\n      MarkdownCodeBlock.findCodeBlocks(path, markdown)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    expect(actualResult == expectedResult)\n  }\n\n  test(\"shebang line is ignored in plain scala code blocks\") {\n    val code     = \"\"\"println(\"Hello\")\"\"\".stripMargin\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala\n         |#!/usr/bin/env -S scala-cli shebang\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = PlainScalaInfo,\n        body = \"\\n\" + code,\n        startLine = 3,\n        endLine = 4\n      )\n    val path                            = os.sub / \"Example.md\"\n    val actualResult: MarkdownCodeBlock =\n      MarkdownCodeBlock.findCodeBlocks(path, markdown)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    showDiffs(actualResult, expectedResult)\n    expect(actualResult == expectedResult)\n  }\n\n  test(\"end-of-shebang token allowed in scala code\") {\n    val code     = \"\"\"println(\"Hello !#\")\"\"\".stripMargin\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala\n         |#!/usr/bin/env -S scala-cli shebang\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = PlainScalaInfo,\n        body = \"\\n\" + code,\n        startLine = 3,\n        endLine = 4\n      )\n    val path                            = os.sub / \"Example.md\"\n    val actualResult: MarkdownCodeBlock =\n      MarkdownCodeBlock.findCodeBlocks(path, markdown)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    showDiffs(actualResult, expectedResult)\n    expect(actualResult == expectedResult)\n  }\n\n  test(\"a raw Scala code block is extracted correctly from markdown\") {\n    val code = \"\"\"object Main extends App {\n                 |  println(\"Hello\")\n                 |}\"\"\".stripMargin\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala raw\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = RawScalaInfo,\n        body = code,\n        startLine = 3,\n        endLine = 5\n      )\n    val path         = os.sub / \"Example.md\"\n    val actualResult =\n      MarkdownCodeBlock.findCodeBlocks(path, markdown)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    expect(actualResult == expectedResult)\n  }\n\n  test(\"shebang line is ignored in raw scala code blocks\") {\n    val code =\n      \"\"\"object Main extends App {\n        |  println(\"Hello\")\n        |}\"\"\".stripMargin\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala raw\n         |#!/usr/bin/env -S scala-cli shebang\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = RawScalaInfo,\n        body = \"\\n\" + code,\n        startLine = 3,\n        endLine = 6\n      )\n    val path                            = os.sub / \"Example.md\"\n    val actualResult: MarkdownCodeBlock =\n      MarkdownCodeBlock.findCodeBlocks(path, markdown)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    showDiffs(actualResult, expectedResult)\n    expect(actualResult == expectedResult)\n  }\n\n  test(\"a test Scala snippet is extracted correctly from markdown\") {\n    val code =\n      \"\"\"//> using dep org.scalameta::munit:0.7.29\n        |class Test extends munit.FunSuite {\n        |  assert(true)\n        |}\"\"\".stripMargin\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala test\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = TestScalaInfo,\n        body = code,\n        startLine = 3,\n        endLine = 6\n      )\n    val path         = os.sub / \"Example.md\"\n    val actualResult =\n      MarkdownCodeBlock.findCodeBlocks(path, markdown)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    expect(actualResult == expectedResult)\n  }\n\n  test(\"a Scala code block is skipped when it's tagged as `ignore` in markdown\") {\n    val code     = \"\"\"println(\"Hello\")\"\"\"\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala ignore\n         |$code\n         |```\n         |\"\"\".stripMargin\n    val path = os.sub / \"Example.md\"\n    expect(MarkdownCodeBlock.findCodeBlocks(path, markdown) == Right(Seq.empty))\n  }\n\n  test(\"an unclosed code block produces a build error\") {\n    val code     = \"\"\"println(\"Hello\")\"\"\"\n    val markdown =\n      s\"\"\"# Some snippet\n         |\n         |```scala\n         |$code\n         |\"\"\".stripMargin\n    val subPath          = os.sub / \"Example.md\"\n    val path             = os.pwd / subPath\n    val expectedPosition = Position.File(Right(path), 2 -> 0, 2 -> 3)\n    val expectedError    = MarkdownUnclosedBackticksError(\"```\", Seq(expectedPosition))\n    val actualException  =\n      MarkdownCodeBlock.findCodeBlocks(subPath, markdown)\n        .left.getOrElse(sys.error(\"failed while finding code blocks\"))\n    expect(actualException.message == expectedError.message)\n    expect(actualException.positions == expectedError.positions)\n  }\n\n  test(\"recovery from an unclosed code block error works correctly\") {\n    val code     = \"\"\"println(\"closed snippet\")\"\"\"\n    val markdown =\n      \"\"\"# Some snippet\n        |```scala\n        |println(\"closed snippet\")\n        |```\n        |\n        |# Some other snippet\n        |\n        |````scala\n        |println(\"unclosed snippet\")\n        |\n        |```scala\n        |println(\"whatever\")\n        |```\n        |\"\"\".stripMargin\n    val subPath                            = os.sub / \"Example.md\"\n    var maybeError: Option[BuildException] = None\n    val recoveryFunction                   = (be: BuildException) => {\n      maybeError = Some(be)\n      None\n    }\n    val actualResult =\n      MarkdownCodeBlock.findCodeBlocks(subPath, markdown, maybeRecoverOnError = recoveryFunction)\n        .getOrElse(sys.error(\"failed while finding code blocks\"))\n        .head\n    val expectedResult =\n      MarkdownCodeBlock(\n        info = PlainScalaInfo,\n        body = code,\n        startLine = 2,\n        endLine = 2\n      )\n    expect(actualResult == expectedResult)\n    val path             = os.pwd / subPath\n    val expectedPosition = Position.File(Right(path), 7 -> 0, 7 -> 4)\n    val expectedError    = MarkdownUnclosedBackticksError(\"````\", Seq(expectedPosition))\n    val actualError      = maybeError.get\n    expect(actualError.positions == expectedError.positions)\n    expect(actualError.message == expectedError.message)\n  }\n\n  def showDiffs(actual: MarkdownCodeBlock, expect: MarkdownCodeBlock): Unit = {\n    if actual != expect then\n      for (((a, b), i) <- (actual.body zip expect.body).zipWithIndex)\n        if (a != b) {\n          val aa = TestUtil.c2s(a)\n          val bb = TestUtil.c2s(b)\n          System.err.printf(\"== index %d: [%s]!=[%s]\\n\", i, aa, bb)\n        }\n      System.err.printf(\"actual[%s]\\nexpect[%s]\\n\", actual, expect)\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/markdown/MarkdownCodeWrapperTests.scala",
    "content": "package scala.build.tests.markdown\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.build.internal.AmmUtil\nimport scala.build.internal.markdown.{MarkdownCodeBlock, MarkdownCodeWrapper}\nimport scala.build.preprocessing.{PreprocessedMarkdown, PreprocessedMarkdownCodeBlocks}\nimport scala.build.tests.TestUtil\nimport scala.build.tests.markdown.MarkdownTestUtil.*\nimport scala.language.reflectiveCalls\n\nclass MarkdownCodeWrapperTests extends TestUtil.ScalaCliBuildSuite {\n\n  test(\"empty markdown produces no wrapped code\") {\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", PreprocessedMarkdown.empty)\n    expect(result == (None, None, None))\n  }\n\n  test(\"a simple Scala code block is wrapped correctly\") {\n    val snippet                = \"\"\"println(\"Hello\")\"\"\"\n    val codeBlock              = MarkdownCodeBlock(PlainScalaInfo, snippet, 3, 3)\n    val preprocessedCodeBlocks = PreprocessedMarkdownCodeBlocks(Seq(codeBlock))\n    val markdown               = PreprocessedMarkdown(scriptCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala          = MarkdownCodeWrapper.WrappedMarkdownCode(\n      s\"\"\"object Example_md { @annotation.nowarn(\"msg=pure expression does nothing\") def main(args: Array[String]): Unit = { Scope; }\n         |\n         |object Scope {\n         |$snippet\n         |}}\"\"\".stripMargin\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (Some(expectedScala), None, None))\n  }\n  test(\"multiple plain Scala code blocks are wrapped correctly\") {\n    val snippet1               = \"\"\"println(\"Hello\")\"\"\"\n    val codeBlock1             = MarkdownCodeBlock(PlainScalaInfo, snippet1, 3, 3)\n    val snippet2               = \"\"\"println(\"world\")\"\"\"\n    val codeBlock2             = MarkdownCodeBlock(PlainScalaInfo, snippet2, 8, 8)\n    val snippet3               = \"\"\"println(\"!\")\"\"\"\n    val codeBlock3             = MarkdownCodeBlock(PlainScalaInfo, snippet3, 12, 12)\n    val preprocessedCodeBlocks =\n      PreprocessedMarkdownCodeBlocks(Seq(codeBlock1, codeBlock2, codeBlock3))\n    val markdown      = PreprocessedMarkdown(scriptCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala = MarkdownCodeWrapper.WrappedMarkdownCode(\n      s\"\"\"object Example_md { @annotation.nowarn(\"msg=pure expression does nothing\") def main(args: Array[String]): Unit = { Scope; }\n         |\n         |object Scope {\n         |$snippet1\n         |\n         |\n         |\n         |\n         |$snippet2\n         |\n         |\n         |\n         |$snippet3\n         |}}\"\"\".stripMargin\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (Some(expectedScala), None, None))\n  }\n\n  test(\"multiple plain Scala code blocks with different scopes are wrapped correctly\") {\n    val snippet1               = \"\"\"println(\"Hello\")\"\"\"\n    val codeBlock1             = MarkdownCodeBlock(PlainScalaInfo, snippet1, 3, 3)\n    val snippet2               = \"\"\"println(\"world\")\"\"\"\n    val codeBlock2             = MarkdownCodeBlock(ResetScalaInfo, snippet2, 8, 8)\n    val snippet3               = \"\"\"println(\"!\")\"\"\"\n    val codeBlock3             = MarkdownCodeBlock(PlainScalaInfo, snippet3, 12, 12)\n    val preprocessedCodeBlocks =\n      PreprocessedMarkdownCodeBlocks(Seq(codeBlock1, codeBlock2, codeBlock3))\n    val markdown      = PreprocessedMarkdown(scriptCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala = MarkdownCodeWrapper.WrappedMarkdownCode(\n      s\"\"\"object Example_md { @annotation.nowarn(\"msg=pure expression does nothing\") def main(args: Array[String]): Unit = { Scope; Scope1; }\n         |\n         |object Scope {\n         |$snippet1\n         |\n         |\n         |\n         |}; object Scope1 {\n         |$snippet2\n         |\n         |\n         |\n         |$snippet3\n         |}}\"\"\".stripMargin\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (Some(expectedScala), None, None))\n  }\n\n  test(\"a raw Scala code block is wrapped correctly\") {\n    val snippet =\n      \"\"\"object Main extends App {\n        |  println(\"Hello\")\n        |}\"\"\".stripMargin\n    val codeBlock              = MarkdownCodeBlock(RawScalaInfo, snippet, 3, 5)\n    val preprocessedCodeBlocks = PreprocessedMarkdownCodeBlocks(Seq(codeBlock))\n    val markdown               = PreprocessedMarkdown(rawCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala          = MarkdownCodeWrapper.WrappedMarkdownCode(\n      s\"\"\"\n         |\n         |\n         |$snippet\n         |\"\"\".stripMargin\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (None, Some(expectedScala), None))\n  }\n\n  test(\"multiple raw Scala code blocks are glued together correctly\") {\n    val snippet1 =\n      \"\"\"case class Message(value: String)\"\"\".stripMargin\n    val codeBlock1 = MarkdownCodeBlock(RawScalaInfo, snippet1, 3, 3)\n    val snippet2   =\n      \"\"\"object Main extends App {\n        |  println(Message(\"Hello\").value)\n        |}\"\"\".stripMargin\n    val codeBlock2             = MarkdownCodeBlock(RawScalaInfo, snippet2, 5, 7)\n    val preprocessedCodeBlocks = PreprocessedMarkdownCodeBlocks(Seq(codeBlock1, codeBlock2))\n    val markdown               = PreprocessedMarkdown(rawCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala          = MarkdownCodeWrapper.WrappedMarkdownCode(\n      s\"\"\"\n         |\n         |\n         |$snippet1\n         |\n         |$snippet2\n         |\"\"\".stripMargin\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (None, Some(expectedScala), None))\n  }\n\n  test(\"a test Scala snippet is wrapped correctly\") {\n    val snippet =\n      \"\"\"//> using dep org.scalameta::munit:0.7.29\n        |class Test extends munit.FunSuite {\n        |  assert(true)\n        |}\"\"\".stripMargin\n    val codeBlock              = MarkdownCodeBlock(TestScalaInfo, snippet, 3, 6)\n    val preprocessedCodeBlocks = PreprocessedMarkdownCodeBlocks(Seq(codeBlock))\n    val markdown               = PreprocessedMarkdown(testCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala          = MarkdownCodeWrapper.WrappedMarkdownCode(\n      s\"\"\"\n         |\n         |\n         |$snippet\n         |\"\"\".stripMargin\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (None, None, Some(expectedScala)))\n  }\n\n  def stringPrep(s: String): String = AmmUtil.normalizeNewlines(s)\n\n  test(\"multiple test Scala snippets are glued together correctly\") {\n    val snippet1 = stringPrep(\n      \"\"\"//> using dep org.scalameta::munit:0.7.29\n        |class Test1 extends munit.FunSuite {\n        |  assert(true)\n        |}\"\"\".stripMargin\n    )\n    val codeBlock1 = MarkdownCodeBlock(TestScalaInfo, snippet1, 3, 6)\n    val snippet2   = stringPrep(\n      \"\"\"class Test2 extends munit.FunSuite {\n        |  assert(true)\n        |}\"\"\".stripMargin\n    )\n    val codeBlock2             = MarkdownCodeBlock(TestScalaInfo, snippet2, 8, 10)\n    val preprocessedCodeBlocks = PreprocessedMarkdownCodeBlocks(Seq(codeBlock1, codeBlock2))\n    val markdown               = PreprocessedMarkdown(testCodeBlocks = preprocessedCodeBlocks)\n    val expectedScala          = MarkdownCodeWrapper.WrappedMarkdownCode(\n      stringPrep(\n        s\"\"\"\n           |\n           |\n           |$snippet1\n           |\n           |$snippet2\n           |\"\"\".stripMargin\n      )\n    )\n    val result = MarkdownCodeWrapper(os.sub / \"Example.md\", markdown)\n    showDiffs(result, expectedScala.code)\n    expect(result == (None, None, Some(expectedScala)))\n  }\n\n  import scala.reflect.Selectable.reflectiveSelectable\n  type HasCode     = { def code: String }\n  type CodeWrapper = (Option[HasCode], Option[HasCode], Option[HasCode])\n\n  def showDiffs(result: CodeWrapper, expect: String): Unit = {\n    val actual: String = result match {\n      case (Some(s), None, None) => s.code\n      case (None, Some(s), None) => s.code\n      case (None, None, Some(s)) => s.code\n      case _                     => result.toString\n\n    }\n    if actual != expect then\n      for (((a, b), i) <- (actual zip expect).zipWithIndex)\n        if (a != b) {\n          val aa = TestUtil.c2s(a)\n          val bb = TestUtil.c2s(b)\n          System.err.printf(\"== index %d: [%s]!=[%s]\\n\", i, aa, bb)\n        }\n      System.err.printf(\"actual[%s]\\nexpect[%s]\\n\", actual, expect)\n  }\n\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/markdown/MarkdownTestUtil.scala",
    "content": "package scala.build.tests.markdown\n\nobject MarkdownTestUtil {\n  val PlainScalaInfo: Seq[String] = Seq(\"scala\")\n  val RawScalaInfo: Seq[String]   = Seq(\"scala\", \"raw\")\n  val TestScalaInfo: Seq[String]  = Seq(\"scala\", \"test\")\n  val ResetScalaInfo: Seq[String] = Seq(\"scala\", \"reset\")\n}\n"
  },
  {
    "path": "modules/build/src/test/scala/scala/build/tests/util/BloopServer.scala",
    "content": "package scala.build.tests.util\n\nimport bloop.rifle.BloopRifleConfig\nimport coursier.cache.FileCache\n\nimport scala.build.internals.EnvVar\nimport scala.build.{Bloop, Logger}\nimport scala.util.Properties\n\nobject BloopServer {\n\n  private def directories = scala.build.Directories.default()\n\n  // FIXME We could use a test-specific Bloop instance here.\n  // Not sure how to properly shut it down or have it exit after a period\n  // of inactivity, so we keep using our default global Bloop for now.\n  private def bloopAddress =\n    BloopRifleConfig.Address.DomainSocket(directories.bloopDaemonDir.toNIO)\n\n  val bloopConfig = {\n    val base = BloopRifleConfig.default(\n      bloopAddress,\n      v => Bloop.bloopClassPath(Logger.nop, FileCache(), v),\n      directories.bloopWorkingDir.toIO\n    )\n    base.copy(\n      javaPath =\n        if (Properties.isWin) base.javaPath\n        else\n          // On Linux / macOS, we start the Bloop server via /bin/sh,\n          // which can have issues with the directory of \"java\" in the PATH,\n          // if it contains '+' or '%' IIRC.\n          // So we hardcode the path to \"java\" here.\n          EnvVar.Java.javaHome.valueOpt\n            .map(os.Path(_, os.pwd))\n            .map(_ / \"bin\" / \"java\")\n            .map(_.toString)\n            .getOrElse(base.javaPath)\n    )\n  }\n\n}\n"
  },
  {
    "path": "modules/build-macros/src/main/scala/scala/build/EitherCps.scala",
    "content": "package scala.build\n\nfinal case class EitherFailure[E](v: E, cps: EitherCps[?]) extends RuntimeException:\n  override def fillInStackTrace() = this // disable stack trace generation\n\nclass EitherCps[E]\n\nobject EitherCps:\n  def value[E, V](using\n    cps: EitherCps[? >: E]\n  )(from: Either[E, V]) = // Adding a context bounds breaks incremental compilation\n    from match\n      case Left(e)  => throw EitherFailure(e, cps)\n      case Right(v) => v\n\n  final class Helper[E]():\n    def apply[V](op: EitherCps[E] ?=> V): Either[E, V] =\n      val cps = new EitherCps[E]\n      try Right(op(using cps))\n      catch\n        case EitherFailure(e: E @unchecked, `cps`) =>\n          Left(e)\n\n  def either[E]: Helper[E] = new Helper[E]()\n"
  },
  {
    "path": "modules/build-macros/src/main/scala/scala/build/EitherSequence.scala",
    "content": "package scala.build\n\nimport scala.collection.mutable.ListBuffer\n\nobject EitherSequence {\n  def sequence[E, T](eithers: Seq[Either[E, T]]): Either[::[E], Seq[T]] = {\n    val errors = new ListBuffer[E]\n    val values = new ListBuffer[T]\n    eithers.foreach {\n      case Left(e)  => errors += e\n      case Right(t) =>\n        if (errors.isEmpty)\n          values += t\n    }\n    errors.result() match {\n      case Nil    => Right(values.result())\n      case h :: t => Left(::(h, t))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/build-macros/src/main/scala/scala/build/Ops.scala",
    "content": "package scala.build\n\nimport scala.collection.mutable.ListBuffer\n\nobject Ops {\n  implicit class EitherSeqOps[E, T](private val seq: Seq[Either[E, T]]) extends AnyVal {\n    def sequence: Either[::[E], Seq[T]] =\n      EitherSequence.sequence(seq)\n  }\n\n  implicit class EitherIteratorOps[E, T](private val it: Iterator[Either[E, T]]) extends AnyVal {\n    def sequence0: Either[E, Seq[T]] = {\n      val b      = new ListBuffer[T]\n      var errOpt = Option.empty[E]\n      while (it.hasNext && errOpt.isEmpty) {\n        val e = it.next()\n        e match {\n          case Left(err) =>\n            errOpt = Some(err)\n          case Right(t) =>\n            b += t\n        }\n      }\n      errOpt.toLeft(b.result())\n    }\n  }\n\n  implicit class EitherOptOps[E, T](private val opt: Option[Either[E, T]]) extends AnyVal {\n    def sequence: Either[E, Option[T]] =\n      opt match {\n        case None           => Right(None)\n        case Some(Left(e))  => Left(e)\n        case Some(Right(t)) => Right(Some(t))\n      }\n  }\n\n  implicit class EitherThrowOps[E <: Throwable, T](private val either: Either[E, T])\n      extends AnyVal {\n    def orThrow: T =\n      either match {\n        case Left(e)  => throw new Exception(e)\n        case Right(t) => t\n      }\n  }\n\n  implicit class EitherMap2[Ex <: Throwable, ExA <: Ex, ExB <: Ex, A, B](\n    private val eithers: (Either[ExA, A], Either[ExB, B])\n  ) extends AnyVal {\n    def traverseN: Either[::[Ex], (A, B)] =\n      eithers match {\n        case (Right(a), Right(b)) => Right((a, b))\n        case _                    =>\n          val errors = eithers._1.left.toOption.toSeq ++\n            eithers._2.left.toOption.toSeq\n          val errors0 = errors.toList match {\n            case Nil    => sys.error(\"Cannot happen\")\n            case h :: t => ::(h, t)\n          }\n          Left(errors0)\n      }\n  }\n\n  implicit class EitherMap3[Ex <: Throwable, ExA <: Ex, ExB <: Ex, ExC <: Ex, A, B, C](\n    private val eithers: (Either[ExA, A], Either[ExB, B], Either[ExC, C])\n  ) extends AnyVal {\n    def traverseN: Either[::[Ex], (A, B, C)] =\n      eithers match {\n        case (Right(a), Right(b), Right(c)) => Right((a, b, c))\n        case _                              =>\n          val errors = eithers._1.left.toOption.toSeq ++\n            eithers._2.left.toOption.toSeq ++\n            eithers._3.left.toOption.toSeq\n          val errors0 = errors.toList match {\n            case Nil    => sys.error(\"Cannot happen\")\n            case h :: t => ::(h, t)\n          }\n          Left(errors0)\n      }\n  }\n\n  implicit class EitherMap4[\n    Ex <: Throwable,\n    ExA <: Ex,\n    ExB <: Ex,\n    ExC <: Ex,\n    ExD <: Ex,\n    A,\n    B,\n    C,\n    D\n  ](\n    private val eithers: (Either[ExA, A], Either[ExB, B], Either[ExC, C], Either[ExD, D])\n  ) extends AnyVal {\n    def traverseN: Either[::[Ex], (A, B, C, D)] =\n      eithers match {\n        case (Right(a), Right(b), Right(c), Right(d)) => Right((a, b, c, d))\n        case _                                        =>\n          val errors = eithers._1.left.toOption.toSeq ++\n            eithers._2.left.toOption.toSeq ++\n            eithers._3.left.toOption.toSeq ++\n            eithers._4.left.toOption.toSeq\n          val errors0 = errors.toList match {\n            case Nil    => sys.error(\"Cannot happen\")\n            case h :: t => ::(h, t)\n          }\n          Left(errors0)\n      }\n  }\n\n  implicit class EitherMap5[\n    Ex <: Throwable,\n    ExA <: Ex,\n    ExB <: Ex,\n    ExC <: Ex,\n    ExD <: Ex,\n    ExE <: Ex,\n    A,\n    B,\n    C,\n    D,\n    E\n  ](\n    private val eithers: (\n      Either[ExA, A],\n      Either[ExB, B],\n      Either[ExC, C],\n      Either[ExD, D],\n      Either[ExE, E]\n    )\n  ) extends AnyVal {\n    def traverseN: Either[::[Ex], (A, B, C, D, E)] =\n      eithers match {\n        case (Right(a), Right(b), Right(c), Right(d), Right(e)) => Right((a, b, c, d, e))\n        case _                                                  =>\n          val errors = eithers._1.left.toOption.toSeq ++\n            eithers._2.left.toOption.toSeq ++\n            eithers._3.left.toOption.toSeq ++\n            eithers._4.left.toOption.toSeq ++\n            eithers._5.left.toOption.toSeq\n          val errors0 = errors.toList match {\n            case Nil    => sys.error(\"Cannot happen\")\n            case h :: t => ::(h, t)\n          }\n          Left(errors0)\n      }\n  }\n\n  implicit class EitherMap6[\n    Ex <: Throwable,\n    ExA <: Ex,\n    ExB <: Ex,\n    ExC <: Ex,\n    ExD <: Ex,\n    ExE <: Ex,\n    ExF <: Ex,\n    A,\n    B,\n    C,\n    D,\n    E,\n    F\n  ](\n    private val eithers: (\n      Either[ExA, A],\n      Either[ExB, B],\n      Either[ExC, C],\n      Either[ExD, D],\n      Either[ExE, E],\n      Either[ExF, F]\n    )\n  ) extends AnyVal {\n    def traverseN: Either[::[Ex], (A, B, C, D, E, F)] =\n      eithers match {\n        case (Right(a), Right(b), Right(c), Right(d), Right(e), Right(f)) =>\n          Right((a, b, c, d, e, f))\n        case _ =>\n          val errors = eithers._1.left.toOption.toSeq ++\n            eithers._2.left.toOption.toSeq ++\n            eithers._3.left.toOption.toSeq ++\n            eithers._4.left.toOption.toSeq ++\n            eithers._5.left.toOption.toSeq ++\n            eithers._6.left.toOption.toSeq\n          val errors0 = errors.toList match {\n            case Nil    => sys.error(\"Cannot happen\")\n            case h :: t => ::(h, t)\n          }\n          Left(errors0)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/build-macros/src/negative-tests/MismatchedLeft.scala",
    "content": "import scala.build.EitherCps.*\n\nclass E\nclass EE1 extends E\nclass EE2 extends E\nclass E2\nclass V\nclass VV extends V\n\nval vv: Either[E, VV] = Right(new VV)\n\ndef ee3: Either[E2, V] = either {\n  value(Left(new EE1))\n  value(vv.left.map(_ => new EE2))\n  new V\n}\n"
  },
  {
    "path": "modules/build-macros/src/test/scala/scala/build/CPSTest.scala",
    "content": "package scala.build\n\nimport scala.build.EitherCps.*\n\nclass CPSTest extends munit.FunSuite {\n\n  val failed1: Either[Int, String] = Left(1)\n  val ok: Either[Int, String]      = Right(\"OK\")\n\n  def checkResult(expected: Either[Int, String])(res: => Either[Int, String]): Unit =\n    assertEquals(expected, res)\n\n  test(\"Basic CPS test\") {\n    checkResult(Right(\"OK\"))(either(\"OK\"))\n    checkResult(Right(\"OK\"))(either(value(ok)))\n    checkResult(Left(1))(either(value(failed1)))\n  }\n\n  test(\"Exceptions\") {\n    intercept[IllegalArgumentException](either(throw new IllegalArgumentException(\"test\")))\n\n    intercept[IllegalArgumentException](either {\n      throw new IllegalArgumentException(\"test\")\n      value(failed1)\n    })\n  }\n\n  test(\"early return\") {\n    checkResult(Left(1)) {\n      either {\n        value(failed1)\n        throw new IllegalArgumentException(\"test\")\n      }\n    }\n  }\n\n  class E\n  class EE  extends E\n  class EEE extends E\n\n  class V\n  class VV extends V\n\n  class E2 { def ala = 123 }\n  class V2\n\n  val ee: Either[EE, V] = Left(new EE)\n  val vv: Either[E, VV] = Right(new VV)\n\n  def ee2: Either[E, V] = either {\n    value(Left(new EEE))\n    value(vv.left.map(_ => new EE))\n    new V\n  }\n\n  // Mainly to see if compiles\n  test(\"variance 123\") {\n    val errorRes: Either[E, V] = either(value(ee))\n    assert(ee == errorRes)\n\n    val valueRes: Either[E, V] = either(value(vv))\n    assert(vv == valueRes)\n\n  }\n\n  test(\"fallback to Right\") {\n    val res               = new V2\n    val a: Either[E2, V2] = either {\n      value(Right(res))\n    }\n    assert(Right(res) == a)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/commands/pgp/PgpCommandsSubst.java",
    "content": "package scala.cli.commands.pgp;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport scala.cli.commands.pgp.ExternalCommand;\nimport scala.cli.commands.pgp.PgpCommand;\n\n@TargetClass(className = \"scala.cli.commands.pgp.PgpCommands\")\npublic final class PgpCommandsSubst {\n  @Substitute\n  public PgpCommand<?>[] allScalaCommands() {\n    return new PgpCommand<?>[0];\n  }\n  @Substitute\n  public ExternalCommand[] allExternalCommands() {\n    return new ExternalCommand[] {\n      new PgpCreateExternal(),\n      new PgpKeyIdExternal(),\n      new PgpSignExternal(),\n      new PgpVerifyExternal()\n    };\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/commands/publish/PgpProxyMakerSubst.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport scala.cli.commands.pgp.PgpProxy;\n\n/** Used for choosing the right PGP proxy implementation when Scala CLI is run as a native image.\n *  This class is used to substitute scala.cli.commands.pgp.PgpProxyMaker.\n *  This decouples Scala CLI native image from BouncyCastle used by scala-cli-signing.\n */\n@TargetClass(className = \"scala.cli.commands.pgp.PgpProxyMaker\")\npublic final class PgpProxyMakerSubst {\n  @Substitute\n  public PgpProxy get(Boolean forceSigningExternally) {\n    return new PgpProxy();\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/Argv0Subst.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport org.graalvm.nativeimage.Platform;\nimport org.graalvm.nativeimage.Platforms;\n\nimport java.nio.file.Path;\n\n@TargetClass(className = \"scala.cli.internal.Argv0\")\n@Platforms({Platform.LINUX.class, Platform.DARWIN.class})\nfinal class Argv0Subst {\n\n    @Substitute\n    String get(String defaultValue) {\n        return com.oracle.svm.core.JavaMainWrapper.getCRuntimeArgument0();\n    }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/Argv0SubstWindows.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport org.graalvm.nativeimage.Platform;\nimport org.graalvm.nativeimage.Platforms;\n\nimport java.nio.file.Path;\n\n@TargetClass(className = \"scala.cli.internal.Argv0\")\n@Platforms({Platform.WINDOWS.class})\nfinal class Argv0SubstWindows {\n\n    @Substitute\n    String get(String defaultValue) {\n        return coursier.jniutils.ModuleFileName.get();\n    }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/BouncycastleSignerMakerSubst.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport coursier.publish.signing.Signer;\n\nimport java.nio.file.Path;\nimport java.util.function.Supplier;\n\nimport scala.build.Logger;\nimport scala.cli.publish.BouncycastleExternalSigner$;\nimport scala.cli.signing.shared.PasswordOption;\n\n/** Used for choosing the right BouncyCastleSigner when Scala CLI is run as a native image.\n *  This class is used to substitute scala.cli.commands.pgp.PgpProxyMaker.\n *  This decouples Scala CLI native image from BouncyCastle used by scala-cli-signing.\n */\n@TargetClass(className = \"scala.cli.publish.BouncycastleSignerMaker\")\npublic final class BouncycastleSignerMakerSubst {\n\n  @Substitute\n  public Signer get(\n    Boolean forceSigningExternally,\n    PasswordOption passwordOrNull,\n    PasswordOption secretKey,\n    Supplier<String[]> command,\n    Logger logger\n  ) {\n    return BouncycastleExternalSigner$.MODULE$.apply(secretKey, passwordOrNull, command.get(), logger);\n  }\n\n  @Substitute\n  void maybeInit() {\n    // do nothing\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/CsJniUtilsFeature.java",
    "content": "package scala.cli.internal;\r\n\r\nimport com.oracle.svm.core.annotate.AutomaticFeature;\r\nimport com.oracle.svm.core.jdk.NativeLibrarySupport;\r\nimport com.oracle.svm.core.jdk.PlatformNativeLibrarySupport;\r\nimport com.oracle.svm.hosted.FeatureImpl;\r\nimport com.oracle.svm.hosted.c.NativeLibraries;\r\nimport org.graalvm.nativeimage.hosted.Feature;\r\nimport org.graalvm.nativeimage.Platform;\r\nimport org.graalvm.nativeimage.Platforms;\r\n\r\n@AutomaticFeature\r\n@Platforms({Platform.WINDOWS.class})\r\npublic class CsJniUtilsFeature implements Feature {\r\n\r\n    @Override\r\n    public void beforeAnalysis(BeforeAnalysisAccess access) {\r\n        NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary(\"csjniutils\");\r\n        PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix(\"coursier_bootstrap_launcher_jniutils_\");\r\n        PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix(\"coursier_jniutils_\");\r\n        PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix(\"coursierapi_internal_jniutils_\");\r\n        PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix(\"lmcoursier_internal_jniutils_\");\r\n        NativeLibraries nativeLibraries = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getNativeLibraries();\r\n        nativeLibraries.addStaticJniLibrary(\"csjniutils\");\r\n        nativeLibraries.addDynamicNonJniLibrary(\"ole32\");\r\n        nativeLibraries.addDynamicNonJniLibrary(\"shell32\");\r\n        nativeLibraries.addDynamicNonJniLibrary(\"Advapi32\");\r\n    }\r\n}\r\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/LibsodiumjniFeature.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.AutomaticFeature;\nimport com.oracle.svm.core.jdk.NativeLibrarySupport;\nimport com.oracle.svm.core.jdk.PlatformNativeLibrarySupport;\nimport com.oracle.svm.hosted.FeatureImpl;\nimport com.oracle.svm.hosted.c.NativeLibraries;\nimport org.graalvm.nativeimage.hosted.Feature;\nimport org.graalvm.nativeimage.Platform;\nimport org.graalvm.nativeimage.Platforms;\nimport java.util.Locale;\n\n@AutomaticFeature\n@Platforms({Platform.LINUX.class, Platform.WINDOWS.class})\npublic class LibsodiumjniFeature implements Feature {\n\n    @Override\n    public void beforeAnalysis(BeforeAnalysisAccess access) {\n        boolean isWindows = System.getProperty(\"os.name\").toLowerCase(Locale.ROOT).contains(\"windows\");\n        boolean isStaticLauncher = Boolean.getBoolean(\"scala-cli.static-launcher\");\n        if (!isWindows && !isStaticLauncher) {\n            System.err.println(\"Actually disabling LibsodiumjniFeature (not Windows nor static launcher)\");\n            return;\n        }\n        NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary(\"sodiumjni\");\n        PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix(\"libsodiumjni_\");\n        NativeLibraries nativeLibraries = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getNativeLibraries();\n        nativeLibraries.addStaticNonJniLibrary(\"sodium\");\n        nativeLibraries.addStaticJniLibrary(\"sodiumjni\");\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/PPrintStringPrefixSubst.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\n\n// Remove once we can use https://github.com/com-lihaoyi/PPrint/pull/80\n\n@TargetClass(className = \"pprint.StringPrefix$\")\nfinal class PPrintStringPrefixSubst {\n\n  @Substitute\n  String apply(scala.collection.Iterable<?> i) {\n    String name = (new PPrintStringPrefixHelper()).apply((scala.collection.Iterable<Object>) i);\n    return name;\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/java/scala/cli/internal/PidSubst.java",
    "content": "package scala.cli.internal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\nimport com.oracle.svm.core.posix.headers.Unistd;\nimport org.graalvm.nativeimage.Platform;\nimport org.graalvm.nativeimage.Platforms;\n\nimport java.nio.file.Path;\n\n@TargetClass(className = \"scala.cli.internal.Pid\")\n@Platforms({Platform.LINUX.class, Platform.DARWIN.class})\nfinal class PidSubst {\n\n    @Substitute\n    Integer get() {\n        return Unistd.getpid();\n    }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/extras/coursier/reflect-config.json",
    "content": "[\n  {\n    \"name\": \"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.AsiExtraField\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.JarMarker\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.ResourceAlignmentExtraField\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.UnicodeCommentExtraField\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.UnicodePathExtraField\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X000A_NTFS\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X0014_X509Certificates\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X0015_CertificateIdForFile\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X0016_CertificateIdForCentralDirectory\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X0017_StrongEncryptionHeader\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X0019_EncryptionRecipientCertificateList\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X5455_ExtendedTimestamp\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.X7875_NewUnix\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  },\n  {\n    \"name\": \"org.apache.commons.compress.archivers.zip.Zip64ExtendedInformationExtraField\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allPublicMethods\": true,\n    \"allDeclaredClasses\": true,\n    \"allPublicClasses\": true\n  }\n]\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/extras/pprint/reflect-config.json",
    "content": "[\n  {\n    \"name\": \"scala.collection.AbstractIterable\",\n    \"methods\": [\n      {\n        \"name\": \"collectionClassName\",\n        \"parameterTypes\": []\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/jni-config.json",
    "content": "[\n  {\n    \"name\": \"com.swoval.files.apple.FileEvent\",\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": [\n          \"java.lang.String\",\n          \"int\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"com.swoval.files.apple.FileEventMonitorImpl$WrappedConsumer\",\n    \"methods\": [\n      {\n        \"name\": \"accept\",\n        \"parameterTypes\": [\n          \"java.lang.Object\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"java.lang.ClassLoader\",\n    \"methods\": [\n      {\n        \"name\": \"getPlatformClassLoader\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"java.lang.NoSuchMethodError\"\n  },\n  {\n    \"name\": \"java.lang.String\"\n  },\n  {\n    \"name\": \"sun.management.VMManagementImpl\",\n    \"fields\": [\n      {\n        \"name\": \"compTimeMonitoringSupport\"\n      },\n      {\n        \"name\": \"currentThreadCpuTimeSupport\"\n      },\n      {\n        \"name\": \"objectMonitorUsageSupport\"\n      },\n      {\n        \"name\": \"otherThreadCpuTimeSupport\"\n      },\n      {\n        \"name\": \"remoteDiagnosticCommandsSupport\"\n      },\n      {\n        \"name\": \"synchronizerUsageSupport\"\n      },\n      {\n        \"name\": \"threadAllocatedMemorySupport\"\n      },\n      {\n        \"name\": \"threadContentionMonitoringSupport\"\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/native-image.properties",
    "content": "Args = --no-fallback \\\n       --enable-url-protocols=http,https \\\n       --initialize-at-build-time=com.google.common.jimfs.SystemJimfsFileSystemProvider \\\n       --initialize-at-build-time=com.google.common.base.Preconditions \\\n       -H:IncludeResources=bootstrap.*.jar \\\n       -H:IncludeResources=coursier/coursier.properties \\\n       -H:IncludeResources=coursier/launcher/coursier.properties \\\n       -H:IncludeResources=coursier/launcher/.*.bat \\\n       --report-unsupported-elements-at-runtime \\\n       -H:+ReportExceptionStackTraces \\\n       -Djdk.http.auth.tunneling.disabledSchemes=\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/proxy-config.json",
    "content": "[\n  [\n    \"bloop.rifle.BuildServer\",\n    \"org.eclipse.lsp4j.jsonrpc.Endpoint\"\n  ],\n  [\n    \"ch.epfl.scala.bsp4j.BuildClient\",\n    \"org.eclipse.lsp4j.jsonrpc.Endpoint\"\n  ]\n]\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json",
    "content": "[\n  {\n    \"name\": \"bloop.rifle.BuildServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"bloop.rifle.bloop4j.BloopExtraBuildParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BspConnectionDetails\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildClient\",\n    \"queryAllDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildClientCapabilities\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildServer\",\n    \"queryAllDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildServerCapabilities\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTarget\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTargetCapabilities\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTargetDataKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTargetEvent\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTargetEventKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTargetIdentifier\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.BuildTargetTag\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CleanCacheParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CleanCacheResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CompileParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CompileProvider\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CompileReport\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CompileResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.CompileTask\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DebugProvider\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DebugSessionAddress\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DebugSessionParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DebugSessionParamsDataKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencyModule\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencyModulesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencyModulesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencyModulesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencySourcesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencySourcesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DependencySourcesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.Diagnostic\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DiagnosticRelatedInformation\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DiagnosticSeverity\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.DidChangeBuildTarget\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.InitializeBuildParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.InitializeBuildResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.InverseSourcesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.InverseSourcesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JavaBuildServer\",\n    \"queryAllDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JavacOptionsItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JavacOptionsParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JavacOptionsResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmBuildServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmBuildTarget\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmEnvironmentItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmMainClass\",\n    \"queryAllDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmRunEnvironmentParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmRunEnvironmentResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmTestEnvironmentParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.JvmTestEnvironmentResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.Location\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.LogMessageParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.MessageType\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.OutputPathItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.OutputPathItemKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.OutputPathsItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.OutputPathsParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.OutputPathsResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.Position\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.PublishDiagnosticsParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.Range\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ResourcesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ResourcesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ResourcesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.RunParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.RunParamsDataKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.RunProvider\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.RunResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.SbtBuildTarget\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaAction\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaBuildServer\",\n    \"queryAllDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaBuildTarget\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaDiagnostic\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaMainClass\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaMainClassesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaMainClassesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaMainClassesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaPlatform\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaTestClassesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaTestClassesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaTestClassesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaTestParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaTextEdit\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalaWorkspaceEdit\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalacOptionsItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalacOptionsParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ScalacOptionsResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.ShowMessageParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.SourceItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.SourceItemKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.SourcesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.SourcesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.SourcesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.StatusCode\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TaskFinishParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TaskId\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TaskProgressParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TaskStartParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestFinish\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestParamsDataKind\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestProvider\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestReport\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestStart\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestStatus\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TestTask\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.TextDocumentIdentifier\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"com.google.api.client.http.GenericUrl\",\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"com.google.api.client.http.HttpHeaders\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true\n  },\n  {\n    \"name\": \"com.google.api.client.util.GenericData\",\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.api.DockerInfoDetails\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.cache.LayerEntriesSelector$LayerEntryTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.docker.json.DockerManifestEntryTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.BuildableManifestTemplate\",\n    \"allDeclaredMethods\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.BuildableManifestTemplate$ContentDescriptorTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate$ConfigurationObjectTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate$HealthCheckObjectTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate$RootFilesystemObjectTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.DescriptorDigestDeserializer\",\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.DescriptorDigestSerializer\",\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.HistoryEntry\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ImageMetadataTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ManifestAndConfigTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.ManifestTemplate\",\n    \"allDeclaredMethods\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.OciIndexTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.OciIndexTemplate$ManifestDescriptorTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.OciIndexTemplate$ManifestDescriptorTemplate$Platform\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.OciManifestTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.V22ManifestListTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.V22ManifestListTemplate$ManifestDescriptorTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.V22ManifestListTemplate$ManifestDescriptorTemplate$Platform\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.image.json.V22ManifestTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.json.JsonTemplate\",\n    \"allDeclaredMethods\": true\n  },\n  {\n    \"name\": \"com.google.cloud.tools.jib.registry.RegistryAuthenticator$AuthenticationResponseTemplate\",\n    \"allDeclaredFields\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true\n  },\n  {\n    \"name\": \"com.google.common.jimfs.SystemJimfsFileSystemProvider\",\n    \"methods\": [\n      {\n        \"name\": \"removeFileSystemRunnable\",\n        \"parameterTypes\": [\n          \"java.net.URI\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"coursier.cache.loggers.RefreshLogger\",\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"java.lang.String\"\n  },\n  {\n    \"name\": \"java.util.List\",\n    \"allDeclaredMethods\": true\n  },\n  {\n    \"name\": \"org.apache.commons.logging.LogFactory\"\n  },\n  {\n    \"name\": \"org.apache.commons.logging.impl.LogFactoryImpl\",\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"org.apache.commons.logging.impl.NoOpLog\",\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": [\n          \"java.lang.String\"\n        ]\n      }\n    ]\n  },\n  {\n    \"name\": \"org.apache.commons.logging.impl.WeakHashtable\",\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"org.eclipse.jgit.internal.JGitText\",\n    \"allPublicFields\": true,\n    \"methods\": [\n      {\n        \"name\": \"<init>\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"org.eclipse.jgit.lib.CoreConfig$LogRefUpdates\",\n    \"methods\": [\n      {\n        \"name\": \"values\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"org.eclipse.jgit.lib.CoreConfig$TrustPackedRefsStat\",\n    \"methods\": [\n      {\n        \"name\": \"values\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"org.eclipse.jgit.lib.CoreConfig$TrustStat\",\n    \"methods\": [\n      {\n        \"name\": \"values\",\n        \"parameterTypes\": []\n      }\n    ]\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.RemoteEndpoint\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.json.MessageConstants\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.json.MethodProvider\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.json.adapters.JsonElementTypeAdapter$Factory\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.CancelParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.Either\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.Message\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.MessageIssue\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.NotificationMessage\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.RequestMessage\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.ResponseError\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"org.scalajs.jsenv.ExternalJSRun\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.BloopBuildClient\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.ConsoleBloopBuildClient\",\n    \"queryAllDeclaredMethods\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.BspClient\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.BspImpl$LoggingBspClient\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.BspServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.BuildClientForwardStubs\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.BuildServerForwardStubs\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.JavaBuildServerForwardStubs\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.JvmBuildServerForwardStubs\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.LoggingBuildClient\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.LoggingBuildServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.LoggingJavaBuildServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.LoggingJvmBuildServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.LoggingScalaBuildServer\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.ScalaBuildServerForwardStubs\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.ScalaScriptBuildServer\",\n    \"queryAllDeclaredMethods\": true,\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.WrappedSourceItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.WrappedSourcesItem\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.WrappedSourcesParams\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.WrappedSourcesResult\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  },\n  {\n    \"name\": \"scala.build.bsp.protocol.TextEdit\",\n    \"allDeclaredConstructors\": true,\n    \"allPublicConstructors\": true,\n    \"allDeclaredMethods\": true,\n    \"allDeclaredFields\": true\n  }\n]\n"
  },
  {
    "path": "modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/resource-config.json",
    "content": "{\n  \"resources\": {\n    \"includes\": [\n      {\n        \"pattern\": \"\\\\QMETA-INF/services/java.nio.file.spi.FileSystemProvider\\\\E\"\n      },\n      {\n        \"pattern\": \"\\\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\\\E\"\n      },\n      {\n        \"pattern\": \"\\\\QMETA-INF/services/org.xnio.XnioProvider\\\\E\"\n      },\n      {\n        \"pattern\": \"\\\\Qlibrary.properties\\\\E\"\n      },\n      {\n        \"pattern\": \"\\\\Qnative/x86_64/libswoval-files0.dylib\\\\E\"\n      },\n      {\n        \"pattern\": \"\\\\Qcommons-logging.properties\\\\E\"\n      },\n      {\n        \"pattern\": \".*scala3RuntimeFixes.jar$\"\n      },\n      {\n        \"pattern\": \".*JGitText.properties$\"\n      }\n    ]\n  },\n  \"bundles\": []\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/coursier/CoursierUtil.scala",
    "content": "package coursier\n\nimport coursier.util.WebPage\n\nobject CoursierUtil {\n\n  def rawVersions(repoUrl: String, page: String) = WebPage.listDirectories(repoUrl, page)\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/CurrentParams.scala",
    "content": "package scala.cli\n\n// Kind of meh to keep stuff in a global mutable state like this.\n// This is only used by the stacktrace persisting stuff in ScalaCli.main\nobject CurrentParams {\n  var workspaceOpt = Option.empty[os.Path]\n  var verbosity    = 0\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/ScalaCli.scala",
    "content": "package scala.cli\n\nimport bloop.rifle.FailedToStartServerException\nimport coursier.version.Version\nimport sun.misc.{Signal, SignalHandler}\n\nimport java.io.{ByteArrayOutputStream, PrintStream}\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Paths\nimport java.util.Locale\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.internal.Constants\nimport scala.build.internals.EnvVar\nimport scala.cli.commands.CommandUtils\nimport scala.cli.config.Keys\nimport scala.cli.internal.Argv0\nimport scala.cli.javaLauncher.JavaLauncherCli\nimport scala.cli.launcher.{LauncherCli, LauncherOptions, PowerOptions}\nimport scala.cli.publish.BouncycastleSignerMaker\nimport scala.cli.util.ConfigDbUtils\nimport scala.util.Properties\n\nobject ScalaCli {\n\n  if (Properties.isWin && isGraalvmNativeImage)\n    // have to be initialized before running (new Argv0).get because Argv0SubstWindows uses csjniutils library\n    // The DLL loaded by LoadWindowsLibrary is statically linke/d in\n    // the Scala CLI native image, no need to manually load it.\n    coursier.jniutils.LoadWindowsLibrary.assumeInitialized()\n\n  private val defaultProgName = \"scala-cli\"\n  var progName: String        = {\n    val argv0 = (new Argv0).get(defaultProgName)\n    val last  = Paths.get(argv0).getFileName.toString\n    last match {\n      case s\".${name}.aux\" =>\n        name // cs installs binaries under .app-name.aux and adds them to the PATH\n      case _ => argv0\n    }\n  }\n  private val scalaCliBinaryName = \"scala-cli\"\n  private var isSipScala         = {\n    lazy val isPowerConfigDb = for {\n      configDb   <- ConfigDbUtils.configDb.toOption\n      powerEntry <- configDb.get(Keys.power).toOption\n      power      <- powerEntry\n    } yield power\n    val isPowerEnv = EnvVar.ScalaCli.power.valueOpt.flatMap(_.toBooleanOption)\n    val isPower    = isPowerEnv.orElse(isPowerConfigDb).getOrElse(false)\n    !isPower\n  }\n  def setPowerMode(power: Boolean): Unit = isSipScala = !power\n  def allowRestrictedFeatures            = !isSipScala\n  def fullRunnerName                     =\n    if (progName.contains(scalaCliBinaryName)) \"Scala CLI\" else \"Scala code runner\"\n  def baseRunnerName = if (progName.contains(scalaCliBinaryName)) scalaCliBinaryName else \"scala\"\n  private def isGraalvmNativeImage: Boolean =\n    sys.props.contains(\"org.graalvm.nativeimage.imagecode\")\n\n  private var maybeLauncherOptions: Option[LauncherOptions] = None\n\n  def launcherOptions: LauncherOptions = maybeLauncherOptions.getOrElse(LauncherOptions())\n  def getDefaultScalaVersion: String   =\n    launcherOptions.scalaRunner.cliUserScalaVersion.getOrElse(Constants.defaultScalaVersion)\n\n  private var launcherJavaPropArgs: List[String] = List.empty\n\n  def getLauncherJavaPropArgs: List[String] = launcherJavaPropArgs\n\n  private def partitionArgs(args: Array[String]): (Array[String], Array[String]) = {\n    val systemProps = args.takeWhile(_.startsWith(\"-D\"))\n    (systemProps, args.drop(systemProps.length))\n  }\n\n  private def setSystemProps(systemProps: Array[String]): Unit = {\n    systemProps.map(_.stripPrefix(\"-D\")).foreach { prop =>\n      prop.split(\"=\", 2) match {\n        case Array(key, value) =>\n          System.setProperty(key, value)\n        case Array(key) =>\n          System.setProperty(key, \"\")\n      }\n    }\n  }\n  private def printThrowable(t: Throwable, out: PrintStream): Unit =\n    if (t != null) {\n      out.println(t.toString)\n      // FIXME Print t.getSuppressed too?\n      for (l <- t.getStackTrace)\n        out.println(s\"  $l\")\n      printThrowable(t.getCause, out)\n    }\n\n  private def printThrowable(t: Throwable): Array[Byte] = {\n    val baos = new ByteArrayOutputStream\n    val ps   = new PrintStream(baos, true, StandardCharsets.UTF_8.name())\n    printThrowable(t, ps)\n    baos.toByteArray\n  }\n\n  private def isCI             = EnvVar.Internal.ci.valueOpt.nonEmpty\n  private def printStackTraces = EnvVar.ScalaCli.printStackTraces.valueOpt\n    .map(_.toLowerCase(Locale.ROOT))\n    .exists {\n      case \"true\" | \"1\" => true\n      case _            => false\n    }\n\n  private def ignoreSigpipe(): Unit =\n    Signal.handle(new Signal(\"PIPE\"), SignalHandler.SIG_IGN)\n\n  private def isJava17ClassName(name: String): Boolean =\n    name == \"java/net/UnixDomainSocketAddress\"\n\n  private lazy val javaMajorVersion =\n    sys.props.getOrElse(\"java.version\", \"0\")\n      .stripPrefix(\"1.\")\n      .takeWhile(_.isDigit)\n      .toInt\n\n  def main(args: Array[String]): Unit =\n    try main0(args)\n    catch {\n      case e: Throwable if !isCI && !printStackTraces =>\n        val workspace = CurrentParams.workspaceOpt.filter(os.isDir).getOrElse(os.pwd)\n        val dir       = workspace / Constants.workspaceDirName / \"stacktraces\"\n        os.makeDir.all(dir)\n        import java.time.Instant\n\n        val tempFile = os.temp(\n          contents = printThrowable(e),\n          dir = dir,\n          prefix = Instant.now().getEpochSecond().toString() + \"-\",\n          suffix = \".log\",\n          deleteOnExit = false\n        )\n\n        if (CurrentParams.verbosity <= 1) {\n          System.err.println(s\"Error: $e\")\n          System.err.println(s\"For more details, please see '$tempFile'\")\n        }\n\n        e match {\n          case _: UnsupportedClassVersionError\n              if javaMajorVersion < Constants.minimumBloopJavaVersion =>\n            warnRequiresMinimumBloopJava()\n          case _: NoClassDefFoundError\n              if isJava17ClassName(e.getMessage) &&\n              CurrentParams.verbosity <= 1 &&\n              javaMajorVersion < Constants.minimumInternalJavaVersion =>\n            // Actually Java >= 16 here, but let's recommend a LTS version…\n            warnRequiresMinimumBloopJava()\n          case _: FailedToStartServerException =>\n            System.err.println(\n              s\"\"\"Running\n                 |  $progName --power bloop output\n                 |might give more details.\"\"\".stripMargin\n            )\n          case ex: java.util.zip.ZipException\n              if !Properties.isWin && ex.getMessage.contains(\"invalid entry CRC\") =>\n            // Suggest workaround of https://github.com/VirtusLab/scala-cli/pull/865\n            // for https://github.com/VirtusLab/scala-cli/issues/828\n            System.err.println(\n              s\"\"\"Running\n                 |  export ${EnvVar.ScalaCli.vendoredZipInputStream.name}=true\n                 |before running $fullRunnerName might fix the issue.\n                 |\"\"\".stripMargin\n            )\n          case _ =>\n        }\n\n        if (CurrentParams.verbosity >= 2) throw e\n        else sys.exit(1)\n    }\n\n  private def warnRequiresMinimumBloopJava(): Unit =\n    System.err.println(\n      s\"Java >= ${Constants.minimumBloopJavaVersion} is required to run $fullRunnerName (found Java $javaMajorVersion)\"\n    )\n\n  def loadJavaProperties(cwd: os.Path) = {\n    // load java properties from scala-cli-properties resource file\n    val prop = new java.util.Properties()\n    val cl   = getClass.getResourceAsStream(\"/java-properties/scala-cli-properties\")\n    if cl != null then\n      prop.load(cl)\n      prop.stringPropertyNames().forEach(name => System.setProperty(name, prop.getProperty(name)))\n    // load java properties from .scala-jvmopts located in the current working directory and filter only java properties and warning if someone used other options\n    val jvmopts = cwd / Constants.jvmPropertiesFileName\n    if os.exists(jvmopts) && os.isFile(jvmopts) then\n      val jvmoptsContent        = os.read(jvmopts)\n      val jvmoptsLines          = jvmoptsContent.linesIterator.toSeq\n      val (javaOpts, otherOpts) = jvmoptsLines.partition(_.startsWith(\"-D\"))\n      javaOpts.foreach { opt =>\n        opt.stripPrefix(\"-D\").split(\"=\", 2) match {\n          case Array(key, value) => System.setProperty(key, value)\n          case _                 => System.err.println(s\"Warning: Invalid java property: $opt\")\n        }\n      }\n      if otherOpts.nonEmpty then\n        System.err.println(\n          s\"Warning: Only java properties are supported in .scala-jvmopts file. Other options are ignored: ${otherOpts.mkString(\", \")}\"\n        )\n    // load java properties from config\n    for {\n      configDb   <- ConfigDbUtils.configDb.toOption\n      properties <- configDb.get(Keys.javaProperties).getOrElse(Nil).iterator\n    }\n      properties.foreach { opt =>\n        opt.stripPrefix(\"-D\").split(\"=\", 2) match {\n          case Array(key, value) => System.setProperty(key, value)\n          case _ => System.err.println(s\"Warning: Invalid java property in config: $opt\")\n        }\n      }\n\n    // load java properties from JAVA_OPTS and JDK_JAVA_OPTIONS environment variables\n    val javaOpts: Seq[String] =\n      EnvVar.Java.javaOpts.valueOpt.toSeq ++ EnvVar.Java.jdkJavaOpts.valueOpt.toSeq\n\n    val ignoredJavaOpts =\n      javaOpts\n        .flatMap(_.split(\"\\\\s+\"))\n        .flatMap { opt =>\n          opt.stripPrefix(\"-D\").split(\"=\", 2) match {\n            case Array(key, value) =>\n              System.setProperty(key, value)\n              None\n            case ignored => Some(ignored) // non-property opts are ignored here\n          }\n        }.flatten\n    if ignoredJavaOpts.nonEmpty then\n      System.err.println(\n        s\"Warning: Only java properties are supported in ${EnvVar.Java.javaOpts.name} and ${EnvVar\n            .Java.jdkJavaOpts.name} environment variables. Other options are ignored: ${ignoredJavaOpts.mkString(\", \")}\"\n      )\n  }\n\n  private def main0(args: Array[String]): Unit = either {\n    loadJavaProperties(cwd = os.pwd) // load java properties to detect launcher kind\n    val remainingArgs = value {\n      LauncherOptions.parser.stopAtFirstUnrecognized.parse(args.toVector) match {\n        case Left(e) =>\n          System.err.println(e.message)\n          sys.exit(1)\n        case Right((launcherOpts, args0)) =>\n          maybeLauncherOptions = Some(launcherOpts)\n          launcherOpts.cliVersion.map(_.trim).filter(_.nonEmpty) match {\n            case Some(ver) =>\n              val powerArgs              = launcherOpts.powerOptions.toCliArgs\n              val initialScalaRunnerArgs = launcherOpts.scalaRunner\n              val finalScalaRunnerArgs   = (Version(ver) match\n                case v if v < Version(\"1.4.0\") && !ver.contains(\"nightly\") =>\n                  initialScalaRunnerArgs.copy(\n                    skipCliUpdates = None,\n                    predefinedCliVersion = None,\n                    initialLauncherPath = None\n                  )\n                case v\n                    if v < Version(\"1.5.0-34-g31a88e428-SNAPSHOT\") && v < Version(\"1.5.1\") &&\n                    !ver.contains(\"nightly\") =>\n                  initialScalaRunnerArgs.copy(\n                    predefinedCliVersion = None,\n                    initialLauncherPath = None\n                  )\n                case _ if initialScalaRunnerArgs.initialLauncherPath.nonEmpty =>\n                  initialScalaRunnerArgs\n                case _ =>\n                  initialScalaRunnerArgs.copy(\n                    predefinedCliVersion = Some(ver),\n                    initialLauncherPath = Some(CommandUtils.getAbsolutePathToScalaCli(progName))\n                  )\n              ).toCliArgs\n              val newArgs = powerArgs ++ finalScalaRunnerArgs ++ args0\n              LauncherCli.runAndExit(ver, launcherOpts, newArgs)\n            case _ if\n                  javaMajorVersion < Constants.minimumLauncherJavaVersion\n                  && sys.props.get(\"scala-cli.kind\").exists(_.startsWith(\"jvm\")) =>\n              System.err.println(\n                s\"[${Console.RED}error${Console.RESET}] Java $javaMajorVersion is not supported with this Scala CLI (JVM) launcher.\"\n              )\n              System.err.println(\n                s\"[${Console.RED}error${Console.RESET}] Please upgrade to at least Java ${Constants.minimumLauncherJavaVersion} or use a native Scala CLI launcher instead.\"\n              )\n              sys.exit(1)\n            case _ if\n                  javaMajorVersion >= Constants.minimumLauncherJavaVersion\n                  && javaMajorVersion < Constants.minimumBloopJavaVersion\n                  && sys.props.get(\"scala-cli.kind\").exists(_.startsWith(\"jvm\")) =>\n              JavaLauncherCli.runAndExit(args.toSeq)\n            case None =>\n              launcherOpts.scalaRunner.progName\n                .foreach(pn => progName = pn)\n              if launcherOpts.powerOptions.power then\n                isSipScala = false\n                Right(args0.toArray)\n              else\n                // Parse again to register --power at any position\n                // Don't consume it, GlobalOptions parsing will do it\n                PowerOptions.parser.ignoreUnrecognized.parse(args0) match {\n                  case Right((powerOptions, _)) =>\n                    if powerOptions.power then\n                      isSipScala = false\n                    Right(args0.toArray)\n                  case Left(e) =>\n                    System.err.println(e.message)\n                    sys.exit(1)\n                }\n          }\n      }\n    }\n    val (systemProps, scalaCliArgs) = partitionArgs(remainingArgs)\n    if systemProps.nonEmpty then launcherJavaPropArgs = systemProps.toList\n    setSystemProps(systemProps)\n\n    (new BouncycastleSignerMaker).maybeInit()\n\n    coursier.Resolve.proxySetup()\n\n    // Getting killed by SIGPIPE quite often when on musl (in the \"static\" native\n    // image), but also sometimes on glibc, or even on macOS, when we use domain\n    // sockets to exchange with Bloop. So let's just ignore those (which should\n    // just make some read / write calls return -1).\n    if (!Properties.isWin && isGraalvmNativeImage)\n      ignoreSigpipe()\n\n    if (Properties.isWin && System.console() != null && coursier.paths.Util.useJni())\n      // Enable ANSI output in Windows terminal\n      try\n        coursier.jniutils.WindowsAnsiTerminal.enableAnsiOutput()\n      catch {\n        // ignore error resulting from redirect STDOUT to /dev/null\n        case e: java.io.IOException\n            if e.getMessage.contains(\"GetConsoleMode error 6\") =>\n      }\n\n    new ScalaCliCommands(progName, baseRunnerName, fullRunnerName)\n      .main(scalaCliArgs)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala",
    "content": "package scala.cli\n\nimport caseapp.core.app.CommandsEntryPoint\nimport caseapp.core.help.{Help, HelpFormat, RuntimeCommandsHelp}\n\nimport java.nio.file.InvalidPathException\n\nimport scala.cli.commands.*\nimport scala.cli.commands.shared.ScalaCliHelp\n\nclass ScalaCliCommands(\n  val progName: String,\n  baseRunnerName: String,\n  fullRunnerName: String\n) extends CommandsEntryPoint {\n\n  lazy val actualDefaultCommand = new default.Default(help)\n\n  // for debugging purposes - allows to run the scala-cli-signing binary from the Scala CLI JVM launcher\n  private lazy val pgpUseBinaryCommands =\n    java.lang.Boolean.getBoolean(\"scala-cli.pgp.binary-commands\")\n  private def pgpCommands       = new pgp.PgpCommands\n  private def pgpBinaryCommands = new pgp.PgpCommandsSubst\n\n  private def allCommands = Seq[ScalaCommand[?]](\n    addpath.AddPath,\n    bloop.Bloop,\n    bloop.BloopExit,\n    bloop.BloopOutput,\n    bloop.BloopStart,\n    bsp.Bsp,\n    clean.Clean,\n    compile.Compile,\n    config.Config,\n    default.DefaultFile,\n    dependencyupdate.DependencyUpdate,\n    directories.Directories,\n    doc.Doc,\n    export0.Export,\n    fix.Fix,\n    fmt.Fmt,\n    new HelpCmd(help),\n    installcompletions.InstallCompletions,\n    installhome.InstallHome,\n    `new`.New,\n    repl.Repl,\n    package0.Package,\n    pgp.PgpPull,\n    pgp.PgpPush,\n    publish.Publish,\n    publish.PublishLocal,\n    publish.PublishSetup,\n    run.Run,\n    github.SecretCreate,\n    github.SecretList,\n    setupide.SetupIde,\n    shebang.Shebang,\n    test.Test,\n    uninstall.Uninstall,\n    uninstallcompletions.UninstallCompletions,\n    update.Update,\n    version.Version\n  ) ++ (if (pgpUseBinaryCommands) Nil else pgpCommands.allScalaCommands.toSeq) ++\n    (if (pgpUseBinaryCommands) pgpBinaryCommands.allScalaCommands.toSeq else Nil)\n\n  def commands =\n    allCommands ++\n      (if (pgpUseBinaryCommands) Nil else pgpCommands.allExternalCommands.toSeq) ++\n      (if (pgpUseBinaryCommands) pgpBinaryCommands.allExternalCommands.toSeq else Nil)\n\n  override def description: String = {\n    val coreFeaturesString =\n      if ScalaCli.allowRestrictedFeatures then \"compile, run, test and package\"\n      else \"compile, run and test\"\n    s\"$fullRunnerName is a command-line tool to interact with the Scala language. It lets you $coreFeaturesString your Scala code.\"\n  }\n\n  override def summaryDesc =\n    s\"\"\"|See '$baseRunnerName <command> --help' to read about a specific subcommand. To see full help run '$baseRunnerName <command> --help-full'.\n        |\n        |To use launcher options, specify them before any other argument.\n        |For example, to run another $fullRunnerName version, specify it with the '--cli-version' launcher option:\n        |  ${Console.BOLD}$baseRunnerName --cli-version <version> args${Console.RESET}\"\"\".stripMargin\n  final override def defaultCommand = Some(actualDefaultCommand)\n\n  // FIXME Report this in case-app default NameFormatter\n  override lazy val help: RuntimeCommandsHelp = {\n    val parent = super.help\n    parent.copy(defaultHelp = Help[Unit]())\n  }\n\n  override def enableCompleteCommand    = true\n  override def enableCompletionsCommand = true\n\n  override def helpFormat: HelpFormat = ScalaCliHelp.helpFormat\n\n  private def isShebangFile(arg: String): Boolean = {\n    val pathOpt =\n      try Some(os.Path(arg, os.pwd))\n      catch {\n        case _: InvalidPathException => None\n      }\n    pathOpt.filter(os.isFile(_)).filter(_.toIO.canRead).exists { path =>\n      val content = os.read(path) // FIXME Charset?\n      content.startsWith(s\"#!/usr/bin/env $progName\" + System.lineSeparator())\n    }\n  }\n\n  override def main(args: Array[String]): Unit = {\n\n    // quick hack, until the raw args are kept in caseapp.RemainingArgs by case-app\n    actualDefaultCommand.rawArgs = args\n\n    commands.foreach {\n      case c: NeedsArgvCommand => c.setArgv(progName +: args)\n      case _                   =>\n    }\n    actualDefaultCommand.setArgv(progName +: args)\n\n    val processedArgs =\n      if (args.lengthCompare(1) > 0 && isShebangFile(args(0)))\n        Array(args(0), \"--\") ++ args.tail\n      else\n        args\n    super.main(processedArgs)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/CommandUtils.scala",
    "content": "package scala.cli.commands\n\nimport java.io.File\nimport java.nio.file.{Files, Paths}\n\nimport scala.build.Os\nimport scala.cli.ScalaCli\nimport scala.cli.internal.ProcUtil\nimport scala.util.Try\n\nobject CommandUtils {\n\n  def isOutOfDateVersion(newVersion: String, oldVersion: String): Boolean = {\n    import coursier.core.Version\n\n    Version(newVersion) > Version(oldVersion)\n  }\n\n  // Ensure the path to the CLI is absolute\n  def getAbsolutePathToScalaCli(programName: String): String =\n    if (programName.replace('\\\\', '/').contains(\"/\"))\n      os.Path(programName, Os.pwd).toString\n    else\n      /*\n  In order to get absolute path we first try to get it from coursier.mainJar (this works for standalone launcher)\n  If this fails we fallback to getting it from this class and finally we may also use rawArg if there is nothing left\n       */\n      sys.props.get(\"coursier.mainJar\")\n        .map(Paths.get(_).toAbsolutePath.toString)\n        .orElse {\n          val scalaCliPathsOnPATH = ProcUtil.findApplicationPathsOnPATH(ScalaCli.progName)\n          /*\n            https://github.com/VirtusLab/scala-cli/issues/1048\n            scalaCLICanonicalPathFromPATH is a map consisting of canonical Scala CLI paths for each symlink find on PATH.\n            If the current launcher path is the same as the canonical Scala CLI path,\n              we use a related symlink that targets to current launcher path.\n           */\n          val scalaCLICanonicalPathsFromPATH =\n            scalaCliPathsOnPATH\n              .map(path => (os.followLink(os.Path(path, os.pwd)), path))\n              .collect {\n                case (Some(canonicalPath), symlinkPath) => (canonicalPath, symlinkPath)\n              }.toMap\n          val currentLauncherPathOpt = Try(\n            // This is weird but on windows we get /D:\\a\\scala-cli...\n            Paths.get(getClass.getProtectionDomain.getCodeSource.getLocation.toURI)\n              .toAbsolutePath\n              .toString\n          ).toOption\n          currentLauncherPathOpt.map(currentLauncherPath =>\n            scalaCLICanonicalPathsFromPATH\n              .getOrElse(os.Path(currentLauncherPath), currentLauncherPath)\n          )\n        }\n        .getOrElse(programName)\n\n  lazy val shouldCheckUpdate: Boolean = scala.util.Random.nextInt() % 10 == 1\n\n  def printablePath(path: os.Path): String =\n    if (path.startsWith(Os.pwd)) \".\" + File.separator + path.relativeTo(Os.pwd).toString\n    else path.toString\n\n  extension (launcher: os.Path) {\n    def isJar: Boolean =\n      if os.isFile(launcher) then\n        val mimeType = Files.probeContentType(launcher.toNIO)\n        mimeType match\n          case \"application/java-archive\" | \"application/x-java-archive\" => true\n          case \"application/zip\"                                         =>\n            // Extra check: ensure META-INF/MANIFEST.MF exists inside\n            val jarFile = new java.util.jar.JarFile(launcher.toIO)\n            try jarFile.getEntry(\"META-INF/MANIFEST.MF\") != null\n            finally jarFile.close()\n          case _ => false\n      else false\n    def hasSelfExecutablePreamble: Boolean = {\n      // Read first 2 bytes raw: look for shebang '#!'\n      val in = Files.newInputStream(launcher.toNIO)\n      try\n        val b1 = in.read()\n        val b2 = in.read()\n        b1 == '#' && b2 == '!'\n      finally\n        in.close()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/CustomWindowsEnvVarUpdater.scala",
    "content": "package scala.cli.commands\n\nimport coursier.env.*\n\n// Only using this instead of coursier.env.WindowsEnvVarUpdater for the \"\\u0000\" striping thing,\n// that earlier version of the Scala CLI may have left behind.\n// We should be able to switch back to coursier.env.WindowsEnvVarUpdater\n// after a bit of time (once super early users used this code more).\n\ncase class CustomWindowsEnvVarUpdater(\n  powershellRunner: PowershellRunner = PowershellRunner(),\n  useJni: Option[Boolean] = None\n) extends EnvVarUpdater {\n\n  def withUseJni(opt: Option[Boolean]) = copy(useJni = opt)\n\n  private lazy val useJni0 = useJni.getOrElse {\n    // FIXME Should be coursier.paths.Util.useJni(), but it's not available from here.\n    !System.getProperty(\"coursier.jni\", \"\").equalsIgnoreCase(\"false\")\n  }\n\n  // https://stackoverflow.com/questions/9546324/adding-directory-to-path-environment-variable-in-windows/29109007#29109007\n  // https://docs.microsoft.com/fr-fr/dotnet/api/system.environment.getenvironmentvariable?view=netframework-4.8#System_Environment_GetEnvironmentVariable_System_String_System_EnvironmentVariableTarget_\n  // https://docs.microsoft.com/fr-fr/dotnet/api/system.environment.setenvironmentvariable?view=netframework-4.8#System_Environment_SetEnvironmentVariable_System_String_System_String_System_EnvironmentVariableTarget_\n\n  private def getEnvironmentVariable(name: String): Option[String] =\n    if (useJni0)\n      Option(coursier.jniutils.WindowsEnvironmentVariables.get(name))\n    else {\n      val output = powershellRunner\n        .runScript(CustomWindowsEnvVarUpdater.getEnvVarScript(name))\n        .stripSuffix(System.lineSeparator())\n      if (output == \"null\") // if ever the actual value is \"null\", we'll miss it\n        None\n      else\n        Some(output)\n    }\n\n  private def setEnvironmentVariable(name: String, value: String): Unit =\n    if (useJni0) {\n      val value0 =\n        if (value.contains(\"\\u0000\"))\n          value.split(';').filter(!_.contains(\"\\u0000\")).mkString(\";\")\n        else\n          value\n      coursier.jniutils.WindowsEnvironmentVariables.set(name, value0)\n    }\n    else\n      powershellRunner.runScript(CustomWindowsEnvVarUpdater.setEnvVarScript(name, value))\n\n  private def clearEnvironmentVariable(name: String): Unit =\n    if (useJni0)\n      coursier.jniutils.WindowsEnvironmentVariables.delete(name)\n    else\n      powershellRunner.runScript(CustomWindowsEnvVarUpdater.clearEnvVarScript(name))\n\n  def applyUpdate(update: EnvironmentUpdate): Boolean = {\n\n    // Beware, these are not an atomic operation overall\n    // (we might discard values added by others between our get and our set)\n\n    var setSomething = false\n\n    for ((k, v) <- update.set) {\n      val formerValueOpt = getEnvironmentVariable(k)\n      val needsUpdate    = formerValueOpt.forall(_ != v)\n      if (needsUpdate) {\n        setEnvironmentVariable(k, v)\n        setSomething = true\n      }\n    }\n\n    for ((k, v) <- update.pathLikeAppends) {\n      val formerValueOpt = getEnvironmentVariable(k)\n      val alreadyInList  = formerValueOpt\n        .exists(_.split(CustomWindowsEnvVarUpdater.windowsPathSeparator).contains(v))\n      if (!alreadyInList) {\n        val newValue = formerValueOpt\n          .fold(v)(_ + CustomWindowsEnvVarUpdater.windowsPathSeparator + v)\n        setEnvironmentVariable(k, newValue)\n        setSomething = true\n      }\n    }\n\n    setSomething\n  }\n\n  def tryRevertUpdate(update: EnvironmentUpdate): Boolean = {\n\n    // Beware, these are not an atomic operation overall\n    // (we might discard values added by others between our get and our set)\n\n    var setSomething = false\n\n    for ((k, v) <- update.set) {\n      val formerValueOpt = getEnvironmentVariable(k)\n      val wasUpdated     = formerValueOpt.exists(_ == v)\n      if (wasUpdated) {\n        clearEnvironmentVariable(k)\n        setSomething = true\n      }\n    }\n\n    for ((k, v) <- update.pathLikeAppends; formerValue <- getEnvironmentVariable(k)) {\n      val parts    = formerValue.split(CustomWindowsEnvVarUpdater.windowsPathSeparator)\n      val isInList = parts.contains(v)\n      if (isInList) {\n        val newValue = parts.filter(_ != v)\n        if (newValue.isEmpty)\n          clearEnvironmentVariable(k)\n        else\n          setEnvironmentVariable(\n            k,\n            newValue.mkString(CustomWindowsEnvVarUpdater.windowsPathSeparator)\n          )\n        setSomething = true\n      }\n    }\n\n    setSomething\n  }\n\n}\n\nobject CustomWindowsEnvVarUpdater {\n\n  private def getEnvVarScript(name: String): String =\n    s\"\"\"[Environment]::GetEnvironmentVariable(\"$name\", \"User\")\n       |\"\"\".stripMargin\n  private def setEnvVarScript(name: String, value: String): String =\n    // FIXME value might need some escaping here\n    s\"\"\"[Environment]::SetEnvironmentVariable(\"$name\", \"$value\", \"User\")\n       |\"\"\".stripMargin\n  private def clearEnvVarScript(name: String): String =\n    // FIXME value might need some escaping here\n    s\"\"\"[Environment]::SetEnvironmentVariable(\"$name\", $$null, \"User\")\n       |\"\"\".stripMargin\n\n  private def windowsPathSeparator: String =\n    \";\"\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/HelpCmd.scala",
    "content": "package scala.cli.commands\n\nimport caseapp.*\nimport caseapp.core.help.RuntimeCommandsHelp\n\nimport scala.build.Logger\nimport scala.cli.commands.shared.HelpOptions\n\nclass HelpCmd(actualHelp: => RuntimeCommandsHelp)\n    extends ScalaCommandWithCustomHelp[HelpOptions](actualHelp) {\n  override def names                   = List(List(\"help\"))\n  override def scalaSpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def runCommand(options: HelpOptions, args: RemainingArgs, logger: Logger): Unit =\n    customHelpAsked(showHidden = false)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/NeedsArgvCommand.scala",
    "content": "package scala.cli.commands\n\ntrait NeedsArgvCommand {\n  def setArgv(argv: Array[String]): Unit\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/OptionsHelper.scala",
    "content": "package scala.cli.commands\n\nobject OptionsHelper {\n  implicit class Mandatory[A](x: Option[A]) {\n    def mandatory(parameter: String, group: String): A =\n      x match {\n        case Some(v) => v\n        case None    =>\n          System.err.println(\n            s\"${parameter.toLowerCase.capitalize} parameter is mandatory for ${group.toLowerCase.capitalize}\"\n          )\n          sys.exit(1)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/RestrictableCommand.scala",
    "content": "package scala.cli.commands\n\nimport caseapp.core.app.Command\nimport caseapp.core.parser.Parser\n\nimport scala.build.Logger\nimport scala.build.input.ScalaCliInvokeData\n\ntrait RestrictableCommand[T](implicit myParser: Parser[T]) {\n  self: Command[T] =>\n\n  def shouldSuppressExperimentalFeatureWarnings: Boolean\n  def shouldSuppressDeprecatedFeatureWarnings: Boolean\n  def logger: Logger\n  protected def invokeData: ScalaCliInvokeData\n  override def parser: Parser[T] =\n    RestrictedCommandsParser(\n      parser = myParser,\n      logger = logger,\n      shouldSuppressExperimentalWarnings = shouldSuppressExperimentalFeatureWarnings,\n      shouldSuppressDeprecatedWarnings = shouldSuppressDeprecatedFeatureWarnings\n    )(using invokeData)\n\n  final def isRestricted: Boolean = scalaSpecificationLevel == SpecificationLevel.RESTRICTED\n\n  final def isExperimental: Boolean = scalaSpecificationLevel == SpecificationLevel.EXPERIMENTAL\n\n  /** Is that command a MUST / SHOULD / NICE TO have for the Scala runner specification? */\n  def scalaSpecificationLevel: SpecificationLevel\n  // To reduce imports...\n  protected def SpecificationLevel = scala.cli.commands.SpecificationLevel\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/RestrictedCommandsParser.scala",
    "content": "package scala.cli.commands\n\nimport caseapp.Name\nimport caseapp.core.parser.Parser\nimport caseapp.core.util.Formatter\nimport caseapp.core.{Arg, Error}\n\nimport scala.build.Logger\nimport scala.build.input.ScalaCliInvokeData\nimport scala.build.internals.FeatureType\nimport scala.cli.util.ArgHelpers.*\n\nobject RestrictedCommandsParser {\n  def apply[T](\n    parser: Parser[T],\n    logger: Logger,\n    shouldSuppressExperimentalWarnings: Boolean,\n    shouldSuppressDeprecatedWarnings: Boolean\n  )(using ScalaCliInvokeData): Parser[T] =\n    new Parser[T] {\n\n      type D = parser.D\n\n      def args: Seq[caseapp.core.Arg] = parser.args.filter(_.isSupported)\n\n      def get(\n        d: D,\n        nameFormatter: caseapp.core.util.Formatter[caseapp.Name]\n      ): Either[caseapp.core.Error, T] =\n        parser.get(d, nameFormatter)\n\n      def init: D = parser.init\n\n      def withDefaultOrigin(origin: String): caseapp.core.parser.Parser[T] =\n        RestrictedCommandsParser(\n          parser.withDefaultOrigin(origin),\n          logger,\n          shouldSuppressExperimentalWarnings,\n          shouldSuppressDeprecatedWarnings\n        )\n\n      override def step(\n        args: List[String],\n        index: Int,\n        d: D,\n        nameFormatter: Formatter[Name]\n      ): Either[(Error, Arg, List[String]), Option[(D, Arg, List[String])]] =\n        (parser.step(args, index, d, nameFormatter), args) match {\n          case (Right(Some(_, arg: Arg, _)), passedOption :: _) if !arg.isSupported =>\n            Left((\n              Error.UnrecognizedArgument(arg.powerOptionUsedInSip(passedOption)),\n              arg,\n              Nil\n            ))\n          case (r @ Right(Some(_, arg: Arg, _)), passedOption :: _)\n              if arg.isExperimental && !shouldSuppressExperimentalWarnings =>\n            logger.experimentalWarning(passedOption, FeatureType.Option)\n            r\n          case (r @ Right(Some(_, arg: Arg, _)), passedOption :: _)\n              if arg.isDeprecated && !shouldSuppressDeprecatedWarnings =>\n            // TODO implement proper deprecation logic: https://github.com/VirtusLab/scala-cli/issues/3258\n            arg.deprecatedOptionAliases.find(_ == passedOption)\n              .foreach { deprecatedAlias =>\n                logger.message(\n                  s\"\"\"[${Console.YELLOW}warn${Console.RESET}] The $deprecatedAlias option alias has been deprecated and may be removed in a future version.\"\"\"\n                )\n              }\n            r\n          case (other, _) =>\n            other\n        }\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala",
    "content": "package scala.cli.commands\n\nimport caseapp.core.app.Command\nimport caseapp.core.complete.{Completer, CompletionItem}\nimport caseapp.core.help.{Help, HelpFormat}\nimport caseapp.core.parser.Parser\nimport caseapp.core.util.Formatter\nimport caseapp.core.{Arg, Error, RemainingArgs}\nimport caseapp.{HelpMessage, Name}\nimport dependency.*\n\nimport java.util.concurrent.atomic.AtomicReference\n\nimport scala.annotation.tailrec\nimport scala.build.EitherCps.{either, value}\nimport scala.build.compiler.SimpleScalaCompiler\nimport scala.build.errors.BuildException\nimport scala.build.input.{ScalaCliInvokeData, SubCommand}\nimport scala.build.internal.util.WarningMessages\nimport scala.build.internal.{Constants, Runner}\nimport scala.build.internals.{EnvVar, FeatureType}\nimport scala.build.options.ScalacOpt.noDashPrefixes\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.build.{Artifacts, Logger, Positioned, ReplArtifacts}\nimport scala.cli.commands.default.LegacyScalaOptions\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.util.CommandHelpers\nimport scala.cli.commands.util.ScalacOptionsUtil.*\nimport scala.cli.config.Keys\nimport scala.cli.internal.ProcUtil\nimport scala.cli.launcher.LauncherOptions\nimport scala.cli.util.ConfigDbUtils.*\nimport scala.cli.{CurrentParams, ScalaCli}\n\nabstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T], inHelp: Help[T])\n    extends Command()(using myParser, inHelp)\n    with NeedsArgvCommand with CommandHelpers with RestrictableCommand[T] {\n\n  private val globalOptionsAtomic: AtomicReference[GlobalOptions] =\n    new AtomicReference(GlobalOptions.default)\n\n  private def globalOptions: GlobalOptions         = globalOptionsAtomic.get()\n  protected def launcherOptions: LauncherOptions   = ScalaCli.launcherOptions\n  protected def defaultScalaVersion: String        = ScalaCli.getDefaultScalaVersion\n  protected def launcherJavaPropArgs: List[String] = ScalaCli.getLauncherJavaPropArgs\n\n  def sharedOptions(t: T): Option[SharedOptions] = // hello borked unused warning\n    None\n  override def hasFullHelp                       = true\n  override def hidden: Boolean                   = shouldExcludeInSip\n  protected var argvOpt                          = Option.empty[Array[String]]\n  protected def allowRestrictedFeatures: Boolean =\n    ScalaCli.allowRestrictedFeatures || globalOptions.powerOptions.power\n  private def shouldExcludeInSip = (isRestricted || isExperimental) && !allowRestrictedFeatures\n  override def setArgv(argv: Array[String]): Unit = {\n    argvOpt = Some(argv)\n  }\n\n  /** @return the actual Scala CLI program name which was run */\n  protected def progName: String = ScalaCli.progName\n\n  /** @return the actual Scala CLI runner name which was run */\n  protected def fullRunnerName = ScalaCli.fullRunnerName\n\n  /** @return the actual Scala CLI base runner name, for SIP it is scala otherwise scala-cli */\n  protected def baseRunnerName = ScalaCli.baseRunnerName\n\n  // TODO Manage to have case-app give use the exact command name that was used instead\n  /** The actual sub-command name that was used. If the sub-command name is a list of strings, space\n    * is used as the separator. If [[argvOpt]] hasn't been defined, it defaults to [[name]].\n    */\n  protected def actualCommandName: String =\n    argvOpt.map { argv =>\n      @tailrec\n      def validCommand(potentialCommandName: List[String]): Option[List[String]] =\n        if potentialCommandName.isEmpty then None\n        else\n          names.find(_ == potentialCommandName) match {\n            case cmd @ Some(_) => cmd\n            case _             => validCommand(potentialCommandName.dropRight(1))\n          }\n\n      val maxCommandLength: Int    = names.map(_.length).max max 1\n      val maxPotentialCommandNames = argv.slice(1, maxCommandLength + 1).toList\n      validCommand(maxPotentialCommandNames).getOrElse(List(\"\"))\n    }.getOrElse(List(inHelp.progName)).mkString(\" \")\n\n  protected def actualFullCommand: String =\n    if actualCommandName.nonEmpty then s\"$progName $actualCommandName\" else progName\n\n  protected def invokeData: ScalaCliInvokeData =\n    ScalaCliInvokeData(\n      progName,\n      actualCommandName,\n      SubCommand.Other,\n      ProcUtil.isShebangCapableShell\n    )\n\n  given ScalaCliInvokeData = invokeData\n\n  override def error(message: Error): Nothing = {\n    System.err.println(\n      s\"\"\"${message.message}\n         |\n         |To list all available options, run\n         |  ${Console.BOLD}$actualFullCommand --help${Console.RESET}\"\"\".stripMargin\n    )\n    sys.exit(1)\n  }\n\n  // FIXME Report this in case-app default NameFormatter\n  override lazy val nameFormatter: Formatter[Name] = {\n    val parent = super.nameFormatter\n    (t: Name) =>\n      if (t.name.startsWith(\"-\")) t.name\n      else parent.format(t)\n  }\n\n  override def completer: Completer[T] = {\n    val parent = super.completer\n    new Completer[T] {\n      def optionName(prefix: String, state: Option[T], args: RemainingArgs): List[CompletionItem] =\n        parent.optionName(prefix, state, args)\n      def optionValue(\n        arg: Arg,\n        prefix: String,\n        state: Option[T],\n        args: RemainingArgs\n      ): List[CompletionItem] = {\n        val candidates = arg.name.name match {\n          case \"dependency\" =>\n            state.flatMap(sharedOptions).toList.flatMap { sharedOptions =>\n              val logger = sharedOptions.logger\n              val cache  = sharedOptions.coursierCache\n              val sv     = sharedOptions.buildOptions().orExit(logger)\n                .scalaParams\n                .toOption\n                .flatten\n                .map(_.scalaVersion)\n                .getOrElse(defaultScalaVersion)\n              val (fromIndex, completions) = cache.logger.use {\n                coursier.complete.Complete(cache)\n                  .withInput(prefix)\n                  .withScalaVersion(sv)\n                  .complete()\n                  .unsafeRun()(using cache.ec)\n              }\n              if (completions.isEmpty) Nil\n              else {\n                val prefix0 = prefix.take(fromIndex)\n                val values  = completions.map(c => prefix0 + c)\n                values.map { str =>\n                  CompletionItem(str)\n                }\n              }\n            }\n          case \"repository\" => Nil // TODO\n          case _            => Nil\n        }\n        candidates ++ parent.optionValue(arg, prefix, state, args)\n      }\n      def argument(prefix: String, state: Option[T], args: RemainingArgs): List[CompletionItem] =\n        parent.argument(prefix, state, args)\n    }\n  }\n\n  def maybePrintGroupHelp(options: T): Unit =\n    for (shared <- sharedOptions(options))\n      shared.helpGroups.maybePrintGroupHelp(help, helpFormat)\n\n  private def maybePrintWarnings(options: T): Unit = {\n    import scala.cli.commands.shared.ScalacOptions.YScriptRunnerOption\n    val logger = options.global.logging.logger\n    sharedOptions(options).foreach { so =>\n      val scalacOpts = so.scalacOptions.toScalacOptShadowingSeq\n      scalacOpts.keys\n        .find(_.value.noDashPrefixes == YScriptRunnerOption)\n        .map(_.value)\n        .foreach(k =>\n          logger.message(LegacyScalaOptions.yScriptRunnerWarning(k, scalacOpts.getOption(k)))\n        )\n    }\n  }\n\n  /** Print `scalac` output if passed options imply no inputs are necessary and raw `scalac` output\n    * is required instead. (i.e. `--scalac-option -help`)\n    * @param options\n    *   command options\n    */\n  def maybePrintSimpleScalacOutput(options: T, buildOptions: BuildOptions): Unit =\n    for {\n      shared <- sharedOptions(options)\n      scalacOptions        = shared.scalacOptions\n      updatedScalacOptions = scalacOptions.withScalacExtraOptions(shared.scalacExtra)\n      if updatedScalacOptions.map(_.noDashPrefixes).exists(ScalacOptions.isScalacPrintOption)\n      logger            = shared.logger\n      fixedBuildOptions = buildOptions.copy(scalaOptions =\n        buildOptions.scalaOptions.copy(defaultScalaVersion = Some(ScalaCli.getDefaultScalaVersion))\n      )\n      artifacts      <- fixedBuildOptions.artifacts(logger, Scope.Main).toOption\n      scalaArtifacts <- artifacts.scalaOpt\n      compilerClassPath   = scalaArtifacts.compilerClassPath\n      scalaVersion        = scalaArtifacts.params.scalaVersion\n      compileClassPath    = artifacts.compileClassPath\n      simpleScalaCompiler = SimpleScalaCompiler(\"java\", Nil, scaladoc = false)\n      javacOptions        = fixedBuildOptions.javaOptions.javacOptions.map(_.value)\n      javaHome            = fixedBuildOptions.javaHomeLocation().value\n    } {\n      val exitCode = simpleScalaCompiler.runSimpleScalacLike(\n        scalaVersion,\n        Option(javaHome),\n        javacOptions,\n        updatedScalacOptions,\n        compileClassPath,\n        compilerClassPath,\n        logger\n      )\n      sys.exit(exitCode)\n    }\n\n  def maybePrintToolsHelp(options: T, buildOptions: BuildOptions): Unit =\n    for {\n      shared <- sharedOptions(options)\n      if shared.helpGroups.helpScaladoc || shared.helpGroups.helpRepl ||\n      shared.helpGroups.helpScalafmt\n      logger = shared.logger\n      artifacts      <- buildOptions.artifacts(logger, Scope.Main).toOption\n      scalaArtifacts <- artifacts.scalaOpt\n      scalaParams = scalaArtifacts.params\n    } {\n      val exitCode: Either[BuildException, Int] = either {\n        val (classPath: Seq[os.Path], mainClass: String) =\n          if (shared.helpGroups.helpScaladoc) {\n            val docArtifacts = value {\n              Artifacts.fetchAnyDependencies(\n                Seq(Positioned.none(dep\"org.scala-lang::scaladoc:${scalaParams.scalaVersion}\")),\n                value(buildOptions.finalRepositories),\n                Some(scalaParams),\n                logger,\n                buildOptions.finalCache,\n                None\n              )\n            }\n            docArtifacts.files.map(os.Path(_, os.pwd)) -> \"dotty.tools.scaladoc.Main\"\n          }\n          else if (shared.helpGroups.helpRepl) {\n            val initialBuildOptions = buildOptionsOrExit(options)\n            val artifacts        = initialBuildOptions.artifacts(logger, Scope.Main).orExit(logger)\n            val javaVersion: Int = initialBuildOptions.javaHome().value.version\n            val replArtifacts    = value {\n              ReplArtifacts.default(\n                scalaParams = scalaParams,\n                dependencies = artifacts.userDependencies,\n                extraClassPath = Nil,\n                logger = logger,\n                cache = buildOptions.finalCache,\n                repositories = Nil,\n                addScalapy = None,\n                javaVersion = javaVersion\n              )\n            }\n            replArtifacts.replClassPath -> replArtifacts.replMainClass\n          }\n          else {\n            val fmtArtifacts = value {\n              Artifacts.fetchAnyDependencies(\n                Seq(Positioned.none(\n                  dep\"${Constants.scalafmtOrganization}:${Constants.scalafmtName}:${Constants.defaultScalafmtVersion}\"\n                )),\n                value(buildOptions.finalRepositories),\n                Some(scalaParams),\n                logger,\n                buildOptions.finalCache,\n                None\n              )\n            }\n            fmtArtifacts.files.map(os.Path(_, os.pwd)) -> \"org.scalafmt.cli.Cli\"\n          }\n        val retCode = Runner.runJvm(\n          buildOptions.javaHome().value.javaCommand,\n          Nil,\n          classPath,\n          mainClass,\n          Seq(\"-help\"),\n          logger\n        ).waitFor()\n        retCode\n      }\n      sys.exit(exitCode.orExit(logger))\n    }\n\n  private def maybePrintEnvsHelp(options: T): Unit =\n    if sharedOptions(options).exists(_.helpGroups.helpEnvs) then\n      println(EnvVar.helpMessage(isPower = allowRestrictedFeatures))\n      sys.exit(0)\n\n  override def helpFormat: HelpFormat = ScalaCliHelp.helpFormat\n\n  override val messages: Help[T] =\n    if shouldExcludeInSip then\n      inHelp.copy(helpMessage =\n        Some(HelpMessage(WarningMessages.powerCommandUsedInSip(\n          actualCommandName,\n          scalaSpecificationLevel\n        )))\n      )\n    else if isExperimental then\n      inHelp.copy(helpMessage =\n        inHelp.helpMessage.map(hm =>\n          hm.copy(\n            message =\n              s\"\"\"${hm.message}\n                 |\n                 |${WarningMessages.experimentalSubcommandWarning(inHelp.progName)}\"\"\".stripMargin,\n            detailedMessage =\n              if hm.detailedMessage.nonEmpty then\n                s\"\"\"${hm.detailedMessage}\n                   |\n                   |${WarningMessages.experimentalSubcommandWarning(inHelp.progName)}\"\"\".stripMargin\n              else hm.detailedMessage\n          )\n        )\n      )\n    else inHelp\n\n  /** @param options\n    *   command-specific [[T]] options\n    * @return\n    *   Tries to create BuildOptions based on [[sharedOptions]] and exits on error. Override to\n    *   change this behaviour.\n    */\n  def buildOptions(options: T): Option[BuildOptions] =\n    sharedOptions(options)\n      .map(shared => shared.buildOptions().orExit(shared.logger))\n\n  protected def buildOptionsOrExit(options: T): BuildOptions =\n    buildOptions(options)\n      .map(bo =>\n        bo.copy(scalaOptions =\n          bo.scalaOptions.copy(defaultScalaVersion = Some(defaultScalaVersion))\n        )\n      )\n      .getOrElse {\n        sharedOptions(options).foreach(_.logger.debug(\"build options could not be initialized\"))\n        sys.exit(1)\n      }\n  override def shouldSuppressExperimentalFeatureWarnings: Boolean =\n    globalOptions.globalSuppress.suppressExperimentalFeatureWarning\n      .orElse {\n        configDb.toOption\n          .flatMap(_.getOpt(Keys.suppressExperimentalFeatureWarning))\n      }\n      .getOrElse(false)\n\n  override def shouldSuppressDeprecatedFeatureWarnings: Boolean =\n    globalOptions.globalSuppress.suppressDeprecatedFeatureWarning\n      .orElse {\n        configDb.toOption\n          .flatMap(_.getOpt(Keys.suppressDeprecatedFeatureWarning))\n      }\n      .getOrElse(false)\n\n  override def logger: Logger = globalOptions.logging.logger\n\n  final override def main(progName: String, args: Array[String]): Unit = {\n    globalOptionsAtomic.set(GlobalOptions.get(args.toList).getOrElse(GlobalOptions.default))\n    super.main(progName, args)\n  }\n\n  /** This should be overridden instead of [[run]] when extending [[ScalaCommand]].\n    *\n    * @param options\n    *   the command's specific set of options\n    * @param remainingArgs\n    *   arguments remaining after parsing options\n    */\n  def runCommand(options: T, remainingArgs: RemainingArgs, logger: Logger): Unit\n\n  /** This implementation is final. Override [[runCommand]] instead. This logic is invoked at the\n    * start of running every [[ScalaCommand]].\n    */\n  final override def run(options: T, remainingArgs: RemainingArgs): Unit = {\n    CurrentParams.verbosity = options.global.logging.verbosity\n    if shouldExcludeInSip then\n      logger.error(WarningMessages.powerCommandUsedInSip(\n        actualCommandName,\n        scalaSpecificationLevel\n      ))\n      sys.exit(1)\n    else if isExperimental && !shouldSuppressExperimentalFeatureWarnings then\n      logger.experimentalWarning(name, FeatureType.Subcommand)\n\n    maybePrintWarnings(options)\n    maybePrintGroupHelp(options)\n    buildOptions(options).foreach { bo =>\n      maybePrintSimpleScalacOutput(options, bo)\n      maybePrintToolsHelp(options, bo)\n    }\n    maybePrintEnvsHelp(options)\n    logger.flushExperimentalWarnings\n    runCommand(options, remainingArgs, options.global.logging.logger)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/ScalaCommandWithCustomHelp.scala",
    "content": "package scala.cli.commands\n\nimport caseapp.core.Error\nimport caseapp.core.help.{Help, HelpCompanion, RuntimeCommandsHelp}\nimport caseapp.core.parser.Parser\n\nimport scala.cli.commands.default.LegacyScalaOptions\nimport scala.cli.commands.shared.{AllExternalHelpOptions, HasGlobalOptions}\nimport scala.cli.commands.util.HelpUtils.*\nimport scala.cli.launcher.LauncherOptions\n\nabstract class ScalaCommandWithCustomHelp[T <: HasGlobalOptions](\n  actualHelp: => RuntimeCommandsHelp\n)(\n  implicit\n  myParser: Parser[T],\n  help: Help[T]\n) extends ScalaCommand[T] {\n  private def launcherHelp: Help[LauncherOptions] = HelpCompanion.deriveHelp[LauncherOptions]\n\n  private def legacyScalaHelp: Help[LegacyScalaOptions] =\n    HelpCompanion.deriveHelp[LegacyScalaOptions]\n\n  protected def customHelp(showHidden: Boolean): String = {\n    val helpString                       = actualHelp.help(helpFormat, showHidden)\n    val launcherHelpString               = launcherHelp.optionsHelp(helpFormat, showHidden)\n    val legacyScalaHelpString            = legacyScalaHelp.optionsHelp(helpFormat, showHidden)\n    val allExternalHelp                  = HelpCompanion.deriveHelp[AllExternalHelpOptions]\n    val allExternalHelpString            = allExternalHelp.optionsHelp(helpFormat, showHidden)\n    val legacyScalaHelpStringWithPadding =\n      if legacyScalaHelpString.nonEmpty then\n        s\"\"\"\n           |$legacyScalaHelpString\n           |\"\"\".stripMargin\n      else \"\"\n    s\"\"\"$helpString\n       |\n       |$launcherHelpString\n       |\n       |$allExternalHelpString\n       |$legacyScalaHelpStringWithPadding\"\"\".stripMargin\n  }\n\n  protected def customHelpAsked(showHidden: Boolean): Nothing = {\n    println(customHelp(showHidden))\n    sys.exit(0)\n  }\n  override def helpAsked(progName: String, maybeOptions: Either[Error, T]): Nothing =\n    customHelpAsked(showHidden = false)\n  override def fullHelpAsked(progName: String): Nothing = customHelpAsked(showHidden = true)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/ScalaVersions.scala",
    "content": "package scala.cli.commands\n\nfinal case class ScalaVersions(\n  version: String,\n  binaryVersion: String\n)\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/WatchUtil.scala",
    "content": "package scala.cli.commands\n\nimport scala.annotation.tailrec\nimport scala.build.internal.StdInConcurrentReader\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\n\nobject WatchUtil {\n\n  lazy val isDevMode: Boolean =\n    Option(getClass.getProtectionDomain.getCodeSource)\n      .exists(_.getLocation.toExternalForm.endsWith(\"classes/\"))\n\n  def waitMessage(message: String): String = {\n    // Both short cuts actually always work, but Ctrl+C also exits mill in mill watch mode.\n    val shortCut = if (isDevMode) \"Ctrl+D\" else \"Ctrl+C\"\n    gray(s\"$message, press $shortCut to exit, or press Enter to re-run.\")\n  }\n\n  private def gray(message: String): String = {\n    val gray  = ScalaCliConsole.GRAY\n    val reset = Console.RESET\n    s\"$gray$message$reset\"\n  }\n\n  def printWatchMessage(): Unit =\n    System.err.println(waitMessage(\"Watching sources\"))\n\n  def printWatchWhileRunningMessage(): Unit =\n    System.err.println(gray(\"Watching sources while your program is running.\"))\n\n  def waitForCtrlC(\n    onPressEnter: () => Unit = () => (),\n    shouldReadInput: () => Boolean = () => true\n  ): Unit = synchronized {\n\n    @tailrec\n    def readNextChar(): Int =\n      if (shouldReadInput())\n        try StdInConcurrentReader.waitForLine().map(s => (s + '\\n').head.toInt).getOrElse(-1)\n        catch {\n          case _: InterruptedException =>\n            // Actually never called, as System.in.read isn't interruptible…\n            // That means we sometimes read input when we shouldn't.\n            readNextChar()\n        }\n      else {\n        try wait()\n        catch {\n          case _: InterruptedException =>\n        }\n        readNextChar()\n      }\n\n    var readKey = -1\n    while ({\n      readKey = readNextChar()\n      readKey != -1\n    })\n      if (readKey == '\\n')\n        onPressEnter()\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/addpath/AddPath.scala",
    "content": "package scala.cli.commands.addpath\n\nimport caseapp.*\nimport coursier.env.{EnvironmentUpdate, ProfileUpdater}\n\nimport java.io.File\n\nimport scala.build.Logger\nimport scala.build.internals.EnvVar\nimport scala.cli.commands.{CustomWindowsEnvVarUpdater, ScalaCommand}\nimport scala.util.Properties\n\nobject AddPath extends ScalaCommand[AddPathOptions] {\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n  override def runCommand(options: AddPathOptions, args: RemainingArgs, logger: Logger): Unit = {\n    if args.all.isEmpty then logger.error(\"Nothing to do\")\n    else {\n      val update = EnvironmentUpdate(\n        Nil,\n        Seq(EnvVar.Misc.path.name -> args.all.mkString(File.pathSeparator))\n      )\n      val didUpdate =\n        if (Properties.isWin) {\n          val updater = CustomWindowsEnvVarUpdater().withUseJni(Some(coursier.paths.Util.useJni()))\n          updater.applyUpdate(update)\n        }\n        else {\n          val updater = ProfileUpdater()\n          updater.applyUpdate(update, Some(options.title).filter(_.nonEmpty))\n        }\n      if !didUpdate then logger.log(\"Everything up-to-date\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/addpath/AddPathOptions.scala",
    "content": "package scala.cli.commands.addpath\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(\"Add entries to the PATH environment variable.\")\nfinal case class AddPathOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Tag(tags.restricted)\n    title: String = \"\"\n) extends HasGlobalOptions\n// format: on\n\nobject AddPathOptions {\n  implicit lazy val parser: Parser[AddPathOptions] = Parser.derive\n  implicit lazy val help: Help[AddPathOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/Bloop.scala",
    "content": "package scala.cli.commands.bloop\n\nimport bloop.rifle.internal.Operations\nimport bloop.rifle.{BloopRifle, BloopRifleConfig, BloopThreads}\nimport caseapp.core.RemainingArgs\n\nimport scala.build.internal.Constants\nimport scala.build.{Directories, Logger}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.SharedOptions\nimport scala.cli.commands.util.JvmUtils\nimport scala.concurrent.Await\nimport scala.concurrent.duration.Duration\n\nobject Bloop extends ScalaCommand[BloopOptions] {\n  override def hidden                  = true\n\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n\n  override def stopAtFirstUnrecognized = true\n\n  private def bloopRifleConfig0(opts: BloopOptions): BloopRifleConfig = {\n\n    // FIXME Basically a tweaked copy of SharedOptions.bloopRifleConfig\n    // Some in progress BuildOptions / JavaOptions refactoring of mine should allow\n    // to stop using SharedOptions and BuildOptions here, and deal with JavaOptions\n    // directly.\n\n    val sharedOptions = SharedOptions(\n      logging = opts.global.logging,\n      compilationServer = opts.compilationServer,\n      jvm = opts.jvm,\n      coursier = opts.coursier\n    )\n    val options = sharedOptions.buildOptions().orExit(opts.global.logging.logger)\n\n    val javaHomeInfo = opts.compilationServer.bloopJvm\n      .map(JvmUtils.downloadJvm(_, options))\n      .getOrElse {\n        JvmUtils.getJavaCmdVersionOrHigher(Constants.minimumBloopJavaVersion, options)\n      }.orExit(logger)\n\n    opts.compilationServer.bloopRifleConfig(\n      opts.global.logging.logger,\n      sharedOptions.coursierCache,\n      opts.global.logging.verbosity,\n      javaHomeInfo.javaCommand,\n      Directories.directories,\n      Some(javaHomeInfo.version)\n    )\n  }\n\n  override def runCommand(options: BloopOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val threads          = BloopThreads.create()\n    val bloopRifleConfig = bloopRifleConfig0(options)\n\n    val isRunning = BloopRifle.check(bloopRifleConfig, logger.bloopRifleLogger)\n\n    if (isRunning)\n      logger.debug(\"Found running Bloop server\")\n    else {\n      logger.debug(\"No running Bloop server found, starting one\")\n      val f = BloopRifle.startServer(\n        bloopRifleConfig,\n        threads.startServerChecks,\n        logger.bloopRifleLogger,\n        bloopRifleConfig.retainedBloopVersion.version.raw,\n        bloopRifleConfig.javaPath\n      )\n      Await.result(f, Duration.Inf)\n      logger.message(\"Bloop server started.\")\n    }\n\n    val args0 = args.all\n\n    args0 match {\n      case Seq() =>\n        // FIXME Give more details?\n        logger.message(\"Bloop server is running.\")\n      case Seq(cmd, args @ _*) =>\n        val assumeTty  = System.console() != null\n        val workingDir = options.workDirOpt.getOrElse(os.pwd).toNIO\n        val retCode = Operations.run(\n          command = cmd,\n          args = args.toArray,\n          workingDir = workingDir,\n          address = bloopRifleConfig.address,\n          inOpt = Some(System.in),\n          out = System.out,\n          err = System.err,\n          logger = logger.bloopRifleLogger,\n          assumeInTty = assumeTty,\n          assumeOutTty = assumeTty,\n          assumeErrTty = assumeTty\n        )\n        if (retCode == 0)\n          logger.debug(s\"Bloop command $cmd ran successfully (return code 0)\")\n        else {\n          logger.debug(s\"Got return code $retCode from Bloop server when running $cmd, exiting with it\")\n          sys.exit(retCode)\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopExit.scala",
    "content": "package scala.cli.commands.bloop\n\nimport bloop.rifle.{BloopRifle, BloopRifleConfig}\nimport caseapp.*\n\nimport scala.build.{Directories, Logger, Os}\nimport scala.cli.commands.ScalaCommand\n\nobject BloopExit extends ScalaCommand[BloopExitOptions] {\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n  override def names: List[List[String]] = List(\n    List(\"bloop\", \"exit\")\n  )\n\n  private def mkBloopRifleConfig(opts: BloopExitOptions): BloopRifleConfig = {\n    import opts.*\n    compilationServer.bloopRifleConfig(\n      global.logging.logger,\n      coursier.coursierCache(global.logging.logger, cacheLoggerPrefix = \"Downloading Bloop\"),\n      global.logging.verbosity,\n      \"java\", // shouldn't be used…\n      Directories.directories\n    )\n  }\n\n  override def runCommand(options: BloopExitOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val bloopRifleConfig = mkBloopRifleConfig(options)\n\n    val isRunning = BloopRifle.check(bloopRifleConfig, logger.bloopRifleLogger)\n\n    if (isRunning) {\n      val ret = BloopRifle.exit(bloopRifleConfig, Os.pwd.toNIO, logger.bloopRifleLogger)\n      logger.debug(s\"Bloop exit returned code $ret\")\n      if (ret == 0)\n        logger.message(\"Stopped Bloop server.\")\n      else {\n        if (options.global.logging.verbosity >= 0)\n          System.err.println(s\"Error running bloop exit command (return code $ret)\")\n        sys.exit(1)\n      }\n    }\n    else\n      logger.message(\"No running Bloop server found.\")\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopExitOptions.scala",
    "content": "package scala.cli.commands.bloop\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.{CoursierOptions, GlobalOptions, HasGlobalOptions, HelpMessages, SharedCompilationServerOptions}\n\n\n// format: off\n@HelpMessage(\n  s\"\"\"Stop Bloop if an instance is running.\n     |\n     |${HelpMessages.bloopInfo}\"\"\".stripMargin)\nfinal case class BloopExitOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions()\n) extends HasGlobalOptions\n// format: on\n\nobject BloopExitOptions {\n  implicit lazy val parser: Parser[BloopExitOptions] = Parser.derive\n  implicit lazy val help: Help[BloopExitOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopJson.scala",
    "content": "package scala.cli.commands.bloop\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nfinal case class BloopJson(javaOptions: List[String] = Nil)\n\nobject BloopJson {\n  val codec: JsonValueCodec[BloopJson] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopOptions.scala",
    "content": "package scala.cli.commands.bloop\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(BloopOptions.helpMessage, \"\", BloopOptions.detailedHelpMessage)\nfinal case class BloopOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(),\n  @Recurse\n    jvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n\n  @ExtraName(\"workingDir\")\n  @ExtraName(\"dir\")\n  @Tag(tags.restricted)\n    workingDirectory: Option[String] = None\n) extends HasGlobalOptions {\n  // format: on\n\n  def workDirOpt: Option[os.Path] =\n    workingDirectory\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n}\n\nobject BloopOptions {\n  implicit lazy val parser: Parser[BloopOptions] = Parser.derive\n  implicit lazy val help: Help[BloopOptions]   = Help.derive\n  val helpMessage: String = \"Interact with Bloop (the build server) or check its status.\"\n  val detailedHelpMessage: String =\n    s\"\"\"$helpMessage\n       |\n       |This sub-command allows to check the current status of Bloop.\n       |If Bloop isn't currently running, it will be started.\n       |\n       |${HelpMessages.bloopInfo}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopOutput.scala",
    "content": "package scala.cli.commands.bloop\n\nimport bloop.rifle.BloopRifleConfig\nimport caseapp.core.RemainingArgs\n\nimport scala.build.{Directories, Logger}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.CoursierOptions\n\nobject BloopOutput extends ScalaCommand[BloopOutputOptions] {\n\n  override def hidden       = true\n\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n\n  override def names: List[List[String]] = List(\n    List(\"bloop\", \"output\")\n  )\n  override def runCommand(options: BloopOutputOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val bloopRifleConfig = options.compilationServer.bloopRifleConfig(\n      logger,\n      CoursierOptions().coursierCache(logger, cacheLoggerPrefix = \"Downloading Bloop\"), // unused here\n      options.global.logging.verbosity,\n      \"unused-java\", // unused here\n      Directories.directories\n    )\n    val outputFile = bloopRifleConfig.address match {\n      case s: BloopRifleConfig.Address.DomainSocket =>\n        logger.debug(s\"Bloop server directory: ${s.path}\")\n        logger.debug(s\"Bloop server output path: ${s.outputPath}\")\n        os.Path(s.outputPath, os.pwd)\n      case tcp: BloopRifleConfig.Address.Tcp =>\n        if (options.global.logging.verbosity >= 0)\n          System.err.println(\n            s\"Error: Bloop server is listening on TCP at ${tcp.render}, output not available.\"\n          )\n        sys.exit(1)\n    }\n    if (!os.isFile(outputFile)) {\n      if (options.global.logging.verbosity >= 0)\n        System.err.println(s\"Error: $outputFile not found\")\n      sys.exit(1)\n    }\n    val content = os.read.bytes(outputFile)\n    logger.debug(s\"Read ${content.length} bytes from $outputFile\")\n    System.out.write(content)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopOutputOptions.scala",
    "content": "package scala.cli.commands.bloop\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpMessages, SharedCompilationServerOptions}\n\n// format: off\n@HelpMessage(\n  s\"\"\"Print Bloop output.\n     |\n     |${HelpMessages.bloopInfo}\"\"\".stripMargin)\nfinal case class BloopOutputOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(),\n) extends HasGlobalOptions\n// format: on\n\nobject BloopOutputOptions {\n  implicit lazy val parser: Parser[BloopOutputOptions] = Parser.derive\n  implicit lazy val help: Help[BloopOutputOptions]   = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopStart.scala",
    "content": "package scala.cli.commands.bloop\n\nimport bloop.rifle.internal.BuildInfo\nimport bloop.rifle.{BloopRifle, BloopRifleConfig, BloopThreads}\nimport caseapp.*\n\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.build.{Directories, Logger, Os}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.util.JvmUtils\nimport scala.concurrent.Await\nimport scala.concurrent.duration.Duration\n\nobject BloopStart extends ScalaCommand[BloopStartOptions] {\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n  override def names: List[List[String]] = List(\n    List(\"bloop\", \"start\")\n  )\n\n  private def mkBloopRifleConfig(opts: BloopStartOptions): BloopRifleConfig = {\n    import opts.*\n    val buildOptions = BuildOptions(\n      javaOptions = JvmUtils.javaOptions(jvm).orExit(global.logging.logger),\n      internal = InternalOptions(cache = Some(coursier.coursierCache(global.logging.logger)))\n    )\n\n    compilationServer.bloopRifleConfig(\n      global.logging.logger,\n      coursier.coursierCache(global.logging.logger, cacheLoggerPrefix = \"Downloading Bloop\"),\n      global.logging.verbosity,\n      buildOptions.javaHome().value.javaCommand,\n      Directories.directories\n    )\n  }\n\n  override def runCommand(options: BloopStartOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val threads          = BloopThreads.create()\n    val bloopRifleConfig = mkBloopRifleConfig(options)\n\n    val isRunning = BloopRifle.check(bloopRifleConfig, logger.bloopRifleLogger)\n\n    if (isRunning && options.force) {\n      logger.message(\"Found Bloop server running, stopping it.\")\n      val ret = BloopRifle.exit(bloopRifleConfig, Os.pwd.toNIO, logger.bloopRifleLogger)\n      logger.debug(s\"Bloop exit returned code $ret\")\n      if (ret == 0)\n        logger.message(\"Stopped Bloop server.\")\n      else {\n        if (options.global.logging.verbosity >= 0)\n          System.err.println(s\"Error running bloop exit command (return code $ret)\")\n        sys.exit(1)\n      }\n    }\n\n    if (isRunning && !options.force)\n      logger.message(\"Bloop server already running.\")\n    else {\n\n      val f = BloopRifle.startServer(\n        bloopRifleConfig,\n        threads.startServerChecks,\n        logger.bloopRifleLogger,\n        BuildInfo.version,\n        bloopRifleConfig.javaPath\n      )\n      Await.result(f, Duration.Inf)\n      logger.message(\"Bloop server started.\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bloop/BloopStartOptions.scala",
    "content": "package scala.cli.commands.bloop\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(\n  s\"\"\"Starts a Bloop instance, if none is running.\n     |\n     |${HelpMessages.bloopInfo}\"\"\".stripMargin)\nfinal case class BloopStartOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(),\n  @Recurse\n    jvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Name(\"f\")\n  @Tag(tags.restricted)\n    force: Boolean = false\n) extends HasGlobalOptions\n// format: on\n\nobject BloopStartOptions {\n  implicit lazy val parser: Parser[BloopStartOptions] = Parser.derive\n  implicit lazy val help: Help[BloopStartOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bsp/Bsp.scala",
    "content": "package scala.cli.commands.bsp\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.bsp.{BspReloadableOptions, BspThreads}\nimport scala.build.errors.BuildException\nimport scala.build.input.Inputs\nimport scala.build.internals.EnvVar\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.SharedOptions\nimport scala.cli.config.Keys\nimport scala.cli.launcher.LauncherOptions\nimport scala.cli.util.ConfigDbUtils\nimport scala.cli.{CurrentParams, ScalaCli}\nimport scala.concurrent.Await\nimport scala.concurrent.duration.Duration\n\nobject Bsp extends ScalaCommand[BspOptions] {\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.IMPLEMENTATION\n  private def latestSharedOptions(options: BspOptions): SharedOptions =\n    options.jsonOptions\n      .map(path => os.Path(path, os.pwd))\n      .filter(path => os.exists(path) && os.isFile(path))\n      .map { optionsPath =>\n        val content = os.read.bytes(os.Path(optionsPath, os.pwd))\n        readFromArray(content)(using SharedOptions.jsonCodec)\n      }.getOrElse(options.shared)\n\n  private def latestLauncherOptions(options: BspOptions): LauncherOptions =\n    options.jsonLauncherOptions\n      .map(path => os.Path(path, os.pwd))\n      .filter(path => os.exists(path) && os.isFile(path))\n      .map { optionsPath =>\n        val content = os.read.bytes(os.Path(optionsPath, os.pwd))\n        readFromArray(content)(using LauncherOptions.jsonCodec)\n      }.getOrElse(launcherOptions)\n  private def latestEnvsFromFile(options: BspOptions): Map[String, String] =\n    options.envs\n      .map(path => os.Path(path, os.pwd))\n      .filter(path => os.exists(path) && os.isFile(path))\n      .map { envsPath =>\n        val content = os.read.bytes(os.Path(envsPath, os.pwd))\n        implicit val mapCodec: JsonValueCodec[Map[String, String]] = JsonCodecMaker.make\n        readFromArray(content)\n      }\n      .getOrElse(Map.empty)\n  override def sharedOptions(options: BspOptions): Option[SharedOptions] =\n    Option(latestSharedOptions(options))\n\n  private def refreshPowerMode(\n    latestLauncherOptions: LauncherOptions,\n    latestSharedOptions: SharedOptions,\n    latestEnvs: Map[String, String]\n  ): Unit = {\n    val previousPowerMode = ScalaCli.allowRestrictedFeatures\n    val configPowerMode   = ConfigDbUtils.getLatestConfigDbOpt(latestSharedOptions.logger)\n      .flatMap(_.get(Keys.power).toOption)\n      .flatten\n      .getOrElse(false)\n    val envPowerMode       = latestEnvs.get(EnvVar.ScalaCli.power.name).exists(_.toBoolean)\n    val launcherPowerArg   = latestLauncherOptions.powerOptions.power\n    val subCommandPowerArg = latestSharedOptions.powerOptions.power\n    val latestPowerMode = configPowerMode || launcherPowerArg || subCommandPowerArg || envPowerMode\n    // only set power mode if it's been turned on since, never turn it off in BSP\n    if !previousPowerMode && latestPowerMode then ScalaCli.setPowerMode(latestPowerMode)\n  }\n\n  // not reusing buildOptions here, since they should be reloaded live instead\n  override def runCommand(options: BspOptions, args: RemainingArgs, logger: Logger): Unit = {\n    if (options.shared.logging.verbosity >= 3)\n      pprint.err.log(args)\n\n    val getSharedOptions: () => SharedOptions      = () => latestSharedOptions(options)\n    val getLauncherOptions: () => LauncherOptions  = () => latestLauncherOptions(options)\n    val getEnvsFromFile: () => Map[String, String] = () => latestEnvsFromFile(options)\n\n    refreshPowerMode(getLauncherOptions(), getSharedOptions(), getEnvsFromFile())\n\n    val preprocessInputs: Seq[String] => Either[BuildException, (Inputs, BuildOptions)] =\n      argsSeq =>\n        either {\n          val sharedOptions   = getSharedOptions()\n          val launcherOptions = getLauncherOptions()\n          val envs            = getEnvsFromFile()\n          val initialInputs   = value(sharedOptions.inputs(argsSeq, () => Inputs.default()))\n\n          refreshPowerMode(launcherOptions, sharedOptions, envs)\n\n          if (sharedOptions.logging.verbosity >= 3)\n            pprint.err.log(initialInputs)\n\n          val baseOptions      = buildOptions(sharedOptions, launcherOptions, envs)\n          val latestLogger     = sharedOptions.logging.logger\n          val persistentLogger = new PersistentDiagnosticLogger(latestLogger)\n\n          val crossResult = CrossSources.forInputs(\n            initialInputs,\n            Sources.defaultPreprocessors(\n              baseOptions.archiveCache,\n              baseOptions.internal.javaClassNameVersionOpt,\n              () => baseOptions.javaHome().value.javaCommand\n            ),\n            persistentLogger,\n            baseOptions.suppressWarningOptions,\n            baseOptions.internal.exclude,\n            download = baseOptions.downloader\n          )\n\n          val (allInputs, finalBuildOptions) = {\n            for\n              crossSourcesAndInputs <- crossResult\n              // compiler bug, can't do :\n              // (crossSources, crossInputs) <- crossResult\n              (crossSources, crossInputs) = crossSourcesAndInputs\n              sharedBuildOptions          = crossSources.sharedOptions(baseOptions)\n              scopedSources <- crossSources.scopedSources(sharedBuildOptions)\n              resolvedBuildOptions =\n                scopedSources.buildOptionsFor(Scope.Main).foldRight(sharedBuildOptions)(_.orElse(_))\n            yield (crossInputs, resolvedBuildOptions)\n          }.getOrElse(initialInputs -> baseOptions)\n\n          Build.updateInputs(allInputs, baseOptions) -> finalBuildOptions\n        }\n\n    val (inputs, finalBuildOptions) = preprocessInputs(args.all).orExit(logger)\n\n    /** values used for launching the bsp, especially for launching the bloop server, they do not\n      * include options extracted from sources, except in bloopRifleConfig - it's needed for\n      * correctly launching the bloop server\n      */\n    val initialBspOptions = {\n      val sharedOptions   = getSharedOptions()\n      val launcherOptions = getLauncherOptions()\n      val envs            = getEnvsFromFile()\n      val bspBuildOptions = buildOptions(sharedOptions, launcherOptions, envs)\n\n      refreshPowerMode(launcherOptions, sharedOptions, envs)\n\n      BspReloadableOptions(\n        buildOptions = bspBuildOptions,\n        bloopRifleConfig = sharedOptions.bloopRifleConfig(Some(finalBuildOptions))\n          .orExit(sharedOptions.logger),\n        logger = sharedOptions.logging.logger,\n        verbosity = sharedOptions.logging.verbosity\n      )\n    }\n\n    val bspReloadableOptionsReference = BspReloadableOptions.Reference { () =>\n      val sharedOptions   = getSharedOptions()\n      val launcherOptions = getLauncherOptions()\n      val envs            = getEnvsFromFile()\n\n      refreshPowerMode(launcherOptions, sharedOptions, envs)\n\n      BspReloadableOptions(\n        buildOptions = buildOptions(sharedOptions, launcherOptions, envs),\n        bloopRifleConfig = sharedOptions.bloopRifleConfig().orExit(sharedOptions.logger),\n        logger = sharedOptions.logging.logger,\n        verbosity = sharedOptions.logging.verbosity\n      )\n    }\n\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions\n\n    BspThreads.withThreads { threads =>\n      val bsp = scala.build.bsp.Bsp.create(\n        preprocessInputs.andThen(_.map(_._1)),\n        bspReloadableOptionsReference,\n        threads,\n        System.in,\n        System.out,\n        actionableDiagnostics\n      )\n\n      try {\n        val doneFuture = bsp.run(inputs, initialBspOptions)\n        Await.result(doneFuture, Duration.Inf)\n      }\n      finally bsp.shutdown()\n    }\n  }\n\n  private def buildOptions(\n    sharedOptions: SharedOptions,\n    launcherOptions: LauncherOptions,\n    envs: Map[String, String]\n  ): BuildOptions = {\n    val logger                     = sharedOptions.logger\n    val baseOptions: BuildOptions  = sharedOptions.buildOptions().orExit(logger)\n    val withDefaults: BuildOptions = baseOptions.copy(\n      classPathOptions = baseOptions.classPathOptions.copy(\n        fetchSources = baseOptions.classPathOptions.fetchSources.orElse(Some(true))\n      ),\n      scalaOptions = baseOptions.scalaOptions.copy(\n        semanticDbOptions = baseOptions.scalaOptions.semanticDbOptions.copy(\n          generateSemanticDbs =\n            baseOptions.scalaOptions.semanticDbOptions.generateSemanticDbs.orElse(Some(true))\n        )\n      ),\n      notForBloopOptions = baseOptions.notForBloopOptions.copy(\n        addRunnerDependencyOpt =\n          baseOptions.notForBloopOptions.addRunnerDependencyOpt.orElse(Some(false))\n      )\n    )\n    val withEnvs: BuildOptions = envs.get(EnvVar.Java.javaHome.name)\n      .filter(_ => withDefaults.javaOptions.javaHomeOpt.isEmpty)\n      .map(javaHome =>\n        withDefaults.copy(javaOptions =\n          withDefaults.javaOptions.copy(javaHomeOpt =\n            Some(Positioned(\n              Seq(Position.Custom(\"ide.env.JAVA_HOME\")),\n              os.Path(javaHome, Os.pwd)\n            ))\n          )\n        )\n      )\n      .getOrElse(withDefaults)\n    val withLauncherOptions = withEnvs.copy(\n      classPathOptions = withEnvs.classPathOptions.copy(\n        extraRepositories =\n          (withEnvs.classPathOptions.extraRepositories ++\n            launcherOptions.scalaRunner\n              .cliPredefinedRepository).distinct\n      ),\n      scalaOptions = withEnvs.scalaOptions.copy(\n        defaultScalaVersion = launcherOptions.scalaRunner.cliUserScalaVersion\n      )\n    )\n    withLauncherOptions\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/bsp/BspOptions.scala",
    "content": "package scala.cli.commands.bsp\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{HasSharedOptions, HelpMessages, SharedOptions}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(BspOptions.helpMessage, \"\", BspOptions.detailedHelpMessage)\nfinal case class BspOptions(\n  // FIXME There might be too many options in SharedOptions for the bsp command…\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n\n  @HelpMessage(\"Command-line options JSON file\")\n  @ValueDescription(\"path\")\n  @Hidden\n  @Tag(tags.implementation)\n    jsonOptions: Option[String] = None,\n\n  @HelpMessage(\"Command-line launcher options JSON file\")\n  @ValueDescription(\"path\")\n  @Hidden\n  @Tag(tags.implementation)\n  jsonLauncherOptions: Option[String] = None,\n\n  @HelpMessage(\"Command-line options environment variables file\")\n  @ValueDescription(\"path\")\n  @Hidden\n  @Tag(tags.implementation)\n  @Name(\"envsFile\")\n    envs: Option[String] = None\n) extends HasSharedOptions {\n  // format: on\n}\n\nobject BspOptions {\n  implicit lazy val parser: Parser[BspOptions] = Parser.derive\n  implicit lazy val help: Help[BspOptions]     = Help.derive\n  val cmdName                                  = \"bsp\"\n  private val helpHeader                       = \"Start BSP server.\"\n  val helpMessage: String                      =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(cmdName)}\n       |${HelpMessages.docsWebsiteReference}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |BSP stands for Build Server Protocol.\n       |For more information refer to https://build-server-protocol.github.io/\n       |\n       |This sub-command is not designed to be used by a human.\n       |It is normally supposed to be invoked by your IDE when a $fullRunnerName project is imported.\n       |\n       |${HelpMessages.docsWebsiteReference}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/clean/Clean.scala",
    "content": "package scala.cli.commands.clean\n\nimport caseapp.*\n\nimport scala.build.input.Inputs\nimport scala.build.internal.Constants\nimport scala.build.{Logger, Os}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.setupide.SetupIde\nimport scala.cli.commands.shared.HelpCommandGroup\n\nobject Clean extends ScalaCommand[CleanOptions] {\n  override def group: String = HelpCommandGroup.Main.toString\n\n  override def scalaSpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def runCommand(options: CleanOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val inputs = Inputs(\n      args.all,\n      Os.pwd,\n      defaultInputs = () => Inputs.default(),\n      forcedWorkspace = options.workspace.forcedWorkspaceOpt,\n      allowRestrictedFeatures = allowRestrictedFeatures,\n      extraClasspathWasPassed = false\n    ) match {\n      case Left(message) =>\n        System.err.println(message)\n        sys.exit(1)\n      case Right(i) => i\n    }\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    val workDir       = inputs.workspace / Constants.workspaceDirName\n    val (_, bspEntry) = SetupIde.bspDetails(inputs.workspace, options.bspFile)\n\n    if (os.exists(workDir)) {\n      logger.debug(s\"Working directory: $workDir\")\n      if (os.isDir(workDir)) {\n        logger.log(s\"Removing $workDir\")\n        os.remove.all(workDir)\n      }\n      else\n        logger.log(s\"$workDir is not a directory, ignoring it.\")\n    }\n\n    if (os.exists(bspEntry)) {\n      logger.log(s\"Removing $bspEntry\")\n      os.remove(bspEntry)\n    }\n    else\n      logger.log(s\"No BSP entry found, so ignoring.\")\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/clean/CleanOptions.scala",
    "content": "package scala.cli.commands.clean\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.*\n\n// format: off\n@HelpMessage(CleanOptions.helpMessage, \"\", CleanOptions.detailedHelpMessage)\nfinal case class CleanOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    bspFile: SharedBspFileOptions = SharedBspFileOptions(),\n  @Recurse\n    workspace: SharedWorkspaceOptions = SharedWorkspaceOptions()\n) extends HasGlobalOptions\n// format: on\n\nobject CleanOptions {\n  implicit lazy val parser: Parser[CleanOptions] = Parser.derive\n  implicit lazy val help: Help[CleanOptions]     = Help.derive\n  val cmdName                                    = \"clean\"\n  private val helpHeader                         = \"Clean the workspace.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |Passed inputs will establish the $fullRunnerName project, for which the workspace will be cleaned.\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/compile/Compile.scala",
    "content": "package scala.cli.commands.compile\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\n\nimport java.io.File\n\nimport scala.build.options.Scope\nimport scala.build.{Build, BuildThreads, Builds, Logger}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.setupide.SetupIde\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}\nimport scala.cli.commands.update.Update\nimport scala.cli.commands.util.BuildCommandHelpers\nimport scala.cli.commands.util.BuildCommandHelpers.*\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel, WatchUtil}\nimport scala.cli.config.Keys\nimport scala.cli.packaging.Library.fullClassPathMaybeAsJar\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nobject Compile extends ScalaCommand[CompileOptions] with BuildCommandHelpers {\n  override def group: String = HelpCommandGroup.Main.toString\n\n  override def sharedOptions(options: CompileOptions): Option[SharedOptions] = Some(options.shared)\n\n  override def buildOptions(options: CompileOptions): Some[scala.build.options.BuildOptions] =\n    Some(options.buildOptions().orExit(options.shared.logger))\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.MUST\n  val primaryHelpGroups: Seq[HelpGroup]                    =\n    Seq(\n      HelpGroup.Compilation,\n      HelpGroup.Scala,\n      HelpGroup.Java,\n      HelpGroup.Watch,\n      HelpGroup.CompilationServer\n    )\n\n  override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroups(primaryHelpGroups)\n\n  override def runCommand(options: CompileOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val buildOptions = buildOptionsOrExit(options)\n    val inputs       = options.shared.inputs(args.all).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    SetupIde.runSafe(\n      options.shared,\n      inputs,\n      logger,\n      buildOptions,\n      Some(name),\n      args.all\n    )\n    if (CommandUtils.shouldCheckUpdate)\n      Update.checkUpdateSafe(logger)\n\n    val cross = options.cross.cross.getOrElse(false)\n    if (options.printClassPath && cross) {\n      System.err.println(s\"Error: cannot specify both --print-class-path and --cross\")\n      sys.exit(1)\n    }\n\n    def postBuild(builds: Builds, allowExit: Boolean): Unit = {\n      val failed = builds.all.exists {\n        case _: Build.Failed => true\n        case _               => false\n      }\n      val cancelled = builds.all.exists {\n        case _: Build.Cancelled => true\n        case _                  => false\n      }\n      if (failed) {\n        System.err.println(\"Compilation failed\")\n        if (allowExit)\n          sys.exit(1)\n      }\n      else if (cancelled) {\n        System.err.println(\"Compilation cancelled\")\n        if (allowExit)\n          sys.exit(1)\n      }\n      else {\n        val successulBuildOpt =\n          for {\n            build <- builds.get(Scope.Test).orElse(builds.get(Scope.Main))\n            s     <- build.successfulOpt\n          } yield s\n        if (options.printClassPath)\n          for (s <- successulBuildOpt) {\n            val cp = s.fullClassPathMaybeAsJar(options.shared.asJar)\n              .map(_.toString)\n              .mkString(File.pathSeparator)\n            println(cp)\n          }\n        successulBuildOpt.foreach(_.copyOutput(options.shared))\n      }\n    }\n\n    val threads = BuildThreads.create()\n\n    val compilerMaker         = options.shared.compilerMaker(threads)\n    val configDb              = ConfigDbUtils.configDb.orExit(logger)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions.orElse(\n        configDb.get(Keys.actions).getOrElse(None)\n      )\n\n    val shouldBuildTestScope = options.shared.scope.test.getOrElse(false)\n    if (options.watch.watchMode) {\n      val watcher = Build.watch(\n        inputs,\n        buildOptions,\n        compilerMaker,\n        None,\n        logger,\n        crossBuilds = cross,\n        buildTests = shouldBuildTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics,\n        postAction = () => WatchUtil.printWatchMessage()\n      ) { res =>\n        for (builds <- res.orReport(logger))\n          postBuild(builds, allowExit = false)\n      }\n      try WatchUtil.waitForCtrlC(() => watcher.schedule())\n      finally watcher.dispose()\n    }\n    else {\n      val res = Build.build(\n        inputs,\n        buildOptions,\n        compilerMaker,\n        None,\n        logger,\n        crossBuilds = cross,\n        buildTests = shouldBuildTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics\n      )\n      val builds = res.orExit(logger)\n      postBuild(builds, allowExit = true)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/compile/CompileOptions.scala",
    "content": "package scala.cli.commands.compile\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n@HelpMessage(CompileOptions.helpMessage, \"\", CompileOptions.detailedHelpMessage)\n// format: off\nfinal case class CompileOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n    cross: CrossOptions = CrossOptions(),\n\n  @Group(HelpGroup.Compilation.toString)\n  @Name(\"p\")\n  @Name(\"printClasspath\")\n  @HelpMessage(\"Print the resulting class path\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n    printClassPath: Boolean = false\n) extends HasSharedOptions with HasSharedWatchOptions\n  // format: on\n\nobject CompileOptions {\n  implicit lazy val parser: Parser[CompileOptions] = Parser.derive\n  implicit lazy val help: Help[CompileOptions]     = Help.derive\n  val cmdName                                      = \"compile\"\n  private val helpHeader                           = \"Compile Scala code.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/config/Config.scala",
    "content": "package scala.cli.commands.config\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.HelpFormat\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException, MalformedCliInputError}\nimport scala.build.internal.util.WarningMessages\nimport scala.build.internals.FeatureType\nimport scala.build.{Directories, Logger}\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.config.*\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nobject Config extends ScalaCommand[ConfigOptions] {\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.MUST\n\n  override def helpFormat: HelpFormat = super.helpFormat\n    .withHiddenGroup(HelpGroup.Java)\n    .withPrimaryGroup(HelpGroup.Config)\n\n  override def runCommand(options: ConfigOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val directories = Directories.directories\n\n    if (options.dump) {\n      val content = os.read.bytes(directories.dbPath)\n      System.out.write(content)\n    }\n    else {\n      val db = ConfigDbUtils.configDb.orExit(logger)\n\n      def unrecognizedKey(key: String): Nothing = {\n        System.err.println(s\"Error: unrecognized key $key\")\n        sys.exit(1)\n      }\n\n      args.all match {\n        case Seq() =>\n          if (options.createPgpKey) {\n            if (options.pgpPassword.isEmpty) {\n              logger.error(\n                s\"--pgp-password not specified, use 'none' to create an unprotected keychain or 'random' to generate a password\"\n              )\n              sys.exit(1)\n            }\n            val coursierCache = options.coursier.coursierCache(logger)\n            val secKeyEntry   = Keys.pgpSecretKey\n            val pubKeyEntry   = Keys.pgpPublicKey\n\n            val mail = options.email\n              .filter(_.trim.nonEmpty)\n              .orElse {\n                db.get(Keys.userEmail)\n                  .wrapConfigException\n                  .orExit(logger)\n              }\n              .getOrElse {\n                logger.error(\n                  s\"--email ... not specified, and ${Keys.userEmail.fullName} not set (either is required to generate a PGP key)\"\n                )\n                sys.exit(1)\n              }\n\n            val passwordOpt = if (options.pgpPassword.contains(\"none\"))\n              None\n            else if (options.pgpPassword.contains(\"random\"))\n              Some(ThrowawayPgpSecret.pgpPassPhrase())\n            else\n              options.pgpPassword.map(scala.cli.signing.shared.Secret.apply)\n\n            val (pgpPublic, pgpSecret) =\n              ThrowawayPgpSecret.pgpSecret(\n                mail,\n                passwordOpt,\n                logger,\n                coursierCache,\n                options.jvm,\n                options.coursier,\n                options.scalaSigning.cliOptions()\n              ).orExit(logger)\n\n            db.set(secKeyEntry, PasswordOption.Value(pgpSecret.toConfig))\n            db.set(pubKeyEntry, PasswordOption.Value(pgpPublic.toConfig))\n            db.save(directories.dbPath.toNIO)\n              .wrapConfigException\n              .orExit(logger)\n\n            logger.message(\"PGP keychains written to config\")\n            if (options.pgpPassword.contains(\"random\"))\n              passwordOpt.foreach { password =>\n                println(\n                  s\"\"\"Password: ${password.value}\n                     |Don't lose it!\n                     |\"\"\".stripMargin\n                )\n              }\n          }\n          else {\n            System.err.println(\"No argument passed\")\n            sys.exit(1)\n          }\n        case Seq(name, values @ _*) =>\n          Keys.map.get(name) match {\n            case None => unrecognizedKey(name)\n            case Some(powerEntry)\n                if (powerEntry.isRestricted || powerEntry.isExperimental) &&\n                !allowRestrictedFeatures =>\n              logger.error(WarningMessages.powerConfigKeyUsedInSip(powerEntry))\n              sys.exit(1)\n            case Some(entry) =>\n              if entry.isExperimental && !shouldSuppressExperimentalFeatureWarnings then\n                logger.experimentalWarning(entry.fullName, FeatureType.ConfigKey)\n              if (values.isEmpty)\n                if (options.unset) {\n                  db.remove(entry)\n                  db.save(directories.dbPath.toNIO)\n                    .wrapConfigException\n                    .orExit(logger)\n                }\n                else {\n                  val valueOpt = db.getAsString(entry)\n                    .wrapConfigException\n                    .orExit(logger)\n                  valueOpt match {\n                    case Some(value) =>\n                      for (v <- value)\n                        if (options.passwordValue && entry.isPasswordOption)\n                          PasswordOption.parse(v) match {\n                            case Left(err) =>\n                              System.err.println(err)\n                              sys.exit(1)\n                            case Right(passwordOption) =>\n                              val password = passwordOption.getBytes\n                              System.out.write(password.value)\n                          }\n                        else\n                          println(v)\n                    case None =>\n                      logger.debug(s\"No value found for $name\")\n                  }\n                }\n              else {\n                def parseSecret(input: String): Either[BuildException, Option[PasswordOption]] =\n                  if (input.trim.isEmpty) Right(None)\n                  else\n                    PasswordOption.parse(input)\n                      .left.map(err =>\n                        new MalformedCliInputError(s\"Malformed secret value '$input': $err\")\n                      )\n                      .map(Some(_))\n                entry match {\n                  case Keys.repositoryCredentials =>\n                    if (options.unset)\n                      values match {\n                        case Seq(host) =>\n                          val valueOpt = db.get(Keys.repositoryCredentials)\n                            .wrapConfigException\n                            .orExit(logger)\n                          def notFound(): Unit =\n                            logger.message(\n                              s\"No ${Keys.repositoryCredentials.fullName} found for host $host\"\n                            )\n                          valueOpt match {\n                            case None           => notFound()\n                            case Some(credList) =>\n                              val idx = credList.indexWhere(_.host == host)\n                              if (idx < 0) notFound()\n                              else {\n                                val updatedCredList = credList.take(idx) ::: credList.drop(idx + 1)\n                                db.set(Keys.repositoryCredentials, updatedCredList)\n                                db.save(directories.dbPath.toNIO).wrapConfigException.orExit(logger)\n                              }\n                          }\n                        case _ =>\n                          System.err.println(\n                            s\"Usage: $progName config --remove ${Keys.repositoryCredentials.fullName} host\"\n                          )\n                          sys.exit(1)\n                      }\n                    else {\n                      val (host, rawUser, rawPassword, realmOpt) = values match {\n                        case Seq(host, rawUser, rawPassword) => (host, rawUser, rawPassword, None)\n                        case Seq(host, rawUser, rawPassword, realm) =>\n                          (host, rawUser, rawPassword, Some(realm))\n                        case _ =>\n                          System.err.println(\n                            s\"Usage: $progName config ${Keys.repositoryCredentials.fullName} host user password [realm]\"\n                          )\n                          System.err.println(\n                            \"Note that user and password are assumed to be secrets, specified like value:... or env:ENV_VAR_NAME, see https://scala-cli.virtuslab.org/docs/reference/password-options for more details\"\n                          )\n                          sys.exit(1)\n                      }\n                      val (userOpt, passwordOpt) = (parseSecret(rawUser), parseSecret(rawPassword))\n                        .traverseN\n                        .left.map(CompositeBuildException(_))\n                        .orExit(logger)\n                      val credentials =\n                        if (options.passwordValue)\n                          RepositoryCredentials(\n                            host,\n                            userOpt.map(user => PasswordOption.Value(user.get())),\n                            passwordOpt.map(password => PasswordOption.Value(password.get())),\n                            realm = realmOpt,\n                            optional = options.optional,\n                            matchHost = options.matchHost.orElse(Some(true)),\n                            httpsOnly = options.httpsOnly,\n                            passOnRedirect = options.passOnRedirect\n                          )\n                        else\n                          RepositoryCredentials(\n                            host,\n                            userOpt,\n                            passwordOpt,\n                            realm = realmOpt,\n                            optional = options.optional,\n                            matchHost = options.matchHost.orElse(Some(true)),\n                            httpsOnly = options.httpsOnly,\n                            passOnRedirect = options.passOnRedirect\n                          )\n                      val previousValueOpt =\n                        db.get(Keys.repositoryCredentials).wrapConfigException.orExit(logger)\n                      val newValue = credentials :: previousValueOpt.getOrElse(Nil)\n                      db.set(Keys.repositoryCredentials, newValue)\n                    }\n\n                  case Keys.publishCredentials =>\n                    val (host, rawUser, rawPassword, realmOpt) = values match {\n                      case Seq(host, rawUser, rawPassword) => (host, rawUser, rawPassword, None)\n                      case Seq(host, rawUser, rawPassword, realm) =>\n                        (host, rawUser, rawPassword, Some(realm))\n                      case _ =>\n                        System.err.println(\n                          s\"Usage: $progName config ${Keys.publishCredentials.fullName} host user password [realm]\"\n                        )\n                        System.err.println(\n                          \"Note that user and password are assumed to be secrets, specified like value:... or env:ENV_VAR_NAME, see https://scala-cli.virtuslab.org/docs/reference/password-options for more details\"\n                        )\n                        sys.exit(1)\n                    }\n                    val (userOpt, passwordOpt) = (parseSecret(rawUser), parseSecret(rawPassword))\n                      .traverseN\n                      .left.map(CompositeBuildException(_))\n                      .orExit(logger)\n                    val credentials =\n                      if (options.passwordValue)\n                        PublishCredentials(\n                          host,\n                          userOpt.map(user => PasswordOption.Value(user.get())),\n                          passwordOpt.map(password => PasswordOption.Value(password.get())),\n                          realm = realmOpt\n                        )\n                      else\n                        PublishCredentials(host, userOpt, passwordOpt, realm = realmOpt)\n                    val previousValueOpt =\n                      db.get(Keys.publishCredentials).wrapConfigException.orExit(logger)\n                    val newValue = credentials :: previousValueOpt.getOrElse(Nil)\n                    db.set(Keys.publishCredentials, newValue)\n\n                  case _ =>\n                    val finalValues =\n                      if (options.passwordValue && entry.isPasswordOption)\n                        values.map { input =>\n                          PasswordOption.parse(input) match {\n                            case Left(err) =>\n                              System.err.println(err)\n                              sys.exit(1)\n                            case Right(passwordOption) =>\n                              PasswordOption.Value(passwordOption.get()).asString.value\n                          }\n                        }\n                      else\n                        values\n\n                    checkIfAskForUpdate(entry, finalValues, db, options)\n\n                    db.setFromString(entry, finalValues)\n                      .wrapConfigException\n                      .orExit(logger)\n                }\n\n                db.save(directories.dbPath.toNIO)\n                  .wrapConfigException\n                  .orExit(logger)\n              }\n          }\n      }\n    }\n\n    logger.flushExperimentalWarnings\n  }\n\n  /** Check whether to ask for an update depending on the provided key.\n    */\n  private def checkIfAskForUpdate(\n    entry: Key[?],\n    newValues: Seq[String],\n    db: ConfigDb,\n    options: ConfigOptions\n  ): Unit = entry match {\n    case listEntry: Key.StringListEntry =>\n      val previousValue = db.get(listEntry).wrapConfigException.orExit(logger).getOrElse(Nil)\n\n      confirmUpdateValue(\n        listEntry.fullName,\n        previousValue,\n        newValues,\n        options\n      ).wrapConfigException.orExit(logger)\n    case _ => ()\n  }\n\n  /** If the new value is different from the previous value, ask user for confirmation or suggest to\n    * use --force option. If the new value is the same as the previous value, confirm the operation.\n    * If force option is provided, skip the confirmation.\n    */\n  private def confirmUpdateValue(\n    keyFullName: String,\n    previousValues: Seq[String],\n    newValues: Seq[String],\n    options: ConfigOptions\n  ): Either[Exception, Unit] =\n    val (newValuesStr, previousValueStr) = (newValues.mkString(\", \"), previousValues.mkString(\", \"))\n    val shouldUpdate = !options.force && newValuesStr != previousValueStr && previousValues.nonEmpty\n\n    if shouldUpdate then\n      val interactive = options.global.logging.verbosityOptions.interactiveInstance()\n      val msg         =\n        s\"Do you want to change the key '$keyFullName' from '$previousValueStr' to '$newValuesStr'?\"\n      interactive.confirmOperation(msg) match {\n        case Some(true) => Right(())\n        case _          => Left(new Exception(\n            s\"Unable to change the value for the key: '$keyFullName' from '$previousValueStr' to '$newValuesStr' without the force flag. Please pass -f or --force to override.\"\n          ))\n      }\n    else Right(())\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/config/ConfigOptions.scala",
    "content": "package scala.cli.commands.config\n\nimport caseapp.*\n\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\nimport scala.cli.ScalaCli.{allowRestrictedFeatures, fullRunnerName, progName}\nimport scala.cli.commands.pgp.PgpScalaSigningOptions\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\nimport scala.cli.config.{Key, Keys}\n\n// format: off\n@HelpMessage(ConfigOptions.helpMessage, \"\", ConfigOptions.detailedHelpMessage)\nfinal case class ConfigOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Recurse\n    jvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    scalaSigning: PgpScalaSigningOptions = PgpScalaSigningOptions(),\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"Dump config DB as JSON\")\n  @Hidden\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n    dump: Boolean = false,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"Create PGP keychain in config\")\n  @Tag(tags.inShortHelp)\n  @Tag(tags.experimental)\n    createPgpKey: Boolean = false,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"A password used to encode the private PGP keychain\")\n  @Tag(tags.experimental)\n  @ValueDescription(\"YOUR_PASSWORD|random|none\")\n  @ExtraName(\"passphrase\")\n  pgpPassword: Option[String] = None,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"Email used to create the PGP keychains in config\")\n  @Tag(tags.experimental)\n    email: Option[String] = None,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\n    \"\"\"When accessing config's content print the password value rather than how to get the password\n      |When saving an entry in config save the password value rather than how to get the password\n      |e.g. print/save the value of environment variable ENV_VAR rather than \"env:ENV_VAR\"\n      |\"\"\".stripMargin)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    passwordValue: Boolean = false,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"Remove an entry from config\")\n  @Tag(tags.inShortHelp)\n  @Tag(tags.should)\n  @ExtraName(\"remove\")\n    unset: Boolean = false,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"For repository.credentials and publish.credentials, whether these credentials should be HTTPS only (default: true)\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    httpsOnly: Option[Boolean] = None,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"For repository.credentials, whether to use these credentials automatically based on the host\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    matchHost: Option[Boolean] = None,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"For repository.credentials, whether to use these credentials are optional\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    optional: Option[Boolean] = None,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"For repository.credentials, whether to use these credentials should be passed upon redirection\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    passOnRedirect: Option[Boolean] = None,\n  @Group(HelpGroup.Config.toString)\n  @HelpMessage(\"Force overwriting values for key\")\n  @ExtraName(\"f\")\n  @Tag(tags.inShortHelp)\n  @Tag(tags.should)\n    force: Boolean = false\n) extends HasGlobalOptions\n// format: on\n\nobject ConfigOptions {\n  implicit lazy val parser: Parser[ConfigOptions] = Parser.derive\n  implicit lazy val help: Help[ConfigOptions]     = Help.derive\n  private val helpHeader: String = s\"Configure global settings for $fullRunnerName.\"\n  private val cmdName            = \"config\"\n  val helpMessage: String        =\n    s\"\"\"$helpHeader\n       |\n       |Available keys:\n       |  ${configKeyMessages(includeHidden = false).mkString(s\"${System.lineSeparator}  \")}\n       |\n       |${HelpMessages.commandFullHelpReference(cmdName)}\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n  private def configKeyMessages(includeHidden: Boolean): Seq[String] = {\n    val allKeys: Seq[Key[?]]     = Keys.map.values.toSeq\n    val allowedKeys: Seq[Key[?]] =\n      if allowRestrictedFeatures then allKeys\n      else allKeys.filterNot(k => k.isRestricted || k.isExperimental)\n    val keys: Seq[Key[?]] =\n      if includeHidden then allowedKeys\n      else allowedKeys.filterNot(k => k.hidden || k.isExperimental)\n    val maxFullNameLength = keys.map(_.fullName.length).max\n    keys.sortBy(_.fullName)\n      .map { key =>\n        val currentKeyFullNameLength = maxFullNameLength - key.fullName.length\n        val extraSpaces              =\n          if currentKeyFullNameLength > 0 then \" \" * currentKeyFullNameLength else \"\"\n        val hiddenOrExperimentalString =\n          if key.hidden then s\"${ScalaCliConsole.GRAY}(hidden)${Console.RESET} \"\n          else if key.isRestricted then s\"${ScalaCliConsole.GRAY}(power)${Console.RESET} \"\n          else if key.isExperimental then s\"${ScalaCliConsole.GRAY}(experimental)${Console.RESET} \"\n          else \"\"\n        s\"${Console.YELLOW}${key.fullName}${Console.RESET}$extraSpaces  $hiddenOrExperimentalString${key.description}\"\n      }\n  }\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |Syntax:\n       |  ${Console.BOLD}$progName $cmdName key value${Console.RESET}\n       |For example, to globally set the interactive mode:\n       |  ${Console.BOLD}$progName $cmdName interactive true${Console.RESET}\n       |  \n       |Available keys:\n       |  ${configKeyMessages(includeHidden = true).mkString(s\"${System.lineSeparator}  \")}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/config/ThrowawayPgpSecret.scala",
    "content": "package scala.cli.commands.config\n\nimport coursier.cache.FileCache\nimport coursier.util.Task\n\nimport java.security.SecureRandom\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.{Logger, options as bo}\nimport scala.cli.commands.pgp.PgpProxyMaker\nimport scala.cli.commands.shared.{CoursierOptions, SharedJvmOptions}\nimport scala.cli.errors.PgpError\nimport scala.cli.signing.shared.Secret\nimport scala.util.Properties\n\nobject ThrowawayPgpSecret {\n\n  private val secretChars =\n    (('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9') ++ Seq('$', '/', '*', '&', '\\'', '\"', '!', '(',\n      ')', '-', '_', '\\\\', ';', '.', ':', '=', '+', '?', ',', '%')).toVector\n  private def secretChars(rng: SecureRandom): Iterator[Char] =\n    Iterator.continually {\n      val idx = rng.nextInt(secretChars.length)\n      secretChars(idx)\n    }\n\n  def pgpPassPhrase(): Secret[String] = {\n    val random = new SecureRandom\n    Secret(secretChars(random).take(32).mkString)\n  }\n  def pgpSecret(\n    mail: String,\n    password: Option[Secret[String]],\n    logger: Logger,\n    cache: FileCache[Task],\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, (Secret[String], Secret[String])] = either {\n\n    val dir     = os.temp.dir(perms = if (Properties.isWin) null else \"rwx------\")\n    val pubKey  = dir / \"pub\"\n    val secKey  = dir / \"sec\"\n    val retCode = value {\n      (new PgpProxyMaker).get(\n        signingCliOptions.forceExternal.getOrElse(false)\n      ).createKey(\n        pubKey.toString,\n        secKey.toString,\n        mail,\n        logger.verbosity <= 0,\n        password.map(_.value),\n        cache,\n        logger,\n        jvmOptions,\n        coursierOptions,\n        signingCliOptions\n      )\n    }\n\n    def cleanUp(): Unit =\n      os.remove.all(dir)\n\n    if (retCode == 0)\n      try (Secret(os.read(pubKey)), Secret(os.read(secKey)))\n      finally cleanUp()\n    else {\n      cleanUp()\n      value {\n        Left {\n          new PgpError(\n            s\"Failed to create PGP key pair (see messages above, scala-cli-signing return code: $retCode)\"\n          )\n        }\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/default/Default.scala",
    "content": "package scala.cli.commands.default\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.RuntimeCommandsHelp\n\nimport scala.build.Logger\nimport scala.build.input.{Inputs, ScalaCliInvokeData, SubCommand}\nimport scala.cli.commands.ScalaCommandWithCustomHelp\nimport scala.cli.commands.repl.{Repl, ReplOptions}\nimport scala.cli.commands.run.{Run, RunOptions}\nimport scala.cli.commands.shared.{HelpCommandGroup, SharedOptions}\nimport scala.cli.commands.version.{Version, VersionOptions}\n\nclass Default(actualHelp: => RuntimeCommandsHelp)\n    extends ScalaCommandWithCustomHelp[DefaultOptions](actualHelp) {\n  private lazy val defaultCommandHelp: String =\n    s\"\"\"\n       |When no subcommand is passed explicitly, an implicit subcommand is used based on context:\n       |  - if the '--version' option is passed, it prints the 'version' subcommand output, unmodified by any other options\n       |  - if any inputs were passed, it defaults to the 'run' subcommand\n       |  - additionally, when no inputs were passed, it defaults to the 'run' subcommand in the following scenarios:\n       |    - if a snippet was passed with any of the '--execute*' options\n       |    - if a main class was passed with the '--main-class' option alongside an extra '--classpath'\n       |  - otherwise, if no inputs were passed, it defaults to the 'repl' subcommand\"\"\".stripMargin\n\n  override def customHelp(showHidden: Boolean): String =\n    super.customHelp(showHidden) + defaultCommandHelp\n\n  override def scalaSpecificationLevel = SpecificationLevel.MUST\n\n  override def group: String = HelpCommandGroup.Main.toString\n\n  override def sharedOptions(options: DefaultOptions): Option[SharedOptions] = Some(options.shared)\n\n  private[cli] var rawArgs = Array.empty[String]\n\n  override def invokeData: ScalaCliInvokeData =\n    super.invokeData.copy(subCommand = SubCommand.Default)\n\n  override def runCommand(options: DefaultOptions, args: RemainingArgs, logger: Logger): Unit =\n    // can't fully re-parse and redirect to Version because of --cli-version and --scala-version clashing\n    if options.version then\n      Version.runCommand(\n        options = VersionOptions(\n          global = options.shared.global,\n          offline = options.shared.coursier.getOffline(logger).getOrElse(false)\n        ),\n        args = args,\n        logger = logger\n      )\n    else\n      {\n        val shouldDefaultToRun =\n          args.remaining.nonEmpty || options.shared.snippet.executeScript.nonEmpty ||\n          options.shared.snippet.executeScala.nonEmpty ||\n          options.shared.snippet.executeJava.nonEmpty ||\n          options.shared.snippet.executeMarkdown.nonEmpty ||\n          (options.shared.extraClasspathWasPassed && options.sharedRun.mainClass.mainClass.nonEmpty)\n        if shouldDefaultToRun then RunOptions.parser else ReplOptions.parser\n      }.parse(options.legacyScala.filterNonDeprecatedArgs(rawArgs, progName, logger).toSeq) match\n        case Left(e)                              => error(e)\n        case Right((replOptions: ReplOptions, _)) => Repl.runCommand(replOptions, args, logger)\n        case Right((runOptions: RunOptions, _))   =>\n          Run.runCommand(\n            runOptions,\n            args.remaining,\n            args.unparsed,\n            () => Inputs.default(),\n            logger,\n            invokeData\n          )\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/default/DefaultFile.scala",
    "content": "package scala.cli.commands.default\n\nimport caseapp.core.RemainingArgs\n\nimport java.io.File\n\nimport scala.build.Logger\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.internal.Constants\nimport scala.util.Using\n\nobject DefaultFile extends ScalaCommand[DefaultFileOptions] {\n\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n\n  private def readDefaultFile(path: String): Array[Byte] = {\n    val resourcePath = Constants.defaultFilesResourcePath + \"/\" + path\n    val cl           = Thread.currentThread().getContextClassLoader\n    val resUrl       = cl.getResource(resourcePath)\n    if (resUrl == null)\n      sys.error(s\"Should not happen - resource $resourcePath not found\")\n    Using.resource(resUrl.openStream())(_.readAllBytes())\n  }\n\n  final case class DefaultFile(\n    path: os.SubPath,\n    content: () => Array[Byte]\n  ) {\n    def printablePath: String = path.segments.mkString(File.separator)\n  }\n\n  def defaultWorkflow: Array[Byte] =\n    readDefaultFile(\"workflows/default.yml\")\n  def defaultGitignore: Array[Byte] =\n    readDefaultFile(\"gitignore\")\n\n  val defaultFiles = Map(\n    \"workflow\"  -> DefaultFile(os.sub / \".github\" / \"workflows\" / \"ci.yml\", () => defaultWorkflow),\n    \"gitignore\" -> DefaultFile(os.sub / \".gitignore\", () => defaultGitignore)\n  )\n  val defaultFilesByRelPath: Map[String, DefaultFile] = defaultFiles.flatMap {\n    case (_, d) =>\n      // d.path.toString and d.printablePath differ on Windows (one uses '/', the other '\\')\n      Seq(\n        d.path.toString -> d,\n        d.printablePath -> d\n      )\n  }\n\n  private def unrecognizedFile(name: String, logger: Logger): Nothing = {\n    logger.error(\n      s\"Error: unrecognized default file $name (available: ${defaultFiles.keys.toVector.sorted.mkString(\", \")})\"\n    )\n    sys.exit(1)\n  }\n\n  override def runCommand(\n    options: DefaultFileOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    lazy val allArgs = {\n      val l = args.all\n      if (l.isEmpty) {\n        logger.error(\"No default file asked\")\n        sys.exit(1)\n      }\n      l\n    }\n\n    if (options.list || options.listIds)\n      for ((name, d) <- defaultFiles.toVector.sortBy(_._1)) {\n        if (options.listIds)\n          println(name)\n        if (options.list)\n          println(d.printablePath)\n      }\n    else if (options.write)\n      for (arg <- allArgs)\n        defaultFiles.get(arg).orElse(defaultFilesByRelPath.get(arg)) match {\n          case Some(f) =>\n            val dest = os.pwd / f.path\n            if (!options.force && os.exists(dest)) {\n              logger.error(\n                s\"Error: ${f.path} already exists. Pass --force to force erasing it.\"\n              )\n              sys.exit(1)\n            }\n            if (options.force)\n              os.write.over(dest, f.content(), createFolders = true)\n            else\n              os.write(dest, f.content(), createFolders = true)\n            logger.message(s\"Wrote ${f.path}\")\n          case None =>\n            unrecognizedFile(arg, logger)\n        }\n    else {\n      if (allArgs.length > 1) {\n        logger.error(s\"Error: expected only one argument, got ${allArgs.length}\")\n        sys.exit(1)\n      }\n\n      val arg = allArgs.head\n      val f   = defaultFiles.get(arg).orElse(defaultFilesByRelPath.get(arg)).getOrElse {\n        unrecognizedFile(arg, logger)\n      }\n      System.out.write(f.content())\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/default/DefaultFileOptions.scala",
    "content": "package scala.cli.commands.default\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup, HelpMessages}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(\n  s\"\"\"Generates default files for a $fullRunnerName project (i.e. .gitignore).\n     |\n     |${HelpMessages.commandDocWebsiteReference(\"misc/default-file\")}\"\"\".stripMargin)\nfinal case class DefaultFileOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Group(HelpGroup.Default.toString)\n  @HelpMessage(\"Write result to files rather than to stdout\")\n  @Tag(tags.restricted)\n    write: Boolean = false,\n  @Group(HelpGroup.Default.toString)\n  @HelpMessage(\"List available default files\")\n  @Tag(tags.restricted)\n    list: Boolean = false,\n  @Group(HelpGroup.Default.toString)\n  @HelpMessage(\"List available default file ids\")\n  @Tag(tags.restricted)\n    listIds: Boolean = false,\n  @Group(HelpGroup.Default.toString)\n  @HelpMessage(\"Force overwriting destination files\")\n  @ExtraName(\"f\")\n  @Tag(tags.restricted)\n    force: Boolean = false\n) extends HasGlobalOptions\n// format: on\n\nobject DefaultFileOptions {\n  implicit lazy val parser: Parser[DefaultFileOptions] = Parser.derive\n  implicit lazy val help: Help[DefaultFileOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/default/DefaultOptions.scala",
    "content": "package scala.cli.commands.default\n\nimport caseapp.*\n\nimport scala.cli.commands.repl.SharedReplOptions\nimport scala.cli.commands.run.SharedRunOptions\nimport scala.cli.commands.shared.{HasSharedOptions, SharedOptions}\n\n// format: off\ncase class DefaultOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    sharedRun: SharedRunOptions = SharedRunOptions(),\n  @Recurse\n    sharedRepl: SharedReplOptions = SharedReplOptions(),\n  @Recurse\n    legacyScala: LegacyScalaOptions = LegacyScalaOptions(),\n  @Name(\"-version\")\n    version: Boolean = false\n) extends HasSharedOptions\n// format: on\n\nobject DefaultOptions {\n  implicit lazy val parser: Parser[DefaultOptions] = Parser.derive\n  implicit lazy val help: Help[DefaultOptions]     = Help.derive\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/default/LegacyScalaOptions.scala",
    "content": "package scala.cli.commands.default\n\nimport caseapp.*\nimport caseapp.core.Indexed\n\nimport scala.build.Logger\nimport scala.cli.ScalaCli.{fullRunnerName, progName}\nimport scala.cli.commands.bloop.BloopExit\nimport scala.cli.commands.default.LegacyScalaOptions.*\nimport scala.cli.commands.package0.Package\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.shared.HelpMessages.PowerString\nimport scala.cli.commands.tags\n\n/** Options covering backwards compatibility with the old scala runner.\n  */\n// format: off\ncase class LegacyScalaOptions(\n  @Group(HelpGroup.LegacyScalaRunner.toString)\n  @HelpMessage(s\"Ignored legacy option. Deprecated equivalent of running a subsequent `$PowerString${Package.name}` command.\")\n  @Tag(tags.must)\n  @Hidden\n  @Name(\"-save\")\n    save: Option[Indexed[Boolean]] = None,\n  @Group(HelpGroup.LegacyScalaRunner.toString)\n  @HelpMessage(\"Ignored legacy option. Deprecated override canceling the `-nosave` option.\")\n  @Tag(tags.must)\n  @Hidden\n  @Name(\"-nosave\")\n    nosave: Option[Indexed[Boolean]] = None,\n  @Group(HelpGroup.LegacyScalaRunner.toString)\n  @HelpMessage(\"Ignored legacy option. Deprecated override defining how the runner should treat the input. Use the appropriate sub-command instead.\")\n  @Tag(tags.must)\n  @Hidden\n  @ValueDescription(\"object|script|jar|repl|guess\")\n  @Name(\"-howtorun\")\n    howToRun: Option[Indexed[String]] = None,\n  @Group(HelpGroup.LegacyScalaRunner.toString)\n  @HelpMessage(\"Ignored legacy option. Deprecated option allowing to preload inputs for the repl or command execution.\")\n  @Tag(tags.must)\n  @Hidden\n  @ValueDescription(\"file\")\n    I: Option[Indexed[List[String]]] = None,\n  @Group(HelpGroup.LegacyScalaRunner.toString)\n  @HelpMessage(\"Ignored legacy option. Deprecated option allowing to prevent the use of the legacy fsc compilation daemon.\")\n  @Tag(tags.must)\n  @Hidden\n  @Name(\"-nc\")\n  @Name(\"-nocompdaemon\")\n    noCompilationDaemon: Option[Indexed[Boolean]] = None,\n  @Group(HelpGroup.LegacyScalaRunner.toString)\n  @HelpMessage(\"Ignored legacy option. Deprecated option allowing to force the `run` mode on an input.\")\n  @Tag(tags.must)\n  @Hidden\n  @ValueDescription(\"file\")\n  @Name(\"-run\")\n    run: Option[Indexed[String]] = None,\n) {\n// format: on\n\n  extension [T](indexedOption: Option[Indexed[T]]) {\n    private def findArg(args: Array[String]): Option[String] =\n      indexedOption.flatMap(io => args.lift(io.index))\n  }\n\n  def filterNonDeprecatedArgs(\n    args: Array[String],\n    progName: String,\n    logger: Logger\n  ): Array[String] = {\n    val saveOptionString          = save.findArg(args)\n    val noSaveOptionString        = nosave.findArg(args)\n    val howToRunString            = howToRun.findArg(args)\n    val iString                   = I.findArg(args)\n    val noCompilationDaemonString = noCompilationDaemon.findArg(args)\n    val runString                 = run.findArg(args)\n    val deprecatedArgs            =\n      Seq(\n        saveOptionString,\n        noSaveOptionString,\n        howToRunString,\n        iString,\n        noCompilationDaemonString,\n        runString\n      )\n        .flatten\n    val filteredArgs       = args.filterNot(deprecatedArgs.contains)\n    val filteredArgsString = filteredArgs.mkString(\" \")\n    saveOptionString.foreach { s =>\n      logger.message(\n        s\"\"\"Deprecated option '$s' is ignored.\n           |The compiled project files will be saved in the '.scala-build' directory in the project root folder.\n           |If you need to produce an actual jar file, run the '$PowerString${Package.name}' sub-command as follows:\n           |  ${Console.BOLD}$progName $PowerString${Package\n            .name} --library $filteredArgsString${Console.RESET}\"\"\".stripMargin\n      )\n    }\n    noSaveOptionString.foreach { ns =>\n      logger.message(\n        s\"\"\"Deprecated option '$ns' is ignored.\n           |A jar file is not saved unless the '$PowerString${Package\n            .name}' sub-command is called.\"\"\".stripMargin\n      )\n    }\n    for {\n      htrString <- howToRunString\n      htrValue  <- howToRun.map(_.value)\n    } {\n      logger.message(s\"Deprecated option '$htrString' is ignored.\".stripMargin)\n      val passedValueExplanation = htrValue match {\n        case v @ (\"object\" | \"script\" | \"jar\") =>\n          s\"\"\"$fullRunnerName does not support explicitly forcing an input to be run as '$v'.\n             |Just make sure your inputs have the correct format and extension.\"\"\".stripMargin\n        case \"guess\" =>\n          s\"\"\"$fullRunnerName does not support `guess` mode.\n             |Just make sure your inputs have the correct format and extension.\"\"\".stripMargin\n        case \"repl\" =>\n          s\"\"\"In order to explicitly run the repl, use the 'repl' sub-command.\n             |  ${Console.BOLD}$progName repl $filteredArgsString${Console.RESET}\n             |\"\"\".stripMargin\n        case invalid @ _ =>\n          s\"\"\"'$invalid' is not an accepted value for the '$htrString' option.\n             |$fullRunnerName uses an equivalent of the old 'guess' mode by default at all times.\"\"\".stripMargin\n      }\n      logger.message(passedValueExplanation)\n      logger.message(\n        s\"\"\"Instead of the deprecated '$htrString' option, $fullRunnerName now uses a sub-command system.\n           |To learn more, try viewing the help.\n           |  ${Console.BOLD}$progName -help${Console.RESET}\"\"\".stripMargin\n      )\n    }\n    for {\n      optionName   <- iString\n      optionValues <- I.map(_.value)\n      exampleReplInputs = optionValues.mkString(\" \")\n    } {\n      logger.message(s\"Deprecated option '$optionName' is ignored.\".stripMargin)\n      logger.message(\n        s\"\"\"To preload the extra files for the repl, try passing them as inputs for the repl sub-command.\n           |  ${Console.BOLD}$progName repl $exampleReplInputs${Console.RESET}\n           |\"\"\".stripMargin\n      )\n    }\n    noCompilationDaemonString.foreach { nc =>\n      logger.message(s\"Deprecated option '$nc' is ignored.\")\n      logger.message(\"The script runner can no longer be picked as before.\")\n    }\n    for {\n      rString <- runString\n      rValue  <- run.map(_.value)\n    } {\n      logger.message(s\"Deprecated option '$rString' is ignored.\")\n      logger.message(\n        s\"\"\"$fullRunnerName does not support explicitly forcing the old `run` mode.\n           |Just pass $rValue as input and make sure it has the correct format and extension.\n           |i.e. to run a JAR, pass it as an input, just make sure it has the `.jar` extension and a main class.\n           |For details on how inputs can be run with $fullRunnerName, check the `run` sub-command.\n           |  ${Console.BOLD}$progName run --help${Console.RESET}\n           |\"\"\".stripMargin\n      )\n    }\n    filteredArgs\n  }\n}\nobject LegacyScalaOptions {\n  implicit lazy val parser: Parser[LegacyScalaOptions] = Parser.derive\n  implicit lazy val help: Help[LegacyScalaOptions]     = Help.derive\n\n  def yScriptRunnerWarning(yScriptRunnerKey: String, yScriptRunnerValue: Option[String]): String = {\n    val valueSpecificMsg = yScriptRunnerValue match {\n      case Some(v @ \"default\") =>\n        s\"scala.tools.nsc.DefaultScriptRunner (the $v script runner) is no longer available.\"\n      case Some(v @ \"resident\") =>\n        s\"scala.tools.nsc.fsc.ResidentScriptRunner (the $v script runner) is no longer available.\"\n      case Some(v @ \"shutdown\") =>\n        val bloopExitCommandName =\n          BloopExit.names.headOption.map(_.mkString(\" \")).getOrElse(BloopExit.name)\n        s\"\"\"scala.tools.nsc.fsc.DaemonKiller (the $v script runner) is no longer available.\n           |Did you want to stop the $fullRunnerName build server (Bloop) instead?\n           |If so, consider using the following command:\n           |  ${Console.BOLD}$progName $PowerString$bloopExitCommandName${Console.RESET}\"\"\".stripMargin\n      case Some(className) =>\n        s\"Using $className as the script runner is no longer supported and will not be attempted.\"\n      case _ => \"\"\n    }\n    s\"\"\"Deprecated option '$yScriptRunnerKey' is ignored.\n       |The script runner can no longer be picked as before.\n       |$valueSpecificMsg\"\"\".stripMargin\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/dependencyupdate/DependencyUpdate.scala",
    "content": "package scala.cli.commands.dependencyupdate\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\n\nimport scala.build.actionable.ActionableDependencyHandler\nimport scala.build.actionable.ActionableDiagnostic.ActionableDependencyUpdateDiagnostic\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.build.options.Scope\nimport scala.build.{CrossSources, Logger, Position, Sources}\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.util.ArgHelpers.*\n\nobject DependencyUpdate extends ScalaCommand[DependencyUpdateOptions] {\n  override def group: String                               = HelpCommandGroup.Main.toString\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.RESTRICTED\n  override def helpFormat: HelpFormat                      =\n    super.helpFormat.withPrimaryGroup(HelpGroup.Dependency)\n  override def sharedOptions(options: DependencyUpdateOptions): Option[SharedOptions] =\n    Some(options.shared)\n  override def runCommand(\n    options: DependencyUpdateOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    if options.shared.scope.test.nonEmpty then\n      logger.message(\n        s\"\"\"$warnPrefix Including the test scope does not change the behaviour of this command. \n           |$warnPrefix Test dependencies are updated regardless.\"\"\".stripMargin\n      )\n    val verbosity    = options.shared.logging.verbosity\n    val buildOptions = buildOptionsOrExit(options)\n\n    val inputs = options.shared.inputs(args.all).orExit(logger)\n\n    val (crossSources, _) =\n      CrossSources.forInputs(\n        inputs,\n        Sources.defaultPreprocessors(\n          buildOptions.archiveCache,\n          buildOptions.internal.javaClassNameVersionOpt,\n          () => buildOptions.javaHome().value.javaCommand\n        ),\n        logger,\n        buildOptions.suppressWarningOptions,\n        buildOptions.internal.exclude,\n        download = buildOptions.downloader\n      ).orExit(logger)\n\n    val sharedOptions = crossSources.sharedOptions(buildOptions)\n    val scopedSources = crossSources.scopedSources(buildOptions).orExit(logger)\n\n    def generateActionableUpdateDiagnostic(scope: Scope)\n      : Seq[ActionableDependencyUpdateDiagnostic] = {\n      val sources =\n        scopedSources.sources(scope, sharedOptions, inputs.workspace, logger).orExit(logger)\n\n      if (verbosity >= 3)\n        pprint.err.log(sources)\n\n      val options = buildOptions.orElse(sources.buildOptions)\n      ActionableDependencyHandler.createActionableDiagnostics(options, Some(logger)).orExit(logger)\n    }\n\n    val actionableMainUpdateDiagnostics = generateActionableUpdateDiagnostic(Scope.Main)\n    val actionableTestUpdateDiagnostics = generateActionableUpdateDiagnostic(Scope.Test)\n    val actionableUpdateDiagnostics     =\n      (actionableMainUpdateDiagnostics ++ actionableTestUpdateDiagnostics).distinct\n\n    if (options.all)\n      updateDependencies(actionableUpdateDiagnostics, logger)\n    else {\n      println(\"Updates\")\n      actionableUpdateDiagnostics.foreach(update =>\n        println(\n          s\"   * ${update.dependencyModuleName} ${update.currentVersion} -> ${update.newVersion}\"\n        )\n      )\n      println(s\"\"\"|To update all dependencies run:\n                  |    $baseRunnerName dependency-update --all\"\"\".stripMargin)\n    }\n  }\n\n  private def updateDependencies(\n    actionableUpdateDiagnostics: Seq[ActionableDependencyUpdateDiagnostic],\n    logger: Logger\n  ): Unit = {\n    val groupedByFileDiagnostics =\n      actionableUpdateDiagnostics.flatMap {\n        diagnostic =>\n          diagnostic.positions.collect {\n            case file: Position.File =>\n              file.path -> (file, diagnostic)\n          }\n      }.groupMap(_._1)(_._2)\n\n    groupedByFileDiagnostics.foreach {\n      case (Right(file), diagnostics) =>\n        val sortedByLine       = diagnostics.sortBy(_._1.startPos._1).reverse\n        val appliedDiagnostics = updateDependencies(file, sortedByLine)\n        os.write.over(file, appliedDiagnostics)\n        diagnostics.foreach(diagnostic =>\n          logger.message(\n            s\"Updated dependency ${diagnostic._2.dependencyModuleName}: ${diagnostic._2\n                .currentVersion} -> ${diagnostic._2.newVersion}\"\n          )\n        )\n      case (Left(file), diagnostics) =>\n        diagnostics.foreach {\n          diagnostic =>\n            logger.message(\n              s\"Warning: $fullRunnerName can't update ${diagnostic._2.suggestion} in $file\"\n            )\n        }\n    }\n  }\n\n  private def updateDependencies(\n    file: os.Path,\n    diagnostics: Seq[(Position.File, ActionableDependencyUpdateDiagnostic)]\n  ): String = {\n    val fileContent   = os.read(file)\n    val startIndicies = Position.Raw.lineStartIndices(fileContent)\n\n    diagnostics.foldLeft(fileContent) {\n      case (fileContent, (file, diagnostic)) =>\n        val (line, column)       = (file.startPos._1, file.startPos._2)\n        val (lineEnd, columnEnd) = (file.endPos._1, file.endPos._2)\n        val startIndex           = startIndicies(line) + column\n        val endIndex             = startIndicies(lineEnd) + columnEnd\n\n        val newDependency = diagnostic.suggestion\n        s\"${fileContent.slice(0, startIndex)}$newDependency${fileContent.drop(endIndex)}\"\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/dependencyupdate/DependencyUpdateOptions.scala",
    "content": "package scala.cli.commands.dependencyupdate\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.commands.shared.{HasSharedOptions, HelpGroup, SharedOptions}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(\"Update dependency directives in the project\")\nfinal case class DependencyUpdateOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Update all dependencies if a newer version was released\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    all: Boolean = false\n) extends HasSharedOptions\n  // format: on\n\nobject DependencyUpdateOptions {\n  implicit lazy val parser: Parser[DependencyUpdateOptions] = Parser.derive\n  implicit lazy val help: Help[DependencyUpdateOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/directories/Directories.scala",
    "content": "package scala.cli.commands.directories\n\nimport caseapp.*\n\nimport scala.build.Logger\nimport scala.cli.commands.ScalaCommand\n\nobject Directories extends ScalaCommand[DirectoriesOptions] {\n  override def hidden: Boolean = true\n\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n\n  override def runCommand(\n    options: DirectoriesOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    if (args.all.nonEmpty) {\n      logger.error(\"The directories command doesn't accept arguments.\")\n      sys.exit(1)\n    }\n\n    val directories = scala.build.Directories.directories\n\n    println(\"Local repository: \" + directories.localRepoDir)\n    println(\"Completions: \" + directories.completionsDir)\n    println(\"Virtual projects: \" + directories.virtualProjectsDir)\n    println(\"BSP sockets: \" + directories.bspSocketDir)\n    println(\"Bloop daemon directory: \" + directories.bloopDaemonDir)\n    println(\"Secrets directory: \" + directories.secretsDir)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/directories/DirectoriesOptions.scala",
    "content": "package scala.cli.commands.directories\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions}\n\n// format: off\n@HelpMessage(s\"Prints directories used by $fullRunnerName.\")\nfinal case class DirectoriesOptions(\n@Recurse\n  global: GlobalOptions = GlobalOptions(),\n) extends HasGlobalOptions\n// format: on\n\nobject DirectoriesOptions {\n  implicit lazy val parser: Parser[DirectoriesOptions] = Parser.derive\n  implicit lazy val help: Help[DirectoriesOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala",
    "content": "package scala.cli.commands.doc\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport coursier.Fetch\nimport dependency.*\n\nimport java.io.File\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.compiler.{ScalaCompilerMaker, SimpleScalaCompilerMaker}\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.interactive.InteractiveFileOps\nimport scala.build.internal.Runner\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}\nimport scala.cli.commands.util.BuildCommandHelpers\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel}\nimport scala.cli.config.Keys\nimport scala.cli.errors.ScaladocGenerationFailedError\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nimport scala.util.Properties\n\nobject Doc extends ScalaCommand[DocOptions] with BuildCommandHelpers {\n  override def group: String = HelpCommandGroup.Main.toString\n\n  override def sharedOptions(options: DocOptions): Option[SharedOptions] = Some(options.shared)\n\n  override def buildOptions(options: DocOptions): Option[BuildOptions] =\n    sharedOptions(options)\n      .map(shared => shared.buildOptions().orExit(shared.logger))\n\n  override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Doc)\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.MUST\n\n  override def runCommand(options: DocOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val initialBuildOptions = buildOptionsOrExit(options)\n    val inputs              = options.shared.inputs(args.remaining).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    val threads = BuildThreads.create()\n\n    val maker               = options.shared.compilerMaker(threads)\n    val compilerMaker       = ScalaCompilerMaker.IgnoreScala2(maker)\n    val docCompilerMakerOpt = Some(SimpleScalaCompilerMaker(\"java\", Nil, scaladoc = true))\n\n    val configDb              = ConfigDbUtils.configDb.orExit(logger)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions.orElse(\n        configDb.get(Keys.actions).getOrElse(None)\n      )\n\n    val cross         = options.compileCross.cross.getOrElse(false)\n    val withTestScope = options.shared.scope.test.getOrElse(false)\n    val buildResult   = Build.build(\n      inputs,\n      initialBuildOptions,\n      compilerMaker,\n      docCompilerMakerOpt,\n      logger,\n      crossBuilds = cross,\n      buildTests = withTestScope,\n      partial = None,\n      actionableDiagnostics = actionableDiagnostics\n    )\n    val docBuilds = buildResult.orExit(logger).allDoc\n    docBuilds match {\n      case b if b.forall(_.success) =>\n        val successfulBuilds = b.collect { case s: Build.Successful => s }\n        if cross && successfulBuilds.nonEmpty then\n          doDocCrossBuilds(\n            logger = logger,\n            outputOpt = options.output.filter(_.nonEmpty),\n            force = options.force,\n            allBuilds = successfulBuilds,\n            extraArgs = args.unparsed,\n            withTestScope = withTestScope\n          ).orExit(logger)\n        else\n          doDoc(\n            logger,\n            options.output.filter(_.nonEmpty),\n            options.force,\n            successfulBuilds,\n            args.unparsed,\n            withTestScope\n          ).orExit(logger)\n      case b if b.exists(bb => !bb.success && !bb.cancelled) =>\n        logger.error(\"Compilation failed\")\n        sys.exit(1)\n      case _ =>\n        logger.error(\"Build cancelled\")\n        sys.exit(1)\n    }\n  }\n\n  /** Determines the output subdirectory name for one cross build when using `--cross`. Used so that\n    * each Scala version (and optionally platform) gets a distinct directory.\n    */\n  def crossDocSubdirName(\n    crossParams: CrossBuildParams,\n    multipleCrossGroups: Boolean,\n    needsPlatformInSuffix: Boolean\n  ): String =\n    if !multipleCrossGroups then \"\"\n    else if needsPlatformInSuffix then s\"${crossParams.scalaVersion}_${crossParams.platform}\"\n    else crossParams.scalaVersion\n\n  private def doDocCrossBuilds(\n    logger: Logger,\n    outputOpt: Option[String],\n    force: Boolean,\n    allBuilds: Seq[Build.Successful],\n    extraArgs: Seq[String],\n    withTestScope: Boolean\n  ): Either[BuildException, Unit] = either {\n    val crossBuildGroups    = allBuilds.groupedByCrossParams.toSeq\n    val multipleCrossGroups = crossBuildGroups.size > 1\n    if multipleCrossGroups then\n      logger.message(s\"Generating documentation for ${crossBuildGroups.size} cross builds...\")\n    val defaultName    = \"scala-doc\"\n    val baseOutputPath = outputOpt.map(p => os.Path(p, Os.pwd)).getOrElse(os.pwd / defaultName)\n    val platforms      = crossBuildGroups.map(_._1.platform).distinct\n    val needsPlatformInSuffix = platforms.size > 1\n    value {\n      crossBuildGroups\n        .map { (crossParams, builds) =>\n          if multipleCrossGroups then\n            logger.message(s\"Generating documentation for ${crossParams.asString}...\")\n          val crossSubDir =\n            Doc.crossDocSubdirName(crossParams, multipleCrossGroups, needsPlatformInSuffix)\n          val groupOutputOpt =\n            if crossSubDir.nonEmpty then Some((baseOutputPath / crossSubDir).toString)\n            else outputOpt.filter(_.nonEmpty).orElse(Some(defaultName))\n          doDoc(\n            logger = logger,\n            outputOpt = groupOutputOpt,\n            force = force,\n            builds = builds,\n            extraArgs = extraArgs,\n            withTestScope = withTestScope\n          )\n        }\n        .sequence\n        .left\n        .map(CompositeBuildException(_))\n        .map(_ => ())\n    }\n  }\n\n  private def doDoc(\n    logger: Logger,\n    outputOpt: Option[String],\n    force: Boolean,\n    builds: Seq[Build.Successful],\n    extraArgs: Seq[String],\n    withTestScope: Boolean\n  ): Either[BuildException, Unit] = either {\n\n    def defaultName = \"scala-doc\"\n\n    val dest          = outputOpt.getOrElse(defaultName)\n    val destPath      = os.Path(dest, Os.pwd)\n    val printableDest = CommandUtils.printablePath(destPath)\n\n    def alreadyExistsCheck(): Either[BuildException, Unit] = {\n      val alreadyExists = !force && os.exists(destPath)\n      if (alreadyExists)\n        builds.head.options.interactive.map { interactive =>\n          InteractiveFileOps.erasingPath(interactive, printableDest, destPath) { () =>\n            val msg = s\"$printableDest already exists\"\n            logger.error(s\"$msg. Pass -f or --force to force erasing it.\")\n            sys.exit(1)\n          }\n        }\n      else\n        Right(())\n    }\n\n    value(alreadyExistsCheck())\n\n    val docJarPath = value(generateScaladocDirPath(builds, logger, extraArgs, withTestScope))\n    value(alreadyExistsCheck())\n    os.makeDir.all(destPath / os.up)\n    if force then os.copy.over(docJarPath, destPath) else os.copy(docJarPath, destPath)\n\n    val printableOutput = CommandUtils.printablePath(destPath)\n\n    logger.message(s\"Wrote Scaladoc to $printableOutput\")\n  }\n\n  private def javadocBaseUrl(javaVersion: Int): String =\n    if javaVersion >= 11 then\n      s\"https://docs.oracle.com/en/java/javase/$javaVersion/docs/api/java.base/\"\n    else\n      s\"https://docs.oracle.com/javase/$javaVersion/docs/api/\"\n\n  private def scaladocBaseUrl(scalaVersion: String): String =\n    s\"https://scala-lang.org/api/$scalaVersion/\"\n\n  // from https://github.com/VirtusLab/scala-cli/pull/103/files#diff-1039b442cbd23f605a61fdb9c3620b600aa4af6cab757932a719c54235d8e402R60\n  private[commands] def defaultScaladocArgs(scalaVersion: String, javaVersion: Int): Seq[String] =\n    Seq(\n      \"-snippet-compiler:compile\",\n      \"-Ygenerate-inkuire\",\n      \"-external-mappings:\" +\n        s\".*/scala/.*::scaladoc3::${scaladocBaseUrl(scalaVersion)},\" +\n        s\".*/java/.*::javadoc::${javadocBaseUrl(javaVersion)}\",\n      \"-author\",\n      \"-groups\"\n    )\n\n  def generateScaladocDirPath(\n    builds: Seq[Build.Successful],\n    logger: Logger,\n    extraArgs: Seq[String],\n    withTestScope: Boolean\n  ): Either[BuildException, os.Path] = either {\n    val docContentDir = builds.head.scalaParams\n      .map(sp => sp -> sp.scalaVersion.startsWith(\"2.\")) match {\n      case Some((_, true)) if withTestScope =>\n        builds.find(_.scope == Scope.Test).getOrElse(builds.head).project.scaladocDir\n      case Some((_, true))        => builds.head.project.scaladocDir\n      case Some((scalaParams, _)) =>\n        val res: Fetch.Result = value {\n          Artifacts.fetchAnyDependencies(\n            Seq(Positioned.none(dep\"org.scala-lang::scaladoc:${scalaParams.scalaVersion}\")),\n            value(builds.head.options.finalRepositories),\n            Some(scalaParams),\n            logger,\n            builds.head.options.finalCache,\n            None\n          )\n        }\n        val destDir = builds.head.project.scaladocDir\n        os.makeDir.all(destDir)\n        val ext      = if Properties.isWin then \".exe\" else \"\"\n        val baseArgs = Seq(\n          \"-classpath\",\n          builds\n            .flatMap(_.fullCompileClassPath)\n            .distinct\n            .map(_.toString)\n            .mkString(File.pathSeparator),\n          \"-d\",\n          destDir.toString\n        )\n        val javaVersion = builds.head.options.javaHome().value.version\n        val defaultArgs =\n          if builds.head.options.notForBloopOptions.packageOptions.useDefaultScaladocOptions\n              .getOrElse(true)\n          then defaultScaladocArgs(scalaParams.scalaVersion, javaVersion)\n          else Nil\n        val args = baseArgs ++\n          builds.head.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++\n          extraArgs ++\n          defaultArgs ++\n          builds.map(_.output.toString)\n        val retCode = Runner.runJvm(\n          (builds.head.options.javaHomeLocation().value / \"bin\" / s\"java$ext\").toString,\n          Nil, // FIXME Allow to customize that?\n          res.files.map(os.Path(_, os.pwd)),\n          \"dotty.tools.scaladoc.Main\",\n          args,\n          logger,\n          cwd = Some(builds.head.inputs.workspace)\n        ).waitFor()\n        if retCode == 0 then destDir\n        else value(Left(new ScaladocGenerationFailedError(retCode)))\n      case None =>\n        val destDir = builds.head.project.scaladocDir\n        os.makeDir.all(destDir)\n        val ext         = if (Properties.isWin) \".exe\" else \"\"\n        val javaSources =\n          builds\n            .flatMap(b => b.sources.paths.map(_._1) ++ b.generatedSources.map(_.generated))\n            .distinct\n            .filter(_.last.endsWith(\".java\"))\n        val command = Seq(\n          (builds.head.options.javaHomeLocation().value / \"bin\" / s\"javadoc$ext\").toString,\n          \"-d\",\n          destDir.toString,\n          \"-classpath\",\n          builds.flatMap(_.fullClassPath).distinct.map(_.toString).mkString(File.pathSeparator)\n        ) ++ javaSources.map(_.toString)\n        val retCode = Runner.run(\n          command,\n          logger,\n          cwd = Some(builds.head.inputs.workspace)\n        ).waitFor()\n        if retCode == 0 then destDir\n        else value(Left(new ScaladocGenerationFailedError(retCode)))\n    }\n    docContentDir\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/doc/DocOptions.scala",
    "content": "package scala.cli.commands.doc\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{\n  CrossOptions, HasSharedOptions, HelpGroup, HelpMessages, SharedOptions\n}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(DocOptions.helpMessage, DocOptions.messageMd, DocOptions.detailedHelpMessage)\nfinal case class DocOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    compileCross: CrossOptions = CrossOptions(),\n  @Group(HelpGroup.Doc.toString)\n  @Tag(tags.must)\n  @HelpMessage(\"Set the destination path\")\n  @Name(\"o\")\n    output: Option[String] = None,\n  @Group(HelpGroup.Doc.toString)\n  @HelpMessage(\"Overwrite the destination directory, if it exists\")\n  @Tag(tags.must)\n  @Name(\"f\")\n    force: Boolean = false,\n  @Group(HelpGroup.Doc.toString)\n  @HelpMessage(s\"Control if $fullRunnerName should use default options for scaladoc, true by default. Use `--default-scaladoc-opts:false` to not include default options.\")\n  @Tag(tags.should)\n  @ExtraName(\"defaultScaladocOpts\")\n    defaultScaladocOptions: Option[Boolean] = None\n) extends HasSharedOptions\n// format: on\n\nobject DocOptions {\n  implicit lazy val parser: Parser[DocOptions] = Parser.derive\n  implicit lazy val help: Help[DocOptions]     = Help.derive\n  val cmdName                                  = \"doc\"\n  private val helpHeader                       = \"Generate Scaladoc documentation.\"\n  val helpMessage: String                      = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String              =\n    s\"\"\"Generate Scaladoc documentation.\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n  val messageMd =\n    s\"By default, $fullRunnerName sets common `scaladoc` options and this mechanism can be disabled by using `--default-scaladoc-opts:false`.\"\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/export0/Export.scala",
    "content": "package scala.cli.commands.export0\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport coursier.cache.FileCache\nimport coursier.util.{Artifact, Task}\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.input.Inputs\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.shared.{HelpGroup, SharedOptions}\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.exportCmd.*\nimport scala.cli.util.ArgHelpers.*\n\nobject Export extends ScalaCommand[ExportOptions] {\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL\n\n  override def helpFormat: HelpFormat =\n    super.helpFormat.withPrimaryGroup(HelpGroup.BuildToolExport)\n\n  private def prepareBuild(\n    inputs: Inputs,\n    buildOptions: BuildOptions,\n    logger: Logger,\n    verbosity: Int,\n    scope: Scope\n  ): Either[BuildException, (Sources, BuildOptions)] = either {\n\n    logger.log(\"Preparing build\")\n\n    val (crossSources: CrossSources, _) = value {\n      CrossSources.forInputs(\n        inputs,\n        Sources.defaultPreprocessors(\n          buildOptions.archiveCache,\n          buildOptions.internal.javaClassNameVersionOpt,\n          () => buildOptions.javaHome().value.javaCommand\n        ),\n        logger,\n        buildOptions.suppressWarningOptions,\n        buildOptions.internal.exclude,\n        download = buildOptions.downloader\n      )\n    }\n\n    val scopedSources: ScopedSources = value(crossSources.scopedSources(buildOptions))\n    val sources: Sources             =\n      scopedSources.sources(\n        scope,\n        crossSources.sharedOptions(buildOptions),\n        inputs.workspace,\n        logger\n      )\n        .orExit(logger)\n\n    if (verbosity >= 3)\n      pprint.err.log(sources)\n\n    val options0 = buildOptions.orElse(sources.buildOptions)\n\n    (sources, options0)\n  }\n\n  // FIXME Auto-update those\n  def sbtProjectDescriptor(\n    extraSettings: Seq[String],\n    sbtVersion: String,\n    logger: Logger\n  ): SbtProjectDescriptor =\n    SbtProjectDescriptor(sbtVersion, extraSettings, logger)\n\n  def mavenProjectDescriptor(\n    mavenPluginVersion: String,\n    mavenScalaPluginVersion: String,\n    mavenExecPluginVersion: String,\n    extraSettings: Seq[String],\n    mavenGroupId: String,\n    mavenArtifactId: String,\n    mavenVersion: String,\n    logger: Logger\n  ): MavenProjectDescriptor =\n    MavenProjectDescriptor(\n      mavenPluginVersion,\n      mavenScalaPluginVersion,\n      mavenExecPluginVersion,\n      extraSettings,\n      mavenGroupId,\n      mavenArtifactId,\n      mavenVersion,\n      logger\n    )\n\n  def millProjectDescriptor(\n    cache: FileCache[Task],\n    projectName: Option[String],\n    millVersion: String,\n    logger: Logger\n  ): MillProjectDescriptor = {\n    val launcherArtifacts = Seq(\n      os.rel / \"mill\"     -> s\"https://github.com/com-lihaoyi/mill/raw/$millVersion/mill\",\n      os.rel / \"mill.bat\" -> s\"https://github.com/com-lihaoyi/mill/raw/$millVersion/mill.bat\"\n    )\n    val launcherTasks = launcherArtifacts.map {\n      case (path, url) =>\n        val art = Artifact(url).withChanging(true)\n        cache.file(art).run.flatMap {\n          case Left(e)  => Task.fail(e)\n          case Right(f) => Task.delay {\n              val content = os.read.bytes(os.Path(f, Os.pwd))\n              path -> content\n            }\n        }\n    }\n    val launchersTask = cache.logger.using(Task.gather.gather(launcherTasks))\n    val launchers     = launchersTask.unsafeRun()(using cache.ec)\n    MillProjectDescriptor(\n      millVersion = millVersion,\n      projectName = projectName,\n      launchers = launchers,\n      logger = logger\n    )\n  }\n\n  def jsonProjectDescriptor(\n    projectName: Option[String],\n    workspace: os.Path,\n    logger: Logger\n  ): JsonProjectDescriptor =\n    JsonProjectDescriptor(projectName, workspace, logger)\n\n  override def sharedOptions(opts: ExportOptions): Option[SharedOptions] = Some(opts.shared)\n\n  override def runCommand(options: ExportOptions, args: RemainingArgs, logger: Logger): Unit = {\n    if options.shared.scope.test.nonEmpty then {\n      logger.error(\n        s\"\"\"Including the test scope sources together with the main scope is currently not supported.\n           |Note that test scope sources will still be exported as per the output build tool tests definition demands.\"\"\".stripMargin\n      )\n      sys.exit(1)\n    }\n\n    val initialBuildOptions = buildOptionsOrExit(options)\n\n    val output                   = options.output.getOrElse(\"dest\")\n    val dest                     = os.Path(output, os.pwd)\n    val shouldExportToJson       = options.json.getOrElse(false)\n    val shouldExportJsonToStdout = shouldExportToJson && options.output.isEmpty\n\n    if (!shouldExportJsonToStdout && os.exists(dest)) {\n      logger.error(\n        s\"\"\"Error: $dest already exists.\n           |To change the destination output directory pass --output path or remove the destination directory first.\"\"\".stripMargin\n      )\n      sys.exit(1)\n    }\n\n    val shouldExportToMill  = options.mill.getOrElse(false)\n    val shouldExportToSbt   = options.sbt.getOrElse(false)\n    val shouldExportToMaven = options.maven.getOrElse(false)\n\n    val exportOptions =\n      (if shouldExportToMill then List(\"Mill\") else Nil) ++\n        (if shouldExportToSbt then List(\"SBT\") else Nil) ++\n        (if shouldExportToMaven then List(\"Maven\") else Nil)\n    val exportOptionsString = exportOptions.mkString(\", \")\n    if exportOptions.length > 1 then {\n      logger.error(\n        s\"\"\"Error: Cannot export to more than one tool at once (currently chosen: $exportOptionsString). \n           |Pick one build tool to export to.\"\"\".stripMargin\n      )\n      sys.exit(1)\n    }\n\n    if (!shouldExportToJson) {\n      val buildToolName =\n        if (shouldExportToMill) \"mill\" else if (shouldExportToMaven) \"maven\" else \"sbt\"\n      logger.message(s\"Exporting to a $buildToolName project...\")\n    }\n    else if (!shouldExportJsonToStdout)\n      logger.message(s\"Exporting to JSON...\")\n\n    val inputs = options.shared.inputs(args.all).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    val baseOptions =\n      initialBuildOptions\n        .copy(\n          scalaNativeOptions = initialBuildOptions.scalaNativeOptions.copy(\n            maxDefaultNativeVersions =\n              initialBuildOptions.scalaNativeOptions.maxDefaultNativeVersions ++\n                (if shouldExportToMill &&\n                   Constants.scalaNativeVersion != Constants.maxScalaNativeForMillExport\n                 then\n                   val warningMsg =\n                     s\"Mill export does not support Scala Native ${Constants.scalaNativeVersion}, ${Constants.maxScalaNativeForMillExport} should be used instead.\"\n                   List(Constants.maxScalaNativeForMillExport -> warningMsg)\n                 else Nil)\n          ),\n          mainClass = options.mainClass.mainClass.filter(_.nonEmpty)\n        )\n\n    val (sourcesMain, optionsMain0) =\n      prepareBuild(\n        inputs,\n        baseOptions,\n        logger,\n        options.shared.logging.verbosity,\n        Scope.Main\n      )\n        .orExit(logger)\n    val (sourcesTest, optionsTest0) =\n      prepareBuild(\n        inputs,\n        baseOptions,\n        logger,\n        options.shared.logging.verbosity,\n        Scope.Test\n      )\n        .orExit(logger)\n\n    for {\n      svMain <- optionsMain0.scalaOptions.scalaVersion\n      svTest <- optionsTest0.scalaOptions.scalaVersion\n    } if (svMain != svTest) {\n      logger.error(\n        s\"\"\"Detected different Scala versions in main and test scopes.\n           |Please set the Scala version explicitly in the main and test scope with using directives or pass -S, --scala-version as parameter\"\"\".stripMargin\n      )\n      sys.exit(1)\n    }\n\n    if (\n      optionsMain0.scalaOptions.scalaVersion.isEmpty &&\n      optionsTest0.scalaOptions.scalaVersion.nonEmpty\n    ) {\n      logger.error(\n        s\"\"\"Detected that the Scala version is only set in test scope.\n           |Please set the Scala version explicitly in the main and test scopes with using directives or pass -S, --scala-version as parameter\"\"\".stripMargin\n      )\n      sys.exit(1)\n    }\n\n    if (shouldExportJsonToStdout) {\n      val project = jsonProjectDescriptor(options.project, inputs.workspace, logger)\n        .`export`(optionsMain0, optionsTest0, sourcesMain, sourcesTest)\n        .orExit(logger)\n\n      project.print(System.out)\n    }\n    else {\n      val sbtVersion                       = options.sbtVersion.getOrElse(Constants.sbtVersion)\n      val defaultMavenCompilerVersion      = options.mvnVersion.getOrElse(Constants.mavenVersion)\n      val defaultScalaMavenCompilerVersion =\n        options.mvnScalaVersion.getOrElse(Constants.mavenScalaCompilerPluginVersion)\n      val defaultMavenExecPluginVersion =\n        options.mvnExecPluginVersion.getOrElse(Constants.mavenExecPluginVersion)\n      val defaultMavenArtifactId =\n        options.mvnAppArtifactId.getOrElse(Constants.mavenAppArtifactId)\n      val defaultMavenGroupId =\n        options.mvnAppGroupId.getOrElse(Constants.mavenAppGroupId)\n      val defaultMavenVersion =\n        options.mvnAppVersion.getOrElse(Constants.mavenAppVersion)\n\n      def sbtProjectDescriptor0 =\n        sbtProjectDescriptor(options.sbtSetting.map(_.trim).filter(_.nonEmpty), sbtVersion, logger)\n\n      val projectDescriptor =\n        if shouldExportToMill then\n          millProjectDescriptor(\n            cache = options.shared.coursierCache,\n            projectName = options.project,\n            millVersion = options.millVersion.getOrElse(Constants.millVersion),\n            logger = logger\n          )\n        else if shouldExportToMaven then\n          mavenProjectDescriptor(\n            mavenPluginVersion = defaultMavenCompilerVersion,\n            mavenScalaPluginVersion = defaultScalaMavenCompilerVersion,\n            mavenExecPluginVersion = defaultMavenExecPluginVersion,\n            extraSettings = Nil,\n            mavenGroupId = defaultMavenGroupId,\n            mavenArtifactId = defaultMavenArtifactId,\n            mavenVersion = defaultMavenVersion,\n            logger = logger\n          )\n        else if shouldExportToJson then\n          jsonProjectDescriptor(\n            projectName = options.project,\n            workspace = inputs.workspace,\n            logger = logger\n          )\n        else // shouldExportToSbt isn't checked, as it's treated as default\n          sbtProjectDescriptor0\n\n      val project = projectDescriptor.`export`(optionsMain0, optionsTest0, sourcesMain, sourcesTest)\n        .orExit(logger)\n\n      os.makeDir.all(dest)\n      project.writeTo(dest)\n      logger.message(s\"Exported to: $dest\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/export0/ExportOptions.scala",
    "content": "package scala.cli.commands.export0\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{\n  HasSharedOptions,\n  HelpGroup,\n  HelpMessages,\n  MainClassOptions,\n  SharedOptions\n}\nimport scala.cli.commands.{Constants, tags}\n\n@HelpMessage(ExportOptions.helpMessage, \"\", ExportOptions.detailedHelpMessage)\nfinal case class ExportOptions(\n  // FIXME There might be too many options for 'scala-cli export' there\n  @Recurse\n  shared: SharedOptions = SharedOptions(),\n  @Recurse\n  mainClass: MainClassOptions = MainClassOptions(),\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Sets the export format to SBT\")\n  sbt: Option[Boolean] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Sets the export format to Maven\")\n  @Name(\"mvn\")\n  maven: Option[Boolean] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Sets the export format to Mill\")\n  mill: Option[Boolean] = None,\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @Group(HelpGroup.BuildToolExport.toString)\n  @HelpMessage(\"Sets the export format to Json\")\n  json: Option[Boolean] = None,\n  @Name(\"setting\")\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  sbtSetting: List[String] = Nil,\n  @Name(\"p\")\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Project name to be used on Mill build file\")\n  project: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\n    s\"Version of SBT to be used for the export (${Constants.defaultSbtVersion} by default)\"\n  )\n  sbtVersion: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\n    s\"Version of Mill to be used for the export (${Constants.defaultMillVersion} by default)\"\n  )\n  millVersion: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\n    s\"Version of Maven Compiler Plugin to be used for the export (${Constants.defaultMavenVersion} by default)\"\n  )\n  mvnVersion: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\n    s\"Version of Maven Scala Plugin to be used for the export (${Constants.defaultMavenScalaCompilerPluginVersion} by default)\"\n  )\n  mvnScalaVersion: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\n    s\"Version of Maven Exec Plugin to be used for the export (${Constants.defaultMavenExecPluginVersion} by default)\"\n  )\n  mvnExecPluginVersion: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"ArtifactId to be used for the maven export\")\n  mvnAppArtifactId: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"GroupId to be used for the maven export\")\n  mvnAppGroupId: Option[String] = None,\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Version to be used for the maven export\")\n  mvnAppVersion: Option[String] = None,\n  @Name(\"o\")\n  @Group(HelpGroup.BuildToolExport.toString)\n  @Tag(tags.restricted)\n  output: Option[String] = None\n) extends HasSharedOptions\nobject ExportOptions {\n  implicit lazy val parser: Parser[ExportOptions] = Parser.derive\n  implicit lazy val help: Help[ExportOptions]     = Help.derive\n\n  private val helpHeader =\n    \"Export current project to an external build tool (like SBT or Mill) or to JSON.\"\n  val helpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.docsWebsiteReference}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |The whole $fullRunnerName project should get exported along with its dependencies configuration.\n       |\n       |Unless otherwise configured, the default export format is SBT.\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.docsWebsiteReference}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fix/BuiltInRules.scala",
    "content": "package scala.cli.commands.fix\nimport os.{BasePathImpl, FilePath}\n\nimport scala.build.Ops.EitherMap2\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.input.*\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, Scope, SuppressWarningOptions}\nimport scala.build.preprocessing.directives.*\nimport scala.build.preprocessing.{ExtractedDirectives, SheBang}\nimport scala.build.{CrossSources, Logger, Position, Sources}\nimport scala.cli.commands.util.CommandHelpers\nimport scala.util.chaining.scalaUtilChainingOps\n\nobject BuiltInRules extends CommandHelpers {\n  private lazy val targetDirectivesKeysSet = DirectivesPreprocessingUtils.requireDirectiveHandlers\n    .flatMap(_.keys.flatMap(_.nameAliases)).toSet\n  private lazy val usingDirectivesKeysGrouped = DirectivesPreprocessingUtils.usingDirectiveHandlers\n    .flatMap(_.keys)\n  private lazy val usingDirectivesWithTestPrefixKeysGrouped =\n    DirectivesPreprocessingUtils.usingDirectiveWithReqsHandlers\n      .flatMap(_.keys)\n\n  private lazy val directiveTestPrefix = \"test.\"\n  extension (strictDirective: StrictDirective) {\n    private def hasTestPrefix: Boolean        = strictDirective.key.startsWith(directiveTestPrefix)\n    private def existsTestEquivalent: Boolean =\n      !strictDirective.hasTestPrefix &&\n      usingDirectivesWithTestPrefixKeysGrouped\n        .exists(_.nameAliases.contains(directiveTestPrefix + strictDirective.key))\n  }\n\n  private val newLine: String = scala.build.internal.AmmUtil.lineSeparator\n\n  def runRules(\n    inputs: Inputs,\n    buildOptions: BuildOptions,\n    logger: Logger\n  )(using ScalaCliInvokeData): Unit = {\n    val (mainSources, testSources) = getProjectSources(inputs, logger)\n      .left.map(CompositeBuildException(_))\n      .orExit(logger)\n\n    val sourcesCount =\n      mainSources.paths.length + mainSources.inMemory.length +\n        testSources.paths.length + testSources.inMemory.length\n    sourcesCount match\n      case 0 =>\n        logger.message(\"No sources to migrate directives from.\")\n        logger.message(\"Nothing to do.\")\n      case 1 =>\n        logger.message(\"No need to migrate directives for a single source file project.\")\n        logger.message(\"Nothing to do.\")\n      case _ => migrateDirectives(inputs, buildOptions, mainSources, testSources, logger)\n  }\n\n  private def migrateDirectives(\n    inputs: Inputs,\n    buildOptions: BuildOptions,\n    mainSources: Sources,\n    testSources: Sources,\n    logger: Logger\n  ): Unit = {\n    // Only initial inputs are used, new inputs discovered during processing of\n    // CrossSources.forInput may be shared between projects\n    val writableInputs: Seq[OnDisk] = inputs.flattened()\n      .collect { case onDisk: OnDisk => onDisk }\n\n    def isExtractedFromWritableInput(position: Option[Position.File]): Boolean = {\n      val originOrPathOpt = position.map(_.path)\n      originOrPathOpt match {\n        case Some(Right(path)) => writableInputs.exists(_.path == path)\n        case _                 => false\n      }\n    }\n\n    val projectFileContents = new StringBuilder()\n\n    given LoggingUtilities(logger, inputs.workspace)\n\n    // Deal with directives from the Main scope\n    val (directivesFromWritableMainInputs, testDirectivesFromMain) = {\n      val originalMainDirectives =\n        getExtractedDirectives(mainSources, buildOptions.suppressWarningOptions)\n          .filterNot(hasTargetDirectives)\n\n      val transformedMainDirectives = unifyCorrespondingNameAliases(originalMainDirectives)\n\n      val allDirectives = for {\n        transformedMainDirective <- transformedMainDirectives\n        directive                <- transformedMainDirective.directives\n      } yield directive\n\n      val (testScopeDirectives, allMainDirectives) =\n        allDirectives.partition(_.key.startsWith(\"test\"))\n\n      createFormattedLinesAndAppend(allMainDirectives, projectFileContents, isTest = false)\n\n      (\n        transformedMainDirectives.filter(d => isExtractedFromWritableInput(d.position)),\n        testScopeDirectives\n      )\n    }\n\n    // Deal with directives from the Test scope\n    val directivesFromWritableTestInputs: Seq[TransformedTestDirectives] =\n      if (\n        testSources.paths.nonEmpty || testSources.inMemory.nonEmpty ||\n        testDirectivesFromMain.nonEmpty\n      ) {\n        val originalTestDirectives =\n          getExtractedDirectives(testSources, buildOptions.suppressWarningOptions)\n            .filterNot(hasTargetDirectives)\n\n        val transformedTestDirectives = unifyCorrespondingNameAliases(originalTestDirectives)\n          .pipe(maybeTransformIntoTestEquivalent)\n\n        val allDirectives = for {\n          directivesWithTestPrefix              <- transformedTestDirectives.map(_.withTestPrefix)\n          directivesWithNoTestPrefixEquivalents <-\n            transformedTestDirectives.map {\n              _.noTestPrefixAvailable\n                .filter(_.existsTestEquivalent)\n            }\n          directive <-\n            directivesWithTestPrefix ++ directivesWithNoTestPrefixEquivalents ++\n              testDirectivesFromMain\n        } yield directive\n\n        createFormattedLinesAndAppend(allDirectives, projectFileContents, isTest = true)\n\n        transformedTestDirectives\n          .filter(ttd => isExtractedFromWritableInput(ttd.positions))\n      }\n      else Seq(TransformedTestDirectives(Nil, Nil, None))\n\n    projectFileContents.append(newLine)\n\n    // Write extracted directives to project.scala\n    logger.message(s\"Writing ${Constants.projectFileName}\")\n    os.write.over(inputs.workspace / Constants.projectFileName, projectFileContents.toString)\n\n    def isProjectFile(position: Option[Position.File]): Boolean =\n      position.exists(_.path.contains(inputs.workspace / Constants.projectFileName))\n\n    // Remove directives from their original files, skip the project.scala file\n    directivesFromWritableMainInputs\n      .filterNot(e => isProjectFile(e.position))\n      .foreach(d => removeDirectivesFrom(d.position))\n    directivesFromWritableTestInputs\n      .filterNot(ttd => isProjectFile(ttd.positions))\n      .foreach(ttd =>\n        removeDirectivesFrom(\n          position = ttd.positions,\n          toKeep = ttd.noTestPrefixAvailable.filterNot(_.existsTestEquivalent)\n        )\n      )\n  }\n\n  private def getProjectSources(inputs: Inputs, logger: Logger)(using\n    ScalaCliInvokeData\n  ): Either[::[BuildException], (Sources, Sources)] = {\n    val buildOptions = BuildOptions()\n\n    val (crossSources, _) = CrossSources.forInputs(\n      inputs,\n      preprocessors = Sources.defaultPreprocessors(\n        buildOptions.archiveCache,\n        buildOptions.internal.javaClassNameVersionOpt,\n        () => buildOptions.javaHome().value.javaCommand\n      ),\n      logger = logger,\n      suppressWarningOptions = SuppressWarningOptions.suppressAll,\n      exclude = buildOptions.internal.exclude,\n      download = buildOptions.downloader\n    ).orExit(logger)\n\n    val sharedOptions = crossSources.sharedOptions(buildOptions)\n    val scopedSources = crossSources.scopedSources(sharedOptions).orExit(logger)\n\n    val mainSources = scopedSources.sources(Scope.Main, sharedOptions, inputs.workspace, logger)\n    val testSources = scopedSources.sources(Scope.Test, sharedOptions, inputs.workspace, logger)\n\n    (mainSources, testSources).traverseN\n  }\n\n  private def getExtractedDirectives(\n    sources: Sources,\n    suppressWarningOptions: SuppressWarningOptions\n  )(\n    using loggingUtilities: LoggingUtilities\n  ): Seq[ExtractedDirectives] = {\n    val logger = loggingUtilities.logger\n\n    val fromPaths = sources.paths.map { (path, _) =>\n      val (_, content, _) = SheBang.partitionOnShebangSection(os.read(path))\n      logger.debug(s\"Extracting directives from ${loggingUtilities.relativePath(path)}\")\n      ExtractedDirectives.from(\n        contentChars = content.toCharArray,\n        path = Right(path),\n        suppressWarningOptions = suppressWarningOptions,\n        logger = logger,\n        maybeRecoverOnError = _ => None\n      ).orExit(logger)\n    }\n\n    val fromInMemory = sources.inMemory.map { inMem =>\n      val originOrPath = inMem.originalPath.map((_, path) => path)\n      val content      = originOrPath match {\n        case Right(path) =>\n          logger.debug(s\"Extracting directives from ${loggingUtilities.relativePath(path)}\")\n          os.read(path)\n        case Left(origin) =>\n          logger.debug(s\"Extracting directives from $origin\")\n          inMem.wrapperParamsOpt match {\n            // In case of script snippets, we need to drop the top wrapper lines\n            case Some(wrapperParams) => String(inMem.content)\n                .linesWithSeparators\n                .drop(wrapperParams.topWrapperLineCount)\n                .mkString\n            case None => String(inMem.content)\n          }\n      }\n\n      val (_, contentWithNoShebang, _) = SheBang.partitionOnShebangSection(content)\n\n      ExtractedDirectives.from(\n        contentChars = contentWithNoShebang.toCharArray,\n        path = originOrPath,\n        suppressWarningOptions = suppressWarningOptions,\n        logger = logger,\n        maybeRecoverOnError = _ => None\n      ).orExit(logger)\n    }\n\n    fromPaths ++ fromInMemory\n  }\n\n  private def hasTargetDirectives(extractedDirectives: ExtractedDirectives): Boolean = {\n    // Filter out all elements that contain using target directives\n    val directivesInElement = extractedDirectives.directives.map(_.key)\n    directivesInElement.exists(key => targetDirectivesKeysSet.contains(key))\n  }\n\n  private def unifyCorrespondingNameAliases(extractedDirectives: Seq[ExtractedDirectives]) =\n    extractedDirectives.map { extracted =>\n      // All keys that we migrate, not all in general\n      val allKeysGrouped   = usingDirectivesKeysGrouped ++ usingDirectivesWithTestPrefixKeysGrouped\n      val strictDirectives = extracted.directives\n\n      val strictDirectivesWithNewKeys = strictDirectives.flatMap { strictDir =>\n        val newKeyOpt = allKeysGrouped.find(_.nameAliases.contains(strictDir.key))\n          .flatMap(_.nameAliases.headOption)\n          .map { key =>\n            if (key.startsWith(\"test\"))\n              val withTestStripped = key.stripPrefix(\"test\").stripPrefix(\".\")\n              \"test.\" + withTestStripped.take(1).toLowerCase + withTestStripped.drop(1)\n            else\n              key\n          }\n\n        newKeyOpt.map(newKey => strictDir.copy(key = newKey))\n      }\n\n      extracted.copy(directives = strictDirectivesWithNewKeys)\n    }\n\n  /** Transforms directives into their 'test.' equivalent if it exists\n    *\n    * @param extractedDirectives\n    * @return\n    *   an instance of TransformedTestDirectives containing transformed directives and those that\n    *   could not be transformed since they have no 'test.' equivalent\n    */\n  private def maybeTransformIntoTestEquivalent(extractedDirectives: Seq[ExtractedDirectives])\n    : Seq[TransformedTestDirectives] =\n    for {\n      extractedFromSingleElement <- extractedDirectives\n      directives = extractedFromSingleElement.directives\n    } yield {\n      val (withInitialTestPrefix, noInitialTestPrefix) = directives.partition(_.hasTestPrefix)\n      val (withTestEquivalent, noTestEquivalent)       =\n        noInitialTestPrefix.partition(_.existsTestEquivalent)\n      val transformedToTestEquivalents = withTestEquivalent.map {\n        case StrictDirective(key, values, _, _) => StrictDirective(\"test.\" + key, values)\n      }\n\n      TransformedTestDirectives(\n        withTestPrefix = transformedToTestEquivalents ++ withInitialTestPrefix,\n        noTestPrefixAvailable = noTestEquivalent,\n        positions = extractedFromSingleElement.position\n      )\n    }\n\n  private def removeDirectivesFrom(\n    position: Option[Position.File],\n    toKeep: Seq[StrictDirective] = Nil\n  )(\n    using loggingUtilities: LoggingUtilities\n  ): Unit = {\n    position match {\n      case Some(Position.File(Right(path), _, _, offset)) =>\n        val (shebangSection, strippedContent, newLine) =\n          SheBang.partitionOnShebangSection(os.read(path))\n\n        def ignoreOrAddNewLine(str: String) = if str.isBlank then \"\" else str + newLine\n\n        val keepLines = ignoreOrAddNewLine(shebangSection) + ignoreOrAddNewLine(toKeep.mkString(\n          \"\",\n          newLine,\n          newLine\n        ))\n        val newContents  = keepLines + strippedContent.drop(offset).stripLeading()\n        val relativePath = loggingUtilities.relativePath(path)\n\n        loggingUtilities.logger.message(s\"Removing directives from $relativePath\")\n        if (toKeep.nonEmpty) {\n          loggingUtilities.logger.message(\"  Keeping:\")\n          toKeep.foreach(d => loggingUtilities.logger.message(s\"    $d\"))\n        }\n\n        os.write.over(path, newContents.stripLeading())\n      case _ => ()\n    }\n  }\n\n  private def createFormattedLinesAndAppend(\n    strictDirectives: Seq[StrictDirective],\n    projectFileContents: StringBuilder,\n    isTest: Boolean\n  ): Unit = {\n    if (strictDirectives.nonEmpty) {\n      projectFileContents\n        .append(if (projectFileContents.nonEmpty) newLine else \"\")\n        .append(if isTest then \"// Test\" else \"// Main\")\n        .append(newLine)\n\n      strictDirectives\n        // group by key to merge values\n        .groupBy(_.key)\n        .map { (key, directives) =>\n          StrictDirective(key, directives.flatMap(_.values))\n        }\n        // group by key prefixes to create splits between groups\n        .groupBy(dir =>\n          (if (isTest) dir.key.stripPrefix(directiveTestPrefix) else dir.key).takeWhile(_ != '.')\n        )\n        .map { (_, directives) =>\n          directives.flatMap(_.explodeToStringsWithColLimit()).toSeq.sorted\n        }\n        .toSeq\n        .filter(_.nonEmpty)\n        .sortBy(_.head)(using directivesOrdering)\n        // append groups to the StringBuilder, add new lines between groups that are bigger than one line\n        .foldLeft(0) { (lastSize, directiveLines) =>\n          val newSize = directiveLines.size\n          if (lastSize > 1 || (lastSize != 0 && newSize > 1)) projectFileContents.append(newLine)\n\n          directiveLines.foreach(projectFileContents.append(_).append(newLine))\n\n          newSize\n        }\n    }\n  }\n\n  private case class TransformedTestDirectives(\n    withTestPrefix: Seq[StrictDirective],\n    noTestPrefixAvailable: Seq[StrictDirective],\n    positions: Option[Position.File]\n  )\n\n  private case class LoggingUtilities(\n    logger: Logger,\n    workspacePath: os.Path\n  ) {\n    def relativePath(path: os.Path): FilePath & BasePathImpl =\n      if (path.startsWith(workspacePath)) path.relativeTo(workspacePath)\n      else path\n  }\n\n  private val directivesOrdering: Ordering[String] = {\n    def directivesOrder(key: String): Int = {\n      val handlersOrder = Seq(\n        ScalaVersion.handler.keys,\n        Platform.handler.keys,\n        Jvm.handler.keys,\n        JavaHome.handler.keys,\n        ScalaNative.handler.keys,\n        ScalaJs.handler.keys,\n        ScalacOptions.handler.keys,\n        JavaOptions.handler.keys,\n        JavacOptions.handler.keys,\n        JavaProps.handler.keys,\n        MainClass.handler.keys,\n        scala.build.preprocessing.directives.Sources.handler.keys,\n        ObjectWrapper.handler.keys,\n        Toolkit.handler.keys,\n        Dependency.handler.keys\n      )\n\n      handlersOrder.zipWithIndex\n        .find(_._1.flatMap(_.nameAliases).contains(key))\n        .map(_._2)\n        .getOrElse(if key.startsWith(\"publish\") then 20 else 15)\n    }\n\n    Ordering.by { directiveLine =>\n      val key = directiveLine\n        .stripPrefix(\"//> using\")\n        .stripLeading()\n        .stripPrefix(\"test.\")\n        // separate key from value\n        .takeWhile(!_.isWhitespace)\n\n      directivesOrder(key)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fix/Fix.scala",
    "content": "package scala.cli.commands.fix\n\nimport caseapp.core.RemainingArgs\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.{BuildThreads, Logger}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.SharedOptions\nimport scala.cli.config.Keys\nimport scala.cli.util.ConfigDbUtils\n\nobject Fix extends ScalaCommand[FixOptions] {\n  override def group                   = \"Main\"\n  override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL\n  override def sharedOptions(options: FixOptions): Option[SharedOptions] = Some(options.shared)\n\n  override def runCommand(options: FixOptions, args: RemainingArgs, logger: Logger): Unit = {\n    if options.areAnyRulesEnabled then {\n      val inputs    = options.shared.inputs(args.all).orExit(logger)\n      val buildOpts = buildOptionsOrExit(options)\n      val configDb  = ConfigDbUtils.configDb.orExit(logger)\n      if options.enableBuiltInRules then {\n        logger.message(\"Running built-in rules...\")\n        if options.check then\n          // TODO support --check for built-in rules: https://github.com/VirtusLab/scala-cli/issues/3423\n          logger.message(\"Skipping, '--check' is not yet supported for built-in rules.\")\n        else {\n          BuiltInRules.runRules(\n            inputs = inputs,\n            buildOptions = buildOpts,\n            logger = logger\n          )\n          logger.message(\"Built-in rules completed.\")\n        }\n      }\n      if options.enableScalafix then\n        either {\n          logger.message(\"Running scalafix rules...\")\n          val threads                      = BuildThreads.create()\n          val compilerMaker                = options.shared.compilerMaker(threads)\n          val workspace: os.Path           = if args.all.isEmpty then os.pwd else inputs.workspace\n          val actionableDiagnosticsEnabled = options.shared.logging.verbosityOptions.actions\n            .orElse(configDb.get(Keys.actions).getOrElse(None))\n          val scalafixExitCode: Int = value {\n            ScalafixRules.runRules(\n              buildOptions = buildOpts,\n              scalafixOptions = options.scalafix,\n              sharedOptions = options.shared,\n              inputs = inputs,\n              check = options.check,\n              compilerMaker = compilerMaker,\n              actionableDiagnostics = actionableDiagnosticsEnabled,\n              workspace = workspace,\n              logger = logger\n            )\n          }\n          if scalafixExitCode != 1 then logger.message(\"scalafix rules completed.\")\n          else logger.error(\"scalafix rules failed.\")\n          sys.exit(scalafixExitCode)\n        }\n    }\n    else logger.message(\"No rules were enabled. Did you disable everything intentionally?\")\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fix/FixOptions.scala",
    "content": "package scala.cli.commands.fix\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{HasSharedOptions, HelpGroup, HelpMessages, SharedOptions}\nimport scala.cli.commands.tags\n\n@HelpMessage(FixOptions.helpMessage, \"\", FixOptions.detailedHelpMessage)\nfinal case class FixOptions(\n  @Recurse\n  shared: SharedOptions = SharedOptions(),\n  @Recurse\n  scalafix: ScalafixOptions = ScalafixOptions(),\n  @Group(HelpGroup.Fix.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Fail the invocation if rewrites are needed\")\n  @Tag(tags.inShortHelp)\n  check: Boolean = false,\n  @Group(HelpGroup.Fix.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Enable running Scalafix rules (enabled by default)\")\n  @Tag(tags.inShortHelp)\n  @Name(\"scalafix\")\n  enableScalafix: Boolean = true,\n  @Group(HelpGroup.Fix.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Enable running built-in rules (enabled by default)\")\n  @Tag(tags.inShortHelp)\n  @Name(\"enableBuiltIn\")\n  @Name(\"builtIn\")\n  @Name(\"builtInRules\")\n  enableBuiltInRules: Boolean = true\n) extends HasSharedOptions {\n  def areAnyRulesEnabled: Boolean = enableScalafix || enableBuiltInRules\n}\n\nobject FixOptions {\n  implicit lazy val parser: Parser[FixOptions] = Parser.derive\n  implicit lazy val help: Help[FixOptions]     = Help.derive\n\n  val cmdName            = \"fix\"\n  private val helpHeader =\n    s\"Run $fullRunnerName & Scalafix rules to lint, rewrite or otherwise rearrange a $fullRunnerName project.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |`scalafix` is used to check project code or rewrite it under the hood with use of specified rules.\n       |\n       |All standard $fullRunnerName inputs are accepted, but only Scala sources will be refactored (.scala and .sc files).\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fix/ScalafixOptions.scala",
    "content": "package scala.cli.commands.fix\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\nfinal case class ScalafixOptions(\n  @Group(HelpGroup.Fix.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Custom path to the scalafix configuration file.\")\n  @Tag(tags.inShortHelp)\n  scalafixConf: Option[String] = None,\n  @Group(HelpGroup.Fix.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Pass extra argument(s) to scalafix.\")\n  @Tag(tags.inShortHelp)\n  scalafixArg: List[String] = Nil,\n  @Group(HelpGroup.Fix.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Run scalafix rule(s) explicitly, overriding the configuration file default.\")\n  @Tag(tags.inShortHelp)\n  scalafixRules: List[String] = Nil\n)\nobject ScalafixOptions {\n  implicit lazy val parser: Parser[ScalafixOptions] = Parser.derive\n  implicit lazy val help: Help[ScalafixOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fix/ScalafixRules.scala",
    "content": "package scala.cli.commands.fix\n\nimport coursier.cache.FileCache\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.compiler.ScalaCompilerMaker\nimport scala.build.errors.BuildException\nimport scala.build.input.{Inputs, ScalaCliInvokeData}\nimport scala.build.internal.{Constants, Runner}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.build.options.BuildOptions\nimport scala.build.{Build, Logger, ScalafixArtifacts}\nimport scala.cli.commands.shared.SharedOptions\nimport scala.cli.commands.util.BuildCommandHelpers.copyOutput\nimport scala.cli.commands.util.CommandHelpers\n\nobject ScalafixRules extends CommandHelpers {\n  def runRules(\n    buildOptions: BuildOptions,\n    scalafixOptions: ScalafixOptions,\n    sharedOptions: SharedOptions,\n    inputs: Inputs,\n    compilerMaker: ScalaCompilerMaker,\n    workspace: os.Path,\n    check: Boolean,\n    actionableDiagnostics: Option[Boolean],\n    logger: Logger\n  )(using ScalaCliInvokeData): Either[BuildException, Int] = {\n    sharedOptions.semanticDbOptions.semanticDb match {\n      case Some(false) =>\n        logger.message(\n          s\"\"\"$warnPrefix SemanticDB files' generation was explicitly set to false.\n             |$warnPrefix Some scalafix rules require .semanticdb files and may not work properly.\"\"\"\n            .stripMargin\n        )\n      case Some(true) =>\n        logger.debug(\"SemanticDB files' generation enabled.\")\n      case None =>\n        logger.debug(\"Defaulting SemanticDB files' generation to true, to satisfy scalafix needs.\")\n    }\n    val buildOptionsWithSemanticDb =\n      if buildOptions.scalaOptions.semanticDbOptions.generateSemanticDbs.isEmpty then\n        buildOptions.copy(scalaOptions =\n          buildOptions.scalaOptions.copy(semanticDbOptions =\n            buildOptions.scalaOptions.semanticDbOptions.copy(generateSemanticDbs =\n              Some(true)\n            )\n          )\n        )\n      else buildOptions\n\n    val shouldBuildTestScope = sharedOptions.scope.test.getOrElse(true)\n    if !shouldBuildTestScope then\n      logger.message(\n        s\"\"\"$warnPrefix Building test scope was explicitly disabled.\n           |$warnPrefix Some scalafix rules may not work correctly with test scope inputs.\"\"\"\n          .stripMargin\n      )\n    val res = Build.build(\n      inputs,\n      buildOptionsWithSemanticDb,\n      compilerMaker,\n      None,\n      logger,\n      crossBuilds = false,\n      buildTests = shouldBuildTestScope,\n      partial = None,\n      actionableDiagnostics = actionableDiagnostics\n    )\n    val builds = res.orExit(logger)\n\n    builds.builds match\n      case b if b.forall(_.success) =>\n        val successfulBuilds = b.collect { case s: Build.Successful => s }\n        successfulBuilds.foreach(_.copyOutput(sharedOptions))\n        val classPaths    = successfulBuilds.flatMap(_.fullClassPath).distinct\n        val scalacOptions =\n          successfulBuilds.headOption.toSeq\n            .flatMap(_.options.scalaOptions.scalacOptions.toSeq.map(_.value.value))\n\n        val scalaVersion = {\n          for {\n            b           <- successfulBuilds.headOption\n            scalaParams <- b.scalaParams\n          } yield scalaParams.scalaVersion\n        }.getOrElse(Constants.defaultScalaVersion)\n\n        either {\n          val artifacts =\n            value(\n              ScalafixArtifacts.artifacts(\n                scalaVersion,\n                successfulBuilds.headOption.toSeq\n                  .flatMap(_.options.classPathOptions.scalafixDependencies.values.flatten),\n                value(buildOptions.finalRepositories),\n                logger,\n                buildOptions.internal.cache.getOrElse(FileCache())\n              )\n            )\n\n          val scalafixCliOptions =\n            scalafixOptions.scalafixConf.toList.flatMap(scalafixConf =>\n              List(\"--config\", scalafixConf)\n            ) ++\n              Seq(\"--sourceroot\", workspace.toString) ++\n              Seq(\"--classpath\", classPaths.mkString(java.io.File.pathSeparator)) ++\n              Seq(\"--scala-version\", scalaVersion) ++\n              (if check then Seq(\"--test\") else Nil) ++\n              (if scalacOptions.nonEmpty then scalacOptions.flatMap(Seq(\"--scalac-options\", _))\n               else Nil) ++\n              (if artifacts.toolsJars.nonEmpty then\n                 Seq(\"--tool-classpath\", artifacts.toolsJars.mkString(java.io.File.pathSeparator))\n               else Nil) ++\n              scalafixOptions.scalafixRules.flatMap(Seq(\"-r\", _))\n              ++ scalafixOptions.scalafixArg\n\n          val proc = Runner.runJvm(\n            buildOptions.javaHome().value.javaCommand,\n            buildOptions.javaOptions.javaOpts.toSeq.map(_.value.value),\n            artifacts.scalafixJars,\n            \"scalafix.cli.Cli\",\n            scalafixCliOptions,\n            logger,\n            cwd = Some(workspace),\n            allowExecve = true\n          )\n\n          proc.waitFor()\n        }\n\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fmt/Fmt.scala",
    "content": "package scala.cli.commands.fmt\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport dependency.*\n\nimport scala.build.Logger\nimport scala.build.input.{ProjectScalaFile, SbtFile, Script, SourceScalaFile}\nimport scala.build.internal.{Constants, ExternalBinaryParams, FetchExternalBinary, Runner}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.cli.CurrentParams\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.fmt.FmtUtil.*\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}\nimport scala.cli.util.ArgHelpers.*\n\nobject Fmt extends ScalaCommand[FmtOptions] {\n  override def group: String = HelpCommandGroup.Main.toString\n  override def sharedOptions(options: FmtOptions): Option[SharedOptions] = Some(options.shared)\n  override def scalaSpecificationLevel                                   = SpecificationLevel.SHOULD\n\n  val hiddenHelpGroups: Seq[HelpGroup] =\n    Seq(\n      HelpGroup.Scala,\n      HelpGroup.Java,\n      HelpGroup.Dependency,\n      HelpGroup.ScalaJs,\n      HelpGroup.ScalaNative,\n      HelpGroup.CompilationServer,\n      HelpGroup.Debug\n    )\n  override def helpFormat: HelpFormat = super.helpFormat\n    .withHiddenGroups(hiddenHelpGroups)\n    .withHiddenGroupsWhenShowHidden(hiddenHelpGroups)\n    .withPrimaryGroup(HelpGroup.Format)\n  override def names: List[List[String]] = List(\n    List(\"fmt\"),\n    List(\"format\"),\n    List(\"scalafmt\")\n  )\n\n  override def runCommand(options: FmtOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val buildOptions = buildOptionsOrExit(options)\n\n    if options.shared.scope.test.nonEmpty then\n      logger.message(\n        s\"\"\"$warnPrefix Including the test scope does not change the behaviour of this command. \n           |$warnPrefix Test scope inputs are formatted, regardless.\"\"\".stripMargin\n      )\n\n    // TODO If no input is given, just pass '.' to scalafmt?\n    val (sourceFiles, workspace, _) =\n      if args.all.isEmpty then (Seq(os.pwd), os.pwd, None)\n      else {\n        val i = options.shared.inputs(args.all).orExit(logger)\n        type FormattableSourceFile = Script | SourceScalaFile | ProjectScalaFile | SbtFile\n        val s = i.sourceFiles().collect { case sc: FormattableSourceFile => sc.path }\n        (s, i.workspace, Some(i))\n      }\n    CurrentParams.workspaceOpt = Some(workspace)\n    val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialect(workspace, options, logger)\n    val cache                                   = buildOptions.archiveCache\n\n    if (sourceFiles.isEmpty)\n      logger.debug(\"No source files, not formatting anything\")\n    else {\n      val version =\n        options.scalafmtVersion.getOrElse(versionMaybe.getOrElse(Constants.defaultScalafmtVersion))\n      val dialectString = options.scalafmtDialect.orElse(dialectMaybe).getOrElse {\n        options.buildOptions.orExit(logger).scalaParams.orExit(logger).map(_.scalaVersion)\n          .getOrElse(Constants.defaultScalaVersion) match\n          case v if v.startsWith(\"2.11.\") => \"scala211\"\n          case v if v.startsWith(\"2.12.\") => \"scala212\"\n          case v if v.startsWith(\"2.13.\") => \"scala213\"\n          case v if v.startsWith(\"3.\")    => \"scala3\"\n          case _                          => \"default\"\n      }\n\n      val entry = {\n        val dialect       = ScalafmtDialect.fromString(dialectString)\n        val prevConfMaybe = pathMaybe.map(p => os.read(p))\n        scalafmtConfigWithFields(prevConfMaybe.getOrElse(\"\"), Some(version), dialect)\n      }\n      val scalaFmtConfPath = {\n        val confFileName = \".scalafmt.conf\"\n        val path         =\n          if (options.saveScalafmtConf) pathMaybe.getOrElse(workspace / confFileName)\n          else workspace / Constants.workspaceDirName / confFileName\n        os.write.over(path, entry, createFolders = true)\n        path\n      }\n\n      val fmtCommand = options.scalafmtLauncher.filter(_.nonEmpty) match {\n        case Some(launcher) =>\n          Seq(launcher)\n        case None =>\n          val (url, changing) = options.binaryUrl(version)\n          val params          = ExternalBinaryParams(\n            url,\n            changing,\n            \"scalafmt\",\n            Seq(dep\"${Constants.scalafmtOrganization}:${Constants.scalafmtName}:$version\"),\n            \"org.scalafmt.cli.Cli\"\n          )\n          FetchExternalBinary.fetch(\n            params,\n            cache,\n            logger,\n            () => buildOptions.javaHome().value.javaCommand\n          )\n            .orExit(logger)\n            .command\n      }\n\n      logger.debug(s\"Launching scalafmt with command $fmtCommand\")\n\n      val command = fmtCommand ++\n        sourceFiles.map(_.toString) ++\n        options.scalafmtCliOptions ++\n        Seq(\"--config\", scalaFmtConfPath.toString)\n      val process = Runner.maybeExec(\n        \"scalafmt\",\n        command,\n        logger,\n        cwd = Some(workspace)\n      )\n      sys.exit(process.waitFor())\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fmt/FmtOptions.scala",
    "content": "package scala.cli.commands.fmt\n\nimport caseapp.*\n\nimport scala.build.coursierVersion\nimport scala.build.errors.BuildException\nimport scala.build.internal.FetchExternalBinary\nimport scala.build.options.BuildOptions\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{HasSharedOptions, HelpGroup, HelpMessages, SharedOptions}\nimport scala.cli.commands.{Constants, tags}\nimport scala.util.Properties\n\n// format: off\n@HelpMessage(FmtOptions.helpMessage, \"\", FmtOptions.detailedHelpMessage)\nfinal case class FmtOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Check if sources are well formatted\")\n    check: Boolean = false,\n\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Use project filters defined in the configuration. Turned on by default, use `--respect-project-filters:false` to disable it.\")\n    respectProjectFilters: Boolean = true,\n\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Saves .scalafmt.conf file if it was created or overwritten\")\n    saveScalafmtConf: Boolean = false,\n\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @Hidden\n    osArchSuffix: Option[String] = None,\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @Hidden\n    scalafmtTag: Option[String] = None,\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @Hidden\n    scalafmtGithubOrgName: Option[String] = None,\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @Hidden\n    scalafmtExtension: Option[String] = None,\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @Hidden\n    scalafmtLauncher: Option[String] = None,\n\n  @Group(HelpGroup.Format.toString)\n  @Name(\"F\")\n  @Tag(tags.implementation)\n  @HelpMessage(\"Pass an argument to scalafmt.\")\n  @Tag(tags.inShortHelp)\n    scalafmtArg: List[String] = Nil,\n\n  @Group(HelpGroup.Format.toString)\n  @HelpMessage(\"Custom path to the scalafmt configuration file.\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @Name(\"scalafmtConfig\")\n    scalafmtConf: Option[String] = None,\n  @Group(HelpGroup.Format.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Pass configuration as a string.\")\n  @Name(\"scalafmtConfigStr\")\n  @Name(\"scalafmtConfSnippet\")\n    scalafmtConfStr: Option[String] = None,\n  @Tag(tags.implementation)\n  @Group(HelpGroup.Format.toString)\n  @HelpMessage(\"Pass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file or inferred based on Scala version used.\")\n  @Tag(tags.implementation)\n  @Name(\"dialect\")\n  @Tag(tags.inShortHelp)\n    scalafmtDialect: Option[String] = None,\n  @Tag(tags.implementation)\n  @Group(HelpGroup.Format.toString)\n  @HelpMessage(s\"Pass scalafmt version before running it (${Constants.defaultScalafmtVersion} by default). If passed, this overrides whatever value is configured in the .scalafmt.conf file.\")\n  @Name(\"fmtVersion\")\n  @Tag(tags.inShortHelp)\n    scalafmtVersion: Option[String] = None\n) extends HasSharedOptions {\n  // format: on\n\n  def binaryUrl(version: String): (String, Boolean) = {\n    val osArchSuffix0 = osArchSuffix.map(_.trim).filter(_.nonEmpty)\n      .getOrElse(FetchExternalBinary.platformSuffix())\n    val tag0           = scalafmtTag.getOrElse(\"v\" + version)\n    val gitHubOrgName0 = scalafmtGithubOrgName.getOrElse {\n      version.coursierVersion match {\n        case v if v < \"3.5.9\".coursierVersion => \"scala-cli/scalafmt-native-image\"\n        // since version 3.5.9 scalafmt-native-image repository was moved to VirtusLab organisation\n        case v if v < \"3.9.1\".coursierVersion => \"virtuslab/scalafmt-native-image\"\n        // since version 3.9.1 native images for all platforms are provided by ScalaMeta\n        case _ => \"scalameta/scalafmt\"\n      }\n    }\n    val extension0 = version match {\n      case v if v.coursierVersion >= \"3.9.1\".coursierVersion || Properties.isWin => \".zip\"\n      case _                                                                     => \".gz\"\n    }\n    val url =\n      s\"https://github.com/$gitHubOrgName0/releases/download/$tag0/scalafmt-$osArchSuffix0$extension0\"\n    (url, !tag0.startsWith(\"v\"))\n  }\n\n  def buildOptions: Either[BuildException, BuildOptions] = shared.buildOptions()\n\n  def scalafmtCliOptions: List[String] =\n    scalafmtArg :::\n      (if (check && !scalafmtArg.contains(\"--check\")) List(\"--check\") else Nil) :::\n      (if (respectProjectFilters && !scalafmtArg.contains(\"--respect-project-filters\"))\n         List(\"--respect-project-filters\")\n       else Nil)\n\n}\nobject FmtOptions {\n  implicit lazy val parser: Parser[FmtOptions] = Parser.derive\n  implicit lazy val help: Help[FmtOptions]     = Help.derive\n\n  val cmdName                     = \"fmt\"\n  private val helpHeader          = \"Formats Scala code.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |`scalafmt` is used to perform the formatting under the hood.\n       |\n       |The `.scalafmt.conf` configuration file is optional.\n       |Default configuration values will be assumed by $fullRunnerName.\n       |\n       |All standard $fullRunnerName inputs are accepted, but only Scala sources will be formatted (.scala and .sc files).\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/fmt/FmtUtil.scala",
    "content": "package scala.cli.commands.fmt\n\nimport com.typesafe.config.parser.{ConfigDocument, ConfigDocumentFactory}\nimport com.typesafe.config.{ConfigParseOptions, ConfigSyntax}\n\nimport scala.build.Logger\nimport scala.build.internal.Constants\nimport scala.util.control.NonFatal\n\nobject FmtUtil {\n  private def getGitRoot(workspace: os.Path, logger: Logger): Option[String] =\n    try {\n      val result = os.proc(\"git\", \"rev-parse\", \"--show-toplevel\").call(\n        cwd = workspace,\n        stderr = os.ProcessOutput.ReadBytes((_, _) => ())\n      ).out.trim()\n      Option(result)\n    }\n    catch {\n      case NonFatal(e) =>\n        logger.log(\n          s\"\"\"Could not get root of the git repository.\n             |Cause: $e\"\"\".stripMargin\n        )\n        None\n    }\n\n  /** Based on scalafmt\n    * [comment](https://github.com/scalameta/scalafmt/blob/d0c11e98898334969f5f4dfc4bd511630cf00ab9/scalafmt-cli/src/main/scala/org/scalafmt/cli/CliArgParser.scala).\n    * First we look for .scalafmt.conf file in the `cwd`. If not found we go to the `git root` and\n    * look there.\n    *\n    * @return\n    *   path to found `.scalafmt.conf` file and `version` with `dialect` read from it\n    */\n  def readVersionAndDialect(\n    workspace: os.Path,\n    options: FmtOptions,\n    logger: Logger\n  ): (Option[String], Option[String], Option[os.Path]) = {\n    case class RunnerMetaconfig(dialect: String = \"\")\n    object RunnerMetaconfig {\n      lazy val default: RunnerMetaconfig                                      = RunnerMetaconfig(\"\")\n      implicit lazy val surface: metaconfig.generic.Surface[RunnerMetaconfig] =\n        metaconfig.generic.deriveSurface[RunnerMetaconfig]\n      implicit lazy val decoder: metaconfig.ConfDecoder[RunnerMetaconfig] =\n        metaconfig.generic.deriveDecoder[RunnerMetaconfig](default)\n    }\n    case class ScalafmtMetaconfig(\n      version: String = \"\",\n      runner: RunnerMetaconfig = RunnerMetaconfig(\"\")\n    )\n    object ScalafmtMetaconfig {\n      lazy val default: ScalafmtMetaconfig = ScalafmtMetaconfig()\n      implicit lazy val surface: metaconfig.generic.Surface[ScalafmtMetaconfig] =\n        metaconfig.generic.deriveSurface[ScalafmtMetaconfig]\n      implicit lazy val decoder: metaconfig.ConfDecoder[ScalafmtMetaconfig] =\n        metaconfig.generic.deriveDecoder[ScalafmtMetaconfig](default)\n    }\n    val confName  = \".scalafmt.conf\"\n    val pathMaybe =\n      options.scalafmtConfStr.flatMap { s =>\n        val tmpConfPath = workspace / Constants.workspaceDirName / \".scalafmt.conf\"\n        os.write.over(tmpConfPath, s, createFolders = true)\n        Some(tmpConfPath)\n      }.orElse {\n        options.scalafmtConf.flatMap { p =>\n          val confPath = os.Path(p, os.pwd)\n          logger.debug(s\"Checking for $confPath.\")\n          if (os.exists(confPath)) Some(confPath)\n          else\n            logger.message(s\"WARNING: provided file doesn't exist $confPath\")\n            None\n        }.orElse {\n          logger.debug(s\"Checking for $confName in cwd.\")\n          val confInCwd = workspace / confName\n          if (os.exists(confInCwd)) Some(confInCwd)\n          else {\n            logger.debug(s\"Checking for $confName in git root.\")\n            val gitRootMaybe       = getGitRoot(workspace, logger)\n            val confInGitRootMaybe = gitRootMaybe.map(os.Path(_) / confName)\n            confInGitRootMaybe.find(os.exists(_))\n          }\n        }\n      }\n\n    val confContentMaybe = pathMaybe.flatMap { path =>\n      val either = metaconfig.Hocon.parseInput[ScalafmtMetaconfig](\n        metaconfig.Input.File(path.toNIO)\n      ).toEither\n      either.left.foreach(confErr => logger.log(confErr.toString()))\n      either.toOption\n    }\n    val versionMaybe = confContentMaybe.collect {\n      case conf if conf.version.trim.nonEmpty => conf.version\n    }\n    val dialectMaybe = confContentMaybe.collect {\n      case conf if conf.runner.dialect.trim.nonEmpty => conf.runner.dialect\n    }\n    (versionMaybe, dialectMaybe, pathMaybe)\n  }\n\n  // Based on https://github.com/scalameta/metals/blob/main/metals/src/main/scala/scala/meta/internal/metals/ScalafmtDialect.scala\n  sealed abstract class ScalafmtDialect(val value: String)\n  object ScalafmtDialect {\n    case object Scala3          extends ScalafmtDialect(\"scala3\")\n    case object Scala213        extends ScalafmtDialect(\"scala213\")\n    case object Scala213Source3 extends ScalafmtDialect(\"scala213source3\")\n    case object Scala212        extends ScalafmtDialect(\"scala212\")\n    case object Scala212Source3 extends ScalafmtDialect(\"scala212source3\")\n    case object Scala211        extends ScalafmtDialect(\"scala211\")\n\n    implicit val ord: Ordering[ScalafmtDialect] = new Ordering[ScalafmtDialect] {\n\n      override def compare(x: ScalafmtDialect, y: ScalafmtDialect): Int =\n        prio(x) - prio(y)\n\n      private def prio(d: ScalafmtDialect): Int = d match {\n        case Scala211        => 1\n        case Scala212        => 2\n        case Scala212Source3 => 3\n        case Scala213        => 4\n        case Scala213Source3 => 5\n        case Scala3          => 6\n      }\n    }\n\n    def fromString(v: String): Option[ScalafmtDialect] = v.toLowerCase match {\n      case \"default\"         => Some(Scala213)\n      case \"scala211\"        => Some(Scala211)\n      case \"scala212\"        => Some(Scala212)\n      case \"scala212source3\" => Some(Scala212Source3)\n      case \"scala213\"        => Some(Scala213)\n      case \"scala213source3\" => Some(Scala213Source3)\n      case \"scala3\"          => Some(Scala3)\n      case _                 => None\n    }\n  }\n\n  /** Based on scalameta [fmt\n    * config](https://github.com/scalameta/metals/blob/main/metals/src/main/scala/scala/meta/internal/metals/ScalafmtConfig.scala)\n    *\n    * @return\n    *   Scalafmt configuration content based on previousConfigText with updated fields\n    */\n  def scalafmtConfigWithFields(\n    previousConfigText: String,\n    version: Option[String] = None,\n    runnerDialect: Option[ScalafmtDialect] = None,\n    fileOverride: Map[String, ScalafmtDialect] = Map.empty\n  ): String = {\n\n    def docFrom(s: String): ConfigDocument = {\n      val options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF)\n      ConfigDocumentFactory.parseString(s, options)\n    }\n\n    def withUpdatedVersion(content: String, v: String): String = {\n      val doc = docFrom(content)\n      if (doc.hasPath(\"version\"))\n        doc.withValueText(\"version\", s\"\\\"$v\\\"\").render\n      else {\n        // prepend to the beggining of file\n        val sb = new StringBuilder\n        sb.append(s\"\"\"version = \"$v\"\"\"\")\n        sb.append(System.lineSeparator)\n        sb.append(content)\n        sb.toString\n      }\n    }\n\n    def withUpdatedDialect(content: String, d: ScalafmtDialect): String = {\n      val doc = docFrom(content)\n      if (doc.hasPath(\"runner.dialect\"))\n        doc.withValueText(\"runner.dialect\", d.value).render\n      else {\n        // append to the end\n        val sb = new StringBuilder\n        sb.append(content)\n        val sep    = System.lineSeparator\n        val lastLn = content.endsWith(sep)\n        if (!lastLn) sb.append(sep)\n        sb.append(s\"runner.dialect = ${d.value}\")\n        sb.append(sep)\n        sb.toString\n      }\n    }\n\n    def withFileOverride(\n      content: String,\n      overrides: Map[String, ScalafmtDialect]\n    ): String =\n      if (overrides.isEmpty) content\n      else {\n        val sep    = System.lineSeparator\n        val values = overrides\n          .map { case (key, dialect) =>\n            s\"\"\"|  \"$key\" {\n                |     runner.dialect = ${dialect.value}\n                |  }\"\"\".stripMargin\n          }\n          .mkString(s\"fileOverride {$sep\", sep, s\"$sep}$sep\")\n\n        val addSep = if (content.endsWith(sep)) \"\" else sep\n        content + addSep + values\n      }\n\n    val doNothing = identity[String]\n    val combined  = List(\n      version.fold(doNothing)(v => withUpdatedVersion(_, v)),\n      runnerDialect.fold(doNothing)(v => withUpdatedDialect(_, v)),\n      withFileOverride(_, fileOverride)\n    ).reduceLeft(_ andThen _)\n    combined(previousConfigText)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/GitHubApi.scala",
    "content": "package scala.cli.commands.github\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport java.util.Base64\n\nobject GitHubApi {\n\n  final case class Secret(\n    name: String,\n    created_at: String,\n    updated_at: String\n  )\n\n  final case class SecretList(\n    total_count: Int,\n    secrets: List[Secret]\n  )\n\n  implicit val secretListCodec: JsonValueCodec[SecretList] =\n    JsonCodecMaker.make\n\n  final case class PublicKey(\n    key_id: String,\n    key: String\n  ) {\n    def decodedKey: Array[Byte] =\n      Base64.getDecoder().decode(key)\n  }\n\n  implicit val publicKeyCodec: JsonValueCodec[PublicKey] =\n    JsonCodecMaker.make\n\n  final case class EncryptedSecret(\n    encrypted_value: String,\n    key_id: String\n  )\n\n  implicit val encryptedSecretCodec: JsonValueCodec[EncryptedSecret] =\n    JsonCodecMaker.make\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/HasSharedSecretOptions.scala",
    "content": "package scala.cli.commands.github\n\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions}\n\ntrait HasSharedSecretOptions extends HasGlobalOptions {\n  def shared: SharedSecretOptions\n\n  override def global: GlobalOptions = shared.global\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/LibSodiumJni.scala",
    "content": "package scala.cli.commands.github\n\nimport coursier.cache.{ArchiveCache, Cache, CacheLogger}\nimport coursier.core.Type\nimport coursier.util.Task\n\nimport java.io.InputStream\nimport java.nio.charset.StandardCharsets\nimport java.util.Locale\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.internal.{Constants, FetchExternalBinary}\nimport scala.build.internals.EnvVar\nimport scala.cli.internal.Constants as CliConstants\nimport scala.util.Properties\nimport scala.util.control.NonFatal\n\nobject LibSodiumJni {\n\n  private def isGraalvmNativeImage: Boolean =\n    sys.props.contains(\"org.graalvm.nativeimage.imagecode\")\n\n  private def launcherKindOpt = {\n    val path   = CliConstants.launcherTypeResourcePath\n    val resUrl = Thread.currentThread.getContextClassLoader.getResource(path)\n    if (resUrl == null) None\n    else {\n      var is: InputStream = null\n      try {\n        is = resUrl.openStream()\n        Some(new String(resUrl.openStream().readAllBytes(), StandardCharsets.UTF_8))\n      }\n      finally\n        if (is != null)\n          is.close()\n    }\n  }\n\n  private def condaLibsodiumVersion  = Constants.condaLibsodiumVersion\n  private def alpineLibsodiumVersion = Constants.alpineLibsodiumVersion\n  private def libsodiumjniVersion    = Constants.libsodiumjniVersion\n  private def alpineVersion          = Constants.alpineVersion\n\n  private def archiveUrlAndPath() =\n    if (Properties.isLinux && launcherKindOpt.contains(\"static\"))\n      // Should actually be unused, as we statically link libsodium from the static launcher\n      // Keeping it just-in-case. This could be useful from a musl-based JVM.\n      (\n        s\"https://dl-cdn.alpinelinux.org/alpine/v$alpineVersion/main/x86_64/libsodium-$alpineLibsodiumVersion-r1.apk\",\n        os.rel / \"usr\" / \"lib\" / \"libsodium.so.23.3.0\" // FIXME Could this change?\n      )\n    else {\n      val condaPlatform = FetchExternalBinary.condaPlatform\n      // FIXME These suffixes seem to be hashes, and seem to change at every version…\n      // We'd need a way to find them automatically.\n      val suffix = condaPlatform match {\n        case \"linux-64\"      => \"-h36c2ea0_1\"\n        case \"linux-aarch64\" => \"-hb9de7d4_1\"\n        case \"osx-64\"        => \"-hbcb3906_1\"\n        case \"osx-arm64\"     => \"-h27ca646_1\"\n        case \"win-64\"        => \"-h62dcd97_1\"\n        case other           => sys.error(s\"Unrecognized conda platform $other\")\n      }\n      val relPath = condaPlatform match {\n        case \"linux-64\"      => os.rel / \"lib\" / \"libsodium.so\"\n        case \"linux-aarch64\" => os.rel / \"lib\" / \"libsodium.so\"\n        case \"osx-64\"        => os.rel / \"lib\" / \"libsodium.dylib\"\n        case \"osx-arm64\"     => os.rel / \"lib\" / \"libsodium.dylib\"\n        case \"win-64\"        => os.rel / \"Library\" / \"bin\" / \"libsodium.dll\"\n        case other           => sys.error(s\"Unrecognized conda platform $other\")\n      }\n      (\n        s\"https://anaconda.org/conda-forge/libsodium/$condaLibsodiumVersion/download/$condaPlatform/libsodium-$condaLibsodiumVersion$suffix.tar.bz2\",\n        relPath\n      )\n    }\n\n  private def jniLibArtifact(cache: Cache[Task]) = {\n\n    import dependency._\n    import scala.build.internal.Util.DependencyOps\n\n    val classifier = FetchExternalBinary.platformSuffix(supportsMusl = false)\n    val ext        =\n      if (Properties.isLinux) \"so\"\n      else if (Properties.isMac) \"dylib\"\n      else if (Properties.isWin) \"dll\"\n      else sys.error(s\"Unrecognized operating system: ${sys.props(\"os.name\")}\")\n\n    val dep =\n      dep\"org.virtuslab.scala-cli:libsodiumjni:$libsodiumjniVersion,intransitive,classifier=$classifier,ext=$ext,type=$ext\"\n    val fetch = coursier.Fetch()\n      .addDependencies(dep.toCs)\n      .addArtifactTypes(Type(ext))\n    val files = cache.loggerOpt.getOrElse(CacheLogger.nop).use {\n      try fetch.run()\n      catch {\n        case NonFatal(e) =>\n          throw new Exception(e)\n      }\n    }\n    files match {\n      case Seq()     => sys.error(s\"Cannot find $dep\")\n      case Seq(file) => file\n      case other     => sys.error(s\"Unexpectedly got too many files while resolving $dep: $other\")\n    }\n  }\n\n  def init(\n    cache: Cache[Task],\n    archiveCache: ArchiveCache[Task],\n    logger: Logger\n  ): Either[BuildException, Unit] = either {\n\n    val allStaticallyLinked =\n      Properties.isWin && isGraalvmNativeImage ||\n      Properties.isLinux && launcherKindOpt.contains(\"static\")\n\n    if (!allStaticallyLinked) {\n      val (archiveUrl, pathInArchive) = archiveUrlAndPath()\n      val sodiumLibOpt                = value {\n        FetchExternalBinary.fetchLauncher(\n          archiveUrl,\n          changing = false,\n          archiveCache,\n          logger,\n          \"\",\n          launcherPathOpt = Some(pathInArchive),\n          makeExecutable = false\n        )\n      }\n\n      val f = jniLibArtifact(cache)\n\n      sodiumLibOpt match {\n        case Some(sodiumLib) =>\n          System.load(sodiumLib.toString)\n        case None =>\n          val allow = EnvVar.ScalaCli.allowSodiumJni.valueOpt\n            .map(_.toLowerCase(Locale.ROOT))\n            .forall {\n              case \"false\" | \"0\" => false\n              case _             => true\n            }\n          if (allow)\n            System.loadLibrary(\"sodium\")\n          else\n            value(Left(new LibSodiumNotFound(archiveUrl)))\n      }\n\n      libsodiumjni.internal.LoadLibrary.initialize(f.toString)\n    }\n\n    libsodiumjni.Sodium.init()\n  }\n\n  final class LibSodiumNotFound(url: String) extends BuildException(s\"libsodium: $url not found\")\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/SecretCreate.scala",
    "content": "package scala.cli.commands.github\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.HelpFormat\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport coursier.cache.ArchiveCache\nimport sttp.client3.*\n\nimport java.nio.charset.StandardCharsets\nimport java.util.Base64\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.util.ScalaCliSttpBackend\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.config.{PasswordOption, Secret}\nimport scala.cli.errors.GitHubApiError\nimport scala.cli.util.ArgHelpers.*\n\nobject SecretCreate extends ScalaCommand[SecretCreateOptions] {\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL\n  override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Secret)\n  override def names                  = List(\n    List(\"github\", \"secret\", \"create\"),\n    List(\"gh\", \"secret\", \"create\")\n  )\n\n  private def parseSecretKv(input: String): (String, Secret[String]) =\n    input.split(\"=\", 2) match {\n      case Array(name, value) =>\n        PasswordOption.parse(value) match {\n          case Left(err)  => sys.error(s\"Error parsing secret: $err\")\n          case Right(opt) => name -> opt.get()\n        }\n      case _ =>\n        sys.error(\n          s\"Malformed secret '$input' (expected name=password, with password either file:path, command:command, or value:value)\"\n        )\n    }\n\n  def publicKey(\n    repoOrg: String,\n    repoName: String,\n    token: Secret[String],\n    backend: SttpBackend[Identity, Any],\n    logger: Logger\n  ): Either[GitHubApiError, GitHubApi.PublicKey] = either {\n    // https://docs.github.com/en/rest/reference/actions#get-a-repository-public-key\n    val publicKeyResp = basicRequest\n      .get(uri\"https://api.github.com/repos/$repoOrg/$repoName/actions/secrets/public-key\")\n      .header(\"Authorization\", s\"token ${token.value}\")\n      .header(\"Accept\", \"application/vnd.github.v3+json\")\n      .send(backend)\n\n    if (publicKeyResp.code.code != 200)\n      value(Left(new GitHubApiError(\n        s\"Error getting public key (code ${publicKeyResp.code}) for $repoOrg/$repoName\"\n      )))\n\n    val publicKeyRespBody = publicKeyResp.body match {\n      case Left(_) =>\n        // should not happen if response code is 200?\n        value(Left(new GitHubApiError(\n          s\"Unexpected missing body in response when listing secrets of $repoOrg/$repoName\"\n        )))\n      case Right(value) => value\n    }\n\n    logger.debug(s\"Public key: $publicKeyRespBody\")\n\n    readFromString(publicKeyRespBody)(using GitHubApi.publicKeyCodec)\n  }\n\n  def createOrUpdate(\n    repoOrg: String,\n    repoName: String,\n    token: Secret[String],\n    secretName: String,\n    secretValue: Secret[String],\n    pubKey: GitHubApi.PublicKey,\n    dummy: Boolean,\n    printRequest: Boolean,\n    backend: SttpBackend[Identity, Any],\n    logger: Logger\n  ): Either[GitHubApiError, Boolean] = either {\n\n    val secretBytes = secretValue.value.getBytes(StandardCharsets.UTF_8)\n\n    val encryptedValue = libsodiumjni.Sodium.seal(secretBytes, pubKey.decodedKey)\n\n    val content = GitHubApi.EncryptedSecret(\n      encrypted_value = Base64.getEncoder().encodeToString(encryptedValue),\n      key_id = pubKey.key_id\n    )\n\n    // https://docs.github.com/en/rest/reference/actions#create-or-update-a-repository-secret\n    val uri =\n      uri\"https://api.github.com/repos/$repoOrg/$repoName/actions/secrets/$secretName\"\n    val requestBody = writeToArray(content)(using GitHubApi.encryptedSecretCodec)\n\n    if (printRequest)\n      System.out.write(requestBody)\n\n    if (dummy) {\n      logger.debug(s\"Dummy mode - would have sent a request to $uri\")\n      logger.message(\n        s\"Dummy mode - NOT uploading secret $secretName to $repoOrg/$repoName\"\n      )\n      false\n    }\n    else {\n      val r = basicRequest\n        .put(uri)\n        .header(\"Authorization\", s\"token ${token.value}\")\n        .header(\"Accept\", \"application/vnd.github.v3+json\")\n        .body(requestBody)\n        .send(backend)\n\n      r.code.code match {\n        case 201 =>\n          logger.message(s\"  created $secretName\")\n          true\n        case 204 =>\n          logger.message(s\"  updated $secretName\")\n          false\n        case code =>\n          value(Left(new GitHubApiError(\n            s\"Unexpected status code $code in response when creating secret $secretName in $repoOrg/$repoName\"\n          )))\n      }\n    }\n  }\n\n  override def runCommand(\n    options: SecretCreateOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    val secrets = args.all.map(parseSecretKv)\n\n    val backend = ScalaCliSttpBackend.httpURLConnection(logger)\n\n    val pubKey = options.publicKey.filter(_.trim.nonEmpty) match {\n      case Some(path) =>\n        val content = os.read.bytes(os.Path(path, os.pwd))\n        readFromArray(content)(using GitHubApi.publicKeyCodec)\n      case None =>\n        publicKey(\n          options.shared.repoOrg,\n          options.shared.repoName,\n          options.shared.token.get().toConfig,\n          backend,\n          logger\n        ).orExit(logger)\n    }\n\n    val cache        = options.coursier.coursierCache(logger)\n    val archiveCache = ArchiveCache().withCache(cache)\n\n    LibSodiumJni.init(cache, archiveCache, logger)\n\n    for ((name, secretValue) <- secrets) {\n\n      logger.debug(s\"Secret name: $name\")\n\n      createOrUpdate(\n        options.shared.repoOrg,\n        options.shared.repoName,\n        options.shared.token.get().toConfig,\n        name,\n        secretValue,\n        pubKey,\n        options.dummy,\n        options.printRequest,\n        backend,\n        logger\n      ).orExit(logger)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/SecretCreateOptions.scala",
    "content": "package scala.cli.commands.github\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.progName\nimport scala.cli.commands.shared.{CoursierOptions, HelpGroup}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(\n  s\"\"\"Creates or updates a GitHub repository secret.\n    |  ${Console.BOLD}$progName --power github secret create --repo repo-org/repo-name SECRET_VALUE=value:secret${Console.RESET}\"\"\".stripMargin\n)\nfinal case class SecretCreateOptions(\n  @Recurse\n    shared: SharedSecretOptions = SharedSecretOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Group(HelpGroup.Secret.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @ExtraName(\"pubKey\")\n    publicKey: Option[String] = None,\n  @Tag(tags.implementation)\n  @ExtraName(\"n\")\n    dummy: Boolean = false,\n  @Hidden\n  @Tag(tags.implementation)\n  @Group(HelpGroup.Secret.toString)\n    printRequest: Boolean = false\n) extends HasSharedSecretOptions\n// format: on\n\nobject SecretCreateOptions {\n  implicit lazy val parser: Parser[SecretCreateOptions] = Parser.derive\n  implicit lazy val help: Help[SecretCreateOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/SecretList.scala",
    "content": "package scala.cli.commands.github\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.HelpFormat\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport sttp.client3.*\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.util.ScalaCliSttpBackend\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.config.Secret\nimport scala.cli.errors.GitHubApiError\nimport scala.cli.util.ArgHelpers.*\n\nobject SecretList extends ScalaCommand[SecretListOptions] {\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL\n\n  override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Secret)\n  override def names                  = List(\n    List(\"github\", \"secret\", \"list\"),\n    List(\"gh\", \"secret\", \"list\")\n  )\n\n  def list(\n    repoOrg: String,\n    repoName: String,\n    token: Secret[String],\n    backend: SttpBackend[Identity, Any],\n    logger: Logger\n  ): Either[GitHubApiError, GitHubApi.SecretList] = either {\n    // https://docs.github.com/en/rest/reference/actions#list-repository-secrets\n    val uri = uri\"https://api.github.com/repos/$repoOrg/$repoName/actions/secrets\"\n    logger.debug(s\"Listing secrets: attempting request: $uri\")\n    val r = basicRequest\n      .get(uri)\n      .header(\"Authorization\", s\"token ${token.value}\")\n      .header(\"Accept\", \"application/vnd.github.v3+json\")\n      .send(backend)\n\n    if r.code.code != 200 then\n      value {\n        Left(new GitHubApiError(\n          s\"Unexpected status code ${r.code.code} in response when listing secrets of $repoOrg/$repoName\"\n        ))\n      }\n    else logger.debug(\"Listing secrets: request successful\")\n\n    // FIXME Paging\n\n    val body = r.body match {\n      case Left(_) =>\n        // should not happen if response code is 200?\n        value(Left(new GitHubApiError(\n          s\"Unexpected missing body in response when listing secrets of $repoOrg/$repoName\"\n        )))\n      case Right(value) => value\n    }\n    readFromString(body)(using GitHubApi.secretListCodec)\n  }\n\n  override def runCommand(\n    options: SecretListOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    val backend = ScalaCliSttpBackend.httpURLConnection(logger)\n\n    val list0 = list(\n      options.shared.repoOrg,\n      options.shared.repoName,\n      options.shared.token.get().toConfig,\n      backend,\n      logger\n    ).orExit(logger)\n\n    // FIXME Paging\n\n    System.err.println(s\"Found ${list0.total_count} secret(s)\")\n    for (s <- list0.secrets)\n      println(s.name)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/SecretListOptions.scala",
    "content": "package scala.cli.commands.github\n\nimport caseapp.*\n\n// format: off\n@HelpMessage(\"Lists secrets for a given GitHub repository.\")\nfinal case class SecretListOptions(\n  @Recurse\n    shared: SharedSecretOptions\n) extends HasSharedSecretOptions\n// format: on\n\nobject SecretListOptions {\n  implicit lazy val parser: Parser[SecretListOptions] = Parser.derive\n  implicit lazy val help: Help[SecretListOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/github/SharedSecretOptions.scala",
    "content": "package scala.cli.commands.github\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup}\nimport scala.cli.commands.tags\nimport scala.cli.signing.shared.{PasswordOption, Secret}\nimport scala.cli.signing.util.ArgParsers.*\n\n// format: off\nfinal case class SharedSecretOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Group(HelpGroup.Secret.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    token: PasswordOption = PasswordOption.Value(Secret(\"\")),\n  @ExtraName(\"repo\")\n  @Group(HelpGroup.Secret.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    repository: String = \"\"\n) extends HasGlobalOptions {\n  // format: on\n\n  lazy val (repoOrg, repoName) =\n    repository.split('/') match {\n      case Array(org, name) => (org, name)\n      case _                =>\n        sys.error(s\"Malformed repository: '$repository' (expected 'org/name')\")\n    }\n}\n\nobject SharedSecretOptions {\n  implicit lazy val parser: Parser[SharedSecretOptions] = Parser.derive\n  implicit lazy val help: Help[SharedSecretOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/installcompletions/InstallCompletions.scala",
    "content": "package scala.cli.commands.installcompletions\n\nimport caseapp.*\nimport caseapp.core.complete.{Bash, Fish, Zsh}\nimport caseapp.core.help.HelpFormat\n\nimport java.nio.charset.Charset\nimport java.nio.file.Paths\nimport java.util\n\nimport scala.build.internals.EnvVar\nimport scala.build.{Directories, Logger}\nimport scala.cli.ScalaCli\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.internal.ProfileFileUpdater\nimport scala.cli.util.ArgHelpers.*\nobject InstallCompletions extends ScalaCommand[InstallCompletionsOptions] {\n  override def names = List(\n    List(\"install\", \"completions\"),\n    List(\"install-completions\")\n  )\n\n  override def helpFormat: HelpFormat =\n    super.helpFormat.withPrimaryGroup(HelpGroup.Install)\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def runCommand(\n    options: InstallCompletionsOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    val interactive         = options.global.logging.verbosityOptions.interactiveInstance()\n    lazy val completionsDir =\n      options.output\n        .map(os.Path(_, os.pwd))\n        .getOrElse(Directories.directories.completionsDir)\n\n    val name   = getName(options.name)\n    val format = getFormat(options.format).getOrElse {\n      val msg = \"Cannot determine current shell. Which would you like to use?\"\n      interactive.chooseOne(msg, List(\"zsh\", \"bash\")).getOrElse {\n        System.err.println(\n          \"Cannot determine current shell, pass the shell you use with --shell, like\"\n        )\n        System.err.println(s\"$name install completions --shell zsh\")\n        System.err.println(s\"$name install completions --shell bash\")\n        System.err.println(s\"$name install completions --shell fish\")\n        sys.exit(1)\n      }\n    }\n\n    val (rcScript, defaultRcFile) = format match {\n      case Bash.id | \"bash\" =>\n        val script        = Bash.script(name)\n        val defaultRcFile = os.home / \".bashrc\"\n        (script, defaultRcFile)\n      case Zsh.id | \"zsh\" =>\n        val completionScript = Zsh.script(name)\n        val zDotDir          = EnvVar.Misc.zDotDir.valueOpt\n          .map(os.Path(_, os.pwd))\n          .getOrElse(os.home)\n        val defaultRcFile        = zDotDir / \".zshrc\"\n        val dir                  = completionsDir / \"zsh\"\n        val completionScriptDest = dir / s\"_$name\"\n        val content              = completionScript.getBytes(Charset.defaultCharset())\n        val needsWrite           = !os.exists(completionScriptDest) ||\n          !util.Arrays.equals(os.read.bytes(completionScriptDest), content)\n        if (needsWrite) {\n          logger.log(s\"Writing $completionScriptDest\")\n          os.write.over(completionScriptDest, content, createFolders = true)\n        }\n        val script = Seq(\n          s\"\"\"fpath=(\"$dir\" $$fpath)\"\"\",\n          \"compinit\"\n        ).map(_ + System.lineSeparator()).mkString\n        (script, defaultRcFile)\n      case Fish.id | \"fish\" =>\n        val script        = Fish.script(name)\n        val defaultRcFile = os.home / \".config\" / \"fish\" / \"config.fish\"\n        (script, defaultRcFile)\n      case _ =>\n        System.err.println(s\"Unrecognized or unsupported shell: $format\")\n        sys.exit(1)\n    }\n\n    if (options.env)\n      println(rcScript)\n    else {\n      val rcFile  = options.rcFile.map(os.Path(_, os.pwd)).getOrElse(defaultRcFile)\n      val banner  = options.banner.replace(\"{NAME}\", name)\n      val updated = ProfileFileUpdater.addToProfileFile(\n        rcFile.toNIO,\n        banner,\n        rcScript,\n        Charset.defaultCharset()\n      )\n\n      if (options.global.logging.verbosity >= 0)\n        if (updated) {\n          System.err.println(s\"Updated $rcFile\")\n          System.err.println(\n            s\"It is recommended to reload your shell, or source $rcFile in the \" +\n              \"current session, for its changes to be taken into account.\"\n          )\n        }\n        else\n          System.err.println(s\"$rcFile already up-to-date\")\n    }\n  }\n\n  def getName(name: Option[String]): String =\n    name.getOrElse {\n      val progName = ScalaCli.progName\n      Paths.get(progName).getFileName.toString\n    }\n\n  def getFormat(format: Option[String]): Option[String] =\n    format.map(_.trim).filter(_.nonEmpty)\n      .orElse {\n        EnvVar.Misc.shell.valueOpt.map(_.split(\"[\\\\/]+\").last).map {\n          case \"bash\" => Bash.id\n          case \"zsh\"  => Zsh.id\n          case \"fish\" => Fish.id\n          case other  => other\n        }\n      }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/installcompletions/InstallCompletionsOptions.scala",
    "content": "package scala.cli.commands.installcompletions\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup, HelpMessages}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(InstallCompletionsOptions.helpMessage, \"\", InstallCompletionsOptions.detailedHelpMessage)\nfinal case class InstallCompletionsOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Group(HelpGroup.Install.toString)\n  @Name(\"shell\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Name of the shell, either zsh or bash\")\n    format: Option[String] = None,\n\n  @Tag(tags.implementation)\n  @Group(HelpGroup.Install.toString)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Path to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\")\n  rcFile: Option[String] = None,\n\n  @Tag(tags.implementation)\n  @HelpMessage(\"Completions output directory\")\n  @Group(HelpGroup.Install.toString)\n  @Name(\"o\")\n  output: Option[String] = None,\n\n  @Hidden\n  @Tag(tags.implementation)\n  @HelpMessage(\"Custom banner in comment placed in rc file\")\n  @Group(HelpGroup.Install.toString)\n  banner: String = \"{NAME} completions\",\n\n  @Hidden\n  @Tag(tags.implementation)\n  @HelpMessage(\"Custom completions name\")\n  @Group(HelpGroup.Install.toString)\n  name: Option[String] = None,\n\n  @Tag(tags.implementation)\n  @HelpMessage(\"Print completions to stdout\")\n  @Group(HelpGroup.Install.toString)\n    env: Boolean = false,\n) extends HasGlobalOptions\n// format: on\n\nobject InstallCompletionsOptions {\n  implicit lazy val parser: Parser[InstallCompletionsOptions] = Parser.derive\n  implicit lazy val help: Help[InstallCompletionsOptions]     = Help.derive\n\n  private val helpHeader  = s\"Installs $fullRunnerName completions into your shell\"\n  val helpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(\"install completions\")}\n       |${HelpMessages.commandDocWebsiteReference(\"completions\")}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandDocWebsiteReference(\"completions\")}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/installhome/InstallHome.scala",
    "content": "package scala.cli.commands.installhome\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport coursier.env.{EnvironmentUpdate, ProfileUpdater}\n\nimport scala.build.Logger\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{\n  CommandUtils,\n  CustomWindowsEnvVarUpdater,\n  ScalaCommand,\n  SpecificationLevel\n}\nimport scala.cli.util.ArgHelpers.*\nimport scala.io.StdIn.readLine\nimport scala.util.Properties\n\nobject InstallHome extends ScalaCommand[InstallHomeOptions] {\n  override def hidden: Boolean                             = true\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def helpFormat: HelpFormat =\n    super.helpFormat.withPrimaryGroup(HelpGroup.Install)\n\n  private def logEqual(version: String, logger: Logger) = {\n    logger.message(s\"$fullRunnerName $version is already installed and up-to-date.\")\n    sys.exit(0)\n  }\n\n  private def logUpdate(\n    env: Boolean,\n    newVersion: String,\n    oldVersion: String,\n    logger: Logger\n  ): Unit =\n    if (!env) logger.message(\n      s\"\"\"$baseRunnerName $oldVersion is already installed and out-of-date.\n         |$baseRunnerName will be updated to version $newVersion\n         |\"\"\".stripMargin\n    )\n\n  private def logDowngrade(\n    env: Boolean,\n    newVersion: String,\n    oldVersion: String,\n    logger: Logger\n  ): Unit =\n    if (!env && coursier.paths.Util.useAnsiOutput()) {\n      logger.message(\n        s\"$baseRunnerName $oldVersion is already installed and up-to-date.\"\n      )\n      logger.error(\n        s\"Do you want to downgrade $baseRunnerName to version $newVersion [Y/n]\"\n      )\n      val response = readLine()\n      if (response != \"Y\") {\n        logger.message(\"Abort\")\n        sys.exit(1)\n      }\n    }\n    else {\n      logger.error(\n        s\"Error: $baseRunnerName is already installed $oldVersion and up-to-date. Downgrade to $newVersion pass -f or --force.\"\n      )\n      sys.exit(1)\n    }\n\n  override def runCommand(\n    options: InstallHomeOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    val binDirPath =\n      options.binDirPath.getOrElse(\n        scala.build.Directories.default().binRepoDir / baseRunnerName\n      )\n    val destBinPath = binDirPath / options.binaryName\n\n    val newScalaCliBinPath = os.Path(options.scalaCliBinaryPath, os.pwd)\n\n    val newVersion: String =\n      os.proc(newScalaCliBinPath, \"version\", \"--cli-version\").call(cwd = os.pwd).out.trim()\n\n    // Backward compatibility - previous versions not have the `--version` parameter\n    val oldVersion: String =\n      if (os.isFile(destBinPath)) {\n        val res = os.proc(destBinPath, \"version\", \"--cli-version\").call(cwd = os.pwd, check = false)\n        if (res.exitCode == 0)\n          res.out.trim()\n        else\n          \"0.0.0\"\n      }\n      else\n        \"0.0.0\"\n\n    if (os.exists(binDirPath))\n      if (options.force) () // skip logging\n      else if (newVersion == oldVersion) logEqual(newVersion, logger)\n      else if (CommandUtils.isOutOfDateVersion(newVersion, oldVersion))\n        logUpdate(options.env, newVersion, oldVersion, logger)\n      else logDowngrade(options.env, newVersion, oldVersion, logger)\n\n    if (os.exists(destBinPath)) os.remove(destBinPath)\n\n    os.copy(\n      from = newScalaCliBinPath,\n      to = destBinPath,\n      createFolders = true\n    )\n    if (!Properties.isWin)\n      os.perms.set(destBinPath, os.PermSet.fromString(\"rwxr-xr-x\"))\n\n    if (options.env)\n      println(s\"\"\"export PATH=\"$binDirPath:$$PATH\"\"\"\")\n    else {\n\n      val update = EnvironmentUpdate(Nil, Seq(\"PATH\" -> binDirPath.toString()))\n\n      val didUpdate =\n        if (Properties.isWin) {\n          val updater = CustomWindowsEnvVarUpdater().withUseJni(Some(coursier.paths.Util.useJni()))\n          updater.applyUpdate(update)\n        }\n        else {\n          val updater = ProfileUpdater()\n          updater.applyUpdate(update)\n        }\n\n      println(s\"Successfully installed $baseRunnerName $newVersion\")\n\n      if (didUpdate) {\n        if (Properties.isLinux)\n          println(\n            s\"\"\"|Profile file(s) updated.\n                |To run $baseRunnerName, log out and log back in, or run 'source ~/.profile'\"\"\".stripMargin\n          )\n        if (Properties.isMac)\n          println(\n            s\"To run $baseRunnerName, open new terminal or run 'source ~/.profile'\"\n          )\n      }\n\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/installhome/InstallHomeOptions.scala",
    "content": "package scala.cli.commands.installhome\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.{baseRunnerName, fullRunnerName}\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup}\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(s\"Install $fullRunnerName in a sub-directory of the home directory\")\nfinal case class InstallHomeOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Group(HelpGroup.Install.toString)\n  @Tag(tags.implementation)\n    scalaCliBinaryPath: String,\n  @Group(HelpGroup.Install.toString)\n  @Name(\"f\")\n  @Tag(tags.implementation)\n  @HelpMessage(\"Overwrite if it exists\")\n    force: Boolean = false,\n  @Group(HelpGroup.Install.toString)\n  @Hidden\n  @Tag(tags.implementation)\n  @HelpMessage(\"Binary name\")\n    binaryName: String = baseRunnerName,\n  @Group(HelpGroup.Install.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Print the update to `env` variable\")\n    env: Boolean = false,\n  @Group(HelpGroup.Install.toString)\n  @Hidden\n  @Tag(tags.implementation)\n  @HelpMessage(\"Binary directory\")\n    binDir: Option[String] = None\n) extends HasGlobalOptions {\n  // format: on\n  lazy val binDirPath = binDir.map(os.Path(_, os.pwd))\n}\n\nobject InstallHomeOptions {\n  implicit lazy val parser: Parser[InstallHomeOptions] = Parser.derive\n  implicit lazy val help: Help[InstallHomeOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/new/New.scala",
    "content": "package scala.cli.commands.`new`\n\nimport caseapp.core.RemainingArgs\nimport dependency.*\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.internal.Util.safeFullDetailedArtifacts\nimport scala.build.internal.{Constants, OsLibc, Runner}\nimport scala.build.options.{BuildOptions, JavaOptions}\nimport scala.build.{Artifacts, Logger, Positioned}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.{CoursierOptions, HelpCommandGroup}\n\nobject New extends ScalaCommand[NewOptions] {\n  override def group: String = HelpCommandGroup.Main.toString\n\n  override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL\n\n  private def giter8Dependency =\n    Seq(dep\"${Constants.giter8Organization}::${Constants.giter8Name}:${Constants.giter8Version}\")\n\n  override def runCommand(options: NewOptions, remainingArgs: RemainingArgs, logger: Logger): Unit =\n    either {\n      val scalaParameters = ScalaParameters(Constants.defaultScala213Version)\n      val fetchedGiter8   = Artifacts.fetchAnyDependencies(\n        giter8Dependency.map(Positioned.none),\n        Seq.empty,\n        Some(scalaParameters),\n        logger,\n        CoursierOptions().coursierCache(logger),\n        None\n      ) match {\n        case Right(value) => value\n        case Left(value)  =>\n          System.err.println(value.message)\n          sys.exit(1)\n      }\n\n      val giter8 = value(fetchedGiter8.fullDetailedArtifacts0.safeFullDetailedArtifacts)\n        .collect { case (_, _, _, Some(f)) => os.Path(f, os.pwd) }\n\n      val buildOptions = BuildOptions(\n        javaOptions = JavaOptions(\n          jvmIdOpt = Some(OsLibc.defaultJvm(OsLibc.jvmIndexOs)).map(Positioned.none)\n        )\n      )\n\n      val exitCode =\n        Runner.runJvm(\n          buildOptions.javaHome().value.javaCommand,\n          buildOptions.javaOptions.javaOpts.toSeq.map(_.value.value),\n          giter8,\n          \"giter8.Giter8\",\n          remainingArgs.remaining,\n          logger,\n          allowExecve = true\n        ).waitFor()\n\n      sys.exit(exitCode)\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/new/NewOptions.scala",
    "content": "package scala.cli.commands.`new`\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.*\n\n@HelpMessage(NewOptions.newMessage, \"\", NewOptions.detailedNewMessage)\nfinal case class NewOptions(\n  @Recurse\n  global: GlobalOptions = GlobalOptions()\n) extends HasGlobalOptions\n\nobject NewOptions {\n  implicit lazy val parser: Parser[NewOptions] = Parser.derive\n  implicit lazy val help: Help[NewOptions]     = Help.derive\n  val cmdName                                  = \"new\"\n  private val newHeader                        = \"New giter8 template.\"\n  val newMessage: String                       = HelpMessages.shortHelpMessage(cmdName, newHeader)\n\n  val detailedNewMessage: String =\n    s\"\"\"$newHeader\n       |\n       | Creates a new project from a giter8 template.\n       |\n       |\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/package0/Package.scala",
    "content": "package scala.cli.commands.package0\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport coursier.core\nimport coursier.core.Resolution\nimport coursier.launcher.*\nimport dependency.*\nimport os.{BasePathImpl, FilePath, Path, SegmentedPath}\nimport packager.config.*\nimport packager.deb.DebianPackage\nimport packager.docker.DockerPackage\nimport packager.mac.dmg.DmgPackage\nimport packager.mac.pkg.PkgPackage\nimport packager.rpm.RedHatPackage\nimport packager.windows.WindowsPackage\n\nimport java.io.{ByteArrayOutputStream, OutputStream}\nimport java.nio.file.attribute.FileTime\nimport java.util.zip.{ZipEntry, ZipOutputStream}\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.errors.*\nimport scala.build.interactive.InteractiveFileOps\nimport scala.build.internal.Util.*\nimport scala.build.internal.resource.NativeResourceMapper\nimport scala.build.internal.{Runner, ScalaJsLinkerConfig}\nimport scala.build.options.PackageType.Native\nimport scala.build.options.{BuildOptions, JavaOpt, PackageType, Platform, ScalaNativeTarget, Scope}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.OptionsHelper.*\nimport scala.cli.commands.doc.Doc\nimport scala.cli.commands.packaging.Spark\nimport scala.cli.commands.run.Run.{createPythonInstance, orPythonDetectionError}\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, MainClassOptions, SharedOptions}\nimport scala.cli.commands.util.BuildCommandHelpers\nimport scala.cli.commands.util.BuildCommandHelpers.*\nimport scala.cli.commands.{CommandUtils, ScalaCommand, WatchUtil}\nimport scala.cli.config.Keys\nimport scala.cli.errors.ScalaJsLinkingError\nimport scala.cli.internal.{CachedBinary, Constants, ProcUtil, ScalaJsLinker}\nimport scala.cli.packaging.{Library, NativeImage}\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nimport scala.util.Properties\n\nobject Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {\n  override def name          = \"package\"\n  override def group: String = HelpCommandGroup.Main.toString\n\n  val primaryHelpGroups: Seq[HelpGroup] = Seq(\n    HelpGroup.Package,\n    HelpGroup.Scala,\n    HelpGroup.Java,\n    HelpGroup.Debian,\n    HelpGroup.MacOS,\n    HelpGroup.RedHat,\n    HelpGroup.Windows,\n    HelpGroup.Docker,\n    HelpGroup.NativeImage\n  )\n  val hiddenHelpGroups: Seq[HelpGroup] = Seq(HelpGroup.Entrypoint, HelpGroup.Watch)\n  override def helpFormat: HelpFormat  = super.helpFormat\n    .withHiddenGroups(hiddenHelpGroups)\n    .withPrimaryGroups(primaryHelpGroups)\n  override def sharedOptions(options: PackageOptions): Option[SharedOptions] = Some(options.shared)\n  override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED\n  override def buildOptions(options: PackageOptions): Option[BuildOptions] =\n    Some(options.baseBuildOptions(options.shared.logger).orExit(options.shared.logger))\n  override def runCommand(options: PackageOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val inputs = options.shared.inputs(args.remaining).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n\n    // FIXME mainClass encoding has issues with special chars, such as '-'\n\n    val initialBuildOptions = finalBuildOptions(options)\n    val threads             = BuildThreads.create()\n    val compilerMaker       = options.compilerMaker(threads).orExit(logger)\n    val docCompilerMakerOpt = options.docCompilerMakerOpt\n\n    val cross                 = options.compileCross.cross.getOrElse(false)\n    val configDb              = ConfigDbUtils.configDb.orExit(logger)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions.orElse(\n        configDb.get(Keys.actions).getOrElse(None)\n      )\n\n    val withTestScope = options.shared.scope.test.getOrElse(false)\n    if options.watch.watchMode then {\n      var expectedModifyEpochSecondOpt = Option.empty[Long]\n      val watcher                      = Build.watch(\n        inputs,\n        initialBuildOptions,\n        compilerMaker,\n        docCompilerMakerOpt,\n        logger,\n        crossBuilds = cross,\n        buildTests = withTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics,\n        postAction = () => WatchUtil.printWatchMessage()\n      ) { res =>\n        res.orReport(logger).map(_.all).foreach {\n          case b if b.forall(_.success) =>\n            val successfulBuilds = b.collect { case s: Build.Successful => s }\n            successfulBuilds.foreach(_.copyOutput(options.shared))\n            val mtimeDestPath = doPackageCrossBuilds(\n              logger = logger,\n              outputOpt = options.output.filter(_.nonEmpty),\n              force = options.force,\n              forcedPackageTypeOpt = options.forcedPackageTypeOpt,\n              allBuilds = successfulBuilds,\n              extraArgs = args.unparsed,\n              expectedModifyEpochSecondOpt = expectedModifyEpochSecondOpt,\n              allowTerminate = !options.watch.watchMode,\n              mainClassOptions = options.mainClass,\n              withTestScope = withTestScope\n            )\n              .orReport(logger)\n            for (valueOpt <- mtimeDestPath)\n              expectedModifyEpochSecondOpt = valueOpt\n          case b if b.exists(bb => !bb.success && !bb.cancelled) =>\n            System.err.println(\"Compilation failed\")\n          case _ => System.err.println(\"Build cancelled\")\n        }\n      }\n      try WatchUtil.waitForCtrlC(() => watcher.schedule())\n      finally watcher.dispose()\n    }\n    else\n      Build.build(\n        inputs,\n        initialBuildOptions,\n        compilerMaker,\n        docCompilerMakerOpt,\n        logger,\n        crossBuilds = cross,\n        buildTests = withTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics\n      )\n        .orExit(logger)\n        .all match {\n        case b if b.forall(_.success) =>\n          val successfulBuilds = b.collect { case s: Build.Successful => s }\n          successfulBuilds.foreach(_.copyOutput(options.shared))\n          val res0 = doPackageCrossBuilds(\n            logger = logger,\n            outputOpt = options.output.filter(_.nonEmpty),\n            force = options.force,\n            forcedPackageTypeOpt = options.forcedPackageTypeOpt,\n            allBuilds = successfulBuilds,\n            extraArgs = args.unparsed,\n            expectedModifyEpochSecondOpt = None,\n            allowTerminate = !options.watch.watchMode,\n            mainClassOptions = options.mainClass,\n            withTestScope = withTestScope\n          )\n          res0.orExit(logger)\n        case b if b.exists(bb => !bb.success && !bb.cancelled) =>\n          System.err.println(\"Compilation failed\")\n          sys.exit(1)\n        case _ =>\n          System.err.println(\"Build cancelled\")\n          sys.exit(1)\n      }\n  }\n\n  def finalBuildOptions(options: PackageOptions): BuildOptions = {\n    val initialOptions =\n      options.finalBuildOptions(options.shared.logger).orExit(options.shared.logger)\n    val finalBuildOptions = initialOptions.copy(scalaOptions =\n      initialOptions.scalaOptions.copy(defaultScalaVersion = Some(defaultScalaVersion))\n    )\n    val buildOptions = finalBuildOptions.copy(\n      javaOptions = finalBuildOptions.javaOptions.copy(\n        javaOpts =\n          finalBuildOptions.javaOptions.javaOpts ++\n            options.java.allJavaOpts.map(JavaOpt(_)).map(Positioned.commandLine)\n      )\n    )\n    buildOptions\n  }\n\n  private def insertSuffixBeforeExtension(name: String, suffix: String): String =\n    if suffix.isEmpty then name\n    else {\n      val dotIdx = name.lastIndexOf('.')\n      if dotIdx > 0 then name.substring(0, dotIdx) + suffix + name.substring(dotIdx)\n      else name + suffix\n    }\n\n  private def doPackageCrossBuilds(\n    logger: Logger,\n    outputOpt: Option[String],\n    force: Boolean,\n    forcedPackageTypeOpt: Option[PackageType],\n    allBuilds: Seq[Build.Successful],\n    extraArgs: Seq[String],\n    expectedModifyEpochSecondOpt: Option[Long],\n    allowTerminate: Boolean,\n    mainClassOptions: MainClassOptions,\n    withTestScope: Boolean\n  ): Either[BuildException, Option[Long]] = either {\n    val crossBuildGroups    = allBuilds.groupedByCrossParams.toSeq\n    val multipleCrossGroups = crossBuildGroups.size > 1\n\n    if multipleCrossGroups then\n      logger.message(s\"Packaging ${crossBuildGroups.size} cross builds...\")\n\n    val platforms             = crossBuildGroups.map(_._1.platform).distinct\n    val needsPlatformInSuffix = platforms.size > 1\n\n    val results = value {\n      crossBuildGroups.map { (crossParams, builds) =>\n        val crossSuffix =\n          if multipleCrossGroups then {\n            val versionPart = s\"_${crossParams.scalaVersion}\"\n            if needsPlatformInSuffix then s\"${versionPart}_${crossParams.platform}\"\n            else versionPart\n          }\n          else \"\"\n\n        if multipleCrossGroups then\n          logger.message(s\"Packaging for ${crossParams.asString}...\")\n\n        doPackage(\n          logger = logger,\n          outputOpt = outputOpt,\n          force = force,\n          forcedPackageTypeOpt = forcedPackageTypeOpt,\n          builds = builds,\n          extraArgs = extraArgs,\n          expectedModifyEpochSecondOpt = expectedModifyEpochSecondOpt,\n          allowTerminate = allowTerminate,\n          mainClassOptions = mainClassOptions,\n          withTestScope = withTestScope,\n          crossSuffix = crossSuffix\n        )\n      }\n        .sequence\n        .left.map(CompositeBuildException(_))\n    }\n\n    results.lastOption.flatten\n  }\n\n  private def doPackage(\n    logger: Logger,\n    outputOpt: Option[String],\n    force: Boolean,\n    forcedPackageTypeOpt: Option[PackageType],\n    builds: Seq[Build.Successful],\n    extraArgs: Seq[String],\n    expectedModifyEpochSecondOpt: Option[Long],\n    allowTerminate: Boolean,\n    mainClassOptions: MainClassOptions,\n    withTestScope: Boolean,\n    crossSuffix: String\n  ): Either[BuildException, Option[Long]] = either {\n    if mainClassOptions.mainClassLs.contains(true) then\n      value {\n        mainClassOptions\n          .maybePrintMainClasses(\n            builds.flatMap(_.foundMainClasses()).distinct,\n            shouldExit = allowTerminate\n          )\n          .map(_ => None)\n      }\n    else {\n      val packageType: PackageType = value(resolvePackageType(builds, forcedPackageTypeOpt))\n      // TODO When possible, call alreadyExistsCheck() before compiling stuff\n\n      def extension = packageType match {\n        case PackageType.LibraryJar  => \".jar\"\n        case PackageType.SourceJar   => \".jar\"\n        case PackageType.DocJar      => \".jar\"\n        case _: PackageType.Assembly => \".jar\"\n        case PackageType.Spark       => \".jar\"\n        case PackageType.Js          => \".js\"\n        case PackageType.Debian      => \".deb\"\n        case PackageType.Dmg         => \".dmg\"\n        case PackageType.Pkg         => \".pkg\"\n        case PackageType.Rpm         => \".rpm\"\n        case PackageType.Msi         => \".msi\"\n\n        case PackageType.Native.Application =>\n          if Properties.isWin then \".exe\" else \"\"\n        case PackageType.Native.LibraryDynamic =>\n          if Properties.isWin then \".dll\" else if Properties.isMac then \".dylib\" else \".so\"\n        case PackageType.Native.LibraryStatic =>\n          if Properties.isWin then \".lib\" else \".a\"\n\n        case PackageType.GraalVMNativeImage if Properties.isWin => \".exe\"\n        case _ if Properties.isWin                              => \".bat\"\n        case _                                                  => \"\"\n      }\n\n      def defaultName = packageType match {\n        case PackageType.LibraryJar  => \"library.jar\"\n        case PackageType.SourceJar   => \"source.jar\"\n        case PackageType.DocJar      => \"scaladoc.jar\"\n        case _: PackageType.Assembly => \"app.jar\"\n        case PackageType.Spark       => \"job.jar\"\n        case PackageType.Js          => \"app.js\"\n        case PackageType.Debian      => \"app.deb\"\n        case PackageType.Dmg         => \"app.dmg\"\n        case PackageType.Pkg         => \"app.pkg\"\n        case PackageType.Rpm         => \"app.rpm\"\n        case PackageType.Msi         => \"app.msi\"\n\n        case PackageType.Native.Application =>\n          if Properties.isWin then \"app.exe\" else \"app\"\n\n        case PackageType.Native.LibraryDynamic =>\n          if Properties.isWin then \"library.dll\"\n          else if Properties.isMac then \"library.dylib\"\n          else \"library.so\"\n\n        case PackageType.Native.LibraryStatic =>\n          if Properties.isWin then \"library.lib\" else \"library.a\"\n\n        case PackageType.GraalVMNativeImage if Properties.isWin => \"app.exe\"\n        case _ if Properties.isWin                              => \"app.bat\"\n        case _                                                  => \"app\"\n      }\n      val output = outputOpt.map {\n        case path\n            if packageType == PackageType.GraalVMNativeImage\n            && Properties.isWin && !path.endsWith(\".exe\") =>\n          s\"$path.exe\" // graalvm-native-image requires .exe extension on Windows\n        case path => path\n      }\n\n      val packageOutput = builds.head.options.notForBloopOptions.packageOptions.output\n      val dest          = output.orElse(packageOutput)\n        .orElse {\n          builds.flatMap(_.sources.defaultMainClass)\n            .headOption\n            .map(n => n.drop(n.lastIndexOf('.') + 1))\n            .map(_.stripSuffix(\"_sc\"))\n            .map(_ + extension)\n        }\n        .orElse {\n          builds.flatMap(_.retainedMainClass(logger).toOption)\n            .headOption\n            .map(_.stripSuffix(\"_sc\") + extension)\n        }\n        .orElse(builds.flatMap(_.sources.paths).collectFirst(_._1.baseName + extension))\n        .getOrElse(defaultName)\n      val destPath = {\n        val base = os.Path(dest, Os.pwd)\n        if crossSuffix.nonEmpty then\n          base / os.up / insertSuffixBeforeExtension(base.last, crossSuffix)\n        else base\n      }\n      val printableDest = CommandUtils.printablePath(destPath)\n\n      def alreadyExistsCheck(): Either[BuildException, Unit] =\n        if !force &&\n          os.exists(destPath) &&\n          !expectedModifyEpochSecondOpt.contains(os.mtime(destPath))\n        then\n          builds.head.options.interactive.map { interactive =>\n            InteractiveFileOps.erasingPath(interactive, printableDest, destPath) { () =>\n              val errorMsg =\n                if expectedModifyEpochSecondOpt.isEmpty then s\"$printableDest already exists\"\n                else s\"$printableDest was overwritten by another process\"\n              System.err.println(s\"Error: $errorMsg. Pass -f or --force to force erasing it.\")\n              sys.exit(1)\n            }\n          }\n        else Right(())\n\n      value(alreadyExistsCheck())\n\n      def mainClass: Either[BuildException, String] =\n        builds.head.options.mainClass.filter(_.nonEmpty) match {\n          case Some(cls) => Right(cls)\n          case None      =>\n            val potentialMainClasses = builds.flatMap(_.foundMainClasses()).distinct\n            builds\n              .map { build =>\n                build.retainedMainClass(logger, potentialMainClasses)\n                  .map(mainClass => build.scope -> mainClass)\n              }\n              .sequence\n              .left\n              .map(CompositeBuildException(_))\n              .map(_.toMap)\n              .map { retainedMainClassesByScope =>\n                if retainedMainClassesByScope.size == 1 then retainedMainClassesByScope.head._2\n                else\n                  retainedMainClassesByScope\n                    .get(Scope.Main)\n                    .orElse(retainedMainClassesByScope.get(Scope.Test))\n                    .get\n              }\n\n        }\n\n      def mainClassOpt: Option[String] = mainClass.toOption\n\n      val packageOptions = builds.head.options.notForBloopOptions.packageOptions\n\n      val outputPath = packageType match {\n        case PackageType.Bootstrap =>\n          value(bootstrap(builds, destPath, value(mainClass), () => alreadyExistsCheck(), logger))\n          destPath\n        case PackageType.LibraryJar =>\n          val libraryJar = Library.libraryJar(builds)\n          value(alreadyExistsCheck())\n          if force then os.copy.over(libraryJar, destPath, createFolders = true)\n          else os.copy(libraryJar, destPath, createFolders = true)\n          destPath\n        case PackageType.SourceJar =>\n          val now     = System.currentTimeMillis()\n          val content = sourceJar(builds, now)\n          value(alreadyExistsCheck())\n          if force then os.write.over(destPath, content, createFolders = true)\n          else os.write(destPath, content, createFolders = true)\n          destPath\n        case PackageType.DocJar =>\n          val docJarPath = value(docJar(builds, logger, extraArgs, withTestScope))\n          value(alreadyExistsCheck())\n          if force then os.copy.over(docJarPath, destPath, createFolders = true)\n          else os.copy(docJarPath, destPath, createFolders = true)\n          destPath\n        case a: PackageType.Assembly =>\n          value {\n            assembly(\n              builds = builds,\n              destPath = destPath,\n              mainClassOpt = a.mainClassInManifest match {\n                case None =>\n                  if a.addPreamble then {\n                    val clsName = value {\n                      mainClass.left.map {\n                        case e: NoMainClassFoundError =>\n                          // This one has a slightly better error message, suggesting --preamble=false\n                          new NoMainClassFoundForAssemblyError(e)\n                        case e => e\n                      }\n                    }\n                    Some(clsName)\n                  }\n                  else\n                    mainClassOpt\n                case Some(false) => None\n                case Some(true)  => Some(value(mainClass))\n              },\n              extraProvided = Nil,\n              withPreamble = a.addPreamble,\n              alreadyExistsCheck = () => alreadyExistsCheck(),\n              logger = logger\n            )\n          }\n          destPath\n        case PackageType.Spark =>\n          value {\n            assembly(\n              builds,\n              destPath,\n              mainClassOpt,\n              // The Spark modules are assumed to be already on the class path,\n              // along with all their transitive dependencies (originating from\n              // the Spark distribution), so we don't include any of them in the\n              // assembly.\n              Spark.sparkModules,\n              withPreamble = false,\n              () => alreadyExistsCheck(),\n              logger\n            )\n          }\n          destPath\n\n        case PackageType.Js =>\n          value(buildJs(builds, destPath, mainClassOpt, logger))\n\n        case tpe: PackageType.Native =>\n          import PackageType.Native.*\n          val mainClassO =\n            tpe match\n              case Application => Some(value(mainClass))\n              case _           => None\n\n          val cachedDest = value(buildNative(\n            builds = builds,\n            mainClass = mainClassO,\n            targetType = tpe,\n            destPath = Some(destPath),\n            logger = logger\n          ))\n          if force then os.copy.over(cachedDest, destPath, createFolders = true)\n          else os.copy(cachedDest, destPath, createFolders = true)\n          destPath\n\n        case PackageType.GraalVMNativeImage =>\n          NativeImage.buildNativeImage(\n            builds,\n            value(mainClass),\n            destPath,\n            builds.head.inputs.nativeImageWorkDir,\n            extraArgs,\n            logger\n          )\n          destPath\n\n        case nativePackagerType: PackageType.NativePackagerType =>\n          val bootstrapPath = os.temp.dir(prefix = \"scala-packager\") / \"app\"\n          value {\n            bootstrap(\n              builds,\n              bootstrapPath,\n              value(mainClass),\n              () => alreadyExistsCheck(),\n              logger\n            )\n          }\n          val sharedSettings = SharedSettings(\n            sourceAppPath = bootstrapPath,\n            version = packageOptions.packageVersion,\n            force = force,\n            outputPath = destPath,\n            logoPath = packageOptions.logoPath,\n            launcherApp = packageOptions.launcherApp\n          )\n\n          lazy val debianSettings = DebianSettings(\n            shared = sharedSettings,\n            maintainer = packageOptions.maintainer.mandatory(\"--maintainer\", \"debian\"),\n            description = packageOptions.description.mandatory(\"--description\", \"debian\"),\n            debianConflicts = packageOptions.debianOptions.conflicts,\n            debianDependencies = packageOptions.debianOptions.dependencies,\n            architecture = packageOptions.debianOptions.architecture.mandatory(\n              \"--deb-architecture\",\n              \"debian\"\n            ),\n            priority = packageOptions.debianOptions.priority,\n            section = packageOptions.debianOptions.section\n          )\n\n          lazy val macOSSettings = MacOSSettings(\n            shared = sharedSettings,\n            identifier =\n              packageOptions.macOSidentifier.mandatory(\"--identifier-parameter\", \"macOs\")\n          )\n\n          lazy val redHatSettings = RedHatSettings(\n            shared = sharedSettings,\n            description = packageOptions.description.mandatory(\"--description\", \"redHat\"),\n            license =\n              packageOptions.redHatOptions.license.mandatory(\"--license\", \"redHat\"),\n            release =\n              packageOptions.redHatOptions.release.mandatory(\"--release\", \"redHat\"),\n            rpmArchitecture = packageOptions.redHatOptions.architecture.mandatory(\n              \"--rpm-architecture\",\n              \"redHat\"\n            )\n          )\n\n          lazy val windowsSettings = WindowsSettings(\n            shared = sharedSettings,\n            maintainer = packageOptions.maintainer.mandatory(\"--maintainer\", \"windows\"),\n            licencePath = packageOptions.windowsOptions.licensePath.mandatory(\n              \"--licence-path\",\n              \"windows\"\n            ),\n            productName = packageOptions.windowsOptions.productName.mandatory(\n              \"--product-name\",\n              \"windows\"\n            ),\n            exitDialog = packageOptions.windowsOptions.exitDialog,\n            suppressValidation =\n              packageOptions.windowsOptions.suppressValidation.getOrElse(false),\n            extraConfigs = packageOptions.windowsOptions.extraConfig,\n            is64Bits = packageOptions.windowsOptions.is64Bits.getOrElse(true),\n            installerVersion = packageOptions.windowsOptions.installerVersion,\n            wixUpgradeCodeGuid = packageOptions.windowsOptions.wixUpgradeCodeGuid\n          )\n\n          nativePackagerType match {\n            case PackageType.Debian =>\n              DebianPackage(debianSettings).build()\n            case PackageType.Dmg =>\n              DmgPackage(macOSSettings).build()\n            case PackageType.Pkg =>\n              PkgPackage(macOSSettings).build()\n            case PackageType.Rpm =>\n              RedHatPackage(redHatSettings).build()\n            case PackageType.Msi =>\n              WindowsPackage(windowsSettings).build()\n          }\n          destPath\n        case PackageType.Docker =>\n          value(docker(builds, value(mainClass), logger))\n          destPath\n      }\n\n      val printableOutput = CommandUtils.printablePath(outputPath)\n\n      if packageType.runnable.nonEmpty then\n        logger.message {\n          if packageType.runnable.contains(true) then\n            s\"Wrote $outputPath, run it with\" + System.lineSeparator() +\n              \"  \" + printableOutput\n          else if packageType == PackageType.Js then\n            s\"Wrote $outputPath, run it with\" + System.lineSeparator() +\n              \"  node \" + printableOutput\n          else s\"Wrote $outputPath\"\n        }\n\n      val mTimeDestPathOpt = if packageType.runnable.isEmpty then None else Some(os.mtime(destPath))\n      mTimeDestPathOpt\n    }\n    // end of doPackage\n  }\n\n  def docJar(\n    builds: Seq[Build.Successful],\n    logger: Logger,\n    extraArgs: Seq[String],\n    withTestScope: Boolean\n  ): Either[BuildException, os.Path] = either {\n\n    val workDir   = builds.head.inputs.docJarWorkDir\n    val dest      = workDir / \"doc.jar\"\n    val cacheData =\n      CachedBinary.getCacheData(\n        builds = builds,\n        config = extraArgs.toList,\n        dest = dest,\n        workDir = workDir\n      )\n\n    if cacheData.changed then {\n\n      val contentDir = value(Doc.generateScaladocDirPath(builds, logger, extraArgs, withTestScope))\n\n      var outputStream: OutputStream = null\n      try {\n        outputStream = os.write.outputStream(dest, createFolders = true)\n        Library.writeLibraryJarTo(\n          outputStream,\n          builds,\n          hasActualManifest = false,\n          contentDirOverride = Some(contentDir)\n        )\n      }\n      finally if outputStream != null then outputStream.close()\n\n      CachedBinary.updateProjectAndOutputSha(dest, workDir, cacheData.projectSha)\n    }\n\n    dest\n  }\n\n  private val generatedSourcesPrefix = os.rel / \"META-INF\" / \"generated\"\n  def sourceJar(builds: Seq[Build.Successful], defaultLastModified: Long): Array[Byte] = {\n\n    val baos                 = new ByteArrayOutputStream\n    var zos: ZipOutputStream = null\n\n    def fromSimpleSources = builds.flatMap(_.sources.paths).distinct.iterator.map {\n      case (path, relPath) =>\n        val lastModified = os.mtime(path)\n        val content      = os.read.bytes(path)\n        (relPath, content, lastModified)\n    }\n\n    def fromGeneratedSources =\n      builds.flatMap(_.sources.inMemory).distinct.iterator.flatMap { inMemSource =>\n        val lastModified = inMemSource.originalPath match {\n          case Right((_, origPath)) => os.mtime(origPath)\n          case Left(_)              => defaultLastModified\n        }\n        val originalOpt = inMemSource.originalPath.toOption.collect {\n          case (subPath, origPath) if subPath != inMemSource.generatedRelPath =>\n            val origContent = os.read.bytes(origPath)\n            (subPath, origContent, lastModified)\n        }\n        val prefix    = if (originalOpt.isEmpty) os.rel else generatedSourcesPrefix\n        val generated = (\n          prefix / inMemSource.generatedRelPath,\n          inMemSource.content,\n          lastModified\n        )\n        Iterator(generated) ++ originalOpt.iterator\n      }\n\n    def paths: Iterator[(FilePath & BasePathImpl & SegmentedPath, Array[Byte], Long)] =\n      fromSimpleSources ++ fromGeneratedSources\n\n    try {\n      zos = new ZipOutputStream(baos)\n      for ((relPath, content, lastModified) <- paths) {\n        val name = relPath.toString\n        val ent  = new ZipEntry(name)\n        ent.setLastModifiedTime(FileTime.fromMillis(lastModified))\n        ent.setSize(content.length)\n\n        zos.putNextEntry(ent)\n        zos.write(content)\n        zos.closeEntry()\n      }\n    }\n    finally if zos != null then zos.close()\n\n    baos.toByteArray\n  }\n\n  private def docker(\n    builds: Seq[Build.Successful],\n    mainClass: String,\n    logger: Logger\n  ): Either[BuildException, Unit] = either {\n    val packageOptions = builds.head.options.notForBloopOptions.packageOptions\n\n    if builds.head.options.platform.value == Platform.Native &&\n      (Properties.isMac || Properties.isWin)\n    then {\n      System.err.println(\n        \"Package scala native application to docker image is not supported on MacOs and Windows\"\n      )\n      sys.exit(1)\n    }\n\n    val exec = packageOptions.dockerOptions.cmd.orElse {\n      builds.head.options.platform.value match {\n        case Platform.JVM    => Some(\"sh\")\n        case Platform.JS     => Some(\"node\")\n        case Platform.Native => None\n      }\n    }\n    val from = packageOptions.dockerOptions.from.getOrElse {\n      builds.head.options.platform.value match {\n        case Platform.JVM    => \"openjdk:17.0.2-slim\"\n        case Platform.JS     => \"node\"\n        case Platform.Native => \"debian:stable-slim\"\n      }\n    }\n    val repository = packageOptions.dockerOptions.imageRepository.mandatory(\n      \"--docker-image-repository\",\n      \"docker\"\n    )\n    val tag = packageOptions.dockerOptions.imageTag.getOrElse(\"latest\")\n\n    val dockerSettings = DockerSettings(\n      from = from,\n      registry = packageOptions.dockerOptions.imageRegistry,\n      repository = repository,\n      tag = Some(tag),\n      exec = exec,\n      dockerExecutable = None,\n      extraDirectories = packageOptions.dockerOptions.extraDirectories.map(_.toNIO)\n    )\n\n    val appPath = os.temp.dir(prefix = \"scala-cli-docker\") / \"app\"\n    builds.head.options.platform.value match {\n      case Platform.JVM    => value(bootstrap(builds, appPath, mainClass, () => Right(()), logger))\n      case Platform.JS     => buildJs(builds, appPath, Some(mainClass), logger)\n      case Platform.Native =>\n        val dest =\n          value(buildNative(\n            builds = builds,\n            mainClass = Some(mainClass),\n            targetType = PackageType.Native.Application,\n            destPath = None,\n            logger = logger\n          ))\n        os.copy(dest, appPath)\n    }\n\n    logger.message(\"Started building docker image with your application, it might take some time\")\n\n    DockerPackage(appPath, dockerSettings).build()\n\n    logger.message(\n      \"Built docker image, run it with\" + System.lineSeparator() +\n        s\"  docker run $repository:$tag\"\n    )\n  }\n\n  private def buildJs(\n    builds: Seq[Build.Successful],\n    destPath: os.Path,\n    mainClass: Option[String],\n    logger: Logger\n  ): Either[BuildException, os.Path] = for {\n    isFullOpt <- builds.head.options.scalaJsOptions.fullOpt\n    linkerConfig = builds.head.options.scalaJsOptions.linkerConfig(logger)\n    linkResult <- linkJs(\n      builds = builds,\n      dest = destPath,\n      mainClassOpt = mainClass,\n      addTestInitializer = false,\n      config = linkerConfig,\n      fullOpt = isFullOpt,\n      noOpt = builds.head.options.scalaJsOptions.noOpt.getOrElse(false),\n      logger = logger\n    )\n  } yield linkResult\n\n  private def bootstrap(\n    builds: Seq[Build.Successful],\n    destPath: os.Path,\n    mainClass: String,\n    alreadyExistsCheck: () => Either[BuildException, Unit],\n    logger: Logger\n  ): Either[BuildException, Unit] = either {\n    val byteCodeZipEntries = builds.flatMap { build =>\n      os.walk(build.output)\n        .filter(os.isFile(_))\n        .map { path =>\n          val name         = path.relativeTo(build.output).toString\n          val content      = os.read.bytes(path)\n          val lastModified = os.mtime(path)\n          val entry        = new ZipEntry(name)\n          entry.setLastModifiedTime(FileTime.fromMillis(lastModified))\n          entry.setSize(content.length)\n          (entry, content)\n        }\n    }\n\n    // TODO Generate that in memory\n    val tmpJar       = os.temp(prefix = destPath.last.stripSuffix(\".jar\"), suffix = \".jar\")\n    val tmpJarParams = Parameters.Assembly()\n      .withExtraZipEntries(byteCodeZipEntries)\n      .withMainClass(mainClass)\n    AssemblyGenerator.generate(tmpJarParams, tmpJar.toNIO)\n    val tmpJarContent = os.read.bytes(tmpJar)\n    os.remove(tmpJar)\n\n    def dependencyEntries: Seq[ClassPathEntry] =\n      builds.flatMap(_.artifacts.artifacts).distinct.map {\n        case (url, path) =>\n          if builds.head.options.notForBloopOptions.packageOptions.isStandalone then\n            ClassPathEntry.Resource(path.last, os.mtime(path), os.read.bytes(path))\n          else ClassPathEntry.Url(url)\n      }\n    val byteCodeEntry  = ClassPathEntry.Resource(s\"${destPath.last}-content.jar\", 0L, tmpJarContent)\n    val extraClassPath = builds.head.options.classPathOptions.extraClassPath.map { classPath =>\n      ClassPathEntry.Resource(classPath.last, os.mtime(classPath), os.read.bytes(classPath))\n    }\n\n    val allEntries    = Seq(byteCodeEntry) ++ dependencyEntries ++ extraClassPath\n    val loaderContent = coursier.launcher.ClassLoaderContent(allEntries)\n    val preamble      = Preamble()\n      .withOsKind(Properties.isWin)\n      .callsItself(Properties.isWin)\n      .withJavaOpts(builds.head.options.javaOptions.javaOpts.toSeq.map(_.value.value))\n    val baseParams = Parameters.Bootstrap(Seq(loaderContent), mainClass)\n      .withDeterministic(true)\n      .withPreamble(preamble)\n\n    val params: Parameters.Bootstrap =\n      if builds.head.options.notForBloopOptions.doSetupPython.getOrElse(false) then {\n        val res = value {\n          Artifacts.fetchAnyDependencies(\n            Seq(Positioned.none(\n              dep\"${Constants.pythonInterfaceOrg}:${Constants.pythonInterfaceName}:${Constants.pythonInterfaceVersion}\"\n            )),\n            Nil,\n            None,\n            logger,\n            builds.head.options.finalCache,\n            None,\n            Some(_)\n          )\n        }\n        val entries = res.artifacts.map {\n          case (a, f) =>\n            val path = os.Path(f)\n            if builds.head.options.notForBloopOptions.packageOptions.isStandalone then\n              ClassPathEntry.Resource(path.last, os.mtime(path), os.read.bytes(path))\n            else ClassPathEntry.Url(a.url)\n        }\n        val pythonContent = Seq(ClassLoaderContent(entries))\n        baseParams.addExtraContent(\"python\", pythonContent).withPython(true)\n      }\n      else baseParams\n\n    value(alreadyExistsCheck())\n    BootstrapGenerator.generate(params, destPath.toNIO)\n    ProcUtil.maybeUpdatePreamble(destPath)\n  }\n\n  /** Returns the dependency sub-graph of the provided modules, that is, all their JARs and their\n    * transitive dependencies' JARs.\n    *\n    * Note that this is not exactly the same as resolving those modules on their own (with their\n    * versions): other dependencies in the whole dependency sub-graph may bump versions in the\n    * provided dependencies sub-graph here.\n    *\n    * Here, among the JARs of the whole dependency graph, we pick the ones that were pulled by the\n    * provided modules, and might have been bumped by other modules. This is strictly a subset of\n    * the whole dependency graph.\n    */\n  def providedFiles(\n    builds: Seq[Build.Successful],\n    provided: Seq[dependency.AnyModule],\n    logger: Logger\n  ): Either[BuildException, Seq[os.Path]] = either {\n    logger.debug(s\"${provided.length} provided dependencies\")\n    val res = builds.map(_.artifacts.resolution.getOrElse {\n      sys.error(\"Internal error: expected resolution to have been kept\")\n    })\n    val modules: Seq[coursier.Module] = value {\n      provided\n        .map(_.toCs(builds.head.scalaParams)) // Scala params should be the same for all scopes\n        .sequence\n        .left.map(CompositeBuildException(_))\n    }\n    val modulesSet                         = modules.toSet\n    val providedDeps: Seq[core.Dependency] = value {\n      res\n        .map(_.dependencyArtifacts0.safeArtifacts.map(_.map(_._1)))\n        .sequence\n        .left\n        .map(CompositeBuildException(_))\n        .map(_.flatten.filter(dep => modulesSet.contains(dep.module)))\n    }\n    val providedRes: Seq[Resolution] = value {\n      res\n        .map(_.subset0(providedDeps).left.map(CoursierDependencyError(_)))\n        .sequence\n        .left\n        .map(CompositeBuildException(_))\n    }\n    val fileMap = builds.flatMap(_.artifacts.detailedRuntimeArtifacts).distinct\n      .map { case (_, _, artifact, path) => artifact -> path }\n      .toMap\n    val providedFiles: Seq[os.Path] = value {\n      providedRes\n        .map(r =>\n          coursier.Artifacts.artifacts0(\n            resolution = r,\n            classifiers = Set.empty,\n            attributes = Seq.empty,\n            mainArtifactsOpt = None,\n            artifactTypesOpt = None,\n            classpathOrder = true\n          ).safeArtifacts\n        )\n        .sequence\n        .left\n        .map(CompositeBuildException(_))\n        .map {\n          _.flatten\n            .distinct\n            .map(_._3)\n            .map(a => fileMap.getOrElse(a, sys.error(s\"should not happen (missing: $a)\")))\n        }\n    }\n    logger.debug {\n      val it = Iterator(s\"${providedFiles.size} provided JAR(s)\") ++\n        providedFiles.toVector.map(_.toString).sorted.iterator.map(f => s\"  $f\")\n      it.mkString(System.lineSeparator())\n    }\n    providedFiles\n  }\n\n  def assembly(\n    builds: Seq[Build.Successful],\n    destPath: os.Path,\n    mainClassOpt: Option[String],\n    extraProvided: Seq[dependency.AnyModule],\n    withPreamble: Boolean,\n    alreadyExistsCheck: () => Either[BuildException, Unit],\n    logger: Logger\n  ): Either[BuildException, Unit] = either {\n    val compiledClassesByOutputDir: Seq[(Path, Path)] =\n      builds.flatMap(build =>\n        os.walk(build.output).filter(os.isFile(_)).map(build.output -> _)\n      ).distinct\n    val (extraClassesFolders, extraJars) =\n      builds.flatMap(_.options.classPathOptions.extraClassPath).partition(os.isDir(_))\n    val extraClassesByDefaultOutputDir =\n      extraClassesFolders.flatMap(os.walk(_)).filter(os.isFile(_)).map(builds.head.output -> _)\n\n    val byteCodeZipEntries = (compiledClassesByOutputDir ++ extraClassesByDefaultOutputDir)\n      .distinct\n      .map { (outputDir, path) =>\n        val name         = path.relativeTo(outputDir).toString\n        val content      = os.read.bytes(path)\n        val lastModified = os.mtime(path)\n        val ent          = new ZipEntry(name)\n        ent.setLastModifiedTime(FileTime.fromMillis(lastModified))\n        ent.setSize(content.length)\n        (ent, content)\n      }\n\n    val provided = builds.head.options.notForBloopOptions.packageOptions.provided ++ extraProvided\n    val allJars  =\n      builds.flatMap(_.artifacts.runtimeArtifacts.map(_._2)) ++ extraJars.filter(os.exists(_))\n    val jars =\n      if (provided.isEmpty) allJars\n      else {\n        val providedFilesSet = value(providedFiles(builds, provided, logger)).toSet\n        allJars.filterNot(providedFilesSet.contains)\n      }\n\n    val preambleOpt =\n      if withPreamble then\n        Some {\n          Preamble()\n            .withOsKind(Properties.isWin)\n            .callsItself(Properties.isWin)\n        }\n      else None\n    val params = Parameters.Assembly()\n      .withExtraZipEntries(byteCodeZipEntries)\n      .withFiles(jars.map(_.toIO))\n      .withMainClass(mainClassOpt)\n      .withPreambleOpt(preambleOpt)\n    value(alreadyExistsCheck())\n    AssemblyGenerator.generate(params, destPath.toNIO)\n    ProcUtil.maybeUpdatePreamble(destPath)\n  }\n\n  final class NoMainClassFoundForAssemblyError(cause: NoMainClassFoundError) extends BuildException(\n        \"No main class found for assembly. Either pass one with --main-class, or make the assembly non-runnable with --preamble=false\",\n        cause = cause\n      )\n\n  private object LinkingDir {\n    case class Input(linkJsInput: ScalaJsLinker.LinkJSInput, scratchDirOpt: Option[os.Path])\n    private var currentInput: Option[Input]        = None\n    private var currentLinkingDir: Option[os.Path] = None\n    def getOrCreate(\n      linkJsInput: ScalaJsLinker.LinkJSInput,\n      scratchDirOpt: Option[os.Path]\n    ): os.Path =\n      val input = Input(linkJsInput, scratchDirOpt)\n      currentLinkingDir match {\n        case Some(linkingDir) if currentInput.contains(input) =>\n          linkingDir\n        case _ =>\n          scratchDirOpt.foreach(os.makeDir.all(_))\n\n          currentLinkingDir.foreach(dir => os.remove.all(dir))\n          currentLinkingDir = None\n\n          val linkingDirectory = os.temp.dir(\n            dir = scratchDirOpt.orNull,\n            prefix = \"scala-cli-js-linking\",\n            deleteOnExit = scratchDirOpt.isEmpty\n          )\n\n          currentInput = Some(input)\n          currentLinkingDir = Some(linkingDirectory)\n\n          linkingDirectory\n      }\n  }\n\n  def linkJs(\n    builds: Seq[Build.Successful],\n    dest: os.Path,\n    mainClassOpt: Option[String],\n    addTestInitializer: Boolean,\n    config: ScalaJsLinkerConfig,\n    fullOpt: Boolean,\n    noOpt: Boolean,\n    logger: Logger,\n    scratchDirOpt: Option[os.Path] = None\n  ): Either[BuildException, os.Path] = {\n    val jar       = Library.libraryJar(builds)\n    val classPath = Seq(jar) ++ builds.flatMap(_.artifacts.classPath)\n    val input     = ScalaJsLinker.LinkJSInput(\n      options = builds.head.options.notForBloopOptions.scalaJsLinkerOptions,\n      javaCommand =\n        builds.head.options.javaHome().value.javaCommand, // FIXME Allow users to use another JVM here?\n      classPath = classPath,\n      mainClassOrNull = mainClassOpt.orNull,\n      addTestInitializer = addTestInitializer,\n      config = config,\n      fullOpt = fullOpt,\n      noOpt = noOpt,\n      scalaJsVersion = builds.head.options.scalaJsOptions.finalVersion\n    )\n\n    val linkingDir = LinkingDir.getOrCreate(input, scratchDirOpt)\n\n    either {\n      value {\n        ScalaJsLinker.link(\n          input,\n          linkingDir,\n          logger,\n          builds.head.options.finalCache,\n          builds.head.options.archiveCache\n        )\n      }\n      os.walk.stream(linkingDir).filter(_.ext == \"js\").toSeq match {\n        case Seq(sourceJs) if os.isFile(sourceJs) && sourceJs.last.endsWith(\".js\") =>\n          // there's just one js file to link, so we copy it directly\n          logger.debug(\n            s\"Scala.js linker generated single file ${sourceJs.last}. Copying it to $dest\"\n          )\n          val sourceMapJs = os.Path(sourceJs.toString + \".map\")\n          os.copy(sourceJs, dest, replaceExisting = true)\n          if builds.head.options.scalaJsOptions.emitSourceMaps && os.exists(sourceMapJs) then {\n            logger.debug(\n              s\"Source maps emission enabled, copying source map file: ${sourceMapJs.last}\"\n            )\n            val sourceMapDest =\n              builds.head.options.scalaJsOptions.sourceMapsDest.getOrElse(os.Path(s\"$dest.map\"))\n            val updatedMainJs = ScalaJsLinker.updateSourceMappingURL(dest)\n            os.write.over(dest, updatedMainJs)\n            os.copy(sourceMapJs, sourceMapDest, replaceExisting = true)\n            logger.message(s\"Emitted js source maps to: $sourceMapDest\")\n          }\n          dest\n        case _ @Seq(jsSource, _*) =>\n          os.copy(\n            linkingDir,\n            dest,\n            createFolders = true,\n            replaceExisting = true,\n            mergeFolders = true\n          )\n          logger.debug(\n            s\"Scala.js linker generated multiple files for js multi-modules. Copied files to $dest directory.\"\n          )\n          val jsFileToReturn = os.rel / {\n            mainClassOpt match {\n              case Some(_) if os.exists(linkingDir / \"main.js\")  => \"main.js\"\n              case Some(mc) if os.exists(linkingDir / s\"$mc.js\") => s\"$mc.js\"\n              case _                                             => jsSource.relativeTo(linkingDir)\n            }\n          }\n          dest / jsFileToReturn\n        case Nil =>\n          logger.debug(\"Scala.js linker did not generate any .js files.\")\n          val allFilesInLinkingDir = os.walk(linkingDir).map(_.relativeTo(linkingDir))\n          value(Left(new ScalaJsLinkingError(\n            expected = if mainClassOpt.nonEmpty then \"main.js\" else \"<module>.js\",\n            foundFiles = allFilesInLinkingDir\n          )))\n      }\n    }\n  }\n\n  def buildNative(\n    builds: Seq[Build.Successful],\n    mainClass: Option[String], // when building a static/dynamic library, we don't need a main class\n    targetType: PackageType.Native,\n    destPath: Option[os.Path],\n    logger: Logger\n  ): Either[BuildException, os.Path] = either {\n    val dest = builds.head.inputs.nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n\n    val cliOptions =\n      builds.head.options.scalaNativeOptions.configCliOptions(builds.exists(\n        _.sources.resourceDirs.nonEmpty\n      ))\n\n    val setupPython   = builds.head.options.notForBloopOptions.doSetupPython.getOrElse(false)\n    val pythonLdFlags =\n      if setupPython then\n        value {\n          val python       = value(createPythonInstance().orPythonDetectionError)\n          val flagsOrError = python.ldflags\n          logger.debug(s\"Python ldflags: $flagsOrError\")\n          flagsOrError.orPythonDetectionError\n        }\n      else Nil\n    val pythonCliOptions = pythonLdFlags.flatMap(f => Seq(\"--linking-option\", f)).toList\n\n    val libraryLinkingOptions: Seq[String] =\n      Option.when(targetType != PackageType.Native.Application) {\n        /* If we are building a library, we make sure to change the name\n         that the linker will put into the loading path - otherwise\n         the built library will depend on some internal path within .scala-build\n         */\n\n        destPath.flatMap(_.lastOpt).toSeq.flatMap { filename =>\n          val linkerOption =\n            if Properties.isLinux then s\"-Wl,-soname,$filename\" else s\"-Wl,-install_name,$filename\"\n          Seq(\"--linking-option\", linkerOption)\n        }\n      }.toSeq.flatten\n\n    val allCliOptions = pythonCliOptions ++\n      cliOptions ++\n      libraryLinkingOptions ++\n      mainClass.toSeq.flatMap(m => Seq(\"--main\", m))\n\n    val nativeWorkDir = builds.head.inputs.nativeWorkDir\n    os.makeDir.all(nativeWorkDir)\n\n    val cacheData =\n      CachedBinary.getCacheData(\n        builds,\n        allCliOptions,\n        dest,\n        nativeWorkDir\n      )\n\n    if (cacheData.changed) {\n      builds.foreach(build => NativeResourceMapper.copyCFilesToScalaNativeDir(build, nativeWorkDir))\n      val jar       = Library.libraryJar(builds)\n      val classpath = (Seq(jar) ++ builds.flatMap(_.artifacts.classPath)).map(_.toString).distinct\n      val args      =\n        allCliOptions ++\n          logger.scalaNativeCliInternalLoggerOptions ++\n          List[String](\n            \"--outpath\",\n            dest.toString(),\n            \"--workdir\",\n            nativeWorkDir.toString()\n          ) ++ classpath\n\n      val scalaNativeCli = builds.flatMap(_.artifacts.scalaOpt).headOption\n        .getOrElse {\n          sys.error(\"Expected Scala artifacts to be fetched\")\n        }\n        .scalaNativeCli\n\n      val exitCode =\n        Runner.runJvm(\n          builds.head.options.javaHome().value.javaCommand,\n          builds.head.options.javaOptions.javaOpts.toSeq.map(_.value.value),\n          scalaNativeCli,\n          \"scala.scalanative.cli.ScalaNativeLd\",\n          args,\n          logger\n        ).waitFor()\n      if exitCode == 0 then\n        CachedBinary.updateProjectAndOutputSha(dest, nativeWorkDir, cacheData.projectSha)\n      else throw new ScalaNativeBuildError\n    }\n\n    dest\n  }\n  def resolvePackageType(\n    builds: Seq[Build.Successful],\n    forcedPackageTypeOpt: Option[PackageType]\n  ): Either[BuildException, PackageType] = {\n    val basePackageTypeOpt = builds.head.options.notForBloopOptions.packageOptions.packageTypeOpt\n    lazy val validPackageScalaJS =\n      Seq(PackageType.Js, PackageType.LibraryJar, PackageType.SourceJar, PackageType.DocJar)\n    lazy val validPackageScalaNative =\n      Seq(\n        PackageType.LibraryJar,\n        PackageType.SourceJar,\n        PackageType.DocJar,\n        PackageType.Native.Application,\n        PackageType.Native.LibraryDynamic,\n        PackageType.Native.LibraryStatic\n      )\n\n    forcedPackageTypeOpt -> builds.head.options.platform.value match {\n      case (Some(forcedPackageType), _) => Right(forcedPackageType)\n      case (_, _) if builds.head.options.notForBloopOptions.packageOptions.isDockerEnabled =>\n        basePackageTypeOpt match {\n          case Some(PackageType.Docker) | None => Right(PackageType.Docker)\n          case Some(packageType)               => Left(new MalformedCliInputError(\n              s\"Unsupported package type: $packageType for Docker.\"\n            ))\n        }\n      case (_, Platform.JS) => {\n          for (basePackageType <- basePackageTypeOpt)\n            yield\n              if validPackageScalaJS.contains(basePackageType) then Right(basePackageType)\n              else\n                Left(new MalformedCliInputError(\n                  s\"Unsupported package type: $basePackageType for Scala.js.\"\n                ))\n        }.getOrElse(Right(PackageType.Js))\n      case (_, Platform.Native) => {\n          val specificNativePackageType: Option[Native] =\n            import ScalaNativeTarget.*\n            builds.head.options.scalaNativeOptions.buildTargetStr.flatMap(fromString).map {\n              case Application    => PackageType.Native.Application\n              case LibraryDynamic => PackageType.Native.LibraryDynamic\n              case LibraryStatic  => PackageType.Native.LibraryStatic\n            }\n          for basePackageType <- specificNativePackageType orElse basePackageTypeOpt\n          yield\n            if validPackageScalaNative.contains(basePackageType) then Right(basePackageType)\n            else\n              Left(new MalformedCliInputError(\n                s\"Unsupported package type: $basePackageType for Scala Native.\"\n              ))\n        }.getOrElse(Right(PackageType.Native.Application))\n      case _ => Right(basePackageTypeOpt.getOrElse(PackageType.Bootstrap))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/package0/PackageOptions.scala",
    "content": "package scala.cli.commands.package0\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.compiler.{ScalaCompilerMaker, SimpleScalaCompilerMaker}\nimport scala.build.errors.{BuildException, CompositeBuildException, ModuleFormatError}\nimport scala.build.options.*\nimport scala.build.options.packaging.*\nimport scala.build.{BuildThreads, Logger, Positioned}\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n@HelpMessage(PackageOptions.helpMessage, \"\", PackageOptions.detailedHelpMessage)\n// format: off\nfinal case class PackageOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n    java: SharedJavaOptions = SharedJavaOptions(),\n  @Recurse\n    compileCross: CrossOptions = CrossOptions(),\n  @Recurse\n    mainClass: MainClassOptions = MainClassOptions(),\n\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Set the destination path\")\n  @Name(\"o\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    output: Option[String] = None,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Overwrite the destination file, if it exists\")\n  @Name(\"f\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    force: Boolean = false,\n\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Generate a library JAR rather than an executable JAR\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    library: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Generate a source JAR rather than an executable JAR\")\n  @Name(\"sourcesJar\")\n  @Name(\"jarSources\")\n  @Name(\"sources\")\n  @Tag(tags.deprecated(\"sources\"))\n  @Name(\"src\")\n  @Tag(tags.deprecated(\"src\"))\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    withSources: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Generate a scaladoc JAR rather than an executable JAR\")\n  @ExtraName(\"scaladoc\")\n  @ExtraName(\"javadoc\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    doc: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Generate an assembly JAR\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    assembly: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"For assembly JAR, whether to add a bash / bat preamble\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    preamble: Boolean = true,\n  @Group(HelpGroup.Package.toString)\n  @Hidden\n  @HelpMessage(\"For assembly JAR, whether to specify a main class in the JAR manifest\")\n  @Tag(tags.restricted)\n    mainClassInManifest: Option[Boolean] = None,\n  @Group(HelpGroup.Package.toString)\n  @Hidden\n  @HelpMessage(\"Generate an assembly JAR for Spark (assembly that doesn't contain Spark, nor any of its dependencies)\")\n  @Tag(tags.experimental)\n    spark: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Package standalone JARs\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    standalone: Option[Boolean] = None,\n  @Recurse\n    packager: PackagerOptions = PackagerOptions(),\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build Debian package, available only on Linux\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    deb: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build dmg package, available only on macOS\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    dmg: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build rpm package, available only on Linux\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    rpm: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build msi package, available only on Windows\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    msi: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build pkg package, available only on macOS\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    pkg: Boolean = false,\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build Docker image\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    docker: Boolean = false,\n\n  @Group(HelpGroup.Package.toString)\n  @Hidden\n  @HelpMessage(\"Exclude modules *and their transitive dependencies* from the JAR to be packaged\")\n  @ValueDescription(\"org:name\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    provided: List[String] = Nil,\n\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Use default scaladoc options\")\n  @ExtraName(\"defaultScaladocOpts\")\n  @Tag(tags.implementation)\n    defaultScaladocOptions: Option[Boolean] = None,\n\n  @Group(HelpGroup.Package.toString)\n  @HelpMessage(\"Build GraalVM native image\")\n  @ExtraName(\"graal\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    nativeImage: Boolean = false\n) extends HasSharedOptions with HasSharedWatchOptions {\n  // format: on\n\n  def packageTypeOpt: Option[PackageType] =\n    forcedPackageTypeOpt.orElse {\n      if (library) Some(PackageType.LibraryJar)\n      else if (withSources) Some(PackageType.SourceJar)\n      else if (assembly) Some(\n        PackageType.Assembly(\n          addPreamble = preamble,\n          mainClassInManifest = mainClassInManifest\n        )\n      )\n      else if (spark) Some(PackageType.Spark)\n      else if (deb) Some(PackageType.Debian)\n      else if (dmg) Some(PackageType.Dmg)\n      else if (pkg) Some(PackageType.Pkg)\n      else if (rpm) Some(PackageType.Rpm)\n      else if (msi) Some(PackageType.Msi)\n      else if (nativeImage) Some(PackageType.GraalVMNativeImage)\n      else None\n    }\n  def forcedPackageTypeOpt: Option[PackageType] =\n    if (doc) Some(PackageType.DocJar)\n    else None\n\n  def providedModules: Either[BuildException, Seq[dependency.AnyModule]] =\n    provided\n      .map { str =>\n        dependency.parser.ModuleParser.parse(str)\n          .left.map(err => new ModuleFormatError(str, err))\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n\n  def baseBuildOptions(logger: Logger): Either[BuildException, BuildOptions] = either {\n    val baseOptions = value(buildOptions())\n    baseOptions.copy(\n      mainClass = mainClass.mainClass.filter(_.nonEmpty),\n      notForBloopOptions = baseOptions.notForBloopOptions.copy(\n        packageOptions = baseOptions.notForBloopOptions.packageOptions.copy(\n          standalone = standalone,\n          version = Some(packager.version),\n          launcherApp = packager.launcherApp,\n          maintainer = packager.maintainer,\n          description = packager.description,\n          output = output,\n          packageTypeOpt = packageTypeOpt,\n          logoPath = packager.logoPath.map(os.Path(_, os.pwd)),\n          macOSidentifier = packager.identifier,\n          debianOptions = DebianOptions(\n            conflicts = packager.debianConflicts,\n            dependencies = packager.debianDependencies,\n            architecture = Some(packager.debArchitecture),\n            priority = packager.priority,\n            section = packager.section\n          ),\n          redHatOptions = RedHatOptions(\n            license = packager.license,\n            release = Some(packager.release),\n            architecture = Some(packager.rpmArchitecture)\n          ),\n          windowsOptions = WindowsOptions(\n            licensePath = packager.licensePath.map(os.Path(_, os.pwd)),\n            productName = Some(packager.productName),\n            exitDialog = packager.exitDialog,\n            suppressValidation = packager.suppressValidation,\n            extraConfig = packager.extraConfig,\n            is64Bits = Some(packager.is64Bits),\n            installerVersion = packager.installerVersion,\n            wixUpgradeCodeGuid = packager.wixUpgradeCodeGuid\n          ),\n          dockerOptions = DockerOptions(\n            from = packager.dockerFrom,\n            imageRegistry = packager.dockerImageRegistry,\n            imageRepository = packager.dockerImageRepository,\n            imageTag = packager.dockerImageTag,\n            cmd = packager.dockerCmd,\n            isDockerEnabled = Some(docker),\n            extraDirectories = packager.dockerExtraDirectories.map(os.Path(_, os.pwd))\n          ),\n          nativeImageOptions = {\n            val graalVmVersion     = packager.graalvmVersion.map(_.trim).filter(_.nonEmpty)\n            val graalVmJavaVersion = packager.graalvmJvmId.map(_.trim)\n            for {\n              vmVersion   <- graalVmVersion\n              javaVersion <- graalVmJavaVersion\n              if !vmVersion.startsWith(javaVersion)\n            } logger.message(\n              s\"\"\"GraalVM Java major version ($javaVersion) does not match GraalVM version ($vmVersion).\n                 |GraalVM version should start with the Java major version to be used.\"\"\".stripMargin\n            )\n            NativeImageOptions(\n              graalvmJvmId = packager.graalvmJvmId.map(_.trim).filter(_.nonEmpty),\n              graalvmJavaVersion = packager.graalvmJavaVersion.filter(_ > 0),\n              graalvmVersion = graalVmVersion,\n              graalvmArgs =\n                packager.graalvmArgs.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine)\n            )\n          },\n          useDefaultScaladocOptions = defaultScaladocOptions\n        ),\n        addRunnerDependencyOpt = Some(false)\n      ),\n      internal = baseOptions.internal.copy(\n        // computing the provided modules sub-graph need the final Resolution instance\n        // Spark packaging adds provided modules, so it needs it too\n        keepResolution = provided.nonEmpty || packageTypeOpt.contains(PackageType.Spark)\n      )\n    )\n  }\n\n  def finalBuildOptions(logger: Logger): Either[BuildException, BuildOptions] = either {\n    val baseOptions = value(baseBuildOptions(logger))\n    baseOptions.copy(\n      notForBloopOptions = baseOptions.notForBloopOptions.copy(\n        packageOptions = baseOptions.notForBloopOptions.packageOptions.copy(\n          provided = value(providedModules)\n        )\n      )\n    )\n  }\n\n  def compilerMaker(threads: BuildThreads): Either[BuildException, ScalaCompilerMaker] = either {\n    val maker = shared.compilerMaker(threads)\n    if (forcedPackageTypeOpt.contains(PackageType.DocJar))\n      ScalaCompilerMaker.IgnoreScala2(maker)\n    else\n      maker\n  }\n  def docCompilerMakerOpt: Option[ScalaCompilerMaker] =\n    if (forcedPackageTypeOpt.contains(PackageType.DocJar))\n      Some(SimpleScalaCompilerMaker(\"java\", Nil, scaladoc = true))\n    else\n      None\n}\n\nobject PackageOptions {\n  implicit lazy val parser: Parser[PackageOptions] = Parser.derive\n  implicit lazy val help: Help[PackageOptions]     = Help.derive\n\n  val cmdName             = \"package\"\n  private val helpHeader  = \"Compile and package Scala code.\"\n  val helpMessage: String = HelpMessages.shortHelpMessage(cmdName, helpHeader, needsPower = true)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/package0/PackagerOptions.scala",
    "content": "package scala.cli.commands.package0\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{Constants, tags}\n\n// format: off\nfinal case class PackagerOptions(\n  @HelpMessage(\"Set the version of the generated package\")\n  @Tag(tags.restricted)\n    version: String = \"1.0.0\",\n  @HelpMessage(\n    \"Path to application logo in PNG format, it will be used to generate icon and banner/dialog in msi installer\"\n  )\n  @Tag(tags.restricted)\n    logoPath: Option[String] = None,\n  @HelpMessage(\"Set launcher app name, which will be linked to the PATH\")\n  @Tag(tags.restricted)\n    launcherApp: Option[String] = None,\n  @ValueDescription(\"Description\")\n    description: Option[String] = None,\n  @HelpMessage(\"This should contain names and email addresses of co-maintainers of the package\")\n  @Name(\"m\")\n    maintainer: Option[String] = None,\n  @Group(HelpGroup.Debian.toString)\n  @HelpMessage(\n    \"The list of Debian package that this package is not compatible with\"\n  )\n  @ValueDescription(\"Debian dependencies conflicts\")\n  @Tag(tags.restricted)\n    debianConflicts: List[String] = Nil,\n  @Group(HelpGroup.Debian.toString)\n  @HelpMessage(\"The list of Debian packages that this package depends on\")\n  @ValueDescription(\"Debian dependencies\")\n  @Tag(tags.restricted)\n    debianDependencies: List[String] = Nil,\n  @Group(HelpGroup.Debian.toString)\n  @HelpMessage(\n    \"Architectures that are supported by the repository (default: all)\"\n  )\n  @Tag(tags.restricted)\n    debArchitecture: String = \"all\",\n  @Group(HelpGroup.Debian.toString)\n  @HelpMessage(\n    \"This field represents how important it is that the user have the package installed\"\n  )\n  @Tag(tags.restricted)\n  priority: Option[String] = None,\n  @Group(HelpGroup.Debian.toString)\n  @HelpMessage(\n    \"This field specifies an application area into which the package has been classified\"\n  )\n  @Tag(tags.restricted)\n  section: Option[String] = None,\n  @Group(HelpGroup.MacOS.toString)\n  @HelpMessage(\n  \"CF Bundle Identifier\"\n  )\n  @Tag(tags.restricted)\n    identifier: Option[String] = None,\n  @Group(HelpGroup.RedHat.toString)\n  @HelpMessage(\n    \"Licenses that are supported by the repository (list of licenses: https://spdx.org/licenses/)\"\n  )\n  @Tag(tags.restricted)\n    license: Option[String] = None,\n  @Group(HelpGroup.RedHat.toString)\n  @HelpMessage(\n    \"The number of times this version of the software was released (default: 1)\"\n  )\n  @Tag(tags.restricted)\n    release: String = \"1\",\n  @HelpMessage(\"Architectures that are supported by the repository (default: noarch)\")\n  @Tag(tags.restricted)\n    rpmArchitecture: String = \"noarch\",\n  @Group(HelpGroup.Windows.toString)\n  @HelpMessage(\"Path to the license file\")\n  @Tag(tags.restricted)\n    licensePath: Option[String] = None,\n  @Group(HelpGroup.Windows.toString)\n  @HelpMessage(\"Name of product (default: Scala packager)\")\n  @Tag(tags.restricted)\n    productName: String = \"Scala packager\",\n  @Group(HelpGroup.Windows.toString)\n  @HelpMessage(\"Text that will be displayed on the exit dialog\")\n  @Tag(tags.restricted)\n    exitDialog: Option[String] = None,\n  @Group(HelpGroup.Windows.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Suppress Wix ICE validation (required for users that are neither interactive, not local administrators)\")\n    suppressValidation: Option[Boolean] = None,\n  @Group(HelpGroup.Windows.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Path to extra WIX configuration content\")\n  @ValueDescription(\"path\")\n    extraConfig: List[String] = Nil,\n  @Group(HelpGroup.Windows.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Whether a 64-bit executable is being packaged\")\n  @Name(\"64\")\n    is64Bits: Boolean = true,\n  @Group(HelpGroup.Windows.toString)\n  @HelpMessage(\"WIX installer version\")\n  @Tag(tags.restricted)\n    installerVersion: Option[String] = None,\n  @Group(HelpGroup.Windows.toString)\n  @HelpMessage(\"The GUID to identify that the windows package can be upgraded.\")\n  @Tag(tags.restricted)\n    wixUpgradeCodeGuid: Option[String] = None,\n  @Group(HelpGroup.Docker.toString)\n  @HelpMessage(\n    \"Building the container from base image\"\n  )\n  @Tag(tags.restricted)\n  dockerFrom: Option[String] = None,\n  @Group(HelpGroup.Docker.toString)\n  @HelpMessage(\n    \"The image registry; if empty, it will use the default registry\"\n  )\n  @Tag(tags.restricted)\n  dockerImageRegistry: Option[String] = None,\n  @Group(HelpGroup.Docker.toString)\n  @HelpMessage(\n    \"The image repository\"\n  )\n  @Tag(tags.restricted)\n  dockerImageRepository: Option[String] = None,\n  @Group(HelpGroup.Docker.toString)\n  @HelpMessage(\n    \"The image tag; the default tag is `latest`\"\n  )\n  @Tag(tags.restricted)\n  dockerImageTag: Option[String] = None,\n  @Group(HelpGroup.Docker.toString)\n  @HelpMessage(\n    \"Allows to override the executable used to run the application in docker, otherwise it defaults to sh for the JVM platform and node for the JS platform\"\n  )\n  @Tag(tags.restricted)\n  dockerCmd: Option[String] = None,\n  \n  @Group(HelpGroup.Docker.toString)\n  @HelpMessage(\"Extra directories to be added to the docker image\")\n  @Tag(tags.restricted)\n  dockerExtraDirectories: List[String] = Nil,\n\n  @Group(HelpGroup.NativeImage.toString)\n  @HelpMessage(s\"GraalVM Java major version to use to build GraalVM native images (${Constants.defaultGraalVMJavaVersion} by default)\")\n  @ValueDescription(\"java-major-version\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    graalvmJavaVersion: Option[Int] = None,\n  @Group(HelpGroup.NativeImage.toString)\n  @HelpMessage(s\"GraalVM version to use to build GraalVM native images (${Constants.defaultGraalVMVersion} by default)\")\n  @ValueDescription(\"version\")\n  @Tag(tags.inShortHelp)\n    graalvmVersion: Option[String] = None,\n  @Group(HelpGroup.NativeImage.toString)\n  @HelpMessage(\"JVM id of GraalVM distribution to build GraalVM native images (like \\\"graalvm-java17:22.0.0\\\")\")\n  @ValueDescription(\"jvm-id\")\n  @Tag(tags.restricted)\n    graalvmJvmId: Option[String] = None,\n  @Group(HelpGroup.NativeImage.toString)\n  @HelpMessage(\"Pass args to GraalVM\")\n  @Tag(tags.restricted)\n   graalvmArgs: List[String] = Nil\n)\n// format: on\n\nobject PackagerOptions {\n  implicit lazy val parser: Parser[PackagerOptions] = Parser.derive\n  implicit lazy val help: Help[PackagerOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/packaging/Spark.scala",
    "content": "package scala.cli.commands.packaging\n\nimport dependency.*\n\nobject Spark {\n\n  private def names = Seq(\n    // FIXME Add more?\n    // (see \"cs complete-dependency org.apache.spark: | grep '_2\\.12$'\"\n    // or `ls \"$(cs get https://archive.apache.org/dist/spark/spark-2.4.2/spark-2.4.2-bin-hadoop2.7.tgz --archive)\"/*/jars | grep '^spark-'`)\n    \"core\",\n    \"graphx\",\n    \"hive\",\n    \"hive-thriftserver\",\n    \"kubernetes\",\n    \"mesos\",\n    \"mllib\",\n    \"repl\",\n    \"sql\",\n    \"streaming\",\n    \"yarn\"\n  )\n\n  def sparkModules: Seq[AnyModule] =\n    names.map(name => mod\"org.apache.spark::spark-$name\")\n\n  def hadoopModules: Seq[AnyModule] =\n    Seq(\n      // TODO Add more for Hadoop 2, maybe for 3 too\n      mod\"org.apache.hadoop:hadoop-client-api\"\n    )\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/DummyOptions.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\n\nfinal case class DummyOptions()\n\nobject DummyOptions {\n  implicit lazy val parser: Parser[DummyOptions] = Parser.derive\n  implicit lazy val help: Help[DummyOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/ExternalCommand.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\n\nimport scala.cli.commands.util.CommandHelpers\n\nabstract class ExternalCommand extends Command[DummyOptions] with CommandHelpers {\n  override def hasHelp                 = false\n  override def stopAtFirstUnrecognized = true\n\n  def actualHelp: Help[?]\n\n  def run(options: DummyOptions, args: RemainingArgs): Unit = {\n    val unparsedPart =\n      if (args.unparsed.isEmpty) Nil\n      else Seq(\"--\") ++ args.unparsed\n    val allArgs = args.remaining ++ unparsedPart\n    run(allArgs)\n  }\n\n  def run(args: Seq[String]): Unit\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/KeyServer.scala",
    "content": "package scala.cli.commands.pgp\n\nimport sttp.client3.*\nimport sttp.model.Uri\n\nobject KeyServer {\n\n  def default =\n    // wouldn't https://keyserver.ubuntu.com work as well (https > http)\n    uri\"http://keyserver.ubuntu.com:11371\"\n\n  def allDefaults = Seq(\n    default\n    // Sonatype sometimes mentions this one when the key wasn't uploaded anywhere,\n    // but lookups are too slow there (and often timeout actually)\n    // seems https://pgp.mit.edu might work too\n    // uri\"http://pgp.mit.edu:11371\"\n  )\n\n  def add(\n    pubKey: String,\n    keyServer: Uri,\n    backend: SttpBackend[Identity, Any]\n  ): Either[String, String] = {\n\n    val resp = basicRequest\n      .body(Map(\"keytext\" -> pubKey))\n      .response(asString)\n      .post(keyServer.addPath(\"pks\", \"add\"))\n      .send(backend)\n\n    if (resp.isSuccess)\n      Right(resp.body.merge)\n    else\n      Left(resp.body.merge)\n  }\n\n  def check(\n    keyId: String,\n    keyServer: Uri,\n    backend: SttpBackend[Identity, Any]\n  ): Either[String, Either[String, String]] = {\n    val resp = basicRequest\n      .get(keyServer.addPath(\"pks\", \"lookup\").addParam(\"op\", \"get\").addParam(\"search\", keyId))\n      .response(asString)\n      .send(backend)\n    if (resp.isSuccess)\n      Right(Right(resp.body.merge))\n    else if (resp.isClientError)\n      Right(Left(resp.body.merge))\n    else\n      Left(resp.body.merge)\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpCommand.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.app.Command\nimport caseapp.core.help.Help\nimport caseapp.core.parser.Parser\n\nimport scala.build.Logger\nimport scala.build.input.{ScalaCliInvokeData, SubCommand}\nimport scala.cli.ScalaCli\nimport scala.cli.commands.RestrictableCommand\nimport scala.cli.commands.util.CommandHelpers\nimport scala.cli.internal.{CliLogger, ProcUtil}\n\nabstract class PgpCommand[T](implicit myParser: Parser[T], help: Help[T])\n    extends Command()(using myParser, help)\n    with CommandHelpers with RestrictableCommand[T] {\n  override protected def invokeData: ScalaCliInvokeData =\n    ScalaCliInvokeData(\n      progName = ScalaCli.progName,\n      subCommandName =\n        name, // FIXME Should be the actual name that was called from the command line\n      subCommand = SubCommand.Other,\n      isShebangCapableShell = ProcUtil.isShebangCapableShell\n    )\n\n  override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL\n\n  override def shouldSuppressExperimentalFeatureWarnings: Boolean =\n    false // TODO add handling for scala-cli-signing\n\n  override def shouldSuppressDeprecatedFeatureWarnings: Boolean =\n    false // TODO add handling for scala-cli-signing\n\n  override def logger: Logger = CliLogger.default // TODO add handling for scala-cli-signing\n\n  override def hidden = true\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpCommandNames.scala",
    "content": "package scala.cli.commands.pgp\n\nobject PgpCommandNames {\n  def pgpCreate = List(\n    List(\"pgp\", \"create\")\n  )\n  def pgpKeyId = List(\n    List(\"pgp\", \"key-id\")\n  )\n  def pgpSign = List(\n    List(\"pgp\", \"sign\")\n  )\n  def pgpVerify = List(\n    List(\"pgp\", \"verify\")\n  )\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpCommands.scala",
    "content": "package scala.cli.commands.pgp\n\nclass PgpCommands {\n  def allScalaCommands: Array[PgpCommand[?]] =\n    Array(PgpCreate, PgpKeyId, PgpSign, PgpVerify)\n  def allExternalCommands: Array[ExternalCommand] =\n    Array.empty\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpCreate.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\n\nimport scala.cli.signing.commands.{PgpCreate as OriginalPgpCreate, PgpCreateOptions}\n\nobject PgpCreate extends PgpCommand[PgpCreateOptions] {\n  override def names = PgpCommandNames.pgpCreate\n\n  override def run(options: PgpCreateOptions, args: RemainingArgs): Unit =\n    OriginalPgpCreate.run(options, args)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpCreateExternal.scala",
    "content": "package scala.cli.commands.pgp\n\nimport scala.cli.signing.commands.PgpCreateOptions\n\nclass PgpCreateExternal extends PgpExternalCommand {\n  override def hidden = true\n  def actualHelp      = PgpCreateOptions.help\n  def externalCommand = Seq(\"pgp\", \"create\")\n\n  override def names = PgpCommandNames.pgpCreate\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala",
    "content": "package scala.cli.commands.pgp\nimport coursier.cache.{ArchiveCache, FileCache}\nimport coursier.util.Task\nimport dependency.*\n\nimport java.io.File\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.errors.BuildException\nimport scala.build.internal.Util.DependencyOps\nimport scala.build.internal.{\n  Constants,\n  ExternalBinary,\n  ExternalBinaryParams,\n  FetchExternalBinary,\n  Runner\n}\nimport scala.build.{Logger, Positioned, RepositoryUtils, options as bo}\nimport scala.cli.ScalaCli\nimport scala.cli.commands.shared.{CoursierOptions, SharedJvmOptions}\nimport scala.cli.commands.util.JvmUtils\nimport scala.util.Properties\n\nabstract class PgpExternalCommand extends ExternalCommand {\n  def progName: String = ScalaCli.progName\n  def externalCommand: Seq[String]\n\n  def tryRun(\n    cache: FileCache[Task],\n    args: Seq[String],\n    extraEnv: Map[String, String],\n    logger: Logger,\n    allowExecve: Boolean,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, Int] = either {\n\n    val archiveCache = ArchiveCache().withCache(cache)\n\n    val binary = value(PgpExternalCommand.launcher(\n      cache,\n      archiveCache,\n      logger,\n      jvmOptions,\n      coursierOptions,\n      signingCliOptions\n    ))\n\n    val command = binary ++ externalCommand ++ args\n\n    Runner.run0(\n      progName,\n      command,\n      logger,\n      allowExecve = allowExecve,\n      cwd = None,\n      extraEnv = extraEnv,\n      inheritStreams = true\n    ).waitFor()\n  }\n\n  def output(\n    cache: FileCache[Task],\n    args: Seq[String],\n    extraEnv: Map[String, String],\n    logger: Logger,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, String] = either {\n\n    val archiveCache = ArchiveCache().withCache(cache)\n\n    val binary = value(PgpExternalCommand.launcher(\n      cache,\n      archiveCache,\n      logger,\n      jvmOptions,\n      coursierOptions,\n      signingCliOptions\n    ))\n\n    val command = binary ++ externalCommand ++ args\n\n    os.proc(command).call(stdin = os.Inherit, env = extraEnv)\n      .out.text()\n  }\n\n  def run(args: Seq[String]): Unit = {\n\n    val (options, remainingArgs) =\n      PgpExternalOptions.parser.stopAtFirstUnrecognized.parse(args) match {\n        case Left(err) =>\n          System.err.println(err.message)\n          sys.exit(1)\n        case Right((options0, remainingArgs0)) => (options0, remainingArgs0)\n      }\n\n    val logger = options.global.logging.logger\n\n    val cache   = options.coursier.coursierCache(logger)\n    val retCode = tryRun(\n      cache,\n      remainingArgs,\n      Map(),\n      logger,\n      allowExecve = true,\n      options.jvm,\n      options.coursier,\n      options.scalaSigning.cliOptions()\n    ).orExit(logger)\n\n    if (retCode != 0)\n      sys.exit(retCode)\n  }\n}\n\nobject PgpExternalCommand {\n  val scalaCliSigningJvmVersion: Int = Constants.signingCliJvmVersion\n\n  def launcher(\n    cache: FileCache[Task],\n    archiveCache: ArchiveCache[Task],\n    logger: Logger,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, Seq[String]] = {\n    val javaCommand = () =>\n      JvmUtils.getJavaCmdVersionOrHigher(\n        scalaCliSigningJvmVersion,\n        jvmOptions,\n        coursierOptions\n      ).orThrow.javaCommand\n\n    launcher(\n      cache,\n      archiveCache,\n      logger,\n      javaCommand,\n      signingCliOptions\n    )\n  }\n\n  def launcher(\n    cache: FileCache[Task],\n    archiveCache: ArchiveCache[Task],\n    logger: Logger,\n    buildOptions: bo.BuildOptions\n  ): Either[BuildException, Seq[String]] = {\n    val javaCommand = () =>\n      JvmUtils.getJavaCmdVersionOrHigher(\n        scalaCliSigningJvmVersion,\n        buildOptions\n      ).orThrow.javaCommand\n\n    launcher(\n      cache,\n      archiveCache,\n      logger,\n      javaCommand,\n      buildOptions.notForBloopOptions.publishOptions.signingCli\n    )\n  }\n\n  private def launcher(\n    cache: FileCache[Task],\n    archiveCache: ArchiveCache[Task],\n    logger: Logger,\n    javaCommand: () => String,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, Seq[String]] = either {\n    val version =\n      signingCliOptions.signingCliVersion\n        .getOrElse(Constants.scalaCliSigningVersion)\n    val ver              = if (version.startsWith(\"latest\")) \"latest.release\" else version\n    val signingMainClass = \"scala.cli.signing.ScalaCliSigning\"\n    val jvmSigningDep    =\n      dep\"${Constants.scalaCliSigningOrganization}:${Constants.scalaCliSigningName}_3:$ver\"\n\n    if (signingCliOptions.forceJvm.getOrElse(false)) {\n      val extraRepos =\n        if version.endsWith(\"SNAPSHOT\") then\n          Seq(\n            RepositoryUtils.snapshotsRepository,\n            RepositoryUtils.scala3NightlyRepository\n          )\n        else Nil\n\n      val (_, signingRes) = value {\n        scala.build.Artifacts.fetchCsDependencies(\n          dependencies = Seq(Positioned.none(jvmSigningDep.toCs)),\n          extraRepositories = extraRepos,\n          forceScalaVersionOpt = None,\n          forcedVersions = Nil,\n          logger = logger,\n          cache = cache,\n          classifiersOpt = None\n        )\n      }\n      val signingClassPath = signingRes.files\n\n      val command = Seq[os.Shellable](\n        javaCommand(),\n        signingCliOptions.javaArgs,\n        \"-cp\",\n        signingClassPath.map(_.getAbsolutePath).mkString(File.pathSeparator),\n        signingMainClass\n      )\n\n      command.flatMap(_.value)\n    }\n    else {\n      val platformSuffix  = FetchExternalBinary.platformSuffix()\n      val (tag, changing) =\n        if (version == \"latest\") (\"launchers\", true)\n        else (\"v\" + version, false)\n      val ext = if (Properties.isWin) \".zip\" else \".gz\"\n      val url =\n        s\"https://github.com/VirtusLab/scala-cli-signing/releases/download/$tag/scala-cli-signing-$platformSuffix$ext\"\n      val params = ExternalBinaryParams(\n        url,\n        changing,\n        \"scala-cli-signing\",\n        Seq(jvmSigningDep),\n        signingMainClass\n      )\n      val binary: ExternalBinary = value {\n        FetchExternalBinary.fetch(params, archiveCache, logger, javaCommand)\n      }\n      binary.command\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalOptions.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.{\n  CoursierOptions,\n  GlobalOptions,\n  HasGlobalOptions,\n  SharedJvmOptions\n}\n\n// format: off\nfinal case class PgpExternalOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    jvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Recurse\n    scalaSigning: PgpScalaSigningOptions = PgpScalaSigningOptions()\n) extends HasGlobalOptions\n// format: on\n\nobject PgpExternalOptions {\n  implicit lazy val parser: Parser[PgpExternalOptions] = Parser.derive\n  implicit lazy val help: Help[PgpExternalOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpKeyId.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\n\nimport scala.cli.signing.commands.{PgpKeyId as OriginalPgpKeyId, PgpKeyIdOptions}\n\nobject PgpKeyId extends PgpCommand[PgpKeyIdOptions] {\n  override def names: List[List[String]] = PgpCommandNames.pgpKeyId\n\n  override def run(options: PgpKeyIdOptions, args: RemainingArgs): Unit =\n    OriginalPgpKeyId.run(options, args)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpKeyIdExternal.scala",
    "content": "package scala.cli.commands.pgp\n\nimport scala.cli.signing.commands.PgpKeyIdOptions\n\nclass PgpKeyIdExternal extends PgpExternalCommand {\n  override def hidden = true\n  def actualHelp      = PgpKeyIdOptions.help\n  def externalCommand = Seq(\"pgp\", \"key-id\")\n\n  override def names = PgpCommandNames.pgpKeyId\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpProxy.scala",
    "content": "package scala.cli.commands.pgp\n\nimport coursier.cache.FileCache\nimport coursier.util.Task\n\nimport scala.build.errors.BuildException\nimport scala.build.{Logger, options as bo}\nimport scala.cli.commands.shared.{CoursierOptions, SharedJvmOptions}\nimport scala.cli.errors.PgpError\nimport scala.util.Properties\n\n/** A proxy running the PGP operations externally using scala-cli-singing. This is done either using\n  * it's native image launchers or running it in a JVM process. This construct is not used when PGP\n  * commands are evoked from CLI (see [[PgpCommandsSubst]] and [[PgpCommands]]), but rather when PGP\n  * operations are used internally. <br>\n  *\n  * This is the 'native' counterpart of [[PgpProxyJvm]]\n  */\nclass PgpProxy {\n  def createKey(\n    pubKey: String,\n    secKey: String,\n    mail: String,\n    quiet: Boolean,\n    passwordOpt: Option[String],\n    cache: FileCache[Task],\n    logger: Logger,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, Int] = {\n\n    val (passwordOption, extraEnv) = passwordOpt match\n      case Some(value) =>\n        (\n          Seq(\"--password\", s\"env:SCALA_CLI_RANDOM_KEY_PASSWORD\"),\n          Map(\"SCALA_CLI_RANDOM_KEY_PASSWORD\" -> value)\n        )\n      case None => (Nil, Map.empty)\n    val quietOptions = if quiet then Seq(\"--quiet\") else Nil\n    (new PgpCreateExternal).tryRun(\n      cache,\n      Seq(\n        \"pgp\",\n        \"create\",\n        \"--pub-dest\",\n        pubKey,\n        \"--secret-dest\",\n        secKey,\n        \"--email\",\n        mail\n      ) ++ passwordOption ++ quietOptions,\n      extraEnv,\n      logger,\n      allowExecve = false,\n      jvmOptions,\n      coursierOptions,\n      signingCliOptions\n    )\n  }\n\n  def keyId(\n    key: String,\n    keyPrintablePath: String,\n    cache: FileCache[Task],\n    logger: Logger,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, String] = {\n    val keyPath =\n      if (Properties.isWin)\n        os.temp(key, prefix = \"key\", suffix = \".pub\")\n      else\n        os.temp(key, prefix = \"key\", suffix = \".pub\", perms = \"rwx------\")\n    val maybeRawOutput =\n      try {\n        (new PgpKeyIdExternal).output(\n          cache,\n          Seq(keyPath.toString),\n          Map(),\n          logger,\n          jvmOptions,\n          coursierOptions,\n          signingCliOptions\n        ).map(_.trim)\n      }\n      finally os.remove(keyPath)\n    maybeRawOutput.flatMap { rawOutput =>\n      if (rawOutput.isEmpty)\n        Left(new PgpError(s\"No public key found in $keyPrintablePath\"))\n      else\n        Right(rawOutput)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpProxyJvm.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\nimport coursier.cache.FileCache\nimport coursier.util.Task\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.errors.BuildException\nimport scala.build.{Logger, options as bo}\nimport scala.cli.commands.shared.{CoursierOptions, SharedJvmOptions}\nimport scala.cli.errors.PgpError\nimport scala.cli.signing.commands.{PgpCreate, PgpCreateOptions, PgpKeyId}\nimport scala.cli.signing.shared.{PasswordOption, Secret}\n\n/** A proxy running the PGP operations using scala-cli-singing as a dependency. This construct is\n  * not used when PGP commands are evoked from CLI (see [[PgpCommandsSubst]] and [[PgpCommands]]),\n  * but rather when PGP operations are used internally. <br>\n  *\n  * This is the 'JVM' counterpart of [[PgpProxy]]\n  */\nclass PgpProxyJvm extends PgpProxy {\n  override def createKey(\n    pubKey: String,\n    secKey: String,\n    mail: String,\n    quiet: Boolean,\n    passwordOpt: Option[String],\n    cache: FileCache[Task],\n    logger: Logger,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, Int] = {\n\n    val password = passwordOpt.map(password => PasswordOption.Value(Secret(password)))\n\n    PgpCreate.tryRun(\n      PgpCreateOptions(\n        email = mail,\n        password = password,\n        pubDest = Some(pubKey),\n        secretDest = Some(secKey),\n        quiet = quiet\n      ),\n      RemainingArgs(Seq(), Nil)\n    )\n    Right(0)\n  }\n\n  override def keyId(\n    key: String,\n    keyPrintablePath: String,\n    cache: FileCache[Task],\n    logger: Logger,\n    jvmOptions: SharedJvmOptions,\n    coursierOptions: CoursierOptions,\n    signingCliOptions: bo.ScalaSigningCliOptions\n  ): Either[BuildException, String] =\n    PgpKeyId.get(key.getBytes(StandardCharsets.UTF_8), fingerprint = false)\n      .headOption\n      .toRight {\n        new PgpError(s\"No public key found in $keyPrintablePath\")\n      }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpProxyMaker.scala",
    "content": "package scala.cli.commands.pgp\n\n/** Used for choosing the right PGP proxy implementation when Scala CLI is run on JVM. <br>\n  *\n  * See [[scala.cli.internal.PgpProxyMakerSubst PgpProxyMakerSubst]]\n  */\nclass PgpProxyMaker {\n  def get(forceSigningExternally: java.lang.Boolean): PgpProxy =\n    if (forceSigningExternally)\n      new PgpProxy\n    else\n      new PgpProxyJvm\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPull.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\n\nimport scala.build.Logger\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.util.ScalaCliSttpBackend\n\nobject PgpPull extends ScalaCommand[PgpPullOptions] {\n\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL\n  override def names                   = List(\n    List(\"pgp\", \"pull\")\n  )\n\n  override def runCommand(options: PgpPullOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val backend = ScalaCliSttpBackend.httpURLConnection(logger)\n\n    val keyServerUri = options.shared.keyServerUriOptOrExit(logger).getOrElse {\n      KeyServer.default\n    }\n\n    val all = args.all\n\n    if (!options.allowEmpty && all.isEmpty) {\n      System.err.println(\"No key passed as argument.\")\n      sys.exit(1)\n    }\n\n    // val lookupEndpoint = keyServerUri\n\n    for (keyId <- all)\n      KeyServer.check(keyId, keyServerUri, backend) match {\n        case Left(err) =>\n          System.err.println(s\"Error checking $keyId: $err\")\n          sys.exit(1)\n        case Right(Right(content)) =>\n          println(content)\n        case Right(Left(message)) =>\n          if (logger.verbosity >= 0)\n            System.err.println(s\"Key $keyId not found: $message\")\n          sys.exit(1)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPullOptions.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup}\n\n// format: off\nfinal case class PgpPullOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    shared: SharedPgpPushPullOptions = SharedPgpPushPullOptions(),\n  @Group(HelpGroup.PGP.toString)\n  @HelpMessage(\"Whether to exit with code 0 if no key is passed\")\n    allowEmpty: Boolean = false\n) extends HasGlobalOptions\n// format: on\n\nobject PgpPullOptions {\n  implicit lazy val parser: Parser[PgpPullOptions] = Parser.derive\n  implicit lazy val help: Help[PgpPullOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPush.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\n\nimport scala.build.Logger\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.util.ScalaCliSttpBackend\n\nobject PgpPush extends ScalaCommand[PgpPushOptions] {\n\n  override def hidden                  = true\n  override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL\n  override def names                   = List(\n    List(\"pgp\", \"push\")\n  )\n\n  override def runCommand(options: PgpPushOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val backend = ScalaCliSttpBackend.httpURLConnection(logger)\n\n    val keyServerUri = options.shared.keyServerUriOptOrExit(logger).getOrElse {\n      KeyServer.default\n    }\n\n    val all = args.all\n\n    if (!options.allowEmpty && all.isEmpty) {\n      System.err.println(\"No key passed as argument.\")\n      sys.exit(1)\n    }\n\n    lazy val coursierCache = options.coursier.coursierCache(logger)\n\n    for (key <- all) {\n      val path = os.Path(key, os.pwd)\n      if (!os.exists(path)) {\n        System.err.println(s\"Error: $key not found\")\n        sys.exit(1)\n      }\n      val keyContent = os.read(path)\n\n      val keyId =\n        (new PgpProxyMaker).get(\n          options.scalaSigning.forceSigningExternally.getOrElse(false)\n        ).keyId(\n          keyContent,\n          key,\n          coursierCache,\n          logger,\n          options.jvm,\n          options.coursier,\n          options.scalaSigning.cliOptions()\n        )\n          .orExit(logger)\n\n      if (keyId.isEmpty)\n        if (options.force) {\n          if (logger.verbosity >= 0)\n            System.err.println(\n              s\"Warning: $key doesn't look like a PGP public key, proceeding anyway.\"\n            )\n        }\n        else {\n          System.err.println(\n            s\"Error: $key doesn't look like a PGP public key. \" +\n              \"Use --force to force uploading it anyway.\"\n          )\n          sys.exit(1)\n        }\n\n      val res = KeyServer.add(\n        keyContent,\n        keyServerUri,\n        backend\n      )\n\n      res match {\n        case Left(error) =>\n          System.err.println(s\"Error uploading key to $keyServerUri.\")\n          if (logger.verbosity >= 0)\n            System.err.println(s\"Server response: $error\")\n          sys.exit(1)\n        case Right(_) =>\n          val name =\n            if (keyId.isEmpty) key\n            else \"0x\" + keyId.stripPrefix(\"0x\")\n          logger.message(s\"Key $name uploaded to $keyServerUri\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPushOptions.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.*\n\n// format: off\nfinal case class PgpPushOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    shared: SharedPgpPushPullOptions = SharedPgpPushPullOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Recurse\n    jvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    scalaSigning: PgpScalaSigningOptions = PgpScalaSigningOptions(),\n\n  @Group(HelpGroup.PGP.toString)\n  @HelpMessage(\"Try to push the key even if Scala CLI thinks it's not a public key\")\n  @ExtraName(\"f\")\n    force: Boolean = false,\n  @Group(HelpGroup.PGP.toString)\n  @HelpMessage(\"Whether to exit with code 0 if no key is passed\")\n    allowEmpty: Boolean = false,\n) extends HasGlobalOptions\n// format: on\n\nobject PgpPushOptions {\n  implicit lazy val parser: Parser[PgpPushOptions] = Parser.derive\n  implicit lazy val help: Help[PgpPushOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpScalaSigningOptions.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\n\nimport scala.build.internal.Constants\nimport scala.build.options as bo\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class PgpScalaSigningOptions(\n  @Group(HelpGroup.Signing.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(s\"scala-cli-signing version when running externally (${Constants.scalaCliSigningVersion} by default)\")\n  @Hidden\n    signingCliVersion: Option[String] = None,\n  @Group(HelpGroup.Signing.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Pass arguments to the Java command when running scala-cli-singing externally on JVM\")\n  @ValueDescription(\"option\")\n  @Hidden\n    signingCliJavaArg: List[String] = Nil,\n  @Group(HelpGroup.Signing.toString)\n  @HelpMessage(\"When running Scala CLI on the JVM, force running scala-cli-singing externally\")\n  @Hidden\n  @Tag(tags.restricted)\n    forceSigningExternally: Option[Boolean] = None,\n  @Group(HelpGroup.Signing.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"When running Scala CLI on the JVM, force running scala-cli-singing using a native launcher or a JVM launcher\")\n  @Hidden\n    forceJvmSigningCli: Option[Boolean] = None\n) { // format: on\n  def cliOptions(): bo.ScalaSigningCliOptions =\n    bo.ScalaSigningCliOptions(\n      javaArgs = signingCliJavaArg,\n      forceExternal = forceSigningExternally,\n      forceJvm = forceJvmSigningCli,\n      signingCliVersion = signingCliVersion\n    )\n}\n\nobject PgpScalaSigningOptions {\n  implicit lazy val parser: Parser[PgpScalaSigningOptions] = Parser.derive\n  implicit lazy val help: Help[PgpScalaSigningOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpSign.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\n\nimport scala.cli.signing.commands.{PgpSign as OriginalPgpSign, PgpSignOptions}\n\nobject PgpSign extends PgpCommand[PgpSignOptions] {\n  override def names = PgpCommandNames.pgpSign\n\n  override def run(options: PgpSignOptions, args: RemainingArgs): Unit =\n    OriginalPgpSign.run(options, args)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpSignExternal.scala",
    "content": "package scala.cli.commands.pgp\n\nimport scala.cli.signing.commands.PgpSignOptions\n\nclass PgpSignExternal extends PgpExternalCommand {\n  override def hidden = true\n  def actualHelp      = PgpSignOptions.help\n  def externalCommand = Seq(\"pgp\", \"sign\")\n\n  override def names = PgpCommandNames.pgpSign\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpVerify.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.core.RemainingArgs\n\nimport scala.cli.signing.commands.{PgpVerify as OriginalPgpVerify, PgpVerifyOptions}\n\nobject PgpVerify extends PgpCommand[PgpVerifyOptions] {\n  override def names = PgpCommandNames.pgpVerify\n\n  override def run(options: PgpVerifyOptions, args: RemainingArgs): Unit =\n    OriginalPgpVerify.run(options, args)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/PgpVerifyExternal.scala",
    "content": "package scala.cli.commands.pgp\n\nimport scala.cli.signing.commands.PgpVerifyOptions\n\nclass PgpVerifyExternal extends PgpExternalCommand {\n  override def hidden = true\n  def actualHelp      = PgpVerifyOptions.help\n  def externalCommand = Seq(\"pgp\", \"verify\")\n\n  override def names = PgpCommandNames.pgpVerify\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/pgp/SharedPgpPushPullOptions.scala",
    "content": "package scala.cli.commands.pgp\n\nimport caseapp.*\nimport sttp.model.Uri\n\nimport scala.build.Logger\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedPgpPushPullOptions(\n  @Group(HelpGroup.PGP.toString)\n  @HelpMessage(\"Key server to push / pull keys from\")\n  @ValueDescription(\"URL\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    keyServer: List[String] = Nil\n) {\n  // format: on\n\n  def keyServerUriOptOrExit(logger: Logger): Option[Uri] =\n    keyServer\n      .filter(_.trim.nonEmpty)\n      .lastOption\n      .map { addr =>\n        Uri.parse(addr) match {\n          case Left(err) =>\n            if (logger.verbosity >= 0)\n              System.err.println(s\"Error parsing key server address '$addr': $err\")\n            sys.exit(1)\n          case Right(uri) => uri\n        }\n      }\n}\n\nobject SharedPgpPushPullOptions {\n  implicit lazy val parser: Parser[SharedPgpPushPullOptions] = Parser.derive\n  implicit lazy val help: Help[SharedPgpPushPullOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/ConfigUtil.scala",
    "content": "package scala.cli.commands.publish\n\nimport scala.build.errors.ConfigDbException\n\nobject ConfigUtil {\n\n  extension [T](sec: scala.cli.signing.shared.Secret[T]) {\n    def toConfig: scala.cli.config.Secret[T] =\n      scala.cli.config.Secret(sec.value)\n  }\n  extension [T](sec: scala.cli.config.Secret[T]) {\n    def toCliSigning: scala.cli.signing.shared.Secret[T] =\n      scala.cli.signing.shared.Secret(sec.value)\n  }\n  extension (opt: scala.cli.signing.shared.PasswordOption) {\n    def toConfig: scala.cli.config.PasswordOption =\n      opt match {\n        case v: scala.cli.signing.shared.PasswordOption.Value =>\n          scala.cli.config.PasswordOption.Value(v.value.toConfig)\n        case v: scala.cli.signing.shared.PasswordOption.Env =>\n          scala.cli.config.PasswordOption.Env(v.name)\n        case v: scala.cli.signing.shared.PasswordOption.File =>\n          scala.cli.config.PasswordOption.File(v.path.toNIO)\n        case v: scala.cli.signing.shared.PasswordOption.Command =>\n          scala.cli.config.PasswordOption.Command(v.command)\n      }\n  }\n  extension (opt: scala.cli.config.PasswordOption) {\n    def toCliSigning: scala.cli.signing.shared.PasswordOption =\n      opt match {\n        case v: scala.cli.config.PasswordOption.Value =>\n          scala.cli.signing.shared.PasswordOption.Value(v.value.toCliSigning)\n        case v: scala.cli.config.PasswordOption.Env =>\n          scala.cli.signing.shared.PasswordOption.Env(v.name)\n        case v: scala.cli.config.PasswordOption.File =>\n          scala.cli.signing.shared.PasswordOption.File(os.Path(v.path, os.pwd))\n        case v: scala.cli.config.PasswordOption.Command =>\n          scala.cli.signing.shared.PasswordOption.Command(v.command)\n      }\n  }\n\n  extension [T](value: Either[Exception, T]) {\n    def wrapConfigException: Either[ConfigDbException, T] =\n      value.left.map(ConfigDbException(_))\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/GitRepo.scala",
    "content": "package scala.cli.commands.publish\n\nimport org.eclipse.jgit.api.Git\n\nimport scala.build.Logger\nimport scala.jdk.CollectionConverters.*\nimport scala.util.{Properties, Using}\n\nobject GitRepo {\n\n  private lazy val user                       = os.owner(os.home)\n  private def trusted(path: os.Path): Boolean =\n    if (Properties.isWin)\n      path.toIO.canWrite()\n    else\n      os.owner(path) == user\n\n  def gitRepoOpt(workspace: os.Path): Option[os.Path] =\n    if (trusted(workspace))\n      if (os.isDir(workspace / \".git\")) Some(workspace)\n      else if (workspace.segmentCount > 0)\n        gitRepoOpt(workspace / os.up)\n      else\n        None\n    else\n      None\n\n  def ghRepoOrgName(\n    workspace: os.Path,\n    logger: Logger\n  ): Either[GitRepoError, (String, String)] =\n    gitRepoOpt(workspace) match {\n      case Some(repo) =>\n        remotes(repo, logger) match {\n          case Seq() =>\n            Left(new GitRepoError(s\"Cannot determine GitHub organization and name for $workspace\"))\n          case Seq((_, orgName)) =>\n            Right(orgName)\n          case more =>\n            val map = more.toMap\n            map.get(\"upstream\").orElse(map.get(\"origin\")).toRight {\n              new GitRepoError(\n                s\"Cannot determine default GitHub organization and name for $workspace\"\n              )\n            }\n        }\n      case None =>\n        Left(new GitRepoError(s\"$workspace is not in a git repository\"))\n    }\n\n  private def remotes(repo: os.Path, logger: Logger): Seq[(String, (String, String))] = {\n\n    val remoteList = Using.resource(Git.open(repo.toIO)) { git =>\n      git.remoteList().call().asScala\n    }\n    logger.debug(s\"Found ${remoteList.length} remotes in Git repo $repo\")\n\n    remoteList\n      .iterator\n      .flatMap { remote =>\n        val name = remote.getName\n        remote\n          .getURIs\n          .asScala\n          .iterator\n          .map(_.toASCIIString)\n          .flatMap(maybeGhOrgName)\n          .map((name, _))\n      }\n      .toVector\n  }\n\n  def maybeGhRepoOrgName(\n    workspace: os.Path,\n    logger: Logger\n  ): Option[(String, String)] =\n    gitRepoOpt(workspace).flatMap { repo =>\n      remotes(repo, logger) match {\n        case Seq() =>\n          logger.debug(s\"No GitHub remote found in $workspace\")\n          None\n        case Seq((_, orgName)) =>\n          Some(orgName)\n        case more =>\n          val map = more.toMap\n          val res = map.get(\"upstream\").orElse(map.get(\"origin\"))\n          if (res.isEmpty)\n            new GitRepoError(\n              s\"Cannot determine default GitHub organization and name for $workspace\"\n            )\n          res\n      }\n    }\n\n  def maybeGhOrgName(uri: String): Option[(String, String)] = {\n    val httpsPattern = \"https://github.com/(.+)/(.+).git\".r\n    val sshPattern   = \"git@github.com:(.+)/(.+).git\".r\n\n    uri match {\n      case httpsPattern(org, name) => Some((org, name))\n      case sshPattern(org, name)   => Some((org, name))\n      case _                       => None\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/GitRepoError.scala",
    "content": "package scala.cli.commands.publish\n\nimport scala.build.errors.BuildException\n\nfinal class GitRepoError(message: String) extends BuildException(message)\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/Ivy.scala",
    "content": "package scala.cli.commands.publish\n\nimport coursier.core.{Configuration, MinimizedExclusions, ModuleName, Organization, Type}\nimport coursier.publish.Pom\n\nimport java.time.format.DateTimeFormatterBuilder\nimport java.time.temporal.ChronoField\nimport java.time.{LocalDateTime, ZoneOffset}\n\nimport scala.collection.mutable\nimport scala.xml.{Elem, NodeSeq}\n\nobject Ivy {\n\n  private val mavenPomNs = \"http://maven.apache.org/POM/4.0.0\"\n\n  private def ivyLicenseNodes(license: Option[Pom.License]): NodeSeq =\n    license match {\n      case None    => NodeSeq.Empty\n      case Some(l) =>\n        val u = l.url.trim\n        if u.nonEmpty then\n          scala.xml.NodeSeq.fromSeq(Seq(<license name={l.name} url={u}/>))\n        else\n          scala.xml.NodeSeq.fromSeq(Seq(<license name={l.name}/>))\n    }\n\n  private def mavenScmNodes(scm: Option[Pom.Scm]): NodeSeq =\n    scm match {\n      case None    => NodeSeq.Empty\n      case Some(s) =>\n        val url        = s.url.trim\n        val connection = s.connection.trim\n        val devConn    = s.developerConnection.trim\n        val children   = Seq(\n          Option.when(url.nonEmpty)(<m:url>{url}</m:url>),\n          Option.when(connection.nonEmpty)(<m:connection>{connection}</m:connection>),\n          Option.when(devConn.nonEmpty)(\n            <m:developerConnection>{devConn}</m:developerConnection>\n          )\n        ).flatten\n        if children.isEmpty then NodeSeq.Empty\n        else scala.xml.NodeSeq.fromSeq(Seq(<m:scm>{children}</m:scm>))\n    }\n\n  private def mavenDeveloperNodes(developers: Seq[Pom.Developer]): NodeSeq =\n    if (developers.isEmpty) NodeSeq.Empty\n    else {\n      val devElems = developers.map { d =>\n        val url   = d.url.trim\n        val parts = Seq(\n          Some(<m:id>{d.id}</m:id>),\n          Some(<m:name>{d.name}</m:name>),\n          d.mail.map(m => <m:email>{m}</m:email>),\n          Option.when(url.nonEmpty)(<m:url>{url}</m:url>)\n        ).flatten\n        <m:developer>{parts}</m:developer>\n      }\n      scala.xml.NodeSeq.fromSeq(Seq(<m:developers>{devElems}</m:developers>))\n    }\n\n  private def mavenProjectNamePackagingNodes(\n    pomProjectName: Option[String],\n    packaging: Option[Type]\n  ): NodeSeq =\n    val namePart = pomProjectName.flatMap { n =>\n      val t = n.trim\n      Option.when(t.nonEmpty)(<m:name>{t}</m:name>)\n    }\n    val packagingPart = packaging.map(p => <m:packaging>{p.value}</m:packaging>)\n    val parts         = namePart.toSeq ++ packagingPart.toSeq\n    if parts.isEmpty then NodeSeq.Empty\n    else scala.xml.NodeSeq.fromSeq(parts)\n\n  private lazy val dateFormatter = new DateTimeFormatterBuilder()\n    .appendValue(ChronoField.YEAR, 4)\n    .appendValue(ChronoField.MONTH_OF_YEAR, 2)\n    .appendValue(ChronoField.DAY_OF_MONTH, 2)\n    .appendValue(ChronoField.HOUR_OF_DAY, 2)\n    .appendValue(ChronoField.MINUTE_OF_HOUR, 2)\n    .appendValue(ChronoField.SECOND_OF_MINUTE, 2)\n    .toFormatter\n\n  /** Ivy descriptor aligned with coursier `Pom.create` metadata (license, SCM, developers, optional\n    * name/packaging).\n    */\n  def create(\n    organization: Organization,\n    moduleName: ModuleName,\n    version: String,\n    description: Option[String] = None,\n    url: Option[String] = None,\n    pomProjectName: Option[String] = None,\n    packaging: Option[Type] = None,\n    // TODO Accept full-fledged coursier.Dependency\n    dependencies: Seq[(\n      Organization,\n      ModuleName,\n      String,\n      Option[Configuration],\n      MinimizedExclusions\n    )] = Nil,\n    license: Option[Pom.License] = None,\n    scm: Option[Pom.Scm] = None,\n    developers: Seq[Pom.Developer] = Nil,\n    time: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC),\n    hasPom: Boolean = true,\n    hasDoc: Boolean = true,\n    hasSources: Boolean = true\n  ): String = {\n\n    val licenseXml       = ivyLicenseNodes(license)\n    val scmXml           = mavenScmNodes(scm)\n    val devXml           = mavenDeveloperNodes(developers)\n    val projectMetaXml   = mavenProjectNamePackagingNodes(pomProjectName, packaging)\n    val hasMavenMetadata = scmXml.nonEmpty || devXml.nonEmpty || projectMetaXml.nonEmpty\n\n    val nodes = new mutable.ListBuffer[NodeSeq]\n\n    nodes += {\n      val desc = (description, url) match {\n        case (Some(d), Some(u)) =>\n          Seq(<description homepage={u}>{d}</description>)\n        case (Some(d), None) =>\n          Seq(<description>{d}</description>)\n        case (None, Some(u)) =>\n          Seq(<description homepage={u}></description>)\n        case (None, None) =>\n          Nil\n      }\n      <info organisation={organization.value} module={moduleName.value} revision={\n        version\n      } status=\"integration\" publication={time.format(dateFormatter)}>\n        {licenseXml}\n        {desc}\n        {projectMetaXml}\n        {scmXml}\n        {devXml}\n      </info>\n    }\n\n    nodes += {\n\n      val docConf =\n        if (hasDoc) Seq(<conf name=\"docs\" visibility=\"public\" description=\"\"/>)\n        else Nil\n      val sourcesConf =\n        if (hasSources) Seq(<conf name=\"sources\" visibility=\"public\" description=\"\"/>)\n        else Nil\n      val pomConf =\n        if (hasPom) Seq(<conf name=\"pom\" visibility=\"public\" description=\"\"/>)\n        else Nil\n\n      <configurations>\n        <conf name=\"compile\" visibility=\"public\" description=\"\"/>\n        <conf name=\"runtime\" extends=\"compile\" visibility=\"public\" description=\"\"/>\n        <conf name=\"test\" extends=\"runtime\" visibility=\"public\" description=\"\"/>\n        <conf name=\"provided\" visibility=\"public\" description=\"\"/>\n        {docConf}\n        {sourcesConf}\n        {pomConf}\n      </configurations>\n    }\n\n    nodes += {\n\n      val docPub =\n        if (hasDoc) Seq(<artifact e:classifier=\"javadoc\" name={\n          moduleName.value\n        } type=\"doc\" ext=\"jar\" conf=\"docs\"/>)\n        else Nil\n      val sourcesPub =\n        if (hasSources) Seq(<artifact e:classifier=\"sources\" name={\n          moduleName.value\n        } type=\"src\" ext=\"jar\" conf=\"sources\"/>)\n        else Nil\n      val pomPub =\n        if (hasPom) Seq(<artifact name={moduleName.value} type=\"pom\" ext=\"pom\" conf=\"pom\"/>)\n        else Nil\n\n      <publications>\n        <artifact name={moduleName.value} type=\"jar\" ext=\"jar\" conf=\"compile\"/>\n        {docPub}\n        {sourcesPub}\n        {pomPub}\n      </publications>\n    }\n\n    nodes += {\n      val depNodes = dependencies.map {\n        case (org, name, ver, confOpt, exclusions) =>\n          val conf           = confOpt.map(_.value).getOrElse(\"compile\")\n          val confSpec       = s\"$conf->default(compile)\"\n          val exclusionNodes =\n            exclusions.data.toSet().map { case (org, module) =>\n              <exclude org={org.value} module={module.value}/>\n            }\n          <dependency org={org.value} name={name.value} rev={ver} conf={confSpec}>\n            {exclusionNodes}\n          </dependency>\n      }\n      <dependencies>\n        {depNodes}\n      </dependencies>\n    }\n\n    val root: Elem =\n      if (hasMavenMetadata)\n        <ivy-module version=\"2.0\" xmlns:e=\"http://ant.apache.org/ivy/extra\" xmlns:m={\n          mavenPomNs\n        }>\n          {nodes.result()}\n        </ivy-module>\n      else\n        <ivy-module version=\"2.0\" xmlns:e=\"http://ant.apache.org/ivy/extra\">\n          {nodes.result()}\n        </ivy-module>\n\n    Pom.print(root)\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/OptionCheck.scala",
    "content": "package scala.cli.commands.publish\n\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\n\n/** A check for missing options in [[PublishOptions]]\n  */\ntrait OptionCheck {\n\n  /** The \"group\" of check this check belongs to, so that users can filter them */\n  def kind: OptionCheck.Kind\n\n  /** Name of the option checked, for display / reporting purposes */\n  def fieldName: String\n\n  /** Directive name of the option checked by this check */\n  def directivePath: String\n\n  /** Checks whether the option value is missing */\n  def check(options: BPublishOptions): Boolean\n\n  /** Provides a way to compute a default value for this option, along with extra directives and\n    * GitHub secrets to be set\n    */\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue]\n}\n\nobject OptionCheck {\n\n  /** Computes a default value for a directive\n    *\n    * @param getValue\n    *   computes a default value\n    * @param extraDirectives\n    *   extra using directives to be set\n    * @param ghSecrets\n    *   GitHub secrets to be set\n    */\n  final case class DefaultValue(\n    getValue: () => Either[BuildException, Option[String]],\n    extraDirectives: Seq[(String, String)],\n    ghSecrets: Seq[SetSecret]\n  )\n\n  object DefaultValue {\n    def simple(\n      value: String,\n      extraDirectives: Seq[(String, String)],\n      ghSecrets: Seq[SetSecret]\n    ): DefaultValue =\n      DefaultValue(\n        () => Right(Some(value)),\n        extraDirectives,\n        ghSecrets\n      )\n    def empty: DefaultValue =\n      DefaultValue(\n        () => Right(None),\n        Nil,\n        Nil\n      )\n  }\n\n  sealed abstract class Kind extends Product with Serializable\n\n  object Kind {\n    case object Core       extends Kind\n    case object Extra      extends Kind\n    case object Repository extends Kind\n    case object Signing    extends Kind\n\n    val all = Seq(Core, Extra, Repository, Signing)\n\n    def parse(input: String): Option[Kind] =\n      input match {\n        case \"core\"                => Some(Core)\n        case \"extra\"               => Some(Extra)\n        case \"repo\" | \"repository\" => Some(Repository)\n        case \"signing\"             => Some(Signing)\n        case _                     => None\n      }\n    def parseList(input: String): Either[Seq[String], Seq[Kind]] = {\n      val results      = input.split(\",\").map(v => (v, parse(v))).toSeq\n      val unrecognized = results.collect {\n        case (v, None) => v\n      }\n      if (unrecognized.isEmpty)\n        Right {\n          results.collect {\n            case (_, Some(kind)) => kind\n          }\n        }\n      else\n        Left(unrecognized)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/OptionChecks.scala",
    "content": "package scala.cli.commands.publish\n\nimport coursier.cache.FileCache\nimport coursier.util.Task\nimport sttp.client3.*\n\nimport scala.build.Logger\nimport scala.cli.commands.publish.checks.*\nimport scala.cli.config.ConfigDb\n\nobject OptionChecks {\n\n  def checks(\n    options: PublishSetupOptions,\n    configDb: => ConfigDb,\n    workspace: os.Path,\n    coursierCache: FileCache[Task],\n    logger: Logger,\n    backend: SttpBackend[Identity, Any]\n  ): Seq[OptionCheck] =\n    Seq(\n      OrganizationCheck(options, workspace, logger),\n      NameCheck(options, workspace, logger),\n      ComputeVersionCheck(options, workspace, logger),\n      RepositoryCheck(options, logger),\n      UserCheck(options, () => configDb, workspace, logger),\n      PasswordCheck(options, () => configDb, workspace, logger),\n      PgpSecretKeyCheck(options, coursierCache, () => configDb, logger, backend),\n      LicenseCheck(options, logger),\n      UrlCheck(options, workspace, logger),\n      ScmCheck(options, workspace, logger),\n      DeveloperCheck(options, () => configDb, logger)\n    )\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.HelpFormat\nimport coursier.core.{Authentication, Configuration}\nimport coursier.publish.checksum.logger.InteractiveChecksumLogger\nimport coursier.publish.checksum.{ChecksumType, Checksums}\nimport coursier.publish.fileset.{FileSet, Path}\nimport coursier.publish.signing.logger.InteractiveSignerLogger\nimport coursier.publish.signing.{NopSigner, Signer}\nimport coursier.publish.upload.logger.InteractiveUploadLogger\nimport coursier.publish.upload.{DummyUpload, FileUpload, HttpURLConnectionUpload, Upload}\nimport coursier.publish.{Content, Hooks, Pom}\n\nimport java.io.{File, OutputStreamWriter}\nimport java.net.URI\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Paths\nimport java.time.{Instant, LocalDateTime, ZoneOffset}\nimport java.util.concurrent.Executors\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.compiler.ScalaCompilerMaker\nimport scala.build.errors.{BuildException, CompositeBuildException, Severity}\nimport scala.build.input.Inputs\nimport scala.build.internal.Util\nimport scala.build.internal.Util.ScalaDependencyOps\nimport scala.build.options.publish.{Developer, License, Signer as PSigner, Vcs}\nimport scala.build.options.{\n  BuildOptions,\n  ComputeVersion,\n  ConfigMonoid,\n  PublishContextualOptions,\n  ScalaSigningCliOptions,\n  Scope\n}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.package0.Package as PackageCmd\nimport scala.cli.commands.pgp.PgpScalaSigningOptions\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.publish.PublishUtils.*\nimport scala.cli.commands.shared.{\n  HelpCommandGroup,\n  HelpGroup,\n  MainClassOptions,\n  SharedOptions,\n  SharedVersionOptions\n}\nimport scala.cli.commands.util.BuildCommandHelpers\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel, WatchUtil}\nimport scala.cli.config.{ConfigDb, Keys, PasswordOption, PublishCredentials}\nimport scala.cli.errors.{\n  FailedToSignFileError,\n  InvalidSonatypePublishCredentials,\n  MalformedChecksumsError,\n  MissingPublishOptionError,\n  UploadError,\n  WrongSonatypeServerError\n}\nimport scala.cli.packaging.Library\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nimport scala.cli.util.ConfigPasswordOptionHelpers.*\nimport scala.concurrent.duration.DurationInt\nimport scala.util.control.NonFatal\n\nobject Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL\n\n  import scala.cli.commands.shared.HelpGroup.*\n\n  val primaryHelpGroups: Seq[HelpGroup] = Seq(Publishing, Signing, PGP)\n  val hiddenHelpGroups: Seq[HelpGroup]  = Seq(Scala, Java, Entrypoint, Dependency, Watch)\n\n  override def helpFormat: HelpFormat = super.helpFormat\n    .withHiddenGroups(hiddenHelpGroups)\n    .withPrimaryGroups(primaryHelpGroups)\n\n  override def group: String = HelpCommandGroup.Main.toString\n\n  override def sharedOptions(options: PublishOptions): Option[SharedOptions] =\n    Some(options.shared)\n\n  override def buildOptions(options: PublishOptions): Some[BuildOptions] =\n    Some(options.buildOptions().orExit(options.shared.logger))\n\n  def mkBuildOptions(\n    baseOptions: BuildOptions,\n    sharedVersionOptions: SharedVersionOptions,\n    publishParams: PublishParamsOptions,\n    sharedPublish: SharedPublishOptions,\n    publishRepo: PublishRepositoryOptions,\n    scalaSigning: PgpScalaSigningOptions,\n    publishConnection: PublishConnectionOptions,\n    mainClass: MainClassOptions,\n    ivy2LocalLike: Option[Boolean]\n  ): Either[BuildException, BuildOptions] = either {\n    val contextualOptions = PublishContextualOptions(\n      repository = publishRepo.publishRepository.filter(_.trim.nonEmpty),\n      repositoryIsIvy2LocalLike = ivy2LocalLike,\n      sourceJar = sharedPublish.withSources,\n      docJar = sharedPublish.doc,\n      gpgSignatureId = sharedPublish.gpgKey.map(_.trim).filter(_.nonEmpty),\n      gpgOptions = sharedPublish.gpgOption,\n      secretKey = publishParams.secretKey.map(_.configPasswordOptions()),\n      secretKeyPassword = publishParams.secretKeyPassword.map(_.configPasswordOptions()),\n      repoUser = publishRepo.user,\n      repoPassword = publishRepo.password,\n      repoRealm = publishRepo.realm,\n      signer = value {\n        sharedPublish.signer\n          .map(Positioned.commandLine)\n          .map(PSigner.parse)\n          .sequence\n      },\n      computeVersion = value {\n        sharedVersionOptions.computeVersion\n          .map(Positioned.commandLine)\n          .map(ComputeVersion.parse)\n          .sequence\n      },\n      checksums = {\n        val input = sharedPublish.checksum.flatMap(_.split(\",\")).map(_.trim).filter(_.nonEmpty)\n        if input.isEmpty then None else Some(input)\n      },\n      connectionTimeoutRetries = publishConnection.connectionTimeoutRetries,\n      connectionTimeoutSeconds = publishConnection.connectionTimeoutSeconds,\n      responseTimeoutSeconds = publishConnection.responseTimeoutSeconds,\n      stagingRepoRetries = publishConnection.stagingRepoRetries,\n      stagingRepoWaitTimeMilis = publishConnection.stagingRepoWaitTimeMilis\n    )\n    baseOptions.copy(\n      mainClass = mainClass.mainClass.filter(_.nonEmpty),\n      notForBloopOptions = baseOptions.notForBloopOptions.copy(\n        publishOptions = baseOptions.notForBloopOptions.publishOptions.copy(\n          organization =\n            publishParams.organization.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine),\n          name = publishParams.name.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine),\n          moduleName =\n            publishParams.moduleName.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine),\n          version =\n            sharedVersionOptions.projectVersion.map(_.trim).filter(_.nonEmpty).map(\n              Positioned.commandLine\n            ),\n          url = publishParams.url.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine),\n          license = value {\n            publishParams.license\n              .map(_.trim).filter(_.nonEmpty)\n              .map(Positioned.commandLine)\n              .map(License.parse)\n              .sequence\n          },\n          versionControl = value {\n            publishParams.vcs\n              .map(_.trim).filter(_.nonEmpty)\n              .map(Positioned.commandLine)\n              .map(Vcs.parse)\n              .sequence\n          },\n          description = publishParams.description.map(_.trim).filter(_.nonEmpty),\n          developers = value {\n            publishParams.developer\n              .filter(_.trim.nonEmpty)\n              .map(Positioned.commandLine)\n              .map(Developer.parse)\n              .sequence\n              .left.map(CompositeBuildException(_))\n          },\n          scalaVersionSuffix = sharedPublish.scalaVersionSuffix.map(_.trim),\n          scalaPlatformSuffix = sharedPublish.scalaPlatformSuffix.map(_.trim),\n          local = ConfigMonoid.sum(Seq(\n            baseOptions.notForBloopOptions.publishOptions.local,\n            if publishParams.isCi then PublishContextualOptions() else contextualOptions\n          )),\n          ci = ConfigMonoid.sum(Seq(\n            baseOptions.notForBloopOptions.publishOptions.ci,\n            if publishParams.isCi then contextualOptions else PublishContextualOptions()\n          )),\n          signingCli = ScalaSigningCliOptions(\n            signingCliVersion = scalaSigning.signingCliVersion,\n            forceExternal = scalaSigning.forceSigningExternally,\n            forceJvm = scalaSigning.forceJvmSigningCli,\n            javaArgs = scalaSigning.signingCliJavaArg\n          )\n        )\n      )\n    )\n  }\n\n  def maybePrintLicensesAndExit(params: PublishParamsOptions): Unit =\n    if params.license.contains(\"list\") then {\n      for (l <- scala.build.internal.Licenses.list)\n        println(s\"${l.id}: ${l.name} (${l.url})\")\n      sys.exit(0)\n    }\n\n  def maybePrintChecksumsAndExit(options: SharedPublishOptions): Unit =\n    if options.checksum.contains(\"list\") then {\n      for (t <- ChecksumType.all)\n        println(t.name)\n      sys.exit(0)\n    }\n\n  override def runCommand(options: PublishOptions, args: RemainingArgs, logger: Logger): Unit = {\n    maybePrintLicensesAndExit(options.publishParams)\n    maybePrintChecksumsAndExit(options.sharedPublish)\n\n    val baseOptions = buildOptionsOrExit(options)\n    val inputs      = options.shared.inputs(args.all).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n\n    val initialBuildOptions = mkBuildOptions(\n      baseOptions = baseOptions,\n      sharedVersionOptions = options.shared.sharedVersionOptions,\n      publishParams = options.publishParams,\n      sharedPublish = options.sharedPublish,\n      publishRepo = options.publishRepo,\n      scalaSigning = options.signingCli,\n      publishConnection = options.connectionOptions,\n      mainClass = options.mainClass,\n      ivy2LocalLike = options.ivy2LocalLike\n    ).orExit(logger)\n    val threads = BuildThreads.create()\n\n    val compilerMaker       = options.shared.compilerMaker(threads)\n    val docCompilerMakerOpt = options.sharedPublish.docCompilerMakerOpt\n\n    val cross = options.compileCross.cross.getOrElse(false)\n\n    lazy val configDb = ConfigDbUtils.configDb.orExit(logger)\n\n    lazy val workingDir = options.sharedPublish.workingDir\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n      .getOrElse {\n        os.temp.dir(\n          prefix = \"scala-cli-publish-\",\n          deleteOnExit = true\n        )\n      }\n\n    val ivy2HomeOpt = options.sharedPublish.ivy2Home\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n\n    doRun(\n      inputs,\n      logger,\n      initialBuildOptions,\n      compilerMaker,\n      docCompilerMakerOpt,\n      cross,\n      workingDir,\n      ivy2HomeOpt,\n      publishLocal = false,\n      m2Local = false,\n      m2HomeOpt = None,\n      forceSigningExternally = options.signingCli.forceSigningExternally.getOrElse(false),\n      parallelUpload = options.parallelUpload,\n      options.watch.watch,\n      isCi = options.publishParams.isCi,\n      () => configDb,\n      options.mainClass,\n      dummy = options.sharedPublish.dummy,\n      buildTests = options.shared.scope.test.getOrElse(false)\n    )\n  }\n\n  /** Build artifacts\n    */\n  def doRun(\n    inputs: Inputs,\n    logger: Logger,\n    initialBuildOptions: BuildOptions,\n    compilerMaker: ScalaCompilerMaker,\n    docCompilerMaker: Option[ScalaCompilerMaker],\n    cross: Boolean,\n    workingDir: => os.Path,\n    ivy2HomeOpt: Option[os.Path],\n    publishLocal: Boolean,\n    m2Local: Boolean = false,\n    m2HomeOpt: Option[os.Path] = None,\n    forceSigningExternally: Boolean,\n    parallelUpload: Option[Boolean],\n    watch: Boolean,\n    isCi: Boolean,\n    configDb: () => ConfigDb,\n    mainClassOptions: MainClassOptions,\n    dummy: Boolean,\n    buildTests: Boolean\n  ): Unit = {\n    val actionableDiagnostics = configDb().get(Keys.actions).getOrElse(None)\n\n    if watch then {\n      val watcher = Build.watch(\n        inputs = inputs,\n        options = initialBuildOptions,\n        compilerMaker = compilerMaker,\n        docCompilerMakerOpt = docCompilerMaker,\n        logger = logger,\n        crossBuilds = cross,\n        buildTests = buildTests,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics,\n        postAction = () => WatchUtil.printWatchMessage()\n      ) {\n        _.orReport(logger).foreach { builds =>\n          maybePublish(\n            builds = builds,\n            workingDir = workingDir,\n            ivy2HomeOpt = ivy2HomeOpt,\n            publishLocal = publishLocal,\n            m2Local = m2Local,\n            m2HomeOpt = m2HomeOpt,\n            logger = logger,\n            allowExit = false,\n            forceSigningExternally = forceSigningExternally,\n            parallelUpload = parallelUpload,\n            isCi = isCi,\n            configDb = configDb,\n            mainClassOptions = mainClassOptions,\n            withTestScope = buildTests,\n            dummy = dummy\n          )\n        }\n      }\n      try WatchUtil.waitForCtrlC(() => watcher.schedule())\n      finally watcher.dispose()\n    }\n    else {\n      val builds =\n        Build.build(\n          inputs = inputs,\n          options = initialBuildOptions,\n          compilerMaker = compilerMaker,\n          docCompilerMakerOpt = docCompilerMaker,\n          logger = logger,\n          crossBuilds = cross,\n          buildTests = buildTests,\n          partial = None,\n          actionableDiagnostics = actionableDiagnostics\n        ).orExit(logger)\n      maybePublish(\n        builds = builds,\n        workingDir = workingDir,\n        ivy2HomeOpt = ivy2HomeOpt,\n        publishLocal = publishLocal,\n        m2Local = m2Local,\n        m2HomeOpt = m2HomeOpt,\n        logger = logger,\n        allowExit = true,\n        forceSigningExternally = forceSigningExternally,\n        parallelUpload = parallelUpload,\n        isCi = isCi,\n        configDb = configDb,\n        mainClassOptions = mainClassOptions,\n        withTestScope = buildTests,\n        dummy = dummy\n      )\n    }\n  }\n\n  /** Check if all builds are successful and proceed with preparing files to be uploaded OR print\n    * main classes if the option is specified\n    */\n  private def maybePublish(\n    builds: Builds,\n    workingDir: os.Path,\n    ivy2HomeOpt: Option[os.Path],\n    publishLocal: Boolean,\n    m2Local: Boolean,\n    m2HomeOpt: Option[os.Path],\n    logger: Logger,\n    allowExit: Boolean,\n    forceSigningExternally: Boolean,\n    parallelUpload: Option[Boolean],\n    isCi: Boolean,\n    configDb: () => ConfigDb,\n    mainClassOptions: MainClassOptions,\n    withTestScope: Boolean,\n    dummy: Boolean\n  ): Unit = {\n    val allOk = builds.all.forall {\n      case _: Build.Successful => true\n      case _: Build.Cancelled  => false\n      case _: Build.Failed     => false\n    }\n    if allOk then logger.log(\"All standard builds ok...\")\n    else {\n      val failedBuilds    = builds.all.filterNot(_.success)\n      val cancelledBuilds = builds.all.filter(_.cancelled)\n      logger.log(\n        s\"Some standard builds were not successful (${failedBuilds.length} failed, ${cancelledBuilds.length} cancelled).\"\n      )\n    }\n    val allDocsOk = builds.allDoc.forall {\n      case _: Build.Successful => true\n      case _: Build.Cancelled  => true\n      case _: Build.Failed     => false\n    }\n    if allDocsOk then logger.log(\"All doc builds ok...\")\n    else {\n      val failedBuilds    = builds.allDoc.filterNot(_.success)\n      val cancelledBuilds = builds.allDoc.filter(_.cancelled)\n      logger.log(\n        s\"Some doc builds were not successful (${failedBuilds.length} failed, ${cancelledBuilds.length} cancelled).\"\n      )\n    }\n    if allOk && allDocsOk then {\n      val builds0 = builds.all.collect {\n        case s: Build.Successful => s\n      }\n      val docBuilds0 = builds.allDoc.collect {\n        case s: Build.Successful => s\n      }\n      val res: Either[BuildException, Unit] =\n        builds.builds match {\n          case b if b.forall(_.success) && mainClassOptions.mainClassLs.contains(true) =>\n            mainClassOptions.maybePrintMainClasses(\n              mainClasses = builds0.flatMap(_.foundMainClasses()).distinct,\n              shouldExit = allowExit\n            )\n          case _ => prepareFilesAndUpload(\n              builds = builds0,\n              docBuilds = docBuilds0,\n              workingDir = workingDir,\n              ivy2HomeOpt = ivy2HomeOpt,\n              publishLocal = publishLocal,\n              m2Local = m2Local,\n              m2HomeOpt = m2HomeOpt,\n              logger = logger,\n              forceSigningExternally = forceSigningExternally,\n              parallelUpload = parallelUpload,\n              withTestScope = withTestScope,\n              isCi = isCi,\n              configDb = configDb,\n              dummy = dummy\n            )\n        }\n      if allowExit then res.orExit(logger) else res.orReport(logger)\n    }\n    else {\n      val msg = if allOk then \"Scaladoc generation failed\" else \"Compilation failed\"\n      System.err.println(msg)\n      if allowExit then sys.exit(1)\n    }\n  }\n\n  private def buildFileSet(\n    builds: Seq[Build.Successful],\n    docBuilds: Seq[Build.Successful],\n    workingDir: os.Path,\n    now: Instant,\n    isIvy2LocalLike: Boolean,\n    isCi: Boolean,\n    isSonatype: Boolean,\n    withTestScope: Boolean,\n    logger: Logger\n  ): Either[BuildException, (FileSet, (coursier.core.Module, String))] = either {\n    logger.debug(s\"Preparing project ${builds.head.project.projectName}\")\n\n    val publishOptions = builds.head.options.notForBloopOptions.publishOptions\n\n    val ArtifactData(org, moduleName, ver) = value {\n      publishOptions.artifactData(\n        workspace = builds.head.inputs.workspace,\n        logger = logger,\n        scalaArtifactsOpt = builds.head.artifacts.scalaOpt,\n        isCi = isCi\n      )\n    }\n\n    logger.message(s\"Publishing $org:$moduleName:$ver\")\n\n    val mainJar: os.Path = {\n      val mainClassOpt: Option[String] =\n        (builds.head.options.mainClass.filter(_.nonEmpty) match {\n          case Some(cls) => Right(cls)\n          case None      =>\n            val potentialMainClasses = builds.flatMap(_.foundMainClasses()).distinct\n            builds\n              .map { build =>\n                build.retainedMainClass(logger, potentialMainClasses)\n                  .map(mainClass => build.scope -> mainClass)\n              }\n              .sequence\n              .left\n              .map(CompositeBuildException(_))\n              .map(_.toMap)\n              .map { retainedMainClassesByScope =>\n                if retainedMainClassesByScope.size == 1 then retainedMainClassesByScope.head._2\n                else\n                  retainedMainClassesByScope\n                    .get(Scope.Main)\n                    .orElse(retainedMainClassesByScope.get(Scope.Test))\n                    .get\n              }\n\n        }).toOption\n      logger.debug(s\"Retained main class: ${mainClassOpt.getOrElse(\"(no main class found)\")}\")\n      val libraryJar: os.Path = Library.libraryJar(builds, mainClassOpt)\n      val dest: os.Path       = workingDir / org / s\"$moduleName-$ver.jar\"\n      logger.debug(s\"Copying library jar from $libraryJar to $dest...\")\n      os.copy.over(libraryJar, dest, createFolders = true)\n      logger.log(s\"Successfully copied library jar from $libraryJar to $dest\")\n      dest\n    }\n\n    val sourceJarOpt =\n      if publishOptions.contextual(isCi).sourceJar.getOrElse(true) then {\n        val content            = PackageCmd.sourceJar(builds, now.toEpochMilli)\n        val sourceJar: os.Path = workingDir / org / s\"$moduleName-$ver-sources.jar\"\n        logger.debug(s\"Saving source jar to $sourceJar...\")\n        os.write.over(sourceJar, content, createFolders = true)\n        logger.log(s\"Successfully saved source jar to $sourceJar\")\n        Some(sourceJar)\n      }\n      else None\n\n    val docJarOpt =\n      if publishOptions.contextual(isCi).docJar.getOrElse(true) then\n        docBuilds match {\n          case Nil       => None\n          case docBuilds =>\n            val docJarPath: os.Path = value(PackageCmd.docJar(\n              builds = docBuilds,\n              logger = logger,\n              extraArgs = Nil,\n              withTestScope = withTestScope\n            ))\n            val docJar: os.Path = workingDir / org / s\"$moduleName-$ver-javadoc.jar\"\n            logger.debug(s\"Copying doc jar from $docJarPath to $docJar...\")\n            os.copy.over(docJarPath, docJar, createFolders = true)\n            logger.log(s\"Successfully copied doc jar from $docJarPath to $docJar\")\n            Some(docJar)\n        }\n      else None\n\n    val dependencies = builds.flatMap(_.artifacts.userDependencies)\n      .map(_.toCs(builds.head.artifacts.scalaOpt.map(_.params)))\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .orExit(logger)\n      .map { dep0 =>\n        val config =\n          builds -> builds.length match {\n            case (b, 1) if b.head.scope != Scope.Main => Some(Configuration(b.head.scope.name))\n            case _                                    => None\n          }\n        logger.debug(\n          s\"Dependency ${dep0.module.organization}:${dep0.module.name}:${dep0.versionConstraint.asString}\"\n        )\n        (\n          dep0.module.organization,\n          dep0.module.name,\n          dep0.versionConstraint.asString,\n          config,\n          dep0.minimizedExclusions\n        )\n      }\n    val url = publishOptions.url.map(_.value)\n    logger.debug(s\"Published project URL: ${url.getOrElse(\"(not set)\")}\")\n    val license = publishOptions.license.map(_.value).map { l =>\n      Pom.License(l.name, l.url)\n    }\n    logger.debug(s\"Published project license: ${license.map(_.name).getOrElse(\"(not set)\")}\")\n    val scm = publishOptions.versionControl.map { vcs =>\n      Pom.Scm(vcs.url, vcs.connection, vcs.developerConnection)\n    }\n    logger.debug(s\"Published project SCM: ${scm.map(_.url).getOrElse(\"(not set)\")}\")\n    val developers = publishOptions.developers.map { dev =>\n      Pom.Developer(dev.id, dev.name, dev.url, dev.mail)\n    }\n    logger.debug(s\"Published project developers: ${developers.map(_.name).mkString(\", \")}\")\n    val description = publishOptions.description.getOrElse(moduleName)\n    logger.debug(s\"Published project description: $description\")\n\n    val pomProjectName = publishOptions.pomProjectNameForMaven(moduleName)\n\n    val pomContent = Pom.create(\n      organization = coursier.Organization(org),\n      moduleName = coursier.ModuleName(moduleName),\n      version = ver,\n      packaging = None,\n      url = url,\n      name = Some(pomProjectName),\n      dependencies = dependencies,\n      description = Some(description),\n      license = license,\n      scm = scm,\n      developers = developers\n    )\n\n    if isSonatype then {\n      if url.isEmpty then\n        logger.diagnostic(\n          \"Publishing to Sonatype, but project URL is empty (set it with the '//> using publish.url' directive).\"\n        )\n      if license.isEmpty then\n        logger.diagnostic(\n          \"Publishing to Sonatype, but license is empty (set it with the '//> using publish.license' directive).\"\n        )\n      if scm.isEmpty then\n        logger.diagnostic(\n          \"Publishing to Sonatype, but SCM details are empty (set them with the '//> using publish.scm' directive).\"\n        )\n      if developers.isEmpty then\n        logger.diagnostic(\n          \"Publishing to Sonatype, but developer details are empty (set them with the '//> using publish.developer' directive).\"\n        )\n    }\n\n    def ivyContent = Ivy.create(\n      organization = coursier.Organization(org),\n      moduleName = coursier.ModuleName(moduleName),\n      version = ver,\n      url = url,\n      pomProjectName = Some(pomProjectName),\n      dependencies = dependencies,\n      description = Some(description),\n      license = license,\n      scm = scm,\n      developers = developers,\n      time = LocalDateTime.ofInstant(now, ZoneOffset.UTC),\n      hasDoc = docJarOpt.isDefined,\n      hasSources = sourceJarOpt.isDefined\n    )\n\n    def mavenFileSet: FileSet = {\n      val basePath = Path(org.split('.').toSeq ++ Seq(moduleName, ver))\n\n      val mainEntries = Seq(\n        (basePath / s\"$moduleName-$ver.pom\") -> Content.InMemory(\n          now,\n          pomContent.getBytes(StandardCharsets.UTF_8)\n        ),\n        (basePath / s\"$moduleName-$ver.jar\") -> Content.File(mainJar.toNIO)\n      )\n\n      val sourceJarEntries = sourceJarOpt\n        .map { sourceJar =>\n          (basePath / s\"$moduleName-$ver-sources.jar\") -> Content.File(sourceJar.toNIO)\n        }\n        .toSeq\n\n      val docJarEntries = docJarOpt\n        .map { docJar =>\n          (basePath / s\"$moduleName-$ver-javadoc.jar\") -> Content.File(docJar.toNIO)\n        }\n        .toSeq\n\n      // TODO version listings, …\n      FileSet(mainEntries ++ sourceJarEntries ++ docJarEntries)\n    }\n\n    def ivy2LocalLikeFileSet: FileSet = {\n      val basePath = Path(Seq(org, moduleName, ver))\n\n      val mainEntries = Seq(\n        (basePath / \"poms\" / s\"$moduleName.pom\") -> Content.InMemory(\n          now,\n          pomContent.getBytes(StandardCharsets.UTF_8)\n        ),\n        (basePath / \"ivys\" / \"ivy.xml\") -> Content.InMemory(\n          now,\n          ivyContent.getBytes(StandardCharsets.UTF_8)\n        ),\n        (basePath / \"jars\" / s\"$moduleName.jar\") -> Content.File(mainJar.toNIO)\n      )\n\n      val sourceJarEntries = sourceJarOpt\n        .map { sourceJar =>\n          (basePath / \"srcs\" / s\"$moduleName-sources.jar\") -> Content.File(sourceJar.toNIO)\n        }\n        .toSeq\n\n      val docJarEntries = docJarOpt\n        .map { docJar =>\n          (basePath / \"docs\" / s\"$moduleName-javadoc.jar\") -> Content.File(docJar.toNIO)\n        }\n        .toSeq\n\n      FileSet(mainEntries ++ sourceJarEntries ++ docJarEntries)\n    }\n\n    val fileSet: FileSet = if isIvy2LocalLike then ivy2LocalLikeFileSet else mavenFileSet\n\n    val mod = coursier.core.Module(\n      coursier.core.Organization(org),\n      coursier.core.ModuleName(moduleName),\n      Map()\n    )\n\n    (fileSet, (mod, ver))\n  }\n\n  /** Sign and checksum files, then upload everything to the target repository\n    */\n  private def prepareFilesAndUpload(\n    builds: Seq[Build.Successful],\n    docBuilds: Seq[Build.Successful],\n    workingDir: os.Path,\n    ivy2HomeOpt: Option[os.Path],\n    publishLocal: Boolean,\n    m2Local: Boolean,\n    m2HomeOpt: Option[os.Path],\n    logger: Logger,\n    forceSigningExternally: Boolean,\n    parallelUpload: Option[Boolean],\n    withTestScope: Boolean,\n    isCi: Boolean,\n    configDb: () => ConfigDb,\n    dummy: Boolean\n  ): Either[BuildException, Unit] = either {\n    assert(docBuilds.isEmpty || docBuilds.length == builds.length)\n    val groupedBuilds    = builds.groupedByCrossParams\n    val groupedDocBuilds = docBuilds.groupedByCrossParams\n    val it: Iterator[(Seq[Build.Successful], Seq[Build.Successful])] =\n      groupedBuilds.keysIterator.map { key =>\n        (groupedBuilds(key), groupedDocBuilds.getOrElse(key, Seq.empty))\n      }\n\n    val publishOptions = ConfigMonoid.sum(\n      builds.map(_.options.notForBloopOptions.publishOptions)\n    )\n\n    val ec = builds.head.options.finalCache.ec\n\n    def authOpt(\n      repo: String,\n      isLegacySonatype: Boolean\n    ): Either[BuildException, Option[Authentication]] =\n      either {\n        val publishCredentials: () => Option[PublishCredentials] =\n          () => value(PublishUtils.getPublishCredentials(repo, configDb))\n        for {\n          password <- publishOptions.contextual(isCi).repoPassword\n            .map(_.toConfig)\n            .orElse(publishCredentials().flatMap(_.password))\n            .map(_.get().value)\n          user = publishOptions.contextual(isCi).repoUser\n            .map(_.toConfig)\n            .orElse(publishCredentials().flatMap(_.user))\n            .map(_.get().value)\n            .getOrElse(\"\")\n          auth = Authentication(user, password)\n        } yield publishOptions.contextual(isCi).repoRealm\n          .orElse {\n            publishCredentials()\n              .flatMap(_.realm)\n              .orElse(if isLegacySonatype then Some(\"Sonatype Nexus Repository Manager\") else None)\n          }\n          .map(auth.withRealm)\n          .getOrElse(auth)\n      }\n\n    val repoParams: RepoParams = {\n      lazy val es =\n        Executors.newSingleThreadScheduledExecutor(Util.daemonThreadFactory(\"publish-retry\"))\n\n      if publishLocal && m2Local then RepoParams.m2Local(m2HomeOpt)\n      else if publishLocal then RepoParams.ivy2Local(ivy2HomeOpt)\n      else\n        value {\n          publishOptions.contextual(isCi).repository match {\n            case None       => Left(MissingPublishOptionError.repositoryError)\n            case Some(repo) =>\n              RepoParams(\n                repo = repo,\n                vcsUrlOpt = publishOptions.versionControl.map(_.url),\n                workspace = builds.head.inputs.workspace,\n                ivy2HomeOpt = ivy2HomeOpt,\n                isIvy2LocalLike =\n                  publishOptions.contextual(isCi).repositoryIsIvy2LocalLike.getOrElse(false),\n                es = es,\n                logger = logger,\n                connectionTimeoutRetries = publishOptions.contextual(isCi).connectionTimeoutRetries,\n                connectionTimeoutSeconds = publishOptions.contextual(isCi).connectionTimeoutSeconds,\n                stagingRepoRetries = publishOptions.contextual(isCi).stagingRepoRetries,\n                stagingRepoWaitTimeMilis = publishOptions.contextual(isCi).stagingRepoWaitTimeMilis\n              )\n          }\n        }\n    }\n\n    val now                       = Instant.now()\n    val (fileSet0, modVersionOpt) = value {\n      it\n        .map {\n          case (builds, docBuilds) =>\n            buildFileSet(\n              builds = builds,\n              docBuilds = docBuilds,\n              workingDir = workingDir,\n              now = now,\n              isIvy2LocalLike = repoParams.isIvy2LocalLike,\n              isCi = isCi,\n              isSonatype = repoParams.isSonatype,\n              withTestScope = withTestScope,\n              logger = logger\n            )\n        }\n        .sequence0\n        .map { l =>\n          val fs             = l.map(_._1).foldLeft(FileSet.empty)(_ ++ _)\n          val modVersionOpt0 = l.headOption.map(_._2)\n          (fs, modVersionOpt0)\n        }\n    }\n\n    def getBouncyCastleSigner(\n      secretKey: PasswordOption,\n      secretKeyPasswordOpt: Option[PasswordOption]\n    ) = PublishUtils.getBouncyCastleSigner(\n      secretKey = secretKey,\n      secretKeyPasswordOpt = secretKeyPasswordOpt,\n      buildOptions = builds.headOption.map(_.options),\n      forceSigningExternally = forceSigningExternally,\n      logger = logger\n    )\n\n    val signerKind: PSigner = publishOptions.contextual(isCi).signer.getOrElse {\n      if !repoParams.supportsSig then PSigner.Nop\n      else if publishOptions.contextual(isCi).gpgSignatureId.isDefined then PSigner.Gpg\n      else if repoParams.shouldSign || publishOptions.contextual(isCi).secretKey.isDefined then\n        PSigner.BouncyCastle\n      else PSigner.Nop\n    }\n\n    def getSecretKeyPasswordOpt: Option[PasswordOption] =\n      publishOptions.contextual(isCi).getSecretKeyPasswordOpt(configDb)\n\n    val signer: Either[BuildException, Signer] = signerKind match {\n      // user specified --signer=gpg or --gpgKey=...\n      case PSigner.Gpg => publishOptions.contextual(isCi).getGpgSigner\n\n      // user specified --signer=bc or --secret-key=... or target repository requires signing\n      // --secret-key-password is possibly specified (not mandatory)\n      case PSigner.BouncyCastle\n          if publishOptions.contextual(isCi).secretKey.isDefined =>\n        val secretKeyConfigOpt = publishOptions.contextual(isCi).secretKey.get\n        for { secretKey <- secretKeyConfigOpt.get(configDb()) } yield getBouncyCastleSigner(\n          secretKey = secretKey,\n          secretKeyPasswordOpt = getSecretKeyPasswordOpt\n        )\n\n      // user specified --signer=bc or target repository requires signing\n      // --secret-key-password is possibly specified (not mandatory)\n      case PSigner.BouncyCastle =>\n        val shouldSignMsg =\n          if repoParams.shouldSign then \"signing is required for chosen repository\" else \"\"\n        for {\n          secretKeyOpt <- configDb().get(Keys.pgpSecretKey).wrapConfigException\n          secretKey    <- secretKeyOpt.toRight(\n            new MissingPublishOptionError(\n              name = \"secret key\",\n              optionName = \"--secret-key\",\n              directiveName = \"\",\n              configKeys = Seq(Keys.pgpSecretKey.fullName),\n              extraMessage = shouldSignMsg\n            )\n          )\n        } yield getBouncyCastleSigner(secretKey, getSecretKeyPasswordOpt)\n      case _ =>\n        if !publishOptions.contextual(isCi).signer.contains(PSigner.Nop) then\n          logger.message(\n            \" \\ud83d\\udd13 Artifacts NOT signed as it's not required nor has it been specified\"\n          )\n        Right(NopSigner)\n    }\n\n    val signerLogger =\n      new InteractiveSignerLogger(out = new OutputStreamWriter(System.err), verbosity = 1)\n    val signRes: Either[(Path, Content, String), FileSet] = value(signer).signatures(\n      fileSet = fileSet0,\n      now = now,\n      dontSignExtensions = ChecksumType.all.map(_.extension).toSet,\n      dontSignFiles = Set(\"maven-metadata.xml\"),\n      logger = signerLogger\n    )\n\n    val fileSet1 = value {\n      signRes\n        .left.map {\n          case (path, content, err) =>\n            val path0 = content.pathOpt\n              .map(os.Path(_, Os.pwd))\n              .toRight(path.repr)\n            new FailedToSignFileError(path0, err)\n        }\n        .map { signatures =>\n          fileSet0 ++ signatures\n        }\n    }\n\n    val checksumLogger =\n      new InteractiveChecksumLogger(new OutputStreamWriter(System.err), verbosity = 1)\n    val checksumTypes = publishOptions.contextual(isCi).checksums match {\n      case None =>\n        if repoParams.acceptsChecksums then Seq(ChecksumType.MD5, ChecksumType.SHA1)\n        else Nil\n      case Some(Seq(\"none\")) => Nil\n      case Some(inputs)      =>\n        value {\n          inputs\n            .map(ChecksumType.parse)\n            .sequence\n            .left.map(errors => new MalformedChecksumsError(inputs, errors))\n        }\n    }\n    val checksums = Checksums(\n      types = checksumTypes,\n      fileSet = fileSet1,\n      now = now,\n      pool = ec,\n      logger = checksumLogger\n    ).unsafeRun()(using ec)\n    val fileSet2 = fileSet1 ++ checksums\n\n    val finalFileSet =\n      if repoParams.isIvy2LocalLike then fileSet2\n      else fileSet2.order(ec).unsafeRun()(using ec)\n\n    val isSnapshot0 = modVersionOpt.exists(_._2.endsWith(\"SNAPSHOT\"))\n    if isSnapshot0 then logger.message(\"Publishing a SNAPSHOT version...\")\n    val authOpt0: Option[Authentication] = value(authOpt(\n      repo = repoParams.repo.repo(isSnapshot0).root,\n      isLegacySonatype = repoParams.isSonatype\n    ))\n    val asciiRegex        = \"\"\"[\\u0000-\\u007f]*\"\"\".r\n    val usernameOnlyAscii = authOpt0.exists(_.userOpt.exists(asciiRegex.matches))\n    val passwordOnlyAscii = authOpt0.exists(_.passwordOpt.exists(asciiRegex.matches))\n\n    if repoParams.shouldAuthenticate && authOpt0.isEmpty then\n      logger.diagnostic(\n        \"Publishing to a repository that needs authentication, but no credentials are available.\",\n        Severity.Warning\n      )\n    val repoParams0: RepoParams = repoParams.withAuth(authOpt0)\n    val isLegacySonatype        =\n      repoParams0.isSonatype && !repoParams0.repo.releaseRepo.root.contains(\"s01\")\n    val hooksDataOpt = Option.when(!dummy) {\n      try repoParams0.hooks.beforeUpload(finalFileSet, isSnapshot0).unsafeRun()(using ec)\n      catch {\n        case NonFatal(e)\n            if \"Failed to get .*oss\\\\.sonatype\\\\.org.*/staging/profiles \\\\(http status: 403,\".r\n              .unanchored.matches(\n                e.getMessage\n              ) =>\n          logger.exit(new WrongSonatypeServerError(isLegacySonatype))\n        case NonFatal(e)\n            if \"Failed to get .*oss\\\\.sonatype\\\\.org.*/staging/profiles \\\\(http status: 401,\".r\n              .unanchored.matches(\n                e.getMessage\n              ) =>\n          logger.exit(new InvalidSonatypePublishCredentials(usernameOnlyAscii, passwordOnlyAscii))\n        case NonFatal(e) =>\n          throw new Exception(e)\n      }\n    }\n\n    val retainedRepo = hooksDataOpt match {\n      case None => // dummy mode\n        repoParams0.repo.repo(isSnapshot0)\n      case Some(hooksData) =>\n        repoParams0.hooks.repository(hooksData, repoParams0.repo, isSnapshot0)\n          .getOrElse(repoParams0.repo.repo(isSnapshot0))\n    }\n\n    val baseUpload =\n      if retainedRepo.root.startsWith(\"http://\") || retainedRepo.root.startsWith(\"https://\") then\n        HttpURLConnectionUpload.create(\n          publishOptions.contextual(isCi)\n            .connectionTimeoutSeconds.map(_.seconds.toMillis.toInt),\n          publishOptions.contextual(isCi)\n            .responseTimeoutSeconds.map(_.seconds.toMillis.toInt)\n        )\n      else\n        FileUpload(Paths.get(new URI(retainedRepo.root)))\n\n    val upload = if dummy then DummyUpload(baseUpload) else baseUpload\n\n    val isLocal      = true\n    val uploadLogger = InteractiveUploadLogger.create(System.err, dummy = dummy, isLocal = isLocal)\n\n    val errors =\n      try\n        upload.uploadFileSet(\n          repository = retainedRepo,\n          fileSet = finalFileSet,\n          logger = uploadLogger,\n          parallel =\n            if parallelUpload.getOrElse(repoParams.defaultParallelUpload) then Some(ec) else None\n        ).unsafeRun()(using ec)\n      catch {\n        case NonFatal(e) =>\n          // Wrap exception from coursier, as it sometimes throws exceptions from other threads,\n          // which lack the current stacktrace.\n          throw new Exception(e)\n      }\n\n    errors.toList match {\n      case (h @ (_, _, e: Upload.Error.HttpError)) :: t\n          if repoParams0.isSonatype && errors.distinctBy(_._3.getMessage()).size == 1 =>\n        logger.log(s\"Error message: ${e.getMessage}\")\n        val httpCodeRegex = \"HTTP (\\\\d+)\\n.*\".r\n        e.getMessage match {\n          case httpCodeRegex(\"403\") =>\n            if logger.verbosity >= 2 then e.printStackTrace()\n            logger.error(\n              s\"\"\"\n                 |Uploading files failed!\n                 |Possible causes:\n                 |- no rights to publish under this organization\n                 |- organization name is misspelled\n                 | -> have you registered your organisation yet?\n                 |\"\"\".stripMargin\n            )\n            value(Left(new UploadError(::(h, t))))\n          case _ => value(Left(new UploadError(::(h, t))))\n        }\n      case h :: t if repoParams0.isSonatype && errors.forall {\n            case (_, _, _: Upload.Error.Unauthorized) => true\n            case _                                    => false\n          } =>\n        logger.error(\n          s\"\"\"\n             |Uploading files failed!\n             |Possible causes:\n             |- incorrect Sonatype credentials\n             |- incorrect Sonatype server was used, try ${\n              if isLegacySonatype then \"'central-s01'\" else \"'central'\"\n            }\n             | -> consult publish subcommand documentation\n             |\"\"\".stripMargin\n        )\n        value(Left(new UploadError(::(h, t))))\n      case h :: t =>\n        value(Left(new UploadError(::(h, t))))\n      case Nil =>\n        for (hooksData <- hooksDataOpt)\n          try repoParams0.hooks.afterUpload(hooksData).unsafeRun()(using ec)\n          catch {\n            case NonFatal(e) =>\n              throw new Exception(e)\n          }\n        for ((mod, version) <- modVersionOpt) {\n          val checkRepo = repoParams0.repo.checkResultsRepo(isSnapshot0)\n          val relPath   = {\n            val elems =\n              if repoParams.isIvy2LocalLike then\n                Seq(mod.organization.value, mod.name.value, version)\n              else mod.organization.value.split('.').toSeq ++ Seq(mod.name.value, version)\n            elems.mkString(\"/\", \"/\", \"/\")\n          }\n          val path = {\n            val url = checkRepo.root.stripSuffix(\"/\") + relPath\n            if url.startsWith(\"file:\") then {\n              val path = os.Path(Paths.get(new URI(url)), os.pwd)\n              if path.startsWith(os.pwd) then\n                path.relativeTo(os.pwd).segments.map(_ + File.separator).mkString\n              else if path.startsWith(os.home) then\n                (\"~\" +: path.relativeTo(os.home).segments).map(_ + File.separator).mkString\n              else path.toString\n            }\n            else url\n          }\n          if dummy then println(\"\\n \\ud83d\\udc40 You could have checked results at\")\n          else println(\"\\n \\ud83d\\udc40 Check results at\")\n          println(s\"  $path\")\n          for (targetRepo <- repoParams.targetRepoOpt if !isSnapshot0) {\n            val url = targetRepo.stripSuffix(\"/\") + relPath\n            if dummy then println(\"before they would have landed at\")\n            else println(\"before they land at\")\n            println(s\"  $url\")\n          }\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishConnectionOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class PublishConnectionOptions(\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Connection timeout, in seconds.\")\n  @Tag(tags.restricted)\n  @Hidden\n    connectionTimeoutSeconds: Option[Int] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"How many times to retry establishing the connection on timeout.\")\n  @Tag(tags.restricted)\n  @Hidden\n    connectionTimeoutRetries: Option[Int] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Waiting for response timeout, in seconds.\")\n  @Tag(tags.restricted)\n  @Hidden\n    responseTimeoutSeconds: Option[Int] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"How many times to retry the staging repository operations on failure.\")\n  @Tag(tags.restricted)\n  @Hidden\n    stagingRepoRetries: Option[Int] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Time to wait between staging repository operation retries, in milliseconds.\")\n  @Tag(tags.restricted)\n  @Hidden\n    stagingRepoWaitTimeMilis: Option[Int] = None\n)\n  // format: on\n\nobject PublishConnectionOptions {\n  implicit lazy val parser: Parser[PublishConnectionOptions] = Parser.derive\n  implicit lazy val help: Help[PublishConnectionOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocal.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.HelpFormat\n\nimport scala.build.{BuildThreads, Logger}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.{HelpCommandGroup, SharedOptions}\nimport scala.cli.config.ConfigDb\nimport scala.cli.util.ArgHelpers.*\n\nobject PublishLocal extends ScalaCommand[PublishLocalOptions] {\n  override def group: String           = HelpCommandGroup.Main.toString\n  override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL\n  override def helpFormat: HelpFormat  =\n    super.helpFormat\n      .withHiddenGroups(Publish.hiddenHelpGroups)\n      .withPrimaryGroups(Publish.primaryHelpGroups)\n  override def sharedOptions(options: PublishLocalOptions): Option[SharedOptions] =\n    Some(options.shared)\n\n  override def buildOptions(options: PublishLocalOptions): Some[scala.build.options.BuildOptions] =\n    Some(options.buildOptions().orExit(options.shared.logger))\n\n  override def names: List[List[String]] = List(\n    List(\"publish\", \"local\")\n  )\n\n  override def runCommand(\n    options: PublishLocalOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    Publish.maybePrintLicensesAndExit(options.publishParams)\n    Publish.maybePrintChecksumsAndExit(options.sharedPublish)\n\n    if options.m2 && options.sharedPublish.ivy2Home.exists(_.trim.nonEmpty) then {\n      logger.error(\"--m2 and --ivy2-home are mutually exclusive.\")\n      sys.exit(1)\n    }\n\n    val baseOptions = buildOptionsOrExit(options)\n    val inputs      = options.shared.inputs(args.all).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n\n    val initialBuildOptions = Publish.mkBuildOptions(\n      baseOptions,\n      options.shared.sharedVersionOptions,\n      options.publishParams,\n      options.sharedPublish,\n      PublishRepositoryOptions(),\n      options.scalaSigning,\n      PublishConnectionOptions(),\n      options.mainClass,\n      None\n    ).orExit(logger)\n    val threads = BuildThreads.create()\n\n    val compilerMaker       = options.shared.compilerMaker(threads)\n    val docCompilerMakerOpt = options.sharedPublish.docCompilerMakerOpt\n\n    val cross = options.compileCross.cross.getOrElse(false)\n\n    lazy val workingDir = options.sharedPublish.workingDir\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n      .getOrElse {\n        os.temp.dir(\n          prefix = \"scala-cli-publish-\",\n          deleteOnExit = true\n        )\n      }\n\n    val ivy2HomeOpt = options.sharedPublish.ivy2Home\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n\n    val m2HomeOpt = options.m2Home\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n\n    Publish.doRun(\n      inputs = inputs,\n      logger = logger,\n      initialBuildOptions = initialBuildOptions,\n      compilerMaker = compilerMaker,\n      docCompilerMaker = docCompilerMakerOpt,\n      cross = cross,\n      workingDir = workingDir,\n      ivy2HomeOpt = ivy2HomeOpt,\n      publishLocal = true,\n      m2Local = options.m2,\n      m2HomeOpt = m2HomeOpt,\n      forceSigningExternally = options.scalaSigning.forceSigningExternally.getOrElse(false),\n      parallelUpload = Some(true),\n      watch = options.watch.watch,\n      isCi = options.publishParams.isCi,\n      configDb = () => ConfigDb.empty, // shouldn't be used, no need of repo credentials here\n      mainClassOptions = options.mainClass,\n      dummy = options.sharedPublish.dummy,\n      buildTests = options.shared.scope.test.getOrElse(false)\n    )\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocalOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.cli.commands.pgp.PgpScalaSigningOptions\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(PublishLocalOptions.helpMessage, \"\", PublishLocalOptions.detailedHelpMessage)\nfinal case class PublishLocalOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n    compileCross: CrossOptions = CrossOptions(),\n  @Recurse\n    mainClass: MainClassOptions = MainClassOptions(),\n  @Recurse\n    publishParams: PublishParamsOptions = PublishParamsOptions(),\n  @Recurse\n    sharedPublish: SharedPublishOptions = SharedPublishOptions(),\n  @Recurse\n    scalaSigning: PgpScalaSigningOptions = PgpScalaSigningOptions(),\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Publish to the local Maven repository (defaults to ~/.m2/repository) instead of Ivy2 local\")\n  @Name(\"mavenLocal\")\n  @Tag(tags.experimental)\n  @Tag(tags.inShortHelp)\n    m2: Boolean = false,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Set the local Maven repository path (defaults to ~/.m2/repository)\")\n  @ValueDescription(\"path\")\n  @Tag(tags.experimental)\n    m2Home: Option[String] = None,\n) extends HasSharedOptions with HasSharedWatchOptions\n// format: on\n\nobject PublishLocalOptions {\n  implicit lazy val parser: Parser[PublishLocalOptions] = Parser.derive\n  implicit lazy val help: Help[PublishLocalOptions]     = Help.derive\n  val cmdName                                           = \"publish local\"\n  private val helpHeader       = \"Publishes build artifacts to the local Ivy2 or Maven repository.\"\n  private val docWebsiteSuffix = \"publishing/publish-local\"\n  val helpMessage: String      =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(cmdName)}\n       |${HelpMessages.commandDocWebsiteReference(docWebsiteSuffix)}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandDocWebsiteReference(docWebsiteSuffix)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.baseRunnerName\nimport scala.cli.commands.pgp.PgpScalaSigningOptions\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n// format: off\n@HelpMessage(PublishOptions.helpMessage, \"\", PublishOptions.detailedHelpMessage)\nfinal case class PublishOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n    compileCross: CrossOptions = CrossOptions(),\n  @Recurse\n    mainClass: MainClassOptions = MainClassOptions(),\n  @Recurse\n    publishParams: PublishParamsOptions = PublishParamsOptions(),\n  @Recurse\n    publishRepo: PublishRepositoryOptions = PublishRepositoryOptions(),\n  @Recurse\n    sharedPublish: SharedPublishOptions = SharedPublishOptions(),\n  @Recurse\n    signingCli: PgpScalaSigningOptions = PgpScalaSigningOptions(),\n @Recurse\n  connectionOptions: PublishConnectionOptions = PublishConnectionOptions(),\n\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Hidden\n    ivy2LocalLike: Option[Boolean] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Hidden\n    parallelUpload: Option[Boolean] = None\n) extends HasSharedOptions with HasSharedWatchOptions\n// format: on\n\nobject PublishOptions {\n  implicit lazy val parser: Parser[PublishOptions] = Parser.derive\n  implicit lazy val help: Help[PublishOptions]     = Help.derive\n\n  val cmdName             = \"publish\"\n  private val helpHeader  = \"Publishes build artifacts to Maven repositories.\"\n  val helpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(cmdName, needsPower = true)}\n       |${HelpMessages.commandDocWebsiteReference(s\"publishing/$cmdName\")}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |We recommend running the `publish setup` sub-command once prior to\n       |running `publish` in order to set missing `using` directives for publishing.\n       |(but this is not mandatory)\n       |    $baseRunnerName --power publish setup .\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(s\"publishing/$cmdName\")}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishParamsOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.build.internals.EnvVar\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\nimport scala.cli.util.ArgParsers.*\nimport scala.cli.util.MaybeConfigPasswordOption\n\n// format: off\nfinal case class PublishParamsOptions(\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Organization to publish artifacts under\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    organization: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Name to publish artifacts as\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    name: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Final name to publish artifacts as, including Scala version and platform suffixes if any\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    moduleName: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"URL to put in publishing metadata\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    url: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"License to put in publishing metadata\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @ValueDescription(\"name:URL\")\n    license: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"VCS information to put in publishing metadata\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    vcs: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Description to put in publishing metadata\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    description: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Developer(s) to add in publishing metadata, like \\\"alex|Alex|https://alex.info\\\" or \\\"alex|Alex|https://alex.info|alex@alex.me\\\"\")\n  @ValueDescription(\"id|name|URL|email\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    developer: List[String] = Nil,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Secret key to use to sign artifacts with Bouncy Castle\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    secretKey: Option[MaybeConfigPasswordOption] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Password of secret key to use to sign artifacts with Bouncy Castle\")\n  @ValueDescription(\"value:…\")\n  @ExtraName(\"secretKeyPass\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    secretKeyPassword: Option[MaybeConfigPasswordOption] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Use or setup publish parameters meant to be used on continuous integration\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    ci: Option[Boolean] = None\n\n) {\n  // format: on\n\n  def setupCi: Boolean = ci.getOrElse(false)\n  def isCi: Boolean    = ci.getOrElse(EnvVar.Internal.ci.valueOpt.nonEmpty)\n}\n\nobject PublishParamsOptions {\n  implicit lazy val parser: Parser[PublishParamsOptions] = Parser.derive\n  implicit lazy val help: Help[PublishParamsOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishRepositoryOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\nimport scala.cli.signing.shared.PasswordOption\nimport scala.cli.signing.util.ArgParsers.*\n\n// format: off\nfinal case class PublishRepositoryOptions(\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Repository to publish to\")\n  @ValueDescription(\"URL or path\")\n  @ExtraName(\"R\")\n  @ExtraName(\"publishRepo\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    publishRepository: Option[String] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"User to use with publishing repository\")\n  @ValueDescription(\"user\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    user: Option[PasswordOption] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Password to use with publishing repository\")\n  @ValueDescription(\"value:…\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    password: Option[PasswordOption] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Realm to use when passing credentials to publishing repository\")\n  @ValueDescription(\"realm\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    realm: Option[String] = None\n\n)\n// format: on\n\nobject PublishRepositoryOptions {\n  implicit lazy val parser: Parser[PublishRepositoryOptions] = Parser.derive\n  implicit lazy val help: Help[PublishRepositoryOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.core.RemainingArgs\nimport caseapp.core.help.HelpFormat\nimport coursier.cache.ArchiveCache\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.Ops.*\nimport scala.build.errors.CompositeBuildException\nimport scala.build.options.{BuildOptions, InternalOptions, Scope}\nimport scala.build.{CrossSources, Directories, Logger, Sources}\nimport scala.cli.ScalaCli\nimport scala.cli.commands.github.{LibSodiumJni, SecretCreate, SecretList}\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.shared.{HelpCommandGroup, SharedOptions}\nimport scala.cli.commands.util.ScalaCliSttpBackend\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel}\nimport scala.cli.config.Keys\nimport scala.cli.internal.Constants\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\n\nobject PublishSetup extends ScalaCommand[PublishSetupOptions] {\n\n  override def group: String                               = HelpCommandGroup.Main.toString\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL\n\n  override def helpFormat: HelpFormat =\n    super.helpFormat\n      .withHiddenGroups(Publish.hiddenHelpGroups)\n      .withPrimaryGroups(Publish.primaryHelpGroups)\n\n  override def names = List(\n    List(\"publish\", \"setup\")\n  )\n\n  override def runCommand(\n    options: PublishSetupOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    Publish.maybePrintLicensesAndExit(options.publishParams)\n\n    val coursierCache = options.coursier.coursierCache(logger)\n    val directories   = Directories.directories\n\n    lazy val configDb = ConfigDbUtils.configDb.orExit(logger)\n\n    val inputArgs = args.all\n\n    val inputs = {\n      val maybeInputs = SharedOptions.inputs(\n        inputArgs,\n        () => None,\n        Nil,\n        directories,\n        logger,\n        coursierCache,\n        None,\n        options.input.defaultForbiddenDirectories,\n        options.input.forbid,\n        Nil,\n        Nil,\n        Nil,\n        Nil\n      )\n      maybeInputs match {\n        case Left(error) =>\n          System.err.println(error)\n          sys.exit(1)\n        case Right(inputs0) => inputs0\n      }\n    }\n\n    val (pureJava, publishOptions) = {\n      val cliBuildOptions = BuildOptions(\n        internal = InternalOptions(\n          cache = Some(coursierCache)\n        )\n      )\n\n      val (crossSources, _) = CrossSources.forInputs(\n        inputs,\n        Sources.defaultPreprocessors(\n          cliBuildOptions.archiveCache,\n          cliBuildOptions.internal.javaClassNameVersionOpt,\n          () => cliBuildOptions.javaHome().value.javaCommand\n        ),\n        logger,\n        cliBuildOptions.suppressWarningOptions,\n        cliBuildOptions.internal.exclude,\n        download = cliBuildOptions.downloader\n      ).orExit(logger)\n\n      val crossSourcesSharedOptions = crossSources.sharedOptions(cliBuildOptions)\n      val scopedSources = crossSources.scopedSources(crossSourcesSharedOptions).orExit(logger)\n      val sources       =\n        scopedSources.sources(Scope.Main, crossSourcesSharedOptions, inputs.workspace, logger)\n          .orExit(logger)\n\n      val pureJava = sources.hasJava && !sources.hasScala\n\n      (pureJava, sources.buildOptions.notForBloopOptions.publishOptions)\n    }\n\n    val backend = ScalaCliSttpBackend.httpURLConnection(logger)\n\n    val checksInputOpt = options.checks.map(_.trim).filter(_.nonEmpty).filter(_ != \"all\")\n    val checkKinds     = checksInputOpt match {\n      case None              => OptionCheck.Kind.all.toSet\n      case Some(checksInput) =>\n        OptionCheck.Kind.parseList(checksInput)\n          .left.map { unrecognized =>\n            System.err.println(s\"Unrecognized check(s): ${unrecognized.mkString(\", \")}\")\n            sys.exit(1)\n          }\n          .merge\n          .toSet\n    }\n\n    val missingFields =\n      OptionChecks.checks(options, configDb, inputs.workspace, coursierCache, logger, backend)\n        .filter(check => checkKinds(check.kind))\n        .flatMap {\n          check =>\n            if (check.check(publishOptions)) {\n              logger.debug(s\"Found field ${check.fieldName}\")\n              Nil\n            }\n            else {\n              logger.debug(s\"Missing field ${check.fieldName}\")\n              Seq(check)\n            }\n        }\n\n    if (missingFields.nonEmpty) {\n      val count = missingFields.length\n      logger.message(s\"$count ${if (count > 1) \"options need\" else \"option needs\"} to be set\")\n      for (check <- missingFields)\n        logger.message(s\"  ${check.fieldName}\")\n      logger.message(\"\") // printing an empty line, for readability\n    }\n\n    lazy val (ghRepoOrg, ghRepoName) = GitRepo.ghRepoOrgName(inputs.workspace, logger)\n      .orExit(logger)\n\n    lazy val token = options.token\n      .map(_.toConfig)\n      .orElse {\n        configDb.get(Keys.ghToken)\n          .wrapConfigException\n          .orExit(logger)\n      }\n      .map(_.get())\n      .getOrElse {\n        System.err.println(\n          s\"No GitHub token passed, please specify one via --token env:ENV_VAR_NAME or --token file:/path/to/token, \" +\n            s\"or by setting ${Keys.ghToken.fullName} in the config.\"\n        )\n        sys.exit(1)\n      }\n\n    if (options.check)\n      if (missingFields.isEmpty)\n        logger.message(\"Setup fine for publishing\")\n      else {\n        logger.message(\"Found missing config for publishing\")\n        sys.exit(1)\n      }\n    else {\n\n      val missingFieldsWithDefaults = missingFields\n        .map { check =>\n          check.defaultValue(publishOptions).map((check, _))\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n        .orExit(logger)\n\n      lazy val secretNames = {\n        val secretList = SecretList.list(\n          ghRepoOrg,\n          ghRepoName,\n          token,\n          backend,\n          logger\n        ).orExit(logger)\n\n        secretList.secrets.map(_.name).toSet\n      }\n\n      val missingSetSecrets = missingFieldsWithDefaults\n        .flatMap {\n          case (_, default) => default.ghSecrets\n        }\n        .filter(s => s.force || !secretNames.contains(s.name))\n\n      if (missingSetSecrets.nonEmpty) {\n\n        logger.message(\"\") // printing an empty line, for readability\n        logger.message {\n          val name = if (missingSetSecrets.length <= 1) \"secret\" else \"secrets\"\n          if (options.dummy)\n            s\"Checking ${missingSetSecrets.length} GitHub repository $name\"\n          else\n            s\"Uploading ${missingSetSecrets.length} GitHub repository $name\"\n        }\n\n        LibSodiumJni.init(coursierCache, ArchiveCache().withCache(coursierCache), logger)\n\n        lazy val pubKey = SecretCreate.publicKey(\n          ghRepoOrg,\n          ghRepoName,\n          token,\n          backend,\n          logger\n        ).orExit(logger)\n\n        missingSetSecrets\n          .map { s =>\n            if (options.dummy) {\n              logger.message(s\"Would have set GitHub secret ${s.name}\")\n              Right(true)\n            }\n            else\n              SecretCreate.createOrUpdate(\n                ghRepoOrg,\n                ghRepoName,\n                token,\n                s.name,\n                s.value,\n                pubKey,\n                dummy = false,\n                printRequest = false,\n                backend,\n                logger\n              )\n          }\n          .sequence\n          .left.map(CompositeBuildException(_))\n          .orExit(logger)\n      }\n\n      var written = Seq.empty[os.Path]\n\n      if (missingFieldsWithDefaults.nonEmpty) {\n\n        val missingFieldsWithDefaultsAndValues = missingFieldsWithDefaults\n          .map {\n            case (check, default) =>\n              default.getValue().map(v => (check, default, v))\n          }\n          .sequence\n          .left.map(CompositeBuildException(_))\n          .orExit(logger)\n\n        val dest = {\n          val ext = if (pureJava) \".java\" else \".scala\"\n          inputs.workspace / s\"publish-conf$ext\"\n        }\n        val nl = System.lineSeparator() // FIXME Get from dest if it exists?\n\n        def extraDirectivesLines(extraDirectives: Seq[(String, String)]) =\n          extraDirectives.map {\n            case (k, v) if v.exists(_.isWhitespace) => s\"\"\"//> using $k \"$v\"\"\"\" + nl\n            case (k, v)                             => s\"\"\"//> using $k $v\"\"\" + nl\n          }.mkString\n\n        val extraLines = missingFieldsWithDefaultsAndValues.map {\n          case (_, default, None) => extraDirectivesLines(default.extraDirectives)\n          case (check, default, Some(value)) if value.exists(_.isWhitespace) =>\n            s\"\"\"//> using ${check.directivePath} \"$value\"\"\"\" + nl +\n              extraDirectivesLines(default.extraDirectives)\n          case (check, default, Some(value)) =>\n            s\"\"\"//> using ${check.directivePath} $value\"\"\" + nl +\n              extraDirectivesLines(default.extraDirectives)\n        }\n\n        val currentContent =\n          if (os.isFile(dest)) os.read.bytes(dest)\n          else if (os.exists(dest)) sys.error(s\"Error: $dest already exists and is not a file\")\n          else Array.emptyByteArray\n        if (extraLines.nonEmpty) {\n          val updatedContent = currentContent ++\n            extraLines.toArray.flatMap(_.getBytes(StandardCharsets.UTF_8))\n          os.write.over(dest, updatedContent)\n          logger.message(\n            s\"\"\"\n               |Wrote ${CommandUtils.printablePath(dest)}\"\"\".stripMargin\n          ) // printing an empty line, for readability\n          written = written :+ dest\n        }\n      }\n\n      if (options.checkWorkflow.getOrElse(options.publishParams.setupCi)) {\n        val workflowDir  = inputs.workspace / \".github\" / \"workflows\"\n        val hasWorkflows = os.isDir(workflowDir) &&\n          os.list(workflowDir)\n            .filter(_.last.endsWith(\".yml\")) // FIXME Accept more extensions?\n            .exists(os.isFile)\n        if (hasWorkflows)\n          logger.message(\n            s\"Found some workflow files under ${CommandUtils.printablePath(workflowDir)}, not writing Scala CLI workflow\"\n          )\n        else {\n          val dest    = workflowDir / \"ci.yml\"\n          val content = {\n            val resourcePath = Constants.defaultFilesResourcePath + \"/workflows/default.yml\"\n            val cl           = Thread.currentThread().getContextClassLoader\n            val resUrl       = cl.getResource(resourcePath)\n            if (resUrl == null)\n              sys.error(s\"Should not happen - resource $resourcePath not found\")\n            val is = resUrl.openStream()\n            try is.readAllBytes()\n            finally is.close()\n          }\n          os.write(dest, content, createFolders = true)\n          logger.message(s\"Wrote workflow in ${CommandUtils.printablePath(dest)}\")\n          written = written :+ dest\n        }\n      }\n\n      if (options.checkGitignore.getOrElse(true)) {\n        val dest         = inputs.workspace / \".gitignore\"\n        val hasGitignore = os.exists(dest)\n        if (hasGitignore)\n          logger.message(\n            s\"Found .gitignore under ${CommandUtils.printablePath(inputs.workspace)}, not writing one\"\n          )\n        else {\n          val content = {\n            val resourcePath = Constants.defaultFilesResourcePath + \"/gitignore\"\n            val cl           = Thread.currentThread().getContextClassLoader\n            val resUrl       = cl.getResource(resourcePath)\n            if (resUrl == null)\n              sys.error(s\"Should not happen - resource $resourcePath not found\")\n            val is = resUrl.openStream()\n            try is.readAllBytes()\n            finally is.close()\n          }\n          os.write(dest, content, createFolders = true)\n          logger.message(s\"Wrote gitignore in ${CommandUtils.printablePath(dest)}\")\n          written = written :+ dest\n        }\n      }\n\n      if (written.nonEmpty)\n        logger.message(\"\") // printing an empty line, for readability\n\n      if (options.publishParams.setupCi && written.nonEmpty)\n        logger.message(\n          s\"Commit and push ${written.map(CommandUtils.printablePath).mkString(\", \")}, to enable publishing from CI\"\n        )\n      else\n        logger.message(\"Project is ready for publishing!\")\n\n      if (!options.publishParams.setupCi) {\n        logger.message(\"To publish your project, run\")\n        logger.message {\n          val inputs = inputArgs\n            .map(a => if (a.exists(_.isSpaceChar)) \"\\\"\" + a + \"\\\"\" else a)\n            .mkString(\" \")\n          s\"  ${ScalaCli.progName} publish $inputs\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetupOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.cli.commands.pgp.{PgpScalaSigningOptions, SharedPgpPushPullOptions}\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\nimport scala.cli.signing.shared.PasswordOption\nimport scala.cli.signing.util.ArgParsers.*\n\n// format: off\n@HelpMessage(PublishSetupOptions.helpMessage, \"\", PublishSetupOptions.detailedHelpMessage)\nfinal case class PublishSetupOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Recurse\n    sharedVersionOptions: SharedVersionOptions = SharedVersionOptions(),\n  @Recurse\n    workspace: SharedWorkspaceOptions = SharedWorkspaceOptions(),\n  @Recurse\n    input: SharedInputOptions = SharedInputOptions(),\n  @Recurse\n    publishParams: PublishParamsOptions = PublishParamsOptions(),\n  @Recurse\n    publishRepo: PublishRepositoryOptions = PublishRepositoryOptions(),\n  @Recurse\n    sharedPgp: SharedPgpPushPullOptions = SharedPgpPushPullOptions(),\n  @Recurse\n    sharedJvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    scalaSigning: PgpScalaSigningOptions = PgpScalaSigningOptions(),\n\n\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Public key to use to verify artifacts (to be uploaded to a key server)\")\n    publicKey: Option[PasswordOption] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Check if some options for publishing are missing, and exit with non-zero return code if that's the case\")\n    check: Boolean = false,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"GitHub token to use to upload secrets to GitHub - password encoded\")\n    token: Option[PasswordOption] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Generate a random key pair for publishing, with a secret key protected by a random password\")\n    randomSecretKey: Option[Boolean] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"When generating a random key pair, the mail to associate to it\")\n    randomSecretKeyMail: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"The option groups to check - can be \\\"all\\\", or a comma-separated list of \\\"core\\\", \\\"signing\\\", \\\"repo\\\", \\\"extra\\\"\")\n    checks: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Whether to check if a GitHub workflow already exists (one for publishing is written if none is found)\")\n    checkWorkflow: Option[Boolean] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Whether to check if a .gitignore file already exists (one is written if none is found)\")\n    checkGitignore: Option[Boolean] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Dummy mode - don't upload any secret to GitHub\")\n    dummy: Boolean = false\n) extends HasGlobalOptions\n// format: on\n\nobject PublishSetupOptions {\n  implicit lazy val parser: Parser[PublishSetupOptions] = Parser.derive\n  implicit lazy val help: Help[PublishSetupOptions]     = Help.derive\n  val cmdName                                           = \"publish setup\"\n  private val helpHeader                                = \"Configures the project for publishing.\"\n  private val docWebsiteSuffix                          = \"publishing/publish-setup\"\n  val helpMessage: String                               =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(cmdName)}\n       |${HelpMessages.commandDocWebsiteReference(docWebsiteSuffix)}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandDocWebsiteReference(docWebsiteSuffix)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/PublishUtils.scala",
    "content": "package scala.cli.commands.publish\n\nimport coursier.cache.{ArchiveCache, FileCache}\nimport coursier.publish.signing.{GpgSigner, Signer}\n\nimport java.net.URI\nimport java.util.function.Supplier\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.options.{BuildOptions, ComputeVersion, PublishContextualOptions, PublishOptions}\nimport scala.build.{Logger, ScalaArtifacts}\nimport scala.cli.commands.pgp.PgpExternalCommand\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.config.{ConfigDb, Keys, PasswordOption, PublishCredentials}\nimport scala.cli.errors.MissingPublishOptionError\nimport scala.cli.publish.BouncycastleSignerMaker\nimport scala.cli.util.ConfigPasswordOptionHelpers.*\n\nobject PublishUtils {\n  def getBouncyCastleSigner(\n    secretKey: PasswordOption,\n    secretKeyPasswordOpt: Option[PasswordOption],\n    buildOptions: Option[BuildOptions],\n    forceSigningExternally: Boolean,\n    logger: Logger\n  ): Signer = {\n    val getLauncher: Supplier[Array[String]] = { () =>\n      val archiveCache = buildOptions.map(_.archiveCache)\n        .getOrElse(ArchiveCache())\n      val fileCache = buildOptions.map(_.finalCache).getOrElse(FileCache())\n      PgpExternalCommand.launcher(\n        fileCache,\n        archiveCache,\n        logger,\n        buildOptions.getOrElse(BuildOptions())\n      ) match {\n        case Left(e)              => throw new Exception(e)\n        case Right(binaryCommand) => binaryCommand.toArray\n      }\n    }\n\n    (new BouncycastleSignerMaker).get(\n      forceSigningExternally,\n      secretKeyPasswordOpt.fold(null)(_.toCliSigning),\n      secretKey.toCliSigning,\n      getLauncher,\n      logger\n    )\n  }\n\n  def getPublishCredentials(\n    repo: String,\n    configDb: () => ConfigDb\n  ): Either[BuildException, Option[PublishCredentials]] = {\n    val uri     = new URI(repo)\n    val isHttps = uri.getScheme == \"https\"\n    val hostOpt = Option.when(isHttps)(uri.getHost)\n    hostOpt match {\n      case None       => Right(None)\n      case Some(host) =>\n        configDb().get(Keys.publishCredentials).wrapConfigException.map { credListOpt =>\n          credListOpt.flatMap { credList =>\n            credList.find { cred =>\n              cred.host == host &&\n              (isHttps || cred.httpsOnly.contains(false))\n            }\n          }\n        }\n    }\n  }\n\n  extension (publishContextualOptions: PublishContextualOptions) {\n    def getSecretKeyPasswordOpt(configDb: () => ConfigDb): Option[PasswordOption] =\n      if publishContextualOptions.secretKeyPassword.isDefined then\n        for {\n          secretKeyPassConfigOpt <- publishContextualOptions.secretKeyPassword\n          secretKeyPass          <- secretKeyPassConfigOpt.get(configDb()).toOption\n        } yield secretKeyPass\n      else\n        for {\n          secretKeyPassOpt <- configDb().get(Keys.pgpSecretKeyPassword).toOption\n          secretKeyPass    <- secretKeyPassOpt\n        } yield secretKeyPass\n\n    def getGpgSigner: Either[MissingPublishOptionError, GpgSigner] =\n      publishContextualOptions.gpgSignatureId.map { gpgSignatureId =>\n        GpgSigner(\n          key = GpgSigner.Key.Id(gpgSignatureId),\n          extraOptions = publishContextualOptions.gpgOptions\n        )\n      }.toRight {\n        new MissingPublishOptionError(\n          name = \"ID of the GPG key\",\n          optionName = \"--gpgKey\",\n          directiveName = \"\"\n        )\n      }\n  }\n  case class ArtifactData(org: String, name: String, version: String)\n  extension (publishOptions: PublishOptions) {\n\n    /** Maven POM `name` / ivy `m:name`: `publish.name` when set, otherwise the published artifact\n      * name.\n      */\n    def pomProjectNameForMaven(fallbackModuleName: String): String =\n      publishOptions.name\n        .map(_.value)\n        .map(_.trim)\n        .filter(_.nonEmpty)\n        .getOrElse(fallbackModuleName)\n\n    def artifactData(\n      workspace: os.Path,\n      logger: Logger,\n      scalaArtifactsOpt: Option[ScalaArtifacts],\n      isCi: Boolean\n    ): Either[BuildException, ArtifactData] = {\n      lazy val orgNameOpt = GitRepo.maybeGhRepoOrgName(workspace, logger)\n\n      val maybeOrg = publishOptions.organization match {\n        case Some(org0) => Right(org0.value)\n        case None       => orgNameOpt.map(_._1) match {\n            case Some(org) =>\n              val mavenOrg = s\"io.github.$org\"\n              logger.message(\n                s\"Using directive publish.organization not set, computed $mavenOrg from GitHub organization $org as default organization\"\n              )\n              Right(mavenOrg)\n            case None =>\n              Left(new MissingPublishOptionError(\n                \"organization\",\n                \"--organization\",\n                \"publish.organization\"\n              ))\n          }\n      }\n\n      val moduleName = publishOptions.moduleName match {\n        case Some(name0) => name0.value\n        case None        =>\n          val name = publishOptions.name match {\n            case Some(name0) => name0.value\n            case None        =>\n              val name = workspace.last\n              logger.message(\n                s\"Using directive publish.name not specified, using workspace directory name $name as default name\"\n              )\n              name\n          }\n          scalaArtifactsOpt.map(_.params) match {\n            case Some(scalaParams) =>\n              val pf = publishOptions.scalaPlatformSuffix.getOrElse {\n                scalaParams.platform.fold(\"\")(\"_\" + _)\n              }\n              val sv = publishOptions.scalaVersionSuffix.getOrElse {\n                // FIXME Allow full cross version too\n                \"_\" + scalaParams.scalaBinaryVersion\n              }\n              name + pf + sv\n            case None =>\n              name\n          }\n      }\n\n      val maybeVer = publishOptions.version match {\n        case Some(ver0) => Right(ver0.value)\n        case None       =>\n          val computeVer = publishOptions.contextual(isCi).computeVersion.orElse {\n            def isGitRepo = GitRepo.gitRepoOpt(workspace).isDefined\n\n            val default = ComputeVersion.defaultComputeVersion(!isCi && isGitRepo)\n            if default.isDefined then\n              logger.message(\n                s\"Using directive ${MissingPublishOptionError.versionError.directiveName} not set, assuming git:tag as publish.computeVersion\"\n              )\n            default\n          }\n          computeVer match {\n            case Some(cv) => cv.get(workspace)\n            case None     => Left(MissingPublishOptionError.versionError)\n          }\n      }\n\n      (maybeOrg, maybeVer)\n        .traverseN\n        .left.map(CompositeBuildException(_))\n        .map {\n          case (org, ver) =>\n            ArtifactData(org, moduleName, ver)\n        }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/RepoParams.scala",
    "content": "package scala.cli.commands.publish\n\nimport coursier.core.Authentication\nimport coursier.maven.MavenRepository\nimport coursier.publish.sonatype.SonatypeApi\nimport coursier.publish.util.EmaRetryParams\nimport coursier.publish.{Hooks, PublishRepository}\n\nimport java.net.URI\nimport java.util.concurrent.ScheduledExecutorService\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.build.{Logger, RepositoryUtils}\nimport scala.cli.commands.util.ScalaCliSttpBackend\n\nfinal case class RepoParams(\n  repo: PublishRepository,\n  targetRepoOpt: Option[String],\n  hooks: Hooks,\n  isIvy2LocalLike: Boolean,\n  defaultParallelUpload: Boolean,\n  supportsSig: Boolean,\n  acceptsChecksums: Boolean,\n  shouldSign: Boolean,\n  shouldAuthenticate: Boolean\n) {\n  import RepoParams.*\n\n  def withAuth(auth: Authentication): RepoParams =\n    copy(\n      repo = repo.withAuthentication(auth),\n      hooks = hooks match {\n        case s: Hooks.Sonatype =>\n          s.copy(\n            repo = s.repo.withAuthentication(auth),\n            api = s.api.copy(\n              authentication = Some(auth)\n            )\n          )\n        case other => other\n      }\n    )\n  def withAuth(authOpt: Option[Authentication]): RepoParams = authOpt.fold(this)(withAuth)\n\n  lazy val isSonatype: Boolean =\n    Option(new URI(repo.snapshotRepo.root))\n      .filter(_.getScheme == \"https\")\n      .map(_.getHost)\n      .exists(sonatypeHosts.contains)\n}\n\nobject RepoParams {\n  private val sonatypeOssrhStagingApiBase = \"https://ossrh-staging-api.central.sonatype.com\"\n  private val sonatypeSnapshotsBase       = s\"${RepositoryUtils.snapshotsRepositoryUrl}/\"\n  private val sonatypeLegacyBase          = \"https://oss.sonatype.org\"\n  private val sonatypeS01LegacyBase       = \"https://s01.oss.sonatype.org\"\n  private def sonatypeHosts: Seq[String]  =\n    Seq(\n      sonatypeLegacyBase,\n      sonatypeSnapshotsBase,\n      sonatypeS01LegacyBase,\n      sonatypeOssrhStagingApiBase\n    ).map(new URI(_).getHost)\n\n  def apply(\n    repo: String,\n    vcsUrlOpt: Option[String],\n    workspace: os.Path,\n    ivy2HomeOpt: Option[os.Path],\n    isIvy2LocalLike: Boolean,\n    es: ScheduledExecutorService,\n    logger: Logger,\n    connectionTimeoutRetries: Option[Int] = None,\n    connectionTimeoutSeconds: Option[Int] = None,\n    stagingRepoRetries: Option[Int] = None,\n    stagingRepoWaitTimeMilis: Option[Int] = None\n  ): Either[BuildException, RepoParams] = either {\n    repo match {\n      case \"ivy2-local\" =>\n        RepoParams.ivy2Local(ivy2HomeOpt)\n      case \"m2-local\" | \"maven-local\" =>\n        RepoParams.m2Local(None)\n      case \"sonatype\" | \"central\" | \"maven-central\" | \"mvn-central\" =>\n        logger.message(s\"Using Portal OSSRH Staging API: $sonatypeOssrhStagingApiBase\")\n        RepoParams.centralRepo(\n          base = sonatypeOssrhStagingApiBase,\n          useLegacySnapshots = false,\n          connectionTimeoutRetries = connectionTimeoutRetries,\n          connectionTimeoutSeconds = connectionTimeoutSeconds,\n          stagingRepoRetries = stagingRepoRetries,\n          stagingRepoWaitTimeMilis = stagingRepoWaitTimeMilis,\n          es = es,\n          logger = logger\n        )\n      case \"sonatype-legacy\" | \"central-legacy\" | \"maven-central-legacy\" | \"mvn-central-legacy\" =>\n        logger.message(s\"$warnPrefix $sonatypeLegacyBase is EOL since 2025-06-30.\")\n        logger.message(s\"$warnPrefix $sonatypeLegacyBase publishing is expected to fail.\")\n        RepoParams.centralRepo(\n          base = sonatypeLegacyBase,\n          useLegacySnapshots = true,\n          connectionTimeoutRetries = connectionTimeoutRetries,\n          connectionTimeoutSeconds = connectionTimeoutSeconds,\n          stagingRepoRetries = stagingRepoRetries,\n          stagingRepoWaitTimeMilis = stagingRepoWaitTimeMilis,\n          es = es,\n          logger = logger\n        )\n      case \"sonatype-s01\" | \"central-s01\" | \"maven-central-s01\" | \"mvn-central-s01\" =>\n        logger.message(s\"$warnPrefix $sonatypeS01LegacyBase is EOL since 2025-06-30.\")\n        logger.message(s\"$warnPrefix it's expected publishing will fail.\")\n        RepoParams.centralRepo(\n          base = sonatypeS01LegacyBase,\n          useLegacySnapshots = true,\n          connectionTimeoutRetries = connectionTimeoutRetries,\n          connectionTimeoutSeconds = connectionTimeoutSeconds,\n          stagingRepoRetries = stagingRepoRetries,\n          stagingRepoWaitTimeMilis = stagingRepoWaitTimeMilis,\n          es = es,\n          logger = logger\n        )\n      case \"github\" =>\n        value(RepoParams.gitHubRepo(vcsUrlOpt, workspace, logger))\n      case repoStr if repoStr.startsWith(\"github:\") && repoStr.count(_ == '/') == 1 =>\n        val (org, name) = repoStr.stripPrefix(\"github:\").split('/') match {\n          case Array(org0, name0) => (org0, name0)\n          case other              => sys.error(s\"Cannot happen ('$repoStr' -> ${other.toSeq})\")\n        }\n        RepoParams.gitHubRepoFor(org, name)\n      case repoStr =>\n        val repo0 = RepositoryParser.repositoryOpt(repoStr).getOrElse {\n          val url =\n            if (repoStr.contains(\"://\")) repoStr\n            else os.Path(repoStr, os.pwd).toNIO.toUri.toASCIIString\n          MavenRepository(url)\n        }\n\n        RepoParams(\n          repo = PublishRepository.Simple(repo0),\n          targetRepoOpt = None,\n          hooks = Hooks.dummy,\n          isIvy2LocalLike = isIvy2LocalLike,\n          defaultParallelUpload = true,\n          supportsSig = true,\n          acceptsChecksums = true,\n          shouldSign = false,\n          shouldAuthenticate = false\n        )\n    }\n  }\n\n  def centralRepo(\n    base: String,\n    useLegacySnapshots: Boolean,\n    connectionTimeoutRetries: Option[Int],\n    connectionTimeoutSeconds: Option[Int],\n    stagingRepoRetries: Option[Int],\n    stagingRepoWaitTimeMilis: Option[Int],\n    es: ScheduledExecutorService,\n    logger: Logger\n  ): RepoParams = {\n    val repo0 = PublishRepository.Sonatype(\n      base = MavenRepository(base),\n      useLegacySnapshots = useLegacySnapshots\n    )\n    val backend = ScalaCliSttpBackend.httpURLConnection(logger, connectionTimeoutSeconds)\n    val api     = SonatypeApi(\n      backend = backend,\n      base = base + \"/service/local\",\n      authentication = None,\n      verbosity = logger.verbosity,\n      retryOnTimeout = connectionTimeoutRetries.getOrElse(3),\n      stagingRepoRetryParams =\n        EmaRetryParams(\n          attempts = stagingRepoRetries.getOrElse(3),\n          initialWaitDurationMs = stagingRepoWaitTimeMilis.getOrElse(10 * 1000),\n          factor = 2.0f\n        )\n    )\n    val hooks0 = Hooks.sonatype(\n      repo = repo0,\n      api = api,\n      out = logger.compilerOutputStream, // meh\n      verbosity = logger.verbosity,\n      batch = coursier.paths.Util.useAnsiOutput(), // FIXME Get via logger\n      es = es\n    )\n    RepoParams(\n      repo = repo0,\n      targetRepoOpt = Some(\"https://repo1.maven.org/maven2\"),\n      hooks = hooks0,\n      isIvy2LocalLike = false,\n      defaultParallelUpload = true,\n      supportsSig = true,\n      acceptsChecksums = true,\n      shouldSign = true,\n      shouldAuthenticate = true\n    )\n  }\n\n  def gitHubRepoFor(org: String, name: String): RepoParams =\n    RepoParams(\n      repo = PublishRepository.Simple(MavenRepository(s\"https://maven.pkg.github.com/$org/$name\")),\n      targetRepoOpt = None,\n      hooks = Hooks.dummy,\n      isIvy2LocalLike = false,\n      defaultParallelUpload = false,\n      supportsSig = false,\n      acceptsChecksums = false,\n      shouldSign = false,\n      shouldAuthenticate = true\n    )\n\n  def gitHubRepo(\n    vcsUrlOpt: Option[String],\n    workspace: os.Path,\n    logger: Logger\n  ): Either[BuildException, RepoParams] = either {\n    val orgNameFromVcsOpt = vcsUrlOpt.flatMap(GitRepo.maybeGhOrgName)\n\n    val (org, name) = orgNameFromVcsOpt match {\n      case Some(orgName) => orgName\n      case None          => value(GitRepo.ghRepoOrgName(workspace, logger))\n    }\n\n    gitHubRepoFor(org, name)\n  }\n\n  def ivy2Local(ivy2HomeOpt: Option[os.Path]): RepoParams = {\n    val home = ivy2HomeOpt\n      .orElse(sys.props.get(\"ivy.home\").map(prop => os.Path(prop)))\n      .orElse(sys.props.get(\"user.home\").map(prop => os.Path(prop) / \".ivy2\"))\n      .getOrElse(os.home / \".ivy2\")\n    val base = home / \"local\"\n    // not really a Maven repo…\n    RepoParams(\n      repo = PublishRepository.Simple(MavenRepository(base.toNIO.toUri.toASCIIString)),\n      targetRepoOpt = None,\n      hooks = Hooks.dummy,\n      isIvy2LocalLike = true,\n      defaultParallelUpload = true,\n      supportsSig = true,\n      acceptsChecksums = true,\n      shouldSign = false,\n      shouldAuthenticate = false\n    )\n  }\n\n  def m2Local(m2HomeOpt: Option[os.Path]): RepoParams = {\n    val base = m2HomeOpt.getOrElse(os.home / \".m2\" / \"repository\")\n    RepoParams(\n      repo = PublishRepository.Simple(MavenRepository(base.toNIO.toUri.toASCIIString)),\n      targetRepoOpt = None,\n      hooks = Hooks.dummy,\n      isIvy2LocalLike = false,\n      defaultParallelUpload = true,\n      supportsSig = true,\n      acceptsChecksums = true,\n      shouldSign = false,\n      shouldAuthenticate = false\n    )\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/RepositoryParser.scala",
    "content": "package scala.cli.commands.publish\n\n// from coursier.internal.SharedRepositoryParser\n// delete when coursier.internal.SharedRepositoryParser.repositoryOpt is available for us\nimport coursier.Repositories\nimport coursier.maven.MavenRepository\n\nobject RepositoryParser {\n\n  def repositoryOpt(s: String): Option[MavenRepository] =\n    if (s == \"central\")\n      Some(Repositories.central)\n    else if (s.startsWith(\"sonatype:\"))\n      Some(Repositories.sonatype(s.stripPrefix(\"sonatype:\")))\n    else if (s.startsWith(\"bintray:\")) {\n      val s0 = s.stripPrefix(\"bintray:\")\n      val id =\n        if (s.contains(\"/\")) s0\n        else s0 + \"/maven\"\n\n      Some(Repositories.bintray(id))\n    }\n    else if (s.startsWith(\"typesafe:\"))\n      Some(Repositories.typesafe(s.stripPrefix(\"typesafe:\")))\n    else if (s.startsWith(\"sbt-maven:\"))\n      Some(Repositories.sbtMaven(s.stripPrefix(\"sbt-maven:\")))\n    else if (s == \"scala-integration\" || s == \"scala-nightlies\")\n      Some(Repositories.scalaIntegration)\n    else if (s == \"jitpack\")\n      Some(Repositories.jitpack)\n    else if (s == \"clojars\")\n      Some(Repositories.clojars)\n    else if (s == \"jcenter\")\n      Some(Repositories.jcenter)\n    else if (s == \"google\")\n      Some(Repositories.google)\n    else if (s == \"gcs\")\n      Some(Repositories.centralGcs)\n    else if (s == \"gcs-eu\")\n      Some(Repositories.centralGcsEu)\n    else if (s == \"gcs-asia\")\n      Some(Repositories.centralGcsAsia)\n    else if (s.startsWith(\"apache:\"))\n      Some(Repositories.apache(s.stripPrefix(\"apache:\")))\n    else\n      None\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/SetSecret.scala",
    "content": "package scala.cli.commands.publish\n\nimport scala.cli.config.Secret\n\nfinal case class SetSecret(\n  name: String,\n  value: Secret[String],\n  force: Boolean\n)\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/SharedPublishOptions.scala",
    "content": "package scala.cli.commands.publish\n\nimport caseapp.*\n\nimport scala.build.compiler.{ScalaCompilerMaker, SimpleScalaCompilerMaker}\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedPublishOptions(\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Directory where temporary files for publishing should be written\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @Hidden\n    workingDir: Option[String] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @Hidden\n  @HelpMessage(\"Scala version suffix to append to the module name, like \\\"_2.13\\\" or \\\"_3\\\"\")\n  @ValueDescription(\"suffix\")\n  @Tag(tags.restricted)\n    scalaVersionSuffix: Option[String] = None,\n  @Group(HelpGroup.Publishing.toString)\n  @Hidden\n  @HelpMessage(\"Scala platform suffix to append to the module name, like \\\"_sjs1\\\" or \\\"_native0.4\\\"\")\n  @ValueDescription(\"suffix\")\n  @Tag(tags.restricted)\n    scalaPlatformSuffix: Option[String] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Whether to build and publish source JARs\")\n  @Name(\"sourcesJar\")\n  @Name(\"jarSources\")\n  @Name(\"sources\")\n  @Tag(tags.deprecated(\"sources\"))\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    withSources: Option[Boolean] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Whether to build and publish doc JARs\")\n  @ExtraName(\"scaladoc\")\n  @ExtraName(\"javadoc\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    doc: Option[Boolean] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"ID of the GPG key to use to sign artifacts\")\n  @ValueDescription(\"key-id\")\n  @ExtraName(\"K\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    gpgKey: Option[String] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Method to use to sign artifacts\")\n  @ValueDescription(\"gpg|bc|none\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    signer: Option[String] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"gpg command-line options\")\n  @ValueDescription(\"argument\")\n  @ExtraName(\"G\")\n  @ExtraName(\"gpgOpt\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    gpgOption: List[String] = Nil,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Set Ivy 2 home directory\")\n  @ValueDescription(\"path\")\n  @Tag(tags.restricted)\n    ivy2Home: Option[String] = None,\n\n  @Group(HelpGroup.Publishing.toString)\n  @Hidden\n  @Tag(tags.restricted)\n    checksum: List[String] = Nil,\n\n  @Group(HelpGroup.Publishing.toString)\n  @HelpMessage(\"Proceed as if publishing, but do not upload / write artifacts to the remote repository\")\n  @Tag(tags.implementation)\n    dummy: Boolean = false\n){\n  // format: on\n\n  def docCompilerMakerOpt: Option[ScalaCompilerMaker] =\n    if (doc.contains(false)) // true by default\n      None\n    else\n      Some(SimpleScalaCompilerMaker(\"java\", Nil, scaladoc = true))\n}\n\nobject SharedPublishOptions {\n  implicit lazy val parser: Parser[SharedPublishOptions] = Parser.derive\n  implicit lazy val help: Help[SharedPublishOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/CheckUtils.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport java.net.URI\n\nimport scala.build.Logger\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{PublishSetupOptions, RepoParams}\n\nobject CheckUtils {\n\n  /** Keep in mind that combinedOptions do not contain all options from cliOptions, e.g.\n    * publishRepo.publishRepository is not propagated\n    */\n  def getRepoOpt(\n    cliOptions: PublishSetupOptions,\n    combinedOptions: BPublishOptions\n  ): Option[String] =\n    cliOptions.publishRepo.publishRepository\n      .orElse {\n        combinedOptions.contextual(cliOptions.publishParams.setupCi).repository\n      }\n      .orElse {\n        if (cliOptions.publishParams.setupCi)\n          combinedOptions.contextual(isCi = false).repository\n        else\n          None\n      }\n\n  def getHostOpt(\n    options: PublishSetupOptions,\n    pubOpt: BPublishOptions,\n    workspace: os.Path,\n    logger: Logger\n  ): Option[String] =\n    getRepoOpt(options, pubOpt).flatMap { repo =>\n      RepoParams(\n        repo,\n        pubOpt.versionControl.map(_.url),\n        workspace,\n        None,\n        false,\n        null,\n        logger\n      ) match {\n        case Left(ex) =>\n          logger.debug(\"Caught exception when trying to compute host to check user credentials\")\n          logger.debug(ex)\n          None\n        case Right(params) =>\n          Some(new URI(params.repo.snapshotRepo.root).getHost)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/ComputeVersionCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{GitRepo, OptionCheck, PublishSetupOptions}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class ComputeVersionCheck(\n  options: PublishSetupOptions,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Core\n  def fieldName     = \"computeVersion\"\n  def directivePath =\n    \"publish\" + (if (options.publishParams.setupCi) \".ci\" else \"\") + \".computeVersion\"\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.version.nonEmpty ||\n    pubOpt.retained(options.publishParams.setupCi).computeVersion.nonEmpty\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n    def fromGitOpt =\n      if (GitRepo.gitRepoOpt(workspace).isDefined) {\n        logger.message(\"computeVersion:\")\n        logger.message(\"  assuming versions are computed from git tags\")\n        Some(\"git:tag\")\n      }\n      else\n        None\n    val cv = options.sharedVersionOptions.computeVersion\n      .orElse(fromGitOpt)\n    cv.map(OptionCheck.DefaultValue.simple(_, Nil, Nil)).toRight {\n      new MissingPublishOptionError(\n        \"compute version\",\n        \"--compute-version\",\n        \"publish.computeVersion\"\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/DeveloperCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions}\nimport scala.cli.config.{ConfigDb, Keys}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class DeveloperCheck(\n  options: PublishSetupOptions,\n  configDb: () => ConfigDb,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Extra\n  def fieldName     = \"developers\"\n  def directivePath = \"publish.developer\"\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.developers.nonEmpty\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] =\n    either {\n      // FIXME No headOption, add all of options.publishParams.developer values…\n      val strValue = options.publishParams.developer.headOption match {\n        case None =>\n          val nameOpt  = value(configDb().get(Keys.userName).wrapConfigException)\n          val emailOpt = value(configDb().get(Keys.userEmail).wrapConfigException)\n          val urlOpt   = value(configDb().get(Keys.userUrl).wrapConfigException)\n\n          (nameOpt, emailOpt, urlOpt) match {\n            case (Some(name), Some(email), Some(url)) =>\n              logger.message(\"developers:\")\n              logger.message(s\"  using $name <$email> ($url) from config\")\n              s\"$name|$email|$url\"\n            case _ =>\n              value {\n                Left {\n                  new MissingPublishOptionError(\n                    \"developer\",\n                    \"--developer\",\n                    \"publish.developer\",\n                    configKeys = Seq(\n                      Keys.userName.fullName,\n                      Keys.userEmail.fullName,\n                      Keys.userUrl.fullName\n                    )\n                  )\n                }\n              }\n          }\n        case Some(value) =>\n          value\n      }\n\n      OptionCheck.DefaultValue.simple(strValue, Nil, Nil)\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/LicenseCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions}\n\nfinal case class LicenseCheck(\n  options: PublishSetupOptions,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Extra\n  def fieldName     = \"license\"\n  def directivePath = \"publish.license\"\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.license.nonEmpty\n\n  private def defaultLicense = \"Apache-2.0\"\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n    val license = options.publishParams.license.getOrElse {\n      logger.message(\"license:\")\n      logger.message(s\"  using $defaultLicense (default)\")\n      defaultLicense\n    }\n    Right(OptionCheck.DefaultValue.simple(license, Nil, Nil))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/NameCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions}\n\nfinal case class NameCheck(\n  options: PublishSetupOptions,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Core\n  def fieldName     = \"name\"\n  def directivePath = \"publish.name\"\n\n  def check(options: BPublishOptions): Boolean =\n    options.name.nonEmpty || options.moduleName.nonEmpty\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n    def fromWorkspaceDirName = {\n      val n = workspace.last\n      logger.message(\"name:\")\n      logger.message(s\"  using workspace directory name $n\")\n      n\n    }\n    val name = options.publishParams.name.getOrElse(fromWorkspaceDirName)\n    Right(OptionCheck.DefaultValue.simple(name, Nil, Nil))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/OrganizationCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{GitRepo, OptionCheck, PublishSetupOptions}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class OrganizationCheck(\n  options: PublishSetupOptions,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Core\n  def fieldName     = \"organization\"\n  def directivePath = \"publish.organization\"\n\n  def check(options: BPublishOptions): Boolean =\n    options.organization.nonEmpty\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n\n    def viaGitHubRemoteOpt = GitRepo.ghRepoOrgName(workspace, logger) match {\n      case Left(err) =>\n        logger.debug(\n          s\"Error when trying to get GitHub repo from git to compute default organization: $err, ignoring it.\"\n        )\n        None\n      case Right((org, _)) =>\n        val publishOrg = s\"io.github.$org\"\n        logger.message(\"organization:\")\n        logger.message(s\"  computed $publishOrg from GitHub account $org\")\n        Some(publishOrg)\n    }\n\n    val orgOpt = options.publishParams.organization\n      .orElse(viaGitHubRemoteOpt)\n\n    orgOpt.map(OptionCheck.DefaultValue.simple(_, Nil, Nil)).toRight {\n      new MissingPublishOptionError(\n        \"organization\",\n        \"--organization\",\n        \"publish.organization\"\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/PasswordCheck.scala",
    "content": "package scala.cli.commands.publish.checks\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions, SetSecret}\nimport scala.cli.config.{ConfigDb, Keys}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class PasswordCheck(\n  options: PublishSetupOptions,\n  configDb: () => ConfigDb,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Repository\n  def fieldName     = \"password\"\n  def directivePath = \"publish\" + (if (options.publishParams.setupCi) \".ci\" else \"\") + \".password\"\n\n  private def passwordOpt(pubOpt: BPublishOptions) =\n    CheckUtils.getHostOpt(\n      options,\n      pubOpt,\n      workspace,\n      logger\n    ) match {\n      case None       => Right(None)\n      case Some(host) =>\n        configDb().get(Keys.publishCredentials).wrapConfigException.map { credListOpt =>\n          credListOpt.flatMap { credList =>\n            credList\n              .iterator\n              .filter(_.host == host)\n              .map(_.password)\n              .collectFirst {\n                case Some(p) => p\n              }\n          }\n        }\n    }\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.retained(options.publishParams.setupCi).repoPassword.nonEmpty ||\n    !options.publishParams.setupCi &&\n    (passwordOpt(pubOpt) match {\n      case Left(ex) =>\n        logger.debug(\"Ignoring error while trying to get password from config\")\n        logger.debug(ex)\n        true\n      case Right(valueOpt) =>\n        valueOpt.isDefined\n    })\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] =\n    either {\n\n      if (options.publishParams.setupCi) {\n        val password = options.publishRepo.password match {\n          case Some(password0) => password0.toConfig\n          case None            =>\n            value(passwordOpt(pubOpt)) match {\n              case Some(password0) =>\n                logger.message(\"publish.credentials:\")\n                logger.message(\n                  s\"  using ${Keys.publishCredentials.fullName} from Scala CLI configuration\"\n                )\n                password0\n              case None =>\n                value {\n                  Left {\n                    new MissingPublishOptionError(\n                      \"publish password\",\n                      \"--password\",\n                      \"publish.credentials\",\n                      configKeys = Seq(Keys.publishCredentials.fullName)\n                    )\n                  }\n                }\n            }\n        }\n\n        OptionCheck.DefaultValue.simple(\n          \"env:PUBLISH_PASSWORD\",\n          Nil,\n          Seq(SetSecret(\"PUBLISH_PASSWORD\", password.get(), force = true))\n        )\n      }\n      else\n        CheckUtils.getHostOpt(\n          options,\n          pubOpt,\n          workspace,\n          logger\n        ) match {\n          case None =>\n            logger.debug(\"No host, not checking for publish repository password\")\n            OptionCheck.DefaultValue.empty\n          case Some(host) =>\n            if (value(passwordOpt(pubOpt)).isDefined) {\n              logger.message(\"publish.password:\")\n              logger.message(\n                s\"  found password for $host in ${Keys.publishCredentials.fullName} in Scala CLI configuration\"\n              )\n              OptionCheck.DefaultValue.empty\n            }\n            else {\n              val optionName =\n                CheckUtils.getRepoOpt(options, pubOpt).map(r =>\n                  s\"publish password for $r\"\n                ).getOrElse(\"publish password\")\n              value {\n                Left {\n                  new MissingPublishOptionError(\n                    optionName,\n                    \"\",\n                    \"publish.credentials\",\n                    configKeys = Seq(Keys.publishCredentials.fullName)\n                  )\n                }\n              }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/PgpSecretKeyCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport coursier.cache.{ArchiveCache, FileCache}\nimport coursier.util.Task\nimport sttp.client3.*\nimport sttp.model.Uri\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException, MalformedCliInputError}\nimport scala.build.internal.util.WarningMessages\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.build.options.publish.ConfigPasswordOption\nimport scala.build.options.publish.ConfigPasswordOption.*\nimport scala.cli.commands.config.ThrowawayPgpSecret\nimport scala.cli.commands.pgp.{KeyServer, PgpProxyMaker}\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions, SetSecret}\nimport scala.cli.commands.util.JvmUtils\nimport scala.cli.config.{ConfigDb, Keys}\nimport scala.cli.errors.MissingPublishOptionError\nimport scala.cli.signing.shared.PasswordOption\nimport scala.cli.util.ConfigPasswordOptionHelpers.*\n\n/** Checks if:\n  *   - keys for signing files are present in using directives (either PGP or GPG)\n  *   - public key is uploaded to specified keyservers (if keys present are PGP)\n  *\n  * If any of the above fails then try the following to find missing keys:\n  *   - if secretKey using directive is already present then fill any missing from CLI options\n  *   - if secretKey is specified in options use only keys from options\n  *   - if --random-secret-key is specified and it's CI use new generated keys\n  *   - default to keys in config if this fails throw\n  *\n  * After previous step figures out which values should be used in setup do:\n  *   - if it's CI then upload github secrets and write using directives as 'using ci.key\n  *     env:GITHUB_SECRET_VAR'\n  *   - otherwise write down the key values to publish.conf file in the same form as they were\n  *     passed to options, if the values come from config don't write them to file\n  *\n  * Finally upload the public key to the keyservers that are specified\n  */\nfinal case class PgpSecretKeyCheck(\n  options: PublishSetupOptions,\n  coursierCache: FileCache[Task],\n  configDb: () => ConfigDb,\n  logger: Logger,\n  backend: SttpBackend[Identity, Any]\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Signing\n  def fieldName     = \"pgp-secret-key\"\n  def directivePath = \"publish\" + (if (options.publishParams.setupCi) \".ci\" else \"\") + \".secretKey\"\n\n  def check(pubOpt: BPublishOptions): Boolean = {\n    val opt0 = pubOpt.retained(options.publishParams.setupCi)\n\n    opt0.repository.orElse(options.publishRepo.publishRepository).contains(\"github\") ||\n    (\n      opt0.secretKey.isDefined &&\n      opt0.secretKeyPassword.isDefined &&\n      opt0.publicKey.isDefined &&\n      isKeyUploaded(opt0.publicKey.get.get(configDb()).toOption.map(_.toCliSigning))\n        .getOrElse(false)\n    ) ||\n    opt0.gpgSignatureId.isDefined\n  }\n\n  def javaCommand: Either[BuildException, () => String] = either {\n    () =>\n      value(JvmUtils.javaOptions(options.sharedJvm)).javaHome(\n        ArchiveCache().withCache(coursierCache),\n        coursierCache,\n        logger.verbosity\n      ).value.javaCommand\n  }\n\n  private lazy val keyServers: Either[BuildException, Seq[Uri]] = {\n    val rawKeyServers = options.sharedPgp.keyServer.filter(_.trim.nonEmpty)\n    if (rawKeyServers.isEmpty)\n      Right(KeyServer.allDefaults)\n    else\n      rawKeyServers\n        .map { keyServerUriStr =>\n          Uri.parse(keyServerUriStr).left.map { err =>\n            new MalformedCliInputError(\n              s\"Malformed key server URI '$keyServerUriStr': $err\"\n            )\n          }\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n  }\n\n  /** Check if the public PGP key is uploaded to all keyservers that were specified\n    */\n  private def isKeyUploaded(pubKeyOpt: Option[PasswordOption]): Either[BuildException, Boolean] =\n    either {\n      pubKeyOpt match {\n        case Some(pubKey) =>\n          val keyId = value {\n            (new PgpProxyMaker).get(\n              options.scalaSigning.forceSigningExternally.getOrElse(false)\n            ).keyId(\n              pubKey.get().value,\n              \"[generated key]\",\n              coursierCache,\n              logger,\n              options.sharedJvm,\n              options.coursier,\n              options.scalaSigning.cliOptions()\n            )\n          }\n\n          value(keyServers).forall { keyServer =>\n            KeyServer.check(keyId, keyServer, backend) match\n              case Right(Right(_))  => true\n              case Right(Left(msg)) =>\n                logger.debug(\n                  s\"\"\"Response from $keyServer:\n                     |$msg\n                     |\"\"\".stripMargin\n                )\n                false\n              case Left(err) =>\n                logger.error(s\"Error checking $keyId at $keyServer: $err\")\n                false\n          }\n        case None => false\n      }\n    }\n\n  private case class PGPKeys(\n    secretKeyOpt: Option[ConfigPasswordOption],\n    secretKeyPasswordOpt: Option[ConfigPasswordOption],\n    publicKeyOpt: Option[ConfigPasswordOption]\n  )\n\n  val missingSecretKeyError = new MissingPublishOptionError(\n    \"publish secret key\",\n    \"--secret-key\",\n    \"publish.secretKey\",\n    configKeys = Seq(Keys.pgpSecretKey.fullName),\n    extraMessage =\n      \"also specify publish.secretKeyPassword / --secret-key-password if needed.\" +\n        (if (options.publishParams.setupCi)\n           \" Alternatively, pass --random-secret-key\"\n         else \"\")\n  )\n\n  private lazy val keysFromOptions: PGPKeys =\n    PGPKeys(\n      options.publishParams.secretKey.map(_.configPasswordOptions()),\n      options.publishParams.secretKeyPassword.map(_.configPasswordOptions()),\n      options.publicKey.map(ConfigPasswordOption.ActualOption.apply)\n    )\n\n  private lazy val maybeKeysFromConfig: Either[BuildException, PGPKeys] =\n    for {\n      secretKeyOpt <- configDb().get(Keys.pgpSecretKey).wrapConfigException\n      pubKeyOpt    <- configDb().get(Keys.pgpPublicKey).wrapConfigException\n      passwordOpt  <- configDb().get(Keys.pgpSecretKeyPassword).wrapConfigException\n    } yield PGPKeys(\n      secretKeyOpt.map(sk => ConfigPasswordOption.ActualOption(sk.toCliSigning)),\n      passwordOpt.map(p => ConfigPasswordOption.ActualOption(p.toCliSigning)),\n      pubKeyOpt.map(pk => ConfigPasswordOption.ActualOption(pk.toCliSigning))\n    )\n\n  private def getRandomPGPKeys: Either[BuildException, PGPKeys] = either {\n    val maybeMail = options.randomSecretKeyMail.toRight(\n      new MissingPublishOptionError(\n        \"the e-mail address to associate to the random key pair\",\n        \"--random-secret-key-mail\",\n        \"\"\n      )\n    )\n\n    val passwordSecret = options.publishParams.secretKeyPassword\n      .map(_.configPasswordOptions())\n      .map { configPasswordOption =>\n        configPasswordOption\n          .get(configDb()).wrapConfigException\n          .map(_.get().toCliSigning)\n          .orThrow\n      }\n      .getOrElse(ThrowawayPgpSecret.pgpPassPhrase())\n\n    val (pgpPublic, pgpSecret) = value {\n      ThrowawayPgpSecret.pgpSecret(\n        value(maybeMail),\n        Some(passwordSecret),\n        logger,\n        coursierCache,\n        options.sharedJvm,\n        options.coursier,\n        options.scalaSigning.cliOptions()\n      )\n    }\n\n    PGPKeys(\n      Some(ConfigPasswordOption.ActualOption(PasswordOption.Value(pgpSecret))),\n      Some(ConfigPasswordOption.ActualOption(PasswordOption.Value(passwordSecret))),\n      Some(ConfigPasswordOption.ActualOption(PasswordOption.Value(pgpPublic)))\n    )\n  }\n\n  private def uploadKey(keyIdOpt: Option[ConfigPasswordOption]): Either[BuildException, Unit] =\n    either {\n      keyIdOpt match\n        case None =>\n          logger.message(\n            \"\"\"\n              |Warning: no public key passed, not checking if the key needs to be uploaded to a key server.\"\"\".stripMargin\n          ) // printing an empty line, for readability\n        case Some(pubKeyConfigPasswordOption) =>\n          val publicKeyString = pubKeyConfigPasswordOption.get(configDb())\n            .orThrow\n            .get()\n            .value\n\n          val keyId = (new PgpProxyMaker).get(\n            options.scalaSigning.forceSigningExternally.getOrElse(false)\n          ).keyId(\n            publicKeyString,\n            \"[generated key]\",\n            coursierCache,\n            logger,\n            options.sharedJvm,\n            options.coursier,\n            options.scalaSigning.cliOptions()\n          ).orThrow\n\n          value(keyServers)\n            .map { keyServer =>\n              if (options.dummy) {\n                logger.message(\n                  s\"\"\"\n                     |Would upload key 0x${keyId.stripPrefix(\"0x\")} to $keyServer\"\"\".stripMargin\n                ) // printing an empty line, for readability\n                Right(())\n              }\n              else {\n                val e: Either[BuildException, Unit] = either {\n                  val checkResp = value {\n                    KeyServer.check(keyId, keyServer, backend)\n                      .left.map(msg =>\n                        new PgpSecretKeyCheck.KeyServerError(\n                          s\"Error getting key $keyId from $keyServer: $msg\"\n                        )\n                      )\n                  }\n                  logger.debug(s\"Key server check response: $checkResp\")\n                  val check = checkResp.isRight\n                  if (!check) {\n                    val resp = value {\n                      KeyServer.add(publicKeyString, keyServer, backend)\n                        .left.map(msg =>\n                          new PgpSecretKeyCheck.KeyServerError(\n                            s\"Error uploading key $keyId to $keyServer: $msg\"\n                          )\n                        )\n                    }\n                    logger.debug(s\"Key server upload response: $resp\")\n                    logger.message(\n                      s\"\"\"\n                         |Uploaded key 0x${keyId.stripPrefix(\"0x\")} to $keyServer\"\"\".stripMargin\n                    ) // printing an empty line, for readability\n                  }\n                }\n                e\n              }\n            }\n            .sequence\n            .left.map(CompositeBuildException(_))\n            .map(_ => ())\n    }\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] =\n    either {\n      val retainedOptions = pubOpt.retained(options.publishParams.setupCi)\n\n      // obtain PGP keys that should be written to publish-conf file\n      val (setupKeys, areConfigDefaults) = if (retainedOptions.secretKey.isDefined) {\n        val publicKeySetup = if (retainedOptions.publicKey.isEmpty)\n          keysFromOptions.publicKeyOpt\n        else None\n\n        val passwordSetup = if (retainedOptions.secretKeyPassword.isEmpty)\n          keysFromOptions.secretKeyPasswordOpt\n        else None\n\n        (PGPKeys(None, passwordSetup, publicKeySetup), false)\n      }\n      else {\n        val randomSecretKey = options.randomSecretKey.getOrElse(false)\n\n        if (keysFromOptions.secretKeyOpt.isDefined)\n          (keysFromOptions, false)\n        else if ( // any PGP key option is specified, but there's no secretKey then notify the user\n          keysFromOptions.publicKeyOpt.isDefined || keysFromOptions.secretKeyPasswordOpt.isDefined\n        )\n          throw missingSecretKeyError\n        else if (randomSecretKey && options.publishParams.setupCi)\n          (getRandomPGPKeys.orThrow, false)\n        else {\n          val keysFromConfig = maybeKeysFromConfig.orThrow\n          if (keysFromConfig.secretKeyOpt.isDefined)\n            logger.message(s\"$fieldName:\")\n            logger.message(\"  found keys in config\")\n          else\n            throw missingSecretKeyError\n\n          if (keysFromConfig.publicKeyOpt.isEmpty)\n            logger.message(\"  warning: no PGP public key found in config\")\n\n          (keysFromConfig, true)\n        }\n      }\n\n      val publicKeyOpt = retainedOptions.publicKey.orElse(setupKeys.publicKeyOpt)\n\n      // if we setup for CI set GitHub secrets together with directives\n      if (options.publishParams.setupCi) {\n        val (passwordSetSecret, passwordDirectives) = setupKeys.secretKeyPasswordOpt\n          .map { p =>\n            val dir    = \"publish.ci.secretKeyPassword\" -> \"env:PUBLISH_SECRET_KEY_PASSWORD\"\n            val secret = p.get(configDb()).orThrow.get()\n            val setSec = SetSecret(\"PUBLISH_SECRET_KEY_PASSWORD\", secret, force = true)\n            (Seq(setSec), Seq(dir))\n          }\n          .getOrElse((Nil, Nil))\n\n        val keySetSecrets = setupKeys.secretKeyOpt match\n          case Some(configPasswordOption) =>\n            val secret = configPasswordOption.get(configDb())\n              .orThrow\n              .get()\n\n            Seq(SetSecret(\n              \"PUBLISH_SECRET_KEY\",\n              secret,\n              force = true\n            ))\n          case _ => Nil\n\n        val (publicKeySetSecret, publicKeyDirective) = setupKeys.publicKeyOpt\n          .map { p =>\n            val dir    = \"publish.ci.publicKey\" -> \"env:PUBLISH_PUBLIC_KEY\"\n            val secret = p.get(configDb()).orThrow.get()\n            val setSec = SetSecret(\"PUBLISH_PUBLIC_KEY\", secret, force = true)\n            (Seq(setSec), Seq(dir))\n          }\n          .getOrElse((Nil, Nil))\n\n        val secretsToSet    = keySetSecrets ++ passwordSetSecret ++ publicKeySetSecret\n        val extraDirectives = passwordDirectives ++ publicKeyDirective\n\n        OptionCheck.DefaultValue(\n          () => uploadKey(publicKeyOpt).map(_ => Some(\"env:PUBLISH_SECRET_KEY\")),\n          extraDirectives,\n          secretsToSet\n        )\n      }\n      else if (areConfigDefaults)\n        OptionCheck.DefaultValue(\n          () => uploadKey(publicKeyOpt).map(_ => None),\n          Nil,\n          Nil\n        )\n      else {\n\n        /** Obtain the text under the ConfigPasswordOption, e.g. \"env:...\", \"file:...\", \"value:...\"\n          */\n        def getDirectiveValue(configPasswordOpt: Option[ConfigPasswordOption]): Option[String] =\n          configPasswordOpt.collect {\n            case ActualOption(passwordOption) =>\n              val optionValue = passwordOption.asString.value\n\n              if (optionValue.startsWith(\"file:\")) {\n                val path = os.Path(optionValue.stripPrefix(\"file:\"))\n                scala.util.Try(path.relativeTo(os.pwd))\n                  .map(p => s\"file:${p.toString}\")\n                  .getOrElse(optionValue)\n              }\n              else optionValue\n            case ConfigOption(fullName) => s\"config:$fullName\"\n          }\n\n        val rawValueRegex = \"^value:(.*)\".r\n\n        // Prevent potential leakage of a secret value\n        val passwordDirectives = getDirectiveValue(setupKeys.secretKeyPasswordOpt)\n          .flatMap {\n            case rawValueRegex(rawValue) =>\n              logger.diagnostic(\n                WarningMessages.rawValueNotWrittenToPublishFile(\n                  rawValue,\n                  \"PGP password\",\n                  \"--secret-key-password\"\n                )\n              )\n              None\n            case secretOption => Some(\"publish.secretKeyPassword\" -> secretOption)\n          }\n          .toSeq\n\n        // Prevent potential leakage of a secret value\n        val secretKeyDirValue = getDirectiveValue(setupKeys.secretKeyOpt).flatMap {\n          case rawValueRegex(rawValue) =>\n            logger.diagnostic(\n              WarningMessages.rawValueNotWrittenToPublishFile(\n                rawValue,\n                \"PGP secret key\",\n                \"--secret-key\"\n              )\n            )\n            None\n          case secretOption => Some(secretOption)\n        }\n\n        // This is safe to be publicly available\n        val publicKeyDirective = getDirectiveValue(setupKeys.publicKeyOpt).map {\n          \"publish.publicKey\" -> _\n        }.toSeq\n\n        val extraDirectives = passwordDirectives ++ publicKeyDirective\n\n        OptionCheck.DefaultValue(\n          () => uploadKey(publicKeyOpt).map(_ => secretKeyDirValue),\n          extraDirectives,\n          Nil\n        )\n      }\n    }\n}\n\nobject PgpSecretKeyCheck {\n  final class KeyServerError(message: String) extends BuildException(message)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/RepositoryCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class RepositoryCheck(\n  options: PublishSetupOptions,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Repository\n  def fieldName     = \"repository\"\n  def directivePath = \"publish\" + (if (options.publishParams.setupCi) \".ci\" else \"\") + \".repository\"\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.retained(options.publishParams.setupCi).repository.nonEmpty\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n    val maybeRepo = options.publishRepo.publishRepository\n      .toRight(RepositoryCheck.missingValueError)\n      .orElse {\n        if (options.publishParams.setupCi) {\n          val repoFromLocal = pubOpt.retained(isCi = false)\n            .repository\n            .toRight(RepositoryCheck.missingValueError)\n          repoFromLocal.foreach { repoName =>\n            logger.message(\"repository:\")\n            logger.message(s\"  using repository from local configuration: $repoName\")\n          }\n          repoFromLocal\n        }\n        else Left(RepositoryCheck.missingValueError)\n      }\n\n    maybeRepo.map(repo => OptionCheck.DefaultValue.simple(repo, Nil, Nil))\n  }\n}\n\nobject RepositoryCheck {\n  def missingValueError = new MissingPublishOptionError(\n    \"repository\",\n    \"--publish-repository\",\n    \"publish.repository\",\n    extraMessage = \"use 'central' or 'central-s01' to publish to Maven Central via Sonatype.\"\n  )\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/ScmCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{GitRepo, OptionCheck, PublishSetupOptions}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class ScmCheck(\n  options: PublishSetupOptions,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Extra\n  def fieldName     = \"vcs\"\n  def directivePath = \"publish.versionControl\"\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.versionControl.nonEmpty\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n    def ghVcsOpt = GitRepo.ghRepoOrgName(workspace, logger) match {\n      case Left(err) =>\n        logger.debug(\n          s\"Error when trying to get GitHub repo from git to get default project VCS: $err, ignoring it.\"\n        )\n        None\n      case Right((org, name)) =>\n        logger.message(\"vcs:\")\n        logger.message(s\"  using GitHub repository $org/$name\")\n        Some(s\"github:$org/$name\")\n    }\n    options.publishParams.vcs.orElse(ghVcsOpt).map(\n      OptionCheck.DefaultValue.simple(_, Nil, Nil)\n    ).toRight {\n      new MissingPublishOptionError(\"version control\", \"--vcs\", \"publish.versionControl\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/UrlCheck.scala",
    "content": "package scala.cli.commands.publish.checks\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.{GitRepo, OptionCheck, PublishSetupOptions}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class UrlCheck(\n  options: PublishSetupOptions,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Extra\n  def fieldName     = \"url\"\n  def directivePath = \"publish.url\"\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.url.nonEmpty\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] = {\n    def ghUrlOpt = GitRepo.ghRepoOrgName(workspace, logger) match {\n      case Left(err) =>\n        logger.debug(\n          s\"Error when trying to get GitHub repo from git to get default project URL: $err, ignoring it.\"\n        )\n        None\n      case Right((org, name)) =>\n        val url = s\"https://github.com/$org/$name\"\n        logger.message(\"url:\")\n        logger.message(s\"  using GitHub repository URL $url\")\n        Some(url)\n    }\n    options.publishParams.url\n      .orElse(ghUrlOpt)\n      .map(OptionCheck.DefaultValue.simple(_, Nil, Nil))\n      .toRight {\n        new MissingPublishOptionError(\"url\", \"--url\", \"publish.url\")\n      }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/publish/checks/UserCheck.scala",
    "content": "package scala.cli.commands.publish.checks\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.BuildException\nimport scala.build.options.PublishOptions as BPublishOptions\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.publish.{OptionCheck, PublishSetupOptions, SetSecret}\nimport scala.cli.config.{ConfigDb, Keys}\nimport scala.cli.errors.MissingPublishOptionError\n\nfinal case class UserCheck(\n  options: PublishSetupOptions,\n  configDb: () => ConfigDb,\n  workspace: os.Path,\n  logger: Logger\n) extends OptionCheck {\n  def kind          = OptionCheck.Kind.Repository\n  def fieldName     = \"user\"\n  def directivePath = \"publish\" + (if (options.publishParams.setupCi) \".ci\" else \"\") + \".user\"\n\n  private def userOpt(pubOpt: BPublishOptions) =\n    CheckUtils.getHostOpt(\n      options,\n      pubOpt,\n      workspace,\n      logger\n    ) match {\n      case None       => Right(None)\n      case Some(host) =>\n        configDb().get(Keys.publishCredentials).wrapConfigException.map { credListOpt =>\n          credListOpt.flatMap { credList =>\n            credList\n              .iterator\n              .filter(_.host == host)\n              .map(_.user)\n              .collectFirst {\n                case Some(p) =>\n                  p\n              }\n          }\n        }\n    }\n\n  def check(pubOpt: BPublishOptions): Boolean =\n    pubOpt.retained(options.publishParams.setupCi).repoUser.nonEmpty ||\n    !options.publishParams.setupCi &&\n    (userOpt(pubOpt) match {\n      case Left(ex) =>\n        logger.debug(\"Ignoring error while trying to get user from config\")\n        logger.debug(ex)\n        true\n      case Right(valueOpt) =>\n        valueOpt.isDefined\n    })\n\n  def defaultValue(pubOpt: BPublishOptions): Either[BuildException, OptionCheck.DefaultValue] =\n    either {\n      if (options.publishParams.setupCi) {\n        val user0 = options.publishRepo.user match {\n          case Some(value0) => value0.toConfig\n          case None         =>\n            value(userOpt(pubOpt)) match {\n              case Some(user) =>\n                logger.message(\"publish.user:\")\n                logger.message(\n                  s\"  using ${Keys.publishCredentials.fullName} from Scala CLI configuration\"\n                )\n                user\n              case None =>\n                value {\n                  Left {\n                    new MissingPublishOptionError(\n                      \"publish user\",\n                      \"--user\",\n                      \"publish.credentials\",\n                      configKeys = Seq(Keys.publishCredentials.fullName)\n                    )\n                  }\n                }\n            }\n        }\n\n        OptionCheck.DefaultValue.simple(\n          \"env:PUBLISH_USER\",\n          Nil,\n          Seq(SetSecret(\"PUBLISH_USER\", user0.get(), force = true))\n        )\n      }\n      else\n        CheckUtils.getHostOpt(\n          options,\n          pubOpt,\n          workspace,\n          logger\n        ) match {\n          case None =>\n            logger.debug(\"No host, not checking for publish repository user\")\n            OptionCheck.DefaultValue.empty\n          case Some(host) =>\n            if (value(userOpt(pubOpt).wrapConfigException).isDefined) {\n              logger.message(\"publish.credentials:\")\n              logger.message(\n                s\"  found user for $host in ${Keys.publishCredentials.fullName} in Scala CLI configuration\"\n              )\n              OptionCheck.DefaultValue.empty\n            }\n            else {\n              val optionName =\n                CheckUtils.getRepoOpt(options, pubOpt).map(r => s\"publish user for $r\").getOrElse(\n                  \"publish user\"\n                )\n              value {\n                Left {\n                  new MissingPublishOptionError(\n                    optionName,\n                    \"\",\n                    \"publish.credentials\",\n                    configKeys = Seq(Keys.publishCredentials.fullName)\n                  )\n                }\n              }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala",
    "content": "package scala.cli.commands.repl\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport coursier.cache.FileCache\nimport coursier.error.ResolutionError\nimport dependency.*\n\nimport java.io.File\nimport java.util.zip.ZipFile\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.{\n  BuildException,\n  CantDownloadAmmoniteError,\n  FetchingDependenciesError,\n  MultipleScalaVersionsError\n}\nimport scala.build.input.Inputs\nimport scala.build.internal.{Constants, Runner}\nimport scala.build.options.ScalacOpt.noDashPrefixes\nimport scala.build.options.{BuildOptions, JavaOpt, MaybeScalaVersion, ScalaVersionUtil, Scope}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.run.Run.{createPythonInstance, orPythonDetectionError, pythonPathEnv}\nimport scala.cli.commands.run.RunMode\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, ScalacOptions, SharedOptions}\nimport scala.cli.commands.util.BuildCommandHelpers\nimport scala.cli.commands.{ScalaCommand, WatchUtil}\nimport scala.cli.config.Keys\nimport scala.cli.packaging.Library\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nobject Repl extends ScalaCommand[ReplOptions] with BuildCommandHelpers {\n  override def group: String           = HelpCommandGroup.Main.toString\n  override def scalaSpecificationLevel = SpecificationLevel.MUST\n  override def helpFormat: HelpFormat  = super.helpFormat\n    .withHiddenGroup(HelpGroup.Watch)\n    .withPrimaryGroup(HelpGroup.Repl)\n  override def names: List[List[String]] = List(\n    List(\"repl\"),\n    List(\"console\")\n  )\n  override def sharedOptions(options: ReplOptions): Option[SharedOptions] = Some(options.shared)\n\n  override def buildOptions(ops: ReplOptions): Some[BuildOptions] =\n    Some(buildOptions0(\n      ops,\n      scala.cli.internal.Constants.maxAmmoniteScala3Version,\n      scala.cli.internal.Constants.maxAmmoniteScala3LtsVersion\n    ))\n  private[commands] def buildOptions0(\n    ops: ReplOptions,\n    maxAmmoniteScalaVer: String,\n    maxAmmoniteScalaLtsVer: String\n  ): BuildOptions = {\n    import ops.*\n    import ops.sharedRepl.*\n\n    val logger = ops.shared.logger\n\n    val ammoniteVersionOpt = ammoniteVersion.map(_.trim).filter(_.nonEmpty)\n    val baseOptions        = shared.buildOptions(watchOptions = watch).orExit(logger)\n\n    val maybeDowngradedScalaVersion = {\n      val isDefaultAmmonite = ammonite.contains(true) && ammoniteVersionOpt.isEmpty\n      extension (s: MaybeScalaVersion)\n        private def isLtsAlias: Boolean =\n          s.versionOpt.exists(v => ScalaVersionUtil.scala3Lts.contains(v.toLowerCase))\n        private def isLts: Boolean =\n          s.versionOpt.exists(_.startsWith(Constants.scala3LtsPrefix)) || isLtsAlias\n      baseOptions.scalaOptions.scalaVersion match {\n        case Some(s)\n            if isDefaultAmmonite &&\n            s.isLts &&\n            (s\n              .versionOpt.exists(_.coursierVersion > maxAmmoniteScalaLtsVer.coursierVersion) ||\n            s.isLtsAlias) =>\n          val versionString = s.versionOpt.filter(_ => !s.isLtsAlias).getOrElse(Constants.scala3Lts)\n          logger.message(s\"Scala $versionString is not yet supported with this version of Ammonite\")\n          logger.message(s\"Defaulting to Scala $maxAmmoniteScalaLtsVer\")\n          Some(MaybeScalaVersion(maxAmmoniteScalaLtsVer))\n        case None\n            if isDefaultAmmonite &&\n            maxAmmoniteScalaVer.coursierVersion < defaultScalaVersion.coursierVersion =>\n          logger.message(\n            s\"Scala $defaultScalaVersion is not yet supported with this version of Ammonite\"\n          )\n          logger.message(s\"Defaulting to Scala $maxAmmoniteScalaVer\")\n          Some(MaybeScalaVersion(maxAmmoniteScalaVer))\n        case s => s\n      }\n    }\n\n    baseOptions.copy(\n      scalaOptions = baseOptions.scalaOptions.copy(scalaVersion = maybeDowngradedScalaVersion),\n      javaOptions = baseOptions.javaOptions.copy(\n        javaOpts =\n          baseOptions.javaOptions.javaOpts ++\n            sharedJava.allJavaOpts.map(JavaOpt(_)).map(Positioned.commandLine)\n      ),\n      notForBloopOptions = baseOptions.notForBloopOptions.copy(\n        replOptions = baseOptions.notForBloopOptions.replOptions.copy(\n          useAmmoniteOpt = ammonite,\n          ammoniteVersionOpt = ammoniteVersion.map(_.trim).filter(_.nonEmpty),\n          ammoniteArgs = ammoniteArg\n        ),\n        addRunnerDependencyOpt = baseOptions.notForBloopOptions.addRunnerDependencyOpt\n          .orElse(Some(false))\n      )\n    )\n  }\n\n  private def runMode(options: ReplOptions): RunMode.HasRepl =\n    RunMode.Default\n\n  override def runCommand(options: ReplOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val initialBuildOptions = buildOptionsOrExit(options)\n    def default             = Inputs.default().getOrElse {\n      Inputs.empty(Os.pwd, options.shared.markdown.enableMarkdown)\n    }\n    val inputs =\n      options.shared.inputs(args.remaining, defaultInputs = () => Some(default)).orExit(logger)\n    val programArgs = args.unparsed\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n\n    val threads = BuildThreads.create()\n    // compilerMaker should be a lazy val to prevent download a JAVA 17 for bloop when users run the repl without sources\n    lazy val compilerMaker = options.shared.compilerMaker(threads)\n\n    def doRunRepl(\n      buildOptions: BuildOptions,\n      allArtifacts: Seq[Artifacts],\n      mainJarsOrClassDirs: Seq[os.Path],\n      allowExit: Boolean,\n      runMode: RunMode.HasRepl,\n      successfulBuilds: Seq[Build.Successful]\n    ): Unit = {\n      val res = runRepl(\n        options = buildOptions,\n        programArgs = programArgs,\n        allArtifacts = allArtifacts,\n        mainJarsOrClassDirs = mainJarsOrClassDirs,\n        logger = logger,\n        allowExit = allowExit,\n        dryRun = options.sharedRepl.replDryRun,\n        runMode = runMode,\n        successfulBuilds = successfulBuilds\n      )\n      res match {\n        case Left(ex) =>\n          if (allowExit) logger.exit(ex)\n          else logger.log(ex)\n        case Right(()) =>\n      }\n    }\n    def doRunReplFromBuild(\n      builds: Seq[Build.Successful],\n      allowExit: Boolean,\n      runMode: RunMode.HasRepl,\n      asJar: Boolean\n    ): Unit = {\n      doRunRepl(\n        // build options should be the same for both scopes\n        // combining them may cause for ammonite args to be duplicated, so we're using the main scope's opts\n        buildOptions = builds.head.options,\n        allArtifacts = builds.map(_.artifacts),\n        mainJarsOrClassDirs =\n          if asJar then Seq(Library.libraryJar(builds)) else builds.map(_.output),\n        allowExit = allowExit,\n        runMode = runMode,\n        successfulBuilds = builds\n      )\n    }\n\n    val cross                 = options.sharedRepl.compileCross.cross.getOrElse(false)\n    val configDb              = ConfigDbUtils.configDb.orExit(logger)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions.orElse(\n        configDb.get(Keys.actions).getOrElse(None)\n      )\n\n    val shouldBuildTestScope = options.shared.scope.test.getOrElse(false)\n    if (inputs.isEmpty) {\n      val allArtifacts =\n        Seq(initialBuildOptions.artifacts(logger, Scope.Main).orExit(logger)) ++\n          (if shouldBuildTestScope\n           then Seq(initialBuildOptions.artifacts(logger, Scope.Test).orExit(logger))\n           else Nil)\n      // synchronizing, so that multiple presses to enter (handled by WatchUtil.waitForCtrlC)\n      // don't try to run repls in parallel\n      val lock       = new Object\n      def runThing() = lock.synchronized {\n        doRunRepl(\n          buildOptions = initialBuildOptions,\n          allArtifacts = allArtifacts,\n          mainJarsOrClassDirs = Seq.empty,\n          allowExit = !options.sharedRepl.watch.watchMode,\n          runMode = runMode(options),\n          successfulBuilds = Seq.empty\n        )\n      }\n      runThing()\n      if (options.sharedRepl.watch.watchMode) {\n        // nothing to watch, just wait for Ctrl+C\n        WatchUtil.printWatchMessage()\n        WatchUtil.waitForCtrlC(() => runThing())\n      }\n    }\n    else if (options.sharedRepl.watch.watchMode) {\n      val watcher = Build.watch(\n        inputs,\n        initialBuildOptions,\n        compilerMaker,\n        None,\n        logger,\n        crossBuilds = cross,\n        buildTests = shouldBuildTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics,\n        postAction = () => WatchUtil.printWatchMessage()\n      ) { res =>\n        for (builds <- res.orReport(logger))\n          postBuild(builds, allowExit = false) {\n            successfulBuilds =>\n              doRunReplFromBuild(\n                successfulBuilds,\n                allowExit = false,\n                runMode = runMode(options),\n                asJar = options.shared.asJar\n              )\n          }\n      }\n      try WatchUtil.waitForCtrlC(() => watcher.schedule())\n      finally watcher.dispose()\n    }\n    else {\n      val builds =\n        Build.build(\n          inputs,\n          initialBuildOptions,\n          compilerMaker,\n          None,\n          logger,\n          crossBuilds = cross,\n          buildTests = shouldBuildTestScope,\n          partial = None,\n          actionableDiagnostics = actionableDiagnostics\n        )\n          .orExit(logger)\n      postBuild(builds, allowExit = false) {\n        successfulBuilds =>\n          doRunReplFromBuild(\n            successfulBuilds,\n            allowExit = true,\n            runMode = runMode(options),\n            asJar = options.shared.asJar\n          )\n      }\n    }\n  }\n\n  def postBuild(builds: Builds, allowExit: Boolean)(f: Seq[Build.Successful] => Unit): Unit = {\n    if builds.anyBuildFailed then {\n      System.err.println(\"Compilation failed\")\n      if allowExit then sys.exit(1)\n    }\n    else if builds.anyBuildCancelled then {\n      System.err.println(\"Build cancelled\")\n      if allowExit then sys.exit(1)\n    }\n    else f(builds.builds.sortBy(_.scope).map(_.asInstanceOf[Build.Successful]))\n  }\n\n  private def maybeAdaptForWindows(args: Seq[String]): Seq[String] =\n    if (Properties.isWin)\n      args.map { a =>\n        if (a.contains(\" \")) \"\\\"\" + a.replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\"\n        else a\n      }\n    else\n      args\n\n  private def runRepl(\n    options: BuildOptions,\n    programArgs: Seq[String],\n    allArtifacts: Seq[Artifacts],\n    mainJarsOrClassDirs: Seq[os.Path],\n    logger: Logger,\n    allowExit: Boolean,\n    dryRun: Boolean,\n    runMode: RunMode.HasRepl,\n    successfulBuilds: Seq[Build.Successful]\n  ): Either[BuildException, Unit] = either {\n    val setupPython = options.notForBloopOptions.python.getOrElse(false)\n\n    val cache             = options.internal.cache.getOrElse(FileCache())\n    val shouldUseAmmonite = options.notForBloopOptions.replOptions.useAmmonite\n\n    val scalaParams: ScalaParameters = value {\n      val distinctScalaParams = allArtifacts.flatMap(_.scalaOpt).map(_.params).distinct\n      if distinctScalaParams.isEmpty then\n        Right(ScalaParameters(Constants.defaultScalaVersion))\n      else if distinctScalaParams.length == 1 then\n        Right(distinctScalaParams.head)\n      else Left(MultipleScalaVersionsError(distinctScalaParams.map(_.scalaVersion)))\n    }\n\n    val (scalapyJavaOpts, scalapyExtraEnv) =\n      if (setupPython) {\n        val props = value {\n          val python       = value(createPythonInstance().orPythonDetectionError)\n          val propsOrError = python.scalapyProperties\n          logger.debug(s\"Python Java properties: $propsOrError\")\n          propsOrError.orPythonDetectionError\n        }\n        val props0 = props.toVector.sorted.map {\n          case (k, v) => s\"-D$k=$v\"\n        }\n        // Putting current dir in PYTHONPATH, see\n        // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174\n        // for context.\n        val dirs = successfulBuilds.map(_.inputs.workspace) ++ Seq(os.pwd)\n        (props0, pythonPathEnv(dirs*))\n      }\n      else\n        (Nil, Map.empty[String, String])\n\n    def additionalArgs = {\n      val pythonArgs =\n        if (setupPython && scalaParams.scalaVersion.startsWith(\"2.13.\"))\n          Seq(\"-Yimports:java.lang,scala,scala.Predef,me.shadaj.scalapy\")\n        else\n          Nil\n      pythonArgs ++ options.scalaOptions.scalacOptions.toSeq.map(_.value.value)\n    }\n\n    def ammoniteAdditionalArgs() = {\n      val pythonPredef =\n        if (setupPython)\n          \"\"\"import me.shadaj.scalapy.py\n            |import me.shadaj.scalapy.py.PyQuote\n            |\"\"\".stripMargin\n        else\n          \"\"\n      val predefArgs =\n        if (pythonPredef.isEmpty) Nil\n        else Seq(\"--predef-code\", pythonPredef)\n      predefArgs ++ options.notForBloopOptions.replOptions.ammoniteArgs\n    }\n\n    // TODO Warn if some entries of artifacts.classPath were evicted in replArtifacts.replClassPath\n    //      (should be artifacts whose version was bumped by Ammonite).\n\n    // TODO Find the common namespace of all user classes, and import it all in the Ammonite session.\n\n    // TODO Allow to disable printing the welcome banner and the \"Loading...\" message in Ammonite.\n\n    val rootClasses = mainJarsOrClassDirs.flatMap {\n      case dir if os.isDir(dir) =>\n        os.list(dir)\n          .filter(_.last.endsWith(\".class\"))\n          .filter(os.isFile(_)) // just in case\n          .map(_.last.stripSuffix(\".class\"))\n          .sorted\n      case jar =>\n        var zf: ZipFile = null\n        try {\n          zf = new ZipFile(jar.toIO)\n          zf.entries()\n            .asScala\n            .map(_.getName)\n            .filter(!_.contains(\"/\"))\n            .filter(_.endsWith(\".class\"))\n            .map(_.stripSuffix(\".class\"))\n            .toVector\n            .sorted\n        }\n        finally\n          if (zf != null)\n            zf.close()\n    }\n    val warnRootClasses = rootClasses.nonEmpty &&\n      options.notForBloopOptions.replOptions.useAmmoniteOpt.contains(true)\n    if (warnRootClasses)\n      logger.message(\n        s\"Warning: found classes defined in the root package (${rootClasses.mkString(\", \")}).\" +\n          \" These will not be accessible from the REPL.\"\n      )\n\n    def maybeRunRepl(\n      replArtifacts: ReplArtifacts,\n      replArgs: Seq[String],\n      extraEnv: Map[String, String] = Map.empty,\n      extraProps: Map[String, String] = Map.empty\n    ): Unit = {\n      val isAmmonite = replArtifacts.replMainClass.startsWith(\"ammonite\")\n      if isAmmonite then\n        replArgs\n          .map(_.noDashPrefixes)\n          .filter(ScalacOptions.replExecuteScriptOptions.contains)\n          .foreach(arg =>\n            logger.message(\n              s\"The '--$arg' option is not supported with Ammonite. Did you mean to use '--ammonite-arg -c' to execute a script?\"\n            )\n          )\n      if dryRun then logger.message(\"Dry run, not running REPL.\")\n      else {\n        val depClassPathArgs: Seq[String] =\n          if replArtifacts.depsClassPath.nonEmpty && !isAmmonite then\n            Seq(\n              \"-classpath\",\n              (mainJarsOrClassDirs ++ replArtifacts.depsClassPath)\n                .map(_.toString).mkString(File.pathSeparator)\n            )\n          else Nil\n        val replLauncherClasspath =\n          if isAmmonite then mainJarsOrClassDirs ++ replArtifacts.replClassPath\n          else replArtifacts.replClassPath\n        val retCode = Runner.runJvm(\n          javaCommand = options.javaHome().value.javaCommand,\n          javaArgs = scalapyJavaOpts ++\n            replArtifacts.replJavaOpts ++\n            options.javaOptions.javaOpts.toSeq.map(_.value.value) ++\n            extraProps.toVector.sorted.map { case (k, v) => s\"-D$k=$v\" },\n          classPath = replLauncherClasspath,\n          mainClass = replArtifacts.replMainClass,\n          args = maybeAdaptForWindows(depClassPathArgs ++ replArgs),\n          logger = logger,\n          allowExecve = allowExit,\n          extraEnv = scalapyExtraEnv ++ extraEnv\n        ).waitFor()\n        if (retCode != 0)\n          value(Left(new ReplError(retCode)))\n      }\n    }\n\n    def defaultArtifacts(): Either[BuildException, ReplArtifacts] = either {\n      value {\n        ReplArtifacts.default(\n          scalaParams = scalaParams,\n          dependencies = allArtifacts.flatMap(_.userDependencies).distinct,\n          extraClassPath = allArtifacts.flatMap(_.extraClassPath).distinct,\n          logger = logger,\n          cache = cache,\n          repositories = value(options.finalRepositories),\n          addScalapy =\n            if setupPython then\n              Some(options.notForBloopOptions.scalaPyVersion.getOrElse(Constants.scalaPyVersion))\n            else None,\n          javaVersion = options.javaHome().value.version\n        )\n      }\n    }\n    def ammoniteArtifacts(): Either[BuildException, ReplArtifacts] =\n      ReplArtifacts.ammonite(\n        scalaParams = scalaParams,\n        ammoniteVersion =\n          options.notForBloopOptions.replOptions.ammoniteVersion(scalaParams.scalaVersion, logger),\n        dependencies = allArtifacts.flatMap(_.userDependencies),\n        extraClassPath = allArtifacts.flatMap(_.extraClassPath),\n        extraSourceJars = allArtifacts.flatMap(_.extraSourceJars),\n        extraRepositories = value(options.finalRepositories),\n        logger = logger,\n        cache = cache,\n        addScalapy =\n          if (setupPython)\n            Some(options.notForBloopOptions.scalaPyVersion.getOrElse(Constants.scalaPyVersion))\n          else None\n      ).left.map {\n        case FetchingDependenciesError(e: ResolutionError.CantDownloadModule, positions)\n            if shouldUseAmmonite &&\n            e.module.name.value == s\"ammonite_${scalaParams.scalaVersion}\" =>\n          CantDownloadAmmoniteError(\n            e.versionConstraint.asString,\n            scalaParams.scalaVersion,\n            e,\n            positions\n          )\n        case other => other\n      }\n\n    if (shouldUseAmmonite)\n      runMode match {\n        case RunMode.Default =>\n          val replArtifacts = value(ammoniteArtifacts())\n          val replArgs      = ammoniteAdditionalArgs() ++ programArgs\n          maybeRunRepl(replArtifacts, replArgs)\n      }\n    else\n      runMode match {\n        case RunMode.Default =>\n          val replArtifacts = value(defaultArtifacts())\n          val replArgs      = additionalArgs ++ programArgs\n          maybeRunRepl(replArtifacts, replArgs)\n      }\n  }\n\n  final class ReplError(retCode: Int)\n      extends BuildException(s\"Failed to run REPL (exit code: $retCode)\")\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/repl/ReplOptions.scala",
    "content": "package scala.cli.commands.repl\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{HasSharedOptions, HelpMessages, SharedOptions}\n\n@HelpMessage(ReplOptions.helpMessage, \"\", ReplOptions.detailedHelpMessage)\n// format: off\nfinal case class ReplOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    sharedRepl: SharedReplOptions = SharedReplOptions()\n) extends HasSharedOptions\n// format: on\n\nobject ReplOptions {\n  implicit lazy val parser: Parser[ReplOptions] = Parser.derive\n  implicit lazy val help: Help[ReplOptions]     = Help.derive\n  val cmdName                                   = \"repl\"\n  private val helpHeader                        = \"Fire-up a Scala REPL.\"\n  val helpMessage: String                       = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String               =\n    s\"\"\"$helpHeader\n       |\n       |The entire $fullRunnerName project's classpath is loaded to the repl.\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/repl/SharedReplOptions.scala",
    "content": "package scala.cli.commands.repl\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.commands.shared.{CrossOptions, HelpGroup, SharedJavaOptions, SharedWatchOptions}\nimport scala.cli.commands.{Constants, tags}\n\n// format: off\nfinal case class SharedReplOptions(\n  @Recurse\n    sharedJava: SharedJavaOptions = SharedJavaOptions(),\n  @Recurse\n    watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n    compileCross: CrossOptions = CrossOptions(),\n\n  @Group(HelpGroup.Repl.toString)\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Use Ammonite (instead of the default Scala REPL)\")\n  @Name(\"A\")\n  @Name(\"amm\")\n    ammonite: Option[Boolean] = None,\n\n  @Group(HelpGroup.Repl.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(s\"Set the Ammonite version (${Constants.ammoniteVersion} by default)\")\n  @Name(\"ammoniteVer\")\n  @Tag(tags.inShortHelp)\n    ammoniteVersion: Option[String] = None,\n\n  @Group(HelpGroup.Repl.toString)\n  @Name(\"a\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Provide arguments for ammonite repl\")\n  @Hidden\n    ammoniteArg: List[String] = Nil,\n\n  @Group(HelpGroup.Repl.toString)\n  @Hidden\n  @Tag(tags.implementation)\n  @HelpMessage(\"Don't actually run the REPL, just fetch it\")\n    replDryRun: Boolean = false\n)\n// format: on\n\nobject SharedReplOptions {\n  implicit lazy val parser: Parser[SharedReplOptions] = Parser.derive\n  implicit lazy val help: Help[SharedReplOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/run/Run.scala",
    "content": "package scala.cli.commands.run\n\nimport ai.kien.python.Python\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\n\nimport java.io.File\nimport java.util.Locale\nimport java.util.concurrent.CompletableFuture\nimport java.util.concurrent.atomic.AtomicReference\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.input.*\nimport scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.build.internals.EnvVar\nimport scala.build.options.{BuildOptions, JavaOpt, PackageType, Platform, Scope}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.package0.Package\nimport scala.cli.commands.setupide.SetupIde\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}\nimport scala.cli.commands.update.Update\nimport scala.cli.commands.util.BuildCommandHelpers.*\nimport scala.cli.commands.util.{BuildCommandHelpers, RunHadoop, RunSpark}\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel, WatchUtil}\nimport scala.cli.config.Keys\nimport scala.cli.internal.ProcUtil\nimport scala.cli.packaging.Library.fullClassPathMaybeAsJar\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\nimport scala.util.{Properties, Try}\n\nobject Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {\n  override def group: String                               = HelpCommandGroup.Main.toString\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.MUST\n\n  val primaryHelpGroups: Seq[HelpGroup] = Seq(HelpGroup.Run, HelpGroup.Entrypoint, HelpGroup.Watch)\n  override def helpFormat: HelpFormat   = super.helpFormat.withPrimaryGroups(primaryHelpGroups)\n  override def sharedOptions(options: RunOptions): Option[SharedOptions] = Some(options.shared)\n\n  private def runMode(options: RunOptions): RunMode =\n    if options.sharedRun.standaloneSpark.getOrElse(false) &&\n      !options.sharedRun.sparkSubmit.contains(false)\n    then RunMode.StandaloneSparkSubmit(options.sharedRun.submitArgument)\n    else if options.sharedRun.sparkSubmit.getOrElse(false)\n    then RunMode.SparkSubmit(options.sharedRun.submitArgument)\n    else if options.sharedRun.hadoopJar\n    then RunMode.HadoopJar\n    else RunMode.Default\n\n  private def scratchDirOpt(options: RunOptions): Option[os.Path] =\n    options.sharedRun.scratchDir\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, os.pwd))\n\n  override def runCommand(options: RunOptions, args: RemainingArgs, logger: Logger): Unit =\n    runCommand(\n      options0 = options,\n      inputArgs = args.remaining,\n      programArgs = args.unparsed,\n      defaultInputs = () => Inputs.default(),\n      logger = logger,\n      invokeData = invokeData\n    )\n\n  override def buildOptions(options: RunOptions): Some[BuildOptions] = Some {\n    import options.*\n    import options.sharedRun.*\n    val logger      = options.shared.logger\n    val baseOptions = options.buildOptions().orExit(logger)\n    baseOptions.copy(\n      mainClass = mainClass.mainClass,\n      javaOptions = baseOptions.javaOptions.copy(\n        javaOpts =\n          baseOptions.javaOptions.javaOpts ++\n            sharedJava.allJavaOpts.map(JavaOpt(_)).map(Positioned.commandLine),\n        jvmIdOpt = baseOptions.javaOptions.jvmIdOpt.orElse {\n          runMode(options) match {\n            case _: RunMode.Spark | RunMode.HadoopJar =>\n              val sparkOrHadoopDefaultJvm = \"8\"\n              logger.message(\n                s\"Defaulting the JVM to  $sparkOrHadoopDefaultJvm for Spark/Hadoop runs.\"\n              )\n              Some(Positioned.none(sparkOrHadoopDefaultJvm))\n            case RunMode.Default => None\n          }\n        }\n      ),\n      internal = baseOptions.internal.copy(\n        keepResolution = baseOptions.internal.keepResolution || {\n          runMode(options) match {\n            case _: RunMode.Spark | RunMode.HadoopJar => true\n            case RunMode.Default                      => false\n          }\n        }\n      ),\n      notForBloopOptions = baseOptions.notForBloopOptions.copy(\n        runWithManifest = options.sharedRun.useManifest,\n        addRunnerDependencyOpt = baseOptions.notForBloopOptions.addRunnerDependencyOpt.orElse {\n          runMode(options) match {\n            case _: RunMode.Spark | RunMode.HadoopJar =>\n              logger.debug(s\"$warnPrefix Skipping the runner dependency when running Spark/Hadoop.\")\n              Some(false)\n            case RunMode.Default => None\n          }\n        }\n      )\n    )\n  }\n\n  def runCommand(\n    options0: RunOptions,\n    inputArgs: Seq[String],\n    programArgs: Seq[String],\n    defaultInputs: () => Option[Inputs],\n    logger: Logger,\n    invokeData: ScalaCliInvokeData\n  ): Unit = {\n    val shouldDefaultServerFalse =\n      inputArgs.isEmpty && options0.shared.compilationServer.server.isEmpty &&\n      !options0.shared.hasSnippets\n    val options = if shouldDefaultServerFalse then {\n      logger.debug(\"No inputs provided, skipping the build server.\")\n      options0.copy(shared =\n        options0.shared.copy(compilationServer =\n          options0.shared.compilationServer.copy(server = Some(false))\n        )\n      )\n    }\n    else options0\n    val initialBuildOptions = {\n      val buildOptions = buildOptionsOrExit(options)\n      if invokeData.subCommand == SubCommand.Shebang then {\n        val suppressDepUpdateOptions = buildOptions.suppressWarningOptions.copy(\n          suppressOutdatedDependencyWarning = Some(true)\n        )\n\n        buildOptions.copy(\n          suppressWarningOptions = suppressDepUpdateOptions\n        )\n      }\n      else buildOptions\n    }\n\n    val inputs = options.shared.inputs(inputArgs, defaultInputs)(using invokeData).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    val threads = BuildThreads.create()\n\n    val compilerMaker = options.shared.compilerMaker(threads)\n\n    def maybeRun(\n      builds: Seq[Build.Successful],\n      allowTerminate: Boolean,\n      runMode: RunMode,\n      showCommand: Boolean,\n      scratchDirOpt: Option[os.Path]\n    ): Either[BuildException, Seq[(Process, CompletableFuture[?])]] = either {\n      val potentialMainClasses = builds.flatMap(_.foundMainClasses()).distinct\n      if options.sharedRun.mainClass.mainClassLs.contains(true) then\n        value {\n          options.sharedRun.mainClass\n            .maybePrintMainClasses(potentialMainClasses, shouldExit = allowTerminate)\n            .map(_ => Seq.empty)\n        }\n      else {\n        val processOrCommand: Either[Seq[Seq[String]], Seq[(Process, Option[() => Unit])]] = value {\n          maybeRunOnce(\n            builds = builds,\n            args = programArgs,\n            logger = logger,\n            allowExecve = allowTerminate,\n            jvmRunner = builds.exists(_.artifacts.hasJvmRunner),\n            potentialMainClasses = potentialMainClasses,\n            runMode = runMode,\n            showCommand = showCommand,\n            scratchDirOpt = scratchDirOpt,\n            asJar = options.shared.asJar\n          )\n        }\n\n        processOrCommand match {\n          case Right(processes) =>\n            processes.map { case (process, onExitOpt) =>\n              val onExitProcess = process.onExit().thenApply { p1 =>\n                val retCode = p1.exitValue()\n                onExitOpt.foreach(_())\n                (retCode, allowTerminate) match {\n                  case (0, true)  =>\n                  case (0, false) =>\n                    val gray  = ScalaCliConsole.GRAY\n                    val reset = Console.RESET\n                    System.err.println(s\"${gray}Program exited with return code $retCode.$reset\")\n                  case (_, true) =>\n                    sys.exit(retCode)\n                  case (_, false) =>\n                    val red      = Console.RED\n                    val lightRed = \"\\u001b[91m\"\n                    val reset    = Console.RESET\n                    System.err.println(\n                      s\"${red}Program exited with return code $lightRed$retCode$red.$reset\"\n                    )\n                }\n              }\n              (process, onExitProcess)\n            }\n          case Left(commands) =>\n            for {\n              command <- commands\n              arg     <- command\n            } println(arg)\n            Seq.empty\n        }\n      }\n    }\n\n    val cross = options.sharedRun.compileCross.cross.getOrElse(false)\n    if cross then\n      logger.log(\n        \"Cross builds enabled, preparing all builds for all Scala versions and platforms...\"\n      )\n    SetupIde.runSafe(\n      options = options.shared,\n      inputs = inputs,\n      logger = logger,\n      buildOptions = initialBuildOptions,\n      previousCommandName = Some(name),\n      args = inputArgs\n    )\n    if CommandUtils.shouldCheckUpdate then Update.checkUpdateSafe(logger)\n\n    val configDb              = ConfigDbUtils.configDb.orExit(logger)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions.orElse(\n        configDb.get(Keys.actions).getOrElse(None)\n      )\n\n    val shouldBuildTestScope = options.shared.scope.test.getOrElse(false)\n    if shouldBuildTestScope then\n      logger.log(\"Test scope enabled, including test scope inputs on the classpath...\")\n    if options.sharedRun.watch.watchMode then {\n\n      /** A handle to the Runner processes, used to kill the process if it's still alive when a\n        * change occured and restarts are allowed or to wait for it if restarts are not allowed\n        */\n      val processesRef = AtomicReference(Seq.empty[(Process, CompletableFuture[?])])\n\n      /** shouldReadInput controls whether [[WatchUtil.waitForCtrlC]](that's keeping the main thread\n        * alive) should try to read StdIn or just call wait()\n        */\n      val shouldReadInput = AtomicReference(false)\n\n      /** A handle to the main thread to interrupt its operations when:\n        *   - it's blocked on reading StdIn, and it's no longer required\n        *   - it's waiting and should start reading StdIn\n        */\n      val mainThreadOpt = AtomicReference(Option.empty[Thread])\n\n      val watcher = Build.watch(\n        inputs = inputs,\n        options = initialBuildOptions,\n        compilerMaker = compilerMaker,\n        docCompilerMakerOpt = None,\n        logger = logger,\n        crossBuilds = cross,\n        buildTests = shouldBuildTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics,\n        postAction = () =>\n          if processesRef.get().exists(_._1.isAlive()) then\n            WatchUtil.printWatchWhileRunningMessage()\n          else WatchUtil.printWatchMessage()\n      ) { res =>\n        for ((process, onExitProcess) <- processesRef.get()) {\n          onExitProcess.cancel(true)\n          ProcUtil.interruptProcess(process, logger)\n        }\n        res.orReport(logger).map(_.builds).foreach {\n          case b if b.forall(_.success) =>\n            val successfulBuilds = b.collect { case s: Build.Successful => s }\n            for ((proc, _) <- processesRef.get() if proc.isAlive)\n              // If the process doesn't exit, send SIGKILL\n              ProcUtil.forceKillProcess(proc, logger)\n            shouldReadInput.set(false)\n            mainThreadOpt.get().foreach(_.interrupt())\n            val maybeProcesses = maybeRun(\n              builds = successfulBuilds,\n              allowTerminate = false,\n              runMode = runMode(options),\n              showCommand = options.sharedRun.command,\n              scratchDirOpt = scratchDirOpt(options)\n            )\n              .orReport(logger)\n              .toSeq\n              .flatten\n              .map {\n                case (proc, onExit) =>\n                  if options.sharedRun.watch.restart then\n                    onExit.thenApply { _ =>\n                      shouldReadInput.set(true)\n                      mainThreadOpt.get().foreach(_.interrupt())\n                    }\n                  (proc, onExit)\n              }\n            successfulBuilds.foreach(_.copyOutput(options.shared))\n            if options.sharedRun.watch.restart\n            then processesRef.set(maybeProcesses)\n            else {\n              for ((proc, onExit) <- maybeProcesses)\n                ProcUtil.waitForProcess(proc, onExit)\n              shouldReadInput.set(true)\n              mainThreadOpt.get().foreach(_.interrupt())\n            }\n          case b if b.exists(bb => !bb.success && !bb.cancelled) =>\n            System.err.println(\"Compilation failed\")\n          case _ => ()\n        }\n      }\n      mainThreadOpt.set(Some(Thread.currentThread()))\n\n      try\n        WatchUtil.waitForCtrlC(\n          onPressEnter = { () =>\n            watcher.schedule()\n            shouldReadInput.set(false)\n          },\n          shouldReadInput = () => shouldReadInput.get()\n        )\n      finally {\n        mainThreadOpt.set(None)\n        watcher.dispose()\n      }\n    }\n    else\n      Build.build(\n        inputs = inputs,\n        options = initialBuildOptions,\n        compilerMaker = compilerMaker,\n        docCompilerMakerOpt = None,\n        logger = logger,\n        crossBuilds = cross,\n        buildTests = shouldBuildTestScope,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics\n      )\n        .orExit(logger)\n        .all match {\n        case b if b.forall(_.success) =>\n          val successfulBuilds = b.collect { case s: Build.Successful => s }\n          successfulBuilds.foreach(_.copyOutput(options.shared))\n          val results = maybeRun(\n            builds = successfulBuilds,\n            allowTerminate = true,\n            runMode = runMode(options),\n            showCommand = options.sharedRun.command,\n            scratchDirOpt = scratchDirOpt(options)\n          )\n            .orExit(logger)\n          for ((process, onExit) <- results)\n            ProcUtil.waitForProcess(process, onExit)\n        case b if b.exists(bb => !bb.success && !bb.cancelled) =>\n          System.err.println(\"Compilation failed\")\n          sys.exit(1)\n        case _ => ()\n      }\n  }\n\n  private def maybeRunOnce(\n    builds: Seq[Build.Successful],\n    args: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean,\n    jvmRunner: Boolean,\n    potentialMainClasses: Seq[String],\n    runMode: RunMode,\n    showCommand: Boolean,\n    scratchDirOpt: Option[os.Path],\n    asJar: Boolean\n  ): Either[BuildException, Either[Seq[Seq[String]], Seq[(Process, Option[() => Unit])]]] = either {\n    val mainClassOpt = builds.head.options.mainClass.filter(_.nonEmpty) // trim it too?\n      .orElse {\n        if builds.head.options.jmhOptions.enableJmh\n            .contains(true) && !builds.head.options.jmhOptions.canRunJmh\n        then Some(\"org.openjdk.jmh.Main\")\n        else None\n      }\n    val mainClass: String = mainClassOpt match {\n      case Some(cls) => cls\n      case None      =>\n        val retainedMainClassesByScope: Map[Scope, String] = value {\n          builds\n            .map { build =>\n              build.retainedMainClass(logger, mainClasses = potentialMainClasses)\n                .map(mainClass => build.scope -> mainClass)\n            }\n            .sequence\n            .left\n            .map(CompositeBuildException(_))\n            .map(_.toMap)\n        }\n        if retainedMainClassesByScope.size == 1 then retainedMainClassesByScope.head._2\n        else\n          retainedMainClassesByScope\n            .get(Scope.Main)\n            .orElse(retainedMainClassesByScope.get(Scope.Test))\n            .get\n    }\n    logger.debug(s\"Retained main class: $mainClass\")\n    val verbosity = builds.head.options.internal.verbosity.getOrElse(0).toString\n\n    val (finalMainClass, finalArgs) =\n      if jvmRunner\n      then (Constants.runnerMainClass, mainClass +: verbosity +: args)\n      else (mainClass, args)\n    logger.debug(s\"Final main class: $finalMainClass\")\n    val res = runOnce(\n      allBuilds = builds,\n      mainClass = finalMainClass,\n      args = finalArgs,\n      logger = logger,\n      allowExecve = allowExecve,\n      runMode = runMode,\n      showCommand = showCommand,\n      scratchDirOpt = scratchDirOpt,\n      asJar = asJar\n    )\n    value(res)\n  }\n\n  def pythonPathEnv(dirs: os.Path*): Map[String, String] = {\n    val onlySafePaths = sys.env.exists {\n      case (k, v) => k.toLowerCase(Locale.ROOT) == \"pythonsafepath\" && v.nonEmpty\n    }\n    // Don't add unsafe directories to PYTHONPATH if PYTHONSAFEPATH is set,\n    // see https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSAFEPATH\n    // and https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1336017760\n    // for more details.\n    if onlySafePaths then Map.empty[String, String]\n    else {\n      val (pythonPathEnvVarName, currentPythonPath) = sys.env\n        .find(_._1.toLowerCase(Locale.ROOT) == \"pythonpath\")\n        .getOrElse((\"PYTHONPATH\", \"\"))\n      val updatedPythonPath = (currentPythonPath +: dirs.map(_.toString))\n        .filter(_.nonEmpty)\n        .mkString(File.pathSeparator)\n      Map(pythonPathEnvVarName -> updatedPythonPath)\n    }\n  }\n\n  private def runOnce(\n    allBuilds: Seq[Build.Successful],\n    mainClass: String,\n    args: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean,\n    runMode: RunMode,\n    showCommand: Boolean,\n    scratchDirOpt: Option[os.Path],\n    asJar: Boolean\n  ): Either[BuildException, Either[Seq[Seq[String]], Seq[(Process, Option[() => Unit])]]] = {\n    val crossBuilds        = allBuilds.groupedByCrossParams.toSeq\n    val shouldLogCrossInfo = crossBuilds.size > 1\n    // execve replaces the current process, so we must not use it when spawning multiple cross-builds\n    val effectiveAllowExecve = allowExecve && !shouldLogCrossInfo\n    if shouldLogCrossInfo then\n      logger.log(\n        s\"Running ${crossBuilds.size} cross builds, one for each Scala version and platform combination.\"\n      )\n    crossBuilds\n      .map { (crossBuildParams, builds) =>\n        if shouldLogCrossInfo then logger.debug(s\"Running build for ${crossBuildParams.asString}\")\n        val build = builds.head\n        either {\n          build.options.platform.value match {\n            case Platform.JS =>\n              val esModule =\n                build.options.scalaJsOptions.moduleKindStr.exists(m => m == \"es\" || m == \"esmodule\")\n\n              val linkerConfig = builds.head.options.scalaJsOptions.linkerConfig(logger)\n              val jsDest       = {\n                val delete = scratchDirOpt.isEmpty\n                scratchDirOpt.foreach(os.makeDir.all(_))\n                os.temp(\n                  dir = scratchDirOpt.orNull,\n                  prefix = \"main\",\n                  suffix = if esModule then \".mjs\" else \".js\",\n                  deleteOnExit = delete\n                )\n              }\n              val res =\n                Package.linkJs(\n                  builds = builds,\n                  dest = jsDest,\n                  mainClassOpt = Some(mainClass),\n                  addTestInitializer = false,\n                  config = linkerConfig,\n                  fullOpt = value(build.options.scalaJsOptions.fullOpt),\n                  noOpt = build.options.scalaJsOptions.noOpt.getOrElse(false),\n                  logger = logger,\n                  scratchDirOpt = scratchDirOpt\n                ).map { outputPath =>\n                  val jsDom = build.options.scalaJsOptions.dom.getOrElse(false)\n                  if showCommand then Left(Runner.jsCommand(outputPath.toIO, args, jsDom = jsDom))\n                  else {\n                    val process = value {\n                      Runner.runJs(\n                        outputPath.toIO,\n                        args,\n                        logger,\n                        allowExecve = effectiveAllowExecve,\n                        jsDom = jsDom,\n                        sourceMap = build.options.scalaJsOptions.emitSourceMaps,\n                        esModule = esModule\n                      )\n                    }\n                    process.onExit().thenApply(_ => if os.exists(jsDest) then os.remove(jsDest))\n                    Right((process, None))\n                  }\n                }\n              value(res)\n            case Platform.Native =>\n              val setupPython = build.options.notForBloopOptions.doSetupPython.getOrElse(false)\n              val (pythonExecutable, pythonLibraryPaths, pythonExtraEnv) =\n                if setupPython then {\n                  val (exec, libPaths) = value {\n                    val python = value(createPythonInstance().orPythonDetectionError)\n                    val pythonPropertiesOrError = for {\n                      paths      <- python.nativeLibraryPaths\n                      executable <- python.executable\n                    } yield (Some(executable), paths)\n                    logger.debug(\n                      s\"Python executable and native library paths: $pythonPropertiesOrError\"\n                    )\n                    pythonPropertiesOrError.orPythonDetectionError\n                  }\n                  // Putting the workspace in PYTHONPATH, see\n                  // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174\n                  // for context.\n                  (exec, libPaths, pythonPathEnv(builds.head.inputs.workspace))\n                }\n                else\n                  (None, Nil, Map())\n              // seems conda doesn't add the lib directory to LD_LIBRARY_PATH (see conda/conda#308),\n              // which prevents apps from finding libpython for example, so we update it manually here\n              val libraryPathsEnv =\n                if pythonLibraryPaths.isEmpty then Map.empty\n                else {\n                  val prependTo =\n                    if Properties.isWin then EnvVar.Misc.path.name\n                    else if Properties.isMac then EnvVar.Misc.dyldLibraryPath.name\n                    else EnvVar.Misc.ldLibraryPath.name\n                  val currentOpt     = Option(System.getenv(prependTo))\n                  val currentEntries = currentOpt\n                    .map(_.split(File.pathSeparator).toSet)\n                    .getOrElse(Set.empty)\n                  val additionalEntries = pythonLibraryPaths.filter(!currentEntries.contains(_))\n                  if additionalEntries.isEmpty then Map.empty\n                  else {\n                    val newValue = (additionalEntries.iterator ++ currentOpt.iterator).mkString(\n                      File.pathSeparator\n                    )\n                    Map(prependTo -> newValue)\n                  }\n                }\n              val programNameEnv =\n                pythonExecutable.fold(Map.empty)(py => Map(\"SCALAPY_PYTHON_PROGRAMNAME\" -> py))\n              val extraEnv    = libraryPathsEnv ++ programNameEnv ++ pythonExtraEnv\n              val maybeResult = withNativeLauncher(\n                builds,\n                mainClass,\n                logger\n              ) { launcher =>\n                if showCommand then\n                  Left(\n                    extraEnv.toVector.sorted.map { case (k, v) => s\"$k=$v\" } ++\n                      Seq(launcher.toString) ++\n                      args\n                  )\n                else {\n                  val proc = Runner.runNative(\n                    launcher = launcher.toIO,\n                    args = args,\n                    logger = logger,\n                    allowExecve = effectiveAllowExecve,\n                    extraEnv = extraEnv\n                  )\n                  Right((proc, None))\n                }\n              }\n              value(maybeResult)\n            case Platform.JVM =>\n              def fwd(s: String): String  = s.replace('\\\\', '/')\n              def base(s: String): String = fwd(s).replaceAll(\".*/\", \"\")\n              runMode match {\n                case RunMode.Default =>\n                  val sourceFiles = builds.head.inputs.sourceFiles().map {\n                    case s: ScalaFile    => fwd(s.path.toString)\n                    case s: Script       => fwd(s.path.toString)\n                    case s: MarkdownFile => fwd(s.path.toString)\n                    case _: SbtFile      => \"\"\n                    case s: OnDisk       => fwd(s.path.toString)\n                    case s               => s.getClass.getName\n                  }.filter(_.nonEmpty).distinct\n                  val sources     = sourceFiles.mkString(File.pathSeparator)\n                  val sourceNames = sourceFiles.map(base).mkString(File.pathSeparator)\n\n                  val baseJavaProps = build.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n                    ++ Seq(s\"-Dscala.sources=$sources\", s\"-Dscala.source.names=$sourceNames\")\n                  val setupPython = build.options.notForBloopOptions.doSetupPython.getOrElse(false)\n                  val (pythonJavaProps, pythonExtraEnv) =\n                    if setupPython then {\n                      val scalapyProps = value {\n                        val python       = value(createPythonInstance().orPythonDetectionError)\n                        val propsOrError = python.scalapyProperties\n                        logger.debug(s\"Python Java properties: $propsOrError\")\n                        propsOrError.orPythonDetectionError\n                      }\n                      val props = scalapyProps.toVector.sorted.map {\n                        case (k, v) => s\"-D$k=$v\"\n                      }\n                      // Putting the workspace in PYTHONPATH, see\n                      // https://github.com/VirtusLab/scala-cli/pull/1616#issuecomment-1333283174\n                      // for context.\n                      (props, pythonPathEnv(build.inputs.workspace))\n                    }\n                    else\n                      (Nil, Map.empty[String, String])\n                  val allJavaOpts = pythonJavaProps ++ baseJavaProps\n                  if showCommand then\n                    Left {\n                      Runner.jvmCommand(\n                        build.options.javaHome().value.javaCommand,\n                        allJavaOpts,\n                        builds.flatMap(_.fullClassPathMaybeAsJar(asJar)).distinct,\n                        mainClass,\n                        args,\n                        extraEnv = pythonExtraEnv,\n                        useManifest = build.options.notForBloopOptions.runWithManifest,\n                        scratchDirOpt = scratchDirOpt\n                      )\n                    }\n                  else {\n                    val proc = Runner.runJvm(\n                      javaCommand = build.options.javaHome().value.javaCommand,\n                      javaArgs = allJavaOpts,\n                      classPath = builds.flatMap(_.fullClassPathMaybeAsJar(asJar)).distinct,\n                      mainClass = mainClass,\n                      args = args,\n                      logger = logger,\n                      allowExecve = effectiveAllowExecve,\n                      extraEnv = pythonExtraEnv,\n                      useManifest = build.options.notForBloopOptions.runWithManifest,\n                      scratchDirOpt = scratchDirOpt\n                    )\n                    Right((proc, None))\n                  }\n                case mode: RunMode.SparkSubmit =>\n                  value {\n                    RunSpark.run(\n                      builds = builds,\n                      mainClass = mainClass,\n                      args = args,\n                      submitArgs = mode.submitArgs,\n                      logger = logger,\n                      allowExecve = effectiveAllowExecve,\n                      showCommand = showCommand,\n                      scratchDirOpt = scratchDirOpt\n                    )\n                  }\n                case mode: RunMode.StandaloneSparkSubmit =>\n                  value {\n                    RunSpark.runStandalone(\n                      builds = builds,\n                      mainClass = mainClass,\n                      args = args,\n                      submitArgs = mode.submitArgs,\n                      logger = logger,\n                      allowExecve = effectiveAllowExecve,\n                      showCommand = showCommand,\n                      scratchDirOpt = scratchDirOpt\n                    )\n                  }\n                case RunMode.HadoopJar =>\n                  value {\n                    RunHadoop.run(\n                      builds = builds,\n                      mainClass = mainClass,\n                      args = args,\n                      logger = logger,\n                      allowExecve = effectiveAllowExecve,\n                      showCommand = showCommand,\n                      scratchDirOpt = scratchDirOpt\n                    )\n                  }\n              }\n          }\n        }\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map(_.sequence.left.map(_.toSeq))\n  }\n\n  def withLinkedJs[T](\n    builds: Seq[Build.Successful],\n    mainClassOpt: Option[String],\n    addTestInitializer: Boolean,\n    config: ScalaJsLinkerConfig,\n    fullOpt: Boolean,\n    noOpt: Boolean,\n    logger: Logger,\n    esModule: Boolean\n  )(f: os.Path => T): Either[BuildException, T] = {\n    val dest = os.temp(prefix = \"main\", suffix = if esModule then \".mjs\" else \".js\")\n    try Package.linkJs(\n        builds = builds,\n        dest = dest,\n        mainClassOpt = mainClassOpt,\n        addTestInitializer = addTestInitializer,\n        config = config,\n        fullOpt = fullOpt,\n        noOpt = noOpt,\n        logger = logger\n      ).map(outputPath => f(outputPath))\n    finally if os.exists(dest) then os.remove(dest)\n  }\n\n  def withNativeLauncher[T](\n    builds: Seq[Build.Successful],\n    mainClass: String,\n    logger: Logger\n  )(f: os.Path => T): Either[BuildException, T] =\n    Package.buildNative(\n      builds = builds,\n      mainClass = Some(mainClass),\n      targetType = PackageType.Native.Application,\n      destPath = None,\n      logger = logger\n    ).map(f)\n\n  def findHomebrewPython(): Option[os.Path] =\n    if (Properties.isMac) {\n      // Try common Homebrew locations\n      val homebrewPaths = Seq(\n        \"/opt/homebrew/bin/python3\",\n        \"/usr/local/bin/python3\"\n      )\n      homebrewPaths\n        .map(os.Path(_, os.pwd))\n        .find { path =>\n          os.exists(path) && {\n            // Verify it has the -config script\n            val configPath = path / os.up / s\"${path.last}-config\"\n            os.exists(configPath)\n          }\n        }\n    }\n    else None\n\n  def createPythonInstance(): Try[Python] =\n    // Try default Python detection first\n    // If it fails on macOS and Homebrew Python is available, the error message will guide the user\n    Try(Python())\n\n  final class PythonDetectionError(cause: Throwable) extends BuildException(\n        {\n          val baseMessage = cause.getMessage\n          if (\n            Properties.isMac && baseMessage != null && baseMessage.contains(\n              \"-config\"\n            ) && baseMessage.contains(\"does not exist\")\n          ) {\n            val homebrewPython = findHomebrewPython()\n            val homebrewHint   = homebrewPython match {\n              case Some(path) =>\n                s\"\"\"\n                   |Homebrew Python was found at $path, but it's not being used.\n                   |Ensure Homebrew's bin directory is first in your PATH:\n                   |  export PATH=\"${path / os.up}:$$PATH\"\n                   |\"\"\".stripMargin\n              case None =>\n                \"\"\"\n                  |Consider installing Python via Homebrew:\n                  |  brew install python\n                  |\n                  |Or install Python from https://www.python.org/downloads/\n                  |\"\"\".stripMargin\n            }\n            s\"\"\"Error detecting Python environment: $baseMessage\n               |\n               |The system Python from CommandLineTools may not include the required -config scripts.\n               |$homebrewHint\n               |\n               |Alternatively, you can disable Python setup with --python=false\"\"\".stripMargin\n          }\n          else\n            s\"Error detecting Python environment: $baseMessage\"\n        },\n        cause = cause\n      )\n\n  extension [T](t: Try[T])\n    def orPythonDetectionError: Either[PythonDetectionError, T] =\n      t.toEither.left.map(new PythonDetectionError(_))\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/run/RunMode.scala",
    "content": "package scala.cli.commands.run\n\nsealed abstract class RunMode extends Product with Serializable\n\nobject RunMode {\n\n  sealed abstract class HasRepl extends RunMode\n  sealed abstract class Spark   extends RunMode {\n    def submitArgs: Seq[String]\n    def withSubmitArgs(args: Seq[String]): Spark\n  }\n\n  case object Default                                   extends HasRepl\n  final case class SparkSubmit(submitArgs: Seq[String]) extends Spark {\n    def withSubmitArgs(args: Seq[String]): SparkSubmit =\n      copy(submitArgs = args)\n  }\n  final case class StandaloneSparkSubmit(submitArgs: Seq[String]) extends Spark {\n    def withSubmitArgs(args: Seq[String]): StandaloneSparkSubmit =\n      copy(submitArgs = args)\n  }\n  case object HadoopJar extends RunMode\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/run/RunOptions.scala",
    "content": "package scala.cli.commands.run\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.ScalaCli\nimport scala.cli.commands.shared.{\n  HasSharedOptions, HasSharedWatchOptions, HelpMessages, SharedOptions, SharedWatchOptions\n}\n\n@HelpMessage(RunOptions.helpMessage, \"\", RunOptions.detailedHelpMessage)\n// format: off\nfinal case class RunOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    sharedRun: SharedRunOptions = SharedRunOptions()\n) extends HasSharedOptions with HasSharedWatchOptions {\n  // format: on\n  override def watch: SharedWatchOptions = sharedRun.watch\n}\n\nobject RunOptions {\n  implicit lazy val parser: Parser[RunOptions] = Parser.derive\n  implicit lazy val help: Help[RunOptions]     = Help.derive\n\n  val cmdName                     = \"run\"\n  private val helpHeader          = \"Compile and run Scala code.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |For a run to be successful, a main method must be present on the classpath.\n       |.sc scripts are an exception, as a main class is provided in their wrapper.\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |To pass arguments to the actual application, just add them after `--`, like:\n       |  ${Console.BOLD}${ScalaCli\n        .progName} run Main.scala AnotherSource.scala -- first-arg second-arg${Console.RESET}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/run/SharedRunOptions.scala",
    "content": "package scala.cli.commands.run\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedRunOptions(\n  @Recurse\n    sharedJava: SharedJavaOptions = SharedJavaOptions(),\n  @Recurse\n    watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n    compileCross: CrossOptions = CrossOptions(),\n  @Recurse\n    mainClass: MainClassOptions = MainClassOptions(),\n  @Group(HelpGroup.Run.toString)\n  @Hidden\n  @Tag(tags.experimental)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Run as a Spark job, using the spark-submit command\")\n  @ExtraName(\"spark\")\n    sparkSubmit: Option[Boolean] = None,\n  @Group(HelpGroup.Run.toString)\n  @Hidden\n  @Tag(tags.experimental)\n  @HelpMessage(\"Spark-submit arguments\")\n  @ExtraName(\"submitArg\")\n    submitArgument: List[String] = Nil,\n  @Group(HelpGroup.Run.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Run as a Spark job, using a vanilla Spark distribution downloaded by Scala CLI\")\n  @ExtraName(\"sparkStandalone\")\n    standaloneSpark: Option[Boolean] = None,\n  @Group(HelpGroup.Run.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Run as a Hadoop job, using the \\\"hadoop jar\\\" command\")\n  @ExtraName(\"hadoop\")\n    hadoopJar: Boolean = false,\n  @Group(HelpGroup.Run.toString)\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Print the command that would have been run (one argument per line), rather than running it\")\n    command: Boolean = false,\n  @Group(HelpGroup.Run.toString)\n  @HelpMessage(\"Temporary / working directory where to write generated launchers\")\n    scratchDir: Option[String] = None,\n  @Group(HelpGroup.Run.toString)\n  @Hidden\n  @Tag(tags.implementation)\n  @HelpMessage(\"Run Java commands using a manifest-based class path (shortens command length)\")\n    useManifest: Option[Boolean] = None\n)\n// format: on\n\nobject SharedRunOptions {\n  implicit lazy val parser: Parser[SharedRunOptions] = Parser.derive\n  implicit lazy val help: Help[SharedRunOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/setupide/SetupIde.scala",
    "content": "package scala.cli.commands.setupide\n\nimport caseapp.*\nimport ch.epfl.scala.bsp4j.BspConnectionDetails\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\nimport com.google.gson.GsonBuilder\n\nimport java.nio.charset.{Charset, StandardCharsets}\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.bsp.IdeInputs\nimport scala.build.errors.{BuildException, WorkspaceError}\nimport scala.build.input.{Inputs, OnDisk, Virtual, WorkspaceOrigin}\nimport scala.build.internal.Constants\nimport scala.build.internals.EnvVar\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.CommandUtils.{hasSelfExecutablePreamble, isJar}\nimport scala.cli.commands.shared.{SharedBspFileOptions, SharedOptions}\nimport scala.cli.commands.util.JvmUtils\nimport scala.cli.commands.{CommandUtils, ScalaCommand}\nimport scala.cli.errors.FoundVirtualInputsError\nimport scala.cli.launcher.LauncherOptions\nimport scala.jdk.CollectionConverters.*\n\nobject SetupIde extends ScalaCommand[SetupIdeOptions] {\n\n  def downloadDeps(\n    inputs: Inputs,\n    options: BuildOptions,\n    logger: Logger\n  ): Either[BuildException, Artifacts] = {\n\n    // ignoring errors related to sources themselves\n    val maybeSourceBuildOptions = either {\n      val (crossSources, allInputs) = value {\n        CrossSources.forInputs(\n          inputs,\n          Sources.defaultPreprocessors(\n            options.archiveCache,\n            options.internal.javaClassNameVersionOpt,\n            () => options.javaHome().value.javaCommand\n          ),\n          logger,\n          options.suppressWarningOptions,\n          options.internal.exclude,\n          download = options.downloader\n        )\n      }\n\n      crossSources.sharedOptions(options)\n\n      val scopedSources = value(crossSources.scopedSources(options))\n      val mainSources   = value(scopedSources.sources(\n        Scope.Main,\n        crossSources.sharedOptions(options),\n        allInputs.workspace,\n        logger\n      ))\n\n      mainSources.buildOptions\n    }\n\n    val joinedBuildOpts = maybeSourceBuildOptions.toOption.map(options.orElse(_)).getOrElse(options)\n    joinedBuildOpts.artifacts(logger, Scope.Main)\n  }\n\n  override def scalaSpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def runCommand(options: SetupIdeOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val buildOptions = buildOptionsOrExit(options)\n    val inputs       = options.shared.inputs(args.all).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n\n    val bspPath = writeBspConfiguration(\n      options,\n      inputs,\n      buildOptions,\n      previousCommandName = None,\n      args = args.all\n    ).orExit(logger)\n\n    bspPath.foreach(path => println(s\"Wrote configuration file for ide in: $path\"))\n  }\n\n  def runSafe(\n    options: SharedOptions,\n    inputs: Inputs,\n    logger: Logger,\n    buildOptions: BuildOptions,\n    previousCommandName: Option[String],\n    args: Seq[String]\n  ): Unit =\n    writeBspConfiguration(\n      SetupIdeOptions(shared = options),\n      inputs,\n      buildOptions,\n      previousCommandName,\n      args\n    ) match {\n      case Left(ex) =>\n        logger.debug(s\"Ignoring error during setup-ide: ${ex.message}\")\n      case Right(_) =>\n    }\n\n  override def sharedOptions(options: SetupIdeOptions): Option[SharedOptions] = Some(options.shared)\n\n  private def writeBspConfiguration(\n    options: SetupIdeOptions,\n    inputs: Inputs,\n    buildOptions: BuildOptions,\n    previousCommandName: Option[String],\n    args: Seq[String]\n  ): Either[BuildException, Option[os.Path]] = either {\n\n    val virtualInputs = inputs.elements.collect {\n      case v: Virtual => v\n    }\n    if (virtualInputs.nonEmpty)\n      value(Left(new FoundVirtualInputsError(virtualInputs)))\n\n    val progName = argvOpt.flatMap(_.headOption).getOrElse {\n      sys.error(\"setup-ide called in a non-standard way :|\")\n    }\n\n    val logger = options.shared.logger\n\n    if (buildOptions.classPathOptions.allExtraDependencies.toSeq.nonEmpty)\n      value(downloadDeps(\n        inputs,\n        buildOptions,\n        logger\n      ))\n\n    val (bspName, bspJsonDestination) = bspDetails(inputs.workspace, options.bspFile)\n    val scalaCliBspJsonDestination    =\n      inputs.workspace / Constants.workspaceDirName / \"ide-options-v2.json\"\n    val scalaCliBspLauncherOptsJsonDestination =\n      inputs.workspace / Constants.workspaceDirName / \"ide-launcher-options.json\"\n    val scalaCliBspInputsJsonDestination =\n      inputs.workspace / Constants.workspaceDirName / \"ide-inputs.json\"\n    val scalaCliBspEnvsJsonDestination =\n      inputs.workspace / Constants.workspaceDirName / \"ide-envs.json\"\n\n    val inputArgs = inputs.elements.collect { case d: OnDisk => d.path.toString }\n\n    val ideInputs = IdeInputs(\n      options.shared.validateInputArgs(args)\n        .flatMap(_.toOption)\n        .flatten\n        .collect { case d: OnDisk => d.path.toString }\n    )\n\n    val debugOpt = options.shared.jvm.bspDebugPort.toSeq.map(port =>\n      s\"-J-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:$port,suspend=y\"\n    )\n\n    val launcher = os.Path {\n      launcherOptions.scalaRunner.initialLauncherPath\n        .getOrElse(CommandUtils.getAbsolutePathToScalaCli(progName))\n    }\n    val finalLauncherOptions = launcherOptions.copy(cliVersion =\n      launcherOptions.cliVersion.orElse(launcherOptions.scalaRunner.predefinedCliVersion)\n    )\n\n    val launcherCommand =\n      if launcher.isJar && !launcher.hasSelfExecutablePreamble\n      then\n        List(\n          value {\n            JvmUtils.getJavaCmdVersionOrHigher(\n              javaVersion =\n                math.max(Constants.minimumInternalJavaVersion, Constants.minimumBloopJavaVersion),\n              options = buildOptions\n            )\n          }.javaCommand,\n          \"-jar\",\n          launcher.toString\n        )\n      else List(launcher.toString)\n\n    val bspArgs =\n      launcherCommand ++\n        finalLauncherOptions.toCliArgs ++\n        launcherJavaPropArgs ++\n        List(\"bsp\") ++\n        debugOpt ++\n        List(\"--json-options\", scalaCliBspJsonDestination.toString) ++\n        List(\"--json-launcher-options\", scalaCliBspLauncherOptsJsonDestination.toString) ++\n        List(\"--envs-file\", scalaCliBspEnvsJsonDestination.toString) ++\n        inputArgs\n    val details = new BspConnectionDetails(\n      bspName,\n      bspArgs.asJava,\n      Constants.version,\n      bloop.rifle.internal.BuildInfo.bspVersion,\n      List(\"scala\", \"java\").asJava\n    )\n\n    val charset =\n      options.charset\n        .map(_.trim)\n        .filter(_.nonEmpty)\n        .map(Charset.forName)\n        .getOrElse(StandardCharsets.UTF_8)\n\n    val gson = new GsonBuilder().setPrettyPrinting().create()\n\n    implicit val mapCodec: JsonValueCodec[Map[String, String]] = JsonCodecMaker.make\n\n    val json                         = gson.toJson(details)\n    val scalaCliOptionsForBspJson    = writeToArray(options.shared)(using SharedOptions.jsonCodec)\n    val scalaCliLaunchOptsForBspJson =\n      writeToArray(finalLauncherOptions)(using LauncherOptions.jsonCodec)\n    val scalaCliBspInputsJson = writeToArray(ideInputs)\n    val envsForBsp            = sys.env.filter((key, _) => EnvVar.allBsp.map(_.name).contains(key))\n    val scalaCliBspEnvsJson   = writeToArray(envsForBsp)\n\n    if (inputs.workspaceOrigin.contains(WorkspaceOrigin.HomeDir))\n      value(Left(new WorkspaceError(\n        s\"\"\"$baseRunnerName can not determine where to write its BSP configuration.\n           |Set an explicit BSP directory path via `--bsp-directory`.\n           |\"\"\".stripMargin\n      )))\n\n    if (previousCommandName.isEmpty || !bspJsonDestination.toIO.exists()) {\n      os.write.over(bspJsonDestination, json.getBytes(charset), createFolders = true)\n      os.write.over(\n        scalaCliBspJsonDestination,\n        scalaCliOptionsForBspJson,\n        createFolders = true\n      )\n      os.write.over(\n        scalaCliBspLauncherOptsJsonDestination,\n        scalaCliLaunchOptsForBspJson,\n        createFolders = true\n      )\n      os.write.over(\n        scalaCliBspInputsJsonDestination,\n        scalaCliBspInputsJson,\n        createFolders = true\n      )\n      os.write.over(\n        scalaCliBspEnvsJsonDestination,\n        scalaCliBspEnvsJson,\n        createFolders = true\n      )\n      logger.debug(s\"Wrote $bspJsonDestination\")\n      Some(bspJsonDestination)\n    }\n    else\n      None\n  }\n\n  def bspDetails(workspace: os.Path, ops: SharedBspFileOptions): (String, os.Path) = {\n    import ops.*\n    val dir = bspDirectory\n      .filter(_.nonEmpty)\n      .map(os.Path(_, Os.pwd))\n      .getOrElse(workspace / \".bsp\")\n    val bspName0 = bspName.map(_.trim).filter(_.nonEmpty).getOrElse(baseRunnerName)\n\n    (bspName0, dir / s\"$bspName0.json\")\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/setupide/SetupIdeOptions.scala",
    "content": "package scala.cli.commands.setupide\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.{baseRunnerName, fullRunnerName}\nimport scala.cli.commands.shared.{\n  HasSharedOptions,\n  HelpMessages,\n  SharedBspFileOptions,\n  SharedOptions\n}\nimport scala.cli.commands.tags\n\n@HelpMessage(SetupIdeOptions.helpMessage, \"\", SetupIdeOptions.detailedHelpMessage)\n// format: off\nfinal case class SetupIdeOptions(\n  @Recurse\n    shared: SharedOptions = SharedOptions(),\n  @Recurse\n    bspFile: SharedBspFileOptions = SharedBspFileOptions(),\n  @Hidden\n  @Tag(tags.implementation)\n  charset: Option[String] = None\n) extends HasSharedOptions\n// format: on\n\nobject SetupIdeOptions {\n  implicit lazy val parser: Parser[SetupIdeOptions] = Parser.derive\n  implicit lazy val help: Help[SetupIdeOptions]     = Help.derive\n  val cmdName                                       = \"setup-ide\"\n  private val helpHeader          = \"Generates a BSP file that you can import into your IDE.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |The $cmdName sub-command allows to pre-configure a $fullRunnerName project to import to an IDE with BSP support.\n       |It is also ran implicitly when `compile`, `run`, `shebang` or `test` sub-commands are called.\n       |\n       |The pre-configuration should be saved in a BSP json connection file under the path:\n       |    ${Console.BOLD}{project-root}/.bsp/$baseRunnerName.json${Console.RESET}\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/AllExternalHelpOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport caseapp.core.help.Help\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\n@HelpMessage(\"Print help message\")\n// this is an aggregate for all external and internal help options\ncase class AllExternalHelpOptions(\n  @Recurse\n  scalacExtra: ScalacExtraOptions = ScalacExtraOptions(),\n  @Recurse\n  helpGroups: HelpGroupOptions = HelpGroupOptions()\n)\n\nobject AllExternalHelpOptions {\n  implicit lazy val parser: Parser[AllExternalHelpOptions]            = Parser.derive\n  implicit lazy val help: Help[AllExternalHelpOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[AllExternalHelpOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ArgSplitter.scala",
    "content": "package scala.cli.commands.shared\n\nimport scala.annotation.tailrec\nimport scala.collection.mutable.ListBuffer\n\nobject ArgSplitter {\n  def splitToArgs(input: String) = {\n    val iter        = input.iterator\n    val accumulator = new ListBuffer[String]\n\n    @tailrec\n    def takeWhile(\n      test: Char => Boolean,\n      acc: List[Char] = Nil,\n      prevWasEscape: Boolean = false\n    ): String =\n      iter.nextOption() match\n        case Some(c) if !prevWasEscape && test(c) => acc.reverse.mkString\n        case None                                 => acc.reverse.mkString\n        case Some('\\\\')                           =>\n          takeWhile(test, '\\\\' :: acc, prevWasEscape = true)\n        case Some(c) =>\n          takeWhile(test, c :: acc, prevWasEscape = false)\n\n    while (iter.hasNext)\n      iter.next() match\n        case c if c.isSpaceChar || c == '\\n' || c == '\\r' =>\n        case c @ ('\\'' | '\"') => accumulator += s\"$c${takeWhile(_ == c)}$c\"\n        case c                =>\n          accumulator += s\"$c${takeWhile(c => c.isSpaceChar || c == '\\n' || c == '\\r')}\"\n\n    accumulator.result()\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/BenchmarkingOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.build.internal.Constants\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class BenchmarkingOptions(\n  @Group(HelpGroup.Benchmarking.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Run JMH benchmarks\")\n    jmh: Option[Boolean] = None,\n  @Group(HelpGroup.Benchmarking.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(s\"Set JMH version (default: ${Constants.jmhVersion})\")\n  @ValueDescription(\"version\")\n    jmhVersion: Option[String] = None\n)\n// format: on\n\nobject BenchmarkingOptions {\n  implicit lazy val parser: Parser[BenchmarkingOptions] = Parser.derive\n  implicit lazy val help: Help[BenchmarkingOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/CoursierOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport coursier.cache.{CacheLogger, CachePolicy, FileCache}\nimport coursier.util.Task\n\nimport scala.build.Logger\nimport scala.build.internals.EnvVar\nimport scala.cli.commands.tags\nimport scala.cli.config.Keys\nimport scala.cli.util.ConfigDbUtils\nimport scala.concurrent.duration.Duration\n\n// format: off\nfinal case class CoursierOptions(\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Specify a TTL for changing dependencies, such as snapshots\")\n  @ValueDescription(\"duration|Inf\")\n  @Tag(tags.implementation)\n  @Hidden\n    ttl: Option[String] = None,\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Set the coursier cache location\")\n  @ValueDescription(\"path\")\n  @Tag(tags.implementation)\n  @Hidden\n    cache: Option[String] = None,\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Enable checksum validation of artifacts downloaded by coursier\")\n  @Tag(tags.implementation)\n  @Hidden\n    coursierValidateChecksums: Option[Boolean] = None,\n\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Disable using the network to download artifacts, use the local cache only\")\n  @Tag(tags.experimental)\n    offline: Option[Boolean] = None\n) {\n  // format: on\n\n  private def validateChecksums =\n    coursierValidateChecksums.getOrElse(true)\n\n  def coursierCache(logger: Logger, cacheLogger: CacheLogger): FileCache[Task] = {\n    var baseCache = FileCache().withLogger(cacheLogger)\n    if (!validateChecksums)\n      baseCache = baseCache.withChecksums(Nil)\n    val ttlOpt = ttl.map(_.trim).filter(_.nonEmpty).map(Duration(_))\n    for (ttl0 <- ttlOpt)\n      baseCache = baseCache.withTtl(ttl0)\n    for (loc <- cache.filter(_.trim.nonEmpty))\n      baseCache = baseCache.withLocation(loc)\n    for (isOffline <- getOffline(logger) if isOffline)\n      baseCache = baseCache.withCachePolicies(Seq(CachePolicy.LocalOnly))\n\n    baseCache\n  }\n\n  def coursierCache(logger: Logger, cacheLoggerPrefix: String = \"\"): FileCache[Task] =\n    coursierCache(logger, logger.coursierLogger(cacheLoggerPrefix))\n\n  def getOffline(logger: Logger): Option[Boolean] = offline\n    .orElse(EnvVar.Coursier.coursierMode.valueOpt.map(_ == \"offline\"))\n    .orElse(Option(System.getProperty(\"coursier.mode\")).map(_ == \"offline\"))\n    .orElse(ConfigDbUtils.getConfigDbOpt(logger).flatMap(_.get(Keys.offline).toOption.flatten))\n}\n\nobject CoursierOptions {\n  implicit lazy val parser: Parser[CoursierOptions]            = Parser.derive\n  implicit lazy val help: Help[CoursierOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[CoursierOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/CrossOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class CrossOptions(\n  @Tag(tags.experimental)\n  @HelpMessage(\"Run given command against all provided Scala versions and/or platforms\")\n    cross: Option[Boolean] = None\n)\n// format: on\n\nobject CrossOptions {\n  implicit lazy val parser: Parser[CrossOptions] = Parser.derive\n  implicit lazy val help: Help[CrossOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/GlobalOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.launcher.PowerOptions\n\ncase class GlobalOptions(\n  @Recurse\n  logging: LoggingOptions = LoggingOptions(),\n  @Recurse\n  globalSuppress: GlobalSuppressWarningOptions = GlobalSuppressWarningOptions(),\n\n  /** Duplication of [[scala.cli.launcher.LauncherOptions.powerOptions]]. Thanks to this, our unit\n    * tests ensure that no subcommand defines an option that will clash with --power.\n    */\n  @Recurse\n  powerOptions: PowerOptions = PowerOptions()\n)\n\nobject GlobalOptions {\n  implicit lazy val parser: Parser[GlobalOptions] = Parser.derive\n  implicit lazy val help: Help[GlobalOptions]     = Help.derive\n\n  lazy val default: GlobalOptions = GlobalOptions()\n\n  def get(args: List[String]): Option[GlobalOptions] =\n    parser\n      .detailedParse(args, stopAtFirstUnrecognized = false, ignoreUnrecognized = true)\n      .toOption\n      .map(_._1)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/GlobalSuppressWarningOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\nfinal case class GlobalSuppressWarningOptions(\n  @Group(HelpGroup.SuppressWarnings.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Suppress warnings about using experimental features\")\n  @Name(\"suppressExperimentalWarning\")\n  suppressExperimentalFeatureWarning: Option[Boolean] = None,\n  @Group(HelpGroup.SuppressWarnings.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Suppress warnings about using deprecated features\")\n  @Name(\"suppressDeprecatedWarning\")\n  @Name(\"suppressDeprecatedWarnings\")\n  @Name(\"suppressDeprecatedFeatureWarnings\")\n  suppressDeprecatedFeatureWarning: Option[Boolean] = None\n)\n\nobject GlobalSuppressWarningOptions {\n  implicit lazy val parser: Parser[GlobalSuppressWarningOptions] = Parser.derive\n  implicit lazy val help: Help[GlobalSuppressWarningOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HasGlobalOptions.scala",
    "content": "package scala.cli.commands.shared\n\ntrait HasGlobalOptions {\n  def global: GlobalOptions\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HasSharedOptions.scala",
    "content": "package scala.cli.commands.shared\n\ntrait HasSharedOptions extends HasGlobalOptions {\n  def shared: SharedOptions\n  override def global: GlobalOptions = shared.global\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HasSharedWatchOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport scala.build.errors.BuildException\n\ntrait HasSharedWatchOptions { this: HasSharedOptions =>\n  def watch: SharedWatchOptions\n\n  def buildOptions(ignoreErrors: Boolean =\n    false): Either[BuildException, scala.build.options.BuildOptions] =\n    shared.buildOptions(ignoreErrors = ignoreErrors, watchOptions = watch)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroupOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport caseapp.core.Scala3Helpers.*\nimport caseapp.core.help.{Help, HelpFormat}\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.tags\n\n@HelpMessage(\"Print help message\")\ncase class HelpGroupOptions(\n  @Group(HelpGroup.Help.toString)\n  @HelpMessage(\"Show environment variable help\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @Name(\"helpEnv\")\n  @Name(\"envHelp\")\n  @Name(\"envsHelp\")\n  helpEnvs: Boolean = false,\n  @Group(HelpGroup.Help.toString)\n  @HelpMessage(\"Show options for ScalaJS\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  helpJs: Boolean = false,\n  @Group(HelpGroup.Help.toString)\n  @HelpMessage(\"Show options for ScalaNative\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  helpNative: Boolean = false,\n  @Group(HelpGroup.Help.toString)\n  @HelpMessage(\"Show options for Scaladoc\")\n  @Name(\"helpDoc\")\n  @Name(\"scaladocHelp\")\n  @Name(\"docHelp\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  helpScaladoc: Boolean = false,\n  @Group(HelpGroup.Help.toString)\n  @HelpMessage(\"Show options for Scala REPL\")\n  @Name(\"replHelp\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  helpRepl: Boolean = false,\n  @Group(HelpGroup.Help.toString)\n  @HelpMessage(\"Show options for Scalafmt\")\n  @Name(\"helpFmt\")\n  @Name(\"scalafmtHelp\")\n  @Name(\"fmtHelp\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  helpScalafmt: Boolean = false\n) {\n\n  private def printHelpWithGroup(help: Help[?], helpFormat: HelpFormat, group: String): Nothing = {\n    val oldHiddenGroups = helpFormat.hiddenGroups.toSeq.flatten\n    val oldSortedGroups = helpFormat.sortedGroups.toSeq.flatten\n    val newHiddenGroups = (oldHiddenGroups ++ oldSortedGroups).filterNot(_ == group)\n    println(\n      help.help(\n        helpFormat.withHiddenGroupsWhenShowHidden(Some(newHiddenGroups)),\n        showHidden = true\n      )\n    )\n    sys.exit(0)\n  }\n\n  def maybePrintGroupHelp(help: Help[?], helpFormat: HelpFormat): Unit = {\n    if (helpJs) printHelpWithGroup(help, helpFormat, HelpGroup.ScalaJs.toString)\n    else if (helpNative) printHelpWithGroup(help, helpFormat, HelpGroup.ScalaNative.toString)\n  }\n}\n\nobject HelpGroupOptions {\n  implicit lazy val parser: Parser[HelpGroupOptions]            = Parser.derive\n  implicit lazy val help: Help[HelpGroupOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[HelpGroupOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HelpGroups.scala",
    "content": "package scala.cli.commands.shared\n\nenum HelpGroup:\n  case Benchmarking, BSP, BuildToolExport,\n    Config, Compilation, CompilationServer,\n    Debian, Debug, Default, Dependency, Doc, Docker,\n    Entrypoint,\n    Fix, Format,\n    Help,\n    Install,\n    Java,\n    Launcher, LegacyScalaRunner, Logging,\n    MacOS, Markdown,\n    NativeImage,\n    Package, PGP, ProjectVersion, Publishing,\n    RedHat, Repl, Run, Runner,\n    Scala, ScalaJs, ScalaNative, Secret, Signing, SuppressWarnings, SourceGenerator,\n    Test,\n    Uninstall, Update,\n    Watch, Windows,\n    Version\n\n  override def toString: String = this match\n    case BuildToolExport   => \"Build Tool export\"\n    case CompilationServer => \"Compilation server\"\n    case LegacyScalaRunner => \"Legacy Scala runner\"\n    case NativeImage       => \"Native image\"\n    case ScalaJs           => \"Scala.js\"\n    case ScalaNative       => \"Scala Native\"\n    case SuppressWarnings  => \"Suppress warnings\"\n    case SourceGenerator   => \"Source generator\"\n    case ProjectVersion    => \"Project version\"\n    case e                 => e.productPrefix\n\nenum HelpCommandGroup:\n  case Main, Miscellaneous, Undefined\n  override def toString: String = this match\n    case Undefined => \"\"\n    case e         => e.productPrefix\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HelpMessages.scala",
    "content": "package scala.cli.commands.shared\nimport scala.cli.ScalaCli\n\nobject HelpMessages {\n  lazy val PowerString: String = if ScalaCli.allowRestrictedFeatures then \"\" else \"--power \"\n  val passwordOption           = \"A github token used to access GitHub. Not needed in most cases.\"\n  private val docsWebsiteUrl   = \"https://scala-cli.virtuslab.org\"\n  def shortHelpMessage(\n    cmdName: String,\n    helpHeader: String,\n    includeFullHelpReference: Boolean = true,\n    needsPower: Boolean = false\n  ): String = {\n    val maybeFullHelpReference =\n      if includeFullHelpReference then\n        s\"\"\"\n           |${HelpMessages.commandFullHelpReference(cmdName, needsPower)}\"\"\".stripMargin\n      else \"\"\n    s\"\"\"$helpHeader\n       |$maybeFullHelpReference\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n  }\n\n  val docsWebsiteReference =\n    s\"Detailed documentation can be found on our website: $docsWebsiteUrl\"\n  def commandFullHelpReference(commandName: String, needsPower: Boolean = false): String = {\n    val maybePowerString = if needsPower then \"--power \" else \"\"\n    s\"\"\"You are currently viewing the basic help for the $commandName sub-command. You can view the full help by running: \n       |   ${Console.BOLD}${ScalaCli.progName} $maybePowerString$commandName --help-full${Console\n        .RESET}\"\"\".stripMargin\n  }\n\n  def commandDocWebsiteReference(websiteSuffix: String): String =\n    s\"For detailed documentation refer to our website: $docsWebsiteUrl/docs/commands/$websiteSuffix\"\n  val installationDocsWebsiteReference =\n    s\"For detailed installation instructions refer to our website: $docsWebsiteUrl/install\"\n  val acceptedInputs: String =\n    \"\"\"Multiple inputs can be passed at once.\n      |Paths to directories, URLs and supported file types are accepted as inputs.\n      |Accepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\n      |For piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\n      |All supported types of inputs can be mixed with each other.\"\"\".stripMargin\n  lazy val bloopInfo: String =\n    s\"\"\"Bloop is the build server used by ${ScalaCli.fullRunnerName}.\n       |For more information about Bloop, refer to https://scalacenter.github.io/bloop/\"\"\".stripMargin\n  def commandConfigurations(cmdName: String): String =\n    s\"\"\"Specific $cmdName configurations can be specified with both command line options and using directives defined in sources.\n       |Command line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\n       |Using directives can be defined in all supported input source file types.\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/HelpOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\n// format: off\n@HelpMessage(\"Print help message\")\ncase class HelpOptions(\n  @Recurse\n     global: GlobalOptions = GlobalOptions(),\n) extends HasGlobalOptions\n// format: on\n\nobject HelpOptions {\n  implicit lazy val parser: Parser[HelpOptions] = Parser.derive\n  implicit lazy val help: Help[HelpOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/JavaPropOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport caseapp.core.parser.{Argument, NilParser, StandardArgument}\nimport caseapp.core.util.Formatter\nimport caseapp.core.{Arg, Error}\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class JavaPropOptions(\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Set java properties\")\n  @ValueDescription(\"key=value|key\")\n  @Tag(tags.must)\n    javaProp: List[String] = Nil\n)\n// format: on\n\nobject JavaPropOptions {\n\n  private val javaPropOptionsArg = Arg(\"javaPropOption\").copy(\n    extraNames = Seq(Name(\"java-prop\")),\n    valueDescription = Some(ValueDescription(\"key=value|key\")),\n    helpMessage = Some(HelpMessage(\n      \"Add java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\"\n    )),\n    group = Some(Group(\"Java\")),\n    origin = Some(\"JavaPropOptions\")\n  )\n\n  private val javaPropOptionsArgument: Argument[List[String]] =\n    new Argument[List[String]] {\n\n      val underlying: StandardArgument[List[String]] = StandardArgument(javaPropOptionsArg)\n\n      val arg: Arg = javaPropOptionsArg\n\n      def withDefaultOrigin(origin: String): Argument[List[String]] = this\n      def init: Option[List[String]]                                = Some(Nil)\n      def step(\n        args: List[String],\n        index: Int,\n        acc: Option[List[String]],\n        formatter: Formatter[Name]\n      ): Either[(Error, List[String]), Option[(Option[List[String]], List[String])]] =\n        args match {\n          case s\"-D${prop}\" :: t =>\n            Right(Some((Some(prop :: acc.getOrElse(Nil)), t)))\n          case _ => underlying.step(args, index, acc, formatter)\n        }\n      def get(acc: Option[List[String]], formatter: Formatter[Name]): Either[Error, List[String]] =\n        Right(acc.getOrElse(Nil))\n    }\n\n  implicit lazy val parser: Parser[JavaPropOptions] = {\n    val baseParser =\n      javaPropOptionsArgument ::\n        NilParser\n    baseParser.to[JavaPropOptions]\n  }\n  implicit lazy val help: Help[JavaPropOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[JavaPropOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/LoggingOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.build.Logger\nimport scala.cli.commands.tags\nimport scala.cli.internal.CliLogger\n\n// format: off\nfinal case class LoggingOptions(\n  @Recurse\n    verbosityOptions: VerbosityOptions = VerbosityOptions(),\n  @Group(HelpGroup.Logging.toString)\n  @HelpMessage(\"Decrease logging verbosity\")\n  @Tag(tags.implementation)\n  @Name(\"q\")\n    quiet: Boolean = false,\n  @Group(HelpGroup.Logging.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Use progress bars\")\n    progress: Option[Boolean] = None\n) {\n  // format: on\n\n  lazy val verbosity = verbosityOptions.verbosity - (if (quiet) 1 else 0)\n\n  lazy val logger: Logger = new CliLogger(verbosity, quiet, progress, System.err)\n}\n\nobject LoggingOptions {\n  implicit lazy val parser: Parser[LoggingOptions]            = Parser.derive\n  implicit lazy val help: Help[LoggingOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[LoggingOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/MainClassOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.build.errors.{MainClassError, NoMainClassFoundError}\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class MainClassOptions(\n  @Group(HelpGroup.Entrypoint.toString)\n  @HelpMessage(\"Specify which main class to run\")\n  @ValueDescription(\"main-class\")\n  @Tag(tags.must)\n  @Name(\"M\")\n    mainClass: Option[String] = None,\n\n  @Group(HelpGroup.Entrypoint.toString)\n  @HelpMessage(\"List main classes available in the current context\")\n  @Name(\"mainClassList\")\n  @Name(\"listMainClass\")\n  @Name(\"listMainClasses\")\n  @Name(\"listMainMethods\")\n  @Name(\"listMainMethod\")\n  @Name(\"mainMethodList\")\n  @Name(\"mainMethodLs\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n    mainClassLs: Option[Boolean] = None\n) {\n  // format: on\n\n  def maybePrintMainClasses(\n    mainClasses: Seq[String],\n    shouldExit: Boolean = true\n  ): Either[MainClassError, Unit] =\n    mainClassLs match {\n      case Some(true) if mainClasses.nonEmpty =>\n        println(mainClasses.mkString(\" \"))\n        if (shouldExit) sys.exit(0)\n        else Right(())\n      case Some(true) => Left(new NoMainClassFoundError)\n      case _          => Right(())\n    }\n}\n\nobject MainClassOptions {\n  implicit lazy val parser: Parser[MainClassOptions] = Parser.derive\n  implicit lazy val help: Help[MainClassOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/MarkdownOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class MarkdownOptions(\n  @Group(HelpGroup.Markdown.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Enable markdown support.\")\n  @Name(\"md\")\n  @Name(\"markdown\")\n    enableMarkdown: Boolean = false // TODO: add a separate scope for Markdown and remove this option once it's stable\n)\n// format: on\n\nobject MarkdownOptions {\n  implicit lazy val parser: Parser[MarkdownOptions] = Parser.derive\n  implicit lazy val help: Help[MarkdownOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ScalaCliHelp.scala",
    "content": "package scala.cli.commands.shared\nimport caseapp.core.help.HelpFormat\n\nimport scala.cli.util.ArgHelpers.*\nimport scala.util.{Properties, Try}\n\nobject ScalaCliHelp {\n  private val sortedHelpGroups = Seq(\n    HelpGroup.Scala,\n    HelpGroup.Java,\n    HelpGroup.Watch,\n    HelpGroup.Dependency,\n    HelpGroup.Entrypoint,\n    HelpGroup.Debug,\n    HelpGroup.Repl,\n    HelpGroup.Run,\n    HelpGroup.Package,\n    HelpGroup.CompilationServer,\n    HelpGroup.Logging,\n    HelpGroup.Runner,\n    HelpGroup.Launcher,\n    HelpGroup.LegacyScalaRunner,\n    HelpGroup.ScalaJs,\n    HelpGroup.ScalaNative,\n    HelpGroup.Help\n  )\n  private val hiddenHelpGroups    = Seq(HelpGroup.ScalaJs, HelpGroup.ScalaNative)\n  private val sortedCommandGroups =\n    Seq(HelpCommandGroup.Main, HelpCommandGroup.Miscellaneous, HelpCommandGroup.Undefined)\n  val helpFormat: HelpFormat = HelpFormat.default()\n    .copy(\n      filterArgs = Some(arg => arg.isSupported && (arg.isMust || arg.isImportant)),\n      filterArgsWhenShowHidden = Some(_.isSupported),\n      terminalWidthOpt =\n        if (Properties.isWin)\n          if (coursier.paths.Util.useJni())\n            Try(coursier.jniutils.WindowsAnsiTerminal.terminalSize()).toOption.map(\n              _.getWidth\n            ).orElse {\n              val fallback = 120\n              if (java.lang.Boolean.getBoolean(\"scala.cli.windows-terminal.verbose\"))\n                System.err.println(s\"Could not get terminal width, falling back to $fallback\")\n              Some(fallback)\n            }\n          else None\n        else\n          // That's how Ammonite gets the terminal width, but I'd rather not spawn a sub-process upfront in Scala CLI…\n          //   val pathedTput = if (os.isFile(os.Path(\"/usr/bin/tput\"))) \"/usr/bin/tput\" else \"tput\"\n          //   val width = os.proc(\"sh\", \"-c\", s\"$pathedTput cols 2>/dev/tty\").call(stderr = os.Pipe).out.trim().toInt\n          //   Some(width)\n          // Ideally, we should do an ioctl, like jansi does here:\n          //   https://github.com/fusesource/jansi/blob/09722b7cccc8a99f14ac1656db3072dbeef34478/src/main/java/org/fusesource/jansi/AnsiConsole.java#L344\n          // This requires writing our own minimal JNI library, that publishes '.a' files too for static linking in the executable of Scala CLI.\n          None\n    )\n    .withSortedCommandGroups(sortedCommandGroups)\n    .withSortedGroups(sortedHelpGroups)\n    .withHiddenGroups(hiddenHelpGroups)\n    .withNamesLimit(2)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ScalaJsOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.{Constants, tags}\n\n// format: off\nfinal case class ScalaJsOptions(\n\n  @Group(HelpGroup.Scala.toString)\n  @Tag(tags.should)\n  @HelpMessage(\"Enable Scala.js. To show more options for Scala.js pass `--help-js`\")\n    js: Boolean = false,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n  @HelpMessage(s\"The Scala.js version (${Constants.scalaJsVersion} by default).\")\n    jsVersion: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n  @HelpMessage(\"The Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\")\n    jsMode: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"Disable optimalisation for Scala.js, overrides `--js-mode`\")\n  @Tag(tags.implementation)\n  @Hidden\n    jsNoOpt: Option[Boolean] = None,\n\n  @HelpMessage(\"The Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\")\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n    jsModuleKind: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n    jsCheckIr: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"Emit source maps\")\n  @Tag(tags.should)\n    jsEmitSourceMaps: Boolean = false,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"Set the destination path of source maps\")\n  @Tag(tags.should)\n   jsSourceMapsPath: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"A file relative to the root directory containing import maps for ES module imports\")\n  @Tag(tags.should)\n    jsEsModuleImportMap: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n  @HelpMessage(\"Enable jsdom\")\n    jsDom: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.experimental)\n  @HelpMessage(\"Emit WASM\")\n    jsEmitWasm: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n  @HelpMessage(\"A header that will be added at the top of generated .js files\")\n    jsHeader: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Primitive Longs *may* be compiled as primitive JavaScript bigints\")\n    jsAllowBigIntsForLongs: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Avoid class'es when using functions and prototypes has the same observable semantics.\")\n    jsAvoidClasses: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Avoid lets and consts when using vars has the same observable semantics.\")\n    jsAvoidLetsAndConsts: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"The Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\")\n    jsModuleSplitStyle: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Create as many small modules as possible for the classes in the passed packages and their subpackages.\")\n    jsSmallModuleForPackage: List[String] = Nil,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @Tag(tags.should)\n  @HelpMessage(\"The Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\")\n    jsEsVersion: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"Path to the Scala.js linker\")\n  @ValueDescription(\"path\")\n  @Tag(tags.implementation)\n  @Hidden\n    jsLinkerPath: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(s\"Scala.js CLI version to use for linking (${Constants.scalaJsCliVersion} by default).\")\n  @ValueDescription(\"version\")\n  @Tag(tags.implementation)\n  @Hidden\n    jsCliVersion: Option[String] = None,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"Scala.js CLI Java options\")\n  @Tag(tags.implementation)\n  @ValueDescription(\"option\")\n  @Hidden\n    jsCliJavaArg: List[String] = Nil,\n\n  @Group(HelpGroup.ScalaJs.toString)\n  @HelpMessage(\"Whether to run the Scala.js CLI on the JVM or using a native executable\")\n  @Tag(tags.implementation)\n  @Hidden\n    jsCliOnJvm: Option[Boolean] = None\n)\n// format: on\n\nobject ScalaJsOptions {\n  implicit lazy val parser: Parser[ScalaJsOptions]            = Parser.derive\n  implicit lazy val help: Help[ScalaJsOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[ScalaJsOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ScalaNativeOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.{Constants, tags}\n\n// format: off\nfinal case class ScalaNativeOptions(\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Enable Scala Native. To show more options for Scala Native pass `--help-native`\")\n  @Tag(tags.should)\n    native: Boolean = false,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @Tag(tags.should)\n  @HelpMessage(s\"Set the Scala Native version (${Constants.scalaNativeVersion} by default).\")\n    nativeVersion: Option[String] = None,\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Set Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\")\n  @Tag(tags.should)\n    nativeMode: Option[String] = None,\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Link-time optimisation mode (none by default): none, full, thin\")\n  @Tag(tags.should)\n    nativeLto: Option[String] = None,\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Set the Scala Native garbage collector (immix by default): immix, commix, boehm, none\")\n  @Tag(tags.should)\n    nativeGc: Option[String] = None,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Path to the Clang command\")\n  @Tag(tags.implementation)\n    nativeClang: Option[String] = None,\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Path to the Clang++ command\")\n   @Tag(tags.implementation)\n    nativeClangpp: Option[String] = None,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Extra options passed to `clang` verbatim during linking\")\n   @Tag(tags.should)\n    nativeLinking: List[String] = Nil,\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Use default linking settings\")\n  @Hidden\n  @Tag(tags.implementation)\n    nativeLinkingDefaults: Option[Boolean] = None, //TODO does it even work when we default it to true while handling?\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"List of compile options\")\n   @Tag(tags.should)\n    nativeCompile: List[String] = Nil,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"List of compile options (C files only)\")\n  @Tag(tags.should)\n    nativeCCompile: List[String] = Nil,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"List of compile options (C++ files only)\")\n  @Tag(tags.should)\n    nativeCppCompile: List[String] = Nil,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @Hidden\n  @HelpMessage(\"Use default compile options\")\n   @Tag(tags.implementation)\n    nativeCompileDefaults: Option[Boolean] = None, //TODO does it even work when we default it to true while handling?\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Build target type\")\n  @Tag(tags.should)\n  @ValueDescription(\"app|static|dynamic\")\n    nativeTarget: Option[String] = None,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Embed resources into the Scala Native binary (can be read with the Java resources API)\")\n   @Tag(tags.should)\n    embedResources: Option[Boolean] = None,\n\n  @Group(HelpGroup.ScalaNative.toString)\n  @HelpMessage(\"Enable/disable Scala Native multithreading support\")\n  @Tag(tags.should)\n    nativeMultithreading: Option[Boolean] = None\n\n)\n// format: on\n\nobject ScalaNativeOptions {\n  implicit lazy val parser: Parser[ScalaNativeOptions]            = Parser.derive\n  implicit lazy val help: Help[ScalaNativeOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[ScalaNativeOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ScalacExtraOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.tags\n\n/** Scala CLI options which aren't strictly scalac options, but directly involve the Scala compiler\n  * in some way.\n  */\n// format: off\nfinal case class ScalacExtraOptions(\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Show help for scalac. This is an alias for --scalac-option -help\")\n  @Name(\"helpScalac\")\n  @Tag(tags.inShortHelp)\n    scalacHelp: Boolean = false,\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Turn verbosity on for scalac. This is an alias for --scalac-option -verbose\")\n  @Name(\"verboseScalac\")\n  @Tag(tags.inShortHelp)\n    scalacVerbose: Boolean = false,\n)\n// format: on\n\nobject ScalacExtraOptions {\n  implicit lazy val parser: Parser[ScalacExtraOptions]            = Parser.derive\n  implicit lazy val help: Help[ScalacExtraOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[ScalacExtraOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ScalacOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport caseapp.core.parser.{Argument, NilParser, StandardArgument}\nimport caseapp.core.util.Formatter\nimport caseapp.core.{Arg, Error}\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.build.options.ScalacOpt.noDashPrefixes\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class ScalacOptions(\n  @Recurse\n  argsFiles: List[ArgFileOption] = Nil,\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Add a scalac option\")\n  @ValueDescription(\"option\")\n  @Name(\"O\")\n  @Name(\"scala-opt\")\n  @Name(\"scala-option\")\n  @Tag(tags.must)\n    scalacOption: List[String] = Nil,\n)\n// format: on\n\nobject ScalacOptions {\n  extension (opt: String) {\n    private def hasValidScalacOptionDashes: Boolean =\n      opt.startsWith(\"-\") && opt.length > 1 && (\n        if opt.length > 2 then opt.charAt(2) != '-'\n        else opt.charAt(1) != '-'\n      )\n  }\n\n  private val scalacOptionsArg = Arg(\"scalacOption\").copy(\n    extraNames = Seq(Name(\"scala-opt\"), Name(\"O\"), Name(\"scala-option\")),\n    valueDescription = Some(ValueDescription(\"option\")),\n    helpMessage = Some(HelpMessage(\n      \"Add a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\"\n    )),\n    group = Some(Group(\"Scala\")),\n    origin = Some(\"ScalacOptions\")\n  )\n  // .withIsFlag(true) // The scalac options we handle accept no value after the -… argument\n  val YScriptRunnerOption               = \"Yscriptrunner\"\n  private val scalacOptionsPurePrefixes = Set(\"V\", \"W\", \"X\", \"Y\")\n  private val scalacOptionsPrefixes     = Set(\"P\") ++ scalacOptionsPurePrefixes\n  val replExecuteScriptOptions @ Seq(replInitScript, replQuitAfterInit) =\n    Seq(\"repl-init-script\", \"repl-quit-after-init\")\n  private val replAliasedOptions      = Set(replInitScript)\n  private val replNoArgAliasedOptions = Set(replQuitAfterInit)\n  private val scalacAliasedOptions = // these options don't require being passed after -O and accept an arg\n    Set(\n      \"bootclasspath\",\n      \"boot-class-path\",\n      \"coverage-exclude-classlikes\",\n      \"coverage-exclude-files\",\n      \"encoding\",\n      \"extdirs\",\n      \"extension-directories\",\n      \"javabootclasspath\",\n      \"java-boot-class-path\",\n      \"javaextdirs\",\n      \"java-extension-directories\",\n      \"java-output-version\",\n      \"release\",\n      \"color\",\n      \"g\",\n      \"language\",\n      \"opt\",\n      \"opt-inline\",\n      \"pagewidth\",\n      \"page-width\",\n      \"target\",\n      \"scalajs-mapSourceURI\",\n      \"scalajs-genStaticForwardersForNonTopLevelObjects\",\n      \"source\",\n      \"sourcepath\",\n      \"source-path\",\n      \"sourceroot\",\n      YScriptRunnerOption\n    ) ++ replAliasedOptions\n  private val scalacNoArgAliasedOptions = // these options don't require being passed after -O and don't accept an arg\n    Set(\n      \"experimental\",\n      \"explain\",\n      \"explaintypes\",\n      \"explain-types\",\n      \"explain-cyclic\",\n      \"from-tasty\",\n      \"unchecked\",\n      \"nowarn\",\n      \"no-warnings\",\n      \"feature\",\n      \"deprecation\",\n      \"rewrite\",\n      \"scalajs\",\n      \"old-syntax\",\n      \"print-tasty\",\n      \"print-lines\",\n      \"new-syntax\",\n      \"indent\",\n      \"no-indent\",\n      \"preview\",\n      \"uniqid\",\n      \"unique-id\"\n    ) ++ replNoArgAliasedOptions\n\n  /** True when the token ends with a `:help` suffix, with any number of colon-separated segments\n    * before it (e.g. `Xlint:help`, `opt:a:b:c:help`). Used together with `ScalacPrintOptions` so\n    * new compiler `…:help` flags work without listing each one.\n    */\n  def isColonHelpPrintOption(noDashPrefixes: String): Boolean =\n    noDashPrefixes.endsWith(\":help\")\n\n  /** `scalac` options that print help or context and exit without requiring source inputs. */\n  val ScalacPrintOptions: Set[String] =\n    scalacOptionsPurePrefixes ++ Set(\n      \"help\",\n      \"Xshow-phases\",\n      \"Xplugin-list\",\n      \"Vphases\"\n    )\n\n  /** Whether `ScalaCommand.maybePrintSimpleScalacOutput` should run for this token (after\n    * `ScalacOpt.noDashPrefixes`).\n    */\n  def isScalacPrintOption(noDashPrefixes: String): Boolean =\n    ScalacPrintOptions.contains(noDashPrefixes) ||\n    isColonHelpPrintOption(noDashPrefixes)\n\n  /** This includes all the scalac options which are redirected to native Scala CLI options. */\n  val ScalaCliRedirectedOptions: Set[String] = Set(\n    \"classpath\",\n    \"cp\",         // redirected to --extra-jars\n    \"class-path\", // redirected to --extra-jars\n    \"d\"           // redirected to --compilation-output\n  )\n  val ScalacDeprecatedOptions: Set[String] = Set(\n    YScriptRunnerOption // old 'scala' runner specific, no longer supported\n  )\n\n  private val scalacOptionsArgument: Argument[List[String]] =\n    new Argument[List[String]] {\n\n      val underlying: StandardArgument[List[String]] = StandardArgument(scalacOptionsArg)\n\n      val arg: Arg = scalacOptionsArg\n\n      def withDefaultOrigin(origin: String): Argument[List[String]] = this\n      def init: Option[List[String]]                                = Some(Nil)\n      def step(\n        args: List[String],\n        index: Int,\n        acc: Option[List[String]],\n        formatter: Formatter[Name]\n      ): Either[(Error, List[String]), Option[(Option[List[String]], List[String])]] =\n        args match {\n          case h :: t\n              if h.hasValidScalacOptionDashes &&\n              scalacOptionsPrefixes.exists(h.noDashPrefixes.startsWith) &&\n              !ScalacDeprecatedOptions.contains(h.noDashPrefixes) =>\n            Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))\n          case h :: t\n              if h.hasValidScalacOptionDashes &&\n              scalacNoArgAliasedOptions.contains(h.noDashPrefixes) =>\n            Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))\n          case h :: t\n              if h.hasValidScalacOptionDashes &&\n              scalacAliasedOptions.exists(o => h.noDashPrefixes.startsWith(o + \":\")) &&\n              h.count(_ == ':') == 1 => Right(Some((Some(acc.getOrElse(Nil) :+ h), t)))\n          case h :: t\n              if h.hasValidScalacOptionDashes && scalacAliasedOptions.contains(h.noDashPrefixes) =>\n            // check if the next scalac arg is a different option or a param to the current option\n            val maybeOptionArg = t.headOption.filter(!_.startsWith(\"-\"))\n            // if it's a param, it'll be treated as such and considered already parsed\n            val newTail = maybeOptionArg.map(_ => t.drop(1)).getOrElse(t)\n            val newHead = List(h) ++ maybeOptionArg\n            Right(Some((Some(acc.getOrElse(Nil) ++ newHead), newTail)))\n          case _ => underlying.step(args, index, acc, formatter)\n        }\n      def get(acc: Option[List[String]], formatter: Formatter[Name]): Either[Error, List[String]] =\n        Right(acc.getOrElse(Nil))\n    }\n\n  implicit lazy val parser: Parser[ScalacOptions] = {\n    val baseParser                              = scalacOptionsArgument :: NilParser\n    implicit val p: Parser[List[ArgFileOption]] = ArgFileOption.parser\n    baseParser.addAll[List[ArgFileOption]].to[ScalacOptions]\n  }\n\n  implicit lazy val help: Help[ScalacOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[ScalacOptions] = JsonCodecMaker.make\n}\n\ncase class ArgFileOption(file: String) extends AnyVal\n\nobject ArgFileOption {\n  val arg: Arg = Arg(\n    name = Name(\"args-file\"),\n    valueDescription = Some(ValueDescription(\"@arguments-file\")),\n    helpMessage = Some(HelpMessage(\"File with scalac options.\")),\n    group = Some(Group(\"Scala\")),\n    origin = Some(\"ScalacOptions\")\n  )\n  implicit lazy val parser: Parser[List[ArgFileOption]] = new Parser[List[ArgFileOption]] {\n    type D = List[ArgFileOption] *: EmptyTuple\n\n    override def withDefaultOrigin(origin: String): Parser[List[ArgFileOption]] = this\n\n    override def init: D = Nil *: EmptyTuple\n\n    override def step(args: List[String], index: Int, d: D, nameFormatter: Formatter[Name])\n      : Either[(core.Error, Arg, List[String]), Option[(D, Arg, List[String])]] =\n      args match\n        case head :: rest if head.startsWith(\"@\") =>\n          val newD = (ArgFileOption(head.stripPrefix(\"@\")) :: d._1) *: EmptyTuple\n          Right(Some(newD, arg, rest))\n        case _ => Right(None)\n\n    override def get(\n      d: D,\n      nameFormatter: Formatter[Name]\n    ): Either[core.Error, List[ArgFileOption]] = Right(d.head)\n\n    override def args: Seq[Arg] = Seq(arg)\n\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/ScopeOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\ncase class ScopeOptions(\n  @Group(HelpGroup.Compilation.toString)\n  @HelpMessage(\"Include test scope\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @Name(\"testScope\")\n  @Name(\"withTestScope\")\n  @Name(\"withTest\")\n  test: Option[Boolean] = None\n)\nobject ScopeOptions {\n  implicit lazy val parser: Parser[ScopeOptions] = Parser.derive\n  implicit lazy val help: Help[ScopeOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SemanticDbOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.tags\n\ncase class SemanticDbOptions(\n  @Hidden\n  @Tag(tags.should)\n  @HelpMessage(\"Generate SemanticDBs\")\n  @Name(\"semanticdb\")\n  semanticDb: Option[Boolean] = None,\n  @Hidden\n  @Tag(tags.should)\n  @HelpMessage(\"SemanticDB target root (default to the compiled classes destination directory)\")\n  @Name(\"semanticdbTargetRoot\")\n  @Name(\"semanticdbTargetroot\")\n  semanticDbTargetRoot: Option[String] = None,\n  @Hidden\n  @Tag(tags.should)\n  @HelpMessage(\"SemanticDB source root (default to the project root directory)\")\n  @Name(\"semanticdbSourceRoot\")\n  @Name(\"semanticdbSourceroot\")\n  semanticDbSourceRoot: Option[String] = None\n)\n\nobject SemanticDbOptions {\n  implicit lazy val parser: Parser[SemanticDbOptions]            = Parser.derive\n  implicit lazy val help: Help[SemanticDbOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[SemanticDbOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedBspFileOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedBspFileOptions(\n  @Group(HelpGroup.BSP.toString)\n  @Name(\"bspDir\")\n  @HelpMessage(\"Custom BSP configuration location\")\n  @Tag(tags.implementation)\n  @Hidden\n    bspDirectory: Option[String] = None,\n  @Group(HelpGroup.BSP.toString)\n  @Name(\"name\")\n  @HelpMessage(\"Name of BSP\")\n  @Hidden\n  @Tag(tags.implementation)\n    bspName: Option[String] = None\n)\n// format: on\n\nobject SharedBspFileOptions {\n  implicit lazy val parser: Parser[SharedBspFileOptions] = Parser.derive\n  implicit lazy val help: Help[SharedBspFileOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedCompilationServerOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport bloop.rifle.internal.BuildInfo\nimport bloop.rifle.{BloopRifleConfig, BloopVersion, BspConnectionAddress}\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport coursier.cache.FileCache\nimport coursier.core.Version as Ver\nimport coursier.util.Task\n\nimport java.io.File\nimport java.nio.file.{AtomicMoveNotSupportedException, FileAlreadyExistsException, Files, Paths}\nimport java.util.Random\n\nimport scala.build.internal.Util\nimport scala.build.{Bloop, Logger, Os}\nimport scala.cli.commands.Constants\nimport scala.cli.commands.bloop.BloopJson\nimport scala.cli.internal.Pid\nimport scala.concurrent.duration.{Duration, FiniteDuration}\nimport scala.util.Properties\n\n// format: off\nfinal case class SharedCompilationServerOptions(\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Protocol to use to open a BSP connection with Bloop\")\n  @ValueDescription(\"tcp|local|default\")\n  @Hidden\n    bloopBspProtocol: Option[String] = None,\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Socket file to use to open a BSP connection with Bloop\")\n  @ValueDescription(\"path\")\n  @Hidden\n    bloopBspSocket: Option[String] = None,\n\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Host the compilation server should bind to\")\n  @ValueDescription(\"host\")\n  @Hidden\n    bloopHost: Option[String] = None,\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Port the compilation server should bind to (pass `-1` to pick a random port)\")\n  @ValueDescription(\"port|-1\")\n  @Hidden\n    bloopPort: Option[Int] = None,\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Daemon directory of the Bloop daemon (directory with lock, pid, and socket files)\")\n  @ValueDescription(\"path\")\n  @Hidden\n    bloopDaemonDir: Option[String] = None,\n\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"If Bloop isn't already running, the version we should start\")\n  @ValueDescription(\"version\")\n  @Hidden\n    bloopVersion: Option[String] = None,\n\n  @Hidden\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Maximum duration to wait for the BSP connection to be opened\")\n  @ValueDescription(\"duration\")\n    bloopBspTimeout: Option[String] = None,\n  @Hidden\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Duration between checks of the BSP connection state\")\n  @ValueDescription(\"duration\")\n    bloopBspCheckPeriod: Option[String] = None,\n  @Hidden\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Maximum duration to wait for the compilation server to start up\")\n  @ValueDescription(\"duration\")\n    bloopStartupTimeout: Option[String] = None,\n\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Include default JVM options for Bloop\")\n  @Hidden\n    bloopDefaultJavaOpts: Boolean = true,\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Pass java options to use by Bloop server\")\n  @Hidden\n    bloopJavaOpt: List[String] = Nil,\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Bloop global options file\")\n  @Hidden\n    bloopGlobalOptionsFile: Option[String] = None,\n\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(s\"JVM to use to start Bloop (e.g. 'system|${Constants.minimumBloopJavaVersion}', 'temurin:21', …)\")\n  @Hidden\n    bloopJvm: Option[String] = None,\n\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Working directory for Bloop, if it needs to be started\")\n  @Hidden\n    bloopWorkingDir: Option[String] = None,\n\n  @Group(HelpGroup.CompilationServer.toString)\n  @HelpMessage(\"Enable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\")\n    server: Option[Boolean] = None\n) {\n  // format: on\n\n  private def pidOrRandom: Either[Int, Int] =\n    Option((new Pid).get()).map(_.intValue()).map(Right(_)).getOrElse {\n      val r = new Random\n      Left(r.nextInt())\n    }\n\n  private def socketDirectory(directories: scala.build.Directories): os.Path = {\n    val dir = directories.bspSocketDir\n    // Ensuring that whenever dir exists, it has the right permissions\n    if (!os.isDir(dir)) {\n      val tmpDir = dir / os.up / s\".${dir.last}.tmp-${pidOrRandom.merge}\"\n      try {\n        os.makeDir.all(tmpDir)\n        if (!Properties.isWin)\n          os.perms.set(tmpDir, \"rwx------\")\n        try os.move(tmpDir, dir, atomicMove = true)\n        catch {\n          case _: AtomicMoveNotSupportedException =>\n            try os.move(tmpDir, dir)\n            catch {\n              case _: FileAlreadyExistsException =>\n            }\n          case _: FileAlreadyExistsException =>\n        }\n      }\n      finally if (os.exists(tmpDir)) os.remove(tmpDir)\n    }\n    dir\n  }\n\n  private def bspSocketFile(directories: => scala.build.Directories): File = {\n    val (socket, deleteOnExit) = bloopBspSocket match {\n      case Some(path) =>\n        (os.Path(path, Os.pwd), false)\n      case None =>\n        val dir      = socketDirectory(directories)\n        val fileName = pidOrRandom\n          .map(\"proc-\" + _)\n          .left.map(\"conn-\" + _)\n          .merge\n        val path = dir / fileName\n        if (os.exists(path)) // isFile is false for domain sockets\n          os.remove(path)\n        (path, true)\n    }\n    if (deleteOnExit)\n      Runtime.getRuntime.addShutdownHook(\n        new Thread(\"delete-bloop-bsp-named-socket\") {\n          override def run() =\n            Files.deleteIfExists(socket.toNIO)\n        }\n      )\n    socket.toIO.getCanonicalFile\n  }\n\n  def defaultBspSocketOrPort(\n    directories: => scala.build.Directories\n  ): Option[() => BspConnectionAddress] = {\n    def namedSocket =\n      Some(() => BspConnectionAddress.UnixDomainSocket(bspSocketFile(directories)))\n\n    def default = namedSocket\n    bloopBspProtocol.filter(_ != \"default\") match {\n      case None          => default\n      case Some(\"tcp\")   => None\n      case Some(\"local\") => namedSocket\n      case Some(other)   =>\n        sys.error(\n          s\"Invalid bloop BSP protocol value: '$other' (expected 'tcp', 'local', or 'default')\"\n        )\n    }\n  }\n\n  private def parseDuration(name: String, valueOpt: Option[String]): Option[FiniteDuration] =\n    valueOpt.map(_.trim).filter(_.nonEmpty).map(Duration(_)).map {\n      case d: FiniteDuration => d\n      case d                 => sys.error(s\"Expected finite $name duration, got $d\")\n    }\n\n  def bloopBspTimeoutDuration: Option[FiniteDuration] =\n    parseDuration(\"BSP connection timeout\", bloopBspTimeout)\n  def bloopBspCheckPeriodDuration: Option[FiniteDuration] =\n    parseDuration(\"BSP connection check period\", bloopBspCheckPeriod)\n  def bloopStartupTimeoutDuration: Option[FiniteDuration] =\n    parseDuration(\"connection server startup timeout\", bloopStartupTimeout)\n\n  def retainedBloopVersion: BloopRifleConfig.BloopVersionConstraint =\n    bloopVersion\n      .map(_.trim)\n      .filter(_.nonEmpty)\n      .fold[BloopRifleConfig.BloopVersionConstraint](BloopRifleConfig.AtLeast(\n        BloopVersion(BuildInfo.version)\n      ))(v => BloopRifleConfig.Strict(BloopVersion(v)))\n\n  def bloopDefaultJvmOptions(logger: Logger): Option[List[String]] = {\n    val filePathOpt = bloopGlobalOptionsFile.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd))\n    for (filePath <- filePathOpt)\n      yield\n        if (os.exists(filePath) && os.isFile(filePath))\n          try {\n            val content   = os.read.bytes(filePath)\n            val bloopJson = readFromArray(content)(using BloopJson.codec)\n            bloopJson.javaOptions\n          }\n          catch {\n            case e: Throwable =>\n              logger.message(s\"Error parsing global bloop config in '$filePath':\")\n              Util.printException(e)\n              List.empty\n          }\n        else {\n          logger.message(s\"Bloop global options file '$filePath' not found.\")\n          List.empty\n        }\n  }\n\n  def bloopRifleConfig(\n    logger: Logger,\n    cache: FileCache[Task],\n    verbosity: Int,\n    javaPath: String,\n    directories: => scala.build.Directories,\n    javaV: Option[Int] = None\n  ): BloopRifleConfig = {\n\n    val portOpt = bloopPort.filter(_ != 0) match {\n      case Some(n) if n < 0 =>\n        Some(_root_.bloop.rifle.internal.Util.randomPort())\n      case other => other\n    }\n    val address =\n      (\n        bloopHost.filter(_.nonEmpty),\n        portOpt,\n        bloopDaemonDir.filter(_.nonEmpty)\n      ) match {\n        case (_, _, Some(path)) =>\n          BloopRifleConfig.Address.DomainSocket(Paths.get(path))\n        case (None, None, None) =>\n          val isBloopMainLine = Ver(retainedBloopVersion.version.raw) < Ver(\"1.4.12\")\n          if (isBloopMainLine)\n            BloopRifleConfig.Address.Tcp(\n              host = BloopRifleConfig.defaultHost,\n              port = BloopRifleConfig.defaultPort\n            )\n          else\n            BloopRifleConfig.Address.DomainSocket(directories.bloopDaemonDir.toNIO)\n        case (hostOpt, portOpt0, _) =>\n          BloopRifleConfig.Address.Tcp(\n            host = hostOpt.getOrElse(BloopRifleConfig.defaultHost),\n            port = portOpt0.getOrElse(BloopRifleConfig.defaultPort)\n          )\n      }\n\n    val workingDir = bloopWorkingDir\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, Os.pwd))\n      .getOrElse(directories.bloopWorkingDir)\n    val baseConfig = BloopRifleConfig.default(\n      address,\n      v => Bloop.bloopClassPath(logger, cache, v),\n      workingDir.toIO\n    )\n\n    baseConfig.copy(\n      javaPath = javaPath,\n      bspSocketOrPort = defaultBspSocketOrPort(directories),\n      bspStdout = if (verbosity >= 3) Some(System.err) else None,\n      bspStderr = Some(System.err),\n      period = bloopBspCheckPeriodDuration.getOrElse(baseConfig.period),\n      timeout = bloopBspTimeoutDuration.getOrElse(baseConfig.timeout),\n      initTimeout = bloopStartupTimeoutDuration.getOrElse(baseConfig.initTimeout),\n      javaOpts =\n        (if (bloopDefaultJavaOpts) baseConfig.javaOpts\n         else Nil) ++ bloopJavaOpt ++ bloopDefaultJvmOptions(logger).getOrElse(Nil),\n      minimumBloopJvm = javaV.getOrElse(Constants.minimumBloopJavaVersion),\n      retainedBloopVersion = retainedBloopVersion\n    )\n  }\n}\n\nobject SharedCompilationServerOptions {\n  implicit lazy val parser: Parser[SharedCompilationServerOptions]            = Parser.derive\n  implicit lazy val help: Help[SharedCompilationServerOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[SharedCompilationServerOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedDebugOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedDebugOptions(\n  @Group(HelpGroup.Debug.toString)\n  @HelpMessage(\"Turn debugging on\")\n  @Tag(tags.should)\n    debug: Boolean = false,\n  @Group(HelpGroup.Debug.toString)\n  @HelpMessage(\"Debug port (5005 by default)\")\n  @Tag(tags.should)\n    debugPort: Option[String] = None,\n  @Group(HelpGroup.Debug.toString)\n  @Tag(tags.should)\n  @HelpMessage(\"Debug mode (attach by default)\")\n  @ValueDescription(\"attach|a|listen|l\")\n    debugMode: Option[String] = None\n)\n// format: on\n\nobject SharedDebugOptions {\n  implicit lazy val parser: Parser[SharedDebugOptions] = Parser.derive\n  implicit lazy val help: Help[SharedDebugOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedDependencyOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.build.preprocessing.directives.Repository\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedDependencyOptions(\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Add dependencies\")\n  @Tag(tags.must)\n  @Name(\"dep\")\n    dependency: List[String] = Nil,\n\n  @Group(HelpGroup.Dependency.toString)\n  @HelpMessage(\"Add compile-only dependencies\")\n  @Tag(tags.must)\n  @Name(\"compileDep\")\n  @Name(\"compileLib\")\n    compileOnlyDependency: List[String] = Nil,\n\n  @Group(HelpGroup.Dependency.toString)\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(Repository.usageMsg)\n  @Name(\"r\")\n  @Name(\"repo\")\n    repository: List[String] = Nil,\n  @Group(HelpGroup.Scala.toString)\n  @Name(\"P\")\n  @Name(\"plugin\")\n  @Tag(tags.must)\n  @HelpMessage(\"Add compiler plugin dependencies\")\n  compilerPlugin: List[String] = Nil\n)\n// format: on\n\nobject SharedDependencyOptions {\n  implicit lazy val parser: Parser[SharedDependencyOptions]            = Parser.derive\n  implicit lazy val help: Help[SharedDependencyOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[SharedDependencyOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedInputOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedInputOptions(\n  @Hidden\n  @Tag(tags.implementation)\n    defaultForbiddenDirectories: Boolean = true,\n  @Hidden\n  @Tag(tags.implementation)\n    forbid: List[String] = Nil\n)\n// format: on\n\nobject SharedInputOptions {\n  implicit lazy val parser: Parser[SharedInputOptions] = Parser.derive\n  implicit lazy val help: Help[SharedInputOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedJavaOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedJavaOptions(\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Set Java options, such as `-Xmx1g`\")\n  @ValueDescription(\"java-options\")\n  @Tag(tags.must)\n  @Name(\"J\")\n    javaOpt: List[String] = Nil,\n  @Recurse\n    javaProperties: JavaPropOptions = JavaPropOptions(),\n) {\n  // format: on\n  def allJavaOpts: Seq[String] =\n    javaOpt ++ javaProperties.javaProp.filter(_.nonEmpty).map(_.split(\"=\", 2)).map {\n      case Array(k)    => s\"-D$k\"\n      case Array(k, v) => s\"-D$k=$v\"\n    }\n}\n\nobject SharedJavaOptions {\n  implicit lazy val parser: Parser[SharedJavaOptions] = Parser.derive\n  implicit lazy val help: Help[SharedJavaOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedJvmOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedJvmOptions(\n  @Recurse\n    sharedDebug: SharedDebugOptions = SharedDebugOptions(),\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Set the Java home directory\")\n  @Tag(tags.should)\n  @ValueDescription(\"path\")\n    javaHome: Option[String] = None,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Use a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. \" +\n  \"scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\")\n  @ValueDescription(\"jvm-name\")\n  @Tag(tags.should)\n  @Name(\"j\")\n  @Tag(tags.inShortHelp)\n    jvm: Option[String] = None,\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"JVM index URL\")\n  @ValueDescription(\"url\")\n  @Tag(tags.implementation)\n  @Hidden\n    jvmIndex: Option[String] = None,\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Operating system to use when looking up in the JVM index\")\n  @ValueDescription(\"linux|linux-musl|darwin|windows|…\")\n  @Tag(tags.implementation)\n  @Hidden\n    jvmIndexOs: Option[String] = None,\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"CPU architecture to use when looking up in the JVM index\")\n  @ValueDescription(\"amd64|arm64|arm|…\")\n  @Tag(tags.implementation)\n  @Hidden\n    jvmIndexArch: Option[String] = None,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Javac plugin dependencies or files\")\n  @Tag(tags.should)\n  @Hidden\n    javacPlugin: List[String] = Nil,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Javac options\")\n  @Name(\"javacOpt\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @Hidden\n    javacOption: List[String] = Nil,\n\n  @Group(HelpGroup.Java.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Port for BSP debugging\")\n  @Hidden\n    bspDebugPort: Option[String] = None\n)\n// format: on\n\nobject SharedJvmOptions {\n  implicit lazy val parser: Parser[SharedJvmOptions]            = Parser.derive\n  implicit lazy val help: Help[SharedJvmOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[SharedJvmOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport bloop.rifle.BloopRifleConfig\nimport caseapp.*\nimport caseapp.core.Arg\nimport caseapp.core.help.Help\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport coursier.cache.FileCache\nimport coursier.util.Task\nimport coursier.version.Version\nimport dependency.AnyDependency\nimport dependency.parser.DependencyParser\n\nimport java.io.{File, InputStream}\nimport java.util.concurrent.atomic.AtomicBoolean\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.EitherOptOps\nimport scala.build.compiler.{BloopCompilerMaker, ScalaCompilerMaker, SimpleScalaCompilerMaker}\nimport scala.build.errors.{AmbiguousPlatformError, BuildException, ConfigDbException}\nimport scala.build.input.{Element, Inputs, ResourceDirectory, ScalaCliInvokeData}\nimport scala.build.interactive.Interactive\nimport scala.build.interactive.Interactive.{InteractiveAsk, InteractiveNop}\nimport scala.build.internal.util.WarningMessages\nimport scala.build.internal.{Constants, FetchExternalBinary, OsLibc}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\nimport scala.build.options.{BuildOptions, Platform, ShadowingSeq}\nimport scala.build.preprocessing.directives.ClasspathUtils.*\nimport scala.build.preprocessing.directives.Toolkit.maxScalaNativeWarningMsg\nimport scala.build.preprocessing.directives.{Python, Toolkit}\nimport scala.cli.ScalaCli\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.commands.tags\nimport scala.cli.commands.util.JvmUtils\nimport scala.cli.commands.util.ScalacOptionsUtil.*\nimport scala.cli.config.Key.BooleanEntry\nimport scala.cli.config.{ConfigDb, Keys}\nimport scala.cli.launcher.PowerOptions\nimport scala.cli.util.ConfigDbUtils\nimport scala.util.Properties\n\n// format: off\nfinal case class SharedOptions(\n  @Recurse\n    sharedVersionOptions: SharedVersionOptions = SharedVersionOptions(),\n  @Recurse\n    sourceGenerator: SourceGeneratorOptions = SourceGeneratorOptions(),\n  @Recurse\n    suppress: SuppressWarningOptions = SuppressWarningOptions(),\n  @Recurse\n    logging: LoggingOptions = LoggingOptions(),\n  @Recurse\n    powerOptions: PowerOptions = PowerOptions(),\n  @Recurse\n    js: ScalaJsOptions = ScalaJsOptions(),\n  @Recurse\n    native: ScalaNativeOptions = ScalaNativeOptions(),\n  @Recurse\n    compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(),\n  @Recurse\n    dependencies: SharedDependencyOptions = SharedDependencyOptions(),\n  @Recurse\n    scalac: ScalacOptions = ScalacOptions(),\n  @Recurse\n    jvm: SharedJvmOptions = SharedJvmOptions(),\n  @Recurse\n    coursier: CoursierOptions = CoursierOptions(),\n  @Recurse\n    workspace: SharedWorkspaceOptions = SharedWorkspaceOptions(),\n  @Recurse\n    sharedPython: SharedPythonOptions = SharedPythonOptions(),\n  @Recurse\n    benchmarking: BenchmarkingOptions = BenchmarkingOptions(),\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(s\"Set the Scala version (${Constants.defaultScalaVersion} by default)\")\n  @ValueDescription(\"version\")\n  @Name(\"S\")\n  @Name(\"scala\")\n  @Tag(tags.must)\n    scalaVersion: Option[String] = None,\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Set the Scala binary version\")\n  @ValueDescription(\"version\")\n  @Hidden\n  @Name(\"B\")\n  @Name(\"scalaBinary\")\n  @Name(\"scalaBin\")\n  @Tag(tags.must)\n    scalaBinaryVersion: Option[String] = None,\n\n  @Recurse\n    scalacExtra: ScalacExtraOptions = ScalacExtraOptions(),\n\n  @Recurse\n    snippet: SnippetOptions = SnippetOptions(),\n\n  @Recurse\n    markdown: MarkdownOptions = MarkdownOptions(),\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Add extra JARs and compiled classes to the class path\")\n  @ValueDescription(\"paths\")\n  @Name(\"jar\")\n  @Name(\"jars\")\n  @Name(\"extraJar\")\n  @Name(\"class\")\n  @Name(\"extraClass\")\n  @Name(\"classes\")\n  @Name(\"extraClasses\")\n  @Name(\"-classpath\")\n  @Name(\"-cp\")\n  @Name(\"classpath\")\n  @Name(\"classPath\")\n  @Name(\"extraClassPath\")\n  @Tag(tags.must)\n    extraJars: List[String] = Nil,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Add extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\")\n  @ValueDescription(\"paths\")\n  @Name(\"compileOnlyJar\")\n  @Name(\"compileOnlyJars\")\n  @Name(\"extraCompileOnlyJar\")\n  @Tag(tags.should)\n    extraCompileOnlyJars: List[String] = Nil,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Add extra source JARs\")\n  @ValueDescription(\"paths\")\n  @Name(\"sourceJar\")\n  @Name(\"sourceJars\")\n  @Name(\"extraSourceJar\")\n  @Tag(tags.should)\n    extraSourceJars: List[String] = Nil,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Add a resource directory\")\n  @ValueDescription(\"paths\")\n  @Name(\"resourceDir\")\n  @Tag(tags.must)\n    resourceDirs: List[String] = Nil,\n\n  @Hidden\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Put project in class paths as a JAR rather than as a byte code directory\")\n  @Tag(tags.experimental)\n    asJar: Boolean = false,\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Specify platform\")\n  @ValueDescription(\"scala-js|scala-native|jvm\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n    platform: Option[String] = None,\n\n  @Group(HelpGroup.Scala.toString)\n  @Tag(tags.implementation)\n  @Hidden\n    scalaLibrary: Option[Boolean] = None,\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Allows to include the Scala compiler artifacts on the classpath.\")\n  @Tag(tags.must)\n  @Name(\"withScalaCompiler\")\n  @Name(\"-with-compiler\")\n    withCompiler: Option[Boolean] = None,\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Do not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\")\n  @Tag(tags.implementation)\n  @Hidden\n    java: Option[Boolean] = None,\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Should include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\")\n  @Tag(tags.implementation)\n  @Hidden\n    runner: Option[Boolean] = None,\n\n  @Recurse\n    semanticDbOptions: SemanticDbOptions = SemanticDbOptions(),\n\n  @Recurse\n    input: SharedInputOptions = SharedInputOptions(),\n  @Recurse\n    helpGroups: HelpGroupOptions = HelpGroupOptions(),\n\n  @Hidden\n    strictBloopJsonCheck: Option[Boolean] = None,\n\n  @Group(HelpGroup.Scala.toString)\n  @Name(\"d\")\n  @Name(\"output-directory\")\n  @Name(\"destination\")\n  @Name(\"compileOutput\")\n  @Name(\"compileOut\")\n  @HelpMessage(\"Copy compilation results to output directory using either relative or absolute path\")\n  @ValueDescription(\"/example/path\")\n  @Tag(tags.must)\n    compilationOutput: Option[String] = None,\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(s\"Add toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: ${Constants.toolkitDefaultVersion}, 'default' version for typelevel toolkit: ${Constants.typelevelToolkitDefaultVersion}\")\n  @ValueDescription(\"version|default\")\n  @Name(\"toolkit\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n    withToolkit: Option[String] = None,\n  @HelpMessage(\"Exclude sources\")\n    exclude: List[String] = Nil,\n  @HelpMessage(\"Force object wrapper for scripts\")\n  @Tag(tags.experimental)\n    objectWrapper: Option[Boolean] = None,\n  @Recurse\n    scope: ScopeOptions = ScopeOptions()\n) extends HasGlobalOptions {\n  // format: on\n\n  def logger: Logger                 = logging.logger\n  override def global: GlobalOptions =\n    GlobalOptions(logging = logging, globalSuppress = suppress.global, powerOptions = powerOptions)\n\n  private def scalaJsOptions(opts: ScalaJsOptions): options.ScalaJsOptions = {\n    import opts._\n    options.ScalaJsOptions(\n      version = jsVersion,\n      mode = options.ScalaJsMode(jsMode),\n      moduleKindStr = jsModuleKind,\n      checkIr = jsCheckIr,\n      emitSourceMaps = jsEmitSourceMaps,\n      sourceMapsDest = jsSourceMapsPath.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)),\n      dom = jsDom,\n      header = jsHeader,\n      allowBigIntsForLongs = jsAllowBigIntsForLongs,\n      avoidClasses = jsAvoidClasses,\n      avoidLetsAndConsts = jsAvoidLetsAndConsts,\n      moduleSplitStyleStr = jsModuleSplitStyle,\n      smallModuleForPackage = jsSmallModuleForPackage,\n      esVersionStr = jsEsVersion,\n      noOpt = jsNoOpt,\n      remapEsModuleImportMap = jsEsModuleImportMap.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd)),\n      jsEmitWasm = jsEmitWasm.getOrElse(false)\n    )\n  }\n\n  private def linkerOptions(opts: ScalaJsOptions): options.scalajs.ScalaJsLinkerOptions = {\n    import opts._\n    options.scalajs.ScalaJsLinkerOptions(\n      linkerPath = jsLinkerPath\n        .filter(_.trim.nonEmpty)\n        .map(os.Path(_, Os.pwd)),\n      scalaJsVersion = jsVersion.map(_.trim).filter(_.nonEmpty),\n      scalaJsCliVersion = jsCliVersion.map(_.trim).filter(_.nonEmpty),\n      javaArgs = jsCliJavaArg,\n      useJvm = jsCliOnJvm.map {\n        case false => Left(FetchExternalBinary.platformSuffix())\n        case true  => Right(())\n      }\n    )\n  }\n\n  private def scalaNativeOptions(\n    opts: ScalaNativeOptions,\n    maxDefaultScalaNativeVersions: List[(String, String)]\n  ): options.ScalaNativeOptions = {\n    import opts._\n    options.ScalaNativeOptions(\n      version = nativeVersion,\n      modeStr = nativeMode,\n      ltoStr = nativeLto,\n      gcStr = nativeGc,\n      clang = nativeClang,\n      clangpp = nativeClangpp,\n      linkingOptions = nativeLinking,\n      linkingDefaults = nativeLinkingDefaults,\n      compileOptions = nativeCompile,\n      cCompileOptions = nativeCCompile,\n      cppCompileOptions = nativeCppCompile,\n      compileDefaults = nativeCompileDefaults,\n      embedResources = embedResources,\n      buildTargetStr = nativeTarget,\n      multithreading = nativeMultithreading,\n      maxDefaultNativeVersions = maxDefaultScalaNativeVersions\n    )\n  }\n\n  lazy val scalacOptionsFromFiles: List[String] =\n    scalac.argsFiles.flatMap(argFile =>\n      ArgSplitter.splitToArgs(os.read(os.Path(argFile.file, os.pwd)))\n    )\n\n  def scalacOptions: List[String] = scalac.scalacOption ++ scalacOptionsFromFiles\n\n  def buildOptions(\n    ignoreErrors: Boolean = false,\n    watchOptions: SharedWatchOptions = SharedWatchOptions()\n  )\n    : Either[BuildException, scala.build.options.BuildOptions] =\n    either {\n      val releaseOpt = scalacOptions.getScalacOption(\"-release\")\n      val targetOpt  = scalacOptions.getScalacPrefixOption(\"-target\")\n      jvm.jvm -> (releaseOpt.toSeq ++ targetOpt) match {\n        case (Some(j), compilerTargets) if compilerTargets.exists(_ != j) =>\n          val compilerTargetsString = compilerTargets.distinct.mkString(\", \")\n          logger.error(\n            s\"Warning: different target JVM ($j) and scala compiler target JVM ($compilerTargetsString) were passed.\"\n          )\n        case _ =>\n      }\n      val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse)\n      val platformOpt    = value {\n        (parsedPlatform, js.js, native.native) match {\n          case (Some(p: Platform.JS.type), _, false)      => Right(Some(p))\n          case (Some(p: Platform.Native.type), false, _)  => Right(Some(p))\n          case (Some(p: Platform.JVM.type), false, false) => Right(Some(p))\n          case (Some(p), _, _)                            =>\n            val jsSeq        = if (js.js) Seq(Platform.JS) else Seq.empty\n            val nativeSeq    = if (native.native) Seq(Platform.Native) else Seq.empty\n            val platformsSeq = Seq(p) ++ jsSeq ++ nativeSeq\n            Left(new AmbiguousPlatformError(platformsSeq.distinct.map(_.toString)))\n          case (_, true, true) =>\n            Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString)))\n          case (_, true, _) => Right(Some(Platform.JS))\n          case (_, _, true) => Right(Some(Platform.Native))\n          case _            => Right(None)\n        }\n      }\n      val (assumedSourceJars, extraRegularJarsAndClasspath) =\n        extraJarsAndClassPath.partition(_.hasSourceJarSuffix)\n      if assumedSourceJars.nonEmpty then\n        val assumedSourceJarsString = assumedSourceJars.mkString(\", \")\n        logger.message(\n          s\"\"\"[${Console.YELLOW}warn${Console.RESET}] Jars with the ${ScalaCliConsole\n              .GRAY}*-sources.jar${Console.RESET} name suffix are assumed to be source jars.\n             |The following jars were assumed to be source jars and will be treated as such: $assumedSourceJarsString\"\"\".stripMargin\n        )\n      val shouldSuppressDeprecatedWarnings = getOptionOrFromConfig(\n        suppress.global.suppressDeprecatedFeatureWarning,\n        Keys.suppressDeprecatedFeatureWarning\n      )\n      val (resolvedToolkitDependency, toolkitMaxDefaultScalaNativeVersions) =\n        SharedOptions.resolveToolkitDependencyAndScalaNativeVersionReqs(\n          withToolkit,\n          shouldSuppressDeprecatedWarnings.getOrElse(false),\n          logger\n        )\n      val scalapyMaxDefaultScalaNativeVersions =\n        if sharedPython.python.contains(true) then\n          List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)\n        else Nil\n      val maxDefaultScalaNativeVersions =\n        toolkitMaxDefaultScalaNativeVersions.toList ++ scalapyMaxDefaultScalaNativeVersions\n      val snOpts = scalaNativeOptions(native, maxDefaultScalaNativeVersions)\n      scala.build.options.BuildOptions(\n        sourceGeneratorOptions = scala.build.options.SourceGeneratorOptions(\n          useBuildInfo = sourceGenerator.useBuildInfo,\n          projectVersion = sharedVersionOptions.projectVersion,\n          computeVersion = value {\n            sharedVersionOptions.computeVersion\n              .map(Positioned.commandLine)\n              .map(scala.build.options.ComputeVersion.parse)\n              .sequence\n          }\n        ),\n        suppressWarningOptions =\n          scala.build.options.SuppressWarningOptions(\n            suppressDirectivesInMultipleFilesWarning = getOptionOrFromConfig(\n              suppress.suppressDirectivesInMultipleFilesWarning,\n              Keys.suppressDirectivesInMultipleFilesWarning\n            ),\n            suppressOutdatedDependencyWarning = getOptionOrFromConfig(\n              suppress.suppressOutdatedDependencyWarning,\n              Keys.suppressOutdatedDependenciessWarning\n            ),\n            suppressExperimentalFeatureWarning = getOptionOrFromConfig(\n              suppress.global.suppressExperimentalFeatureWarning,\n              Keys.suppressExperimentalFeatureWarning\n            ),\n            suppressDeprecatedFeatureWarning = shouldSuppressDeprecatedWarnings\n          ),\n        scalaOptions = scala.build.options.ScalaOptions(\n          scalaVersion = scalaVersion\n            .map(_.trim)\n            .filter(_.nonEmpty)\n            .map(scala.build.options.MaybeScalaVersion(_)),\n          scalaBinaryVersion = scalaBinaryVersion.map(_.trim).filter(_.nonEmpty),\n          addScalaLibrary = scalaLibrary.orElse(java.map(!_)),\n          addScalaCompiler = withCompiler,\n          semanticDbOptions = scala.build.options.SemanticDbOptions(\n            generateSemanticDbs = semanticDbOptions.semanticDb,\n            semanticDbTargetRoot = semanticDbOptions.semanticDbTargetRoot.map(os.Path(_, os.pwd)),\n            semanticDbSourceRoot = semanticDbOptions.semanticDbSourceRoot.map(os.Path(_, os.pwd))\n          ),\n          scalacOptions = scalacOptions\n            .withScalacExtraOptions(scalacExtra)\n            .toScalacOptShadowingSeq\n            .filterNonRedirected\n            .filterNonDeprecated\n            .map(Positioned.commandLine),\n          compilerPlugins =\n            SharedOptions.parseDependencies(\n              dependencies.compilerPlugin.map(Positioned.none),\n              ignoreErrors\n            ),\n          platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))\n        ),\n        scriptOptions = scala.build.options.ScriptOptions(\n          forceObjectWrapper = objectWrapper\n        ),\n        scalaJsOptions = scalaJsOptions(js),\n        scalaNativeOptions = snOpts,\n        javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)),\n        jmhOptions = scala.build.options.JmhOptions(\n          jmhVersion = benchmarking.jmhVersion,\n          enableJmh = benchmarking.jmh,\n          runJmh = benchmarking.jmh\n        ),\n        classPathOptions = scala.build.options.ClassPathOptions(\n          extraClassPath = extraRegularJarsAndClasspath,\n          extraCompileOnlyJars = extraCompileOnlyClassPath,\n          extraSourceJars = extraSourceJars.extractedClassPath ++ assumedSourceJars,\n          extraRepositories =\n            (ScalaCli.launcherOptions.scalaRunner.cliPredefinedRepository ++\n              dependencies.repository)\n              .map(_.trim)\n              .filter(_.nonEmpty),\n          extraDependencies = extraDependencies(ignoreErrors, resolvedToolkitDependency),\n          extraCompileOnlyDependencies =\n            extraCompileOnlyDependencies(ignoreErrors, resolvedToolkitDependency)\n        ),\n        internal = scala.build.options.InternalOptions(\n          cache = Some(coursierCache),\n          localRepository = LocalRepo.localRepo(Directories.directories.localRepoDir, logger),\n          verbosity = Some(logging.verbosity),\n          strictBloopJsonCheck = strictBloopJsonCheck,\n          interactive = Some(() => interactive),\n          exclude = exclude.map(Positioned.commandLine),\n          offline = coursier.getOffline(logger)\n        ),\n        notForBloopOptions = scala.build.options.PostBuildOptions(\n          scalaJsLinkerOptions = linkerOptions(js),\n          addRunnerDependencyOpt = runner,\n          python = sharedPython.python,\n          pythonSetup = sharedPython.pythonSetup,\n          scalaPyVersion = sharedPython.scalaPyVersion\n        ),\n        useBuildServer = compilationServer.server\n      ).orElse(watchOptions.buildOptions())\n    }\n\n  private def resolvedDependencies(\n    deps: List[String],\n    ignoreErrors: Boolean,\n    extraResolvedDependencies: Seq[Positioned[AnyDependency]]\n  ) = ShadowingSeq.from {\n    SharedOptions.parseDependencies(deps.map(Positioned.none), ignoreErrors) ++\n      extraResolvedDependencies\n  }\n\n  private def extraCompileOnlyDependencies(\n    ignoreErrors: Boolean,\n    resolvedDeps: Seq[Positioned[AnyDependency]]\n  ) = resolvedDependencies(dependencies.compileOnlyDependency, ignoreErrors, resolvedDeps)\n\n  private def extraDependencies(\n    ignoreErrors: Boolean,\n    resolvedDeps: Seq[Positioned[AnyDependency]]\n  ) = resolvedDependencies(dependencies.dependency, ignoreErrors, resolvedDeps)\n\n  extension (rawClassPath: List[String]) {\n    def extractedClassPath: List[os.Path] =\n      rawClassPath\n        .flatMap(_.split(File.pathSeparator).toSeq)\n        .filter(_.nonEmpty)\n        .distinct\n        .map(os.Path(_, os.pwd))\n        .flatMap {\n          case cp if os.isDir(cp) =>\n            val jarsInTheDirectory =\n              os.walk(cp)\n                .filter(p => os.isFile(p) && p.last.endsWith(\".jar\"))\n            List(cp) ++ jarsInTheDirectory // .jar paths have to be passed directly, unlike .class\n          case cp => List(cp)\n        }\n  }\n\n  def extraJarsAndClassPath: List[os.Path] =\n    (extraJars ++ scalacOptions.getScalacOption(\"-classpath\") ++ scalacOptions.getScalacOption(\n      \"-cp\"\n    ))\n      .extractedClassPath\n\n  def extraClasspathWasPassed: Boolean =\n    extraJarsAndClassPath.exists(!_.hasSourceJarSuffix) || dependencies.dependency.nonEmpty\n\n  def extraCompileOnlyClassPath: List[os.Path] = extraCompileOnlyJars.extractedClassPath\n\n  def globalInteractiveWasSuggested: Either[BuildException, Option[Boolean]] = either {\n    value(ConfigDbUtils.configDb).get(Keys.globalInteractiveWasSuggested) match {\n      case Right(opt) => opt\n      case Left(ex)   =>\n        logger.debug(ConfigDbException(ex))\n        None\n    }\n  }\n\n  def interactive: Either[BuildException, Interactive] = either {\n    (\n      logging.verbosityOptions.interactive,\n      value(ConfigDbUtils.configDb).get(Keys.interactive) match {\n        case Right(opt) => opt\n        case Left(ex)   =>\n          logger.debug(ConfigDbException(ex))\n          None\n      },\n      value(globalInteractiveWasSuggested)\n    ) match {\n      case (Some(true), _, Some(true)) => InteractiveAsk\n      case (_, Some(true), _)          => InteractiveAsk\n      case (Some(true), _, _)          =>\n        val answers @ List(yesAnswer, _) = List(\"Yes\", \"No\")\n        InteractiveAsk.chooseOne(\n          s\"\"\"You have run the current ${ScalaCli.baseRunnerName} command with the --interactive mode turned on.\n             |Would you like to leave it on permanently?\"\"\".stripMargin,\n          answers\n        ) match {\n          case Some(answer) if answer == yesAnswer =>\n            val configDb0 = value(ConfigDbUtils.configDb)\n            value {\n              configDb0\n                .set(Keys.interactive, true)\n                .set(Keys.globalInteractiveWasSuggested, true)\n                .save(Directories.directories.dbPath.toNIO)\n                .wrapConfigException\n            }\n            logger.message(\n              s\"--interactive is now set permanently. All future ${ScalaCli.baseRunnerName} commands will run with the flag set to true.\"\n            )\n            logger.message(\n              s\"If you want to turn this setting off at any point, just run `${ScalaCli.baseRunnerName} config interactive false`.\"\n            )\n          case _ =>\n            val configDb0 = value(ConfigDbUtils.configDb)\n            value {\n              configDb0\n                .set(Keys.globalInteractiveWasSuggested, true)\n                .save(Directories.directories.dbPath.toNIO)\n                .wrapConfigException\n            }\n            logger.message(\n              s\"If you want to turn this setting permanently on at any point, just run `${ScalaCli.baseRunnerName} config interactive true`.\"\n            )\n        }\n        InteractiveAsk\n      case _ => InteractiveNop\n    }\n  }\n\n  def getOptionOrFromConfig(cliOption: Option[Boolean], configDbKey: BooleanEntry) =\n    cliOption.orElse(\n      ConfigDbUtils.configDb.map(_.get(configDbKey))\n        .map {\n          case Right(opt) => opt\n          case Left(ex)   =>\n            logger.debug(ConfigDbException(ex))\n            None\n        }\n        .getOrElse(None)\n    )\n\n  def bloopRifleConfig(extraBuildOptions: Option[scala.build.options.BuildOptions] = None)\n    : Either[BuildException, BloopRifleConfig] = either {\n    val options             = extraBuildOptions.foldLeft(value(buildOptions()))(_.orElse(_))\n    lazy val defaultJvmHome = value {\n      JvmUtils.downloadJvm(OsLibc.defaultJvm(OsLibc.jvmIndexOs), options)\n    }\n\n    val javaHomeInfo = compilationServer.bloopJvm\n      .map(jvmId => value(JvmUtils.downloadJvm(jvmId, options)))\n      .orElse {\n        for (javaHome <- options.javaHomeLocationOpt()) yield {\n          val (javaHomeVersion, javaHomeCmd) = OsLibc.javaHomeVersion(javaHome.value)\n          if (javaHomeVersion >= Constants.minimumBloopJavaVersion)\n            scala.build.options.BuildOptions.JavaHomeInfo(\n              javaHome.value,\n              javaHomeCmd,\n              javaHomeVersion\n            )\n          else defaultJvmHome\n        }\n      }.getOrElse(defaultJvmHome)\n\n    compilationServer.bloopRifleConfig(\n      logging.logger,\n      coursierCache,\n      logging.verbosity,\n      javaHomeInfo.javaCommand,\n      Directories.directories,\n      Some(javaHomeInfo.version)\n    )\n  }\n\n  def compilerMaker(\n    threads: BuildThreads,\n    scaladoc: Boolean = false\n  ): ScalaCompilerMaker =\n    if (scaladoc)\n      SimpleScalaCompilerMaker(\"java\", Nil, scaladoc = true)\n    else if (compilationServer.server.getOrElse(true))\n      new BloopCompilerMaker(\n        options => bloopRifleConfig(Some(options)),\n        threads.bloop,\n        strictBloopJsonCheckOrDefault,\n        coursier.getOffline(logger).getOrElse(false)\n      )\n    else SimpleScalaCompilerMaker(\"java\", Nil)\n\n  lazy val coursierCache: FileCache[Task] = coursier.coursierCache(logging.logger)\n\n  def inputs(\n    args: Seq[String],\n    defaultInputs: () => Option[Inputs] = () => Inputs.default()\n  )(using ScalaCliInvokeData): Either[BuildException, Inputs] =\n    SharedOptions.inputs(\n      args,\n      defaultInputs,\n      resourceDirs,\n      Directories.directories,\n      logger = logger,\n      coursierCache,\n      workspace.forcedWorkspaceOpt,\n      input.defaultForbiddenDirectories,\n      input.forbid,\n      scriptSnippetList = allScriptSnippets,\n      scalaSnippetList = allScalaSnippets,\n      javaSnippetList = allJavaSnippets,\n      markdownSnippetList = allMarkdownSnippets,\n      enableMarkdown = markdown.enableMarkdown,\n      extraClasspathWasPassed = extraClasspathWasPassed\n    )\n\n  def allScriptSnippets: List[String]   = snippet.scriptSnippet ++ snippet.executeScript\n  def allScalaSnippets: List[String]    = snippet.scalaSnippet ++ snippet.executeScala\n  def allJavaSnippets: List[String]     = snippet.javaSnippet ++ snippet.executeJava\n  def allMarkdownSnippets: List[String] = snippet.markdownSnippet ++ snippet.executeMarkdown\n\n  def hasSnippets =\n    allScriptSnippets.nonEmpty || allScalaSnippets.nonEmpty ||\n    allJavaSnippets\n      .nonEmpty || allMarkdownSnippets.nonEmpty\n\n  def validateInputArgs(\n    args: Seq[String]\n  )(using ScalaCliInvokeData): Seq[Either[String, Seq[Element]]] =\n    Inputs.validateArgs(\n      args,\n      Os.pwd,\n      BuildOptions.Download.changing(coursierCache),\n      SharedOptions.readStdin(logger = logger),\n      !Properties.isWin,\n      enableMarkdown = true\n    )\n\n  def strictBloopJsonCheckOrDefault: Boolean =\n    strictBloopJsonCheck.getOrElse(scala.build.options.InternalOptions.defaultStrictBloopJsonCheck)\n\n}\n\nobject SharedOptions {\n  implicit lazy val parser: Parser[SharedOptions]            = Parser.derive\n  implicit lazy val help: Help[SharedOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[SharedOptions] = JsonCodecMaker.make\n\n  /** [[Inputs]] builder, handy when you don't have a [[SharedOptions]] instance at hand */\n  def inputs(\n    args: Seq[String],\n    defaultInputs: () => Option[Inputs],\n    resourceDirs: Seq[String],\n    directories: scala.build.Directories,\n    logger: scala.build.Logger,\n    cache: FileCache[Task],\n    forcedWorkspaceOpt: Option[os.Path],\n    defaultForbiddenDirectories: Boolean,\n    forbid: List[String],\n    scriptSnippetList: List[String],\n    scalaSnippetList: List[String],\n    javaSnippetList: List[String],\n    markdownSnippetList: List[String],\n    enableMarkdown: Boolean = false,\n    extraClasspathWasPassed: Boolean = false\n  )(using ScalaCliInvokeData): Either[BuildException, Inputs] = {\n    val resourceInputs = resourceDirs\n      .map(os.Path(_, Os.pwd))\n      .map { path =>\n        if (!os.exists(path))\n          logger.message(s\"WARNING: provided resource directory path doesn't exist: $path\")\n        path\n      }\n      .map(ResourceDirectory.apply)\n\n    val maybeInputs = Inputs(\n      args,\n      Os.pwd,\n      defaultInputs = defaultInputs,\n      download = BuildOptions.Download.changing(cache),\n      stdinOpt = readStdin(logger = logger),\n      scriptSnippetList = scriptSnippetList,\n      scalaSnippetList = scalaSnippetList,\n      javaSnippetList = javaSnippetList,\n      markdownSnippetList = markdownSnippetList,\n      acceptFds = !Properties.isWin,\n      forcedWorkspace = forcedWorkspaceOpt,\n      enableMarkdown = enableMarkdown,\n      allowRestrictedFeatures = ScalaCli.allowRestrictedFeatures,\n      extraClasspathWasPassed = extraClasspathWasPassed\n    )\n\n    maybeInputs.map { inputs =>\n      val forbiddenDirs =\n        (if (defaultForbiddenDirectories) myDefaultForbiddenDirectories else Nil) ++\n          forbid.filter(_.trim.nonEmpty).map(os.Path(_, Os.pwd))\n\n      inputs\n        .add(resourceInputs)\n        .checkAttributes(directories)\n        .avoid(forbiddenDirs, directories)\n    }\n  }\n\n  private def readStdin(in: InputStream = System.in, logger: Logger): Option[Array[Byte]] =\n    if (in == null) {\n      logger.debug(\"No stdin available\")\n      None\n    }\n    else {\n      logger.debug(\"Reading stdin\")\n      val result = in.readAllBytes()\n      logger.debug(s\"Done reading stdin (${result.length} B)\")\n      Some(result)\n    }\n\n  private def myDefaultForbiddenDirectories: Seq[os.Path] =\n    if (Properties.isWin)\n      Seq(os.Path(\"\"\"C:\\Windows\\System32\"\"\"))\n    else\n      Nil\n\n  def parseDependencies(\n    deps: List[Positioned[String]],\n    ignoreErrors: Boolean\n  ): Seq[Positioned[AnyDependency]] =\n    deps.map(_.map(_.trim)).filter(_.value.nonEmpty)\n      .flatMap { posDepStr =>\n        val depStr = posDepStr.value\n        DependencyParser.parse(depStr) match {\n          case Left(err) =>\n            if (ignoreErrors) Nil\n            else sys.error(s\"Error parsing dependency '$depStr': $err\")\n          case Right(dep) => Seq(posDepStr.map(_ => dep))\n        }\n      }\n\n  // TODO: remove this state after resolving https://github.com/VirtusLab/scala-cli/issues/2658\n  private val loggedDeprecatedToolkitWarning: AtomicBoolean = AtomicBoolean(false)\n  private def resolveToolkitDependencyAndScalaNativeVersionReqs(\n    toolkitVersion: Option[String],\n    shouldSuppressDeprecatedWarnings: Boolean,\n    logger: Logger\n  ): (Seq[Positioned[AnyDependency]], Seq[(String, String)]) = {\n    if (\n      !shouldSuppressDeprecatedWarnings &&\n      (toolkitVersion.contains(\"latest\")\n      || toolkitVersion.contains(Toolkit.typelevel + \":latest\")\n      || toolkitVersion.contains(\n        Constants.typelevelOrganization + \":latest\"\n      )) && !loggedDeprecatedToolkitWarning.getAndSet(true)\n    ) logger.message(\n      WarningMessages.deprecatedToolkitLatest(\n        s\"--toolkit ${toolkitVersion.map(_.replace(\"latest\", \"default\")).getOrElse(\"default\")}\"\n      )\n    )\n\n    val (dependencies, toolkitDefinitions) =\n      toolkitVersion.toList.map(Positioned.commandLine)\n        .flatMap(Toolkit.resolveDependenciesWithRequirements(_).map((wbr, td) => wbr.value -> td))\n        .unzip\n    val maxScalaNativeVersions =\n      toolkitDefinitions.flatMap {\n        case Toolkit.ToolkitDefinitions(\n              isScalaToolkitDefault,\n              explicitScalaToolkitVersion,\n              isTypelevelToolkitDefault,\n              _\n            ) =>\n          val st = if (isScalaToolkitDefault)\n            Seq(Constants.toolkitMaxScalaNative -> Toolkit.maxScalaNativeWarningMsg(\n              toolkitName = \"Scala Toolkit\",\n              toolkitVersion = Constants.toolkitDefaultVersion,\n              maxNative = Constants.toolkitMaxScalaNative\n            ))\n          else explicitScalaToolkitVersion.toList\n            .map(Version(_))\n            .filter(_ <= Version(Constants.toolkitVersionForNative04))\n            .flatMap(v =>\n              List(Constants.scalaNativeVersion04 -> maxScalaNativeWarningMsg(\n                toolkitName = \"Scala Toolkit\",\n                toolkitVersion = v.toString(),\n                Constants.scalaNativeVersion04\n              ))\n            )\n          val tlt =\n            if (isTypelevelToolkitDefault)\n              Seq(Constants.typelevelToolkitMaxScalaNative -> Toolkit.maxScalaNativeWarningMsg(\n                toolkitName = \"TypeLevel Toolkit\",\n                toolkitVersion = Constants.typelevelToolkitDefaultVersion,\n                maxNative = Constants.typelevelToolkitMaxScalaNative\n              ))\n            else Nil\n          st ++ tlt\n      }\n    dependencies -> maxScalaNativeVersions\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedPythonOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.{Constants, tags}\n\n// format: off\nfinal case class SharedPythonOptions(\n  @Tag(tags.experimental)\n  @HelpMessage(\"Set Java options so that Python can be loaded\")\n    pythonSetup: Option[Boolean] = None,\n  @Tag(tags.experimental)\n  @HelpMessage(\"Enable Python support via ScalaPy\")\n  @ExtraName(\"py\")\n    python: Option[Boolean] = None,\n  @Tag(tags.experimental)\n  @HelpMessage(s\"Set ScalaPy version (${Constants.scalaPyVersion} by default)\")\n  @ExtraName(\"scalapyVersion\")\n    scalaPyVersion: Option[String] = None\n)\n// format: on\n\nobject SharedPythonOptions {\n  implicit lazy val parser: Parser[SharedPythonOptions] = Parser.derive\n  implicit lazy val help: Help[SharedPythonOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedVersionOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedVersionOptions(\n  @Group(HelpGroup.ProjectVersion.toString)\n  @HelpMessage(\"Method used to compute the project version\")\n  @ValueDescription(\"git|git:tag|command:...\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    computeVersion: Option[String] = None,\n  @Group(HelpGroup.ProjectVersion.toString)\n  @HelpMessage(\"Set the project version\")\n  @Tag(tags.restricted)\n  @Tag(tags.inShortHelp)\n    projectVersion: Option[String] = None\n)\n// format: on\n\nobject SharedVersionOptions {\n  implicit lazy val parser: Parser[SharedVersionOptions] = Parser.derive\n  implicit lazy val help: Help[SharedVersionOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedWatchOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.build.options.{BuildOptions, WatchOptions}\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedWatchOptions(\n\n  @Group(HelpGroup.Watch.toString)\n  @HelpMessage(\"Run the application in the background, automatically wake the thread and re-run if sources have been changed\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @Name(\"w\")\n    watch: Boolean = false,\n  @Group(HelpGroup.Watch.toString)\n  @HelpMessage(\"Run the application in the background, automatically kill the process and restart if sources have been changed\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @Name(\"revolver\")\n    restart: Boolean = false,\n  @Group(HelpGroup.Watch.toString)\n  @HelpMessage(\"Watch additional paths for changes (used together with --watch or --restart)\")\n  @Tag(tags.experimental)\n  @Name(\"watchingPath\")\n    watching: List[String] = Nil\n) { // format: on\n\n  lazy val watchMode: Boolean = watch || restart\n\n  def buildOptions(cwd: os.Path = os.pwd): BuildOptions =\n    BuildOptions(\n      watchOptions = WatchOptions(\n        extraWatchPaths = watching.map(os.Path(_, cwd))\n      )\n    )\n}\n\nobject SharedWatchOptions {\n  implicit lazy val parser: Parser[SharedWatchOptions] = Parser.derive\n  implicit lazy val help: Help[SharedWatchOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SharedWorkspaceOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.build.Os\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedWorkspaceOptions(\n  @Hidden\n  @HelpMessage(\"Directory where .scala-build is written\")\n  @ValueDescription(\"path\")\n  @Tag(tags.implementation)\n    workspace: Option[String] = None\n) {\n  // format: on\n\n  def forcedWorkspaceOpt: Option[os.Path] =\n    workspace\n      .filter(_.trim.nonEmpty)\n      .map(os.Path(_, Os.pwd))\n}\n\nobject SharedWorkspaceOptions {\n  implicit lazy val parser: Parser[SharedWorkspaceOptions]            = Parser.derive\n  implicit lazy val help: Help[SharedWorkspaceOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[SharedWorkspaceOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SnippetOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SnippetOptions(\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Allows to execute a passed string as a Scala script\")\n  @Tag(tags.should)\n    scriptSnippet: List[String] = List.empty,\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"A synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\")\n  @Hidden\n  @Name(\"e\")\n  @Name(\"executeScalaScript\")\n  @Name(\"executeSc\")\n  @Tag(tags.should)\n    executeScript: List[String] = List.empty,\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"Allows to execute a passed string as Scala code\")\n  @Tag(tags.should)\n    scalaSnippet: List[String] = List.empty,\n\n  @Group(HelpGroup.Scala.toString)\n  @HelpMessage(\"A synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\")\n  @Hidden\n  @Tag(tags.implementation)\n    executeScala: List[String] = List.empty,\n\n  @Group(HelpGroup.Java.toString)\n  @HelpMessage(\"Allows to execute a passed string as Java code\")\n  @Tag(tags.implementation)\n    javaSnippet: List[String] = List.empty,\n\n   @Group(HelpGroup.Java.toString)\n   @Tag(tags.implementation)\n  @HelpMessage(\"A synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\")\n    executeJava: List[String] = List.empty,\n\n  @Group(HelpGroup.Markdown.toString)\n  @HelpMessage(\"Allows to execute a passed string as Markdown code\")\n  @Name(\"mdSnippet\")\n  @Tag(tags.experimental)\n    markdownSnippet: List[String] = List.empty,\n\n  @Group(HelpGroup.Markdown.toString)\n  @HelpMessage(\"A synonym to --markdown-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\")\n  @Name(\"executeMd\")\n  @Tag(tags.experimental)\n  @Hidden\n    executeMarkdown: List[String] = List.empty,\n)\n// format: on\n\nobject SnippetOptions {\n  implicit lazy val parser: Parser[SnippetOptions] = Parser.derive\n  implicit lazy val help: Help[SnippetOptions]     = Help.derive\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SourceGeneratorOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SourceGeneratorOptions(\n  @Group(HelpGroup.SourceGenerator.toString)\n  @Tag(tags.restricted)\n  @HelpMessage(\"Generate BuildInfo for project\")\n  @Name(\"buildInfo\")\n    useBuildInfo: Option[Boolean] = None\n)\n// format: on\n\nobject SourceGeneratorOptions {\n  implicit lazy val parser: Parser[SourceGeneratorOptions] = Parser.derive\n  implicit lazy val help: Help[SourceGeneratorOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/SuppressWarningOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\n\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SuppressWarningOptions(\n  @Group(HelpGroup.SuppressWarnings.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Suppress warnings about using directives in multiple files\")\n  @Name(\"suppressWarningDirectivesInMultipleFiles\")\n    suppressDirectivesInMultipleFilesWarning: Option[Boolean] = None,\n  @Group(HelpGroup.SuppressWarnings.toString)\n  @Tag(tags.implementation)\n  @HelpMessage(\"Suppress warnings about outdated dependencies in project\")\n    suppressOutdatedDependencyWarning: Option[Boolean] = None,\n  @Recurse\n    global: GlobalSuppressWarningOptions = GlobalSuppressWarningOptions()\n)\n// format: on\n\nobject SuppressWarningOptions {\n  implicit lazy val parser: Parser[SuppressWarningOptions] = Parser.derive\n  implicit lazy val help: Help[SuppressWarningOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shared/VerbosityOptions.scala",
    "content": "package scala.cli.commands.shared\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.build.interactive.Interactive.*\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class VerbosityOptions(\n  @Group(HelpGroup.Logging.toString)\n  @HelpMessage(\"Increase verbosity (can be specified multiple times)\")\n  @Tag(tags.implementation)\n  @Name(\"v\")\n  @Name(\"-verbose\")\n    verbose: Int @@ Counter = Tag.of(0),\n  @Group(HelpGroup.Logging.toString)\n  @HelpMessage(\"Interactive mode\")\n  @Name(\"i\")\n  @Tag(tags.implementation)\n    interactive: Option[Boolean] = None,\n  @Group(HelpGroup.Logging.toString)\n  @HelpMessage(\"Enable actionable diagnostics\")\n  @Tag(tags.implementation)\n    actions: Option[Boolean] = None\n) {\n  // format: on\n\n  lazy val verbosity = Tag.unwrap(verbose)\n\n  def interactiveInstance(forceEnable: Boolean = false) =\n    if (interactive.getOrElse(forceEnable)) InteractiveAsk else InteractiveNop\n}\n\nobject VerbosityOptions {\n  implicit lazy val parser: Parser[VerbosityOptions]     = Parser.derive\n  implicit lazy val help: Help[VerbosityOptions]         = Help.derive\n  implicit val rwCounter: JsonValueCodec[Int @@ Counter] =\n    new JsonValueCodec[Int @@ Counter] {\n      private val intCodec: JsonValueCodec[Int]                = JsonCodecMaker.make\n      def decodeValue(in: JsonReader, default: Int @@ Counter) =\n        Tag.of(intCodec.decodeValue(in, Tag.unwrap(default)))\n      def encodeValue(x: Int @@ Counter, out: JsonWriter): Unit =\n        intCodec.encodeValue(Tag.unwrap(x), out)\n      def nullValue: Int @@ Counter =\n        Tag.of(0)\n    }\n  implicit lazy val jsonCodec: JsonValueCodec[VerbosityOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shebang/Shebang.scala",
    "content": "package scala.cli.commands.shebang\n\nimport caseapp.RemainingArgs\nimport caseapp.core.help.HelpFormat\n\nimport scala.build.Logger\nimport scala.build.input.{ScalaCliInvokeData, SubCommand}\nimport scala.cli.commands.run.Run\nimport scala.cli.commands.shared.SharedOptions\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.util.ArgHelpers.*\n\nobject Shebang extends ScalaCommand[ShebangOptions] {\n  override def stopAtFirstUnrecognized: Boolean = true\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.MUST\n  override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroups(Run.primaryHelpGroups)\n\n  override def sharedOptions(options: ShebangOptions): Option[SharedOptions] =\n    Run.sharedOptions(options.runOptions)\n\n  override def invokeData: ScalaCliInvokeData =\n    super.invokeData.copy(subCommand = SubCommand.Shebang)\n  override def runCommand(options: ShebangOptions, args: RemainingArgs, logger: Logger): Unit =\n    Run.runCommand(\n      options.runOptions,\n      args.remaining.headOption.toSeq,\n      args.remaining.drop(1),\n      () => None,\n      logger,\n      invokeData\n    )\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/shebang/ShebangOptions.scala",
    "content": "package scala.cli.commands.shebang\n\nimport caseapp.*\n\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\nimport scala.cli.ScalaCli.{baseRunnerName, fullRunnerName, progName}\nimport scala.cli.commands.run.RunOptions\nimport scala.cli.commands.shared.{HasSharedOptions, HelpMessages, SharedOptions}\n\n@HelpMessage(ShebangOptions.helpMessage, \"\", ShebangOptions.detailedHelpMessage)\nfinal case class ShebangOptions(\n  @Recurse\n  runOptions: RunOptions = RunOptions()\n) extends HasSharedOptions {\n  override def shared: SharedOptions = runOptions.shared\n}\n\nobject ShebangOptions {\n  implicit lazy val parser: Parser[ShebangOptions] = Parser.derive\n  implicit lazy val help: Help[ShebangOptions]     = Help.derive\n\n  val cmdName                     = \"shebang\"\n  private val helpHeader          = \"Like `run`, but handier for shebang scripts.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |This command is equivalent to the `run` sub-command, but it changes the way\n       |$fullRunnerName parses its command-line arguments in order to be compatible\n       |with shebang scripts.\n       |\n       |When relying on the `run` sub-command, inputs and $baseRunnerName options can be mixed,\n       |while program args have to be specified after `--`\n       |  ${Console\n        .BOLD}$progName [command] [${baseRunnerName}_options | input]... -- [program_arguments]...${Console\n        .RESET}\n       |\n       |However, for the `shebang` sub-command, only a single input file can be set, while all $baseRunnerName options\n       |have to be set before the input file.\n       |All inputs after the first are treated as program arguments, without the need for `--`\n       |  ${Console\n        .BOLD}$progName shebang [${baseRunnerName}_options]... input [program_arguments]...${Console\n        .RESET}\n       |\n       |Using this, it is possible to conveniently set up Unix shebang scripts. For example:\n       |  ${ScalaCliConsole.GRAY}#!/usr/bin/env -S $progName shebang --scala-version 2.13\n       |  println(\"Hello, world\")${Console.RESET}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/test/Test.scala",
    "content": "package scala.cli.commands.test\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\n\nimport java.nio.file.Path\n\nimport scala.build.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.internal.{Constants, Runner}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\nimport scala.build.options.{BuildOptions, JavaOpt, Platform, Scope}\nimport scala.build.testrunner.{AsmTestRunner, Logger as TestRunnerLogger}\nimport scala.cli.CurrentParams\nimport scala.cli.commands.run.Run\nimport scala.cli.commands.setupide.SetupIde\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}\nimport scala.cli.commands.update.Update\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel, WatchUtil}\nimport scala.cli.config.Keys\nimport scala.cli.packaging.Library.fullClassPathMaybeAsJar\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.util.ConfigDbUtils\n\nobject Test extends ScalaCommand[TestOptions] {\n  override def group: String = HelpCommandGroup.Main.toString\n  override def sharedOptions(options: TestOptions): Option[SharedOptions] = Some(options.shared)\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.SHOULD\n\n  override def helpFormat: HelpFormat =\n    super.helpFormat.withPrimaryGroups(Seq(HelpGroup.Test, HelpGroup.Watch))\n\n  private def gray  = ScalaCliConsole.GRAY\n  private def reset = Console.RESET\n\n  override def buildOptions(opts: TestOptions): Option[BuildOptions] = Some {\n    import opts.*\n    val baseOptions = shared.buildOptions(watchOptions = watch).orExit(opts.shared.logger)\n    baseOptions.copy(\n      javaOptions = baseOptions.javaOptions.copy(\n        javaOpts =\n          baseOptions.javaOptions.javaOpts ++\n            sharedJava.allJavaOpts.map(JavaOpt(_)).map(Positioned.commandLine)\n      ),\n      testOptions = baseOptions.testOptions.copy(\n        frameworks = testFrameworks.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine),\n        testOnly = testOnly.map(_.trim).filter(_.nonEmpty)\n      ),\n      internalDependencies = baseOptions.internalDependencies.copy(\n        addTestRunnerDependencyOpt = Some(true)\n      )\n    )\n  }\n\n  override def runCommand(options: TestOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val initialBuildOptions = buildOptionsOrExit(options)\n    val inputs              = options.shared.inputs(args.remaining).orExit(logger)\n    CurrentParams.workspaceOpt = Some(inputs.workspace)\n    SetupIde.runSafe(\n      options.shared,\n      inputs,\n      logger,\n      initialBuildOptions,\n      Some(name),\n      args.remaining\n    )\n    if (CommandUtils.shouldCheckUpdate)\n      Update.checkUpdateSafe(logger)\n\n    val threads = BuildThreads.create()\n\n    val compilerMaker = options.shared.compilerMaker(threads)\n\n    val cross                 = options.compileCross.cross.getOrElse(false)\n    val configDb              = ConfigDbUtils.configDb.orExit(logger)\n    val actionableDiagnostics =\n      options.shared.logging.verbosityOptions.actions.orElse(\n        configDb.get(Keys.actions).getOrElse(None)\n      )\n\n    /** Runs the tests via [[testOnce]] if build was successful\n      * @param builds\n      *   build results, checked for failures\n      * @param allowExit\n      *   false in watchMode\n      */\n    def maybeTest(builds: Builds, allowExit: Boolean): Unit =\n      if (builds.anyFailed) {\n        System.err.println(\"Compilation failed\")\n        if (allowExit)\n          sys.exit(1)\n      }\n      else {\n        val optionsKeys = builds.map.keys.toVector.map(_.optionsKey).distinct\n        val builds0     = optionsKeys.flatMap { optionsKey =>\n          builds.map.get(CrossKey(optionsKey, Scope.Test))\n        }\n        val buildsLen                = builds0.length\n        val printBeforeAfterMessages =\n          buildsLen > 1 && options.shared.logging.verbosity >= 0\n        val results =\n          for ((s, idx) <- builds0.zipWithIndex) yield {\n            if (printBeforeAfterMessages) {\n              val scalaStr    = s.crossKey.scalaVersion.versionOpt.fold(\"\")(v => s\" for Scala $v\")\n              val platformStr = s.crossKey.platform.fold(\"\")(p => s\", ${p.repr}\")\n              System.err.println(\n                s\"${gray}Running tests$scalaStr$platformStr$reset\"\n              )\n              System.err.println()\n            }\n            val retCodeOrError = testOnce(\n              s,\n              options.requireTests,\n              args.unparsed,\n              logger,\n              allowExecve = allowExit && buildsLen <= 1,\n              asJar = options.shared.asJar\n            )\n            if (printBeforeAfterMessages && idx < buildsLen - 1)\n              System.err.println()\n            retCodeOrError\n          }\n\n        val maybeRetCodes = results.sequence\n          .left.map(CompositeBuildException(_))\n\n        val retCodesOpt =\n          if (allowExit)\n            Some(maybeRetCodes.orExit(logger))\n          else\n            maybeRetCodes.orReport(logger)\n\n        for (retCodes <- retCodesOpt if !retCodes.forall(_ == 0))\n          if (allowExit)\n            sys.exit(retCodes.find(_ != 0).getOrElse(1))\n          else {\n            val red      = Console.RED\n            val lightRed = \"\\u001b[91m\"\n            val reset    = Console.RESET\n            System.err.println(\n              s\"${red}Tests exited with return code $lightRed${retCodes.mkString(\", \")}$red.$reset\"\n            )\n          }\n      }\n\n    if (options.watch.watchMode) {\n      val watcher = Build.watch(\n        inputs,\n        initialBuildOptions,\n        compilerMaker,\n        None,\n        logger,\n        crossBuilds = cross,\n        buildTests = true,\n        partial = None,\n        actionableDiagnostics = actionableDiagnostics,\n        postAction = () => WatchUtil.printWatchMessage()\n      ) { res =>\n        for (builds <- res.orReport(logger))\n          maybeTest(builds, allowExit = false)\n      }\n      try WatchUtil.waitForCtrlC(() => watcher.schedule())\n      finally watcher.dispose()\n    }\n    else {\n      val builds =\n        Build.build(\n          inputs,\n          initialBuildOptions,\n          compilerMaker,\n          None,\n          logger,\n          crossBuilds = cross,\n          buildTests = true,\n          partial = None,\n          actionableDiagnostics = actionableDiagnostics\n        )\n          .orExit(logger)\n      maybeTest(builds, allowExit = true)\n    }\n  }\n\n  private def testOnce(\n    build: Build.Successful,\n    requireTests: Boolean,\n    args: Seq[String],\n    logger: Logger,\n    asJar: Boolean,\n    allowExecve: Boolean\n  ): Either[BuildException, Int] = either {\n\n    val predefinedTestFrameworks = build.options.testOptions.frameworks\n\n    build.options.platform.value match {\n      case Platform.JS =>\n        val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger)\n        val esModule     =\n          build.options.scalaJsOptions.moduleKindStr.exists(m => m == \"es\" || m == \"esmodule\")\n        value {\n          Run.withLinkedJs(\n            Seq(build),\n            None,\n            addTestInitializer = true,\n            linkerConfig,\n            value(build.options.scalaJsOptions.fullOpt),\n            build.options.scalaJsOptions.noOpt.getOrElse(false),\n            logger,\n            esModule\n          ) { js =>\n            Runner.testJs(\n              build.fullClassPath.map(_.toNIO),\n              js.toIO,\n              requireTests,\n              args,\n              predefinedTestFrameworks.map(_.value),\n              logger,\n              build.options.scalaJsOptions.dom.getOrElse(false),\n              esModule\n            )\n          }.flatten\n        }\n      case Platform.Native =>\n        value {\n          Run.withNativeLauncher(\n            Seq(build),\n            \"scala.scalanative.testinterface.TestMain\",\n            logger\n          ) { launcher =>\n            Runner.testNative(\n              build.fullClassPath.map(_.toNIO),\n              launcher.toIO,\n              predefinedTestFrameworks.map(_.value),\n              requireTests,\n              args,\n              logger\n            )\n          }.flatten\n        }\n      case Platform.JVM =>\n        val classPath = build.fullClassPathMaybeAsJar(asJar)\n\n        val predefinedTestFrameworks0 =\n          predefinedTestFrameworks match {\n            case f if f.nonEmpty => f\n            case Nil             =>\n              findTestFramework(classPath.map(_.toNIO), logger).map(Positioned.none).toList\n          }\n        val testOnly = build.options.testOptions.testOnly\n\n        val extraArgs =\n          (if requireTests then Seq(\"--require-tests\") else Nil) ++\n            build.options.internal.verbosity.map(v => s\"--verbosity=$v\") ++\n            predefinedTestFrameworks0.map(_.value).map(fw => s\"--test-framework=$fw\") ++\n            testOnly.map(to => s\"--test-only=$to\").toSeq ++\n            Seq(\"--\") ++ args\n\n        val testRunnerMainClass =\n          if build.artifacts.hasJavaTestRunner\n          then Constants.javaTestRunnerMainClass\n          else Constants.testRunnerMainClass\n\n        Runner.runJvm(\n          build.options.javaHome().value.javaCommand,\n          build.options.javaOptions.javaOpts.toSeq.map(_.value.value),\n          classPath,\n          testRunnerMainClass,\n          extraArgs,\n          logger,\n          allowExecve = allowExecve\n        ).waitFor()\n    }\n  }\n\n  private def findTestFramework(classPath: Seq[Path], logger: Logger): Option[String] = {\n    val classPath0 = classPath.map(_.toString)\n\n    // https://github.com/VirtusLab/scala-cli/issues/426\n    if classPath0.exists(_.contains(\"zio-test\")) && !classPath0.exists(_.contains(\"zio-test-sbt\"))\n    then {\n      val parentInspector =\n        new AsmTestRunner.ParentInspector(classPath, TestRunnerLogger(logger.verbosity))\n      Runner.frameworkNames(classPath, parentInspector, logger) match {\n        case Right(f) => f.headOption\n        case Left(_)  =>\n          logger.message(\n            s\"zio-test found in the class path, zio-test-sbt should be added to run zio tests with $fullRunnerName.\"\n          )\n          None\n      }\n    }\n    else None\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/test/TestOptions.scala",
    "content": "package scala.cli.commands.test\n\nimport caseapp.*\nimport caseapp.core.help.Help\n\nimport scala.cli.commands.shared.*\nimport scala.cli.commands.tags\n\n@HelpMessage(TestOptions.helpMessage, \"\", TestOptions.detailedHelpMessage)\nfinal case class TestOptions(\n  @Recurse\n  shared: SharedOptions = SharedOptions(),\n  @Recurse\n  sharedJava: SharedJavaOptions = SharedJavaOptions(),\n  @Recurse\n  watch: SharedWatchOptions = SharedWatchOptions(),\n  @Recurse\n  compileCross: CrossOptions = CrossOptions(),\n  @Group(HelpGroup.Test.toString)\n  @HelpMessage(\n    \"\"\"Names of the test frameworks' runner classes to use while running tests.\n      |Skips framework lookup and only runs passed frameworks.\"\"\".stripMargin\n  )\n  @ValueDescription(\"class-name\")\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @Name(\"testFramework\")\n  testFrameworks: List[String] = Nil,\n  @Group(HelpGroup.Test.toString)\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Fail if no test suites were run\")\n  requireTests: Boolean = false,\n  @Group(HelpGroup.Test.toString)\n  @Tag(tags.should)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(\"Specify a glob pattern to filter the tests suite to be run.\")\n  testOnly: Option[String] = None\n) extends HasSharedOptions\n\nobject TestOptions {\n  implicit lazy val parser: Parser[TestOptions] = Parser.derive\n  implicit lazy val help: Help[TestOptions]     = Help.derive\n\n  val cmdName                     = \"test\"\n  private val helpHeader          = \"Compile and test Scala code.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |Test sources are compiled separately (after the 'main' sources), and may use different dependencies, compiler options, and other configurations.\n       |A source file is treated as a test source if:\n       |  - the file name ends with `.test.scala`\n       |  - the file comes from a directory that is provided as input, and the relative path from that file to its original directory contains a `test` directory\n       |  - it contains the `//> using target.scope test` directive (Experimental)\n       |\n       |${HelpMessages.commandConfigurations(cmdName)}\n       |\n       |${HelpMessages.acceptedInputs}\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/uninstall/Uninstall.scala",
    "content": "package scala.cli.commands.uninstall\n\nimport caseapp.*\n\nimport scala.build.Logger\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.bloop.BloopExit\nimport scala.cli.commands.uninstallcompletions.{UninstallCompletions, UninstallCompletionsOptions}\nimport scala.cli.commands.update.Update\n\nobject Uninstall extends ScalaCommand[UninstallOptions] {\n\n  override def scalaSpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def runCommand(options: UninstallOptions, args: RemainingArgs, logger: Logger): Unit = {\n    val interactive =\n      options.bloopExit.global.logging.verbosityOptions.interactiveInstance(forceEnable = true)\n\n    val binDirPath =\n      options.binDirPath.getOrElse(\n        scala.build.Directories.default().binRepoDir / baseRunnerName\n      )\n    val destBinPath = binDirPath / options.binaryName\n    val cacheDir    = scala.build.Directories.default().cacheDir\n\n    if (\n      !Update.isScalaCLIInstalledByInstallationScript && (options.binDir.isEmpty || !options.force)\n    ) {\n      logger.error(\n        s\"$fullRunnerName was not installed by the installation script, please use your package manager to uninstall $baseRunnerName.\"\n      )\n      sys.exit(1)\n    }\n    if (!options.force) {\n      val fallbackAction = () => {\n        logger.error(s\"To uninstall $baseRunnerName pass -f or --force\")\n        sys.exit(1)\n      }\n      val msg = s\"Do you want to uninstall $baseRunnerName?\"\n      interactive.confirmOperation(msg).getOrElse(fallbackAction())\n    }\n    if (os.exists(destBinPath)) {\n      // uninstall completions\n      logger.debug(\"Uninstalling completions...\")\n      UninstallCompletions.run(\n        UninstallCompletionsOptions(options.sharedUninstallCompletions, options.bloopExit.global),\n        args\n      )\n      // exit bloop server\n      logger.debug(\"Stopping Bloop server...\")\n      BloopExit.runCommand(options.bloopExit, args, options.global.logging.logger)\n      // remove scala-cli launcher\n      logger.debug(s\"Removing $baseRunnerName binary...\")\n      os.remove.all(binDirPath)\n      // remove scala-cli caches\n      logger.debug(s\"Removing $baseRunnerName cache directory...\")\n      if (!options.skipCache) os.remove.all(cacheDir)\n      logger.message(\"Uninstalled successfully.\")\n    }\n    else {\n      logger.error(s\"Could't find $baseRunnerName binary at $destBinPath.\")\n      sys.exit(1)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/uninstall/UninstallOptions.scala",
    "content": "package scala.cli.commands.uninstall\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.{baseRunnerName, fullRunnerName}\nimport scala.cli.commands.bloop.BloopExitOptions\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup, HelpMessages}\nimport scala.cli.commands.tags\nimport scala.cli.commands.uninstallcompletions.SharedUninstallCompletionsOptions\n\n// format: off\n@HelpMessage(\n  s\"\"\"Uninstalls $fullRunnerName.\n     |Works only when installed with the installation script.\n     |${HelpMessages.installationDocsWebsiteReference}\"\"\".stripMargin)\nfinal case class UninstallOptions(\n  @Recurse\n    bloopExit: BloopExitOptions = BloopExitOptions(),\n  @Recurse\n    sharedUninstallCompletions: SharedUninstallCompletionsOptions = SharedUninstallCompletionsOptions(),\n  @Group(HelpGroup.Uninstall.toString)\n  @Name(\"f\")\n  @HelpMessage(s\"Force $baseRunnerName uninstall\")\n  @Tag(tags.implementation)\n    force: Boolean = false,\n  @Hidden\n  @Group(HelpGroup.Uninstall.toString)\n  @HelpMessage(s\"Don't clear $fullRunnerName cache\")\n  @Tag(tags.implementation)\n    skipCache: Boolean = false,\n  @Hidden\n  @Group(HelpGroup.Uninstall.toString)\n  @HelpMessage(\"Binary name\")\n  @Tag(tags.implementation)\n    binaryName: String = baseRunnerName,\n  @Hidden\n  @Group(HelpGroup.Uninstall.toString)\n  @HelpMessage(\"Binary directory\")\n  @Tag(tags.implementation)\n    binDir: Option[String] = None\n) extends HasGlobalOptions {\n  override def global: GlobalOptions = bloopExit.global\n  // format: on\n  lazy val binDirPath: Option[os.Path] = binDir.map(os.Path(_, os.pwd))\n}\n\nobject UninstallOptions {\n  implicit lazy val parser: Parser[UninstallOptions] = Parser.derive\n  implicit lazy val help: Help[UninstallOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/uninstallcompletions/SharedUninstallCompletionsOptions.scala",
    "content": "package scala.cli.commands.uninstallcompletions\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n// format: off\nfinal case class SharedUninstallCompletionsOptions(\n  @Group(HelpGroup.Uninstall.toString)\n  @HelpMessage(\"Path to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  rcFile: Option[String] = None,\n  @Group(HelpGroup.Uninstall.toString)\n  @Hidden\n  @HelpMessage(\"Custom banner in comment placed in rc file\")\n  @Tag(tags.implementation)\n  banner: String = \"{NAME} completions\",\n  @Group(HelpGroup.Uninstall.toString)\n  @Hidden\n  @HelpMessage(\"Custom completions name\")\n  @Tag(tags.implementation)\n  name: Option[String] = None\n)\n// format: on\n\nobject SharedUninstallCompletionsOptions {\n  implicit lazy val parser: Parser[SharedUninstallCompletionsOptions] = Parser.derive\n  implicit lazy val help: Help[SharedUninstallCompletionsOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/uninstallcompletions/UninstallCompletions.scala",
    "content": "package scala.cli.commands.uninstallcompletions\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\n\nimport java.nio.charset.Charset\n\nimport scala.build.Logger\nimport scala.build.internals.EnvVar\nimport scala.cli.commands.installcompletions.InstallCompletions\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.internal.ProfileFileUpdater\nimport scala.cli.util.ArgHelpers.*\n\nobject UninstallCompletions extends ScalaCommand[UninstallCompletionsOptions] {\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def helpFormat: HelpFormat =\n    super.helpFormat.withPrimaryGroup(HelpGroup.Uninstall)\n\n  override def names = List(\n    List(\"uninstall\", \"completions\"),\n    List(\"uninstall-completions\")\n  )\n  override def runCommand(\n    options: UninstallCompletionsOptions,\n    args: RemainingArgs,\n    logger: Logger\n  ): Unit = {\n    val name = InstallCompletions.getName(options.shared.name)\n\n    val zDotDir = EnvVar.Misc.zDotDir.valueOpt\n      .map(os.Path(_, os.pwd))\n      .getOrElse(os.home)\n    val rcFiles = options.shared.rcFile.map(file => Seq(os.Path(file, os.pwd))).getOrElse(Seq(\n      zDotDir / \".zshrc\",\n      os.home / \".bashrc\",\n      os.home / \".config\" / \"fish\" / \"config.fish\"\n    )).filter(os.exists(_))\n\n    rcFiles.foreach { rcFile =>\n      val banner = options.shared.banner.replace(\"{NAME}\", name)\n\n      val updated = ProfileFileUpdater.removeFromProfileFile(\n        rcFile.toNIO,\n        banner,\n        Charset.defaultCharset()\n      )\n\n      if (options.global.logging.verbosity >= 0)\n        if (updated) {\n          logger.message(s\"Updated $rcFile\")\n          logger.message(s\"$baseRunnerName completions uninstalled successfully\")\n        }\n        else\n          logger.error(\n            s\"Problem occurred while uninstalling $baseRunnerName completions\"\n          )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/uninstallcompletions/UninstallCompletionsOptions.scala",
    "content": "package scala.cli.commands.uninstallcompletions\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpMessages}\n\n// format: off\n@HelpMessage(UninstallCompletionsOptions.helpMessage, \"\", UninstallCompletionsOptions.detailedHelpMessage)\nfinal case class UninstallCompletionsOptions(\n  @Recurse\n    shared: SharedUninstallCompletionsOptions = SharedUninstallCompletionsOptions(),\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n) extends HasGlobalOptions\n// format: on\n\nobject UninstallCompletionsOptions {\n  implicit lazy val parser: Parser[UninstallCompletionsOptions] = Parser.derive\n  implicit lazy val help: Help[UninstallCompletionsOptions]     = Help.derive\n\n  private val helpHeader  = s\"Uninstalls $fullRunnerName completions from your shell.\"\n  val helpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(\"uninstall completions\")}\n       |${HelpMessages.commandDocWebsiteReference(\"completions\")}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandDocWebsiteReference(\"completions\")}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/update/Update.scala",
    "content": "package scala.cli.commands.update\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport coursier.version.Version\n\nimport java.net.{HttpURLConnection, URL, URLConnection}\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.Logger\nimport scala.build.errors.CheckScalaCliVersionError\nimport scala.build.internal.Constants.{ghName, ghOrg, version as scalaCliVersion}\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel}\nimport scala.cli.signing.shared.Secret\nimport scala.cli.util.ArgHelpers.*\nimport scala.util.control.NonFatal\nimport scala.util.{Properties, Try}\n\nobject Update extends ScalaCommand[UpdateOptions] {\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.IMPLEMENTATION\n\n  override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Update)\n\n  private final case class Release(\n    draft: Boolean,\n    prerelease: Boolean,\n    tag_name: String\n  ) {\n    lazy val version: Version =\n      Version(tag_name.stripPrefix(\"v\"))\n    def actualRelease: Boolean =\n      !draft && !prerelease\n  }\n\n  private lazy val releaseListCodec: JsonValueCodec[List[Release]] = JsonCodecMaker.make\n\n  def newestScalaCliVersion(tokenOpt: Option[Secret[String]])\n    : Either[CheckScalaCliVersionError, String] = {\n    // FIXME Do we need paging here?\n    val url     = s\"https://api.github.com/repos/$ghOrg/$ghName/releases\"\n    val headers =\n      Seq(\"Accept\" -> \"application/vnd.github.v3+json\") ++\n        tokenOpt.toSeq.map(tk => \"Authorization\" -> s\"token ${tk.value}\")\n\n    try {\n      val resp = download(url, headers*)\n      readFromArray(resp)(using releaseListCodec).filter(_.actualRelease)\n        .maxByOption(_.version)\n        .map(_.version.repr)\n        .toRight(CheckScalaCliVersionError(s\"No $fullRunnerName versions found in $url\"))\n    }\n    catch {\n      case e: JsonReaderException => Left(CheckScalaCliVersionError(s\"Error reading $url\", e))\n      case e: Throwable           => Left(CheckScalaCliVersionError(\n          s\"Failed to check for the newest Scala CLI version upstream: ${e.getMessage}\",\n          e\n        ))\n    }\n  }\n\n  def installDirPath(options: UpdateOptions): os.Path =\n    options.binDir.map(os.Path(_, os.pwd)).getOrElse(\n      scala.build.Directories.default().binRepoDir / options.binaryName\n    )\n\n  private def updateScalaCli(options: UpdateOptions, newVersion: String, logger: Logger): Unit = {\n    val interactive =\n      options.global.logging.verbosityOptions.interactiveInstance(forceEnable = true)\n    if (!options.force) {\n      val fallbackAction = () => {\n        logger.error(s\"To update $baseRunnerName to $newVersion pass -f or --force\")\n        sys.exit(1)\n      }\n      val msg = s\"Do you want to update $baseRunnerName to version $newVersion?\"\n      interactive.confirmOperation(msg).getOrElse(fallbackAction())\n    }\n\n    val installationScript =\n      downloadFile(\"https://scala-cli.virtuslab.org/get\")\n\n    // format: off\n    val res = os.proc(\n      \"bash\", \"-s\", \"--\",\n      \"--version\", newVersion,\n      \"--force\",\n      \"--binary-name\", options.binaryName,\n      \"--bin-dir\", installDirPath(options),\n    ).call(\n      cwd = os.pwd,\n      stdin = installationScript,\n      stdout = os.Inherit,\n      check = false,\n      mergeErrIntoOut = true\n    )\n    // format: on\n    val output = res.out.trim()\n    if (res.exitCode != 0) {\n      logger.error(s\"Error during updating $baseRunnerName: $output\")\n      sys.exit(1)\n    }\n  }\n\n  private def getCurrentVersion(scalaCliBinPath: os.Path): String = {\n    val res = os.proc(scalaCliBinPath, \"version\", \"--cli-version\").call(cwd = os.pwd, check = false)\n    if (res.exitCode == 0)\n      res.out.trim()\n    else\n      \"0.0.0\"\n  }\n\n  private def update(options: UpdateOptions, currentVersion: String, logger: Logger): Unit = {\n\n    val newestScalaCliVersion0 = newestScalaCliVersion(options.ghToken.map(_.get())) match {\n      case Left(e) =>\n        logger.log(e.message)\n        sys.error(e.message)\n      case Right(v) => v\n    }\n    val isOutdated = CommandUtils.isOutOfDateVersion(newestScalaCliVersion0, currentVersion)\n\n    if (!options.isInternalRun)\n      if (isOutdated)\n        updateScalaCli(options, newestScalaCliVersion0, logger)\n      else println(s\"$fullRunnerName is up-to-date\")\n    else if (isOutdated)\n      println(\n        s\"\"\"Your $fullRunnerName $currentVersion is outdated, please update $fullRunnerName to $newestScalaCliVersion0\n           |Run 'curl -sSLf https://scala-cli.virtuslab.org/get | sh' to update $fullRunnerName.\"\"\".stripMargin\n      )\n  }\n\n  override def runCommand(\n    options: UpdateOptions,\n    remainingArgs: RemainingArgs,\n    logger: Logger\n  ): Unit =\n    checkUpdate(options, logger)\n\n  def checkUpdate(options: UpdateOptions, logger: Logger): Unit = {\n    val scalaCliBinPath = installDirPath(options) / options.binaryName\n\n    val programName = argvOpt.flatMap(_.headOption).getOrElse {\n      sys.error(\"update called in a non-standard way :|\")\n    }\n\n    lazy val isScalaCliInPath = // if binDir is non empty, we not except scala-cli in PATH, it is useful in tests\n      CommandUtils.getAbsolutePathToScalaCli(programName).contains(\n        installDirPath(options).toString()\n      ) || options.binDir.isDefined\n\n    if (!os.exists(scalaCliBinPath) || !isScalaCliInPath) {\n      if (!options.isInternalRun) {\n        logger.error(\n          s\"$fullRunnerName was not installed by the installation script, please use your package manager to update $baseRunnerName.\"\n        )\n        sys.exit(1)\n      }\n    }\n    else if (Properties.isWin) {\n      if (!options.isInternalRun) {\n        logger.error(s\"$fullRunnerName update is not supported on Windows.\")\n        sys.exit(1)\n      }\n    }\n    else if (options.binaryName == baseRunnerName)\n      update(options, scalaCliVersion, logger)\n    else\n      update(options, getCurrentVersion(scalaCliBinPath), logger)\n  }\n\n  def checkUpdateSafe(logger: Logger): Unit =\n    try\n      // log about update only if scala-cli was installed from installation script\n      if (isScalaCLIInstalledByInstallationScript)\n        checkUpdate(UpdateOptions(isInternalRun = true), logger)\n    catch {\n      case NonFatal(ex) =>\n        logger.debug(s\"Ignoring error during checking update: $ex\")\n    }\n\n  def isScalaCLIInstalledByInstallationScript: Boolean = {\n    val classesDir =\n      getClass.getProtectionDomain.getCodeSource.getLocation.toURI.toString\n    val binRepoDir = build.Directories.default().binRepoDir.toString()\n\n    classesDir.contains(binRepoDir)\n  }\n\n  // from https://github.com/coursier/coursier/blob/7b7c2c312aea26e850f0cd2cf15e688d0777f819/modules/cache/jvm/src/main/scala/coursier/cache/CacheUrl.scala#L489-L497\n  private def closeConn(conn: URLConnection): Unit = {\n    Try(conn.getInputStream).toOption.filter(_ != null).foreach(_.close())\n    conn match {\n      case conn0: HttpURLConnection =>\n        Try(conn0.getErrorStream).toOption.filter(_ != null).foreach(_.close())\n        conn0.disconnect()\n      case _ =>\n    }\n  }\n\n  private def download(\n    url: String,\n    headers: (String, String)*\n  ): Array[Byte] = {\n    var conn: URLConnection = null\n    val url0                = new URL(url)\n    try {\n      conn = url0.openConnection()\n      for ((k, v) <- headers)\n        conn.setRequestProperty(k, v)\n      conn.getInputStream.readAllBytes()\n    }\n    catch {\n      case NonFatal(ex) =>\n        throw new RuntimeException(s\"Error downloading $url: $ex\", ex)\n    }\n    finally\n      if (conn != null)\n        closeConn(conn)\n  }\n\n  private def downloadFile(url: String): String = {\n    val data = download(url)\n    new String(data, StandardCharsets.UTF_8)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/update/UpdateOptions.scala",
    "content": "package scala.cli.commands.update\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.{baseRunnerName, fullRunnerName}\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup, HelpMessages}\nimport scala.cli.commands.tags\nimport scala.cli.signing.shared.PasswordOption\nimport scala.cli.signing.util.ArgParsers.*\n\n// format: off\n@HelpMessage(UpdateOptions.helpMessage, \"\", UpdateOptions.detailedHelpMessage)\nfinal case class UpdateOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Hidden\n  @Group(HelpGroup.Update.toString)\n  @HelpMessage(\"Binary name\")\n  @Tag(tags.implementation)\n    binaryName: String = baseRunnerName,\n  @Hidden\n  @Group(HelpGroup.Update.toString)\n  @HelpMessage(\"Binary directory\")\n  @Tag(tags.implementation)\n    binDir: Option[String] = None,\n  @Name(\"f\")\n  @Group(HelpGroup.Update.toString)\n  @HelpMessage(s\"Force update $fullRunnerName if it is outdated\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n    force: Boolean = false,\n  @Hidden\n  @Tag(tags.implementation)\n    isInternalRun: Boolean = false,\n  @Hidden\n  @HelpMessage(HelpMessages.passwordOption)\n  @Tag(tags.implementation)\n    ghToken: Option[PasswordOption] = None\n) extends HasGlobalOptions\n// format: on\n\nobject UpdateOptions {\n  implicit lazy val parser: Parser[UpdateOptions] = Parser.derive\n  implicit lazy val help: Help[UpdateOptions]     = Help.derive\n\n  private val helpHeader =\n    s\"\"\"Updates $fullRunnerName.\n       |Works only when installed with the installation script.\n       |If $fullRunnerName was installed with an external tool, refer to its update methods.\"\"\".stripMargin\n  val helpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.commandFullHelpReference(\"update\")}\n       |${HelpMessages.installationDocsWebsiteReference}\"\"\".stripMargin\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader\n       |\n       |${HelpMessages.installationDocsWebsiteReference}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/BuildCommandHelpers.scala",
    "content": "package scala.cli.commands.util\n\nimport scala.build.errors.BuildException\nimport scala.build.{Build, Builds, CrossBuildParams, Logger, Os}\nimport scala.cli.commands.ScalaCommand\nimport scala.cli.commands.shared.SharedOptions\nimport scala.cli.commands.util.ScalacOptionsUtil.*\n\ntrait BuildCommandHelpers { self: ScalaCommand[?] =>\n  extension (b: Seq[Build.Successful]) {\n    def groupedByCrossParams: Map[CrossBuildParams, Seq[Build.Successful]] =\n      b.groupBy(bb => CrossBuildParams(bb.scalaParams, bb.options))\n  }\n  extension (successfulBuild: Build.Successful) {\n    def retainedMainClass(\n      logger: Logger,\n      mainClasses: Seq[String] = successfulBuild.foundMainClasses()\n    ): Either[BuildException, String] =\n      successfulBuild.retainedMainClass(\n        mainClasses,\n        self.argvOpt.map(_.mkString(\" \")).getOrElse(actualFullCommand),\n        logger\n      )\n  }\n\n  extension (builds: Builds) {\n    def anyBuildCancelled: Boolean = builds.all.exists {\n      case _: Build.Cancelled => true\n      case _                  => false\n    }\n\n    def anyBuildFailed: Boolean = builds.all.exists {\n      case _: Build.Failed => true\n      case _               => false\n    }\n  }\n}\n\nobject BuildCommandHelpers {\n  extension (successfulBuild: Build.Successful) {\n\n    /** -O -d defaults to --compile-output; if both are defined, --compile-output takes precedence\n      */\n    def copyOutput(sharedOptions: SharedOptions): Unit =\n      sharedOptions.compilationOutput.filter(_.nonEmpty)\n        .orElse(sharedOptions.scalacOptions.getScalacOption(\"-d\"))\n        .filter(_.nonEmpty)\n        .map(os.Path(_, Os.pwd)).foreach { output =>\n          os.copy(\n            successfulBuild.output,\n            output,\n            createFolders = true,\n            mergeFolders = true,\n            replaceExisting = true\n          )\n        }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/CommandHelpers.scala",
    "content": "package scala.cli.commands.util\n\nimport scala.build.Logger\nimport scala.build.errors.BuildException\n\ntrait CommandHelpers {\n\n  implicit class EitherBuildExceptionOps[E <: BuildException, T](private val either: Either[E, T]) {\n    def orReport(logger: Logger): Option[T] =\n      either match {\n        case Left(ex) =>\n          logger.log(ex)\n          None\n        case Right(t) => Some(t)\n      }\n    def orExit(logger: Logger): T =\n      either match {\n        case Left(ex) => logger.exit(ex)\n        case Right(t) => t\n      }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/HelpUtils.scala",
    "content": "package scala.cli.commands.util\n\nimport caseapp.core.help.{Help, HelpFormat}\n\nobject HelpUtils {\n  extension (help: Help[?]) {\n    private def abstractHelp(\n      format: HelpFormat,\n      showHidden: Boolean\n    )(f: (StringBuilder, HelpFormat, Boolean) => Unit): String = {\n      val b = new StringBuilder\n      f(b, format, showHidden)\n      b.result()\n    }\n\n    def optionsHelp(format: HelpFormat, showHidden: Boolean = false): String =\n      abstractHelp(format, showHidden)(help.printOptions)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/JvmUtils.scala",
    "content": "package scala.cli.commands\npackage util\n\nimport java.io.File\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.{BuildException, JvmDownloadError, UnrecognizedDebugModeError}\nimport scala.build.internal.CsLoggerUtil.*\nimport scala.build.internal.OsLibc\nimport scala.build.options.BuildOptions.JavaHomeInfo\nimport scala.build.options.{JavaOpt, JavaOptions, ShadowingSeq}\nimport scala.build.{Os, Position, Positioned, options as bo}\nimport scala.cli.commands.shared.{CoursierOptions, SharedJvmOptions, SharedOptions}\nimport scala.concurrent.ExecutionContextExecutorService\nimport scala.util.{Failure, Properties, Success, Try}\n\nobject JvmUtils {\n  def javaOptions(opts: SharedJvmOptions): Either[BuildException, JavaOptions] = either {\n    import opts._\n\n    val (javacFilePlugins, javacPluginDeps) =\n      javacPlugin\n        .filter(_.trim.nonEmpty)\n        .partition { input =>\n          input.contains(File.separator) ||\n          (Properties.isWin && input.contains(\"/\")) ||\n          input.count(_ == ':') < 2\n        }\n\n    val javaOptsSeq = {\n      val isDebug =\n        opts.sharedDebug.debug ||\n        opts.sharedDebug.debugMode.nonEmpty ||\n        opts.sharedDebug.debugPort.nonEmpty\n      if (isDebug) {\n        val server = value {\n          opts.sharedDebug.debugMode match {\n            case Some(\"attach\") | Some(\"a\") | None => Right(\"y\")\n            case Some(\"listen\") | Some(\"l\")        => Right(\"n\")\n            case Some(m)                           => Left(new UnrecognizedDebugModeError(m))\n          }\n        }\n        val port = opts.sharedDebug.debugPort.getOrElse(\"5005\")\n        Seq(Positioned.none(\n          JavaOpt(s\"-agentlib:jdwp=transport=dt_socket,server=$server,suspend=y,address=$port\")\n        ))\n      }\n      else\n        Seq.empty\n    }\n\n    JavaOptions(\n      javaHomeOpt = javaHome.filter(_.nonEmpty).map(v =>\n        Positioned(Seq(Position.CommandLine(\"--java-home\")), os.Path(v, Os.pwd))\n      ),\n      jvmIdOpt = jvm.filter(_.nonEmpty).map(Positioned.commandLine),\n      jvmIndexOpt = jvmIndex.filter(_.nonEmpty),\n      jvmIndexOs = jvmIndexOs.map(_.trim).filter(_.nonEmpty),\n      jvmIndexArch = jvmIndexArch.map(_.trim).filter(_.nonEmpty),\n      javaOpts = ShadowingSeq.from(javaOptsSeq),\n      javacPluginDependencies = SharedOptions.parseDependencies(\n        javacPluginDeps.map(Positioned.none(_)),\n        ignoreErrors = false\n      ),\n      javacPlugins = javacFilePlugins.map(s => Positioned.none(os.Path(s, Os.pwd))),\n      javacOptions = javacOption.map(Positioned.commandLine)\n    )\n  }\n\n  def downloadJvm(jvmId: String, options: bo.BuildOptions): Either[BuildException, JavaHomeInfo] = {\n    implicit val ec: ExecutionContextExecutorService = options.finalCache.ec\n    val javaHomeManager                              = options.javaHomeManager\n      .withMessage(s\"Downloading JVM $jvmId\")\n    javaHomeManager.cache\n      .flatMap(_.archiveCache.cache.loggerOpt)\n      .getOrElse(_root_.coursier.cache.CacheLogger.nop)\n\n    val javaHomeOrError = Try(javaHomeManager.get(jvmId).unsafeRun()) match {\n      case Success(path) => Right(path)\n      case Failure(e)    => Left(JvmDownloadError(jvmId, e))\n    }\n\n    for {\n      javaHome <- javaHomeOrError\n    } yield {\n      val javaHomePath           = os.Path(javaHome)\n      val (javaVersion, javaCmd) = OsLibc.javaHomeVersion(javaHomePath)\n      JavaHomeInfo(javaHomePath, javaCmd, javaVersion)\n    }\n  }\n\n  def getJavaCmdVersionOrHigher(\n    javaVersion: Int,\n    options: bo.BuildOptions\n  ): Either[BuildException, JavaHomeInfo] = {\n    val javaHomeCmdOpt = for {\n      javaHome <- options.javaHomeLocationOpt()\n      (javaHomeVersion, javaHomeCmd) = OsLibc.javaHomeVersion(javaHome.value)\n      if javaHomeVersion >= javaVersion\n    } yield JavaHomeInfo(javaHome.value, javaHomeCmd, javaHomeVersion)\n\n    javaHomeCmdOpt match {\n      case Some(cmd) => Right(cmd)\n      case None      => downloadJvm(javaVersion.toString, options)\n    }\n  }\n\n  def getJavaCmdVersionOrHigher(\n    javaVersion: Int,\n    jvmOpts: SharedJvmOptions,\n    coursierOpts: CoursierOptions\n  ): Either[BuildException, JavaHomeInfo] = {\n    val sharedOpts = SharedOptions(jvm = jvmOpts, coursier = coursierOpts)\n\n    for {\n      options <- sharedOpts.buildOptions()\n      javaCmd <- getJavaCmdVersionOrHigher(javaVersion, options)\n    } yield javaCmd\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/RunHadoop.scala",
    "content": "package scala.cli.commands.util\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.internal.Runner\nimport scala.build.{Build, Logger}\nimport scala.cli.commands.package0.Package as PackageCmd\nimport scala.cli.commands.packaging.Spark\n\nobject RunHadoop {\n\n  def run(\n    builds: Seq[Build.Successful],\n    mainClass: String,\n    args: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean,\n    showCommand: Boolean,\n    scratchDirOpt: Option[os.Path]\n  ): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {\n    // FIXME Get Spark.hadoopModules via provided settings?\n    val providedModules = Spark.hadoopModules\n    scratchDirOpt.foreach(os.makeDir.all(_))\n    val assembly = os.temp(\n      dir = scratchDirOpt.orNull,\n      prefix = \"hadoop-job\",\n      suffix = \".jar\",\n      deleteOnExit = scratchDirOpt.isEmpty\n    )\n    value {\n      PackageCmd.assembly(\n        builds,\n        assembly,\n        // \"hadoop jar\" doesn't accept a main class as second argument if the jar as first argument has a main class in its manifest…\n        None,\n        providedModules,\n        withPreamble = false,\n        () => Right(()),\n        logger\n      )\n    }\n\n    val javaOpts = builds.head.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n    val extraEnv =\n      if javaOpts.isEmpty then Map[String, String]()\n      else\n        Map(\n          \"HADOOP_CLIENT_OPTS\" -> javaOpts.mkString(\" \") // no escaping…\n        )\n    val hadoopJarCommand = Seq(\"hadoop\", \"jar\")\n    val finalCommand     =\n      hadoopJarCommand ++ Seq(assembly.toString, mainClass) ++ args\n    if showCommand then Left(Runner.envCommand(extraEnv) ++ finalCommand)\n    else {\n      val proc =\n        if allowExecve then Runner.maybeExec(\"hadoop\", finalCommand, logger, extraEnv = extraEnv)\n        else Runner.run(finalCommand, logger, extraEnv = extraEnv)\n      Right((\n        proc,\n        if scratchDirOpt.isEmpty then Some(() => os.remove(assembly, checkExists = true))\n        else None\n      ))\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/RunSpark.scala",
    "content": "package scala.cli.commands.util\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.internal.Runner\nimport scala.build.internals.EnvVar\nimport scala.build.{Build, Logger}\nimport scala.cli.commands.package0.Package as PackageCmd\nimport scala.cli.commands.packaging.Spark\nimport scala.cli.packaging.Library\nimport scala.util.Properties\n\nobject RunSpark {\n\n  def run(\n    builds: Seq[Build.Successful],\n    mainClass: String,\n    args: Seq[String],\n    submitArgs: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean,\n    showCommand: Boolean,\n    scratchDirOpt: Option[os.Path]\n  ): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {\n\n    // FIXME Get Spark.sparkModules via provided settings?\n    val providedModules = Spark.sparkModules\n    val providedFiles   =\n      value(PackageCmd.providedFiles(builds, providedModules, logger)).toSet\n    val depCp        = builds.flatMap(_.dependencyClassPath).distinct.filterNot(providedFiles)\n    val javaHomeInfo = builds.head.options.javaHome().value\n    val javaOpts     = builds.head.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n    val ext          = if Properties.isWin then \".cmd\" else \"\"\n    val submitCommand: String =\n      EnvVar.Spark.sparkHome.valueOpt\n        .map(os.Path(_, os.pwd))\n        .map(_ / \"bin\" / s\"spark-submit$ext\")\n        .filter(os.exists(_))\n        .map(_.toString)\n        .getOrElse(s\"spark-submit$ext\")\n    val jarsArgs =\n      if (depCp.isEmpty) Nil\n      else Seq(\"--jars\", depCp.mkString(\",\"))\n\n    scratchDirOpt.foreach(os.makeDir.all(_))\n    val library = Library.libraryJar(builds)\n\n    val finalCommand =\n      Seq(submitCommand, \"--class\", mainClass) ++\n        jarsArgs ++\n        javaOpts.flatMap(opt => Seq(\"--driver-java-options\", opt)) ++\n        submitArgs ++\n        Seq(library.toString) ++\n        args\n    val envUpdates = javaHomeInfo.envUpdates(sys.env)\n    if showCommand then Left(Runner.envCommand(envUpdates) ++ finalCommand)\n    else {\n      val proc =\n        if allowExecve then\n          Runner.maybeExec(\"spark-submit\", finalCommand, logger, extraEnv = envUpdates)\n        else Runner.run(finalCommand, logger, extraEnv = envUpdates)\n      Right((\n        proc,\n        if scratchDirOpt.isEmpty then\n          Some(() => os.remove(library, checkExists = true))\n        else None\n      ))\n    }\n  }\n\n  def runStandalone(\n    builds: Seq[Build.Successful],\n    mainClass: String,\n    args: Seq[String],\n    submitArgs: Seq[String],\n    logger: Logger,\n    allowExecve: Boolean,\n    showCommand: Boolean,\n    scratchDirOpt: Option[os.Path]\n  ): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {\n\n    // FIXME Get Spark.sparkModules via provided settings?\n    val providedModules              = Spark.sparkModules\n    val sparkClassPath: Seq[os.Path] = value(PackageCmd.providedFiles(\n      builds,\n      providedModules,\n      logger\n    ))\n\n    scratchDirOpt.foreach(os.makeDir.all(_))\n    val library = Library.libraryJar(builds)\n\n    val finalMainClass = \"org.apache.spark.deploy.SparkSubmit\"\n    val depCp = builds.flatMap(_.dependencyClassPath).distinct.filterNot(sparkClassPath.toSet)\n    val javaHomeInfo = builds.head.options.javaHome().value\n    val javaOpts     = builds.head.options.javaOptions.javaOpts.toSeq.map(_.value.value)\n    val jarsArgs     = if depCp.isEmpty then Nil else Seq(\"--jars\", depCp.mkString(\",\"))\n    val finalArgs    =\n      Seq(\"--class\", mainClass) ++\n        jarsArgs ++\n        javaOpts.flatMap(opt => Seq(\"--driver-java-options\", opt)) ++\n        submitArgs ++\n        Seq(library.toString) ++\n        args\n    val envUpdates = javaHomeInfo.envUpdates(sys.env)\n    if showCommand then\n      Left {\n        Runner.jvmCommand(\n          javaHomeInfo.javaCommand,\n          javaOpts,\n          sparkClassPath,\n          finalMainClass,\n          finalArgs,\n          extraEnv = envUpdates,\n          useManifest = builds.head.options.notForBloopOptions.runWithManifest,\n          scratchDirOpt = scratchDirOpt\n        )\n      }\n    else {\n      val proc = Runner.runJvm(\n        javaHomeInfo.javaCommand,\n        javaOpts,\n        sparkClassPath,\n        finalMainClass,\n        finalArgs,\n        logger,\n        allowExecve = allowExecve,\n        extraEnv = envUpdates,\n        useManifest = builds.head.options.notForBloopOptions.runWithManifest,\n        scratchDirOpt = scratchDirOpt\n      )\n      Right((\n        proc,\n        if scratchDirOpt.isEmpty then Some(() => os.remove(library, checkExists = true)) else None\n      ))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/ScalaCliSttpBackend.scala",
    "content": "package scala.cli.commands.util\n\nimport sttp.capabilities.Effect\nimport sttp.client3.*\nimport sttp.monad.MonadError\n\nimport scala.build.Logger\nimport scala.concurrent.duration\nimport scala.concurrent.duration.FiniteDuration\nimport scala.util.Try\n\nclass ScalaCliSttpBackend(\n  underlying: SttpBackend[Identity, Any],\n  logger: Logger\n) extends SttpBackend[Identity, Any] {\n\n  override def send[T, R >: Any & Effect[Identity]](request: Request[T, R]): Response[T] = {\n    logger.debug(s\"HTTP ${request.method} ${request.uri}\")\n    if (logger.verbosity >= 3)\n      logger.debug(s\"request: '${request.show()}'\")\n    val resp = underlying.send[T, R](request)\n    logger.debug(s\"HTTP ${request.method} ${request.uri}: HTTP ${resp.code} ${resp.statusText}\")\n    if (logger.verbosity >= 3) {\n      val logResp = request.response match {\n        case ResponseAsByteArray =>\n          resp.copy(\n            body = Try(new String(resp.body.asInstanceOf[Array[Byte]]))\n          )\n        case _ =>\n          resp\n      }\n      logger.debug(s\"response: '${logResp.show()}'\")\n    }\n    resp\n  }\n  override def close(): Unit =\n    underlying.close()\n  override def responseMonad: MonadError[Identity] =\n    underlying.responseMonad\n}\n\nobject ScalaCliSttpBackend {\n  def httpURLConnection(logger: Logger, timeoutSeconds: Option[Int] = None): ScalaCliSttpBackend =\n    new ScalaCliSttpBackend(\n      HttpURLConnectionBackend(\n        options = timeoutSeconds\n          .fold(SttpBackendOptions.Default)(t =>\n            SttpBackendOptions.connectionTimeout(FiniteDuration(t, duration.SECONDS))\n          )\n      ),\n      logger\n    )\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/util/ScalacOptionsUtil.scala",
    "content": "package scala.cli.commands.util\nimport scala.build.options.ScalacOpt.{filterScalacOptionKeys, noDashPrefixes}\nimport scala.build.options.{ScalacOpt, ShadowingSeq}\nimport scala.cli.commands.shared.{ScalacExtraOptions, ScalacOptions}\n\nobject ScalacOptionsUtil {\n  extension (opts: List[String]) {\n\n    def withScalacExtraOptions(scalacExtra: ScalacExtraOptions): List[String] = {\n      def maybeScalacExtraOption(\n        get: ScalacExtraOptions => Boolean,\n        scalacName: String\n      ): Option[String] =\n        if get(scalacExtra) && !opts.contains(scalacName) then Some(scalacName) else None\n      val scalacHelp    = maybeScalacExtraOption(_.scalacHelp, \"-help\")\n      val scalacVerbose = maybeScalacExtraOption(_.scalacVerbose, \"-verbose\")\n      opts ++ scalacHelp ++ scalacVerbose\n    }\n    def toScalacOptShadowingSeq: ShadowingSeq[ScalacOpt] =\n      ShadowingSeq.from(opts.filter(_.nonEmpty).map(ScalacOpt(_)))\n\n    def getScalacPrefixOption(prefixKey: String): Option[String] =\n      opts.find(_.startsWith(s\"$prefixKey:\")).map(_.stripPrefix(s\"$prefixKey:\"))\n\n    def getScalacOption(key: String): Option[String] = opts.toScalacOptShadowingSeq.getOption(key)\n\n  }\n\n  extension (opts: ShadowingSeq[ScalacOpt]) {\n    def filterNonRedirected: ShadowingSeq[ScalacOpt] =\n      opts.filterScalacOptionKeys(k =>\n        !ScalacOptions.ScalaCliRedirectedOptions.contains(k.noDashPrefixes)\n      )\n    def filterNonDeprecated: ShadowingSeq[ScalacOpt] =\n      opts.filterScalacOptionKeys(k =>\n        !ScalacOptions.ScalacDeprecatedOptions.contains(k.noDashPrefixes)\n      )\n    def getOption(key: String): Option[String] =\n      opts.get(ScalacOpt(key)).headOption.map(_.value)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/version/Version.scala",
    "content": "package scala.cli.commands.version\n\nimport caseapp.*\nimport caseapp.core.help.HelpFormat\n\nimport scala.build.Logger\nimport scala.build.internal.Constants\nimport scala.cli.ScalaCli\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup}\nimport scala.cli.commands.update.Update\nimport scala.cli.commands.{CommandUtils, ScalaCommand, SpecificationLevel}\nimport scala.cli.util.ArgHelpers.*\n\nobject Version extends ScalaCommand[VersionOptions] {\n  override def group: String = HelpCommandGroup.Miscellaneous.toString\n\n  override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.SHOULD\n  override def helpFormat: HelpFormat                      =\n    super.helpFormat\n      .withHiddenGroup(HelpGroup.Logging)\n      .withHiddenGroupWhenShowHidden(HelpGroup.Logging)\n      .withPrimaryGroup(HelpGroup.Version)\n\n  override def runCommand(options: VersionOptions, args: RemainingArgs, logger: Logger): Unit = {\n    lazy val maybeNewerScalaCliVersion: Option[String] =\n      Update.newestScalaCliVersion(options.ghToken.map(_.get())) match {\n        case Left(e) =>\n          logger.debug(e.message)\n          None\n        case Right(newestScalaCliVersion) =>\n          if CommandUtils.isOutOfDateVersion(newestScalaCliVersion, Constants.version) then\n            Some(newestScalaCliVersion)\n          else None\n      }\n    if options.cliVersion then println(Constants.version)\n    else if options.scalaVersion then println(defaultScalaVersion)\n    else {\n      println(versionInfo)\n      val skipCliUpdates = ScalaCli.launcherOptions.scalaRunner.skipCliUpdates.getOrElse(false)\n      if !options.offline && !skipCliUpdates then\n        maybeNewerScalaCliVersion.foreach { v =>\n          logger.message(\n            s\"\"\"Your $fullRunnerName version is outdated. The newest version is $v\n               |It is recommended that you update $fullRunnerName through the same tool or method you used for its initial installation for avoiding the creation of outdated duplicates.\"\"\".stripMargin\n          )\n        }\n    }\n  }\n\n  private def versionInfo: String =\n    val version            = Constants.version\n    val detailedVersionOpt = Constants.detailedVersion.filter(_ != version).fold(\"\")(\" (\" + _ + \")\")\n    s\"\"\"$fullRunnerName version: $version$detailedVersionOpt\n       |Scala version (default): $defaultScalaVersion\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/commands/version/VersionOptions.scala",
    "content": "package scala.cli.commands.version\n\nimport caseapp.*\n\nimport scala.cli.ScalaCli.fullRunnerName\nimport scala.cli.commands.shared.{GlobalOptions, HasGlobalOptions, HelpGroup, HelpMessages}\nimport scala.cli.commands.tags\nimport scala.cli.signing.shared.PasswordOption\nimport scala.cli.signing.util.ArgParsers.*\n\n// format: off\n@HelpMessage(VersionOptions.helpMessage, \"\", VersionOptions.detailedHelpMessage)\nfinal case class VersionOptions(\n  @Recurse\n    global: GlobalOptions = GlobalOptions(),\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @Group(HelpGroup.Version.toString)\n  @HelpMessage(s\"Show plain $fullRunnerName version only\")\n  @Name(\"cli\")\n    cliVersion: Boolean = false,\n  @Group(HelpGroup.Version.toString)\n  @HelpMessage(\"Show plain Scala version only\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @Name(\"scala\")\n    scalaVersion: Boolean = false,\n  @Hidden\n  @HelpMessage(HelpMessages.passwordOption)\n  @Tag(tags.implementation)\n    ghToken: Option[PasswordOption] = None,\n  @Group(HelpGroup.Version.toString)\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  @HelpMessage(s\"Don't check for the newest available $fullRunnerName version upstream\")\n    offline: Boolean = false\n) extends HasGlobalOptions\n// format: on\n\nobject VersionOptions {\n  implicit lazy val parser: Parser[VersionOptions] = Parser.derive\n  implicit lazy val help: Help[VersionOptions]     = Help.derive\n\n  val cmdName            = \"version\"\n  private val helpHeader =\n    s\"Prints the version of the $fullRunnerName and the default version of Scala.\"\n  val helpMessage: String         = HelpMessages.shortHelpMessage(cmdName, helpHeader)\n  val detailedHelpMessage: String =\n    s\"\"\"$helpHeader (which can be overridden in the project)\n       |If network connection is available, this sub-command also checks if the installed $fullRunnerName is up-to-date.\n       |\n       |The version of the $fullRunnerName is the version of the command-line tool that runs Scala programs, which\n       |is distinct from the Scala version of the compiler. We recommend to specify the version of the Scala compiler\n       |for a project in its sources (via a using directive). Otherwise, $fullRunnerName falls back to the default\n       |Scala version defined by the runner.\n       |\n       |${HelpMessages.commandDocWebsiteReference(cmdName)}\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/FailedToSignFileError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class FailedToSignFileError(val path: Either[String, os.Path], val error: String)\n    extends BuildException(s\"Failed to sign ${path.fold(identity, _.toString)}: $error\")\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/FoundVirtualInputsError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\nimport scala.build.input.Virtual\n\nfinal class FoundVirtualInputsError(\n  val virtualInputs: Seq[Virtual]\n) extends BuildException(\n      s\"Found virtual inputs: ${virtualInputs.map(_.source).mkString(\", \")}\"\n    ) {\n  assert(virtualInputs.nonEmpty)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/GitHubApiError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class GitHubApiError(msg: String) extends BuildException(msg)\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/GraalVMNativeImageError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class GraalVMNativeImageError()\n    extends BuildException(s\"Error building native image with GraalVM\")\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/InvalidSonatypePublishCredentials.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class InvalidSonatypePublishCredentials(usernameIsAscii: Boolean, passwordIsAscii: Boolean)\n    extends BuildException(\n      if (usernameIsAscii && passwordIsAscii)\n        \"Username or password to the publish repository are incorrect\"\n      else\n        s\"Your Sonatype ${InvalidSonatypePublishCredentials.isUsernameOrPassword(\n            usernameIsAscii,\n            passwordIsAscii\n          )} unsupported characters\"\n    )\n\nobject InvalidSonatypePublishCredentials {\n  def isUsernameOrPassword(usernameIsAscii: Boolean, passwordIsAscii: Boolean): String =\n    if (!usernameIsAscii && !passwordIsAscii)\n      \"password and username contain\"\n    else if (!usernameIsAscii)\n      \"username contains\"\n    else\n      \"password contains\"\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/MalformedChecksumsError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class MalformedChecksumsError(input: Seq[String], errors: ::[String])\n    extends BuildException(\n      s\"Malformed checksums: ${errors.mkString(\", \")} for inputs: ${input.mkString(\", \")}\"\n    )\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/MalformedOptionError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class MalformedOptionError(\n  val optionName: String,\n  val optionValue: String,\n  val expected: String\n) extends BuildException(\n      {\n        val q = \"\\\"\"\n        s\"Malformed option $optionName: got $q$optionValue$q, expected $q$expected$q\"\n      }\n    )\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/MissingConfigEntryError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class MissingConfigEntryError(key: String)\n    extends BuildException(s\"Missing config entry $key\")\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/MissingPublishOptionError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class MissingPublishOptionError(\n  val name: String,\n  val optionName: String,\n  val directiveName: String,\n  val configKeys: Seq[String] = Nil,\n  val extraMessage: String = \"\"\n) extends BuildException(\n      {\n        val directivePart =\n          if (directiveName.isEmpty) \"\"\n          else\n            s\" or with a '//> using $directiveName' directive\"\n        val configPart =\n          if (configKeys.isEmpty) \"\"\n          else\n            s\" or by setting ${configKeys.mkString(\", \")} in the configuration\"\n        val extraPart =\n          if (extraMessage.isEmpty) \"\" else s\", ${extraMessage.dropWhile(_.isWhitespace)}\"\n\n        s\"Missing $name for publishing, specify one with $optionName\" +\n          directivePart +\n          configPart +\n          extraPart\n      }\n    )\n\nobject MissingPublishOptionError {\n  def versionError =\n    new MissingPublishOptionError(\"version\", \"--project-version\", \"publish.version\")\n  def repositoryError =\n    new MissingPublishOptionError(\"repository\", \"--publish-repository\", \"publish.repository\")\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/PgpError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class PgpError(message: String) extends BuildException(message)\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/ScalaJsLinkingError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class ScalaJsLinkingError(\n  val expected: os.RelPath,\n  val foundFiles: Seq[os.RelPath]\n) extends BuildException(\n      s\"Error: $expected not found after Scala.js linking \" +\n        (if (foundFiles.isEmpty) \"(no files found)\" else s\"(found ${foundFiles.mkString(\", \")})\")\n    )\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/ScaladocGenerationFailedError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class ScaladocGenerationFailedError(val retCode: Int)\n    extends BuildException(s\"Scaladoc generation failed (exit code: $retCode)\") {\n  assert(retCode != 0)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/UploadError.scala",
    "content": "package scala.cli.errors\n\nimport coursier.publish.Content\nimport coursier.publish.fileset.Path\nimport coursier.publish.upload.Upload\n\nimport scala.build.errors.BuildException\n\nfinal class UploadError(errors: ::[(Path, Content, Upload.Error)]) extends BuildException(\n      s\"Error uploading ${errors.length} file(s):\" +\n        errors\n          .map {\n            case (path, _, err) =>\n              System.lineSeparator() + s\"  ${path.repr}: ${err.getMessage}\"\n          }\n          .mkString\n    )\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/errors/WrongSonatypeServerError.scala",
    "content": "package scala.cli.errors\n\nimport scala.build.errors.BuildException\n\nfinal class WrongSonatypeServerError(legacyChosen: Boolean)\n    extends BuildException(\n      s\"Wrong Sonatype server, try ${if legacyChosen then \"'central-s01'\" else \"'central'\"}\"\n    )\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/JsonProject.scala",
    "content": "package scala.cli.exportCmd\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.{JsonValueCodec, WriterConfig, writeToStream}\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\n\nimport java.io.PrintStream\n\nimport scala.build.info.{BuildInfo, ExportDependencyFormat, ScopedBuildInfo}\nimport scala.util.Using\n\nfinal case class JsonProject(buildInfo: BuildInfo) extends Project {\n  def sorted = this.copy(\n    buildInfo = buildInfo.copy(\n      scopes = buildInfo.scopes.map { case (k, v) => k -> v.sorted }\n    )\n  )\n\n  def withEmptyScopesRemoved = this.copy(\n    buildInfo = buildInfo.copy(\n      scopes = buildInfo.scopes.filter(_._2 != ScopedBuildInfo.empty)\n    )\n  )\n\n  def writeTo(dir: os.Path): Unit = {\n    val config = WriterConfig.withIndentionStep(1)\n\n    Using(os.write.outputStream(dir / \"export.json\")) {\n      outputStream =>\n        writeToStream(\n          sorted.withEmptyScopesRemoved.buildInfo,\n          outputStream,\n          config\n        )(\n          using JsonProject.jsonCodecBuildInfo\n        )\n    }\n  }\n\n  def print(printStream: PrintStream): Unit = {\n    val config = WriterConfig.withIndentionStep(1)\n\n    writeToStream(\n      sorted.withEmptyScopesRemoved.buildInfo,\n      printStream,\n      config\n    )(\n      using JsonProject.jsonCodecBuildInfo\n    )\n  }\n}\n\nextension (s: ScopedBuildInfo) {\n  def sorted(using ord: Ordering[String]) = s.copy(\n    s.sources.sorted,\n    s.scalacOptions.sorted,\n    s.scalaCompilerPlugins.sorted(using JsonProject.ordering),\n    s.dependencies.sorted(using JsonProject.ordering),\n    s.compileOnlyDependencies.sorted(using JsonProject.ordering),\n    s.resolvers.sorted,\n    s.resourceDirs.sorted,\n    s.customJarsDecls.sorted\n  )\n}\n\nobject JsonProject {\n  implicit lazy val jsonCodecBuildInfo: JsonValueCodec[BuildInfo]             = JsonCodecMaker.make\n  implicit lazy val jsonCodecScopedBuildInfo: JsonValueCodec[ScopedBuildInfo] = JsonCodecMaker.make\n\n  implicit val ordering: Ordering[ExportDependencyFormat] =\n    Ordering.by(x => x.groupId + x.artifactId.fullName)\n  implicit lazy val jsonCodecExportDependencyFormat: JsonValueCodec[ExportDependencyFormat] =\n    JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/JsonProjectDescriptor.scala",
    "content": "package scala.cli.exportCmd\nimport scala.build.errors.BuildException\nimport scala.build.info.{BuildInfo, ScopedBuildInfo}\nimport scala.build.options.{BuildOptions, Scope}\nimport scala.build.{Logger, Sources}\n\nfinal case class JsonProjectDescriptor(\n  projectName: Option[String] = None,\n  workspace: os.Path,\n  logger: Logger\n) extends ProjectDescriptor {\n\n  def `export`(\n    optionsMain: BuildOptions,\n    optionsTest: BuildOptions,\n    sourcesMain: Sources,\n    sourcesTest: Sources\n  ): Either[BuildException, JsonProject] = {\n    def getScopedBuildInfo(options: BuildOptions, sources: Sources) =\n      val sourcePaths   = sources.paths.map(_._1.toString)\n      val inMemoryPaths = sources.inMemory.flatMap(_.originalPath.toSeq.map(_._2.toString))\n\n      ScopedBuildInfo(options, sourcePaths ++ inMemoryPaths)\n\n    for {\n      baseBuildInfo <- BuildInfo(optionsMain, workspace)\n      mainBuildInfo = getScopedBuildInfo(optionsMain, sourcesMain)\n      testBuildInfo = getScopedBuildInfo(optionsTest, sourcesTest)\n    } yield JsonProject(baseBuildInfo\n      .withScope(Scope.Main.name, mainBuildInfo)\n      .withScope(Scope.Test.name, testBuildInfo))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/MavenProject.scala",
    "content": "package scala.cli.exportCmd\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.options.ConfigMonoid\nimport scala.xml.{Elem, NodeSeq, PrettyPrinter}\n\nfinal case class MavenProject(\n  groupId: Option[String] = None,\n  artifactId: Option[String] = None,\n  version: Option[String] = None,\n  plugins: Seq[MavenPlugin] = Nil,\n  imports: Seq[String] = Nil,\n  settings: Seq[Seq[String]] = Nil,\n  dependencies: Seq[MavenLibraryDependency] = Nil,\n  mainSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,\n  testSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,\n  resourceDirectories: Seq[String] = Nil\n) extends Project {\n\n  def +(other: MavenProject): MavenProject =\n    MavenProject.monoid.orElse(this, other)\n\n  def writeTo(dir: os.Path): Unit = {\n\n    System.lineSeparator()\n    val charset = StandardCharsets.UTF_8\n\n    val buildMavenContent = MavenModel(\n      \"4.0.0\",\n      groupId.getOrElse(\"groupId\"),\n      artifactId.getOrElse(\"artifactId\"),\n      version.getOrElse(\"0.1-SNAPSHOT\"),\n      dependencies,\n      plugins,\n      resourceDirectories\n    )\n\n    val prettyPrinter = new PrettyPrinter(width = 80, step = 2)\n    val formattedXml  = prettyPrinter.format(buildMavenContent.toXml)\n\n    os.write(\n      dir / \"pom.xml\",\n      formattedXml.getBytes(charset)\n    )\n\n    for ((path, language, content) <- mainSources) {\n      val path0 = dir / \"src\" / \"main\" / language / path\n      os.write(path0, content, createFolders = true)\n    }\n    for ((path, language, content) <- testSources) {\n      val path0 = dir / \"src\" / \"test\" / language / path\n      os.write(path0, content, createFolders = true)\n    }\n\n  }\n}\n\nobject MavenProject {\n  implicit val monoid: ConfigMonoid[MavenProject] = ConfigMonoid.derive\n}\n\nfinal case class MavenModel(\n  model: String,\n  groupId: String,\n  artifactId: String,\n  version: String,\n  dependencies: Seq[MavenLibraryDependency],\n  plugins: Seq[MavenPlugin],\n  resourceDirectories: Seq[String]\n) {\n\n  private def resourceNodes: NodeSeq =\n    if (resourceDirectories.isEmpty)\n      NodeSeq.Empty\n    else {\n      val resourceNodes = resourceDirectories.map { path =>\n        <resource>\n          <directory>\n            {path}\n          </directory>\n        </resource>\n      }\n      <resources>\n        {resourceNodes}\n      </resources>\n    }\n\n  def toXml: Elem =\n    <project>\n      <modelVersion>{model}</modelVersion>\n      <groupId>{groupId}</groupId>\n      <artifactId>{artifactId}</artifactId>\n      <version>{version}</version>\n\n      <dependencies>\n        {dependencies.map(_.toXml)}\n      </dependencies>\n      <build>\n        {resourceNodes}\n        <plugins>\n          {plugins.map(_.toXml)}\n        </plugins>\n      </build>\n    </project>\n}\n\nfinal case class MavenLibraryDependency(\n  groupId: String,\n  artifactId: String,\n  version: String,\n  scope: MavenScopes\n) {\n\n  private val scopeParam =\n    if scope == MavenScopes.Main then scala.xml.Null else <scope>{scope.name}</scope>\n\n  def toXml: Elem =\n    <dependency>\n      <groupId>{groupId}</groupId>\n      <artifactId>{artifactId}</artifactId>\n      <version>{version}</version>\n      {scopeParam}\n    </dependency>\n}\n\nfinal case class MavenPlugin(\n  groupId: String,\n  artifactId: String,\n  version: String,\n  jdk: String,\n  additionalNode: Elem\n) {\n\n  def toXml: Elem =\n    <plugin>\n      <groupId>{groupId}</groupId>\n      <artifactId>{artifactId}</artifactId>\n      <version>{version}</version>\n      {additionalNode}\n    </plugin>\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/MavenProjectDescriptor.scala",
    "content": "package scala.cli.exportCmd\nimport dependency.{AnyDependency, NoAttributes, ScalaNameAttributes}\n\nimport scala.annotation.unused\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, Scope, ShadowingSeq}\nimport scala.build.{Logger, Positioned, Sources}\nimport scala.cli.ScalaCli\nimport scala.cli.exportCmd.POMBuilderHelper.*\nimport scala.xml.Elem\n\nobject POMBuilderHelper {\n  def buildNode(name: String, value: String): Elem =\n    new Elem(\n      null,\n      name,\n      scala.xml.Null,\n      scala.xml.TopScope,\n      minimizeEmpty = false,\n      scala.xml.Text(value)\n    )\n}\n\nfinal case class MavenProjectDescriptor(\n  mavenPluginVersion: String,\n  mavenScalaPluginVersion: String,\n  mavenExecPluginVersion: String,\n  extraSettings: Seq[String],\n  mavenAppGroupId: String,\n  mavenAppArtifactId: String,\n  mavenAppVersion: String,\n  logger: Logger\n) extends ProjectDescriptor {\n\n  private def sources(sourcesMain: Sources, sourcesTest: Sources): MavenProject = {\n    val mainSources = ProjectDescriptor.sources(sourcesMain)\n    val testSources = ProjectDescriptor.sources(sourcesTest)\n    MavenProject(\n      mainSources = mainSources,\n      testSources = testSources\n    )\n  }\n\n  // todo: fill this - to be done in separate issue to reduce scope for maven export\n  private def javaOptionsSettings(@unused options: BuildOptions): MavenProject =\n    MavenProject(\n      settings = Nil\n    )\n\n  private def javacOptionsSettings(options: BuildOptions): List[String] = {\n\n    val javacOptionsSettings =\n      if (options.javaOptions.javacOptions.toSeq.isEmpty) Nil\n      else {\n        val options0 = options\n          .javaOptions\n          .javacOptions\n          .toSeq\n          .map(_.value)\n          .map(o => \"\\\"\" + o.replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\")\n        options0\n      }\n\n    javacOptionsSettings.toList\n  }\n\n  private def projectArtifactSettings(\n    mavenAppGroupId: String,\n    mavenAppArtifactId: String,\n    mavenAppVersion: String\n  ): MavenProject =\n    MavenProject(\n      groupId = Some(mavenAppGroupId),\n      artifactId = Some(mavenAppArtifactId),\n      version = Some(mavenAppVersion)\n    )\n\n  private def dependencySettings(\n    options: BuildOptions,\n    testOptions: BuildOptions,\n    scope: Scope,\n    sources: Sources\n  ): MavenProject = {\n\n    val scalaV         = getScalaVersion(options)\n    def getScalaPrefix =\n      if scalaV.startsWith(\"3\") then \"3\"\n      else if scalaV.startsWith(\"2.13\") then \"2.13\"\n      else \"2.12\"\n\n    def buildMavenDepModels(\n      mainDeps: ShadowingSeq[Positioned[AnyDependency]],\n      isCompileOnly: Boolean\n    ) =\n      mainDeps.toSeq.toList.map(_.value).map { dep =>\n        val org  = dep.organization\n        val name = dep.name\n        val ver  = dep.version\n        // TODO dep.userParams\n        // TODO dep.exclude\n        // TODO dep.attributes\n        val artNameWithPrefix = dep.nameAttributes match {\n          case NoAttributes           => name\n          case _: ScalaNameAttributes => s\"${name}_$getScalaPrefix\"\n        }\n        val scope0 =\n          if (scope == Scope.Test) MavenScopes.Test\n          else if (isCompileOnly) {\n            System.err.println(\n              s\"Warning: Maven seems to support either test or provided, not both. So falling back to use Provided scope.\"\n            )\n            MavenScopes.Provided\n          }\n          else MavenScopes.Main\n\n        MavenLibraryDependency(org, artNameWithPrefix, ver, scope0)\n      }\n\n    val depSettings = {\n      def toDependencies(\n        mainDeps: ShadowingSeq[Positioned[AnyDependency]],\n        testDeps: ShadowingSeq[Positioned[AnyDependency]],\n        isCompileOnly: Boolean\n      ): Seq[MavenLibraryDependency] = {\n        val mainDependenciesMaven = buildMavenDepModels(mainDeps, isCompileOnly)\n        val testDependenciesMaven = buildMavenDepModels(testDeps, isCompileOnly)\n        val resolvedDeps          = (mainDependenciesMaven ++ testDependenciesMaven).groupBy(k =>\n          k.groupId + k.artifactId + k.version\n        ).map { (_, list) =>\n          val highestScope = MavenScopes.getHighestPriorityScope(list.map(_.scope))\n          list.head.copy(scope = highestScope)\n        }.toList\n\n        val scalaDep = if (!ProjectDescriptor.isPureJavaProject(options, sources)) {\n          val scalaDep = if scalaV.startsWith(\"3\") then \"scala3-library_3\" else \"scala-library\"\n          val scalaCompilerDep =\n            if scalaV.startsWith(\"3\") then \"scala3-compiler_3\" else \"scala-compiler\"\n          List(\n            MavenLibraryDependency(\"org.scala-lang\", scalaDep, scalaV, MavenScopes.Main),\n            MavenLibraryDependency(\"org.scala-lang\", scalaCompilerDep, scalaV, MavenScopes.Main)\n          )\n        }\n        else Nil\n\n        resolvedDeps ++ scalaDep\n      }\n\n      toDependencies(\n        options.classPathOptions.allExtraDependencies,\n        testOptions.classPathOptions.allExtraDependencies,\n        true\n      )\n    }\n\n    MavenProject(\n      dependencies = depSettings\n    )\n  }\n\n  private def getScalaVersion(options: BuildOptions): String =\n    options.scalaParams.toOption.flatten.map(_.scalaVersion).getOrElse(\n      ScalaCli.getDefaultScalaVersion\n    )\n\n  private def plugins(\n    options: BuildOptions,\n    jdkVersion: String,\n    sourcesMain: Sources\n  ): MavenProject = {\n\n    val pureJava = ProjectDescriptor.isPureJavaProject(options, sourcesMain)\n\n    val javacOptions = javacOptionsSettings(options)\n\n    val mavenJavaPlugin = buildJavaCompilerPlugin(javacOptions, jdkVersion)\n    val mavenExecPlugin = buildJavaExecPlugin(jdkVersion)\n    val scalaPlugin     = buildScalaPlugin(jdkVersion)\n\n    val reqdPlugins =\n      if (pureJava) Seq(mavenJavaPlugin, mavenExecPlugin) else Seq(mavenJavaPlugin, scalaPlugin)\n\n    MavenProject(\n      plugins = reqdPlugins\n    )\n  }\n\n  private def buildScalaPlugin(jdkVersion: String): MavenPlugin = {\n    val execElements =\n      <executions>\n        <execution>\n          <goals>\n            <goal>compile</goal>\n            <goal>testCompile</goal>\n          </goals>\n        </execution>\n      </executions>\n\n    MavenPlugin(\n      \"net.alchim31.maven\",\n      \"scala-maven-plugin\",\n      mavenScalaPluginVersion,\n      jdkVersion,\n      execElements\n    )\n  }\n\n  private def buildJavaCompilerPlugin(\n    javacOptions: Seq[String],\n    jdkVersion: String\n  ): MavenPlugin = {\n    val javacOptionsElem = {\n      val opts = javacOptions.map { opt =>\n        buildNode(\"arg\", opt)\n      }\n      <compilerArgs>\n        {opts}\n      </compilerArgs>\n    }\n\n    val sourceArg  = buildNode(\"source\", jdkVersion)\n    val targetArg  = buildNode(\"target\", jdkVersion)\n    val configNode =\n      <configuration>\n        {javacOptionsElem}\n        {sourceArg}\n        {targetArg}\n      </configuration>\n\n    MavenPlugin(\n      \"org.apache.maven.plugins\",\n      \"maven-compiler-plugin\",\n      mavenPluginVersion,\n      jdkVersion,\n      configNode\n    )\n  }\n\n  private def buildJavaExecPlugin(jdkVersion: String): MavenPlugin =\n    MavenPlugin(\n      \"org.codehaus.mojo\",\n      \"exec-maven-plugin\",\n      mavenExecPluginVersion,\n      jdkVersion,\n      <configuration></configuration>\n    )\n\n  private def customResourcesSettings(options: BuildOptions): MavenProject = {\n    val resourceDirs =\n      if (options.classPathOptions.resourcesDir.isEmpty) Nil\n      else\n        options.classPathOptions.resourcesDir.map(_.toNIO.toAbsolutePath.toString)\n    MavenProject(\n      resourceDirectories = resourceDirs\n    )\n  }\n\n  def `export`(\n    optionsMain: BuildOptions,\n    optionsTest: BuildOptions,\n    sourcesMain: Sources,\n    sourcesTest: Sources\n  ): Either[BuildException, MavenProject] = {\n    val jdk =\n      optionsMain.javaOptions.jvmIdOpt.map(_.value)\n        .getOrElse(Constants.defaultJavaVersion.toString)\n    val projectChunks = Seq(\n      sources(sourcesMain, sourcesTest),\n      javaOptionsSettings(optionsMain),\n      dependencySettings(optionsMain, optionsTest, Scope.Main, sourcesMain),\n      customResourcesSettings(optionsMain),\n      plugins(optionsMain, jdk, sourcesMain),\n      projectArtifactSettings(mavenAppGroupId, mavenAppArtifactId, mavenAppVersion)\n    )\n    Right(projectChunks.foldLeft(MavenProject())(_ + _))\n  }\n\n}\n\nenum MavenScopes(val priority: Int, val name: String) {\n  case Main     extends MavenScopes(1, \"main\")\n  case Test     extends MavenScopes(2, \"test\")\n  case Provided extends MavenScopes(3, \"provided\")\n}\n\nobject MavenScopes {\n  def getHighestPriorityScope(scopes: Seq[MavenScopes]): MavenScopes =\n    // if scope is empty return Main Scope, depending on priority, with 1 being highest\n    scopes.minByOption(_.priority).getOrElse(Main)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/MillProject.scala",
    "content": "package scala.cli.exportCmd\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.options.ConfigMonoid\nimport scala.cli.util.SeqHelpers.*\nimport scala.reflect.NameTransformer\nimport scala.util.Properties\n\nfinal case class MillProject(\n  millVersion: Option[String] = None,\n  mainDeps: Seq[String] = Nil,\n  mainCompileOnlyDeps: Seq[String] = Nil,\n  testDeps: Seq[String] = Nil,\n  testCompileOnlyDeps: Seq[String] = Nil,\n  scalaVersion: Option[String] = None,\n  scalacOptions: Seq[String] = Nil,\n  scalaCompilerPlugins: Seq[String] = Nil,\n  scalaJsVersion: Option[String] = None,\n  scalaNativeVersion: Option[String] = None,\n  nameOpt: Option[String] = None,\n  launchers: Seq[(os.RelPath, Array[Byte])] = Nil,\n  mainSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,\n  testSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,\n  extraDecls: Seq[String] = Nil,\n  resourcesDirs: Seq[os.Path] = Nil,\n  extraTestDecls: Seq[String] = Nil,\n  mainClass: Option[String] = None\n) extends Project {\n  private lazy val isMill1OrNewer: Boolean = !millVersion.exists(_.startsWith(\"0.\"))\n\n  def +(other: MillProject): MillProject =\n    MillProject.monoid.orElse(this, other)\n\n  private def name = nameOpt.getOrElse(\"project\")\n\n  def writeTo(dir: os.Path): Unit = {\n    val nl      = System.lineSeparator()\n    val charSet = StandardCharsets.UTF_8\n\n    for ((relPath, content) <- launchers) {\n      val dest = dir / relPath\n      os.write(dest, content, createFolders = true)\n      if (!Properties.isWin)\n        os.perms.set(dest, \"rwxr-xr-x\")\n    }\n\n    for (ver <- millVersion)\n      os.write(dir / \".mill-version\", ver.getBytes(charSet), createFolders = true)\n\n    val escapedName =\n      if (NameTransformer.encode(name) == name) name\n      else \"`\" + name + \"`\"\n    val (parentModule, maybeExtraImport, maybePlatformVer) =\n      if (scalaVersion.isEmpty) (\"JavaModule\", None, None)\n      else\n        scalaJsVersion\n          .map(ver =>\n            (\n              \"ScalaJSModule\",\n              Some(\"import mill.scalajslib._\"),\n              Some(s\"\"\"def scalaJSVersion = \"$ver\"\"\"\")\n            )\n          )\n          .orElse(\n            scalaNativeVersion.map(ver =>\n              (\n                \"ScalaNativeModule\",\n                Some(\"import mill.scalanativelib._\"),\n                Some(s\"\"\"def scalaNativeVersion = \"$ver\"\"\"\")\n              )\n            )\n          )\n          .getOrElse((\"ScalaModule\", None, None))\n\n    val maybeScalaVer = scalaVersion.map { sv =>\n      s\"\"\"def scalaVersion = \"$sv\"\"\"\"\n    }\n    val maybeScalacOptions =\n      if (scalacOptions.isEmpty) None\n      else {\n        val optsString = scalacOptions.map(opt => s\"\\\"$opt\\\"\").mkString(\", \")\n        Some(s\"\"\"def scalacOptions = super.scalacOptions() ++ Seq($optsString)\"\"\")\n      }\n    def maybeDeps(deps: Seq[String], isCompileOnly: Boolean = false) = {\n      val depsDefinition = isCompileOnly -> isMill1OrNewer match {\n        case (true, true)   => Seq(\"def compileMvnDeps = super.compileMvnDeps() ++ Seq(\")\n        case (true, false)  => Seq(\"def compileIvyDeps = super.compileIvyDeps() ++ Seq(\")\n        case (false, true)  => Seq(\"def mvnDeps = super.mvnDeps() ++ Seq(\")\n        case (false, false) => Seq(\"def ivyDeps = super.ivyDeps() ++ Seq(\")\n      }\n      if deps.isEmpty then Seq.empty[String]\n      else\n        depsDefinition ++\n          deps\n            .map {\n              case dep if isMill1OrNewer => s\"\"\"  mvn\"$dep\"\"\"\"\n              case dep                   => s\"\"\"  ivy\"$dep\"\"\"\"\n            }\n            .appendOnInit(\",\") ++\n          Seq(\")\")\n    }\n\n    val maybeScalaCompilerPlugins =\n      if scalaCompilerPlugins.isEmpty then Seq.empty\n      else\n        Seq(\n          if isMill1OrNewer\n          then \"def scalacPluginMvnDeps = super.scalacPluginMvnDeps() ++ Seq(\"\n          else \"def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Seq(\"\n        ) ++\n          scalaCompilerPlugins\n            .map {\n              case dep if isMill1OrNewer => s\"  mvn\\\"$dep\\\"\"\n              case dep                   => s\"  ivy\\\"$dep\\\"\"\n            }\n            .appendOnInit(\",\")\n          ++ Seq(\")\")\n\n    val maybeMain = mainClass.map { mc =>\n      s\"\"\"def mainClass = Some(\"$mc\")\"\"\"\n    }\n    val customResourcesDecls =\n      if (resourcesDirs.isEmpty) Nil\n      else {\n        val resources =\n          resourcesDirs.map {\n            case p if isMill1OrNewer =>\n              s\"\"\"mill.api.BuildCtx.workspaceRoot / os.RelPath(\"${p.relativeTo(dir)}\")\"\"\"\n            case p => s\"\"\"T.workspace / os.RelPath(\"${p.relativeTo(dir)}\")\"\"\"\n          }\n        Seq(\"def runClasspath = super.runClasspath() ++ Seq(\") ++\n          resources.map(resource => s\"  $resource\").appendOnInit(\",\") ++\n          Seq(\").map(PathRef(_))\")\n      }\n    val millScalaTestPlatform = if (scalaJsVersion.nonEmpty) \"ScalaJSTests\"\n    else if (scalaNativeVersion.nonEmpty) \"ScalaNativeTests\"\n    else \"ScalaTests\"\n    val maybeTestDefinition = if (testSources.nonEmpty)\n      Seq(\n        \"\",\n        s\"  object test extends $millScalaTestPlatform {\"\n      ) ++\n        maybeDeps(testDeps).map(s => s\"    $s\") ++\n        maybeDeps(testCompileOnlyDeps, isCompileOnly = true).map(s => s\"    $s\") ++\n        extraTestDecls.map(s => s\"    $s\") ++ Seq(\"  }\")\n    else Seq.empty\n\n    val buildFileContent: String = {\n      val parts: Seq[String] = Seq(\n        \"import mill._\",\n        \"import mill.scalalib._\"\n      ) ++ maybeExtraImport ++\n        Seq(\n          s\"object $escapedName extends $parentModule {\"\n        ) ++\n        maybeScalaVer.map(s => s\"  $s\") ++\n        maybePlatformVer.map(s => s\"  $s\") ++\n        maybeScalacOptions.map(s => s\"  $s\") ++\n        maybeDeps(mainDeps).map(s => s\"  $s\") ++\n        maybeDeps(mainCompileOnlyDeps, isCompileOnly = true).map(s => s\"  $s\") ++\n        maybeScalaCompilerPlugins.map(s => s\"  $s\") ++\n        maybeMain.map(s => s\"  $s\") ++\n        customResourcesDecls.map(s => s\"  $s\") ++\n        extraDecls.map(\"  \" + _) ++\n        maybeTestDefinition ++\n        Seq(\"}\", \"\")\n      parts.mkString(nl)\n    }\n\n    for ((path, _, content) <- mainSources) {\n      val path0 = dir / name / \"src\" / path\n      os.write(path0, content, createFolders = true)\n    }\n    for ((path, _, content) <- testSources) {\n      val path0 = dir / name / \"test\" / \"src\" / path\n      os.write(path0, content, createFolders = true)\n    }\n\n    val outputBuildFile = if isMill1OrNewer then dir / \"build.mill\" else dir / \"build.sc\"\n    os.write(outputBuildFile, buildFileContent.getBytes(charSet))\n  }\n}\n\nobject MillProject {\n  implicit val monoid: ConfigMonoid[MillProject] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/MillProjectDescriptor.scala",
    "content": "package scala.cli.exportCmd\n\nimport coursier.maven.MavenRepository\nimport coursier.parse.RepositoryParser\n\nimport java.nio.file.Path\n\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.internal.Runner.frameworkNames\nimport scala.build.options.{BuildOptions, Platform, ScalaJsOptions, ScalaNativeOptions, Scope}\nimport scala.build.testrunner.{AsmTestRunner, Logger as TestRunnerLogger}\nimport scala.build.{Logger, Sources}\nimport scala.cli.ScalaCli\n\nfinal case class MillProjectDescriptor(\n  millVersion: String,\n  projectName: Option[String] = None,\n  launchers: Seq[(os.RelPath, Array[Byte])],\n  logger: Logger\n) extends ProjectDescriptor {\n\n  private def sourcesSettings(mainSources: Sources, testSources: Sources): MillProject = {\n    val mainSources0 = ProjectDescriptor.sources(mainSources)\n    val testSources0 = ProjectDescriptor.sources(testSources)\n    MillProject(mainSources = mainSources0, testSources = testSources0)\n  }\n\n  private def scalaVersionSettings(options: BuildOptions, sources: Sources): MillProject = {\n\n    val pureJava = ProjectDescriptor.isPureJavaProject(options, sources)\n\n    val sv = options.scalaParams\n      .toOption\n      .flatten\n      .map(_.scalaVersion)\n      .getOrElse(ScalaCli.getDefaultScalaVersion)\n\n    if pureJava then MillProject()\n    else MillProject(scalaVersion = Some(sv))\n  }\n\n  private def scalaCompilerPlugins(buildOptions: BuildOptions): MillProject =\n    MillProject(scalaCompilerPlugins =\n      buildOptions.scalaOptions.compilerPlugins.toSeq.map(_.value.render)\n    )\n\n  private def scalacOptionsSettings(buildOptions: BuildOptions): MillProject =\n    MillProject(scalacOptions = buildOptions.scalaOptions.scalacOptions.toSeq.map(_.value.value))\n\n  private def scalaJsSettings(options: ScalaJsOptions): MillProject = {\n    val scalaJsVersion  = Some(options.version.getOrElse(Constants.scalaJsVersion))\n    val moduleKindDecls =\n      if (options.moduleKindStr.isEmpty) Nil\n      else\n        Seq(s\"\"\"def moduleKind = ModuleKind.${options.moduleKind(logger)}\"\"\")\n\n    MillProject(\n      scalaJsVersion = scalaJsVersion,\n      extraDecls = moduleKindDecls\n    )\n  }\n\n  private def scalaNativeSettings(options: ScalaNativeOptions): MillProject =\n    MillProject(scalaNativeVersion = Some(options.finalVersion))\n\n  private def dependencySettings(\n    mainOptions: BuildOptions,\n    testOptions: BuildOptions\n  ): MillProject = {\n    val mainDeps        = mainOptions.classPathOptions.extraDependencies.toSeq.map(_.value.render)\n    val compileMainDeps =\n      mainOptions.classPathOptions.extraCompileOnlyDependencies.toSeq.map(_.value.render)\n    val testDeps        = testOptions.classPathOptions.extraDependencies.toSeq.map(_.value.render)\n    val compileTestDeps =\n      testOptions.classPathOptions.extraCompileOnlyDependencies.toSeq.map(_.value.render)\n    MillProject(\n      mainDeps = mainDeps.toSeq,\n      mainCompileOnlyDeps = compileMainDeps.toSeq,\n      testDeps = testDeps.toSeq,\n      testCompileOnlyDeps = compileTestDeps.toSeq\n    )\n  }\n\n  private def repositorySettings(options: BuildOptions): MillProject = {\n\n    val repoDecls =\n      if (options.classPathOptions.extraRepositories.isEmpty) Nil\n      else {\n        val repos = options.classPathOptions\n          .extraRepositories\n          .map(repo => RepositoryParser.repository(repo))\n          .map {\n            case Right(repo: MavenRepository) =>\n              // TODO repo.authentication?\n              s\"\"\"coursier.maven.MavenRepository(\"${repo.root}\")\"\"\"\n            case _ =>\n              ???\n          }\n        Seq(s\"\"\"def repositories = super.repositories ++ Seq(${repos.mkString(\", \")})\"\"\")\n      }\n\n    MillProject(\n      extraDecls = repoDecls\n    )\n  }\n\n  private def customResourcesSettings(options: BuildOptions): MillProject =\n    MillProject(resourcesDirs = options.classPathOptions.resourcesDir)\n\n  private def customJarsSettings(options: BuildOptions): MillProject = {\n\n    val customCompileOnlyJarsDecls =\n      if options.classPathOptions.extraCompileOnlyJars.isEmpty then Nil\n      else {\n        val jars =\n          options.classPathOptions.extraCompileOnlyJars.map(p => s\"\"\"PathRef(os.Path(\"$p\"))\"\"\")\n        Seq(s\"\"\"def compileClasspath = super.compileClasspath() ++ Seq(${jars.mkString(\", \")})\"\"\")\n      }\n\n    val customJarsDecls =\n      if options.classPathOptions.extraClassPath.isEmpty then Nil\n      else {\n        val jars = options.classPathOptions.extraClassPath.map(p => s\"\"\"PathRef(os.Path(\"$p\"))\"\"\")\n        Seq(\n          s\"\"\"def unmanagedClasspath = super.unmanagedClasspath() ++ Seq(${jars.mkString(\", \")})\"\"\"\n        )\n      }\n\n    MillProject(extraDecls = customCompileOnlyJarsDecls ++ customJarsDecls)\n  }\n\n  private def testFrameworkSettings(options: BuildOptions): MillProject = {\n    val testClassPath: Seq[Path] = options.artifacts(logger, Scope.Test) match {\n      case Right(artifacts) => artifacts.classPath.map(_.toNIO)\n      case Left(exception)  =>\n        logger.debug(exception.message)\n        Seq.empty\n    }\n    val parentInspector =\n      new AsmTestRunner.ParentInspector(testClassPath, TestRunnerLogger(logger.verbosity))\n    val frameworkName0 = options.testOptions.frameworks.headOption.orElse {\n      frameworkNames(testClassPath, parentInspector, logger).toOption\n        .flatMap(_.headOption) // TODO: handle multiple frameworks here\n    }\n\n    val testFrameworkDecls = frameworkName0 match {\n      case None     => Nil\n      case Some(fw) =>\n        Seq(s\"\"\"def testFramework = \"$fw\"\"\"\")\n    }\n\n    MillProject(\n      extraTestDecls = testFrameworkDecls\n    )\n  }\n\n  def `export`(\n    optionsMain: BuildOptions,\n    optionsTest: BuildOptions,\n    sourcesMain: Sources,\n    sourcesTest: Sources\n  ): Either[BuildException, MillProject] = {\n\n    // FIXME Put a sensible value in MillProject.nameOpt\n\n    val baseSettings = MillProject(\n      millVersion = Some(millVersion),\n      nameOpt = projectName,\n      launchers = launchers,\n      mainClass = optionsMain.mainClass\n    )\n\n    val settings = Seq(\n      baseSettings,\n      sourcesSettings(sourcesMain, sourcesTest),\n      scalaVersionSettings(optionsMain, sourcesMain),\n      scalacOptionsSettings(optionsMain),\n      scalaCompilerPlugins(optionsMain),\n      dependencySettings(optionsMain, optionsTest),\n      repositorySettings(optionsMain),\n      if optionsMain.platform.value == Platform.JS then scalaJsSettings(optionsMain.scalaJsOptions)\n      else MillProject(),\n      if optionsMain.platform.value == Platform.Native then\n        scalaNativeSettings(optionsMain.scalaNativeOptions)\n      else MillProject(),\n      customResourcesSettings(optionsMain),\n      customJarsSettings(optionsMain),\n      testFrameworkSettings(optionsTest)\n    )\n\n    Right(settings.foldLeft(MillProject())(_ + _))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/Project.scala",
    "content": "package scala.cli.exportCmd\n\nabstract class Project extends Product with Serializable {\n  def writeTo(dir: os.Path): Unit\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/ProjectDescriptor.scala",
    "content": "package scala.cli.exportCmd\n\nimport dependency.NoAttributes\n\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, ScalaJsOptions}\nimport scala.build.{Logger, Sources}\n\nabstract class ProjectDescriptor extends Product with Serializable {\n  def `export`(\n    optionsMain: BuildOptions,\n    optionsTest: BuildOptions,\n    sourcesMain: Sources,\n    sourcesTest: Sources\n  ): Either[BuildException, Project]\n}\n\nobject ProjectDescriptor {\n  def sources(sources: Sources): Seq[(os.SubPath, String, Array[Byte])] = {\n\n    val mainSources = sources.paths.map {\n      case (path, relPath) =>\n        val language =\n          if (path.last.endsWith(\".java\")) \"java\"\n          else \"scala\" // FIXME Others\n        // FIXME asSubPath might throw… Make it a SubPath earlier in the API?\n        (relPath.asSubPath, language, os.read.bytes(path))\n    }\n\n    val extraMainSources = sources.inMemory.map { inMemSource =>\n      val language =\n        if (inMemSource.generatedRelPath.last.endsWith(\".java\")) \"java\"\n        else \"scala\"\n      (\n        inMemSource.generatedRelPath.asSubPath,\n        language,\n        inMemSource.content\n      )\n    }\n\n    mainSources ++ extraMainSources\n  }\n\n  def scalaJsLinkerCalls(options: ScalaJsOptions, logger: Logger): Seq[String] = {\n\n    var calls = Seq.empty[String]\n\n    calls = calls ++ {\n      if (options.moduleKindStr.isEmpty) Nil\n      else\n        Seq(s\"\"\".withModuleKind(ModuleKind.${options.moduleKind(logger)})\"\"\")\n    }\n\n    for (checkIr <- options.checkIr)\n      calls = calls :+ s\".withCheckIR($checkIr)\"\n\n    val withOptimizer = options.fullOpt.getOrElse(false)\n    calls = calls :+ s\".withOptimizer($withOptimizer)\"\n    calls = calls :+ s\".withClosureCompiler($withOptimizer)\"\n\n    calls = calls :+ s\".withSourceMap(${options.emitSourceMaps})\"\n\n    calls\n  }\n\n  def isPureJavaProject(options: BuildOptions, sources: Sources): Boolean =\n    !options.scalaOptions.addScalaLibrary.contains(true) &&\n    !options.scalaOptions.addScalaCompiler.contains(true) &&\n    sources.hasJava &&\n    !sources.hasScala &&\n    options.classPathOptions.allExtraDependencies.toSeq\n      .forall(_.value.nameAttributes == NoAttributes)\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/SbtProject.scala",
    "content": "package scala.cli.exportCmd\n\nimport java.nio.charset.StandardCharsets\n\nimport scala.build.options.ConfigMonoid\n\nfinal case class SbtProject(\n  plugins: Seq[String] = Nil,\n  imports: Seq[String] = Nil,\n  settings: Seq[Seq[String]] = Nil,\n  sbtVersion: Option[String] = None,\n  mainSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,\n  testSources: Seq[(os.SubPath, String, Array[Byte])] = Nil\n) extends Project {\n\n  def +(other: SbtProject): SbtProject =\n    SbtProject.monoid.orElse(this, other)\n\n  def writeTo(dir: os.Path): Unit = {\n    val nl      = System.lineSeparator()\n    val charset = StandardCharsets.UTF_8\n\n    for (ver <- sbtVersion) {\n      val buildPropsContent = s\"sbt.version=$ver\" + nl\n      os.write(\n        dir / \"project\" / \"build.properties\",\n        buildPropsContent.getBytes(charset),\n        createFolders = true\n      )\n    }\n\n    if (plugins.nonEmpty) {\n      val pluginsSbtContent = plugins\n        .map { p =>\n          s\"addSbtPlugin($p)\" + nl\n        }\n        .mkString\n      os.write(dir / \"project\" / \"plugins.sbt\", pluginsSbtContent.getBytes(charset))\n    }\n\n    val buildSbtImportsContent  = imports.map(_ + nl).mkString\n    val buildSbtSettingsContent = settings\n      .filter(_.nonEmpty)\n      .map { settings0 =>\n        settings0.map(s => s + nl).mkString + nl\n      }\n      .mkString\n    val buildSbtContent = buildSbtImportsContent + buildSbtSettingsContent\n    os.write(dir / \"build.sbt\", buildSbtContent.getBytes(charset))\n\n    for ((path, language, content) <- mainSources) {\n      val path0 = dir / \"src\" / \"main\" / language / path\n      os.write(path0, content, createFolders = true)\n    }\n    for ((path, language, content) <- testSources) {\n      val path0 = dir / \"src\" / \"test\" / language / path\n      os.write(path0, content, createFolders = true)\n    }\n  }\n}\n\nobject SbtProject {\n  implicit val monoid: ConfigMonoid[SbtProject] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/exportCmd/SbtProjectDescriptor.scala",
    "content": "package scala.cli.exportCmd\n\nimport coursier.ivy.IvyRepository\nimport coursier.maven.MavenRepository\nimport coursier.parse.RepositoryParser\nimport dependency.{AnyDependency, NoAttributes, ScalaNameAttributes}\n\nimport java.nio.file.Path\n\nimport scala.build.errors.BuildException\nimport scala.build.internal.Runner.frameworkNames\nimport scala.build.options.{\n  BuildOptions,\n  Platform,\n  ScalaJsOptions,\n  ScalaNativeOptions,\n  Scope,\n  ShadowingSeq\n}\nimport scala.build.testrunner.{AsmTestRunner, Logger as TestRunnerLogger}\nimport scala.build.{Logger, Positioned, Sources}\nimport scala.cli.ScalaCli\n\nfinal case class SbtProjectDescriptor(\n  sbtVersion: String,\n  extraSettings: Seq[String],\n  logger: Logger\n) extends ProjectDescriptor {\n  private val q  = \"\\\"\"\n  private val nl = System.lineSeparator()\n\n  private def sources(sourcesMain: Sources, sourcesTest: Sources): SbtProject = {\n    val mainSources = ProjectDescriptor.sources(sourcesMain)\n    val testSources = ProjectDescriptor.sources(sourcesTest)\n    SbtProject(\n      mainSources = mainSources,\n      testSources = testSources\n    )\n  }\n\n  private def sbtVersionProject: SbtProject =\n    SbtProject(sbtVersion = Some(sbtVersion))\n\n  private def pureJavaSettings(options: BuildOptions, sources: Sources): SbtProject = {\n\n    val pureJava = ProjectDescriptor.isPureJavaProject(options, sources)\n\n    val settings =\n      if (pureJava)\n        Seq(\n          \"crossPaths := false\",\n          \"autoScalaLibrary := false\"\n        )\n      else if (options.scalaOptions.addScalaLibrary.getOrElse(true))\n        Nil\n      else\n        Seq(\n          \"autoScalaLibrary := false\"\n        )\n\n    SbtProject(settings = Seq(settings))\n  }\n\n  private def scalaJsSettings(options: ScalaJsOptions): SbtProject = {\n\n    val plugins = Seq(\n      s\"\"\"\"org.scala-js\" % \"sbt-scalajs\" % \"${options.finalVersion}\"\"\"\"\n    )\n    val pluginSettings = Seq(\n      \"enablePlugins(ScalaJSPlugin)\",\n      \"scalaJSUseMainModuleInitializer := true\"\n    )\n\n    val linkerConfigCalls    = ProjectDescriptor.scalaJsLinkerCalls(options, logger)\n    val linkerConfigSettings =\n      if (linkerConfigCalls.isEmpty) Nil\n      else\n        Seq(s\"\"\"scalaJSLinkerConfig ~= { _${linkerConfigCalls.mkString} }\"\"\")\n\n    // TODO options.dom\n\n    SbtProject(\n      plugins = plugins,\n      settings = Seq(pluginSettings, linkerConfigSettings)\n    )\n  }\n\n  private def scalaNativeSettings(options: ScalaNativeOptions): SbtProject = {\n\n    val plugins = Seq(\n      s\"\"\"\"org.scala-native\" % \"sbt-scala-native\" % \"${options.finalVersion}\"\"\"\"\n    )\n    val pluginSettings = Seq(\n      \"enablePlugins(ScalaNativePlugin)\"\n    )\n\n    val configCalls = Seq.empty[String]\n\n    val (configImports, configSettings) =\n      if (configCalls.isEmpty) (\"\", Nil)\n      else\n        (\n          \"import scala.scalanative.build._\",\n          Seq(s\"\"\"nativeConfig ~= { _${configCalls.mkString} }\"\"\")\n        )\n\n    SbtProject(\n      plugins = plugins,\n      settings = Seq(pluginSettings, configSettings),\n      imports = Seq(configImports)\n    )\n  }\n\n  private def scalaVersionSettings(options: BuildOptions): SbtProject = {\n\n    val scalaVerSetting = {\n\n      val sv = options.scalaParams.toOption.flatten.map(_.scalaVersion).getOrElse(\n        ScalaCli.getDefaultScalaVersion\n      )\n\n      s\"\"\"scalaVersion := \"$sv\"\"\"\"\n    }\n\n    SbtProject(\n      settings = Seq(Seq(scalaVerSetting))\n    )\n  }\n\n  private def repositorySettings(options: BuildOptions): SbtProject = {\n\n    val repoSettings =\n      if (options.classPathOptions.extraRepositories.isEmpty) Nil\n      else {\n        val repos = options.classPathOptions\n          .extraRepositories\n          .map(repo => (repo, RepositoryParser.repository(repo)))\n          .zipWithIndex\n          .map {\n            case ((_, Right(repo: IvyRepository)), idx) =>\n              // TODO repo.authentication?\n              // TODO repo.metadataPatternOpt\n              s\"\"\"Resolver.url(\"repo-$idx\") artifacts \"${repo.pattern.string}\"\"\"\"\n            case ((_, Right(repo: MavenRepository)), idx) =>\n              // TODO repo.authentication?\n              s\"\"\"\"repo-$idx\" at \"${repo.root}\"\"\"\"\n            case _ =>\n              ???\n          }\n        Seq(s\"\"\"resolvers ++= Seq(${repos.mkString(\", \")})\"\"\")\n      }\n\n    SbtProject(\n      settings = Seq(repoSettings)\n    )\n  }\n\n  private def customResourcesSettings(options: BuildOptions): SbtProject = {\n    val customResourceSettings =\n      if (options.classPathOptions.resourcesDir.isEmpty) Nil\n      else {\n        val resources = options.classPathOptions.resourcesDir.map(p => s\"\"\"file(\"$p\")\"\"\")\n        Seq(\n          s\"\"\"Compile / unmanagedResourceDirectories ++= Seq(${resources.mkString(\", \")})\"\"\"\n        )\n      }\n\n    SbtProject(\n      settings = Seq(customResourceSettings)\n    )\n  }\n\n  private def customJarsSettings(options: BuildOptions): SbtProject = {\n\n    val customCompileOnlyJarsSettings =\n      if (options.classPathOptions.extraCompileOnlyJars.isEmpty) Nil\n      else {\n        val jars = options.classPathOptions.extraCompileOnlyJars.map(p => s\"\"\"file(\"$p\")\"\"\")\n        Seq(s\"\"\"Compile / unmanagedClasspath ++= Seq(${jars.mkString(\", \")})\"\"\")\n      }\n\n    val customJarsSettings =\n      if (options.classPathOptions.extraClassPath.isEmpty) Nil\n      else {\n        val jars = options.classPathOptions.extraClassPath.map(p => s\"\"\"file(\"$p\")\"\"\")\n        Seq(\n          s\"\"\"Compile / unmanagedClasspath ++= Seq(${jars.mkString(\", \")})\"\"\",\n          s\"\"\"Runtime / unmanagedClasspath ++= Seq(${jars.mkString(\", \")})\"\"\"\n        )\n      }\n\n    SbtProject(\n      settings = Seq(customCompileOnlyJarsSettings, customJarsSettings)\n    )\n  }\n\n  private def javaOptionsSettings(options: BuildOptions): SbtProject = {\n\n    val javaOptionsSettings =\n      if (options.javaOptions.javaOpts.toSeq.isEmpty) Nil\n      else\n        Seq(\n          \"run / javaOptions ++= Seq(\" + nl +\n            options.javaOptions\n              .javaOpts\n              .toSeq\n              .map(_.value.value)\n              .map { opt =>\n                \"  \\\"\" + opt + \"\\\",\" + nl\n              }\n              .mkString +\n            \")\"\n        )\n\n    SbtProject(\n      settings = Seq(javaOptionsSettings)\n    )\n  }\n\n  private def mainClassSettings(options: BuildOptions): SbtProject = {\n\n    val mainClassOptions = options.mainClass match {\n      case None            => Nil\n      case Some(mainClass) =>\n        Seq(s\"\"\"Compile / mainClass := Some(\"$mainClass\")\"\"\")\n    }\n\n    SbtProject(\n      settings = Seq(mainClassOptions)\n    )\n  }\n\n  private def scalacOptionsSettings(options: BuildOptions): SbtProject = {\n\n    val scalacOptionsSettings =\n      if (options.scalaOptions.scalacOptions.toSeq.isEmpty) Nil\n      else {\n        val options0 = options\n          .scalaOptions\n          .scalacOptions\n          .toSeq\n          .map(_.value.value)\n          .map(o => \"\\\"\" + o.replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\")\n        Seq(s\"\"\"scalacOptions ++= Seq(${options0.mkString(\", \")})\"\"\")\n      }\n\n    SbtProject(\n      settings = Seq(scalacOptionsSettings)\n    )\n  }\n\n  private def testFrameworkSettings(options: BuildOptions): SbtProject = {\n\n    val testClassPath: Seq[Path] = options.artifacts(logger, Scope.Test) match {\n      case Right(artifacts) => artifacts.classPath.map(_.toNIO)\n      case Left(exception)  =>\n        logger.debug(exception.message)\n        Seq.empty\n    }\n\n    val parentInspector =\n      new AsmTestRunner.ParentInspector(testClassPath, TestRunnerLogger(logger.verbosity))\n    val frameworkName0 = options.testOptions.frameworks.headOption.orElse {\n      frameworkNames(testClassPath, parentInspector, logger).toOption\n        .flatMap(_.headOption) // TODO: handle multiple frameworks here\n    }\n\n    val testFrameworkSettings = frameworkName0 match {\n      case None     => Nil\n      case Some(fw) =>\n        Seq(s\"\"\"testFrameworks += new TestFramework(\"$fw\")\"\"\")\n    }\n\n    SbtProject(\n      settings = Seq(testFrameworkSettings)\n    )\n  }\n\n  private def dependencySettings(options: BuildOptions, scope: Scope): SbtProject = {\n\n    val depSettings = {\n      def toDepString(deps: ShadowingSeq[Positioned[AnyDependency]], isCompileOnly: Boolean) =\n        deps.toSeq.toList.map(_.value).map { dep =>\n          val org  = dep.organization\n          val name = dep.name\n          val ver  = dep.version\n          // TODO dep.userParams\n          // TODO dep.exclude\n          // TODO dep.attributes\n          val (sep, suffixOpt) = dep.nameAttributes match {\n            case NoAttributes           => (\"%\", None)\n            case s: ScalaNameAttributes =>\n              val suffixOpt0 =\n                if (s.fullCrossVersion.getOrElse(false)) Some(\".cross(CrossVersion.full)\")\n                else None\n              val sep = \"%%\"\n              (sep, suffixOpt0)\n          }\n          val scope0 =\n            // FIXME This ignores the isCompileOnly when scope == Scope.Test\n            if (scope == Scope.Test) \"% Test\"\n            else if (isCompileOnly) \"% Provided\"\n            else \"\"\n\n          val baseDep = s\"\"\"$q$org$q $sep $q$name$q % $q$ver$q $scope0\"\"\"\n          suffixOpt.fold(baseDep)(suffix => s\"($baseDep)$suffix\")\n        }\n\n      val allDepStrings = toDepString(options.classPathOptions.extraDependencies, false) ++\n        toDepString(options.classPathOptions.extraCompileOnlyDependencies, true)\n\n      if (allDepStrings.isEmpty) Nil\n      else if (allDepStrings.lengthCompare(1) == 0)\n        Seq(s\"\"\"libraryDependencies += ${allDepStrings.head}\"\"\")\n      else {\n        val count   = allDepStrings.length\n        val allDeps = allDepStrings\n          .iterator\n          .zipWithIndex\n          .map {\n            case (dep, idx) =>\n              val maybeComma = if (idx == count - 1) \"\" else \",\"\n              \"  \" + dep + maybeComma + nl\n          }\n          .mkString\n        Seq(s\"\"\"libraryDependencies ++= Seq($nl$allDeps)\"\"\")\n      }\n    }\n\n    SbtProject(\n      settings = Seq(depSettings)\n    )\n  }\n\n  def `export`(\n    optionsMain: BuildOptions,\n    optionsTest: BuildOptions,\n    sourcesMain: Sources,\n    sourcesTest: Sources\n  ): Either[BuildException, SbtProject] = {\n\n    // TODO Handle Scala CLI cross-builds\n\n    val projectChunks = Seq(\n      SbtProject(settings = Seq(extraSettings)),\n      sources(sourcesMain, sourcesTest),\n      sbtVersionProject,\n      scalaVersionSettings(optionsMain),\n      scalacOptionsSettings(optionsMain),\n      mainClassSettings(optionsMain),\n      pureJavaSettings(optionsMain, sourcesMain),\n      javaOptionsSettings(optionsMain),\n      if (optionsMain.platform.value == Platform.JS)\n        scalaJsSettings(optionsMain.scalaJsOptions)\n      else\n        SbtProject(),\n      if (optionsMain.platform.value == Platform.Native)\n        scalaNativeSettings(optionsMain.scalaNativeOptions)\n      else\n        SbtProject(),\n      customJarsSettings(optionsMain),\n      customResourcesSettings(optionsMain),\n      testFrameworkSettings(optionsTest),\n      repositorySettings(optionsMain),\n      dependencySettings(optionsMain, Scope.Main),\n      dependencySettings(optionsTest, Scope.Test)\n    )\n\n    Right(projectChunks.foldLeft(SbtProject())(_ + _))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/Argv0.scala",
    "content": "package scala.cli.internal\n\nclass Argv0 {\n  def get(defaultValue: String): String = defaultValue\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/CachedBinary.scala",
    "content": "package scala.cli.internal\n\nimport java.math.BigInteger\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\n\nimport scala.build.Build\nimport scala.build.input.{OnDisk, ResourceDirectory}\nimport scala.build.internal.Constants\n\nobject CachedBinary {\n\n  final case class CacheData(changed: Boolean, projectSha: String)\n\n  private def resolveProjectShaPath(workDir: os.Path) = workDir / \".project_sha\"\n  private def resolveOutputShaPath(workDir: os.Path)  = workDir / \".output_sha\"\n\n  private def fileSha(filePath: os.Path): String = {\n    val md = MessageDigest.getInstance(\"SHA-1\")\n    md.update(os.read.bytes(filePath))\n\n    val digest        = md.digest()\n    val calculatedSum = new BigInteger(1, digest)\n    String.format(s\"%040x\", calculatedSum)\n  }\n\n  private def hashResources(build: Build.Successful) = {\n    def hashResourceDir(path: os.Path) =\n      os.walk(path)\n        .filter(os.isFile(_))\n        .map { filePath =>\n          val md = MessageDigest.getInstance(\"SHA-1\")\n          md.update(os.read.bytes(filePath))\n          s\"$filePath:\" + new BigInteger(1, md.digest()).toString()\n        }\n\n    val classpathResourceDirsIt =\n      build.options\n        .classPathOptions\n        .resourcesDir\n        .flatMap(dir => hashResourceDir(dir))\n        .iterator ++\n        Iterator(\"\\n\")\n\n    val projectResourceDirsIt = build.inputs.elements.iterator.flatMap {\n      case elem: OnDisk =>\n        val content = elem match {\n          case resDirInput: ResourceDirectory =>\n            hashResourceDir(resDirInput.path)\n          case _ => List.empty\n        }\n        Iterator(elem.path.toString) ++ content.iterator ++ Iterator(\"\\n\")\n      case _ =>\n        Iterator.empty\n    }\n\n    (classpathResourceDirsIt ++ projectResourceDirsIt)\n      .map(_.getBytes(StandardCharsets.UTF_8))\n  }\n\n  private def projectSha(builds: Seq[Build.Successful], config: List[String]): String = {\n    val md      = MessageDigest.getInstance(\"SHA-1\")\n    val charset = StandardCharsets.UTF_8\n    md.update(builds.map(_.inputs.sourceHash()).reduce(_ + _).getBytes(charset))\n    md.update(\"<resources>\".getBytes())\n    // Resource changes for SN require relinking, so they should also be hashed\n    builds.foreach(build => hashResources(build).foreach(md.update))\n    md.update(\"</resources>\".getBytes())\n    md.update(0: Byte)\n    md.update(\"<config>\".getBytes(charset))\n    for (elem <- config) {\n      md.update(elem.getBytes(charset))\n      md.update(0: Byte)\n    }\n    md.update(\"</config>\".getBytes(charset))\n    md.update(Constants.version.getBytes)\n    md.update(0: Byte)\n    for (h <- builds.map(_.options).reduce(_.orElse(_)).hash) {\n      md.update(h.getBytes(charset))\n      md.update(0: Byte)\n    }\n\n    val digest        = md.digest()\n    val calculatedSum = new BigInteger(1, digest)\n    String.format(s\"%040x\", calculatedSum)\n  }\n\n  def updateProjectAndOutputSha(\n    dest: os.Path,\n    workDir: os.Path,\n    currentProjectSha: String\n  ): Unit = {\n    val projectShaPath = resolveProjectShaPath(workDir)\n    os.write.over(projectShaPath, currentProjectSha, createFolders = true)\n\n    val outputShaPath = resolveOutputShaPath(workDir)\n    val sha           = fileSha(dest)\n    os.write.over(outputShaPath, sha)\n  }\n\n  def getCacheData(\n    builds: Seq[Build.Successful],\n    config: List[String],\n    dest: os.Path,\n    workDir: os.Path\n  ): CacheData = {\n    val projectShaPath = resolveProjectShaPath(workDir)\n    val outputShaPath  = resolveOutputShaPath(workDir)\n\n    val currentProjectSha = projectSha(builds, config)\n    val currentOutputSha  = if os.exists(dest) then Some(fileSha(dest)) else None\n\n    val previousProjectSha =\n      if os.exists(projectShaPath) then Some(os.read(projectShaPath)) else None\n    val previousOutputSha = if os.exists(outputShaPath) then Some(os.read(outputShaPath)) else None\n\n    val changed =\n      !previousProjectSha.contains(currentProjectSha) ||\n      previousOutputSha != currentOutputSha ||\n      !os.exists(dest)\n\n    CacheData(changed, currentProjectSha)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/CliLogger.scala",
    "content": "package scala.cli.internal\n\nimport bloop.rifle.BloopRifleLogger\nimport ch.epfl.scala.bsp4j as b\nimport coursier.cache.CacheLogger\nimport coursier.cache.loggers.{FallbackRefreshDisplay, RefreshLogger}\nimport org.scalajs.logging.{Level as ScalaJsLevel, Logger as ScalaJsLogger, ScalaConsoleLogger}\n\nimport java.io.PrintStream\n\nimport scala.build.bsp.protocol.TextEdit\nimport scala.build.errors.{BuildException, CompositeBuildException, Diagnostic, Severity}\nimport scala.build.internal.CustomProgressBarRefreshDisplay\nimport scala.build.internal.util.WarningMessages\nimport scala.build.internals.FeatureType\nimport scala.build.{ConsoleBloopBuildClient, Logger, Position}\nimport scala.collection.mutable\nimport scala.scalanative.build as sn\n\nclass CliLogger(\n  val verbosity: Int,\n  quiet: Boolean,\n  progress: Option[Boolean],\n  out: PrintStream\n) extends Logger { logger =>\n\n  override def log(diagnostics: Seq[Diagnostic]): Unit = {\n    val hashMap = new mutable.HashMap[os.Path, Seq[String]]\n    diagnostics.foreach { d =>\n      printDiagnostic(\n        d.positions,\n        d.severity,\n        d.message,\n        hashMap,\n        d.textEdit\n      )\n    }\n  }\n\n  def error(message: String) =\n    out.println(message)\n  def message(message: => String) =\n    if (verbosity >= 0)\n      out.println(message)\n  def log(message: => String) =\n    if (verbosity >= 1)\n      out.println(message)\n  def log(message: => String, debugMessage: => String) =\n    if (verbosity >= 2)\n      out.println(debugMessage)\n    else if (verbosity >= 1)\n      out.println(message)\n  def debug(message: => String) =\n    if (verbosity >= 2)\n      out.println(message)\n\n  def printDiagnostic(\n    positions: Seq[Position],\n    severity: Severity,\n    message: String,\n    contentCache: mutable.Map[os.Path, Seq[String]],\n    textEditOpt: Option[Diagnostic.TextEdit]\n  ) =\n    if (positions.isEmpty)\n      out.println(\n        s\"${ConsoleBloopBuildClient.diagnosticPrefix(severity)} $message\"\n      )\n    else {\n      val positions0    = positions.distinct\n      val filePositions = positions0.collect {\n        case f: Position.File => f\n      }\n      val otherPositions = positions0.filter {\n        case _: Position.File => false\n        case _                => true\n      }\n\n      for (f <- filePositions) {\n        val startPos = new b.Position(f.startPos._1, f.startPos._2)\n        val endPos   = new b.Position(f.endPos._1, f.endPos._2)\n        val range    = new b.Range(startPos, endPos)\n        val diag     = new b.Diagnostic(range, message)\n        diag.setSeverity(severity.toBsp4j)\n        diag.setSource(\"scala-cli\")\n\n        for (textEdit <- textEditOpt) {\n          val bTextEdit = TextEdit(range, textEdit.newText)\n          diag.setData(bTextEdit.toJsonTree())\n        }\n\n        for (file <- f.path) {\n          val lines = contentCache.getOrElseUpdate(file, os.read(file).linesIterator.toVector)\n          if (f.startPos._1 < lines.length)\n            diag.setCode(lines(f.startPos._1))\n        }\n        ConsoleBloopBuildClient.printFileDiagnostic(\n          this,\n          f.path,\n          diag\n        )\n      }\n\n      if (otherPositions.nonEmpty)\n        ConsoleBloopBuildClient.printOtherDiagnostic(\n          this,\n          message,\n          severity,\n          otherPositions\n        )\n    }\n\n  private def printEx(\n    ex: BuildException,\n    contentCache: mutable.Map[os.Path, Seq[String]]\n  ): Unit =\n    ex match {\n      case c: CompositeBuildException =>\n        // FIXME We might want to order things here… Or maybe just collect all b.Diagnostics\n        // below, and order them before printing them.\n        for (ex <- c.exceptions)\n          printEx(ex, contentCache)\n      case _ =>\n        printDiagnostic(ex.positions, Severity.Error, ex.getMessage(), contentCache, None)\n    }\n\n  def log(ex: BuildException): Unit =\n    if (verbosity >= 0)\n      printEx(ex, new mutable.HashMap)\n\n  def debug(ex: BuildException): Unit =\n    if (verbosity >= 2)\n      printEx(ex, new mutable.HashMap)\n  def exit(ex: BuildException): Nothing =\n    flushExperimentalWarnings\n    if (verbosity < 0)\n      sys.exit(1)\n    else if (verbosity == 0) {\n      printEx(ex, new mutable.HashMap)\n      sys.exit(1)\n    }\n    else\n      throw new Exception(ex)\n\n  def coursierLogger(printBefore: String) =\n    if (quiet)\n      CacheLogger.nop\n    else if (progress.getOrElse(coursier.paths.Util.useAnsiOutput()))\n      RefreshLogger.create(\n        CustomProgressBarRefreshDisplay.create(\n          keepOnScreen = verbosity >= 1,\n          if (printBefore.nonEmpty) System.err.println(printBefore),\n          ()\n        )\n      )\n    else\n      RefreshLogger.create(new FallbackRefreshDisplay)\n\n  def bloopRifleLogger =\n    new BloopRifleLogger {\n      def info(msg: => String)  = logger.message(msg)\n      def debug(msg: => String) =\n        if (verbosity >= 3) logger.debug(msg)\n      def debug(msg: => String, ex: Throwable) =\n        if (verbosity >= 3) {\n          logger.debug(msg)\n          if (ex != null)\n            ex.printStackTrace(out)\n        }\n      def error(msg: => String, ex: Throwable) = {\n        logger.error(s\"Error: $msg ($ex)\")\n        if (verbosity >= 1 && ex != null)\n          ex.printStackTrace(out)\n      }\n      def error(msg: => String) = logger.error(msg)\n      def bloopBspStdout        =\n        if (verbosity >= 2) Some(out)\n        else None\n      def bloopBspStderr =\n        if (verbosity >= 2) Some(out)\n        else None\n      def bloopCliInheritStdout = verbosity >= 3\n      def bloopCliInheritStderr = verbosity >= 3\n    }\n\n  def scalaJsLogger: ScalaJsLogger =\n    // FIXME Doesn't use 'out'\n    new ScalaConsoleLogger(\n      minLevel =\n        if (verbosity >= 2) ScalaJsLevel.Debug\n        else if (verbosity >= 1) ScalaJsLevel.Info\n        else if (verbosity >= 0) ScalaJsLevel.Warn\n        else ScalaJsLevel.Error\n    )\n\n  def scalaNativeTestLogger: sn.Logger =\n    new sn.Logger {\n      def trace(msg: Throwable) = ()\n      def debug(msg: String)    = logger.debug(msg)\n      def info(msg: String)     = logger.log(msg)\n      def warn(msg: String)     = logger.log(msg)\n      def error(msg: String)    = logger.message(msg)\n    }\n\n  val scalaNativeCliInternalLoggerOptions: List[String] =\n    if (verbosity >= 1) List(\"-v\", \"-v\", \"-v\") // debug\n    else if (verbosity >= 0) List(\"-v\", \"-v\")  // info\n    else List()                                // error\n\n  // Allow to disable that?\n  def compilerOutputStream = out\n\n  private var experimentalWarnings: Map[FeatureType, Set[String]]              = Map.empty\n  private var reported: Map[FeatureType, Set[String]]                          = Map.empty\n  def experimentalWarning(featureName: String, featureType: FeatureType): Unit =\n    if (!reported.get(featureType).exists(_.contains(featureName)))\n      experimentalWarnings ++= experimentalWarnings.updatedWith(featureType) {\n        case None           => Some(Set(featureName))\n        case Some(namesSet) => Some(namesSet + featureName)\n      }\n  def flushExperimentalWarnings: Unit = if (experimentalWarnings.nonEmpty) {\n    val messageStr: String = {\n      val namesAndTypes = for {\n        (featureType, names) <- experimentalWarnings.toSeq.sortBy(_._1) // by feature type\n        name                 <- names\n      } yield name -> featureType\n      WarningMessages.experimentalFeaturesUsed(namesAndTypes)\n    }\n    message(messageStr)\n    reported = for {\n      (featureType, names) <- experimentalWarnings\n      reportedNames = reported.getOrElse(featureType, Set.empty[String])\n    } yield featureType -> (names ++ reportedNames)\n    experimentalWarnings = Map.empty\n  }\n\n  override def cliFriendlyDiagnostic(\n    message: String,\n    cliFriendlyMessage: String,\n    severity: Severity,\n    positions: Seq[Position]\n  ): Unit =\n    diagnostic(cliFriendlyMessage, severity, Nil)\n}\n\nobject CliLogger {\n  def default: CliLogger = new CliLogger(0, false, None, System.err)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/PPrintStringPrefixHelper.scala",
    "content": "package scala.cli.internal\n\n// Remove once we can use https://github.com/com-lihaoyi/PPrint/pull/80\n\nfinal class PPrintStringPrefixHelper {\n  def apply(i: Iterable[Object]): String =\n    i.collectionClassName\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/Pid.scala",
    "content": "package scala.cli.internal\n\nimport java.lang.management.ManagementFactory\n\nclass Pid {\n  def get(): Integer =\n    try {\n      val pid = ManagementFactory.getRuntimeMXBean.getName.takeWhile(_ != '@').toInt\n      pid: Integer\n    }\n    catch {\n      case _: NumberFormatException => null\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/ProcUtil.scala",
    "content": "package scala.cli.internal\nimport java.nio.charset.StandardCharsets\nimport java.util.concurrent.{CancellationException, CompletableFuture, CompletionException}\n\nimport scala.build.Logger\nimport scala.build.internals.EnvVar\nimport scala.util.Properties\nimport scala.util.control.NonFatal\n\nobject ProcUtil {\n\n  def maybeUpdatePreamble(file: os.Path): Boolean = {\n    val header = os.read.bytes(file, offset = 0, count = \"#!/usr/bin/env sh\".length).toSeq\n    val hasBinEnvShHeader =\n      header.startsWith(\"#!/usr/bin/env sh\".getBytes(StandardCharsets.UTF_8))\n    val hasBinShHeader =\n      header.startsWith(\"#!/bin/sh\".getBytes(StandardCharsets.UTF_8))\n    val usesSh = hasBinEnvShHeader || hasBinShHeader\n\n    if (usesSh) {\n      val content        = os.read.bytes(file)\n      val updatedContent =\n        if (hasBinEnvShHeader)\n          \"#!/usr/bin/env bash\".getBytes(StandardCharsets.UTF_8) ++\n            content.drop(\"#!/usr/bin/env sh\".length)\n        else if (hasBinShHeader)\n          \"#!/bin/bash\".getBytes(StandardCharsets.UTF_8) ++\n            content.drop(\"#!/bin/sh\".length)\n        else\n          sys.error(\"Can't happen\")\n      os.write.over(file, updatedContent, createFolders = true)\n    }\n\n    usesSh\n  }\n\n  def forceKillProcess(process: Process, logger: Logger): Unit = {\n    if (process.isAlive) {\n      process.destroyForcibly()\n      logger.debug(s\"Killing user process ${process.pid()}\")\n    }\n  }\n\n  def interruptProcess(process: Process, logger: Logger): Unit = {\n    val pid = process.pid()\n    try\n      if (process.isAlive) {\n        logger.debug(\"Interrupting running process\")\n        if (Properties.isWin) {\n          os.proc(\"taskkill\", \"/PID\", pid).call()\n          logger.debug(s\"Run following command to interrupt process: 'taskkill /PID $pid'\")\n        }\n        else {\n          os.proc(\"kill\", \"-2\", pid).call()\n          logger.debug(s\"Run following command to interrupt process: 'kill -2 $pid'\")\n        }\n      }\n    catch { // ignore the failure if the process isn't running, might mean it exited between the first check and the call of the command to kill it\n      case NonFatal(e) =>\n        logger.debug(s\"Ignoring error during interrupt process: $e\")\n    }\n  }\n\n  def waitForProcess(process: Process, onExit: CompletableFuture[?]): Unit = {\n    process.waitFor()\n    try onExit.join()\n    catch {\n      case _: CancellationException | _: CompletionException => // ignored\n    }\n  }\n\n  def findApplicationPathsOnPATH(appName: String): List[String] = {\n    import java.io.File.pathSeparator, java.io.File.pathSeparatorChar\n\n    var path: String = EnvVar.Misc.path.valueOpt.getOrElse(\"\")\n    val pwd: String  = os.pwd.toString\n\n    // on unix & macs, an empty PATH counts as \".\", the working directory\n    if (path.length == 0)\n      path = pwd\n    else {\n      // scala 'split' doesn't handle leading or trailing pathSeparators\n      // correctly so expand them now.\n      if (path.head == pathSeparatorChar) path = pwd + path\n      if (path.last == pathSeparatorChar) path = path + pwd\n      // on unix and macs, an empty PATH item is like \".\" (current dir).\n      path = s\"$pathSeparator$pathSeparator\".r\n        .replaceAllIn(path, pathSeparator + pwd + pathSeparator)\n    }\n\n    val appPaths = path\n      .split(pathSeparator)\n      .map(d => if (d == \".\") pwd else d) // on unix a bare \".\" counts as the current dir\n      .map(_ + s\"/$appName\")\n      .filter(f => os.isFile(os.Path(f, os.pwd)))\n      .toSet\n\n    appPaths.toList\n  }\n\n  // Copied from https://github.com/scalacenter/bloop/blob/a249e0a710ce169ca05d0606778f96f44a398680/shared/src/main/scala/bloop/io/Environment.scala\n  private lazy val shebangCapableShells = Seq(\n    \"/bin/sh\",\n    \"/bin/ash\",\n    \"/bin/bash\",\n    \"/bin/dash\",\n    \"/bin/mksh\",\n    \"/bin/pdksh\",\n    \"/bin/posh\",\n    \"/bin/tcsh\",\n    \"/bin/zsh\",\n    \"/bin/fish\"\n  )\n\n  def isShebangCapableShell = EnvVar.Misc.shell.valueOpt match\n    case Some(currentShell) if shebangCapableShells.exists(sh => currentShell.contains(sh)) => true\n    case _                                                                                  => false\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/ProfileFileUpdater.scala",
    "content": "package scala.cli.internal\n\nimport java.nio.charset.Charset\nimport java.nio.file.{FileAlreadyExistsException, Files, Path}\n\n// initially adapted from https://github.com/coursier/coursier/blob/d9a0fcc1af4876bec7f19a18f2c93d808e06df8d/modules/env/src/main/scala/coursier/env/ProfileUpdater.scala#L44-L137\n\nobject ProfileFileUpdater {\n\n  private def startEndIndices(start: String, end: String, content: String): Option[(Int, Int)] = {\n    val startIdx = content.indexOf(start)\n    if (startIdx >= 0) {\n      val endIdx = content.indexOf(end, startIdx + 1)\n      if (endIdx >= 0)\n        Some(startIdx, endIdx + end.length)\n      else\n        None\n    }\n    else\n      None\n  }\n\n  def addToProfileFile(\n    file: Path,\n    title: String,\n    addition: String,\n    charset: Charset\n  ): Boolean = {\n\n    def updated(content: String): Option[String] = {\n      val start    = s\"# >>> $title >>>\\n\"\n      val endStr   = s\"# <<< $title <<<\\n\"\n      val withTags = \"\\n\" +\n        start +\n        addition.stripSuffix(\"\\n\") + \"\\n\" + endStr\n      if (content.contains(withTags))\n        None\n      else\n        Some {\n          startEndIndices(start, endStr, content) match {\n            case None =>\n              content + withTags\n            case Some((startIdx, endIdx)) =>\n              content.take(startIdx) +\n                withTags +\n                content.drop(endIdx)\n          }\n        }\n    }\n\n    var updatedSomething = false\n    val contentOpt       = Some(file)\n      .filter(Files.exists(_))\n      .map(f => new String(Files.readAllBytes(f), charset))\n    for (updatedContent <- updated(contentOpt.getOrElse(\"\"))) {\n      Option(file.getParent).map(createDirectories(_))\n      Files.write(file, updatedContent.getBytes(charset))\n      updatedSomething = true\n    }\n    updatedSomething\n  }\n\n  def removeFromProfileFile(\n    file: Path,\n    title: String,\n    charset: Charset\n  ): Boolean = {\n\n    def updated(content: String): Option[String] = {\n      val start = s\"# >>> $title >>>\\n\"\n      val end   = s\"# <<< $title <<<\\n\"\n      startEndIndices(start, end, content).map {\n        case (startIdx, endIdx) =>\n          content.take(startIdx).stripSuffix(\"\\n\") +\n            content.drop(endIdx)\n      }\n    }\n\n    var updatedSomething = false\n    val contentOpt       = Some(file)\n      .filter(Files.exists(_))\n      .map(f => new String(Files.readAllBytes(f), charset))\n    for (updatedContent <- updated(contentOpt.getOrElse(\"\"))) {\n      Option(file.getParent).map(createDirectories(_))\n      Files.write(file, updatedContent.getBytes(charset))\n      updatedSomething = true\n    }\n    updatedSomething\n  }\n\n  private def createDirectories(path: Path): Unit =\n    try Files.createDirectories(path)\n    catch {\n      // Ignored, see https://bugs.openjdk.java.net/browse/JDK-8130464\n      case _: FileAlreadyExistsException if Files.isDirectory(path) =>\n    }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/internal/ScalaJsLinker.scala",
    "content": "package scala.cli.internal\n\nimport coursier.VersionConstraint\nimport coursier.cache.{ArchiveCache, FileCache}\nimport coursier.util.Task\nimport dependency.*\nimport org.scalajs.testing.adapter.TestAdapterInitializer as TAI\n\nimport java.io.{File, InputStream, OutputStream}\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.{BuildException, ScalaJsLinkingError}\nimport scala.build.internal.Util.{DependencyOps, ModuleOps}\nimport scala.build.internal.{ExternalBinaryParams, FetchExternalBinary, Runner, ScalaJsLinkerConfig}\nimport scala.build.options.scalajs.ScalaJsLinkerOptions\nimport scala.build.{Logger, Positioned, RepositoryUtils}\nimport scala.io.Source\nimport scala.util.Properties\n\nobject ScalaJsLinker {\n\n  case class LinkJSInput(\n    options: ScalaJsLinkerOptions,\n    javaCommand: String,\n    classPath: Seq[os.Path],\n    mainClassOrNull: String,\n    addTestInitializer: Boolean,\n    config: ScalaJsLinkerConfig,\n    fullOpt: Boolean,\n    noOpt: Boolean,\n    scalaJsVersion: String\n  )\n\n  private def linkerMainClass = \"org.scalajs.cli.Scalajsld\"\n\n  private def linkerCommand(\n    options: ScalaJsLinkerOptions,\n    javaCommand: String,\n    logger: Logger,\n    cache: FileCache[Task],\n    archiveCache: ArchiveCache[Task],\n    scalaJsVersion: String\n  ): Either[BuildException, Seq[String]] = either {\n\n    options.linkerPath match {\n      case Some(path) =>\n        Seq(path.toString)\n      case None =>\n        val scalaJsCliVersion = options.finalScalaJsCliVersion\n        val scalaJsCliDep     = {\n          val mod = mod\"org.virtuslab.scala-cli:scalajscli_2.13\"\n          dependency.Dependency(mod, s\"$scalaJsCliVersion+\")\n        }\n\n        val forcedVersions = Seq(\n          mod\"org.scala-js:scalajs-linker_2.13\" -> scalaJsVersion\n        )\n\n        val extraRepos =\n          if scalaJsVersion.endsWith(\"SNAPSHOT\") || scalaJsCliVersion.endsWith(\"SNAPSHOT\")\n          then\n            Seq(\n              RepositoryUtils.snapshotsRepository,\n              RepositoryUtils.scala3NightlyRepository\n            )\n          else Nil\n\n        options.finalUseJvm match {\n          case Right(()) =>\n            val (_, linkerRes) = value {\n              scala.build.Artifacts.fetchCsDependencies(\n                dependencies = Seq(Positioned.none(scalaJsCliDep.toCs)),\n                extraRepositories = extraRepos,\n                forceScalaVersionOpt = None,\n                forcedVersions = forcedVersions.map { case (m, v) =>\n                  (m.toCs, VersionConstraint(v))\n                },\n                logger = logger,\n                cache = cache,\n                classifiersOpt = None\n              )\n            }\n            val linkerClassPath = linkerRes.files\n\n            val command = Seq[os.Shellable](\n              javaCommand,\n              options.javaArgs,\n              \"-cp\",\n              linkerClassPath.map(_.getAbsolutePath).mkString(File.pathSeparator),\n              linkerMainClass\n            )\n\n            command.flatMap(_.value)\n\n          case Left(osArch) =>\n            val useLatest = scalaJsVersion == \"latest\"\n            val ext       = if (Properties.isWin) \".zip\" else \".gz\"\n            val tag       = if (useLatest) \"launchers\" else s\"v$scalaJsCliVersion\"\n            val url       =\n              s\"https://github.com/virtusLab/scala-js-cli/releases/download/$tag/scala-js-ld-$osArch$ext\"\n            val params = ExternalBinaryParams(\n              url,\n              useLatest,\n              \"scala-js-ld\",\n              Seq(scalaJsCliDep),\n              linkerMainClass,\n              forcedVersions = forcedVersions,\n              extraRepos = extraRepos\n            )\n            val binary = value {\n              FetchExternalBinary.fetch(params, archiveCache, logger, () => javaCommand)\n            }\n            binary.command\n        }\n    }\n  }\n\n  private def getCommand(\n    input: LinkJSInput,\n    linkingDir: os.Path,\n    logger: Logger,\n    cache: FileCache[Task],\n    archiveCache: ArchiveCache[Task],\n    useLongRunning: Boolean\n  ) = either {\n    val command = value {\n      linkerCommand(\n        input.options,\n        input.javaCommand,\n        logger,\n        cache,\n        archiveCache,\n        input.scalaJsVersion\n      )\n    }\n\n    val allArgs = {\n      val outputArgs    = Seq(\"--outputDir\", linkingDir.toString)\n      val longRunning   = if (useLongRunning) Seq(\"--longRunning\") else Seq.empty[String]\n      val mainClassArgs =\n        Option(input.mainClassOrNull).toSeq.flatMap(mainClass =>\n          Seq(\"--mainMethod\", mainClass + \".main\")\n        )\n      val testInitializerArgs =\n        if (input.addTestInitializer)\n          Seq(\"--mainMethodWithNoArgs\", TAI.ModuleClassName + \".\" + TAI.MainMethodName)\n        else\n          Nil\n      val optArg =\n        if (input.noOpt) \"--noOpt\"\n        else if (input.fullOpt) \"--fullOpt\"\n        else \"--fastOpt\"\n\n      Seq[os.Shellable](\n        outputArgs,\n        mainClassArgs,\n        testInitializerArgs,\n        optArg,\n        input.config.linkerCliArgs,\n        input.classPath.map(_.toString),\n        longRunning\n      )\n    }\n\n    command ++ allArgs.flatMap(_.value)\n  }\n\n  def link(\n    input: LinkJSInput,\n    linkingDir: os.Path,\n    logger: Logger,\n    cache: FileCache[Task],\n    archiveCache: ArchiveCache[Task]\n  ): Either[BuildException, Unit] = either {\n    val useLongRunning = !input.fullOpt\n\n    if (useLongRunning)\n      longRunningProcess.startOrReuse(input, linkingDir, logger, cache, archiveCache)\n    else {\n      val cmd =\n        value(getCommand(input, linkingDir, logger, cache, archiveCache, useLongRunning = false))\n      val res     = Runner.run(cmd, logger)\n      val retCode = res.waitFor()\n\n      if (retCode == 0)\n        logger.debug(\"Scala.js linker ran successfully\")\n      else {\n        logger.debug(s\"Scala.js linker exited with return code $retCode\")\n        value(Left(new ScalaJsLinkingError))\n      }\n    }\n  }\n\n  private object longRunningProcess {\n    case class Proc(process: Process, stdin: OutputStream, stdout: InputStream) {\n      val stdoutLineIterator: Iterator[String] = Source.fromInputStream(stdout).getLines()\n    }\n    case class Input(input: LinkJSInput, linkingDir: os.Path)\n    var currentInput: Option[Input] = None\n    var currentProc: Option[Proc]   = None\n\n    def startOrReuse(\n      linkJsInput: LinkJSInput,\n      linkingDir: os.Path,\n      logger: Logger,\n      cache: FileCache[Task],\n      archiveCache: ArchiveCache[Task]\n    ) = either {\n      val input = Input(linkJsInput, linkingDir)\n\n      def createProcess(): Proc = {\n        val cmd =\n          value(getCommand(\n            linkJsInput,\n            linkingDir,\n            logger,\n            cache,\n            archiveCache,\n            useLongRunning = true\n          ))\n        val process = Runner.run(cmd, logger, inheritStreams = false)\n        val stdin   = process.getOutputStream()\n        val stdout  = process.getInputStream()\n        val proc    = Proc(process, stdin, stdout)\n        currentProc = Some(proc)\n        currentInput = Some(input)\n        proc\n      }\n\n      def loop(proc: Proc): Unit =\n        if (proc.stdoutLineIterator.hasNext) {\n          val line = proc.stdoutLineIterator.next()\n\n          if (line == \"SCALA_JS_LINKING_DONE\")\n            logger.debug(\"Scala.js linker ran successfully\")\n          else {\n            // inherit other stdout from Scala.js\n            println(line)\n\n            loop(proc)\n          }\n        }\n        else {\n          val retCode = proc.process.waitFor()\n          logger.debug(s\"Scala.js linker exited with return code $retCode\")\n          value(Left(new ScalaJsLinkingError))\n        }\n\n      val proc = currentProc match {\n        case Some(proc) if currentInput.contains(input) && proc.process.isAlive() =>\n          // trigger new linking\n          proc.stdin.write('\\n')\n          proc.stdin.flush()\n\n          proc\n        case Some(proc) =>\n          proc.stdin.close()\n          proc.stdout.close()\n          proc.process.destroy()\n          createProcess()\n        case _ =>\n          createProcess()\n      }\n\n      loop(proc)\n    }\n  }\n\n  def updateSourceMappingURL(mainJsPath: os.Path) =\n    val content = os.read(mainJsPath)\n    content.replace(\n      \"//# sourceMappingURL=main.js.map\",\n      s\"//# sourceMappingURL=${mainJsPath.last}.map\"\n    )\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/javaLauncher/JavaLauncherCli.scala",
    "content": "package scala.cli.javaLauncher\n\nimport java.io.File\n\nimport scala.build.Positioned\nimport scala.build.internal.{OsLibc, Runner}\nimport scala.build.options.{BuildOptions, JavaOptions}\nimport scala.cli.commands.shared.LoggingOptions\nimport scala.cli.javaLauncher.JavaLauncherCli.LauncherKind.*\nobject JavaLauncherCli {\n\n  def runAndExit(remainingArgs: Seq[String]): Nothing = {\n    val logger       = LoggingOptions().logger\n    val scalaCliPath =\n      System.getProperty(\"java.class.path\").split(File.pathSeparator).iterator.toList.map { f =>\n        os.Path(f, os.pwd)\n      }\n\n    val buildOptions = BuildOptions(\n      javaOptions = JavaOptions(\n        jvmIdOpt = Some(OsLibc.defaultJvm(OsLibc.jvmIndexOs)).map(Positioned.none)\n      )\n    )\n    val launcherKind = sys.props.get(\"scala-cli.kind\") match {\n      case Some(\"jvm.bootstrapped\")       => Bootstrapped\n      case Some(\"jvm.standaloneLauncher\") => StandaloneLauncher\n      case _                              => sys.error(\"should not happen\")\n    }\n    val classPath = launcherKind match {\n      case Bootstrapped       => scalaCliPath\n      case StandaloneLauncher => scalaCliPath.headOption.toList\n    }\n    val mainClass = launcherKind match {\n      case Bootstrapped       => \"scala.cli.ScalaCli\"\n      case StandaloneLauncher => \"coursier.bootstrap.launcher.ResourcesLauncher\"\n    }\n\n    val exitCode =\n      Runner.runJvm(\n        buildOptions.javaHome().value.javaCommand,\n        buildOptions.javaOptions.javaOpts.toSeq.map(_.value.value),\n        classPath,\n        mainClass,\n        remainingArgs,\n        logger,\n        allowExecve = true\n      ).waitFor()\n\n    sys.exit(exitCode)\n  }\n\n  enum LauncherKind {\n    case Bootstrapped, StandaloneLauncher\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala",
    "content": "package scala.cli.launcher\n\nimport coursier.Repositories\nimport coursier.cache.FileCache\nimport coursier.util.{Artifact, Task}\nimport coursier.version.Version\nimport dependency.*\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.BuildException\nimport scala.build.internal.CsLoggerUtil.CsCacheExtensions\nimport scala.build.internal.Util.safeFullDetailedArtifacts\nimport scala.build.internal.{Constants, OsLibc, Runner}\nimport scala.build.options.ScalaVersionUtil.fileWithTtl0\nimport scala.build.options.{BuildOptions, JavaOptions}\nimport scala.build.{Artifacts, Os, Positioned, RepositoryUtils}\nimport scala.cli.ScalaCli\nimport scala.cli.commands.shared.{CoursierOptions, LoggingOptions}\nimport scala.xml.XML\n\nobject LauncherCli {\n  def runAndExit(\n    version: String,\n    options: LauncherOptions,\n    remainingArgs: Seq[String]\n  ): Either[BuildException, Nothing] =\n    either {\n      val logger          = LoggingOptions().logger\n      val cache           = CoursierOptions().coursierCache(logger)\n      val scalaVersion    = options.cliScalaVersion.getOrElse(scalaCliScalaVersion(version))\n      val scalaParameters = ScalaParameters(scalaVersion)\n      val snapshotsRepo   = Seq(\n        Repositories.central,\n        RepositoryUtils.snapshotsRepository,\n        RepositoryUtils.scala3NightlyRepository\n      )\n\n      val cliVersion: String =\n        if version == \"nightly\"\n        then resolveNightlyScalaCliVersion(cache, scalaParameters.scalaBinaryVersion)\n        else version\n      val scalaCliDependency = Seq(dep\"org.virtuslab.scala-cli::cli:$cliVersion\")\n\n      val fetchedScalaCli =\n        Artifacts.fetchAnyDependencies(\n          dependencies = scalaCliDependency.map(Positioned.none),\n          extraRepositories = snapshotsRepo,\n          paramsOpt = Some(scalaParameters),\n          logger = logger,\n          cache = cache.withMessage(s\"Fetching ${ScalaCli.fullRunnerName} $cliVersion\"),\n          classifiersOpt = None\n        ) match {\n          case Right(value) => value\n          case Left(value)  =>\n            System.err.println(value.message)\n            sys.exit(1)\n        }\n\n      val scalaCli: Seq[os.Path] =\n        value(fetchedScalaCli.fullDetailedArtifacts0.safeFullDetailedArtifacts)\n          .collect { case (_, _, _, Some(f)) => os.Path(f, os.pwd) }\n\n      val buildOptions = BuildOptions(\n        javaOptions = JavaOptions(\n          jvmIdOpt = Some(OsLibc.defaultJvm(OsLibc.jvmIndexOs)).map(Positioned.none)\n        )\n      )\n\n      val exitCode =\n        Runner.runJvm(\n          javaCommand = buildOptions.javaHome().value.javaCommand,\n          javaArgs = buildOptions.javaOptions.javaOpts.toSeq.map(_.value.value),\n          classPath = scalaCli,\n          mainClass = \"scala.cli.ScalaCli\",\n          args = remainingArgs,\n          logger = logger,\n          allowExecve = true\n        ).waitFor()\n\n      sys.exit(exitCode)\n    }\n\n  def scalaCliScalaVersion(cliVersion: String): String =\n    if cliVersion == \"nightly\" then Constants.defaultScalaVersion\n    else if Version(cliVersion) <= Version(\"0.1.2\") then Constants.defaultScala212Version\n    else if Version(cliVersion) <= Version(\"0.1.4\") then Constants.defaultScala213Version\n    else Constants.defaultScalaVersion\n\n  def resolveNightlyScalaCliVersion(\n    cache: FileCache[Task],\n    scalaBinaryVersion: String\n  ): String = {\n    val cliSubPath       = s\"org/virtuslab/scala-cli/cli_$scalaBinaryVersion\"\n    val mavenMetadataUrl =\n      s\"${RepositoryUtils.snapshotsRepositoryUrl}/$cliSubPath/maven-metadata.xml\"\n    val artifact = Artifact(mavenMetadataUrl).withChanging(true)\n    cache.fileWithTtl0(artifact) match {\n      case Left(_) =>\n        System.err.println(s\"Unable to find nightly ${ScalaCli.fullRunnerName} version\")\n        sys.exit(1)\n      case Right(mavenMetadataXml) =>\n        val metadataXmlContent = os.read(os.Path(mavenMetadataXml, Os.pwd))\n        val parsed             = XML.loadString(metadataXmlContent)\n        val rawVersions        = (parsed \\ \"versioning\" \\ \"versions\" \\ \"version\").map(_.text)\n        val versions           = rawVersions.map(Version(_))\n        if versions.isEmpty\n        then sys.error(s\"No versions found in $mavenMetadataUrl (locally at $mavenMetadataXml)\")\n        else versions.max.repr\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/launcher/LauncherOptions.scala",
    "content": "package scala.cli.launcher\n\nimport caseapp.*\nimport com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n@HelpMessage(\"Run another Scala CLI version\")\nfinal case class LauncherOptions(\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\"Set the Scala CLI version\")\n  @ValueDescription(\"nightly|version\")\n  @Tag(tags.implementation)\n  @Tag(tags.inShortHelp)\n  cliVersion: Option[String] = None,\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\"The version of Scala on which Scala CLI was published\")\n  @ValueDescription(\"2.12|2.13|3\")\n  @Hidden\n  @Tag(tags.implementation)\n  cliScalaVersion: Option[String] = None,\n  @Recurse\n  scalaRunner: ScalaRunnerLauncherOptions = ScalaRunnerLauncherOptions(),\n  @Recurse\n  powerOptions: PowerOptions = PowerOptions()\n) {\n  def toCliArgs: List[String] =\n    cliVersion.toList.flatMap(v => List(\"--cli-version\", v)) ++\n      cliScalaVersion.toList.flatMap(v => List(\"--cli-scala-version\", v)) ++\n      scalaRunner.toCliArgs ++\n      powerOptions.toCliArgs\n}\n\nobject LauncherOptions {\n  implicit lazy val parser: Parser[LauncherOptions]            = Parser.derive\n  implicit lazy val help: Help[LauncherOptions]                = Help.derive\n  implicit lazy val jsonCodec: JsonValueCodec[LauncherOptions] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/launcher/PowerOptions.scala",
    "content": "package scala.cli.launcher\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.tags\n\n/** Options extracted from [[LauncherOptions]] to allow for parsing them separately. Thanks to this\n  * and additional parsing we can read the --power flag placed anywhere in the command invocation.\n  *\n  * This option is duplicated in [[scala.cli.commands.shared.GlobalOptions]] so that we can ensure\n  * that no subcommand defines its own --power option Checking for clashing names is done in unit\n  * tests.\n  */\ncase class PowerOptions(\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\"Allows to use restricted & experimental features\")\n  @Tag(tags.must)\n  power: Boolean = false\n) {\n  def toCliArgs: List[String] = if power then List(\"--power\") else Nil\n}\n\nobject PowerOptions {\n  implicit val parser: Parser[PowerOptions] = Parser.derive\n  implicit val help: Help[PowerOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/launcher/ScalaRunnerLauncherOptions.scala",
    "content": "package scala.cli.launcher\n\nimport caseapp.*\n\nimport scala.cli.commands.shared.HelpGroup\nimport scala.cli.commands.{Constants, tags}\n\ncase class ScalaRunnerLauncherOptions(\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\n    s\"The default version of Scala used when processing user inputs (current default: ${Constants.defaultScalaVersion}). Can be overridden with --scala-version. \"\n  )\n  @ValueDescription(\"version\")\n  @Hidden\n  @Tag(tags.implementation)\n  @Name(\"cliDefaultScalaVersion\")\n  cliUserScalaVersion: Option[String] = None,\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\"\")\n  @Hidden\n  @Tag(tags.implementation)\n  @Name(\"r\")\n  @Name(\"repo\")\n  @Name(\"repository\")\n  @Name(\"predefinedRepository\")\n  cliPredefinedRepository: List[String] = Nil,\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\n    \"This allows to override the program name identified by Scala CLI as itself (the default is 'scala-cli')\"\n  )\n  @Hidden\n  @Tag(tags.implementation)\n  progName: Option[String] = None,\n  @Group(HelpGroup.Launcher.toString)\n  @HelpMessage(\n    \"This allows to skip checking for newest Scala CLI versions. --offline covers this scenario as well.\"\n  )\n  @Hidden\n  @Tag(tags.implementation)\n  skipCliUpdates: Option[Boolean] = None,\n  @Hidden\n  @Tag(tags.implementation)\n  predefinedCliVersion: Option[String] = None,\n  @Hidden\n  @Tag(tags.implementation)\n  @Name(\"initialLauncher\")\n  initialLauncherPath: Option[String] = None\n) {\n  def toCliArgs: List[String] =\n    cliUserScalaVersion.toList.flatMap(v => List(\"--cli-default-scala-version\", v)) ++\n      cliPredefinedRepository.flatMap(v => List(\"--repository\", v)) ++\n      progName.toList.flatMap(v => List(\"--prog-name\", v)) ++\n      skipCliUpdates.toList.filter(v => v).map(_ => \"--skip-cli-updates\") ++\n      predefinedCliVersion.toList.flatMap(v => List(\"--predefined-cli-version\", v)) ++\n      initialLauncherPath.toList.flatMap(v => List(\"--initial-launcher-path\", v))\n}\n\nobject ScalaRunnerLauncherOptions {\n  implicit val parser: Parser[ScalaRunnerLauncherOptions] = Parser.derive\n  implicit val help: Help[ScalaRunnerLauncherOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/packaging/Library.scala",
    "content": "package scala.cli.packaging\n\nimport java.io.OutputStream\nimport java.nio.file.StandardOpenOption.{CREATE, TRUNCATE_EXISTING}\nimport java.nio.file.attribute.FileTime\nimport java.util.jar.{Attributes as JarAttributes, JarOutputStream}\nimport java.util.zip.{ZipEntry, ZipOutputStream}\n\nimport scala.build.Build\nimport scala.cli.internal.CachedBinary\n\nobject Library {\n  def libraryJar(\n    builds: Seq[Build.Successful],\n    mainClassOpt: Option[String] = None\n  ): os.Path = {\n    val workDir   = builds.head.inputs.libraryJarWorkDir\n    val dest      = workDir / \"library.jar\"\n    val cacheData =\n      CachedBinary.getCacheData(\n        builds,\n        mainClassOpt.toList.flatMap(c => List(\"--main-class\", c)),\n        dest,\n        workDir\n      )\n\n    if cacheData.changed then {\n      var outputStream: OutputStream = null\n      try {\n        outputStream = os.write.outputStream(\n          dest,\n          createFolders = true,\n          openOptions = Seq(CREATE, TRUNCATE_EXISTING)\n        )\n        writeLibraryJarTo(\n          outputStream,\n          builds,\n          mainClassOpt\n        )\n      }\n      finally\n        if outputStream != null then outputStream.close()\n\n      CachedBinary.updateProjectAndOutputSha(dest, workDir, cacheData.projectSha)\n    }\n\n    dest\n  }\n\n  def writeLibraryJarTo(\n    outputStream: OutputStream,\n    builds: Seq[Build.Successful],\n    mainClassOpt: Option[String] = None,\n    hasActualManifest: Boolean = true,\n    contentDirOverride: Option[os.Path] = None\n  ): Unit = {\n\n    val manifest = new java.util.jar.Manifest\n    manifest.getMainAttributes.put(JarAttributes.Name.MANIFEST_VERSION, \"1.0\")\n\n    if hasActualManifest then\n      for {\n        mainClass <- mainClassOpt.orElse(builds.flatMap(_.sources.defaultMainClass).headOption)\n        if mainClass.nonEmpty\n      } manifest.getMainAttributes.put(JarAttributes.Name.MAIN_CLASS, mainClass)\n\n    var zos: ZipOutputStream = null\n    val contentDirs          = builds.map(b => contentDirOverride.getOrElse(b.output)).distinct\n\n    try {\n      zos = new JarOutputStream(outputStream, manifest)\n      for {\n        contentDir <- contentDirs\n        path       <- os.walk(contentDir) if os.isFile(path)\n      } {\n        val name         = path.relativeTo(contentDir).toString\n        val lastModified = os.mtime(path)\n        val ent          = new ZipEntry(name)\n        ent.setLastModifiedTime(FileTime.fromMillis(lastModified))\n\n        val content = os.read.bytes(path)\n        ent.setSize(content.length)\n\n        zos.putNextEntry(ent)\n        zos.write(content)\n        zos.closeEntry()\n      }\n    }\n    finally if (zos != null) zos.close()\n  }\n\n  extension (build: Build.Successful) {\n    private def fullClassPathAsJar: Seq[os.Path] =\n      Seq(libraryJar(Seq(build))) ++ build.dependencyClassPath\n    def fullClassPathMaybeAsJar(asJar: Boolean): Seq[os.Path] =\n      if asJar then fullClassPathAsJar else build.fullClassPath\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala",
    "content": "package scala.cli.packaging\n\nimport java.io.File\n\nimport scala.build.internal.{ManifestJar, Runner}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.build.internals.MsvcEnvironment\nimport scala.build.internals.MsvcEnvironment.*\nimport scala.build.{Build, Logger, Positioned, coursierVersion}\nimport scala.cli.errors.GraalVMNativeImageError\nimport scala.cli.graal.{BytecodeProcessor, TempCache}\nimport scala.cli.internal.CachedBinary\nimport scala.util.Properties\n\nobject NativeImage {\n\n  private def ensureHasNativeImageCommand(\n    graalVMHome: os.Path,\n    logger: Logger\n  ): os.Path = {\n\n    val ext         = if (Properties.isWin) \".cmd\" else \"\"\n    val nativeImage = graalVMHome / \"bin\" / s\"native-image$ext\"\n\n    if (os.exists(nativeImage))\n      logger.debug(s\"$nativeImage found\")\n    else {\n      val proc = os.proc(graalVMHome / \"bin\" / s\"gu$ext\", \"install\", \"native-image\")\n      logger.debug(s\"$nativeImage not found, running ${proc.command.flatMap(_.value)}\")\n      proc.call(stdin = os.Inherit, stdout = os.Inherit)\n      if (!os.exists(nativeImage))\n        logger.message(\n          s\"Seems gu install command didn't install $nativeImage, trying to run it anyway\"\n        )\n    }\n\n    nativeImage\n  }\n\n  /** Alias currentHome to the root of a drive, so that its files can be accessed with shorter paths\n    * (hopefully not going above the ~260 char limit of some Windows apps, such as cl.exe).\n    *\n    * Couldn't manage to make\n    * https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=powershell#enable-long-paths-in-windows-10-version-1607-and-later\n    * work, so I went down the path here.\n    */\n  private def maybeWithShorterGraalvmHome[T](\n    currentHome: os.Path,\n    logger: Logger\n  )(\n    f: os.Path => T\n  ): T =\n    // Lower threshold (was 180) to ensure native-image's internal paths don't exceed 260-char limit\n    if (Properties.isWin && currentHome.toString.length >= 90) {\n      val (driveLetter, newHome) = getShortenedPath(currentHome, logger)\n      val savedCodepage: String  = getCodePage(logger)\n      val result                 =\n        try\n          f(newHome)\n        finally {\n          unaliasDriveLetter(driveLetter)\n          setCodePage(savedCodepage)\n        }\n      result\n    }\n    else\n      f(currentHome)\n\n  def buildNativeImage(\n    builds: Seq[Build.Successful],\n    mainClass: String,\n    dest: os.Path,\n    nativeImageWorkDir: os.Path,\n    extraOptions: Seq[String],\n    logger: Logger\n  ): Unit = {\n\n    os.makeDir.all(nativeImageWorkDir)\n\n    val jvmId   = builds.head.options.notForBloopOptions.packageOptions.nativeImageOptions.jvmId\n    val options = builds.head.options.copy(\n      javaOptions = builds.head.options.javaOptions.copy(\n        jvmIdOpt = Some(Positioned.none(jvmId))\n      )\n    )\n\n    val javaHome        = options.javaHome().value\n    val nativeImageArgs =\n      options.notForBloopOptions.packageOptions.nativeImageOptions.graalvmArgs.map(_.value)\n\n    val cacheData = CachedBinary.getCacheData(\n      builds,\n      s\"--java-home=${javaHome.javaHome.toString}\" :: \"--\" ::\n        extraOptions.toList ++ nativeImageArgs,\n      dest,\n      nativeImageWorkDir\n    )\n\n    if cacheData.changed then {\n      val mainJar           = Library.libraryJar(builds)\n      val originalClassPath = mainJar +: builds.flatMap(_.dependencyClassPath).distinct\n\n      ManifestJar.maybeWithManifestClassPath(\n        createManifest = Properties.isWin,\n        classPath = originalClassPath,\n        // seems native-image doesn't correctly parse paths in manifests - this is especially a problem on Windows\n        wrongSimplePathsInManifest = true\n      ) { processedClassPath =>\n        val needsProcessing =\n          builds.head.scalaParams.map(_.scalaVersion.coursierVersion)\n            .exists(sv => (sv >= \"3.0.0\".coursierVersion) && (sv < \"3.3.0\".coursierVersion))\n        if needsProcessing then\n          logger.message(\n            s\"\"\"$warnPrefix building native images with Scala 3 older than 3.3.0 is deprecated.\n               |$warnPrefix support will be dropped in a future Scala CLI version.\n               |$warnPrefix it is advised to upgrade to a more recent Scala version.\"\"\".stripMargin\n          )\n        val (classPath, toClean, scala3extraOptions) =\n          if needsProcessing then {\n            val cpString         = processedClassPath.mkString(File.pathSeparator)\n            val processed        = BytecodeProcessor.processClassPath(cpString, TempCache).toSeq\n            val nativeConfigFile = os.temp(suffix = \".json\")\n            os.write.over(\n              nativeConfigFile,\n              \"\"\"[\n                |  {\n                |    \"name\": \"sun.misc.Unsafe\",\n                |    \"allDeclaredConstructors\": true,\n                |    \"allPublicConstructors\": true,\n                |    \"allDeclaredMethods\": true,\n                |    \"allDeclaredFields\": true\n                |  }\n                |]\n                |\"\"\".stripMargin\n            )\n            val cp      = processed.map(_.path)\n            val options = Seq(s\"-H:ReflectionConfigurationFiles=$nativeConfigFile\")\n\n            (cp, nativeConfigFile +: BytecodeProcessor.toClean(processed), options)\n          }\n          else (processedClassPath, Seq[os.Path](), Seq[String]())\n\n        def stripSuffixIgnoreCase(s: String, suffix: String): String =\n          if (s.toLowerCase.endsWith(suffix.toLowerCase))\n            s.substring(0, s.length - suffix.length)\n          else\n            s\n\n        try {\n          val args = extraOptions ++ scala3extraOptions ++ Seq(\n            s\"-H:Path=${dest / os.up}\",\n            s\"-H:Name=${stripSuffixIgnoreCase(dest.last, \".exe\")}\", // Case-insensitive strip suffix\n            \"-cp\",\n            classPath.map(_.toString).mkString(File.pathSeparator),\n            mainClass\n          ) ++ nativeImageArgs\n\n          maybeWithShorterGraalvmHome(javaHome.javaHome, logger) { graalVMHome =>\n\n            val nativeImageCommand = ensureHasNativeImageCommand(graalVMHome, logger)\n            val command            = nativeImageCommand.toString +: args\n\n            val exitCode =\n              if Properties.isWin then\n                MsvcEnvironment.msvcNativeImageProcess(\n                  command = command,\n                  workingDir = nativeImageWorkDir,\n                  logger = logger\n                )\n              else Runner.run(command, logger).waitFor()\n            if exitCode == 0 then {\n              val actualDest =\n                if Properties.isWin then\n                  if dest.last.endsWith(\".exe\") then dest\n                  else dest / os.up / s\"${dest.last}.exe\"\n                else dest\n              CachedBinary.updateProjectAndOutputSha(\n                actualDest,\n                nativeImageWorkDir,\n                cacheData.projectSha\n              )\n            }\n            else throw new GraalVMNativeImageError\n          }\n        }\n        finally util.Try(toClean.foreach(os.remove.all))\n      }\n    }\n    else\n      logger.message(\"Found cached native image binary.\")\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/publish/BouncycastleExternalSigner.scala",
    "content": "package scala.cli.publish\n\nimport coursier.publish.Content\nimport coursier.publish.signing.Signer\n\nimport scala.build.Logger\nimport scala.cli.signing.shared.PasswordOption\nimport scala.util.Properties\n\nfinal case class BouncycastleExternalSigner(\n  secretKey: PasswordOption,\n  passwordOpt: Option[PasswordOption],\n  command: Seq[String],\n  logger: Logger\n) extends Signer {\n\n  private def withFileContent[T](content: Content)(f: os.Path => T): T =\n    content match {\n      case file: Content.File  => f(os.Path(file.path, os.pwd))\n      case m: Content.InMemory =>\n        val permsOpt =\n          if (Properties.isWin) None\n          else Some(\"rw-------\": os.PermSet)\n        val tmpFile = os.temp(m.content0, perms = permsOpt.orNull)\n        try f(tmpFile)\n        finally os.remove(tmpFile)\n    }\n\n  def sign(content: Content): Either[String, String] =\n    withFileContent(content) { path =>\n      val passwordArgs = passwordOpt.toSeq.flatMap(p => Seq(\"--password\", p.asString.value))\n      val proc         =\n        os.proc(\n          command,\n          \"pgp\",\n          \"sign\",\n          passwordArgs,\n          \"--secret-key\",\n          secretKey.asString.value,\n          \"--stdout\",\n          path\n        )\n      logger.debug(s\"Running command ${proc.command.flatMap(_.value)}\")\n      val res    = proc.call(stdin = os.Inherit, check = false)\n      val output = res.out.trim()\n      if (res.exitCode == 0) Right(output)\n      else Left(output)\n    }\n}\n\nobject BouncycastleExternalSigner {\n\n  def apply(\n    secretKey: PasswordOption,\n    passwordOrNull: PasswordOption,\n    command: Array[String],\n    logger: Logger\n  ): BouncycastleExternalSigner =\n    BouncycastleExternalSigner(\n      secretKey,\n      Option(passwordOrNull),\n      command.toSeq,\n      logger\n    )\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/publish/BouncycastleSignerMaker.scala",
    "content": "package scala.cli.publish\n\nimport coursier.publish.signing.Signer\nimport org.bouncycastle.jce.provider.BouncyCastleProvider\n\nimport java.security.Security\nimport java.util.function.Supplier\n\nimport scala.build.Logger\nimport scala.cli.signing.shared.PasswordOption\nimport scala.cli.signing.util.BouncycastleSigner\n\n/** Used for choosing the right BouncyCastleSigner when Scala CLI is run on JVM. <br>\n  *\n  * See [[scala.cli.internal.BouncycastleSignerMakerSubst BouncycastleSignerMakerSubst]]\n  */\nclass BouncycastleSignerMaker {\n  def get(\n    forceSigningExternally: java.lang.Boolean,\n    passwordOrNull: PasswordOption,\n    secretKey: PasswordOption,\n    command: Supplier[Array[String]], // unused here, but used in the GraalVM substitution\n    logger: Logger                    // unused here, but used in the GraalVM substitution\n  ): Signer =\n    if (forceSigningExternally)\n      BouncycastleExternalSigner(secretKey, passwordOrNull, command.get, logger)\n    else\n      BouncycastleSigner(secretKey.getBytes(), Option(passwordOrNull).map(_.get()))\n\n  def maybeInit(): Unit =\n    Security.addProvider(new BouncyCastleProvider)\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/util/ArgHelpers.scala",
    "content": "package scala.cli.util\n\nimport caseapp.core.Arg\nimport caseapp.core.help.HelpFormat\nimport caseapp.core.util.CaseUtil\n\nimport scala.build.input.ScalaCliInvokeData\nimport scala.build.internal.util.WarningMessages\nimport scala.cli.ScalaCli\nimport scala.cli.commands.shared.{HelpCommandGroup, HelpGroup}\nimport scala.cli.commands.{SpecificationLevel, tags}\n\nobject ArgHelpers {\n  extension (arg: Arg) {\n    private def hasTag(tag: String): Boolean             = arg.tags.exists(_.name == tag)\n    private def hasTagPrefix(tagPrefix: String): Boolean =\n      arg.tags.exists(_.name.startsWith(tagPrefix))\n    def isExperimental: Boolean = arg.hasTag(tags.experimental)\n    def isRestricted: Boolean   = arg.hasTag(tags.restricted)\n    def isDeprecated: Boolean   = arg.hasTagPrefix(tags.deprecatedPrefix)\n\n    def deprecatedNames: List[String] = arg.tags\n      .filter(_.name.startsWith(tags.deprecatedPrefix))\n      .map(_.name.stripPrefix(s\"${tags.deprecatedPrefix}${tags.valueSeparator}\"))\n      .toList\n\n    def deprecatedOptionAliases: List[String] = arg.deprecatedNames.map {\n      case name if name.startsWith(\"-\") => name\n      case name if name.length == 1     => \"-\" + name\n      case name => \"--\" + CaseUtil.pascalCaseSplit(name.toCharArray.toList).map(\n          _.toLowerCase\n        ).mkString(\"-\")\n    }\n\n    def isExperimentalOrRestricted: Boolean = arg.isRestricted || arg.isExperimental\n\n    def isSupported: Boolean = ScalaCli.allowRestrictedFeatures || !arg.isExperimentalOrRestricted\n    def isImportant: Boolean = arg.hasTag(tags.inShortHelp)\n\n    def isMust: Boolean = arg.hasTag(tags.must)\n\n    def level: SpecificationLevel = arg.tags\n      .flatMap(t => tags.levelFor(t.name))\n      .headOption\n      .getOrElse(SpecificationLevel.IMPLEMENTATION)\n    def powerOptionUsedInSip(optionName: String)(using ScalaCliInvokeData): String = {\n      val specificationLevel =\n        if arg.isExperimental then SpecificationLevel.EXPERIMENTAL\n        else if arg.isRestricted then SpecificationLevel.RESTRICTED\n        else\n          arg.tags\n            .flatMap(t => tags.levelFor(t.name))\n            .headOption\n            .getOrElse(SpecificationLevel.EXPERIMENTAL)\n      WarningMessages.powerOptionUsedInSip(optionName, specificationLevel)\n    }\n  }\n\n  extension (helpFormat: HelpFormat) {\n    def withPrimaryGroup(primaryGroup: HelpGroup): HelpFormat =\n      helpFormat.withPrimaryGroups(Seq(primaryGroup))\n    def withPrimaryGroups(primaryGroups: Seq[HelpGroup]): HelpFormat = {\n      val primaryStringGroups     = primaryGroups.map(_.toString)\n      val oldSortedGroups         = helpFormat.sortedGroups.getOrElse(Seq.empty)\n      val filteredOldSortedGroups = oldSortedGroups.filterNot(primaryStringGroups.contains)\n      helpFormat.copy(sortedGroups = Some(primaryStringGroups ++ filteredOldSortedGroups))\n    }\n    def withHiddenGroups(hiddenGroups: Seq[HelpGroup]): HelpFormat =\n      helpFormat.copy(hiddenGroups = Some(hiddenGroups.map(_.toString)))\n\n    def withHiddenGroup(hiddenGroup: HelpGroup): HelpFormat =\n      helpFormat.withHiddenGroups(Seq(hiddenGroup))\n    def withHiddenGroupsWhenShowHidden(hiddenGroups: Seq[HelpGroup]): HelpFormat =\n      helpFormat.copy(hiddenGroupsWhenShowHidden = Some(hiddenGroups.map(_.toString)))\n    def withHiddenGroupWhenShowHidden(hiddenGroup: HelpGroup): HelpFormat =\n      helpFormat.withHiddenGroupsWhenShowHidden(Seq(hiddenGroup))\n    def withSortedGroups(sortedGroups: Seq[HelpGroup]): HelpFormat =\n      helpFormat.copy(sortedGroups = Some(sortedGroups.map(_.toString)))\n    def withSortedCommandGroups(sortedGroups: Seq[HelpCommandGroup]): HelpFormat =\n      helpFormat.copy(sortedCommandGroups = Some(sortedGroups.map(_.toString)))\n    def withNamesLimit(namesLimit: Int): HelpFormat =\n      helpFormat.copy(namesLimit = Some(namesLimit))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/util/ArgParsers.scala",
    "content": "package scala.cli.util\n\nimport caseapp.core.argparser.{ArgParser, SimpleArgParser}\n\nabstract class LowPriorityArgParsers {\n\n  /** case-app [[ArgParser]] for [[MaybeConfigPasswordOption]]\n    *\n    * Given a lower priority than the one for `Option[MaybeConfigPasswordOption]`, as the latter\n    * falls back to `None` when given an empty string (like in `--password \"\"`), while letting it be\n    * automatically derived from this one (with the former parser and the generic [[ArgParser]] for\n    * `Option[T]` from case-app) would fail on such empty input.\n    */\n  implicit lazy val maybeConfigPasswordOptionArgParser: ArgParser[MaybeConfigPasswordOption] =\n    SimpleArgParser.from(\"password\") { str =>\n      MaybeConfigPasswordOption.parse(str)\n        .left.map(caseapp.core.Error.Other(_))\n    }\n\n}\n\nobject ArgParsers extends LowPriorityArgParsers {\n\n  /** case-app [[ArgParser]] for `Option[MaybeConfigPasswordOption]`\n    *\n    * Unlike a parser automatically derived through case-app [[ArgParser]] for `Option[T]`, the\n    * parser here accepts empty input (like in `--password \"\"`), and returns a `None` value in that\n    * case.\n    */\n  implicit lazy val optionMaybeConfigPasswordOptionArgParser\n    : ArgParser[Option[MaybeConfigPasswordOption]] =\n    SimpleArgParser.from(\"password\") { str =>\n      if (str.trim.isEmpty) Right(None)\n      else maybeConfigPasswordOptionArgParser(None, -1, -1, str).map(Some(_))\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/util/ConfigDbUtils.scala",
    "content": "package scala.cli.util\n\nimport scala.build.errors.{BuildException, ConfigDbException}\nimport scala.build.{Directories, Logger}\nimport scala.cli.commands.publish.ConfigUtil.wrapConfigException\nimport scala.cli.config.{ConfigDb, Key}\n\nobject ConfigDbUtils {\n  private def getLatestConfigDb: Either[ConfigDbException, ConfigDb] =\n    ConfigDb.open(Directories.directories.dbPath.toNIO).wrapConfigException\n\n  lazy val configDb: Either[ConfigDbException, ConfigDb] = getLatestConfigDb\n\n  extension [T](either: Either[Exception, T]) {\n    private def handleConfigDbException(f: BuildException => Unit): Option[T] =\n      either match\n        case Left(e: BuildException) =>\n          f(e)\n          None\n        case Left(e: Exception) =>\n          f(new ConfigDbException(e))\n          None\n        case Right(value) => Some(value)\n  }\n\n  def getConfigDbOpt(logger: Logger): Option[ConfigDb] =\n    configDb.handleConfigDbException(logger.debug)\n\n  def getLatestConfigDbOpt(logger: Logger): Option[ConfigDb] =\n    getLatestConfigDb.handleConfigDbException(logger.debug)\n\n  extension (db: ConfigDb) {\n    def getOpt[T](configDbKey: Key[T], f: BuildException => Unit): Option[T] =\n      db.get(configDbKey).handleConfigDbException(f).flatten\n    def getOpt[T](configDbKey: Key[T], logger: Logger): Option[T] =\n      getOpt(configDbKey, logger.debug(_))\n    def getOpt[T](configDbKey: Key[T]): Option[T] =\n      getOpt(configDbKey, _.printStackTrace(System.err))\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/util/ConfigPasswordOptionHelpers.scala",
    "content": "package scala.cli.util\n\nimport scala.build.errors.BuildException\nimport scala.build.options.publish.ConfigPasswordOption\nimport scala.cli.commands.SpecificationLevel\nimport scala.cli.commands.publish.ConfigUtil.*\nimport scala.cli.config.{ConfigDb, Key, PasswordOption}\nimport scala.cli.errors.MissingConfigEntryError\n\nobject ConfigPasswordOptionHelpers {\n\n  implicit class ConfigPasswordOptionOps(private val opt: ConfigPasswordOption)\n      extends AnyVal {\n    def get(configDb: => ConfigDb): Either[BuildException, PasswordOption] =\n      opt match {\n        case a: ConfigPasswordOption.ActualOption =>\n          Right(a.option.toConfig)\n        case c: ConfigPasswordOption.ConfigOption =>\n          val key = new Key.PasswordEntry(c.prefix, c.name, SpecificationLevel.IMPLEMENTATION)\n          configDb.get(key).wrapConfigException.flatMap {\n            case None        => Left(new MissingConfigEntryError(c.fullName))\n            case Some(value) => Right(value)\n          }\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/util/MaybeConfigPasswordOption.scala",
    "content": "package scala.cli.util\n\nimport scala.build.options.publish.ConfigPasswordOption\nimport scala.cli.signing.shared.PasswordOption\n\n/** Can be either a [[PasswordOption]], or something like \"config:…\" pointing at a config entry */\nsealed abstract class MaybeConfigPasswordOption extends Product with Serializable {\n  def configPasswordOptions() =\n    this match {\n      case MaybeConfigPasswordOption.ActualOption(option) =>\n        ConfigPasswordOption.ActualOption(option)\n      case MaybeConfigPasswordOption.ConfigOption(fullName) =>\n        ConfigPasswordOption.ConfigOption(fullName)\n    }\n}\n\nobject MaybeConfigPasswordOption {\n  final case class ActualOption(option: PasswordOption) extends MaybeConfigPasswordOption\n  final case class ConfigOption(fullName: String)       extends MaybeConfigPasswordOption\n\n  def parse(input: String): Either[String, MaybeConfigPasswordOption] =\n    if (input.startsWith(\"config:\"))\n      Right(ConfigOption(input.stripPrefix(\"config:\")))\n    else\n      PasswordOption.parse(input).map(ActualOption(_))\n}\n"
  },
  {
    "path": "modules/cli/src/main/scala/scala/cli/util/SeqHelpers.scala",
    "content": "package scala.cli.util\n\nobject SeqHelpers {\n  implicit class StringSeqOpt(val seq: Seq[String]) extends AnyVal {\n    def appendOnInit(s: String): Seq[String] =\n      if seq.isEmpty || seq.tail.isEmpty then seq\n      else (seq.head + s) +: seq.tail.appendOnInit(s)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/commands/tests/DocTests.scala",
    "content": "package scala.cli.commands.tests\n\nimport com.eed3si9n.expecty.Expecty.assert as expect\n\nimport scala.build.CrossBuildParams\nimport scala.build.internal.Constants\nimport scala.cli.commands.doc.Doc\n\nclass DocTests extends munit.FunSuite {\n\n  test(\"crossDocSubdirName: single cross group yields empty subdir\") {\n    val params = CrossBuildParams(Constants.defaultScala213Version, \"jvm\")\n    expect(Doc.crossDocSubdirName(\n      params,\n      multipleCrossGroups = false,\n      needsPlatformInSuffix = false\n    ) == \"\")\n    expect(Doc.crossDocSubdirName(\n      params,\n      multipleCrossGroups = false,\n      needsPlatformInSuffix = true\n    ) == \"\")\n  }\n\n  test(\"crossDocSubdirName: multiple groups, single platform uses only Scala version\") {\n    val params = CrossBuildParams(Constants.scala3Lts, \"jvm\")\n    expect(\n      Doc.crossDocSubdirName(params, multipleCrossGroups = true, needsPlatformInSuffix = false) ==\n        Constants.scala3Lts\n    )\n  }\n\n  test(\"crossDocSubdirName: multiple groups and platforms include platform in suffix\") {\n    val paramsJvm = CrossBuildParams(Constants.defaultScala213Version, \"jvm\")\n    val paramsJs  = CrossBuildParams(Constants.defaultScala213Version, \"js\")\n    val paramsNat = CrossBuildParams(Constants.scala3Lts, \"native\")\n    expect(\n      Doc.crossDocSubdirName(paramsJvm, multipleCrossGroups = true, needsPlatformInSuffix = true) ==\n        s\"${Constants.defaultScala213Version}_jvm\"\n    )\n    expect(\n      Doc.crossDocSubdirName(paramsJs, multipleCrossGroups = true, needsPlatformInSuffix = true) ==\n        s\"${Constants.defaultScala213Version}_js\"\n    )\n    expect(\n      Doc.crossDocSubdirName(paramsNat, multipleCrossGroups = true, needsPlatformInSuffix = true) ==\n        s\"${Constants.scala3Lts}_native\"\n    )\n  }\n\n  for (javaVersion <- Constants.mainJavaVersions)\n    test(s\"correct external mappings for JVM $javaVersion\") {\n      val args        = Doc.defaultScaladocArgs(Constants.defaultScalaVersion, javaVersion)\n      val mappingsArg = args.find(_.startsWith(\"-external-mappings:\")).get\n      if javaVersion >= 11 then\n        expect(mappingsArg.contains(s\"javase/$javaVersion/docs/api/java.base/\"))\n      else\n        expect(mappingsArg.contains(s\"javase/$javaVersion/docs/api/\"))\n        expect(!mappingsArg.contains(\"java.base/\"))\n      expect(mappingsArg.contains(s\"scala-lang.org/api/${Constants.defaultScalaVersion}/\"))\n    }\n\n  test(s\"correct external mappings for Scala 3 LTS (${Constants.scala3Lts})\") {\n    val args        = Doc.defaultScaladocArgs(Constants.scala3Lts, Constants.defaultJavaVersion)\n    val mappingsArg = args.find(_.startsWith(\"-external-mappings:\")).get\n    expect(mappingsArg.contains(s\"scala-lang.org/api/${Constants.scala3Lts}/\"))\n    expect(\n      mappingsArg.contains(s\"javase/${Constants.defaultJavaVersion}/docs/api/java.base/\")\n    )\n  }\n\n  test(s\"correct external mappings for default Scala (${Constants.defaultScalaVersion})\") {\n    val args =\n      Doc.defaultScaladocArgs(Constants.defaultScalaVersion, Constants.defaultJavaVersion)\n    val mappingsArg = args.find(_.startsWith(\"-external-mappings:\")).get\n    expect(mappingsArg.contains(s\"scala-lang.org/api/${Constants.defaultScalaVersion}/\"))\n    expect(\n      mappingsArg.contains(s\"javase/${Constants.defaultJavaVersion}/docs/api/java.base/\")\n    )\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/commands/tests/ReplOptionsTests.scala",
    "content": "package scala.cli.commands.tests\n\nimport com.eed3si9n.expecty.Expecty.assert as expect\n\nimport scala.build.internal.Constants\nimport scala.cli.commands.repl.{Repl, ReplOptions, SharedReplOptions}\nimport scala.cli.commands.shared.{SharedOptions, SharedPythonOptions}\n\nclass ReplOptionsTests extends munit.FunSuite {\n  test(\"ScalaPy version\") {\n    val ver         = \"X.Y.Z\"\n    val replOptions = ReplOptions(\n      shared = SharedOptions(\n        sharedPython = SharedPythonOptions(\n          scalaPyVersion = Some(ver)\n        )\n      )\n    )\n    val buildOptions = Repl.buildOptions(replOptions).value\n    expect(buildOptions.notForBloopOptions.scalaPyVersion.contains(ver))\n  }\n\n  test(\"Downgrade Scala version if needed\") {\n    val replOptions = ReplOptions(\n      sharedRepl = SharedReplOptions(\n        ammonite = Some(true)\n      )\n    )\n    val maxVersion    = \"3.1.3\"\n    val maxLtsVersion = Constants.scala3Lts\n    val buildOptions  = Repl.buildOptions0(replOptions, maxVersion, maxLtsVersion)\n    expect(buildOptions.scalaOptions.scalaVersion.flatMap(_.versionOpt).contains(maxVersion))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/commands/tests/RunOptionsTests.scala",
    "content": "package scala.cli.commands.tests\n\nimport com.eed3si9n.expecty.Expecty.assert as expect\n\nimport scala.cli.commands.run.{Run, RunOptions}\nimport scala.cli.commands.shared.{SharedOptions, SharedPythonOptions}\n\nclass RunOptionsTests extends munit.FunSuite {\n  test(\"ScalaPy version\") {\n    val ver        = \"X.Y.Z\"\n    val runOptions = RunOptions(\n      shared = SharedOptions(\n        sharedPython = SharedPythonOptions(\n          scalaPyVersion = Some(ver)\n        )\n      )\n    )\n    val buildOptions = Run.buildOptions(runOptions).value\n    expect(buildOptions.notForBloopOptions.scalaPyVersion.contains(ver))\n  }\n\n  test(\"resolve toolkit dependency\") {\n    val runOptions = RunOptions(\n      shared = SharedOptions(\n        withToolkit = Some(\"latest\")\n      )\n    )\n    val buildOptions = Run.buildOptions(runOptions).value\n    val dep          = buildOptions.classPathOptions.extraDependencies.toSeq.headOption\n    assert(dep.nonEmpty)\n\n    val toolkitDep = dep.get.value\n    expect(toolkitDep.organization == \"org.scala-lang\")\n    expect(toolkitDep.name == \"toolkit\")\n    expect(toolkitDep.version == \"latest.release\")\n  }\n\n  test(\"resolve typelevel toolkit dependency\") {\n    val runOptions = RunOptions(\n      shared = SharedOptions(\n        withToolkit = Some(\"typelevel:latest\")\n      )\n    )\n    val buildOptions = Run.buildOptions(runOptions).value\n    val dep          = buildOptions.classPathOptions.extraDependencies.toSeq.headOption\n    assert(dep.nonEmpty)\n\n    val toolkitDep = dep.get.value\n    expect(toolkitDep.organization == \"org.typelevel\")\n    expect(toolkitDep.name == \"toolkit\")\n    expect(toolkitDep.version == \"latest.release\")\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/ArgSplitterTest.scala",
    "content": "package cli.tests\n\nimport scala.cli.commands.shared.ArgSplitter\n\nclass ArgSplitterTest extends TestUtil.ScalaCliSuite {\n  test(\"test scalac options are split correctly\") {\n    val args = List(\n      List(\"-arg\", \"-other-arg\"),\n      List(\"-yet-another-arg\", \"dir/path\\\\ with\\\\ space\", \"'another arg with space'\"),\n      List(\"\\\"yet another arg with space\\\"\")\n    )\n    val input = args.map(_.mkString(\"   \", \" \", \"\")).mkString(\" \", \"\\n\", \"\")\n    assertEquals(ArgSplitter.splitToArgs(input), args.flatten)\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala",
    "content": "package scala.cli.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport cli.tests.TestUtil\nimport com.eed3si9n.expecty.Expecty.assert as expect\nimport os.Path\n\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.build.tests.util.BloopServer\nimport scala.build.tests.{TestInputs, TestLogger}\nimport scala.build.{Build, BuildThreads, Directories, LocalRepo}\nimport scala.cli.internal.CachedBinary\nimport scala.util.{Properties, Random}\n\nclass CachedBinaryTests extends TestUtil.ScalaCliSuite {\n  val buildThreads: BuildThreads    = BuildThreads.create()\n  def bloopConfig: BloopRifleConfig = BloopServer.bloopConfig\n\n  val helloFileName = \"Hello.scala\"\n\n  val inputs: TestInputs = TestInputs(\n    os.rel / helloFileName ->\n      s\"\"\"object Hello extends App {\n         |  println(\"Hello\")\n         |}\n         |\"\"\".stripMargin,\n    os.rel / \"main\" / \"Main.scala\" ->\n      s\"\"\"object Main extends App {\n         |  println(\"Hello\")\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val extraRepoTmpDir: Path    = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n\n  val defaultOptions: BuildOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger())\n    )\n  )\n\n  for {\n    fromDirectory <- List(false, true)\n    additionalMessage = if (fromDirectory) \"built from a directory\" else \"built from a set of files\"\n  } {\n    test(s\"should build native app with added test scope at first time ($additionalMessage)\") {\n      TestInputs(\n        os.rel / \"main\" / \"Main.scala\" ->\n          s\"\"\"object Main extends App {\n             |  println(\"Hello\")\n             |}\n             |\"\"\".stripMargin,\n        os.rel / \"test\" / \"TestScope.scala\" ->\n          s\"\"\"object TestScope extends App {\n             |  println(\"Hello from the test scope\")\n             |}\n             |\"\"\".stripMargin\n      ).withLoadedBuilds(\n        defaultOptions,\n        buildThreads,\n        Some(bloopConfig),\n        fromDirectory\n      ) {\n        (_, _, builds) =>\n          expect(builds.builds.forall(_.success))\n\n          val config =\n            builds.main.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = builds.main.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          // generate dummy output\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val successfulBuilds = builds.builds.map { case s: Build.Successful => s }\n          val cacheData        =\n            CachedBinary.getCacheData(successfulBuilds, config, destPath, nativeWorkDir)\n          expect(cacheData.changed)\n      }\n    }\n\n    test(s\"should build native app at first time ($additionalMessage)\") {\n      inputs.withLoadedBuild(defaultOptions, buildThreads, Some(bloopConfig), fromDirectory) {\n        (_, _, maybeBuild) =>\n          val build = maybeBuild.successfulOpt.get\n\n          val config = build.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = build.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          // generate dummy output\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val cacheData =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          expect(cacheData.changed)\n      }\n    }\n\n    test(s\"should not rebuild the second time ($additionalMessage)\") {\n      inputs.withLoadedBuild(defaultOptions, buildThreads, Some(bloopConfig), fromDirectory) {\n        (_, _, maybeBuild) =>\n          val build = maybeBuild.successfulOpt.get\n\n          val config = build.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = build.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          // generate dummy output\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val cacheData =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          CachedBinary.updateProjectAndOutputSha(\n            destPath,\n            nativeWorkDir,\n            cacheData.projectSha\n          )\n          expect(cacheData.changed)\n\n          val sameBuildCache =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          expect(!sameBuildCache.changed)\n      }\n    }\n\n    test(s\"should build native if output file was deleted ($additionalMessage)\") {\n      inputs.withLoadedBuild(defaultOptions, buildThreads, Some(bloopConfig), fromDirectory) {\n        (_, _, maybeBuild) =>\n          val build = maybeBuild.successfulOpt.get\n\n          val config = build.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = build.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          // generate dummy output\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val cacheData =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          CachedBinary.updateProjectAndOutputSha(\n            destPath,\n            nativeWorkDir,\n            cacheData.projectSha\n          )\n          expect(cacheData.changed)\n\n          os.remove(destPath)\n          val afterDeleteCache =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          expect(afterDeleteCache.changed)\n      }\n    }\n\n    test(s\"should build native if output file was changed ($additionalMessage)\") {\n      inputs.withLoadedBuild(defaultOptions, buildThreads, Some(bloopConfig), fromDirectory) {\n        (_, _, maybeBuild) =>\n          val build = maybeBuild.successfulOpt.get\n\n          val config = build.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = build.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          // generate dummy output\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val cacheData =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          CachedBinary.updateProjectAndOutputSha(\n            destPath,\n            nativeWorkDir,\n            cacheData.projectSha\n          )\n          expect(cacheData.changed)\n\n          os.write.over(destPath, Random.alphanumeric.take(10).mkString(\"\"))\n          val cacheAfterFileUpdate =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          expect(cacheAfterFileUpdate.changed)\n      }\n    }\n\n    test(s\"should build native if input file was changed ($additionalMessage)\") {\n      inputs.withLoadedBuild(defaultOptions, buildThreads, Some(bloopConfig), fromDirectory) {\n        (root, _, maybeBuild) =>\n          val build = maybeBuild.successfulOpt.get\n\n          val config = build.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = build.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val cacheData =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          CachedBinary.updateProjectAndOutputSha(\n            destPath,\n            nativeWorkDir,\n            cacheData.projectSha\n          )\n          expect(cacheData.changed)\n\n          os.write.append(root / helloFileName, Random.alphanumeric.take(10).mkString(\"\"))\n          val cacheAfterFileUpdate =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          expect(cacheAfterFileUpdate.changed)\n      }\n    }\n\n    test(s\"should build native if native config was changed ($additionalMessage)\") {\n      inputs.withLoadedBuild(defaultOptions, buildThreads, Some(bloopConfig), fromDirectory) {\n        (_, _, maybeBuild) =>\n          val build = maybeBuild.successfulOpt.get\n\n          val config = build.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n          val nativeWorkDir = build.inputs.nativeWorkDir\n          val destPath      = nativeWorkDir / s\"main${if (Properties.isWin) \".exe\" else \"\"}\"\n          os.write(destPath, Random.alphanumeric.take(10).mkString(\"\"), createFolders = true)\n\n          val cacheData =\n            CachedBinary.getCacheData(Seq(build), config, destPath, nativeWorkDir)\n          CachedBinary.updateProjectAndOutputSha(\n            destPath,\n            nativeWorkDir,\n            cacheData.projectSha\n          )\n          expect(cacheData.changed)\n\n          val updatedBuild = build.copy(\n            options = build.options.copy(\n              scalaNativeOptions = build.options.scalaNativeOptions.copy(\n                clang = Some(Random.alphanumeric.take(10).mkString(\"\"))\n              )\n            )\n          )\n          val updatedConfig =\n            updatedBuild.options.scalaNativeOptions.configCliOptions(resourcesExist = false)\n\n          val cacheAfterConfigUpdate =\n            CachedBinary.getCacheData(\n              Seq(updatedBuild),\n              updatedConfig,\n              destPath,\n              nativeWorkDir\n            )\n          expect(cacheAfterConfigUpdate.changed)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/HelpCheck.scala",
    "content": "package cli.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.ScalaCliCommands\nimport scala.cli.commands.version.Version\n\nclass HelpCheck extends TestUtil.ScalaCliSuite {\n  test(\"help message should be shorter then 80 lines\") {\n    val scalaCli    = new ScalaCliCommands(\"scala-cli\", \"scala-cli\", \"Scala CLI\")\n    val helpMessage = scalaCli.help.help(scalaCli.helpFormat)\n\n    val lines = helpMessage.split(\"\\r\\n|\\r|\\n\").length\n    assert(lines <= 80)\n  }\n\n  test(\n    \"version help message should only contain relevant options\"\n  ) { // regression test - https://github.com/VirtusLab/scala-cli/issues/1666\n    val helpMessage = Version.finalHelp.help(Version.helpFormat)\n\n    expect(helpMessage.contains(\"Version options:\"))\n\n    expect(!helpMessage.contains(\"--usage\"))\n    expect(!helpMessage.contains(\"Logging options:\"))\n    expect(!helpMessage.contains(\"Other options:\"))\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala",
    "content": "package cli.tests\nimport com.eed3si9n.expecty.Expecty.expect\nimport dependency.ScalaParameters\n\nimport scala.build.internal.Constants\nimport scala.build.tests.TestLogger\nimport scala.cli.commands.shared.CoursierOptions\nimport scala.cli.launcher.LauncherCli\n\nclass LauncherCliTest extends TestUtil.ScalaCliSuite {\n  test(\"resolve nightly version\".flaky) {\n    val logger          = TestLogger()\n    val cache           = CoursierOptions().coursierCache(logger)\n    val scalaParameters = ScalaParameters(Constants.defaultScalaVersion)\n\n    val nightlyCliVersion =\n      LauncherCli.resolveNightlyScalaCliVersion(cache, scalaParameters.scalaBinaryVersion)\n    expect(nightlyCliVersion.endsWith(\"-SNAPSHOT\"))\n  }\n\n  val expectedScalaCliVersions: Seq[(String, String)] = Seq(\n    \"0.1.2\"                       -> Constants.defaultScala212Version,\n    \"0.1.1+43-g15666b67-SNAPSHOT\" -> Constants.defaultScala212Version,\n    \"0.1.3\"                       -> Constants.defaultScala213Version,\n    \"nightly\"                     -> Constants.defaultScalaVersion\n  )\n\n  for ((cliVersion, expectedScalaVersion) <- expectedScalaCliVersions)\n    test(s\"use expected scala version for Scala CLI launcher: $cliVersion\") {\n      val scalaVersion = LauncherCli.scalaCliScalaVersion(cliVersion)\n      expect(scalaVersion == expectedScalaVersion)\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/OptionsCheck.scala",
    "content": "package scala.cli.tests\n\nimport cli.tests.TestUtil\n\nimport scala.cli.ScalaCliCommands\nimport scala.cli.commands.shared.HasGlobalOptions\n\nclass OptionsCheck extends TestUtil.ScalaCliSuite {\n  for (command <- new ScalaCliCommands(\"scala-cli\", \"scala-cli\", \"Scala CLI\").commands)\n    test(s\"No duplicated options in ${command.names.head.mkString(\" \")}\") {\n      command.ensureNoDuplicates()\n    }\n\n    test(s\"--power option present in $command\") {\n      command.parser.stopAtFirstUnrecognized.parse(Seq(\"--power\")) match {\n        case Right((_: HasGlobalOptions, _ +: _)) => fail(\"Expected --power to be recognized\")\n        case _                                    => ()\n      }\n    }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/PackageTests.scala",
    "content": "package cli.tests\n\nimport bloop.rifle.BloopRifleConfig\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.file.FileSystems\n\nimport scala.build.Ops.*\nimport scala.build.options.{BuildOptions, InternalOptions, PackageType}\nimport scala.build.tests.util.BloopServer\nimport scala.build.tests.{TestInputs, TestLogger}\nimport scala.build.{BuildThreads, Directories, LocalRepo}\nimport scala.cli.commands.package0.Package\nimport scala.cli.packaging.Library\n\nclass PackageTests extends TestUtil.ScalaCliSuite {\n  val buildThreads: BuildThreads    = BuildThreads.create()\n  def bloopConfig: BloopRifleConfig = BloopServer.bloopConfig\n\n  val extraRepoTmpDir: os.Path = os.temp.dir(prefix = \"scala-cli-tests-extra-repo-\")\n  val directories: Directories = Directories.under(extraRepoTmpDir)\n\n  val defaultOptions = BuildOptions(\n    internal = InternalOptions(\n      localRepository = LocalRepo.localRepo(directories.localRepoDir, TestLogger())\n    )\n  )\n\n  /** Fixes - https://github.com/VirtusLab/scala-cli/issues/2166 */\n  test(s\"should generate a correct jar library when the project was changed\") {\n    TestInputs().fromRoot { root =>\n      val inputs = TestInputs(\n        files = Seq(os.rel / \"Hello.scala\" ->\n          \"\"\"//> using platform scala-js\n            |//> using options -Wvalue-discard -Wunused:all\n            |\n            |object Hello extends App {\n            |  println(\"Hello, World World World\")\n            |}\"\"\".stripMargin),\n        forceCwd = Some(root)\n      )\n      inputs.withBuild(defaultOptions, buildThreads, Some(bloopConfig)) {\n        (_, _, maybeFirstBuild) =>\n          val firstBuild      = maybeFirstBuild.orThrow.successfulOpt.get\n          val firstLibraryJar = Library.libraryJar(Seq(firstBuild))\n          expect(os.exists(firstLibraryJar)) // should create library jar\n\n          // change Hello.scala and recompile\n          os.write.over(\n            root / \"Hello.scala\",\n            \"\"\"//> using platform scala-js\n              |//> using options -Wvalue-discard -Wunused:all\n              |\n              |object Hello extends App {\n              |  println(\"hello\")\n              |}\"\"\".stripMargin\n          )\n\n          inputs.withBuild(\n            defaultOptions,\n            buildThreads,\n            Some(bloopConfig),\n            skipCreatingSources = true\n          ) {\n            (_, _, maybeSecondBuild) =>\n              val secondBuild = maybeSecondBuild.orThrow.successfulOpt.get\n              val libraryJar  = Library.libraryJar(Seq(secondBuild))\n              val fs = // should not throw \"invalid CEN header (bad signature)\" ZipException\n                FileSystems.newFileSystem(libraryJar.toNIO, null: ClassLoader)\n              expect(fs.isOpen)\n              fs.close()\n          }\n      }\n    }\n  }\n\n  /** Fixes - https://github.com/VirtusLab/scala-cli/issues/2303 */\n  test(\"accept packageType-native when using native platform\") {\n    val inputs = TestInputs(\n      files = Seq(os.rel / \"Hello.scala\" ->\n        \"\"\"//> using platform native\n          |//> using packaging.packageType native\n          |\n          |object Hello extends App {\n          |  println(\"Hello World\")\n          |}\"\"\".stripMargin)\n    )\n    inputs.withBuild(defaultOptions, buildThreads, Some(bloopConfig)) {\n      (_, _, maybeFirstBuild) =>\n        val build = maybeFirstBuild.orThrow.successfulOpt.get\n\n        val packageType = Package.resolvePackageType(Seq(build), None).orThrow\n        expect(packageType == PackageType.Native.Application)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/ScalafmtTests.scala",
    "content": "package cli.tests\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.build.Ops.*\nimport scala.build.internal.Constants\nimport scala.build.tests.{TestInputs, TestLogger}\nimport scala.cli.commands.fmt.{FmtOptions, FmtUtil}\n\nclass ScalafmtTests extends TestUtil.ScalaCliSuite {\n  private lazy val defaultScalafmtVersion = Constants.defaultScalafmtVersion\n\n  test(\"readVersionFromFile with non-default scalafmt version\") {\n    val confFile = \"\"\"runner.dialect = scala213\n                     |version = \"3.1.2\"\n                     |\"\"\".stripMargin\n\n    TestInputs.withTmpDir(\"temp-dir\") { dirPath =>\n      val confFilePath = dirPath / \".scalafmt.conf\"\n      os.write(confFilePath, confFile)\n\n      val readVersionAndDialect =\n        FmtUtil.readVersionAndDialect(workspace = dirPath, FmtOptions(), TestLogger())\n      expect(readVersionAndDialect == (Some(\"3.1.2\"), Some(\"scala213\"), Some(confFilePath)))\n    }\n  }\n\n  test(\"readVersionFromFile with missing .scalafmt.conf file\") {\n    TestInputs.withTmpDir(\"temp-dir\") { dirPath =>\n      val readVersionAndDialect =\n        FmtUtil.readVersionAndDialect(workspace = dirPath, FmtOptions(), TestLogger())\n      expect(readVersionAndDialect == (None, None, None))\n    }\n  }\n\n  test(s\"check native launcher availability for scalafmt $defaultScalafmtVersion\") {\n    final case class Asset(name: String)\n    final case class Release(tag_name: String, assets: List[Asset])\n    lazy val releaseCodec: JsonValueCodec[Release] = JsonCodecMaker.make\n    val url                                        =\n      s\"https://api.github.com/repos/scalameta/scalafmt/releases/tags/v$defaultScalafmtVersion\"\n\n    val expectedAssets = Seq(\n      \"scalafmt-x86_64-apple-darwin.zip\",\n      \"scalafmt-x86_64-pc-linux.zip\",\n      \"scalafmt-x86_64-pc-win32.zip\",\n      \"scalafmt-aarch64-apple-darwin.zip\",\n      \"scalafmt-aarch64-pc-linux.zip\"\n    )\n    val errorMsg =\n      s\"\"\"scalafmt native images missing for v$defaultScalafmtVersion at https://github.com/scalameta/scalafmt\n         |Ensure that all expected assets are available in the release:\n         |  ${expectedAssets.mkString(\", \")}\n         |under tag v$defaultScalafmtVersion.\"\"\".stripMargin\n    try {\n      val resp    = TestUtil.downloadFile(url).orThrow\n      val release = readFromArray(resp)(using releaseCodec)\n      val assets  = release.assets.map(_.name)\n\n      assert(\n        expectedAssets.forall(assets.contains),\n        clue = errorMsg\n      )\n    }\n    catch {\n      case e: JsonReaderException => throw new Exception(s\"Error reading $url\", e)\n      case e: Throwable           => throw new Exception(\n          s\"\"\"Failed to check for the ScalaFmt $defaultScalafmtVersion native launcher assets: ${e.getMessage}\n             |\n             |$errorMsg\n             |\"\"\".stripMargin,\n          e\n        )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala",
    "content": "package cli.tests\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.util.{Collections, Properties}\n\nimport scala.build.internal.Constants\nimport scala.build.tests.TestInputs\nimport scala.cli.ScalaCli\nimport scala.jdk.CollectionConverters.{MapHasAsJava, MapHasAsScala}\n\nclass SetupScalaCLITests extends TestUtil.ScalaCliSuite {\n  test(s\"should read java properties from file\") {\n    val key    = \"scala-cli\"\n    val value  = \"true\"\n    val inputs = TestInputs(\n      os.rel / Constants.jvmPropertiesFileName ->\n        s\"\"\"-Xignored_1\n           |-Xignored_2\n           |-Xignored_3\n           |-D$key=$value\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot(root =>\n      // save current props to restore them after test\n      val currentProps = System.getProperties.clone().asInstanceOf[Properties]\n      ScalaCli.loadJavaProperties(root)\n      expect(sys.props.get(key).contains(value))\n\n      expect(sys.props.get(\"ignored_1\").isEmpty)\n      expect(sys.props.get(\"ignored_2\").isEmpty)\n      expect(sys.props.get(\"ignored_3\").isEmpty)\n      // restore original props\n      System.setProperties(currentProps)\n    )\n  }\n\n  test(s\"should read java properties from JAVA_OPTS and JDK_JAVA_OPTIONS\") {\n    // Adapted from https://stackoverflow.com/a/496849\n    def setEnvVars(newEnv: Map[String, String]): Unit = {\n      val classes = classOf[Collections].getDeclaredClasses\n      val env     = System.getenv()\n      for (cl <- classes)\n        if (cl.getName.equals(\"java.util.Collections$UnmodifiableMap\")) {\n          val field = cl.getDeclaredField(\"m\")\n          field.setAccessible(true)\n          val obj = field.get(env)\n          val map = obj.asInstanceOf[java.util.Map[String, String]]\n          map.clear()\n          map.putAll(newEnv.asJava)\n        }\n    }\n\n    val javaOptsValues       = \"  -Xignored_1   -Dhttp.proxy=4.4.4.4   -Xignored_2\"\n    val jdkJavaOptionsValues = \" -Xignored_3 -Dscala-cli=true  -Xignored_4\"\n\n    TestInputs().fromRoot(root =>\n      //\n      val currentEnv = System.getenv().asScala.toMap\n      // modify environment variable of this process\n      setEnvVars(Map(\"JAVA_OPTS\" -> javaOptsValues, \"JDK_JAVA_OPTIONS\" -> jdkJavaOptionsValues))\n      ScalaCli.loadJavaProperties(root)\n      expect(sys.props.get(\"http.proxy\").contains(\"4.4.4.4\"))\n      expect(sys.props.get(\"scala-cli\").contains(\"true\"))\n\n      expect(sys.props.get(\"ignored_1\").isEmpty)\n      expect(sys.props.get(\"ignored_2\").isEmpty)\n      expect(sys.props.get(\"ignored_3\").isEmpty)\n      expect(sys.props.get(\"ignored_4\").isEmpty)\n      // reset the env\n      setEnvVars(currentEnv)\n    )\n  }\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/cli/tests/TestUtil.scala",
    "content": "package cli.tests\n\nimport coursier.cache.{ArtifactError, FileCache}\nimport coursier.util.{Artifact, Task}\nimport munit.AnyFixture\n\nimport java.io.File\nimport java.util.concurrent.TimeUnit\n\nimport scala.concurrent.duration.{DurationInt, FiniteDuration}\nimport scala.util.control.NonFatal\n\nobject TestUtil {\n  abstract class ScalaCliSuite extends munit.FunSuite {\n    extension (munitContext: BeforeEach | AfterEach) {\n      def locationAbsolutePath: os.Path =\n        os.Path {\n          (munitContext match {\n            case beforeEach: BeforeEach => beforeEach.test\n            case afterEach: AfterEach   => afterEach.test\n          }).location.path\n        }\n    }\n\n    override def munitTimeout = new FiniteDuration(120, TimeUnit.SECONDS)\n\n    val testStartEndLogger: Fixture[Unit] = new Fixture[Unit](\"files\") {\n      def apply(): Unit = ()\n\n      override def beforeEach(context: BeforeEach): Unit = {\n        val fileName = context.locationAbsolutePath.baseName\n        System.err.println(\n          s\">==== ${Console.CYAN}Running '${context.test.name}' from $fileName${Console.RESET}\"\n        )\n      }\n\n      override def afterEach(context: AfterEach): Unit = {\n        val fileName = context.locationAbsolutePath.baseName\n        System.err.println(\n          s\"X==== ${Console.CYAN}Finishing '${context.test.name}' from $fileName${Console.RESET}\"\n        )\n      }\n    }\n\n    override def munitFixtures: Seq[AnyFixture[?]] = List(testStartEndLogger)\n  }\n\n  def downloadFile(url: String): Either[ArtifactError, Array[Byte]] = {\n    val artifact = Artifact(url).withChanging(true)\n    val cache    = FileCache()\n\n    val file: Either[ArtifactError, File] = cache.logger.use {\n      try cache.withTtl(0.seconds).file(artifact).run.unsafeRun()(using cache.ec)\n      catch {\n        case NonFatal(e) => throw new Exception(e)\n      }\n    }\n\n    file.map(f => os.read.bytes(os.Path(f, os.pwd)))\n  }\n  val isCI: Boolean = System.getenv(\"CI\") != null\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/scala/cli/commands/publish/IvyTests.scala",
    "content": "package scala.cli.commands.publish\n\nimport coursier.core.{ModuleName, Organization, Type}\nimport coursier.publish.Pom\n\nimport java.time.LocalDateTime\n\nclass IvyTests extends munit.FunSuite {\n\n  test(\"ivy includes Apache Ivy license and Maven POM scm and developers\") {\n    val organization = Organization(\"org.example\")\n    val moduleName   = ModuleName(\"demo\")\n    val version      = \"1.0\"\n    val description  = \"A demo\"\n    val homepage     = \"https://example.org\"\n\n    val pomProjectName = \"Demo library\"\n    val packaging      = Type(\"jar\")\n\n    val licenseName = \"Apache-2.0\"\n    val licenseUrl  = \"https://spdx.org/licenses/Apache-2.0.html\"\n\n    val scmUrl           = \"https://github.com/foo/bar.git\"\n    val scmConnection    = \"scm:git:github.com/foo/bar.git\"\n    val scmDevConnection = \"scm:git:git@github.com:foo/bar.git\"\n\n    val devId     = \"jdu\"\n    val devName   = \"Jane\"\n    val devUrl    = \"https://jane.example\"\n    val devMail   = \"jane@example.org\"\n    val fixedTime = LocalDateTime.of(2024, 1, 2, 3, 4, 5)\n\n    val xml = Ivy.create(\n      organization = organization,\n      moduleName = moduleName,\n      version = version,\n      description = Some(description),\n      url = Some(homepage),\n      pomProjectName = Some(pomProjectName),\n      packaging = Some(packaging),\n      license = Some(Pom.License(licenseName, licenseUrl)),\n      scm = Some(Pom.Scm(scmUrl, scmConnection, scmDevConnection)),\n      developers = Seq(\n        Pom.Developer(devId, devName, devUrl, Some(devMail))\n      ),\n      dependencies = Nil,\n      time = fixedTime\n    )\n    assert(xml.contains(s\"\"\"<license name=\"$licenseName\"\"\"\"))\n    assert(xml.contains(s\"\"\"url=\"$licenseUrl\"\"\"\"))\n    assert(xml.contains(s\"<m:name>$pomProjectName</m:name>\"))\n    assert(xml.contains(s\"<m:packaging>${packaging.value}</m:packaging>\"))\n    assert(xml.contains(s\"<m:url>$scmUrl</m:url>\"))\n    assert(xml.contains(s\"<m:connection>$scmConnection</m:connection>\"))\n    assert(xml.contains(s\"<m:developerConnection>$scmDevConnection</m:developerConnection>\"))\n    assert(xml.contains(s\"<m:id>$devId</m:id>\"))\n    assert(xml.contains(s\"<m:name>$devName</m:name>\"))\n    assert(xml.contains(s\"<m:url>$devUrl</m:url>\"))\n    assert(xml.contains(s\"<m:email>$devMail</m:email>\"))\n    assert(xml.contains(\"xmlns:m=\"))\n  }\n\n  test(\"ivy omits Maven namespace when there is no scm or developer XML\") {\n    val organization = Organization(\"org.example\")\n    val moduleName   = ModuleName(\"demo\")\n    val version      = \"1.0\"\n    val licenseName  = \"MIT\"\n    val licenseUrl   = \"https://opensource.org/licenses/MIT\"\n    val fixedTime    = LocalDateTime.of(2024, 1, 2, 3, 4, 5)\n\n    val xml = Ivy.create(\n      organization = organization,\n      moduleName = moduleName,\n      version = version,\n      license = Some(Pom.License(licenseName, licenseUrl)),\n      pomProjectName = None,\n      packaging = None,\n      scm = Some(Pom.Scm(\"\", \"\", \"\")),\n      developers = Nil,\n      time = fixedTime\n    )\n    assert(!xml.contains(\"xmlns:m=\"))\n    assert(xml.contains(s\"\"\"<license name=\"$licenseName\"\"\"\"))\n    assert(xml.contains(s\"\"\"url=\"$licenseUrl\"\"\"\"))\n  }\n\n}\n"
  },
  {
    "path": "modules/cli/src/test/scala/scala/cli/tests/ScalacOptionsPrintTest.scala",
    "content": "package scala.cli.tests\n\nimport munit.FunSuite\n\nimport scala.cli.commands.shared.ScalacOptions\n\nfinal class ScalacOptionsPrintTest extends FunSuite {\n\n  test(\"isColonHelpPrintOption: :help suffix (single segment)\") {\n    assert(ScalacOptions.isColonHelpPrintOption(\"opt-inline:help\"))\n    assert(ScalacOptions.isColonHelpPrintOption(\"Xlint:help\"))\n    assert(ScalacOptions.isColonHelpPrintOption(\"opt:help\"))\n  }\n\n  test(\"isColonHelpPrintOption: :help suffix (multi-level colons)\") {\n    assert(ScalacOptions.isColonHelpPrintOption(\"opt:l:inline:help\"))\n    assert(ScalacOptions.isColonHelpPrintOption(\"a:b:help\"))\n    assert(ScalacOptions.isColonHelpPrintOption(\"foo:bar:baz:help\"))\n  }\n\n  test(\"isColonHelpPrintOption: reject non-suffix\") {\n    assert(!ScalacOptions.isColonHelpPrintOption(\"help\"))\n    assert(!ScalacOptions.isColonHelpPrintOption(\"Xlint:infer-any\"))\n    assert(!ScalacOptions.isColonHelpPrintOption(\"help:foo\"))\n    assert(!ScalacOptions.isColonHelpPrintOption(\"something:helpme\"))\n  }\n\n  test(\"isScalacPrintOption: combines explicit set and :help rule\") {\n    assert(ScalacOptions.isScalacPrintOption(\"Xshow-phases\"))\n    assert(ScalacOptions.isScalacPrintOption(\"help\"))\n    assert(ScalacOptions.isScalacPrintOption(\"Xsource:help\"))\n    assert(ScalacOptions.isScalacPrintOption(\"opt:l:inline:help\"))\n    assert(!ScalacOptions.isScalacPrintOption(\"random-flag\"))\n  }\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/ConfigDb.scala",
    "content": "package scala.cli.config\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.{Key as _, *}\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.attribute.{PosixFilePermission, PosixFilePermissions}\nimport java.nio.file.{Files, Path}\n\nimport scala.collection.immutable.ListMap\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\n/** In-memory representation of a configuration DB content.\n  *\n  * Use [[ConfigDb.apply]] or [[ConfigDb.open]] to create an instance of it.\n  *\n  * [[set]], [[setFromString]], and [[remove]] only change values in memory.\n  *\n  * Use [[save]] to persist values on disk.\n  */\nfinal class ConfigDb private (\n  var rawEntries: Map[String, Array[Byte]]\n) {\n\n  /** Gets an entry.\n    *\n    * If the value cannot be decoded, an error is returned on the left side of the either.\n    *\n    * If the key isn't in DB, None is returned on the right side of the either.\n    *\n    * Else, the value is returned wrapped in Some on the right side of the either.\n    */\n  def get[T](key: Key[T]): Either[ConfigDb.ConfigDbFormatError, Option[T]] =\n    rawEntries.get(key.fullName) match {\n      case None                  => Right(None)\n      case Some(rawEntryContent) =>\n        key.parse(rawEntryContent)\n          .left.map { e =>\n            new ConfigDb.ConfigDbFormatError(s\"Error parsing ${key.fullName} value\", Some(e))\n          }\n          .map(Some(_))\n    }\n\n  /** Sets an entry in memory */\n  def set[T](key: Key[T], value: T): this.type = {\n    val b = key.write(value)\n    rawEntries += key.fullName -> b\n    this\n  }\n\n  /** Removes an entry from memory */\n  def remove(key: Key[?]): this.type = {\n    rawEntries -= key.fullName\n    this\n  }\n\n  /** Gets an entry in printable form.\n    *\n    * See [[get]] for when a left value, or a None on the right, can be returned.\n    */\n  def getAsString[T](key: Key[T]): Either[ConfigDb.ConfigDbFormatError, Option[Seq[String]]] =\n    get(key).map(_.map(key.asString))\n\n  /** Sets an entry in memory, from a printable / user-writable representation.\n    */\n  def setFromString[T](\n    key: Key[T],\n    values: Seq[String]\n  ): Either[Key.MalformedValue, this.type] =\n    key.fromString(values).map { typedValue =>\n      set(key, typedValue)\n    }\n\n  /** Dumps this DB content as JSON */\n  def dump: Array[Byte] = {\n\n    def serializeMap(m: Map[String, Array[Byte]], level: Int): Array[Byte] = {\n      val keyValues = m\n        .groupBy(_._1.split(\"\\\\.\", 2).apply(0))\n        .toVector\n        .sortBy(_._1)\n        .map {\n          case (k, v) =>\n            val v0 = v.map {\n              case (k1, v1) =>\n                (k1.stripPrefix(k).stripPrefix(\".\"), v1)\n            }\n            (k, serialize(v0, level + 1))\n        }\n      val sortedMap: Map[String, RawJson] = ListMap.empty ++ keyValues\n      val b                               =\n        writeToArray(sortedMap, WriterConfig.withIndentionStep((level + 1) * 2))(using\n          ConfigDb.codec\n        )\n      if (b.nonEmpty && b.last == '}'.toByte)\n        // FIXME We're copying / moving arrays around quite a bit here\n        b.init ++ (\"  \" * level).getBytes(StandardCharsets.US_ASCII) ++ Array('}'.toByte)\n      else\n        b\n    }\n\n    def serialize(m: Map[String, Array[Byte]], level: Int): RawJson =\n      m.get(\"\") match {\n        case Some(value) =>\n          if (m.size == 1)\n            RawJson(value)\n          else\n            sys.error(s\"Inconsistent keys: ${m.keySet.toVector.sorted}\")\n        case None =>\n          RawJson(serializeMap(m, level))\n      }\n\n    serializeMap(rawEntries, level = 0) ++\n      // using just '\\n' rather then \"\\r\\n\" on Windows, as that's what jsoniter-scala uses\n      Array('\\n': Byte)\n  }\n\n  private def saveUnsafe(path: Path): Either[ConfigDb.ConfigDbPermissionsError, Unit] = {\n    val dir = path.getParent\n\n    if (Properties.isWin) {\n      Files.createDirectories(dir)\n      Files.write(path, dump)\n      Right(())\n    }\n    else {\n      if (!Files.exists(dir))\n        Files.createDirectories(\n          dir,\n          PosixFilePermissions.asFileAttribute(Set(\n            PosixFilePermission.OWNER_READ,\n            PosixFilePermission.OWNER_WRITE,\n            PosixFilePermission.OWNER_EXECUTE\n          ).asJava)\n        )\n      val dirPerms = Files.getPosixFilePermissions(dir).asScala.toSet\n      val permsOk  =\n        !dirPerms.contains(PosixFilePermission.GROUP_READ) &&\n        !dirPerms.contains(PosixFilePermission.GROUP_WRITE) &&\n        !dirPerms.contains(PosixFilePermission.GROUP_EXECUTE) &&\n        !dirPerms.contains(PosixFilePermission.OTHERS_READ) &&\n        !dirPerms.contains(PosixFilePermission.OTHERS_WRITE) &&\n        !dirPerms.contains(PosixFilePermission.OTHERS_EXECUTE)\n      if (permsOk) {\n        Files.write(path, Array.emptyByteArray)\n        Files.setPosixFilePermissions(\n          path,\n          Set(\n            PosixFilePermission.OWNER_READ,\n            PosixFilePermission.OWNER_WRITE\n          ).asJava\n        )\n        Files.write(path, dump)\n        Right(())\n      }\n      else\n        Left(new ConfigDb.ConfigDbPermissionsError(dir, dirPerms))\n    }\n  }\n\n  /** Saves this DB at the passed path */\n  def save(path: Path): Either[Exception, Unit] =\n    // no file locks…\n    saveUnsafe(path)\n}\n\nobject ConfigDb {\n  final class ConfigDbFormatError(\n    message: String,\n    causeOpt: Option[Throwable] = None\n  ) extends Exception(message, causeOpt.orNull)\n\n  private def permsString(perms: Set[PosixFilePermission]): String = {\n    val res = new StringBuilder\n    res += (if (perms.contains(PosixFilePermission.OWNER_READ)) 'r' else '-')\n    res += (if (perms.contains(PosixFilePermission.OWNER_WRITE)) 'w' else '-')\n    res += (if (perms.contains(PosixFilePermission.OWNER_EXECUTE)) 'x' else '-')\n    res += (if (perms.contains(PosixFilePermission.GROUP_READ)) 'r' else '-')\n    res += (if (perms.contains(PosixFilePermission.GROUP_WRITE)) 'w' else '-')\n    res += (if (perms.contains(PosixFilePermission.GROUP_EXECUTE)) 'x' else '-')\n    res += (if (perms.contains(PosixFilePermission.OTHERS_READ)) 'r' else '-')\n    res += (if (perms.contains(PosixFilePermission.OTHERS_WRITE)) 'w' else '-')\n    res += (if (perms.contains(PosixFilePermission.OTHERS_EXECUTE)) 'x' else '-')\n    res.result()\n  }\n  private final class ConfigDbPermissionsError(path: Path, perms: Set[PosixFilePermission])\n      extends Exception(\n        s\"$path has wrong permissions ${permsString(perms)} (expected at most rwx------)\"\n      )\n\n  private val codec: JsonValueCodec[Map[String, RawJson]] = JsonCodecMaker.make\n\n  /** Create a ConfigDb instance from binary content\n    *\n    * @param dbContent:\n    *   JSON, as a UTF-8 array of bytes\n    * @param printablePath:\n    *   DB location, for error messages\n    * @return\n    *   either an error on failure, or a ConfigDb instance on success\n    */\n  def apply(\n    dbContent: Array[Byte],\n    printablePath: Option[String] = None\n  ): Either[ConfigDbFormatError, ConfigDb] = {\n\n    def flatten(map: Map[String, RawJson]): Map[String, Array[Byte]] =\n      map.flatMap {\n        case (k, v) =>\n          try {\n            val subMap = flatten(readFromArray(v.value)(using codec))\n            subMap.toSeq.map {\n              case (k0, v0) =>\n                (k + \".\" + k0, v0)\n            }\n          }\n          catch {\n            case _: JsonReaderException =>\n              Seq(k -> v.value)\n          }\n      }\n\n    val maybeRawEntries =\n      try Right(flatten(readFromArray(dbContent)(using codec)))\n      catch {\n        case e: JsonReaderException =>\n          Left(new ConfigDbFormatError(\n            \"Error parsing config DB\" + printablePath.fold(\"\")(\" \" + _),\n            Some(e)\n          ))\n      }\n\n    maybeRawEntries.map(rawEntries => new ConfigDb(rawEntries))\n  }\n\n  /** Creates a ConfigDb from a file\n    *\n    * @param path:\n    *   path to a config UTF-8 JSON file\n    * @return\n    *   either an error on failure, or a ConfigDb instance on success\n    */\n  def open(path: Path): Either[Exception, ConfigDb] =\n    if Files.exists(path)\n    then apply(Files.readAllBytes(path), Some(path.toString))\n    else Right(empty)\n\n  def empty: ConfigDb = new ConfigDb(Map())\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/CredentialsValue.scala",
    "content": "package scala.cli.config\n\ntrait CredentialsValue {\n  def host: String\n  def user: Option[PasswordOption]\n  def password: Option[PasswordOption]\n  def realm: Option[String]\n  def httpsOnly: Option[Boolean]\n  def asString: String\n}\n\nabstract class CredentialsAsJson[T <: CredentialsValue] {\n  def user: Option[String]\n  def password: Option[String]\n  def toCredentialsValue(userOpt: Option[PasswordOption], passwordOpt: Option[PasswordOption]): T\n  def credentialsType: String\n  private def malformedMessage(valueType: String): String =\n    s\"Malformed $credentialsType credentials $valueType value (expected 'value:…', or 'file:/path', or 'env:ENV_VAR_NAME')\"\n  def credentials: Either[::[String], T] = {\n    val maybeUser = user\n      .map { u =>\n        PasswordOption.parse(u) match {\n          case Left(error)  => Left(s\"${malformedMessage(\"user\")}: $error\")\n          case Right(value) => Right(Some(value))\n        }\n      }\n      .getOrElse(Right(None))\n    val maybePassword = password\n      .filter(_.nonEmpty)\n      .map { p =>\n        PasswordOption.parse(p) match {\n          case Left(error)  => Left(s\"${malformedMessage(\"password\")}: $error\")\n          case Right(value) => Right(Some(value))\n        }\n      }\n      .getOrElse(Right(None))\n    (maybeUser, maybePassword) match {\n      case (Right(userOpt), Right(passwordOpt)) => Right(toCredentialsValue(userOpt, passwordOpt))\n      case _                                    =>\n        val errors =\n          maybeUser.left.toOption.toList ::: maybePassword.left.toOption.toList match {\n            case Nil    => sys.error(\"Cannot happen\")\n            case h :: t => ::(h, t)\n          }\n        Left(errors)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/ErrorMessages.scala",
    "content": "package scala.cli.config\n\nobject ErrorMessages {\n  val inlineCredentialsError =\n    \"Inline credentials not accepted, please edit the config file manually.\"\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/Key.scala",
    "content": "package scala.cli.config\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport scala.cli.commands.SpecificationLevel\nimport scala.cli.config.PublishCredentialsAsJson.*\nimport scala.cli.config.RepositoryCredentialsAsJson.*\n\n/** A configuration key\n  */\nabstract class Key[T] {\n\n  /** Key prefix, such as \"foo.a\" in \"foo.a.b\" */\n  def prefix: Seq[String]\n\n  /** Key name, such as \"b\" in \"foo.a.b\" */\n  def name: String\n\n  /** Try to parse a value of this key */\n  def parse(json: Array[Byte]): Either[Key.EntryError, T]\n\n  /** Converts a value of this key to JSON\n    *\n    * @return\n    *   UTF-8 encoded JSON\n    */\n  def write(value: T): Array[Byte]\n\n  /** Converts a value of this key to a sequence of strings\n    *\n    * Such a sequence can be printed in the console, and converted back to a [[T]] with\n    * [[fromString]].\n    */\n  def asString(value: T): Seq[String]\n\n  /** Reads a value of this key from a sequence of string */\n  def fromString(values: Seq[String]): Either[Key.MalformedValue, T]\n\n  /** The fully qualified name of this key */\n  final def fullName: String = (prefix :+ name).mkString(\".\")\n\n  /** A short description of a particular key's purpose and syntax for its values. */\n  def description: String\n\n  /** A flag indicating whether the key should by default be hidden in help outputs or not. */\n  def hidden: Boolean = false\n\n  /** Whether this key corresponds to a password (see [[Key.PasswordEntry]]) */\n  def isPasswordOption: Boolean = false\n\n  /** The [[SpecificationLevel]] of the key. [[SpecificationLevel.RESTRICTED]] &&\n    * [[SpecificationLevel.EXPERIMENTAL]] keys are only available in `power` mode.\n    */\n  def specificationLevel: SpecificationLevel\n\n  def isExperimental: Boolean = specificationLevel == SpecificationLevel.EXPERIMENTAL\n  def isRestricted: Boolean   = specificationLevel == SpecificationLevel.RESTRICTED\n}\n\nobject Key {\n  private implicit lazy val stringJsonCodec: JsonValueCodec[String]           = JsonCodecMaker.make\n  private implicit lazy val stringListJsonCodec: JsonValueCodec[List[String]] = JsonCodecMaker.make\n  private implicit lazy val booleanJsonCodec: JsonValueCodec[Boolean]         = JsonCodecMaker.make\n\n  abstract class EntryError(\n    message: String,\n    causeOpt: Option[Throwable] = None\n  ) extends Exception(message, causeOpt.orNull)\n\n  private final class JsonReaderError(cause: JsonReaderException)\n      extends EntryError(\"Error parsing config JSON\", Some(cause))\n\n  final class MalformedValue(\n    entry: Key[?],\n    input: Seq[String],\n    messageOrExpectedShape: Either[String, String],\n    cause: Option[Throwable] = None\n  ) extends EntryError(\n        {\n          val valueWord    = if (input.length > 1) \"values\" else \"value\"\n          val valuesString = input.map(s => s\"'$s'\").mkString(\", \")\n          val errorMessage = messageOrExpectedShape\n            .fold(shape => s\", expected $shape\", errorMessage => s\". $errorMessage\")\n          s\"Malformed $valueWord $valuesString for the '${entry.fullName}' entry$errorMessage\"\n        },\n        cause\n      )\n\n  private final class MalformedEntry(\n    entry: Key[?],\n    messages: ::[String]\n  ) extends EntryError(\n        s\"Malformed entry ${entry.fullName}, \" +\n          messages.mkString(\", \")\n      )\n\n  abstract class KeyWithJsonCodec[T](implicit jsonCodec: JsonValueCodec[T]) extends Key[T] {\n    def parse(json: Array[Byte]): Either[Key.EntryError, T] =\n      try Right(readFromArray(json))\n      catch {\n        case e: JsonReaderException =>\n          Left(new Key.JsonReaderError(e))\n      }\n\n    def write(value: T): Array[Byte] = writeToArray(value)\n  }\n\n  final class StringEntry(\n    val prefix: Seq[String],\n    val name: String,\n    override val specificationLevel: SpecificationLevel,\n    val description: String = \"\",\n    override val hidden: Boolean = false\n  ) extends KeyWithJsonCodec[String] {\n    def asString(value: String): Seq[String] =\n      Seq(value)\n    def fromString(values: Seq[String]): Either[MalformedValue, String] =\n      values match {\n        case Seq(value) => Right(value)\n        case _          => Left(new MalformedValue(this, values, Left(\"a single string value.\")))\n      }\n  }\n\n  final class BooleanEntry(\n    val prefix: Seq[String],\n    val name: String,\n    override val specificationLevel: SpecificationLevel,\n    val description: String = \"\",\n    override val hidden: Boolean = false\n  ) extends KeyWithJsonCodec[Boolean] {\n    def asString(value: Boolean): Seq[String] =\n      Seq(value.toString)\n    def fromString(values: Seq[String]): Either[MalformedValue, Boolean] =\n      values match {\n        case Seq(value) if value.toBooleanOption.isDefined => Right(value.toBoolean)\n        case _                                             =>\n          Left(new MalformedValue(\n            this,\n            values,\n            Left(\"a single boolean value ('true' or 'false').\")\n          ))\n      }\n  }\n\n  final class PasswordEntry(\n    val prefix: Seq[String],\n    val name: String,\n    override val specificationLevel: SpecificationLevel,\n    val description: String = \"\",\n    override val hidden: Boolean = false\n  ) extends Key[PasswordOption] {\n    def parse(json: Array[Byte]): Either[EntryError, PasswordOption] =\n      try {\n        val str = readFromArray(json)(using stringJsonCodec)\n        PasswordOption.parse(str).left.map { e =>\n          new MalformedValue(this, Seq(str), Right(e))\n        }\n      }\n      catch {\n        case e: JsonReaderException =>\n          Left(new JsonReaderError(e))\n      }\n    def write(value: PasswordOption): Array[Byte] =\n      writeToArray(value.asString.value)(using stringJsonCodec)\n    def asString(value: PasswordOption): Seq[String] = Seq(value.asString.value)\n    def fromString(values: Seq[String]): Either[MalformedValue, PasswordOption] =\n      values match {\n        case Seq(value) =>\n          PasswordOption.parse(value).left.map { err =>\n            new MalformedValue(this, values, Right(err))\n          }\n        case _ => Left(new MalformedValue(\n            this,\n            values,\n            Left(\"a single password value (format: 'value:password').\")\n          ))\n      }\n\n    override def isPasswordOption: Boolean = true\n  }\n\n  final class StringListEntry(\n    val prefix: Seq[String],\n    val name: String,\n    override val specificationLevel: SpecificationLevel,\n    val description: String = \"\",\n    override val hidden: Boolean = false\n  ) extends KeyWithJsonCodec[List[String]] {\n    def asString(value: List[String]): Seq[String]                            = value\n    def fromString(values: Seq[String]): Either[MalformedValue, List[String]] =\n      Right(values.toList)\n  }\n  abstract class CredentialsEntry[T <: CredentialsValue, U <: CredentialsAsJson[T]](implicit\n    jsonCodec: JsonValueCodec[List[U]]\n  ) extends Key[List[T]] {\n    protected def asJson(credentials: T): U\n    def parse(json: Array[Byte]): Either[Key.EntryError, List[T]] =\n      try {\n        val list   = readFromArray(json).map(_.credentials)\n        val errors = list.collect { case Left(errors) => errors }.flatten\n        errors match {\n          case Nil =>\n            Right(list.collect { case Right(v) => v })\n          case h :: t =>\n            Left(new Key.MalformedEntry(this, ::(h, t)))\n        }\n      }\n      catch {\n        case e: JsonReaderException =>\n          Left(new Key.JsonReaderError(e))\n      }\n    def write(value: List[T]): Array[Byte] = writeToArray(value.map(asJson))\n    def fromString(values: Seq[String]): Either[MalformedValue, List[T]] =\n      Left(new Key.MalformedValue(this, values, Right(ErrorMessages.inlineCredentialsError)))\n    def asString(value: List[T]): Seq[String] = value.map(_.asString)\n  }\n\n  final class RepositoryCredentialsEntry(\n    val prefix: Seq[String],\n    val name: String,\n    override val specificationLevel: SpecificationLevel,\n    val description: String = \"\",\n    override val hidden: Boolean = false\n  ) extends CredentialsEntry[RepositoryCredentials, RepositoryCredentialsAsJson] {\n    def asJson(credentials: RepositoryCredentials): RepositoryCredentialsAsJson =\n      RepositoryCredentialsAsJson(\n        credentials.host,\n        credentials.user.map(_.asString.value),\n        credentials.password.map(_.asString.value),\n        credentials.realm,\n        credentials.optional,\n        credentials.matchHost,\n        credentials.httpsOnly,\n        credentials.passOnRedirect\n      )\n\n    override def asString(value: List[RepositoryCredentials]): Seq[String] =\n      value\n        .zipWithIndex\n        .map {\n          case (cred, idx) =>\n            val prefix = s\"configRepo$idx.\"\n            cred.asString.linesWithSeparators.map(prefix + _).mkString\n        }\n  }\n\n  class PublishCredentialsEntry(\n    val prefix: Seq[String],\n    val name: String,\n    override val specificationLevel: SpecificationLevel,\n    val description: String = \"\",\n    override val hidden: Boolean = false\n  ) extends CredentialsEntry[PublishCredentials, PublishCredentialsAsJson] {\n    def asJson(credentials: PublishCredentials): PublishCredentialsAsJson =\n      PublishCredentialsAsJson(\n        credentials.host,\n        credentials.user.map(_.asString.value),\n        credentials.password.map(_.asString.value),\n        credentials.realm,\n        credentials.httpsOnly\n      )\n  }\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/Keys.scala",
    "content": "package scala.cli.config\nimport scala.cli.commands.SpecificationLevel\n\nobject Keys {\n  val userName = new Key.StringEntry(\n    prefix = Seq(\"publish\", \"user\"),\n    name = \"name\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"The 'name' user detail, used for publishing.\"\n  )\n  val userEmail = new Key.StringEntry(\n    prefix = Seq(\"publish\", \"user\"),\n    name = \"email\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"The 'email' user detail, used for publishing.\"\n  )\n  val userUrl = new Key.StringEntry(\n    prefix = Seq(\"publish\", \"user\"),\n    name = \"url\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"The 'url' user detail, used for publishing.\"\n  )\n\n  val ghToken = new Key.PasswordEntry(\n    prefix = Seq(\"github\"),\n    name = \"token\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"GitHub token.\"\n  )\n\n  val pgpSecretKey = new Key.PasswordEntry(\n    prefix = Seq(\"pgp\"),\n    name = \"secret-key\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"The PGP secret key, used for signing.\"\n  )\n  val pgpSecretKeyPassword = new Key.PasswordEntry(\n    prefix = Seq(\"pgp\"),\n    name = \"secret-key-password\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"The PGP secret key password, used for signing.\"\n  )\n  val pgpPublicKey = new Key.PasswordEntry(\n    prefix = Seq(\"pgp\"),\n    name = \"public-key\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description = \"The PGP public key, used for signing.\",\n    hidden = true\n  )\n\n  val actions = new Key.BooleanEntry(\n    prefix = Seq.empty,\n    name = \"actions\",\n    specificationLevel = SpecificationLevel.IMPLEMENTATION,\n    description = \"Globally enables actionable diagnostics. Enabled by default.\"\n  )\n  val interactive = new Key.BooleanEntry(\n    prefix = Seq.empty,\n    name = \"interactive\",\n    specificationLevel = SpecificationLevel.IMPLEMENTATION,\n    description = \"Globally enables interactive mode (the '--interactive' flag).\"\n  )\n  val power = new Key.BooleanEntry(\n    prefix = Seq.empty,\n    name = \"power\",\n    specificationLevel = SpecificationLevel.MUST,\n    description = \"Globally enables power mode (the '--power' launcher flag).\"\n  )\n\n  val offline = new Key.BooleanEntry(\n    prefix = Seq.empty,\n    name = \"offline\",\n    specificationLevel = SpecificationLevel.IMPLEMENTATION,\n    description = \"Globally enables offline mode (the '--offline' flag).\"\n  )\n\n  val suppressDirectivesInMultipleFilesWarning =\n    new Key.BooleanEntry(\n      prefix = Seq(\"suppress-warning\"),\n      name = \"directives-in-multiple-files\",\n      specificationLevel = SpecificationLevel.IMPLEMENTATION,\n      description =\n        \"Globally suppresses warnings about directives declared in multiple source files.\"\n    )\n  val suppressOutdatedDependenciessWarning =\n    new Key.BooleanEntry(\n      prefix = Seq(\"suppress-warning\"),\n      name = \"outdated-dependencies-files\",\n      specificationLevel = SpecificationLevel.IMPLEMENTATION,\n      description = \"Globally suppresses warnings about outdated dependencies.\"\n    )\n  val suppressExperimentalFeatureWarning =\n    new Key.BooleanEntry(\n      prefix = Seq(\"suppress-warning\"),\n      name = \"experimental-features\",\n      specificationLevel = SpecificationLevel.IMPLEMENTATION,\n      description = \"Globally suppresses warnings about experimental features.\"\n    )\n  val suppressDeprecatedFeatureWarning =\n    new Key.BooleanEntry(\n      prefix = Seq(\"suppress-warning\"),\n      name = \"deprecated-features\",\n      specificationLevel = SpecificationLevel.IMPLEMENTATION,\n      description = \"Globally suppresses warnings about deprecated features.\"\n    )\n\n  val proxyAddress = new Key.StringEntry(\n    prefix = Seq(\"httpProxy\"),\n    name = \"address\",\n    specificationLevel = SpecificationLevel.RESTRICTED,\n    description = \"HTTP proxy address.\"\n  )\n  val proxyUser = new Key.PasswordEntry(\n    prefix = Seq(\"httpProxy\"),\n    name = \"user\",\n    specificationLevel = SpecificationLevel.RESTRICTED,\n    description = \"HTTP proxy user (used for authentication).\"\n  )\n  val proxyPassword = new Key.PasswordEntry(\n    prefix = Seq(\"httpProxy\"),\n    name = \"password\",\n    specificationLevel = SpecificationLevel.RESTRICTED,\n    description = \"HTTP proxy password (used for authentication).\"\n  )\n\n  val repositoryMirrors = new Key.StringListEntry(\n    prefix = Seq(\"repositories\"),\n    name = \"mirrors\",\n    description =\n      s\"Repository mirrors, syntax: repositories.mirrors maven:*=https://repository.company.com/maven\",\n    specificationLevel = SpecificationLevel.RESTRICTED\n  )\n  val defaultRepositories = new Key.StringListEntry(\n    prefix = Seq(\"repositories\"),\n    name = \"default\",\n    description =\n      \"Default repository, syntax: https://first-repo.company.com https://second-repo.company.com\",\n    specificationLevel = SpecificationLevel.RESTRICTED\n  )\n  val javaProperties = new Key.StringListEntry(\n    prefix = Nil,\n    name = \"java.properties\",\n    description =\n      \"Java properties for Scala CLI's execution.\",\n    specificationLevel = SpecificationLevel.SHOULD\n  )\n\n  // Kept for binary compatibility\n  val repositoriesMirrors: Key.StringListEntry = repositoryMirrors\n\n  // setting indicating if the global interactive mode was suggested\n  val globalInteractiveWasSuggested = new Key.BooleanEntry(\n    prefix = Seq.empty,\n    name = \"interactive-was-suggested\",\n    specificationLevel = SpecificationLevel.IMPLEMENTATION,\n    description = \"Setting indicating if the global interactive mode was already suggested.\",\n    hidden = true\n  )\n\n  val repositoryCredentials: Key.RepositoryCredentialsEntry = new Key.RepositoryCredentialsEntry(\n    prefix = Seq(\"repositories\"),\n    name = \"credentials\",\n    specificationLevel = SpecificationLevel.RESTRICTED,\n    description =\n      \"Repository credentials, syntax: repositoryAddress value:user value:password [realm]\"\n  )\n\n  val publishCredentials: Key.PublishCredentialsEntry = new Key.PublishCredentialsEntry(\n    prefix = Seq(\"publish\"),\n    name = \"credentials\",\n    specificationLevel = SpecificationLevel.EXPERIMENTAL,\n    description =\n      \"Publishing credentials, syntax: repositoryAddress value:user value:password [realm]\"\n  )\n\n  def all: Seq[Key[?]] = Seq[Key[?]](\n    actions,\n    defaultRepositories,\n    ghToken,\n    globalInteractiveWasSuggested,\n    interactive,\n    javaProperties,\n    suppressDirectivesInMultipleFilesWarning,\n    suppressOutdatedDependenciessWarning,\n    suppressExperimentalFeatureWarning,\n    suppressDeprecatedFeatureWarning,\n    offline,\n    pgpPublicKey,\n    pgpSecretKey,\n    pgpSecretKeyPassword,\n    power,\n    proxyAddress,\n    proxyPassword,\n    proxyUser,\n    publishCredentials,\n    repositoryCredentials,\n    repositoryMirrors,\n    userEmail,\n    userName,\n    userUrl\n  )\n  lazy val map: Map[String, Key[?]] = all.map(e => e.fullName -> e).toMap\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/PasswordOption.scala",
    "content": "package scala.cli.config\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\n\nimport java.io.ByteArrayOutputStream\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.{Files, Path, Paths}\n\nsealed abstract class PasswordOption extends Product with Serializable {\n  def get(): Secret[String]\n  def getBytes: Secret[Array[Byte]] = get().map(_.getBytes(StandardCharsets.UTF_8))\n  def asString: Secret[String]\n}\n\nabstract class LowPriorityPasswordOption {\n\n  protected lazy val commandCodec: JsonValueCodec[List[String]] =\n    JsonCodecMaker.make\n\n  def parse(str: String): Either[String, PasswordOption] =\n    if (str.startsWith(\"value:\"))\n      Right(PasswordOption.Value(Secret(str.stripPrefix(\"value:\"))))\n    else if (str.startsWith(\"file:\"))\n      Right(PasswordOption.File(Paths.get(str.stripPrefix(\"file:\"))))\n    else if (str.startsWith(\"env:\"))\n      Right(PasswordOption.Env(str.stripPrefix(\"env:\")))\n    else if (str.startsWith(\"command:[\"))\n      try {\n        val command = readFromString(str.stripPrefix(\"command:\"))(using commandCodec)\n        Right(PasswordOption.Command(command))\n      }\n      catch {\n        case e: JsonReaderException =>\n          Left(s\"Error decoding password command: ${e.getMessage}\")\n      }\n    else if (str.startsWith(\"command:\")) {\n      val command = str.stripPrefix(\"command:\").split(\"\\\\s+\").toSeq\n      Right(PasswordOption.Command(command))\n    }\n    else\n      Left(\"Malformed password value (expected \\\"value:password\\\")\")\n\n}\n\nobject PasswordOption extends LowPriorityPasswordOption {\n\n  final case class Value(value: Secret[String]) extends PasswordOption {\n    def get(): Secret[String]    = value\n    def asString: Secret[String] = get().map(v => s\"value:$v\")\n  }\n  final case class Env(name: String) extends PasswordOption {\n    def get(): Secret[String] = {\n      val value = Option(System.getenv(name)).getOrElse {\n        sys.error(s\"Error: environment variable $name not set\")\n      }\n      Secret(value)\n    }\n    def asString: Secret[String] =\n      Secret(s\"env:$name\")\n  }\n  final case class File(path: Path) extends PasswordOption {\n    def get(): Secret[String] = {\n      val value = new String(Files.readAllBytes(path), StandardCharsets.UTF_8) // trim that?\n      Secret(value)\n    }\n    override def getBytes: Secret[Array[Byte]] = {\n      val value = Files.readAllBytes(path)\n      Secret(value)\n    }\n    def asString: Secret[String] =\n      Secret(s\"file:$path\")\n  }\n  final case class Command(command: Seq[String]) extends PasswordOption {\n    def get(): Secret[String] = {\n      // should we add a timeout?\n\n      // Better use ProcessBuilder than sys.process here, as the latter\n      // adds superfluous new line characters when reading the command output.\n      // That way, we know we are reading the actual output of the command, and we\n      // don't have to speculatively trim the result (which would work if the new\n      // line is added by sys.process, but wouldn't if the new line is legit and is in\n      // the original output).\n\n      val b = new ProcessBuilder(command*)\n      b.redirectInput(ProcessBuilder.Redirect.INHERIT)\n      b.redirectError(ProcessBuilder.Redirect.INHERIT)\n      b.redirectOutput(ProcessBuilder.Redirect.PIPE)\n\n      val p = b.start()\n\n      val is   = p.getInputStream\n      var read = -1\n      val buf  = Array.ofDim[Byte](2048)\n      val baos = new ByteArrayOutputStream\n      while ({\n        read = is.read(buf)\n        read >= 0\n      })\n        if (read > 0)\n          baos.write(buf, 0, read)\n\n      val exitCode = p.waitFor()\n      if (exitCode != 0)\n        throw new RuntimeException(\n          s\"Error running command ${command.mkString(\" \")} (exit code: $exitCode)\"\n        )\n\n      val res = new String(baos.toByteArray) // Using default codec here\n      Secret(res)\n    }\n    def asString: Secret[String] = {\n      val json = writeToString(command.toList)(using commandCodec)\n      Secret(s\"command:$json\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/PublishCredentials.scala",
    "content": "package scala.cli.config\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\n\nfinal case class PublishCredentials(\n  host: String = \"\",\n  user: Option[PasswordOption] = None,\n  password: Option[PasswordOption] = None,\n  realm: Option[String] = None,\n  httpsOnly: Option[Boolean] = None\n) extends CredentialsValue {\n  override def asString: String = {\n    val prefix = httpsOnly match {\n      case Some(true)  => \"https://\"\n      case Some(false) => \"http://\"\n      case None        => \"//\"\n    }\n    // FIXME We're getting secrets and putting them in a non-Secret guarded string here\n    val credentialsPart = {\n      val realmPart    = realm.map(\"(\" + _ + \")\").getOrElse(\"\")\n      val userPart     = user.map(_.get().value).getOrElse(\"\")\n      val passwordPart = password.map(\":\" + _.get().value).getOrElse(\"\")\n      if (realmPart.nonEmpty || userPart.nonEmpty || passwordPart.nonEmpty)\n        realmPart + userPart + passwordPart + \"@\"\n      else\n        \"\"\n    }\n    prefix + credentialsPart + host\n  }\n}\n\nfinal case class PublishCredentialsAsJson(\n  host: String,\n  user: Option[String] = None,\n  password: Option[String] = None,\n  realm: Option[String] = None,\n  httpsOnly: Option[Boolean] = None\n) extends CredentialsAsJson[PublishCredentials] {\n  def credentialsType: String = \"publish\"\n  def toCredentialsValue(\n    userOpt: Option[PasswordOption],\n    passwordOpt: Option[PasswordOption]\n  ): PublishCredentials =\n    PublishCredentials(\n      host = host,\n      user = userOpt,\n      password = passwordOpt,\n      realm = realm,\n      httpsOnly = httpsOnly\n    )\n}\n\nobject PublishCredentialsAsJson {\n  implicit lazy val listJsonCodec: JsonValueCodec[List[PublishCredentialsAsJson]] =\n    JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/RawJson.scala",
    "content": "package scala.cli.config\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\n\nimport java.nio.charset.StandardCharsets\nimport java.util\n\nimport scala.util.Try\nimport scala.util.hashing.MurmurHash3\n\n// adapted from https://github.com/plokhotnyuk/jsoniter-scala/blob/209d918a030b188f064ee55505a6c47257731b4b/jsoniter-scala-macros/src/test/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMakerSpec.scala#L645-L666\nprivate[config] final case class RawJson(value: Array[Byte]) {\n  override lazy val hashCode: Int        = MurmurHash3.arrayHash(value)\n  override def equals(obj: Any): Boolean = obj match {\n    case that: RawJson => util.Arrays.equals(value, that.value)\n    case _             => false\n  }\n  override def toString: String =\n    Try(new String(value, StandardCharsets.UTF_8))\n      .toOption\n      .getOrElse(value.toString)\n}\n\nprivate[config] object RawJson {\n\n  implicit val codec: JsonValueCodec[RawJson] = new JsonValueCodec[RawJson] {\n    def decodeValue(in: JsonReader, default: RawJson): RawJson =\n      new RawJson(in.readRawValAsBytes())\n    def encodeValue(x: RawJson, out: JsonWriter): Unit =\n      out.writeRawVal(x.value)\n    val nullValue: RawJson =\n      new RawJson(new Array[Byte](0))\n  }\n\n  val emptyObj: RawJson =\n    RawJson(\"{}\".getBytes(StandardCharsets.UTF_8))\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/RepositoryCredentials.scala",
    "content": "package scala.cli.config\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\n\nimport scala.collection.mutable.ListBuffer\n\nfinal case class RepositoryCredentials(\n  host: String = \"\",\n  user: Option[PasswordOption] = None,\n  password: Option[PasswordOption] = None,\n  realm: Option[String] = None,\n  optional: Option[Boolean] = None,\n  matchHost: Option[Boolean] = None,\n  httpsOnly: Option[Boolean] = None,\n  passOnRedirect: Option[Boolean] = None\n) extends CredentialsValue {\n  def asString: String = {\n    val lines = new ListBuffer[String]\n    if (host.nonEmpty)\n      lines += s\"host=$host\"\n    for (u <- user)\n      lines += s\"username=${u.asString.value}\"\n    for (p <- password)\n      lines += s\"password=${p.asString.value}\"\n    for (r <- realm)\n      lines += s\"realm=$r\"\n    for (b <- httpsOnly)\n      lines += s\"https-only=$b\"\n    for (b <- matchHost)\n      lines += s\"auto=$b\"\n    for (b <- passOnRedirect)\n      lines += s\"pass-on-redirect=$b\"\n    // seems cred.optional can't be changed from properties…\n    lines.map(_ + System.lineSeparator()).mkString\n  }\n}\n\nfinal case class RepositoryCredentialsAsJson(\n  host: String,\n  user: Option[String] = None,\n  password: Option[String] = None,\n  realm: Option[String] = None,\n  optional: Option[Boolean] = None,\n  matchHost: Option[Boolean] = None,\n  httpsOnly: Option[Boolean] = None,\n  passOnRedirect: Option[Boolean] = None\n) extends CredentialsAsJson[RepositoryCredentials] {\n  def credentialsType: String = \"repository\"\n  def toCredentialsValue(\n    userOpt: Option[PasswordOption],\n    passwordOpt: Option[PasswordOption]\n  ): RepositoryCredentials =\n    RepositoryCredentials(\n      host = host,\n      user = userOpt,\n      password = passwordOpt,\n      realm = realm,\n      optional = optional,\n      matchHost = matchHost,\n      httpsOnly = httpsOnly,\n      passOnRedirect = passOnRedirect\n    )\n}\n\nobject RepositoryCredentialsAsJson {\n  implicit lazy val listJsonCodec: JsonValueCodec[List[RepositoryCredentialsAsJson]] =\n    JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/Secret.scala",
    "content": "package scala.cli.config\n\nfinal class Secret[+T](\n  value0: T\n) {\n  def value: T = value0\n\n  def map[U](f: T => U): Secret[U] =\n    Secret(f(value0))\n\n  override def equals(obj: Any): Boolean =\n    obj match {\n      case other: Secret[_] => value == other.value\n      case _                => false\n    }\n\n  // not leaking details about the secret here\n  override def hashCode(): Int  = 0\n  override def toString: String = \"****\"\n}\n\nobject Secret {\n  def apply[T](value: T): Secret[T] =\n    new Secret(value)\n}\n"
  },
  {
    "path": "modules/config/src/main/scala/scala/cli/config/internal/JavaHelper.scala",
    "content": "package scala.cli.config.internal\n\nimport java.lang.Boolean as JBoolean\nimport java.nio.file.Path\n\nimport scala.cli.commands.SpecificationLevel\nimport scala.cli.config.*\n\nobject JavaHelper {\n\n  private var dbOpt = Option.empty[ConfigDb]\n\n  def open(dbPath: Path): Unit =\n    if (dbOpt.isEmpty) {\n      val db0 = ConfigDb.open(dbPath) match {\n        case Left(ex)   => throw new Exception(ex)\n        case Right(db1) => db1\n      }\n      dbOpt = Some(db0)\n    }\n\n  def close(): Unit =\n    if (dbOpt.nonEmpty)\n      dbOpt = None\n\n  private def split(key: String): (Seq[String], String) = {\n    val elems = key.split(\"\\\\.\")\n    (elems.init.toSeq, elems.last)\n  }\n\n  def getString(key: String): String = {\n    val db             = dbOpt.getOrElse(sys.error(\"DB not open\"))\n    val (prefix, name) = split(key)\n    val key0           = new Key.StringEntry(prefix, name, SpecificationLevel.IMPLEMENTATION)\n    db.get(key0) match {\n      case Left(ex)         => throw new Exception(ex)\n      case Right(None)      => null\n      case Right(Some(str)) => str\n    }\n  }\n\n  def getBoolean(key: String): JBoolean = {\n    val db             = dbOpt.getOrElse(sys.error(\"DB not open\"))\n    val (prefix, name) = split(key)\n    val key0           = new Key.BooleanEntry(prefix, name, SpecificationLevel.IMPLEMENTATION)\n    db.get(key0) match {\n      case Left(ex)           => throw new Exception(ex)\n      case Right(None)        => null\n      case Right(Some(value)) => value\n    }\n  }\n\n  def getStringList(key: String): Array[String] = {\n    val db             = dbOpt.getOrElse(sys.error(\"DB not open\"))\n    val (prefix, name) = split(key)\n    val key0           = new Key.StringListEntry(prefix, name, SpecificationLevel.IMPLEMENTATION)\n    db.get(key0) match {\n      case Left(ex)           => throw new Exception(ex)\n      case Right(None)        => null\n      case Right(Some(value)) => value.toArray\n    }\n  }\n\n  def getPassword(key: String): String = {\n    val db             = dbOpt.getOrElse(sys.error(\"DB not open\"))\n    val (prefix, name) = split(key)\n    val key0           = new Key.PasswordEntry(prefix, name, SpecificationLevel.IMPLEMENTATION)\n    db.get(key0) match {\n      case Left(ex)         => throw new Exception(ex)\n      case Right(None)      => null\n      case Right(Some(str)) => str.get().value\n    }\n  }\n\n  def getPasswordBytes(key: String): Array[Byte] = {\n    val db             = dbOpt.getOrElse(sys.error(\"DB not open\"))\n    val (prefix, name) = split(key)\n    val key0           = new Key.PasswordEntry(prefix, name, SpecificationLevel.IMPLEMENTATION)\n    db.get(key0) match {\n      case Left(ex)         => throw new Exception(ex)\n      case Right(None)      => null\n      case Right(Some(str)) => str.getBytes.value\n    }\n  }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/CsUtils.scala",
    "content": "package scala.build\n\nimport coursier.version.Version\n\nimport scala.build.internal.Constants\n\nextension (s: String) def coursierVersion: Version = Version(s)\n\nextension (csv: Version)\n  def isScala38OrNewer: Boolean =\n    Constants.scala38Versions\n      .map(_.coursierVersion)\n      .exists(_ <= csv)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/Logger.scala",
    "content": "package scala.build\n\nimport bloop.rifle.BloopRifleLogger\nimport org.scalajs.logging.{Logger as ScalaJsLogger, NullLogger}\n\nimport java.io.{OutputStream, PrintStream}\n\nimport scala.annotation.unused\nimport scala.build.errors.{BuildException, Diagnostic, Severity}\nimport scala.build.internals.FeatureType\nimport scala.scalanative.build as sn\n\ntrait Logger {\n  def error(message: String): Unit\n  // TODO Use macros for log and debug calls to have zero cost when verbosity <= 0\n  def message(message: => String): Unit\n  def log(s: => String): Unit\n  def log(s: => String, debug: => String): Unit\n  def debug(s: => String): Unit\n\n  def debugStackTrace(t: => Throwable): Unit = t.getStackTrace.foreach(ste => debug(ste.toString))\n\n  def log(diagnostics: Seq[Diagnostic]): Unit\n\n  def diagnostic(\n    message: String,\n    severity: Severity = Severity.Warning,\n    positions: Seq[Position] = Nil\n  ): Unit = log(Seq(Diagnostic(message, severity, positions)))\n\n  def log(ex: BuildException): Unit\n  def debug(ex: BuildException): Unit\n  def exit(ex: BuildException): Nothing\n\n  def coursierLogger(printBefore: String): coursier.cache.CacheLogger\n  def bloopRifleLogger: BloopRifleLogger\n  def scalaJsLogger: ScalaJsLogger\n  def scalaNativeTestLogger: sn.Logger\n  def scalaNativeCliInternalLoggerOptions: List[String]\n\n  def compilerOutputStream: PrintStream\n\n  def verbosity: Int\n\n  /** Since we have a lot of experimental warnings all over the build process, this method can be\n    * used to accumulate them for a better UX\n    */\n  def experimentalWarning(featureName: String, featureType: FeatureType): Unit\n  def flushExperimentalWarnings: Unit\n\n  def cliFriendlyDiagnostic(\n    message: String,\n    @unused cliFriendlyMessage: String,\n    severity: Severity = Severity.Warning,\n    positions: Seq[Position] = Nil\n  ): Unit = diagnostic(message, severity, positions)\n}\n\nobject Logger {\n  private class Nop extends Logger {\n    def error(message: String): Unit              = ()\n    def message(message: => String): Unit         = ()\n    def log(s: => String): Unit                   = ()\n    def log(s: => String, debug: => String): Unit = ()\n    def debug(s: => String): Unit                 = ()\n\n    def log(diagnostics: Seq[Diagnostic]): Unit = ()\n    def log(ex: BuildException): Unit           = ()\n    def debug(ex: BuildException): Unit         = ()\n    def exit(ex: BuildException): Nothing       =\n      throw new Exception(ex)\n\n    def coursierLogger(printBefore: String): coursier.cache.CacheLogger =\n      coursier.cache.CacheLogger.nop\n    def bloopRifleLogger: BloopRifleLogger =\n      BloopRifleLogger.nop\n    def scalaJsLogger: ScalaJsLogger =\n      NullLogger\n    def scalaNativeTestLogger: sn.Logger =\n      sn.Logger.nullLogger\n    def scalaNativeCliInternalLoggerOptions: List[String] =\n      List()\n\n    def compilerOutputStream: PrintStream =\n      new PrintStream(\n        new OutputStream {\n          override def write(b: Int): Unit                             = ()\n          override def write(b: Array[Byte], off: Int, len: Int): Unit = ()\n        }\n      )\n\n    def verbosity: Int = -1\n\n    def experimentalWarning(featureUsed: String, featureType: FeatureType): Unit = ()\n    def flushExperimentalWarnings: Unit                                          = ()\n  }\n  def nop: Logger = new Nop\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/Os.scala",
    "content": "package scala.build\n\nimport java.util.Locale\n\nimport scala.util.Properties\n\nobject Os {\n  lazy val pwd: os.Path =\n    if (Properties.isWin)\n      os.Path(os.pwd.toIO.getCanonicalFile)\n    else\n      os.pwd\n  lazy val isArmArchitecture: Boolean =\n    sys.props.getOrElse(\"os.arch\", \"\").toLowerCase(Locale.ROOT) == \"aarch64\"\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/Position.scala",
    "content": "package scala.build\n\nimport scala.collection.mutable\n\nsealed abstract class Position {\n  def render(): String =\n    render(Os.pwd, java.io.File.separator)\n  def render(cwd: os.Path): String =\n    render(cwd, java.io.File.separator)\n  def render(cwd: os.Path, sep: String): String\n}\n\nobject Position {\n\n  final case class File(\n    path: Either[String, os.Path],\n    startPos: (Int, Int),\n    endPos: (Int, Int),\n    offset: Int = 0\n  ) extends Position {\n    def render(cwd: os.Path, sep: String): String = {\n      val p = path match {\n        case Left(p0)  => p0\n        case Right(p0) =>\n          if (p0.startsWith(cwd)) p0.relativeTo(cwd).segments.mkString(sep)\n          else p0.toString\n      }\n      if (startPos == endPos)\n        s\"$p:${startPos._1 + 1}:${startPos._2 + 1}\"\n      else if (startPos._1 == endPos._1)\n        s\"$p:${startPos._1 + 1}:${startPos._2 + 1}-${endPos._2 + 1}\"\n      else\n        s\"$p:${startPos._1 + 1}:${startPos._2 + 1}-${endPos._1 + 1}:${endPos._2 + 1}\"\n    }\n  }\n\n  final case class Raw(startIdx: Int, endIdx: Int) extends Position {\n    def render(cwd: os.Path, sep: String): String =\n      s\"raw $startIdx:$endIdx\"\n    def +(shift: Int): Raw =\n      Raw(startIdx + shift, endIdx + shift)\n  }\n\n  object Raw {\n\n    // from https://github.com/com-lihaoyi/Ammonite/blob/76673f7f3eb9d9ae054482635f57a31527d248de/amm/interp/src/main/scala/ammonite/interp/script/PositionOffsetConversion.scala#L7-L69\n\n    def lineStartIndices(content: String): Array[Int] = {\n\n      val content0 = content.toCharArray\n\n      // adapted from scala/scala SourceFile.scala\n\n      val length = content0.length\n      val CR     = '\\r'\n      val LF     = '\\n'\n\n      def charAtIsEOL(idx: Int)(p: Char => Boolean) = {\n        // don't identify the CR in CR LF as a line break, since LF will do.\n        def notCRLF0 =\n          content0(idx) != CR ||\n          !content0.isDefinedAt(idx + 1) ||\n          content0(idx + 1) != LF\n\n        idx < length && notCRLF0 && p(content0(idx))\n      }\n\n      def isAtEndOfLine(idx: Int) = charAtIsEOL(idx) {\n        case CR | LF => true\n        case _       => false\n      }\n\n      val buf = new mutable.ArrayBuffer[Int]\n      buf += 0\n      for (i <- 0 until content0.length if isAtEndOfLine(i))\n        buf += i + 1\n      buf.toArray\n    }\n\n    private def offsetToPos(content: String): Int => (Int, Int) = {\n\n      val lineStartIndices0 = lineStartIndices(content)\n\n      def offsetToLine(offset: Int): Int = {\n\n        assert(lineStartIndices0.nonEmpty)\n\n        if (offset >= lineStartIndices0.last) lineStartIndices0.length - 1\n        else {\n          def find(a: Int, b: Int): Int =\n            if (a + 1 >= b) a\n            else {\n              val c   = (a + b) / 2\n              val idx = lineStartIndices0(c)\n              if (idx == offset) c\n              else if (idx < offset) find(c, b)\n              else find(a, c)\n            }\n          find(0, lineStartIndices0.length - 1)\n        }\n      }\n\n      offset =>\n        assert(offset >= 0)\n        assert(offset <= content.length)\n        val line = offsetToLine(offset)\n        (line, offset - lineStartIndices0(line))\n    }\n\n    def filePos(path: Either[String, os.Path], content: String): Raw => File = {\n      val f = offsetToPos(content)\n      raw =>\n        val startPos = f(raw.startIdx)\n        val endPos   = f(raw.endIdx)\n        File(path, startPos, endPos)\n    }\n  }\n  final case class CommandLine(arg: String =\n    \"\") extends Position { // todo the exact arg should be somehow taken from CaseApp\n    def render(cwd: os.Path, sep: String): String = \"COMMAND_LINE\"\n  }\n\n  final case class Bloop(bloopJavaPath: String) extends Position {\n    def render(cwd: os.Path, sep: String): String = bloopJavaPath\n  }\n\n  final case class Custom(msg: String) extends Position {\n    def render(cwd: os.Path, sep: String): String = msg\n  }\n\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/RepositoryUtils.scala",
    "content": "package scala.build\n\nimport coursier.maven.MavenRepository\n\nobject RepositoryUtils {\n  lazy val snapshotsRepositoryUrl     = \"https://central.sonatype.com/repository/maven-snapshots\"\n  lazy val snapshotsRepository        = MavenRepository(snapshotsRepositoryUrl)\n  lazy val scala3NightlyRepositoryUrl = \"https://repo.scala-lang.org/artifactory/maven-nightlies\"\n  lazy val scala3NightlyRepository    = MavenRepository(scala3NightlyRepositoryUrl)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/AmbiguousPlatformError.scala",
    "content": "package scala.build.errors\n\nfinal class AmbiguousPlatformError(passedPlatformTypes: Seq[String]) extends BuildException(\n      s\"Ambiguous platform: more than one type of platform has been passed: ${passedPlatformTypes.mkString(\", \")}.\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/BuildException.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nabstract class BuildException(\n  override val message: String,\n  override val positions: Seq[Position] = Nil,\n  cause: Throwable = null\n) extends Exception(message, cause) with Diagnostic {\n  final override def severity: Severity = Severity.Error\n\n  /** @param default\n    *   default value returned as a [[Right]] instance on recovery\n    * @param maybeRecoverFunction\n    *   potential recovery function, returns [[None]] on recovery and Some(identity(_)) otherwise\n    * @tparam T\n    *   type of the default value\n    * @return\n    *   Right(default) on recovery, Left(buildException) otherwise\n    */\n  def maybeRecoverWithDefault[T](\n    default: T,\n    maybeRecoverFunction: BuildException => Option[BuildException]\n  ): Either[BuildException, T] = maybeRecoverFunction(this) match {\n    case Some(e) => Left(e)\n    case None    => Right(default)\n  }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/BuildInfoGenerationError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class BuildInfoGenerationError(msg: String, positions: Seq[Position], cause: Exception)\n    extends BuildException(\n      s\"BuildInfo generation error: $msg\",\n      positions = positions,\n      cause = cause\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/CantDownloadAmmoniteError.scala",
    "content": "package scala.build.errors\n\nimport coursier.error.ResolutionError\n\nimport scala.build.Position\n\nfinal class CantDownloadAmmoniteError(\n  ammoniteVersion: String,\n  scalaVersion: String,\n  underlying: ResolutionError.CantDownloadModule,\n  override val positions: Seq[Position]\n) extends BuildException(\n      s\"\"\"Can't download Ammonite $ammoniteVersion for Scala $scalaVersion.\n         |Ammonite with this Scala version might not yet be supported.\n         |Try passing a different Scala version with the -S option.\"\"\".stripMargin,\n      positions,\n      underlying\n    )\n\nobject CantDownloadAmmoniteError {\n  def apply(\n    ammoniteVersion: String,\n    scalaVersion: String,\n    underlying: ResolutionError.CantDownloadModule,\n    positions: Seq[Position]\n  ): CantDownloadAmmoniteError =\n    new CantDownloadAmmoniteError(ammoniteVersion, scalaVersion, underlying, positions)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/CheckScalaCliVersionError.scala",
    "content": "package scala.build.errors\n\nclass CheckScalaCliVersionError(val message: String, val cause: Throwable)\n    extends Exception(message, cause)\n\nobject CheckScalaCliVersionError {\n  def apply(message: String, cause: Throwable = null): CheckScalaCliVersionError =\n    new CheckScalaCliVersionError(message, cause)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/CompositeBuildException.scala",
    "content": "package scala.build.errors\n\nfinal class CompositeBuildException private (\n  val mainException: BuildException,\n  val others: Seq[BuildException]\n) extends BuildException(\n      s\"${others.length + 1} exceptions, first one: ${mainException.getMessage}\",\n      Nil,\n      mainException\n    ) {\n\n  def exceptions: Seq[BuildException] =\n    mainException +: others\n\n}\n\nobject CompositeBuildException {\n  private def flatten(list: ::[BuildException]): ::[BuildException] = {\n    val list0 = list.flatMap {\n      case c: CompositeBuildException => c.mainException :: c.others.toList\n      case e                          => e :: Nil\n    }\n    list0 match {\n      case Nil    => sys.error(\"Can't happen\")\n      case h :: t => ::(h, t)\n    }\n  }\n  def apply(exceptions: ::[BuildException]): BuildException =\n    flatten(exceptions) match {\n      case h :: Nil => h\n      case h :: t   => new CompositeBuildException(h, t)\n    }\n\n  def apply(exceptions: Seq[BuildException]): BuildException =\n    exceptions.distinct match {\n      case Seq(head)    => head\n      case head +: tail => new CompositeBuildException(head, tail)\n    }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ConfigDbException.scala",
    "content": "package scala.build.errors\n\nfinal class ConfigDbException(cause: Exception)\n    extends BuildException(s\"Config DB error: ${cause.getMessage}\", cause = cause)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/CoursierDependencyError.scala",
    "content": "package scala.build.errors\n\nimport coursier.error.DependencyError\n\nimport scala.build.Position\n\nclass CoursierDependencyError(val underlying: DependencyError, positions: Seq[Position] = Seq.empty)\n    extends BuildException(\n      s\"Could not fetch dependency: ${underlying.message}\",\n      positions = positions,\n      cause = underlying.getCause\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/DependencyFormatError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class DependencyFormatError(\n  val dependencyString: String,\n  val error: String,\n  positions: Seq[Position]\n) extends BuildException(\n      s\"Error parsing dependency '$dependencyString': $error\",\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/Diagnostic.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\nimport scala.build.errors.Diagnostic.TextEdit\n\ntrait Diagnostic {\n  def message: String\n  def severity: Severity\n  def positions: Seq[Position]\n  def textEdit: Option[TextEdit] = None\n}\n\nobject Diagnostic {\n  case class TextEdit(title: String, newText: String)\n  object Messages {\n    val bloopTooOld =\n      \"\"\"JVM that is hosting bloop is older than the requested runtime. Please restart the Build Server from your IDE.\n        |Or run the command `bloop exit`, and then use `--jvm` flag to request a sufficient JVM version.\n        |\"\"\".stripMargin\n  }\n\n  private case class ADiagnostic(\n    message: String,\n    severity: Severity,\n    positions: Seq[Position],\n    override val textEdit: Option[TextEdit]\n  ) extends Diagnostic\n\n  def apply(\n    message: String,\n    severity: Severity,\n    positions: Seq[Position] = Nil,\n    textEdit: Option[TextEdit] = None\n  ): Diagnostic = ADiagnostic(message, severity, positions, textEdit)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/DirectiveErrors.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class DirectiveErrors(errors: ::[String], positions: Seq[Position]) extends BuildException(\n      \"Directives errors: \" + errors.mkString(\", \"),\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ExcludeDefinitionError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class ExcludeDefinitionError(positions: Seq[Position], expectedProjectFilePath: os.Path)\n    extends BuildException(\n      s\"\"\"Found exclude directives in files:\n         |  ${positions.map(_.render()).distinct.mkString(\", \")}\n         |exclude directive must be defined in project configuration file: $expectedProjectFilePath.\"\"\".stripMargin\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/FetchingDependenciesError.scala",
    "content": "package scala.build.errors\n\nimport coursier.error.CoursierError\n\nimport scala.build.Position\n\nfinal class FetchingDependenciesError(\n  val underlying: CoursierError,\n  override val positions: Seq[Position]\n) extends BuildException(\n      underlying.getMessage,\n      positions,\n      underlying\n    )\n\nobject FetchingDependenciesError {\n  def unapply(e: FetchingDependenciesError): Option[(CoursierError, Seq[Position])] =\n    Some(e.underlying -> e.positions)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/FileNotFoundException.scala",
    "content": "package scala.build.errors\n\nfinal class FileNotFoundException(val path: os.Path)\n    extends BuildException(s\"File not found: $path\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ForbiddenPathReferenceError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class ForbiddenPathReferenceError(\n  val virtualRoot: String,\n  val positionOpt: Option[Position]\n) extends BuildException(\n      s\"Can't reference paths from sources from $virtualRoot\",\n      positionOpt.toSeq\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/InputsException.scala",
    "content": "package scala.build.errors\n\nclass InputsException(message: String)\n    extends BuildException(\n      message = message\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/InvalidBinaryScalaVersionError.scala",
    "content": "package scala.build.errors\n\nfinal class InvalidBinaryScalaVersionError(val invalidBinaryVersion: String)\n    extends ScalaVersionError(s\"Cannot find matching Scala version for '$invalidBinaryVersion'\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/JmhBuildFailedError.scala",
    "content": "package scala.build.errors\n\nfinal class JmhBuildFailedError extends BuildException(\"JMH build failed\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/JvmDownloadError.scala",
    "content": "package scala.build.errors\n\nfinal class JvmDownloadError(jvmId: String, cause: Throwable)\n    extends BuildException(\n      s\"Cannot download JVM: $jvmId\",\n      cause = cause\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MainClassError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nabstract class MainClassError(\n  message: String,\n  positions: Seq[Position]\n) extends BuildException(message, positions = positions)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MalformedCliInputError.scala",
    "content": "package scala.build.errors\n\nfinal class MalformedCliInputError(message: String)\n    extends BuildException(message)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MalformedDirectiveError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class MalformedDirectiveError(message: String, positions: Seq[Position])\n    extends BuildException(message, positions)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MalformedInputError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class MalformedInputError(\n  val inputType: String,\n  val input: String,\n  val expectedShape: String,\n  positions: Seq[Position] = Nil,\n  cause: Option[Throwable] = None\n) extends BuildException(\n      {\n        val q = \"\\\"\"\n        s\"Malformed $inputType $q$input$q, expected $expectedShape\"\n      },\n      positions = positions,\n      cause = cause.orNull\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MalformedPlatformError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class MalformedPlatformError(\n  marformedInput: String,\n  positions: Seq[Position] = Nil\n) extends BuildException(\n      s\"Unrecognized platform: $marformedInput\",\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MarkdownUnclosedBackticksError.scala",
    "content": "package scala.build.errors\nimport scala.build.Position\n\nclass MarkdownUnclosedBackticksError(\n  backticks: String,\n  positions: Seq[Position]\n) extends BuildException(s\"Unclosed $backticks code block in a Markdown input\", positions)\n\nobject MarkdownUnclosedBackticksError {\n  def apply(backticks: String, positions: Seq[Position]) =\n    new MarkdownUnclosedBackticksError(backticks, positions)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ModuleFormatError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class ModuleFormatError(\n  val moduleString: String,\n  val error: String,\n  val originOpt: Option[String] = None,\n  positions: Seq[Position] = Nil\n) extends BuildException(\n      s\"Error parsing ${originOpt.getOrElse(\"\")}module '$moduleString': $error\",\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/MultipleScalaVersionsError.scala",
    "content": "package scala.build.errors\n\nfinal class MultipleScalaVersionsError(scalaVersions: Seq[String])\n    extends BuildException(\n      message =\n        s\"Multiple Scala versions are present in the build (${scalaVersions.mkString(\" \")}), even though only one is allowed in this context.\",\n      positions = Nil\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoDocBuildError.scala",
    "content": "package scala.build.errors\n\nfinal class NoDocBuildError extends BuildException(\n      \"Doc build not present. It may have been cancelled.\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoFrameworkFoundByBridgeError.scala",
    "content": "package scala.build.errors\n\nfinal class NoFrameworkFoundByBridgeError\n    extends TestError(\"No framework found by Scala.js test bridge\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoFrameworkFoundByNativeBridgeError.scala",
    "content": "package scala.build.errors\n\nfinal class NoFrameworkFoundByNativeBridgeError\n    extends TestError(\"No framework found by Scala Native test bridge\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoMainClassFoundError.scala",
    "content": "package scala.build.errors\n\nfinal class NoMainClassFoundError extends MainClassError(\"No main class found\", Nil)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoScalaVersionProvidedError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class NoScalaVersionProvidedError(\n  val depOrModule: Either[dependency.AnyModule, dependency.AnyDependency],\n  positions: Seq[Position] = Nil\n) extends BuildException(\n      {\n        val str = depOrModule match {\n          case Left(mod)  => \"module \" + mod.render\n          case Right(dep) => \"dependency \" + dep.render\n        }\n        s\"Got Scala $str, but no Scala version is provided\"\n      },\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoTestFrameworkFoundError.scala",
    "content": "package scala.build.errors\n\nfinal class NoTestFrameworkFoundError extends TestError(\"No test framework found\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoTestFrameworkValueProvidedError.scala",
    "content": "package scala.build.errors\n\nfinal class NoTestFrameworkValueProvidedError extends BuildException(\n      \"No test framework value provided to using test-framework directive\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoTestsRun.scala",
    "content": "package scala.build.errors\n\nfinal class NoTestsRun extends TestError(\"No tests were run\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoValidScalaVersionFoundError.scala",
    "content": "package scala.build.errors\n\nfinal class NoValidScalaVersionFoundError(val versionString: String = \"\")\n    extends ScalaVersionError({\n      val suffix = if versionString.nonEmpty then s\" for $versionString\" else \"\"\n      s\"Cannot find a valid matching Scala version$suffix.\"\n    })\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NoValueProvidedError.scala",
    "content": "package scala.build.errors\n\nfinal class NoValueProvidedError(val key: String) extends BuildException(\n      s\"Expected a value for directive $key\",\n      // TODO - this seems like outdated thing\n      positions = Nil // I wish using_directives provided the key position…\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/NodeNotFoundError.scala",
    "content": "package scala.build.errors\n\nfinal class NodeNotFoundError extends BuildException(\n      \"The Node was not found on the PATH. Please ensure that Node is installed correctly and then try again\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ParsingInputsException.scala",
    "content": "package scala.build.errors\n\nclass ParsingInputsException(exceptionMessage: String, cause: Throwable)\n    extends BuildException(\n      message = exceptionMessage,\n      cause = cause\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/RepositoryFormatError.scala",
    "content": "package scala.build.errors\n\nfinal class RepositoryFormatError(errors: ::[String]) extends BuildException(\n      s\"Error parsing repositories: ${errors.mkString(\", \")}\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ScalaNativeBuildError.scala",
    "content": "package scala.build.errors\n\nfinal class ScalaNativeBuildError()\n    extends BuildException(s\"Error compiling with Scala Native\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ScalaNativeCompatibilityError.scala",
    "content": "package scala.build.errors\n\nfinal class ScalaNativeCompatibilityError(\n  val scalaVersion: String,\n  val scalaNativeVersion: String\n) extends BuildException(\n      s\"\"\"Used Scala Native version $scalaNativeVersion is incompatible with Scala $scalaVersion.\n         |Please try one of the following combinations:\n         |  Scala Native version >= 0.4.4 for Scala 3.1 (*.sc & *.scala files)\n         |  Scala Native version >= 0.4.0 for Scala 2.13 (*.sc & *.scala files)\n         |  Scala Native version >= 0.4.0 for Scala 2.12 (*.scala files)\n         |Windows is supported since Scala Native 0.4.1.\n         |\"\"\".stripMargin\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ScalaVersionError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\nimport scala.build.internal.Constants.*\n\nclass ScalaVersionError(message: String, positions: Seq[Position] = Nil, cause: Throwable = null)\n    extends BuildException(\n      s\"$message${ScalaVersionError.getTheGeneralErrorInfo}\",\n      positions = positions,\n      cause\n    ) {}\n\nobject ScalaVersionError {\n  private lazy val defaultScalaVersions =\n    Seq(defaultScala212Version, defaultScala213Version, defaultScalaVersion)\n  lazy val getTheGeneralErrorInfo: String =\n    s\"\"\"\n       |You can only choose one of the 3.x, 2.13.x, and 2.12.x. versions.\n       |The latest supported stable versions are ${defaultScalaVersions.mkString(\", \")}.\n       |In addition, you can request compilation with the last nightly versions of Scala,\n       |by passing the 2.nightly, 2.12.nightly, 2.13.nightly, or 3.nightly arguments.\n       |Specific Scala 2 or Scala 3 nightly versions are also accepted.\n       |You can also request the latest Scala 3 LTS by passing lts or 3.lts.\n       |\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ScalafixPropertiesError.scala",
    "content": "package scala.build.errors\n\nfinal class ScalafixPropertiesError(\n  path: os.Path,\n  cause: Option[Throwable] = None\n) extends BuildException(\n      message = {\n        val causeMessage = cause.map(c => s\": ${c.getMessage}\").getOrElse(\"\")\n        s\"Failed to load Scalafix properties at $path$causeMessage\"\n      },\n      cause = cause.orNull\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/SeveralMainClassesFoundError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class SeveralMainClassesFoundError(\n  mainClasses: ::[String],\n  commandString: String,\n  positions: Seq[Position]\n) extends MainClassError(\n      s\"\"\"Found several main classes: ${mainClasses.mkString(\", \")}\n         |You can run one of them by passing it with the --main-class option, e.g.\n         |  ${Console.BOLD}$commandString --main-class ${mainClasses.head}${Console.RESET}\n         |\n         |You can pick the main class interactively by passing the --interactive option.\n         |  ${Console.BOLD}$commandString --interactive${Console.RESET}\"\"\".stripMargin,\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/Severity.scala",
    "content": "package scala.build.errors\n\nimport ch.epfl.scala.bsp4j as b\n\nsealed abstract class Severity extends Product with Serializable {\n  def toBsp4j: b.DiagnosticSeverity\n}\n\nobject Severity {\n  case object Error extends Severity {\n    override def toBsp4j: b.DiagnosticSeverity = b.DiagnosticSeverity.ERROR\n  }\n  case object Warning extends Severity {\n    override def toBsp4j: b.DiagnosticSeverity = b.DiagnosticSeverity.WARNING\n  }\n  case object Hint extends Severity {\n    override def toBsp4j: b.DiagnosticSeverity = b.DiagnosticSeverity.HINT\n  }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/TestError.scala",
    "content": "package scala.build.errors\n\nabstract class TestError(message: String) extends BuildException(message)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/TooManyFrameworksFoundByBridgeError.scala",
    "content": "package scala.build.errors\n\nfinal class TooManyFrameworksFoundByBridgeError\n    extends TestError(\"Too many frameworks found by Scala.js test bridge\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/ToolkitVersionError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class ToolkitVersionError(msg: String, positions: Seq[Position])\n    extends BuildException(msg, positions)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnexpectedDirectiveError.scala",
    "content": "package scala.build.errors\n\nfinal class UnexpectedDirectiveError(val key: String)\n    extends BuildException(s\"Unexpected directive: $key}\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnexpectedJvmPlatformVersionError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class UnexpectedJvmPlatformVersionError(\n  version: String,\n  positions: Seq[Position]\n) extends BuildException(\n      s\"Unexpected version '$version' specified for JVM platform\",\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnrecognizedDebugModeError.scala",
    "content": "package scala.build.errors\n\nfinal class UnrecognizedDebugModeError(mode: String) extends BuildException(\n      s\"Unrecognized debug mode: $mode.\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnrecognizedJsOptModeError.scala",
    "content": "package scala.build.errors\n\nfinal class UnrecognizedJsOptModeError(\n  mode: String,\n  aliasesForFullLink: Seq[String],\n  aliasesForFastLink: Seq[String]\n) extends BuildException(\n      s\"\"\"Unrecognized JS optimization mode: $mode.\n         |Available options:\n         |- for fastLinkJs: ${aliasesForFastLink.mkString(\", \")}\n         |- for fullLinkJs: ${aliasesForFullLink.mkString(\", \")}\"\"\".stripMargin\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnsupportedFeatureError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nabstract class UnsupportedFeatureError(\n  val featureDescription: String,\n  override val positions: Seq[Position] = Nil\n) extends BuildException(s\"Unsupported feature: $featureDescription\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnsupportedGradleModuleVariantError.scala",
    "content": "package scala.build.errors\n\nimport coursier.core.VariantPublication\n\nimport scala.build.Position\n\nclass UnsupportedGradleModuleVariantError(\n  val variantPublication: VariantPublication,\n  override val positions: Seq[Position] = Nil\n) extends UnsupportedFeatureError(featureDescription =\n      s\"Gradle Module variant: ${variantPublication.name} (${variantPublication.url})\"\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnsupportedScalaVersionError.scala",
    "content": "package scala.build.errors\n\nfinal class UnsupportedScalaVersionError(val binaryVersion: String)\n    extends ScalaVersionError(s\"Unsupported Scala version: $binaryVersion\")\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/UnusedDirectiveError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nfinal class UnusedDirectiveError(key: String, values: Seq[String], position: Position)\n    extends BuildException(\n      s\"Unrecognized directive: $key${\n          if values.isEmpty then \"\" else s\" with values: ${values.mkString(\", \")}\"\n        }\",\n      positions = List(position)\n    )\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/errors/WorkspaceError.scala",
    "content": "package scala.build.errors\n\nfinal class WorkspaceError(message: String)\n    extends BuildException(message)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/CodeWrapper.scala",
    "content": "package scala.build.internal\n\nabstract class CodeWrapper {\n  def apply(\n    code: String,\n    pkgName: Seq[Name],\n    indexedWrapperName: Name,\n    extraCode: String,\n    scriptPath: String\n  ): (String, String)\n\n  def mainClassObject(className: Name): Name\n\n  def wrapCode(\n    pkgName: Seq[Name],\n    indexedWrapperName: Name,\n    code: String,\n    scriptPath: String\n  ): (String, WrapperParams) = {\n\n    // we need to normalize topWrapper and bottomWrapper in order to ensure\n    // the snippets always use the platform-specific newLine\n    val extraCode0                  = \"/*</generated>*/\"\n    val (topWrapper, bottomWrapper) =\n      apply(code, pkgName, indexedWrapperName, extraCode0, scriptPath)\n\n    // match lineSeparator to existing code\n    val nl = code.indexOf(\"\\n\") match {\n      case n if n > 0 && code(n - 1) == '\\r' => System.lineSeparator()\n      case _                                 => \"\\n\"\n    }\n    val (topWrapper0, bottomWrapper0) =\n      (\n        topWrapper + \"/*<script>*/\" + nl,\n        nl + \"/*</script>*/ /*<generated>*/\" + bottomWrapper\n      )\n\n    val mainClassName = (pkgName :+ mainClassObject(indexedWrapperName)).map(\n      _.encoded\n    ).mkString(\".\")\n\n    val wrapperParams =\n      WrapperParams(topWrapper0.linesIterator.size, code.linesIterator.size, mainClassName)\n\n    (topWrapper0 + code + bottomWrapper0, wrapperParams)\n  }\n}\n\ncase class WrapperParams(topWrapperLineCount: Int, userCodeLineCount: Int, mainClass: String)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/ConsoleUtils.scala",
    "content": "package scala.build.internals\n\nobject ConsoleUtils {\n  import Console.*\n\n  object ScalaCliConsole {\n    lazy val warnPrefix = s\"[${YELLOW}warn$RESET]\"\n    val GRAY: String    = \"\\u001b[90m\"\n  }\n\n  val ansiFormattingKeys: Set[String] = Set(RESET, BOLD, UNDERLINED, REVERSED, INVISIBLE)\n  val ansiColors: Set[String]         =\n    Set(BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, ScalaCliConsole.GRAY)\n  val ansiBoldColors: Set[String] =\n    Set(BLACK_B, RED_B, GREEN_B, YELLOW_B, BLUE_B, MAGENTA_B, CYAN_B, WHITE_B)\n  val allAnsiColors: Set[String]  = ansiColors ++ ansiBoldColors\n  val allConsoleKeys: Set[String] = allAnsiColors ++ ansiFormattingKeys\n\n  extension (s: String) {\n    def noConsoleKeys: String =\n      allConsoleKeys.fold(s)((acc, consoleKey) => acc.replace(consoleKey, \"\"))\n  }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/CsLoggerUtil.scala",
    "content": "package scala.build.internal\n\nimport coursier.cache.FileCache\nimport coursier.cache.loggers.{ProgressBarRefreshDisplay, RefreshDisplay, RefreshLogger}\nimport coursier.jvm.JavaHome\nimport coursier.util.Task\n\nobject CsLoggerUtil {\n\n  // All of these methods are a bit flaky…\n\n  private lazy val loggerDisplay: RefreshLogger => RefreshDisplay = {\n    val m = classOf[RefreshLogger].getDeclaredField(\"display\")\n    m.setAccessible(true)\n    logger => m.get(logger).asInstanceOf[RefreshDisplay]\n  }\n\n  implicit class CsCacheExtensions(private val cache: FileCache[Task]) extends AnyVal {\n    def withMessage(message: String): FileCache[Task] =\n      cache.logger match {\n        case logger: RefreshLogger =>\n          val shouldUpdateLogger = loggerDisplay(logger) match {\n            case _: CustomProgressBarRefreshDisplay => true\n            case _: ProgressBarRefreshDisplay       => true\n            case _                                  => false\n          }\n          if (shouldUpdateLogger) {\n            var displayed     = false\n            val updatedLogger = RefreshLogger.create(\n              CustomProgressBarRefreshDisplay.create(\n                keepOnScreen = false,\n                if (!displayed) {\n                  System.err.println(message)\n                  displayed = true\n                },\n                ()\n              )\n            )\n            updatedLogger.init()\n            cache.withLogger(updatedLogger)\n          }\n          else cache\n        case _ => cache\n      }\n  }\n  implicit class CsJavaHomeExtensions(private val javaHome: JavaHome) extends AnyVal {\n    def withMessage(message: String): JavaHome =\n      javaHome.cache.map(_.archiveCache.cache) match {\n        case Some(f: FileCache[Task]) =>\n          val cache0 = f.withMessage(message)\n          javaHome.withCache(\n            javaHome.cache.map(c => c.withArchiveCache(c.archiveCache.withCache(cache0)))\n          )\n        case _ => javaHome\n      }\n  }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/CustomProgressBarRefreshDisplay.scala",
    "content": "package scala.build.internal\n\n// Same as ProgressBarRefreshDisplay in coursier, but allowing not to keep progress\n// bars on screen\n\nimport coursier.cache.internal.ConsoleDim\nimport coursier.cache.loggers.*\n\nimport java.io.Writer\nimport java.sql.Timestamp\n\nimport scala.concurrent.duration.{Duration, DurationInt}\n\nclass CustomProgressBarRefreshDisplay(\n  /** Whether to keep details on screen after this display is stopped */\n  keepOnScreen: Boolean,\n  beforeOutput: => Unit,\n  afterOutput: => Unit\n) extends RefreshDisplay {\n\n  import coursier.cache.internal.Terminal.Ansi\n  import CustomProgressBarRefreshDisplay.display\n\n  val refreshInterval: Duration =\n    20.millis\n\n  private var printedAnything0 = false\n  private var currentHeight    = 0\n\n  override def stop(out: Writer): Unit = {\n\n    for (_ <- 1 to 2; _ <- 0 until currentHeight) {\n      out.clearLine(2)\n      out.down(1)\n    }\n    for (_ <- 0 until currentHeight)\n      out.up(2)\n\n    out.flush()\n\n    if (printedAnything0) {\n      afterOutput\n      printedAnything0 = false\n    }\n\n    currentHeight = 0\n  }\n\n  private def truncatedPrintln(out: Writer, s: String, width: Int): Unit = {\n    out.clearLine(2)\n    out.write(RefreshDisplay.truncated(s, width))\n    out.write('\\n')\n  }\n\n  def update(\n    out: Writer,\n    done: Seq[(String, RefreshInfo)],\n    downloads: Seq[(String, RefreshInfo)],\n    changed: Boolean\n  ): Unit =\n    if (changed) {\n\n      val width = ConsoleDim.width()\n\n      val done0 = done\n        .filter {\n          case (url, _) =>\n            !url.endsWith(\".sha1\") &&\n            !url.endsWith(\".sha256\") &&\n            !url.endsWith(\".md5\") &&\n            !url.endsWith(\"/\")\n        }\n\n      val elems = done0.iterator.map((_, true)) ++ downloads.iterator.map((_, false))\n      for (((url, info), isDone) <- elems) {\n        assert(info != null, s\"Incoherent state ($url)\")\n\n        if (!printedAnything0) {\n          beforeOutput\n          printedAnything0 = true\n        }\n\n        truncatedPrintln(out, url, width)\n        out.clearLine(2)\n        out.write(s\"  ${display(info, isDone)}\" + System.lineSeparator())\n      }\n\n      val displayedCount = done0.length + downloads.length\n\n      if (displayedCount < currentHeight) {\n        for (_ <- 1 to 2; _ <- displayedCount until currentHeight) {\n          out.clearLine(2)\n          out.down(1)\n        }\n\n        for (_ <- displayedCount until currentHeight)\n          out.up(2)\n      }\n\n      for (_ <- downloads.indices)\n        out.up(2)\n      if (!keepOnScreen)\n        for (_ <- done0.indices)\n          out.up(2)\n\n      out.left(10000)\n\n      out.flush()\n\n      currentHeight =\n        if (keepOnScreen) downloads.length\n        else displayedCount\n    }\n\n}\n\nobject CustomProgressBarRefreshDisplay {\n\n  def create(): CustomProgressBarRefreshDisplay =\n    new CustomProgressBarRefreshDisplay(keepOnScreen = true, (), ())\n\n  def create(\n    beforeOutput: => Unit,\n    afterOutput: => Unit\n  ): CustomProgressBarRefreshDisplay =\n    new CustomProgressBarRefreshDisplay(keepOnScreen = true, beforeOutput, afterOutput)\n\n  def create(\n    keepOnScreen: Boolean,\n    beforeOutput: => Unit,\n    afterOutput: => Unit\n  ): CustomProgressBarRefreshDisplay =\n    new CustomProgressBarRefreshDisplay(keepOnScreen, beforeOutput, afterOutput)\n\n  // Scala version of http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java/3758880#3758880\n  def byteCount(bytes: Long, si: Boolean = false) = {\n    val unit = if (si) 1000 else 1024\n    if (bytes < unit)\n      bytes.toString + \"B\"\n    else {\n      val prefixes = if (si) \"kMGTPE\" else \"KMGTPE\"\n      val exp      = (math.log(bytes.toDouble) / math.log(unit.toDouble)).toInt min prefixes.length\n      val pre      = prefixes.charAt(exp - 1).toString + (if (si) \"\" else \"i\")\n      f\"${bytes / math.pow(unit.toDouble, exp.toDouble)}%.1f ${pre}B\"\n    }\n  }\n\n  private val format =\n    new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\")\n  private def formatTimestamp(ts: Long): String =\n    format.format(new Timestamp(ts))\n\n  private def display(info: RefreshInfo, isDone: Boolean): String =\n    info match {\n      case d: RefreshInfo.DownloadInfo =>\n        val actualFraction = d.fraction\n          .orElse(if (isDone) Some(1.0) else None)\n          .orElse(if (d.downloaded == 0L) Some(0.0) else None)\n\n        val start =\n          actualFraction match {\n            case None =>\n              \"       [          ] \"\n            case Some(frac) =>\n              val elem = if (d.watching) \".\" else \"#\"\n\n              val decile = (10.0 * frac).toInt\n              assert(decile >= 0)\n              assert(decile <= 10)\n\n              f\"${100.0 * frac}%5.1f%%\" +\n                \" [\" + (elem * decile) + (\" \" * (10 - decile)) + \"] \"\n          }\n\n        start +\n          byteCount(d.downloaded) +\n          d.rate().fold(\"\")(r => s\" (${byteCount(r.toLong)} / s)\")\n\n      case c: RefreshInfo.CheckUpdateInfo =>\n        if (isDone)\n          (c.currentTimeOpt, c.remoteTimeOpt) match {\n            case (Some(current), Some(remote)) =>\n              if (current < remote)\n                s\"Updated since ${formatTimestamp(current)} (${formatTimestamp(remote)})\"\n              else if (current == remote)\n                s\"No new update since ${formatTimestamp(current)}\"\n              else\n                s\"Warning: local copy newer than remote one (${formatTimestamp(current)} > ${formatTimestamp(remote)})\"\n            case (Some(_), None) =>\n              // FIXME Likely a 404 Not found, that should be taken into account by the cache\n              \"No modified time in response\"\n            case (None, Some(remote)) =>\n              s\"Last update: ${formatTimestamp(remote)}\"\n            case (None, None) =>\n              \"\" // ???\n          }\n        else\n          c.currentTimeOpt match {\n            case Some(current) =>\n              s\"Checking for updates since ${formatTimestamp(current)}\"\n            case None =>\n              \"\" // ???\n          }\n    }\n\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/EnvVar.scala",
    "content": "package scala.build.internals\n\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole\n\n/** @param name\n  *   The name of the environment variable\n  * @param description\n  *   A short description what is it used for\n  * @param passToIde\n  *   Whether to pass this variable to the IDE/BSP client (true by default, should only be disabled\n  *   for env vars which aren't safe to save on disk)\n  * @param requiresPower\n  *   Whether this variable is related to a feature that requires power mode; also used for internal\n  *   toggles and such\n  */\ncase class EnvVar(\n  name: String,\n  description: String,\n  passToIde: Boolean = true,\n  requiresPower: Boolean = false\n) {\n  def valueOpt: Option[String]            = Option(System.getenv(name))\n  override def toString: String           = s\"$name=${valueOpt.getOrElse(\"\")}\"\n  def helpMessage(spaces: String): String = {\n    val powerString =\n      if requiresPower then s\"${ScalaCliConsole.GRAY}(power)${Console.RESET} \" else \"\"\n    s\"${Console.YELLOW}$name${Console.RESET}$spaces$powerString$description\"\n  }\n}\nobject EnvVar {\n  def helpMessage(isPower: Boolean): String =\n    s\"\"\"The following is the list of environment variables used and recognized by Scala CLI.\n       |It should by no means be treated as an exhaustive list\n       |Some tools and libraries Scala CLI integrates with may have their own, which may or may not be listed here.\n       |${if isPower then \"\" else \"For the expanded list, pass --power.\" + System.lineSeparator()}\n       |${\n        val maxFullNameLength =\n          EnvVar.all.filter(!_.requiresPower || isPower).map(_.name.length).max\n        EnvVar.allGroups\n          .map(_.subsectionMessage(maxFullNameLength, isPower))\n          .filter(_.linesIterator.size > 1)\n          .mkString(s\"${System.lineSeparator() * 2}\")\n      }\"\"\".stripMargin\n\n  trait EnvVarGroup {\n    def all: Seq[EnvVar]\n    def groupName: String\n    def subsectionMessage(maxFullNameLength: Int, isPower: Boolean): String = {\n      val envsToInclude = all.filter(!_.requiresPower || isPower)\n      s\"\"\"$groupName\n         |${\n          envsToInclude\n            .map(ev =>\n              s\"  ${ev.helpMessage(spaces = \" \" * (maxFullNameLength - ev.name.length + 2))}\"\n            )\n            .mkString(System.lineSeparator())\n        }\"\"\".stripMargin\n    }\n  }\n  def allGroups: Seq[EnvVarGroup] = Seq(ScalaCli, Java, Bloop, Coursier, Spark, Misc, Internal)\n  def all: Seq[EnvVar]            = allGroups.flatMap(_.all)\n  def allBsp: Seq[EnvVar]         = all.filter(_.passToIde)\n  object Java extends EnvVarGroup {\n    override def groupName: String = \"Java\"\n    override def all               = Seq(javaHome, javaOpts, jdkJavaOpts)\n    val javaHome                   = EnvVar(\"JAVA_HOME\", \"Java installation directory\")\n    val javaOpts                   = EnvVar(\"JAVA_OPTS\", \"Java options\")\n    val jdkJavaOpts                = EnvVar(\"JDK_JAVA_OPTIONS\", \"JDK Java options\")\n  }\n\n  object Misc extends EnvVarGroup {\n    override def groupName: String = \"Miscellaneous\"\n    override def all               = Seq(\n      path,\n      dyldLibraryPath,\n      ldLibraryPath,\n      pathExt,\n      shell,\n      vcVarsAll,\n      zDotDir\n    )\n    val path            = EnvVar(\"PATH\", \"The app path variable\")\n    val dyldLibraryPath = EnvVar(\"DYLD_LIBRARY_PATH\", \"Runtime library paths on Mac OS X\")\n    val ldLibraryPath   = EnvVar(\"LD_LIBRARY_PATH\", \"Runtime library paths on Linux\")\n    val pathExt         = EnvVar(\"PATHEXT\", \"Executable file extensions on Windows\")\n    val pwd             = EnvVar(\"PWD\", \"Current working directory\", passToIde = false)\n    val shell           = EnvVar(\"SHELL\", \"The currently used shell\")\n    val vcVarsAll       = EnvVar(\"VCVARSALL\", \"Visual C++ Redistributable Runtimes\")\n    val zDotDir         = EnvVar(\"ZDOTDIR\", \"Zsh configuration directory\")\n    val mavenHome       = EnvVar(\"MAVEN_HOME\", \"Maven home directory\")\n  }\n\n  object Coursier extends EnvVarGroup {\n    override def groupName: String = \"Coursier\"\n    override def all               = Seq(\n      coursierBinDir,\n      coursierCache,\n      coursierConfigDir,\n      coursierCredentials,\n      insideEmacs,\n      coursierExperimental,\n      coursierJni,\n      coursierMode,\n      coursierNoTerm,\n      coursierProgress,\n      coursierRepositories,\n      coursierVendoredZis,\n      csMavenHome\n    )\n    val coursierBinDir       = EnvVar(\"COURSIER_BIN_DIR\", \"Coursier app binaries directory\")\n    val coursierCache        = EnvVar(\"COURSIER_CACHE\", \"Coursier cache location\")\n    val coursierConfigDir    = EnvVar(\"COURSIER_CONFIG_DIR\", \"Coursier configuration directory\")\n    val coursierCredentials  = EnvVar(\"COURSIER_CREDENTIALS\", \"Coursier credentials\")\n    val coursierExperimental = EnvVar(\"COURSIER_EXPERIMENTAL\", \"Experimental mode toggle\")\n    val coursierJni          = EnvVar(\"COURSIER_JNI\", \"Coursier JNI toggle\")\n    val coursierMode         = EnvVar(\"COURSIER_MODE\", \"Coursier mode (can be set to 'offline')\")\n    val coursierNoTerm       = EnvVar(\"COURSIER_NO_TERM\", \"Terminal toggle\")\n    val coursierProgress     = EnvVar(\"COURSIER_PROGRESS\", \"Progress bar toggle\")\n    val coursierRepositories = EnvVar(\"COURSIER_REPOSITORIES\", \"Coursier repositories\")\n    val coursierVendoredZis  =\n      EnvVar(\"COURSIER_VENDORED_ZIS\", \"Toggle io.github.scala_cli.zip.ZipInputStream\")\n    val csMavenHome = EnvVar(\"CS_MAVEN_HOME\", \"Coursier Maven home directory\")\n    val insideEmacs = EnvVar(\"INSIDE_EMACS\", \"Emacs toggle\")\n  }\n\n  object ScalaCli extends EnvVarGroup {\n    override def groupName: String = \"Scala CLI\"\n    def all                        = Seq(\n      config,\n      home,\n      interactive,\n      interactiveInputs,\n      power,\n      printStackTraces,\n      allowSodiumJni,\n      vendoredZipInputStream\n    )\n    val config                 = EnvVar(\"SCALA_CLI_CONFIG\", \"Scala CLI configuration file path\")\n    val extraTimeout           = Bloop.bloopExtraTimeout.copy(requiresPower = false)\n    val home                   = EnvVar(\"SCALA_CLI_HOME\", \"Scala CLI home directory\")\n    val interactive            = EnvVar(\"SCALA_CLI_INTERACTIVE\", \"Interactive mode toggle\")\n    val interactiveInputs      = EnvVar(\"SCALA_CLI_INTERACTIVE_INPUTS\", \"Interactive mode inputs\")\n    val power                  = EnvVar(\"SCALA_CLI_POWER\", \"Power mode toggle\")\n    val printStackTraces       = EnvVar(\"SCALA_CLI_PRINT_STACK_TRACES\", \"Print stack traces toggle\")\n    val allowSodiumJni         = EnvVar(\"SCALA_CLI_SODIUM_JNI_ALLOW\", \"Allow to load libsodiumjni\")\n    val vendoredZipInputStream =\n      EnvVar(\"SCALA_CLI_VENDORED_ZIS\", \"Toggle io.github.scala_cli.zip.ZipInputStream\")\n  }\n\n  object Spark extends EnvVarGroup {\n    override def groupName: String = \"Spark\"\n    override def all               = Seq(sparkHome)\n    val sparkHome = EnvVar(\"SPARK_HOME\", \"Spark installation directory\", requiresPower = true)\n  }\n\n  object Bloop extends EnvVarGroup {\n    override def groupName: String = \"Bloop\"\n    override def all               = Seq(\n      bloopComputationCores,\n      bloopDaemonDir,\n      bloopJavaOpts,\n      bloopModule,\n      bloopPort,\n      bloopScalaVersion,\n      bloopVersion,\n      bloopServer,\n      bloopExtraTimeout\n    )\n    val bloopComputationCores = EnvVar(\n      \"BLOOP_COMPUTATION_CORES\",\n      \"Number of computation cores to be used\",\n      requiresPower = true\n    )\n    val bloopDaemonDir = EnvVar(\"BLOOP_DAEMON_DIR\", \"Bloop daemon directory\", requiresPower = true)\n    val bloopJavaOpts  = EnvVar(\"BLOOP_JAVA_OPTS\", \"Bloop Java options\", requiresPower = true)\n    val bloopModule    = EnvVar(\"BLOOP_MODULE\", \"Bloop default module\", requiresPower = true)\n    val bloopPort      = EnvVar(\"BLOOP_PORT\", \"Bloop default port\", requiresPower = true)\n    val bloopScalaVersion =\n      EnvVar(\"BLOOP_SCALA_VERSION\", \"Bloop default Scala version\", requiresPower = true)\n    val bloopVersion      = EnvVar(\"BLOOP_VERSION\", \"Bloop default version\", requiresPower = true)\n    val bloopServer       = EnvVar(\"BLOOP_SERVER\", \"Bloop default host\", requiresPower = true)\n    val bloopExtraTimeout = EnvVar(\"SCALA_CLI_EXTRA_TIMEOUT\", \"Extra timeout\", requiresPower = true)\n  }\n\n  object Internal extends EnvVarGroup {\n    override def groupName: String = \"Internal\"\n    def all                        = Seq(ci)\n    val ci = EnvVar(\"CI\", \"Marker for running on the CI\", requiresPower = true)\n  }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/FeatureType.scala",
    "content": "package scala.build.internals\n\nenum FeatureType(stringRepr: String) {\n  override def toString: String = stringRepr\n\n  case Option     extends FeatureType(\"option\")\n  case Directive  extends FeatureType(\"directive\")\n  case Subcommand extends FeatureType(\"sub-command\")\n  case ConfigKey  extends FeatureType(\"configuration key\")\n}\n\nobject FeatureType {\n  private val ordering = Map(\n    FeatureType.Subcommand -> 0,\n    FeatureType.Option     -> 1,\n    FeatureType.Directive  -> 2,\n    FeatureType.ConfigKey  -> 3\n  )\n\n  given Ordering[FeatureType] = Ordering.by(ordering)\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/License.scala",
    "content": "package scala.build.internal\n\nfinal case class License(\n  id: String,\n  name: String,\n  url: String\n)\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/Licenses.scala",
    "content": "package scala.build.internal\n\nobject Licenses {\n  // format: off\n  val list = Seq(\n    License(\"0BSD\", \"BSD Zero Clause License\", \"https://spdx.org/licenses/0BSD.html\"),\n    License(\"AAL\", \"Attribution Assurance License\", \"https://spdx.org/licenses/AAL.html\"),\n    License(\"ADSL\", \"Amazon Digital Services License\", \"https://spdx.org/licenses/ADSL.html\"),\n    License(\"AFL-1.1\", \"Academic Free License v1.1\", \"https://spdx.org/licenses/AFL-1.1.html\"),\n    License(\"AFL-1.2\", \"Academic Free License v1.2\", \"https://spdx.org/licenses/AFL-1.2.html\"),\n    License(\"AFL-2.0\", \"Academic Free License v2.0\", \"https://spdx.org/licenses/AFL-2.0.html\"),\n    License(\"AFL-2.1\", \"Academic Free License v2.1\", \"https://spdx.org/licenses/AFL-2.1.html\"),\n    License(\"AFL-3.0\", \"Academic Free License v3.0\", \"https://spdx.org/licenses/AFL-3.0.html\"),\n    License(\"AGPL-1.0\", \"Affero General Public License v1.0\", \"https://spdx.org/licenses/AGPL-1.0.html\"),\n    License(\"AGPL-1.0-only\", \"Affero General Public License v1.0 only\", \"https://spdx.org/licenses/AGPL-1.0-only.html\"),\n    License(\"AGPL-1.0-or-later\", \"Affero General Public License v1.0 or later\", \"https://spdx.org/licenses/AGPL-1.0-or-later.html\"),\n    License(\"AGPL-3.0\", \"GNU Affero General Public License v3.0\", \"https://spdx.org/licenses/AGPL-3.0.html\"),\n    License(\"AGPL-3.0-only\", \"GNU Affero General Public License v3.0 only\", \"https://spdx.org/licenses/AGPL-3.0-only.html\"),\n    License(\"AGPL-3.0-or-later\", \"GNU Affero General Public License v3.0 or later\", \"https://spdx.org/licenses/AGPL-3.0-or-later.html\"),\n    License(\"AMDPLPA\", \"AMD's plpa_map.c License\", \"https://spdx.org/licenses/AMDPLPA.html\"),\n    License(\"AML\", \"Apple MIT License\", \"https://spdx.org/licenses/AML.html\"),\n    License(\"AMPAS\", \"Academy of Motion Picture Arts and Sciences BSD\", \"https://spdx.org/licenses/AMPAS.html\"),\n    License(\"ANTLR-PD\", \"ANTLR Software Rights Notice\", \"https://spdx.org/licenses/ANTLR-PD.html\"),\n    License(\"ANTLR-PD-fallback\", \"ANTLR Software Rights Notice with license fallback\", \"https://spdx.org/licenses/ANTLR-PD-fallback.html\"),\n    License(\"APAFML\", \"Adobe Postscript AFM License\", \"https://spdx.org/licenses/APAFML.html\"),\n    License(\"APL-1.0\", \"Adaptive Public License 1.0\", \"https://spdx.org/licenses/APL-1.0.html\"),\n    License(\"APSL-1.0\", \"Apple Public Source License 1.0\", \"https://spdx.org/licenses/APSL-1.0.html\"),\n    License(\"APSL-1.1\", \"Apple Public Source License 1.1\", \"https://spdx.org/licenses/APSL-1.1.html\"),\n    License(\"APSL-1.2\", \"Apple Public Source License 1.2\", \"https://spdx.org/licenses/APSL-1.2.html\"),\n    License(\"APSL-2.0\", \"Apple Public Source License 2.0\", \"https://spdx.org/licenses/APSL-2.0.html\"),\n    License(\"Abstyles\", \"Abstyles License\", \"https://spdx.org/licenses/Abstyles.html\"),\n    License(\"Adobe-2006\", \"Adobe Systems Incorporated Source Code License Agreement\", \"https://spdx.org/licenses/Adobe-2006.html\"),\n    License(\"Adobe-Glyph\", \"Adobe Glyph List License\", \"https://spdx.org/licenses/Adobe-Glyph.html\"),\n    License(\"Afmparse\", \"Afmparse License\", \"https://spdx.org/licenses/Afmparse.html\"),\n    License(\"Aladdin\", \"Aladdin Free Public License\", \"https://spdx.org/licenses/Aladdin.html\"),\n    License(\"Apache-1.0\", \"Apache License 1.0\", \"https://spdx.org/licenses/Apache-1.0.html\"),\n    License(\"Apache-1.1\", \"Apache License 1.1\", \"https://spdx.org/licenses/Apache-1.1.html\"),\n    License(\"Apache-2.0\", \"Apache License 2.0\", \"https://spdx.org/licenses/Apache-2.0.html\"),\n    License(\"App-s2p\", \"App::s2p License\", \"https://spdx.org/licenses/App-s2p.html\"),\n    License(\"Arphic-1999\", \"Arphic Public License\", \"https://spdx.org/licenses/Arphic-1999.html\"),\n    License(\"Artistic-1.0\", \"Artistic License 1.0\", \"https://spdx.org/licenses/Artistic-1.0.html\"),\n    License(\"Artistic-1.0-Perl\", \"Artistic License 1.0 (Perl)\", \"https://spdx.org/licenses/Artistic-1.0-Perl.html\"),\n    License(\"Artistic-1.0-cl8\", \"Artistic License 1.0 w/clause 8\", \"https://spdx.org/licenses/Artistic-1.0-cl8.html\"),\n    License(\"Artistic-2.0\", \"Artistic License 2.0\", \"https://spdx.org/licenses/Artistic-2.0.html\"),\n    License(\"BSD-1-Clause\", \"BSD 1-Clause License\", \"https://spdx.org/licenses/BSD-1-Clause.html\"),\n    License(\"BSD-2-Clause\", \"BSD 2-Clause \\\"Simplified\\\" License\", \"https://spdx.org/licenses/BSD-2-Clause.html\"),\n    License(\"BSD-2-Clause-FreeBSD\", \"BSD 2-Clause FreeBSD License\", \"https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html\"),\n    License(\"BSD-2-Clause-NetBSD\", \"BSD 2-Clause NetBSD License\", \"https://spdx.org/licenses/BSD-2-Clause-NetBSD.html\"),\n    License(\"BSD-2-Clause-Patent\", \"BSD-2-Clause Plus Patent License\", \"https://spdx.org/licenses/BSD-2-Clause-Patent.html\"),\n    License(\"BSD-2-Clause-Views\", \"BSD 2-Clause with views sentence\", \"https://spdx.org/licenses/BSD-2-Clause-Views.html\"),\n    License(\"BSD-3-Clause\", \"BSD 3-Clause \\\"New\\\" or \\\"Revised\\\" License\", \"https://spdx.org/licenses/BSD-3-Clause.html\"),\n    License(\"BSD-3-Clause-Attribution\", \"BSD with attribution\", \"https://spdx.org/licenses/BSD-3-Clause-Attribution.html\"),\n    License(\"BSD-3-Clause-Clear\", \"BSD 3-Clause Clear License\", \"https://spdx.org/licenses/BSD-3-Clause-Clear.html\"),\n    License(\"BSD-3-Clause-LBNL\", \"Lawrence Berkeley National Labs BSD variant license\", \"https://spdx.org/licenses/BSD-3-Clause-LBNL.html\"),\n    License(\"BSD-3-Clause-Modification\", \"BSD 3-Clause Modification\", \"https://spdx.org/licenses/BSD-3-Clause-Modification.html\"),\n    License(\"BSD-3-Clause-No-Military-License\", \"BSD 3-Clause No Military License\", \"https://spdx.org/licenses/BSD-3-Clause-No-Military-License.html\"),\n    License(\"BSD-3-Clause-No-Nuclear-License\", \"BSD 3-Clause No Nuclear License\", \"https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html\"),\n    License(\"BSD-3-Clause-No-Nuclear-License-2014\", \"BSD 3-Clause No Nuclear License 2014\", \"https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html\"),\n    License(\"BSD-3-Clause-No-Nuclear-Warranty\", \"BSD 3-Clause No Nuclear Warranty\", \"https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html\"),\n    License(\"BSD-3-Clause-Open-MPI\", \"BSD 3-Clause Open MPI variant\", \"https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html\"),\n    License(\"BSD-4-Clause\", \"BSD 4-Clause \\\"Original\\\" or \\\"Old\\\" License\", \"https://spdx.org/licenses/BSD-4-Clause.html\"),\n    License(\"BSD-4-Clause-Shortened\", \"BSD 4 Clause Shortened\", \"https://spdx.org/licenses/BSD-4-Clause-Shortened.html\"),\n    License(\"BSD-4-Clause-UC\", \"BSD-4-Clause (University of California-Specific)\", \"https://spdx.org/licenses/BSD-4-Clause-UC.html\"),\n    License(\"BSD-Protection\", \"BSD Protection License\", \"https://spdx.org/licenses/BSD-Protection.html\"),\n    License(\"BSD-Source-Code\", \"BSD Source Code Attribution\", \"https://spdx.org/licenses/BSD-Source-Code.html\"),\n    License(\"BSL-1.0\", \"Boost Software License 1.0\", \"https://spdx.org/licenses/BSL-1.0.html\"),\n    License(\"BUSL-1.1\", \"Business Source License 1.1\", \"https://spdx.org/licenses/BUSL-1.1.html\"),\n    License(\"Bahyph\", \"Bahyph License\", \"https://spdx.org/licenses/Bahyph.html\"),\n    License(\"Barr\", \"Barr License\", \"https://spdx.org/licenses/Barr.html\"),\n    License(\"Beerware\", \"Beerware License\", \"https://spdx.org/licenses/Beerware.html\"),\n    License(\"BitTorrent-1.0\", \"BitTorrent Open Source License v1.0\", \"https://spdx.org/licenses/BitTorrent-1.0.html\"),\n    License(\"BitTorrent-1.1\", \"BitTorrent Open Source License v1.1\", \"https://spdx.org/licenses/BitTorrent-1.1.html\"),\n    License(\"BlueOak-1.0.0\", \"Blue Oak Model License 1.0.0\", \"https://spdx.org/licenses/BlueOak-1.0.0.html\"),\n    License(\"Borceux\", \"Borceux license\", \"https://spdx.org/licenses/Borceux.html\"),\n    License(\"C-UDA-1.0\", \"Computational Use of Data Agreement v1.0\", \"https://spdx.org/licenses/C-UDA-1.0.html\"),\n    License(\"CAL-1.0\", \"Cryptographic Autonomy License 1.0\", \"https://spdx.org/licenses/CAL-1.0.html\"),\n    License(\"CAL-1.0-Combined-Work-Exception\", \"Cryptographic Autonomy License 1.0 (Combined Work Exception)\", \"https://spdx.org/licenses/CAL-1.0-Combined-Work-Exception.html\"),\n    License(\"CATOSL-1.1\", \"Computer Associates Trusted Open Source License 1.1\", \"https://spdx.org/licenses/CATOSL-1.1.html\"),\n    License(\"CC-BY-1.0\", \"Creative Commons Attribution 1.0 Generic\", \"https://spdx.org/licenses/CC-BY-1.0.html\"),\n    License(\"CC-BY-2.0\", \"Creative Commons Attribution 2.0 Generic\", \"https://spdx.org/licenses/CC-BY-2.0.html\"),\n    License(\"CC-BY-2.5\", \"Creative Commons Attribution 2.5 Generic\", \"https://spdx.org/licenses/CC-BY-2.5.html\"),\n    License(\"CC-BY-2.5-AU\", \"Creative Commons Attribution 2.5 Australia\", \"https://spdx.org/licenses/CC-BY-2.5-AU.html\"),\n    License(\"CC-BY-3.0\", \"Creative Commons Attribution 3.0 Unported\", \"https://spdx.org/licenses/CC-BY-3.0.html\"),\n    License(\"CC-BY-3.0-AT\", \"Creative Commons Attribution 3.0 Austria\", \"https://spdx.org/licenses/CC-BY-3.0-AT.html\"),\n    License(\"CC-BY-3.0-DE\", \"Creative Commons Attribution 3.0 Germany\", \"https://spdx.org/licenses/CC-BY-3.0-DE.html\"),\n    License(\"CC-BY-3.0-NL\", \"Creative Commons Attribution 3.0 Netherlands\", \"https://spdx.org/licenses/CC-BY-3.0-NL.html\"),\n    License(\"CC-BY-3.0-US\", \"Creative Commons Attribution 3.0 United States\", \"https://spdx.org/licenses/CC-BY-3.0-US.html\"),\n    License(\"CC-BY-4.0\", \"Creative Commons Attribution 4.0 International\", \"https://spdx.org/licenses/CC-BY-4.0.html\"),\n    License(\"CC-BY-NC-1.0\", \"Creative Commons Attribution Non Commercial 1.0 Generic\", \"https://spdx.org/licenses/CC-BY-NC-1.0.html\"),\n    License(\"CC-BY-NC-2.0\", \"Creative Commons Attribution Non Commercial 2.0 Generic\", \"https://spdx.org/licenses/CC-BY-NC-2.0.html\"),\n    License(\"CC-BY-NC-2.5\", \"Creative Commons Attribution Non Commercial 2.5 Generic\", \"https://spdx.org/licenses/CC-BY-NC-2.5.html\"),\n    License(\"CC-BY-NC-3.0\", \"Creative Commons Attribution Non Commercial 3.0 Unported\", \"https://spdx.org/licenses/CC-BY-NC-3.0.html\"),\n    License(\"CC-BY-NC-3.0-DE\", \"Creative Commons Attribution Non Commercial 3.0 Germany\", \"https://spdx.org/licenses/CC-BY-NC-3.0-DE.html\"),\n    License(\"CC-BY-NC-4.0\", \"Creative Commons Attribution Non Commercial 4.0 International\", \"https://spdx.org/licenses/CC-BY-NC-4.0.html\"),\n    License(\"CC-BY-NC-ND-1.0\", \"Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic\", \"https://spdx.org/licenses/CC-BY-NC-ND-1.0.html\"),\n    License(\"CC-BY-NC-ND-2.0\", \"Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic\", \"https://spdx.org/licenses/CC-BY-NC-ND-2.0.html\"),\n    License(\"CC-BY-NC-ND-2.5\", \"Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic\", \"https://spdx.org/licenses/CC-BY-NC-ND-2.5.html\"),\n    License(\"CC-BY-NC-ND-3.0\", \"Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported\", \"https://spdx.org/licenses/CC-BY-NC-ND-3.0.html\"),\n    License(\"CC-BY-NC-ND-3.0-DE\", \"Creative Commons Attribution Non Commercial No Derivatives 3.0 Germany\", \"https://spdx.org/licenses/CC-BY-NC-ND-3.0-DE.html\"),\n    License(\"CC-BY-NC-ND-3.0-IGO\", \"Creative Commons Attribution Non Commercial No Derivatives 3.0 IGO\", \"https://spdx.org/licenses/CC-BY-NC-ND-3.0-IGO.html\"),\n    License(\"CC-BY-NC-ND-4.0\", \"Creative Commons Attribution Non Commercial No Derivatives 4.0 International\", \"https://spdx.org/licenses/CC-BY-NC-ND-4.0.html\"),\n    License(\"CC-BY-NC-SA-1.0\", \"Creative Commons Attribution Non Commercial Share Alike 1.0 Generic\", \"https://spdx.org/licenses/CC-BY-NC-SA-1.0.html\"),\n    License(\"CC-BY-NC-SA-2.0\", \"Creative Commons Attribution Non Commercial Share Alike 2.0 Generic\", \"https://spdx.org/licenses/CC-BY-NC-SA-2.0.html\"),\n    License(\"CC-BY-NC-SA-2.0-FR\", \"Creative Commons Attribution-NonCommercial-ShareAlike 2.0 France\", \"https://spdx.org/licenses/CC-BY-NC-SA-2.0-FR.html\"),\n    License(\"CC-BY-NC-SA-2.0-UK\", \"Creative Commons Attribution Non Commercial Share Alike 2.0 England and Wales\", \"https://spdx.org/licenses/CC-BY-NC-SA-2.0-UK.html\"),\n    License(\"CC-BY-NC-SA-2.5\", \"Creative Commons Attribution Non Commercial Share Alike 2.5 Generic\", \"https://spdx.org/licenses/CC-BY-NC-SA-2.5.html\"),\n    License(\"CC-BY-NC-SA-3.0\", \"Creative Commons Attribution Non Commercial Share Alike 3.0 Unported\", \"https://spdx.org/licenses/CC-BY-NC-SA-3.0.html\"),\n    License(\"CC-BY-NC-SA-3.0-DE\", \"Creative Commons Attribution Non Commercial Share Alike 3.0 Germany\", \"https://spdx.org/licenses/CC-BY-NC-SA-3.0-DE.html\"),\n    License(\"CC-BY-NC-SA-3.0-IGO\", \"Creative Commons Attribution Non Commercial Share Alike 3.0 IGO\", \"https://spdx.org/licenses/CC-BY-NC-SA-3.0-IGO.html\"),\n    License(\"CC-BY-NC-SA-4.0\", \"Creative Commons Attribution Non Commercial Share Alike 4.0 International\", \"https://spdx.org/licenses/CC-BY-NC-SA-4.0.html\"),\n    License(\"CC-BY-ND-1.0\", \"Creative Commons Attribution No Derivatives 1.0 Generic\", \"https://spdx.org/licenses/CC-BY-ND-1.0.html\"),\n    License(\"CC-BY-ND-2.0\", \"Creative Commons Attribution No Derivatives 2.0 Generic\", \"https://spdx.org/licenses/CC-BY-ND-2.0.html\"),\n    License(\"CC-BY-ND-2.5\", \"Creative Commons Attribution No Derivatives 2.5 Generic\", \"https://spdx.org/licenses/CC-BY-ND-2.5.html\"),\n    License(\"CC-BY-ND-3.0\", \"Creative Commons Attribution No Derivatives 3.0 Unported\", \"https://spdx.org/licenses/CC-BY-ND-3.0.html\"),\n    License(\"CC-BY-ND-3.0-DE\", \"Creative Commons Attribution No Derivatives 3.0 Germany\", \"https://spdx.org/licenses/CC-BY-ND-3.0-DE.html\"),\n    License(\"CC-BY-ND-4.0\", \"Creative Commons Attribution No Derivatives 4.0 International\", \"https://spdx.org/licenses/CC-BY-ND-4.0.html\"),\n    License(\"CC-BY-SA-1.0\", \"Creative Commons Attribution Share Alike 1.0 Generic\", \"https://spdx.org/licenses/CC-BY-SA-1.0.html\"),\n    License(\"CC-BY-SA-2.0\", \"Creative Commons Attribution Share Alike 2.0 Generic\", \"https://spdx.org/licenses/CC-BY-SA-2.0.html\"),\n    License(\"CC-BY-SA-2.0-UK\", \"Creative Commons Attribution Share Alike 2.0 England and Wales\", \"https://spdx.org/licenses/CC-BY-SA-2.0-UK.html\"),\n    License(\"CC-BY-SA-2.1-JP\", \"Creative Commons Attribution Share Alike 2.1 Japan\", \"https://spdx.org/licenses/CC-BY-SA-2.1-JP.html\"),\n    License(\"CC-BY-SA-2.5\", \"Creative Commons Attribution Share Alike 2.5 Generic\", \"https://spdx.org/licenses/CC-BY-SA-2.5.html\"),\n    License(\"CC-BY-SA-3.0\", \"Creative Commons Attribution Share Alike 3.0 Unported\", \"https://spdx.org/licenses/CC-BY-SA-3.0.html\"),\n    License(\"CC-BY-SA-3.0-AT\", \"Creative Commons Attribution Share Alike 3.0 Austria\", \"https://spdx.org/licenses/CC-BY-SA-3.0-AT.html\"),\n    License(\"CC-BY-SA-3.0-DE\", \"Creative Commons Attribution Share Alike 3.0 Germany\", \"https://spdx.org/licenses/CC-BY-SA-3.0-DE.html\"),\n    License(\"CC-BY-SA-4.0\", \"Creative Commons Attribution Share Alike 4.0 International\", \"https://spdx.org/licenses/CC-BY-SA-4.0.html\"),\n    License(\"CC-PDDC\", \"Creative Commons Public Domain Dedication and Certification\", \"https://spdx.org/licenses/CC-PDDC.html\"),\n    License(\"CC0-1.0\", \"Creative Commons Zero v1.0 Universal\", \"https://spdx.org/licenses/CC0-1.0.html\"),\n    License(\"CDDL-1.0\", \"Common Development and Distribution License 1.0\", \"https://spdx.org/licenses/CDDL-1.0.html\"),\n    License(\"CDDL-1.1\", \"Common Development and Distribution License 1.1\", \"https://spdx.org/licenses/CDDL-1.1.html\"),\n    License(\"CDL-1.0\", \"Common Documentation License 1.0\", \"https://spdx.org/licenses/CDL-1.0.html\"),\n    License(\"CDLA-Permissive-1.0\", \"Community Data License Agreement Permissive 1.0\", \"https://spdx.org/licenses/CDLA-Permissive-1.0.html\"),\n    License(\"CDLA-Permissive-2.0\", \"Community Data License Agreement Permissive 2.0\", \"https://spdx.org/licenses/CDLA-Permissive-2.0.html\"),\n    License(\"CDLA-Sharing-1.0\", \"Community Data License Agreement Sharing 1.0\", \"https://spdx.org/licenses/CDLA-Sharing-1.0.html\"),\n    License(\"CECILL-1.0\", \"CeCILL Free Software License Agreement v1.0\", \"https://spdx.org/licenses/CECILL-1.0.html\"),\n    License(\"CECILL-1.1\", \"CeCILL Free Software License Agreement v1.1\", \"https://spdx.org/licenses/CECILL-1.1.html\"),\n    License(\"CECILL-2.0\", \"CeCILL Free Software License Agreement v2.0\", \"https://spdx.org/licenses/CECILL-2.0.html\"),\n    License(\"CECILL-2.1\", \"CeCILL Free Software License Agreement v2.1\", \"https://spdx.org/licenses/CECILL-2.1.html\"),\n    License(\"CECILL-B\", \"CeCILL-B Free Software License Agreement\", \"https://spdx.org/licenses/CECILL-B.html\"),\n    License(\"CECILL-C\", \"CeCILL-C Free Software License Agreement\", \"https://spdx.org/licenses/CECILL-C.html\"),\n    License(\"CERN-OHL-1.1\", \"CERN Open Hardware Licence v1.1\", \"https://spdx.org/licenses/CERN-OHL-1.1.html\"),\n    License(\"CERN-OHL-1.2\", \"CERN Open Hardware Licence v1.2\", \"https://spdx.org/licenses/CERN-OHL-1.2.html\"),\n    License(\"CERN-OHL-P-2.0\", \"CERN Open Hardware Licence Version 2 - Permissive\", \"https://spdx.org/licenses/CERN-OHL-P-2.0.html\"),\n    License(\"CERN-OHL-S-2.0\", \"CERN Open Hardware Licence Version 2 - Strongly Reciprocal\", \"https://spdx.org/licenses/CERN-OHL-S-2.0.html\"),\n    License(\"CERN-OHL-W-2.0\", \"CERN Open Hardware Licence Version 2 - Weakly Reciprocal\", \"https://spdx.org/licenses/CERN-OHL-W-2.0.html\"),\n    License(\"CNRI-Jython\", \"CNRI Jython License\", \"https://spdx.org/licenses/CNRI-Jython.html\"),\n    License(\"CNRI-Python\", \"CNRI Python License\", \"https://spdx.org/licenses/CNRI-Python.html\"),\n    License(\"CNRI-Python-GPL-Compatible\", \"CNRI Python Open Source GPL Compatible License Agreement\", \"https://spdx.org/licenses/CNRI-Python-GPL-Compatible.html\"),\n    License(\"COIL-1.0\", \"Copyfree Open Innovation License\", \"https://spdx.org/licenses/COIL-1.0.html\"),\n    License(\"CPAL-1.0\", \"Common Public Attribution License 1.0\", \"https://spdx.org/licenses/CPAL-1.0.html\"),\n    License(\"CPL-1.0\", \"Common Public License 1.0\", \"https://spdx.org/licenses/CPL-1.0.html\"),\n    License(\"CPOL-1.02\", \"Code Project Open License 1.02\", \"https://spdx.org/licenses/CPOL-1.02.html\"),\n    License(\"CUA-OPL-1.0\", \"CUA Office Public License v1.0\", \"https://spdx.org/licenses/CUA-OPL-1.0.html\"),\n    License(\"Caldera\", \"Caldera License\", \"https://spdx.org/licenses/Caldera.html\"),\n    License(\"ClArtistic\", \"Clarified Artistic License\", \"https://spdx.org/licenses/ClArtistic.html\"),\n    License(\"Community-Spec-1.0\", \"Community Specification License 1.0\", \"https://spdx.org/licenses/Community-Spec-1.0.html\"),\n    License(\"Condor-1.1\", \"Condor Public License v1.1\", \"https://spdx.org/licenses/Condor-1.1.html\"),\n    License(\"Crossword\", \"Crossword License\", \"https://spdx.org/licenses/Crossword.html\"),\n    License(\"CrystalStacker\", \"CrystalStacker License\", \"https://spdx.org/licenses/CrystalStacker.html\"),\n    License(\"Cube\", \"Cube License\", \"https://spdx.org/licenses/Cube.html\"),\n    License(\"D-FSL-1.0\", \"Deutsche Freie Software Lizenz\", \"https://spdx.org/licenses/D-FSL-1.0.html\"),\n    License(\"DL-DE-BY-2.0\", \"Data licence Germany – attribution – version 2.0\", \"https://spdx.org/licenses/DL-DE-BY-2.0.html\"),\n    License(\"DOC\", \"DOC License\", \"https://spdx.org/licenses/DOC.html\"),\n    License(\"DRL-1.0\", \"Detection Rule License 1.0\", \"https://spdx.org/licenses/DRL-1.0.html\"),\n    License(\"DSDP\", \"DSDP License\", \"https://spdx.org/licenses/DSDP.html\"),\n    License(\"Dotseqn\", \"Dotseqn License\", \"https://spdx.org/licenses/Dotseqn.html\"),\n    License(\"ECL-1.0\", \"Educational Community License v1.0\", \"https://spdx.org/licenses/ECL-1.0.html\"),\n    License(\"ECL-2.0\", \"Educational Community License v2.0\", \"https://spdx.org/licenses/ECL-2.0.html\"),\n    License(\"EFL-1.0\", \"Eiffel Forum License v1.0\", \"https://spdx.org/licenses/EFL-1.0.html\"),\n    License(\"EFL-2.0\", \"Eiffel Forum License v2.0\", \"https://spdx.org/licenses/EFL-2.0.html\"),\n    License(\"EPICS\", \"EPICS Open License\", \"https://spdx.org/licenses/EPICS.html\"),\n    License(\"EPL-1.0\", \"Eclipse Public License 1.0\", \"https://spdx.org/licenses/EPL-1.0.html\"),\n    License(\"EPL-2.0\", \"Eclipse Public License 2.0\", \"https://spdx.org/licenses/EPL-2.0.html\"),\n    License(\"EUDatagrid\", \"EU DataGrid Software License\", \"https://spdx.org/licenses/EUDatagrid.html\"),\n    License(\"EUPL-1.0\", \"European Union Public License 1.0\", \"https://spdx.org/licenses/EUPL-1.0.html\"),\n    License(\"EUPL-1.1\", \"European Union Public License 1.1\", \"https://spdx.org/licenses/EUPL-1.1.html\"),\n    License(\"EUPL-1.2\", \"European Union Public License 1.2\", \"https://spdx.org/licenses/EUPL-1.2.html\"),\n    License(\"Elastic-2.0\", \"Elastic License 2.0\", \"https://spdx.org/licenses/Elastic-2.0.html\"),\n    License(\"Entessa\", \"Entessa Public License v1.0\", \"https://spdx.org/licenses/Entessa.html\"),\n    License(\"ErlPL-1.1\", \"Erlang Public License v1.1\", \"https://spdx.org/licenses/ErlPL-1.1.html\"),\n    License(\"Eurosym\", \"Eurosym License\", \"https://spdx.org/licenses/Eurosym.html\"),\n    License(\"FDK-AAC\", \"Fraunhofer FDK AAC Codec Library\", \"https://spdx.org/licenses/FDK-AAC.html\"),\n    License(\"FSFAP\", \"FSF All Permissive License\", \"https://spdx.org/licenses/FSFAP.html\"),\n    License(\"FSFUL\", \"FSF Unlimited License\", \"https://spdx.org/licenses/FSFUL.html\"),\n    License(\"FSFULLR\", \"FSF Unlimited License (with License Retention)\", \"https://spdx.org/licenses/FSFULLR.html\"),\n    License(\"FTL\", \"Freetype Project License\", \"https://spdx.org/licenses/FTL.html\"),\n    License(\"Fair\", \"Fair License\", \"https://spdx.org/licenses/Fair.html\"),\n    License(\"Frameworx-1.0\", \"Frameworx Open License 1.0\", \"https://spdx.org/licenses/Frameworx-1.0.html\"),\n    License(\"FreeBSD-DOC\", \"FreeBSD Documentation License\", \"https://spdx.org/licenses/FreeBSD-DOC.html\"),\n    License(\"FreeImage\", \"FreeImage Public License v1.0\", \"https://spdx.org/licenses/FreeImage.html\"),\n    License(\"GD\", \"GD License\", \"https://spdx.org/licenses/GD.html\"),\n    License(\"GFDL-1.1\", \"GNU Free Documentation License v1.1\", \"https://spdx.org/licenses/GFDL-1.1.html\"),\n    License(\"GFDL-1.1-invariants-only\", \"GNU Free Documentation License v1.1 only - invariants\", \"https://spdx.org/licenses/GFDL-1.1-invariants-only.html\"),\n    License(\"GFDL-1.1-invariants-or-later\", \"GNU Free Documentation License v1.1 or later - invariants\", \"https://spdx.org/licenses/GFDL-1.1-invariants-or-later.html\"),\n    License(\"GFDL-1.1-no-invariants-only\", \"GNU Free Documentation License v1.1 only - no invariants\", \"https://spdx.org/licenses/GFDL-1.1-no-invariants-only.html\"),\n    License(\"GFDL-1.1-no-invariants-or-later\", \"GNU Free Documentation License v1.1 or later - no invariants\", \"https://spdx.org/licenses/GFDL-1.1-no-invariants-or-later.html\"),\n    License(\"GFDL-1.1-only\", \"GNU Free Documentation License v1.1 only\", \"https://spdx.org/licenses/GFDL-1.1-only.html\"),\n    License(\"GFDL-1.1-or-later\", \"GNU Free Documentation License v1.1 or later\", \"https://spdx.org/licenses/GFDL-1.1-or-later.html\"),\n    License(\"GFDL-1.2\", \"GNU Free Documentation License v1.2\", \"https://spdx.org/licenses/GFDL-1.2.html\"),\n    License(\"GFDL-1.2-invariants-only\", \"GNU Free Documentation License v1.2 only - invariants\", \"https://spdx.org/licenses/GFDL-1.2-invariants-only.html\"),\n    License(\"GFDL-1.2-invariants-or-later\", \"GNU Free Documentation License v1.2 or later - invariants\", \"https://spdx.org/licenses/GFDL-1.2-invariants-or-later.html\"),\n    License(\"GFDL-1.2-no-invariants-only\", \"GNU Free Documentation License v1.2 only - no invariants\", \"https://spdx.org/licenses/GFDL-1.2-no-invariants-only.html\"),\n    License(\"GFDL-1.2-no-invariants-or-later\", \"GNU Free Documentation License v1.2 or later - no invariants\", \"https://spdx.org/licenses/GFDL-1.2-no-invariants-or-later.html\"),\n    License(\"GFDL-1.2-only\", \"GNU Free Documentation License v1.2 only\", \"https://spdx.org/licenses/GFDL-1.2-only.html\"),\n    License(\"GFDL-1.2-or-later\", \"GNU Free Documentation License v1.2 or later\", \"https://spdx.org/licenses/GFDL-1.2-or-later.html\"),\n    License(\"GFDL-1.3\", \"GNU Free Documentation License v1.3\", \"https://spdx.org/licenses/GFDL-1.3.html\"),\n    License(\"GFDL-1.3-invariants-only\", \"GNU Free Documentation License v1.3 only - invariants\", \"https://spdx.org/licenses/GFDL-1.3-invariants-only.html\"),\n    License(\"GFDL-1.3-invariants-or-later\", \"GNU Free Documentation License v1.3 or later - invariants\", \"https://spdx.org/licenses/GFDL-1.3-invariants-or-later.html\"),\n    License(\"GFDL-1.3-no-invariants-only\", \"GNU Free Documentation License v1.3 only - no invariants\", \"https://spdx.org/licenses/GFDL-1.3-no-invariants-only.html\"),\n    License(\"GFDL-1.3-no-invariants-or-later\", \"GNU Free Documentation License v1.3 or later - no invariants\", \"https://spdx.org/licenses/GFDL-1.3-no-invariants-or-later.html\"),\n    License(\"GFDL-1.3-only\", \"GNU Free Documentation License v1.3 only\", \"https://spdx.org/licenses/GFDL-1.3-only.html\"),\n    License(\"GFDL-1.3-or-later\", \"GNU Free Documentation License v1.3 or later\", \"https://spdx.org/licenses/GFDL-1.3-or-later.html\"),\n    License(\"GL2PS\", \"GL2PS License\", \"https://spdx.org/licenses/GL2PS.html\"),\n    License(\"GLWTPL\", \"Good Luck With That Public License\", \"https://spdx.org/licenses/GLWTPL.html\"),\n    License(\"GPL-1.0\", \"GNU General Public License v1.0 only\", \"https://spdx.org/licenses/GPL-1.0.html\"),\n    License(\"GPL-1.0+\", \"GNU General Public License v1.0 or later\", \"https://spdx.org/licenses/GPL-1.0+.html\"),\n    License(\"GPL-1.0-only\", \"GNU General Public License v1.0 only\", \"https://spdx.org/licenses/GPL-1.0-only.html\"),\n    License(\"GPL-1.0-or-later\", \"GNU General Public License v1.0 or later\", \"https://spdx.org/licenses/GPL-1.0-or-later.html\"),\n    License(\"GPL-2.0\", \"GNU General Public License v2.0 only\", \"https://spdx.org/licenses/GPL-2.0.html\"),\n    License(\"GPL-2.0+\", \"GNU General Public License v2.0 or later\", \"https://spdx.org/licenses/GPL-2.0+.html\"),\n    License(\"GPL-2.0-only\", \"GNU General Public License v2.0 only\", \"https://spdx.org/licenses/GPL-2.0-only.html\"),\n    License(\"GPL-2.0-or-later\", \"GNU General Public License v2.0 or later\", \"https://spdx.org/licenses/GPL-2.0-or-later.html\"),\n    License(\"GPL-2.0-with-GCC-exception\", \"GNU General Public License v2.0 w/GCC Runtime Library exception\", \"https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html\"),\n    License(\"GPL-2.0-with-autoconf-exception\", \"GNU General Public License v2.0 w/Autoconf exception\", \"https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html\"),\n    License(\"GPL-2.0-with-bison-exception\", \"GNU General Public License v2.0 w/Bison exception\", \"https://spdx.org/licenses/GPL-2.0-with-bison-exception.html\"),\n    License(\"GPL-2.0-with-classpath-exception\", \"GNU General Public License v2.0 w/Classpath exception\", \"https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html\"),\n    License(\"GPL-2.0-with-font-exception\", \"GNU General Public License v2.0 w/Font exception\", \"https://spdx.org/licenses/GPL-2.0-with-font-exception.html\"),\n    License(\"GPL-3.0\", \"GNU General Public License v3.0 only\", \"https://spdx.org/licenses/GPL-3.0.html\"),\n    License(\"GPL-3.0+\", \"GNU General Public License v3.0 or later\", \"https://spdx.org/licenses/GPL-3.0+.html\"),\n    License(\"GPL-3.0-only\", \"GNU General Public License v3.0 only\", \"https://spdx.org/licenses/GPL-3.0-only.html\"),\n    License(\"GPL-3.0-or-later\", \"GNU General Public License v3.0 or later\", \"https://spdx.org/licenses/GPL-3.0-or-later.html\"),\n    License(\"GPL-3.0-with-GCC-exception\", \"GNU General Public License v3.0 w/GCC Runtime Library exception\", \"https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html\"),\n    License(\"GPL-3.0-with-autoconf-exception\", \"GNU General Public License v3.0 w/Autoconf exception\", \"https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html\"),\n    License(\"Giftware\", \"Giftware License\", \"https://spdx.org/licenses/Giftware.html\"),\n    License(\"Glide\", \"3dfx Glide License\", \"https://spdx.org/licenses/Glide.html\"),\n    License(\"Glulxe\", \"Glulxe License\", \"https://spdx.org/licenses/Glulxe.html\"),\n    License(\"HPND\", \"Historical Permission Notice and Disclaimer\", \"https://spdx.org/licenses/HPND.html\"),\n    License(\"HPND-sell-variant\", \"Historical Permission Notice and Disclaimer - sell variant\", \"https://spdx.org/licenses/HPND-sell-variant.html\"),\n    License(\"HTMLTIDY\", \"HTML Tidy License\", \"https://spdx.org/licenses/HTMLTIDY.html\"),\n    License(\"HaskellReport\", \"Haskell Language Report License\", \"https://spdx.org/licenses/HaskellReport.html\"),\n    License(\"Hippocratic-2.1\", \"Hippocratic License 2.1\", \"https://spdx.org/licenses/Hippocratic-2.1.html\"),\n    License(\"IBM-pibs\", \"IBM PowerPC Initialization and Boot Software\", \"https://spdx.org/licenses/IBM-pibs.html\"),\n    License(\"ICU\", \"ICU License\", \"https://spdx.org/licenses/ICU.html\"),\n    License(\"IJG\", \"Independent JPEG Group License\", \"https://spdx.org/licenses/IJG.html\"),\n    License(\"IPA\", \"IPA Font License\", \"https://spdx.org/licenses/IPA.html\"),\n    License(\"IPL-1.0\", \"IBM Public License v1.0\", \"https://spdx.org/licenses/IPL-1.0.html\"),\n    License(\"ISC\", \"ISC License\", \"https://spdx.org/licenses/ISC.html\"),\n    License(\"ImageMagick\", \"ImageMagick License\", \"https://spdx.org/licenses/ImageMagick.html\"),\n    License(\"Imlib2\", \"Imlib2 License\", \"https://spdx.org/licenses/Imlib2.html\"),\n    License(\"Info-ZIP\", \"Info-ZIP License\", \"https://spdx.org/licenses/Info-ZIP.html\"),\n    License(\"Intel\", \"Intel Open Source License\", \"https://spdx.org/licenses/Intel.html\"),\n    License(\"Intel-ACPI\", \"Intel ACPI Software License Agreement\", \"https://spdx.org/licenses/Intel-ACPI.html\"),\n    License(\"Interbase-1.0\", \"Interbase Public License v1.0\", \"https://spdx.org/licenses/Interbase-1.0.html\"),\n    License(\"JPNIC\", \"Japan Network Information Center License\", \"https://spdx.org/licenses/JPNIC.html\"),\n    License(\"JSON\", \"JSON License\", \"https://spdx.org/licenses/JSON.html\"),\n    License(\"Jam\", \"Jam License\", \"https://spdx.org/licenses/Jam.html\"),\n    License(\"JasPer-2.0\", \"JasPer License\", \"https://spdx.org/licenses/JasPer-2.0.html\"),\n    License(\"LAL-1.2\", \"Licence Art Libre 1.2\", \"https://spdx.org/licenses/LAL-1.2.html\"),\n    License(\"LAL-1.3\", \"Licence Art Libre 1.3\", \"https://spdx.org/licenses/LAL-1.3.html\"),\n    License(\"LGPL-2.0\", \"GNU Library General Public License v2 only\", \"https://spdx.org/licenses/LGPL-2.0.html\"),\n    License(\"LGPL-2.0+\", \"GNU Library General Public License v2 or later\", \"https://spdx.org/licenses/LGPL-2.0+.html\"),\n    License(\"LGPL-2.0-only\", \"GNU Library General Public License v2 only\", \"https://spdx.org/licenses/LGPL-2.0-only.html\"),\n    License(\"LGPL-2.0-or-later\", \"GNU Library General Public License v2 or later\", \"https://spdx.org/licenses/LGPL-2.0-or-later.html\"),\n    License(\"LGPL-2.1\", \"GNU Lesser General Public License v2.1 only\", \"https://spdx.org/licenses/LGPL-2.1.html\"),\n    License(\"LGPL-2.1+\", \"GNU Library General Public License v2.1 or later\", \"https://spdx.org/licenses/LGPL-2.1+.html\"),\n    License(\"LGPL-2.1-only\", \"GNU Lesser General Public License v2.1 only\", \"https://spdx.org/licenses/LGPL-2.1-only.html\"),\n    License(\"LGPL-2.1-or-later\", \"GNU Lesser General Public License v2.1 or later\", \"https://spdx.org/licenses/LGPL-2.1-or-later.html\"),\n    License(\"LGPL-3.0\", \"GNU Lesser General Public License v3.0 only\", \"https://spdx.org/licenses/LGPL-3.0.html\"),\n    License(\"LGPL-3.0+\", \"GNU Lesser General Public License v3.0 or later\", \"https://spdx.org/licenses/LGPL-3.0+.html\"),\n    License(\"LGPL-3.0-only\", \"GNU Lesser General Public License v3.0 only\", \"https://spdx.org/licenses/LGPL-3.0-only.html\"),\n    License(\"LGPL-3.0-or-later\", \"GNU Lesser General Public License v3.0 or later\", \"https://spdx.org/licenses/LGPL-3.0-or-later.html\"),\n    License(\"LGPLLR\", \"Lesser General Public License For Linguistic Resources\", \"https://spdx.org/licenses/LGPLLR.html\"),\n    License(\"LPL-1.0\", \"Lucent Public License Version 1.0\", \"https://spdx.org/licenses/LPL-1.0.html\"),\n    License(\"LPL-1.02\", \"Lucent Public License v1.02\", \"https://spdx.org/licenses/LPL-1.02.html\"),\n    License(\"LPPL-1.0\", \"LaTeX Project Public License v1.0\", \"https://spdx.org/licenses/LPPL-1.0.html\"),\n    License(\"LPPL-1.1\", \"LaTeX Project Public License v1.1\", \"https://spdx.org/licenses/LPPL-1.1.html\"),\n    License(\"LPPL-1.2\", \"LaTeX Project Public License v1.2\", \"https://spdx.org/licenses/LPPL-1.2.html\"),\n    License(\"LPPL-1.3a\", \"LaTeX Project Public License v1.3a\", \"https://spdx.org/licenses/LPPL-1.3a.html\"),\n    License(\"LPPL-1.3c\", \"LaTeX Project Public License v1.3c\", \"https://spdx.org/licenses/LPPL-1.3c.html\"),\n    License(\"Latex2e\", \"Latex2e License\", \"https://spdx.org/licenses/Latex2e.html\"),\n    License(\"Leptonica\", \"Leptonica License\", \"https://spdx.org/licenses/Leptonica.html\"),\n    License(\"LiLiQ-P-1.1\", \"Licence Libre du Québec – Permissive version 1.1\", \"https://spdx.org/licenses/LiLiQ-P-1.1.html\"),\n    License(\"LiLiQ-R-1.1\", \"Licence Libre du Québec – Réciprocité version 1.1\", \"https://spdx.org/licenses/LiLiQ-R-1.1.html\"),\n    License(\"LiLiQ-Rplus-1.1\", \"Licence Libre du Québec – Réciprocité forte version 1.1\", \"https://spdx.org/licenses/LiLiQ-Rplus-1.1.html\"),\n    License(\"Libpng\", \"libpng License\", \"https://spdx.org/licenses/Libpng.html\"),\n    License(\"Linux-OpenIB\", \"Linux Kernel Variant of OpenIB.org license\", \"https://spdx.org/licenses/Linux-OpenIB.html\"),\n    License(\"Linux-man-pages-copyleft\", \"Linux man-pages Copyleft\", \"https://spdx.org/licenses/Linux-man-pages-copyleft.html\"),\n    License(\"MIT\", \"MIT License\", \"https://spdx.org/licenses/MIT.html\"),\n    License(\"MIT-0\", \"MIT No Attribution\", \"https://spdx.org/licenses/MIT-0.html\"),\n    License(\"MIT-CMU\", \"CMU License\", \"https://spdx.org/licenses/MIT-CMU.html\"),\n    License(\"MIT-Modern-Variant\", \"MIT License Modern Variant\", \"https://spdx.org/licenses/MIT-Modern-Variant.html\"),\n    License(\"MIT-advertising\", \"Enlightenment License (e16)\", \"https://spdx.org/licenses/MIT-advertising.html\"),\n    License(\"MIT-enna\", \"enna License\", \"https://spdx.org/licenses/MIT-enna.html\"),\n    License(\"MIT-feh\", \"feh License\", \"https://spdx.org/licenses/MIT-feh.html\"),\n    License(\"MIT-open-group\", \"MIT Open Group variant\", \"https://spdx.org/licenses/MIT-open-group.html\"),\n    License(\"MITNFA\", \"MIT +no-false-attribs license\", \"https://spdx.org/licenses/MITNFA.html\"),\n    License(\"MPL-1.0\", \"Mozilla Public License 1.0\", \"https://spdx.org/licenses/MPL-1.0.html\"),\n    License(\"MPL-1.1\", \"Mozilla Public License 1.1\", \"https://spdx.org/licenses/MPL-1.1.html\"),\n    License(\"MPL-2.0\", \"Mozilla Public License 2.0\", \"https://spdx.org/licenses/MPL-2.0.html\"),\n    License(\"MPL-2.0-no-copyleft-exception\", \"Mozilla Public License 2.0 (no copyleft exception)\", \"https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html\"),\n    License(\"MS-PL\", \"Microsoft Public License\", \"https://spdx.org/licenses/MS-PL.html\"),\n    License(\"MS-RL\", \"Microsoft Reciprocal License\", \"https://spdx.org/licenses/MS-RL.html\"),\n    License(\"MTLL\", \"Matrix Template Library License\", \"https://spdx.org/licenses/MTLL.html\"),\n    License(\"MakeIndex\", \"MakeIndex License\", \"https://spdx.org/licenses/MakeIndex.html\"),\n    License(\"MirOS\", \"The MirOS Licence\", \"https://spdx.org/licenses/MirOS.html\"),\n    License(\"Motosoto\", \"Motosoto License\", \"https://spdx.org/licenses/Motosoto.html\"),\n    License(\"MulanPSL-1.0\", \"Mulan Permissive Software License, Version 1\", \"https://spdx.org/licenses/MulanPSL-1.0.html\"),\n    License(\"MulanPSL-2.0\", \"Mulan Permissive Software License, Version 2\", \"https://spdx.org/licenses/MulanPSL-2.0.html\"),\n    License(\"Multics\", \"Multics License\", \"https://spdx.org/licenses/Multics.html\"),\n    License(\"Mup\", \"Mup License\", \"https://spdx.org/licenses/Mup.html\"),\n    License(\"NAIST-2003\", \"Nara Institute of Science and Technology License (2003)\", \"https://spdx.org/licenses/NAIST-2003.html\"),\n    License(\"NASA-1.3\", \"NASA Open Source Agreement 1.3\", \"https://spdx.org/licenses/NASA-1.3.html\"),\n    License(\"NBPL-1.0\", \"Net Boolean Public License v1\", \"https://spdx.org/licenses/NBPL-1.0.html\"),\n    License(\"NCGL-UK-2.0\", \"Non-Commercial Government Licence\", \"https://spdx.org/licenses/NCGL-UK-2.0.html\"),\n    License(\"NCSA\", \"University of Illinois/NCSA Open Source License\", \"https://spdx.org/licenses/NCSA.html\"),\n    License(\"NGPL\", \"Nethack General Public License\", \"https://spdx.org/licenses/NGPL.html\"),\n    License(\"NIST-PD\", \"NIST Public Domain Notice\", \"https://spdx.org/licenses/NIST-PD.html\"),\n    License(\"NIST-PD-fallback\", \"NIST Public Domain Notice with license fallback\", \"https://spdx.org/licenses/NIST-PD-fallback.html\"),\n    License(\"NLOD-1.0\", \"Norwegian Licence for Open Government Data (NLOD) 1.0\", \"https://spdx.org/licenses/NLOD-1.0.html\"),\n    License(\"NLOD-2.0\", \"Norwegian Licence for Open Government Data (NLOD) 2.0\", \"https://spdx.org/licenses/NLOD-2.0.html\"),\n    License(\"NLPL\", \"No Limit Public License\", \"https://spdx.org/licenses/NLPL.html\"),\n    License(\"NOSL\", \"Netizen Open Source License\", \"https://spdx.org/licenses/NOSL.html\"),\n    License(\"NPL-1.0\", \"Netscape Public License v1.0\", \"https://spdx.org/licenses/NPL-1.0.html\"),\n    License(\"NPL-1.1\", \"Netscape Public License v1.1\", \"https://spdx.org/licenses/NPL-1.1.html\"),\n    License(\"NPOSL-3.0\", \"Non-Profit Open Software License 3.0\", \"https://spdx.org/licenses/NPOSL-3.0.html\"),\n    License(\"NRL\", \"NRL License\", \"https://spdx.org/licenses/NRL.html\"),\n    License(\"NTP\", \"NTP License\", \"https://spdx.org/licenses/NTP.html\"),\n    License(\"NTP-0\", \"NTP No Attribution\", \"https://spdx.org/licenses/NTP-0.html\"),\n    License(\"Naumen\", \"Naumen Public License\", \"https://spdx.org/licenses/Naumen.html\"),\n    License(\"Net-SNMP\", \"Net-SNMP License\", \"https://spdx.org/licenses/Net-SNMP.html\"),\n    License(\"NetCDF\", \"NetCDF license\", \"https://spdx.org/licenses/NetCDF.html\"),\n    License(\"Newsletr\", \"Newsletr License\", \"https://spdx.org/licenses/Newsletr.html\"),\n    License(\"Nokia\", \"Nokia Open Source License\", \"https://spdx.org/licenses/Nokia.html\"),\n    License(\"Noweb\", \"Noweb License\", \"https://spdx.org/licenses/Noweb.html\"),\n    License(\"Nunit\", \"Nunit License\", \"https://spdx.org/licenses/Nunit.html\"),\n    License(\"O-UDA-1.0\", \"Open Use of Data Agreement v1.0\", \"https://spdx.org/licenses/O-UDA-1.0.html\"),\n    License(\"OCCT-PL\", \"Open CASCADE Technology Public License\", \"https://spdx.org/licenses/OCCT-PL.html\"),\n    License(\"OCLC-2.0\", \"OCLC Research Public License 2.0\", \"https://spdx.org/licenses/OCLC-2.0.html\"),\n    License(\"ODC-By-1.0\", \"Open Data Commons Attribution License v1.0\", \"https://spdx.org/licenses/ODC-By-1.0.html\"),\n    License(\"ODbL-1.0\", \"Open Data Commons Open Database License v1.0\", \"https://spdx.org/licenses/ODbL-1.0.html\"),\n    License(\"OFL-1.0\", \"SIL Open Font License 1.0\", \"https://spdx.org/licenses/OFL-1.0.html\"),\n    License(\"OFL-1.0-RFN\", \"SIL Open Font License 1.0 with Reserved Font Name\", \"https://spdx.org/licenses/OFL-1.0-RFN.html\"),\n    License(\"OFL-1.0-no-RFN\", \"SIL Open Font License 1.0 with no Reserved Font Name\", \"https://spdx.org/licenses/OFL-1.0-no-RFN.html\"),\n    License(\"OFL-1.1\", \"SIL Open Font License 1.1\", \"https://spdx.org/licenses/OFL-1.1.html\"),\n    License(\"OFL-1.1-RFN\", \"SIL Open Font License 1.1 with Reserved Font Name\", \"https://spdx.org/licenses/OFL-1.1-RFN.html\"),\n    License(\"OFL-1.1-no-RFN\", \"SIL Open Font License 1.1 with no Reserved Font Name\", \"https://spdx.org/licenses/OFL-1.1-no-RFN.html\"),\n    License(\"OGC-1.0\", \"OGC Software License, Version 1.0\", \"https://spdx.org/licenses/OGC-1.0.html\"),\n    License(\"OGDL-Taiwan-1.0\", \"Taiwan Open Government Data License, version 1.0\", \"https://spdx.org/licenses/OGDL-Taiwan-1.0.html\"),\n    License(\"OGL-Canada-2.0\", \"Open Government Licence - Canada\", \"https://spdx.org/licenses/OGL-Canada-2.0.html\"),\n    License(\"OGL-UK-1.0\", \"Open Government Licence v1.0\", \"https://spdx.org/licenses/OGL-UK-1.0.html\"),\n    License(\"OGL-UK-2.0\", \"Open Government Licence v2.0\", \"https://spdx.org/licenses/OGL-UK-2.0.html\"),\n    License(\"OGL-UK-3.0\", \"Open Government Licence v3.0\", \"https://spdx.org/licenses/OGL-UK-3.0.html\"),\n    License(\"OGTSL\", \"Open Group Test Suite License\", \"https://spdx.org/licenses/OGTSL.html\"),\n    License(\"OLDAP-1.1\", \"Open LDAP Public License v1.1\", \"https://spdx.org/licenses/OLDAP-1.1.html\"),\n    License(\"OLDAP-1.2\", \"Open LDAP Public License v1.2\", \"https://spdx.org/licenses/OLDAP-1.2.html\"),\n    License(\"OLDAP-1.3\", \"Open LDAP Public License v1.3\", \"https://spdx.org/licenses/OLDAP-1.3.html\"),\n    License(\"OLDAP-1.4\", \"Open LDAP Public License v1.4\", \"https://spdx.org/licenses/OLDAP-1.4.html\"),\n    License(\"OLDAP-2.0\", \"Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)\", \"https://spdx.org/licenses/OLDAP-2.0.html\"),\n    License(\"OLDAP-2.0.1\", \"Open LDAP Public License v2.0.1\", \"https://spdx.org/licenses/OLDAP-2.0.1.html\"),\n    License(\"OLDAP-2.1\", \"Open LDAP Public License v2.1\", \"https://spdx.org/licenses/OLDAP-2.1.html\"),\n    License(\"OLDAP-2.2\", \"Open LDAP Public License v2.2\", \"https://spdx.org/licenses/OLDAP-2.2.html\"),\n    License(\"OLDAP-2.2.1\", \"Open LDAP Public License v2.2.1\", \"https://spdx.org/licenses/OLDAP-2.2.1.html\"),\n    License(\"OLDAP-2.2.2\", \"Open LDAP Public License 2.2.2\", \"https://spdx.org/licenses/OLDAP-2.2.2.html\"),\n    License(\"OLDAP-2.3\", \"Open LDAP Public License v2.3\", \"https://spdx.org/licenses/OLDAP-2.3.html\"),\n    License(\"OLDAP-2.4\", \"Open LDAP Public License v2.4\", \"https://spdx.org/licenses/OLDAP-2.4.html\"),\n    License(\"OLDAP-2.5\", \"Open LDAP Public License v2.5\", \"https://spdx.org/licenses/OLDAP-2.5.html\"),\n    License(\"OLDAP-2.6\", \"Open LDAP Public License v2.6\", \"https://spdx.org/licenses/OLDAP-2.6.html\"),\n    License(\"OLDAP-2.7\", \"Open LDAP Public License v2.7\", \"https://spdx.org/licenses/OLDAP-2.7.html\"),\n    License(\"OLDAP-2.8\", \"Open LDAP Public License v2.8\", \"https://spdx.org/licenses/OLDAP-2.8.html\"),\n    License(\"OML\", \"Open Market License\", \"https://spdx.org/licenses/OML.html\"),\n    License(\"OPL-1.0\", \"Open Public License v1.0\", \"https://spdx.org/licenses/OPL-1.0.html\"),\n    License(\"OPUBL-1.0\", \"Open Publication License v1.0\", \"https://spdx.org/licenses/OPUBL-1.0.html\"),\n    License(\"OSET-PL-2.1\", \"OSET Public License version 2.1\", \"https://spdx.org/licenses/OSET-PL-2.1.html\"),\n    License(\"OSL-1.0\", \"Open Software License 1.0\", \"https://spdx.org/licenses/OSL-1.0.html\"),\n    License(\"OSL-1.1\", \"Open Software License 1.1\", \"https://spdx.org/licenses/OSL-1.1.html\"),\n    License(\"OSL-2.0\", \"Open Software License 2.0\", \"https://spdx.org/licenses/OSL-2.0.html\"),\n    License(\"OSL-2.1\", \"Open Software License 2.1\", \"https://spdx.org/licenses/OSL-2.1.html\"),\n    License(\"OSL-3.0\", \"Open Software License 3.0\", \"https://spdx.org/licenses/OSL-3.0.html\"),\n    License(\"OpenSSL\", \"OpenSSL License\", \"https://spdx.org/licenses/OpenSSL.html\"),\n    License(\"PDDL-1.0\", \"Open Data Commons Public Domain Dedication & License 1.0\", \"https://spdx.org/licenses/PDDL-1.0.html\"),\n    License(\"PHP-3.0\", \"PHP License v3.0\", \"https://spdx.org/licenses/PHP-3.0.html\"),\n    License(\"PHP-3.01\", \"PHP License v3.01\", \"https://spdx.org/licenses/PHP-3.01.html\"),\n    License(\"PSF-2.0\", \"Python Software Foundation License 2.0\", \"https://spdx.org/licenses/PSF-2.0.html\"),\n    License(\"Parity-6.0.0\", \"The Parity Public License 6.0.0\", \"https://spdx.org/licenses/Parity-6.0.0.html\"),\n    License(\"Parity-7.0.0\", \"The Parity Public License 7.0.0\", \"https://spdx.org/licenses/Parity-7.0.0.html\"),\n    License(\"Plexus\", \"Plexus Classworlds License\", \"https://spdx.org/licenses/Plexus.html\"),\n    License(\"PolyForm-Noncommercial-1.0.0\", \"PolyForm Noncommercial License 1.0.0\", \"https://spdx.org/licenses/PolyForm-Noncommercial-1.0.0.html\"),\n    License(\"PolyForm-Small-Business-1.0.0\", \"PolyForm Small Business License 1.0.0\", \"https://spdx.org/licenses/PolyForm-Small-Business-1.0.0.html\"),\n    License(\"PostgreSQL\", \"PostgreSQL License\", \"https://spdx.org/licenses/PostgreSQL.html\"),\n    License(\"Python-2.0\", \"Python License 2.0\", \"https://spdx.org/licenses/Python-2.0.html\"),\n    License(\"QPL-1.0\", \"Q Public License 1.0\", \"https://spdx.org/licenses/QPL-1.0.html\"),\n    License(\"Qhull\", \"Qhull License\", \"https://spdx.org/licenses/Qhull.html\"),\n    License(\"RHeCos-1.1\", \"Red Hat eCos Public License v1.1\", \"https://spdx.org/licenses/RHeCos-1.1.html\"),\n    License(\"RPL-1.1\", \"Reciprocal Public License 1.1\", \"https://spdx.org/licenses/RPL-1.1.html\"),\n    License(\"RPL-1.5\", \"Reciprocal Public License 1.5\", \"https://spdx.org/licenses/RPL-1.5.html\"),\n    License(\"RPSL-1.0\", \"RealNetworks Public Source License v1.0\", \"https://spdx.org/licenses/RPSL-1.0.html\"),\n    License(\"RSA-MD\", \"RSA Message-Digest License\", \"https://spdx.org/licenses/RSA-MD.html\"),\n    License(\"RSCPL\", \"Ricoh Source Code Public License\", \"https://spdx.org/licenses/RSCPL.html\"),\n    License(\"Rdisc\", \"Rdisc License\", \"https://spdx.org/licenses/Rdisc.html\"),\n    License(\"Ruby\", \"Ruby License\", \"https://spdx.org/licenses/Ruby.html\"),\n    License(\"SAX-PD\", \"Sax Public Domain Notice\", \"https://spdx.org/licenses/SAX-PD.html\"),\n    License(\"SCEA\", \"SCEA Shared Source License\", \"https://spdx.org/licenses/SCEA.html\"),\n    License(\"SGI-B-1.0\", \"SGI Free Software License B v1.0\", \"https://spdx.org/licenses/SGI-B-1.0.html\"),\n    License(\"SGI-B-1.1\", \"SGI Free Software License B v1.1\", \"https://spdx.org/licenses/SGI-B-1.1.html\"),\n    License(\"SGI-B-2.0\", \"SGI Free Software License B v2.0\", \"https://spdx.org/licenses/SGI-B-2.0.html\"),\n    License(\"SHL-0.5\", \"Solderpad Hardware License v0.5\", \"https://spdx.org/licenses/SHL-0.5.html\"),\n    License(\"SHL-0.51\", \"Solderpad Hardware License, Version 0.51\", \"https://spdx.org/licenses/SHL-0.51.html\"),\n    License(\"SISSL\", \"Sun Industry Standards Source License v1.1\", \"https://spdx.org/licenses/SISSL.html\"),\n    License(\"SISSL-1.2\", \"Sun Industry Standards Source License v1.2\", \"https://spdx.org/licenses/SISSL-1.2.html\"),\n    License(\"SMLNJ\", \"Standard ML of New Jersey License\", \"https://spdx.org/licenses/SMLNJ.html\"),\n    License(\"SMPPL\", \"Secure Messaging Protocol Public License\", \"https://spdx.org/licenses/SMPPL.html\"),\n    License(\"SNIA\", \"SNIA Public License 1.1\", \"https://spdx.org/licenses/SNIA.html\"),\n    License(\"SPL-1.0\", \"Sun Public License v1.0\", \"https://spdx.org/licenses/SPL-1.0.html\"),\n    License(\"SSH-OpenSSH\", \"SSH OpenSSH license\", \"https://spdx.org/licenses/SSH-OpenSSH.html\"),\n    License(\"SSH-short\", \"SSH short notice\", \"https://spdx.org/licenses/SSH-short.html\"),\n    License(\"SSPL-1.0\", \"Server Side Public License, v 1\", \"https://spdx.org/licenses/SSPL-1.0.html\"),\n    License(\"SWL\", \"Scheme Widget Library (SWL) Software License Agreement\", \"https://spdx.org/licenses/SWL.html\"),\n    License(\"Saxpath\", \"Saxpath License\", \"https://spdx.org/licenses/Saxpath.html\"),\n    License(\"SchemeReport\", \"Scheme Language Report License\", \"https://spdx.org/licenses/SchemeReport.html\"),\n    License(\"Sendmail\", \"Sendmail License\", \"https://spdx.org/licenses/Sendmail.html\"),\n    License(\"Sendmail-8.23\", \"Sendmail License 8.23\", \"https://spdx.org/licenses/Sendmail-8.23.html\"),\n    License(\"SimPL-2.0\", \"Simple Public License 2.0\", \"https://spdx.org/licenses/SimPL-2.0.html\"),\n    License(\"Sleepycat\", \"Sleepycat License\", \"https://spdx.org/licenses/Sleepycat.html\"),\n    License(\"Spencer-86\", \"Spencer License 86\", \"https://spdx.org/licenses/Spencer-86.html\"),\n    License(\"Spencer-94\", \"Spencer License 94\", \"https://spdx.org/licenses/Spencer-94.html\"),\n    License(\"Spencer-99\", \"Spencer License 99\", \"https://spdx.org/licenses/Spencer-99.html\"),\n    License(\"StandardML-NJ\", \"Standard ML of New Jersey License\", \"https://spdx.org/licenses/StandardML-NJ.html\"),\n    License(\"SugarCRM-1.1.3\", \"SugarCRM Public License v1.1.3\", \"https://spdx.org/licenses/SugarCRM-1.1.3.html\"),\n    License(\"TAPR-OHL-1.0\", \"TAPR Open Hardware License v1.0\", \"https://spdx.org/licenses/TAPR-OHL-1.0.html\"),\n    License(\"TCL\", \"TCL/TK License\", \"https://spdx.org/licenses/TCL.html\"),\n    License(\"TCP-wrappers\", \"TCP Wrappers License\", \"https://spdx.org/licenses/TCP-wrappers.html\"),\n    License(\"TMate\", \"TMate Open Source License\", \"https://spdx.org/licenses/TMate.html\"),\n    License(\"TORQUE-1.1\", \"TORQUE v2.5+ Software License v1.1\", \"https://spdx.org/licenses/TORQUE-1.1.html\"),\n    License(\"TOSL\", \"Trusster Open Source License\", \"https://spdx.org/licenses/TOSL.html\"),\n    License(\"TU-Berlin-1.0\", \"Technische Universitaet Berlin License 1.0\", \"https://spdx.org/licenses/TU-Berlin-1.0.html\"),\n    License(\"TU-Berlin-2.0\", \"Technische Universitaet Berlin License 2.0\", \"https://spdx.org/licenses/TU-Berlin-2.0.html\"),\n    License(\"UCL-1.0\", \"Upstream Compatibility License v1.0\", \"https://spdx.org/licenses/UCL-1.0.html\"),\n    License(\"UPL-1.0\", \"Universal Permissive License v1.0\", \"https://spdx.org/licenses/UPL-1.0.html\"),\n    License(\"Unicode-DFS-2015\", \"Unicode License Agreement - Data Files and Software (2015)\", \"https://spdx.org/licenses/Unicode-DFS-2015.html\"),\n    License(\"Unicode-DFS-2016\", \"Unicode License Agreement - Data Files and Software (2016)\", \"https://spdx.org/licenses/Unicode-DFS-2016.html\"),\n    License(\"Unicode-TOU\", \"Unicode Terms of Use\", \"https://spdx.org/licenses/Unicode-TOU.html\"),\n    License(\"Unlicense\", \"The Unlicense\", \"https://spdx.org/licenses/Unlicense.html\"),\n    License(\"VOSTROM\", \"VOSTROM Public License for Open Source\", \"https://spdx.org/licenses/VOSTROM.html\"),\n    License(\"VSL-1.0\", \"Vovida Software License v1.0\", \"https://spdx.org/licenses/VSL-1.0.html\"),\n    License(\"Vim\", \"Vim License\", \"https://spdx.org/licenses/Vim.html\"),\n    License(\"W3C\", \"W3C Software Notice and License (2002-12-31)\", \"https://spdx.org/licenses/W3C.html\"),\n    License(\"W3C-19980720\", \"W3C Software Notice and License (1998-07-20)\", \"https://spdx.org/licenses/W3C-19980720.html\"),\n    License(\"W3C-20150513\", \"W3C Software Notice and Document License (2015-05-13)\", \"https://spdx.org/licenses/W3C-20150513.html\"),\n    License(\"WTFPL\", \"Do What The F*ck You Want To Public License\", \"https://spdx.org/licenses/WTFPL.html\"),\n    License(\"Watcom-1.0\", \"Sybase Open Watcom Public License 1.0\", \"https://spdx.org/licenses/Watcom-1.0.html\"),\n    License(\"Wsuipa\", \"Wsuipa License\", \"https://spdx.org/licenses/Wsuipa.html\"),\n    License(\"X11\", \"X11 License\", \"https://spdx.org/licenses/X11.html\"),\n    License(\"X11-distribute-modifications-variant\", \"X11 License Distribution Modification Variant\", \"https://spdx.org/licenses/X11-distribute-modifications-variant.html\"),\n    License(\"XFree86-1.1\", \"XFree86 License 1.1\", \"https://spdx.org/licenses/XFree86-1.1.html\"),\n    License(\"XSkat\", \"XSkat License\", \"https://spdx.org/licenses/XSkat.html\"),\n    License(\"Xerox\", \"Xerox License\", \"https://spdx.org/licenses/Xerox.html\"),\n    License(\"Xnet\", \"X.Net License\", \"https://spdx.org/licenses/Xnet.html\"),\n    License(\"YPL-1.0\", \"Yahoo! Public License v1.0\", \"https://spdx.org/licenses/YPL-1.0.html\"),\n    License(\"YPL-1.1\", \"Yahoo! Public License v1.1\", \"https://spdx.org/licenses/YPL-1.1.html\"),\n    License(\"ZPL-1.1\", \"Zope Public License 1.1\", \"https://spdx.org/licenses/ZPL-1.1.html\"),\n    License(\"ZPL-2.0\", \"Zope Public License 2.0\", \"https://spdx.org/licenses/ZPL-2.0.html\"),\n    License(\"ZPL-2.1\", \"Zope Public License 2.1\", \"https://spdx.org/licenses/ZPL-2.1.html\"),\n    License(\"Zed\", \"Zed License\", \"https://spdx.org/licenses/Zed.html\"),\n    License(\"Zend-2.0\", \"Zend License v2.0\", \"https://spdx.org/licenses/Zend-2.0.html\"),\n    License(\"Zimbra-1.3\", \"Zimbra Public License v1.3\", \"https://spdx.org/licenses/Zimbra-1.3.html\"),\n    License(\"Zimbra-1.4\", \"Zimbra Public License v1.4\", \"https://spdx.org/licenses/Zimbra-1.4.html\"),\n    License(\"Zlib\", \"zlib License\", \"https://spdx.org/licenses/Zlib.html\"),\n    License(\"blessing\", \"SQLite Blessing\", \"https://spdx.org/licenses/blessing.html\"),\n    License(\"bzip2-1.0.5\", \"bzip2 and libbzip2 License v1.0.5\", \"https://spdx.org/licenses/bzip2-1.0.5.html\"),\n    License(\"bzip2-1.0.6\", \"bzip2 and libbzip2 License v1.0.6\", \"https://spdx.org/licenses/bzip2-1.0.6.html\"),\n    License(\"copyleft-next-0.3.0\", \"copyleft-next 0.3.0\", \"https://spdx.org/licenses/copyleft-next-0.3.0.html\"),\n    License(\"copyleft-next-0.3.1\", \"copyleft-next 0.3.1\", \"https://spdx.org/licenses/copyleft-next-0.3.1.html\"),\n    License(\"curl\", \"curl License\", \"https://spdx.org/licenses/curl.html\"),\n    License(\"diffmark\", \"diffmark license\", \"https://spdx.org/licenses/diffmark.html\"),\n    License(\"dvipdfm\", \"dvipdfm License\", \"https://spdx.org/licenses/dvipdfm.html\"),\n    License(\"eCos-2.0\", \"eCos license version 2.0\", \"https://spdx.org/licenses/eCos-2.0.html\"),\n    License(\"eGenix\", \"eGenix.com Public License 1.1.0\", \"https://spdx.org/licenses/eGenix.html\"),\n    License(\"etalab-2.0\", \"Etalab Open License 2.0\", \"https://spdx.org/licenses/etalab-2.0.html\"),\n    License(\"gSOAP-1.3b\", \"gSOAP Public License v1.3b\", \"https://spdx.org/licenses/gSOAP-1.3b.html\"),\n    License(\"gnuplot\", \"gnuplot License\", \"https://spdx.org/licenses/gnuplot.html\"),\n    License(\"iMatix\", \"iMatix Standard Function Library Agreement\", \"https://spdx.org/licenses/iMatix.html\"),\n    License(\"libpng-2.0\", \"PNG Reference Library version 2\", \"https://spdx.org/licenses/libpng-2.0.html\"),\n    License(\"libselinux-1.0\", \"libselinux public domain notice\", \"https://spdx.org/licenses/libselinux-1.0.html\"),\n    License(\"libtiff\", \"libtiff License\", \"https://spdx.org/licenses/libtiff.html\"),\n    License(\"mpich2\", \"mpich2 License\", \"https://spdx.org/licenses/mpich2.html\"),\n    License(\"psfrag\", \"psfrag License\", \"https://spdx.org/licenses/psfrag.html\"),\n    License(\"psutils\", \"psutils License\", \"https://spdx.org/licenses/psutils.html\"),\n    License(\"wxWindows\", \"wxWindows Library License\", \"https://spdx.org/licenses/wxWindows.html\"),\n    License(\"xinetd\", \"xinetd License\", \"https://spdx.org/licenses/xinetd.html\"),\n    License(\"xpp\", \"XPP License\", \"https://spdx.org/licenses/xpp.html\"),\n    License(\"zlib-acknowledgement\", \"zlib/libpng License with Acknowledgement\", \"https://spdx.org/licenses/zlib-acknowledgement.html\")\n  )\n  // format: on\n\n  lazy val map = list.map(l => l.id -> l).toMap\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/Name.scala",
    "content": "package scala.build.internal\n\n// adapted from https://github.com/com-lihaoyi/Ammonite/blob/9be39debc367abad5f5541ef58f4b986b2a8d045/amm/util/src/main/scala/ammonite/util/Model.scala#L45-L110\n\nimport scala.reflect.NameTransformer\n\ncase class Name(raw: String) {\n  assert(\n    NameTransformer.decode(raw) == raw,\n    \"Name() must be created with un-encoded text\"\n  )\n  assert(raw.charAt(0) != '`', \"Cannot create already-backticked identifiers\")\n  override def toString = s\"Name($backticked)\"\n  def encoded           = NameTransformer.encode(raw)\n  def backticked        = Name.backtickWrap(encoded)\n}\n\nobject Name {\n\n  def decoded(name: String) = NameTransformer.decode(name)\n\n  val alphaKeywords = Set(\n    \"abstract\",\n    \"case\",\n    \"catch\",\n    \"class\",\n    \"def\",\n    \"do\",\n    \"else\",\n    \"extends\",\n    \"false\",\n    \"finally\",\n    \"final\",\n    \"finally\",\n    \"forSome\",\n    \"for\",\n    \"if\",\n    \"implicit\",\n    \"import\",\n    \"lazy\",\n    \"match\",\n    \"new\",\n    \"null\",\n    \"object\",\n    \"override\",\n    \"package\",\n    \"private\",\n    \"protected\",\n    \"return\",\n    \"sealed\",\n    \"super\",\n    \"this\",\n    \"throw\",\n    \"trait\",\n    \"try\",\n    \"true\",\n    \"type\",\n    \"val\",\n    \"var\",\n    \"while\",\n    \"with\",\n    \"yield\",\n    \"_\",\n    \"macro\"\n  )\n  val symbolKeywords = Set(\n    \":\",\n    \";\",\n    \"=>\",\n    \"=\",\n    \"<-\",\n    \"<:\",\n    \"<%\",\n    \">:\",\n    \"#\",\n    \"@\",\n    \"\\u21d2\",\n    \"\\u2190\"\n  )\n  val blockCommentStart = \"/*\"\n  val lineCommentStart  = \"//\"\n\n  /** Custom implementation of ID parsing, instead of using the ScalaParse version. This lets us\n    * avoid loading FastParse and ScalaParse entirely if we're running a cached script, which shaves\n    * off 200-300ms of startup time.\n    */\n  def backtickWrap(s: String) =\n    if (s.isEmpty) \"``\"\n    else if (s(0) == '`' && s.last == '`') s\n    else {\n      val chunks                 = s.split(\"_\", -1)\n      def validOperator(c: Char) =\n        c.getType == Character.MATH_SYMBOL ||\n        c.getType == Character.OTHER_SYMBOL ||\n        \"!#%&*+-/:<=>?@\\\\^|~\".contains(c)\n      val validChunks = chunks.zipWithIndex.forall { case (chunk, index) =>\n        chunk.forall(c => c.isLetter || c.isDigit || c == '$') ||\n        (\n          chunk.forall(validOperator) &&\n          // operators can only come last\n          index == chunks.length - 1 &&\n          // but cannot be preceded by only a _\n          !(chunks.lift(index - 1).exists(_ == \"\") && index - 1 == 0)\n        )\n      }\n\n      val firstLetterValid = s(0).isLetter || s(0) == '_' || s(0) == '$' || validOperator(s(0))\n\n      val valid =\n        validChunks &&\n        firstLetterValid &&\n        !alphaKeywords.contains(s) &&\n        !symbolKeywords.contains(s) &&\n        !s.contains(blockCommentStart) &&\n        !s.contains(lineCommentStart)\n\n      if (valid) s else \"`\" + s + '`'\n    }\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/NativeWrapper.scala",
    "content": "package scala.build.internals\n\nimport scala.annotation.tailrec\nimport scala.build.Logger\nimport scala.io.Source\nimport scala.util.Using\n\n/*\n * Invoke `native-image.exe` inside a vcvars-initialized cmd.exe session.\n *\n * A temp batch file calls `vcvars64.bat` to set up MSVC, then runs\n * `native-image.exe` directly in the same session.  This avoids the\n * fragile pattern of capturing the vcvars environment via `set` and\n * replaying it through Java's ProcessBuilder, which silently loses\n * PATH entries on some JVM / Windows combinations.\n */\n\nobject MsvcEnvironment {\n\n  // Lower threshold to ensure native-image's internal paths (which can add 100-130+ chars\n  // for deeply nested source files) don't exceed Windows 260-char MAX_PATH limit.\n  // Native-image creates paths like: native-sources\\graal\\com\\oracle\\svm\\...\\Target_ClassName.c\n  private val pathLengthLimit = 90\n\n  /*\n   * Call `native-image.exe` inside a vcvars-initialized cmd.exe session.\n   *\n   * Rather than capturing the vcvars environment and replaying it (which is\n   * fragile — Java's ProcessBuilder env handling on Windows can silently lose\n   * PATH entries set by vcvars64.bat), we write a small batch file that:\n   *   1. calls vcvars64.bat  (sets up MSVC in the session)\n   *   2. runs native-image.exe directly (inherits the live session env)\n   *\n   * @return process exit code.\n   */\n  def msvcNativeImageProcess(\n    command: Seq[String],\n    workingDir: os.Path,\n    logger: Logger\n  ): Int = {\n    // Shorten the working dir for native-image (it creates deeply nested internal\n    // paths that can exceed the Windows 260-char MAX_PATH limit).\n    val (nativeImageWorkDir, driveToUnalias) =\n      if (workingDir.toString.length >= pathLengthLimit) {\n        val (driveLetter, shortPath) = getShortenedPath(workingDir, logger)\n        (shortPath, Some(driveLetter))\n      }\n      else\n        (workingDir, None)\n\n    try {\n      val vcvOpt = vcvarsOpt(logger)\n      vcvOpt match {\n        case None =>\n          logger.debug(s\"not found: vcvars64.bat\")\n          -1\n        case Some(vcvars) =>\n          logger.debug(s\"Using vcvars script $vcvars\")\n\n          // show aliased drive map\n          getSubstMappings.foreach((k, v) => logger.message(s\"substMap  $k: -> $v\"))\n\n          val vcvarsCmd = vcvars.toIO.getAbsolutePath\n\n          // Replace native-image.cmd with native-image.exe, if applicable\n          val updatedCommand: Seq[String] =\n            command.headOption match {\n              case Some(cmd) if cmd.toLowerCase.endsWith(\"native-image.cmd\") =>\n                val cmdPath   = os.Path(cmd, os.pwd)\n                val graalHome = cmdPath / os.up / os.up\n                resolveNativeImage(graalHome) match {\n                  case Some(exe) =>\n                    exe.toString +: command.tail\n                  case None =>\n                    command // fall back to the .cmd wrapper\n                }\n              case _ =>\n                command\n            }\n\n          // Quote arguments that contain batch-special characters\n          val quotedArgs = updatedCommand.map { arg =>\n            if arg.exists(c => \" &|^<>()\".contains(c)) then s\"\"\"\"$arg\"\"\"\"\n            else arg\n          }.mkString(\" \")\n\n          // Build a batch file that:\n          //   1. calls vcvars64.bat (with the inherited, non-SUBST CWD)\n          //   2. locates cl.exe and passes it explicitly to native-image\n          //      (works around GraalVM native-image not finding cl.exe via\n          //       PATH when the process runs from a SUBST-drive CWD)\n          //   3. switches to the shortened SUBST working directory\n          //   4. runs native-image.exe\n          val batchContent =\n            s\"\"\"@call \"$vcvarsCmd\"\n               |@if errorlevel 1 exit /b %ERRORLEVEL%\n               |@set GRAALVM_ARGUMENT_VECTOR_PROGRAM_NAME=native-image\n               |@for /f \"delims=\" %%i in ('where cl.exe 2^>nul') do @set \"CL_EXE=%%i\"\n               |@if not defined CL_EXE (\n               |  echo cl.exe not found in PATH after vcvars 1>&2\n               |  exit /b 1\n               |)\n               |@cd /d \"$nativeImageWorkDir\"\n               |@$quotedArgs --native-compiler-path=\"%CL_EXE%\"\n               |\"\"\".stripMargin\n          val batchFile = os.temp(suffix = \".bat\", contents = batchContent)\n\n          logger.debug(s\"native-image w/args: $updatedCommand\")\n\n          try\n            // Don't pass cwd here — let cmd.exe inherit the parent's real\n            // (non-SUBST) CWD so that vcvars64.bat runs without SUBST issues.\n            // The batch file does `cd /d` to the shortened workdir before\n            // launching native-image.\n            val result = os.proc(cmdExe, \"/c\", batchFile.toString).call(\n              stdout = os.Inherit,\n              stderr = os.Inherit,\n              check = false\n            )\n            result.exitCode\n          finally\n            try os.remove(batchFile)\n            catch { case _: Exception => }\n      }\n    }\n    finally\n      driveToUnalias.foreach(unaliasDriveLetter)\n  }\n\n  def getSubstMappings: Map[Char, String] =\n    try\n      val (exitCode, output) = execWindowsCmd(cmdExe, \"/c\", \"subst\")\n      if exitCode != 0 then Map.empty\n      else\n        output\n          .linesIterator\n          .flatMap { line =>\n            // Example: \"X:\\: => C:\\path\\to\\something\"\n            val parts = line.split(\"=>\").map(_.trim)\n            if parts.length == 2 then\n              val drivePart = parts(0) // \"X:\\:\"\n              val target    = parts(1) // \"C:\\path\\to\\something\"\n\n              // Extract the drive letter safely\n              val maybeDrive: Option[Char] =\n                if drivePart.length >= 2 && drivePart(1) == ':' then\n                  Some(drivePart(0)) // 'X'\n                else None\n\n              maybeDrive.map(_ -> target)\n            else None\n          }\n          .toMap\n    catch\n      case _: Throwable => Map.empty\n\n  // Reduce duplicate drive aliases to no more than one;\n  // @return Some(single-alias) or None.\n  private def consolidateAliases(\n    targetPath: os.Path,\n    logger: Logger\n  ): Option[Char] = {\n    val mappings  = getSubstMappings\n    val targetStr = targetPath.toString.toLowerCase\n\n    // Find all drives pointing to our target (case-insensitive on Windows)\n    val matchingDrives = mappings.filter { case (_, target) =>\n      target.toLowerCase == targetStr\n    }.keys.toList.sorted\n\n    matchingDrives match {\n      case Nil =>\n        // No existing aliases for this target\n        None\n\n      case kept :: duples =>\n        // Keep first one, remove the rest\n        duples.foreach { drive =>\n          logger.debug(s\"Removing duplicate alias $drive: -> $targetStr\")\n          try\n            unaliasDriveLetter(drive)\n          catch {\n            case e: Exception =>\n              logger.debug(s\"Failed to remove duplicate alias $drive: ${e.getMessage}\")\n          }\n        }\n\n        if (duples.isEmpty)\n          logger.debug(s\"Reusing existing alias $kept: -> $targetStr\")\n        else\n          logger.debug(\n            s\"Consolidated ${duples.size + 1} aliases to $kept:, removed: ${duples.mkString(\", \")}\"\n          )\n\n        Some(kept)\n    }\n  }\n\n  // Find or create a shortened alias for the given path\n  def getShortenedPath(\n    currentHome: os.Path,\n    logger: Logger\n  ): (Char, os.Path) = {\n    val from = currentHome / os.up\n\n    val driveLetter = consolidateAliases(from, logger) match {\n      case Some(existingDrive) =>\n        existingDrive // Reuse existing alias\n\n      case None =>\n        // Create new alias\n        val driveLetter = availableDriveLetter()\n        logger.debug(s\"Creating drive alias $driveLetter: -> $from\")\n        aliasDriveLetter(driveLetter, from.toString)\n        driveLetter\n    }\n    val drivePath = os.Path(s\"$driveLetter:\" + \"\\\\\")\n    val newHome   = drivePath / currentHome.last\n    (driveLetter, newHome)\n  }\n\n  private def availableDriveLetter(): Char = {\n    // if a drive letter has already been mapped by SUBST, it isn't free\n    val substDrives: Set[Char] = getSubstMappings.keySet\n    @tailrec\n    def helper(from: Char): Char =\n      if (from > 'Z') sys.error(\"Cannot find free drive letter\")\n      else if (mountedDrives.contains(from) || substDrives.contains(from))\n        helper((from + 1).toChar)\n      else from\n\n    helper('D')\n  }\n\n  def aliasDriveLetter(driveLetter: Char, from: String): Unit =\n    execWindowsCmd(cmdExe, \"/c\", s\"subst $driveLetter: \\\"$from\\\"\")\n\n  def unaliasDriveLetter(driveLetter: Char): Int =\n    execWindowsCmd(cmdExe, \"/c\", s\"subst $driveLetter: /d\")._1\n\n  def execWindowsCmd(cmd: String*): (Int, String) =\n    val pb = new ProcessBuilder(cmd*)\n    pb.redirectInput(ProcessBuilder.Redirect.INHERIT)\n    pb.redirectError(ProcessBuilder.Redirect.INHERIT)\n    pb.redirectOutput(ProcessBuilder.Redirect.PIPE)\n    val p = pb.start()\n    // read stdout fully\n    val output = Using(Source.fromInputStream(p.getInputStream, \"UTF-8\")) { source =>\n      source.mkString\n    }.getOrElse(\"\")\n\n    val exitCode = p.waitFor()\n    (exitCode, output)\n\n  def setCodePage(cp: String): Int =\n    execWindowsCmd(cmdExe, \"/c\", s\"chcp $cp\")._1\n\n  def getCodePage: String = {\n    val out = execWindowsCmd(cmdExe, \"/c\", \"chcp\")._2\n    out.split(\":\").lastOption.map(_.trim).getOrElse(\"\") // Extract the number\n  }\n\n  def getCodePage(logger: Logger): String =\n    try {\n      val out = os.proc(cmdExe, \"/c\", \"chcp\").call().out.text().trim\n      out.split(\":\").lastOption.map(_.trim).getOrElse(\"\") // Extract the number\n    }\n    catch {\n      case e: Exception =>\n        logger.debug(s\"unable to get initial code page: ${e.getMessage}\")\n        \"\"\n    }\n\n  private def resolveNativeImage(graalHome: os.Path): Option[os.Path] = {\n    val candidates = Seq(\n      graalHome / \"lib\" / \"svm\" / \"bin\" / \"native-image.exe\",\n      graalHome / \"bin\" / \"native-image.exe\",\n      graalHome / \"native-image.exe\"\n    )\n    candidates.find(os.exists)\n  }\n\n  private def vcvarsOpt(logger: Logger): Option[os.Path] = {\n    val candidates =\n      vcVarsCandidates\n        .iterator\n        .map(os.Path(_, os.pwd))\n        .filter(os.exists(_))\n        .toSeq\n\n    if (candidates.isEmpty) None\n    else {\n      // Sort lexicographically; newest VS installs always sort last\n      val sorted = candidates.sortBy(_.toString)\n      sorted.foreach(s => logger.debug(s\"candidate: $s\"))\n      sorted.lastOption\n    }\n  }\n\n  // newest VS first, Enterprise > Community > BuildTools\n  private def vcVersions                              = Seq(\"2022\", \"2019\", \"2017\")\n  private def vcEditions                              = Seq(\"Enterprise\", \"Community\", \"BuildTools\")\n  private lazy val vcVarsCandidates: Iterable[String] =\n    EnvVar.Misc.vcVarsAll.valueOpt ++ {\n      for {\n        isX86   <- Seq(false, true)\n        version <- vcVersions\n        edition <- vcEditions\n      } yield {\n        val programFiles = if (isX86) \"Program Files (x86)\" else \"Program Files\"\n        \"\"\"C:\\\"\"\" + programFiles + \"\"\"\\Microsoft Visual Studio\\\"\"\" + version + \"\\\\\" + edition +\n          \"\"\"\\VC\\Auxiliary\\Build\\vcvars64.bat\"\"\"\n      }\n    }\n\n  lazy val mountedDrives: String = {\n    val str         = \"HKEY_LOCAL_MACHINE/SYSTEM/MountedDevices\".replace('/', '\\\\')\n    val queryDrives = s\"reg query $str\"\n    val lines       = os.proc(cmdExe, \"/c\", queryDrives).call().out.lines()\n    val dosDevices  = lines.filter { s =>\n      s.contains(\"DosDevices\")\n    }.map { s =>\n      s.replaceAll(\".DosDevices.\", \"\").replaceAll(\":.*\", \"\")\n    }\n    dosDevices.mkString\n  }\n\n  lazy val systemRoot: String = sys.env.getOrElse(\"SystemRoot\", \"C:\\\\Windows\").stripSuffix(\"\\\\\")\n  lazy val cmdExe: String     = s\"$systemRoot\\\\System32\\\\cmd.exe\"\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/OsLibc.scala",
    "content": "package scala.build.internal\n\nimport bloop.rifle.VersionUtil.parseJavaVersion\nimport coursier.jvm.JvmChannel\n\nimport java.io.IOException\nimport java.nio.charset.Charset\n\nimport scala.build.Os\nimport scala.util.{Properties, Try}\n\nobject OsLibc {\n\n  lazy val isMusl: Option[Boolean] = {\n\n    def tryRun(cmd: String*): Option[os.CommandResult] =\n      try {\n        val res = os.proc(cmd).call(\n          mergeErrIntoOut = true,\n          check = false\n        )\n        Some(res)\n      }\n      catch {\n        case _: IOException =>\n          None\n      }\n\n    val getconfResOpt = tryRun(\"getconf\", \"GNU_LIBC_VERSION\")\n    if (getconfResOpt.exists(_.exitCode == 0)) Some(false)\n    else {\n\n      val lddResOpt = tryRun(\"ldd\", \"--version\")\n\n      val foundMusl = lddResOpt.exists { lddRes =>\n        (lddRes.exitCode == 0 || lddRes.exitCode == 1) &&\n        lddRes.out.text(Charset.defaultCharset()).contains(\"musl\")\n      }\n\n      if (foundMusl)\n        Some(true)\n      else {\n        val inLib = os.list(os.Path(\"/lib\")).map(_.last)\n        if (inLib.exists(_.contains(\"-linux-gnu\"))) Some(false)\n        else if (inLib.exists(name => name.contains(\"libc.musl-\") || name.contains(\"ld-musl-\")))\n          Some(true)\n        else {\n          val inUsrSbin = os.list(os.Path(\"/usr/sbin\")).map(_.last)\n          if (inUsrSbin.exists(_.contains(\"glibc\"))) Some(false)\n          else None\n        }\n      }\n    }\n  }\n\n  // FIXME These values should be the default ones in coursier-jvm\n\n  lazy val jvmIndexOs: String = {\n    val default = JvmChannel.defaultOs\n    if (default == \"linux\" && isMusl.getOrElse(false)) \"linux-musl\"\n    else default\n  }\n\n  def baseDefaultJvm(os: String, jvmVersion: String): String = {\n    def bloomMinimumJavaOrHigher = Try(jvmVersion.takeWhile(_.isDigit).toInt)\n      .toOption\n      .forall(_ >= Constants.minimumBloopJavaVersion)\n    if (os == \"linux-musl\") s\"liberica:$jvmVersion\" // zulu could work too\n    else if (bloomMinimumJavaOrHigher) s\"temurin:$jvmVersion\"\n    else if (Os.isArmArchitecture) s\"zulu:$jvmVersion\" // adopt doesn't support Java 8 on macOS arm\n    else s\"temurin:$jvmVersion\"\n  }\n\n  def defaultJvm(os: String): String =\n    baseDefaultJvm(os, Constants.defaultJavaVersion.toString)\n\n  def javaVersion(javaCmd: String): Int = {\n    val javaVersionOutput = os.proc(javaCmd, \"-version\").call(\n      cwd = os.pwd,\n      stdout = os.Pipe,\n      stderr = os.Pipe,\n      mergeErrIntoOut = true\n    ).out.trim()\n    parseJavaVersion(javaVersionOutput).getOrElse {\n      throw new Exception(s\"Could not parse java version from output: $javaVersionOutput\")\n    }\n  }\n\n  def javaHomeVersion(javaHome: os.Path): (Int, String) = {\n    val ext     = if (Properties.isWin) \".exe\" else \"\"\n    val javaCmd = (javaHome / \"bin\" / s\"java$ext\").toString\n\n    (javaVersion(javaCmd), javaCmd)\n  }\n\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/Regexes.scala",
    "content": "package scala.build.internal\n\nobject Regexes {\n  val scala2NightlyRegex         = raw\"\"\"2\\.(\\d+)\\.(\\d+)-bin-[a-f0-9]*\"\"\".r\n  val scala3NightlyNicknameRegex = raw\"\"\"3\\.([0-9]*)\\.nightly\"\"\".r\n  val scala3RcRegex              = raw\"\"\"3\\.([0-9]*\\.[0-9]*-[rR][cC][0-9]+)\"\"\".r\n  val scala3RcNicknameRegex      = raw\"\"\"3\\.([0-9]*)\\.?[rR][cC]\"\"\".r\n  val scala3LtsRegex             = raw\"\"\"3\\.3\\.[0-9]+\"\"\".r\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/internals/StableScalaVersion.scala",
    "content": "package scala.build.internal\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport coursier.core.Version\n\nfinal case class StableScalaVersion(\n  scalaCliVersion: String,\n  supportedScalaVersions: Seq[String]\n) {\n  lazy val scalaCliVersion0 = Version(scalaCliVersion)\n}\n\nobject StableScalaVersion {\n  val seqCodec: JsonValueCodec[Seq[StableScalaVersion]] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/core/src/main/scala/scala/build/warnings/DeprecatedWarning.scala",
    "content": "package scala.build.warnings\n\nimport scala.build.Position\nimport scala.build.errors.Diagnostic.TextEdit\nimport scala.build.errors.{Diagnostic, Severity}\n\nfinal case class DeprecatedWarning(\n  message: String,\n  positions: Seq[Position],\n  override val textEdit: Option[TextEdit]\n) extends Diagnostic {\n  def severity: Severity = Severity.Warning\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveDescription.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\n\nfinal case class DirectiveDescription(description: String, descriptionMd: String = \"\")\n    extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveExamples.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\n\nfinal case class DirectiveExamples(examples: String) extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveGroupDetails.scala",
    "content": "package scala.build.directives\n\nfinal case class DirectiveGroupDetails(\n  name: String,\n  description: String,\n  usage: String,\n  descriptionMdOpt: Option[String] = None,\n  usageMdOpt: Option[String] = None,\n  examples: Seq[String] = Nil\n) {\n  def descriptionMd: String =\n    descriptionMdOpt.getOrElse(description)\n  def usageMd: String =\n    usageMdOpt.getOrElse(s\"`$usage`\")\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveGroupName.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\n\nfinal case class DirectiveGroupName(name: String) extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveLevel.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\nimport scala.cli.commands.SpecificationLevel\n\nfinal case class DirectiveLevel(level: SpecificationLevel) extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveName.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\n\nfinal case class DirectiveName(name: String) extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveSpecialSyntax.scala",
    "content": "package scala.build.directives\n\nimport scala.util.matching.Regex\n\nobject DirectiveSpecialSyntax {\n\n  /** Replaces the `${.}` pattern in the directive value with the parent directory of the file\n    * containing the directive. Skips replacement if the pattern is preceded by two dollar signs\n    * ($$). https://github.com/VirtusLab/scala-cli/issues/1098\n    *\n    * @param directiveValue\n    *   the value of the directive, e.g., \"-coverage-out:${.}\" for example for the directive \"//>\n    *   using options \"-coverage-out:${.}\"\"\n    * @param path\n    *   the file path from which the directive is read; replacement occurs only if the directive is\n    *   from a local file\n    * @return\n    *   the directive value with the `${.}` pattern replaced by the parent directory, if applicable\n    */\n  def handlingSpecialPathSyntax(directiveValue: String, path: Either[String, os.Path]): String = {\n    val pattern = \"\"\"(((?:\\$)+)(\\{\\.\\}))\"\"\".r\n    path match {\n      case Right(p) =>\n        pattern.replaceAllIn(\n          directiveValue,\n          (m: Regex.Match) => {\n            val dollarSigns = m.group(2)\n            val dollars     = \"\\\\$\" * (dollarSigns.length / 2)\n            if (dollarSigns.length % 2 == 0)\n              s\"$dollars${m.group(3)}\"\n            else\n              s\"$dollars${p / os.up}\"\n          }\n        )\n      case _ => directiveValue\n    }\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveUsage.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\n\nfinal case class DirectiveUsage(usage: String, usageMd: String = \"\") extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/DirectiveValueParser.scala",
    "content": "package scala.build.directives\n\nimport com.virtuslab.using_directives.custom.model.{BooleanValue, EmptyValue, StringValue, Value}\n\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  MalformedDirectiveError,\n  ToolkitDirectiveMissingVersionError,\n  UsingDirectiveValueNumError,\n  UsingDirectiveWrongValueTypeError\n}\nimport scala.build.preprocessing.ScopePath\nimport scala.build.preprocessing.directives.DirectiveUtil\nimport scala.build.{Position, Positioned}\n\nabstract class DirectiveValueParser[+T] {\n  def parse(\n    key: String,\n    values: Seq[Value[?]],\n    scopePath: ScopePath,\n    path: Either[String, os.Path]\n  ): Either[BuildException, T]\n\n  final def map[U](f: T => U): DirectiveValueParser[U] =\n    new DirectiveValueParser.Mapped[T, U](this, f)\n}\n\nobject DirectiveValueParser {\n\n  private final class Mapped[T, +U](underlying: DirectiveValueParser[T], f: T => U)\n      extends DirectiveValueParser[U] {\n    def parse(\n      key: String,\n      values: Seq[Value[?]],\n      scopePath: ScopePath,\n      path: Either[String, os.Path]\n    ): Either[BuildException, U] =\n      underlying.parse(key, values, scopePath, path).map(f)\n  }\n\n  abstract class DirectiveSingleValueParser[+T] extends DirectiveValueParser[T] {\n    def parseValue(\n      key: String,\n      value: Value[?],\n      cwd: ScopePath,\n      path: Either[String, os.Path]\n    ): Either[BuildException, T]\n\n    final def parse(\n      key: String,\n      values: Seq[Value[?]],\n      scopePath: ScopePath,\n      path: Either[String, os.Path]\n    ): Either[BuildException, T] =\n      values match {\n        case Seq(value) if !value.isEmpty => parseValue(key, value, scopePath, path)\n        case Seq(value) if value.isEmpty && (key == \"toolkit\" || key == \"test.toolkit\") =>\n          // FIXME: handle similar parsing errors in the directive declaration instead of hacks like this one\n          Left(ToolkitDirectiveMissingVersionError(maybePath = path, key = key))\n        case resultValues @ _ =>\n          Left(\n            new UsingDirectiveValueNumError(\n              maybePath = path,\n              key = key,\n              expectedValueNum = 1,\n              providedValueNum = resultValues.count(!_.isEmpty)\n            )\n          )\n      }\n  }\n\n  given DirectiveValueParser[Unit] = { (_, values, _, path) =>\n    values match {\n      case Seq()          => Right(())\n      case Seq(value, _*) =>\n        val pos = value.position(path)\n        Left(new MalformedDirectiveError(\"Expected no value in directive\", Seq(pos)))\n    }\n  }\n\n  extension (value: Value[?]) {\n\n    def isEmpty: Boolean =\n      value match {\n        case _: EmptyValue => true\n        case _             => false\n      }\n\n    def isString: Boolean =\n      value match {\n        case _: StringValue => true\n        case _              => false\n      }\n    def asString: Option[String] =\n      value match {\n        case s: StringValue => Some(s.get())\n        case _              => None\n      }\n    def isBoolean: Boolean =\n      value match {\n        case _: BooleanValue => true\n        case _               => false\n      }\n    def asBoolean: Option[Boolean] =\n      value match {\n        case s: BooleanValue => Some(s.get())\n        case _               => None\n      }\n\n    def position(path: Either[String, os.Path]): Position =\n      DirectiveUtil.position(value, path)\n  }\n\n  given DirectiveValueParser[Boolean] = { (key, values, _, path) =>\n    values.filter(!_.isEmpty) match {\n      case Seq()  => Right(true)\n      case Seq(v) =>\n        v.asBoolean.toRight {\n          new UsingDirectiveWrongValueTypeError(\n            maybePath = path,\n            key = key,\n            expectedTypes = Seq(\"boolean\"),\n            hint = \"\"\n          )\n        }\n      case values0 =>\n        Left(\n          new MalformedDirectiveError(\n            s\"Unexpected values ${values0.map(_.toString).mkString(\", \")}\",\n            values0.map(_.position(path))\n          )\n        )\n    }\n  }\n\n  given DirectiveSingleValueParser[String] =\n    (key, value, _, path) =>\n      value.asString.toRight {\n        val pos = value.position(path)\n        new MalformedDirectiveError(\n          message =\n            s\"\"\"Encountered an error for the $key using directive.\n               |Expected a string, got '${value.getRelatedASTNode.toString}'\"\"\".stripMargin,\n          positions = Seq(pos)\n        )\n      }.map(DirectiveSpecialSyntax.handlingSpecialPathSyntax(_, path))\n\n  final case class MaybeNumericalString(value: String)\n\n  given DirectiveSingleValueParser[MaybeNumericalString] =\n    (key, value, _, path) =>\n      value.asString.map(MaybeNumericalString(_)).toRight {\n        val pos = value.position(path)\n        new MalformedDirectiveError(\n          s\"\"\"Encountered an error for the $key using directive.\n             |Expected a string value, got '${value.getRelatedASTNode.toString}'\"\"\".stripMargin,\n          Seq(pos)\n        )\n      }\n\n  final case class WithScopePath[+T](scopePath: ScopePath, value: T)\n\n  object WithScopePath {\n    def empty[T](value: T): WithScopePath[T] =\n      WithScopePath(ScopePath(Left(\"invalid\"), os.sub), value)\n  }\n\n  given [T](using underlying: DirectiveValueParser[T]): DirectiveValueParser[WithScopePath[T]] = {\n    (key, values, scopePath, path) =>\n      underlying.parse(key, values, scopePath, path)\n        .map(WithScopePath(scopePath, _))\n  }\n  given [T](using\n    underlying: DirectiveSingleValueParser[T]\n  ): DirectiveSingleValueParser[Positioned[T]] = {\n    (key, value, scopePath, path) =>\n      underlying.parseValue(key, value, scopePath, path)\n        .map(Positioned(value.position(path), _))\n  }\n  given [T](using underlying: DirectiveValueParser[T]): DirectiveValueParser[Option[T]] =\n    underlying.map(Some(_))\n  given [T](using underlying: DirectiveSingleValueParser[T]): DirectiveValueParser[List[T]] = {\n    (key, values, scopePath, path) =>\n      val res    = values.filter(!_.isEmpty).map(underlying.parseValue(key, _, scopePath, path))\n      val errors = res.collect {\n        case Left(e) => e\n      }\n      if (errors.isEmpty) Right(res.collect { case Right(v) => v }.toList)\n      else Left(CompositeBuildException(errors))\n  }\n\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/HasBuildOptions.scala",
    "content": "package scala.build.directives\n\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildOptions\n\ntrait HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions]\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/HasBuildOptionsWithRequirements.scala",
    "content": "package scala.build.directives\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.options.{BuildOptions, WithBuildRequirements}\n\ntrait HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]]\n  final def buildOptionsWithRequirements\n    : Either[BuildException, List[WithBuildRequirements[BuildOptions]]] =\n    buildOptionsList\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map(_.toList)\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/HasBuildRequirements.scala",
    "content": "package scala.build.directives\n\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildRequirements\n\ntrait HasBuildRequirements {\n  def buildRequirements: Either[BuildException, BuildRequirements]\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/directives/ScopedValue.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport com.virtuslab.using_directives.custom.model.Value\n\nimport scala.build.Positioned\nimport scala.build.preprocessing.ScopePath\n\ncase class ScopedValue[T <: Value[?]](\n  positioned: Positioned[String],\n  maybeScopePath: Option[ScopePath] = None\n)\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/ScalaJsLinkingError.scala",
    "content": "package scala.build.errors\n\nfinal class ScalaJsLinkingError extends BuildException(\"Error linking Scala.js\")\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/SingleValueExpectedError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.preprocessing.directives.{DirectiveUtil, StrictDirective}\n\nfinal class SingleValueExpectedError(\n  val directive: StrictDirective,\n  val path: Either[String, os.Path]\n) extends BuildException(\n      s\"Expected a single value for directive ${directive.key} \" +\n        s\"(got ${directive.values.length} values: ${directive.values.map(_.get().toString).mkString(\", \")})\",\n      positions = DirectiveUtil.positions(directive.values, path)\n    ) {\n  assert(directive.stringValuesCount > 1)\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/UsingDirectiveExpectationError.scala",
    "content": "package scala.build.errors\n\nsealed abstract class UsingDirectiveExpectationError(\n  message: String\n) extends BuildException(message)\n\nfinal class UsingDirectiveWrongValueTypeError(\n  maybePath: Either[String, os.Path],\n  key: String,\n  expectedTypes: Seq[String],\n  hint: String = \"\"\n) extends UsingDirectiveExpectationError(\n      s\"\"\"${expectedTypes.mkString(\n          \", or \"\n        )} expected for the $key using directive key${maybePath.map(path => s\" at $path\").getOrElse(\n          \"\"\n        )}.\n         |$hint\"\"\".stripMargin\n    )\n\nfinal class UsingDirectiveValueNumError(\n  maybePath: Either[String, os.Path],\n  key: String,\n  expectedValueNum: Int,\n  providedValueNum: Int\n) extends UsingDirectiveExpectationError({\n      val pathString = maybePath.map(p => s\" at $p\").getOrElse(\"\")\n      s\"\"\"Encountered an error when parsing the `$key` using directive$pathString.\n         |Expected $expectedValueNum values, but got $providedValueNum values instead.\"\"\".stripMargin\n    })\n\nfinal class ToolkitDirectiveMissingVersionError(\n  val maybePath: Either[String, os.Path],\n  val key: String\n) extends UsingDirectiveExpectationError({\n      val pathString = maybePath.map(p => s\" at $p\").getOrElse(\"\")\n      s\"\"\"Encountered an error when parsing the `$key` using directive$pathString.\n         |Expected a version or \"default\" to be passed.\n         |Example: `//> using $key default`\n         |\"\"\".stripMargin\n    })\nobject ToolkitDirectiveMissingVersionError {\n  def apply(maybePath: Either[String, os.Path], key: String): ToolkitDirectiveMissingVersionError =\n    new ToolkitDirectiveMissingVersionError(maybePath, key)\n  def unapply(arg: ToolkitDirectiveMissingVersionError): (Either[String, os.Path], String) =\n    arg.maybePath -> arg.key\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/UsingFileFromUriError.scala",
    "content": "package scala.build.errors\n\nimport java.net.URI\n\nimport scala.build.Position\n\nfinal class UsingFileFromUriError(uri: URI, positions: Seq[Position], description: String)\n    extends BuildException(\n      message = s\"Error using file from $uri - $description\",\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/WrongDirectoryPathError.scala",
    "content": "package scala.build.errors\n\nclass WrongDirectoryPathError(cause: Throwable) extends BuildException(\n      message = s\"\"\"The directory path argument in the using directives at could not be found!\n                   |${cause.getLocalizedMessage}\"\"\".stripMargin,\n      cause = cause\n    )\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/WrongJarPathError.scala",
    "content": "package scala.build.errors\n\nclass WrongJarPathError(cause: Throwable) extends BuildException(\n      message = s\"\"\"The jar path argument in the using directives at could not be found!\n                   |${cause.getLocalizedMessage}\"\"\".stripMargin,\n      cause = cause\n    )\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/WrongJavaHomePathError.scala",
    "content": "package scala.build.errors\n\nclass WrongJavaHomePathError(javaHomeValue: String, cause: Throwable)\n    extends BuildException(\n      message =\n        s\"\"\"The java home path argument in the using directives at $javaHomeValue could not be found!\n           |${cause.getLocalizedMessage}\"\"\".stripMargin,\n      cause = cause\n    )\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/errors/WrongSourcePathError.scala",
    "content": "package scala.build.errors\n\nimport scala.build.Position\n\nclass WrongSourcePathError(path: String, cause: Throwable, positions: Seq[Position])\n    extends BuildException(\n      message = s\"Invalid path argument '$path' in using directives\".stripMargin,\n      cause = cause,\n      positions = positions\n    )\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/ScopePath.scala",
    "content": "package scala.build.preprocessing\n\nfinal case class ScopePath(\n  root: Either[String, os.Path],\n  subPath: os.SubPath\n) {\n  def /(subPath: os.PathChunk): ScopePath =\n    copy(subPath = this.subPath / subPath)\n  def path: Either[String, os.Path] =\n    root\n      .left.map(r => s\"$r/$subPath\")\n      .map(_ / subPath)\n}\n\nobject ScopePath {\n  def fromPath(path: os.Path): ScopePath = {\n    def root(p: os.Path): os.Path =\n      if (p.segmentCount > 0) root(p / os.up) else p\n    val root0 = root(path)\n    ScopePath(Right(root0), path.subRelativeTo(root0))\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/Scoped.scala",
    "content": "package scala.build.preprocessing\n\nimport scala.build.errors.BuildException\n\nfinal case class Scoped[+T](path: ScopePath, value: T) {\n  def appliesTo(candidate: ScopePath): Boolean =\n    path.root == candidate.root &&\n    candidate.subPath.startsWith(path.subPath)\n  def valueFor(candidate: ScopePath): Option[T] =\n    if (appliesTo(candidate)) Some(value) else None\n\n  def map[U](f: T => U): Scoped[U] =\n    copy(value = f(value))\n  def mapE[U](f: T => Either[BuildException, U]): Either[BuildException, Scoped[U]] =\n    f(value).map(u => copy(value = u))\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Benchmarking.scala",
    "content": "package scala.build.preprocessing.directives\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, JmhOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Benchmarking options\")\n@DirectiveExamples(s\"//> using jmh\")\n@DirectiveExamples(s\"//> using jmh true\")\n@DirectiveExamples(s\"//> using jmhVersion ${Constants.jmhVersion}\")\n@DirectiveUsage(\n  \"//> using jmh _value_ | using jmhVersion _value_\",\n  \"\"\"`//> using jmh` _value_\n    |\n    |`//> using jmhVersion` _value_\n    \"\"\".stripMargin.trim\n)\n@DirectiveDescription(\"Add benchmarking options\")\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\ncase class Benchmarking(\n  jmh: Option[Boolean] = None,\n  jmhVersion: Option[Positioned[String]] = None\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    Right(\n      BuildOptions(\n        jmhOptions = JmhOptions(\n          enableJmh = jmh,\n          runJmh = jmh,\n          jmhVersion = jmhVersion.map(_.value)\n        )\n      )\n    )\n}\n\nobject Benchmarking {\n  val handler: DirectiveHandler[Benchmarking] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/BuildInfo.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, SourceGeneratorOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveExamples(\"//> using buildInfo\")\n@DirectiveUsage(\"//> using buildInfo\", \"`//> using buildInfo`\")\n@DirectiveDescription(\"Generate BuildInfo for project\")\n@DirectiveLevel(SpecificationLevel.RESTRICTED)\nfinal case class BuildInfo(\n  buildInfo: Boolean = false\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    val options = BuildOptions(sourceGeneratorOptions =\n      SourceGeneratorOptions(useBuildInfo = Some(buildInfo))\n    )\n    Right(options)\n}\n\nobject BuildInfo {\n  val handler: DirectiveHandler[BuildInfo] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ClasspathUtils.scala",
    "content": "package scala.build.preprocessing.directives\n\nobject ClasspathUtils {\n  extension (classpathItem: os.Path) {\n    def hasSourceJarSuffix: Boolean = classpathItem.last.endsWith(\"-sources.jar\")\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ComputeVersion.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.EitherOptOps\nimport scala.build.Positioned\nimport scala.build.directives.{\n  DirectiveDescription,\n  DirectiveExamples,\n  DirectiveGroupName,\n  DirectiveLevel,\n  DirectiveUsage,\n  HasBuildOptions\n}\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, ComputeVersion as cv, SourceGeneratorOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Compute Version\")\n@DirectiveExamples(\"//> using computeVersion git\")\n@DirectiveExamples(\"//> using computeVersion git:tag\")\n@DirectiveExamples(\"//> using computeVersion git:dynver\")\n@DirectiveUsage(\"//> using computeVersion git:tag\", \"`//> using computeVersion` _method_\")\n@DirectiveDescription(\"Method used to compute the version for BuildInfo\")\n@DirectiveLevel(SpecificationLevel.RESTRICTED)\nfinal case class ComputeVersion(computeVersion: Option[Positioned[String]] = None)\n    extends HasBuildOptions {\n\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n    BuildOptions(\n      sourceGeneratorOptions = SourceGeneratorOptions(\n        computeVersion = value {\n          computeVersion\n            .map(cv.parse)\n            .sequence\n        }\n      )\n    )\n  }\n}\n\nobject ComputeVersion {\n  val handler: DirectiveHandler[ComputeVersion] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/CustomJar.scala",
    "content": "package scala.build.preprocessing.directives\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, CompositeBuildException, WrongJarPathError}\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{BuildOptions, ClassPathOptions, Scope, WithBuildRequirements}\nimport scala.build.preprocessing.directives.ClasspathUtils.*\nimport scala.build.preprocessing.directives.CustomJar.JarType\nimport scala.cli.commands.SpecificationLevel\nimport scala.util.Try\n\n@DirectiveGroupName(\"Custom JAR\")\n@DirectiveExamples(\n  \"//> using jar /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.13/2.3.7/shapeless_2.13-2.3.7.jar\"\n)\n@DirectiveExamples(\n  \"//> using test.jar /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.13/2.3.7/shapeless_2.13-2.3.7.jar\"\n)\n@DirectiveExamples(\"//> using sourceJar /path/to/custom-jar-sources.jar\")\n@DirectiveExamples(\n  \"//> using sourceJars /path/to/custom-jar-sources.jar /path/to/another-jar-sources.jar\"\n)\n@DirectiveExamples(\"//> using test.sourceJar /path/to/test-custom-jar-sources.jar\")\n@DirectiveUsage(\n  \"`//> using jar `_path_ | `//> using jars `_path1_ _path2_ …\",\n  \"\"\"`//> using jar` _path_\n    |\n    |`//> using jars` _path1_ _path2_ …\n    |\n    |`//> using test.jar` _path_\n    |\n    |`//> using test.jars` _path1_ _path2_ …\n    |\n    |`//> using source.jar` _path_\n    |\n    |`//> using source.jars` _path1_ _path2_ …\n    |\n    |`//> using test.source.jar` _path_\n    |\n    |`//> using test.source.jars` _path1_ _path2_ …\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Manually add JAR(s) to the class path\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class CustomJar(\n  @DirectiveName(\"jars\")\n  jar: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil),\n  @DirectiveName(\"test.jar\")\n  @DirectiveName(\"test.jars\")\n  testJar: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil),\n  @DirectiveName(\"sources.jar\")\n  @DirectiveName(\"sourcesJars\")\n  @DirectiveName(\"sources.jars\")\n  @DirectiveName(\"sourceJar\")\n  @DirectiveName(\"source.jar\")\n  @DirectiveName(\"sourceJars\")\n  @DirectiveName(\"source.jars\")\n  sourcesJar: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil),\n  @DirectiveName(\"test.sourcesJar\")\n  @DirectiveName(\"test.sources.jar\")\n  @DirectiveName(\"test.sourcesJars\")\n  @DirectiveName(\"test.sources.jars\")\n  @DirectiveName(\"test.sourceJar\")\n  @DirectiveName(\"test.source.jar\")\n  @DirectiveName(\"test.sourceJars\")\n  @DirectiveName(\"test.source.jars\")\n  testSourcesJar: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil)\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] =\n    List(\n      CustomJar.buildOptions(jar, JarType.Jar)\n        .map(_.withEmptyRequirements),\n      CustomJar.buildOptions(testJar, JarType.Jar)\n        .map(_.withScopeRequirement(Scope.Test)),\n      CustomJar.buildOptions(sourcesJar, JarType.SourcesJar)\n        .map(_.withEmptyRequirements),\n      CustomJar.buildOptions(testSourcesJar, JarType.SourcesJar)\n        .map(_.withScopeRequirement(Scope.Test))\n    )\n}\n\nobject CustomJar {\n  val handler: DirectiveHandler[CustomJar] = DirectiveHandler.derive\n  enum JarType:\n    case Jar, SourcesJar\n  def buildOptions(\n    jar: DirectiveValueParser.WithScopePath[List[Positioned[String]]],\n    jarType: JarType\n  ): Either[BuildException, BuildOptions] = {\n    val cwd = jar.scopePath\n    jar.value\n      .map { posPathStr =>\n        val eitherRootPathOrBuildException =\n          Directive.osRoot(cwd, posPathStr.positions.headOption)\n        eitherRootPathOrBuildException.flatMap { root =>\n          Try(os.Path(posPathStr.value, root))\n            .toEither\n            .left.map(new WrongJarPathError(_))\n        }\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map { paths =>\n        val classPathOptions = jarType match\n          case JarType.Jar =>\n            val (sourceJars, regularJars) = paths.partition(_.hasSourceJarSuffix)\n            ClassPathOptions(extraClassPath = regularJars, extraSourceJars = sourceJars)\n          case JarType.SourcesJar => ClassPathOptions(extraSourceJars = paths)\n        BuildOptions(classPathOptions = classPathOptions)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Dependency.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport dependency.AnyDependency\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{\n  BuildOptions,\n  ClassPathOptions,\n  Scope,\n  ShadowingSeq,\n  WithBuildRequirements\n}\nimport scala.build.preprocessing.directives.Dependency.DependencyType\nimport scala.build.preprocessing.directives.DirectiveUtil.*\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveExamples(\"//> using dep com.lihaoyi::os-lib:0.9.1\")\n@DirectiveExamples(\n  \"//> using dep tabby:tabby:0.2.3,url=https://github.com/bjornregnell/tabby/releases/download/v0.2.3/tabby_3-0.2.3.jar\"\n)\n@DirectiveExamples(\"//> using test.dep org.scalatest::scalatest:3.2.10\")\n@DirectiveExamples(\"//> using test.dep org.scalameta::munit:0.7.29\")\n@DirectiveExamples(\n  \"//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\"\n)\n@DirectiveExamples(\n  \"//> using scalafix.dep com.github.xuwei-k::scalafix-rules:0.5.1\"\n)\n@DirectiveUsage(\n  \"//> using dep org:name:ver | //> using deps org:name:ver org2:name2:ver2\",\n  \"\"\"`//> using dep` _org_`:`name`:`ver\n    |\n    |`//> using deps` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using test.dep` _org_`:`name`:`ver\n    |\n    |`//> using test.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using test.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using compileOnly.dep` _org_`:`name`:`ver\n    |\n    |`//> using compileOnly.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using compileOnly.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using scalafix.dep` _org_`:`name`:`ver\n    |\n    |`//> using scalafix.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\n    |`//> using scalafix.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Add dependencies\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class Dependency(\n  @DirectiveName(\"lib\")  // backwards compat\n  @DirectiveName(\"libs\") // backwards compat\n  @DirectiveName(\"dep\")\n  @DirectiveName(\"deps\")\n  @DirectiveName(\"dependencies\")\n  dependency: List[Positioned[String]] = Nil,\n  @DirectiveName(\"test.dependency\")\n  @DirectiveName(\"test.dep\")\n  @DirectiveName(\"test.deps\")\n  @DirectiveName(\"test.dependencies\")\n  testDependency: List[Positioned[String]] = Nil,\n  @DirectiveName(\"compileOnly.lib\")  // backwards compat\n  @DirectiveName(\"compileOnly.libs\") // backwards compat\n  @DirectiveName(\"compileOnly.dep\")\n  @DirectiveName(\"compileOnly.deps\")\n  @DirectiveName(\"compileOnly.dependencies\")\n  compileOnlyDependency: List[Positioned[String]] = Nil,\n  @DirectiveName(\"scalafix.dep\")\n  @DirectiveName(\"scalafix.deps\")\n  @DirectiveName(\"scalafix.dependencies\")\n  scalafixDependency: List[Positioned[String]] = Nil\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(\n    Dependency.buildOptions(dependency, DependencyType.Runtime).map(_.withEmptyRequirements),\n    Dependency.buildOptions(testDependency, DependencyType.Runtime).map(\n      _.withScopeRequirement(Scope.Test)\n    ),\n    Dependency.buildOptions(compileOnlyDependency, DependencyType.CompileOnly)\n      .map(_.withEmptyRequirements),\n    Dependency.buildOptions(scalafixDependency, DependencyType.Scalafix)\n      .map(_.withEmptyRequirements)\n  )\n}\n\nobject Dependency {\n  val handler: DirectiveHandler[Dependency] = DirectiveHandler.derive\n\n  sealed trait DependencyType\n  object DependencyType {\n    case object Runtime     extends DependencyType\n    case object CompileOnly extends DependencyType\n    case object Scalafix    extends DependencyType\n  }\n\n  def buildOptions(\n    ds: List[Positioned[String]],\n    tpe: DependencyType\n  ): Either[BuildException, BuildOptions] = either {\n    val dependencies: ShadowingSeq[Positioned[AnyDependency]] =\n      value(ds.asDependencies.map(ShadowingSeq.from))\n    val classPathOptions =\n      tpe match {\n        case DependencyType.Runtime     => ClassPathOptions(extraDependencies = dependencies)\n        case DependencyType.CompileOnly =>\n          ClassPathOptions(extraCompileOnlyDependencies = dependencies)\n        case DependencyType.Scalafix => ClassPathOptions(scalafixDependencies = dependencies)\n      }\n\n    BuildOptions(classPathOptions = classPathOptions)\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Directive.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Position\nimport scala.build.errors.{BuildException, ForbiddenPathReferenceError}\nimport scala.build.preprocessing.ScopePath\n\nfinal case class Directive(\n  tpe: Directive.Type,\n  values: Seq[String],\n  scope: Option[String],\n  isComment: Boolean,\n  position: Position\n)\n\nobject Directive {\n  sealed abstract class Type(val name: String) extends Product with Serializable\n  case object Using                            extends Type(\"using\")\n  case object Require                          extends Type(\"require\")\n\n  def osRootResource(cwd: ScopePath): (Option[os.SubPath], Option[os.Path]) =\n    cwd.root match {\n      case Left(_)     => (Some(cwd.subPath), None)\n      case Right(root) => (None, Some(root / cwd.subPath))\n    }\n\n  def osRoot(cwd: ScopePath, pos: Option[Position]): Either[BuildException, os.Path] =\n    cwd.root match {\n      case Left(virtualRoot) =>\n        Left(new ForbiddenPathReferenceError(virtualRoot, pos))\n      case Right(root) =>\n        Right(root / cwd.subPath)\n    }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveHandler.scala",
    "content": "package scala.build.preprocessing.directives\nimport java.util.Locale\n\nimport scala.build.Logger\nimport scala.build.Ops.*\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, CompositeBuildException, UnexpectedDirectiveError}\nimport scala.build.preprocessing.Scoped\nimport scala.cli.commands.SpecificationLevel\nimport scala.quoted.*\n\ntrait DirectiveHandler[+T] { self =>\n  def name: String\n  def description: String\n  def descriptionMd: String = description\n  def usage: String\n  def usageMd: String       = s\"`$usage`\"\n  def examples: Seq[String] = Nil\n\n  /** Is this directive an advanved feature, that will not be accessible when running scala-cli as\n    * `scala`\n    */\n  def scalaSpecificationLevel: SpecificationLevel\n  protected def SpecificationLevel = scala.cli.commands.SpecificationLevel\n\n  final def isRestricted: Boolean   = scalaSpecificationLevel == SpecificationLevel.RESTRICTED\n  final def isExperimental: Boolean = scalaSpecificationLevel == SpecificationLevel.EXPERIMENTAL\n\n  def keys: Seq[Key]\n\n  def handleValues(\n    scopedDirective: ScopedDirective,\n    logger: Logger\n  ): Either[BuildException, ProcessedDirective[T]]\n\n  def map[U](f: T => U): DirectiveHandler[U] =\n    new DirectiveHandler[U] {\n      def name                   = self.name\n      def usage                  = self.usage\n      override def usageMd       = self.usageMd\n      def description            = self.description\n      override def descriptionMd = self.descriptionMd\n      override def examples      = self.examples\n\n      def scalaSpecificationLevel = self.scalaSpecificationLevel\n\n      def keys = self.keys\n\n      def handleValues(scopedDirective: ScopedDirective, logger: Logger) =\n        self.handleValues(scopedDirective, logger)\n          .map(_.map(f))\n    }\n  def mapE[U](f: T => Either[BuildException, U]): DirectiveHandler[U] =\n    new DirectiveHandler[U] {\n      def name                   = self.name\n      def usage                  = self.usage\n      override def usageMd       = self.usageMd\n      def description            = self.description\n      override def descriptionMd = self.descriptionMd\n      override def examples      = self.examples\n\n      def scalaSpecificationLevel = self.scalaSpecificationLevel\n\n      def keys = self.keys\n\n      def handleValues(scopedDirective: ScopedDirective, logger: Logger) =\n        self.handleValues(scopedDirective, logger).flatMap(_.mapE(f))\n    }\n\n}\n\n/** Using directive key with all its aliases */\ncase class Key(nameAliases: Seq[String])\n\nobject DirectiveHandler {\n\n  // from https://github.com/alexarchambault/case-app/blob/7ac9ae7cc6765df48eab27c4e35c66b00e4469a7/core/shared/src/main/scala/caseapp/core/util/CaseUtil.scala#L5-L22\n  def pascalCaseSplit(s: List[Char]): List[String] =\n    if (s.isEmpty)\n      Nil\n    else if (!s.head.isUpper) {\n      val (w, tail) = s.span(!_.isUpper)\n      w.mkString :: pascalCaseSplit(tail)\n    }\n    else if (s.tail.headOption.forall(!_.isUpper)) {\n      val (w, tail) = s.tail.span(!_.isUpper)\n      (s.head :: w).mkString :: pascalCaseSplit(tail)\n    }\n    else {\n      val (w, tail) = s.span(_.isUpper)\n      if (tail.isEmpty)\n        w.mkString :: pascalCaseSplit(tail)\n      else\n        w.init.mkString :: pascalCaseSplit(w.last :: tail)\n    }\n\n  def normalizeName(s: String): String = {\n    val elems = s.split('-')\n    (elems.head +: elems.tail.map(_.capitalize)).mkString\n  }\n\n  private def fields[U](using\n    q: Quotes,\n    t: Type[U]\n  ): List[(q.reflect.Symbol, q.reflect.TypeRepr)] = {\n    import quotes.reflect.*\n    val sym = TypeRepr.of[U] match {\n      case AppliedType(base, _) =>\n        base.typeSymbol\n      case _ =>\n        TypeTree.of[U].symbol\n    }\n\n    // Many things inspired by https://github.com/plokhotnyuk/jsoniter-scala/blob/8f39e1d45fde2a04984498f036cad93286344c30/jsoniter-scala-macros/shared/src/main/scala-3/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala#L564-L613\n    // and around, here\n    sym.primaryConstructor\n      .paramSymss\n      .flatten\n      .map(f => (f, f.tree))\n      .collect {\n        case (sym, v: ValDef) =>\n          (sym, v.tpt.tpe)\n      }\n  }\n\n  def shortName[T](using Quotes, Type[T]): String = {\n    val fullName = Type.show[T]\n    // attempt at getting a simple name out of fullName (this is likely broken)\n    fullName.takeWhile(_ != '[').split('.').last\n  }\n\n  inline private def deriveParser[T]: DirectiveHandler[T] =\n    ${ deriveParserImpl[T] }\n  private def deriveParserImpl[T](using q: Quotes, t: Type[T]): Expr[DirectiveHandler[T]] = {\n    import quotes.reflect.*\n    val tSym    = TypeTree.of[T].symbol\n    val fields0 = fields[T]\n\n    val defaultMap: Map[String, Expr[Any]] = {\n      val comp =\n        if (tSym.isClassDef && !tSym.companionClass.isNoSymbol) tSym.companionClass\n        else tSym\n      val bodyOpt = Some(comp)\n        .filter(!_.isNoSymbol)\n        .map(_.tree)\n        .collect {\n          case cd: ClassDef => cd.body\n        }\n      bodyOpt match {\n        case Some(body) =>\n          val names = fields0\n            .map(_._1)\n            .filter(_.flags.is(Flags.HasDefault))\n            .map(_.name)\n          val values = body.collect {\n            case d @ DefDef(name, _, _, _) if name.startsWith(\"$lessinit$greater$default\") =>\n              Ref(d.symbol).asExpr\n          }\n          names.zip(values).toMap\n        case None =>\n          Map.empty\n      }\n    }\n\n    val nameValue = tSym.annotations\n      .find(_.tpe =:= TypeRepr.of[DirectiveGroupName])\n      .collect {\n        case Apply(_, List(arg)) =>\n          arg.asExprOf[String]\n      }\n      .getOrElse {\n        Expr(shortName[T].stripSuffix(\"Directives\"))\n      }\n\n    val (usageValue, usageMdValue) = tSym.annotations\n      .find(_.tpe =:= TypeRepr.of[DirectiveUsage])\n      .collect {\n        case Apply(_, List(arg)) =>\n          (arg.asExprOf[String], Expr(\"\"))\n        case Apply(_, List(arg, argMd)) =>\n          (arg.asExprOf[String], argMd.asExprOf[String])\n      }\n      .getOrElse {\n        sys.error(s\"Missing DirectiveUsage directive on ${Type.show[T]}\")\n      }\n\n    val (descriptionValue, descriptionMdValue) = tSym.annotations\n      .find(_.tpe =:= TypeRepr.of[DirectiveDescription])\n      .collect {\n        case Apply(_, List(arg)) =>\n          (arg.asExprOf[String], Expr(\"\"))\n        case Apply(_, List(arg, argMd)) =>\n          (arg.asExprOf[String], argMd.asExprOf[String])\n      }\n      .getOrElse {\n        sys.error(s\"Missing DirectiveDescription directive on ${Type.show[T]}\")\n      }\n\n    val prefixValueOpt = tSym.annotations\n      .find(_.tpe =:= TypeRepr.of[DirectivePrefix])\n      .collect {\n        case Apply(_, List(arg)) =>\n          arg.asExprOf[String]\n      }\n    def withPrefix(name: Expr[String]): Expr[String] =\n      prefixValueOpt match {\n        case None              => name\n        case Some(prefixValue) => '{ $prefixValue + $name }\n      }\n\n    val examplesValue = tSym.annotations\n      .filter(_.tpe =:= TypeRepr.of[DirectiveExamples])\n      .collect {\n        case Apply(_, List(arg)) =>\n          arg.asExprOf[String]\n      }\n      .reverse // not sure in what extent we can rely on the ordering here…\n\n    val levelValue = tSym.annotations\n      .find(_.tpe =:= TypeRepr.of[DirectiveLevel])\n      .collect {\n        case Apply(_, List(arg)) =>\n          arg.asExprOf[SpecificationLevel]\n      }\n      .getOrElse {\n        sys.error(s\"Missing DirectiveLevel directive on ${Type.show[T]}\")\n      }\n\n    def namesFromAnnotations(sym: Symbol) = sym.annotations\n      .filter(_.tpe =:= TypeRepr.of[DirectiveName])\n      .collect {\n        case Apply(_, List(arg)) =>\n          withPrefix(arg.asExprOf[String])\n      }\n\n    val keysValue = Expr.ofList {\n      fields0.map {\n        case (sym, _) =>\n          Expr.ofList(withPrefix(Expr(sym.name)) +: namesFromAnnotations(sym))\n      }\n    }\n\n    val elseCase: (\n      Expr[ScopedDirective],\n      Expr[Logger]\n    ) => Expr[Either[BuildException, ProcessedDirective[T]]] =\n      (scopedDirective, _) =>\n        '{\n          Left(new UnexpectedDirectiveError($scopedDirective.directive.key))\n        }\n\n    val handleValuesImpl = fields0.zipWithIndex.foldRight(elseCase) {\n      case (((sym, tRepr), idx), elseCase0) =>\n        val namesFromAnnotations0 = namesFromAnnotations(sym)\n\n        def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match\n          case AppliedType(_, typeArgs) => typeArgs.map(_.dealias)\n          case _                        => Nil\n\n        // from https://github.com/plokhotnyuk/jsoniter-scala/blob/1704a9cbb22b75a59f21ddf2a11427ba24df3212/jsoniter-scala-macros/shared/src/main/scala-3/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala#L849-L854\n        def genNew(argss: List[List[Term]]): Term =\n          val constructorNoTypes = Select(New(Inferred(TypeRepr.of[T])), tSym.primaryConstructor)\n          val constructor        = typeArgs(TypeRepr.of[T]) match\n            case Nil      => constructorNoTypes\n            case typeArgs => TypeApply(constructorNoTypes, typeArgs.map(Inferred(_)))\n          argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args))\n\n        val newArgs = fields0.map {\n          case (sym, _) =>\n            defaultMap.getOrElse(sym.name, sys.error(s\"Field ${sym.name} has no default value\"))\n        }\n\n        tRepr.asType match {\n          case '[t] =>\n            val parser = Expr.summon[DirectiveValueParser[t]].getOrElse {\n              sys.error(s\"Cannot get implicit DirectiveValueParser[${Type.show[t]}]\")\n            }\n\n            val name = withPrefix(Expr(sym.name))\n\n            val cond: Expr[String] => Expr[Boolean] =\n              if (namesFromAnnotations0.isEmpty)\n                keyName => '{ DirectiveHandler.normalizeName($keyName) == $name }\n              else {\n                val names = Expr.ofList(name +: namesFromAnnotations0)\n                keyName => '{ $names.contains(DirectiveHandler.normalizeName($keyName)) }\n              }\n\n            (scopedDirective, logger) =>\n              '{\n                if (${ cond('{ $scopedDirective.directive.key }) }) {\n                  val valuesByScope = $scopedDirective.directive.values.groupBy(_.getScope)\n                    .toVector\n                    .map {\n                      case (scopeOrNull, values) =>\n                        (Option(scopeOrNull), values)\n                    }\n                    .sortBy(_._1.getOrElse(\"\"))\n                  valuesByScope\n                    .map {\n                      case (scopeOpt, _) =>\n                        $parser.parse(\n                          $scopedDirective.directive.key,\n                          $scopedDirective.directive.values,\n                          $scopedDirective.cwd,\n                          $scopedDirective.maybePath\n                        ).map { r =>\n                          scopeOpt -> ${\n                            genNew(List(newArgs.updated(idx, 'r).map(_.asTerm)))\n                              .asExprOf[T]\n                          }\n                        }\n                    }\n                    .sequence\n                    .left.map(CompositeBuildException(_))\n                    .map { v =>\n                      val mainOpt = v.collectFirst {\n                        case (None, t) => t\n                      }\n                      val scoped = v.collect {\n                        case (Some(scopeStr), t) =>\n                          // FIXME os.RelPath(…) might fail\n                          Scoped(\n                            $scopedDirective.cwd / os.RelPath(scopeStr),\n                            t\n                          )\n                      }\n                      ProcessedDirective(mainOpt, scoped)\n                    }\n                }\n                else\n                  ${ elseCase0(scopedDirective, logger) }\n              }\n        }\n    }\n\n    '{\n      new DirectiveHandler[T] {\n        def name             = $nameValue\n        def usage            = $usageValue\n        override def usageMd =\n          Some($usageMdValue).filter(_.nonEmpty).getOrElse(usage)\n        def description            = $descriptionValue\n        override def descriptionMd =\n          Some($descriptionMdValue).filter(_.nonEmpty).getOrElse(description)\n        override def examples = ${ Expr.ofList(examplesValue) }\n\n        def scalaSpecificationLevel = $levelValue\n\n        lazy val keys = $keysValue\n          .map { nameAliases =>\n            val allAliases = nameAliases.flatMap(key =>\n              List(\n                key,\n                DirectiveHandler.pascalCaseSplit(key.toCharArray.toList)\n                  .map(_.toLowerCase(Locale.ROOT))\n                  .mkString(\"-\")\n              )\n            ).distinct\n            Key(allAliases)\n          }\n\n        def handleValues(scopedDirective: ScopedDirective, logger: Logger) =\n          ${ handleValuesImpl('scopedDirective, 'logger) }\n      }\n    }\n  }\n\n  inline given derive[T]: DirectiveHandler[T] =\n    DirectiveHandler.deriveParser[T]\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectivePrefix.scala",
    "content": "package scala.build.directives\n\nimport scala.annotation.StaticAnnotation\n\nfinal case class DirectivePrefix(prefix: String)\n    extends StaticAnnotation\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveUtil.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport com.virtuslab.using_directives.custom.model.{BooleanValue, StringValue, Value}\nimport com.virtuslab.using_directives.custom.utils.ast.StringLiteral\nimport dependency.AnyDependency\nimport dependency.parser.DependencyParser\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException, DependencyFormatError}\nimport scala.build.preprocessing.ScopePath\nimport scala.build.{Position, Positioned}\n\nobject DirectiveUtil {\n  def isWrappedInDoubleQuotes(v: Value[?]): Boolean =\n    v match {\n      case stringValue: StringValue =>\n        stringValue.getRelatedASTNode match {\n          case literal: StringLiteral => literal.getIsWrappedDoubleQuotes()\n          case _                      => false\n        }\n      case _ => false\n    }\n  def position(v: Value[?], path: Either[String, os.Path]): Position.File = {\n    val skipQuotes: Boolean = isWrappedInDoubleQuotes(v)\n    val line                = v.getRelatedASTNode.getPosition.getLine\n    val column              = v.getRelatedASTNode.getPosition.getColumn + (if (skipQuotes) 1 else 0)\n    val endLinePos          = column + v.toString.length\n    Position.File(path, (line, column), (line, endLinePos))\n  }\n\n  def scope(v: Value[?], cwd: ScopePath): Option[ScopePath] =\n    Option(v.getScope).map((p: String) => cwd / os.RelPath(p))\n\n  def concatAllValues(\n    scopedDirective: ScopedDirective\n  ): Seq[String] =\n    scopedDirective.directive.values.collect:\n      case v: StringValue  => v.get\n      case v: BooleanValue => v.get.toString\n\n  def positions(values: Seq[Value[?]], path: Either[String, os.Path]): Seq[Position] =\n    values.map { v =>\n      val line   = v.getRelatedASTNode.getPosition.getLine\n      val column = v.getRelatedASTNode.getPosition.getColumn\n      Position.File(path, (line, column), (line, column))\n    }\n\n  extension (deps: List[Positioned[String]]) {\n    def asDependencies: Either[BuildException, Seq[Positioned[AnyDependency]]] =\n      deps\n        .map { positionedDep =>\n          positionedDep.map { str =>\n            DependencyParser.parse(str).left.map { error =>\n              new DependencyFormatError(\n                str,\n                error,\n                positions = positionedDep.positions\n              )\n            }\n          }.eitherSequence\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Exclude.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.either\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Exclude sources\")\n@DirectiveExamples(\"//> using exclude utils.scala\")\n@DirectiveExamples(\"//> using exclude examples/* */resources/*\")\n@DirectiveExamples(\"//> using exclude *.sc\")\n@DirectiveUsage(\n  \"`//> using exclude `_pattern_ | `//> using exclude `_pattern_ _pattern_ …\",\n  \"\"\"`//> using exclude` _pattern_\n    |\n    |`//> using exclude` _pattern1_ _pattern2_ …\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Exclude sources from the project\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Exclude(exclude: List[Positioned[String]] = Nil) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n    BuildOptions(\n      internal = InternalOptions(\n        exclude = exclude\n      )\n    )\n  }\n}\n\nobject Exclude {\n  val handler: DirectiveHandler[Exclude] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/JavaHome.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, WrongJavaHomePathError}\nimport scala.build.options.{BuildOptions, JavaOptions}\nimport scala.cli.commands.SpecificationLevel\nimport scala.util.Try\n\n@DirectiveGroupName(\"Java home\")\n@DirectiveExamples(\"//> using javaHome /Users/Me/jdks/11\")\n@DirectiveUsage(\n  \"//> using javaHome _path_\",\n  \"`//> using javaHome` _path_\"\n)\n@DirectiveDescription(\"Sets Java home used to run your application or tests\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class JavaHome(\n  javaHome: DirectiveValueParser.WithScopePath[Option[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(None)\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n    javaHome.value match {\n      case None             => BuildOptions()\n      case Some(homePosStr) =>\n        val root = value(Directive.osRoot(javaHome.scopePath, homePosStr.positions.headOption))\n        val home = value {\n          homePosStr\n            .map { homeStr =>\n              Try(os.Path(homeStr, root)).toEither.left.map { ex =>\n                new WrongJavaHomePathError(homeStr, ex)\n              }\n            }\n            .eitherSequence\n        }\n        BuildOptions(\n          javaOptions = JavaOptions(\n            javaHomeOpt = Some(home)\n          )\n        )\n    }\n  }\n}\n\nobject JavaHome {\n  val handler: DirectiveHandler[JavaHome] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/JavaOptions.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{BuildOptions, JavaOpt, Scope, ShadowingSeq, WithBuildRequirements}\nimport scala.build.{Positioned, options}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Java options\")\n@DirectiveExamples(\"//> using javaOpt -Xmx2g -Dsomething=a\")\n@DirectiveExamples(\"//> using test.javaOpt -Dsomething=a\")\n@DirectiveUsage(\n  \"//> using javaOpt _options_\",\n  \"\"\"`//> using javaOpt` _options_\n    |`//> using javaOptions` _options_`\n    |\n    |`//> using test.javaOpt` _options_\n    |`//> using test.javaOptions` _options_`\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Add Java options which will be passed when running an application.\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class JavaOptions(\n  @DirectiveName(\"javaOpt\")\n  javaOptions: List[Positioned[String]] = Nil,\n  @DirectiveName(\"test.javaOptions\")\n  @DirectiveName(\"test.javaOpt\")\n  testJavaOptions: List[Positioned[String]] = Nil\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(\n    JavaOptions.buildOptions(javaOptions).map(_.withEmptyRequirements),\n    JavaOptions.buildOptions(testJavaOptions).map(_.withScopeRequirement(Scope.Test))\n  )\n}\n\nobject JavaOptions {\n  val handler: DirectiveHandler[JavaOptions] = DirectiveHandler.derive\n  def buildOptions(javaOptions: List[Positioned[String]]): Either[BuildException, BuildOptions] =\n    Right {\n      BuildOptions(javaOptions =\n        options.JavaOptions(javaOpts = ShadowingSeq.from(javaOptions.map(_.map(JavaOpt(_)))))\n      )\n    }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/JavaProps.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{BuildOptions, JavaOpt, Scope, ShadowingSeq, WithBuildRequirements}\nimport scala.build.{Positioned, options}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Java properties\")\n@DirectiveExamples(\"//> using javaProp foo1=bar foo2\")\n@DirectiveExamples(\"//> using test.javaProp foo3=bar foo4\")\n@DirectiveUsage(\n  \"//> using javaProp _key=val_\",\n  \"\"\"`//> using javaProp` _key=value_\n    |\n    |`//> using javaProp` _key_\n    |\n    |`//> using test.javaProp` _key=value_\n    |\n    |`//> using test.javaProp` _key_\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Add Java properties\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class JavaProps(\n  @DirectiveName(\"javaProp\")\n  javaProperty: List[Positioned[String]] = Nil,\n  @DirectiveName(\"test.javaProperty\")\n  @DirectiveName(\"test.javaProp\")\n  testJavaProperty: List[Positioned[String]] = Nil\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(\n    JavaProps.buildOptions(javaProperty).map(_.withEmptyRequirements),\n    JavaProps.buildOptions(testJavaProperty).map(_.withScopeRequirement(Scope.Test))\n  )\n}\n\nobject JavaProps {\n  val handler: DirectiveHandler[JavaProps] = DirectiveHandler.derive\n  def buildOptions(javaProperties: List[Positioned[String]]): Either[BuildException, BuildOptions] =\n    Right {\n      val javaOpts: Seq[Positioned[JavaOpt]] = javaProperties.map { positioned =>\n        positioned.map { v =>\n          v.split(\"=\") match {\n            case Array(k)    => JavaOpt(s\"-D$k\")\n            case Array(k, v) => JavaOpt(s\"-D$k=$v\")\n          }\n        }\n      }\n      BuildOptions(\n        javaOptions = options.JavaOptions(\n          javaOpts = ShadowingSeq.from(javaOpts)\n        )\n      )\n    }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/JavacOptions.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{BuildOptions, Scope, WithBuildRequirements}\nimport scala.build.{Positioned, options}\nimport scala.cli.commands.SpecificationLevel\n@DirectiveGroupName(\"Javac options\")\n@DirectiveExamples(\"//> using javacOpt -source 1.8 -target 1.8\")\n@DirectiveExamples(\"//> using test.javacOpt -source 1.8 -target 1.8\")\n@DirectiveUsage(\n  \"//> using javacOpt _options_\",\n  \"\"\"`//> using javacOpt` _options_\n    |\n    |`//> using javacOptions` _options_\n    |\n    |`//> using test.javacOpt` _options_\n    |\n    |`//> using test.javacOptions` _options_\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Add Javac options which will be passed when compiling sources.\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class JavacOptions(\n  @DirectiveName(\"javacOpt\")\n  javacOptions: List[Positioned[String]] = Nil,\n  @DirectiveName(\"test.javacOptions\")\n  @DirectiveName(\"test.javacOpt\")\n  testJavacOptions: List[Positioned[String]] = Nil\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(\n    JavacOptions.buildOptions(javacOptions).map(_.withEmptyRequirements),\n    JavacOptions.buildOptions(testJavacOptions).map(_.withScopeRequirement(Scope.Test))\n  )\n}\n\nobject JavacOptions {\n  val handler: DirectiveHandler[JavacOptions] = DirectiveHandler.derive\n  def buildOptions(javacOptions: List[Positioned[String]]): Either[BuildException, BuildOptions] =\n    Right(BuildOptions(javaOptions = options.JavaOptions(javacOptions = javacOptions)))\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Jvm.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildOptions\nimport scala.build.{Positioned, options}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"JVM version\")\n@DirectiveExamples(\"//> using jvm 11\")\n@DirectiveExamples(\"//> using jvm temurin:11\")\n@DirectiveExamples(\"//> using jvm graalvm:21\")\n@DirectiveUsage(\n  \"//> using jvm _value_\",\n  \"`//> using jvm` _value_\"\n)\n@DirectiveDescription(\n  \"Use a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. \" +\n    \"scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\"\n)\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Jvm(jvm: Option[Positioned[String]] = None) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = {\n    val buildOpt = BuildOptions(\n      javaOptions = options.JavaOptions(\n        jvmIdOpt = jvm\n      )\n    )\n    Right(buildOpt)\n  }\n}\n\nobject Jvm {\n  val handler: DirectiveHandler[Jvm] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/MainClass.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildOptions\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Main class\")\n@DirectiveExamples(\"//> using mainClass HelloWorld\")\n@DirectiveUsage(\n  \"//> using mainClass _main-class_\",\n  \"`//> using mainClass` _main-class_\"\n)\n@DirectiveDescription(\"Specify default main class\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class MainClass(mainClass: Option[String] = None) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    Right(BuildOptions(mainClass = mainClass))\n}\n\nobject MainClass {\n  val handler: DirectiveHandler[MainClass] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ObjectWrapper.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, ScriptOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveExamples(\"//> using objectWrapper\")\n@DirectiveUsage(\"//> using objectWrapper\", \"`//> using objectWrapper`\")\n@DirectiveDescription(\"Set the default code wrapper for scripts to object wrapper\")\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class ObjectWrapper(\n  @DirectiveName(\"object.wrapper\")\n  @DirectiveName(\"wrapper.object\")\n  objectWrapper: Boolean = false\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    val options = BuildOptions(scriptOptions =\n      ScriptOptions(forceObjectWrapper = Some(true))\n    )\n    Right(options)\n}\n\nobject ObjectWrapper {\n  val handler: DirectiveHandler[ObjectWrapper] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Packaging.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport dependency.parser.ModuleParser\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  MalformedInputError,\n  ModuleFormatError,\n  WrongDirectoryPathError\n}\nimport scala.build.options.*\nimport scala.build.options.packaging.{DockerOptions, NativeImageOptions}\nimport scala.cli.commands.SpecificationLevel\nimport scala.util.Try\n\n@DirectiveGroupName(\"Packaging\")\n@DirectivePrefix(\"packaging.\")\n@DirectiveExamples(\"//> using packaging.packageType assembly\")\n@DirectiveExamples(\"//> using packaging.output foo\")\n@DirectiveExamples(\"//> using packaging.provided org.apache.spark::spark-sql\")\n@DirectiveExamples(\"//> using packaging.graalvmArgs --no-fallback\")\n@DirectiveExamples(\"//> using packaging.dockerFrom openjdk:11\")\n@DirectiveExamples(\"//> using packaging.dockerImageTag 1.0.0\")\n@DirectiveExamples(\"//> using packaging.dockerImageRegistry virtuslab\")\n@DirectiveExamples(\"//> using packaging.dockerImageRepository scala-cli\")\n@DirectiveExamples(\"//> using packaging.dockerCmd sh\")\n@DirectiveExamples(\"//> using packaging.dockerCmd node\")\n@DirectiveExamples(\n  \"//> using packaging.dockerExtraDirectories path/to/directory1 path/to/directory2\"\n)\n@DirectiveExamples(\"//> using packaging.dockerExtraDirectory path/to/directory\")\n@DirectiveUsage(\n  \"\"\"using packaging.packageType [package type]\n    |using packaging.output [destination path]\n    |using packaging.provided [module]\n    |using packaging.graalvmArgs [args]\n    |using packaging.dockerFrom [base docker image]\n    |using packaging.dockerImageTag [image tag]\n    |using packaging.dockerImageRegistry [image registry]\n    |using packaging.dockerImageRepository [image repository]\n    |using packaging.dockerCmd [docker command]\n    |using packaging.dockerExtraDirectories [directories]\n    |\"\"\".stripMargin,\n  \"\"\"`//> using packaging.packageType` _package-type_\n    |\n    |`//> using packaging.output` _destination-path_\n    |\n    |`//> using packaging.provided` _module_\n    |\n    |`//> using packaging.graalvmArgs` _args_\n    |\n    |`//> using packaging.dockerFrom` _base-docker-image_\n    |\n    |`//> using packaging.dockerImageTag` _image-tag_\n    |\n    |`//> using packaging.dockerImageRegistry` _image-registry_\n    |\n    |`//> using packaging.dockerImageRepository` _image-repository_\n    |\n    |`//> using packaging.dockerCmd` _docker-command_\n    |\n    |`//> using packaging.dockerExtraDirectories` _directories_\n    |`//> using packaging.dockerExtraDirectory` _directory_\n    |\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Set parameters for packaging\")\n@DirectiveLevel(SpecificationLevel.RESTRICTED)\nfinal case class Packaging(\n  packageType: Option[Positioned[String]] = None,\n  output: Option[String] = None,\n  provided: List[Positioned[String]] = Nil,\n  graalvmArgs: List[Positioned[String]] = Nil,\n  dockerFrom: Option[String] = None,\n  dockerImageTag: Option[String] = None,\n  dockerImageRegistry: Option[String] = None,\n  dockerImageRepository: Option[String] = None,\n  dockerCmd: Option[String] = None,\n  @DirectiveName(\"dockerExtraDirectory\")\n  dockerExtraDirectories: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil)\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n    val maybePackageTypeOpt = packageType\n      .map { input =>\n        PackageType.parse(input.value).toRight {\n          new MalformedInputError(\n            \"package-type\",\n            input.value,\n            PackageType.mapping.map(_._1).mkString(\"|\"),\n            positions = input.positions\n          )\n        }\n      }\n      .sequence\n    val maybeOutput = output\n      .map { path =>\n        try Right(os.Path(path, os.pwd)) // !!!\n        catch {\n          case _: IllegalArgumentException =>\n            Left(???)\n        }\n      }\n      .sequence\n    val maybeProvided = provided\n      .map { input =>\n        ModuleParser.parse(input.value)\n          .left.map { err =>\n            new ModuleFormatError(input.value, err, positions = input.positions)\n          }\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n\n    val (packageTypeOpt, output0, provided0) = value {\n      (maybePackageTypeOpt, maybeOutput, maybeProvided)\n        .traverseN\n        .left.map(CompositeBuildException(_))\n    }\n\n    val cwd              = dockerExtraDirectories.scopePath\n    val extraDirectories = value {\n      dockerExtraDirectories\n        .value\n        .map { posPathStr =>\n          val eitherRootPathOrBuildException =\n            Directive.osRoot(cwd, posPathStr.positions.headOption)\n          eitherRootPathOrBuildException.flatMap { root =>\n            Try(os.Path(posPathStr.value, root))\n              .toEither\n              .left.map(new WrongDirectoryPathError(_))\n          }\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n    }\n\n    BuildOptions(\n      internal = InternalOptions(\n        keepResolution = provided0.nonEmpty || packageTypeOpt.contains(PackageType.Spark)\n      ),\n      notForBloopOptions = PostBuildOptions(\n        packageOptions = PackageOptions(\n          packageTypeOpt = packageTypeOpt,\n          output = output0.map(_.toString),\n          provided = provided0,\n          dockerOptions = DockerOptions(\n            from = dockerFrom,\n            imageRegistry = dockerImageRegistry,\n            imageRepository = dockerImageRepository,\n            imageTag = dockerImageTag,\n            cmd = dockerCmd,\n            extraDirectories = extraDirectories\n          ),\n          nativeImageOptions = NativeImageOptions(\n            graalvmArgs = graalvmArgs\n          )\n        )\n      )\n    )\n  }\n}\n\nobject Packaging {\n  val handler: DirectiveHandler[Packaging] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Platform.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.directives.*\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  MalformedPlatformError,\n  UnexpectedJvmPlatformVersionError\n}\nimport scala.build.options.{\n  BuildOptions,\n  ConfigMonoid,\n  ScalaJsOptions,\n  ScalaNativeOptions,\n  ScalaOptions\n}\nimport scala.build.{Positioned, options}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Platform\")\n@DirectiveExamples(\"//> using platform scala-js\")\n@DirectiveExamples(\"//> using platforms jvm scala-native\")\n@DirectiveUsage(\n  \"//> using platform (jvm|scala-js|js|scala-native|native)+\",\n  \"\"\"`//> using platform` (`jvm`|`scala-js`|`js`|`scala-native`|`native`)+\n    |\n    |`//> using platforms` (`jvm`|`scala-js`|`js`|`scala-native`|`native`)+\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Set the default platform to Scala.js or Scala Native\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Platform(\n  @DirectiveName(\"platform\")\n  platforms: List[Positioned[String]] = Nil\n) extends HasBuildOptions {\n\n  private def split(input: String): (String, Option[String]) = {\n    val idx = input.indexOf(':')\n    if (idx < 0) (input, None)\n    else (input.take(idx), Some(input.drop(idx + 1)))\n  }\n\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n\n    val allBuildOptions = value {\n      platforms\n        .map { input =>\n          val (pfStr, pfVerOpt) = split(input.value)\n          options.Platform.parse(options.Platform.normalize(pfStr)) match {\n            case None =>\n              Left(new MalformedPlatformError(pfStr, positions = input.positions))\n            case Some(pf) =>\n              (pf, pfVerOpt) match {\n                case (_, None) =>\n                  Right(\n                    BuildOptions(\n                      scalaOptions = ScalaOptions(\n                        platform = Some(input.map(_ => pf))\n                      )\n                    )\n                  )\n                case (options.Platform.JVM, Some(ver)) =>\n                  Left(new UnexpectedJvmPlatformVersionError(ver, input.positions))\n                case (options.Platform.JS, Some(ver)) =>\n                  Right(\n                    BuildOptions(\n                      scalaOptions = ScalaOptions(\n                        platform = Some(input.map(_ => pf))\n                      ),\n                      scalaJsOptions = ScalaJsOptions(\n                        version = Some(ver)\n                      )\n                    )\n                  )\n                case (options.Platform.Native, Some(ver)) =>\n                  Right(\n                    BuildOptions(\n                      scalaOptions = ScalaOptions(\n                        platform = Some(input.map(_ => pf))\n                      ),\n                      scalaNativeOptions = ScalaNativeOptions(\n                        version = Some(ver)\n                      )\n                    )\n                  )\n              }\n          }\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n    }\n\n    allBuildOptions.headOption.fold(BuildOptions()) { _ =>\n      val mergedBuildOptions = allBuildOptions.foldLeft(BuildOptions())(_.orElse(_))\n      val mainPlatformOpt    =\n        mergedBuildOptions.scalaOptions.platform.map(_.value) // shouldn't be empty…\n      val extraPlatforms = ConfigMonoid.sum {\n        allBuildOptions\n          .flatMap(_.scalaOptions.platform.toSeq)\n          .filter(p => !mainPlatformOpt.contains(p.value))\n          .map(p => Map(p.value -> p.map(_ => ())))\n      }\n      mergedBuildOptions.copy(\n        scalaOptions = mergedBuildOptions.scalaOptions.copy(\n          extraPlatforms = mergedBuildOptions.scalaOptions.extraPlatforms ++ extraPlatforms\n        )\n      )\n    }\n  }\n}\n\nobject Platform {\n  val handler: DirectiveHandler[Platform] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Plugin.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport dependency.AnyDependency\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, ScalaOptions}\nimport scala.build.preprocessing.directives.DirectiveUtil.*\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Compiler plugins\")\n@DirectiveExamples(\"//> using plugin org.typelevel:::kind-projector:0.13.4\")\n@DirectiveUsage(\n  \"//> using plugin org:name:ver | //> using plugins org:name:ver org2:name2:ver2\",\n  \"`using plugin` _org_`:`_name_`:`_ver_\"\n)\n@DirectiveDescription(\"Adds compiler plugins\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class Plugin(\n  @DirectiveName(\"plugins\")\n  plugin: List[Positioned[String]] = Nil\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n    val dependencies: Seq[Positioned[AnyDependency]] = value(plugin.asDependencies)\n    BuildOptions(scalaOptions = ScalaOptions(compilerPlugins = dependencies))\n  }\n}\n\nobject Plugin {\n  val handler: DirectiveHandler[Plugin] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ProcessedDirective.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.preprocessing.Scoped\n\nfinal case class ProcessedDirective[+T](global: Option[T], scoped: Seq[Scoped[T]]) {\n  def map[U](f: T => U): ProcessedDirective[U] =\n    ProcessedDirective(global.map(f), scoped.map(_.map(f)))\n  def mapE[U](f: T => Either[BuildException, U]): Either[BuildException, ProcessedDirective[U]] = {\n    val maybeGlobal = global.map(f) match {\n      case None           => Right(None)\n      case Some(Left(e))  => Left(e)\n      case Some(Right(u)) => Right(Some(u))\n    }\n    val maybeScoped = scoped.map(_.mapE(f)).sequence.left.map(CompositeBuildException(_))\n    (maybeGlobal, maybeScoped)\n      .traverseN\n      .left.map(CompositeBuildException(_))\n      .map {\n        case (global0, scoped0) =>\n          ProcessedDirective(global0, scoped0)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Publish.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, CompositeBuildException}\nimport scala.build.options.publish.{Developer, License, Vcs}\nimport scala.build.options.{BuildOptions, PostBuildOptions, PublishOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Publish\")\n@DirectivePrefix(\"publish.\")\n@DirectiveExamples(\"//> using publish.organization io.github.myself\")\n@DirectiveExamples(\"//> using publish.name my-library\")\n@DirectiveExamples(\"//> using publish.moduleName scala-cli_3\")\n@DirectiveExamples(\"//> using publish.version 0.1.1\")\n@DirectiveExamples(\"//> using publish.url https://github.com/VirtusLab/scala-cli\")\n@DirectiveExamples(\"//> using publish.license MIT\")\n@DirectiveExamples(\"//> using publish.vcs https://github.com/VirtusLab/scala-cli.git\")\n@DirectiveExamples(\"//> using publish.vcs github:VirtusLab/scala-cli\")\n@DirectiveExamples(\"//> using publish.description \\\"Lorem ipsum dolor sit amet\\\"\")\n@DirectiveExamples(\"//> using publish.developer alexme|Alex Me|https://alex.me\")\n@DirectiveExamples(\n  \"//> using publish.developers alexme|Alex Me|https://alex.me Gedochao|Gedo Chao|https://github.com/Gedochao\"\n)\n@DirectiveExamples(\"//> using publish.scalaVersionSuffix _2.13\")\n@DirectiveExamples(\"//> using publish.scalaVersionSuffix _3\")\n@DirectiveExamples(\"//> using publish.scalaPlatformSuffix _sjs1\")\n@DirectiveExamples(\"//> using publish.scalaPlatformSuffix _native0.4\")\n@DirectiveUsage(\n  \"//> using publish.[key] [value]\",\n  \"\"\"`//> using publish.organization` value\n    |\n    |`//> using publish.name` value\n    |\n    |`//> using publish.moduleName` value\n    |\n    |`//> using publish.version` value\n    |\n    |`//> using publish.url` value\n    |\n    |`//> using publish.license` value\n    |\n    |`//> using publish.vcs` value\n    |\n    |`//> using publish.scm` value\n    |\n    |`//> using publish.versionControl` value\n    |\n    |`//> using publish.description` value\n    |\n    |`//> using publish.developer` value\n    |\n    |`//> using publish.developers` value1 value2\n    |\n    |`//> using publish.scalaVersionSuffix` value\n    |\n    |`//> using publish.scalaPlatformSuffix` value\n    |\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Set parameters for publishing\")\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class Publish(\n  organization: Option[Positioned[String]] = None,\n  name: Option[Positioned[String]] = None,\n  moduleName: Option[Positioned[String]] = None,\n  version: Option[Positioned[String]] = None,\n  url: Option[Positioned[String]] = None,\n  license: Option[Positioned[String]] = None,\n  @DirectiveName(\"scm\")\n  @DirectiveName(\"versionControl\")\n  vcs: Option[Positioned[String]] = None,\n  description: Option[String] = None,\n  @DirectiveName(\"developer\")\n  developers: List[Positioned[String]] = Nil,\n  scalaVersionSuffix: Option[String] = None,\n  scalaPlatformSuffix: Option[String] = None\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n\n    val maybeLicense = license\n      .map(License.parse)\n      .sequence\n    val maybeVcs = vcs\n      .map(Vcs.parse)\n      .sequence\n    val maybeDevelopers = developers\n      .map(Developer.parse)\n      .sequence\n      .left.map(CompositeBuildException(_))\n\n    val (licenseOpt, vcsOpt, developers0) = value {\n      (maybeLicense, maybeVcs, maybeDevelopers)\n        .traverseN\n        .left.map(CompositeBuildException(_))\n    }\n\n    val publishOptions = PublishOptions(\n      organization = organization,\n      name = name,\n      moduleName = moduleName,\n      version = version,\n      url = url,\n      license = licenseOpt,\n      versionControl = vcsOpt,\n      description = description,\n      developers = developers0,\n      scalaVersionSuffix = scalaVersionSuffix,\n      scalaPlatformSuffix = scalaPlatformSuffix\n    )\n\n    BuildOptions(\n      notForBloopOptions = PostBuildOptions(\n        publishOptions = publishOptions\n      )\n    )\n  }\n}\n\nobject Publish {\n  val handler: DirectiveHandler[Publish] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/PublishContextual.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, CompositeBuildException, MalformedInputError}\nimport scala.build.options.*\nimport scala.build.options.publish.ConfigPasswordOption\nimport scala.cli.commands.SpecificationLevel\nimport scala.cli.signing.shared.PasswordOption\n\ntrait PublishContextual {\n  def computeVersion: Option[Positioned[String]]\n  def repository: Option[String]\n  def gpgKey: Option[String]\n  def gpgOptions: List[String]\n  def secretKey: Option[Positioned[String]]\n  def secretKeyPassword: Option[Positioned[String]]\n  def publicKey: Option[Positioned[String]]\n  def user: Option[Positioned[String]]\n  def password: Option[Positioned[String]]\n  def realm: Option[String]\n  def doc: Option[Boolean]\n\n  def buildOptions(isCi: Boolean): Either[BuildException, BuildOptions] = either {\n\n    val maybeComputeVersion = computeVersion\n      .map(ComputeVersion.parse)\n      .sequence\n\n    val maybeSecretKey = secretKey\n      .map { input =>\n        PublishContextual.parsePasswordOption(input)\n          .map(ConfigPasswordOption.ActualOption(_))\n      }\n      .sequence\n    val maybeSecretKeyPassword = secretKeyPassword\n      .map { input =>\n        PublishContextual.parsePasswordOption(input)\n          .map(ConfigPasswordOption.ActualOption(_))\n      }\n      .sequence\n    val maybePublicKey = publicKey\n      .map { input =>\n        PublishContextual.parsePasswordOption(input)\n          .map(ConfigPasswordOption.ActualOption(_))\n      }\n      .sequence\n\n    val maybeUser = user\n      .map(PublishContextual.parsePasswordOption)\n      .sequence\n    val maybePassword = password\n      .map(PublishContextual.parsePasswordOption)\n      .sequence\n\n    val (\n      computeVersionOpt,\n      secretKeyOpt,\n      secretKeyPasswordOpt,\n      publicKeyOpt,\n      userOpt,\n      passwordOpt\n    ) = value {\n      (\n        maybeComputeVersion,\n        maybeSecretKey,\n        maybeSecretKeyPassword,\n        maybePublicKey,\n        maybeUser,\n        maybePassword\n      )\n        .traverseN\n        .left.map(CompositeBuildException(_))\n    }\n\n    val publishContextualOptions = PublishContextualOptions(\n      computeVersion = computeVersionOpt,\n      repository = repository,\n      docJar = doc,\n      gpgSignatureId = gpgKey,\n      gpgOptions = gpgOptions,\n      secretKey = secretKeyOpt,\n      secretKeyPassword = secretKeyPasswordOpt,\n      publicKey = publicKeyOpt,\n      repoUser = userOpt,\n      repoPassword = passwordOpt,\n      repoRealm = realm\n    )\n\n    val publishOptions =\n      if (isCi) PublishOptions(ci = publishContextualOptions)\n      else PublishOptions(local = publishContextualOptions)\n\n    BuildOptions(\n      notForBloopOptions = PostBuildOptions(\n        publishOptions = publishOptions\n      )\n    )\n  }\n}\n\nobject PublishContextual {\n\n  @DirectiveGroupName(\"Publish (contextual)\")\n  @DirectivePrefix(\"publish.\")\n  @DirectiveExamples(\"//> using publish.computeVersion git:tag\")\n  @DirectiveExamples(\"//> using publish.repository central-s01\")\n  @DirectiveExamples(\"//> using publish.secretKey env:PUBLISH_SECRET_KEY\")\n  @DirectiveExamples(\"//> using publish.doc false\")\n  @DirectiveUsage(\n    \"//> using publish.(computeVersion|repository|secretKey|…) [value]\",\n    \"\"\"`//> using publish.computeVersion` value\n      |\n      |`//> using publish.repository` value\n      |\n      |`//> using publish.secretKey` value\n      |\n      |`//> using publish.doc` boolean\n      |\n      |\"\"\".stripMargin\n  )\n  @DirectiveDescription(\"Set contextual parameters for publishing\")\n  @DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\n  // format: off\n  final case class Local(\n    computeVersion: Option[Positioned[String]] = None,\n    repository: Option[String] = None,\n    gpgKey: Option[String] = None,\n    gpgOptions: List[String] = Nil,\n    secretKey: Option[Positioned[String]] = None,\n    secretKeyPassword: Option[Positioned[String]] = None,\n    publicKey: Option[Positioned[String]] = None,\n    user: Option[Positioned[String]] = None,\n    password: Option[Positioned[String]] = None,\n    realm: Option[String] = None,\n    doc: Option[Boolean] = None\n  ) extends HasBuildOptions with PublishContextual {\n    // format: on\n    def buildOptions: Either[BuildException, BuildOptions] =\n      buildOptions(isCi = false)\n  }\n\n  object Local {\n    val handler: DirectiveHandler[Local] = DirectiveHandler.derive\n  }\n\n  @DirectiveGroupName(\"Publish (CI)\")\n  @DirectivePrefix(\"publish.ci.\")\n  @DirectiveExamples(\"//> using publish.ci.computeVersion git:tag\")\n  @DirectiveExamples(\"//> using publish.ci.repository central-s01\")\n  @DirectiveExamples(\"//> using publish.ci.secretKey env:PUBLISH_SECRET_KEY\")\n  @DirectiveUsage(\n    \"//> using publish.[.ci](computeVersion|repository|secretKey|…) [value]\",\n    \"\"\"`//> using publish.ci.computeVersion` value\n      |\n      |`//> using publish.ci.repository` value\n      |\n      |`//> using publish.ci.secretKey` value\n      |\n      |\"\"\".stripMargin\n  )\n  @DirectiveDescription(\"Set CI parameters for publishing\")\n  @DirectiveLevel(SpecificationLevel.RESTRICTED)\n  // format: off\n  final case class CI(\n    computeVersion: Option[Positioned[String]] = None,\n    repository: Option[String] = None,\n    gpgKey: Option[String] = None,\n    gpgOptions: List[String] = Nil,\n    secretKey: Option[Positioned[String]] = None,\n    secretKeyPassword: Option[Positioned[String]] = None,\n    publicKey: Option[Positioned[String]] = None,\n    user: Option[Positioned[String]] = None,\n    password: Option[Positioned[String]] = None,\n    realm: Option[String] = None,\n    doc: Option[Boolean] = None\n  ) extends HasBuildOptions with PublishContextual {\n    // format: on\n    def buildOptions: Either[BuildException, BuildOptions] =\n      buildOptions(isCi = true)\n  }\n\n  object CI {\n    val handler: DirectiveHandler[CI] = DirectiveHandler.derive\n  }\n\n  private def parsePasswordOption(input: Positioned[String])\n    : Either[BuildException, PasswordOption] =\n    PasswordOption.parse(input.value).left.map { _ =>\n      new MalformedInputError(\n        \"secret\",\n        input.value,\n        \"file:_path_|value:_value_|env:_env_var_name_\",\n        positions = input.positions\n      )\n    }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Python.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, PostBuildOptions, ScalaNativeOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveExamples(\"//> using python\")\n@DirectiveUsage(\"//> using python\", \"`//> using python`\")\n@DirectiveDescription(\"Enable Python support\")\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class Python(\n  python: Boolean = false\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = {\n    val options = BuildOptions(\n      notForBloopOptions = PostBuildOptions(\n        python = Some(true)\n      ),\n      scalaNativeOptions = ScalaNativeOptions(\n        maxDefaultNativeVersions =\n          List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)\n      )\n    )\n    Right(options)\n  }\n}\n\nobject Python {\n  val handler: DirectiveHandler[Python] = DirectiveHandler.derive\n  val maxScalaNativeWarningMsg          =\n    s\"ScalaPy does not support Scala Native ${Constants.scalaNativeVersion}, ${Constants.scalaPyMaxScalaNative} should be used instead.\"\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Repository.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, ClassPathOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Repository\")\n@DirectiveExamples(\"//> using repository jitpack\")\n@DirectiveExamples(\"//> using repository sonatype:snapshots\")\n@DirectiveExamples(\"//> using repository ivy2Local\")\n@DirectiveExamples(\"//> using repository m2Local\")\n@DirectiveExamples(\n  \"//> using repository https://maven-central.storage-download.googleapis.com/maven2\"\n)\n@DirectiveUsage(\n  \"//> using repository _repository_\",\n  \"`//> using repository` _repository_\"\n)\n@DirectiveDescription(Repository.usageMsg)\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Repository(\n  @DirectiveName(\"repository\")\n  repositories: List[String] = Nil\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = {\n    val buildOpt = BuildOptions(\n      classPathOptions = ClassPathOptions(\n        extraRepositories = repositories\n      )\n    )\n    Right(buildOpt)\n  }\n}\n\nobject Repository {\n  val handler: DirectiveHandler[Repository] = DirectiveHandler.derive\n\n  val usageMsg =\n    \"\"\"Add repositories for dependency resolution.\n      |\n      |Accepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/RequirePlatform.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, MalformedPlatformError}\nimport scala.build.options.{BuildRequirements, Platform}\nimport scala.build.{Positioned, options}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Platform\")\n@DirectivePrefix(\"target.\")\n@DirectiveDescription(\"Require a Scala platform for the current file\")\n@DirectiveExamples(\"//> using target.platform scala-js\")\n@DirectiveExamples(\"//> using target.platform scala-js scala-native\")\n@DirectiveExamples(\"//> using target.platform jvm\")\n@DirectiveUsage(\n  \"//> using target.platform _platform_\",\n  \"`//> using target.platform` _platform_\"\n)\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class RequirePlatform(\n  @DirectiveName(\"platform\")\n  platforms: List[Positioned[String]] = Nil\n) extends HasBuildRequirements {\n  def buildRequirements: Either[BuildException, BuildRequirements] = either {\n    val platformSet = value {\n      Platform.parseSpec(platforms.map(_.value).map(options.Platform.normalize)).toRight {\n        new MalformedPlatformError(\n          platforms.map(_.value).mkString(\", \"),\n          Positioned.sequence(platforms).positions\n        )\n      }\n    }\n    BuildRequirements(\n      platform = Seq(BuildRequirements.PlatformRequirement(platformSet))\n    )\n  }\n}\n\nobject RequirePlatform {\n  val handler: DirectiveHandler[RequirePlatform] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersion.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildRequirements\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Scala version\")\n@DirectivePrefix(\"target.\")\n@DirectiveDescription(\"Require a Scala version for the current file\")\n@DirectiveExamples(\"//> using target.scala 3\")\n@DirectiveUsage(\n  \"//> using target.scala _version_\",\n  \"`//> using target.scala` _version_\"\n)\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class RequireScalaVersion(\n  scala: Option[DirectiveValueParser.MaybeNumericalString] = None\n) extends HasBuildRequirements {\n  def buildRequirements: Either[BuildException, BuildRequirements] = {\n    val requirements = BuildRequirements(\n      scalaVersion = scala.toSeq.map { ns =>\n        BuildRequirements.VersionEquals(ns.value, loose = true)\n      }\n    )\n    Right(requirements)\n  }\n}\n\nobject RequireScalaVersion {\n  val handler: DirectiveHandler[RequireScalaVersion] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersionBounds.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.BuildRequirements\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Scala version bounds\")\n@DirectivePrefix(\"target.scala.\")\n@DirectiveDescription(\"Require a Scala version for the current file\")\n@DirectiveExamples(\"//> using target.scala.>= 2.13\")\n@DirectiveExamples(\"//> using target.scala.< 3.0.2\")\n@DirectiveUsage(\n  \"//> using target.scala.>= _version_\",\n  \"`//> using target.scala.>=` _version_\"\n)\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class RequireScalaVersionBounds(\n  `==`: Option[DirectiveValueParser.MaybeNumericalString] = None,\n  `>=`: Option[DirectiveValueParser.MaybeNumericalString] = None,\n  `<=`: Option[DirectiveValueParser.MaybeNumericalString] = None,\n  `>`: Option[DirectiveValueParser.MaybeNumericalString] = None,\n  `<`: Option[DirectiveValueParser.MaybeNumericalString] = None\n) extends HasBuildRequirements {\n  def buildRequirements: Either[BuildException, BuildRequirements] = {\n    val versionRequirements =\n      `==`.toSeq.map { ns =>\n        BuildRequirements.VersionEquals(ns.value, loose = true)\n      } ++\n        `>=`.toSeq.map { ns =>\n          BuildRequirements.VersionHigherThan(ns.value, orEqual = true)\n        } ++\n        `<=`.toSeq.map { ns =>\n          BuildRequirements.VersionLowerThan(ns.value, orEqual = true)\n        } ++\n        `>`.toSeq.map { ns =>\n          BuildRequirements.VersionHigherThan(ns.value, orEqual = false)\n        } ++\n        `<`.toSeq.map { ns =>\n          BuildRequirements.VersionLowerThan(ns.value, orEqual = false)\n        }\n    val requirements = BuildRequirements(\n      scalaVersion = versionRequirements\n    )\n    Right(requirements)\n  }\n}\n\nobject RequireScalaVersionBounds {\n  val handler: DirectiveHandler[RequireScalaVersionBounds] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScope.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, DirectiveErrors}\nimport scala.build.options.{BuildRequirements, Scope}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Scope\")\n@DirectivePrefix(\"target.\")\n@DirectiveDescription(\"Require a scope for the current file\")\n@DirectiveExamples(\"//> using target.scope test\")\n@DirectiveUsage(\n  \"//> using target.scope _scope_\",\n  \"`//> using target.scope` _scope_\"\n)\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class RequireScope(\n  scope: Option[Positioned[String]] = None\n) extends HasBuildRequirements {\n  def buildRequirements: Either[BuildException, BuildRequirements] = either {\n    val scopeOpt = value {\n      scope\n        .map { posStr =>\n          RequireScope.scopesByName.get(posStr.value).toRight {\n            new DirectiveErrors(::(\"No such scope\", Nil), posStr.positions)\n          }\n        }\n        .sequence\n    }\n    BuildRequirements(\n      scope = scopeOpt.map(BuildRequirements.ScopeRequirement(_))\n    )\n  }\n}\n\nobject RequireScope {\n  private lazy val scopesByName               = Scope.all.map(s => s.name -> s).toMap\n  val handler: DirectiveHandler[RequireScope] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Resources.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{BuildOptions, ClassPathOptions, Scope, WithBuildRequirements}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Resource directories\")\n@DirectiveExamples(\"//> using resourceDir ./resources\")\n@DirectiveExamples(\"//> using test.resourceDir ./resources\")\n@DirectiveUsage(\n  \"\"\"//> using resourceDir _path_\n    |\n    |//> using resourceDirs _path1_ _path2_ …\"\"\".stripMargin,\n  \"\"\"`//> using resourceDir` _path_\n    |\n    |`//> using resourceDirs` _path1_ _path2_ …\n    |\n    |`//> using test.resourceDir` _path_\n    |\n    |`//> using test.resourceDirs` _path1_ _path2_ …\n    |\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Manually add a resource directory to the class path\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Resources(\n  @DirectiveName(\"resourceDir\")\n  resourceDirs: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil),\n  @DirectiveName(\"test.resourceDir\")\n  @DirectiveName(\"test.resourceDirs\")\n  testResourceDirs: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil)\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(\n    Resources.buildOptions(resourceDirs).map(_.withEmptyRequirements),\n    Resources.buildOptions(testResourceDirs).map(_.withScopeRequirement(Scope.Test))\n  )\n}\n\nobject Resources {\n  val handler: DirectiveHandler[Resources] = DirectiveHandler.derive\n  def buildOptions(resourceDirs: DirectiveValueParser.WithScopePath[List[Positioned[String]]])\n    : Either[BuildException, BuildOptions] = Right {\n    val paths = resourceDirs.value.map(_.value)\n\n    val (virtualRootOpt, rootOpt) = Directive.osRootResource(resourceDirs.scopePath)\n\n    // TODO Return a BuildException for malformed paths\n\n    val paths0 = rootOpt\n      .toList\n      .flatMap { root =>\n        paths.map(os.Path(_, root))\n      }\n    val virtualPaths = virtualRootOpt.map { virtualRoot =>\n      paths.map(path => virtualRoot / os.SubPath(path))\n    }\n    // warnIfNotExistsPath(paths0, logger) // this should be reported elsewhere (more from BuildOptions)\n\n    BuildOptions(\n      classPathOptions = ClassPathOptions(\n        resourcesDir = paths0,\n        resourcesVirtualDir = virtualPaths.toList.flatten\n      )\n    )\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaJs.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport os.Path\n\nimport scala.build.Ops.EitherOptOps\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, ScalaJsMode, ScalaJsOptions}\nimport scala.cli.commands.SpecificationLevel\nimport scala.util.Try\n\n@DirectiveGroupName(\"Scala.js options\")\n@DirectiveExamples(s\"//> using jsVersion ${Constants.scalaJsVersion}\")\n@DirectiveExamples(\"//> using jsMode mode\")\n@DirectiveExamples(\"//> using jsNoOpt\")\n@DirectiveExamples(\"//> using jsModuleKind common\")\n@DirectiveExamples(\"//> using jsCheckIr\")\n@DirectiveExamples(\"//> using jsEmitSourceMaps\")\n@DirectiveExamples(\"//> using jsEsModuleImportMap importmap.json\")\n@DirectiveExamples(\"//> using jsSmallModuleForPackage test\")\n@DirectiveExamples(\"//> using jsDom\")\n@DirectiveExamples(\"//> using jsHeader \\\"#!/usr/bin/env node\\n\\\"\")\n@DirectiveExamples(\"//> using jsAllowBigIntsForLongs\")\n@DirectiveExamples(\"//> using jsAvoidClasses\")\n@DirectiveExamples(\"//> using jsAvoidLetsAndConsts\")\n@DirectiveExamples(\"//> using jsModuleSplitStyleStr smallestmodules\")\n@DirectiveExamples(\"//> using jsEsVersionStr es2017\")\n@DirectiveExamples(\"//> using jsEmitWasm\")\n@DirectiveUsage(\n  \"//> using jsVersion|jsMode|jsModuleKind|… _value_\",\n  \"\"\"\n    |`//> using jsVersion` _value_\n    |\n    |`//> using jsMode` _value_\n    |\n    |`//> using jsNoOpt` _true|false_\n    |\n    |`//> using jsNoOpt`\n    |\n    |`//> using jsModuleKind` _value_\n    |\n    |`//> using jsCheckIr` _true|false_\n    |\n    |`//> using jsCheckIr`\n    |\n    |`//> using jsEmitSourceMaps` _true|false_\n    |\n    |`//> using jsEmitSourceMaps`\n    |\n    |`//> using jsEsModuleImportMap` _value_\n    |\n    |`//> using jsSmallModuleForPackage` _value1_ _value2_ …\n    |\n    |`//> using jsDom` _true|false_\n    |\n    |`//> using jsDom`\n    |\n    |`//> using jsHeader` _value_\n    |\n    |`//> using jsAllowBigIntsForLongs` _true|false_\n    |\n    |`//> using jsAllowBigIntsForLongs`\n    |\n    |`//> using jsAvoidClasses` _true|false_\n    |\n    |`//> using jsAvoidClasses`\n    |\n    |`//> using jsAvoidLetsAndConsts` _true|false_\n    |\n    |`//> using jsAvoidLetsAndConsts`\n    |\n    |`//> using jsModuleSplitStyleStr` _value_\n    |\n    |`//> using jsEsVersionStr` _value_\n    |    \n    |`//> using jsEmitWasm` _true|false_\n    |\n    |`//> using jsEmitWasm`\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Add Scala.js options\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class ScalaJs(\n  jsVersion: Option[String] = None,\n  jsMode: Option[String] = None,\n  jsNoOpt: Option[Boolean] = None,\n  jsModuleKind: Option[String] = None,\n  jsCheckIr: Option[Boolean] = None,\n  jsEmitSourceMaps: Option[Boolean] = None,\n  jsEsModuleImportMap: Option[String] = None,\n  jsSmallModuleForPackage: List[String] = Nil,\n  jsDom: Option[Boolean] = None,\n  jsHeader: Option[String] = None,\n  jsAllowBigIntsForLongs: Option[Boolean] = None,\n  jsAvoidClasses: Option[Boolean] = None,\n  jsAvoidLetsAndConsts: Option[Boolean] = None,\n  jsModuleSplitStyleStr: Option[String] = None,\n  jsEsVersionStr: Option[String] = None,\n  jsEmitWasm: Option[Boolean] = None\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    val scalaJsOptions = ScalaJsOptions(\n      version = jsVersion,\n      mode = ScalaJsMode(jsMode),\n      moduleKindStr = jsModuleKind,\n      checkIr = jsCheckIr,\n      emitSourceMaps = jsEmitSourceMaps.getOrElse(ScalaJsOptions().emitSourceMaps),\n      smallModuleForPackage = jsSmallModuleForPackage,\n      dom = jsDom,\n      header = jsHeader,\n      allowBigIntsForLongs = jsAllowBigIntsForLongs,\n      avoidClasses = jsAvoidClasses,\n      avoidLetsAndConsts = jsAvoidLetsAndConsts,\n      moduleSplitStyleStr = jsModuleSplitStyleStr,\n      esVersionStr = jsEsVersionStr,\n      noOpt = jsNoOpt,\n      jsEmitWasm = jsEmitWasm.getOrElse(false)\n    )\n\n    def absFilePath(pathStr: String): Either[ImportMapNotFound, Path] =\n      Try(os.Path(pathStr, os.pwd)).toEither.fold(\n        ex =>\n          Left(ImportMapNotFound(\n            s\"\"\"Invalid path to EsImportMap. Please check your \"using jsEsModuleImportMap xxxx\" directive. Does this file exist $pathStr ?\"\"\",\n            ex\n          )),\n        path =>\n          os.isFile(path) && os.exists(path) match {\n            case false => Left(ImportMapNotFound(\n                s\"\"\"Invalid path to EsImportMap. Please check your \"using jsEsModuleImportMap xxxx\" directive. Does this file exist $pathStr ?\"\"\",\n                null\n              ))\n            case true => Right(path)\n          }\n      )\n    val jsImportMapAsPath = jsEsModuleImportMap.map(absFilePath).sequence\n    jsImportMapAsPath.map(_ match\n      case None            => BuildOptions(scalaJsOptions = scalaJsOptions)\n      case Some(importmap) =>\n        BuildOptions(\n          scalaJsOptions = scalaJsOptions.copy(remapEsModuleImportMap = Some(importmap))\n        ))\n}\n\nclass ImportMapNotFound(message: String, cause: Throwable)\n    extends BuildException(message, cause = cause)\n\nobject ScalaJs {\n  val handler: DirectiveHandler[ScalaJs] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaNative.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.options.{BuildOptions, ScalaNativeOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Scala Native options\")\n@DirectiveExamples(s\"//> using nativeGc immix\")\n@DirectiveExamples(s\"//> using nativeMode debug\")\n@DirectiveExamples(s\"//> using nativeLto full\")\n@DirectiveExamples(s\"//> using nativeVersion ${Constants.scalaNativeVersion}\")\n@DirectiveExamples(s\"//> using nativeCompile -flto=thin\")\n@DirectiveExamples(s\"//> using nativeCCompile -std=c17\")\n@DirectiveExamples(s\"//> using nativeCppCompile -std=c++17 -fcxx-exceptions\")\n@DirectiveExamples(s\"//> using nativeLinking -flto=thin\")\n@DirectiveExamples(s\"//> using nativeClang ./clang\")\n@DirectiveExamples(s\"//> using nativeClangPP ./clang++\")\n@DirectiveExamples(s\"//> using nativeEmbedResources\")\n@DirectiveExamples(s\"//> using nativeEmbedResources true\")\n@DirectiveExamples(s\"//> using nativeTarget library-dynamic\")\n@DirectiveExamples(s\"//> using nativeMultithreading\")\n@DirectiveExamples(s\"//> using nativeMultithreading false\")\n@DirectiveUsage(\n  \"//> using nativeGc _value_ | using native-version _value_\",\n  \"\"\"`//> using nativeGc` **immix**_|commix|boehm|none_\n    |\n    |`//> using nativeMode` **debug**_|release-fast|release-size|release-full_\n    |\n    |`//> using nativeLto` **none**_|full|thin_\n    |\n    |`//> using nativeVersion` _value_\n    |\n    |`//> using nativeCompile` _value1_ _value2_ …\n    |\n    |`//> using nativeCCompile` _value1_ _value2_ …\n    |\n    |`//> using nativeCppCompile` _value1_ _value2_ …\n    |\n    |`//> using nativeLinking` _value1_ _value2_ …\n    |\n    |`//> using nativeClang` _value_\n    |\n    |`//> using nativeClangPP` _value_\n    |\n    |`//> using nativeClangPp` _value_\n    |\n    |`//> using nativeEmbedResources` _true|false_\n    |\n    |`//> using nativeEmbedResources`\n    |\n    |`//> using nativeTarget` _application|library-dynamic|library-static_\n    |\n    |`//> using nativeMultithreading` _true|false_\n    |\n    |`//> using nativeMultithreading`\n    \"\"\".stripMargin.trim\n)\n@DirectiveDescription(\"Add Scala Native options\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class ScalaNative(\n  nativeGc: Option[String] = None,\n  nativeMode: Option[String] = None,\n  nativeLto: Option[String] = None,\n  nativeVersion: Option[String] = None,\n  nativeCompile: List[String] = Nil,\n  nativeCCompile: List[String] = Nil,\n  nativeCppCompile: List[String] = Nil,\n  nativeLinking: List[String] = Nil,\n  nativeClang: Option[String] = None,\n  @DirectiveName(\"nativeClangPp\")\n  nativeClangPP: Option[String] = None,\n  nativeEmbedResources: Option[Boolean] = None,\n  nativeTarget: Option[String] = None,\n  nativeMultithreading: Option[Boolean] = None\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = {\n    val nativeOptions = ScalaNativeOptions(\n      gcStr = nativeGc,\n      modeStr = nativeMode,\n      ltoStr = nativeLto,\n      version = nativeVersion,\n      compileOptions = nativeCompile,\n      cCompileOptions = nativeCCompile,\n      cppCompileOptions = nativeCppCompile,\n      linkingOptions = nativeLinking,\n      clang = nativeClang,\n      clangpp = nativeClangPP,\n      embedResources = nativeEmbedResources,\n      buildTargetStr = nativeTarget,\n      multithreading = nativeMultithreading\n    )\n    val buildOpt = BuildOptions(scalaNativeOptions = nativeOptions)\n    Right(buildOpt)\n  }\n}\n\nobject ScalaNative {\n  val handler: DirectiveHandler[ScalaNative] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalaVersion.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, MaybeScalaVersion, ScalaOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Scala version\")\n@DirectiveExamples(\"//> using scala 3.0.2\")\n@DirectiveExamples(\"//> using scala 2.13\")\n@DirectiveExamples(\"//> using scala 2\")\n@DirectiveExamples(\"//> using scala 2.13.6 2.12.16\")\n@DirectiveUsage(\n  \"//> using scala _version_+\",\n  \"`//> using scala` _version_+\"\n)\n@DirectiveDescription(\"Set the default Scala version\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class ScalaVersion(\n  scala: List[DirectiveValueParser.MaybeNumericalString] = Nil\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    scala match {\n      case Nil             => Right(BuildOptions())\n      case first :: others =>\n        val buildOpt = BuildOptions(\n          scalaOptions = ScalaOptions(\n            scalaVersion = Some(MaybeScalaVersion(first.value)),\n            extraScalaVersions = others.map(_.value).toSet\n          )\n        )\n        Right(buildOpt)\n    }\n}\n\nobject ScalaVersion {\n  val handler: DirectiveHandler[ScalaVersion] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ScalacOptions.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.WithBuildRequirements.*\nimport scala.build.options.{\n  BuildOptions,\n  ScalaOptions,\n  ScalacOpt,\n  Scope,\n  ShadowingSeq,\n  WithBuildRequirements\n}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Compiler options\")\n@DirectiveExamples(\"//> using option -Xasync\")\n@DirectiveExamples(\"//> using options -Xasync -Xfatal-warnings\")\n@DirectiveExamples(\"//> using test.option -Xasync\")\n@DirectiveExamples(\"//> using test.options -Xasync -Xfatal-warnings\")\n@DirectiveUsage(\n  \"using option _option_ | using options _option1_ _option2_ …\",\n  \"\"\"`//> using scalacOption` _option_\n    |\n    |`//> using option` _option_\n    |\n    |`//> using scalacOptions` _option1_ _option2_ …\n    |\n    |`//> using options` _option1_ _option2_ …\n    |\n    |`//> using test.scalacOption` _option_\n    |\n    |`//> using test.option` _option_\n    |\n    |`//> using test.scalacOptions` _option1_ _option2_ …\n    |\n    |`//> using test.options` _option1_ _option2_ …\n    |\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Add Scala compiler options\")\n@DirectiveLevel(SpecificationLevel.MUST)\nfinal case class ScalacOptions(\n  @DirectiveName(\"option\")\n  @DirectiveName(\"scalacOption\")\n  @DirectiveName(\"scalacOptions\")\n  options: List[Positioned[String]] = Nil,\n  @DirectiveName(\"test.option\")\n  @DirectiveName(\"test.options\")\n  @DirectiveName(\"test.scalacOption\")\n  @DirectiveName(\"test.scalacOptions\")\n  testOptions: List[Positioned[String]] = Nil\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(\n    ScalacOptions.buildOptions(options).map(_.withEmptyRequirements),\n    ScalacOptions.buildOptions(testOptions).map(_.withScopeRequirement(Scope.Test))\n  )\n}\n\nobject ScalacOptions {\n  val handler: DirectiveHandler[ScalacOptions] = DirectiveHandler.derive\n  def buildOptions(options: List[Positioned[String]]): Either[BuildException, BuildOptions] =\n    Right {\n      BuildOptions(\n        scalaOptions = ScalaOptions(\n          scalacOptions = ShadowingSeq.from(options.map(_.map(ScalacOpt(_))))\n        )\n      )\n    }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/ScopedDirective.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Position\nimport scala.build.errors.UnusedDirectiveError\nimport scala.build.preprocessing.ScopePath\n\ncase class ScopedDirective(\n  directive: StrictDirective,\n  maybePath: Either[String, os.Path],\n  cwd: ScopePath\n) {\n  def unusedDirectiveError: UnusedDirectiveError = {\n    val values = DirectiveUtil.concatAllValues(this)\n    val keyPos = Position.File(\n      maybePath,\n      (directive.startLine, directive.startColumn),\n      (directive.startLine, directive.startColumn + directive.key.length())\n    )\n    new UnusedDirectiveError(\n      directive.key,\n      values,\n      keyPos\n    )\n  }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Sources.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.{BuildException, CompositeBuildException, WrongSourcePathError}\nimport scala.build.options.{BuildOptions, InternalOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Custom sources\")\n@DirectiveExamples(\"//> using file utils.scala\")\n@DirectiveExamples(\n  \"//> using file https://raw.githubusercontent.com/softwaremill/sttp/refs/heads/master/examples/src/main/scala/sttp/client4/examples/json/GetAndParseJsonCatsEffectCirce.scala\"\n)\n@DirectiveUsage(\n  \"`//> using file `_path_ | `//> using files `_path1_ _path2_ …\",\n  \"\"\"`//> using file` _path_\n    |\n    |`//> using files` _path1_ _path2_ …\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\n  \"Manually add sources to the project. Does not support chaining, sources are added only once, not recursively.\"\n)\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Sources(\n  @DirectiveName(\"file\")\n  files: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil)\n) extends HasBuildOptions {\n\n  private def codeFile(codeFile: String, root: os.Path): Sources.CodeFile =\n    scala.util.Try {\n      val uri = java.net.URI.create(codeFile)\n      uri.getScheme match {\n        case \"file\" | \"http\" | \"https\" => uri\n      }\n    }.getOrElse {\n      os.Path(codeFile, root)\n    }\n\n  def buildOptions: Either[BuildException, BuildOptions] = either {\n\n    val paths = files\n      .value\n      .map { positioned =>\n        for {\n          root <- Directive.osRoot(files.scopePath, positioned.positions.headOption)\n          path <- {\n            try Right(positioned.map(codeFile(_, root)))\n            catch {\n              case e: IllegalArgumentException =>\n                Left(new WrongSourcePathError(positioned.value, e, positioned.positions))\n            }\n          }\n        } yield path\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n\n    BuildOptions(\n      internal = InternalOptions(\n        extraSourceFiles = value(paths)\n      )\n    )\n  }\n}\n\nobject Sources {\n\n  type CodeFile = os.Path | java.net.URI\n\n  val handler: DirectiveHandler[Sources] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/StrictDirective.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport com.virtuslab.using_directives.custom.model.{EmptyValue, Value}\n\nimport scala.build.Position\n\n/** Represents a directive with a key and a sequence of values.\n  *\n  * @param key\n  *   the key of the directive\n  * @param values\n  *   the sequence of values of the directive\n  * @param startColumn\n  *   the column where the key of the directive starts\n  */\n\ncase class StrictDirective(\n  key: String,\n  values: Seq[Value[?]],\n  startColumn: Int = 0,\n  startLine: Int = 0\n) {\n  override def toString: String = {\n    val suffix = if validValues.isEmpty then \"\" else s\" ${validValues.mkString(\"  \")}\"\n    s\"//> using $key$suffix\"\n  }\n\n  private def validValues = values.filter {\n    case _: EmptyValue => false\n    case _             => true\n  }\n\n  /** Checks whether the directive with the sequence of values will fit into the given column limit,\n    * if it does then the function returns the single directive with all the values. If the\n    * directive does not fit then the function explodes it into a sequence of directives with\n    * distinct values, each with a single value.\n    */\n  def explodeToStringsWithColLimit(colLimit: Int = 100): Seq[String] = {\n    val validValues = values.filter {\n      case _: EmptyValue => false\n      case _             => true\n    }\n\n    val usingKeyString = s\"//> using $key\"\n\n    if (validValues.isEmpty)\n      Seq(usingKeyString)\n    else {\n      val distinctValuesStrings = validValues\n        .map {\n          case s if s.toString.exists(_.isWhitespace) => s\"\\\"$s\\\"\"\n          case s                                      => s.toString\n        }\n        .distinct\n        .sorted\n\n      if (distinctValuesStrings.map(_.length).sum + usingKeyString.length < colLimit)\n        Seq(s\"$usingKeyString ${distinctValuesStrings.mkString(\" \")}\")\n      else\n        distinctValuesStrings.map(v => s\"$usingKeyString $v\")\n    }\n  }\n\n  def stringValuesCount: Int = validValues.length\n\n  def toStringValues: Seq[String] = validValues.map(_.toString)\n\n  def position(path: Either[String, os.Path]): Position.File =\n    values.lastOption\n      .map { v =>\n        val position = DirectiveUtil.position(v, path)\n        v match\n          case _: EmptyValue                                 => position.startPos\n          case v if DirectiveUtil.isWrappedInDoubleQuotes(v) =>\n            position.endPos._1 -> (position.endPos._2 + 1)\n          case _ => position.endPos\n      }.map { (line, endColumn) =>\n        Position.File(\n          path,\n          (line, startColumn),\n          (line, endColumn)\n        )\n      }.getOrElse(Position.File(path, (0, 0), (0, 0)))\n\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Tests.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, TestOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Test framework\")\n@DirectiveExamples(\"//> using testFramework utest.runner.Framework\")\n@DirectiveExamples(\"//> using test.frameworks utest.runner.Framework munit.Framework\")\n@DirectiveUsage(\n  \"\"\"using testFramework _class_name_\n    |\n    |using testFrameworks _class_name_ _another_class_name_\n    |\n    |using test.framework _class_name_\n    |\n    |using test.frameworks _class_name_ _another_class_name_\"\"\".stripMargin,\n  \"`//> using testFramework`  _class-name_\"\n)\n@DirectiveDescription(\"Set the test framework\")\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Tests(\n  @DirectiveName(\"testFramework\")\n  @DirectiveName(\"test.framework\")\n  @DirectiveName(\"test.frameworks\")\n  testFrameworks: Seq[Positioned[String]] = Nil\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] = {\n    val buildOpt = BuildOptions(\n      testOptions = TestOptions(\n        frameworks = testFrameworks\n      )\n    )\n    Right(buildOpt)\n  }\n}\n\nobject Tests {\n  val handler: DirectiveHandler[Tests] = DirectiveHandler.derive\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Toolkit.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport coursier.version.Version\nimport dependency.*\n\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.internal.Constants\nimport scala.build.options.*\nimport scala.build.options.WithBuildRequirements.*\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Toolkit\")\n@DirectiveExamples(s\"//> using toolkit ${Constants.toolkitDefaultVersion}\")\n@DirectiveExamples(\"//> using toolkit default\")\n@DirectiveExamples(\"//> using test.toolkit default\")\n@DirectiveUsage(\n  \"//> using toolkit _version_\",\n  \"\"\"`//> using toolkit` _version_\n    |\n    |`//> using test.toolkit` _version_\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\n  s\"Use a toolkit as dependency (not supported in Scala 2.12), 'default' version for Scala toolkit: ${Constants.toolkitDefaultVersion}, 'default' version for typelevel toolkit: ${Constants.typelevelToolkitDefaultVersion}\"\n)\n@DirectiveLevel(SpecificationLevel.SHOULD)\nfinal case class Toolkit(\n  toolkit: Option[Positioned[String]] = None,\n  @DirectiveName(\"test.toolkit\")\n  testToolkit: Option[Positioned[String]] = None\n) extends HasBuildOptionsWithRequirements {\n  def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] =\n    Toolkit.buildOptionsWithScopeRequirement(toolkit, defaultScope = None) ++\n      Toolkit.buildOptionsWithScopeRequirement(testToolkit, defaultScope = Some(Scope.Test))\n}\n\nobject Toolkit {\n  val typelevel = \"typelevel\"\n  val scala     = \"scala\"\n\n  def maxScalaNativeWarningMsg(\n    toolkitName: String,\n    toolkitVersion: String,\n    maxNative: String\n  ): String =\n    s\"$toolkitName $toolkitVersion does not support Scala Native ${Constants.scalaNativeVersion}, $maxNative should be used instead.\"\n\n  object TypelevelToolkit {\n    def unapply(s: Option[String]): Boolean =\n      s.contains(typelevel) || s.contains(Constants.typelevelOrganization)\n  }\n\n  object ScalaToolkit {\n    def unapply(s: Option[String]): Boolean =\n      s.isEmpty || s.contains(Constants.toolkitOrganization) || s.contains(scala)\n  }\n\n  case class ToolkitDefinitions(\n    isScalaToolkitDefault: Boolean = false,\n    scalaToolkitExplicitVersion: Option[String] = None,\n    isTypelevelToolkitDefault: Boolean = false,\n    typelevelToolkitExplicitVersion: Option[String] = None\n  )\n\n  /** @param toolkitCoords\n    *   the toolkit coordinates\n    * @return\n    *   the `toolkit` and `toolkit-test` dependencies with the appropriate build requirements\n    */\n  def resolveDependenciesWithRequirements(toolkitCoords: Positioned[String]): List[(\n    WithBuildRequirements[Positioned[DependencyLike[NameAttributes, NameAttributes]]],\n    ToolkitDefinitions\n  )] =\n    toolkitCoords match\n      case Positioned(positions, coords) =>\n        val tokens            = coords.split(':')\n        val rawVersion        = tokens.last\n        def isDefault         = rawVersion == \"default\"\n        val notDefaultVersion = if rawVersion == \"latest\" then \"latest.release\" else rawVersion\n        val flavor            = tokens.dropRight(1).headOption\n        val (org, v, trv: ToolkitDefinitions) = flavor match {\n          case TypelevelToolkit() =>\n            val typelevelToolkitVersion =\n              if isDefault then Constants.typelevelToolkitDefaultVersion\n              else notDefaultVersion\n            val explicitVersion =\n              if isDefault then None else Some(typelevelToolkitVersion)\n            (\n              Constants.typelevelOrganization,\n              typelevelToolkitVersion,\n              ToolkitDefinitions(\n                isTypelevelToolkitDefault = isDefault,\n                typelevelToolkitExplicitVersion = explicitVersion\n              )\n            )\n          case ScalaToolkit() | None =>\n            val scalaToolkitVersion =\n              if isDefault then Constants.toolkitDefaultVersion\n              else notDefaultVersion\n            val explicitVersion =\n              if isDefault then None else Some(scalaToolkitVersion)\n            (\n              Constants.toolkitOrganization,\n              scalaToolkitVersion,\n              ToolkitDefinitions(\n                isScalaToolkitDefault = isDefault,\n                scalaToolkitExplicitVersion = explicitVersion\n              )\n            )\n          case Some(org) => (org, notDefaultVersion, ToolkitDefinitions())\n        }\n        List(\n          Positioned(positions, dep\"$org::${Constants.toolkitName}::$v,toolkit\")\n            .withEmptyRequirements -> trv,\n          Positioned(positions, dep\"$org::${Constants.toolkitTestName}::$v,toolkit\")\n            .withScopeRequirement(Scope.Test) -> trv\n        )\n  val handler: DirectiveHandler[Toolkit] = DirectiveHandler.derive\n\n  /** Returns the `toolkit` (and potentially `toolkit-test`) dependency with the appropriate\n    * requirements.\n    *\n    * If [[defaultScope]] == None, it yields a List of up to 2 instances [[WithBuildRequirements]]\n    * of [[BuildOptions]], one with the `toolkit` dependency and no requirements, and one with the\n    * `toolkit-test` dependency and test scope requirements.\n    *\n    * If [[defaultScope]] == Some([[Scope.Test]]), then it yields a List with a single instance\n    * containing both dependencies and the test scope requirement.\n    *\n    * @param t\n    *   toolkit coordinates\n    * @param defaultScope\n    *   the scope requirement for the `toolkit` dependency\n    * @return\n    *   a list of [[Either]] [[BuildException]] [[WithBuildRequirements]] [[BuildOptions]]\n    *   containing the `toolkit` and `toolkit-test` dependencies.\n    */\n  private def buildOptionsWithScopeRequirement(\n    t: Option[Positioned[String]],\n    defaultScope: Option[Scope]\n  ): List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = t\n    .toList\n    .flatMap(resolveDependenciesWithRequirements) // resolve dependencies\n    .map {\n      case (\n            WithBuildRequirements(requirements, positionedDep),\n            ToolkitDefinitions(\n              isScalaToolkitDefault,\n              explicitScalaToolkitVersion,\n              isTypelevelToolkitDefault,\n              _\n            )\n          ) =>\n        val scalaToolkitMaxNativeVersions =\n          if isScalaToolkitDefault then\n            List(Constants.toolkitMaxScalaNative -> maxScalaNativeWarningMsg(\n              toolkitName = \"Scala Toolkit\",\n              toolkitVersion = Constants.toolkitDefaultVersion,\n              maxNative = Constants.toolkitMaxScalaNative\n            ))\n          else\n            explicitScalaToolkitVersion.toList\n              .map(Version(_))\n              .filter(_ <= Version(Constants.toolkitVersionForNative04))\n              .flatMap(v =>\n                List(Constants.scalaNativeVersion04 -> maxScalaNativeWarningMsg(\n                  toolkitName = \"Scala Toolkit\",\n                  toolkitVersion = v.toString(),\n                  Constants.scalaNativeVersion04\n                ))\n              )\n        val typelevelToolkitMaxNativeVersions =\n          if isTypelevelToolkitDefault then\n            List(Constants.typelevelToolkitMaxScalaNative -> maxScalaNativeWarningMsg(\n              toolkitName = \"TypeLevel Toolkit\",\n              toolkitVersion = Constants.typelevelToolkitDefaultVersion,\n              maxNative = Constants.typelevelToolkitMaxScalaNative\n            ))\n          else Nil\n        val maxNativeVersions =\n          (scalaToolkitMaxNativeVersions ++ typelevelToolkitMaxNativeVersions).distinct\n        positionedDep\n          .withBuildRequirements {\n            if requirements.scope.isEmpty then // if the scope is not set, set it to the default\n              requirements.copy(scope = defaultScope.map(_.asScopeRequirement))\n            else requirements\n          }\n          .map { dep =>\n            BuildOptions(\n              classPathOptions = ClassPathOptions(\n                extraDependencies = ShadowingSeq.from(List(dep))\n              ),\n              scalaNativeOptions = ScalaNativeOptions(\n                maxDefaultNativeVersions = maxNativeVersions\n              )\n            )\n          }\n    }\n    .groupBy(_.requirements.scope.map(_.scope))\n    .toList\n    .map { (scope: Option[Scope], boWithReqsList: List[WithBuildRequirements[BuildOptions]]) =>\n      Right {\n        boWithReqsList.foldLeft { // merge all the BuildOptions with the same scope requirement\n          scope\n            .map(s => BuildOptions.empty.withScopeRequirement(s))\n            .getOrElse(BuildOptions.empty.withEmptyRequirements)\n        } { (acc, boWithReqs) =>\n          acc.map(_.orElse(boWithReqs.value))\n        }\n      }\n    }\n}\n"
  },
  {
    "path": "modules/directives/src/main/scala/scala/build/preprocessing/directives/Watching.scala",
    "content": "package scala.build.preprocessing.directives\n\nimport scala.build.Positioned\nimport scala.build.directives.*\nimport scala.build.errors.BuildException\nimport scala.build.options.{BuildOptions, WatchOptions}\nimport scala.cli.commands.SpecificationLevel\n\n@DirectiveGroupName(\"Watch additional inputs\")\n@DirectiveExamples(\"//> using watching ./data\")\n@DirectiveUsage(\n  \"\"\"//> using watching _path_\n    |\n    |//> using watching _path1_ _path2_ …\"\"\".stripMargin,\n  \"\"\"`//> using watching` _path_\n    |\n    |`//> using watching` _path1_ _path2_ …\n    |\n    |\"\"\".stripMargin\n)\n@DirectiveDescription(\"Watch additional files or directories when using watch mode\")\n@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)\nfinal case class Watching(\n  watching: DirectiveValueParser.WithScopePath[List[Positioned[String]]] =\n    DirectiveValueParser.WithScopePath.empty(Nil)\n) extends HasBuildOptions {\n  def buildOptions: Either[BuildException, BuildOptions] =\n    Watching.buildOptions(watching)\n}\n\nobject Watching {\n  val handler: DirectiveHandler[Watching] = DirectiveHandler.derive\n\n  def buildOptions(\n    watching: DirectiveValueParser.WithScopePath[List[Positioned[String]]]\n  ): Either[BuildException, BuildOptions] = Right {\n    val paths         = watching.value.map(_.value)\n    val (_, rootOpt)  = Directive.osRootResource(watching.scopePath)\n    val resolvedPaths = rootOpt.toList.flatMap { root =>\n      paths.map(os.Path(_, root))\n    }\n    BuildOptions(\n      watchOptions = WatchOptions(\n        extraWatchPaths = resolvedPaths\n      )\n    )\n  }\n}\n"
  },
  {
    "path": "modules/docs-tests/README.md",
    "content": "# Sclicheck - simple tool to verify scala-cli cookbooks\n\nSclicheck `[sklicheck]` is a simple command line tool to verify documentation.\n\nIt uses regexes under the hood so in some cases we may not parse the file properly.\n\nSclicheck extracts commands from `.md` file and then run this commands as defined within a file using a single workspace. Commands are not run in isolation by design. The whole point of the tool is to maintain a state of the current example and modify / test it using commands.\n\nCurrently following commands are supported:\n - [Write code](#write-code) - stores snippet into a file\n - [Run code](#run-code) - runs provided commands as a bash script\n - [Check output](#check-output) - check if lates optput contains provided patterns\n - [Clear](#clear) - clears all file in workspace\n\nUsually, within a doc one want to first define a code, then run some commands (e.g. compilation) to test it and then check the output from last command (e.g. compilation).\n\nSclicheck accepts following arguments: `[--dest <dest>] [--step] [--stopAtFailure] <files>` where:\n - `<files>` - list of `.md` files or directories to check. In case of directories Sclicheck will recusievly check all `.md` files\n - `--dest <dest>`- Sclicheck after checking given file (`<name.md>`) store all of the generated sources as well as `<name.md>` (as `Readme.md`) file in `<dest>/<name>`. This is usefull to generate examples directly from documents\n - `--step` - stops after each command. Useful for debugging.\n - `--stopAtFailure` - stops after each failed command. Useful for debugging.\n\n## Running from the Scala CLI sources\n\nSclicheck can be run from the Scala CLI sources root with\n```text\n$ ./mill -i 'docs-tests[]'.run …args…\n```\n\n## Example\n\nLet consider this simple document we want to check:\n\n````md\n# Testing cat command\n\nCat command can print a content of a file. Let's start with simple file\n\n```md title=a.txt\nA text\n```\n\nLet's read it using `cat`:\n\n```bash\ncat a.txt\n```\n\n<!-- Expected:\nA text\n-->\n\n`cat` fails if file does not exists:\n\n```bash fail\ncat no_a_file\n```\n<!-- Expected:\nno_a_file\n-->\n````\n\nFor the example above Sclicheck will:\n - write a file `a.txt` with `A text` as content\n - runs `cat a.txt` and store output (`A text`)\n - check if a patten `A text` exisits in output from last command (`A text`)\n - runs `cat no_a_file` (expecting that command will fail)\n - check if a patten `no_a_file` exisits in output from last command (`ls: cannot access 'no_a_file': No such file or directory`)\n\n## Actions\n\n### Write code\n\nIt extracts code to file in workspace for all code snippets marked with ` ```<language> title=<file-name> `\n\nfor example:\n\n````\n```scala title=A.scala\ndef a = 123\n```\n````\n\nWill create (or override if exists) file `A.scala` with provided context. We support writing into subdirectories as well using `title=dir/file.ext`.\n\nSclicheck generates the sources for `.scala` and `.java` files in such a way that the lines with actual code matches the lines in provided .md files to make debugging easier.\n\n**Important!**\n\nCode block is ignored if any additional properties are passed to first line of a snippet.\n\nTo add a named code snippet that should be ignore provide any additional option like `ignore` for example:\n\n````\n```scala title=A.scala ignore\ndef a = 123\n```\n````\n\n### Running bash scripts\n\nIt will run code snippets starting with ` ```bash ` for example:\n\n```bash\nscala-cli clean\nscala-cli compile .\n```\n\nThe output from last command is stored for following [check output](#check-output) commands.\n\nWe turn such snippet into a bash script so example below becomes:\n\n```bash\n#!/usr/bin/env bash\n\nset -e\n\nscala-cli clean\nscala-cli compile .\n```\n\nSclicheck expect that script return 0 but when `fail` is provided it expects failure (return non-zero exit code). Example:\n\n````\n```bash fail\nls non_exisiting_dir\n```\n````\n\n**Important** Code block is ignored if any additional properties are passed to first line of a snippet.\n\nTo add a `bash` code snippet that should be ignored any additional option like `ignore` for example:\n\n````\n```bash ignore\nls non_exisiting_dir\n```\n````\n\n# Check output\n\nSclicheck can check the output latest run command\n\nFor that we use html comments starting with:\n    - `<!-- Expected-regex:` for regex pattern to match at least single line from last output\n    - `<!-- Expected` for pattern that needs to exists in at least one line from last output\n\nFollowed by lines containing patterns/regexes, for example:\n\n```\n<!-- Expected-regex:\nUsing Scala version: 2.*\nWith care\\.\n-->\n```\n\nSclicheck, for each provided pattern check if there is at least a one line in the output that contains the pattern (for non-regex) or matches the provided regex.\n\n## Clear\n\nIn some cases we want to start with fresh context. For such cases Sclicheck provides a Clear command. It is defined as single line html comment containing single word `clear`:\n\n`<!-- clear -->`"
  },
  {
    "path": "modules/docs-tests/src/main/scala/sclicheck/sclicheck.scala",
    "content": "package sclicheck\n\nimport fansi.Color.{Blue, Green, Red}\n\nimport java.io.File\nimport java.security.SecureRandom\n\nimport scala.annotation.tailrec\nimport scala.io.StdIn.readLine\nimport scala.util.Random\nimport scala.util.matching.Regex\n\nval SnippetBlock = \"\"\" *(`{2}`+)[^ ]+ title=([\\w\\d.\\-/_]+) *\"\"\".r\nval CompileBlock = \"\"\" *(`{2}`+) *(\\w+) +(compile|fail) *(?:title=([\\w\\d.\\-/_]+))? *(power)? *\"\"\".r\ndef compileBlockEnds(backticks: String) = s\"\"\" *$backticks *\"\"\".r\nval BashCommand                         = \"\"\" *```bash *(fail|run-fail)? *(clean)? *\"\"\".r\nval CheckBlock                          = \"\"\" *\\<\\!-- Expected(-regex)?: *\"\"\".r\nval CheckBlockEnd                       = \"\"\" *\\--> *\"\"\".r\nval Clear                               = \"\"\" *<!--+ *clear *-+-> *\"\"\".r\n\ncase class Options(\n  scalaCliCommand: Seq[String],\n  files: Seq[String] = Nil,\n  dest: Option[os.Path] = None,\n  stopAtFailure: Boolean = false,\n  statusFile: Option[os.Path] = None,\n  step: Boolean = false\n)\n\nenum Commands:\n  def context: Context\n  def name: String = toString.takeWhile(_ != '(')\n  def log: Any     = this match {\n    case _: Clear                  => \"\"\n    case Check(patterns, regex, _) =>\n      val kind = if regex then \"regexes\" else \"patterns\"\n      s\"last output matches $kind: ${patterns.map(p => s\"'$p'\").mkString(\", \")}\"\n    case Run(cmd, shouldFail, shouldClean, _) =>\n      val prefix = shouldFail -> shouldClean match\n        case (true, true)  => \"[failure expected, clean]\"\n        case (true, false) => \"[failure expected]\"\n        case (false, true) => \"[clean]\"\n        case _             => \"\"\n      cmd.mkString(prefix, \" \", \"\")\n    case Write(name, _, _) =>\n      name\n    case Compile(_, _, _, _, _) =>\n      \"compile snippet\"\n  }\n\n  case Write(fileName: String, lines: Seq[String], context: Context)\n\n  case Compile(\n    fileName: String,\n    lines: Seq[String],\n    context: Context,\n    shouldFail: Boolean,\n    power: Boolean\n  )\n  case Run(scriptLines: Seq[String], shouldFail: Boolean, shouldClean: Boolean, context: Context)\n  case Check(patterns: Seq[String], regex: Boolean, context: Context)\n  case Clear(context: Context)\n\ncase class Context(file: os.RelPath, line: Int):\n  def proceed(linesToSkip: Int = 1): Context = copy(line = line + linesToSkip)\n  override def toString                      = s\"$file:$line\"\n\ncase class FailedCheck(line: Int, file: os.RelPath, txt: String)\n    extends RuntimeException(s\"[$file:$line] $txt\")\n\ndef check(cond: Boolean, msg: => String)(using c: Context): Unit =\n  if !cond then throw FailedCheck(c.line, c.file, msg)\n\n@annotation.tailrec\ndef parse(content: Seq[String], currentCommands: Seq[Commands], context: Context): Seq[Commands] =\n  given Context = context\n\n  inline def parseMultiline(\n    lines: Seq[String],\n    newCommand: Seq[String] => Commands,\n    endMarker: Regex = compileBlockEnds(\"```\")\n  ) =\n    val codeLines = lines.takeWhile(l => !endMarker.matches(l))\n    check(codeLines.size > 0, \"Block cannot be empty!\")\n    check(codeLines.size < lines.size, \"Block should end!\")\n    parse(\n      content = lines.drop(codeLines.size + 1),\n      currentCommands = currentCommands :+ newCommand(codeLines),\n      context = context.proceed(codeLines.size + 2)\n    )\n\n  content match\n    case Nil => currentCommands\n\n    case SnippetBlock(backticks, name) :: tail =>\n      parseMultiline(tail, Commands.Write(name, _, context), compileBlockEnds(backticks))\n\n    case CompileBlock(backticks, name, status, fileName, power) :: tail =>\n      val fileSuffix = if name == \"markdown\" then \".md\" else s\".$name\"\n      val file       = Option(fileName).getOrElse(\"snippet_\" + Random.nextInt(1000) + fileSuffix)\n      parseMultiline(\n        tail,\n        Commands.Compile(file, _, context, status == \"fail\", power == \"power\"),\n        compileBlockEnds(backticks)\n      )\n\n    case BashCommand(failGroup, clean) :: tail =>\n      parseMultiline(tail, Commands.Run(_, failGroup != null, clean != null, context))\n\n    case CheckBlock(regexOpt) :: tail =>\n      val isRegex = regexOpt == \"-regex\"\n      parseMultiline(tail, Commands.Check(_, isRegex, context), CheckBlockEnd)\n\n    case Clear() :: rest =>\n      parse(rest, currentCommands :+ Commands.Clear(context), context.proceed())\n\n    case _ :: tail =>\n      parse(tail, currentCommands, context.proceed())\n\ncase class TestCase(path: os.RelPath, failure: Option[String])\n\ndef checkPath(options: Options)(path: os.Path): Seq[TestCase] =\n  try\n    if !os.isDir(path) then\n      if path.last.endsWith(\".md\") then\n        checkFile(path, options)\n        println(Green(s\"[${path.relativeTo(os.pwd)}] Completed.\"))\n        Seq(TestCase(path.relativeTo(os.pwd), None))\n      else Nil\n    else\n      val toCheck =\n        os.list(path).filterNot(_.last.startsWith(\".\"))\n      toCheck.toList.flatMap(checkPath(options))\n  catch\n    case e @ FailedCheck(_, _, _) =>\n      println(Red(e.getMessage))\n      Seq(TestCase(path.relativeTo(os.pwd), Some(e.getMessage)))\n    case e: Throwable =>\n      val short = s\"Unexpected exception ${e.getClass.getName}\"\n      println(Red(short))\n      e.printStackTrace()\n      Seq(TestCase(path.relativeTo(os.pwd), Some(s\"$short: ${e.getMessage}\")))\n\nval fakeLineMarker = \"//fakeline\"\n\ndef shouldAlignContent(file: String | os.Path) =\n  val isSourceFile = Seq(\".scala\", \".sc\", \".java\").exists(file.toString.endsWith)\n  !sys.env.contains(\"SCLICHECK_REMOVE_MARKERS\") && isSourceFile\n\ndef mkBashScript(content: Seq[String]) =\n  s\"\"\"#!/usr/bin/env bash\n     |\n     |set -e\n     |\n     |${content.mkString(\"\\n\")}\n     |\"\"\".stripMargin\n\ndef removeAnsiColors(str: String) = str.replaceAll(\"\\\\e\\\\[[0-9]+m\", \"\")\n\nprivate lazy val baseTmpDir = {\n  val random  = new SecureRandom\n  val dirName = s\"run-${math.abs(random.nextInt.toLong)}\"\n  val dir     = os.pwd / \"out\" / \"sclicheck\" / dirName\n  dir.toIO.deleteOnExit()\n  dir\n}\n\ndef checkFile(file: os.Path, options: Options): Unit =\n  val content  = os.read.lines(file).toList\n  val commands = parse(content, Vector(), Context(file.relativeTo(os.pwd), 1))\n  val destName = file.last.stripSuffix(\".md\")\n  val out      =\n    sys.env.get(\"SCLICHECK_DEST\") match\n      case None =>\n        val isCi = System.getenv(\"CI\") != null\n        if (isCi) {\n          val dir = baseTmpDir / destName\n          os.makeDir.all(dir)\n          dir\n        }\n        else\n          os.temp.dir(prefix = destName)\n      case Some(path) =>\n        val dest = os.Path(path)\n        println(s\"Cleaning dest directory $dest\")\n        os.remove.all(dest)\n        dest\n\n  // putting a custom scala-cli binary in the PATH, that in turn calls the Scala CLI launcher\n  // build from Mill, so that doc scripts run it rather than any user-installed scala-cli.\n  val binDir = {\n    val binDir0 = out / \".scala-cli\"\n    os.makeDir.all(binDir0)\n\n    def createHelperScript(command: Seq[String], scriptName: String): Unit = {\n      val escapedCommand = command\n        .map(arg => \"\\\"\" + arg.replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\")\n        .mkString(\" \")\n      val scriptCode =\n        s\"\"\"#!/usr/bin/env bash\n           |exec $escapedCommand \"$$@\"\n           |\"\"\".stripMargin\n      os.write(binDir0 / scriptName, scriptCode)\n      if !scala.util.Properties.isWin then\n        os.perms.set(binDir0 / scriptName, \"rwxr-xr-x\")\n    }\n    createHelperScript(options.scalaCliCommand, \"scala-cli\")\n    createHelperScript(options.scalaCliCommand, \"scala\")\n    val coursierCliDep =\n      s\"${Constants.coursierOrg}:${Constants.coursierCliModule}_3:${Constants.coursierCliVersion}\"\n    createHelperScript(\n      options.scalaCliCommand ++ Seq(\n        \"run\",\n        \"--dep\",\n        coursierCliDep,\n        \"--\",\n        \"launch\",\n        s\"scala:${Constants.scalaLegacyRunnerVersion}\",\n        \"-M\",\n        \"dotty.tools.MainGenericRunner\",\n        \"--\"\n      ),\n      \"scala_legacy\"\n    )\n    createHelperScript(\n      options.scalaCliCommand ++ Seq(\n        \"run\",\n        \"--dep\",\n        coursierCliDep,\n        \"--\",\n        \"launch\",\n        s\"scala:${Constants.defaultScalaVersion}\",\n        \"-M\",\n        \"dotty.tools.dotc.Main\",\n        \"--\"\n      ),\n      \"scalac\"\n    )\n    binDir0\n  }\n  val extraEnv = {\n    val currentPath = Option(System.getenv(\"PATH\")).getOrElse(\"\")\n    Map(\"PATH\" -> s\"$binDir${File.pathSeparator}$currentPath\")\n  }\n\n  var lastOutput: String = null\n  val allSources         = Set.newBuilder[os.Path]\n\n  def runCommand(cmd: Commands, log: String => Unit): Unit =\n    given Context = cmd.context\n\n    def writeFile(file: os.Path, code: Seq[String], c: Context): Unit =\n      val (prefixLines, codeLines) =\n        code match\n          case shbang :: tail if shbang.startsWith(\"#!\") =>\n            List(shbang + \"\\n\") -> tail\n          case other =>\n            Nil -> other\n\n      codeLines.foreach(log)\n\n      val prefix =\n        if !shouldAlignContent(file) then prefixLines.mkString(\"\")\n        else prefixLines.mkString(\"\", \"\", s\"$fakeLineMarker\\n\" * c.line)\n\n      os.write.over(file, codeLines.mkString(prefix, \"\\n\", \"\"), createFolders = true)\n\n    def run(cmd: os.proc): Int =\n      val res = cmd.call(\n        cwd = out,\n        mergeErrIntoOut = true,\n        check = false,\n        env = extraEnv\n      )\n\n      log(res.out.text())\n\n      lastOutput = res.out.text()\n      res.exitCode\n\n    cmd match\n      case Commands.Run(cmds, shouldFail, shouldClean, _) =>\n        if shouldClean then\n          os.list(out)\n            .filterNot(_ == binDir)\n            .filterNot(_.last.endsWith(\".scala\"))\n            .filterNot(_.last.endsWith(\".sc\"))\n            .filterNot(_.last.endsWith(\".java\"))\n            .filterNot(_.last.endsWith(\".md\"))\n            .foreach(os.remove.all)\n        val script = out / \".scala-build\" / \"run.sh\"\n        os.write.over(script, mkBashScript(cmds), createFolders = true)\n        val scriptProc = if scala.util.Properties.isWin then\n          os.proc(\"sh.exe\", script)\n        else\n          os.perms.set(script, \"rwxr-xr-x\")\n          os.proc(script)\n        val exitCode = run(scriptProc)\n        if shouldFail then\n          check(exitCode != 0, s\"Commands should fail.\")\n        else\n          check(exitCode == 0, s\"Commands failed.\")\n\n      case Commands.Write(name, code, c) =>\n        writeFile(out / os.RelPath(name), code, c)\n\n      case Commands.Compile(name, code, c, shouldFail, power) =>\n        val dest = out / \".snippets\" / name\n        writeFile(dest, code, c)\n        val powerArg = if power then Seq(\"--power\") else Nil\n        val exitCode = run(os.proc(options.scalaCliCommand, powerArg, \"compile\", dest))\n        if shouldFail then\n          check(exitCode != 0, s\"Compilation should fail.\")\n        else\n          check(exitCode == 0, s\"Compilation failed.\")\n\n      case Commands.Check(patterns, regex, _) =>\n        check(lastOutput != null, \"No output stored from previous commands\")\n        val lines = lastOutput.linesIterator.toList.map(removeAnsiColors)\n\n        if regex then\n          patterns.foreach { pattern =>\n            val regex = pattern.r\n            check(\n              lines.exists(regex.findFirstIn(_).nonEmpty),\n              s\"Regex: $pattern, does not matches any line in:\\n$lastOutput\"\n            )\n          }\n        else\n          patterns.foreach { pattern =>\n            check(\n              lines.exists(_.contains(pattern)),\n              s\"Pattern: $pattern does not exists in  any line in:\\n$lastOutput\"\n            )\n          }\n      case Commands.Clear(_) =>\n        os.list(out).filterNot(_ == binDir).foreach(os.remove.all)\n        os.remove(os.Path(sys.env(\"SCALA_CLI_CONFIG\")))\n\n  try\n    println(Blue(s\"\\n[${file.relativeTo(os.pwd)}]  Running checks in $out\"))\n\n    commands.foreach { cmd =>\n      val logs = List.newBuilder[String]\n\n      def printResult(success: Boolean, startTime: Long): Unit =\n        val duration    = System.currentTimeMillis - startTime\n        val commandName = s\"[${cmd.name} in $duration ms]\"\n        val cmdLog      =\n          if success then Green(commandName)\n          else Red(commandName)\n        println(s\"$cmdLog ${cmd.log}\")\n        println(logs.result.mkString(\"\\n\"))\n\n      def pause(): Unit =\n        println(s\"After [${cmd.context}] using $out. Press ENTER key to continue...\")\n        readLine()\n\n      val start = System.currentTimeMillis\n      try\n        runCommand(cmd, logs.addOne)\n        printResult(success = true, start)\n        if options.step then pause()\n      catch\n        case e: Throwable =>\n          printResult(success = false, start)\n          if options.stopAtFailure then pause()\n          throw e\n    }\n  finally\n    if options.dest.isEmpty then\n      try os.remove.all(out)\n      catch\n        case ex: Throwable => ex.printStackTrace()\n\n  // remove empty space at beginning of all files\n  for (dest <- options.dest)\n    val exampleDir = dest / destName\n    os.remove.all(exampleDir)\n    os.makeDir(exampleDir)\n\n    val relFile = file.relativeTo(os.pwd)\n    val header  = s\"File was generated from based on $relFile, do not edit manually!\"\n    allSources.result().foreach { s =>\n      val content = os.read.lines(s)\n      val cleared =\n        if !shouldAlignContent(s) || content.size < 2 then content\n        else\n          val head = content.take(1).dropWhile(_ == fakeLineMarker)\n          val tail = content.drop(1).dropWhile(_ == fakeLineMarker)\n          head ++ tail\n\n      os.write.over(s, cleared.mkString(s\"// $header\\n\\n\", \"\\n\", \"\"))\n    }\n    val withoutFrontMatter =\n      if !content.head.startsWith(\"---\") then content\n      else\n        content.tail.dropWhile(l => !l.startsWith(\"---\")).tail\n\n    val readmeLines = List(\"<!--\", \"  \" + header, \"-->\", \"\") ++ withoutFrontMatter\n    os.write(exampleDir / \"README.md\", readmeLines.mkString(\"\\n\"))\n\n    os.list(out).filter(_.last.endsWith(\".scala\")).foreach(p => os.copy.into(p, exampleDir))\n\n@main def check(args: String*): Unit =\n  def processFiles(options: Options): Unit =\n    val paths = options.files.map { str =>\n      val path = os.Path(str, os.pwd)\n      assert(os.exists(path), s\"Provided path $str does not exists in ${os.pwd}\")\n      path\n    }\n    val testCases    = paths.flatMap(checkPath(options))\n    val (failed, ok) = testCases.partition(_.failure.nonEmpty)\n    if testCases.size > 1 then\n      if ok.nonEmpty then\n        println(Green(\"Completed:\"))\n        val lines = ok.map(tc => s\"\\t${Green(tc.path.toString)}\")\n        println(lines.mkString(\"\\n\"))\n        println(\"\")\n      if failed.nonEmpty then\n        println(Red(\"Failed:\"))\n        val lines = failed.map(tc => s\"\\t${Red(tc.path.toString)}: ${tc.failure.get}\")\n        println(lines.mkString(\"\\n\"))\n        println(\"\")\n        sys.exit(1)\n\n    options.statusFile.foreach { file =>\n      os.write.over(file, s\"Test completed:\\n${testCases.map(_.path).mkString(\"\\n\")}\")\n    }\n\n  case class PathParameter(name: String):\n    def unapply(args: Seq[String]): Option[(os.Path, Seq[String])] = args.match\n      case `name` :: param :: tail =>\n        if param.startsWith(\"--\") then\n          println(s\"Please provide file name not an option: $param\")\n          sys.exit(1)\n        Some((os.Path(param, os.pwd), tail))\n      case `name` :: Nil =>\n        println(Red(s\"Expected an argument after `--$name` parameter\"))\n        sys.exit(1)\n      case _ => None\n\n  val Dest       = PathParameter(\"--dest\")\n  val StatusFile = PathParameter(\"--status-file\")\n\n  @tailrec\n  def parseArgs(args: Seq[String], options: Options): Options = args match\n    case Nil              => options\n    case \"--step\" :: rest =>\n      parseArgs(rest, options.copy(step = true))\n    case \"--stopAtFailure\" :: rest =>\n      parseArgs(rest, options.copy(stopAtFailure = true))\n    case Dest(dest, rest) =>\n      parseArgs(rest, options.copy(dest = Some(dest)))\n    case StatusFile(file, rest) =>\n      parseArgs(rest, options.copy(statusFile = Some(file)))\n    case path :: rest => parseArgs(rest, options.copy(files = options.files :+ path))\n\n  val options = Options(\n    scalaCliCommand =\n      Seq(Option(System.getenv(\"SCLICHECK_SCALA_CLI\")).getOrElse(\"scala-cli\"))\n  )\n  processFiles(parseArgs(args, options))\n"
  },
  {
    "path": "modules/docs-tests/src/test/resources/test.md",
    "content": "# Testing cat command\n\nThis is test document for Sclicheck\n\nCat command can print a content of a file. Let's start with simple file\n\n```md title=a.txt\nA text\n```\n\nLet's read it using `cat`:\n\n```bash\ncat a.txt\n```\n\n<!-- Expected: \nA text\n-->\n\n`cat` fails if file does not exists:\n\n```bash fail\ncat no_a_file\n```\n<!-- Expected: \nno_a_file\n-->"
  },
  {
    "path": "modules/docs-tests/src/test/scala/sclicheck/DocTests.scala",
    "content": "package sclicheck\n\nimport java.util.concurrent.TimeUnit\n\nimport scala.concurrent.duration.FiniteDuration\nimport scala.util.matching.Regex\n\nclass DocTests extends munit.FunSuite {\n  override def munitTimeout = new FiniteDuration(600, TimeUnit.SECONDS)\n  case class DocTestEntry(name: String, path: os.Path, depth: Int = Int.MaxValue)\n\n  val docsRootPath: os.Path      = os.Path(sys.env(\"MILL_WORKSPACE_ROOT\")) / \"website\" / \"docs\"\n  val entries: Seq[DocTestEntry] = Seq(\n    DocTestEntry(\"root\", docsRootPath, depth = 1),\n    DocTestEntry(\"cookbook\", docsRootPath / \"cookbooks\"),\n    DocTestEntry(\"command\", docsRootPath / \"commands\"),\n    DocTestEntry(\"guide\", docsRootPath / \"guides\"),\n    DocTestEntry(\"reference\", docsRootPath / \"reference\")\n  )\n\n  val options: Options = Options(scalaCliCommand = Seq(TestUtil.scalaCliPath.toString))\n\n  private val ReleaseNotesMd = os.rel / \"release_notes.md\"\n\n  /** `## [v1.12.0](https://…)` style headings that delimit per-version release note sections. */\n  private val ReleaseVersionHeading: Regex = \"\"\"^##\\s+\\[(v[\\w.\\-]+)\\]\"\"\".r\n\n  private def isReleaseVersionHeadingLine(line: String): Boolean =\n    ReleaseVersionHeading.findFirstMatchIn(line).nonEmpty\n\n  private def lineContainsAnyChecks(l: String): Boolean =\n    l.startsWith(\"```md\") || l.startsWith(\"```bash\") ||\n    l.startsWith(\"```scala compile\") || l.startsWith(\"```scala fail\") ||\n    l.startsWith(\"````markdown compile\") || l.startsWith(\"````markdown fail\") ||\n    l.startsWith(\"```java compile\") || l.startsWith(\"````java fail\")\n  private def fileContainsAnyChecks(f: os.Path): Boolean =\n    os.read.lines(f).exists(lineContainsAnyChecks)\n\n  /** One sclicheck run per `## [v…]` section so each gets its own timeout and workspace (Option C).\n    */\n  private def releaseNotesSections(file: os.Path): Seq[(String, IndexedSeq[String])] =\n    val lines  = os.read.lines(file).toIndexedSeq\n    val starts = lines.zipWithIndex.collect {\n      case (line, i) if isReleaseVersionHeadingLine(line) => i\n    }\n    if starts.isEmpty && fileContainsAnyChecks(file) then Seq((\"release_notes\", lines))\n    else if starts.isEmpty then Nil\n    else\n      starts.zipWithIndex.map { case (startIdx, chunkIdx) =>\n        val endIdx =\n          if chunkIdx + 1 < starts.size then starts(chunkIdx + 1)\n          else lines.size\n        val slice =\n          if chunkIdx == 0 then lines.slice(0, endIdx)\n          else lines.slice(startIdx, endIdx)\n        val ver = ReleaseVersionHeading.findFirstMatchIn(lines(startIdx)).get.group(1)\n        (ver, slice)\n      }.filter { case (_, slice) => slice.exists(lineContainsAnyChecks) }\n\n  for {\n    DocTestEntry(tpe, dir, depth) <- entries\n    inputs = os.walk(dir, maxDepth = depth)\n      .filter(_.last.endsWith(\".md\"))\n      .filter(os.isFile(_))\n      .filter(fileContainsAnyChecks)\n      .map(_.relativeTo(dir))\n      .sortBy(_.toString)\n    md <- inputs\n    if !(tpe == \"root\" && md == ReleaseNotesMd)\n  }\n    test(s\"$tpe ${md.toString.stripSuffix(\".md\")}\") {\n      TestUtil.retryOnCi()(checkFile(dir / md, options))\n    }\n\n  private val releaseNotesFile = docsRootPath / \"release_notes.md\"\n  if os.isFile(releaseNotesFile) && fileContainsAnyChecks(releaseNotesFile) then\n    for (ver, slice) <- releaseNotesSections(releaseNotesFile) do\n      val safeStem = ver.replaceAll(\"[^a-zA-Z0-9._\\\\-]\", \"_\")\n      test(s\"root release_notes $ver\") {\n        TestUtil.retryOnCi() {\n          TestUtil.withTmpDir(\"sclicheck-release-notes\") { tmp =>\n            val chunkFile = tmp / s\"release_notes-$safeStem.md\"\n            os.write.over(chunkFile, slice.mkString(\"\", \"\\n\", \"\\n\"))\n            checkFile(chunkFile, options)\n          }\n        }\n      }\n\n}\n"
  },
  {
    "path": "modules/docs-tests/src/test/scala/sclicheck/GifTests.scala",
    "content": "package sclicheck\n\nimport java.util.concurrent.TimeUnit\n\nimport scala.concurrent.duration.FiniteDuration\n\nclass GifTests extends munit.FunSuite {\n  override def munitTimeout = new FiniteDuration(360, TimeUnit.SECONDS)\n\n  val scenariosDir =\n    Option(System.getenv(\"SCALA_CLI_GIF_SCENARIOS\")).map(os.Path(_, os.pwd)).getOrElse {\n      sys.error(\"SCALA_CLI_GIF_SCENARIOS not set\")\n    }\n  val websiteImgDir =\n    Option(System.getenv(\"SCALA_CLI_WEBSITE_IMG\")).map(os.Path(_, os.pwd)).getOrElse {\n      sys.error(\"SCALA_CLI_WEBSITE_IMG not set\")\n    }\n  lazy val gifRenderedDockerDir =\n    Option(System.getenv(\"SCALA_CLI_GIF_RENDERER_DOCKER_DIR\")).map(os.Path(_, os.pwd)).getOrElse {\n      sys.error(\"SCALA_CLI_GIF_RENDERER_DOCKER_DIR not set\")\n    }\n  lazy val svgRenderedDockerDir =\n    Option(System.getenv(\"SCALA_CLI_SVG_RENDERER_DOCKER_DIR\")).map(os.Path(_, os.pwd)).getOrElse {\n      sys.error(\"SCALA_CLI_SVG_RENDERER_DOCKER_DIR not set\")\n    }\n\n  val scenarioScripts = os.list(scenariosDir)\n    .filter(!_.last.startsWith(\".\"))\n    .filter(_.last.endsWith(\".sh\"))\n    .filter(os.isFile(_))\n\n  lazy val hasTty =\n    os.proc(\"tty\").call(stdin = os.Inherit, stdout = os.Inherit, check = false).exitCode == 0\n  lazy val ttyOpts = if (hasTty) Seq(\"-it\") else Nil\n\n  def buildImages      = true\n  def forceBuildImages = true\n\n  def columns = 70\n  def rows    = 20\n  def record  = true\n  def gifs    = true\n  def svgs    = true\n\n  def maybeBuildImages(): Unit =\n    if (buildImages || forceBuildImages) {\n      def hasImage(imageName: String): Boolean = {\n        val res = os.proc(\"docker\", \"images\", \"-q\", imageName).call()\n        res.out.trim().nonEmpty\n      }\n      if (forceBuildImages || !hasImage(\"gif-renderer\"))\n        val scalaCliJvmPath = gifRenderedDockerDir / \"scala-cli-jvm\"\n        os.copy.over(TestUtil.scalaCliPath, scalaCliJvmPath)\n        os.proc(\"docker\", \"build\", gifRenderedDockerDir, \"--tag\", \"gif-renderer\")\n          .call(stdin = os.Inherit, stdout = os.Inherit)\n        os.remove(scalaCliJvmPath)\n      if (forceBuildImages || !hasImage(\"svg_rendrer\"))\n        os.proc(\"docker\", \"build\", svgRenderedDockerDir, \"--tag\", \"svg_rendrer\")\n          .call(stdin = os.Inherit, stdout = os.Inherit)\n    }\n\n  for (script <- scenarioScripts) {\n    val name = script.last.stripSuffix(\".sh\")\n    test(name) {\n      maybeBuildImages()\n\n      TestUtil.withTmpDir(s\"scala-cli-gif-test-$name\") { out =>\n\n        try {\n          if (record)\n            os.proc(\n              \"docker\",\n              \"run\",\n              \"--rm\",\n              ttyOpts,\n              \"-v\",\n              s\"$out/.scala:/data/out\",\n              \"gif-renderer\",\n              \"./run_scenario.sh\",\n              name\n            )\n              .call(stdin = os.Inherit, stdout = os.Inherit)\n\n          if (hasTty) {\n            val svgRenderMappings =\n              Seq(\"-v\", s\"$websiteImgDir:/data\", \"-v\", s\"$out/.scala:/out\")\n            if (svgs) {\n              val svgRenderOps = Seq(\n                \"--in\",\n                s\"/out/$name.cast\",\n                \"--width\",\n                columns.toString,\n                \"--height\",\n                rows.toString,\n                \"--term\",\n                \"iterm2\",\n                \"--padding\",\n                \"20\"\n              )\n              os.proc(\n                \"docker\",\n                \"run\",\n                \"--rm\",\n                svgRenderMappings,\n                \"svg_rendrer\",\n                \"a\",\n                svgRenderOps,\n                \"--out\",\n                s\"/data/$name.svg\",\n                \"--profile\",\n                \"/profiles/light\"\n              )\n                .call(stdin = os.Inherit, stdout = os.Inherit)\n              os.proc(\n                \"docker\",\n                \"run\",\n                \"--rm\",\n                svgRenderMappings,\n                \"svg_rendrer\",\n                \"a\",\n                svgRenderOps,\n                \"--out\",\n                s\"/data/dark/$name.svg\",\n                \"--profile\",\n                \"/profiles/dark\"\n              )\n                .call(stdin = os.Inherit, stdout = os.Inherit)\n            }\n            if (gifs) {\n              os.proc(\n                \"docker\",\n                \"run\",\n                \"--rm\",\n                svgRenderMappings,\n                \"asciinema/asciicast2gif\",\n                \"-w\",\n                columns,\n                \"-h\",\n                rows,\n                \"-t\",\n                \"monokai\",\n                s\"/out/$name.cast\",\n                s\"/data/gifs/$name.gif\"\n              )\n                .call(stdin = os.Inherit, stdout = os.Inherit)\n              os.proc(\n                \"docker\",\n                \"run\",\n                \"--rm\",\n                svgRenderMappings,\n                \"asciinema/asciicast2gif\",\n                \"-w\",\n                columns,\n                \"-h\",\n                rows,\n                \"-t\",\n                \"solarized-dark\",\n                s\"/out/$name.cast\",\n                s\"/data/dark/gifs/$name.gif\"\n              )\n                .call(stdin = os.Inherit, stdout = os.Inherit)\n            }\n          }\n        }\n        finally\n          // Clean-up out dir with the same rights as the images above (should be root - from docker)\n          os.proc(\n            \"docker\",\n            \"run\",\n            \"--rm\",\n            ttyOpts,\n            \"-v\",\n            s\"$out:/out\",\n            s\"alpine:${Constants.alpineVersion}\",\n            \"sh\",\n            \"-c\",\n            \"rm -rf /out/* || true; rm -rf /out/.* || true\"\n          )\n            .call(stdin = os.Inherit, stdout = os.Inherit)\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/docs-tests/src/test/scala/sclicheck/MarkdownLinkTests.scala",
    "content": "package sclicheck\n\nclass MarkdownLinkTests extends munit.FunSuite {\n\n  private val docsRootPath: os.Path = os.Path(sys.env(\"MILL_WORKSPACE_ROOT\")) / \"website\" / \"docs\"\n\n  private val markdownLinkPattern = \"\"\"\\[([^\\]]*)\\]\\(([^)]+)\\)\"\"\".r\n\n  private def stripFragment(url: String): String = url.split('#').head\n\n  private def hasFileExtension(url: String): Boolean = {\n    val lastSegment = stripFragment(url).split('/').last\n    lastSegment.contains('.')\n  }\n\n  private def isRelativeDocLink(url: String): Boolean =\n    url.startsWith(\".\") && !url.contains(\"://\")\n\n  private def hasDoubleSlash(url: String): Boolean =\n    url.replace(\"://\", \"\").contains(\"//\")\n\n  private def mdFileExistsFor(sourceFile: os.Path, relativeUrl: String): Boolean = {\n    val dir        = sourceFile / os.up\n    val targetPath = stripFragment(relativeUrl)\n    try os.isFile(dir / os.RelPath(targetPath + \".md\"))\n    catch { case _: Exception => false }\n  }\n\n  case class LinkIssue(file: os.Path, line: Int, linkText: String, url: String, problem: String)\n\n  private def findIssuesInFile(file: os.Path): Seq[LinkIssue] = {\n    val lines  = os.read.lines(file)\n    val issues = Seq.newBuilder[LinkIssue]\n    for {\n      (lineContent, lineIdx) <- lines.zipWithIndex\n      m                      <- markdownLinkPattern.findAllMatchIn(lineContent)\n    } {\n      val linkText = m.group(1)\n      val url      = m.group(2)\n      if isRelativeDocLink(url) then {\n        if hasDoubleSlash(url) then\n          issues += LinkIssue(\n            file,\n            lineIdx + 1,\n            linkText,\n            url,\n            \"relative link contains double slash\"\n          )\n        if !hasFileExtension(url) && mdFileExistsFor(file, url) then\n          issues += LinkIssue(\n            file,\n            lineIdx + 1,\n            linkText,\n            url,\n            \"relative link to doc page without .md extension\"\n          )\n      }\n    }\n    issues.result()\n  }\n\n  test(\"all relative doc links should use .md extension\") {\n    val allMdFiles = os.walk(docsRootPath)\n      .filter(_.last.endsWith(\".md\"))\n      .filter(os.isFile(_))\n      .sorted\n\n    val allIssues = allMdFiles.flatMap(findIssuesInFile)\n\n    if allIssues.nonEmpty then {\n      val report = allIssues\n        .map { issue =>\n          val relPath = issue.file.relativeTo(docsRootPath)\n          s\"  $relPath:${issue.line} — [${issue.linkText}](${issue.url})\\n    ${issue.problem}\"\n        }\n        .mkString(\"\\n\")\n      fail(\n        s\"Found ${allIssues.size} relative doc link(s) with issues:\\n$report\\n\\n\" +\n          \"Relative links to other doc pages must include the .md extension, \" +\n          \"otherwise they resolve incorrectly in production due to trailing slashes.\"\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/docs-tests/src/test/scala/sclicheck/SclicheckTests.scala",
    "content": "package sclicheck\n\nclass SclicheckTests extends munit.FunSuite:\n  test(\"Run regex\") {\n    assert(clue(CompileBlock.unapplySeq(\"``scala compile\")).isEmpty)\n    assert(\n      clue(CompileBlock.unapplySeq(\"```scala compile\"))\n        .contains(Seq(\"```\", \"scala\", \"compile\", null, null))\n    )\n    assert(\n      clue(CompileBlock.unapplySeq(\"```scala compile power\"))\n        .contains(Seq(\"```\", \"scala\", \"compile\", null, \"power\"))\n    )\n    assert(\n      clue(CompileBlock.unapplySeq(\"```scala fail\"))\n        .contains(Seq(\"```\", \"scala\", \"fail\", null, null))\n    )\n    assert(\n      clue(CompileBlock.unapplySeq(\"````markdown compile\"))\n        .contains(Seq(\"````\", \"markdown\", \"compile\", null, null))\n    )\n    assert(\n      clue(CompileBlock.unapplySeq(\"````markdown fail  title=a.md\"))\n        .contains(Seq(\"````\", \"markdown\", \"fail\", \"a.md\", null))\n    )\n    assert(clue(CompileBlock.unapplySeq(\"``scala fail  title=a.sc\")).isEmpty)\n    assert(\n      clue(CompileBlock.unapplySeq(\"```scala fail  title=a.sc\"))\n        .contains(Seq(\"```\", \"scala\", \"fail\", \"a.sc\", null))\n    )\n  }\n"
  },
  {
    "path": "modules/docs-tests/src/test/scala/sclicheck/TestUtil.scala",
    "content": "package sclicheck\n\nimport scala.annotation.tailrec\nimport scala.concurrent.duration.{DurationInt, FiniteDuration}\n\nobject TestUtil {\n  val isCI: Boolean = System.getenv(\"CI\") != null\n\n  def withTmpDir[T](prefix: String)(f: os.Path => T): T = {\n    val tmpDir = os.temp.dir(prefix = prefix)\n    try f(tmpDir)\n    finally tryRemoveAll(tmpDir)\n  }\n\n  def tryRemoveAll(f: os.Path): Unit =\n    try os.remove.all(f)\n    catch {\n      case ex: java.nio.file.FileSystemException =>\n        System.err.println(s\"Could not remove $f ($ex), ignoring it.\")\n    }\n\n  lazy val scalaCliPath =\n    Option(System.getenv(\"SCLICHECK_SCALA_CLI\")).map(os.Path(_, os.pwd)).getOrElse {\n      sys.error(\"SCLICHECK_SCALA_CLI not set\")\n    }\n\n  def retry[T](\n    maxAttempts: Int = 3,\n    waitDuration: FiniteDuration = 5.seconds\n  )(\n    run: => T\n  ): T = {\n    @tailrec\n    def helper(count: Int): T =\n      try run\n      catch {\n        case t: Throwable =>\n          if (count >= maxAttempts) {\n            System.err.println(s\"$maxAttempts attempts failed, caught $t. Giving up.\")\n            throw new Exception(t)\n          }\n          else {\n            val remainingAttempts = maxAttempts - count\n            System.err.println(\n              s\"Caught $t, $remainingAttempts attempts remaining, trying again in $waitDuration…\"\n            )\n            Thread.sleep(waitDuration.toMillis)\n            System.err.println(s\"Trying attempt $count out of $maxAttempts...\")\n            helper(count + 1)\n          }\n      }\n\n    helper(1)\n  }\n\n  def retryOnCi[T](maxAttempts: Int = 3, waitDuration: FiniteDuration = 5.seconds)(\n    run: => T\n  ): T = retry(if (isCI) maxAttempts else 1, waitDuration)(run)\n}\n"
  },
  {
    "path": "modules/dummy/amm/src/main/scala/AmmDummy.scala",
    "content": "/** Create an empty class to enforce resolving ivy deps by mill for `amm` module\n  */\nclass AmmDummy\n"
  },
  {
    "path": "modules/dummy/scalafmt/src/main/scala/ScalafmtDummy.scala",
    "content": "/** Create an empty class to enforce resolving ivy deps by mill for `scalafmt` module\n  */\nclass ScalafmtDummy\n"
  },
  {
    "path": "modules/generate-reference-doc/src/main/scala/scala/cli/doc/GenerateReferenceDoc.scala",
    "content": "package scala.cli.doc\n\nimport caseapp.*\nimport caseapp.core.Arg\nimport caseapp.core.Scala3Helpers.*\nimport caseapp.core.util.Formatter\nimport munit.diff.Diff\n\nimport java.nio.charset.StandardCharsets\nimport java.util\n\nimport scala.build.info.{ArtifactId, BuildInfo, ExportDependencyFormat, ScopedBuildInfo}\nimport scala.build.internals.EnvVar\nimport scala.build.options.{BuildOptions, BuildRequirements, WithBuildRequirements}\nimport scala.build.preprocessing.directives.DirectiveHandler\nimport scala.build.preprocessing.directives.DirectivesPreprocessingUtils.*\nimport scala.cli.commands.{ScalaCommand, SpecificationLevel}\nimport scala.cli.doc.ReferenceDocUtils.*\nimport scala.cli.util.ArgHelpers.*\nimport scala.cli.{ScalaCli, ScalaCliCommands}\nobject GenerateReferenceDoc extends CaseApp[InternalDocOptions] {\n\n  implicit class PBUtils(sb: StringBuilder) {\n    def section(t: String*): StringBuilder =\n      sb.append(t.mkString(\"\", \"\\n\", \"\\n\\n\"))\n  }\n\n  private def cleanUpOrigin(origin: String): String = {\n    val origin0      = origin.takeWhile(_ != '[').stripSuffix(\"Options\")\n    val actualOrigin = if (origin0 == \"WithFullHelp\" || origin0 == \"WithHelp\") \"Help\" else origin0\n    if (actualOrigin == \"Shared\") actualOrigin\n    else actualOrigin.stripPrefix(\"Shared\")\n  }\n\n  private def formatOrigin(origin: String, keepCapitalization: Boolean = true): String = {\n    val l = origin.head :: origin.toList.tail.flatMap { c =>\n      if (c.isUpper) \" \" :: c.toLower :: Nil\n      else c :: Nil\n    }\n    val value = l.mkString\n      .replace(\"Scala native\", \"Scala Native\")\n      .replace(\"Scala js\", \"Scala.js\")\n      .split(\"\\\\s+\")\n      .map(w => if (w == \"ide\") \"IDE\" else w)\n      .mkString(\" \")\n    val valueNeedsLowerCasing = keepCapitalization ||\n      (value.startsWith(\"Scala\") && !value.startsWith(\"Scalac\")) ||\n      !value.head.isUpper\n    if (valueNeedsLowerCasing) value\n    else value.head.toLower +: value.tail\n  }\n\n  private def prettyPath(path: os.Path): String =\n    if (path.startsWith(os.pwd)) path.relativeTo(os.pwd).toString\n    else path.toString\n\n  private def maybeWrite(dest: os.Path, content: String): Unit = {\n    val content0    = content.getBytes(StandardCharsets.UTF_8)\n    val needsUpdate = !os.exists(dest) || {\n      val currentContent = os.read.bytes(dest)\n      !util.Arrays.equals(content0, currentContent)\n    }\n    if (needsUpdate) {\n      os.write.over(dest, content0, createFolders = true)\n      System.err.println(s\"Wrote ${prettyPath(dest)}\")\n    }\n    else\n      System.err.println(s\"${prettyPath(dest)} doesn't need updating\")\n  }\n\n  private def maybeDiff(dest: os.Path, content: String): Option[Diff] = {\n    val currentContentOpt =\n      if (os.exists(dest)) Some(new String(os.read.bytes(dest), StandardCharsets.UTF_8))\n      else None\n    currentContentOpt.filter(_ != content).map { currentContent =>\n      new Diff(currentContent, content)\n    }\n  }\n\n  private def actualHelp(command: Command[?]): Help[?] =\n    command match {\n      case ext: scala.cli.commands.pgp.ExternalCommand =>\n        ext.actualHelp\n      case _ =>\n        command.finalHelp\n    }\n\n  private def scalacOptionForwarding =\n    \"\"\"## Scalac options forwarding\n      |\n      | All options that start with:\n      |\n      |\n      |- `-g`\n      |- `-language`\n      |- `-opt`\n      |- `-P`\n      |- `-target`\n      |- `-V`\n      |- `-W`\n      |- `-X`\n      |- `-Y`\n      |\n      |are assumed to be Scala compiler options and will be propagated to Scala Compiler. This applies to all commands that uses compiler directly or indirectly.\n      |\n      |\n      | ## Scalac options that are directly supported in scala CLI (so can be provided as is, without any prefixes etc.):\n      |\n      | - `-encoding`\n      | - `-release`\n      | - `-color`\n      | - `-nowarn`\n      | - `-feature`\n      | - `-deprecation`\n      | - `-indent`\n      | - `-no-indent`\n      | - `-unchecked`\n      | - `-rewrite`\n      | - `-old-syntax`\n      | - `-new-syntax`\n      |\n      |\"\"\".stripMargin\n\n  private def cliOptionsContent(\n    commands: Seq[Command[?]],\n    allArgs: Seq[Arg],\n    nameFormatter: Formatter[Name],\n    onlyRestricted: Boolean = false\n  ): String = {\n    val argsToShow = if (!onlyRestricted) allArgs\n    else\n      allArgs.filterNot(_.isExperimentalOrRestricted)\n\n    val argsByOrigin = argsToShow.groupBy(arg => cleanUpOrigin(arg.origin.getOrElse(\"\")))\n\n    val commandOrigins = for {\n      command <- commands\n      origin  <- actualHelp(command).args.map(_.origin.getOrElse(\"\")).map(cleanUpOrigin)\n    } yield origin -> command\n\n    val commandOriginsMap = commandOrigins.groupBy(_._1)\n      .map {\n        case (k, v) =>\n          (k, v.map(_._2).distinct.sortBy(_.name))\n      }\n\n    // Collect all origins that are referenced by commands, even if they have no args\n    val allReferencedOrigins = commandOriginsMap.keySet ++ argsByOrigin.keySet\n\n    val mainOptionsContent   = new StringBuilder\n    val hiddenOptionsContent = new StringBuilder\n\n    mainOptionsContent.append(\n      s\"\"\"---\n         |title: Command-line options\n         |sidebar_position: 1\n         |---\n         |\n         |${\n          if (onlyRestricted)\n            \"**This document describes as scala-cli behaves if run as `scala` command. See more information in [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\"\n          else \"\"\n        }\n         |\n         |This is a summary of options that are available for each subcommand of the `${ScalaCli\n          .baseRunnerName}` command.\n         |\n         |\"\"\".stripMargin\n    )\n\n    mainOptionsContent.section(scalacOptionForwarding)\n\n    for (origin <- allReferencedOrigins.toVector.sortBy(identity)) {\n      val originArgs            = argsByOrigin.getOrElse(origin, Nil)\n      val distinctArgs          = originArgs.map(_.withOrigin(None)).distinct\n      val originCommands        = commandOriginsMap.getOrElse(origin, Nil)\n      val onlyForHiddenCommands = originCommands.nonEmpty && originCommands.forall(_.hidden)\n      // Empty option groups should not be treated as internal if they're referenced by commands\n      val allArgsHidden = distinctArgs.nonEmpty && distinctArgs.forall(_.noHelp)\n      val isInternal    = onlyForHiddenCommands || allArgsHidden\n      val b             = if (isInternal) hiddenOptionsContent else mainOptionsContent\n      if (originCommands.nonEmpty) {\n        val formattedOrigin   = formatOrigin(origin)\n        val formattedCommands = originCommands.map { c =>\n          // https://scala-cli.virtuslab.org/docs/reference/commands#install-completions\n          val names = c.names.map(_.mkString(\" \"))\n          val text  = names.map(\"`\" + _ + \"`\").mkString(\" , \")\n          s\"[$text](./commands.md#${names.head.replace(\" \", \"-\")})\"\n        }\n        val availableIn = \"Available in commands:\\n\\n\" + formattedCommands.mkString(\", \")\n        val header      = if (isInternal) \"###\" else \"##\"\n        b.append(\n          s\"\"\"$header $formattedOrigin options\n             |\n             |$availableIn\n             |\n             |<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n             |\n             |\"\"\".stripMargin\n        )\n\n        if (distinctArgs.nonEmpty)\n          for (arg <- distinctArgs) {\n            import caseapp.core.util.NameOps._\n            arg.name.option(nameFormatter)\n            val names = (arg.name +: arg.extraNames).map(_.option(nameFormatter))\n            b.append(s\"### `${names.head}`\\n\\n\")\n            if (names.tail.nonEmpty)\n              b.append(\n                names\n                  .tail\n                  .sortBy(_.dropWhile(_ == '-'))\n                  .map {\n                    case name if arg.deprecatedOptionAliases.contains(name) =>\n                      s\"[deprecated] `$name`\"\n                    case name => s\"`$name`\"\n                  }\n                  .mkString(\"Aliases: \", \", \", \"\\n\\n\")\n              )\n\n            if (onlyRestricted)\n              b.section(s\"`${arg.level.md}` per Scala Runner specification\")\n            else if (isInternal || arg.noHelp) b.append(\"[Internal]\\n\")\n\n            for (desc <- arg.helpMessage.map(_.referenceDocMessage))\n              b.append(\n                s\"\"\"$desc\n                   |\n                   |\"\"\".stripMargin\n              )\n          }\n        else\n          // Empty option group - add a note\n          b.append(\n            \"*This section was automatically generated and may be empty if no options were available.*\\n\\n\"\n          )\n      }\n    }\n\n    mainOptionsContent.append(\"## Internal options \\n\")\n    mainOptionsContent.append(hiddenOptionsContent.toString)\n    mainOptionsContent.toString\n  }\n\n  private def optionsReference(\n    commands: Seq[Command[?]],\n    nameFormatter: Formatter[Name]\n  ): String = {\n    val b = new StringBuilder\n\n    b.section(\n      \"\"\"---\n        |title: Scala Runner specification\n        |sidebar_position: 1\n        |---\n      \"\"\".stripMargin\n    )\n\n    b.section(\n      \"**This document describes proposed specification for Scala runner based on Scala CLI documentation as requested per [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\"\n    )\n\n    b.section(\n      \"Commands and options are marked with MUST and SHOULD (in the RFC style) for ones applicable for Scala Runner.\",\n      \"Options and commands marked as **Implementation** are needed for smooth running of Scala CLI.\",\n      \"We recommend for those options and commands to be supported by the `scala` command (when based on Scala CLI) but not to be a part of the Scala Runner specification.\"\n    )\n\n    b.section(\n      \"The proposed Scala runner specification should also contain supported `Using directives` defined in the dedicated [document](./directives.md)]\"\n    )\n\n    b.section(scalacOptionForwarding)\n\n    def optionsForCommand(command: Command[?]): Unit = {\n      val supportedArgs = actualHelp(command).args\n      val argsByLevel   = supportedArgs.groupBy(_.level)\n\n      import caseapp.core.util.NameOps._\n\n      (SpecificationLevel.inSpecification :+ SpecificationLevel.IMPLEMENTATION).foreach { level =>\n        val args = argsByLevel.getOrElse(level, Nil)\n\n        if (args.nonEmpty) {\n          if (level == SpecificationLevel.IMPLEMENTATION) b.section(\n            \"<details><summary>\",\n            s\"\\n### Implementantation specific options\\n\",\n            \"</summary>\"\n          )\n          else b.section(s\"### ${level.md} options\")\n          args.foreach { arg =>\n            val names = (arg.name +: arg.extraNames).map(_.option(nameFormatter))\n            b.section(s\"**${names.head}**\")\n            b.section(arg.helpMessage.fold(\"\")(_.referenceDocMessage))\n            if (names.tail.nonEmpty) b.section(names.tail.mkString(\"Aliases: `\", \"` ,`\", \"`\"))\n\n          }\n          if (level == SpecificationLevel.IMPLEMENTATION) b.section(\"</details>\")\n        }\n      }\n    }\n\n    (SpecificationLevel.inSpecification :+ SpecificationLevel.IMPLEMENTATION).foreach { level =>\n      val levelCommands =\n        commands.collect { case s: ScalaCommand[_] if s.scalaSpecificationLevel == level => s }\n\n      if (levelCommands.nonEmpty) b.section(s\"# ${level.md} commands\")\n\n      levelCommands.foreach { command =>\n        b.section(\n          s\"## `${command.name}` command\",\n          s\"**${level.md} for Scala Runner specification.**\"\n        )\n\n        if (command.names.tail.nonEmpty)\n          b.section(command.names.map(_.mkString(\" \")).tail.mkString(\"Aliases: `\", \"`, `\", \"`\"))\n        for (desc <- command.messages.helpMessage.map(_.referenceDocDetailedMessage))\n          b.section(desc)\n        optionsForCommand(command)\n        b.section(\"---\")\n      }\n    }\n    b.toString\n  }\n\n  private def commandsContent(commands: Seq[Command[?]], onlyRestricted: Boolean): String = {\n\n    val b = new StringBuilder\n\n    b.append(\n      s\"\"\"---\n         |title: Commands\n         |sidebar_position: 3\n         |---\n         |\n         |${\n          if (onlyRestricted)\n            \"**This document describes as scala-cli behaves if run as `scala` command. See more information in [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\"\n          else \"\"\n        }\n         |\n         |\n         |\"\"\".stripMargin\n    )\n\n    val (hiddenCommands, mainCommands) = commands.partition(_.hidden)\n\n    def addCommand(c: Command[?], additionalIndentation: Int = 0): Unit = {\n\n      val origins = c.parser0.args.flatMap(_.origin.toSeq).map(cleanUpOrigin).distinct.sorted\n\n      val headerPrefix = \"#\" * additionalIndentation\n      val names        = c.names.map(_.mkString(\" \"))\n\n      b.append(s\"$headerPrefix## ${names.head}\\n\\n\")\n      if (names.tail.nonEmpty) b.append(names.tail.sorted.mkString(\"Aliases: `\", \"`, `\", \"`\\n\\n\"))\n\n      for (desc <- c.messages.helpMessage.map(_.referenceDocDetailedMessage)) b.section(desc)\n\n      if (origins.nonEmpty) {\n        val links = origins.map { origin =>\n          val cleanedUp = formatOrigin(origin, keepCapitalization = false)\n          val linkPart  = cleanedUp\n            .split(\"\\\\s+\")\n            .map(_.toLowerCase(util.Locale.ROOT).filter(_ != '.'))\n            .mkString(\"-\")\n          s\"[$cleanedUp](./cli-options.md#$linkPart-options)\"\n        }\n        b.append(\n          s\"\"\"Accepts option groups: ${links.mkString(\", \")}\n             |\"\"\".stripMargin\n        )\n        b.append(\"\\n\")\n      }\n    }\n\n    if (onlyRestricted) {\n      val scalaCommands = commands.collect { case s: ScalaCommand[_] => s }\n      b.section(\"# `scala` commands\")\n      // TODO add links to RFC\n      b.section(\n        \"This document is a specification of the `scala` runner.\",\n        \"For now it uses documentation specific to Scala CLI but at some point it may be refactored to provide more abstract documentation.\",\n        \"Documentation is split into sections in the spirit of RFC keywords (`MUST`, `SHOULD`, `NICE TO HAVE`) including the `IMPLEMENTATION` category,\",\n        \"that is reserved for commands that need to be present for Scala CLI to work properly but should not be a part of the official API.\"\n      )\n\n      SpecificationLevel.inSpecification.foreach { level =>\n        val commands = scalaCommands.filter(_.scalaSpecificationLevel == level)\n        if (commands.nonEmpty) {\n          b.section(s\"## ${level.md.capitalize} commands:\")\n          commands.foreach(addCommand(_, additionalIndentation = 1))\n        }\n      }\n\n      b.section(\"## Implementation-specific commands\")\n      b.section(\n        \"Commands which are used within Scala CLI and should be a part of the `scala` command but aren't a part of the specification.\"\n      )\n\n      scalaCommands\n        .filter(_.scalaSpecificationLevel == SpecificationLevel.IMPLEMENTATION)\n        .foreach(c => addCommand(c, additionalIndentation = 1))\n\n    }\n    else {\n      mainCommands.foreach(addCommand(_))\n      b.section(\"## Hidden commands\")\n      hiddenCommands.foreach(c => addCommand(c, additionalIndentation = 1))\n    }\n    b.toString\n  }\n\n  private def usingContent(\n    allUsingHandlers: Seq[\n      DirectiveHandler[BuildOptions | List[WithBuildRequirements[BuildOptions]]]\n    ],\n    requireHandlers: Seq[DirectiveHandler[BuildRequirements]],\n    onlyRestricted: Boolean\n  ): String = {\n    val b = new StringBuilder\n\n    b.section(\n      \"\"\"---\n        |title: Directives\n        |sidebar_position: 2\n        |---\"\"\".stripMargin\n    )\n    b.section(\n      if (onlyRestricted)\n        \"**This document describes as scala-cli behaves if run as `scala` command. See more information in [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\"\n      else \"## using directives\"\n    )\n\n    def addHandlers(handlers: Seq[DirectiveHandler[?]]): Unit =\n      for (handler <- handlers.sortBy(_.name)) {\n        b.append(\n          s\"\"\"### ${handler.name}\n             |\n             |${handler.descriptionMd}\n             |\n             |${handler.usageMd}\n             |\n             |\"\"\".stripMargin\n        )\n        val examples = handler.examples\n        if (examples.nonEmpty) {\n          b.append(\n            \"\"\"#### Examples\n              |\"\"\".stripMargin\n          )\n          for (ex <- examples)\n            b.append(\n              s\"\"\"`$ex`\n                 |\n                 |\"\"\".stripMargin\n            )\n        }\n      }\n\n    if (onlyRestricted) {\n      // TODO add links to RFC\n      b.section(\n        \"This document is a specification of the `scala` runner.\",\n        \"For now it uses documentation specific to Scala CLI but at some point it may be refactored to provide more abstract documentation.\",\n        \"Documentation is split into sections in the spirit of RFC keywords (`MUST`, `SHOULD`).\"\n      )\n\n      SpecificationLevel.inSpecification.foreach { level =>\n        val handlers = allUsingHandlers.filter(_.scalaSpecificationLevel == level)\n        if (handlers.nonEmpty) {\n          b.section(s\"## ${level.md.capitalize} directives:\")\n          addHandlers(handlers)\n        }\n      }\n\n      val implHandlers =\n        allUsingHandlers.filter(_.scalaSpecificationLevel == SpecificationLevel.IMPLEMENTATION)\n\n      if (implHandlers.nonEmpty) {\n        b.section(\"## Implementation-specific directices\")\n        b.section(\n          \"Directives which are used within Scala CLI and should be a part of the `scala` command but aren't a part of the specification.\"\n        )\n\n        addHandlers(implHandlers)\n      }\n    }\n    else {\n      addHandlers(allUsingHandlers)\n\n      b.append(\n        \"\"\"\n          |## target directives\n          |\n          |\"\"\".stripMargin\n      )\n\n      addHandlers(requireHandlers)\n    }\n\n    b.toString\n  }\n\n  private def buildInfoContent: String = {\n    val b = new StringBuilder\n\n    b.section(\n      \"\"\"---\n        |title: BuildInfo\n        |sidebar_position: 6\n        |---\"\"\".stripMargin\n    )\n    b.section(\n      \"\"\":::caution\n        |BuildInfo is a restricted feature and requires setting the `--power` option to be used.\n        |You can pass it explicitly or set it globally by running:\n        |\n        |    scala-cli config power true\n        |:::\"\"\".stripMargin\n    )\n\n    b.section(\n      \"\"\"During the building process Scala CLI collects information about the project's configuration,\n        |both from the console options and `using directives` found in the project's sources.\n        |You can access this information from your code using the `BuildInfo` object, that's automatically generated for your\n        |build on compile when that information changes.\"\"\".stripMargin\n    )\n\n    b.section(\n      \"\"\"To enable BuildInfo generation pass the `--build-info` option to Scala CLI or use a\n        |`//> using buildInfo` directive.\"\"\".stripMargin\n    )\n\n    b.section(\n      \"\"\"## Usage\n        |\n        |The generated BuildInfo object is available on the project's classpath. To access it you need to import it first.\n        |It is available in the package `scala.cli.build` so use\n        |```scala\n        |import scala.cli.build.BuildInfo\n        |```\n        |to import it.\n        |\n        |Below you can find an example instance of the BuildInfo object, with all fields explained.\n        |Some of the values have been shortened for readability.\"\"\".stripMargin\n    )\n\n    val osLibDep = ExportDependencyFormat(\"com.lihaoyi\", ArtifactId(\"os-lib\", \"os-lib_3\"), \"0.9.1\")\n    val toolkitDep =\n      ExportDependencyFormat(\"org.scala-lang\", ArtifactId(\"toolkit\", \"toolkit_3\"), \"latest.release\")\n\n    val mainScopedBuildInfo = ScopedBuildInfo(BuildOptions(), Seq(\".../Main.scala\")).copy(\n      scalacOptions = Seq(\"-Werror\"),\n      scalaCompilerPlugins = Nil,\n      dependencies = Seq(osLibDep),\n      resolvers = Seq(\"https://repo1.maven.org/maven2\", \"ivy:file:...\"),\n      resourceDirs = Seq(\".../resources\"),\n      customJarsDecls = Seq(\".../AwesomeJar1.jar\", \".../AwesomeJar2.jar\")\n    )\n\n    val testScopedBuildInfo = ScopedBuildInfo(BuildOptions(), Seq(\".../MyTests.scala\")).copy(\n      scalacOptions = Seq(\"-Vdebug\"),\n      scalaCompilerPlugins = Nil,\n      dependencies = Seq(toolkitDep),\n      resolvers = Seq(\"https://repo1.maven.org/maven2\", \"ivy:file:...\"),\n      resourceDirs = Seq(\".../test/resources\"),\n      customJarsDecls = Nil\n    )\n\n    val generatedBuildInfo = BuildInfo(BuildOptions(), os.pwd) match {\n      case Right(bv)       => bv\n      case Left(exception) =>\n        System.err.println(s\"Failed to generate BuildInfo: ${exception.message}\")\n        sys.exit(1)\n    }\n\n    val buildInfo = generatedBuildInfo.copy(\n      scalaVersion = Some(\"3.3.0\"),\n      platform = Some(\"JVM\"),\n      jvmVersion = Some(\"11\"),\n      scalaJsVersion = None,\n      jsEsVersion = None,\n      scalaNativeVersion = None,\n      mainClass = Some(\"Main\"),\n      projectVersion = None,\n      scalaCliVersion = Some(\"1.7.0\")\n    )\n      .withScope(\"main\", mainScopedBuildInfo)\n      .withScope(\"test\", testScopedBuildInfo)\n\n    b.section(\n      s\"\"\"```scala\n         |${buildInfo.generateContents().strip()}\n         |```\"\"\".stripMargin\n    )\n\n    b.section(\n      \"\"\"## Project version\n        |\n        |A part of the BuildInfo object is the project version. By default, an attempt is made to deduce it using git tags\n        |of the workspace repository. If this fails (e.g. no git repository is present), the version is set to `0.1.0-SNAPSHOT`.\n        |You can override this behaviour by passing the `--project-version` option to Scala CLI or by using a\n        |`//> using projectVersion` directive.\n        |\n        |Please note that only tags that follow the semantic versioning are taken into consideration.\n        |\n        |Values available for project version configuration are:\n        |- `git:tag` or `git`: use the latest stable git tag, if it is older than HEAD then try to increment it\n        |    and add a suffix `-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`\n        |- `git:dynver`: use the latest (stable or unstable) git tag, if it is older than HEAD then use the output of\n        |    `-{distance from last tag}-g{shortened version of HEAD commit hash}-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`\n        |\n        |The difference between stable and unstable tags are, that the latter can contain letters, e.g. `v0.1.0-RC1`.\n        |It is also possible to specify the path to the repository, e.g. `git:tag:../my-repo`, `git:dynver:../my-repo`.\n        |\n        |\"\"\".stripMargin\n    )\n\n    b.mkString\n  }\n\n  private def envVarContent(groups: Seq[EnvVar.EnvVarGroup], onlyRestricted: Boolean): String = {\n    val b = new StringBuilder\n    b.section(\n      \"\"\"---\n        |title: Environment variables\n        |sidebar_position: 7\n        |---\"\"\".stripMargin\n    )\n    b.section(\n      \"\"\"Scala CLI uses environment variables to configure its behavior.\n        |Below you can find a list of environment variables used and recognized by Scala CLI.\n        |\n        |However, it should by no means be treated as an exhaustive list.\n        |Some tools and libraries Scala CLI integrates with may have their own, which may or may not be listed here.\n        |\"\"\".stripMargin\n    )\n    groups.foreach { group =>\n      b.section(\n        s\"## ${group.groupName}\",\n        group.all\n          .filter(ev => !ev.requiresPower || !onlyRestricted)\n          .map(ev => s\"  - `${ev.name}`: ${if ev.requiresPower then \"⚡ \" else \"\"}${ev.description}\")\n          .mkString(\"\\n\")\n      )\n    }\n    b.mkString\n  }\n\n  def run(options: InternalDocOptions, args: RemainingArgs): Unit = {\n\n    val scalaCli = new ScalaCliCommands(\n      \"scala-cli\",\n      ScalaCli.baseRunnerName,\n      ScalaCli.fullRunnerName\n    )\n    val commands           = scalaCli.commands\n    val restrictedCommands =\n      commands.iterator.collect {\n        case s: ScalaCommand[_] if !s.isRestricted && !s.isExperimental => s\n      }.toSeq\n    val allArgs       = commands.flatMap(actualHelp(_).args)\n    val nameFormatter = scalaCli.actualDefaultCommand.nameFormatter\n\n    val allCliOptionsContent        = cliOptionsContent(commands, allArgs, nameFormatter)\n    val restrictedCliOptionsContent =\n      cliOptionsContent(restrictedCommands, allArgs, nameFormatter, onlyRestricted = true)\n\n    val allCommandsContent        = commandsContent(commands, onlyRestricted = false)\n    val restrictedCommandsContent = commandsContent(restrictedCommands, onlyRestricted = true)\n\n    val scalaOptionsReference = optionsReference(restrictedCommands, nameFormatter)\n\n    val allUsingDirectiveHandlers = usingDirectiveHandlers ++ usingDirectiveWithReqsHandlers\n    val allDirectivesContent      = usingContent(\n      allUsingDirectiveHandlers,\n      requireDirectiveHandlers,\n      onlyRestricted = false\n    )\n\n    val restrictedDirectivesContent = usingContent(\n      allUsingDirectiveHandlers.filterNot(_.isRestricted),\n      requireDirectiveHandlers.filterNot(_.isRestricted),\n      onlyRestricted = true\n    )\n    val restrictedDocsDir = os.rel / \"scala-command\"\n\n    val allEnvVarsContent        = envVarContent(EnvVar.allGroups, onlyRestricted = false)\n    val restrictedEnvVarsContent = envVarContent(Seq(EnvVar.ScalaCli), onlyRestricted = true)\n\n    if (options.check) {\n      val content = Seq(\n        (os.rel / \"cli-options.md\")                           -> allCliOptionsContent,\n        (os.rel / \"commands.md\")                              -> allCommandsContent,\n        (os.rel / \"directives.md\")                            -> allDirectivesContent,\n        (os.rel / \"build-info.md\")                            -> buildInfoContent,\n        (os.rel / \"env-vars.md\")                              -> allEnvVarsContent,\n        (os.rel / restrictedDocsDir / \"cli-options.md\")       -> restrictedCliOptionsContent,\n        (os.rel / restrictedDocsDir / \"commands.md\")          -> restrictedCommandsContent,\n        (os.rel / restrictedDocsDir / \"directives.md\")        -> restrictedDirectivesContent,\n        (os.rel / restrictedDocsDir / \"runner-specification\") -> scalaOptionsReference,\n        (os.rel / restrictedDocsDir / \"env-vars.md\")          -> restrictedEnvVarsContent\n      )\n      var anyDiff = false\n      for ((dest, content0) <- content) {\n        val dest0   = options.outputPath / dest\n        val diffOpt = maybeDiff(options.outputPath / dest, content0)\n        diffOpt match {\n          case Some(diff) =>\n            anyDiff = true\n            System.err.println(Console.RED + prettyPath(dest0) + Console.RESET + \" differs:\")\n            System.err.println(diff.unifiedDiff)\n          case None =>\n            System.err.println(\n              Console.GREEN + prettyPath(dest0) + Console.RESET + \" is up-to-date.\"\n            )\n        }\n      }\n      if (anyDiff)\n        sys.exit(1)\n    }\n    else {\n      maybeWrite(options.outputPath / \"cli-options.md\", allCliOptionsContent)\n      maybeWrite(options.outputPath / \"commands.md\", allCommandsContent)\n      maybeWrite(options.outputPath / \"directives.md\", allDirectivesContent)\n      maybeWrite(options.outputPath / \"build-info.md\", buildInfoContent)\n      maybeWrite(options.outputPath / \"env-vars.md\", allEnvVarsContent)\n\n      maybeWrite(\n        options.outputPath / restrictedDocsDir / \"cli-options.md\",\n        restrictedCliOptionsContent\n      )\n      maybeWrite(options.outputPath / restrictedDocsDir / \"commands.md\", restrictedCommandsContent)\n      maybeWrite(\n        options.outputPath / restrictedDocsDir / \"directives.md\",\n        restrictedDirectivesContent\n      )\n      maybeWrite(\n        options.outputPath / restrictedDocsDir / \"runner-specification.md\",\n        scalaOptionsReference\n      )\n      maybeWrite(\n        options.outputPath / restrictedDocsDir / \"env-vars.md\",\n        restrictedEnvVarsContent\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/generate-reference-doc/src/main/scala/scala/cli/doc/InternalDocOptions.scala",
    "content": "package scala.cli.doc\n\nimport caseapp.*\n\nfinal case class InternalDocOptions(\n  outputDir: String = \"website/docs/reference\",\n  check: Boolean = false\n) {\n  lazy val outputPath: os.Path =\n    os.Path(outputDir, os.pwd)\n}\n\nobject InternalDocOptions {\n  implicit lazy val parser: Parser[InternalDocOptions] = Parser.derive\n  implicit lazy val help: Help[InternalDocOptions]     = Help.derive\n}\n"
  },
  {
    "path": "modules/generate-reference-doc/src/main/scala/scala/cli/doc/ReferenceDocUtils.scala",
    "content": "package scala.cli.doc\n\nimport caseapp.HelpMessage\n\nimport scala.annotation.tailrec\nimport scala.build.internals.ConsoleUtils.*\n\nobject ReferenceDocUtils {\n  extension (s: String) {\n    def consoleToFence: String = {\n      @tailrec\n      def consoleToFenceRec(\n        remainingLines: Seq[String],\n        fenceOpen: Boolean = false,\n        acc: String = \"\"\n      ): String =\n        remainingLines.headOption match\n          case None       => acc\n          case Some(line) =>\n            val openFenceString =\n              if line.contains(Console.BOLD) then\n                \"\"\"```sh\n                  |\"\"\".stripMargin\n              else if line.contains(ScalaCliConsole.GRAY) then\n                \"\"\"```scala\n                  |\"\"\".stripMargin\n              else \"\"\n            val currentFenceOpen = fenceOpen || openFenceString.nonEmpty\n            val closeFenceString =\n              if currentFenceOpen && line.contains(Console.RESET) then\n                \"\"\"\n                  |```\"\"\".stripMargin\n              else \"\"\n            val newFenceOpen = currentFenceOpen && closeFenceString.isEmpty\n            val newLine      = s\"$openFenceString${line.noConsoleKeys}$closeFenceString\"\n            val newAcc       =\n              if acc.isEmpty then newLine\n              else\n                s\"\"\"$acc\n                   |$newLine\"\"\".stripMargin\n            consoleToFenceRec(remainingLines.tail, newFenceOpen, newAcc)\n      consoleToFenceRec(s.linesIterator.toSeq)\n    }\n    def filterOutHiddenStrings: String =\n      s\n        .replace(s\"${ScalaCliConsole.GRAY}(hidden)${Console.RESET} \", \"\")\n        .replace(s\"${ScalaCliConsole.GRAY}(experimental)${Console.RESET} \", \"\")\n        .replace(s\"${ScalaCliConsole.GRAY}(power)${Console.RESET} \", \"\")\n    def consoleYellowToMdBullets: String = s.replace(Console.YELLOW, \"- \")\n    def consoleToMarkdown: String = s.filterOutHiddenStrings.consoleYellowToMdBullets.consoleToFence\n  }\n  extension (helpMessage: HelpMessage) {\n    def referenceDocMessage: String         = helpMessage.message.consoleToMarkdown\n    def referenceDocDetailedMessage: String = {\n      val msg =\n        if helpMessage.detailedMessage.nonEmpty then helpMessage.detailedMessage\n        else helpMessage.message\n      msg.consoleToMarkdown\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/docker/src/test/scala/scala/cli/integration/RunDockerTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.ByteArrayOutputStream\nimport java.nio.charset.Charset\nimport java.util.concurrent.TimeUnit\nimport scala.concurrent.duration.{Duration, FiniteDuration}\n\nclass RunDockerTests extends munit.FunSuite {\n  override def munitTimeout: Duration = new FiniteDuration(240, TimeUnit.SECONDS)\n\n  lazy val imageName = Option(System.getenv(\"SCALA_CLI_IMAGE\")).getOrElse {\n    sys.error(\"SCALA_CLI_IMAGE not set\")\n  }\n  lazy val termOpt = if (System.console() == null) Nil else Seq(\"-t\")\n  lazy val ciOpt   = Option(System.getenv(\"CI\")).map(v => Seq(\"-e\", s\"CI=$v\")).getOrElse(Nil)\n  lazy val slimScalaCliImage = \"scala-cli-slim\"\n\n  test(\"run simple app in in docker\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val rawOutput = new ByteArrayOutputStream\n      val cmd       = Seq[os.Shellable](\n        // format: off\n        \"docker\", \"run\", \"--rm\", termOpt, \"-v\", s\"$root:/data\", \"-w\", \"/data\", ciOpt,\n        imageName, fileName\n        // format: on\n      )\n      os.proc(cmd).call(\n        cwd = root,\n        stdout = os.ProcessOutput { (b, len) =>\n          rawOutput.write(b, 0, len)\n          System.err.write(b, 0, len)\n        },\n        mergeErrIntoOut = true\n      )\n      val output = new String(rawOutput.toByteArray, Charset.defaultCharset())\n      expect(output.linesIterator.toVector.last == message)\n    }\n  }\n\n  if (!imageName.contains(slimScalaCliImage)) {\n    test(\"package simple app with native in docker\") {\n      val fileName = \"simple.sc\"\n      val inputs   = TestInputs(\n        os.rel / fileName -> \"\"\"println(\"Hello\")\"\"\"\n      )\n      inputs.fromRoot { root =>\n        val cmdPackage = Seq[os.Shellable](\n          // format: off\n          \"docker\", \"run\", \"--rm\", termOpt, \"-v\", s\"$root:/data\", \"-w\", \"/data\", ciOpt,\n          imageName, \"--power\", \"package\", \"--native\", fileName, \"-o\", \"Hello\"\n          // format: on\n        )\n        val procPackage = os.proc(cmdPackage).call(cwd = root, check = false)\n        expect(procPackage.exitCode == 0)\n      }\n    }\n    test(\"package simple app with graalVM in docker\") {\n      val fileName = \"simple.sc\"\n      val inputs   = TestInputs(\n        os.rel / fileName -> \"\"\"println(\"Hello\")\"\"\"\n      )\n      inputs.fromRoot { root =>\n        val cmdPackage = Seq[os.Shellable](\n          // format: off\n          \"docker\", \"run\", \"--rm\", termOpt, \"-v\", s\"$root:/data\", \"-w\", \"/data\", ciOpt,\n          imageName, \"--power\", \"package\", \"--native-image\", fileName, \"-o\", \"Hello\"\n          // format: on\n        )\n        val procPackage = os.proc(cmdPackage).call(cwd = root, check = false)\n        expect(procPackage.exitCode == 0)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/main/scala/scala/cli/integration/TestInputs.scala",
    "content": "package scala.cli.integration\n\nimport java.io.{FileOutputStream, IOException}\nimport java.nio.charset.{Charset, StandardCharsets}\nimport java.security.SecureRandom\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.zip.{ZipEntry, ZipOutputStream}\n\nimport scala.cli.integration.TestInputs.compress\nimport scala.util.control.NonFatal\n\nfinal case class TestInputs(maybeCharset: Option[Charset], files: (os.RelPath, String)*) {\n  private lazy val charset = maybeCharset.getOrElse(StandardCharsets.UTF_8)\n  def add(extraFiles: (os.RelPath, String)*): TestInputs = TestInputs((files ++ extraFiles)*)\n\n  private def writeIn(dir: os.Path): Unit =\n    for ((relPath, content) <- files) {\n      val path = dir / relPath\n      os.write(path, content.getBytes(charset), createFolders = true)\n    }\n  def root(): os.Path = {\n    val tmpDir = TestInputs.tmpDir\n    writeIn(tmpDir)\n    tmpDir\n  }\n  def asZip[T](f: (os.Path, os.Path) => T): T =\n    TestInputs.withTmpDir { tmpDir =>\n      val zipArchivePath = tmpDir / s\"${tmpDir.last}.zip\"\n      compress(zipArchivePath, files.map { case (relPath, content) => (relPath, content, charset) })\n      f(tmpDir, zipArchivePath)\n    }\n  def fromRoot[T](f: os.Path => T): T =\n    TestInputs.withTmpDir { tmpDir =>\n      writeIn(tmpDir)\n      f(tmpDir)\n    }\n  def fileNames: Seq[String] = files.flatMap(_._1.lastOpt)\n}\n\nobject TestInputs {\n  def apply(files: (os.RelPath, String)*): TestInputs = new TestInputs(None, files*)\n\n  def apply(charsetName: String, files: (os.RelPath, String)*): TestInputs = {\n    val charset: Charset = Charset.forName(charsetName)\n    new TestInputs(Some(charset), files*)\n  }\n\n  def empty: TestInputs = TestInputs()\n\n  def compress(zipFilepath: os.Path, files: Seq[(os.RelPath, String, Charset)]) = {\n    val zip = new ZipOutputStream(new FileOutputStream(zipFilepath.toString()))\n    try for ((relPath, content, charset) <- files) {\n        zip.putNextEntry(new ZipEntry(relPath.toString()))\n        val in: Array[Byte] = content.getBytes(charset)\n        zip.write(in)\n        zip.closeEntry()\n      }\n    finally zip.close()\n  }\n\n  private lazy val baseTmpDir = {\n    Option(System.getenv(\"SCALA_CLI_TMP\")).getOrElse {\n      sys.error(\"SCALA_CLI_TMP not set\")\n    }\n    val base = os.Path(System.getenv(\"SCALA_CLI_TMP\"), os.pwd)\n    val rng  = new SecureRandom\n    val d    = base / s\"run-${math.abs(rng.nextInt().toLong)}\"\n    os.makeDir.all(d)\n    Runtime.getRuntime.addShutdownHook(\n      new Thread(\"scala-cli-its-clean-up-tmp-dir\") {\n        setDaemon(true)\n        override def run(): Unit =\n          try os.remove.all(d)\n          catch {\n            case NonFatal(_) =>\n              System.err.println(s\"Could not remove $d, ignoring it.\")\n          }\n      }\n    )\n    d\n  }\n\n  private val tmpCount = new AtomicInteger\n\n  private def withTmpDir[T](f: os.Path => T): T = {\n    val tmpDir = baseTmpDir / s\"test-${tmpCount.incrementAndGet()}\"\n    os.makeDir.all(tmpDir)\n    val tmpDir0           = os.Path(tmpDir.toIO.getCanonicalFile)\n    def removeAll(): Unit =\n      try os.remove.all(tmpDir0)\n      catch {\n        case ex: IOException =>\n          System.err.println(s\"Ignoring $ex while removing $tmpDir0\")\n      }\n    try f(tmpDir0)\n    finally removeAll()\n  }\n\n  private def tmpDir: os.Path = {\n    val tmpDir = baseTmpDir / s\"test-${tmpCount.incrementAndGet()}\"\n    os.makeDir.all(tmpDir)\n    os.Path(tmpDir.toIO.getCanonicalFile)\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/java/scala/cli/integration/bsp/WrappedSourceItem.java",
    "content": "package scala.cli.integration.bsp;\n\npublic class WrappedSourceItem {\n  public String uri;\n  public String generatedUri;\n  public String topWrapper;\n  public String bottomWrapper;\n}\n"
  },
  {
    "path": "modules/integration/src/test/java/scala/cli/integration/bsp/WrappedSourcesItem.java",
    "content": "package scala.cli.integration.bsp;\n\nimport ch.epfl.scala.bsp4j.BuildTargetIdentifier;\nimport java.util.List;\n\npublic class WrappedSourcesItem {\n  public BuildTargetIdentifier target;\n  public List<WrappedSourceItem> sources;\n}\n"
  },
  {
    "path": "modules/integration/src/test/java/scala/cli/integration/bsp/WrappedSourcesResult.java",
    "content": "package scala.cli.integration.bsp;\n\nimport java.util.List;\n\npublic class WrappedSourcesResult {\n  public List<WrappedSourcesItem> items;\n}\n"
  },
  {
    "path": "modules/integration/src/test/resources/test-keys/key.asc",
    "content": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: BCPG v1.68\n\nmQENBGIXxr8DCAC8aS9gAPsdaZukOb9Q83+5+U8IeDdWhOkwPjbIM534BO/LRYVv\nvKcXcv2X6r5eOnA2NBnAuyZ0GBwiAMVm6agZT2HGY6LjnFlIxn6L/Miz5vlplAXt\nQ72IybRJUTVEUrV1a4mAxZsM5+mXHmQI7BZ97v9//3uJOlUtXLyjCPA7PwmzQCws\nnpdXb+oOodHTUptsG+8r8Y7XYuFXiuvGq6NRY7aESE9pRupfCAwERRAp7qEddyTo\nV5xiVSDAn0vxphKd1ZlKGWsK0SH2Rm+QnZZAM5FW0gaGNzp2n6vXtCVSeMs2ZzlE\nLJCt+ZuxQANuICL9X0YL6hDlq3/co5qSbuxXABEBAAG0Em5vcmVwbHlAZ2l0aHVi\nLmNvbYkBLgQTAwIAGAUCYhfGvwIbAwQLCQgHBhUIAgkKCwIeAQAKCRA7ulNUJg/z\npwFIB/96ntiTdjfr3xzsvCn+iuq3SJBdGALVbIA8htIEfQeafFtFq9fOY26b8efg\nxO5Foe0gtLkCycviR/ok6xMrTsE2Qcq1clUanAkH5RffAz/jea8fYN150FpANvlN\n1Ru3fjX5+FxmPLZa5gj1nPYU+t9uvRphAQxAFaDxbUOgaCfPD03pdLWBhhlG+wrE\nIvR2aY1JdATgsHqFcpJal6Qs0nNQAX/298OLegcKKEBAllVZ+rwbZ9NEn23X6YP3\nwBerflBiV7KDf70R49d99i64V7A2Jh+LiAmyBqRn2E+dUluTX+d1Cv3dia6/dDxL\n/MQCI5BBKoTFiunGRpolcvu+MKnZuQENBGIXxr8CCADScBUwWHEH8JK2LvPfJRn0\noKpz1xYr1e4Uhh+LangmoESHXfvr+NMtKpMJzhfzKqSREVsQ3iMkk0IqZIKEf2Ay\nMzBVnbbHQDVUd2c3fu6H0RIJI9zZlaUVAkBL/8c2BvAjqe94wPJkSUjHYalI177X\nuMmgfpq42gWs3F+W4TmvXkXynOWnNezZWXcJyfODJsraKgBcHJW2Gn86w4EO5jGF\nKR7zNZfSMU+WZuM4XajDpO8iwRNsu/eYlB8jBPLOlV2jqaOJVgjGwzOKyjfD+9j3\n2duOB/da1NWIJi9jzI3tp0PyIAP6fhBuh4Ou5NErasLvU1e/mUAsrYrnLicdBv7x\nABEBAAGJAR8EGAMCAAkFAmIXxr8CGwwACgkQO7pTVCYP86f1dQf8DMEG2LkVH6fB\nqktyVl7cfcHUPa4uqlB2NiHYwrwAr45ftU5/kDBXf9AxoZvoq8pF+y3RMboYu1CU\nFoxoVh7GB+LtpHk1SyWxZcPo48RoANHkpp4y4XndZcJ4XDbaMkrF65E/CGK7etEX\n/P+xMyovli64iaXHpIJ9ejKyhSKpFuD7w5U1UzArIfR8xjwrGeonruyiHTB/ULq5\nTp7dU/066ZxlHddXh3WNYhkaD7b3PJP+hCZzn0+LSiA2cKiQDpLX5xm/iAh8CWyq\nN2Ix77qdIgS7lWd0CEomQ3QV4SYff4u13aUhAer2YZcmFhsejHp2hJD0pvUW41f5\neiL+IwMGMQ==\n=TGbn\n-----END PGP PUBLIC KEY BLOCK-----\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ArgsFileTests.scala",
    "content": "package scala.cli.integration\n\nimport scala.util.Properties\n\nclass ArgsFileTests extends ScalaCliSuite {\n\n  val forOption = List(true, false)\n\n  for (useServer <- forOption) test(\n    s\"pass scalac options using arguments file ${if (useServer) \"with bloop\" else \"without bloop\"}\"\n  ) {\n    val fileName   = \"Simple.sc\"\n    val serverArgs = if (useServer) Nil else List(\"--server=false\")\n    val inputs     = TestInputs(\n      os.rel / \"args.txt\" -> \"\"\"|-release\n                                |8\"\"\".stripMargin,\n      os.rel / fileName ->\n        s\"\"\"|\n            |println(\"Hello :)\".repeat(11))\n            |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, serverArgs, \"@args.txt\", fileName).call(\n        cwd = root,\n        check = false,\n        stderr = os.Pipe\n      )\n      assert(res.exitCode == 1)\n\n      val compilationError = res.err.text()\n      assert(compilationError.contains(\"Compilation failed\"))\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"pass scalac options using arguments file in shebang script\") {\n      val inputs = TestInputs(\n        os.rel / \"args.txt\"            -> \"\"\"|-release 8\"\"\".stripMargin,\n        os.rel / \"script-with-shebang\" ->\n          s\"\"\"|#!/usr/bin/env -S ${TestUtil.cli.mkString(\" \")} shebang @args.txt\n              |\n              |println(\"Hello :)\".repeat(11))\n              |\"\"\".stripMargin\n      )\n\n      inputs.fromRoot { root =>\n        os.perms.set(root / \"script-with-shebang\", os.PermSet.fromString(\"rwx------\"))\n        val res = os.proc(\"./script-with-shebang\").call(cwd = root, check = false, stderr = os.Pipe)\n        assert(res.exitCode == 1)\n\n        val compilationError = res.err.text()\n        assert(compilationError.contains(\"Compilation failed\"))\n      }\n    }\n\n  test(\"multiple args files\") {\n    val preCompileDir = \"PreCompileDir\"\n    val runDir        = \"RunDir\"\n\n    val preCompiledInput = \"Message.scala\"\n    val mainInput        = \"Main.scala\"\n\n    val expectedOutput = \"Hello\"\n\n    val outputDir = os.rel / \"out\"\n\n    TestInputs(\n      os.rel / preCompileDir / preCompiledInput -> \"case class Message(value: String)\",\n      os.rel / runDir / mainInput               ->\n        s\"\"\"object Main extends App { println(Message(\"$expectedOutput\").value) }\"\"\",\n      os.rel / runDir / \"args.txt\" -> s\"\"\"|-d\n                                          |$outputDir\"\"\".stripMargin,\n      os.rel / runDir / \"args2.txt\" ->\n        s\"\"\"|-cp\n            |${os.rel / os.up / preCompileDir / outputDir}\"\"\".stripMargin\n    ).fromRoot { (root: os.Path) =>\n\n      os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \"--scala-opt\",\n        \"-d\",\n        \"--scala-opt\",\n        outputDir.toString,\n        preCompiledInput\n      ).call(cwd = root / preCompileDir, stderr = os.Pipe)\n      assert((root / preCompileDir / outputDir / \"Message.class\").toNIO.toFile().exists())\n\n      val compileOutput = root / runDir / outputDir\n      os.makeDir.all(compileOutput)\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"run\",\n        \"@args.txt\",\n        \"--server=false\",\n        \"@args2.txt\",\n        mainInput\n      ).call(cwd = root / runDir, stderr = os.Pipe)\n      assert(runRes.out.trim() == expectedOutput)\n      assert((compileOutput / \"Main.class\").toNIO.toFile().exists())\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BloopTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.util.BloopUtil\nimport scala.concurrent.ExecutionContext\nimport scala.concurrent.duration.Duration\nimport scala.util.Properties\n\nclass BloopTests extends ScalaCliSuite {\n  def runScalaCli(args: String*): os.proc = os.proc(TestUtil.cli, args)\n\n  private lazy val bloopDaemonDir =\n    BloopUtil.bloopDaemonDir(runScalaCli(\"--power\", \"directories\").call().out.text())\n\n  val dummyInputs: TestInputs = TestInputs(\n    os.rel / \"Test.scala\" ->\n      \"\"\"//> using scala 2.13\n        |object Test {\n        |  def main(args: Array[String]): Unit =\n        |    println(\"Hello \" + \"from test\")\n        |}\n        |\"\"\".stripMargin\n  )\n\n  def testScalaTermination(\n    currentBloopVersion: String,\n    shouldRestart: Boolean\n  ): Unit = TestUtil.retryOnCi() {\n    dummyInputs.fromRoot { root =>\n      BloopUtil.killBloop()\n\n      val bloop = BloopUtil.bloop(currentBloopVersion, bloopDaemonDir)\n      bloop(Seq(\"about\")).call(cwd = root, stdout = os.Inherit)\n\n      val output = os.proc(TestUtil.cli, \"run\", \".\")\n        .call(cwd = root, stderr = os.Pipe, mergeErrIntoOut = true)\n        .out.text()\n      expect(output.contains(\"Hello from test\"))\n      if (shouldRestart)\n        output.contains(\"Shutting down unsupported Bloop\")\n      else\n        output.contains(\"No need to restart Bloop\")\n\n      val versionLine = bloop(Seq(\"about\")).call(cwd = root).out.lines()(0)\n      expect(versionLine == \"bloop v\" + Constants.bloopVersion)\n    }\n  }\n\n  // Disabled until we have at least 2 Bleep releases\n  // test(\"scala-cli terminates incompatible bloop\") {\n  //   testScalaTermination(\"1.4.8-122-794af022\", shouldRestart = true)\n  // }\n\n  test(\"scala-cli keeps compatible bloop running\") {\n    testScalaTermination(Constants.bloopVersion, shouldRestart = false)\n  }\n\n  test(\"invalid bloop options passed via global bloop config json file cause bloop start failure\") {\n    val inputs = TestInputs(\n      os.rel / \"bloop.json\" ->\n        \"\"\"|{\n           | \"javaOptions\" : [\"-Xmx1k\"]\n           | }\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      runScalaCli(\"--power\", \"bloop\", \"exit\").call()\n      val res = runScalaCli(\n        \"--power\",\n        \"bloop\",\n        \"start\",\n        \"--bloop-global-options-file\",\n        (root / \"bloop.json\").toString()\n      ).call(cwd = root, stderr = os.Pipe, check = false)\n      expect(res.exitCode == 1)\n      expect(res.err.text().contains(\"Server failed with exit code 1\") || res.err.text().contains(\n        \"java.lang.OutOfMemoryError: Garbage-collected heap size exceeded\"\n      ))\n    }\n  }\n\n  test(\"bloop exit works\") {\n    def bloopRunning(): Boolean = {\n      val javaProcesses = os.proc(\"jps\", \"-l\").call().out.text()\n      javaProcesses.contains(\"bloop.BloopServer\")\n    }\n\n    val inputs = TestInputs.empty\n    inputs.fromRoot { _ =>\n      BloopUtil.killBloop()\n      TestUtil.retry()(assert(!bloopRunning()))\n\n      val res = runScalaCli(\"--power\", \"bloop\", \"start\").call(check = false)\n      assert(res.exitCode == 0, clues(res.out.text()))\n      assert(bloopRunning(), clues(res.out.text()))\n\n      val resExit = runScalaCli(\"--power\", \"bloop\", \"exit\").call(check = false)\n      assert(resExit.exitCode == 0, clues(resExit.out.text()))\n      assert(!bloopRunning())\n    }\n  }\n\n  test(\"bloop projects and bloop compile works\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"object Hello {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n\n      os.proc(TestUtil.cli, \"compile\", \".\")\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val projRes = os.proc(TestUtil.cli, \"--power\", \"bloop\", \"projects\")\n        .call(cwd = root / Constants.workspaceDirName)\n\n      val projList = projRes.out.trim().linesIterator.toVector\n\n      expect(projList.length == 1)\n      val proj = projList.head\n\n      os.proc(TestUtil.cli, \"--power\", \"bloop\", \"compile\", proj)\n        .call(cwd = root / Constants.workspaceDirName)\n\n      val failRes = os.proc(TestUtil.cli, \"--power\", \"bloop\", \"foo\")\n        .call(cwd = root / Constants.workspaceDirName, check = false, mergeErrIntoOut = true)\n      val failOutput = failRes.out.text()\n      expect(failRes.exitCode == 4)\n      expect(failOutput.contains(\"Command not found: foo\"))\n    }\n  }\n\n  if (!Properties.isMac || !TestUtil.isNativeCli || !TestUtil.isCI)\n    // TODO make this pass reliably on Mac CI\n    test(\"Restart Bloop server while watching\") {\n      TestUtil.withThreadPool(\"bloop-restart-test\", 2) { pool =>\n        val timeout = Duration(\"90 seconds\")\n        val ec      = ExecutionContext.fromExecutorService(pool)\n\n        def content(message: String) =\n          s\"\"\"object Hello {\n             |  def main(args: Array[String]): Unit =\n             |    println(\"$message\")\n             |}\n             |\"\"\".stripMargin\n        val sourcePath = os.rel / \"Hello.scala\"\n        val inputs     = TestInputs(\n          sourcePath -> content(\"Hello\")\n        )\n        inputs.fromRoot { root =>\n          val proc = os.proc(TestUtil.cli, \"run\", \"--power\", \"--offline\", \"-w\", \".\")\n            .spawn(cwd = root)\n          val firstLine = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(firstLine == \"Hello\")\n\n          os.proc(TestUtil.cli, \"--power\", \"bloop\", \"exit\")\n            .call(cwd = root)\n\n          os.write.over(root / sourcePath, content(\"Foo\"))\n          val secondLine = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(secondLine == \"Foo\")\n\n          proc.destroy()\n        }\n      }\n    }\n\n  test(\"run bloop with jvm version if > 17\") {\n    val hello  = \"Hello from Java 21\"\n    val inputs = TestInputs(\n      os.rel / \"Simple.java\" ->\n        s\"\"\"|//> using jvm 21\n            |//> using javacOpt --enable-preview -Xlint:preview --release 21\n            |//> using javaOpt --enable-preview\n            |//> using mainClass Simple\n            |\n            |void main() {\n            |    System.out.println(\"$hello\");\n            |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      // start bloop with jvm 17\n      runScalaCli(\"--power\", \"bloop\", \"start\", \"--jvm\", \"17\").call(cwd = root)\n      val res = runScalaCli(\"Simple.java\").call(cwd = root)\n      res.out.text().contains(hello)\n      // shut down bloop so other tests are run on JDK 17\n      runScalaCli(\"bloop\", \"exit\", \"--power\").call(cwd = root)\n    }\n  }\n\n  for {\n    lang         <- List(\"scala\", \"java\")\n    useDirective <- List(true, false)\n    option       <- List(\"java-home\", \"jvm\")\n    jvm = Constants.allJavaVersions.filter(_ < 23).max\n  }\n    test(s\"compiles $lang file with correct jdk version ($jvm) for $option ${\n        if useDirective then \"use directive\" else \"option\"\n      }\") {\n      def isScala     = lang == \"scala\"\n      val optionValue =\n        if option == \"java-home\" then\n          os.Path(os.proc(TestUtil.cs, \"java-home\", \"--jvm\", jvm).call().out.trim()).toString()\n        else jvm.toString\n      val directive =\n        if useDirective then s\"//> using ${option.replace(\"-h\", \"H\")} $optionValue\\n\" else \"\"\n      val options = if useDirective then Nil else List(s\"--$option\", optionValue)\n      val content =\n        if isScala then\n          \"object Simple { System.out.println(javax.print.attribute.standard.OutputBin.LEFT) }\"\n        else \"\"\"public class Simple {\n              |  public static void main(String[] args) {\n              |      System.out.println(javax.print.attribute.standard.OutputBin.LEFT);\n              |  }\n              |}\"\"\".stripMargin\n      val inputs = TestInputs(os.rel / s\"Simple.$lang\" -> s\"$directive$content\")\n\n      inputs.fromRoot { root =>\n        val res =\n          runScalaCli(\"compile\" :: \".\" :: options*).call(root, check = false, stderr = os.Pipe)\n        assert(res.exitCode == 1)\n\n        val compilationError = res.err.text()\n        val message          =\n          if isScala\n          then \"value OutputBin is not a member of javax.print.attribute.standard\"\n          else \"cannot find symbol\"\n\n        assert(compilationError.contains(\"Compilation failed\"))\n        assert(compilationError.contains(message))\n      }\n    }\n\n  {\n    val bloopSnapshotVersion = \"2.0.6-51-38c118d4-SNAPSHOT\"\n    test(s\"compilation works with a Bloop snapshot version: $bloopSnapshotVersion\".flaky) {\n      val input = \"script.sc\"\n      TestInputs(os.rel / input -> \"\"\"println(\"Hello\")\"\"\").fromRoot { root =>\n        os.proc(TestUtil.cli, \"compile\", input, \"--bloop-version\", bloopSnapshotVersion)\n          .call(cwd = root)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspSuite.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport com.google.gson.Gson\nimport com.google.gson.internal.LinkedTreeMap\nimport org.eclipse.lsp4j.jsonrpc.messages.ResponseError\n\nimport java.net.URI\nimport java.util.concurrent.{ExecutorService, ScheduledExecutorService}\n\nimport scala.annotation.tailrec\nimport scala.cli.integration.BspSuite.{Details, detailsCodec}\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.duration.*\nimport scala.concurrent.{Await, Future, Promise}\nimport scala.jdk.CollectionConverters.*\nimport scala.util.control.NonFatal\nimport scala.util.{Failure, Success, Try}\n\ntrait BspSuite { this: ScalaCliSuite =>\n  protected def extraOptions: Seq[String]\n  def initParams(root: os.Path): b.InitializeBuildParams =\n    new b.InitializeBuildParams(\n      \"Scala CLI ITs\",\n      \"0\",\n      Constants.bspVersion,\n      root.toNIO.toUri.toASCIIString,\n      new b.BuildClientCapabilities(List(\"java\", \"scala\").asJava)\n    )\n\n  val pool: ExecutorService               = TestUtil.threadPool(\"bsp-tests-jsonrpc\", 4)\n  val scheduler: ScheduledExecutorService = TestUtil.scheduler(\"bsp-tests-scheduler\")\n\n  def completeIn(duration: FiniteDuration): Future[Unit] = {\n    val p = Promise[Unit]()\n    scheduler.schedule(\n      new Runnable {\n        def run(): Unit =\n          try p.success(())\n          catch {\n            case t: Throwable =>\n              System.err.println(s\"Caught $t while trying to complete timer, ignoring it\")\n          }\n      },\n      duration.length,\n      duration.unit\n    )\n    p.future\n  }\n\n  override def afterAll(): Unit = {\n    pool.shutdown()\n  }\n\n  protected def extractMainTargets(targets: Seq[b.BuildTargetIdentifier]): b.BuildTargetIdentifier =\n    targets.collectFirst {\n      case t if !t.getUri.contains(\"-test\") => t\n    }.get\n\n  protected def extractTestTargets(targets: Seq[b.BuildTargetIdentifier]): b.BuildTargetIdentifier =\n    targets.collectFirst {\n      case t if t.getUri.contains(\"-test\") => t\n    }.get\n\n  def withBsp[T](\n    inputs: TestInputs,\n    args: Seq[String],\n    attempts: Int = if (TestUtil.isCI) 3 else 1,\n    pauseDuration: FiniteDuration = 5.seconds,\n    bspOptions: List[String] = List.empty,\n    bspEnvs: Map[String, String] = Map.empty,\n    reuseRoot: Option[os.Path] = None,\n    stdErrOpt: Option[os.RelPath] = None,\n    extraOptionsOverride: Seq[String] = extraOptions\n  )(\n    f: (\n      os.Path,\n      TestBspClient,\n      b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer &\n        TestBspClient.WrappedSourcesBuildServer\n    ) => Future[T]\n  ): T = withBspInitResults(\n    inputs,\n    args,\n    attempts,\n    pauseDuration,\n    bspOptions,\n    bspEnvs,\n    reuseRoot,\n    stdErrOpt,\n    extraOptionsOverride\n  )((root, client, server, _: b.InitializeBuildResult) => f(root, client, server))\n\n  def withBspInitResults[T](\n    inputs: TestInputs,\n    args: Seq[String],\n    attempts: Int = if (TestUtil.isCI) 3 else 1,\n    pauseDuration: FiniteDuration = 5.seconds,\n    bspOptions: List[String] = List.empty,\n    bspEnvs: Map[String, String] = Map.empty,\n    reuseRoot: Option[os.Path] = None,\n    stdErrOpt: Option[os.RelPath] = None,\n    extraOptionsOverride: Seq[String] = extraOptions\n  )(\n    f: (\n      os.Path,\n      TestBspClient,\n      b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer &\n        TestBspClient.WrappedSourcesBuildServer,\n      b.InitializeBuildResult\n    ) => Future[T]\n  ): T = {\n\n    def attempt(): Try[T] = Try {\n      val inputsRoot                              = inputs.root()\n      val root                                    = reuseRoot.getOrElse(inputsRoot)\n      val stdErrPathOpt: Option[os.ProcessOutput] = stdErrOpt.map(path => root / path)\n      val stderr: os.ProcessOutput                = stdErrPathOpt.getOrElse(os.Inherit)\n\n      val proc = os.proc(TestUtil.cli, \"bsp\", bspOptions ++ extraOptionsOverride, args)\n        .spawn(cwd = root, stderr = stderr, env = bspEnvs)\n      var remoteServer\n        : b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer &\n          TestBspClient.WrappedSourcesBuildServer = null\n\n      val bspServerExited = Promise[Unit]()\n      val t               = new Thread(\"bsp-server-watcher\") {\n        setDaemon(true)\n        override def run() = {\n          proc.join()\n          bspServerExited.success(())\n        }\n      }\n      t.start()\n\n      def whileBspServerIsRunning[T](f: Future[T]): Future[T] = {\n        val ex = new Exception\n        Future.firstCompletedOf(Seq(f.map(Right(_)), bspServerExited.future.map(Left(_))))\n          .transform {\n            case Success(Right(t)) => Success(t)\n            case Success(Left(())) => Failure(new Exception(\"BSP server exited too early\", ex))\n            case Failure(ex)       => Failure(ex)\n          }\n      }\n\n      try {\n        val (localClient, remoteServer0, _) =\n          TestBspClient.connect(proc.stdout, proc.stdin, pool)\n        remoteServer = remoteServer0\n        val initRes: b.InitializeBuildResult = Await.result(\n          whileBspServerIsRunning(remoteServer.buildInitialize(initParams(root)).asScala),\n          Duration.Inf\n        )\n        Await.result(\n          whileBspServerIsRunning(f(root, localClient, remoteServer, initRes)),\n          Duration.Inf\n        )\n      }\n      finally {\n        if (remoteServer != null)\n          try\n            Await.result(whileBspServerIsRunning(remoteServer.buildShutdown().asScala), 20.seconds)\n          catch {\n            case NonFatal(e) =>\n              System.err.println(s\"Ignoring $e while shutting down BSP server\")\n          }\n        proc.join(2.seconds.toMillis)\n        proc.destroy()\n        proc.join(2.seconds.toMillis)\n        proc.destroy(shutdownGracePeriod = 0)\n      }\n    }\n\n    @tailrec\n    def helper(count: Int): T =\n      attempt() match {\n        case Success(t)  => t\n        case Failure(ex) =>\n          if (count <= 1)\n            throw new Exception(ex)\n          else {\n            System.err.println(s\"Caught $ex, trying again in $pauseDuration…\")\n            Thread.sleep(pauseDuration.toMillis)\n            helper(count - 1)\n          }\n      }\n\n    helper(attempts)\n  }\n\n  def checkTargetUri(root: os.Path, uri: String): Unit = {\n    val baseUri =\n      TestUtil.normalizeUri((root / Constants.workspaceDirName).toNIO.toUri.toASCIIString)\n        .stripSuffix(\"/\")\n    val expectedPrefixes = Set(\n      baseUri + \"?id=\",\n      baseUri + \"/?id=\"\n    )\n    expect(expectedPrefixes.exists(uri.startsWith))\n  }\n\n  protected def readBspConfig(\n    root: os.Path,\n    connectionJsonFileName: String = \"scala-cli.json\"\n  ): Details = {\n    val bspFile = root / \".bsp\" / connectionJsonFileName\n    expect(os.isFile(bspFile))\n    val content = os.read.bytes(bspFile)\n    // check that we can decode the connection details\n    readFromArray(content)(detailsCodec)\n  }\n\n  protected def checkIfBloopProjectIsInitialised(\n    root: os.Path,\n    buildTargetsResp: b.WorkspaceBuildTargetsResult\n  ): Unit = {\n    val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n    expect(targets.length == 2)\n\n    val bloopProjectNames = targets.map { target =>\n      val targetUri = TestUtil.normalizeUri(target.getUri)\n      checkTargetUri(root, targetUri)\n      new URI(targetUri).getQuery.stripPrefix(\"id=\")\n    }\n\n    val bloopDir = root / Constants.workspaceDirName / \".bloop\"\n    expect(os.isDir(bloopDir))\n\n    bloopProjectNames.foreach { bloopProjectName =>\n      val bloopProjectJsonPath = bloopDir / s\"$bloopProjectName.json\"\n      expect(os.isFile(bloopProjectJsonPath))\n    }\n  }\n\n  protected def extractDiagnosticsParams(\n    relevantFilePath: os.Path,\n    localClient: TestBspClient\n  ): b.PublishDiagnosticsParams = {\n    val params = localClient.latestDiagnostics().getOrElse {\n      sys.error(\"No diagnostics found\")\n    }\n    expect {\n      TestUtil.normalizeUri(params.getTextDocument.getUri) == TestUtil.normalizeUri(\n        relevantFilePath.toNIO.toUri.toASCIIString\n      )\n    }\n    params\n  }\n\n  protected def checkDiagnostic(\n    diagnostic: b.Diagnostic,\n    expectedMessage: String,\n    expectedSeverity: b.DiagnosticSeverity,\n    expectedStartLine: Int,\n    expectedStartCharacter: Int,\n    expectedEndLine: Int,\n    expectedEndCharacter: Int,\n    expectedSource: Option[String] = None,\n    strictlyCheckMessage: Boolean = true\n  ): Unit = {\n    expect(diagnostic.getSeverity == expectedSeverity)\n    expect(diagnostic.getRange.getStart.getLine == expectedStartLine)\n    expect(diagnostic.getRange.getStart.getCharacter == expectedStartCharacter)\n    expect(diagnostic.getRange.getEnd.getLine == expectedEndLine)\n    expect(diagnostic.getRange.getEnd.getCharacter == expectedEndCharacter)\n    val message = TestUtil.removeAnsiColors(diagnostic.getMessage)\n    if (strictlyCheckMessage)\n      assertNoDiff(message, expectedMessage)\n    else\n      expect(message.contains(expectedMessage))\n    for (es <- expectedSource)\n      expect(diagnostic.getSource == es)\n  }\n\n  protected def checkScalaAction(\n    diagnostic: b.Diagnostic,\n    expectedActionsSize: Int,\n    expectedTitle: String,\n    expectedChanges: Int,\n    expectedStartLine: Int,\n    expectedStartCharacter: Int,\n    expectedEndLine: Int,\n    expectedEndCharacter: Int,\n    expectedNewText: String\n  ): Unit = {\n    expect(diagnostic.getDataKind == \"scala\")\n\n    val gson = new com.google.gson.Gson()\n\n    val scalaDiagnostic: b.ScalaDiagnostic = gson.fromJson(\n      diagnostic.getData.toString,\n      classOf[b.ScalaDiagnostic]\n    )\n\n    val actions = scalaDiagnostic.getActions.asScala\n\n    expect(actions.size == expectedActionsSize)\n\n    val action = actions.head\n    expect(action.getTitle == expectedTitle)\n\n    val edit = action.getEdit\n    expect(edit.getChanges.asScala.size == expectedChanges)\n    val change = edit.getChanges.asScala.head\n\n    val expectedRange = new b.Range(\n      new b.Position(expectedStartLine, expectedStartCharacter),\n      new b.Position(expectedEndLine, expectedEndCharacter)\n    )\n    expect(change.getRange == expectedRange)\n    expect(change.getNewText == expectedNewText)\n  }\n\n  protected def extractWorkspaceReloadResponse(workspaceReloadResult: AnyRef)\n    : Option[ResponseError] =\n    workspaceReloadResult match {\n      case gsonMap: LinkedTreeMap[?, ?] if !gsonMap.isEmpty =>\n        val gson = new Gson()\n        Some(gson.fromJson(gson.toJson(gsonMap), classOf[ResponseError]))\n      case _ => None\n    }\n}\n\nobject BspSuite {\n  final protected case class Details(\n    name: String,\n    version: String,\n    bspVersion: String,\n    argv: List[String],\n    languages: List[String]\n  )\n  protected val detailsCodec: JsonValueCodec[Details] = JsonCodecMaker.make\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport ch.epfl.scala.bsp4j.JvmTestEnvironmentParams\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.google.gson.{Gson, JsonElement}\n\nimport java.net.URI\nimport java.nio.file.Paths\n\nimport scala.cli.integration.TestUtil.await\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\nimport scala.concurrent.duration.*\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nabstract class BspTestDefinitions extends ScalaCliSuite\n    with TestScalaVersionArgs\n    with BspSuite\n    with ScriptWrapperTestDefinitions\n    with CoursierScalaInstallationTestHelper {\n  this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n\n  test(\"setup-ide\") {\n    val inputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"val msg = \"Hello\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", extraOptions).call(cwd = root, stdout = os.Inherit)\n      val details = readBspConfig(root)\n      expect(details.argv.length >= 2)\n      expect(details.argv.dropWhile(_ != TestUtil.cliPath).drop(1).head == \"bsp\")\n    }\n  }\n\n  for (command <- Seq(\"setup-ide\", \"compile\", \"run\"))\n    test(command + \" should result in generated bsp file\") {\n      val inputs = TestInputs(\n        os.rel / \"simple.sc\" ->\n          s\"\"\"val msg = \"Hello\"\n             |println(msg)\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"--power\", command, \".\", extraOptions)\n          .call(cwd = root, stdout = os.Inherit)\n        val details                = readBspConfig(root)\n        val expectedIdeOptionsFile = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n        val expectedIdeLaunchFile  = root / Constants.workspaceDirName / \"ide-launcher-options.json\"\n        val expectedIdeInputsFile  = root / Constants.workspaceDirName / \"ide-inputs.json\"\n        val expectedIdeEnvsFile    = root / Constants.workspaceDirName / \"ide-envs.json\"\n        val expectedArgv           = Seq(\n          TestUtil.cliPath,\n          \"--power\",\n          \"bsp\",\n          \"--json-options\",\n          expectedIdeOptionsFile.toString,\n          \"--json-launcher-options\",\n          expectedIdeLaunchFile.toString,\n          \"--envs-file\",\n          expectedIdeEnvsFile.toString,\n          root.toString\n        )\n        if (TestUtil.isJvmBootstrappedCli) {\n          expect(details.argv.head.endsWith(\"java\"))\n          expect(details.argv.drop(1).head == \"-jar\")\n        }\n        expect(details.argv.dropWhile(_ != TestUtil.cliPath) == expectedArgv)\n        expect(os.isFile(expectedIdeOptionsFile))\n        expect(os.isFile(expectedIdeInputsFile))\n        expect(os.isFile(expectedIdeEnvsFile))\n      }\n    }\n\n  val importPprintOnlyProject: TestInputs = TestInputs(\n    os.rel / \"simple.sc\" -> s\"//> using dep \\\"com.lihaoyi::pprint:${Constants.pprintVersion}\\\"\"\n  )\n\n  test(\"setup-ide should have only absolute paths even if relative ones were specified\") {\n    val path   = os.rel / \"directory\" / \"simple.sc\"\n    val inputs =\n      TestInputs(path -> s\"//> using dep \\\"com.lihaoyi::pprint:${Constants.pprintVersion}\\\"\")\n    inputs.fromRoot { root =>\n      val relativeCliCommand = TestUtil.cliCommand(\n        TestUtil.relPathStr(os.Path(TestUtil.cliPath).relativeTo(root))\n      )\n\n      val proc =\n        if (Properties.isWin && TestUtil.isNativeCli)\n          os.proc(\n            \"cmd\",\n            \"/c\",\n            (relativeCliCommand ++ Seq(\"--power\", \"setup-ide\", path.toString) ++ extraOptions)\n              .mkString(\" \")\n          )\n        else\n          os.proc(relativeCliCommand, \"--power\", \"setup-ide\", path, extraOptions)\n      proc.call(cwd = root, stdout = os.Inherit)\n\n      val details      = readBspConfig(root / \"directory\")\n      val expectedArgv = List(\n        TestUtil.cliPath,\n        \"--power\",\n        \"bsp\",\n        \"--json-options\",\n        (root / \"directory\" / Constants.workspaceDirName / \"ide-options-v2.json\").toString,\n        \"--json-launcher-options\",\n        (root / \"directory\" / Constants.workspaceDirName / \"ide-launcher-options.json\").toString,\n        \"--envs-file\",\n        (root / \"directory\" / Constants.workspaceDirName / \"ide-envs.json\").toString,\n        (root / \"directory\" / \"simple.sc\").toString\n      )\n      expect(details.argv.dropWhile(_ != TestUtil.cliPath) == expectedArgv)\n    }\n  }\n\n  test(\"setup-ide should succeed for valid dependencies\") {\n    importPprintOnlyProject.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"setup-ide\",\n        \".\",\n        extraOptions,\n        \"--dependency\",\n        s\"org.scalameta::munit:${Constants.munitVersion}\"\n      ).call(cwd = root)\n    }\n  }\n\n  test(\"setup-ide should fail for invalid dependencies\") {\n    importPprintOnlyProject.fromRoot { root =>\n\n      val p = os.proc(\n        TestUtil.cli,\n        \"setup-ide\",\n        \".\",\n        extraOptions,\n        \"--dependency\",\n        \"org.scalameta::munit:0.7.119999\"\n      ).call(cwd = root, check = false, stderr = os.Pipe)\n      expect(p.err.text().contains(s\"Error downloading org.scalameta:munit\"))\n      expect(p.exitCode == 1)\n    }\n  }\n\n  test(\"simple\") {\n    val inputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"val msg = \"Hello\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, _, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        {\n          val resp =\n            remoteServer\n              .buildTargetDependencySources(new b.DependencySourcesParams(targets))\n              .asScala\n              .await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundDepSources = resp.getItems.asScala\n            .flatMap(_.getSources.asScala)\n            .toSeq\n            .map { uri =>\n              val idx = uri.lastIndexOf('/')\n              uri.drop(idx + 1)\n            }\n          if (actualScalaVersion.startsWith(\"2.\")) {\n            expect(foundDepSources.length == 1)\n            expect(foundDepSources.forall(_.startsWith(\"scala-library-\")))\n          }\n          else {\n            expect(foundDepSources.length == 2)\n            expect(foundDepSources.exists(_.startsWith(\"scala-library-\")))\n            expect(foundDepSources.exists(_.startsWith(\"scala3-library_3-3\")))\n          }\n          expect(foundDepSources.forall(_.endsWith(\"-sources.jar\")))\n        }\n\n        {\n          val resp = remoteServer.buildTargetSources(new b.SourcesParams(targets)).asScala.await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundSources = resp.getItems.asScala\n            .map(_.getSources.asScala.map(_.getUri).toSeq)\n            .toSeq\n            .map(_.map(TestUtil.normalizeUri))\n          val expectedSources = Seq(\n            Seq(\n              TestUtil.normalizeUri((root / \"simple.sc\").toNIO.toUri.toASCIIString)\n            )\n          )\n          expect(foundSources == expectedSources)\n        }\n\n        val scalacOptionsResp = {\n          val resp = remoteServer\n            .buildTargetScalacOptions(new b.ScalacOptionsParams(targets))\n            .asScala\n            .await\n          val foundTargets = resp\n            .getItems\n            .asScala\n            .map(_.getTarget.getUri)\n            .map(TestUtil.normalizeUri)\n          expect(foundTargets == Seq(targetUri))\n          val foundOptions = resp.getItems.asScala.flatMap(_.getOptions.asScala).toSeq\n          if (actualScalaVersion.startsWith(\"2.\"))\n            expect(foundOptions.exists { opt =>\n              opt.startsWith(\"-Xplugin:\") && opt.contains(\"semanticdb-scalac\")\n            })\n          else\n            expect(foundOptions.contains(\"-Xsemanticdb\"))\n          resp\n        }\n\n        {\n          val resp =\n            remoteServer.buildTargetJavacOptions(new b.JavacOptionsParams(targets))\n              .asScala.await\n          val foundTargets = resp\n            .getItems\n            .asScala\n            .map(_.getTarget.getUri)\n            .map(TestUtil.normalizeUri)\n          expect(foundTargets == Seq(targetUri))\n        }\n\n        val classDir = os.Path(\n          Paths.get(new URI(scalacOptionsResp.getItems.asScala.head.getClassDirectory))\n        )\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        val compileProducts = os.walk(classDir).filter(os.isFile(_)).map(_.relativeTo(classDir))\n\n        val classFilePath = os.rel / {\n          if actualScalaVersion.startsWith(\"3.\")\n          then \"simple$_.class\"\n          else \"simple$.class\"\n        }\n        expect(compileProducts.contains(classFilePath))\n\n        val relPath = os.rel / \"META-INF\" / \"semanticdb\" / \"simple.sc.semanticdb\"\n        expect(compileProducts.contains(relPath))\n      }\n    }\n  }\n\n  test(\"diagnostics\") {\n    val inputs = TestInputs(\n      os.rel / \"Test.scala\" ->\n        s\"\"\"object Test {\n           |  val msg = \"Hello\"\n           |  zz\n           |  println(msg)\n           |}\n           |\"\"\".stripMargin\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        val compileResp =\n          remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n        expect(compileResp.getStatusCode == b.StatusCode.ERROR)\n\n        val diagnosticsParams: b.PublishDiagnosticsParams =\n          extractDiagnosticsParams(root / \"Test.scala\", localClient)\n        expect(diagnosticsParams.getBuildTarget.getUri == targetUri)\n\n        val diagnostics = diagnosticsParams.getDiagnostics.asScala.toSeq\n        expect(diagnostics.length == 1)\n\n        val (expectedMessage, expectedEndCharacter) =\n          if (actualScalaVersion.startsWith(\"2.\"))\n            \"not found: value zz\" -> 4\n          else if (actualScalaVersion == \"3.0.0\")\n            \"Not found: zz\" -> 2\n          else\n            \"Not found: zz\" -> 4\n        checkDiagnostic(\n          diagnostic = diagnostics.head,\n          expectedMessage = expectedMessage,\n          expectedSeverity = b.DiagnosticSeverity.ERROR,\n          expectedStartLine = 2,\n          expectedStartCharacter = 2,\n          expectedEndLine = 2,\n          expectedEndCharacter = expectedEndCharacter\n        )\n      }\n    }\n  }\n\n  test(\"diagnostics in script\") {\n    val inputs = TestInputs(\n      os.rel / \"test.sc\" ->\n        \"\"\"val msg: NonExistent = \"Hello\"\"\"\"\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        val compileResp =\n          remoteServer\n            .buildTargetCompile(new b.CompileParams(targets))\n            .asScala\n            .await\n        val actualStatusCode = compileResp.getStatusCode\n        expect(actualStatusCode == b.StatusCode.ERROR)\n\n        val diagnosticsParams = {\n          val diagnostics = localClient.diagnostics()\n          val params      = diagnostics(2)\n          expect(params.getBuildTarget.getUri == targetUri)\n          val actualUri   = TestUtil.normalizeUri(params.getTextDocument.getUri)\n          val expectedUri = TestUtil.normalizeUri((root / \"test.sc\").toNIO.toUri.toASCIIString)\n          expect(actualUri == expectedUri)\n          params\n        }\n\n        val diagnostics = diagnosticsParams.getDiagnostics.asScala.toSeq\n        expect(diagnostics.length == 1)\n\n        val expectedMessage =\n          if (actualScalaVersion.startsWith(\"2.\"))\n            \"not found: type NonExistent\"\n          else\n            \"Not found: type NonExistent\"\n\n        checkDiagnostic(\n          diagnostic = diagnostics.head,\n          expectedMessage = expectedMessage,\n          expectedSeverity = b.DiagnosticSeverity.ERROR,\n          expectedStartLine = 0,\n          expectedStartCharacter = 9,\n          expectedEndLine = 0,\n          expectedEndCharacter = 20\n        )\n      }\n    }\n  }\n\n  test(\"invalid diagnostics at startup\") {\n    val inputs = TestInputs(\n      os.rel / \"A.scala\" ->\n        s\"\"\"//> using resource ./resources\n           |\n           |object A {}\n           |\"\"\".stripMargin\n    )\n\n    withBsp(inputs, Seq(\".\")) { (_, localClient, remoteServer) =>\n      Future {\n        remoteServer.workspaceBuildTargets().asScala.await\n\n        val diagnosticsParams = localClient.latestDiagnostics().getOrElse {\n          fail(\"No diagnostics found\")\n        }\n\n        checkDiagnostic(\n          diagnostic = diagnosticsParams.getDiagnostics.asScala.toSeq.head,\n          expectedMessage = \"Unrecognized directive: resource with values: ./resources\",\n          expectedSeverity = b.DiagnosticSeverity.ERROR,\n          expectedStartLine = 0,\n          expectedStartCharacter = 10,\n          expectedEndLine = 0,\n          expectedEndCharacter = 18,\n          strictlyCheckMessage = false\n        )\n      }\n    }\n  }\n\n  test(\"directive diagnostics\") {\n    val inputs = TestInputs(\n      os.rel / \"Test.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::pprint:0.0.0.0.0.1\n           |\n           |object Test {\n           |  val msg = \"Hello\"\n           |  println(msg)\n           |}\n           |\"\"\".stripMargin\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n      Future {\n        remoteServer.workspaceBuildTargets().asScala.await\n        val diagnosticsParams = extractDiagnosticsParams(root / \"Test.scala\", localClient)\n\n        val diagnostics = diagnosticsParams.getDiagnostics.asScala.toSeq\n        expect(diagnostics.length == 1)\n\n        val sbv =\n          if (actualScalaVersion.startsWith(\"2.12.\")) \"2.12\"\n          else if (actualScalaVersion.startsWith(\"2.13.\")) \"2.13\"\n          else if (actualScalaVersion.startsWith(\"3.\")) \"3\"\n          else ???\n        val expectedMessage = s\"Error downloading com.lihaoyi:pprint_$sbv:0.0.0.0.0.1\"\n        checkDiagnostic(\n          diagnostic = diagnostics.head,\n          expectedMessage = expectedMessage,\n          expectedSeverity = b.DiagnosticSeverity.ERROR,\n          expectedStartLine = 0,\n          expectedStartCharacter = 14,\n          expectedEndLine = 0,\n          expectedEndCharacter = 45,\n          strictlyCheckMessage = false\n        )\n      }\n    }\n  }\n\n  test(\"directives in multiple files diagnostics\") {\n    val javaVersion = Constants.allJavaVersions.filter(_ > Constants.defaultJvmVersion).min\n    val inputs      = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using scala $actualScalaVersion\n           |\n           |object Foo extends App {\n           |  println(\"Foo\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Bar.scala\"  -> \"\",\n      os.rel / \"Hello.java\" -> s\"//> using jvm $javaVersion\"\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n      Future {\n        remoteServer.workspaceBuildTargets().asScala.await\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        val compileResp =\n          remoteServer\n            .buildTargetCompile(new b.CompileParams(targets))\n            .asScala\n            .await\n        expect(compileResp.getStatusCode == b.StatusCode.OK)\n\n        def checkDirectivesInMultipleFilesWarnings(\n          fileName: String,\n          expectedStartLine: Int,\n          expectedStartCharacter: Int,\n          expectedEndLine: Int,\n          expectedEndCharacter: Int\n        ): Unit = {\n          val diagnosticsParams = localClient.diagnostics().collectFirst {\n            case diag\n                if !diag.getDiagnostics.isEmpty &&\n                TestUtil.normalizeUri(diag.getTextDocument.getUri) ==\n                  TestUtil.normalizeUri((root / fileName).toNIO.toUri.toASCIIString) => diag\n          }\n          expect(diagnosticsParams.isDefined)\n          val diagnostics = diagnosticsParams.get.getDiagnostics.asScala.toSeq\n\n          val expectedMessage =\n            \"Using directives detected in multiple files. It is recommended to keep them centralized in the\"\n          checkDiagnostic(\n            diagnostic = diagnostics.head,\n            expectedMessage = expectedMessage,\n            expectedSeverity = b.DiagnosticSeverity.WARNING,\n            expectedStartLine = expectedStartLine,\n            expectedStartCharacter = expectedStartCharacter,\n            expectedEndLine = expectedEndLine,\n            expectedEndCharacter = expectedEndCharacter,\n            strictlyCheckMessage = false\n          )\n        }\n\n        checkDirectivesInMultipleFilesWarnings(\n          fileName = \"Foo.scala\",\n          expectedStartLine = 0,\n          expectedStartCharacter = 0,\n          expectedEndLine = 0,\n          expectedEndCharacter = 16 + actualScalaVersion.length\n        )\n        checkDirectivesInMultipleFilesWarnings(\n          fileName = \"Hello.java\",\n          expectedStartLine = 0,\n          expectedStartCharacter = 0,\n          expectedEndLine = 0,\n          expectedEndCharacter = 16\n        )\n      }\n    }\n  }\n\n  test(\"workspace update\") {\n    val inputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"val msg = \"Hello\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    val extraArgs =\n      if (Properties.isWin) Seq(\"-v\", \"-v\", \"-v\")\n      else Nil\n\n    withBsp(inputs, Seq(\".\") ++ extraArgs) { (root, localClient, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        {\n          val resp =\n            remoteServer\n              .buildTargetDependencySources(new b.DependencySourcesParams(targets))\n              .asScala\n              .await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundDepSources = resp.getItems.asScala\n            .flatMap(_.getSources.asScala)\n            .toSeq\n            .map { uri =>\n              val idx = uri.lastIndexOf('/')\n              uri.drop(idx + 1)\n            }\n          if (actualScalaVersion.startsWith(\"2.\")) {\n            expect(foundDepSources.length == 1)\n            expect(foundDepSources.forall(_.startsWith(\"scala-library-\")))\n          }\n          else {\n            expect(foundDepSources.length == 2)\n            expect(foundDepSources.exists(_.startsWith(\"scala-library-\")))\n            expect(foundDepSources.exists(_.startsWith(\"scala3-library_3-3\")))\n          }\n          expect(foundDepSources.forall(_.endsWith(\"-sources.jar\")))\n        }\n\n        val didChangeParamsFuture = localClient.buildTargetDidChange()\n        val updatedContent        =\n          \"\"\"//> using dep com.lihaoyi::pprint:0.6.6\n            |val msg = \"Hello\"\n            |pprint.log(msg)\n            |\"\"\".stripMargin\n        os.write.over(root / \"simple.sc\", updatedContent)\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        val didChangeParamsOptFuture = Future.firstCompletedOf(Seq(\n          didChangeParamsFuture.map(Some(_)),\n          completeIn(5.seconds).map(_ => None)\n        ))\n        val didChangeParams = didChangeParamsOptFuture.await.getOrElse {\n          sys.error(\"No buildTargetDidChange notification received\")\n        }\n\n        val changes = didChangeParams.getChanges.asScala.toSeq\n        expect(changes.length == 2)\n\n        val change = changes.head\n        expect(change.getTarget.getUri == targetUri)\n        val expectedKind = b.BuildTargetEventKind.CHANGED\n        expect(change.getKind == expectedKind)\n\n        {\n          val resp =\n            remoteServer\n              .buildTargetDependencySources(new b.DependencySourcesParams(targets))\n              .asScala\n              .await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundDepSources = resp.getItems.asScala\n            .flatMap(_.getSources.asScala)\n            .toSeq\n            .map { uri =>\n              val idx = uri.lastIndexOf('/')\n              uri.drop(idx + 1)\n            }\n          expect(foundDepSources.length > 1)\n          expect(foundDepSources.forall(_.endsWith(\"-sources.jar\")))\n          expect(foundDepSources.exists(_.startsWith(\"scala-library-\")))\n          expect(foundDepSources.exists(_.startsWith(\"pprint_\")))\n          resp\n        }\n      }\n    }\n  }\n\n  test(\"workspace update - new file\") {\n    val inputs = TestInputs(\n      os.rel / \"Test.scala\" ->\n        s\"\"\"object Test {\n           |  val msg = \"Hello\"\n           |  println(msg)\n           |}\n           |\"\"\".stripMargin\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        {\n          val resp =\n            remoteServer\n              .buildTargetDependencySources(new b.DependencySourcesParams(targets))\n              .asScala\n              .await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundDepSources = resp.getItems.asScala\n            .flatMap(_.getSources.asScala)\n            .toSeq\n            .map { uri =>\n              val idx = uri.lastIndexOf('/')\n              uri.drop(idx + 1)\n            }\n          if (actualScalaVersion.startsWith(\"2.\")) {\n            expect(foundDepSources.length == 1)\n            expect(foundDepSources.forall(_.startsWith(\"scala-library-\")))\n          }\n          else {\n            expect(foundDepSources.length == 2)\n            expect(foundDepSources.exists(_.startsWith(\"scala-library-\")))\n            expect(foundDepSources.exists(_.startsWith(\"scala3-library_3-3\")))\n          }\n          expect(foundDepSources.forall(_.endsWith(\"-sources.jar\")))\n        }\n\n        val didChangeParamsFuture = localClient.buildTargetDidChange()\n\n        val newFileContent =\n          \"\"\"object Messages {\n            |  def msg = \"Hello\"\n            |}\n            |\"\"\".stripMargin\n        os.write(root / \"Messages.scala\", newFileContent)\n        val updatedContent =\n          \"\"\"object Test {\n            |  println(Messages.msg)\n            |}\n            |\"\"\".stripMargin\n        os.write.over(root / \"Test.scala\", updatedContent)\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        val didChangeParamsOptFuture = Future.firstCompletedOf(Seq(\n          didChangeParamsFuture.map(Some(_)),\n          completeIn(5.seconds).map(_ => None)\n        ))\n        val didChangeParams = didChangeParamsOptFuture.await.getOrElse {\n          sys.error(\"No buildTargetDidChange notification received\")\n        }\n\n        val changes = didChangeParams.getChanges.asScala.toSeq\n        expect(changes.length == 2)\n\n        val change = changes.head\n        expect(change.getTarget.getUri == targetUri)\n        val expectedKind = b.BuildTargetEventKind.CHANGED\n        expect(change.getKind == expectedKind)\n      }\n    }\n  }\n\n  test(\"test workspace update after adding file to main scope\") {\n    val inputs = TestInputs(\n      os.rel / \"Messages.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.7.8\n          |object Messages {\n          |  def msg = \"Hello\"\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"MyTests.test.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::utest::0.7.10\n          |import utest._\n          |\n          |object MyTests extends TestSuite {\n          |  val tests = Tests {\n          |    test(\"foo\") {\n          |      assert(Messages.msg == \"Hello\")\n          |    }\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n\n    val actualScalaMajorVersion = actualScalaVersion.split(\"\\\\.\")\n      .take(if (actualScalaVersion.startsWith(\"3\")) 1 else 2)\n      .mkString(\".\")\n\n    withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractTestTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        {\n          val resp =\n            remoteServer\n              .buildTargetDependencySources(new b.DependencySourcesParams(targets))\n              .asScala\n              .await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundDepSources = resp.getItems.asScala\n            .flatMap(_.getSources.asScala)\n            .toSeq\n            .map { uri =>\n              val idx = uri.lastIndexOf('/')\n              uri.drop(idx + 1)\n            }\n\n          expect(foundDepSources.exists(_.startsWith(s\"utest_$actualScalaMajorVersion-0.7.10\")))\n          expect(foundDepSources.exists(_.startsWith(s\"os-lib_$actualScalaMajorVersion-0.7.8\")))\n\n          expect(foundDepSources.exists(_.startsWith(\"test-interface-1.0\")))\n          expect(foundDepSources.forall(_.endsWith(\"-sources.jar\")))\n        }\n\n        val changeFuture = localClient.buildTargetDidChange()\n\n        val newFileContent =\n          \"\"\"object Messages {\n            |  def msg = \"Hello2\"\n            |}\n            |\"\"\".stripMargin\n        os.write.over(root / \"Messages.scala\", newFileContent)\n\n        {\n          val resp = remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n        }\n\n        expect(changeFuture.isCompleted)\n\n        {\n          val resp =\n            remoteServer\n              .buildTargetDependencySources(new b.DependencySourcesParams(targets))\n              .asScala\n              .await\n          val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq\n          expect(foundTargets == Seq(targetUri))\n          val foundDepSources = resp.getItems.asScala\n            .flatMap(_.getSources.asScala)\n            .toSeq\n            .map { uri =>\n              val idx = uri.lastIndexOf('/')\n              uri.drop(idx + 1)\n            }\n\n          expect(foundDepSources.exists(_.startsWith(s\"utest_$actualScalaMajorVersion-0.7.10\")))\n          expect(!foundDepSources.exists(_.startsWith(s\"os-lib_$actualScalaMajorVersion-0.7.8\")))\n\n          expect(foundDepSources.exists(_.startsWith(\"test-interface-1.0\")))\n          expect(foundDepSources.forall(_.endsWith(\"-sources.jar\")))\n        }\n      }\n    }\n  }\n\n  test(\"return .scala-build directory as a output paths\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"object Hello extends App {\n          |  println(\"Hello World\")\n          |}\n          |\"\"\".stripMargin\n    )\n    withBspInitResults(inputs, Seq(\".\")) { (root, _, remoteServer, buildInitRes) =>\n      val serverCapabilities: b.BuildServerCapabilities = buildInitRes.getCapabilities\n      expect(serverCapabilities.getOutputPathsProvider)\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          extractTestTargets(targets)\n        }\n\n        val resp =\n          remoteServer.buildTargetOutputPaths(new b.OutputPathsParams(List(target).asJava))\n            .asScala.await\n        val outputPathsItems = resp.getItems.asScala\n        assert(outputPathsItems.nonEmpty)\n\n        val outputPathItem         = outputPathsItems.head\n        val expectedOutputPathUri  = (root / Constants.workspaceDirName).toIO.toURI.toASCIIString\n        val expectedOutputPathItem =\n          new b.OutputPathsItem(\n            target,\n            List(new b.OutputPathItem(expectedOutputPathUri, b.OutputPathItemKind.DIRECTORY)).asJava\n          )\n        expect(outputPathItem == expectedOutputPathItem)\n\n      }\n    }\n  }\n\n  test(\"workspace/reload --dependency option\") {\n    val inputs = TestInputs(\n      os.rel / \"ReloadTest.scala\" ->\n        s\"\"\"object ReloadTest {\n           |  println(os.pwd)\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", extraOptions)\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      val jsonOptions    = List(\"--json-options\", ideOptionsPath.toString)\n      withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n            val resp =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava))\n                .asScala.await\n            expect(resp.getStatusCode == b.StatusCode.ERROR)\n\n            val dependencyOptions = List(\"--dependency\", \"com.lihaoyi::os-lib::0.8.0\")\n            os.proc(TestUtil.cli, \"setup-ide\", \".\", dependencyOptions ++ extraOptions)\n              .call(\n                cwd = root,\n                stdout = os.Inherit\n              )\n\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n\n            val buildTargetsResp0 = remoteServer.workspaceBuildTargets().asScala.await\n            val targets0          = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq\n\n            val resp0 =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets0.asJava))\n                .asScala.await\n            expect(resp0.getStatusCode == b.StatusCode.OK)\n          }\n      }\n    }\n  }\n\n  test(\"workspace/reload extra dependency directive\") {\n    val sourceFilePath = os.rel / \"ReloadTest.scala\"\n    val inputs         = TestInputs(\n      sourceFilePath ->\n        s\"\"\"object ReloadTest {\n           |  println(os.pwd)\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", extraOptions)\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      val jsonOptions    = List(\"--json-options\", ideOptionsPath.toString)\n      withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n            val resp =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava))\n                .asScala.await\n            expect(resp.getStatusCode == b.StatusCode.ERROR)\n\n            val depName           = \"os-lib\"\n            val depVersion        = \"0.8.1\"\n            val updatedSourceFile =\n              s\"\"\"//> using dep com.lihaoyi::$depName:$depVersion\n                 |\n                 |object ReloadTest {\n                 |  println(os.pwd)\n                 |}\n                 |\"\"\".stripMargin\n            os.write.over(root / sourceFilePath, updatedSourceFile)\n\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n\n            val depSourcesParams   = new b.DependencySourcesParams(targets.asJava)\n            val depSourcesResponse =\n              remoteServer.buildTargetDependencySources(depSourcesParams).asScala.await\n            val depSources = depSourcesResponse.getItems.asScala.flatMap(_.getSources.asScala)\n            expect(depSources.exists(s => s.contains(depName) && s.contains(depVersion)))\n          }\n      }\n\n    }\n  }\n\n  test(\"workspace/reload of an extra sources directory\") {\n    val dir1   = \"dir1\"\n    val dir2   = \"dir2\"\n    val inputs = TestInputs(\n      os.rel / dir1 / \"ReloadTest.scala\" ->\n        s\"\"\"object ReloadTest {\n           |  val container = MissingCaseClass(value = \"Hello\")\n           |  println(container.value)\n           |}\n           |\"\"\".stripMargin\n    )\n    val extraInputs = inputs.add(\n      os.rel / dir2 / \"MissingCaseClass.scala\" -> \"case class MissingCaseClass(value: String)\"\n    )\n    extraInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"setup-ide\", dir1, extraOptions)\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      withBsp(inputs, Seq(dir1), reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n            val resp =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava))\n                .asScala.await\n            expect(resp.getStatusCode == b.StatusCode.ERROR)\n\n            os.proc(TestUtil.cli, \"setup-ide\", dir1, dir2, extraOptions)\n              .call(\n                cwd = root,\n                stdout = os.Inherit\n              )\n\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n\n            val buildTargetsResp0 = remoteServer.workspaceBuildTargets().asScala.await\n            val targets0          = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq\n\n            val resp0 =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets0.asJava))\n                .asScala.await\n            expect(resp0.getStatusCode == b.StatusCode.OK)\n          }\n      }\n\n    }\n  }\n\n  test(\"workspace/reload error response when no inputs json present\") {\n    val inputs = TestInputs(\n      os.rel / \"ReloadTest.scala\" ->\n        s\"\"\"object ReloadTest {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\")) {\n      (_, _, remoteServer) =>\n        Future {\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n          val resp =\n            remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava))\n              .asScala.await\n          expect(resp.getStatusCode == b.StatusCode.OK)\n\n          val reloadResp    = remoteServer.workspaceReload().asScala.await\n          val responseError = extractWorkspaceReloadResponse(reloadResp).getOrElse {\n            sys.error(s\"Unexpected workspace reload response shape $reloadResp\")\n          }\n          expect(responseError.getCode == -32603)\n          expect(responseError.getMessage.nonEmpty)\n        }\n    }\n\n  }\n\n  test(\"workspace/reload when updated source element in using directive\") {\n    val utilsFileName = \"Utils.scala\"\n    val inputs        = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"|//> using file Utils.scala\n            |\n            |object Hello extends App {\n            |   println(\"Hello World\")\n            |}\"\"\".stripMargin,\n      os.rel / utilsFileName ->\n        s\"\"\"|object Utils {\n            |  val hello = \"Hello World\"\n            |}\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\"Hello.scala\")) { (root, _, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targetUri = TestUtil.normalizeUri(target.getUri)\n        checkTargetUri(root, targetUri)\n\n        val targets = List(target).asJava\n\n        val compileResp =\n          remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n        expect(compileResp.getStatusCode == b.StatusCode.OK)\n\n        // after reload compilation should fail, Utils.scala file contains invalid scala code\n        val updatedUtilsFile =\n          s\"\"\"|object Utils {\n              |  val hello = \"Hello World\n              |}\"\"\".stripMargin\n        os.write.over(root / utilsFileName, updatedUtilsFile)\n\n        val buildTargetsResp0 = remoteServer.workspaceBuildTargets().asScala.await\n        val targets0          = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq\n\n        val resp0 =\n          remoteServer.buildTargetCompile(new b.CompileParams(targets0.asJava)).asScala.await\n        expect(resp0.getStatusCode == b.StatusCode.ERROR)\n      }\n    }\n  }\n\n  test(\"workspace/reload should restart bloop with correct JVM version from options\") {\n    val sourceFilePath = os.rel / \"ReloadTest.java\"\n    val inputs         = TestInputs(\n      sourceFilePath ->\n        s\"\"\"public class ReloadTest {\n           |  public static void main(String[] args) {\n           |    String a = \"Hello World\";\n           |\n           |    switch (a) {\n           |      case String s when s.length() > 6 -> System.out.println(s.toUpperCase());\n           |      case String s -> System.out.println(s.toLowerCase());\n           |    }\n           |  }\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"bloop\", \"exit\")\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", \"--jvm\", \"11\", extraOptions)\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      val jsonOptions    = List(\"--json-options\", ideOptionsPath.toString)\n      withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n            val errorResponse =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(errorResponse.getStatusCode == b.StatusCode.ERROR)\n\n            val javacOptions = Seq(\n              \"--javac-opt\",\n              \"--enable-preview\",\n              \"--javac-opt\",\n              \"--release\",\n              \"--javac-opt\",\n              \"19\"\n            )\n\n            os.proc(TestUtil.cli, \"setup-ide\", \".\", \"--jvm\", \"19\", javacOptions, extraOptions)\n              .call(\n                cwd = root,\n                stdout = os.Inherit\n              )\n\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n\n            val buildTargetsResp0 = remoteServer.workspaceBuildTargets().asScala.await\n            val reloadedTargets   = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq\n\n            val okResponse =\n              remoteServer.buildTargetCompile(new b.CompileParams(reloadedTargets.asJava))\n                .asScala.await\n            expect(okResponse.getStatusCode == b.StatusCode.OK)\n          }\n      }\n\n    }\n  }\n\n  test(\"workspace/reload should restart bloop with correct JVM version from directives\") {\n    val sourceFilePath = os.rel / \"ReloadTest.java\"\n    val inputs         = TestInputs(\n      sourceFilePath ->\n        s\"\"\"//> using jvm 11\n           |\n           |public class ReloadTest {\n           |  public static void main(String[] args) {\n           |    System.out.println(\"Hello World\");\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"bloop\", \"exit\")\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", extraOptions)\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      val jsonOptions    = List(\"--json-options\", ideOptionsPath.toString)\n      withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n        (_, localClient, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n            val resp =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava))\n                .asScala.await\n            expect(resp.getStatusCode == b.StatusCode.OK)\n\n            val updatedSourceFile =\n              s\"\"\"//> using jvm 19\n                 |//> using javacOpt --enable-preview --release 19\n                 |\n                 |public class ReloadTest {\n                 |  public static void main(String[] args) {\n                 |    String a = \"Hello World\";\n                 |\n                 |    switch (a) {\n                 |      case String s when s.length() > 6 -> System.out.println(s.toUpperCase());\n                 |      case String s -> System.out.println(s.toLowerCase());\n                 |    }\n                 |  }\n                 |}\n                 |\"\"\".stripMargin\n            os.write.over(root / sourceFilePath, updatedSourceFile)\n\n            expect(!localClient.logMessages().exists(_.getMessage.startsWith(\n              \"Error reading API from class file: ReloadTest : java.lang.UnsupportedClassVersionError: ReloadTest has been compiled by a more recent version of the Java Runtime\"\n            )))\n\n            val errorResponse =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(errorResponse.getStatusCode == b.StatusCode.OK)\n            expect(localClient.logMessages().exists(_.getMessage.startsWith(\n              \"Error reading API from class file: ReloadTest : java.lang.UnsupportedClassVersionError: ReloadTest has been compiled by a more recent version of the Java Runtime\"\n            )))\n\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n\n            val buildTargetsResp0 = remoteServer.workspaceBuildTargets().asScala.await\n            val reloadedTargets   = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq\n\n            val okResponse =\n              remoteServer.buildTargetCompile(new b.CompileParams(reloadedTargets.asJava))\n                .asScala.await\n            expect(okResponse.getStatusCode == b.StatusCode.OK)\n          }\n      }\n\n    }\n  }\n\n  test(\"bsp should start bloop with correct JVM version from directives\") {\n    val sourceFilePath = os.rel / \"ReloadTest.java\"\n    val inputs         = TestInputs(\n      sourceFilePath ->\n        s\"\"\"//> using jvm 19\n           |//> using javacOpt --enable-preview --release 19\n           |\n           |public class ReloadTest {\n           |  public static void main(String[] args) {\n           |    String a = \"Hello World\";\n           |\n           |    switch (a) {\n           |      case String s when s.length() > 6 -> System.out.println(s.toUpperCase());\n           |      case String s -> System.out.println(s.toLowerCase());\n           |    }\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"bloop\", \"exit\")\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      os.proc(TestUtil.cli, \"--power\", \"bloop\", \"start\", \"--jvm\", \"17\")\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", extraOptions)\n        .call(\n          cwd = root,\n          stdout = os.Inherit\n        )\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      val jsonOptions    = List(\"--json-options\", ideOptionsPath.toString)\n      withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n\n            val resp =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(resp.getStatusCode == b.StatusCode.OK)\n          }\n      }\n    }\n  }\n\n  test(\"bloop projects are initialised properly for an invalid directive value\") {\n    val inputs = TestInputs(\n      os.rel / \"InvalidUsingDirective.scala\" ->\n        s\"\"\"//> using scala true\n           |\n           |object InvalidUsingDirective extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\")) {\n      (root, localClient, remoteServer) =>\n        Future {\n          checkIfBloopProjectIsInitialised(\n            root = root,\n            buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          )\n          val diagnosticsParams =\n            extractDiagnosticsParams(root / \"InvalidUsingDirective.scala\", localClient)\n          val diagnostics = diagnosticsParams.getDiagnostics.asScala.toSeq\n          expect(diagnostics.length == 1)\n          checkDiagnostic(\n            diagnostic = diagnostics.head,\n            expectedMessage =\n              \"\"\"Encountered an error for the scala using directive.\n                |Expected a string value, got 'true'\"\"\".stripMargin,\n            expectedSeverity = b.DiagnosticSeverity.ERROR,\n            expectedStartLine = 0,\n            expectedStartCharacter = 16,\n            expectedEndLine = 0,\n            expectedEndCharacter = 20\n          )\n        }\n    }\n  }\n\n  test(\"bloop projects are initialised properly for an unrecognised directive\") {\n    val sourceFileName = \"UnrecognisedUsingDirective.scala\"\n    val directiveKey   = \"unrecognised.directive\"\n    val directiveValue = \"value\"\n    val inputs         = TestInputs(\n      os.rel / sourceFileName ->\n        s\"\"\"//> using $directiveKey $directiveValue\n           |\n           |object UnrecognisedUsingDirective extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\")) {\n      (root, localClient, remoteServer) =>\n        Future {\n          checkIfBloopProjectIsInitialised(\n            root = root,\n            buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          )\n          val diagnosticsParams =\n            extractDiagnosticsParams(root / sourceFileName, localClient)\n          val diagnostics = diagnosticsParams.getDiagnostics.asScala\n          expect(diagnostics.length == 1)\n          checkDiagnostic(\n            diagnostic = diagnostics.head,\n            expectedMessage =\n              s\"Unrecognized directive: $directiveKey with values: $directiveValue\",\n            expectedSeverity = b.DiagnosticSeverity.ERROR,\n            expectedStartLine = 0,\n            expectedStartCharacter = 10,\n            expectedEndLine = 0,\n            expectedEndCharacter = 32\n          )\n        }\n    }\n  }\n\n  test(\"bloop projects are initialised properly for a directive for an unfetchable dependency\") {\n    val inputs = TestInputs(\n      os.rel / \"InvalidUsingDirective.scala\" ->\n        s\"\"\"//> using dep no::lib:123\n           |\n           |object InvalidUsingDirective extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\")) {\n      (root, localClient, remoteServer) =>\n        Future {\n          checkIfBloopProjectIsInitialised(\n            root = root,\n            buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          )\n          val diagnosticsParams =\n            extractDiagnosticsParams(root / \"InvalidUsingDirective.scala\", localClient)\n          val diagnostics = diagnosticsParams.getDiagnostics.asScala.toSeq\n          expect(diagnostics.length == 1)\n          checkDiagnostic(\n            diagnostic = diagnostics.head,\n            expectedMessage = \"Error downloading no:lib\",\n            expectedSeverity = b.DiagnosticSeverity.ERROR,\n            expectedStartLine = 0,\n            expectedStartCharacter = 14,\n            expectedEndLine = 0,\n            expectedEndCharacter = 25,\n            strictlyCheckMessage = false\n          )\n        }\n    }\n  }\n  test(\"bsp should support parsing cancel params\") { // TODO This test only checks if the native launcher of Scala CLI is able to parse cancel params,\n    // this test does not check if Bloop supports $/cancelRequest. The status of that is tracked under the https://github.com/scalacenter/bloop/issues/2030.\n    val fileName = \"Hello.scala\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"object Hello extends App {\n           |  while(true) {\n           |    println(\"Hello World\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\"), stdErrOpt = Some(os.rel / \"stderr.txt\")) {\n      (root, _, remoteServer) =>\n        Future {\n          // prepare build\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          // build code\n          val targets     = buildTargetsResp.getTargets.asScala.map(_.getId())\n          val compileResp =\n            remoteServer\n              .buildTargetCompile(new b.CompileParams(targets.asJava))\n              .asScala\n              .await\n          expect(compileResp.getStatusCode == b.StatusCode.OK)\n\n          val mainTarget    = targets.find(!_.getUri.contains(\"-test\")).get\n          val runRespFuture =\n            remoteServer\n              .buildTargetRun(new b.RunParams(mainTarget))\n          runRespFuture.cancel(true)\n          expect(runRespFuture.isCancelled || runRespFuture.isCompletedExceptionally)\n          val stderrPath = root / \"stderr.txt\"\n          expect(!os.read(stderrPath).contains(\"Unmatched cancel notification for request id null\"))\n        }\n    }\n  }\n  test(\"bsp should report actionable diagnostic when enabled\") {\n    val fileName = \"Hello.scala\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.7.8\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\", \"--actions\")) {\n      (_, localClient, remoteServer) =>\n        Future {\n          // prepare build\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          // build code\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId()).asJava\n          remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n\n          val visibleDiagnostics =\n            localClient.diagnostics().takeWhile(!_.getReset).flatMap(_.getDiagnostics.asScala)\n\n          expect(visibleDiagnostics.length == 1)\n\n          val updateActionableDiagnostic = visibleDiagnostics.head\n\n          checkDiagnostic(\n            diagnostic = updateActionableDiagnostic,\n            expectedMessage = \"os-lib is outdated\",\n            expectedSeverity = b.DiagnosticSeverity.HINT,\n            expectedStartLine = 0,\n            expectedStartCharacter = 14,\n            expectedEndLine = 0,\n            expectedEndCharacter = 39,\n            expectedSource = Some(\"scala-cli\"),\n            strictlyCheckMessage = false\n          )\n\n          val scalaDiagnostic = new Gson().fromJson[b.ScalaDiagnostic](\n            updateActionableDiagnostic.getData.asInstanceOf[JsonElement],\n            classOf[b.ScalaDiagnostic]\n          )\n\n          val actions = scalaDiagnostic.getActions.asScala.toList\n          assert(actions.size == 1)\n          val changes = actions.head.getEdit.getChanges.asScala.toList\n          assert(changes.size == 1)\n          val textEdit = changes.head\n\n          expect(textEdit.getNewText.contains(\"com.lihaoyi::os-lib:\"))\n          expect(textEdit.getRange.getStart.getLine == 0)\n          expect(textEdit.getRange.getStart.getCharacter == 14)\n          expect(textEdit.getRange.getEnd.getLine == 0)\n          expect(textEdit.getRange.getEnd.getCharacter == 39)\n        }\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3.\"))\n    List(\".sc\", \".scala\").foreach { filetype =>\n      test(s\"bsp should report actionable diagnostic from bloop for $filetype files (Scala 3)\") {\n        val fileName = s\"Hello$filetype\"\n        val inputs   = TestInputs(\n          os.rel / fileName ->\n            s\"\"\"\n               |object Hello {\n               |  sealed trait TestTrait\n               |  case class TestA() extends TestTrait\n               |  case class TestB() extends TestTrait\n               |  val traitInstance: TestTrait = ???\n               |  traitInstance match {\n               |    case TestA() =>\n               |  }\n               |}\n               |\"\"\".stripMargin\n        )\n        withBsp(inputs, Seq(\".\")) {\n          (_, localClient, remoteServer) =>\n            Future {\n              // prepare build\n              val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n              // build code\n              val targets = buildTargetsResp.getTargets.asScala.map(_.getId()).asJava\n              remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n\n              val visibleDiagnostics =\n                localClient\n                  .diagnostics()\n                  .map(_.getDiagnostics.asScala)\n                  .find(_.nonEmpty)\n                  .getOrElse(Nil)\n\n              expect(visibleDiagnostics.size == 1)\n\n              val updateActionableDiagnostic = visibleDiagnostics.head\n\n              checkDiagnostic(\n                diagnostic = updateActionableDiagnostic,\n                expectedMessage = \"match may not be exhaustive.\",\n                expectedSeverity = b.DiagnosticSeverity.WARNING,\n                expectedStartLine = 6,\n                expectedStartCharacter = 2,\n                expectedEndLine = 6,\n                expectedEndCharacter = 15,\n                expectedSource = Some(\"bloop\"),\n                strictlyCheckMessage = false\n              )\n\n              val scalaDiagnostic = new Gson().fromJson[b.ScalaDiagnostic](\n                updateActionableDiagnostic.getData.asInstanceOf[JsonElement],\n                classOf[b.ScalaDiagnostic]\n              )\n\n              val actions = scalaDiagnostic.getActions.asScala.toList\n              assert(actions.size == 1)\n              val changes = actions.head.getEdit.getChanges.asScala.toList\n              assert(changes.size == 1)\n              val textEdit = changes.head\n\n              expect(textEdit.getNewText.contains(\"\\n    case TestB() => ???\"))\n              expect(textEdit.getRange.getStart.getLine == 7)\n              expect(textEdit.getRange.getStart.getCharacter == 19)\n              expect(textEdit.getRange.getEnd.getLine == 7)\n              expect(textEdit.getRange.getEnd.getCharacter == 19)\n            }\n        }\n      }\n    }\n  test(\"bsp should support jvmRunEnvironment request\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.7.8\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\")) {\n      (_, _, remoteServer) =>\n        Future {\n          // prepare build\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          // build code\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId()).asJava\n\n          val jvmRunEnvironmentResult: b.JvmRunEnvironmentResult =\n            remoteServer\n              .buildTargetJvmRunEnvironment(new b.JvmRunEnvironmentParams(targets))\n              .asScala\n              .await\n          expect(jvmRunEnvironmentResult.getItems.asScala.toList.nonEmpty)\n\n          val jvmTestEnvironmentResult: b.JvmTestEnvironmentResult =\n            remoteServer\n              .buildTargetJvmTestEnvironment(new JvmTestEnvironmentParams(targets))\n              .asScala\n              .await\n          expect(jvmTestEnvironmentResult.getItems.asScala.toList.nonEmpty)\n        }\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"@main in script\") {\n      val inputs = TestInputs(\n        os.rel / \"test.sc\" ->\n          \"\"\"@main def main(args: Strings*): Unit = println(\"Args: \" + args.mkString(\" \"))\n            |\"\"\".stripMargin\n      )\n\n      withBsp(inputs, Seq(\".\")) { (root, localClient, remoteServer) =>\n        Future {\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          val target           = {\n            val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n            expect(targets.length == 2)\n            extractMainTargets(targets)\n          }\n\n          val targetUri = TestUtil.normalizeUri(target.getUri)\n          checkTargetUri(root, targetUri)\n\n          val targets = List(target).asJava\n\n          val compileResp =\n            remoteServer\n              .buildTargetCompile(new b.CompileParams(targets))\n              .asScala\n              .await\n          expect(compileResp.getStatusCode == b.StatusCode.ERROR)\n\n          val diagnosticsParams = {\n            val diagnostics = localClient.diagnostics()\n            val params      = diagnostics(2)\n            expect(params.getBuildTarget.getUri == targetUri)\n            val actualUri   = TestUtil.normalizeUri(params.getTextDocument.getUri)\n            val expectedUri = TestUtil.normalizeUri((root / \"test.sc\").toNIO.toUri.toASCIIString)\n            expect(actualUri == expectedUri)\n            params\n          }\n\n          val diagnostics = diagnosticsParams.getDiagnostics.asScala.toSeq\n          expect(diagnostics.length == 1)\n\n          checkDiagnostic(\n            diagnostic = diagnostics.head,\n            expectedMessage =\n              \"Annotation @main in .sc scripts is not supported, use .scala format instead\",\n            expectedSeverity = b.DiagnosticSeverity.ERROR,\n            expectedStartLine = 0,\n            expectedStartCharacter = 0,\n            expectedEndLine = 0,\n            expectedEndCharacter = 5\n          )\n        }\n      }\n    }\n\n  def testSourceJars(\n    directives: String = \"//> using jar Message.jar\",\n    getBspOptions: os.RelPath => List[String] = _ => List.empty,\n    checkTestTarget: Boolean = false\n  ): Unit = {\n    val jarSources    = os.rel / \"jarStuff\"\n    val mainSources   = os.rel / \"src\"\n    val jarPath       = mainSources / \"Message.jar\"\n    val sourceJarPath = mainSources / \"Message-sources.jar\"\n    val inputs        = TestInputs(\n      jarSources / \"Message.scala\" -> \"case class Message(value: String)\",\n      mainSources / \"Main.scala\"   ->\n        s\"\"\"$directives\n           |object Main extends App {\n           |  println(Message(\"Hello\").value)\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      // package the library jar\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        jarSources,\n        \"--library\",\n        \"-o\",\n        jarPath,\n        extraOptions\n      )\n        .call(cwd = root)\n      // package the sources jar\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        jarSources,\n        \"--with-sources\",\n        \"-o\",\n        sourceJarPath,\n        extraOptions\n      )\n        .call(cwd = root)\n      withBsp(\n        inputs,\n        Seq(mainSources.toString),\n        reuseRoot = Some(root),\n        bspOptions = getBspOptions(sourceJarPath)\n      ) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp\n              .getTargets\n              .asScala\n            val mainTarget = targets.find(!_.getId.getUri.contains(\"-test\")).get\n            val testTarget = targets.find(_.getId.getUri.contains(\"-test\")).get\n            // ensure that the project compiles\n            val compileRes = remoteServer.buildTargetCompile(\n              new b.CompileParams(List(mainTarget.getId).asJava)\n            ).asScala.await\n            expect(compileRes.getStatusCode == b.StatusCode.OK)\n            // ensure that the source jar is in the dependency sources\n            val dependencySourcesResp =\n              remoteServer\n                .buildTargetDependencySources(\n                  new b.DependencySourcesParams(List(mainTarget.getId, testTarget.getId).asJava)\n                )\n                .asScala\n                .await\n            val dependencySourceItems = dependencySourcesResp.getItems.asScala\n            val sources               = dependencySourceItems\n              .filter(dsi =>\n                if (checkTestTarget) dsi.getTarget == testTarget.getId\n                else dsi.getTarget == mainTarget.getId\n              )\n              .flatMap(_.getSources.asScala)\n            expect(sources.exists(_.endsWith(sourceJarPath.last)))\n          }\n      }\n    }\n  }\n\n  test(\"source jars handled correctly from the command line\") {\n    testSourceJars(getBspOptions = sourceJarPath => List(\"--source-jar\", sourceJarPath.toString))\n  }\n\n  test(\n    \"source jars handled correctly from the command line smartly assuming a *-sources.jar is a source jar\"\n  ) {\n    testSourceJars(getBspOptions = sourceJarPath => List(\"--extra-jar\", sourceJarPath.toString))\n  }\n\n  test(\"source jars handled correctly from a test scope using directive\") {\n    testSourceJars(\n      directives = \"\"\"//> using jar Message.jar\n                     |//> using test.sourceJar Message-sources.jar\"\"\".stripMargin,\n      checkTestTarget = true\n    )\n  }\n\n  if (!actualScalaVersion.startsWith(\"2.12\"))\n    test(\"actionable diagnostics on deprecated using directives\") {\n      val inputs = TestInputs(\n        os.rel / \"test.sc\" ->\n          \"\"\"//> using toolkit latest\n            |//> using test.toolkit typelevel:latest\n            |\n            |//> using lib org.typelevel::cats-core:2.6.1\n            |\n            |object Test extends App {\n            | println(\"Hello\")\n            |}\n            |\"\"\".stripMargin\n      )\n\n      withBsp(inputs, Seq(\".\", \"--actions=false\")) { (root, localClient, remoteServer) =>\n        Future {\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          val target           = {\n            val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n            expect(targets.length == 2)\n            extractMainTargets(targets)\n          }\n\n          val targetUri = TestUtil.normalizeUri(target.getUri)\n          checkTargetUri(root, targetUri)\n\n          val targets = List(target).asJava\n\n          val compileResp =\n            remoteServer\n              .buildTargetCompile(new b.CompileParams(targets))\n              .asScala\n              .await\n          expect(compileResp.getStatusCode == b.StatusCode.OK)\n\n          val diagnosticsParams = {\n            val diagnostics = localClient.diagnostics()\n              .filter(_.getReset == false)\n            expect(diagnostics.size == 3)\n            val params = diagnostics.head\n            expect(params.getBuildTarget.getUri == targetUri)\n            val actualUri   = TestUtil.normalizeUri(params.getTextDocument.getUri)\n            val expectedUri = TestUtil.normalizeUri((root / \"test.sc\").toNIO.toUri.toASCIIString)\n            expect(actualUri == expectedUri)\n            diagnostics\n          }\n\n          val diagnostics = diagnosticsParams.flatMap(_.getDiagnostics.asScala)\n            .sortBy(_.getRange.getEnd.getCharacter())\n\n          {\n            checkDiagnostic(\n              diagnostic = diagnostics.head,\n              expectedMessage =\n                \"Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour\",\n              expectedSeverity = b.DiagnosticSeverity.WARNING,\n              expectedStartLine = 0,\n              expectedStartCharacter = 10,\n              expectedEndLine = 0,\n              expectedEndCharacter = 24\n            )\n\n            checkScalaAction(\n              diagnostic = diagnostics.head,\n              expectedActionsSize = 1,\n              expectedTitle = \"Change to: toolkit default\",\n              expectedChanges = 1,\n              expectedStartLine = 0,\n              expectedStartCharacter = 10,\n              expectedEndLine = 0,\n              expectedEndCharacter = 24,\n              expectedNewText = \"toolkit default\"\n            )\n          }\n\n          {\n            checkDiagnostic(\n              diagnostic = diagnostics.apply(1),\n              expectedMessage =\n                \"Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour\",\n              expectedSeverity = b.DiagnosticSeverity.WARNING,\n              expectedStartLine = 1,\n              expectedStartCharacter = 10,\n              expectedEndLine = 1,\n              expectedEndCharacter = 39\n            )\n\n            checkScalaAction(\n              diagnostic = diagnostics.apply(1),\n              expectedActionsSize = 1,\n              expectedTitle = \"Change to: test.toolkit typelevel:default\",\n              expectedChanges = 1,\n              expectedStartLine = 1,\n              expectedStartCharacter = 10,\n              expectedEndLine = 1,\n              expectedEndCharacter = 39,\n              expectedNewText = \"test.toolkit typelevel:default\"\n            )\n          }\n\n          {\n            checkDiagnostic(\n              diagnostic = diagnostics.apply(2),\n              expectedMessage =\n                \"Using 'lib' is deprecated, use 'dep' instead\",\n              expectedSeverity = b.DiagnosticSeverity.WARNING,\n              expectedStartLine = 3,\n              expectedStartCharacter = 10,\n              expectedEndLine = 3,\n              expectedEndCharacter = 44\n            )\n\n            checkScalaAction(\n              diagnostic = diagnostics.apply(2),\n              expectedActionsSize = 1,\n              expectedTitle = \"Change to: dep org.typelevel::cats-core:2.6.1\",\n              expectedChanges = 1,\n              expectedStartLine = 3,\n              expectedStartCharacter = 10,\n              expectedEndLine = 3,\n              expectedEndCharacter = 44,\n              expectedNewText = \"dep org.typelevel::cats-core:2.6.1\"\n            )\n          }\n        }\n      }\n    }\n\n  test(\"BSP respects JAVA_HOME\") {\n    TestUtil.retryOnCi() {\n      val javaVersion = \"23\"\n      val inputs      = TestInputs(os.rel / \"check-java.sc\" ->\n        s\"\"\"assert(System.getProperty(\"java.version\").startsWith(\"$javaVersion\"))\n           |println(System.getProperty(\"java.home\"))\"\"\".stripMargin)\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n        val java23Home =\n          os.Path(\n            os.proc(TestUtil.cs, \"java-home\", \"--jvm\", s\"zulu:$javaVersion\").call().out.trim(),\n            os.pwd\n          )\n        os.proc(TestUtil.cli, \"setup-ide\", \"check-java.sc\")\n          .call(cwd = root, env = Map(\"JAVA_HOME\" -> java23Home.toString()))\n        val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n        expect(ideOptionsPath.toNIO.toFile.exists())\n        val ideEnvsPath = root / Constants.workspaceDirName / \"ide-envs.json\"\n        expect(ideEnvsPath.toNIO.toFile.exists())\n        val jsonOptions = List(\"--json-options\", ideOptionsPath.toString)\n        val envOptions  = List(\"--envs-file\", ideEnvsPath.toString)\n        withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions ++ envOptions, reuseRoot = Some(root)) {\n          (_, _, remoteServer) =>\n            Future {\n              val targets = remoteServer.workspaceBuildTargets().asScala.await\n                .getTargets.asScala\n                .filter(!_.getId.getUri.contains(\"-test\"))\n                .map(_.getId())\n              val compileResult =\n                remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n              expect(compileResult.getStatusCode == b.StatusCode.OK)\n              val runResult =\n                remoteServer.buildTargetRun(new b.RunParams(targets.head)).asScala.await\n              expect(runResult.getStatusCode == b.StatusCode.OK)\n            }\n        }\n      }\n    }\n  }\n\n  test(\"BSP respects --java-home\") {\n    TestUtil.retryOnCi() {\n      val javaVersion = \"23\"\n      val inputs      = TestInputs(os.rel / \"check-java.sc\" ->\n        s\"\"\"assert(System.getProperty(\"java.version\").startsWith(\"$javaVersion\"))\n           |println(System.getProperty(\"java.home\"))\"\"\".stripMargin)\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n        val java22Home =\n          os.Path(\n            os.proc(TestUtil.cs, \"java-home\", \"--jvm\", s\"zulu:$javaVersion\").call().out.trim(),\n            os.pwd\n          )\n        os.proc(TestUtil.cli, \"setup-ide\", \"check-java.sc\", \"--java-home\", java22Home.toString())\n          .call(cwd = root)\n        val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n        expect(ideOptionsPath.toNIO.toFile.exists())\n        val jsonOptions = List(\"--json-options\", ideOptionsPath.toString)\n        withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n          (_, _, remoteServer) =>\n            Future {\n              val targets = remoteServer.workspaceBuildTargets().asScala.await\n                .getTargets.asScala\n                .filter(!_.getId.getUri.contains(\"-test\"))\n                .map(_.getId())\n              val compileResult =\n                remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n              expect(compileResult.getStatusCode == b.StatusCode.OK)\n              val runResult =\n                remoteServer.buildTargetRun(new b.RunParams(targets.head)).asScala.await\n              expect(runResult.getStatusCode == b.StatusCode.OK)\n            }\n        }\n      }\n    }\n  }\n\n  for {\n    setPowerByLauncherOpt   <- Seq(true, false)\n    setPowerBySubCommandOpt <- Seq(true, false)\n    setPowerByEnv           <- Seq(true, false)\n    setPowerByConfig        <- Seq(true, false)\n    powerIsSet =\n      setPowerByLauncherOpt || setPowerBySubCommandOpt || setPowerByEnv || setPowerByConfig\n    powerSettingDescription = {\n      val launcherSetting   = if (setPowerByLauncherOpt) \"launcher option\" else \"\"\n      val subCommandSetting = if (setPowerBySubCommandOpt) \"setup-ide option\" else \"\"\n      val envSetting        = if (setPowerByEnv) \"environment variable\" else \"\"\n      val configSetting     = if (setPowerByConfig) \"config\" else \"\"\n      List(launcherSetting, subCommandSetting, envSetting, configSetting)\n        .filter(_.nonEmpty)\n        .mkString(\", \")\n    }\n    testDescription =\n      if (powerIsSet)\n        s\"BSP respects --power mode set by $powerSettingDescription (example: using python directive)\"\n      else\n        \"BSP fails when --power mode is not set for experimental directives (example: using python directive)\"\n  } test(testDescription) {\n    val scriptName = \"requires-power.sc\"\n    val inputs     = TestInputs(os.rel / scriptName ->\n      s\"\"\"//> using python\n         |println(\"scalapy is experimental\")\"\"\".stripMargin)\n    inputs.fromRoot { root =>\n      val configFile                        = os.rel / \"config\" / \"config.json\"\n      val configEnvs                        = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString())\n      val setupIdeEnvs: Map[String, String] =\n        if (setPowerByEnv) Map(\"SCALA_CLI_POWER\" -> \"true\") ++ configEnvs\n        else configEnvs\n      val launcherOpts =\n        if (setPowerByLauncherOpt) List(\"--power\")\n        else List.empty\n      val subCommandOpts =\n        if (setPowerBySubCommandOpt) List(\"--power\")\n        else List.empty\n      val args = launcherOpts ++ List(\"setup-ide\", scriptName) ++ subCommandOpts\n      os.proc(TestUtil.cli, args).call(cwd = root, env = setupIdeEnvs)\n      if (setPowerByConfig)\n        os.proc(TestUtil.cli, \"config\", \"power\", \"true\")\n          .call(cwd = root, env = configEnvs)\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      expect(ideOptionsPath.toNIO.toFile.exists())\n      val ideLauncherOptsPath = root / Constants.workspaceDirName / \"ide-launcher-options.json\"\n      expect(ideLauncherOptsPath.toNIO.toFile.exists())\n      val ideEnvsPath = root / Constants.workspaceDirName / \"ide-envs.json\"\n      expect(ideEnvsPath.toNIO.toFile.exists())\n      val jsonOptions = List(\n        \"--json-options\",\n        ideOptionsPath.toString,\n        \"--json-launcher-options\",\n        ideLauncherOptsPath.toString,\n        \"--envs-file\",\n        ideEnvsPath.toString\n      )\n      withBsp(\n        inputs,\n        Seq(\".\"),\n        bspOptions = jsonOptions,\n        bspEnvs = configEnvs,\n        reuseRoot = Some(root)\n      ) {\n        (_, _, remoteServer) =>\n          Future {\n            val targets = remoteServer.workspaceBuildTargets().asScala.await\n              .getTargets.asScala\n              .filter(!_.getId.getUri.contains(\"-test\"))\n              .map(_.getId())\n            val compileResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            if powerIsSet then {\n              expect(compileResult.getStatusCode == b.StatusCode.OK)\n              val runResult =\n                remoteServer.buildTargetRun(new b.RunParams(targets.head)).asScala.await\n              expect(runResult.getStatusCode == b.StatusCode.OK)\n            }\n            else expect(compileResult.getStatusCode == b.StatusCode.ERROR)\n          }\n      }\n    }\n  }\n\n  test(\"BSP reloads --power mode after setting it via env passed to setup-ide\") {\n    val scriptName = \"requires-power.sc\"\n    val inputs     = TestInputs(os.rel / scriptName ->\n      s\"\"\"//> using python\n         |println(\"scalapy is experimental\")\"\"\".stripMargin)\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"setup-ide\", scriptName, extraOptions).call(cwd = root)\n      val ideEnvsPath = root / Constants.workspaceDirName / \"ide-envs.json\"\n      expect(ideEnvsPath.toNIO.toFile.exists())\n      val jsonOptions = List(\"--envs-file\", ideEnvsPath.toString)\n      withBsp(inputs, Seq(scriptName), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val targets = remoteServer.workspaceBuildTargets().asScala.await\n              .getTargets.asScala\n              .filter(!_.getId.getUri.contains(\"-test\"))\n              .map(_.getId())\n\n            // compilation should fail before reload, as --power mode is off\n            val compileBeforeReloadResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(compileBeforeReloadResult.getStatusCode == b.StatusCode.ERROR)\n\n            // enable --power mode via env for setup-ide\n            os.proc(TestUtil.cli, \"setup-ide\", scriptName, extraOptions)\n              .call(cwd = root, env = Map(\"SCALA_CLI_POWER\" -> \"true\"))\n\n            // compilation should now succeed\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n            val compileAfterReloadResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(compileAfterReloadResult.getStatusCode == b.StatusCode.OK)\n\n            // code should also be runnable via BSP now\n            val runResult =\n              remoteServer.buildTargetRun(new b.RunParams(targets.head)).asScala.await\n            expect(runResult.getStatusCode == b.StatusCode.OK)\n          }\n      }\n    }\n  }\n\n  test(\"BSP reloads --power mode after setting it via config\") {\n    val scriptName = \"requires-power.sc\"\n    val inputs     = TestInputs(os.rel / scriptName ->\n      s\"\"\"//> using python\n         |println(\"scalapy is experimental\")\"\"\".stripMargin)\n    inputs.fromRoot { root =>\n      val configFile = os.rel / \"config\" / \"config.json\"\n      val configEnvs = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString())\n      os.proc(TestUtil.cli, \"setup-ide\", scriptName, extraOptions).call(\n        cwd = root,\n        env = configEnvs\n      )\n      val ideEnvsPath = root / Constants.workspaceDirName / \"ide-envs.json\"\n      expect(ideEnvsPath.toNIO.toFile.exists())\n      val jsonOptions = List(\"--envs-file\", ideEnvsPath.toString)\n      withBsp(\n        inputs,\n        Seq(scriptName),\n        bspOptions = jsonOptions,\n        bspEnvs = configEnvs,\n        reuseRoot = Some(root)\n      ) {\n        (_, _, remoteServer) =>\n          Future {\n            val targets = remoteServer.workspaceBuildTargets().asScala.await\n              .getTargets.asScala\n              .filter(!_.getId.getUri.contains(\"-test\"))\n              .map(_.getId())\n\n            // compilation should fail before reload, as --power mode is off\n            val compileBeforeReloadResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(compileBeforeReloadResult.getStatusCode == b.StatusCode.ERROR)\n\n            // enable --power mode via config\n            os.proc(TestUtil.cli, \"config\", \"power\", \"true\")\n              .call(cwd = root, env = configEnvs)\n\n            // compilation should now succeed\n            val reloadResponse =\n              extractWorkspaceReloadResponse(remoteServer.workspaceReload().asScala.await)\n            expect(reloadResponse.isEmpty)\n            val compileAfterReloadResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(compileAfterReloadResult.getStatusCode == b.StatusCode.OK)\n\n            // code should also be runnable via BSP now\n            val runResult =\n              remoteServer.buildTargetRun(new b.RunParams(targets.head)).asScala.await\n            expect(runResult.getStatusCode == b.StatusCode.OK)\n          }\n      }\n    }\n  }\n\n  for {\n    cliVersion <- Seq(\"1.8.4\", \"1.5.0\", \"1.0.0\")\n  } // TODO: test for nightly, too\n    test(s\"setup-ide doesn't pass unrecognised arguments to old --cli-versions: $cliVersion\") {\n      TestUtil.retryOnCi() {\n        val scriptName = \"cli-version.sc\"\n        val inputs     = TestInputs(\n          os.rel / scriptName -> s\"\"\"println(\"Hello from launcher v$cliVersion\"\"\"\n        )\n        inputs.fromRoot { root =>\n          val r =\n            os.proc(\n              TestUtil.cli,\n              \"--cli-version\",\n              cliVersion,\n              \"setup-ide\",\n              scriptName,\n              extraOptions\n            )\n              .call(cwd = root, stderr = os.Pipe, check = false)\n          expect(!r.err.text().contains(\"Unrecognized argument\"))\n          expect(r.exitCode == 0)\n        }\n      }\n    }\n\n  for {\n    cliVersion <- Seq(\"1.8.4\")\n  } // TODO: test for nightly, too\n    test(\n      s\"setup-ide prepares a valid BSP configuration with --cli-version $cliVersion\"\n    ) {\n      TestUtil.retryOnCi() {\n        val scriptName = \"cli-version.sc\"\n        TestInputs(os.rel / scriptName -> s\"\"\"println(\"Hello from launcher v$cliVersion\")\"\"\")\n          .fromRoot { root =>\n            val cliVersionArgs = List(\"--cli-version\", cliVersion)\n            os.proc(TestUtil.cli, cliVersionArgs, \"setup-ide\", scriptName, extraOptions)\n              .call(cwd = root)\n            val expectedIdeLauncherFile =\n              root / Constants.workspaceDirName / \"ide-launcher-options.json\"\n            expect(expectedIdeLauncherFile.toNIO.toFile.exists())\n            expect(os.read(expectedIdeLauncherFile).contains(cliVersion))\n            val bspConfig = readBspConfig(root)\n            expect(bspConfig.argv.head == TestUtil.cliPath)\n            expect(bspConfig.argv.containsSlice(cliVersionArgs))\n            expect(bspConfig.argv.indexOfSlice(cliVersionArgs) < bspConfig.argv.indexOf(\"bsp\"))\n          }\n      }\n    }\n\n  for {\n    useScalaWrapper <- Seq(false, true)\n    if actualScalaVersion.coursierVersion >= \"3.5.0\".coursierVersion\n    scalaVersion =\n      if actualScalaVersion == Constants.scala3NextRc then Constants.scala3NextRcAnnounced\n      else if actualScalaVersion == Constants.scala3Next then Constants.scala3NextAnnounced\n      else actualScalaVersion\n    withLauncher = (root: os.Path) =>\n      (f: Seq[os.Shellable] => Unit) =>\n        if (useScalaWrapper)\n          withScalaRunnerWrapper(\n            root = root,\n            localBin = root / \"local-bin\",\n            localCache = Some(root / \"local-cache\"),\n            scalaVersion = scalaVersion,\n            shouldCleanUp = false\n          )(launcher => f(Seq(launcher)))\n        else\n          f(Seq(TestUtil.cli))\n    launcherString         = if (useScalaWrapper) s\"scala wrapper ($scalaVersion)\" else \"Scala CLI\"\n    connectionJsonFileName = if (useScalaWrapper) \"scala.json\" else \"scala-cli.json\"\n  }\n    test(\n      s\"setup-ide prepares valid BSP connection json with a valid launcher ($launcherString)\"\n    ) {\n      TestUtil.retryOnCi() {\n        val scriptName = \"example.sc\"\n        TestInputs(\n          os.rel / scriptName -> s\"\"\"println(\"Hello\")\"\"\"\n        )\n          .fromRoot { root =>\n            withLauncher(root) { launcher =>\n              val jvmIndex = if (TestUtil.isJvmCli) Constants.minimumLauncherJavaVersion else 8\n              val javaHome =\n                os.Path(\n                  os.proc(TestUtil.cs, \"java-home\", \"--jvm\", jvmIndex).call().out.trim(),\n                  os.pwd\n                )\n              os.proc(launcher, \"setup-ide\", scriptName, extraOptions)\n                .call(cwd = root, env = Map(\"JAVA_HOME\" -> javaHome.toString))\n              val expectedIdeLauncherFile =\n                root / Constants.workspaceDirName / \"ide-launcher-options.json\"\n              expect(expectedIdeLauncherFile.toNIO.toFile.exists())\n              val bspConfig          = readBspConfig(root, connectionJsonFileName)\n              val bspLauncherCommand = {\n                val launcherPrefix = bspConfig.argv.takeWhile(_ != TestUtil.cliPath)\n                launcherPrefix :+ bspConfig.argv.drop(launcherPrefix.length).head\n              }\n              expect(bspLauncherCommand.last == TestUtil.cliPath)\n              if (TestUtil.isJvmBootstrappedCli) {\n                // this launcher is not self-executable and has to be launched with `java -jar`\n                expect(bspLauncherCommand.head.endsWith(\"java\"))\n                expect(bspLauncherCommand.drop(1) == List(\"-jar\", TestUtil.cliPath))\n                val bspJavaVersionResult = os.proc(bspLauncherCommand.head, \"-version\")\n                  .call(\n                    cwd = root,\n                    env = Map(\"JAVA_HOME\" -> javaHome.toString),\n                    mergeErrIntoOut = true\n                  )\n                val bspJavaVersion = TestUtil.parseJavaVersion(bspJavaVersionResult.out.trim()).get\n                // the bsp launcher has to know to run itself on a supported JVM\n                expect(bspJavaVersion >= math.max(\n                  Constants.minimumInternalJvmVersion,\n                  Constants.bloopMinimumJvmVersion\n                ))\n              }\n              else\n                expect(bspLauncherCommand == List(TestUtil.cliPath))\n              val r = os.proc(bspLauncherCommand, \"version\", \"--cli-version\")\n                .call(cwd = root, env = Map(\"JAVA_HOME\" -> javaHome.toString))\n              expect(r.out.trim() == Constants.cliVersion)\n            }\n          }\n      }\n    }\n\n  test(\"setup-ide passes Java props to the BSP configuration correctly\") {\n    val scriptName = \"hello.sc\"\n    TestInputs(os.rel / scriptName -> s\"\"\"println(\"Hello\")\"\"\").fromRoot { root =>\n      val javaProps = List(\"-Dfoo=bar\", \"-Dbar=baz\")\n      os.proc(TestUtil.cli, javaProps, \"setup-ide\", scriptName, extraOptions)\n        .call(cwd = root)\n      val bspConfig = readBspConfig(root)\n      if (TestUtil.isJvmBootstrappedCli) {\n        expect(bspConfig.argv.head.endsWith(\"java\"))\n        expect(bspConfig.argv.drop(1).head == \"-jar\")\n        expect(bspConfig.argv.dropWhile(_ != TestUtil.cliPath).head == TestUtil.cliPath)\n      }\n      else expect(bspConfig.argv.head == TestUtil.cliPath)\n      expect(bspConfig.argv.containsSlice(javaProps))\n      expect(bspConfig.argv.indexOfSlice(javaProps) < bspConfig.argv.indexOf(\"bsp\"))\n    }\n  }\n\n  test(\"BSP loads verbosity on compile\") {\n    val stderrFile = os.rel / \"stderr.txt\"\n    val inputs     = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(\"Hello World\")\n           |}\n           |\"\"\".stripMargin\n    )\n    withBsp(inputs, Seq(\".\", \"-v\"), stdErrOpt = Some(stderrFile)) {\n      (root, _, remoteServer) =>\n        Future {\n          val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n          val targets          = buildTargetsResp.getTargets.asScala.map(_.getId())\n          val compileResp      =\n            remoteServer\n              .buildTargetCompile(new b.CompileParams(targets.asJava))\n              .asScala\n              .await\n          expect(compileResp.getStatusCode == b.StatusCode.OK)\n          expect(os.read(root / stderrFile).contains(\"Scheduling compilation\"))\n        }\n    }\n  }\n\n  test(\"BSP loads verbosity on compile when passed from setup-ide\") {\n    val stderrFile = os.rel / \"stderr.txt\"\n    val inputs     = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(\"Hello World\")\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"setup-ide\", \".\", \"-v\").call(cwd = root)\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      val jsonOptions    = List(\"--json-options\", ideOptionsPath.toString)\n      withBsp(\n        inputs = inputs,\n        args = Seq(\".\"),\n        bspOptions = jsonOptions,\n        reuseRoot = Some(root),\n        stdErrOpt = Some(stderrFile)\n      ) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId())\n            val compileResp      =\n              remoteServer\n                .buildTargetCompile(new b.CompileParams(targets.asJava))\n                .asScala\n                .await\n            expect(compileResp.getStatusCode == b.StatusCode.OK)\n            expect(os.read(root / stderrFile).contains(\"Scheduling compilation\"))\n          }\n      }\n    }\n  }\n\n  test(\"buildTarget/wrappedSources\") {\n    val inputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"val msg = \"Hello\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n\n    withBsp(inputs, Seq(\".\")) { (root, _, remoteServer) =>\n      Future {\n        val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n        val target           = {\n          val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n          expect(targets.length == 2)\n          extractMainTargets(targets)\n        }\n\n        val targets = List(target).asJava\n\n        val compileResp =\n          remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n        expect(compileResp.getStatusCode == b.StatusCode.OK)\n\n        val resp = remoteServer\n          .buildTargetWrappedSources(new b.SourcesParams(targets))\n          .asScala\n          .await\n\n        val items = resp.items.asScala\n        expect(items.nonEmpty)\n\n        val mainItem = items.find(_.target.getUri == target.getUri).get\n\n        val sources = mainItem.sources.asScala\n        expect(sources.size == 1)\n\n        val wrappedSource = sources.head\n        val sourceUri     = TestUtil.normalizeUri(wrappedSource.uri)\n        val expectedUri   =\n          TestUtil.normalizeUri((root / \"simple.sc\").toNIO.toUri.toASCIIString)\n        expect(sourceUri == expectedUri)\n\n        expect(wrappedSource.topWrapper.contains(\"simple\"))\n        expect(wrappedSource.topWrapper.contains(\"scriptPath\"))\n        expect(wrappedSource.bottomWrapper == \"}\")\n        expect(wrappedSource.generatedUri.endsWith(\".scala\"))\n        expect(wrappedSource.generatedUri.contains(\"simple\"))\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTests212.scala",
    "content": "package scala.cli.integration\n\nclass BspTests212 extends BspTestDefinitions with BspTests2Definitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTests213.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.google.gson.{Gson, JsonElement}\n\nimport scala.cli.integration.TestUtil.await\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\nimport scala.jdk.CollectionConverters.*\n\nclass BspTests213 extends BspTestDefinitions with BspTests2Definitions with Test213 {\n  List(\".sc\", \".scala\").foreach { filetype =>\n    test(s\"bsp should report actionable diagnostic from bloop for $filetype files (Scala 2.13)\") {\n      val fileName = s\"Hello$filetype\"\n      val inputs   = TestInputs(\n        os.rel / fileName ->\n          s\"\"\"\n             |object Hello {\n             |  def foo: Any = {\n             |    x: Int => x * 2\n             |  }\n             |}\n             |\"\"\".stripMargin\n      )\n      withBsp(inputs, Seq(\".\", \"-O\", \"-Xsource:3\")) {\n        (_, localClient, remoteServer) =>\n          Future {\n            // prepare build\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            // build code\n            val targets = buildTargetsResp.getTargets.asScala.map(_.getId()).asJava\n            remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala.await\n\n            val visibleDiagnostics =\n              localClient\n                .diagnostics()\n                .map(_.getDiagnostics.asScala)\n                .find(_.nonEmpty)\n                .getOrElse(Nil)\n\n            expect(visibleDiagnostics.size == 1)\n\n            val updateActionableDiagnostic = visibleDiagnostics.head\n\n            checkDiagnostic(\n              diagnostic = updateActionableDiagnostic,\n              expectedMessage = \"parentheses are required around the parameter of a lambda\",\n              expectedSeverity = b.DiagnosticSeverity.ERROR,\n              expectedStartLine = 3,\n              expectedStartCharacter = 5,\n              expectedEndLine = 3,\n              expectedEndCharacter = 5,\n              expectedSource = Some(\"bloop\"),\n              strictlyCheckMessage = false\n            )\n\n            val scalaDiagnostic = new Gson().fromJson[b.ScalaDiagnostic](\n              updateActionableDiagnostic.getData.asInstanceOf[JsonElement],\n              classOf[b.ScalaDiagnostic]\n            )\n\n            val actions = scalaDiagnostic.getActions.asScala.toList\n            assert(actions.size == 1)\n            val changes = actions.head.getEdit.getChanges.asScala.toList\n            assert(changes.size == 1)\n            val textEdit = changes.head\n\n            expect(textEdit.getNewText.contains(\"(x: Int)\"))\n            expect(textEdit.getRange.getStart.getLine == 3)\n            expect(textEdit.getRange.getStart.getCharacter == 4)\n            expect(textEdit.getRange.getEnd.getLine == 3)\n            expect(textEdit.getRange.getEnd.getCharacter == 10)\n          }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTests2Definitions.scala",
    "content": "package scala.cli.integration\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\ntrait BspTests2Definitions { this: BspTestDefinitions =>\n  for {\n    useDirectives        <- Seq(true, false)\n    (directive, options) <- Seq(\n      (s\"//> using scala $actualScalaVersion\", Seq(\"--scala\", actualScalaVersion))\n    )\n    extraOptionsOverride =\n      if (useDirectives) TestUtil.extraOptions else TestUtil.extraOptions ++ options\n    testNameSuffix = if (useDirectives) directive else options.mkString(\" \")\n  } test(s\"BSP App object wrapper forced with $testNameSuffix\") {\n    val (script1, script2) = \"script1.sc\" -> \"script2.sc\"\n    val directiveString    = if (useDirectives) directive else \"\"\n    val inputs             = TestInputs(\n      os.rel / script1 ->\n        s\"\"\"//> using platform js\n           |$directiveString\n           |\n           |def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / script2 ->\n        \"\"\"println(\"Hello\")\n          |\"\"\".stripMargin\n    )\n    testScriptWrappers(inputs, extraOptionsOverride = extraOptionsOverride)(expectAppWrapper)\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTests3Definitions.scala",
    "content": "package scala.cli.integration\n\nimport scala.concurrent.ExecutionContext.Implicits.global\n\ntrait BspTests3Definitions { this: BspTestDefinitions =>\n  test(\"BSP class wrapper for Scala 3\") {\n    val (script1, script2) = \"script1.sc\" -> \"script2.sc\"\n    val inputs             = TestInputs(\n      os.rel / script1 ->\n        s\"\"\"def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / script2 ->\n        s\"\"\"//> using dep org.scalatest::scalatest:3.2.15\n           |\n           |import org.scalatest.*, flatspec.*, matchers.*\n           |\n           |class PiTest extends AnyFlatSpec with should.Matchers {\n           |  \"pi calculus\" should \"return a precise enough pi value\" in {\n           |    math.Pi shouldBe 3.14158d +- 0.001d\n           |  }\n           |}\n           |org.scalatest.tools.Runner.main(Array(\"-oDF\", \"-s\", classOf[PiTest].getName))\"\"\".stripMargin\n    )\n    testScriptWrappers(inputs)(expectClassWrapper)\n  }\n\n  for {\n    useDirectives        <- Seq(true, false)\n    (directive, options) <- Seq(\n      (\"//> using object.wrapper\", Seq(\"--object-wrapper\")),\n      (\"//> using platform js\", Seq(\"--js\"))\n    )\n    wrapperOptions = if (useDirectives) Nil else options\n    testNameSuffix = if (useDirectives) directive else options.mkString(\" \")\n  } test(s\"BSP object wrapper forced with $testNameSuffix\") {\n    val (script1, script2) = \"script1.sc\" -> \"script2.sc\"\n    val directiveString    = if (useDirectives) directive else \"\"\n    val inputs             = TestInputs(\n      os.rel / script1 ->\n        s\"\"\"$directiveString\n           |\n           |def main(args: String*): Unit = println(\"Hello\")\n           |main()\n           |\"\"\".stripMargin,\n      os.rel / script2 ->\n        \"\"\"println(\"Hello\")\n          |\"\"\".stripMargin\n    )\n    testScriptWrappers(inputs, bspOptions = wrapperOptions)(expectObjectWrapper)\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass BspTests3Lts extends BspTestDefinitions with BspTests3Definitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.await\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nclass BspTests3NextRc extends BspTestDefinitions with BspTests3Definitions with Test3NextRc {\n  test(\"BSP respects --cli-default-scala-version & --predefined-repository launcher options\") {\n    // 3.5.0-RC1-fakeversion-bin-SNAPSHOT has too long filenames for Windows.\n    // Yes, seriously. Which is why we can't use it there.\n    val sv = if (Properties.isWin) Constants.scala3NextRc else \"3.5.0-RC1-fakeversion-bin-SNAPSHOT\"\n    val inputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"assert(dotty.tools.dotc.config.Properties.versionNumberString == \"$sv\")\"\"\"\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n      val localRepoPath = root / \"local-repo\"\n      if (Properties.isWin) {\n        val artifactNames = Seq(\n          \"scala3-compiler_3\",\n          \"scala3-staging_3\",\n          \"scala3-tasty-inspector_3\",\n          \"scala3-sbt-bridge\"\n        )\n        for { artifactName <- artifactNames } {\n          val csRes = os.proc(\n            TestUtil.cs,\n            \"fetch\",\n            \"--cache\",\n            localRepoPath,\n            s\"org.scala-lang:$artifactName:$sv\"\n          )\n            .call(cwd = root)\n          expect(csRes.exitCode == 0)\n        }\n      }\n      else {\n        TestUtil.initializeGit(root)\n        os.proc(\n          \"git\",\n          \"clone\",\n          \"https://github.com/dotty-staging/maven-test-repo.git\",\n          localRepoPath.toString\n        ).call(cwd = root)\n      }\n      val predefinedRepository =\n        if (Properties.isWin)\n          (localRepoPath / \"https\" / \"repo1.maven.org\" / \"maven2\").toNIO.toUri.toASCIIString\n        else\n          (localRepoPath / \"thecache\" / \"https\" / \"repo1.maven.org\" /\n            \"maven2\").toNIO.toUri.toASCIIString\n      os.proc(\n        TestUtil.cli,\n        \"--cli-default-scala-version\",\n        sv,\n        \"--predefined-repository\",\n        predefinedRepository,\n        \"setup-ide\",\n        \"simple.sc\",\n        \"--with-compiler\",\n        \"--offline\",\n        \"--power\"\n      )\n        .call(cwd = root)\n      val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n      expect(ideOptionsPath.toNIO.toFile.exists())\n      val ideEnvsPath = root / Constants.workspaceDirName / \"ide-envs.json\"\n      expect(ideEnvsPath.toNIO.toFile.exists())\n      val ideLauncherOptionsPath = root / Constants.workspaceDirName / \"ide-launcher-options.json\"\n      expect(ideLauncherOptionsPath.toNIO.toFile.exists())\n      val jsonOptions     = List(\"--json-options\", ideOptionsPath.toString)\n      val launcherOptions = List(\"--json-launcher-options\", ideLauncherOptionsPath.toString)\n      val envOptions      = List(\"--envs-file\", ideEnvsPath.toString)\n      val bspOptions      = jsonOptions ++ launcherOptions ++ envOptions\n      withBsp(inputs, Seq(\".\"), bspOptions = bspOptions, reuseRoot = Some(root)) {\n        (_, _, remoteServer) =>\n          Future {\n            val targets = remoteServer.workspaceBuildTargets().asScala.await\n              .getTargets.asScala\n              .filter(!_.getId.getUri.contains(\"-test\"))\n              .map(_.getId())\n            val compileResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(compileResult.getStatusCode == b.StatusCode.OK)\n            val runResult =\n              remoteServer.buildTargetRun(new b.RunParams(targets.head)).asScala.await\n            expect(runResult.getStatusCode == b.StatusCode.OK)\n          }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/BspTestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass BspTestsDefault extends BspTestDefinitions with BspTests3Definitions with TestDefault\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CleanTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass CleanTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  test(\"simple\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"object Hello {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val dir      = root / Constants.workspaceDirName\n      val bspEntry = root / \".bsp\" / \"scala-cli.json\"\n\n      val res = os.proc(TestUtil.cli, \"run\", \".\").call(cwd = root)\n      expect(res.out.trim() == \"Hello\")\n      expect(os.exists(dir))\n      expect(os.exists(bspEntry))\n\n      os.proc(TestUtil.cli, \"clean\", \".\").call(cwd = root)\n      expect(!os.exists(dir))\n      expect(!os.exists(bspEntry))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileScalacCompatTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\n/** For the `run` counterpart, refer to [[RunScalacCompatTestDefinitions]] */\ntrait CompileScalacCompatTestDefinitions { this: CompileTestDefinitions =>\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"consecutive -language:* flags are not ignored\") {\n      val sourceFileName = \"example.scala\"\n      TestInputs(os.rel / sourceFileName ->\n        s\"\"\"//> using scala $actualScalaVersion\n           |//> using options -color:never -language:noAutoTupling -language:strictEquality\n           |case class Cat(name: String)\n           |case class Dog(name: String)\n           |def strictEquality(c: Cat, d: Dog):Boolean = c == d\n           |def takesTuple(tpl: Tuple) = ???\n           |def withTuple() = takesTuple(1, 2)\n           |\"\"\".stripMargin).fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"compile\", sourceFileName)\n          .call(cwd = root, check = false, stderr = os.Pipe)\n        expect(res.exitCode == 1)\n        val errOutput                   = res.err.trim()\n        val expectedStrictEqualityError =\n          \" Values of types Cat and Dog cannot be compared with == or !=\"\n        expect(errOutput.contains(expectedStrictEqualityError))\n        val expectedNoAutoTuplingError =\n          \"too many arguments for method takesTuple: (tpl: Tuple): Nothing\"\n        expect(errOutput.trim().contains(expectedNoAutoTuplingError))\n      }\n    }\n\n  // Given the vast number of ways compiler options can be passed from the CLI,\n  // we test them all (or most, at the very least), as a (perhaps overkill) sanity check.\n  // Pieces of the existing `-language:*` test are reused, but kept separate for clarity.\n  {\n    val modes @ Seq(viaDirective, viaCli, viaCliWithExplicitOpt, mixed, mixedWithExplicitOpt) =\n      Seq(\"directive\", \"cli\", \"cli with -O\", \"mixed\", \"mixed with -O\")\n    for {\n      mode <- modes\n      if actualScalaVersion == Constants.scala3Next\n      dashPrefix    <- Seq(\"-\", \"--\")\n      syntaxVariant <- Seq(\n        Seq(\n          Seq(s\"${dashPrefix}color:never\"),\n          Seq(s\"${dashPrefix}language:noAutoTupling\"),\n          Seq(s\"${dashPrefix}language:strictEquality\")\n        ),\n        Seq(\n          Seq(s\"${dashPrefix}color\", \"never\"),\n          Seq(s\"${dashPrefix}language\", \"noAutoTupling\"),\n          Seq(s\"${dashPrefix}language\", \"strictEquality\")\n        ),\n        Seq(\n          Seq(s\"${dashPrefix}color:never\"),\n          Seq(s\"${dashPrefix}language:noAutoTupling,strictEquality\")\n        ),\n        Seq(\n          Seq(s\"${dashPrefix}color\", \"never\"),\n          Seq(s\"${dashPrefix}language\", \"noAutoTupling,strictEquality\")\n        )\n      )\n      (cliOpts, directiveOpts) = {\n        val (initialCliOpts, initialDirectiveOpts) = mode match {\n          case m if m == mixed                => syntaxVariant.splitAt(syntaxVariant.length - 1)\n          case m if m == mixedWithExplicitOpt =>\n            val (initialCliOpts, initialDirectiveOpts) =\n              syntaxVariant.splitAt(syntaxVariant.length - 1)\n            initialCliOpts.map(_.flatMap(o => Seq(\"-O\", o))) -> initialDirectiveOpts\n          case c if c == viaCli                => syntaxVariant -> Nil\n          case c if c == viaCliWithExplicitOpt =>\n            syntaxVariant.map(_.flatMap(o => Seq(\"-O\", o))) -> Nil\n          case _ => Nil -> syntaxVariant\n        }\n        initialCliOpts.flatten.map(_.filter(_ != '\"')) -> initialDirectiveOpts.flatten\n      }\n      cliOptsString       = cliOpts.mkString(\" \")\n      directiveOptsString = directiveOpts.mkString(\" \")\n      includeDirective = (mode == viaDirective || mode == mixed || mode == mixedWithExplicitOpt) &&\n        directiveOpts.nonEmpty\n      directiveString = if (includeDirective) s\"//> using options $directiveOptsString\" else \"\"\n      allOptsString   = mode match {\n        case m if m.startsWith(mixed) =>\n          s\"opts passed via command line: $cliOptsString, opts passed via directive: $directiveString\"\n        case c if c.startsWith(viaCli) =>\n          s\"opts passed via command line: $cliOptsString\"\n        case _ =>\n          s\"opts passed via directive: $directiveString\"\n      }\n    } test(s\"compiler options passed in $mode mode: $allOptsString\") {\n      val sourceFileName = \"example.scala\"\n      TestInputs(os.rel / sourceFileName ->\n        s\"\"\"//> using scala $actualScalaVersion\n           |$directiveString\n           |case class Cat(name: String)\n           |case class Dog(name: String)\n           |def strictEquality(c: Cat, d: Dog):Boolean = c == d\n           |def takesTuple(tpl: Tuple) = ???\n           |def withTuple() = takesTuple(1, 2)\n           |\"\"\".stripMargin).fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"compile\", sourceFileName, cliOpts)\n          .call(cwd = root, check = false, stderr = os.Pipe)\n        println(res.err.trim())\n        expect(res.exitCode == 1)\n        val errOutput                   = res.err.trim()\n        val expectedStrictEqualityError =\n          \"Values of types Cat and Dog cannot be compared with == or !=\"\n        expect(errOutput.contains(expectedStrictEqualityError))\n        val expectedNoAutoTuplingError =\n          \"too many arguments for method takesTuple: (tpl: Tuple): Nothing\"\n        expect(errOutput.trim().contains(expectedNoAutoTuplingError))\n      }\n    }\n  }\n\n  for {\n    useDirective <- Seq(true, false)\n    if !Properties.isWin\n    optionsSource = if (useDirective) \"using directive\" else \"command line\"\n    sv            = actualScalaVersion\n  } {\n    test(s\"consecutive -Wconf:* flags are not ignored (passed via $optionsSource)\") {\n      val sourceFileName       = \"example.scala\"\n      val warningConfOptions   = Seq(\"-Wconf:cat=deprecation:e\", \"-Wconf:any:s\")\n      val maybeDirectiveString =\n        if (useDirective) s\"//> using options ${warningConfOptions.mkString(\" \")}\" else \"\"\n      TestInputs(os.rel / sourceFileName ->\n        s\"\"\"//> using scala $sv\n           |$maybeDirectiveString\n           |object WConfExample extends App {\n           |  @deprecated(\"This method will be removed\", \"1.0.0\")\n           |  def oldMethod(): Unit = println(\"This is an old method.\")\n           |  oldMethod()\n           |}\n           |\"\"\".stripMargin).fromRoot { root =>\n        val localBin = root / \"local-bin\"\n        os.proc(\n          TestUtil.cs,\n          \"install\",\n          \"--install-dir\",\n          localBin,\n          s\"scalac:$sv\"\n        ).call(cwd = root)\n        val cliRes =\n          os.proc(\n            TestUtil.cli,\n            \"compile\",\n            sourceFileName,\n            \"--server=false\",\n            if (useDirective) Nil else warningConfOptions\n          )\n            .call(cwd = root, check = false, stderr = os.Pipe)\n        val scalacRes = os.proc(localBin / \"scalac\", warningConfOptions, sourceFileName)\n          .call(cwd = root, check = false, stderr = os.Pipe)\n        expect(scalacRes.exitCode == cliRes.exitCode)\n        val scalacResErr = scalacRes.err.trim()\n        val cliResErr    =\n          cliRes.err.trim().linesIterator.toList\n            // skip potentially irrelevant logs\n            .dropWhile(_.contains(\"Check\"))\n            .mkString(System.lineSeparator())\n        expect(cliResErr == scalacResErr)\n      }\n    }\n\n    if (!sv.startsWith(\"2.12\"))\n      test(s\"consecutive -Wunused:* flags are not ignored (passed via $optionsSource)\") {\n        val sourceFileName       = \"example.scala\"\n        val unusedLintOptions    = Seq(\"-Wunused:locals\", \"-Wunused:privates\")\n        val maybeDirectiveString =\n          if (useDirective) s\"//> using options ${unusedLintOptions.mkString(\" \")}\" else \"\"\n        TestInputs(os.rel / sourceFileName ->\n          s\"\"\"//> using scala $sv\n             |$maybeDirectiveString\n             |object WUnusedExample {\n             |  private def unusedPrivate(): String = \"stuff\"\n             |  def methodWithUnusedLocal() = {\n             |    val smth = \"hello\"\n             |    println(\"Hello\")\n             |  }\n             |}\n             |\"\"\".stripMargin).fromRoot { root =>\n          val r =\n            os.proc(\n              TestUtil.cli,\n              \"compile\",\n              sourceFileName,\n              if (useDirective) Nil else unusedLintOptions\n            )\n              .call(cwd = root, stderr = os.Pipe)\n          val err           = r.err.trim()\n          val unusedKeyword = if (sv.startsWith(\"2\")) \"never used\" else \"unused\"\n          expect(err.linesIterator.exists(l => l.contains(unusedKeyword) && l.contains(\"local\")))\n          expect(err.linesIterator.exists(l => l.contains(unusedKeyword) && l.contains(\"private\")))\n        }\n      }\n  }\n\n  {\n    val prefixes = Seq(\"-\", \"--\")\n    for {\n      prefix1 <- prefixes\n      prefix2 <- prefixes\n      optionKey = \"Werror\"\n      option1   = prefix1 + optionKey\n      option2   = prefix2 + optionKey\n      if actualScalaVersion.startsWith(\"3\")\n    } test(\n      s\"allow to override $option1 compiler option passed via directive by passing $option2 from the command line\"\n    ) {\n      val file = \"example.scala\"\n      TestInputs(os.rel / file ->\n        s\"\"\"//> using options -Wunused:all $option1\n           |@main def main() = {\n           |  val unused = \"\"\n           |  println(\"Hello, world!\")\n           |}\n           |\"\"\".stripMargin).fromRoot { root =>\n        os.proc(\n          TestUtil.cli,\n          \"compile\",\n          file,\n          s\"$option2:false\",\n          extraOptions\n        )\n          .call(cwd = root, stderr = os.Pipe)\n      }\n    }\n  }\n\n  for {\n    scalaVersion <- Seq(\"3.nightly\", \"3.8.0-RC1-bin-20250825-ee2f641-NIGHTLY\")\n    withBloop    <- Seq(false, true)\n    withBloopString = if (withBloop) \"with Bloop\" else \"scalac\"\n    buildServerOpts = if (withBloop) Nil else Seq(\"--server=false\")\n    if (!Properties.isWin || withBloop) && actualScalaVersion == Constants.scala3Next\n  }\n    test(s\"sanity check for Scala $scalaVersion standard library with cc ($withBloopString)\") {\n      TestUtil.retryOnCi() {\n        val input = \"example.scala\"\n        TestInputs(os.rel / input ->\n          s\"\"\"//> using scala $scalaVersion\n             |import language.experimental.captureChecking\n             |\n             |trait File extends caps.SharedCapability:\n             |  def count(): Int\n             |\n             |def f(file: File): IterableOnce[Int]^{file} =\n             |  Iterator(1)\n             |    .map(_ + file.count())\n             |\"\"\".stripMargin).fromRoot { root =>\n          os.proc(TestUtil.cli, \"compile\", input, buildServerOpts).call(cwd = root)\n        }\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\nimport scala.cli.integration.TestUtil.ProcOps\nimport scala.cli.integration.util.BloopUtil\nimport scala.concurrent.duration.DurationInt\nimport scala.util.Properties\n\nabstract class CompileTestDefinitions\n    extends ScalaCliSuite\n    with TestScalaVersionArgs\n    with CompilerPluginTestDefinitions\n    with CompileScalacCompatTestDefinitions\n    with SemanticDbTestDefinitions { this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n\n  private lazy val bloopDaemonDir = BloopUtil.bloopDaemonDir {\n    os.proc(TestUtil.cli, \"--power\", \"directories\").call().out.text()\n  }\n\n  val simpleInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.scala\" ->\n      \"\"\"//> using dep com.lihaoyi::os-lib::0.8.1\n        |\n        |object MyTests {\n        |  def main(args: Array[String]): Unit = {\n        |    for (l <- os.list(os.pwd))\n        |      println(l.last)\n        |  }\n        |}\n        |\"\"\".stripMargin\n  )\n\n  val mainAndTestInputs: TestInputs = TestInputs(\n    os.rel / \"Main.scala\" ->\n      \"\"\"//> using dep com.lihaoyi::utest:0.7.10\n        |\n        |object Main {\n        |  val err = utest.compileError(\"pprint.log(2)\")\n        |  def message = \"Hello from \" + \"tests\"\n        |  def main(args: Array[String]): Unit = {\n        |    println(message)\n        |    println(err)\n        |  }\n        |}\n        |\"\"\".stripMargin,\n    os.rel / \"Tests.test.scala\" ->\n      \"\"\"//> using dep com.lihaoyi::pprint:0.6.6\n        |\n        |import utest._\n        |\n        |object Tests extends TestSuite {\n        |  val tests = Tests {\n        |    test(\"message\") {\n        |      assert(Main.message.startsWith(\"Hello\"))\n        |    }\n        |  }\n        |}\n        |\"\"\".stripMargin\n  )\n\n  {\n    val inputs = TestInputs(\n      os.rel / \"Bar.java\" ->\n        \"\"\"public class Bar {}\n          |\"\"\".stripMargin,\n      os.rel / \"Foo.java\" ->\n        \"\"\"public class Foo {}\n          |\"\"\".stripMargin\n    )\n    test(\n      \"java files with no using directives should not produce warnings about using directives in multiple files\"\n    ) {\n      inputs.fromRoot { root =>\n        val warningMessage = \"Using directives detected in multiple files\"\n        val output         = os.proc(TestUtil.cli, \"compile\", extraOptions, \".\")\n          .call(cwd = root, stderr = os.Pipe).err.trim()\n        expect(!output.contains(warningMessage))\n      }\n    }\n    test(\"Pure Java with --server=false: no warning about .java files not being compiled\") {\n      inputs.fromRoot { root =>\n        val warningMessage = \".java files are not compiled to .class files\"\n        val output         = os.proc(TestUtil.cli, \"compile\", \"--server=false\", extraOptions, \".\")\n          .call(cwd = root, stderr = os.Pipe).err.text()\n        expect(!output.contains(warningMessage))\n      }\n    }\n  }\n\n  test(\"with one file per scope, no warning about spread directives should be printed\") {\n    TestInputs(\n      os.rel / \"Bar.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |\n          |object Bar extends App {\n          |  println(os.pwd)\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Foo.test.scala\" ->\n        \"\"\"//> using dep org.scalameta::munit:0.7.29\n          |\n          |class Foo extends munit.FunSuite {\n          |  test(\"Hello\") {\n          |    assert(true)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val warningMessage = \"Using directives detected in multiple files\"\n      val output         = os.proc(TestUtil.cli, \"compile\", \".\", \"--test\", extraOptions)\n        .call(cwd = root, stderr = os.Pipe).err.trim()\n      expect(!output.contains(warningMessage))\n    }\n  }\n\n  test(\"with >1 file per scope, the warning about spread directives should be printed\") {\n    TestInputs(\n      os.rel / \"Bar.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |\n          |object Bar extends App {\n          |  pprint.pprintln(Foo(os.pwd.toString).value)\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Foo.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::pprint:0.9.6\n          |\n          |case class Foo(value: String)\n          |\"\"\".stripMargin,\n      os.rel / \"Foo.test.scala\" ->\n        \"\"\"//> using dep org.scalameta::munit:0.7.29\n          |\n          |class FooTest extends munit.FunSuite {\n          |  test(\"Hello\") {\n          |    assert(true)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val warningMessage = \"Using directives detected in multiple files\"\n      val output         = os.proc(TestUtil.cli, \"compile\", \".\", \"--test\", extraOptions)\n        .call(cwd = root, stderr = os.Pipe).err.trim()\n      expect(output.contains(warningMessage))\n    }\n  }\n\n  test(\n    \"having target + using directives in files: no using-directives or .java-not-compiled warnings\"\n  ) {\n    val inputs = TestInputs(\n      os.rel / \"Bar.java\" ->\n        \"\"\"//> using target.platform jvm\n          |//> using jvm 17\n          |public class Bar {}\n          |\"\"\".stripMargin,\n      os.rel / \"Foo.test.scala\" ->\n        \"\"\"//> using target.scala.>= 2.13\n          |//> using dep com.lihaoyi::os-lib::0.8.1\n          |class Foo {}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val warningMessage = \"Using directives detected in multiple files\"\n      val output         = os.proc(TestUtil.cli, \"--power\", \"compile\", extraOptions, \".\")\n        .call(cwd = root).err.trim()\n      expect(!output.contains(warningMessage))\n      expect(!output.contains(\".java files are not compiled to .class files\"))\n    }\n  }\n\n  {\n    val javaSourceFile = \"Bar.java\"\n    val inputs         = TestInputs(\n      os.rel / javaSourceFile ->\n        \"\"\"//> using jvm 17\n          |public class Bar {}\n          |\"\"\".stripMargin,\n      os.rel / \"Foo.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib::0.8.1\n          |class Foo {}\n          |\"\"\".stripMargin\n    )\n    test(\"warn about directives in multiple files\") {\n      inputs.fromRoot { root =>\n        val warningMessage = \"Using directives detected in multiple files\"\n        val output         = os.proc(TestUtil.cli, \"--power\", \"compile\", extraOptions, \".\")\n          .call(cwd = root, stderr = os.Pipe).err.trim()\n        expect(output.contains(warningMessage))\n      }\n    }\n\n    test(\"mixed .java/.scala: with --server=false warn about .java not compiled\") {\n      inputs.fromRoot { root =>\n        val warningMessage = \".java files are not compiled to .class files\"\n        val output         =\n          os.proc(TestUtil.cli, \"--power\", \"compile\", extraOptions, \".\", \"--server=false\")\n            .call(cwd = root, stderr = os.Pipe).err.trim()\n        expect(output.contains(warningMessage))\n        expect(output.contains(javaSourceFile))\n      }\n    }\n  }\n\n  test(\"no arg\") {\n    simpleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"compile\", extraOptions, \".\").call(cwd = root)\n    }\n  }\n\n  test(\"exit code\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main {\n          |  def main(args: Array[String]): Unit =\n          |    println(nope)\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"compile\", extraOptions, \".\")\n        .call(cwd = root, check = false, stderr = os.Pipe, mergeErrIntoOut = true)\n      expect(res.exitCode == 1)\n    }\n  }\n\n  def checkIfCompileOutputIsCopied(baseName: String, output: os.Path): Unit = {\n    val extraExtensions = if (actualScalaVersion.startsWith(\"2.\")) Nil else Seq(\".tasty\")\n    val extensions      = Seq(\".class\", \"$.class\") ++ extraExtensions\n    val foundFiles      = os.list(output).map(_.relativeTo(output))\n    val expectedFiles   = extensions.map(ext => os.rel / s\"$baseName$ext\")\n    expect(foundFiles.toSet == expectedFiles.toSet)\n  }\n\n  test(\"copy compile output\") {\n    mainAndTestInputs.fromRoot { root =>\n      val tempOutput = root / \"output\"\n      os.proc(TestUtil.cli, \"compile\", \"--compile-output\", tempOutput, extraOptions, \".\").call(cwd =\n        root\n      )\n      checkIfCompileOutputIsCopied(\"Main\", tempOutput)\n    }\n  }\n\n  test(\"test scope\") {\n    mainAndTestInputs.fromRoot { root =>\n      val tempOutput = root / \"output\"\n      val output     =\n        os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"--test\",\n          \"--compile-output\",\n          tempOutput,\n          \"--print-class-path\",\n          extraOptions,\n          \".\"\n        ).call(cwd =\n          root\n        ).out.trim()\n      val classPath = output.split(File.pathSeparator).map(_.trim).filter(_.nonEmpty)\n      val isDefinedTestPathInClassPath = // expected test class path - root / Constants.workspaceDirName / project_(hash) / classes / test\n        classPath.exists(p =>\n          p.startsWith((root / Constants.workspaceDirName).toString()) &&\n          p.endsWith(Seq(\"classes\", \"test\").mkString(File.separator))\n        )\n      expect(isDefinedTestPathInClassPath)\n      checkIfCompileOutputIsCopied(\"Tests\", tempOutput)\n    }\n  }\n\n  test(\"test scope error\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main {\n          |  def message = \"Hello from \" + \"tests\"\n          |  def main(args: Array[String]): Unit =\n          |    println(message)\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Tests.test.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::utest:0.7.10\n          |\n          |import utest._\n          |\n          |object Tests extends TestSuite {\n          |  val tests = Tests {\n          |    test(\"message\") {\n          |      pprint.log(Main.message)\n          |      assert(Main.message.startsWith(\"Hello\"))\n          |    }\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"compile\", \"--test\", extraOptions, \".\")\n        .call(cwd = root, check = false, stderr = os.Pipe, mergeErrIntoOut = true)\n      expect(res.exitCode == 1)\n      val expectedInOutput =\n        if (actualScalaVersion.startsWith(\"2.\"))\n          \"not found: value pprint\"\n        else\n          \"Not found: pprint\"\n      expect(res.out.text().contains(expectedInOutput))\n    }\n  }\n\n  test(\"code in test error\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main {\n          |  def message = \"Hello from \" + \"tests\"\n          |  def main(args: Array[String]): Unit = {\n          |    zz // zz value\n          |    println(message)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"compile\", extraOptions, \".\")\n        .call(cwd = root, check = false, stderr = os.Pipe, mergeErrIntoOut = true)\n      expect(res.exitCode == 1)\n      val expectedInOutput =\n        if (actualScalaVersion.startsWith(\"2.\"))\n          \"not found: value zz\"\n        else\n          \"Not found: zz\"\n      val output = res.out.text()\n      expect(output.contains(expectedInOutput))\n      // errored line should be printed too\n      expect(output.contains(\"zz // zz value\"))\n      if (actualScalaVersion.startsWith(\"2.12.\"))\n        // seems the ranges returned by Bloop / scalac are only one character wide in 2.12\n        expect(output.contains(\"^\"))\n      else\n        // underline should have length 2\n        expect(output.contains(\"^^\"))\n      expect(!output.contains(\"^^^\"))\n    }\n  }\n\n  val jvmT = new munit.Tag(\"jvm-resolution\")\n\n  val scalaJvm8Project: TestInputs =\n    TestInputs(os.rel / \"Main.scala\" -> s\"object Main{java.util.Optional.of(1).isPresent}\")\n  val scalaJvm11Project: TestInputs =\n    TestInputs(os.rel / \"Main.scala\" -> s\"object Main{java.util.Optional.of(1).isEmpty}\")\n  val scalaJvm17Project: TestInputs =\n    TestInputs(os.rel / \"Main.scala\" -> s\"object Main{java.util.HexFormat.of().toHexDigits(255)}\")\n  val scalaJvm23Project: TestInputs =\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        s\"object Main{System.out.println(javax.print.attribute.standard.OutputBin.LEFT)}\"\n    )\n  val javaJvm8Project: TestInputs =\n    TestInputs(os.rel / \"Main.java\" -> \"\"\"|public class Main{\n                                          |  public static void main(String[] args) {\n                                          |      java.util.Optional.of(1).isPresent();\n                                          |  }\n                                          |}\"\"\".stripMargin)\n  val javaJvm11Project: TestInputs =\n    TestInputs(os.rel / \"Main.java\" -> \"\"\"|public class Main{\n                                          |  public static void main(String[] args) {\n                                          |      java.util.Optional.of(1).isEmpty();\n                                          |  }\n                                          |}\"\"\".stripMargin)\n  val javaJvm17Project: TestInputs =\n    TestInputs(os.rel / \"Main.java\" -> \"\"\"|public class Main{\n                                          |  public static void main(String[] args) {\n                                          |      java.util.HexFormat.of().toHexDigits(255);\n                                          |  }\n                                          |}\"\"\".stripMargin)\n  val javaJvm23Project: TestInputs =\n    TestInputs(os.rel / \"Main.java\" ->\n      \"\"\"|public class Main{\n         |  public static void main(String[] args) {\n         |      System.out.println(javax.print.attribute.standard.OutputBin.LEFT);\n         |  }\n         |}\"\"\".stripMargin)\n\n  def inputs: Map[(String, Int), TestInputs] =\n    if isScala38OrNewer\n    then\n      Map(\n        (\"scala\", 17) -> scalaJvm17Project,\n        (\"scala\", 23) -> scalaJvm23Project,\n        (\"java\", 17)  -> javaJvm17Project,\n        (\"java\", 23)  -> javaJvm23Project\n      )\n    else\n      Map(\n        (\"scala\", 8)  -> scalaJvm8Project,\n        (\"scala\", 11) -> scalaJvm11Project,\n        (\"scala\", 17) -> scalaJvm17Project,\n        (\"scala\", 23) -> scalaJvm23Project,\n        (\"java\", 8)   -> javaJvm8Project,\n        (\"java\", 11)  -> javaJvm11Project,\n        (\"java\", 17)  -> javaJvm17Project,\n        (\"java\", 23)  -> javaJvm23Project\n      )\n\n  {\n    val legacyJvms  = List(8, 11)\n    val currentJvms = List(17, 23)\n    val jvms        = if isScala38OrNewer then currentJvms else legacyJvms ++ currentJvms\n    for {\n      bloopJvm                      <- jvms\n      targetJvm                     <- jvms\n      ((lang, sourcesJvm), project) <- inputs\n    } test(s\"JvmCompatibilityTest: bloopJvm:$bloopJvm/targetJvm:$targetJvm/lang:$lang/sourcesJvm:$sourcesJvm\"\n      .tag(jvmT)) {\n      compileToADifferentJvmThanBloops(\n        bloopJvm.toString,\n        targetJvm.toString,\n        targetJvm >= sourcesJvm,\n        project\n      )\n    }\n  }\n\n  test(\"Scala CLI should not infer scalac --release if --release is passed\".tag(jvmT)) {\n    scalaJvm23Project.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        extraOptions,\n        \"--jvm\",\n        \"23\",\n        \"-release\",\n        \"17\",\n        \".\"\n      ).call(cwd = root, check = false, stderr = os.Pipe)\n      expect(res.exitCode != 0)\n      val errOutput = res.err.trim()\n      System.err.println(errOutput)\n      expect(errOutput.contains(\"OutputBin is not a member\"))\n      expect(errOutput.contains(\n        \"Warning: different target JVM (23) and scala compiler target JVM (17) were passed.\"\n      ))\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2.1\"))\n    test(\"warn for different target JVMs in --jvm, -target:x and -release\".tag(jvmT)) {\n      scalaJvm8Project.fromRoot { root =>\n        val res = os.proc(\n          TestUtil.cli,\n          \"compile\",\n          extraOptions,\n          \"--jvm\",\n          \"11\",\n          \"-release\",\n          \"8\",\n          \"-target:8\",\n          \".\"\n        ).call(cwd = root, check = false, stderr = os.Pipe)\n        expect(res.exitCode == 0)\n        val errOutput = res.err.trim()\n        expect(errOutput.contains(\n          \"Warning: different target JVM (11) and scala compiler target JVM (8) were passed.\"\n        ))\n      }\n    }\n\n  def compileToADifferentJvmThanBloops(\n    bloopJvm: String,\n    targetJvm: String,\n    shouldSucceed: Boolean,\n    inputs: TestInputs\n  ): Unit =\n    inputs.fromRoot { root =>\n      val bloop = BloopUtil.bloop(Constants.bloopVersion, bloopDaemonDir, jvm = Some(bloopJvm))\n      bloop(Seq(\"exit\")).call(\n        cwd = root,\n        check = false,\n        stdout = os.Inherit\n      )\n      bloop(Seq(\"about\")).call(\n        cwd = root,\n        check = false,\n        stdout = os.Inherit\n      )\n      val res = os.proc(TestUtil.cli, \"compile\", extraOptions, \"--jvm\", targetJvm, \".\")\n        .call(cwd = root, check = false, stderr = os.Pipe)\n      val succeeded = res.exitCode == 0\n      if succeeded != shouldSucceed then System.err.println(res.err.text())\n      expect(succeeded == shouldSucceed)\n      if !shouldSucceed then\n        expect(\n          res.err.text().contains(\"is not a member\") ||\n          res.err.text().contains(\"cannot find symbol\")\n        )\n    }\n  if (actualScalaVersion.startsWith(\"2.12\"))\n    test(\"JVM options only for JVM platform\") {\n      val inputs = TestInputs(os.rel / \"Main.scala\" -> \"//> using `java-opt` \\\"-Xss1g\\\"\")\n      inputs.fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"compile\", extraOptions, \"--native\", \".\").call(\n          cwd = root,\n          stderr = os.Pipe\n        )\n        val stderr = res.err.text()\n        expect(s\"\\\\[.*warn.*].*Conflicting options.*\".r.findFirstMatchIn(stderr).isDefined)\n\n      }\n    }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"generate scoverage.coverage file\") {\n      val fileName = \"Hello.scala\"\n      val inputs   = TestInputs(\n        os.rel / fileName ->\n          s\"\"\"//> using options -coverage-out:.\n             |\n             |@main def main = ()\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"compile\", extraOptions, fileName)\n          .call(cwd = root)\n          .out.trim()\n\n        val expectedCoverageFilePath = root / \"scoverage.coverage\"\n        expect(os.exists(expectedCoverageFilePath))\n      }\n    }\n\n  if (actualScalaVersion.startsWith(\"2.\"))\n    test(\"no duplicates in class path\") {\n      noDuplicatesInClassPathTest()\n    }\n  def noDuplicatesInClassPathTest(): Unit = {\n    val sparkVersion = \"3.3.0\"\n    val inputs       = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"//> using dep org.apache.spark::spark-sql:$sparkVersion\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"--print-class-path\",\n          extraOptions,\n          \".\"\n        ).call(cwd = root)\n      val classPath          = res.out.trim().split(File.pathSeparator)\n      val classPathFileNames = classPath.map(_.split(\"[\\\\\\\\/]+\").last)\n      expect(classPathFileNames.exists(_.startsWith(\"spark-core_\")))\n      // usually a duplicate is there if we don't call .distrinct when necessary here or there\n      expect(classPathFileNames.exists(_.startsWith(\"snappy-java\")))\n      val duplicates =\n        classPath.groupBy(identity).view.mapValues(_.length).filter(_._2 > 1).toVector\n      expect(duplicates.isEmpty)\n    }\n  }\n\n  test(\"override settings from tests\") {\n    val olderJava = Constants.scala38MinJavaVersion.toString\n    val newerJava = Constants.allJavaVersions.max.toString\n    val inputs    = TestInputs(\n      os.rel / \"MainStuff.scala\" ->\n        s\"\"\"//> using jvm $olderJava\n           |object MainStuff {\n           |  def javaVer = sys.props(\"java.version\")\n           |  def main(args: Array[String]): Unit = {\n           |    println(s\"Found Java $$javaVer in main scope\")\n           |    assert(javaVer == \"$olderJava\" || javaVer.startsWith(\"$olderJava.\"))\n           |  }\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"TestStuff.test.scala\" ->\n        s\"\"\"//> using jvm $newerJava\n           |//> using dep org.scalameta::munit:0.7.29\n           |class TestStuff extends munit.FunSuite {\n           |  test(\"the test\") {\n           |    val javaVer = MainStuff.javaVer\n           |    println(s\"Found Java $$javaVer in test scope\")\n           |    val javaVer0 = {\n           |      val bais = new java.io.ByteArrayInputStream(javaVer.getBytes(\"UTF-8\"))\n           |      new String(bais.readAllBytes(), \"UTF-8\") // readAllBytes available only on Java 17 (not on Java 8)\n           |    }\n           |    assert(javaVer0 == \"$newerJava\" || javaVer0.startsWith(\"$newerJava.\"))\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"compile\", \"--test\", \".\")\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n      os.proc(TestUtil.cli, \"run\", \".\")\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n      os.proc(TestUtil.cli, \"test\", \".\")\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n    }\n  }\n\n  test(\"scalapy\") {\n\n    def maybeScalapyPrefix =\n      if (actualScalaVersion.startsWith(\"2.13.\")) \"\"\n      else \"import me.shadaj.scalapy.py\" + System.lineSeparator()\n\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"$maybeScalapyPrefix\n           |object Hello {\n           |  def main(args: Array[String]): Unit = {\n           |    py.Dynamic.global.print(\"Hello from Python\", flush = true)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"compile\",\n          \"--python\",\n          \"--print-class-path\",\n          \".\",\n          extraOptions\n        )\n          .call(cwd = root)\n      val classPath  = res.out.trim().split(File.pathSeparator)\n      val outputDir  = os.Path(classPath.head, root)\n      val classFiles = os.walk(outputDir)\n        .filter(_.last.endsWith(\".class\"))\n        .filter(os.isFile(_))\n        .map(_.relativeTo(outputDir))\n      val path = os.rel / \"Hello.class\"\n      expect(classFiles.contains(path))\n    }\n  }\n\n  private def compilerArtifactName: String =\n    if (actualScalaVersion.startsWith(\"3\")) \"scala3-compiler\" else \"scala-compiler\"\n\n  test(s\"ensure the -with-compiler option adds $compilerArtifactName to the classpath\") {\n    TestInputs(os.rel / \"s.sc\" -> \"\"\"println(\"Hello\")\"\"\")\n      .fromRoot { root =>\n        val compileRes = os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"s.sc\",\n          \"--print-classpath\",\n          extraOptions\n        )\n          .call(cwd = root)\n        expect(!compileRes.out.trim().contains(compilerArtifactName))\n        val compileWithCompilerRes = os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"s.sc\",\n          \"-with-compiler\",\n          \"--print-classpath\",\n          extraOptions\n        )\n          .call(cwd = root)\n        expect(compileWithCompilerRes.out.trim().contains(compilerArtifactName))\n      }\n  }\n\n  test(s\"reuse cached project file under .scala-build\") {\n    TestInputs(os.rel / \"main.scala\" ->\n      \"\"\"object Main extends App {\n        |  println(\"Hello\")\n        |}\"\"\".stripMargin)\n      .fromRoot { root =>\n        val firstRes = os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"main.scala\"\n        ).call(cwd = root, mergeErrIntoOut = true)\n\n        expect(os.list(root / Constants.workspaceDirName).count(\n          _.baseName.startsWith(root.baseName)\n        ) == 1)\n        val firstOutput = TestUtil.normalizeConsoleOutput(firstRes.out.text())\n        expect(firstOutput.contains(\"Compiled project\"))\n\n        val differentRes = os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"main.scala\",\n          \"--jvm\",\n          Constants.scala38MinJavaVersion.toString\n        ).call(cwd = root, mergeErrIntoOut = true)\n\n        expect(os.list(root / Constants.workspaceDirName).count(\n          _.baseName.startsWith(root.baseName)\n        ) == 2)\n        val differentOutput = TestUtil.normalizeConsoleOutput(differentRes.out.text())\n        expect(differentOutput.contains(\"Compiled project\"))\n\n        val secondRes = os.proc(\n          TestUtil.cli,\n          \"compile\",\n          \"main.scala\"\n        ).call(cwd = root, mergeErrIntoOut = true)\n\n        expect(os.list(root / Constants.workspaceDirName).count(\n          _.baseName.startsWith(root.baseName)\n        ) == 2)\n        val secondOutput = TestUtil.normalizeConsoleOutput(secondRes.out.text())\n        expect(!secondOutput.contains(\"Compiled project\"))\n      }\n  }\n\n  test(\n    \"pass java options to scalac when server=false (Scala-only, no .java-not-compiled warning)\"\n  ) {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main extends App {\n          |  println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \"--scalac-option=-J-XX:MaxHeapSize=1k\",\n        \"--server=false\",\n        extraOptions,\n        \".\"\n      )\n        .call(cwd = root, check = false, mergeErrIntoOut = true)\n      expect(res.exitCode == 1)\n      val out = res.out.text()\n      expect(out.contains(\"Error occurred during initialization of VM\"))\n      expect(out.contains(\"Too small maximum heap\"))\n      expect(!out.contains(\".java files are not compiled to .class files\"))\n    }\n  }\n\n  test(\"new build targets should only be created when CLI options change\") {\n    val filename = \"Main.scala\"\n    val inputs   = TestInputs(\n      os.rel / filename ->\n        \"\"\"object Main extends App {\n          |  println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Test.test.scala\" ->\n        \"\"\"object Test extends App {\n          |  println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"compile\", extraOptions :+ \"--test\", \".\").call(cwd = root)\n\n      def buildTargetDirs = os.list(root / Constants.workspaceDirName)\n        .filter(os.isDir)\n        .filter(_.last != \".bloop\")\n\n      expect(buildTargetDirs.size == 1)\n\n      os.write.over(\n        root / filename,\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |\n          |object Main extends App {\n          |  println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n      )\n\n      os.proc(TestUtil.cli, \"compile\", extraOptions :+ \"--test\", \".\").call(cwd = root)\n      expect(buildTargetDirs.size == 1)\n\n      os.proc(TestUtil.cli, \"compile\", extraOptions ++ Seq(\"--test\", \"-nowarn\"), \".\").call(cwd =\n        root\n      )\n      expect(buildTargetDirs.size == 2)\n    }\n  }\n\n  if (!Properties.isWin)\n    // TODO: make this work on Windows: https://github.com/VirtusLab/scala-cli/issues/2973\n    test(\n      \"nested wildcard path source exclusion with a directive and no special character escaping\"\n    ) {\n      val excludedFileName = \"Foo.scala\"\n      val excludedPath     = os.rel / \"dir1\" / \"dir2\" / excludedFileName\n      val inputs           = TestInputs(\n        os.rel / \"project.scala\" -> s\"//> using exclude */*/$excludedFileName\",\n        excludedPath             -> \"val foo // invalid code\"\n      )\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"compile\", extraOptions, \".\").call(cwd = root)\n      }\n    }\n\n  test(\"no previous compilation error should be printed\") {\n    val filename = \"Main.scala\"\n    val inputs   = TestInputs(\n      os.rel / filename ->\n        \"\"\"|object Main {\n           |  val msg: String = \"1\"\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"compile\", \".\", extraOptions).call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n\n      val jvmVersion     = Constants.defaultGraalVMJavaVersion\n      val expectedOutput =\n        s\"\"\"|Compiling project (Scala $actualScalaVersion, JVM ($jvmVersion))\n            |Compiled project (Scala $actualScalaVersion, JVM ($jvmVersion))\"\"\".stripMargin\n      val actualOutput = TestUtil.fullStableOutput(result)\n      assertEquals(actualOutput, expectedOutput)\n\n      os.write.over(\n        root / filename,\n        \"\"\"|object Main {\n           |    val msg: String = 1\n           |}\n           |\"\"\".stripMargin\n      )\n\n      val result2 = os.proc(TestUtil.cli, \"compile\", \".\", extraOptions).call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n\n      val expectedError =\n        if actualScalaVersion.startsWith(\"2\") then\n          \"\"\"|[error] type mismatch;\n             |[error]  found   : Int(1)\n             |[error]  required: String\"\"\".stripMargin\n        else\n          \"\"\"|[error] Found:    (1 : Int)\n             |[error] Required: String\"\"\".stripMargin\n\n      val actualOutput2   = TestUtil.fullStableOutput(result2).trim\n      val expectedOutput2 =\n        s\"\"\"|Compiling project (Scala $actualScalaVersion, JVM ($jvmVersion))\n            |[error] .${File.separatorChar}Main.scala:2:23\n            |$expectedError\n            |[error]     val msg: String = 1\n            |[error]                       ^\n            |Error compiling project (Scala $actualScalaVersion, JVM ($jvmVersion))\n            |Compilation failed\"\"\".stripMargin\n      assertEquals(actualOutput2, expectedOutput2)\n\n      os.write.over(\n        root / filename,\n        \"\"\"|object Main {\n           |    val msg: String = \"1\"\n           |}\n           |\"\"\".stripMargin\n      )\n\n      val result3 = os.proc(TestUtil.cli, \"compile\", \".\", extraOptions).call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n\n      val actualOutput3   = TestUtil.fullStableOutput(result3)\n      val expectedOutput3 =\n        s\"\"\"|Compiling project (Scala $actualScalaVersion, JVM ($jvmVersion))\n            |Compiled project (Scala $actualScalaVersion, JVM ($jvmVersion))\"\"\".stripMargin\n      assertEquals(actualOutput3, expectedOutput3)\n    }\n  }\n\n  test(\"i3389\") {\n    val filename = \"Main.scala\"\n    val inputs   = TestInputs(\n      os.rel / filename ->\n        \"\"\"//> using optionsdeprecation\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"compile\", \".\", extraOptions).call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n      assertEquals(\n        TestUtil.fullStableOutput(result).trim(),\n        s\"\"\"|[error] .${File.separatorChar}Main.scala:1:11\n            |[error] Unrecognized directive: optionsdeprecation\n            |[error] //> using optionsdeprecation\n            |[error]           ^^^^^^^^^^^^^^^^^^\"\"\".stripMargin\n      )\n    }\n  }\n\n  test(\"i3389-2\") {\n    val filename = \"Main.scala\"\n    val inputs   = TestInputs(\n      os.rel / filename ->\n        \"\"\"//> using unrecognised.directive value1 value2\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"compile\", \".\", extraOptions).call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n      assertEquals(\n        TestUtil.fullStableOutput(result).trim(),\n        s\"\"\"|[error] .${File.separatorChar}Main.scala:1:11\n            |[error] Unrecognized directive: unrecognised.directive with values: value1, value2\n            |[error] //> using unrecognised.directive value1 value2\n            |[error]           ^^^^^^^^^^^^^^^^^^^^^^\"\"\".stripMargin\n      )\n    }\n  }\n\n  if (!Properties.isMac || !TestUtil.isCI)\n    test(\"--watching with --watch re-compiles on external file change\") {\n      val sourceFile   = os.rel / \"Main.scala\"\n      val externalFile = os.rel / \"data\" / \"input.txt\"\n      TestInputs(\n        sourceFile ->\n          \"\"\"object Main {\n            |  def value = 1\n            |}\n            |\"\"\".stripMargin,\n        externalFile -> \"Hello\"\n      ).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          proc = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"compile\",\n            \".\",\n            \"--watch\",\n            \"--watching\",\n            \"data\",\n            extraOptions\n          )\n            .spawn(cwd = root, stderr = os.Pipe),\n          timeout = 120.seconds\n        ) { (proc, timeout, ec) =>\n          implicit val ec0  = ec\n          val initialOutput = proc.readStderrUntilWatchingMessage(timeout)\n          expect(initialOutput.exists(_.contains(\"Compiled\")))\n\n          Thread.sleep(2000L)\n          os.write.over(root / externalFile, \"World\")\n\n          val rerunOutput = proc.readStderrUntilWatchingMessage(timeout)\n          expect(rerunOutput.nonEmpty)\n        }\n      }\n    }\n\n  test(\"sbt file in directory does not break compile\") {\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main {\n          |  def main(args: Array[String]): Unit = println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"build.sbt\" -> \"\"\"name := \"my-project\"\"\"\"\n    ).fromRoot { root =>\n      os.proc(TestUtil.cli, \"compile\", extraOptions, \".\").call(cwd = root)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTests212.scala",
    "content": "package scala.cli.integration\n\nclass CompileTests212 extends CompileTestDefinitions with Test212 {\n  val pluginInputs: TestInputs = TestInputs(\n    os.rel / \"Plugin.scala\" ->\n      // Copied from (https://github.com/typelevel/kind-projector/blob/00bf25cef1b7d01d61a3555cccb6cf38fe30e117/src/test/scala/polylambda.scala)\n      \"\"\"object Plugin {\n        |  trait ~>[-F[_], +G[_]] {\n        |    def apply[A](x: F[A]): G[A]\n        |  }\n        |  type ToSelf[F[_]] = F ~> F\n        |  val kf5 = λ[Map[*, Int] ~> Map[*, Long]](_.map { case (k, v) => (k, v.toLong) }.toMap)\n        |  val kf6 = λ[ToSelf[Map[*, Int]]](_.map { case (k, v) => (k, v * 2) }.toMap)\n        |}\n        |\"\"\".stripMargin\n  )\n\n  val kindProjectPlugin = \"org.typelevel:::kind-projector:0.13.4\"\n\n  test(\"should compile with compiler plugin\") {\n    pluginInputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"compile\",\n        extraOptions,\n        \".\",\n        \"--compiler-plugin\",\n        kindProjectPlugin\n      ).call(cwd = root).out.text()\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTests213.scala",
    "content": "package scala.cli.integration\n\nimport scala.util.Properties\n\nclass CompileTests213 extends CompileTestDefinitions with Test213 {\n\n  test(\"test-macro-output\") {\n    val triple = \"\\\"\\\"\\\"\"\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        s\"\"\"|//> using scala ${Constants.scala213}\n            |//> using dep org.scala-lang:scala-reflect:${Constants.scala213}\n            |package example\n            |import scala.reflect.macros.blackbox\n            |import scala.language.experimental.macros\n            |\n            |object Scala2Example {\n            |  def macroMethod[A](a: A): String =\n            |    macro Scala2Example.macroMethodImpl[A]\n            |\n            |  def macroMethodImpl[A: c.WeakTypeTag](\n            |    c: blackbox.Context\n            |  )(a: c.Expr[A]): c.Expr[String] = {\n            |    import c.universe._\n            |    val output = s$triple$${show(a.tree)}\n            |                  |$${showCode(a.tree)}\n            |                  |$${showRaw(a.tree)}\n            |                  |$${weakTypeTag[A]}\n            |                  |$${weakTypeOf[A]}\n            |                  |$${showRaw(weakTypeOf[A])}$triple.stripMargin\n            |    c.echo(c.enclosingPosition, output)\n            |    c.warning(c.enclosingPosition, \"example error message\")\n            |    c.abort(c.enclosingPosition, \"example error message\")\n            |  }\n            |}\n            |\"\"\".stripMargin,\n      os.rel / \"Test.test.scala\" ->\n        \"\"\"|//> using test.dep org.scalameta::munit::1.0.0\n           |package example\n           |\n           |class Tests extends munit.FunSuite {\n           |  test(\"macro works OK\") {\n           |    Scala2Example.macroMethod(1 -> \"test\")\n           |  }\n           |}\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"test\", \".\", extraOptions).call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n      val separator           = if (Properties.isWin) \"\\\\\" else \"/\"\n      val graalVmVersion      = Constants.defaultGraalVMJavaVersion\n      val legacyRunnerVersion = Constants.runnerScala2LegacyVersion\n      val ltsPrefix           = Constants.scala3LtsPrefix\n\n      val expectedOutput =\n        s\"\"\"|Compiling project (Scala $actualScalaVersion, JVM ($graalVmVersion))\n            |Compiled project (Scala $actualScalaVersion, JVM ($graalVmVersion))\n            |[warn] Scala $actualScalaVersion is no longer supported by the test-runner module.\n            |[warn] Defaulting to a legacy test-runner module version: $legacyRunnerVersion.\n            |[warn] To use the latest test-runner, upgrade Scala to at least $ltsPrefix.\n            |Compiling project (test, Scala $actualScalaVersion, JVM ($graalVmVersion))\n            |[info] .${separator}Test.test.scala:6:5\n            |[info] scala.Predef.ArrowAssoc[Int](1).->[String](\"test\")\n            |[info] scala.Predef.ArrowAssoc[Int](1).->[String](\"test\")\n            |[info] Apply(TypeApply(Select(Apply(TypeApply(Select(Select(Ident(scala), scala.Predef), TermName(\"ArrowAssoc\")), List(TypeTree())), List(Literal(Constant(1)))), TermName(\"$$minus$$greater\")), List(TypeTree())), List(Literal(Constant(\"test\"))))\n            |[info] WeakTypeTag[(Int, String)]\n            |[info] (Int, String)\n            |[info] TypeRef(ThisType(scala), scala.Tuple2, List(TypeRef(ThisType(scala), scala.Int, List()), TypeRef(ThisType(java.lang), java.lang.String, List())))\n            |[info]     Scala2Example.macroMethod(1 -> \"test\")\n            |[info]     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n            |[error] .${separator}Test.test.scala:6:5\n            |[error] example error message\n            |[error]     Scala2Example.macroMethod(1 -> \"test\")\n            |[error]     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n            |Error compiling project (test, Scala $actualScalaVersion, JVM ($graalVmVersion))\n            |Compilation failed\n            |\"\"\".stripMargin\n\n      assertNoDiff(\n        TestUtil.fullStableOutput(result),\n        expectedOutput\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass CompileTests3Lts extends CompileTestDefinitions with CompileTests3StableDefinitions\n    with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass CompileTests3NextRc extends CompileTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTests3StableDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\ntrait CompileTests3StableDefinitions { this: CompileTestDefinitions =>\n  test(s\"TASTY processor does not warn about Scala $actualScalaVersion\") {\n    TestInputs(os.rel / \"simple.sc\" -> s\"\"\"println(\"Hello\")\"\"\")\n      .fromRoot { root =>\n        val result =\n          os.proc(TestUtil.cli, \"compile\", \".\", extraOptions)\n            .call(cwd = root, stderr = os.Pipe)\n        expect(result.exitCode == 0)\n        expect(!result.err.text().contains(\"cannot post process TASTY files\"))\n      }\n  }\n\n  test(\"render explain message\") {\n    val fileName = \"Hello.scala\"\n    val inputs   = TestInputs(\n      os.rel / fileName -> // should be dump to 3.3.1 after release\n        s\"\"\"//> using scala 3.3.1-RC1-bin-20230203-3ef1e73-NIGHTLY\n           |//> using options --explain\n           |\n           |class A\n           |val i: Int = A()\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val out = os.proc(TestUtil.cli, \"compile\", extraOptions, fileName)\n        .call(cwd = root, check = false, mergeErrIntoOut = true).out.trim()\n\n      expect(out.contains(\"Explanation\"))\n    }\n  }\n\n  test(\"as jar\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        \"\"\"object Foo {\n          |  def n = 2\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val out = os.proc(TestUtil.cli, \"compile\", extraOptions, \".\", \"--print-class-path\")\n        .call(cwd = root)\n        .out.trim()\n      val cp = out.split(File.pathSeparator).toVector.map(os.Path(_, root))\n      expect(cp.headOption.exists(os.isDir(_)))\n      expect(cp.drop(1).forall(os.isFile(_)))\n\n      val asJarOut = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"compile\",\n        extraOptions,\n        \".\",\n        \"--print-class-path\",\n        \"--as-jar\"\n      )\n        .call(cwd = root)\n        .out.trim()\n      val asJarCp = asJarOut.split(File.pathSeparator).toVector.map(os.Path(_, root))\n      expect(asJarCp.forall(os.isFile(_)))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompileTestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass CompileTestsDefault extends CompileTestDefinitions with CompileTests3StableDefinitions\n    with TestDefault {\n  test(\n    s\"compile --cross $actualScalaVersion with ${Constants.scala213} and ${Constants.scala212}\"\n  ) {\n    val crossVersions = Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n    simpleInputs\n      .add(os.rel / \"project.scala\" -> s\"//> using scala ${crossVersions.mkString(\" \")}\")\n      .fromRoot { root =>\n        os.proc(TestUtil.cli, \"compile\", \".\", \"--cross\", \"--power\", extraOptions).call(cwd = root)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompilerPluginTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.util.CompilerPluginUtil\n\ntrait CompilerPluginTestDefinitions { this: CompileTestDefinitions =>\n  def compilerPluginInputs(pluginName: String, pluginErrorMsg: String): TestInputs =\n    if (actualScalaVersion.startsWith(\"3\"))\n      CompilerPluginUtil.compilerPluginForScala3(pluginName, pluginErrorMsg)\n    else CompilerPluginUtil.compilerPluginForScala2(pluginName, pluginErrorMsg)\n\n  for {\n    pluginViaDirective <- Seq(true, false)\n    testLabel = if (pluginViaDirective) \"use plugin via directive\" else \"use plugin via CLI option\"\n  }\n    test(s\"build a custom compiler plugin and use it ($testLabel)\") {\n      val pluginName     = \"divbyzero\"\n      val usePluginFile  = \"Main.scala\"\n      val outputJar      = \"div-by-zero.jar\"\n      val pluginErrorMsg = \"definitely division by zero\"\n      compilerPluginInputs(pluginName, pluginErrorMsg)\n        .add(os.rel / usePluginFile ->\n          s\"\"\"${if (pluginViaDirective) s\"//> using option -Xplugin:$outputJar\" else \"\"}\n             |\n             |object Test {\n             |  val five = 5\n             |  val amount = five / 0\n             |  def main(args: Array[String]): Unit = {\n             |    println(amount)\n             |  }\n             |}\n             |\"\"\".stripMargin)\n        .fromRoot { root =>\n          // build the compiler plugin\n          os.proc(\n            TestUtil.cli,\n            \"package\",\n            s\"$pluginName.scala\",\n            \"--power\",\n            \"--with-compiler\",\n            \"--library\",\n            \"-o\",\n            outputJar,\n            extraOptions\n          ).call(cwd = root)\n          expect(os.isFile(root / outputJar))\n\n          // verify the plugin is loaded\n          val pluginListResult = os.proc(\n            TestUtil.cli,\n            \"compile\",\n            s\"-Xplugin:$outputJar\",\n            \"-Xplugin-list\",\n            extraOptions\n          ).call(cwd = root, mergeErrIntoOut = true)\n          expect(pluginListResult.out.text().contains(pluginName))\n\n          // verify the compiler plugin phase is being added correctly\n          os.proc(\n            TestUtil.cli,\n            \"compile\",\n            s\"-Xplugin:$outputJar\",\n            \"-Xshow-phases\",\n            extraOptions\n          ).call(cwd = root, mergeErrIntoOut = true)\n          expect(pluginListResult.out.text().contains(pluginName))\n\n          val pluginOptions = if (pluginViaDirective) Nil else Seq(s\"-Xplugin:$outputJar\")\n          // verify the compiler plugin is working\n          // TODO: this shouldn't require running with --server=false\n          val res = os.proc(\n            TestUtil.cli,\n            \"compile\",\n            pluginOptions,\n            usePluginFile,\n            \"--server=false\",\n            extraOptions\n          )\n            .call(cwd = root, mergeErrIntoOut = true, check = false)\n          expect(res.exitCode == 1)\n          expect(res.out.text().contains(pluginErrorMsg))\n        }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CompleteTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass CompleteTests extends ScalaCliSuite {\n  test(\"simple\") {\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"complete\", shellFormat, \"2\", \"com\").call(cwd = root)\n      expect(res.exitCode == 0)\n      expect(res.out.trim().nonEmpty)\n    }\n  }\n\n  test(\"zsh bug\") {\n    // guard against https://github.com/alexarchambault/case-app/issues/475\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"complete\", shellFormat, \"2\", \"scala-cli\").call(cwd = root)\n      expect(res.exitCode == 0)\n      expect(!res.out.text().contains(raw\"\\'\"))\n    }\n  }\n\n  def shellFormat: String =\n    if (Properties.isMac) \"zsh-v1\"\n    else \"bash-v1\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ConfigTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\nimport scala.util.Properties\n\nclass ConfigTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  test(\"simple\") {\n    val configFile = os.rel / \"config\" / \"config.json\"\n    val configEnv  = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString())\n    val name       = \"Alex\"\n    TestInputs.empty.fromRoot { root =>\n      val before =\n        // Test --power placed after subcommand name\n        os.proc(TestUtil.cli, \"config\", \"publish.user.name\", \"--power\").call(\n          cwd = root,\n          env = configEnv\n        )\n      expect(before.out.trim().isEmpty)\n\n      os.proc(TestUtil.cli, \"config\", \"publish.user.name\", name, \"--power\").call(\n        cwd = root,\n        env = configEnv\n      )\n      val res =\n        os.proc(TestUtil.cli, \"config\", \"publish.user.name\", \"--power\").call(\n          cwd = root,\n          env = configEnv\n        )\n      expect(res.out.trim() == name)\n\n      os.proc(TestUtil.cli, \"config\", \"publish.user.name\", \"--unset\", \"--power\").call(\n        cwd = root,\n        env = configEnv\n      )\n      val after =\n        os.proc(TestUtil.cli, \"config\", \"publish.user.name\", \"--power\").call(\n          cwd = root,\n          env = configEnv\n        )\n      expect(after.out.trim().isEmpty)\n    }\n  }\n\n  test(\"password\") {\n    val configFile = os.rel / \"config\" / \"config.json\"\n    val configEnv  = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n    val password   = \"1234\"\n    val key        = \"httpProxy.password\"\n    TestInputs.empty.fromRoot { root =>\n\n      def emptyCheck(): Unit = {\n        val value = os.proc(TestUtil.cli, \"--power\", \"config\", key)\n          .call(cwd = root, env = configEnv)\n        expect(value.out.trim().isEmpty)\n      }\n\n      def unset(): Unit =\n        os.proc(TestUtil.cli, \"--power\", \"config\", key, \"--unset\")\n          .call(cwd = root, env = configEnv)\n\n      def read(): String = {\n        val res = os.proc(TestUtil.cli, \"--power\", \"config\", key)\n          .call(cwd = root, env = configEnv)\n        res.out.trim()\n      }\n      def readDecoded(env: Map[String, String] = Map.empty): String = {\n        val res = os.proc(TestUtil.cli, \"--power\", \"config\", key, \"--password-value\")\n          .call(cwd = root, env = configEnv ++ env)\n        res.out.trim()\n      }\n\n      emptyCheck()\n\n      os.proc(TestUtil.cli, \"--power\", \"config\", key, s\"value:$password\")\n        .call(cwd = root, env = configEnv)\n      expect(read() == s\"value:$password\")\n      expect(readDecoded() == password)\n      unset()\n      emptyCheck()\n\n      os.proc(TestUtil.cli, \"--power\", \"config\", key, \"env:MY_PASSWORD\")\n        .call(cwd = root, env = configEnv)\n      expect(read() == \"env:MY_PASSWORD\")\n      expect(readDecoded(env = Map(\"MY_PASSWORD\" -> password)) == password)\n      unset()\n      emptyCheck()\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"config\",\n        key,\n        \"env:MY_PASSWORD\",\n        \"--password-value\"\n      )\n        .call(cwd = root, env = Map(\"MY_PASSWORD\" -> password) ++ configEnv)\n      expect(read() == s\"value:$password\")\n      expect(readDecoded() == password)\n      unset()\n      emptyCheck()\n    }\n  }\n\n  test(\"Respect SCALA_CLI_CONFIG and format on write\") {\n    val proxyAddr = \"https://foo.bar.com\"\n    TestInputs().fromRoot { root =>\n      val confDir  = root / \"config\"\n      val confFile = confDir / \"test-config.json\"\n      val content  =\n        // non-formatted on purpose\n        s\"\"\"{\n           |  \"httpProxy\": {  \"address\" :      \"$proxyAddr\"     } }\n           |\"\"\".stripMargin\n      os.write(confFile, content, createFolders = true)\n\n      if (!Properties.isWin)\n        os.perms.set(confDir, \"rwx------\")\n\n      val extraEnv =\n        Map(\"SCALA_CLI_CONFIG\" -> confFile.toString) ++\n          TestUtil.putCsInPathViaEnv(root / \"bin\")\n\n      val res = os.proc(TestUtil.cli, \"--power\", \"config\", \"httpProxy.address\")\n        .call(cwd = root, env = extraEnv)\n      val value = res.out.trim()\n      expect(value == proxyAddr)\n\n      os.proc(TestUtil.cli, \"config\", \"interactive\", \"false\")\n        .call(cwd = root, env = extraEnv)\n\n      val expectedUpdatedContent =\n        // too many spaces after some ':' (jsoniter-scala bug?)\n        s\"\"\"{\n           |  \"httpProxy\": {\n           |    \"address\":       \"https://foo.bar.com\"\n           |  },\n           |  \"interactive\": false\n           |}\n           |\"\"\".stripMargin.replace(\"\\r\\n\", \"\\n\")\n      val updatedContent = os.read(confFile)\n      expect(updatedContent == expectedUpdatedContent)\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"Exit with non-zero error code if saving failed\") {\n      nonZeroErrorCodeOnFailedSaveTest()\n    }\n  def nonZeroErrorCodeOnFailedSaveTest(): Unit = {\n    val proxyAddr = \"https://foo.bar.com\"\n    TestInputs().fromRoot { root =>\n      val confDir = root / \"config\"\n      os.makeDir.all(confDir) // not adjusting perms - should make things fail below\n\n      val confFile = confDir / \"test-config.json\"\n      val extraEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n      val res = os.proc(TestUtil.cli, \"--power\", \"config\", \"httpProxy.address\", proxyAddr)\n        .call(cwd = root, env = extraEnv, check = false, mergeErrIntoOut = true)\n      val output = res.out.trim()\n      expect(output.contains(\" has wrong permissions\"))\n    }\n  }\n\n  if (!TestUtil.isCI || !Properties.isWin)\n    for (pgpPasswordOption <- List(\"none\", \"random\", \"MY_CHOSEN_PASSWORD\"))\n      test(s\"Create a default PGP key, password: $pgpPasswordOption\") {\n        createDefaultPgpKeyTest(pgpPasswordOption)\n      }\n\n  if (TestUtil.isNativeCli)\n    test(s\"Create a PGP key with external JVM process, java version too low\") {\n      TestUtil.retryOnCi() {\n        TestInputs().fromRoot { root =>\n          val configFile = {\n            val dir = root / \"config\"\n            os.makeDir.all(dir, perms = if (Properties.isWin) null else \"rwx------\")\n            dir / \"config.json\"\n          }\n\n          val java8Home =\n            os.Path(os.proc(TestUtil.cs, \"java-home\", \"--jvm\", \"zulu:8\").call().out.trim(), os.pwd)\n\n          val extraEnv = Map(\n            \"JAVA_HOME\" -> java8Home.toString,\n            \"PATH\" -> ((java8Home / \"bin\").toString + File.pathSeparator + System.getenv(\"PATH\")),\n            \"SCALA_CLI_CONFIG\" -> configFile.toString\n          )\n\n          val pgpCreated = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"config\",\n            \"--create-pgp-key\",\n            \"--email\",\n            \"alex@alex.me\",\n            \"--pgp-password\",\n            \"none\",\n            \"--force-jvm-signing-cli\",\n            \"-v\",\n            \"-v\",\n            \"-v\"\n          )\n            .call(cwd = root, env = extraEnv, mergeErrIntoOut = true)\n\n          val javaCommandLine = pgpCreated.out.text()\n            .linesIterator\n            .dropWhile(!_.equals(\"  Running\")).slice(1, 2)\n            .toSeq\n\n          expect(javaCommandLine.nonEmpty)\n          expect(javaCommandLine.head.contains(\"17\"))\n\n          val passwordInConfig =\n            os.proc(TestUtil.cli, \"--power\", \"config\", \"pgp.secret-key-password\")\n              .call(cwd = root, env = extraEnv, stderr = os.Pipe)\n          expect(passwordInConfig.out.text().isEmpty())\n\n          val secretKey = os.proc(TestUtil.cli, \"--power\", \"config\", \"pgp.secret-key\")\n            .call(cwd = root, env = extraEnv, stderr = os.Pipe)\n            .out.trim()\n          val rawPublicKey =\n            os.proc(TestUtil.cli, \"--power\", \"config\", \"pgp.public-key\", \"--password-value\")\n              .call(cwd = root, env = extraEnv, stderr = os.Pipe)\n              .out.trim()\n\n          val tmpFile    = root / \"test-file\"\n          val tmpFileAsc = root / \"test-file.asc\"\n          os.write(tmpFile, \"Hello\")\n\n          val q = \"\\\"\"\n\n          def maybeEscape(arg: String): String =\n            if (Properties.isWin) q + arg + q\n            else arg\n\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"pgp\",\n            \"sign\",\n            \"--secret-key\",\n            maybeEscape(secretKey),\n            tmpFile\n          ).call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = extraEnv)\n\n          val pubKeyFile = root / \"key.pub\"\n          os.write(pubKeyFile, rawPublicKey)\n          val verifyResult =\n            os.proc(TestUtil.cli, \"--power\", \"pgp\", \"verify\", \"--key\", pubKeyFile, tmpFileAsc)\n              .call(cwd = root, env = extraEnv, mergeErrIntoOut = true)\n\n          expect(verifyResult.out.text().contains(\"valid signature\"))\n        }\n      }\n    }\n\n  def createDefaultPgpKeyTest(pgpPasswordOption: String): Unit = {\n    TestInputs().fromRoot { root =>\n      val configFile = {\n        val dir = root / \"config\"\n        os.makeDir.all(dir, perms = if (Properties.isWin) null else \"rwx------\")\n        dir / \"config.json\"\n      }\n      val extraEnv = Map(\n        \"SCALA_CLI_CONFIG\" -> configFile.toString\n      )\n      val checkPassword = os.proc(TestUtil.cli, \"--power\", \"config\", \"--create-pgp-key\")\n        .call(cwd = root, env = extraEnv, check = false, mergeErrIntoOut = true)\n      expect(checkPassword.exitCode != 0)\n      expect(checkPassword.out.text().contains(\"--pgp-password\"))\n\n      val checkEmail = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"config\",\n        \"--create-pgp-key\",\n        \"--pgp-password\",\n        pgpPasswordOption\n      )\n        .call(cwd = root, env = extraEnv, check = false, mergeErrIntoOut = true)\n      expect(checkEmail.exitCode != 0)\n      expect(checkEmail.out.text().contains(\"--email\"))\n\n      val pgpCreated = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"config\",\n        \"--create-pgp-key\",\n        \"--email\",\n        \"alex@alex.me\",\n        \"--pgp-password\",\n        pgpPasswordOption\n      )\n        .call(cwd = root, env = extraEnv, mergeErrIntoOut = true)\n\n      val pgpPasswordOpt: Option[String] = pgpCreated.out.text()\n        .linesIterator\n        .toSeq\n        .find(_.startsWith(\"Password\"))\n        .map(_.stripPrefix(\"Password:\").trim())\n\n      if (pgpPasswordOption != \"random\")\n        expect(pgpPasswordOpt.isEmpty)\n      else\n        expect(pgpPasswordOpt.isDefined)\n\n      val passwordInConfig = os.proc(TestUtil.cli, \"--power\", \"config\", \"pgp.secret-key-password\")\n        .call(cwd = root, env = extraEnv, stderr = os.Pipe)\n      expect(passwordInConfig.out.text().isEmpty())\n\n      val secretKey = os.proc(TestUtil.cli, \"--power\", \"config\", \"pgp.secret-key\")\n        .call(cwd = root, env = extraEnv, stderr = os.Pipe)\n        .out.trim()\n      val rawPublicKey =\n        os.proc(TestUtil.cli, \"--power\", \"config\", \"pgp.public-key\", \"--password-value\")\n          .call(cwd = root, env = extraEnv, stderr = os.Pipe)\n          .out.trim()\n\n      val tmpFile    = root / \"test-file\"\n      val tmpFileAsc = root / \"test-file.asc\"\n      os.write(tmpFile, \"Hello\")\n\n      val q                                = \"\\\"\"\n      def maybeEscape(arg: String): String =\n        if (Properties.isWin) q + arg + q\n        else arg\n      val signProcess = if (pgpPasswordOption != \"none\")\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"pgp\",\n          \"sign\",\n          \"--password\",\n          s\"value:${maybeEscape(pgpPasswordOpt.getOrElse(\"MY_CHOSEN_PASSWORD\"))}\",\n          \"--secret-key\",\n          maybeEscape(secretKey),\n          tmpFile\n        )\n      else\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"pgp\",\n          \"sign\",\n          \"--secret-key\",\n          maybeEscape(secretKey),\n          tmpFile\n        )\n      signProcess.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = extraEnv)\n\n      val pubKeyFile = root / \"key.pub\"\n      os.write(pubKeyFile, rawPublicKey)\n      val verifyResult =\n        os.proc(TestUtil.cli, \"--power\", \"pgp\", \"verify\", \"--key\", pubKeyFile, tmpFileAsc)\n          .call(cwd = root, env = extraEnv, mergeErrIntoOut = true)\n\n      expect(verifyResult.out.text().contains(\"valid signature\"))\n    }\n  }\n\n  test(\"repository credentials\") {\n    val testOrg     = \"test-org\"\n    val testName    = \"the-messages\"\n    val testVersion = \"0.1.2\"\n    val user        = \"alex\"\n    val password    = \"1234\"\n    val realm       = \"LeTestRealm\"\n    val inputs      = TestInputs(\n      os.rel / \"messages\" / \"Messages.scala\" ->\n        \"\"\"package messages\n          |\n          |object Messages {\n          |  def hello(name: String): String =\n          |    s\"Hello $name\"\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"hello\" / \"Hello.scala\" ->\n        s\"\"\"//> using dep $testOrg::$testName:$testVersion\n           |import messages.Messages\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(Messages.hello(args.headOption.getOrElse(\"Unknown\")))\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val configFile = {\n        val dir = root / \"conf\"\n        os.makeDir.all(dir, if (Properties.isWin) null else \"rwx------\")\n        dir / \"config.json\"\n      }\n      val extraEnv = Map(\n        \"SCALA_CLI_CONFIG\" -> configFile.toString\n      )\n      val repoPath = root / \"the-repo\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"--publish-repo\",\n        repoPath.toNIO.toUri.toASCIIString,\n        \"messages\",\n        \"--organization\",\n        testOrg,\n        \"--name\",\n        testName,\n        \"--project-version\",\n        testVersion\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = extraEnv)\n\n      TestUtil.serveFilesInHttpServer(repoPath, user, password, realm) { (host, port) =>\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"config\",\n          \"repositories.credentials\",\n          host,\n          s\"value:$user\",\n          s\"value:$password\",\n          realm\n        )\n          .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = extraEnv)\n        val credentialsAsStringRes = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"config\",\n          \"repositories.credentials\"\n        ).call(cwd = root, env = extraEnv)\n        val linePrefix                  = \"configRepo0\"\n        val expectedCredentialsAsString =\n          s\"\"\"$linePrefix.host=$host\n             |$linePrefix.username=value:$user\n             |$linePrefix.password=value:$password\n             |$linePrefix.realm=$realm\n             |$linePrefix.auto=true\"\"\".stripMargin\n        expect(credentialsAsStringRes.out.trim() == expectedCredentialsAsString)\n        val res = os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--repository\",\n          s\"http://$host:$port\",\n          \"hello\",\n          \"--\",\n          \"TestUser\"\n        )\n          .call(cwd = root, env = extraEnv)\n        val output = res.out.trim()\n        expect(output == \"Hello TestUser\")\n      }\n    }\n  }\n\n  test(\"password-value in credentials\") {\n    val configFile         = os.rel / \"config\" / \"config.json\"\n    val passwordEnvVarName = \"REPO_PASSWORD\"\n    val userEnvVarName     = \"REPO_USER\"\n    val password           = \"1234\"\n    val user               = \"user\"\n    val envVars            = Map(\n      userEnvVarName     -> user,\n      passwordEnvVarName -> password\n    )\n    val configEnv = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n\n    val keys = List(\"repositories.credentials\", \"publish.credentials\")\n    TestInputs.empty.fromRoot { root =>\n      for (key <- keys) {\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"config\",\n          key,\n          \"s1.oss.sonatype.org\",\n          s\"env:$userEnvVarName\",\n          s\"env:$passwordEnvVarName\",\n          \"--password-value\"\n        )\n          .call(cwd = root, env = configEnv ++ envVars)\n        val credsFromConfig = os.proc(TestUtil.cli, \"--power\", \"config\", key)\n          .call(cwd = root, env = configEnv)\n          .out.trim()\n        expect(credsFromConfig.contains(password))\n        expect(credsFromConfig.contains(user))\n      }\n    }\n  }\n\n  for (\n    (entryType, key, valuesPlural, invalidValue) <- Seq(\n      (\"boolean\", \"power\", Seq(\"true\", \"false\", \"true\"), \"true.\"),\n      (\"string\", \"publish.user.name\", Seq(\"abc\", \"def\", \"xyz\"), \"\"),\n      (\"password\", \"httpProxy.password\", Seq(\"value:pass1\", \"value:pass2\", \"value:pass3\"), \"pass\")\n    )\n  ) {\n    test(s\"print a meaningful error when multiple values are passed for a $entryType key: $key\") {\n      val configFile = os.rel / \"config\" / \"config.json\"\n      val env        = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n      TestInputs.empty.fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"--power\", \"config\", key, valuesPlural)\n          .call(cwd = root, env = env, stderr = os.Pipe, check = false)\n        expect(res.exitCode == 1)\n        expect(res.err.trim().contains(s\"expected a single $entryType value\"))\n      }\n    }\n\n    if (entryType != \"string\")\n      test(s\"print a meaningful error when an invalid value is passed for a $entryType key: $key\") {\n        val configFile = os.rel / \"config\" / \"config.json\"\n        val env        = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n        TestInputs.empty.fromRoot { root =>\n          val res = os.proc(TestUtil.cli, \"--power\", \"config\", key, invalidValue)\n            .call(cwd = root, env = env, stderr = os.Pipe, check = false)\n          expect(res.exitCode == 1)\n          expect(res.err.trim().contains(\"Malformed\"))\n          expect(res.err.trim().contains(invalidValue))\n        }\n      }\n  }\n\n  test(\"change value for key\") {\n    val configFile              = os.rel / \"config\" / \"config.json\"\n    val configEnv               = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n    val (props, props2, props3) = (\"props=test\", \"props2=test2\", \"props3=test3\")\n    val key                     = \"java.properties\"\n    TestInputs.empty.fromRoot { root =>\n      // set some values first time\n      os.proc(TestUtil.cli, \"--power\", \"config\", key, props, props2).call(\n        cwd = root,\n        env = configEnv\n      )\n\n      // override some values should throw error without force flag\n      val res = os.proc(TestUtil.cli, \"--power\", \"config\", key, props, props2, props3).call(\n        cwd = root,\n        env = configEnv,\n        check = false,\n        mergeErrIntoOut = true\n      )\n\n      expect(res.exitCode == 1)\n      expect(res.out.trim().contains(\"pass -f or --force\"))\n\n      os.proc(TestUtil.cli, \"--power\", \"config\", key, props, props2, props3, \"-f\").call(\n        cwd = root,\n        env = configEnv,\n        check = false\n      )\n      val propertiesFromConfig = os.proc(TestUtil.cli, \"--power\", \"config\", key)\n        .call(cwd = root, env = configEnv)\n        .out.trim()\n\n      expect(propertiesFromConfig.contains(props))\n      expect(propertiesFromConfig.contains(props2))\n      expect(propertiesFromConfig.contains(props3))\n    }\n  }\n\n  for {\n    offlineSetting <- Seq(true, false)\n    prefillCache   <- if (offlineSetting) Seq(true, false) else Seq(false)\n    caption = s\"offline mode: $offlineSetting, \" +\n      (offlineSetting -> prefillCache match {\n        case (true, true)  => \"build should succeed when cache was pre-filled\"\n        case (true, false) => \"build should fail when cache is empty\"\n        case _             => \"dependencies should be downloaded as normal\"\n      })\n  }\n    test(caption) {\n      TestInputs(\n        os.rel / \"simple.sc\" -> \"println(dotty.tools.dotc.config.Properties.versionNumberString)\"\n      )\n        .fromRoot { root =>\n          val configFile    = os.rel / \"config\" / \"config.json\"\n          val localRepoPath = root / \"local-repo\"\n          val envs          = Map(\n            \"COURSIER_CACHE\"   -> localRepoPath.toString,\n            \"SCALA_CLI_CONFIG\" -> configFile.toString\n          )\n          os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n          os.proc(TestUtil.cli, \"config\", \"offline\", offlineSetting.toString)\n            .call(cwd = root, env = envs)\n          if (prefillCache) for {\n            artifactName <- Seq(\n              \"scala3-compiler_3\",\n              \"scala3-staging_3\",\n              \"scala3-tasty-inspector_3\",\n              \"scala3-sbt-bridge\"\n            )\n            artifact = s\"org.scala-lang:$artifactName:${Constants.scala3Next}\"\n          } os.proc(TestUtil.cs, \"fetch\", \"--cache\", localRepoPath, artifact).call(cwd = root)\n          val buildExpectedToSucceed = !offlineSetting || prefillCache\n          val r                      = os.proc(TestUtil.cli, \"run\", \"simple.sc\", \"--with-compiler\")\n            .call(cwd = root, env = envs, check = buildExpectedToSucceed)\n          if (buildExpectedToSucceed) expect(r.out.trim() == Constants.scala3Next)\n          else expect(r.exitCode == 1)\n          os.proc(TestUtil.cli, \"config\", \"offline\", \"--unset\")\n            .call(cwd = root, env = envs)\n          os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n        }\n    }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/CoursierScalaInstallationTestHelper.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.charset.Charset\n\nimport scala.jdk.CollectionConverters.IteratorHasAsScala\nimport scala.util.Properties\n\ntrait CoursierScalaInstallationTestHelper {\n  def withScalaRunnerWrapper(\n    root: os.Path,\n    localBin: os.Path,\n    scalaVersion: String,\n    localCache: Option[os.Path] = None,\n    shouldCleanUp: Boolean = true\n  )(f: os.Path => Unit): Unit = {\n    val localCacheArgs = localCache.fold(Seq.empty[String])(c => Seq(\"--cache\", c.toString))\n    os.proc(\n      TestUtil.cs,\n      \"install\",\n      localCacheArgs,\n      \"--install-dir\",\n      localBin,\n      s\"scala:$scalaVersion\"\n    ).call(cwd = root)\n    val (launchScalaPath: os.Path, underlyingScriptPath: os.Path) =\n      if (Properties.isWin) {\n        val batchWrapperScript: os.Path = localBin / \"scala.bat\"\n        val charset                     = Charset.defaultCharset().toString\n        val batchWrapperContent         = new String(os.read.bytes(batchWrapperScript), charset)\n        val setCommandLine              = batchWrapperContent\n          .lines()\n          .iterator()\n          .asScala\n          .toList\n          .find(_.startsWith(\"SET CMDLINE=\"))\n          .getOrElse(\"\")\n        val scriptPathRegex = \"\"\"SET CMDLINE=\"(.*\\\\bin\\\\scala\\.bat)\" %CMD_LINE_ARGS%\"\"\".r\n        val batchScript     =\n          setCommandLine match { case scriptPathRegex(extractedPath) => extractedPath }\n        val batchScriptPath = os.Path(batchScript)\n        val oldContent      = os.read(batchScriptPath)\n        val newContent      = oldContent.replace(\n          \"call %SCALA_CLI_CMD_WIN%\",\n          s\"\"\"set \"SCALA_CLI_CMD_WIN=${TestUtil.cliPath}\"\n             |call %SCALA_CLI_CMD_WIN%\"\"\".stripMargin\n        )\n        expect(newContent != oldContent)\n        os.write.over(batchScriptPath, newContent)\n        batchWrapperScript -> batchScriptPath\n      }\n      else {\n        val scalaBinary: os.Path = localBin / \"scala\"\n        val fileBytes            = os.read.bytes(scalaBinary)\n        val shebang              = new String(fileBytes.takeWhile(_ != '\\n'), \"UTF-8\")\n        val binaryData           = fileBytes.drop(shebang.length + 1)\n        val execLine             = new String(binaryData.takeWhile(_ != '\\n'), \"UTF-8\")\n        val scriptPathRegex      = \"\"\"exec \"([^\"]+/bin/scala).*\"\"\"\".r\n        val scalaScript = execLine match { case scriptPathRegex(extractedPath) => extractedPath }\n        val scalaScriptPath = os.Path(scalaScript)\n        val lineToChange    = \"eval \\\"${SCALA_CLI_CMD_BASH[@]}\\\" \\\\\"\n        val changedLine     =\n          s\"\"\"eval \\\"${TestUtil.cli.mkString(s\"\\\" \\\\${System.lineSeparator()}\")}\\\" \\\\\"\"\"\n        val newContent = os.read(scalaScriptPath).replace(lineToChange, changedLine)\n        os.write.over(scalaScriptPath, newContent)\n        scalaBinary -> scalaScriptPath\n      }\n    val wrapperVersion = os.proc(launchScalaPath, \"version\", \"--cli-version\")\n      .call(cwd = root).out.trim()\n    val cliVersion = os.proc(TestUtil.cli, \"version\", \"--cli-version\")\n      .call(cwd = root).out.trim()\n    expect(wrapperVersion == cliVersion)\n    f(launchScalaPath)\n    if (shouldCleanUp) {\n      // clean up cs local binaries\n      val csPrebuiltBinaryDir =\n        os.Path(underlyingScriptPath.toString().substring(\n          0,\n          underlyingScriptPath.toString().indexOf(scalaVersion) + scalaVersion.length\n        ))\n      System.err.println(s\"Cleaning up, trying to remove $csPrebuiltBinaryDir\")\n      try {\n        os.remove.all(csPrebuiltBinaryDir)\n\n        System.err.println(s\"Cleanup complete. Removed $csPrebuiltBinaryDir\")\n      }\n      catch {\n        case ex: java.nio.file.FileSystemException =>\n          System.err.println(s\"Failed to remove $csPrebuiltBinaryDir: $ex\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DefaultFileTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass DefaultFileTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  test(\"Print .gitignore\") {\n    val res = os.proc(TestUtil.cli, \"--power\", \"default-file\", \".gitignore\")\n      .call()\n    val output = res.out.text()\n    expect(output.linesIterator.toVector.contains(\"/.scala-build/\"))\n  }\n\n  test(\"Write .gitignore\") {\n    TestInputs.empty.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"default-file\", \".gitignore\", \"--write\")\n        .call(cwd = root, stdout = os.Inherit)\n      val dest = root / \".gitignore\"\n      expect(os.isFile(dest))\n      val content = os.read(dest)\n      expect(content.linesIterator.toVector.contains(\"/.scala-build/\"))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DefaultTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass DefaultTests extends WithWarmUpScalaCliSuite with LegacyScalaRunnerTestDefinitions {\n  override def warmUpExtraTestOptions: Seq[String] = TestUtil.extraOptions\n\n  test(\"running scala-cli with no args should default to repl\") {\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"--repl-dry-run\").call(cwd = root, mergeErrIntoOut = true)\n      expect(res.out.lines().lastOption.contains(replDryRunOutput))\n    }\n  }\n  test(\"running scala-cli with no args should not accept run-only options\") {\n    TestInputs.empty.fromRoot { root =>\n      val runSpecificOption = \"--list-main-classes\"\n      val res               = os.proc(TestUtil.cli, runSpecificOption).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        check = false\n      )\n      expect(res.exitCode == 1)\n      expect(res.out.lines().endsWith(unrecognizedArgMessage(runSpecificOption)))\n    }\n  }\n  test(\"running scala-cli with args should not accept repl-only options\") {\n    TestInputs(os.rel / \"Hello.sc\" -> \"\"\"println(\"Hello\")\"\"\").fromRoot { root =>\n      val replSpecificOption = \"--ammonite\"\n      val res                = os.proc(TestUtil.cli, \"--power\", \".\", replSpecificOption).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        check = false\n      )\n      expect(res.exitCode == 1)\n      expect(res.out.lines().endsWith(unrecognizedArgMessage(replSpecificOption)))\n    }\n  }\n\n  test(\"default to the run sub-command when a scala snippet is passed with --execute-scala\") {\n    TestInputs.empty.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(\n          TestUtil.cli,\n          \"--execute-scala\",\n          s\"@main def main() = println($quotation$msg$quotation)\",\n          TestUtil.extraOptions\n        )\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"default to the run sub-command when a java snippet is passed with --execute-java\") {\n    TestInputs.empty.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(\n          TestUtil.cli,\n          \"--execute-java\",\n          s\"public class Main { public static void main(String[] args) { System.out.println($quotation$msg$quotation); } }\",\n          TestUtil.extraOptions\n        )\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"default to the run sub-command when a md snippet is passed with --execute-markdown\") {\n    TestInputs.empty.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"--execute-markdown\",\n          s\"\"\"# A Markdown snippet\n             |With some scala code\n             |```scala\n             |println($quotation$msg$quotation)\n             |```\"\"\".stripMargin,\n          TestUtil.extraOptions\n        )\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"default to the run sub-command if -classpath and --main-class are passed\") {\n    val expectedOutput = \"Hello\"\n    val mainClassName  = \"Main\"\n    TestInputs(\n      os.rel / s\"$mainClassName.scala\" ->\n        s\"\"\"object $mainClassName extends App { println(\"$expectedOutput\") }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val compilationOutputDir = os.rel / \"compilationOutput\"\n      // first, precompile to an explicitly specified output directory with -d\n      os.proc(\n        TestUtil.cli,\n        \".\",\n        \"-d\",\n        compilationOutputDir\n      ).call(cwd = root)\n\n      // next, run while relying on the pre-compiled class instead of passing inputs\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"--main-class\",\n        mainClassName,\n        \"-classpath\",\n        (os.rel / compilationOutputDir).toString\n      ).call(cwd = root)\n      expect(runRes.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"default to the repl sub-command if -classpath is passed, but --main-class isn't\") {\n    val expectedOutput = \"Hello\"\n    val mainClassName  = \"Main\"\n    TestInputs(\n      os.rel / s\"$mainClassName.scala\" ->\n        s\"\"\"object $mainClassName extends App { println(\"$expectedOutput\") }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val compilationOutputDir = os.rel / \"compilationOutput\"\n      // first, precompile to an explicitly specified output directory with -d\n      os.proc(\n        TestUtil.cli,\n        \".\",\n        \"-d\",\n        compilationOutputDir\n      ).call(cwd = root)\n\n      // next, run the repl while relying on the pre-compiled classes\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"--repl-dry-run\",\n        \"-classpath\",\n        (os.rel / compilationOutputDir).toString\n      ).call(cwd = root, mergeErrIntoOut = true)\n      expect(runRes.out.lines().lastOption.contains(replDryRunOutput))\n    }\n  }\n\n  test(\"ensure --help-native works with the default command\") {\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"--help-native\").call(cwd = root)\n      expect(res.out.trim().contains(\"Scala Native options:\"))\n    }\n  }\n\n  test(\"ensure --help-js works with the default command\") {\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"--help-js\").call(cwd = root)\n      expect(res.out.trim().contains(\"Scala.js options:\"))\n    }\n  }\n\n  protected def unrecognizedArgMessage(argName: String): Vector[String] =\n    s\"\"\"\n       |Unrecognized argument: $argName\n       |\n       |To list all available options, run\n       |  ${Console.BOLD}${TestUtil.detectCliPath} --help${Console.RESET}\n       |\"\"\".stripMargin.trim.linesIterator.toVector\n\n  protected lazy val replDryRunOutput = \"Dry run, not running REPL.\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DependencyUpdateTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.version.Version\n\nclass DependencyUpdateTests extends ScalaCliSuite {\n  test(\"dependency update test\") {\n    val fileName    = \"Hello.scala\"\n    val message     = \"Hello World\"\n    val fileContent =\n      s\"\"\"|//> using dep com.lihaoyi::os-lib:0.7.8\n          |//> using dep com.lihaoyi::utest:0.7.10\n          |\n          |object Hello extends App {\n          |  println(\"$message\")\n          |}\"\"\".stripMargin\n    val inputs = TestInputs(os.rel / fileName -> fileContent)\n    inputs.fromRoot { root =>\n      // update dependencies\n      val p = os.proc(TestUtil.cli, \"--power\", \"dependency-update\", \"--all\", fileName)\n        .call(\n          cwd = root,\n          stdin = os.Inherit,\n          mergeErrIntoOut = true\n        )\n      expect(p.out.trim().contains(\"Updated dependency\"))\n      expect( // check if dependency update command modify file\n        os.read(root / fileName) != fileContent\n      )\n\n      // after updating dependencies app should run\n      val out = os.proc(TestUtil.cli, fileName).call(cwd = root).out.trim()\n      expect(out == message)\n    }\n  }\n\n  test(\"update toolkit dependency\") {\n    val toolkitVersion = \"0.1.3\"\n    val testInputs     = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using toolkit $toolkitVersion\n           |\n           |object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    testInputs.fromRoot { root =>\n      // update toolkit\n      os.proc(TestUtil.cli, \"--power\", \"dependency-update\", \"--all\", \".\")\n        .call(cwd = root)\n\n      val toolkitDirective         = \"//> using toolkit (.*)\".r\n      val updatedToolkitVersionOpt = {\n        val regexMatch = toolkitDirective.findFirstMatchIn(os.read(root / \"Foo.scala\"))\n        regexMatch.map(_.group(1))\n      }\n      expect(updatedToolkitVersionOpt.nonEmpty)\n      val updatedToolkitVersion = updatedToolkitVersionOpt.get\n      expect(Version(updatedToolkitVersion) > Version(toolkitVersion))\n    }\n  }\n\n  test(\"update typelevel toolkit dependency\") {\n    val toolkitVersion = \"0.0.1\"\n    val testInputs     = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using toolkit typelevel:$toolkitVersion\n           |\n           |import cats.effect.*\n           |\n           |object Hello extends IOApp.Simple {\n           |  def run = IO.println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n    testInputs.fromRoot { root =>\n      // update toolkit\n      os.proc(TestUtil.cli, \"--power\", \"dependency-update\", \"--all\", \".\")\n        .call(cwd = root)\n\n      val toolkitDirective         = \"//> using toolkit typelevel:(.*)\".r\n      val updatedToolkitVersionOpt = {\n        val regexMatch = toolkitDirective.findFirstMatchIn(os.read(root / \"Foo.scala\"))\n        regexMatch.map(_.group(1))\n      }\n      expect(updatedToolkitVersionOpt.nonEmpty)\n      val updatedToolkitVersion = updatedToolkitVersionOpt.get\n      expect(Version(updatedToolkitVersion) > Version(toolkitVersion))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DirectoriesTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass DirectoriesTests extends ScalaCliSuite {\n  test(\"running directories with args should fail\") {\n    TestInputs(os.rel / \"s.sc\" -> \"\"\"println(\"Hello\")\"\"\").fromRoot { root =>\n      val r = os.proc(TestUtil.cli, \"--power\", \"directories\", \"s.sc\")\n        .call(cwd = root, stderr = os.Pipe, check = false)\n      expect(r.exitCode == 1)\n      expect(r.err.trim() == \"The directories command doesn't accept arguments.\")\n    }\n  }\n\n  if (Properties.isMac)\n    test(\"running directories on Mac with no args should give valid results\") {\n      TestInputs.empty.fromRoot { root =>\n        val r              = os.proc(TestUtil.cli, \"--power\", \"directories\").call(cwd = root)\n        val cachesPath     = os.home / \"Library\" / \"Caches\" / \"ScalaCli\"\n        val appSupportPath = os.home / \"Library\" / \"Application Support\" / \"ScalaCli\"\n        val expectedOutput =\n          s\"\"\"Local repository: ${cachesPath / \"local-repo\"}\n             |Completions: ${appSupportPath / \"completions\"}\n             |Virtual projects: ${cachesPath / \"virtual-projects\"}\n             |BSP sockets: ${cachesPath / \"bsp-sockets\"}\n             |Bloop daemon directory: ${cachesPath / \"bloop\" / \"daemon\"}\n             |Secrets directory: ${appSupportPath / \"secrets\"}\"\"\".stripMargin\n        expect(r.out.trim() == expectedOutput)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DocTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport org.jsoup.*\n\nimport scala.jdk.CollectionConverters.*\n\nabstract class DocTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n\n  for {\n    useTestScope <- Seq(true, false)\n    scopeOpts        = if (useTestScope) Seq(\"--test\", \"--power\") else Nil\n    scopeDirective   = if (useTestScope) \"//> using target.scope test\" else \"\"\n    scopeDescription = scopeOpts.headOption.getOrElse(\"main\")\n  }\n    test(s\"generate static scala doc ($scopeDescription)\") {\n      val dest   = os.rel / \"doc-static\"\n      val inputs = TestInputs(\n        os.rel / \"lib\" / \"Messages.scala\" ->\n          \"\"\"package lib\n            |\n            |object Messages {\n            |  def msg = \"Hello\"\n            |}\n            |\"\"\".stripMargin,\n        os.rel / \"simple.sc\" ->\n          s\"\"\"$scopeDirective\n             |val msg = lib.Messages.msg\n             |println(msg)\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"doc\", extraOptions, \".\", \"-o\", dest, scopeOpts).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val expectedDestDocPath = root / dest\n        expect(os.isDir(expectedDestDocPath))\n        val expectedEntries =\n          if (actualScalaVersion.startsWith(\"2.\"))\n            Seq(\n              \"index.html\",\n              \"lib/Messages$.html\",\n              \"simple$.html\"\n            )\n          else if (\n            actualScalaVersion.coursierVersion >= \"3.5.0\".coursierVersion ||\n            (actualScalaVersion.coursierVersion >= \"3.3.4\".coursierVersion &&\n            actualScalaVersion.coursierVersion < \"3.4.0\".coursierVersion) ||\n            actualScalaVersion.startsWith(\"3.3.4\") ||\n            actualScalaVersion.startsWith(\"3.5\")\n          )\n            Seq(\n              \"index.html\",\n              \"inkuire-db.json\",\n              \"$lessempty$greater$/simple$_.html\",\n              \"lib/Messages$.html\"\n            )\n          else\n            Seq(\n              \"index.html\",\n              \"inkuire-db.json\",\n              \"_empty_/simple$_.html\",\n              \"lib/Messages$.html\"\n            )\n        val entries =\n          os.walk(root / dest).filter(!os.isDir(_)).map { path =>\n            path.relativeTo(expectedDestDocPath).toString() -> os.read(path)\n          }.toMap\n        expect(expectedEntries.forall(e => entries.contains(e)))\n\n        val documentableNameElement =\n          Jsoup.parse(entries(\"index.html\")).select(\".documentableName\").asScala\n        documentableNameElement.filter(_.text().contains(\"lib\")).foreach { element =>\n          expect(!element.attr(\"href\").startsWith(\"http\"))\n        }\n      }\n    }\n\n  if actualScalaVersion.startsWith(\"3\") then\n    test(\"doc with compileOnly.dep\") {\n      TestInputs(\n        os.rel / \"project.scala\" ->\n          s\"\"\"//> using compileOnly.dep org.springframework.boot:spring-boot:3.5.6\n             |//> using test.dep org.springframework.boot:spring-boot:3.5.6\n             |\"\"\".stripMargin,\n        os.rel / \"RootLoggerConfigurer.scala\" ->\n          s\"\"\"import org.springframework.beans.factory.annotation.Autowired\n             |import scala.compiletime.uninitialized\n             |\n             |class RootLoggerConfigurer:\n             |  @Autowired var sentryClient: String = uninitialized\n             |\"\"\".stripMargin\n      ).fromRoot(root => os.proc(TestUtil.cli, \"doc\", \".\", extraOptions).call(cwd = root))\n    }\n\n  if actualScalaVersion.startsWith(\"3\") then\n    for {\n      javaVersion <-\n        if isScala38OrNewer then\n          Constants.allJavaVersions.filter(_ >= Constants.scala38MinJavaVersion)\n        else Constants.allJavaVersions\n    }\n      test(s\"doc generates correct external mapping URLs for JVM $javaVersion\") {\n        TestUtil.retryOnCi() {\n          val dest   = os.rel / \"doc-out\"\n          val inputs = TestInputs(\n            os.rel / \"Lib.scala\" ->\n              \"\"\"package mylib\n                |\n                |/** A wrapper around [[java.util.HashMap]] and [[scala.Option]]. */\n                |class Lib:\n                |  /** Returns a [[java.util.HashMap]]. */\n                |  def getMap: java.util.HashMap[String, String] = new java.util.HashMap()\n                |  /** Returns a [[scala.Option]]. */\n                |  def getOpt: Option[String] = Some(\"hello\")\n                |\"\"\".stripMargin\n          )\n          inputs.fromRoot { root =>\n            os.proc(\n              TestUtil.cli,\n              \"doc\",\n              extraOptions,\n              \".\",\n              \"-o\",\n              dest,\n              \"--jvm\",\n              javaVersion.toString\n            ).call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n            val docDir = root / dest\n            expect(os.isDir(docDir))\n\n            val htmlContent = os.walk(docDir)\n              .filter(_.last.endsWith(\".html\"))\n              .map(os.read(_))\n              .mkString\n\n            val expectedJavadocFragment =\n              if javaVersion >= 11 then\n                s\"docs.oracle.com/en/java/javase/$javaVersion/docs/api/java.base/\"\n              else\n                s\"docs.oracle.com/javase/$javaVersion/docs/api/\"\n            expect(htmlContent.contains(expectedJavadocFragment))\n\n            if javaVersion < 11 then\n              expect(!htmlContent.contains(\"java.base/\"))\n\n            expect(htmlContent.contains(s\"scala-lang.org/api/$actualScalaVersion/\"))\n          }\n        }\n      }\n\n  test(s\"doc --cross with multiple Scala versions produces doc output per cross\") {\n    val crossScalaVersions = Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n    val dest               = os.rel / \"doc-cross\"\n    TestInputs(\n      os.rel / \"project.scala\" -> s\"//> using scala ${crossScalaVersions.mkString(\" \")}\",\n      os.rel / \"Lib.scala\"     ->\n        \"\"\"package mylib\n          |\n          |/** A sample class. */\n          |class Lib {\n          |  def value: Int = 42\n          |}\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"doc\",\n        \"--cross\",\n        \"--power\",\n        extraOptions,\n        \".\",\n        \"-o\",\n        dest\n      ).call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val baseDocPath = root / dest\n      expect(os.isDir(baseDocPath))\n      crossScalaVersions.foreach { version =>\n        val subDir = baseDocPath / version\n        expect(os.isDir(subDir))\n        expect(os.list(subDir).exists(_.last.endsWith(\".html\")))\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DocTests212.scala",
    "content": "package scala.cli.integration\n\nclass DocTests212 extends DocTestDefinitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DocTests213.scala",
    "content": "package scala.cli.integration\n\nclass DocTests213 extends DocTestDefinitions with Test213\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DocTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass DocTests3Lts extends DocTestDefinitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DocTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass DocTests3NextRc extends DocTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/DocTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass DocTestsDefault extends DocTestDefinitions with TestDefault {\n  test(\"javadoc\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.java\" ->\n        \"\"\"//> using dep org.graalvm.nativeimage:svm:22.0.0.2\n          |\n          |import com.oracle.svm.core.annotate.TargetClass;\n          |import org.graalvm.nativeimage.Platform;\n          |import org.graalvm.nativeimage.Platforms;\n          |\n          |/**\n          | * Foo class\n          | */\n          |@TargetClass(className = \"something\")\n          |@Platforms({Platform.LINUX.class, Platform.DARWIN.class})\n          |public class Foo {\n          |  /**\n          |   * Gets the value\n          |   *\n          |   * @return the value\n          |   */\n          |  public int getValue() {\n          |    return 2;\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val dest = root / \"doc\"\n      os.proc(TestUtil.cli, \"doc\", extraOptions, \".\", \"-o\", dest).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      expect(os.isDir(dest))\n      val expectedEntries = Seq(\n        \"index.html\",\n        \"overview-tree.html\",\n        \"Foo.html\"\n      )\n      val entries = os.walk(dest).map(_.relativeTo(dest)).map(_.toString).toSet\n      expect(expectedEntries.forall(entries))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportCommonTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.charset.Charset\n\nimport scala.util.Properties\n\ntrait ExportCommonTestDefinitions { this: ScalaCliSuite & TestScalaVersionArgs =>\n  protected lazy val extraOptions: Seq[String] =\n    scalaVersionArgs ++ TestUtil.extraOptions ++ Seq(\"--suppress-experimental-warning\")\n\n  protected def commonTestDescriptionSuffix: String = \"\"\n\n  protected def runExportTests: Boolean = Properties.isMac\n  protected def exportCommand(args: String*): os.proc\n  protected def defaultExportCommandArgs: Seq[String] = Nil\n\n  protected def buildToolCommand(root: os.Path, mainClass: Option[String], args: String*): os.proc\n\n  protected def runMainArgs(mainClass: Option[String]): Seq[String]\n  protected def runTestsArgs(mainClass: Option[String]): Seq[String]\n\n  protected val prepareTestInputs: TestInputs => TestInputs = identity\n\n  protected val outputDir: os.RelPath = os.rel / \"output-project\"\n\n  protected def simpleTest(\n    inputs: TestInputs,\n    mainClass: Option[String],\n    extraExportArgs: Seq[String] = Nil\n  ): Unit =\n    prepareTestInputs(inputs).fromRoot { root =>\n      val exportArgs = \".\" +: extraExportArgs\n      exportCommand(exportArgs*).call(cwd = root, stdout = os.Inherit)\n      val res =\n        buildToolCommand(root, mainClass, runMainArgs(mainClass)*).call(cwd = root / outputDir)\n      val output = res.out.text(Charset.defaultCharset())\n      expect(output.contains(\"Hello from exported Scala CLI project\"))\n    }\n\n  protected def jvmTest(\n    mainArgs: Seq[String],\n    testArgs: Seq[String],\n    extraExportArgs: Seq[String] = Nil,\n    mainClassName: String\n  ): Unit =\n    prepareTestInputs(ExportTestProjects.jvmTest(actualScalaVersion, mainClassName)).fromRoot {\n      root =>\n        val exportArgs = \".\" +: extraExportArgs\n        exportCommand(exportArgs*).call(cwd = root, stdout = os.Inherit)\n        // main\n        val res =\n          buildToolCommand(root, Some(mainClassName), mainArgs*).call(cwd = root / outputDir)\n        val output = res.out.text(Charset.defaultCharset())\n        expect(output.contains(\"Hello from \" + actualScalaVersion))\n        // resource\n        expect(output.contains(\"resource:1,2\"))\n        // test\n        val testRes =\n          buildToolCommand(root, Some(mainClassName), testArgs*).call(cwd = root / outputDir)\n        val testOutput = testRes.out.text(Charset.defaultCharset())\n        expect(\n          testOutput.contains(\"1 succeeded\") || testOutput.contains(\"BUILD SUCCESS\")\n        ) // maven returns 'BUILD SUCCESS'\n    }\n\n  protected def scalaVersionTest(\n    scalaVersion: String,\n    mainClass: String,\n    extraExportArgs: Seq[String] = Nil\n  ): Unit =\n    prepareTestInputs(ExportTestProjects.scalaVersionTest(scalaVersion, mainClass)).fromRoot {\n      root =>\n        exportCommand(\".\" +: extraExportArgs*).call(cwd = root, stdout = os.Inherit)\n        val res = buildToolCommand(root, Some(mainClass), runMainArgs(Some(mainClass))*)\n          .call(cwd = root / outputDir)\n        val output = res.out.text(Charset.defaultCharset())\n        expect(output.contains(\"Hello\"))\n    }\n\n  def extraSourceFromDirectiveWithExtraDependency(\n    mainClass: String,\n    extraExportArgs: Seq[String],\n    inputs: String*\n  ): Unit =\n    prepareTestInputs(\n      ExportTestProjects.extraSourceFromDirectiveWithExtraDependency(actualScalaVersion, mainClass)\n    ).fromRoot { root =>\n      exportCommand(extraExportArgs ++ inputs*).call(cwd = root, stdout = os.Inherit)\n      val res = buildToolCommand(root, Some(mainClass), runMainArgs(Some(mainClass))*)\n        .call(cwd = root / outputDir)\n      val output = res.out.trim(Charset.defaultCharset())\n      expect(output.contains(root.toString))\n    }\n\n  def justTestScope(mainClass: String, extraExportArgs: Seq[String] = Nil): Unit = {\n    val expectedMessage = \"exporting just the test scope actually works!\"\n    prepareTestInputs(ExportTestProjects.justTestScope(mainClass, expectedMessage))\n      .fromRoot { root =>\n        exportCommand(\".\" +: extraExportArgs*).call(cwd = root)\n        val testRes = buildToolCommand(root, Some(mainClass), runTestsArgs(Some(mainClass))*)\n          .call(cwd = root / outputDir)\n        val testOutput = testRes.out.text()\n        expect(testOutput.contains(expectedMessage))\n      }\n  }\n\n  protected val scalaVersionsInDir: Seq[String] = Seq(\"2.12\", \"2.13\", \"2\", \"3\", \"3.lts\")\n\n  if (runExportTests) {\n    test(s\"JVM$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        jvmTest(\n          mainArgs = runMainArgs(Some(\"Main\")),\n          testArgs = runTestsArgs(Some(\"Main\")),\n          mainClassName = \"Main\",\n          extraExportArgs = defaultExportCommandArgs\n        )\n      }\n    }\n    test(s\"extra source from a directive introducing a dependency$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        extraSourceFromDirectiveWithExtraDependency(\n          mainClass = \"Main\",\n          extraExportArgs = defaultExportCommandArgs,\n          inputs = \"Main.scala\"\n        )\n      }\n    }\n    test(\n      s\"extra source passed both via directive and from command line$commonTestDescriptionSuffix\"\n    ) {\n      TestUtil.retryOnCi() {\n        extraSourceFromDirectiveWithExtraDependency(\n          mainClass = \"Main\",\n          extraExportArgs = defaultExportCommandArgs,\n          inputs = \".\"\n        )\n      }\n    }\n    scalaVersionsInDir.foreach { scalaV =>\n      test(\n        s\"check export for project with scala version in directive as $scalaV$commonTestDescriptionSuffix\"\n      ) {\n        TestUtil.retryOnCi() {\n          scalaVersionTest(\n            scalaVersion = scalaV,\n            mainClass = \"Main\",\n            extraExportArgs = defaultExportCommandArgs\n          )\n        }\n      }\n    }\n\n    test(s\"just test scope$commonTestDescriptionSuffix\") {\n      // Keeping the test name ends with Test to support maven convention\n      TestUtil.retryOnCi() {\n        justTestScope(mainClass = \"MyTest\", extraExportArgs = defaultExportCommandArgs)\n      }\n    }\n\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportJsonTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nabstract class ExportJsonTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  private def readJson(path: os.ReadablePath): String =\n    readJson(os.read(path))\n\n  private def readJson(json: String): String =\n    json\n      .replaceAll(\"\\\\s\", \"\")\n      .replaceAll(\n        \"ivy:file:[^\\\"]*/local-repo/[^\\\"]*\",\n        \"ivy:file:.../local-repo/...\"\n      )\n      .replaceAll(\n        \"ivy:file:[^\\\"]*\\\\.ivy2/local[^\\\"]*\",\n        \"ivy:file:.../.ivy2/local/\"\n      )\n      .replaceAll(\n        \"\\\"scalaCliVersion\\\":(\\\"[^\\\"]*\\\")\",\n        \"\\\"scalaCliVersion\\\":\\\"1.1.1-SNAPSHOT\\\"\"\n      )\n\n  private def withEscapedBackslashes(s: os.Path): String =\n    s.toString.replaceAll(\"\\\\\\\\\", \"\\\\\\\\\\\\\\\\\")\n\n  test(\"export json\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.7.8\n          |\n          |object Main {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      TestUtil.initializeGit(root, \"v1.1.2\")\n\n      val exportJsonProc =\n        // Test --power placed after subcommand name\n        os.proc(TestUtil.cli, \"export\", \"--power\", \"--json\", \".\", \"--jvm\", \"temurin:11\")\n          .call(cwd = root)\n\n      val jsonContents = readJson(exportJsonProc.out.text())\n\n      val expectedJsonContents =\n        s\"\"\"{\n           |\"projectVersion\":\"1.1.2\",\n           |\"scalaVersion\":\"${Constants.scala3Next}\",\n           |\"platform\":\"JVM\",\n           |\"jvmVersion\":\"temurin:11\",\n           |\"scopes\": {\n           | \"main\": {\n           |   \"sources\": [\"${withEscapedBackslashes(root / \"Main.scala\")}\"],\n           |   \"dependencies\": [\n           |     {\n           |       \"groupId\":\"com.lihaoyi\",\n           |       \"artifactId\": {\n           |         \"name\":\"os-lib\",\n           |         \"fullName\": \"os-lib_3\"\n           |       },\n           |       \"version\":\"0.7.8\"\n           |     }\n           |   ],\n           |   \"resolvers\": [\n           |     \"https://repo1.maven.org/maven2\",\n           |     \"ivy:file:.../local-repo/...\",\n           |     \"ivy:file:.../.ivy2/local/\"\n           |   ]\n           | }\n           |}\n           |,\"scalaCliVersion\":\"1.1.1-SNAPSHOT\"\n           |}\n           |\"\"\".replaceAll(\"\\\\s|\\\\|\", \"\")\n      expect(jsonContents == expectedJsonContents)\n    }\n  }\n\n  test(\"export json with test scope\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.7.8\n          |//> using option -Xasync\n          |//> using plugin org.wartremover:::wartremover:3.0.9\n          |//> using scala 3.2.2\n          |\n          |object Main {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"unit.test.scala\" ->\n        \"\"\"//> using repository sonatype:snapshots\n          |//> using resourceDir ./resources\n          |//> using jar TEST.jar\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val exportJsonProc = os.proc(TestUtil.cli, \"--power\", \"export\", \"--json\", \".\", \"--native\")\n        .call(cwd = root)\n\n      val jsonContents = readJson(exportJsonProc.out.text())\n\n      val expectedJsonContents =\n        s\"\"\"{\n           |\"scalaVersion\":\"3.2.2\",\n           |\"platform\":\"Native\",\n           |\"scalaNativeVersion\":\"${Constants.scalaNativeVersion}\",\n           |\"scopes\": {\n           | \"main\": {\n           |   \"sources\": [\"${withEscapedBackslashes(root / \"Main.scala\")}\"],\n           |   \"scalacOptions\":[\"-Xasync\"],\n           |   \"scalaCompilerPlugins\": [\n           |     {\n           |       \"groupId\": \"org.wartremover\",\n           |       \"artifactId\": {\n           |         \"name\": \"wartremover\",\n           |         \"fullName\": \"wartremover_3.2.2\"\n           |       },\n           |       \"version\": \"3.0.9\"\n           |     }\n           |   ],\n           |   \"dependencies\": [\n           |     {\n           |       \"groupId\":\"com.lihaoyi\",\n           |       \"artifactId\": {\n           |         \"name\":\"os-lib\",\n           |         \"fullName\": \"os-lib_3\"\n           |       },\n           |       \"version\":\"0.7.8\"\n           |     }\n           |   ],\n           |   \"resolvers\": [\n           |     \"https://repo1.maven.org/maven2\",\n           |     \"ivy:file:.../local-repo/...\",\n           |     \"ivy:file:.../.ivy2/local/\"\n           |   ]\n           | },\n           | \"test\": {\n           |   \"sources\":[\"${withEscapedBackslashes(root / \"unit.test.scala\")}\"],\n           |   \"scalacOptions\":[\"-Xasync\"],\n           |   \"scalaCompilerPlugins\": [\n           |     {\n           |       \"groupId\": \"org.wartremover\",\n           |       \"artifactId\": {\n           |         \"name\": \"wartremover\",\n           |         \"fullName\": \"wartremover_3.2.2\"\n           |       },\n           |       \"version\": \"3.0.9\"\n           |     }\n           |   ],\n           |   \"dependencies\": [\n           |     {\n           |       \"groupId\": \"com.lihaoyi\",\n           |       \"artifactId\": {\n           |         \"name\":\"os-lib\",\n           |         \"fullName\": \"os-lib_3\"\n           |       },\n           |       \"version\": \"0.7.8\"\n           |     }\n           |   ],\n           |   \"resolvers\": [\n           |     \"https://oss.sonatype.org/content/repositories/snapshots\",\n           |     \"https://repo1.maven.org/maven2\",\n           |     \"ivy:file:.../local-repo/...\",\n           |     \"ivy:file:.../.ivy2/local/\"\n           |   ],\n           |   \"resourceDirs\":[\"${withEscapedBackslashes(root / \"resources\")}\"],\n           |   \"customJarsDecls\":[\"${withEscapedBackslashes(root / \"TEST.jar\")}\"]\n           |  }\n           |}\n           |,\"scalaCliVersion\":\"1.1.1-SNAPSHOT\"\n           |}\n           |\"\"\".replaceAll(\"\\\\s|\\\\|\", \"\")\n      expect(jsonContents == expectedJsonContents)\n    }\n  }\n\n  test(\"export json with js\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using scala 3.1.3\n          |//> using platform scala-js\n          |//> using lib com.lihaoyi::os-lib:0.7.8\n          |//> using option -Xasync\n          |//> using plugin org.wartremover:::wartremover:3.0.9\n          |\n          |object Main {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\")\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val exportJsonProc = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"export\",\n        \"--json\",\n        \"--output\",\n        \"json_dir\",\n        \".\",\n        \"--js-es-version\",\n        \"es2015\"\n      )\n        .call(cwd = root)\n\n      expect(exportJsonProc.out.text().isEmpty)\n\n      val fileContents         = readJson(root / \"json_dir\" / \"export.json\")\n      val expectedFileContents =\n        s\"\"\"{\n           |\"scalaVersion\": \"3.1.3\",\n           |\"platform\": \"JS\",\n           |\"scalaJsVersion\": \"${Constants.scalaJsVersion}\",\n           |\"jsEsVersion\":\"es2015\",\n           |\"scopes\": {\n           | \"main\": {\n           |   \"sources\": [\"${withEscapedBackslashes(root / \"Main.scala\")}\"],\n           |   \"scalacOptions\": [\"-Xasync\"],\n           |   \"scalaCompilerPlugins\": [\n           |     {\n           |       \"groupId\": \"org.wartremover\",\n           |       \"artifactId\": {\n           |         \"name\": \"wartremover\",\n           |         \"fullName\": \"wartremover_3.1.3\"\n           |       },\n           |       \"version\": \"3.0.9\"\n           |     }\n           |   ],\n           |   \"dependencies\": [\n           |     {\n           |       \"groupId\": \"com.lihaoyi\",\n           |       \"artifactId\": {\n           |         \"name\": \"os-lib\",\n           |         \"fullName\": \"os-lib_3\"\n           |       },\n           |       \"version\": \"0.7.8\"\n           |     }\n           |   ],\n           |   \"resolvers\": [\n           |     \"https://repo1.maven.org/maven2\",\n           |     \"ivy:file:.../local-repo/...\",\n           |     \"ivy:file:.../.ivy2/local/\"\n           |   ]\n           | }\n           |},\n           |\"scalaCliVersion\":\"1.1.1-SNAPSHOT\"\n           |}\n           |\"\"\".replaceAll(\"\\\\s|\\\\|\", \"\")\n      expect(fileContents == expectedFileContents)\n\n      val exportToExistingProc = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"export\",\n        \"--json\",\n        \"--output\",\n        \"json_dir\",\n        \".\",\n        \"--js-es-version\",\n        \"es2015\"\n      )\n        .call(cwd = root, check = false, mergeErrIntoOut = true)\n\n      expect(exportToExistingProc.exitCode != 0)\n      val jsonDirPath = root / \"json_dir\"\n      expect(exportToExistingProc.out.text().contains(s\"Error: $jsonDirPath already exists.\"))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportJsonTestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass ExportJsonTestsDefault extends ExportJsonTestDefinitions with TestDefault\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMavenTest3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass ExportMavenTest3NextRc extends ExportMavenTestDefinitions with Test3NextRc with MavenScala {}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMavenTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nabstract class ExportMavenTestDefinitions extends ScalaCliSuite\n    with TestScalaVersionArgs with ExportCommonTestDefinitions with MavenTestHelper {\n  this: TestScalaVersion & MavenLanguageMode =>\n  override def exportCommand(args: String*): os.proc =\n    os.proc(\n      TestUtil.cli,\n      \"--power\",\n      \"export\",\n      extraOptions,\n      \"--mvn\",\n      \"-o\",\n      outputDir.toString,\n      args\n    )\n\n  override def buildToolCommand(root: os.Path, mainClass: Option[String], args: String*): os.proc =\n    mavenCommand(args*)\n\n  override def runMainArgs(mainClass: Option[String]): Seq[String] = {\n    require(mainClass.nonEmpty, \"Main class or Test class is mandatory to build in maven\")\n    if (language == JAVA) Seq(\"exec:java\", s\"-Dexec.mainClass=${mainClass.get}\")\n    else Seq(\"scala:run\", s\"-DmainClass=${mainClass.get}\")\n  }\n\n  override def runTestsArgs(mainClass: Option[String]): Seq[String] =\n    if (language == JAVA) Seq(\"test\")\n    else Seq(\"test\")\n\n}\n\nsealed trait Language\ncase object JAVA  extends Language\ncase object SCALA extends Language\n\nsealed trait MavenLanguageMode {\n  def language: Language\n}\n\ntrait MavenJava extends MavenLanguageMode {\n  final override def language: Language = JAVA\n}\n\ntrait MavenScala extends MavenLanguageMode {\n  final override def language: Language = SCALA\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMavenTestJava.scala",
    "content": "package scala.cli.integration\n\nimport scala.util.Properties\n\nclass ExportMavenTestJava extends ExportMavenTestDefinitions with Test3Lts with MavenJava {\n  // disable running scala tests in java maven export\n  override def runExportTests: Boolean = false\n  if (!Properties.isWin) test(\"pure java\") {\n    simpleTest(\n      ExportTestProjects.pureJavaTest(\"ScalaCliJavaTest\"),\n      mainClass = Some(\"ScalaCliJavaTest\")\n    )\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMavenTests212.scala",
    "content": "package scala.cli.integration\n\nclass ExportMavenTests212 extends ExportMavenTestDefinitions with Test212 with MavenScala\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMavenTests213.scala",
    "content": "package scala.cli.integration\n\nclass ExportMavenTests213 extends ExportMavenTestDefinitions with Test213 with MavenScala\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMavenTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass ExportMavenTests3Lts extends ExportMavenTestDefinitions with Test3Lts with MavenScala\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill012Tests212.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill012Tests212 extends ExportMillTestDefinitions with Test212 with TestMill012\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill012Tests213.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill012Tests213 extends ExportMillTestDefinitions with Test213 with TestMill012 {\n  if runExportTests then {\n    test(s\"scalac options$commonTestDescriptionSuffix\") {\n      simpleTest(\n        inputs = ExportTestProjects.scalacOptionsScala2Test(actualScalaVersion),\n        mainClass = None,\n        extraExportArgs = defaultExportCommandArgs\n      )\n    }\n    test(s\"pure java$commonTestDescriptionSuffix\") {\n      simpleTest(\n        inputs = ExportTestProjects.pureJavaTest(\"ScalaCliJavaTest\"),\n        mainClass = None,\n        extraExportArgs = defaultExportCommandArgs\n      )\n    }\n    test(s\"custom JAR$commonTestDescriptionSuffix\") {\n      simpleTest(\n        inputs = ExportTestProjects.customJarTest(actualScalaVersion),\n        mainClass = None,\n        extraExportArgs = defaultExportCommandArgs\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill012Tests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill012Tests3Lts extends ExportMillTestDefinitions with Test3Lts with TestMill012\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill012Tests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill012Tests3NextRc extends ExportMillTestDefinitions with Test3NextRc with TestMill012\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill012TestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill012TestsDefault extends ExportMillTestDefinitions with TestDefault with TestMill012\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill1Tests212.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill1Tests212 extends ExportMillTestDefinitions with Test212 with TestMill1\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill1Tests213.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill1Tests213 extends ExportMillTestDefinitions with Test213 with TestMill1 {\n  if runExportTests then {\n    test(s\"scalac options$commonTestDescriptionSuffix\") {\n      simpleTest(\n        inputs = ExportTestProjects.scalacOptionsScala2Test(actualScalaVersion),\n        mainClass = None,\n        extraExportArgs = defaultExportCommandArgs\n      )\n    }\n    test(s\"pure java$commonTestDescriptionSuffix\") {\n      simpleTest(\n        inputs = ExportTestProjects.pureJavaTest(\"ScalaCliJavaTest\"),\n        mainClass = None,\n        extraExportArgs = defaultExportCommandArgs\n      )\n    }\n    test(s\"custom JAR$commonTestDescriptionSuffix\") {\n      simpleTest(\n        inputs = ExportTestProjects.customJarTest(actualScalaVersion),\n        mainClass = None,\n        extraExportArgs = defaultExportCommandArgs\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill1Tests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill1Tests3Lts extends ExportMillTestDefinitions with Test3Lts with TestMill1\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill1Tests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill1Tests3NextRc extends ExportMillTestDefinitions with Test3NextRc with TestMill1\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMill1TestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass ExportMill1TestsDefault extends ExportMillTestDefinitions with TestDefault with TestMill1\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportMillTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport os.RelPath\n\nimport java.nio.charset.Charset\n\nabstract class ExportMillTestDefinitions extends ScalaCliSuite\n    with TestScalaVersionArgs\n    with ExportCommonTestDefinitions\n    with ExportScalaOrientedBuildToolsTestDefinitions\n    with MillTestHelper { this: TestScalaVersion & TestMillVersion =>\n  override val prepareTestInputs: TestInputs => TestInputs = _.withMillJvmOpts\n\n  override val outputDir: RelPath                    = millOutputDir\n  override def exportCommand(args: String*): os.proc =\n    os.proc(\n      TestUtil.cli,\n      \"--power\",\n      \"export\",\n      extraOptions,\n      \"--mill\",\n      \"-o\",\n      outputDir.toString,\n      args\n    )\n\n  override def buildToolCommand(root: os.Path, mainClass: Option[String], args: String*): os.proc =\n    millCommand(root, args*)\n\n  override def runMainArgs(mainClass: Option[String]): Seq[String] =\n    Seq(s\"$millDefaultProjectName.run\")\n\n  override def runTestsArgs(mainClass: Option[String]): Seq[String] =\n    Seq(s\"$millDefaultProjectName.test\")\n\n  override def commonTestDescriptionSuffix = s\" (Mill $millVersion & Scala $actualScalaVersion)\"\n\n  override protected def defaultExportCommandArgs: Seq[String] = Seq(\"--mill-version\", millVersion)\n\n  def jvmTestScalacOptions(className: String, exportArgs: Seq[String]): Unit =\n    ExportTestProjects.jvmTest(actualScalaVersion, className).withMillJvmOpts.fromRoot { root =>\n      exportCommand(exportArgs :+ \".\"*).call(cwd = root, stdout = os.Inherit)\n      val res =\n        buildToolCommand(\n          root,\n          Some(className),\n          \"--disable-ticker\",\n          \"show\",\n          s\"$millDefaultProjectName.scalacOptions\"\n        )\n          .call(cwd = root / outputDir)\n      val output = res.out.text(Charset.defaultCharset())\n      expect(output.filterNot(_.isWhitespace) == \"[\\\"-deprecation\\\"]\")\n    }\n\n  def jvmTestCompilerPlugin(mainClass: String, exportArgs: Seq[String]): Unit = {\n    val message = \"Hello\"\n    ExportTestProjects.jvmTestWithCompilerPlugin(\n      scalaVersion = actualScalaVersion,\n      mainClassName = mainClass,\n      message = message\n    )\n      .withMillJvmOpts.fromRoot { root =>\n        exportCommand(exportArgs :+ \".\"*).call(cwd = root, stdout = os.Inherit)\n        locally {\n          val millDepsCommand =\n            if millVersion.startsWith(\"1.\") then \"scalacPluginMvnDeps\" else \"scalacPluginIvyDeps\"\n          val res =\n            buildToolCommand(\n              root,\n              Some(mainClass),\n              \"show\",\n              s\"$millDefaultProjectName.$millDepsCommand\"\n            )\n              .call(cwd = root / outputDir)\n          val output = res.out.text(Charset.defaultCharset())\n          expect(output.contains(\"hearth-cross-quotes\"))\n        }\n        locally {\n          val res =\n            buildToolCommand(root, Some(mainClass), s\"$millDefaultProjectName.run\")\n              .call(cwd = root / outputDir)\n          val output = res.out.text(Charset.defaultCharset())\n          expect(output.contains(message))\n        }\n      }\n  }\n\n  if runExportTests then {\n    test(s\"JVM custom project name$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        val customProjectName = \"newproject\"\n        jvmTest(\n          mainArgs = Seq(s\"$customProjectName.run\"),\n          testArgs = Seq(s\"$customProjectName.test\"),\n          extraExportArgs = Seq(\"-p\", customProjectName) ++ defaultExportCommandArgs,\n          mainClassName = \"Hello\"\n        )\n      }\n    }\n    test(s\"JVM scalac options$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        jvmTestScalacOptions(className = \"Hello\", exportArgs = defaultExportCommandArgs)\n      }\n    }\n    if !actualScalaVersion.startsWith(\"2.12\") then\n      test(s\"JVM with a compiler plugin$commonTestDescriptionSuffix\") {\n        TestUtil.retryOnCi() {\n          jvmTestCompilerPlugin(mainClass = \"Hello\", exportArgs = defaultExportCommandArgs)\n        }\n      }\n  }\n}\n\nsealed trait TestMillVersion:\n  def millVersion: String\ntrait TestMill012 extends TestMillVersion:\n  self: ExportMillTestDefinitions =>\n  override def millVersion: String = Constants.mill012Version\ntrait TestMill1 extends TestMillVersion:\n  self: ExportMillTestDefinitions =>\n  override def millVersion: String = Constants.mill1Version\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportSbtTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nabstract class ExportSbtTestDefinitions extends ScalaCliSuite\n    with TestScalaVersionArgs with ExportCommonTestDefinitions\n    with ExportScalaOrientedBuildToolsTestDefinitions with SbtTestHelper {\n  this: TestScalaVersion =>\n  override def exportCommand(args: String*): os.proc =\n    os.proc(\n      TestUtil.cli,\n      \"--power\",\n      \"export\",\n      extraOptions,\n      \"--sbt\",\n      \"-o\",\n      outputDir.toString,\n      args\n    )\n\n  override def buildToolCommand(root: os.Path, mainClass: Option[String], args: String*): os.proc =\n    sbtCommand(args*)\n\n  override def runMainArgs(mainClass: Option[String]): Seq[String]  = Seq(\"run\")\n  override def runTestsArgs(mainClass: Option[String]): Seq[String] = Seq(\"test\")\n\n  test(\"Scala Native\") {\n    TestUtil.retryOnCi() {\n      simpleTest(ExportTestProjects.nativeTest(actualScalaVersion), mainClass = None)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportSbtTests212.scala",
    "content": "package scala.cli.integration\n\nclass ExportSbtTests212 extends ExportSbtTestDefinitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportSbtTests213.scala",
    "content": "package scala.cli.integration\n\nclass ExportSbtTests213 extends ExportSbtTestDefinitions with Test213 {\n  if (runExportTests) {\n    test(\"scalac options\") {\n      simpleTest(ExportTestProjects.scalacOptionsScala2Test(actualScalaVersion), mainClass = None)\n    }\n    test(\"pure java\") {\n      simpleTest(\n        ExportTestProjects.pureJavaTest(\"ScalaCliJavaTest\"),\n        mainClass = None,\n        extraExportArgs = Seq(\"--sbt-setting=fork := true\")\n      )\n    }\n    test(\"custom JAR\") {\n      simpleTest(ExportTestProjects.customJarTest(actualScalaVersion), mainClass = None)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportSbtTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass ExportSbtTests3Lts extends ExportSbtTestDefinitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportSbtTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass ExportSbtTests3NextRc extends ExportSbtTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportSbtTestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass ExportSbtTestsDefault extends ExportSbtTestDefinitions with TestDefault\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportScalaOrientedBuildToolsTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.charset.Charset\n\n/** This is a trait that defined test definitions for scala-oriented build tools like sbt and mill.\n  * The build tools like maven doesn't support some of the features like scalaJs, ScalaNative or\n  * compile-only dependencies.\n  */\ntrait ExportScalaOrientedBuildToolsTestDefinitions {\n  this: ExportCommonTestDefinitions & ScalaCliSuite & TestScalaVersionArgs =>\n\n  def compileOnlyTest(mainClass: String, extraExportArgs: Seq[String] = Nil): Unit = {\n    val userName = \"John\"\n    prepareTestInputs(\n      ExportTestProjects.compileOnlySource(actualScalaVersion, userName = userName)\n    ).fromRoot { root =>\n      exportCommand(\".\" +: extraExportArgs*).call(cwd = root, stdout = os.Inherit)\n      val res = buildToolCommand(root, None, runMainArgs(Some(mainClass))*)\n        .call(cwd = root / outputDir)\n      val output = res.out.trim(Charset.defaultCharset())\n      expect(output.contains(userName))\n      expect(!output.contains(\"jsoniter-scala-macros\"))\n    }\n  }\n\n  def testZioTest(testClassName: String, extraExportArgs: Seq[String] = Nil): Unit = {\n    val testInput = TestInputs(\n      // todo: remove this hack after the PR https://github.com/VirtusLab/scala-cli/pull/3046 is merged\n      os.rel / \"Hello.scala\"    -> \"\"\"object Hello extends App\"\"\",\n      os.rel / \"Zio.test.scala\" ->\n        s\"\"\"|//> using dep dev.zio::zio::1.0.8\n            |//> using dep dev.zio::zio-test-sbt::1.0.8\n            |\n            |import zio._\n            |import zio.test._\n            |import zio.test.Assertion.equalTo\n            |\n            |object $testClassName extends DefaultRunnableSpec {\n            |  def spec = suite(\"associativity\")(\n            |    testM(\"associativity\") {\n            |      check(Gen.anyInt, Gen.anyInt, Gen.anyInt) { (x, y, z) =>\n            |        assert((x + y) + z)(equalTo(x + (y + z)))\n            |      }\n            |    }\n            |  )\n            |}\n            |\"\"\".stripMargin,\n      os.rel / \"input\" / \"input\" ->\n        \"\"\"|1\n           |2\"\"\".stripMargin\n    )\n\n    prepareTestInputs(testInput).fromRoot { root =>\n      val exportArgs     = \".\" +: extraExportArgs\n      val testArgsToPass = runTestsArgs(None)\n      exportCommand(exportArgs*).call(cwd = root, stdout = os.Inherit)\n      val testRes    = buildToolCommand(root, None, testArgsToPass*).call(cwd = root / outputDir)\n      val testOutput = testRes.out.text(Charset.defaultCharset())\n      expect(testOutput.contains(\"1 succeeded\"))\n    }\n  }\n  protected def logbackBugCase(mainClass: String, extraExportArgs: Seq[String] = Nil): Unit =\n    prepareTestInputs(ExportTestProjects.logbackBugCase(actualScalaVersion)).fromRoot { root =>\n      exportCommand(\".\" +: extraExportArgs*).call(cwd = root, stdout = os.Inherit)\n      val res = buildToolCommand(root, Some(mainClass), runMainArgs(Some(mainClass))*)\n        .call(cwd = root / outputDir)\n      val output = res.out.text(Charset.defaultCharset())\n      expect(output.contains(\"Hello\"))\n    }\n\n  if runExportTests then {\n    test(s\"compile-time only for jsoniter macros$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        compileOnlyTest(mainClass = \"main\", extraExportArgs = defaultExportCommandArgs)\n      }\n    }\n    test(s\"Scala.js$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        simpleTest(\n          inputs = ExportTestProjects.jsTest(actualScalaVersion),\n          mainClass = None,\n          extraExportArgs = defaultExportCommandArgs\n        )\n      }\n    }\n    test(s\"zio test$commonTestDescriptionSuffix\") {\n      TestUtil.retryOnCi() {\n        testZioTest(testClassName = \"ZioSpec\", extraExportArgs = defaultExportCommandArgs)\n      }\n    }\n    test(\n      s\"Ensure test framework NPE is not thrown when depending on logback$commonTestDescriptionSuffix\"\n    ) {\n      TestUtil.retryOnCi() {\n        logbackBugCase(mainClass = \"main\", extraExportArgs = defaultExportCommandArgs)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ExportTestProjects.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.Constants.munitVersion\n\nobject ExportTestProjects {\n  def jvmTest(scalaVersion: String, mainClassName: String): TestInputs = {\n    val mainFile =\n      if scalaVersion.startsWith(\"3.\") then\n        s\"\"\"//> using scala $scalaVersion\n           |//> using resourceDir ./input\n           |//> using dep org.scala-lang::scala3-compiler:$scalaVersion\n           |//> using option -deprecation\n           |\n           |import scala.io.Source\n           |\n           |object $mainClassName {\n           |  def main(args: Array[String]): Unit = {\n           |    val message = \"Hello from \" + dotty.tools.dotc.config.Properties.simpleVersionString\n           |    println(message)\n           |    val inputs = Source.fromResource(\"input\").getLines().map(_.toInt).toSeq\n           |    println(s\"resource:$${inputs.mkString(\",\")}\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n      else\n        s\"\"\"//> using scala $scalaVersion\n           |//> using resourceDir ./input\n           |//> using option -deprecation\n           |//> using plugins com.olegpy::better-monadic-for:0.3.1\n           |\n           |import scala.io.Source\n           |\n           |object $mainClassName {\n           |  def main(args: Array[String]): Unit = {\n           |    val message = \"Hello from \" + scala.util.Properties.versionNumberString\n           |    println(message)\n           |    val inputs = Source.fromResource(\"input\").getLines().map(_.toInt).toSeq\n           |    println(s\"resource:$${inputs.mkString(\",\")}\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n\n    TestInputs(\n      os.rel / s\"$mainClassName.scala\" -> mainFile,\n      os.rel / \"Zio.test.scala\"        ->\n        \"\"\"|//> using dep dev.zio::zio::1.0.8\n           |//> using dep dev.zio::zio-test-sbt::1.0.8\n           |\n           |import zio._\n           |import zio.test._\n           |import zio.test.Assertion.equalTo\n           |\n           |object HelloWorldSpec extends DefaultRunnableSpec {\n           |  def spec = suite(\"associativity\")(\n           |    testM(\"associativity\") {\n           |      check(Gen.anyInt, Gen.anyInt, Gen.anyInt) { (x, y, z) =>\n           |        assert((x + y) + z)(equalTo(x + (y + z)))\n           |      }\n           |    }\n           |  )\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"input\" / \"input\" ->\n        \"\"\"|1\n           |2\"\"\".stripMargin\n    )\n  }\n\n  def jvmTestWithCompilerPlugin(\n    scalaVersion: String,\n    mainClassName: String,\n    message: String\n  ): TestInputs =\n    TestInputs(\n      os.rel / s\"$mainClassName.scala\" ->\n        s\"\"\"//> using scala $scalaVersion\n           |//> using option -deprecation\n           |//> using plugin com.kubuszok::hearth-cross-quotes:0.2.0\n           |\n           |object $mainClassName {\n           |  def main(args: Array[String]): Unit = {\n           |    println(\"$message\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n\n  def jsTest(scalaVersion: String): TestInputs = {\n\n    val testFile =\n      if (scalaVersion.startsWith(\"3.\"))\n        s\"\"\"//> using scala $scalaVersion\n           |//> using platform scala-js\n           |\n           |import scala.scalajs.js\n           |\n           |object Test:\n           |  def main(args: Array[String]): Unit =\n           |    val console = js.Dynamic.global.console\n           |    console.log(\"Hello from \" + \"exported Scala CLI project\")\n           |\"\"\".stripMargin\n      else\n        s\"\"\"//> using scala $scalaVersion\n           |//> using platform scala-js\n           |\n           |import scala.scalajs.js\n           |\n           |object Test {\n           |  def main(args: Array[String]): Unit = {\n           |    val console = js.Dynamic.global.console\n           |    console.log(\"Hello from \" + \"exported Scala CLI project\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    TestInputs(os.rel / \"Test.scala\" -> testFile)\n  }\n\n  def nativeTest(scalaVersion: String): TestInputs = {\n    val nl       = \"\\\\n\"\n    val testFile =\n      if (scalaVersion.startsWith(\"3.\"))\n        s\"\"\"//> using scala $scalaVersion\n           |//> using platform scala-native\n           |\n           |import scala.scalanative.libc._\n           |import scala.scalanative.unsafe._\n           |\n           |object Test:\n           |  def main(args: Array[String]): Unit =\n           |    val message = \"Hello from \" + \"exported Scala CLI project\" + \"$nl\"\n           |    Zone {\n           |      val io = StdioHelpers(stdio)\n           |      io.printf(c\"%s\", toCString(message))\n           |    }\n           |\"\"\".stripMargin\n      else\n        s\"\"\"//> using scala $scalaVersion\n           |//> using platform scala-native\n           |\n           |import scala.scalanative.libc._\n           |import scala.scalanative.unsafe._\n           |\n           |object Test {\n           |  def main(args: Array[String]): Unit = {\n           |    val message = \"Hello from \" + \"exported Scala CLI project\" + \"$nl\"\n           |    Zone { implicit z =>\n           |      val io = StdioHelpers(stdio)\n           |      io.printf(c\"%s\", toCString(message))\n           |    }\n           |  }\n           |}\n           |\"\"\".stripMargin\n    TestInputs(os.rel / \"Test.scala\" -> testFile)\n  }\n\n  def repositoryScala3Test(scalaVersion: String): TestInputs = {\n    val testFile =\n      s\"\"\"//> using scala $scalaVersion\n         |//> using dep com.github.jupyter:jvm-repr:0.4.0\n         |//> using repository jitpack\n         |import jupyter._\n         |object Test:\n         |  def main(args: Array[String]): Unit =\n         |    val message = \"Hello from \" + \"exported Scala CLI project\"\n         |    println(message)\n         |\"\"\".stripMargin\n    TestInputs(os.rel / \"Test.scala\" -> testFile)\n  }\n\n  def mainClassScala3Test(scalaVersion: String): TestInputs = {\n    val testFile =\n      s\"\"\"//> using scala $scalaVersion\n         |\n         |object Test:\n         |  def main(args: Array[String]): Unit =\n         |    val message = \"Hello from \" + \"exported Scala CLI project\"\n         |    println(message)\n         |\"\"\".stripMargin\n    val otherTestFile =\n      s\"\"\"object Other:\n         |  def main(args: Array[String]): Unit =\n         |    val message = \"Hello from \" + \"other file\"\n         |    println(message)\n         |\"\"\".stripMargin\n    TestInputs(\n      os.rel / \"Test.scala\"  -> testFile,\n      os.rel / \"Other.scala\" -> otherTestFile\n    )\n  }\n\n  def scalacOptionsScala2Test(scalaVersion: String): TestInputs = {\n    val testFile =\n      s\"\"\"//> using scala $scalaVersion\n         |//> using dep org.scala-lang.modules::scala-async:0.10.0\n         |//> using dep org.scala-lang:scala-reflect:$scalaVersion\n         |import scala.async.Async.{async, await}\n         |import scala.concurrent.{Await, Future}\n         |import scala.concurrent.duration.Duration\n         |import scala.concurrent.ExecutionContext.Implicits.global\n         |\n         |object Test {\n         |  def main(args: Array[String]): Unit = {\n         |    val messageF = Future.successful(\n         |      \"Hello from \" + \"exported Scala CLI project\"\n         |    )\n         |    val f = async {\n         |      val message = await(messageF)\n         |      println(message)\n         |    }\n         |    Await.result(f, Duration.Inf)\n         |  }\n         |}\n         |\"\"\".stripMargin\n    TestInputs(os.rel / \"Test.scala\" -> testFile)\n  }\n\n  def pureJavaTest(mainClass: String): TestInputs = {\n    val testFile =\n      s\"\"\"public class $mainClass {\n         |  public static void main(String[] args) {\n         |    String className = \"scala.concurrent.ExecutionContext\";\n         |    ClassLoader cl = Thread.currentThread().getContextClassLoader();\n         |    boolean found = true;\n         |    try {\n         |      cl.loadClass(className);\n         |    } catch (ClassNotFoundException ex) {\n         |      found = false;\n         |    }\n         |    if (found) {\n         |      throw new RuntimeException(\"Didn't expect \" + className + \" to be in class path.\");\n         |    }\n         |    System.out.println(\"Hello from \" + \"exported Scala CLI project\");\n         |  }\n         |}\n         |\"\"\".stripMargin\n    TestInputs(os.rel / \"ScalaCliJavaTest.java\" -> testFile)\n  }\n\n  def testFrameworkTest(scalaVersion: String): TestInputs = {\n    val testFile =\n      s\"\"\"//> using scala $scalaVersion\n         |//> using dep com.lihaoyi::utest:0.7.10\n         |//> using test-framework utest.runner.Framework\n         |\n         |import utest._\n         |\n         |object MyTests extends TestSuite {\n         |  val tests = Tests {\n         |    test(\"foo\") {\n         |      assert(2 + 2 == 4)\n         |      println(\"Hello from \" + \"exported Scala CLI project\")\n         |    }\n         |  }\n         |}\n         |\"\"\".stripMargin\n    TestInputs(os.rel / \"MyTests.scala\" -> testFile)\n  }\n\n  def customJarTest(scalaVersion: String): TestInputs = {\n    val shapelessJar = {\n      val res = os.proc(\n        TestUtil.cs,\n        \"fetch\",\n        \"--intransitive\",\n        \"com.chuusai::shapeless:2.3.9\",\n        \"--scala\",\n        scalaVersion\n      )\n        .call()\n      val path  = res.out.trim()\n      val path0 = os.Path(path, os.pwd)\n      expect(os.isFile(path0))\n      path0\n    }\n    val shapelessJarStr =\n      \"\\\"\" + shapelessJar.toString.replace(\"\\\\\", \"\\\\\\\\\") + \"\\\"\"\n    val testFile =\n      s\"\"\"//> using scala $scalaVersion\n         |//> using jar $shapelessJarStr\n         |\n         |import shapeless._\n         |\n         |object Test {\n         |  def main(args: Array[String]): Unit = {\n         |    val l = \"exported Scala CLI project\" :: 2 :: true :: HNil\n         |    val messageEnd: String = l.head\n         |    println(\"Hello from \" + messageEnd)\n         |  }\n         |}\n         |\"\"\".stripMargin\n    TestInputs(os.rel / \"Test.scala\" -> testFile)\n  }\n\n  def logbackBugCase(scalaVersion: String): TestInputs =\n    TestInputs(os.rel / \"script.sc\" ->\n      s\"\"\"//> using scala $scalaVersion\n         |//> using dep ch.qos.logback:logback-classic:1.4.5\n         |println(\"Hello\")\n         |\"\"\".stripMargin)\n\n  def extraSourceFromDirectiveWithExtraDependency(\n    scalaVersion: String,\n    mainClass: String\n  ): TestInputs =\n    TestInputs(\n      os.rel / s\"$mainClass.scala\" ->\n        s\"\"\"//> using scala $scalaVersion\n           |//> using file Message.scala\n           |object $mainClass extends App {\n           |  println(Message(value = os.pwd.toString).value)\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Message.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |case class Message(value: String)\n           |\"\"\".stripMargin\n    )\n  def compileOnlySource(scalaVersion: String, userName: String): TestInputs =\n    TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"//> using scala $scalaVersion\n           |//> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:2.23.2\n           |//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\n           |\n           |import com.github.plokhotnyuk.jsoniter_scala.core._\n           |import com.github.plokhotnyuk.jsoniter_scala.macros._\n           |\n           |object Hello extends App {\n           |  case class User(name: String, friends: Seq[String])\n           |  implicit val codec: JsonValueCodec[User] = JsonCodecMaker.make\n           |\n           |  val user = readFromString[User](\"{\\\\\"name\\\\\":\\\\\"$userName\\\\\",\\\\\"friends\\\\\":[\\\\\"Mark\\\\\"]}\")\n           |  System.out.println(user.name)\n           |  val classPath = System.getProperty(\"java.class.path\").split(java.io.File.pathSeparator).iterator.toList\n           |  System.out.println(classPath)\n           |}\n           |\"\"\".stripMargin\n    )\n\n  def scalaVersionTest(scalaVersion: String, mainClass: String): TestInputs =\n    TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"//> using scala $scalaVersion\n           |object $mainClass extends App {\n           |        println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    )\n\n  def justTestScope(testClass: String, msg: String): TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n         |\n         |class $testClass extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 4)\n         |    println(\"$msg\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixBuiltInRulesTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\ntrait FixBuiltInRulesTestDefinitions { this: FixTestDefinitions =>\n  test(\"basic built-in rules\") {\n    val mainFileName = \"Main.scala\"\n    val inputs       = TestInputs(\n      os.rel / mainFileName ->\n        s\"\"\"//> using objectWrapper\n           |//> using dep com.lihaoyi::os-lib:0.9.1 com.lihaoyi::upickle:3.1.2\n           |\n           |package com.foo.main\n           |\n           |object Main extends App {\n           |  println(os.pwd)\n           |}\n           |\"\"\".stripMargin,\n      os.rel / projectFileName ->\n        s\"\"\"//> using deps com.lihaoyi::pprint:0.6.6\n           |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n\n      val fixOutput = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"fix\",\n        \".\",\n        \"-v\",\n        \"-v\",\n        extraOptions,\n        enableRulesOptions(enableScalafix = false)\n      )\n        .call(cwd = root, mergeErrIntoOut = true).out.trim()\n\n      assertNoDiff(\n        filterDebugOutputs(fixOutput),\n        \"\"\"Running built-in rules...\n          |Extracting directives from Main.scala\n          |Extracting directives from project.scala\n          |Writing project.scala\n          |Removing directives from Main.scala\n          |Built-in rules completed.\"\"\".stripMargin\n      )\n\n      val projectFileContents = os.read(root / projectFileName)\n      val mainFileContents    = os.read(root / mainFileName)\n\n      assertNoDiff(\n        projectFileContents,\n        \"\"\"// Main\n          |//> using objectWrapper\n          |//> using dependency com.lihaoyi::os-lib:0.9.1 com.lihaoyi::pprint:0.6.6 com.lihaoyi::upickle:3.1.2\n          |\"\"\".stripMargin\n      )\n\n      assertNoDiff(\n        mainFileContents,\n        \"\"\"package com.foo.main\n          |\n          |object Main extends App {\n          |  println(os.pwd)\n          |}\n          |\"\"\".stripMargin\n      )\n\n      val runProc = os.proc(TestUtil.cli, \"--power\", \"compile\", \".\", extraOptions)\n        .call(cwd = root, stderr = os.Pipe)\n\n      expect(!runProc.err.trim().contains(\"Using directives detected in multiple files\"))\n    }\n  }\n\n  test(\"built-in rules for script with shebang\") {\n    val mainFileName = \"main.sc\"\n    val inputs       = TestInputs(\n      os.rel / mainFileName ->\n        s\"\"\"#!/usr/bin/env -S scala-cli shebang\n           |\n           |//> using objectWrapper\n           |//> using dep com.lihaoyi::os-lib:0.9.1 com.lihaoyi::upickle:3.1.2\n           |\n           |println(os.pwd)\n           |\"\"\".stripMargin,\n      os.rel / projectFileName ->\n        s\"\"\"//> using deps com.lihaoyi::pprint:0.6.6\n           |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n\n      val fixOutput =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"fix\",\n          \".\",\n          \"-v\",\n          \"-v\",\n          extraOptions,\n          enableRulesOptions(enableScalafix = false)\n        )\n          .call(cwd = root, mergeErrIntoOut = true).out.trim()\n\n      assertNoDiff(\n        filterDebugOutputs(fixOutput),\n        \"\"\"Running built-in rules...\n          |Extracting directives from project.scala\n          |Extracting directives from main.sc\n          |Writing project.scala\n          |Removing directives from main.sc\n          |Built-in rules completed.\"\"\".stripMargin\n      )\n\n      val projectFileContents = os.read(root / projectFileName)\n      val mainFileContents    = os.read(root / mainFileName)\n\n      assertNoDiff(\n        projectFileContents,\n        \"\"\"// Main\n          |//> using objectWrapper\n          |//> using dependency com.lihaoyi::os-lib:0.9.1 com.lihaoyi::pprint:0.6.6 com.lihaoyi::upickle:3.1.2\n          |\"\"\".stripMargin\n      )\n\n      assertNoDiff(\n        mainFileContents,\n        \"\"\"#!/usr/bin/env -S scala-cli shebang\n          |\n          |println(os.pwd)\n          |\"\"\".stripMargin\n      )\n\n      val runProc = os.proc(TestUtil.cli, \"--power\", \"compile\", \".\", extraOptions)\n        .call(cwd = root, stderr = os.Pipe)\n\n      expect(!runProc.err.trim().contains(\"Using directives detected in multiple files\"))\n    }\n  }\n\n  test(\"built-in rules with test scope\") {\n    val mainSubPath = os.rel / \"src\" / \"Main.scala\"\n    val testSubPath = os.rel / \"test\" / \"MyTests.scala\"\n    val inputs      = TestInputs(\n      mainSubPath ->\n        s\"\"\"//> using objectWrapper\n           |//> using dep com.lihaoyi::os-lib:0.9.1\n           |\n           |//> using test.dep org.typelevel::cats-core:2.9.0\n           |\n           |package com.foo.main\n           |\n           |object Main extends App {\n           |  println(os.pwd)\n           |}\n           |\"\"\".stripMargin,\n      testSubPath ->\n        s\"\"\"//> using options -Xasync -Xfatal-warnings\n           |//> using dep org.scalameta::munit::0.7.29\n           |\n           |package com.foo.test.bar\n           |\n           |class MyTests extends munit.FunSuite {\n           |  test(\"bar\") {\n           |    assert(2 + 2 == 4)\n           |    println(\"Hello from \" + \"tests\")\n           |  }\n           |}\n           |\"\"\".stripMargin,\n      os.rel / projectFileName ->\n        s\"\"\"//> using deps com.lihaoyi::pprint:0.6.6\n           |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n\n      val fixOutput =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"fix\",\n          \".\",\n          \"-v\",\n          \"-v\",\n          extraOptions,\n          enableRulesOptions(enableScalafix = false)\n        )\n          .call(cwd = root, mergeErrIntoOut = true).out.trim()\n\n      assertNoDiff(\n        filterDebugOutputs(fixOutput),\n        \"\"\"Running built-in rules...\n          |Extracting directives from project.scala\n          |Extracting directives from src/Main.scala\n          |Extracting directives from test/MyTests.scala\n          |Writing project.scala\n          |Removing directives from src/Main.scala\n          |Removing directives from test/MyTests.scala\n          |Built-in rules completed.\"\"\".stripMargin\n      )\n\n      val projectFileContents = os.read(root / projectFileName)\n      val mainFileContents    = os.read(root / mainSubPath)\n      val testFileContents    = os.read(root / testSubPath)\n\n      assertNoDiff(\n        projectFileContents,\n        \"\"\"// Main\n          |//> using objectWrapper\n          |//> using dependency com.lihaoyi::os-lib:0.9.1 com.lihaoyi::pprint:0.6.6\n          |\n          |// Test\n          |//> using test.options -Xasync -Xfatal-warnings\n          |//> using test.dependency org.scalameta::munit::0.7.29 org.typelevel::cats-core:2.9.0\n          |\"\"\".stripMargin\n      )\n\n      assertNoDiff(\n        mainFileContents,\n        \"\"\"package com.foo.main\n          |\n          |object Main extends App {\n          |  println(os.pwd)\n          |}\n          |\"\"\".stripMargin\n      )\n\n      assertNoDiff(\n        testFileContents,\n        \"\"\"package com.foo.test.bar\n          |\n          |class MyTests extends munit.FunSuite {\n          |  test(\"bar\") {\n          |    assert(2 + 2 == 4)\n          |    println(\"Hello from \" + \"tests\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n      )\n\n      val runProc = os.proc(TestUtil.cli, \"--power\", \"compile\", \".\", extraOptions)\n        .call(cwd = root, stderr = os.Pipe)\n\n      expect(!runProc.err.trim().contains(\"Using directives detected in multiple files\"))\n    }\n  }\n\n  test(\"built-in rules with complex inputs\") {\n    val mainSubPath = os.rel / \"src\" / \"Main.scala\"\n    val testSubPath = os.rel / \"test\" / \"MyTests.scala\"\n\n    val withUsedTargetSubPath  = os.rel / \"src\" / \"UsedTarget.scala\"\n    val withUsedTargetContents =\n      s\"\"\"//> using target.scala 3.3.0\n         |//> using dep com.lihaoyi::upickle:3.1.2\n         |case class UsedTarget(x: Int)\n         |\"\"\".stripMargin\n    val withUnusedTargetSubPath  = os.rel / \"src\" / \"UnusedTarget.scala\"\n    val withUnusedTargetContents =\n      s\"\"\"//> using target.scala 2.13\n         |//> using dep com.lihaoyi::upickle:3.1.2\n         |case class UnusedTarget(x: Int)\n         |\"\"\".stripMargin\n\n    val includedInputs = TestInputs(\n      os.rel / \"Included.scala\" ->\n        \"\"\"//> using options -Werror\n          |\n          |case class Included(x: Int)\n          |\"\"\".stripMargin\n    )\n\n    includedInputs.fromRoot { includeRoot =>\n      val includePath = (includeRoot / \"Included.scala\").toString.replace(\"\\\\\", \"\\\\\\\\\")\n\n      val inputs = TestInputs(\n        mainSubPath ->\n          s\"\"\"//> using platforms jvm\n             |//> using scala 3.3.0\n             |//> using jvm 17\n             |//> using objectWrapper\n             |//> using dep com.lihaoyi::os-lib:0.9.1\n             |//> using file $includePath\n             |\n             |//> using test.dep org.typelevel::cats-core:2.9.0\n             |\n             |package com.foo.main\n             |\n             |object Main extends App {\n             |  println(os.pwd)\n             |}\n             |\"\"\".stripMargin,\n        withUsedTargetSubPath   -> withUsedTargetContents,\n        withUnusedTargetSubPath -> withUnusedTargetContents,\n        testSubPath             ->\n          s\"\"\"//> using options -Xasync -Xfatal-warnings\n             |//> using dep org.scalameta::munit::0.7.29\n             |//> using scala 3.2.2\n             |\n             |package com.foo.test.bar\n             |\n             |class MyTests extends munit.FunSuite {\n             |  test(\"bar\") {\n             |    assert(2 + 2 == 4)\n             |    println(\"Hello from \" + \"tests\")\n             |  }\n             |}\n             |\"\"\".stripMargin,\n        os.rel / projectFileName ->\n          s\"\"\"//> using deps com.lihaoyi::pprint:0.6.6\n             |\n             |//> using publish.ci.password env:PUBLISH_PASSWORD\n             |//> using publish.ci.secretKey env:PUBLISH_SECRET_KEY\n             |//> using publish.ci.secretKeyPassword env:PUBLISH_SECRET_KEY_PASSWORD\n             |//> using publish.ci.user env:PUBLISH_USER\n             |\"\"\".stripMargin\n      )\n\n      inputs.fromRoot { root =>\n        val res = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"fix\",\n          \".\",\n          \"--script-snippet\",\n          \"//> using toolkit default\",\n          \"-v\",\n          \"-v\",\n          extraOptions,\n          enableRulesOptions(enableScalafix = false)\n        ).call(cwd = root, stderr = os.Pipe)\n\n        assertNoDiff(\n          filterDebugOutputs(res.err.trim()),\n          s\"\"\"Running built-in rules...\n             |Extracting directives from project.scala\n             |Extracting directives from src/Main.scala\n             |Extracting directives from src/UsedTarget.scala\n             |Extracting directives from ${includeRoot / \"Included.scala\"}\n             |Extracting directives from snippet\n             |Extracting directives from test/MyTests.scala\n             |Writing project.scala\n             |Removing directives from src/Main.scala\n             |Removing directives from test/MyTests.scala\n             |  Keeping:\n             |    //> using scala 3.2.2\n             |Built-in rules completed.\"\"\".stripMargin\n        )\n\n        val projectFileContents          = os.read(root / projectFileName)\n        val mainFileContents             = os.read(root / mainSubPath)\n        val testFileContents             = os.read(root / testSubPath)\n        val withUsedTargetContentsRead   = os.read(root / withUsedTargetSubPath)\n        val withUnusedTargetContentsRead = os.read(root / withUnusedTargetSubPath)\n\n        assertNoDiff(\n          projectFileContents,\n          s\"\"\"// Main\n             |//> using scala 3.3.0\n             |//> using platforms jvm\n             |//> using jvm 17\n             |//> using options -Werror\n             |//> using files $includePath\n             |//> using objectWrapper\n             |//> using toolkit default\n             |//> using dependency com.lihaoyi::os-lib:0.9.1 com.lihaoyi::pprint:0.6.6\n             |\n             |//> using publish.ci.password env:PUBLISH_PASSWORD\n             |//> using publish.ci.secretKey env:PUBLISH_SECRET_KEY\n             |//> using publish.ci.secretKeyPassword env:PUBLISH_SECRET_KEY_PASSWORD\n             |//> using publish.ci.user env:PUBLISH_USER\n             |\n             |// Test\n             |//> using test.options -Xasync -Xfatal-warnings\n             |//> using test.dependency org.scalameta::munit::0.7.29 org.typelevel::cats-core:2.9.0\n             |\"\"\".stripMargin\n        )\n\n        assertNoDiff(\n          mainFileContents,\n          \"\"\"package com.foo.main\n            |\n            |object Main extends App {\n            |  println(os.pwd)\n            |}\n            |\"\"\".stripMargin\n        )\n\n        // Directives with no 'test.' equivalent are retained\n        assertNoDiff(\n          testFileContents,\n          \"\"\"//> using scala 3.2.2\n            |\n            |package com.foo.test.bar\n            |\n            |class MyTests extends munit.FunSuite {\n            |  test(\"bar\") {\n            |    assert(2 + 2 == 4)\n            |    println(\"Hello from \" + \"tests\")\n            |  }\n            |}\n            |\"\"\".stripMargin\n        )\n\n        assertNoDiff(withUsedTargetContents, withUsedTargetContentsRead)\n        assertNoDiff(withUnusedTargetContents, withUnusedTargetContentsRead)\n      }\n\n      assertNoDiff(\n        os.read(includeRoot / \"Included.scala\"),\n        \"\"\"//> using options -Werror\n          |\n          |case class Included(x: Int)\n          |\"\"\".stripMargin\n      )\n    }\n  }\n\n  if (!Properties.isWin) // TODO: fix this test for Windows CI\n    test(\"using directives with boolean values are handled correctly\") {\n      val expectedMessage    = \"Hello, world!\"\n      def maybeScalapyPrefix =\n        if (actualScalaVersion.startsWith(\"2.13.\")) \"\"\n        else \"import me.shadaj.scalapy.py\" + System.lineSeparator()\n      TestInputs(\n        os.rel / \"Messages.scala\" ->\n          s\"\"\"object Messages {\n             |  def hello: String = \"$expectedMessage\"\n             |}\n             |\"\"\".stripMargin,\n        os.rel / \"Main.scala\" ->\n          s\"\"\"//> using python true\n             |$maybeScalapyPrefix\n             |object Main extends App {\n             |  py.Dynamic.global.print(Messages.hello, flush = true)\n             |}\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        os.proc(TestUtil.cli, \"--power\", \"fix\", \".\", extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        val r = os.proc(TestUtil.cli, \"--power\", \"run\", \".\", extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        expect(r.out.trim() == expectedMessage)\n      }\n    }\n\n  {\n    val directive = \"//> using dep com.lihaoyi::os-lib:0.11.3\"\n    for {\n      (inputFileName, code) <- Seq(\n        \"raw.scala\" -> s\"\"\"$directive\n                          |object Main extends App {\n                          |  println(os.pwd)\n                          |}\n                          |\"\"\".stripMargin,\n        \"script.sc\" -> s\"\"\"$directive\n                          |println(os.pwd)\n                          |\"\"\".stripMargin\n      )\n      if !Properties.isWin // TODO: make this run on Windows CI\n      testInputs = TestInputs(os.rel / inputFileName -> code)\n    }\n      test(\n        s\"dont extract directives into project.scala for a single-file project: $inputFileName\"\n      ) {\n        testInputs.fromRoot { root =>\n          val fixResult = os.proc(TestUtil.cli, \"--power\", \"fix\", \".\", extraOptions)\n            .call(cwd = root, stderr = os.Pipe)\n          expect(fixResult.err.trim().contains(\n            \"No need to migrate directives for a single source file project\"\n          ))\n          expect(!os.exists(root / projectFileName))\n          expect(os.read(root / inputFileName) == code)\n          val runResult = os.proc(TestUtil.cli, \"run\", \".\", extraOptions)\n            .call(cwd = root, stderr = os.Pipe)\n          expect(runResult.out.trim() == root.toString)\n        }\n      }\n  }\n\n  if (!Properties.isWin)\n    test(\"all test directives get extracted into project.scala\") {\n      val osLibDep               = \"com.lihaoyi::os-lib:0.11.5\"\n      val munitDep               = \"org.scalameta::munit:1.1.1\"\n      val pprintDep              = \"com.lihaoyi::pprint:0.9.3\"\n      val osLibDepDirective      = s\"//> using dependency $osLibDep\"\n      val osLibTestDepDirective  = s\"//> using test.dependency $osLibDep\"\n      val munitTestDepDirective  = s\"//> using test.dependency $munitDep\"\n      val pprintTestDepDirective = s\"//> using test.dependency $pprintDep\"\n      val mainFilePath           = os.rel / \"Main.scala\"\n      val testFilePath           = os.rel / \"MyTests.test.scala\"\n      TestInputs(\n        mainFilePath -> s\"\"\"$munitTestDepDirective\n                           |object Main extends App {\n                           |  def hello: String = \"Hello, world!\"\n                           |  println(hello)\n                           |}\n                           |\"\"\".stripMargin,\n        testFilePath -> s\"\"\"$osLibDepDirective\n                           |$pprintTestDepDirective\n                           |import munit.FunSuite\n                           |\n                           |class MyTests extends FunSuite {\n                           |  test(\"hello\") {\n                           |    pprint.pprintln(os.pwd)\n                           |    assert(Main.hello == \"Hello, world!\")\n                           |  }\n                           |}\n                           |\"\"\".stripMargin\n      ).fromRoot { root =>\n        os.proc(TestUtil.cli, \"--power\", \"fix\", \".\", extraOptions).call(cwd = root)\n        val expectedProjectFileContents =\n          s\"\"\"// Test\n             |$osLibTestDepDirective\n             |$pprintTestDepDirective\n             |$munitTestDepDirective\"\"\".stripMargin\n        val projectFileContents = os.read(root / projectFileName)\n        expect(projectFileContents.trim() == expectedProjectFileContents)\n        val mainFileContents = os.read(root / mainFilePath)\n        expect(!mainFileContents.contains(\"//> using\"))\n        val testFileContents = os.read(root / testFilePath)\n        expect(!testFileContents.contains(\"//> using\"))\n        os.proc(TestUtil.cli, \"test\", \".\", extraOptions).call(cwd = root)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixScalafixRulesTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\ntrait FixScalafixRulesTestDefinitions {\n  this: FixTestDefinitions =>\n  protected val scalafixConfFileName: String = \".scalafix.conf\"\n  protected def scalafixUnusedRuleOption: String\n  protected def noCrLf(input: String): String =\n    input.replaceAll(\"\\r\\n\", \"\\n\")\n\n  private val simpleInputsOriginalContent: String =\n    \"\"\"package foo\n      |\n      |final object Hello {\n      |  def main(args: Array[String]): Unit = {\n      |    println(\"Hello\")\n      |  }\n      |}\n      |\"\"\".stripMargin\n  private val simpleInputs: TestInputs = TestInputs(\n    os.rel / scalafixConfFileName ->\n      s\"\"\"|rules = [\n          |  RedundantSyntax\n          |]\n          |\"\"\".stripMargin,\n    os.rel / \"Hello.scala\" -> simpleInputsOriginalContent\n  )\n  private val expectedContent: String = noCrLf {\n    \"\"\"package foo\n      |\n      |object Hello {\n      |  def main(args: Array[String]): Unit = {\n      |    println(\"Hello\")\n      |  }\n      |}\n      |\"\"\".stripMargin\n  }\n\n  test(\"simple\") {\n    simpleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fix\", \".\", \"--power\", scalaVersionArgs).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == expectedContent)\n    }\n  }\n\n  test(\"with --check\") {\n    simpleInputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"fix\", \"--power\", \"--check\", \".\", scalaVersionArgs).call(\n        cwd = root,\n        check = false\n      )\n      expect(res.exitCode != 0)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == noCrLf(simpleInputsOriginalContent))\n    }\n  }\n\n  test(\"semantic rule\") {\n    val unusedValueInputsContent: String =\n      s\"\"\"//> using options $scalafixUnusedRuleOption\n         |package foo\n         |\n         |object Hello {\n         |  def main(args: Array[String]): Unit = {\n         |    val name = \"John\"\n         |    println(\"Hello\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n    val semanticRuleInputs: TestInputs = TestInputs(\n      os.rel / scalafixConfFileName ->\n        s\"\"\"|rules = [\n            |  RemoveUnused\n            |]\n            |\"\"\".stripMargin,\n      os.rel / \"Hello.scala\" -> unusedValueInputsContent\n    )\n    val expectedContent: String = noCrLf {\n      s\"\"\"//> using options $scalafixUnusedRuleOption\n         |package foo\n         |\n         |object Hello {\n         |  def main(args: Array[String]): Unit = {\n         |    \n         |    println(\"Hello\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n    }\n\n    semanticRuleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fix\", \"--power\", \".\", scalaVersionArgs).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == expectedContent)\n    }\n  }\n\n  test(\"--rules args\") {\n    val input = TestInputs(\n      os.rel / scalafixConfFileName ->\n        s\"\"\"|rules = [\n            |  RemoveUnused,\n            |  RedundantSyntax\n            |]\n            |\"\"\".stripMargin,\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"|//> using options $scalafixUnusedRuleOption\n            |package hello\n            |\n            |object Hello {\n            |  def a = {\n            |    val x = 1 // keep unused - exec only RedundantSyntax\n            |    s\"Foo\"\n            |  }\n            |}\n            |\"\"\".stripMargin\n    )\n\n    input.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"fix\",\n        \".\",\n        \"--scalafix-rules\",\n        \"RedundantSyntax\",\n        \"--power\",\n        scalaVersionArgs\n      ).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      val expected       = noCrLf {\n        s\"\"\"|//> using options $scalafixUnusedRuleOption\n            |package hello\n            |\n            |object Hello {\n            |  def a = {\n            |    val x = 1 // keep unused - exec only RedundantSyntax\n            |    \"Foo\"\n            |  }\n            |}\n            |\"\"\".stripMargin\n      }\n\n      expect(updatedContent == expected)\n\n    }\n  }\n\n  test(\"--scalafix-arg arg\") {\n    val original: String =\n      \"\"\"|package foo\n         |\n         |final object Hello { // keep `final` beucase of parameter finalObject=false\n         |  s\"Foo\"\n         |}\n         |\"\"\".stripMargin\n    val inputs: TestInputs = TestInputs(\n      os.rel / scalafixConfFileName ->\n        s\"\"\"|rules = [\n            |  RedundantSyntax\n            |]\n            |\"\"\".stripMargin,\n      os.rel / \"Hello.scala\" -> original\n    )\n    val expectedContent: String = noCrLf {\n      \"\"\"|package foo\n         |\n         |final object Hello { // keep `final` beucase of parameter finalObject=false\n         |  \"Foo\"\n         |}\n         |\"\"\".stripMargin\n    }\n\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"fix\",\n        \".\",\n        \"--scalafix-arg=--settings.RedundantSyntax.finalObject=false\",\n        \"--power\",\n        scalaVersionArgs\n      ).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == expectedContent)\n    }\n  }\n\n  test(\"--scalafix-conf arg\") {\n    val original: String =\n      \"\"\"|package foo\n         |\n         |final object Hello {\n         |  s\"Foo\"\n         |}\n         |\"\"\".stripMargin\n\n    val confFileName       = \"unusual-scalafix-filename\"\n    val inputs: TestInputs = TestInputs(\n      os.rel / confFileName ->\n        s\"\"\"|rules = [\n            |  RedundantSyntax\n            |]\n            |\"\"\".stripMargin,\n      os.rel / \"Hello.scala\" -> original\n    )\n    val expectedContent: String = noCrLf {\n      \"\"\"|package foo\n         |\n         |object Hello {\n         |  \"Foo\"\n         |}\n         |\"\"\".stripMargin\n    }\n\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"fix\",\n        \".\",\n        s\"--scalafix-conf=$confFileName\",\n        \"--power\",\n        scalaVersionArgs\n      ).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == expectedContent)\n    }\n  }\n\n  test(\"external rule\") {\n    val directive        = s\"//> using scalafixDependency com.github.xuwei-k::scalafix-rules:0.5.1\"\n    val original: String =\n      s\"\"\"|$directive\n          |\n          |object CollectHeadOptionTest {\n          |  def x1: Option[String] = List(1, 2, 3).collect { case n if n % 2 == 0 => n.toString }.headOption\n          |}\n          |\"\"\".stripMargin\n    val inputs: TestInputs = TestInputs(\n      os.rel / scalafixConfFileName ->\n        s\"\"\"|rules = [\n            |  CollectHeadOption\n            |]\n            |\"\"\".stripMargin,\n      os.rel / \"Hello.scala\" -> original\n    )\n    val expectedContent: String = noCrLf {\n      s\"\"\"|$directive\n          |\n          |object CollectHeadOptionTest {\n          |  def x1: Option[String] = List(1, 2, 3).collectFirst{ case n if n % 2 == 0 => n.toString }\n          |}\n          |\"\"\".stripMargin\n    }\n\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fix\", \".\", \"--power\", scalaVersionArgs).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == expectedContent)\n    }\n  }\n\n  test(\"explicit-result-types\") {\n    val original: String =\n      \"\"\"|package foo\n         |\n         |object Hello {\n         |  def a(a: Int) = \"asdasd\" + a.toString\n         |}\n         |\"\"\".stripMargin\n    val inputs: TestInputs = TestInputs(\n      os.rel / scalafixConfFileName ->\n        s\"\"\"|rules = [\n            |  ExplicitResultTypes\n            |]\n            |ExplicitResultTypes.fetchScala3CompilerArtifactsOnVersionMismatch = true\n            |\"\"\".stripMargin,\n      os.rel / \"Hello.scala\" -> original\n    )\n    val expectedContent: String = noCrLf {\n      \"\"\"|package foo\n         |\n         |object Hello {\n         |  def a(a: Int): String = \"asdasd\" + a.toString\n         |}\n         |\"\"\".stripMargin\n    }\n\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fix\", \".\", \"--power\", scalaVersionArgs).call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Hello.scala\"))\n      expect(updatedContent == expectedContent)\n    }\n  }\n\n  for {\n    (semanticDbOptions, expectedSuccess) <- Seq(\n      Nil -> true, // .semanticdb files should be implicitly generated when Scalafix is run\n      Seq(\"--semanticdb\")       -> true,\n      Seq(\"--semanticdb=false\") -> false\n    )\n    semanticDbOptionsDescription =\n      if (semanticDbOptions.nonEmpty) s\" (${semanticDbOptions.mkString(\" \")})\"\n      else \"\"\n    verb = if (expectedSuccess) \"run\" else \"fail\"\n    if !Properties.isWin || expectedSuccess\n  }\n    test(\n      s\"scalafix rules requiring SemanticDB $verb correctly with test scope sources$semanticDbOptionsDescription\"\n    ) {\n      val compilerOptions =\n        if (actualScalaVersion.startsWith(\"2.12\")) Seq(\"-Ywarn-unused-import\")\n        else Seq(\"-Wunused:imports\")\n      TestInputs(\n        os.rel / scalafixConfFileName ->\n          \"\"\"rules = [\n            |  DisableSyntax,\n            |  LeakingImplicitClassVal,\n            |  NoValInForComprehension,\n            |  ExplicitResultTypes,\n            |  OrganizeImports\n            |]\n            |ExplicitResultTypes.fetchScala3CompilerArtifactsOnVersionMismatch = true\n            |\"\"\".stripMargin,\n        os.rel / projectFileName ->\n          s\"\"\"//> using test.dep org.scalameta::munit::${Constants.munitVersion}\n             |//> using options ${compilerOptions.mkString(\" \")}\n             |\"\"\".stripMargin,\n        os.rel / \"example.test.scala\" ->\n          \"\"\"import munit.FunSuite\n            |\n            |class Munit extends FunSuite {\n            |  test(\"foo\") {\n            |    assert(2 + 2 == 4)\n            |    println(\"Hello from Munit\")\n            |  }\n            |}\n            |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"fix\", \".\", \"--power\", semanticDbOptions, extraOptions)\n          .call(cwd = root, check = false, stderr = os.Pipe)\n        val successful = res.exitCode == 0\n        expect(successful == expectedSuccess)\n        if (!expectedSuccess)\n          expect(\n            res.err.trim().contains(\"SemanticDB files' generation was explicitly set to false\")\n          )\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nabstract class FixTestDefinitions\n    extends ScalaCliSuite\n    with TestScalaVersionArgs\n    with FixBuiltInRulesTestDefinitions\n    with FixScalafixRulesTestDefinitions { this: TestScalaVersion =>\n  val projectFileName           = \"project.scala\"\n  val extraOptions: Seq[String] =\n    scalaVersionArgs ++ TestUtil.extraOptions ++ Seq(\"--suppress-experimental-feature-warning\")\n  def enableRulesOptions(\n    enableScalafix: Boolean = true,\n    enableBuiltIn: Boolean = true\n  ): Seq[String] =\n    Seq(\n      s\"--enable-scalafix=${enableScalafix.toString}\",\n      s\"--enable-built-in-rules=${enableBuiltIn.toString}\"\n    )\n\n  test(\"built-in + scalafix rules\") {\n    val mainFileName         = \"Main.scala\"\n    val unusedValName        = \"unused\"\n    val directive1           = \"//> using dep com.lihaoyi::os-lib:0.11.3\"\n    val directive2           = \"//> using dep com.lihaoyi::pprint:0.9.0\"\n    val mergedDirective1And2 =\n      \"using dependency com.lihaoyi::os-lib:0.11.3 com.lihaoyi::pprint:0.9.0\"\n    val directive3 =\n      if (actualScalaVersion.startsWith(\"2\")) \"//> using options -Xlint:unused\"\n      else \"//> using options -Wunused:all\"\n    TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"$directive1\n           |object Foo {\n           |  def hello: String = \"hello\"\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Bar.scala\" ->\n        s\"\"\"$directive2\n           |object Bar {\n           |  def world: String = \"world\"\n           |}\n           |\"\"\".stripMargin,\n      os.rel / mainFileName ->\n        s\"\"\"$directive3\n           |object Main {\n           |  def main(args: Array[String]): Unit = {\n           |    val unused = \"unused\"\n           |    pprint.pprintln(Foo.hello + Bar.world)\n           |    pprint.pprintln(os.pwd)\n           |  }\n           |}\n           |\"\"\".stripMargin,\n      os.rel / scalafixConfFileName ->\n        \"\"\"rules = [\n          |  RemoveUnused\n          |]\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      os.proc(TestUtil.cli, \"fix\", \".\", extraOptions, \"--power\").call(cwd = root)\n      val projectFileContents = os.read(root / projectFileName)\n      expect(projectFileContents.contains(mergedDirective1And2))\n      expect(projectFileContents.contains(directive3))\n      val mainFileContents = os.read(root / mainFileName)\n      expect(!mainFileContents.contains(unusedValName))\n      os.proc(TestUtil.cli, \"compile\", \".\", extraOptions).call(cwd = root)\n    }\n  }\n\n  test(\"sbt file in directory does not break fix\") {\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main {\n          |  def main(args: Array[String]): Unit = println(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"build.sbt\"          -> \"\"\"name := \"my-project\"\"\"\",\n      os.rel / scalafixConfFileName ->\n        \"\"\"rules = [\n          |  RedundantSyntax\n          |]\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"fix\",\n        \".\",\n        extraOptions\n      ).call(cwd = root)\n    }\n  }\n\n  def filterDebugOutputs(output: String): String =\n    output\n      .linesIterator\n      .filterNot(_.trim().contains(\"repo dir\"))\n      .filterNot(_.trim().contains(\"local repo\"))\n      .filterNot(_.trim().contains(\"archive url\"))\n      .mkString(System.lineSeparator())\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixTests212.scala",
    "content": "package scala.cli.integration\n\nclass FixTests212 extends FixTestDefinitions with Test212 {\n  override val scalafixUnusedRuleOption: String = \"-Ywarn-unused\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixTests213.scala",
    "content": "package scala.cli.integration\n\nclass FixTests213 extends FixTestDefinitions with Test213 {\n  override val scalafixUnusedRuleOption: String = \"-Wunused\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass FixTests3Lts extends FixTestDefinitions with Test3Lts {\n  override val scalafixUnusedRuleOption: String = \"-Wunused:all\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass FixTests3NextRc extends FixTestDefinitions with Test3NextRc {\n  override val scalafixUnusedRuleOption: String = \"-Wunused:all\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FixTestsDefault.scala",
    "content": "package scala.cli.integration\n\nclass FixTestsDefault extends FixTestDefinitions with TestDefault {\n  override val scalafixUnusedRuleOption: String = \"-Wunused:all\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/FmtTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.removeAnsiColors\n\nclass FmtTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  val confFileName = \".scalafmt.conf\"\n\n  val emptyInputs: TestInputs = TestInputs(os.rel / \".placeholder\" -> \"\")\n\n  val simpleInputsUnformattedContent: String =\n    \"\"\"package foo\n      |\n      |    object Foo       extends       java.lang.Object  {\n      |                     def           get()             = 2\n      | }\n      |\"\"\".stripMargin\n  val simpleInputs: TestInputs = TestInputs(\n    os.rel / confFileName ->\n      s\"\"\"|version = \"${Constants.defaultScalafmtVersion}\"\n          |runner.dialect = scala213\n          |\"\"\".stripMargin,\n    os.rel / \"Foo.scala\" -> simpleInputsUnformattedContent\n  )\n  val expectedSimpleInputsFormattedContent: String = noCrLf {\n    \"\"\"package foo\n      |\n      |object Foo extends java.lang.Object {\n      |  def get() = 2\n      |}\n      |\"\"\".stripMargin\n  }\n\n  val simpleInputsWithFilter: TestInputs = TestInputs(\n    os.rel / confFileName ->\n      s\"\"\"|version = \"${Constants.defaultScalafmtVersion}\"\n          |runner.dialect = scala213\n          |project.excludePaths = [ \"glob:**/should/not/format/**.scala\" ]\n          |\"\"\".stripMargin,\n    os.rel / \"Foo.scala\"                 -> expectedSimpleInputsFormattedContent,\n    os.rel / \"scripts\" / \"SomeScript.sc\" -> \"println()\\n\",\n    os.rel / \"should\" / \"not\" / \"format\" / \"ShouldNotFormat.scala\" -> simpleInputsUnformattedContent\n  )\n\n  val simpleInputsWithDialectOnly: TestInputs = TestInputs(\n    os.rel / confFileName -> \"runner.dialect = scala213\".stripMargin,\n    os.rel / \"Foo.scala\"  -> simpleInputsUnformattedContent\n  )\n\n  val simpleInputsWithVersionOnly: TestInputs = TestInputs(\n    os.rel / confFileName -> \"version = \\\"3.5.5\\\"\".stripMargin,\n    os.rel / \"Foo.scala\"  -> simpleInputsUnformattedContent\n  )\n\n  val simpleInputsWithCustomConfLocation: TestInputs = TestInputs(\n    os.rel / \"custom.conf\" ->\n      s\"\"\"|version = \"3.5.5\"\n          |runner.dialect = scala213\n          |\"\"\".stripMargin,\n    os.rel / \"Foo.scala\" -> simpleInputsUnformattedContent\n  )\n\n  private def noCrLf(input: String): String =\n    input.replaceAll(\"\\r\\n\", \"\\n\")\n\n  test(\"simple\") {\n    simpleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\").call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"no inputs\") {\n    simpleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\").call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"with --check\") {\n    simpleInputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"fmt\", \"--check\").call(cwd = root, check = false)\n      expect(res.exitCode == 1)\n      val out            = res.out.text()\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(updatedContent == noCrLf(simpleInputsUnformattedContent))\n      expect(noCrLf(out) == \"error: --test failed\\n\")\n    }\n  }\n\n  test(\"filter correctly with --check\") {\n    simpleInputsWithFilter.fromRoot { root =>\n      val out = os.proc(TestUtil.cli, \"fmt\", \".\", \"--check\").call(cwd = root).out.trim()\n      expect(out == \"All files are formatted with scalafmt :)\")\n    }\n  }\n\n  test(\"--scalafmt-help\") {\n    emptyInputs.fromRoot { root =>\n      val out1 = os.proc(TestUtil.cli, \"fmt\", \"--scalafmt-help\").call(cwd = root).out.trim()\n      val out2 = os.proc(TestUtil.cli, \"fmt\", \"-F\", \"--help\").call(cwd = root).out.trim()\n      expect(out1.nonEmpty)\n      expect(out1 == out2)\n      val outLines       = out1.linesIterator.toSeq\n      val outVersionLine = outLines.head\n      expect(outVersionLine == s\"scalafmt ${Constants.defaultScalafmtVersion}\")\n      val outUsageLine = outLines.drop(1).head\n      expect(outUsageLine == \"Usage: scalafmt [options] [<file>...]\")\n    }\n  }\n\n  test(\"--save-scalafmt-conf\") {\n    simpleInputsWithDialectOnly.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\", \"--save-scalafmt-conf\").call(cwd = root)\n      val confLines      = os.read.lines(root / confFileName)\n      val versionInConf  = confLines(0).stripPrefix(\"version = \")\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(versionInConf == s\"\\\"${Constants.defaultScalafmtVersion}\\\"\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"--scalafmt-dialect\") {\n    simpleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\", \"--scalafmt-dialect\", \"scala3\").call(cwd = root)\n      val confLines      = os.read.lines(root / Constants.workspaceDirName / confFileName)\n      val dialectInConf  = confLines(1).stripPrefix(\"runner.dialect = \").trim\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(dialectInConf == \"scala3\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"--scalafmt-version\") {\n    simpleInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\", \"--scalafmt-version\", \"3.5.5\").call(cwd = root)\n      val confLines      = os.read.lines(root / Constants.workspaceDirName / confFileName)\n      val versionInConf  = confLines(0).stripPrefix(\"version = \").trim\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(versionInConf == \"\\\"3.5.5\\\"\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"--scalafmt-conf\") {\n    simpleInputsWithCustomConfLocation.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\", \"--scalafmt-conf\", \"custom.conf\").call(cwd = root)\n      val confLines      = os.read.lines(root / Constants.workspaceDirName / confFileName)\n      val versionInConf  = confLines(0).stripPrefix(\"version = \").trim\n      val dialectInConf  = confLines(1).stripPrefix(\"runner.dialect = \").trim\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(versionInConf == \"\\\"3.5.5\\\"\")\n      expect(dialectInConf == \"scala213\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"--scalafmt-conf-str\") {\n    simpleInputsWithVersionOnly.fromRoot { root =>\n      val confStr =\n        s\"\"\"version = 3.5.7${System.lineSeparator}runner.dialect = scala213${System.lineSeparator}\"\"\"\n      os.proc(TestUtil.cli, \"fmt\", \".\", \"--scalafmt-conf-str\", s\"$confStr\").call(cwd = root)\n      val confLines      = os.read.lines(root / Constants.workspaceDirName / confFileName)\n      val versionInConf  = confLines(0).stripPrefix(\"version = \")\n      val dialectInConf  = confLines(1).stripPrefix(\"runner.dialect = \")\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(versionInConf == \"\\\"3.5.7\\\"\")\n      expect(dialectInConf == \"scala213\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"creating workspace conf file\") {\n    simpleInputsWithDialectOnly.fromRoot { root =>\n      val workspaceConfPath = root / Constants.workspaceDirName / confFileName\n      expect(!os.exists(workspaceConfPath))\n      os.proc(TestUtil.cli, \"fmt\", \".\").call(cwd = root)\n      expect(os.exists(workspaceConfPath))\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"scalafmt conf without version\") {\n    simpleInputsWithDialectOnly.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\").call(cwd = root)\n      val confLines      = os.read.lines(root / Constants.workspaceDirName / confFileName)\n      val versionInConf  = confLines(0).stripPrefix(\"version = \").trim\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(versionInConf == s\"\\\"${Constants.defaultScalafmtVersion}\\\"\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"scalafmt conf without dialect\") {\n    simpleInputsWithVersionOnly.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\").call(cwd = root)\n      val confLines      = os.read.lines(root / Constants.workspaceDirName / confFileName)\n      val dialectInConf  = confLines(1).stripPrefix(\"runner.dialect = \")\n      val updatedContent = noCrLf(os.read(root / \"Foo.scala\"))\n      expect(dialectInConf == \"scala3\")\n      expect(updatedContent == expectedSimpleInputsFormattedContent)\n    }\n  }\n\n  test(\"default values in help\") {\n    TestInputs.empty.fromRoot { root =>\n      val res            = os.proc(TestUtil.cli, \"fmt\", \"--help\").call(cwd = root)\n      val lines          = removeAnsiColors(res.out.trim()).linesIterator.toVector\n      val fmtVersionHelp = lines.find(_.contains(\"--fmt-version\")).getOrElse(\"\")\n      expect(fmtVersionHelp.contains(s\"(${Constants.defaultScalafmtVersion} by default)\"))\n    }\n  }\n\n  test(\"project.scala gets formatted correctly, as any other input\") {\n    val projectFileName = \"project.scala\"\n    TestInputs(\n      os.rel / projectFileName -> simpleInputsUnformattedContent,\n      os.rel / confFileName    ->\n        s\"\"\"|version = \"${Constants.defaultScalafmtVersion}\"\n            |runner.dialect = scala3\n            |\"\"\".stripMargin\n    )\n      .fromRoot { root =>\n        os.proc(TestUtil.cli, \"fmt\", \".\").call(cwd = root)\n        val updatedContent = noCrLf(os.read(root / projectFileName))\n        expect(updatedContent == expectedSimpleInputsFormattedContent)\n      }\n  }\n\n  val sbtUnformattedContent: String =\n    \"\"\"val message    =       \"hello\"\n      |\"\"\".stripMargin\n  val expectedSbtFormattedContent: String = noCrLf {\n    \"\"\"val message = \"hello\"\n      |\"\"\".stripMargin\n  }\n  val sbtInputs: TestInputs = TestInputs(\n    os.rel / confFileName ->\n      s\"\"\"|version = \"${Constants.defaultScalafmtVersion}\"\n          |runner.dialect = scala213\n          |\"\"\".stripMargin,\n    os.rel / \"build.sbt\" -> sbtUnformattedContent\n  )\n\n  test(\"sbt file is formatted when passed explicitly\") {\n    sbtInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \"build.sbt\").call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"build.sbt\"))\n      expect(updatedContent == expectedSbtFormattedContent)\n    }\n  }\n\n  test(\"sbt file is formatted when directory is passed\") {\n    sbtInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"fmt\", \".\").call(cwd = root)\n      val updatedContent = noCrLf(os.read(root / \"build.sbt\"))\n      expect(updatedContent == expectedSbtFormattedContent)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/GitHubTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker\nimport coursier.cache.ArchiveCache\nimport coursier.util.Artifact\nimport libsodiumjni.Sodium\nimport libsodiumjni.internal.LoadLibrary\n\nimport java.nio.charset.StandardCharsets\nimport java.util.{Base64, Locale}\n\nimport scala.util.Properties\n\nclass GitHubTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  def createSecretTest(): Unit = {\n    GitHubTests.initSodium()\n\n    val keyId   = \"the-key-id\"\n    val keyPair = Sodium.keyPair()\n    val value   = \"1234\"\n    TestInputs.empty.fromRoot { root =>\n      val pubKey = GitHubTests.PublicKey(keyId, Base64.getEncoder.encodeToString(keyPair.getPubKey))\n      os.write(root / \"pub-key.json\", writeToArray(pubKey))\n\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"github\",\n        \"secret\",\n        \"create\",\n        \"--repo\",\n        \"foo/foo\",\n        s\"FOO=value:$value\",\n        \"--dummy\",\n        \"--print-request\",\n        \"--pub-key\",\n        root / \"pub-key.json\"\n      )\n        .call(cwd = root)\n      val output = readFromArray(res.out.bytes)(GitHubTests.encryptedSecretCodec)\n\n      expect(output.key_id == keyId)\n\n      val decrypted      = Sodium.sealOpen(output.encrypted, keyPair.getPubKey, keyPair.getSecKey)\n      val decryptedValue = new String(decrypted, StandardCharsets.UTF_8)\n      expect(decryptedValue == value)\n    }\n  }\n\n  override def munitFlakyOK: Boolean = TestUtil.isCI\n\n  def createSecret(): Unit = {\n    try\n      createSecretTest()\n    catch {\n      case e: UnsatisfiedLinkError if e.getMessage.contains(\"libsodium\") =>\n        fail(\"libsodium, couldn't be loaded\")\n    }\n\n  }\n\n  // currently having issues loading libsodium from the static launcher\n  // that launcher is mainly meant to be used on CIs or from docker, missing\n  // that feature shouldn't be a big deal there\n  if !TestUtil.isNativeStaticCli && !Properties.isMac && !TestUtil.isAarch64 then\n    // TODO fix this for static launchers: https://github.com/VirtusLab/scala-cli/issues/4068\n    // TODO fix this for MacOS: https://github.com/VirtusLab/scala-cli/issues/4067\n    // TODO fix this for Linux arm64: https://github.com/VirtusLab/scala-cli/issues/4069\n    test(\"create secret\")(TestUtil.retryOnCi()(createSecret()))\n\n}\n\nobject GitHubTests {\n\n  final case class PublicKey(\n    key_id: String,\n    key: String\n  )\n\n  implicit val publicKeyCodec: JsonValueCodec[PublicKey] =\n    JsonCodecMaker.make\n\n  final case class EncryptedSecret(\n    encrypted_value: String,\n    key_id: String\n  ) {\n    def encrypted: Array[Byte] =\n      Base64.getDecoder.decode(encrypted_value)\n  }\n\n  implicit val encryptedSecretCodec: JsonValueCodec[EncryptedSecret] =\n    JsonCodecMaker.make\n\n  private def condaLibsodiumVersion = Constants.condaLibsodiumVersion\n\n  // Warning: somehow also in settings.sc in the build, and in FetchExternalBinary\n  lazy val condaPlatform: String = {\n    val mambaOs =\n      if (Properties.isWin) \"win\"\n      else if (Properties.isMac) \"osx\"\n      else if (Properties.isLinux) \"linux\"\n      else sys.error(s\"Unsupported mamba OS: ${sys.props(\"os.name\")}\")\n    val arch      = sys.props(\"os.arch\").toLowerCase(Locale.ROOT)\n    val mambaArch = arch match {\n      case \"x86_64\" | \"amd64\"  => \"64\"\n      case \"arm64\" | \"aarch64\" => \"arm64\"\n      case \"ppc64le\"           => \"ppc64le\"\n      case _                   =>\n        sys.error(s\"Unsupported mamba architecture: $arch\")\n    }\n    s\"$mambaOs-$mambaArch\"\n  }\n\n  private def archiveUrlAndPath() = {\n    val suffix = condaPlatform match {\n      case \"linux-64\"      => \"-h36c2ea0_1\"\n      case \"linux-aarch64\" => \"-hb9de7d4_1\"\n      case \"osx-64\"        => \"-hbcb3906_1\"\n      case \"osx-arm64\"     => \"-h27ca646_1\"\n      case \"win-64\"        => \"-h62dcd97_1\"\n      case other           => sys.error(s\"Unrecognized conda platform $other\")\n    }\n    val relPath = condaPlatform match {\n      case \"linux-64\"      => os.rel / \"lib\" / \"libsodium.so\"\n      case \"linux-aarch64\" => os.rel / \"lib\" / \"libsodium.so\"\n      case \"osx-64\"        => os.rel / \"lib\" / \"libsodium.dylib\"\n      case \"osx-arm64\"     => os.rel / \"lib\" / \"libsodium.dylib\"\n      case \"win-64\"        => os.rel / \"Library\" / \"bin\" / \"libsodium.dll\"\n      case other           => sys.error(s\"Unrecognized conda platform $other\")\n    }\n    (\n      s\"https://anaconda.org/conda-forge/libsodium/$condaLibsodiumVersion/download/$condaPlatform/libsodium-$condaLibsodiumVersion$suffix.tar.bz2\",\n      relPath\n    )\n  }\n\n  private def initSodium(): Unit = {\n    val (url, relPath) = archiveUrlAndPath()\n    val archiveCache   = ArchiveCache()\n    val dir            = archiveCache.get(Artifact(url)).unsafeRun()(archiveCache.cache.ec)\n      .fold(e => throw new Exception(e), os.Path(_, os.pwd))\n    val lib = dir / relPath\n    System.load(lib.toString)\n\n    LoadLibrary.initializeFromResources()\n    Sodium.init()\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/HadoopTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass HadoopTests extends munit.FunSuite {\n  protected lazy val extraOptions: Seq[String] = TestUtil.extraOptions\n\n  for {\n    withTestScope <- Seq(true, false)\n    scopeDescription = if (withTestScope) \"test scope\" else \"main scope\"\n    inputPath        =\n      if (withTestScope) os.rel / \"test\" / \"WordCount.java\" else os.rel / \"main\" / \"WordCount.java\"\n    directiveKey = if (withTestScope) \"test.dep\" else \"dep\"\n    scopeOptions = if (withTestScope) Seq(\"--test\") else Nil\n  }\n    test(s\"simple map-reduce ($scopeDescription)\") {\n      TestUtil.retryOnCi() {\n        val inputs = TestInputs(\n          inputPath ->\n            s\"\"\"//> using $directiveKey org.apache.hadoop:hadoop-client-api:3.3.3\n               |\n               |// from https://hadoop.apache.org/docs/r3.3.3/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html\n               |\n               |package foo;\n               |\n               |import java.io.IOException;\n               |import java.util.StringTokenizer;\n               |\n               |import org.apache.hadoop.conf.Configuration;\n               |import org.apache.hadoop.fs.Path;\n               |import org.apache.hadoop.io.IntWritable;\n               |import org.apache.hadoop.io.Text;\n               |import org.apache.hadoop.mapreduce.Job;\n               |import org.apache.hadoop.mapreduce.Mapper;\n               |import org.apache.hadoop.mapreduce.Reducer;\n               |import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;\n               |import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;\n               |\n               |public class WordCount {\n               |\n               |  public static class TokenizerMapper\n               |       extends Mapper<Object, Text, Text, IntWritable>{\n               |\n               |    private final static IntWritable one = new IntWritable(1);\n               |    private Text word = new Text();\n               |\n               |    public void map(Object key, Text value, Context context\n               |                    ) throws IOException, InterruptedException {\n               |      StringTokenizer itr = new StringTokenizer(value.toString());\n               |      while (itr.hasMoreTokens()) {\n               |        word.set(itr.nextToken());\n               |        context.write(word, one);\n               |      }\n               |    }\n               |  }\n               |\n               |  public static class IntSumReducer\n               |       extends Reducer<Text,IntWritable,Text,IntWritable> {\n               |    private IntWritable result = new IntWritable();\n               |\n               |    public void reduce(Text key, Iterable<IntWritable> values,\n               |                       Context context\n               |                       ) throws IOException, InterruptedException {\n               |      int sum = 0;\n               |      for (IntWritable val : values) {\n               |        sum += val.get();\n               |      }\n               |      result.set(sum);\n               |      context.write(key, result);\n               |    }\n               |  }\n               |\n               |  public static void main(String[] args) throws Exception {\n               |    Configuration conf = new Configuration();\n               |    Job job = Job.getInstance(conf, \"word count\");\n               |    job.setJarByClass(WordCount.class);\n               |    job.setMapperClass(TokenizerMapper.class);\n               |    job.setCombinerClass(IntSumReducer.class);\n               |    job.setReducerClass(IntSumReducer.class);\n               |    job.setOutputKeyClass(Text.class);\n               |    job.setOutputValueClass(IntWritable.class);\n               |    FileInputFormat.addInputPath(job, new Path(args[0]));\n               |    FileOutputFormat.setOutputPath(job, new Path(args[1]));\n               |    System.exit(job.waitForCompletion(true) ? 0 : 1);\n               |  }\n               |}\n               |\"\"\".stripMargin\n        )\n        inputs.fromRoot { root =>\n          val res = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"run\",\n            TestUtil.extraOptions,\n            \".\",\n            \"--hadoop\",\n            \"--command\",\n            \"--scratch-dir\",\n            \"tmp\",\n            scopeOptions,\n            \"--\",\n            \"foo\"\n          )\n            .call(cwd = root)\n          val command = res.out.lines()\n          pprint.err.log(command)\n          expect(command.take(2) == Seq(\"hadoop\", \"jar\"))\n          expect(command.takeRight(2) == Seq(\"foo.WordCount\", \"foo\"))\n        }\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/HelpTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass HelpTests extends ScalaCliSuite {\n  for (helpOptions <- HelpTests.variants) {\n    lazy val help         = os.proc(TestUtil.cli, helpOptions).call(check = false)\n    lazy val helpOutput   = help.out.trim()\n    val helpOptionsString = helpOptions.mkString(\" \")\n    test(s\"$helpOptionsString works correctly\") {\n      assert(\n        help.exitCode == 0,\n        clues(helpOptions, help.out.text(), help.err.text(), help.exitCode)\n      )\n      expect(helpOutput.contains(\"Usage:\"))\n    }\n\n    test(s\"$helpOptionsString output command groups are ordered correctly\") {\n      val mainCommandsIndex  = helpOutput.indexOf(\"Main commands:\")\n      val miscellaneousIndex = helpOutput.indexOf(\"Miscellaneous commands:\")\n      expect(mainCommandsIndex < miscellaneousIndex)\n      expect(miscellaneousIndex < helpOutput.indexOf(\"Other commands:\"))\n    }\n\n    test(s\"$helpOptionsString output includes launcher options\") {\n      expect(helpOutput.contains(\"--power\"))\n    }\n\n    test(s\"$helpOptionsString output does not include legacy scala runner options\") {\n      expect(!helpOutput.contains(\"Legacy Scala runner options\"))\n    }\n\n    test(s\"$helpOptionsString output includes external help options\") {\n      expect(helpOutput.contains(\"--scalac-help\"))\n      expect(helpOutput.contains(\"--help-js\"))\n      expect(helpOutput.contains(\"--help-native\"))\n      expect(helpOutput.contains(\"--help-doc\"))\n      expect(helpOutput.contains(\"--help-repl\"))\n      expect(helpOutput.contains(\"--help-fmt\"))\n    }\n  }\n\n  for (fullHelpOptions <- HelpTests.fullHelpVariants) {\n    lazy val fullHelp         = os.proc(TestUtil.cli, fullHelpOptions).call(check = false)\n    lazy val fullHelpOutput   = fullHelp.out.trim()\n    val fullHelpOptionsString = fullHelpOptions.mkString(\" \")\n    test(s\"$fullHelpOptionsString works correctly\") {\n      assert(\n        fullHelp.exitCode == 0,\n        clues(fullHelpOptions, fullHelp.out.text(), fullHelp.err.text(), fullHelp.exitCode)\n      )\n      expect(fullHelpOutput.contains(\"Usage:\"))\n    }\n    test(s\"$fullHelpOptionsString output includes legacy scala runner options\") {\n      expect(fullHelpOutput.contains(\"Legacy Scala runner options\"))\n    }\n  }\n\n  test(\"name aliases limited for standard help\") {\n    val help       = os.proc(TestUtil.cli, \"run\", \"--help\").call()\n    val helpOutput = help.out.trim()\n\n    expect(TestUtil.removeAnsiColors(helpOutput).contains(\n      \"--jar, --extra-jars paths\"\n    ))\n  }\n\n  test(\"name aliases not limited for full help\") {\n    val help       = os.proc(TestUtil.cli, \"run\", \"--full-help\").call()\n    val helpOutput = help.out.trim()\n    expect(TestUtil.removeAnsiColors(helpOutput).contains(\n      \"-cp, --jar, --jars, --class, --classes, -classpath, --extra-jar, --classpath, --extra-jars, --class-path, --extra-class, --extra-classes, --extra-class-path paths\"\n    ))\n  }\n  for {\n    (subcommandLabel, leadArgs) <- Seq(\n      (\"compile subcommand\", Seq(\"compile\")),\n      (\"default subcommand\", Seq.empty)\n    )\n  } test(s\"-opt-inline:help works without inputs ($subcommandLabel) (Scala 3.8.3+)\") {\n    TestInputs.empty.fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, leadArgs, \"-S\", Constants.scala3Next, \"-opt-inline:help\")\n          .call(cwd = root, mergeErrIntoOut = true, check = false)\n      expect(res.exitCode == 0)\n      val out = res.out.text()\n      expect(out.nonEmpty)\n      expect(out.contains(\"Inlining requires\"))\n    }\n  }\n\n  for (withPower <- Seq(true, false))\n    test(\"envs help\" + (if (withPower) \" with power\" else \"\")) {\n      val powerOptions = if (withPower) Seq(\"--power\") else Nil\n      val help         = os.proc(TestUtil.cli, \"--envs-help\", powerOptions).call()\n      val helpOutput   = help.out.trim()\n      if (!withPower) expect(!helpOutput.contains(\"(power)\"))\n      expect(helpOutput.nonEmpty)\n      expect(helpOutput.contains(\"environment variables\"))\n    }\n}\n\nobject HelpTests {\n  val variants =\n    Seq(\n      Seq(\"help\"),\n      Seq(\"help\", \"-help\"),\n      Seq(\"help\", \"--help\"),\n      Seq(\"-help\"),\n      Seq(\"--help\")\n    )\n  val fullHelpVariants =\n    Seq(\n      Seq(\"help\", \"--full-help\"),\n      Seq(\"help\", \"-full-help\"),\n      Seq(\"help\", \"--help-full\"),\n      Seq(\"help\", \"-help-full\"),\n      Seq(\"--full-help\"),\n      Seq(\"-full-help\"),\n      Seq(\"-help-full\")\n    )\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/InstallAndUninstallCompletionsTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.paths.shaded.dirs.ProjectDirectories\n\nimport scala.util.Properties\n\nclass InstallAndUninstallCompletionsTests extends ScalaCliSuite {\n  val zshRcFile: String  = \".zshrc\"\n  val bashRcFile: String = \".bashrc\"\n  val fishRcFile: String = \"config.fish\"\n  val rcContent: String  = s\"\"\"\n                              |dummy line\n                              |dummy line\"\"\".stripMargin\n  val testInputs: TestInputs = TestInputs(\n    os.rel / zshRcFile                       -> rcContent,\n    os.rel / bashRcFile                      -> rcContent,\n    os.rel / \".config\" / \"fish\" / fishRcFile -> rcContent\n  )\n\n  def runInstallAndUninstallCompletions(): Unit = {\n    testInputs.fromRoot { root =>\n      val zshRcPath  = root / zshRcFile\n      val bashRcPath = root / bashRcFile\n      val fishRcPath = root / \".config\" / \"fish\" / fishRcFile\n      // install completions to the dummy rc files\n      os.proc(TestUtil.cli, \"install-completions\", \"--rc-file\", zshRcPath, \"--shell\", \"zsh\").call(\n        cwd = root\n      )\n      os.proc(TestUtil.cli, \"install-completions\", \"--rc-file\", bashRcPath, \"--shell\", \"bash\").call(\n        cwd = root\n      )\n      os.proc(TestUtil.cli, \"install-completions\", \"--rc-file\", fishRcPath, \"--shell\", \"fish\").call(\n        cwd = root\n      )\n      expect(os.read(bashRcPath).contains(bashRcScript))\n      expect(os.read(zshRcPath).contains(zshRcScript))\n      expect(os.read(fishRcPath).contains(fishRcScript))\n      // uninstall completions from the dummy rc files\n      os.proc(TestUtil.cli, \"uninstall-completions\", \"--rc-file\", zshRcPath).call(cwd = root)\n      os.proc(TestUtil.cli, \"uninstall-completions\", \"--rc-file\", bashRcPath).call(cwd = root)\n      os.proc(TestUtil.cli, \"uninstall-completions\", \"--rc-file\", fishRcPath).call(cwd = root)\n      expect(os.read(zshRcPath) == rcContent)\n      expect(os.read(bashRcPath) == rcContent)\n      expect(os.read(fishRcPath) == rcContent)\n    }\n  }\n\n  def isWinShell: Boolean = Option(System.getenv(\"OSTYPE\")).nonEmpty\n  if (!Properties.isWin || isWinShell)\n    test(\"installing and uninstalling completions\") {\n      runInstallAndUninstallCompletions()\n    }\n\n  lazy val bashRcScript: String = {\n    val progName = \"scala-cli\"\n    val ifs      = \"\\\\n\"\n    val script   =\n      s\"\"\"_${progName}_completions() {\n         |  local IFS=$$'$ifs'\n         |  eval \"$$($progName complete bash-v1 \"$$(( $$COMP_CWORD + 1 ))\" \"$${COMP_WORDS[@]}\")\"\n         |}\n         |\n         |complete -F _${progName}_completions $progName\n         |\"\"\".stripMargin\n    addTags(script)\n  }\n\n  lazy val fishRcScript: String = {\n    val progName = \"scala-cli\"\n    val script   =\n      s\"\"\"complete $progName -a '($progName complete fish-v1 (math 1 + (count (__fish_print_cmd_args))) (__fish_print_cmd_args))'\"\"\"\n    addTags(script)\n  }\n\n  lazy val zshRcScript: String = {\n    val projDirs = ProjectDirectories.from(null, null, \"ScalaCli\")\n    val dir      = os.Path(projDirs.dataLocalDir, TestUtil.pwd) / \"completions\" / \"zsh\"\n    val script   = Seq(\n      s\"\"\"fpath=(\"$dir\" $$fpath)\"\"\",\n      \"compinit\"\n    ).map(_ + System.lineSeparator()).mkString\n    addTags(script)\n  }\n\n  def addTags(script: String): String = {\n    val start    = \"# >>> scala-cli completions >>>\\n\"\n    val end      = \"# <<< scala-cli completions <<<\\n\"\n    val withTags = \"\\n\" + start + script.stripSuffix(\"\\n\") + \"\\n\" + end\n    withTags\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/InstallHomeTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass InstallHomeTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  val firstVersion            = \"0.0.1\"\n  val secondVersion           = \"0.0.2\"\n  val dummyScalaCliFirstName  = \"DummyScalaCli-1.scala\"\n  val dummyScalaCliSecondName = \"DummyScalaCli-2.scala\"\n  val dummyScalaCliBinName    = \"scala-cli-dummy-test\"\n  val testInputs: TestInputs  = TestInputs(\n    os.rel / dummyScalaCliFirstName ->\n      s\"\"\"\n         |object DummyScalaCli extends App {\n         |  println(\\\"$firstVersion\\\")\n         |}\"\"\".stripMargin,\n    os.rel / dummyScalaCliSecondName ->\n      s\"\"\"\n         |object DummyScalaCli extends App {\n         |  println(\\\"$secondVersion\\\")\n         |}\"\"\".stripMargin\n  )\n\n  private def packageDummyScalaCli(root: os.Path, dummyScalaCliFileName: String, output: String) = {\n    val cmd = Seq[os.Shellable](\n      TestUtil.cli,\n      \"--power\",\n      \"package\",\n      dummyScalaCliFileName,\n      \"-o\",\n      output\n    )\n    os.proc(cmd).call(\n      cwd = root,\n      stdin = os.Inherit,\n      stdout = os.Inherit\n    )\n  }\n\n  private def installScalaCli(\n    root: os.Path,\n    binVersion: String,\n    binDirPath: os.Path,\n    force: Boolean\n  ) = {\n    val cmdInstallVersion = Seq[os.Shellable](\n      TestUtil.cli,\n      \"install-home\",\n      \"--env\",\n      \"--scala-cli-binary-path\",\n      binVersion,\n      \"--binary-name\",\n      dummyScalaCliBinName,\n      \"--bin-dir\",\n      binDirPath\n    ) ++ (if (force) Seq[os.Shellable](\"--force\") else Seq.empty)\n    os.proc(cmdInstallVersion).call(\n      cwd = root,\n      stdin = os.Inherit,\n      stdout = os.Inherit\n    )\n  }\n\n  private def uninstallScalaCli(\n    root: os.Path,\n    binDirPath: os.Path,\n    force: Boolean,\n    skipCache: Boolean\n  ) = {\n    val cmdUninstall = Seq[os.Shellable](\n      TestUtil.cli,\n      \"uninstall\",\n      \"--binary-name\",\n      dummyScalaCliBinName,\n      \"--bin-dir\",\n      binDirPath\n    )\n    val forceOpts     = if (force) Seq(\"--force\") else Seq.empty\n    val skipCacheOpts = if (skipCache) Seq(\"--skip-cache\") else Seq.empty\n    os.proc(cmdUninstall, forceOpts, skipCacheOpts).call(cwd = root)\n  }\n\n  def runInstallHome(): Unit = {\n\n    testInputs.fromRoot { root =>\n      val binDirPath = root / Constants.workspaceDirName / \"scala-cli\"\n\n      val binDummyScalaCliFirst  = dummyScalaCliFirstName.stripSuffix(\".scala\").toLowerCase\n      val binDummyScalaCliSecond = dummyScalaCliSecondName.stripSuffix(\".scala\").toLowerCase\n\n      packageDummyScalaCli(root, dummyScalaCliFirstName, binDummyScalaCliFirst)\n      packageDummyScalaCli(root, dummyScalaCliSecondName, binDummyScalaCliSecond)\n\n      // install 1 version\n      installScalaCli(root, binDummyScalaCliFirst, binDirPath, force = true)\n\n      val v1Install = os.proc(binDirPath / dummyScalaCliBinName).call(\n        cwd = root,\n        stdin = os.Inherit\n      ).out.trim()\n      expect(v1Install == firstVersion)\n\n      // update to 2 version\n      installScalaCli(root, binDummyScalaCliSecond, binDirPath, force = false)\n\n      val v2Update = os.proc(binDirPath / dummyScalaCliBinName).call(\n        cwd = root,\n        stdin = os.Inherit\n      ).out.trim()\n      expect(v2Update == secondVersion)\n\n      // downgrade to 1 version with force\n      installScalaCli(root, binDummyScalaCliFirst, binDirPath, force = true)\n\n      val v1Downgrade = os.proc(binDirPath / dummyScalaCliBinName).call(\n        cwd = root,\n        stdin = os.Inherit\n      ).out.trim()\n      expect(v1Downgrade == firstVersion)\n\n      uninstallScalaCli(root, binDirPath, force = true, skipCache = true)\n      expect(!os.exists(binDirPath))\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\n      \"updating and downgrading dummy scala-cli using install-home command, uninstalling scala-cli using uninstall command\"\n    ) {\n      runInstallHome()\n    }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/JmhSuite.scala",
    "content": "package scala.cli.integration\n\ntrait JmhSuite { this: ScalaCliSuite =>\n  protected def simpleBenchmarkingInputs(directivesString: String = \"\"): TestInputs = TestInputs(\n    os.rel / \"benchmark.scala\" ->\n      s\"\"\"$directivesString\n         |package bench\n         |\n         |import java.util.concurrent.TimeUnit\n         |import org.openjdk.jmh.annotations._\n         |\n         |@BenchmarkMode(Array(Mode.AverageTime))\n         |@OutputTimeUnit(TimeUnit.NANOSECONDS)\n         |@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)\n         |@Measurement(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)\n         |@Fork(0)\n         |class Benchmarks {\n         |\n         |  @Benchmark\n         |  def foo(): Unit = {\n         |    (1L to 10000000L).sum\n         |  }\n         |\n         |}\n         |\"\"\".stripMargin\n  )\n  protected lazy val expectedInBenchmarkingOutput = \"\"\"Result \"bench.Benchmarks.foo\":\"\"\"\n  protected lazy val exampleOldJmhVersion         = \"1.29\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/JmhTests.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.file.Files\n\nimport scala.cli.integration.TestUtil.await\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.Future\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nclass JmhTests extends ScalaCliSuite with JmhSuite with BspSuite {\n  override def group: ScalaCliSuite.TestGroup      = ScalaCliSuite.TestGroup.First\n  override protected val extraOptions: Seq[String] = TestUtil.extraOptions\n\n  for {\n    useDirective <- Seq(None, Some(\"//> using jmh\"))\n    directiveString = useDirective.getOrElse(\"\")\n    jmhOptions      = if (useDirective.isEmpty) Seq(\"--jmh\") else Nil\n    testMessage     = useDirective match {\n      case None            => jmhOptions.mkString(\" \")\n      case Some(directive) => directive\n    }\n  } {\n    test(s\"run ($testMessage)\") {\n      // TODO extract running benchmarks to a separate scope, or a separate sub-command\n      simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"--power\", extraOptions, \".\", jmhOptions).call(cwd = root)\n        val output = res.out.trim()\n        expect(output.contains(expectedInBenchmarkingOutput))\n        expect(output.contains(s\"JMH version: ${Constants.jmhVersion}\"))\n      }\n    }\n\n    test(s\"compile ($testMessage)\") {\n      simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n        os.proc(TestUtil.cli, \"compile\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root)\n      }\n    }\n\n    test(s\"doc ($testMessage)\") {\n      simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"doc\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        expect(!res.err.trim().contains(\"Error\"))\n      }\n    }\n\n    test(s\"setup-ide ($testMessage)\") {\n      // TODO fix setting jmh via a reload & add tests for it\n      simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n        os.proc(TestUtil.cli, \"setup-ide\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root)\n      }\n    }\n\n    test(s\"bsp ($testMessage)\") {\n      withBsp(simpleBenchmarkingInputs(directiveString), Seq(\".\", \"--power\") ++ jmhOptions) {\n        (_, _, remoteServer) =>\n          Future {\n            val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n            val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n            expect(targets.length == 2)\n\n            val compileResult =\n              remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n            expect(compileResult.getStatusCode == b.StatusCode.OK)\n\n          }\n      }\n    }\n\n    test(s\"setup-ide + bsp ($testMessage)\") {\n      val inputs = simpleBenchmarkingInputs(directiveString)\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"setup-ide\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root)\n        val ideOptionsPath = root / Constants.workspaceDirName / \"ide-options-v2.json\"\n        expect(ideOptionsPath.toNIO.toFile.exists())\n        val ideLauncherOptsPath = root / Constants.workspaceDirName / \"ide-launcher-options.json\"\n        expect(ideLauncherOptsPath.toNIO.toFile.exists())\n        val ideEnvsPath = root / Constants.workspaceDirName / \"ide-envs.json\"\n        expect(ideEnvsPath.toNIO.toFile.exists())\n        val jsonOptions = List(\n          \"--json-options\",\n          ideOptionsPath.toString,\n          \"--json-launcher-options\",\n          ideLauncherOptsPath.toString,\n          \"--envs-file\",\n          ideEnvsPath.toString\n        )\n        withBsp(inputs, Seq(\".\"), bspOptions = jsonOptions, reuseRoot = Some(root)) {\n          (_, _, remoteServer) =>\n            Future {\n              val buildTargetsResp = remoteServer.workspaceBuildTargets().asScala.await\n              val targets          = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq\n              expect(targets.length == 2)\n\n              val compileResult =\n                remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala.await\n              expect(compileResult.getStatusCode == b.StatusCode.OK)\n            }\n        }\n      }\n    }\n\n    test(s\"package ($testMessage)\") {\n      // TODO make package with --jmh build an artifact that actually runs benchmarks\n      val expectedMessage = \"Placeholder main method\"\n      simpleBenchmarkingInputs(directiveString)\n        .add(os.rel / \"Main.scala\" -> s\"\"\"@main def main: Unit = println(\"$expectedMessage\")\"\"\")\n        .fromRoot { root =>\n          val launcherName = {\n            val ext = if (Properties.isWin) \".bat\" else \"\"\n            \"launcher\" + ext\n          }\n          os.proc(\n            TestUtil.cli,\n            \"package\",\n            \"--power\",\n            TestUtil.extraOptions,\n            \".\",\n            jmhOptions,\n            \"-o\",\n            launcherName\n          )\n            .call(cwd = root)\n          val launcher = root / launcherName\n          expect(os.isFile(launcher))\n          expect(Files.isExecutable(launcher.toNIO))\n          val output = TestUtil.maybeUseBash(launcher)(cwd = root).out.trim()\n          expect(output == expectedMessage)\n        }\n    }\n\n    test(s\"export ($testMessage)\") {\n      simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n        // TODO add proper support for JMH export, we're checking if it doesn't fail the command for now\n        os.proc(TestUtil.cli, \"export\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root)\n      }\n    }\n  }\n\n  for {\n    useDirective <- Seq(None, Some(\"//> using jmh false\"))\n    directiveString = useDirective.getOrElse(\"\")\n    jmhOptions      = if (useDirective.isEmpty) Seq(\"--jmh=false\") else Nil\n    testMessage     = useDirective match {\n      case None             => jmhOptions.mkString(\" \")\n      case Some(directives) => directives.linesIterator.mkString(\"; \")\n    }\n    if !Properties.isWin\n  } test(s\"should not compile when jmh is explicitly disabled ($testMessage)\") {\n    simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, \"compile\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root, check = false)\n      expect(res.exitCode == 1)\n    }\n  }\n\n  for {\n    useDirective <- Seq(\n      None,\n      Some(\n        s\"\"\"//> using jmh\n           |//> using jmhVersion $exampleOldJmhVersion\n           |\"\"\".stripMargin\n      )\n    )\n    directiveString = useDirective.getOrElse(\"\")\n    jmhOptions      =\n      if (useDirective.isEmpty) Seq(\"--jmh\", \"--jmh-version\", exampleOldJmhVersion) else Nil\n    testMessage = useDirective match {\n      case None             => jmhOptions.mkString(\" \")\n      case Some(directives) => directives.linesIterator.mkString(\"; \")\n    }\n    if !Properties.isWin\n  } test(s\"should use the passed jmh version ($testMessage)\") {\n    simpleBenchmarkingInputs(directiveString).fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, \"run\", \"--power\", extraOptions, \".\", jmhOptions)\n          .call(cwd = root)\n      val output = res.out.trim()\n      expect(output.contains(expectedInBenchmarkingOutput))\n      expect(output.contains(s\"JMH version: $exampleOldJmhVersion\"))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/LegacyScalaRunnerTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\ntrait LegacyScalaRunnerTestDefinitions { this: DefaultTests =>\n  test(\"default to the run sub-command when a script snippet is passed with -e\") {\n    TestInputs.empty.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(TestUtil.cli, \"-e\", s\"println($quotation$msg$quotation)\", TestUtil.extraOptions)\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"running scala-cli with a script snippet passed with -e shouldn't allow repl-only options\") {\n    TestInputs.empty.fromRoot { root =>\n      val replSpecificOption = \"--repl-dry-run\"\n      val res                =\n        os.proc(\n          TestUtil.cli,\n          \"-e\",\n          \"println()\",\n          replSpecificOption,\n          TestUtil.extraOptions\n        )\n          .call(cwd = root, mergeErrIntoOut = true, check = false)\n      expect(res.exitCode == 1)\n      expect(res.out.lines().endsWith(unrecognizedArgMessage(replSpecificOption)))\n    }\n  }\n\n  test(\"ensure -save/--save works with the default command\") {\n    simpleLegacyOptionBackwardsCompatTest(\"-save\", \"--save\")\n  }\n\n  test(\"ensure -nosave/--nosave works with the default command\") {\n    simpleLegacyOptionBackwardsCompatTest(\"-nosave\", \"--nosave\")\n  }\n\n  test(\"ensure -howtorun/--how-to-run works with the default command\") {\n    legacyOptionBackwardsCompatTest(\"-howtorun\", \"--how-to-run\") {\n      (legacyHtrOption, inputFile, root) =>\n        Seq(\"object\", \"script\", \"jar\", \"repl\", \"guess\", \"invalid\").foreach { htrValue =>\n          val res =\n            os.proc(TestUtil.cli, legacyHtrOption, htrValue, inputFile, TestUtil.extraOptions)\n              .call(cwd = root, stderr = os.Pipe)\n          expect(res.err.trim().contains(deprecatedOptionWarning(legacyHtrOption)))\n          expect(res.err.trim().contains(htrValue))\n        }\n    }\n  }\n\n  test(\"ensure -I works with the default command\") {\n    legacyOptionBackwardsCompatTest(\"-I\") {\n      (legacyOption, inputFile, root) =>\n        val anotherInputFile = \"smth.scala\"\n        val res              = os.proc(\n          TestUtil.cli,\n          legacyOption,\n          inputFile,\n          legacyOption,\n          anotherInputFile,\n          \"--repl-dry-run\",\n          TestUtil.extraOptions\n        )\n          .call(cwd = root, stderr = os.Pipe)\n        expect(res.err.trim().contains(deprecatedOptionWarning(legacyOption)))\n        expect(res.err.trim().contains(inputFile))\n        expect(res.err.trim().contains(anotherInputFile))\n    }\n  }\n\n  test(\"ensure -nc/-nocompdaemon/--no-compilation-daemon works with the default command\") {\n    simpleLegacyOptionBackwardsCompatTest(\"-nc\", \"-nocompdaemon\", \"--no-compilation-daemon\")\n  }\n\n  test(\"ensure -run works with the default command\") {\n    legacyOptionBackwardsCompatTest(\"-run\") {\n      (legacyOption, inputFile, root) =>\n        val res = os.proc(TestUtil.cli, legacyOption, inputFile, \".\", TestUtil.extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        expect(res.err.trim().contains(deprecatedOptionWarning(legacyOption)))\n        expect(res.err.trim().contains(inputFile))\n    }\n  }\n\n  test(\"ensure -Yscriptrunner works with the default command\") {\n    legacyOptionBackwardsCompatTest(\"-Yscriptrunner\") {\n      (legacyOption, inputFile, root) =>\n        Seq(\"default\", \"resident\", \"shutdown\", \"scala.tools.nsc.fsc.ResidentScriptRunner\").foreach {\n          legacyOptionValue =>\n            val res =\n              os.proc(\n                TestUtil.cli,\n                legacyOption,\n                legacyOptionValue,\n                inputFile,\n                TestUtil.extraOptions\n              )\n                .call(cwd = root, stderr = os.Pipe)\n            expect(res.err.trim().contains(deprecatedOptionWarning(legacyOption)))\n            expect(res.err.trim().contains(legacyOptionValue))\n        }\n    }\n  }\n\n  private def simpleLegacyOptionBackwardsCompatTest(optionAliases: String*): Unit =\n    abstractLegacyOptionBackwardsCompatTest(optionAliases) {\n      (legacyOption, expectedMsg, _, root) =>\n        val res = os.proc(TestUtil.cli, legacyOption, \"s.sc\", TestUtil.extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        expect(res.out.trim() == expectedMsg)\n        expect(res.err.trim().contains(deprecatedOptionWarning(legacyOption)))\n    }\n\n  private def legacyOptionBackwardsCompatTest(optionAliases: String*)(f: (\n    String,\n    String,\n    os.Path\n  ) => Unit): Unit =\n    abstractLegacyOptionBackwardsCompatTest(optionAliases) { (legacyOption, _, inputFile, root) =>\n      f(legacyOption, inputFile, root)\n    }\n\n  private def abstractLegacyOptionBackwardsCompatTest(optionAliases: Seq[String])(f: (\n    String,\n    String,\n    String,\n    os.Path\n  ) => Unit): Unit = {\n    val msg       = \"Hello world\"\n    val inputFile = \"s.sc\"\n    TestInputs(os.rel / inputFile -> s\"\"\"println(\"$msg\")\"\"\").fromRoot { root =>\n      optionAliases.foreach(f(_, msg, inputFile, root))\n    }\n  }\n\n  private def deprecatedOptionWarning(optionName: String) =\n    s\"Deprecated option '$optionName' is ignored\"\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/LoggingTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass LoggingTests extends ScalaCliSuite {\n  test(\"single -q should not suppresses compilation errors\") {\n    TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(\"Hello\"\n           |}\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, \".\", \"-q\").call(cwd = root, check = false, mergeErrIntoOut = true)\n      val output = res.out.trim()\n      expect(output.contains(\"Hello.scala:3:1\"))\n      expect(output.contains(\"error\"))\n    }\n  }\n  test(\"single -q should not suppresses output from app\") {\n    TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(\"Hello\")\n           |}\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val res    = os.proc(TestUtil.cli, \".\", \"-q\").call(cwd = root)\n      val output = res.out.trim()\n      expect(output.contains(\"Hello\"))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/MarkdownTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass MarkdownTests extends ScalaCliSuite {\n  test(\"run a simple .md file with a scala script snippet\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |A simple scala script snippet.\n           |```scala\n           |println(\"$expectedOutput\")\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run a simple .md file with a scala raw snippet\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |A simple scala raw snippet.\n           |```scala raw\n           |object Hello extends App {\n           |  println(\"$expectedOutput\")\n           |}\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run a simple .md file with multiple interdependent scala snippets\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |## 1\n           |message case class\n           |```scala\n           |case class Message(value: String)\n           |```\n           |## 2\n           |message declaration\n           |```scala\n           |val message = Message(\"$expectedOutput\")\n           |```\n           |\n           |## 3\n           |output\n           |```scala\n           |println(message.value)\n           |```\n           |\n           |##\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run a simple .md file with multiple scala snippets resetting the context\") {\n    val msg1           = \"Hello\"\n    val msg2           = \"world\"\n    val expectedOutput = s\"$msg1 $msg2\"\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |## Scope 0\n           |```scala\n           |val msg = \"$msg1\"\n           |```\n           |\n           |## Scope 1\n           |```scala reset\n           |val msg = \" \"\n           |```\n           |\n           |## Scope 2\n           |```scala reset\n           |val msg = \"$msg2\"\n           |```\n           |\n           |## Output\n           |```scala reset\n           |val msg = Scope.msg + Scope1.msg + Scope2.msg\n           |println(msg)\n           |```\n           |##\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run markdown alongside other sources\") {\n    val msg1           = \"Hello\"\n    val msg2           = \" \"\n    val msg3           = \"world\"\n    val msg4           = \"!\"\n    val expectedOutput = msg1 + msg2 + msg3 + msg4\n    TestInputs(\n      os.rel / \"ScalaMessage.scala\" -> \"case class ScalaMessage(value: String)\",\n      os.rel / \"JavaMessage.java\"   ->\n        \"\"\"public class JavaMessage {\n          |  public String value;\n          |  public JavaMessage(String value) {\n          |    this.value = value;\n          |  }\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"scripts\" / \"script.sc\" -> \"case class ScriptMessage(value: String)\",\n      os.rel / \"Main.md\"               ->\n        s\"\"\"# Main\n           |Run it all from a snippet.\n           |```scala\n           |val javaMsg = new JavaMessage(\"$msg1\")\n           |val scalaMsg = ScalaMessage(\"$msg2\")\n           |val snippetMsg = snippet.SnippetMessage(\"$msg3\")\n           |val scriptMsg = scripts.script.ScriptMessage(\"$msg4\")\n           |val msg = javaMsg.value + scalaMsg.value + snippetMsg.value + scriptMsg.value\n           |println(msg)\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val snippetCode = \"case class SnippetMessage(value: String)\"\n      val result      =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \".\",\n          \"-e\",\n          snippetCode,\n          \"--markdown\",\n          \"--main-class\",\n          \"Main_md\"\n        )\n          .call(cwd = root)\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n  test(\"run a simple .md file with a using directive in a scala script snippet\") {\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |A simple scala script snippet.\n           |```scala\n           |//> using dep com.lihaoyi::os-lib:0.8.1\n           |println(os.pwd)\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      expect(result.out.trim() == root.toString())\n    }\n  }\n\n  test(\"run a simple .md file with a using directive in a scala raw snippet\") {\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |A simple scala raw snippet.\n           |```scala raw\n           |//> using dep com.lihaoyi::os-lib:0.8.1\n           |object Hello extends App {\n           |  println(os.pwd)\n           |}\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      expect(result.out.trim() == root.toString())\n    }\n  }\n\n  test(\"run a simple .md file with multiple using directives spread across scala script snippets\") {\n    val msg1 = \"Hello\"\n    val msg2 = \"world\"\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |Let's try to spread the using directives into multiple simple `scala` code blocks of various kinds.\n           |\n           |## Circe\n           |Let's depend on `circe-parser` in this one.\n           |```scala\n           |//> using dep io.circe::circe-parser:0.14.3\n           |import io.circe._, io.circe.parser._\n           |val json = \\\"\\\"\\\"{ \"message\": \"$msg1\"}\\\"\\\"\\\"\n           |val parsed = parse(json).getOrElse(Json.Null)\n           |val msg = parsed.hcursor.downField(\"message\").as[String].getOrElse(\"\")\n           |println(msg)\n           |```\n           |\n           |## `pprint`\n           |And `pprint`, too.\n           |```scala\n           |//> using dep com.lihaoyi::pprint:0.8.0\n           |pprint.PPrinter.BlackWhite.pprintln(\"$msg2\")\n           |```\n           |\n           |## `os-lib`\n           |And then on `os-lib`, just because.\n           |And let's reset the scope for good measure, too.\n           |```scala reset\n           |//> using dep com.lihaoyi::os-lib:0.8.1\n           |val msg = os.pwd.toString\n           |println(msg)\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result         = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      val expectedOutput =\n        s\"\"\"$msg1\n           |\"$msg2\"\n           |$root\"\"\".stripMargin\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run a simple .md file with multiple using directives spread across scala raw snippets\") {\n    val msg1 = \"Hello\"\n    val msg2 = \"world\"\n    TestInputs(\n      os.rel / \"sample.md\" ->\n        s\"\"\"# Sample Markdown file\n           |Let's try to spread the using directives into multiple simple `scala` code blocks of various kinds.\n           |\n           |## Circe\n           |Let's depend on `circe-parser` in this one.\n           |```scala raw\n           |//> using dep io.circe::circe-parser:0.14.3\n           |\n           |object CirceSnippet {\n           |  import io.circe._, io.circe.parser._\n           |\n           |  def printStuff(): Unit = {\n           |    val json = \\\"\\\"\\\"{ \"message\": \"$msg1\"}\\\"\\\"\\\"\n           |    val parsed = parse(json).getOrElse(Json.Null)\n           |    val msg = parsed.hcursor.downField(\"message\").as[String].getOrElse(\"\")\n           |    println(msg)\n           |  }\n           |}\n           |```\n           |\n           |## `pprint`\n           |And `pprint`, too.\n           |```scala raw\n           |//> using dep com.lihaoyi::pprint:0.8.0\n           |object PprintSnippet {\n           |  def printStuff(): Unit =\n           |    pprint.PPrinter.BlackWhite.pprintln(\"$msg2\")\n           |}\n           |```\n           |\n           |## `os-lib`\n           |And then on `os-lib`, just because.\n           |And let's reset the scope for good measure, too.\n           |```scala raw\n           |//> using dep com.lihaoyi::os-lib:0.8.1\n           |\n           |object OsLibSnippet extends App {\n           |  CirceSnippet.printStuff()\n           |  PprintSnippet.printStuff()\n           |  println(os.pwd)\n           |}\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result         = os.proc(TestUtil.cli, \"sample.md\").call(cwd = root)\n      val expectedOutput =\n        s\"\"\"$msg1\n           |\"$msg2\"\n           |$root\"\"\".stripMargin\n      expect(result.out.trim() == expectedOutput)\n    }\n  }\n  test(\"source file name start with a number\") {\n    val fileNameWithNumber = \"01-intro.md\"\n    TestInputs(\n      os.rel / fileNameWithNumber ->\n        s\"\"\"# Introduction\n           |\n           |Welcome to the tutorial!\n           |\n           |```scala\n           |println(\"Hello\")\n           |```\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val result = os.proc(TestUtil.cli, fileNameWithNumber).call(cwd = root)\n      expect(result.out.trim() == \"Hello\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/MavenTestHelper.scala",
    "content": "package scala.cli.integration\n\ntrait MavenTestHelper {\n\n  protected def mavenCommand(args: String*): os.proc = os.proc(maven, args)\n\n  protected lazy val maven: os.Shellable =\n    Seq[os.Shellable](\n      \"mvn\",\n      \"clean\",\n      \"compile\"\n    )\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/MetaCheck.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass MetaCheck extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  /*\n   * We don't run tests with --scala 3.… any more, and only rely on those\n   * with no --scala option.\n   * The test here ensures the default version is indeed Scala 3.\n   */\n  test(\"Scala 3 is the default\") {\n    val testInputs = TestInputs(\n      os.rel / \"PrintScalaVersion.scala\" ->\n        \"\"\"// https://gist.github.com/romanowski/de14691cab7340134e197419bc48919a\n          |\n          |object PrintScalaVersion extends App {\n          |  def props(url: java.net.URL): java.util.Properties = {\n          |    val properties = new java.util.Properties()\n          |    val is = url.openStream()\n          |    try {\n          |      properties.load(is)\n          |      properties\n          |    } finally is.close()\n          |  }\n          |\n          |  def scala2Version: String =\n          |    props(getClass.getResource(\"/library.properties\")).getProperty(\"version.number\")\n          |\n          |  def checkScala3(res: java.util.Enumeration[java.net.URL]): String =\n          |    if (!res.hasMoreElements) scala2Version else {\n          |      val manifest = props(res.nextElement)\n          |      manifest.getProperty(\"Specification-Title\") match {\n          |        case \"scala3-library-bootstrapped\" =>\n          |          manifest.getProperty(\"Implementation-Version\")\n          |        case _ => checkScala3(res)\n          |      }\n          |    }\n          |  val manifests = getClass.getClassLoader.getResources(\"META-INF/MANIFEST.MF\")\n          |\n          |  val scalaVersion = checkScala3(manifests)\n          |\n          |  println(scalaVersion)\n          |}\n          |\"\"\".stripMargin\n    )\n    testInputs.fromRoot { root =>\n      // --ttl 0s so that we are sure we use the latest supported Scala versions listing\n      val res          = os.proc(TestUtil.cli, \".\", \"--ttl\", \"0s\").call(cwd = root)\n      val scalaVersion = res.out.trim()\n      expect(scalaVersion == Constants.defaultScala)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/MillTestHelper.scala",
    "content": "package scala.cli.integration\n\nimport scala.util.Properties\n\ntrait MillTestHelper {\n\n  protected def millLauncher: os.RelPath =\n    if (Properties.isWin) os.rel / \"mill.bat\"\n    else os.rel / \"mill\"\n\n  protected val millJvmOptsFileName: String = \".mill-jvm-opts\"\n  protected val millJvmOptsContent: String  = \"\"\"-Xmx512m\n                                                |-Xms128m\n                                                |\"\"\".stripMargin\n\n  protected val millDefaultProjectName = \"project\"\n\n  implicit class MillTestInputs(inputs: TestInputs) {\n    def withMillJvmOpts: TestInputs = inputs.add(os.rel / millJvmOptsFileName -> millJvmOptsContent)\n  }\n\n  protected val millOutputDir: os.RelPath = os.rel / \"output-project\"\n\n  protected def millCommand(root: os.Path, args: String*): os.proc =\n    os.proc(root / millOutputDir / millLauncher, args)\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/NativePackagerTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass NativePackagerTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n  override def munitFlakyOK: Boolean          = TestUtil.isCI\n  val helloWorldFileName                      = \"HelloWorldScalaCli.scala\"\n  val message                                 = \"Hello, world!\"\n  val licencePath                             = \"DummyLICENSE\"\n  val testInputs: TestInputs                  = TestInputs(\n    os.rel / helloWorldFileName ->\n      s\"\"\"\n         |object HelloWorld {\n         |  def main(args: Array[String]): Unit = {\n         |    println(\"$message\")\n         |  }\n         |}\"\"\".stripMargin,\n    os.rel / licencePath -> \"LICENSE\"\n  )\n\n  private val ciOpt = Option(System.getenv(\"CI\")).map(v => Seq(\"-e\", s\"CI=$v\")).getOrElse(Nil)\n\n  if (Properties.isMac) {\n    test(\"building pkg package\") {\n\n      testInputs.fromRoot { root =>\n\n        val appName    = helloWorldFileName.stripSuffix(\".scala\").toLowerCase\n        val pkgAppFile = s\"$appName.pkg\"\n        val cmd        = Seq[os.Shellable](\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          TestUtil.extraOptions,\n          helloWorldFileName,\n          \"--pkg\",\n          \"--output\",\n          pkgAppFile,\n          \"--identifier\",\n          \"scala-cli\",\n          \"--launcher-app\",\n          appName\n        )\n        os.proc(cmd).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val pkgAppPath = root / pkgAppFile\n        expect(os.isFile(pkgAppPath))\n\n        if (TestUtil.isCI) {\n          os.proc(\"installer\", \"-pkg\", pkgAppFile, \"-target\", \"CurrentUserHomeDirectory\").call(\n            cwd = root,\n            stdin = os.Inherit,\n            stdout = os.Inherit\n          )\n\n          val home   = sys.props(\"user.home\")\n          val output = os.proc(s\"$home/Applications/$appName.app/Contents/MacOS/$appName\")\n            .call(cwd = os.root)\n            .out.trim()\n          expect(output == message)\n        }\n      }\n    }\n    def testBuildingDmgPackage(): Unit =\n      testInputs.fromRoot { root =>\n\n        val appName = helloWorldFileName.stripSuffix(\".scala\").toLowerCase()\n        val output  = s\"$appName.dmg\"\n\n        val cmd = Seq[os.Shellable](\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          TestUtil.extraOptions,\n          helloWorldFileName,\n          \"--dmg\",\n          \"--output\",\n          output,\n          \"--identifier\",\n          \"scala-cli\",\n          \"--launcher-app\",\n          appName\n        )\n        os.proc(cmd).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val launcher = root / output\n        expect(os.isFile(launcher))\n\n        if (TestUtil.isCI) {\n          os.proc(\"hdiutil\", \"attach\", launcher).call(\n            cwd = root,\n            stdin = os.Inherit,\n            stdout = os.Inherit\n          )\n\n          val output = os.proc(s\"/Volumes/$appName/$appName.app/Contents/MacOS/$appName\")\n            .call(cwd = os.root)\n            .out.trim()\n          expect(output == message)\n\n          os.proc(\"hdiutil\", \"detach\", s\"/Volumes/$appName\").call(\n            cwd = root,\n            stdin = os.Inherit,\n            stdout = os.Inherit\n          )\n        }\n      }\n\n    // FIXME: building dmg package sometimes fails with:\n    // 'hdiutil: couldn't eject \"disk2\" - Resource busy'\n    if (TestUtil.isAarch64 || !TestUtil.isCI)\n      test(\"building dmg package\") {\n        testBuildingDmgPackage()\n      }\n    else test(\"building dmg package\".flaky) {\n      testBuildingDmgPackage()\n    }\n  }\n\n  if (Properties.isLinux) {\n\n    test(\"building deb package\") {\n\n      testInputs.fromRoot { root =>\n\n        val appName  = helloWorldFileName.stripSuffix(\".scala\").toLowerCase()\n        val priority = \"optional\"\n        val section  = \"devel\"\n\n        val destDir = os.rel / \"package\"\n        os.makeDir.all(root / destDir)\n\n        val cmd = Seq[os.Shellable](\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          TestUtil.extraOptions,\n          helloWorldFileName,\n          \"--deb\",\n          \"--output\",\n          destDir / s\"$appName.deb\",\n          \"--maintainer\",\n          \"scala-cli-test\",\n          \"--description\",\n          \"scala-cli-test\",\n          \"--launcher-app\",\n          appName,\n          \"--priority\",\n          priority,\n          \"--section\",\n          section\n        )\n        os.proc(cmd).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val launcher = root / destDir / s\"$appName.deb\"\n        expect(os.isFile(launcher))\n\n        // check flags\n        val debInfo = os.proc(\"dpkg\", \"--info\", launcher).call().out.text().trim\n        expect(debInfo.contains(s\"Priority: $priority\"))\n        expect(debInfo.contains(s\"Section: $section\"))\n\n        if (hasDocker) {\n          val script =\n            s\"\"\"#!/usr/bin/env bash\n               |set -e\n               |dpkg -x \"$appName.deb\" .\n               |exec ./usr/share/scala/$appName\n               |\"\"\".stripMargin\n          os.write(root / destDir / \"run.sh\", script)\n          os.perms.set(root / destDir / \"run.sh\", \"rwxr-xr-x\")\n          val termOpt = if (System.console() == null) Nil else Seq(\"-t\")\n          val ciOpt   = Option(System.getenv(\"CI\")).map(v => Seq(\"-e\", s\"CI=$v\")).getOrElse(Nil)\n          val res     = os.proc(\n            \"docker\",\n            \"run\",\n            termOpt,\n            ciOpt,\n            \"--rm\",\n            \"-w\",\n            \"/workdir\",\n            \"-v\",\n            s\"${root / destDir}:/workdir\",\n            \"eclipse-temurin:17-jdk\",\n            \"./run.sh\"\n          ).call(\n            cwd = root,\n            stdout = os.Pipe,\n            mergeErrIntoOut = true\n          )\n          expect(res.exitCode == 0)\n          val output = res.out.trim()\n          expect(output.endsWith(message))\n        }\n      }\n    }\n\n    test(\"building rpm package\") {\n\n      testInputs.fromRoot { root =>\n\n        val appName = helloWorldFileName.stripSuffix(\".scala\").toLowerCase()\n\n        val cmd = Seq[os.Shellable](\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          TestUtil.extraOptions,\n          helloWorldFileName,\n          \"--rpm\",\n          \"--output\",\n          s\"$appName.rpm\",\n          \"--description\",\n          \"scala-cli\",\n          \"--license\",\n          \"ASL 2.0\",\n          \"--version\",\n          \"1.0.0\",\n          \"--launcher-app\",\n          appName\n        )\n        os.proc(cmd).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val launcher = root / s\"$appName.rpm\"\n        expect(os.isFile(launcher))\n      }\n    }\n  }\n\n  if (Properties.isWin)\n    test(\"building msi package\") {\n\n      testInputs.fromRoot { root =>\n\n        val appName = helloWorldFileName.stripSuffix(\".scala\").toLowerCase()\n\n        val cmd = Seq[os.Shellable](\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          helloWorldFileName,\n          \"--msi\",\n          \"--output\",\n          s\"$appName.msi\",\n          \"--product-name\",\n          \"scala-cli\",\n          \"--license-path\",\n          licencePath,\n          \"--maintainer\",\n          \"Scala-CLI\",\n          \"--launcher-app\",\n          appName,\n          \"--suppress-validation\"\n        )\n        os.proc(cmd).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val launcher = root / s\"$appName.msi\"\n        expect(os.isFile(launcher))\n      }\n    }\n\n  def runTest(): Unit =\n    testInputs.fromRoot { root =>\n\n      val appName         = helloWorldFileName.stripSuffix(\".scala\")\n      val imageRepository = appName.toLowerCase()\n      val imageTag        = \"latest\"\n\n      val cmd = Seq[os.Shellable](\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        helloWorldFileName,\n        \"--docker\",\n        \"--docker-image-repository\",\n        imageRepository,\n        \"--docker-image-tag\",\n        imageTag\n      )\n\n      os.proc(cmd)\n        .call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n      val expectedImage =\n        s\"$imageRepository:$imageTag\"\n\n      try {\n        val output =\n          os.proc(\"docker\", \"run\", ciOpt, expectedImage).call(cwd = os.root).out.trim()\n        expect(output == message)\n      }\n      // clear\n      finally os.proc(\"docker\", \"rmi\", \"-f\", expectedImage).call(cwd = os.root)\n    }\n\n  def runJsTest(): Unit =\n    testInputs.fromRoot { root =>\n\n      val appName         = helloWorldFileName.stripSuffix(\".scala\")\n      val imageRepository = appName.toLowerCase()\n      val imageTag        = \"latest\"\n\n      val cmd = Seq[os.Shellable](\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        helloWorldFileName,\n        \"--js\",\n        \"--docker\",\n        \"--docker-image-repository\",\n        imageRepository,\n        \"--docker-image-tag\",\n        imageTag\n      )\n      // format: on\n\n      os.proc(cmd)\n        .call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n      val expectedImage =\n        s\"$imageRepository:$imageTag\"\n\n      try {\n        val output =\n          os.proc(\"docker\", \"run\", ciOpt, expectedImage).call(cwd = os.root).out.trim()\n        expect(output == message)\n\n      }\n      // clear\n      finally os.proc(\"docker\", \"rmi\", \"-f\", expectedImage).call(cwd = os.root)\n    }\n\n  def runNativeTest(): Unit =\n    testInputs.fromRoot { root =>\n\n      val appName         = helloWorldFileName.stripSuffix(\".scala\")\n      val imageRepository = appName.toLowerCase()\n      val imageTag        = \"latest\"\n\n      val cmd = Seq[os.Shellable](\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        helloWorldFileName,\n        \"--native\",\n        \"-S\",\n        \"2.13\",\n        \"--docker\",\n        \"--docker-image-repository\",\n        imageRepository,\n        \"--docker-image-tag\",\n        imageTag\n      )\n\n      os.proc(cmd)\n        .call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n      val expectedImage =\n        s\"$imageRepository:$imageTag\"\n\n      try {\n        val output =\n          os.proc(\"docker\", \"run\", ciOpt, expectedImage).call(cwd = os.root).out.trim()\n        expect(output == message)\n\n      }\n      // clear\n      finally os.proc(\"docker\", \"rmi\", \"-f\", expectedImage).call(cwd = os.root)\n    }\n\n  def hasDocker: Boolean =\n    Properties.isLinux ||\n    // no docker command or no Linux from it on Github actions macOS / Windows runners\n    ((Properties.isMac || Properties.isWin) && !TestUtil.isCI)\n\n  if (hasDocker) {\n    // TODO: restore this test when `registry-1.docker.io` is stable again\n    test(\"building docker image\".flaky) {\n      TestUtil.retryOnCi() {\n        runTest()\n      }\n    }\n\n    // FIXME for some reason, this test became flaky on the CI\n    if (TestUtil.isNativeCli)\n      test(\"building docker image with scala.js app\".flaky) {\n        TestUtil.retryOnCi() {\n          runJsTest()\n        }\n      }\n    else test(\"building docker image with scala.js app\") {\n      TestUtil.retryOnCi() {\n        runJsTest()\n      }\n    }\n  }\n\n  if (Properties.isLinux)\n    // FIXME this got flaky on the CI again\n    test(\"building docker image with scala native app\".flaky) {\n      TestUtil.retryOnCi() {\n        runNativeTest()\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/NewTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.removeAnsiColors\n\nclass NewTests extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  val simpleTemplateName = \"VirtusLab/scala-cli.g8\"\n\n  val expectedTemplateContent =\n    \"\"\"\n      |object HelloWorld {\n      |  def main(args: Array[String]): Unit = {\n      |    println(\"Hello, world!\")\n      |  }\n      |}\"\"\".stripMargin\n\n  val simpleTemplate: TestInputs = TestInputs(\n    os.rel / \"HelloWorld.scala\" -> expectedTemplateContent\n  )\n\n  test(\"simple new template\") {\n    simpleTemplate.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"new\", simpleTemplateName).call(cwd = root)\n      val content = os.read(root / \"HelloWorld.scala\")\n      expect(content == expectedTemplateContent)\n    }\n  }\n\n  test(\"error missing template argument\") {\n    TestInputs.empty.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, \"--power\", \"new\").call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n      val lines = removeAnsiColors(result.out.text()).linesIterator.toVector\n      assert(result.exitCode == 1)\n      expect(lines.contains(\"Error: Missing argument <template>\"))\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.{File, InputStream}\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Files\nimport java.util\nimport java.util.zip.ZipFile\n\nimport scala.cli.integration.TestUtil.*\nimport scala.concurrent.duration.DurationInt\nimport scala.jdk.CollectionConverters.*\nimport scala.util.{Properties, Using}\n\nabstract class PackageTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n  protected lazy val node: String              = TestUtil.fromPath(\"node\").getOrElse(\"node\")\n\n  test(\"simple script\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    val launcherName = {\n      val ext = if (Properties.isWin) \".bat\" else \"\"\n      fileName.stripSuffix(\".sc\") + ext\n    }\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, fileName).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / launcherName\n\n      expect(os.isFile(launcher))\n      expect(Files.isExecutable(launcher.toNIO))\n\n      val output = TestUtil.maybeUseBash(launcher)(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"current directory as default input\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \".\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val outputName = if (Properties.isWin) \"simple.bat\" else \"simple\"\n      val launcher   = root / outputName\n\n      expect(os.isFile(launcher))\n      expect(Files.isExecutable(launcher.toNIO))\n\n      val output = TestUtil.maybeUseBash(launcher.toString)(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"resource directory for coursier bootstrap launcher\") {\n    val fileName = \"hello.sc\"\n    val message  = \"1,2,3\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"|//> using resourceDir .\n            |import scala.io.Source\n            |\n            |val inputs = Source.fromResource(\"input\").getLines.toSeq\n            |println(inputs.mkString)\n            |\"\"\".stripMargin,\n      os.rel / \"input\" -> message\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \".\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val outputName = if (Properties.isWin) \"hello.bat\" else \"hello\"\n      val launcher   = root / outputName\n\n      val output = os.proc(launcher.toString).call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"resource directory for library package\") {\n    val fileName     = \"MyLibrary.scala\"\n    val outputLib    = \"my-library.jar\"\n    val resourceFile = \"input\"\n    val inputs       = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"|//> using resourceDir .\n            |\n            |class MyLibrary {\n            |  def message = \"Hello\"\n            |}\n            |\"\"\".stripMargin,\n      os.rel / resourceFile -> \"1,2,3\"\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \".\",\n        \"-o\",\n        outputLib,\n        \"--library\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val zf    = new ZipFile((root / outputLib).toIO)\n      val entry = zf.getEntry(resourceFile)\n      expect(entry != null)\n    }\n  }\n\n  test(\"Zip with Scala Script containing resource directive\") {\n    val inputs = TestInputs(\n      os.rel / \"hello.sc\" ->\n        s\"\"\"//> using resourceDir ./\n           |import scala.io.Source\n           |\n           |val inputs = Source.fromResource(\"input\").getLines.map(_.toInt).toSeq\n           |println(inputs.mkString(\",\"))\n           |\"\"\".stripMargin,\n      os.rel / \"input\" ->\n        s\"\"\"1\n           |2\n           |\"\"\".stripMargin\n    )\n    inputs.asZip { (root, zipPath) =>\n      val message = \"1,2\"\n\n      os.proc(TestUtil.cli, \"--power\", \"package\", zipPath, extraOptions, \".\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val outputName = if (Properties.isWin) \"hello.bat\" else \"hello\"\n      val launcher   = root / outputName\n\n      val output = os.proc(launcher.toString).call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  def simpleJsTest(): Unit = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    val destName = fileName.stripSuffix(\".sc\") + \".js\"\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, fileName, \"--js\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / destName\n      expect(os.isFile(launcher))\n\n      val nodePath = node\n      val output   = os.proc(nodePath, launcher.toString).call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  def sourceMapJsTest(): Unit = {\n    val fileName = \"Hello.scala\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"import scala.scalajs.js\n           |\n           |object Hello extends App {\n           |  println(\"Hello World\")\n           |}\n           |\"\"\".stripMargin\n    )\n    val destName = fileName.stripSuffix(\".scala\") + \".js\"\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        fileName,\n        \"--js\",\n        \"--js-emit-source-maps\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val expectedSourceMapsPath = root / s\"$destName.map\"\n      val expectedHelloJsPath    = root / destName\n      expect(os.isFile(expectedHelloJsPath))\n      expect(os.isFile(expectedSourceMapsPath))\n\n      val jsContent        = os.read(expectedHelloJsPath)\n      val sourceMappingURL = jsContent.split(System.lineSeparator()).toList.lastOption\n      expect(sourceMappingURL.nonEmpty)\n      expect(sourceMappingURL.get == s\"//# sourceMappingURL=$destName.map\")\n    }\n  }\n\n  def multiModulesJsTest(): Unit = {\n    val fileName = \"Hello.scala\"\n    val message  = \"Hello World from JS\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"|//> using jsModuleKind es\n            |//> using jsModuleSplitStyleStr smallestmodules\n            |\n            |case class Foo(bar: String)\n            |\n            |object Hello extends App {\n            |  println(Foo(\"$message\").bar)\n            |}\n            |\"\"\".stripMargin\n    )\n    val destDir = fileName.stripSuffix(\".scala\")\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        fileName,\n        \"--js\",\n        \"-o\",\n        destDir\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / destDir / \"main.js\"\n      val nodePath = node\n      os.write(root / \"package.json\", \"{\\n\\n  \\\"type\\\": \\\"module\\\"\\n\\n}\") // enable es module\n      val output = os.proc(nodePath, launcher.toString).call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  def smallModulesJsTest(jvm: Boolean): Unit = {\n    val fileName = \"Hello.scala\"\n    val message  = \"Hello World from JS\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"|//> using jsModuleKind es\n            |//> using jsModuleSplitStyleStr smallmodulesfor\n            |//> using jsSmallModuleForPackage test\n            |\n            |package test\n            |\n            |case class Foo(bar: String)\n            |\n            |object Hello extends App {\n            |  println(Foo(\"$message\").bar)\n            |}\n            |\"\"\".stripMargin\n    )\n    val destDir = fileName.stripSuffix(\".scala\")\n    inputs.fromRoot { root =>\n      val extraArgs = if (jvm) Seq(\"--js-cli-on-jvm\") else Nil\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        fileName,\n        \"--js\",\n        \"-o\",\n        destDir,\n        extraArgs\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / destDir / \"main.js\"\n      val nodePath = node\n      os.write(root / \"package.json\", \"{\\n\\n  \\\"type\\\": \\\"module\\\"\\n\\n}\") // enable es module\n      val output = os.proc(nodePath, launcher.toString).call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  def jsHeaderTest(): Unit = {\n    val fileName        = \"Hello.scala\"\n    val jsHeader        = \"#!/usr/bin/env node\"\n    val jsHeaderNewLine = s\"$jsHeader\\\\n\"\n    val inputs          = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"|//> using jsHeader \"$jsHeaderNewLine\"\n            |//> using jsMode release\n            |\n            |object Hello extends App {\n            |  println(\"Hello\")\n            |}\n            |\"\"\".stripMargin\n    )\n    val destName = fileName.stripSuffix(\".sc\") + \".js\"\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        fileName,\n        \"--js\",\n        \"-o\",\n        destName\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher        = root / destName\n      val launcherContent = os.read(launcher)\n      expect(launcherContent.startsWith(jsHeader))\n    }\n  }\n\n  def jsWithoutMainTest(): Unit = {\n    val fileName = \"Hello.scala\"\n    val msg      = \"Hello World\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"|import scala.scalajs.js.annotation._\n            |\n            |@JSExportTopLevel(\"Hello\")\n            |object Hello {\n            |  @JSExport\n            |  def helloWorld: String = \"$msg\"\n            |}\n            |\"\"\".stripMargin,\n      os.rel / \"runHello.js\" ->\n        s\"\"\"const { Hello } = require('./Hello.js');\n           |console.log(Hello.helloWorld);\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        fileName,\n        \"--js\",\n        \"--js-module-kind\",\n        \"common\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val runHelloWorld = root / \"runHello.js\"\n      val nodePath      = node\n      val output        = os.proc(nodePath, runHelloWorld.toString).call(cwd = root).out.trim()\n      expect(output == msg)\n    }\n  }\n\n  if (!TestUtil.isNativeCli || !Properties.isWin) {\n    test(\"simple JS\") {\n      simpleJsTest()\n    }\n    test(\"source maps js\") {\n      sourceMapJsTest()\n    }\n    test(\"multi modules js\") {\n      multiModulesJsTest()\n    }\n    test(\"small modules js with native scalajs-cli\") {\n      smallModulesJsTest(jvm = false)\n    }\n    test(\"small modules js with jvm scalajs-cli\") {\n      smallModulesJsTest(jvm = true)\n    }\n    test(\"js header in release mode\") {\n      jsHeaderTest()\n    }\n    test(\"js without main\") {\n      jsWithoutMainTest()\n    }\n  }\n\n  def simpleNativeTest(): Unit = {\n    val fileName   = \"simple.sc\"\n    val message    = \"Hello\"\n    val platformNl = if (Properties.isWin) \"\\\\r\\\\n\" else \"\\\\n\"\n    val inputs     = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"import scala.scalanative.libc._\n           |import scala.scalanative.unsafe._\n           |\n           |Zone { implicit z =>\n           |   val io = StdioHelpers(stdio)\n           |   io.printf(c\"%s$platformNl\", c\"$message\")\n           |}\n           |\"\"\".stripMargin\n    )\n    val destName = {\n      val ext = if (Properties.isWin) \".exe\" else \"\"\n      fileName.stripSuffix(\".sc\") + ext\n    }\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, fileName, \"--native\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / destName\n      expect(os.isFile(launcher))\n      expect(Files.isExecutable(launcher.toNIO))\n\n      val output = os.proc(launcher.toString).call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  def libraryNativeTest(\n    shared: Boolean = false,\n    commandLineShared: Option[Boolean] = None\n  ): Unit = {\n    val fileName              = \"simple.sc\"\n    val directiveNativeTarget = if (shared) \"dynamic\" else \"static\"\n    val inputs                = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"\n           |//> using platform scala-native\n           |//> using nativeTarget $directiveNativeTarget\n           |import scala.scalanative.unsafe._\n           |object myLib{\n           |  @exported\n           |  def addLongs(l: Long, r: Long): Long = l + r\n           |  @exported(\"mylib_addInts\")\n           |  def addInts(l: Int, r: Int): Int = l + r\n           |}\"\"\".stripMargin\n    )\n    val destName = {\n      val ext =\n        if (!shared && !commandLineShared.getOrElse(false))\n          if (Properties.isWin) \".lib\" else \".a\"\n        else if (Properties.isWin) \".dll\"\n        else if (Properties.isMac) \".dylib\"\n        else \".so\"\n      fileName.stripSuffix(\".sc\") + ext\n    }\n\n    val nativeTargetOpts = commandLineShared match {\n      case Some(true)  => Seq(\"--native-target\", \"dynamic\")\n      case Some(false) => Seq(\"--native-target\", \"static\")\n      case None        => Seq.empty\n    }\n\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, nativeTargetOpts, fileName).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val library = root / destName\n      expect(os.isFile(library))\n    }\n  }\n\n  if (!Properties.isWin && actualScalaVersion.startsWith(\"2.13\")) {\n    test(\"simple native\") {\n      TestUtil.retryOnCi() {\n        simpleNativeTest()\n      }\n    }\n    test(\"dynamic library native\") {\n      TestUtil.retryOnCi() {\n        libraryNativeTest(shared = true)\n      }\n    }\n\n    test(\"dynamic library native override from command line\") {\n      TestUtil.retryOnCi() {\n        libraryNativeTest(shared = false, commandLineShared = Some(true))\n      }\n    }\n\n    // To produce a static library, `LLVM_BIN` environment variable needs to be\n    // present (for `llvm-ar` utility)\n    if (sys.env.contains(\"LLVM_BIN\"))\n      test(\"shared library native\") {\n        TestUtil.retryOnCi() {\n          libraryNativeTest(shared = false)\n        }\n      }\n\n  }\n\n  test(\"assembly\") {\n    TestUtil.retryOnCi() {\n      val fileName = \"simple.sc\"\n      val message  = \"Hello\"\n      val inputs   = TestInputs(\n        os.rel / fileName ->\n          s\"\"\"//> using dep org.typelevel::cats-kernel:2.6.1\n             |import cats.kernel._\n             |val m = Monoid.instance[String](\"\", (a, b) => a + b)\n             |val msgStuff = m.combineAll(List(\"$message\", \"\", \"\"))\n             |println(msgStuff)\n             |\"\"\".stripMargin\n      )\n      val launcherName = fileName.stripSuffix(\".sc\") + \".jar\"\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \"--assembly\", fileName).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val launcher = root / launcherName\n        expect(os.isFile(launcher))\n\n        var zf: ZipFile = null\n        try {\n          zf = new ZipFile(launcher.toIO)\n          expect(zf.getEntry(\"cats/kernel/Monoid.class\") != null)\n        }\n        finally if (zf != null) zf.close()\n\n        val runnableLauncher =\n          if (Properties.isWin) {\n            val bat = root / \"assembly.bat\"\n            os.copy(launcher, bat)\n            bat\n          }\n          else {\n            expect(Files.isExecutable(launcher.toNIO))\n            launcher\n          }\n        val runnableLauncherSize = os.size(runnableLauncher)\n\n        val output = TestUtil.maybeUseBash(runnableLauncher.toString)(cwd = root).out.trim()\n        val maxRunnableLauncherSize = 1024 * 1024 * 12 // should be smaller than 12MB\n        expect(output == message)\n        expect(runnableLauncherSize < maxRunnableLauncherSize)\n      }\n    }\n  }\n\n  test(\"assembly no preamble\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"package hello\n           |\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(\"Hello from \" + \"assembly\")\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \"--assembly\",\n        \"-o\",\n        \"hello\",\n        \"--preamble=false\",\n        \".\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / \"hello\"\n      expect(os.isFile(launcher))\n\n      val preambleStart = \"#\".getBytes(StandardCharsets.UTF_8)\n      val contentStart  = os.read.bytes(launcher).take(preambleStart.length)\n      expect(!util.Arrays.equals(contentStart, preambleStart))\n\n      val output = os.proc(\"java\", \"-cp\", launcher, \"hello.Hello\")\n        .call(cwd = root).out.trim()\n      expect(output == \"Hello from assembly\")\n    }\n  }\n\n  test(\"assembly no preamble nor main class\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"package hello\n           |\n           |object Hello {\n           |  def message: String =\n           |    \"Hello from \" + \"assembly\"\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \"--assembly\",\n        \"-o\",\n        \"hello.jar\",\n        \"--preamble=false\",\n        \".\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / \"hello.jar\"\n      expect(os.isFile(launcher))\n\n      Using.resource(new ZipFile(launcher.toIO)) { zf =>\n        val entries = zf.entries()\n          .asScala\n          .iterator\n          .map(_.getName)\n          .filter(_.startsWith(\"hello/\"))\n          .toVector\n        expect(entries.contains(\"hello/Hello.class\"))\n      }\n    }\n  }\n\n  test(\"assembly classpath\") {\n    val lib    = os.rel / \"lib\"\n    val app    = os.rel / \"app\"\n    val inputs = TestInputs(\n      lib / \"lib\" / \"Message.scala\" ->\n        s\"\"\"package lib\n           |\n           |object Message {\n           |  def hello(name: String) = s\"Hello $$name\"\n           |}\n           |\"\"\".stripMargin,\n      app / \"app\" / \"Hello.scala\" ->\n        s\"\"\"package app\n           |\n           |import lib.Message.hello\n           |\n           |object Hello {\n           |  def main(args: Array[String]): Unit = println(hello(\"assembly\"))\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val classpath = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        extraOptions,\n        \"--print-classpath\",\n        lib.toString\n      ).call(\n        cwd = root,\n        stdin = os.Inherit\n      ).out.text().trim\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \"--main-class\",\n        \"app.Hello\",\n        \"--assembly\",\n        \"-o\",\n        \"hello.jar\",\n        s\"--classpath=$classpath\",\n        app.toString\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / \"hello.jar\"\n      expect(os.isFile(launcher))\n\n      Using.resource(new ZipFile(launcher.toIO)) { zf =>\n        val entries = zf.entries()\n          .asScala\n          .iterator\n          .map(_.getName)\n          .toVector\n        expect(entries.exists(_.endsWith(\"lib/Message.class\")))\n        expect(entries.contains(\"app/Hello.class\"))\n      }\n    }\n  }\n\n  test(\"assembly provided\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"package hello\n           |\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(\"Hello from Scala \" + scala.util.Properties.versionNumberString)\n           |}\n           |\"\"\".stripMargin\n    )\n    val providedModule =\n      if (actualScalaVersion.startsWith(\"2.\")) \"org.scala-lang:scala-library\"\n      else \"org.scala-lang:scala3-library_3\"\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \"--assembly\",\n        \"-o\",\n        \"hello\",\n        \"--provided\",\n        providedModule,\n        \".\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val launcher = root / \"hello\"\n      expect(os.isFile(launcher))\n\n      var zf: ZipFile = null\n      val entries     =\n        try {\n          zf = new ZipFile(launcher.toIO)\n          expect(zf.getEntry(\"hello/Hello.class\") != null)\n          expect(zf.getEntry(\"scala/Function.class\") == null) // no scala-library\n          expect(zf.getEntry(\"scala/Tuple.class\") == null)    // no scala3-library\n\n          zf.entries().asScala.map(_.getName).toVector.sorted\n        }\n        finally if (zf != null) zf.close()\n\n      val noMetaEntries = entries.filter(!_.startsWith(\"META-INF/\"))\n      expect(noMetaEntries.nonEmpty)\n      expect(noMetaEntries.forall(_.startsWith(\"hello/\")))\n\n      val scalaLibCp =\n        os.proc(TestUtil.cs, \"fetch\", \"--classpath\", s\"$providedModule:$actualScalaVersion\")\n          .call(cwd = root).out.trim()\n      val output =\n        os.proc(\"java\", \"-cp\", s\"$launcher${File.pathSeparator}$scalaLibCp\", \"hello.Hello\")\n          .call(cwd = root).out.trim()\n      val expectedScalaVerInOutput =\n        if (actualScalaVersion.startsWith(\"2.\")) actualScalaVersion\n        else {\n          val scalaLibJarName = scalaLibCp.split(File.pathSeparator)\n            .map(_.split(\"[\\\\\\\\/]+\").last).find(_.startsWith(\"scala-library-\"))\n            .getOrElse {\n              sys.error(s\"scala-library not found in provided class path $scalaLibCp\")\n            }\n          scalaLibJarName\n            .stripPrefix(\"scala-library-\")\n            .stripSuffix(\".jar\")\n        }\n      expect(output == \"Hello from Scala \" + expectedScalaVerInOutput)\n    }\n  }\n\n  test(\"ignore test scope\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"|object Main {\n           |  def main(args: Array[String]): Unit = {\n           |    println(\"Hello World\")\n           |  }\n           |}\"\"\".stripMargin,\n      os.rel / \"Tests.test.scala\" ->\n        \"\"\"|import utest._ // compilation error, not included test library\n           |\n           |object Tests extends TestSuite {\n           |  val tests = Tests {\n           |    test(\"message\") {\n           |      assert(1 == 1)\n           |    }\n           |  }\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \".\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val outputName = if (Properties.isWin) \"Main.bat\" else \"Main\"\n      val launcher   = root / outputName\n\n      val output = os.proc(launcher.toString).call(cwd = root).out.trim()\n      expect(output == \"Hello World\")\n    }\n  }\n\n  private def readEntry(zf: ZipFile, name: String): Array[Byte] = {\n    val ent             = zf.getEntry(name)\n    var is: InputStream = null\n    try {\n      is = zf.getInputStream(ent)\n      is.readAllBytes()\n    }\n    finally\n      if (is != null)\n        is.close()\n  }\n\n  private val simpleInputWithScalaAndSc = TestInputs(\n    os.rel / \"lib\" / \"Messages.scala\" ->\n      \"\"\"package lib\n        |\n        |object Messages {\n        |  def msg = \"Hello\"\n        |}\n        |\"\"\".stripMargin,\n    os.rel / \"simple.sc\" ->\n      \"\"\"val msg = lib.Messages.msg\n        |println(msg)\n        |\"\"\".stripMargin\n  )\n  test(\"source JAR\") {\n    val dest = os.rel / \"sources.jar\"\n    simpleInputWithScalaAndSc.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \".\",\n        \"-o\",\n        dest,\n        \"--with-sources\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      expect(os.isFile(root / dest))\n\n      val zf                 = new ZipFile((root / dest).toIO)\n      val genSourceEntryName = \"META-INF/generated/simple.scala\"\n      val expectedEntries    = Set(\n        \"lib/Messages.scala\",\n        genSourceEntryName,\n        \"simple.sc\"\n      )\n      val entries = zf.entries().asScala.iterator.map(_.getName).toSet\n      expect(entries == expectedEntries)\n\n      for ((relPath, expectedStrContent) <- simpleInputWithScalaAndSc.files) {\n        val content    = readEntry(zf, relPath.toString)\n        val strContent = new String(content, StandardCharsets.UTF_8)\n        expect(strContent == expectedStrContent)\n      }\n\n      val genContent    = readEntry(zf, genSourceEntryName)\n      val genContentStr = new String(genContent, StandardCharsets.UTF_8)\n      if (actualScalaVersion.startsWith(\"2.\"))\n        expect(genContentStr.contains(\"object simple\"))\n      else\n        expect(genContentStr.contains(\"class simple$_\"))\n    }\n  }\n\n  test(\"doc JAR\") {\n    val dest = os.rel / \"doc.jar\"\n    simpleInputWithScalaAndSc.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \".\", \"-o\", dest, \"--doc\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      expect(os.isFile(root / dest))\n      val zf              = new ZipFile((root / dest).toIO)\n      val expectedEntries =\n        if (actualScalaVersion.startsWith(\"2.\"))\n          Seq(\n            \"index.html\",\n            \"lib/Messages$.html\",\n            \"simple$.html\"\n          )\n        else if (\n          actualScalaVersion.coursierVersion >= \"3.5.0\".coursierVersion ||\n          (actualScalaVersion.coursierVersion >= \"3.3.4\".coursierVersion &&\n          actualScalaVersion.coursierVersion < \"3.4.0\".coursierVersion) ||\n          actualScalaVersion.startsWith(\"3.3.4\") ||\n          actualScalaVersion.startsWith(\"3.5\")\n        )\n          Seq(\n            \"index.html\",\n            \"inkuire-db.json\",\n            \"$lessempty$greater$/simple$_.html\",\n            \"lib/Messages$.html\"\n          )\n        else\n          Seq(\n            \"index.html\",\n            \"inkuire-db.json\",\n            \"_empty_/simple$_.html\",\n            \"lib/Messages$.html\"\n          )\n      val entries = zf.entries().asScala.iterator.map(_.getName).toSet\n\n      expect(expectedEntries.forall(e => entries.contains(e)))\n    }\n  }\n\n  test(\"native image\") {\n    val message    = \"Hello from native-image\"\n    val dest       = \"hello\"\n    val actualDest =\n      if (Properties.isWin) \"hello.exe\"\n      else \"hello\"\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(\"$message\")\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \".\",\n        \"--native-image\",\n        \"-o\",\n        dest,\n        \"--\",\n        \"--no-fallback\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      expect(os.isFile(root / actualDest))\n\n      // FIXME Check that dest is indeed a binary?\n\n      val res    = os.proc(root / actualDest).call(cwd = root)\n      val output = res.out.trim()\n      expect(output == message)\n    }\n  }\n\n  if (Properties.isWin)\n    test(\"availableDriveLetter\") {\n      val message    = \"Hello from native-image\"\n      val dest       = \"hello\"\n      val actualDest =\n        if (Properties.isWin) \"hello.exe\"\n        else \"hello\"\n      val inputs = TestInputs(\n        os.rel / \"Hello.scala\" ->\n          s\"\"\"object Hello {\n             |  def main(args: Array[String]): Unit =\n             |    println(\"$message\")\n             |}\n             |\"\"\".stripMargin\n      )\n      setCodePage(\"65001\")\n      val codePageBefore = getCodePage\n      val driveLetter    = availableDriveLetter()\n      val substedBefore  = substedDrives\n      aliasDriveLetter(driveLetter, \"C:\\\\Windows\\\\Temp\") // trigger for #4005\n\n      inputs.fromRoot { root =>\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          extraOptions,\n          \".\",\n          \"--native-image\",\n          \"-o\",\n          dest,\n          \"--\",\n          \"--no-fallback\"\n        ).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        expect(os.isFile(root / actualDest))\n\n        val res    = os.proc(root / actualDest).call(cwd = root)\n        val output = res.out.trim()\n        expect(output == message)\n\n        unaliasDriveLetter(driveLetter) // undo test condition\n        val substedAfter = substedDrives\n        expect(substedBefore == substedAfter)\n        val codePageAfter = getCodePage\n        expect(codePageBefore == codePageAfter)\n      }\n    }\n\n  test(\"correctly list main classes\") {\n    val (scalaFile1, scalaFile2, scriptName) = (\"ScalaMainClass1\", \"ScalaMainClass2\", \"ScalaScript\")\n    val scriptsDir                           = \"scripts\"\n    val inputs                               = TestInputs(\n      os.rel / s\"$scalaFile1.scala\"           -> s\"object $scalaFile1 extends App { println() }\",\n      os.rel / s\"$scalaFile2.scala\"           -> s\"object $scalaFile2 extends App { println() }\",\n      os.rel / scriptsDir / s\"$scriptName.sc\" -> \"println()\"\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \".\",\n        \"--list-main-classes\"\n      )\n        .call(cwd = root)\n      val output      = res.out.trim()\n      val mainClasses = output.split(\" \").toSet\n\n      val scriptMainClassName = if (actualScalaVersion.startsWith(\"3\"))\n        s\"$scriptsDir.${scriptName}_sc\"\n      else\n        s\"$scriptsDir.$scriptName\"\n\n      expect(mainClasses == Set(scalaFile1, scalaFile2, scriptMainClassName))\n    }\n  }\n\n  test(\"pass java and javac options\") {\n    val fileName           = \"Hello.scala\"\n    val destFile           = if (Properties.isWin) \"hello.bat\" else \"hello\"\n    val (fooProp, barProp) = (\"abc\", \"xyz\")\n    val inputs             = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(s\"$${sys.props(\"foo\")}$${sys.props(\"bar\")}\")\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        fileName,\n        \"-o\",\n        destFile,\n        \"--java-prop\",\n        s\"foo=$fooProp\",\n        \"--java-opt\",\n        s\"-Dbar=$barProp\",\n        \"--javac-option\",\n        \"-source\",\n        \"--javac-option\",\n        \"1.8\",\n        \"--javac-option\",\n        \"-target\",\n        \"--javac-option\",\n        \"1.8\",\n        \"-f\"\n      ).call(cwd = root)\n      val output = os.proc(root / destFile).call(cwd = root).out.trim()\n      expect(output == s\"$fooProp$barProp\")\n    }\n  }\n\n  test(\"ensure directories are created recursively when packaging a jar\") {\n    TestInputs(\n      os.rel / \"Simple.scala\" -> s\"\"\"object Simple extends App { println() }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val jarPath =\n        os.rel / \"out\" / \"inner-out\" /\n          \"Simple.jar\" // the `out` directory doesn't exist and should be created\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \".\",\n        \"--library\",\n        \"-o\",\n        jarPath,\n        extraOptions\n      ).call(cwd = root)\n    }\n  }\n\n  def javaOptionsDockerTest(): Unit = {\n    val fileName           = \"Hello.scala\"\n    val imageName          = \"hello\"\n    val (fooProp, barProp) = (\"abc\", \"xyz\")\n    val inputs             = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(s\"$${sys.props(\"foo\")}$${sys.props(\"bar\")}\")\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        fileName,\n        \"--docker\",\n        \"--docker-image-repository\",\n        imageName,\n        \"--java-prop\",\n        s\"foo=$fooProp\",\n        \"--java-opt\",\n        s\"-Dbar=$barProp\",\n        \"-f\"\n      ).call(cwd = root)\n      val output = os.proc(\"docker\", \"run\", imageName).call(cwd = root).out.trim()\n      expect(output == s\"$fooProp$barProp\")\n    }\n  }\n\n  def dockerWithExtraDirsTest(): Unit = {\n    val codePath       = os.rel / \"src\" / \"Hello.scala\"\n    val extraFileName  = \"extraFile.txt\"\n    val extraFileDir   = os.rel / \"extraDir\"\n    val extraFilePath  = extraFileDir / extraFileName\n    val imageName      = \"extradir\"\n    val expectedOutput = \"hello\"\n    val inputs         = TestInputs(\n      codePath ->\n        s\"\"\"//> using toolkit default\n           |\n           |object Smth extends App {\n           |  val content = \n           |   os.walk(os.pwd)\n           |     .filter(os.isFile)\n           |     .filter(_.endsWith(os.rel / \"$extraFileName\"))\n           |     .headOption\n           |     .map(file => os.read(file).trim())\n           |     .getOrElse(\"No matching files found\")\n           |  println(content)\n           |}\n           |\"\"\".stripMargin,\n      extraFilePath -> expectedOutput\n    )\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        codePath,\n        \"--docker\",\n        \"--docker-image-repository\",\n        imageName,\n        \"--docker-extra-directories\",\n        root / extraFileDir,\n        \"-f\"\n      ).call(cwd = root)\n      val output = os.proc(\"docker\", \"run\", imageName).call(cwd = root).out.trim()\n      expect(output == expectedOutput)\n    }\n  }\n\n  if (Properties.isLinux) {\n    // TODO: restore this test when `registry-1.docker.io` is stable again\n    test(\"pass java options to docker\".flaky) {\n      TestUtil.retryOnCi() {\n        javaOptionsDockerTest()\n      }\n    }\n\n    // TODO: restore this test when `registry-1.docker.io` is stable again\n    test(\"pass extra directory to docker\".flaky) {\n      TestUtil.retryOnCi() {\n        dockerWithExtraDirsTest()\n      }\n    }\n  }\n\n  test(\"default values in help\") {\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \"--help\").call(cwd = root)\n      val lines = removeAnsiColors(res.out.trim()).linesIterator.toVector\n\n      val graalVmVersionHelp     = lines.find(_.contains(\"--graalvm-version\")).getOrElse(\"\")\n      val graalVmJavaVersionHelp = lines.find(_.contains(\"--graalvm-java-version\")).getOrElse(\"\")\n\n      expect(graalVmVersionHelp.contains(s\"(${Constants.defaultGraalVMVersion} by default)\"))\n      expect(\n        graalVmJavaVersionHelp.contains(s\"(${Constants.defaultGraalVMJavaVersion} by default)\")\n      )\n    }\n  }\n\n  test(\"scalapy\") {\n\n    def maybeScalapyPrefix =\n      if (actualScalaVersion.startsWith(\"2.13.\")) \"\"\n      else \"import me.shadaj.scalapy.py\" + System.lineSeparator()\n\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"$maybeScalapyPrefix\n           |object Hello {\n           |  def main(args: Array[String]): Unit = {\n           |    py.Dynamic.global.print(\"Hello from Python\", flush = true)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n\n    val dest =\n      if (Properties.isWin) \"hello.bat\"\n      else \"hello\"\n\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", \"--python\", \".\", \"-o\", dest, extraOptions)\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val launcher = root / dest\n      val res      = os.proc(launcher).call(cwd = root)\n      val output   = res.out.trim()\n      expect(output == \"Hello from Python\")\n    }\n  }\n\n  test(\"fat jar\") {\n    val inputs = TestInputs(\n      os.rel / \"OsLibFatJar.scala\" -> s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.0\"\"\",\n      os.rel / \"Hello.scala\"       ->\n        s\"\"\"object Main extends App {\n           |  println(os.pwd)\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val fatJarPath = root / \"OsLibFatJar.jar\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \"OsLibFatJar.scala\",\n        \"-o\",\n        fatJarPath,\n        \"--assembly\",\n        \"--preamble=false\",\n        extraOptions\n      ).call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val outputName  = if (Properties.isWin) \"hello.bat\" else \"hello\"\n      val launcher    = root / outputName\n      val packageCmds = Seq[os.Shellable](\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \"Hello.scala\",\n        \"-M\",\n        \"Main\",\n        \"--jar\",\n        fatJarPath,\n        \"-o\",\n        launcher,\n        extraOptions\n      )\n\n      // bootstrap\n      os.proc(packageCmds).call(cwd = root).out.trim()\n      val output = TestUtil.maybeUseBash(launcher.toString)(cwd = root).out.trim()\n      expect(output == root.toString)\n\n      // assembly\n      os.proc(packageCmds, \"--assembly\", \"-f\").call(cwd = root).out.trim()\n      val outputAssembly = TestUtil.maybeUseBash(launcher.toString)(cwd = root).out.trim()\n      expect(outputAssembly == root.toString)\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2\")) {\n    test(\"resolution is kept for assemblies with provided spark deps (packaging.provided)\") {\n      TestUtil.retryOnCi() {\n        val msg       = \"Hello\"\n        val inputPath = os.rel / \"Hello.scala\"\n        TestInputs(\n          inputPath ->\n            s\"\"\"//> using lib org.apache.spark::spark-sql:3.3.2\n               |//> using lib org.apache.spark::spark-hive:3.3.2\n               |//> using lib org.apache.spark::spark-sql-kafka-0-10:3.3.2\n               |//> using packaging.packageType assembly\n               |//> using packaging.provided org.apache.spark::spark-sql\n               |//> using packaging.provided org.apache.spark::spark-hive\n               |\n               |object Main extends App {\n               |  println(\"$msg\")\n               |}\n               |\"\"\".stripMargin\n        ).fromRoot { root =>\n          val outputJarPath = root / \"Hello.jar\"\n          val res           = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"package\",\n            inputPath,\n            \"-o\",\n            outputJarPath,\n            extraOptions\n          ).call(cwd = root, stderr = os.Pipe)\n          expect(os.isFile(outputJarPath))\n          expect(res.err.trim().contains(s\"Wrote $outputJarPath\"))\n        }\n      }\n    }\n\n    test(\n      \"resolution is kept for assemblies with provided spark deps (packaging.packageType spark)\"\n    ) {\n      val msg       = \"Hello\"\n      val inputPath = os.rel / \"Hello.scala\"\n      TestInputs(\n        inputPath ->\n          s\"\"\"//> using lib org.apache.spark::spark-sql:3.3.2\n             |//> using lib org.apache.spark::spark-hive:3.3.2\n             |//> using lib org.apache.spark::spark-sql-kafka-0-10:3.3.2\n             |//> using packaging.packageType spark\n             |\n             |object Main extends App {\n             |  println(\"$msg\")\n             |}\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val outputJarPath = root / \"Hello.jar\"\n        val res           = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          inputPath,\n          \"-o\",\n          outputJarPath,\n          extraOptions\n        ).call(cwd = root, stderr = os.Pipe)\n        expect(os.isFile(outputJarPath))\n        expect(res.err.trim().contains(s\"Wrote $outputJarPath\"))\n      }\n    }\n  }\n\n  test(\"pass resource dir with command line option\") {\n    val child       = \"<name>exampleResource</name>\"\n    val mainClass   = \"Hello\"\n    val xmlFileName = \"example.xml\"\n    val resourceDir = \"resources\"\n    TestInputs(\n      os.rel / resourceDir / xmlFileName -> s\"<example>$child</example>\",\n      os.rel / s\"$mainClass.scala\"       ->\n        s\"\"\"//> using dep org.scala-lang.modules::scala-xml:2.2.0\n           |object $mainClass {\n           |  def main(args: Array[String]): Unit = {\n           |    val xml = scala.xml.XML.load(getClass.getResourceAsStream(\"$xmlFileName\"))\n           |    xml.child.foreach(println)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val outputJarPath = root / \"hello.jar\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \"--library\",\n        s\"$mainClass.scala\",\n        \"--resource-dir\",\n        resourceDir,\n        \"-o\",\n        outputJarPath\n      )\n        .call(cwd = root)\n      expect(os.isFile(outputJarPath))\n      val res =\n        os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--jar\",\n          outputJarPath,\n          \"--main-class\",\n          mainClass,\n          \"--dep\",\n          \"org.scala-lang.modules::scala-xml:2.2.0\",\n          extraOptions\n        ).call(cwd = root)\n      expect(res.out.trim() == child)\n    }\n  }\n\n  {\n    val libraryArg = \"--library\"\n    val jsArg      = \"--js\"\n    for {\n      (packageOpts, extension) <- Seq(\n        Seq(\"--native\") -> (if (Properties.isWin) \".exe\" else \"\"),\n        Nil             -> (if (Properties.isWin) \".bat\" else \"\"),\n        Seq(libraryArg) -> \".jar\"\n      ) ++\n        (if (!TestUtil.isNativeCli || !Properties.isWin) Seq(\n           Seq(\"--assembly\")     -> \".jar\",\n           Seq(\"--native-image\") -> (if (Properties.isWin) \".exe\" else \"\"),\n           Seq(jsArg)            -> \".js\"\n         )\n         else Nil)\n      packageDescription = packageOpts.headOption.getOrElse(\"bootstrap\")\n    } {\n      test(s\"package with main method in test scope ($packageDescription)\") {\n        TestUtil.retryOnCi() {\n          val mainClass         = \"TestScopeMain\"\n          val testScopeFileName = s\"$mainClass.test.scala\"\n          val message           = \"Hello\"\n          val outputFile        = mainClass + extension\n          TestInputs(\n            os.rel / \"Messages.scala\"  -> s\"\"\"object Messages { val msg = \"$message\" }\"\"\",\n            os.rel / testScopeFileName ->\n              s\"\"\"object $mainClass extends App { println(Messages.msg) }\"\"\"\n          ).fromRoot { root =>\n            os.proc(\n              TestUtil.cli,\n              \"--power\",\n              \"package\",\n              \"--test\",\n              extraOptions,\n              \".\",\n              packageOpts\n            )\n              .call(cwd = root)\n            val outputFilePath = root / outputFile\n            expect(os.isFile(outputFilePath))\n            val output =\n              if (packageDescription == libraryArg)\n                os.proc(TestUtil.cli, \"run\", outputFilePath).call(cwd = root).out.trim()\n              else if (packageDescription == jsArg)\n                os.proc(node, outputFilePath).call(cwd = root).out.trim()\n              else {\n                expect(Files.isExecutable(outputFilePath.toNIO))\n                TestUtil.maybeUseBash(outputFilePath)(cwd = root).out.trim()\n              }\n            expect(output == message)\n          }\n        }\n      }\n\n      if (actualScalaVersion == Constants.scala3Next) {\n        val crossScalaVersions =\n          Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n        val numberOfBuilds = crossScalaVersions.size\n        test(s\"package ($packageDescription, --cross) produces $numberOfBuilds artifacts\") {\n          TestUtil.retryOnCi() {\n            val crossDirective =\n              s\"//> using scala ${crossScalaVersions.mkString(\" \")}\"\n            val mainClass = \"TestScopeMain\"\n            val mainFile  = s\"$mainClass.scala\"\n            val message   = \"Hello\"\n            TestInputs(\n              os.rel / \"Messages.scala\" ->\n                s\"\"\"$crossDirective\n                   |object Messages { val msg = \"$message\" }\"\"\".stripMargin,\n              os.rel / mainFile ->\n                s\"\"\"object $mainClass extends App { println(Messages.msg) }\"\"\".stripMargin\n            ).fromRoot { root =>\n              os.proc(\n                TestUtil.cli,\n                \"--power\",\n                \"package\",\n                \"--cross\",\n                extraOptions,\n                \".\",\n                packageOpts\n              )\n                .call(cwd = root)\n\n              crossScalaVersions.foreach { version =>\n                val outputFilePath = root / s\"${mainClass}_$version$extension\"\n                expect(os.isFile(outputFilePath))\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  if actualScalaVersion.startsWith(\"3\") then\n    test(\"package Scala.js without a main method\") {\n      val moduleName = \"whatever\"\n      TestInputs(\n        os.rel / s\"$moduleName.scala\" ->\n          s\"\"\"import scala.scalajs.js.annotation.*\n             |\n             |object $moduleName {\n             |  @JSExportTopLevel(name = \"handler\", moduleID = \"$moduleName\")\n             |  def handler(): Unit = {\n             |    println(\"Hello world!\")\n             |  }\n             |}\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"package\", \".\", \"--js\", \"--power\", extraOptions)\n            .call(cwd = root, mergeErrIntoOut = true, stderr = os.Pipe)\n        expect(res.out.trim().contains(s\"$moduleName.js\"))\n      }\n    }\n\n  if (!Properties.isMac || !TestUtil.isCI)\n    test(\"--watching with --watch re-packages on external file change\") {\n      val sourceFile   = os.rel / \"Main.scala\"\n      val externalFile = os.rel / \"data\" / \"input.txt\"\n      TestInputs(\n        sourceFile ->\n          \"\"\"object Main extends App {\n            |  println(\"Hello\")\n            |}\n            |\"\"\".stripMargin,\n        externalFile -> \"Hello\"\n      ).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          proc = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"package\",\n            \".\",\n            \"--watch\",\n            \"--watching\",\n            \"data\",\n            \"-o\",\n            \"app\",\n            extraOptions\n          )\n            .spawn(cwd = root, mergeErrIntoOut = true),\n          timeout = 120.seconds\n        ) { (proc, timeout, ec) =>\n          implicit val ec0  = ec\n          val initialOutput = proc.readOutputUntilWatchingMessage(timeout)\n          expect(initialOutput.exists(_.contains(\"Wrote\")))\n\n          Thread.sleep(2000L)\n          os.write.over(root / externalFile, \"World\")\n\n          val rerunOutput = proc.readOutputUntilWatchingMessage(timeout)\n          expect(rerunOutput.nonEmpty)\n        }\n      }\n    }\n\n  test(\"sbt file in directory does not break package\") {\n    val message = \"Hello from package\"\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        s\"\"\"object Main {\n           |  def main(args: Array[String]): Unit = println(\"$message\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"build.sbt\" -> \"\"\"name := \"my-project\"\"\"\"\n    ).fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"package\", extraOptions, \".\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n      val launcher = root / (if Properties.isWin then \"Main.bat\" else \"Main\")\n      expect(os.isFile(launcher))\n      val output = TestUtil.maybeUseBash(launcher)(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PackageTests212.scala",
    "content": "package scala.cli.integration\n\nclass PackageTests212 extends PackageTestDefinitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PackageTests213.scala",
    "content": "package scala.cli.integration\n\nclass PackageTests213 extends PackageTestDefinitions with Test213\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PackageTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass PackageTests3Lts extends PackageTestDefinitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PackageTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass PackageTests3NextRc extends PackageTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PackageTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.file.Files\n\nimport scala.util.Properties\n\nclass PackageTestsDefault extends PackageTestDefinitions with TestDefault {\n  test(\"reuse run native binary\") {\n    TestUtil.retryOnCi() {\n      val inputs = TestInputs(\n        os.rel / \"Hello.scala\" ->\n          \"\"\"object Hello {\n            |  def main(args: Array[String]): Unit =\n            |    println(\"Hello\")\n            |}\n            |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val runRes = os.proc(TestUtil.cli, \"run\", \"--native\", \".\", extraOptions)\n          .call(cwd = root)\n        val runOutput = runRes.out.trim().linesIterator.filter(!_.startsWith(\"[info] \")).toVector\n        expect(runOutput == Seq(\"Hello\"))\n\n        val packageRes =\n          os.proc(TestUtil.cli, \"--power\", \"package\", \"--native\", \".\", \"-o\", \"hello\", extraOptions)\n            .call(cwd = root, mergeErrIntoOut = true)\n        val packageOutput    = packageRes.out.trim()\n        val topPackageOutput =\n          packageOutput.linesIterator.takeWhile(!_.startsWith(\"Wrote \")).toVector\n        expect(topPackageOutput.forall(!_.startsWith(\"[info] \")))\n      }\n    }\n  }\n\n  for {\n    (packageOpts, extension) <- Seq(\n      Nil              -> (if (Properties.isWin) \".bat\" else \"\"),\n      Seq(\"--library\") -> \".jar\"\n    ) ++\n      (if (!TestUtil.isNativeCli || !Properties.isWin) Seq(\n         Seq(\"--assembly\") -> \".jar\"\n       )\n       else Nil)\n    packageDescription = packageOpts.headOption.getOrElse(\"bootstrap\")\n    crossScalaVersions = Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n    numberOfBuilds     = crossScalaVersions.size\n  } {\n    test(s\"package --cross ($packageDescription) produces $numberOfBuilds artifacts\") {\n      TestUtil.retryOnCi() {\n        val mainClass = \"Main\"\n        val message   = \"Hello\"\n        TestInputs(\n          os.rel / \"project.scala\"     -> s\"//> using scala ${crossScalaVersions.mkString(\" \")}\",\n          os.rel / s\"$mainClass.scala\" ->\n            s\"\"\"object $mainClass extends App { println(\"$message\") }\"\"\"\n        ).fromRoot { root =>\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"package\",\n            \"--cross\",\n            extraOptions,\n            \".\",\n            packageOpts\n          ).call(cwd = root)\n\n          crossScalaVersions.foreach { version =>\n            val expectedFile = root / s\"${mainClass}_$version$extension\"\n            expect(os.isFile(expectedFile))\n          }\n\n          if packageDescription == \"bootstrap\" then\n            crossScalaVersions.foreach { version =>\n              val outputFile = root / s\"${mainClass}_$version$extension\"\n              expect(Files.isExecutable(outputFile.toNIO))\n              val output = TestUtil.maybeUseBash(outputFile)(cwd = root).out.trim()\n              expect(output == message)\n            }\n        }\n      }\n    }\n\n    test(s\"package without --cross ($packageDescription) produces single artifact\") {\n      TestUtil.retryOnCi() {\n        val mainClass = \"Main\"\n        val message   = \"Hello\"\n        TestInputs(\n          os.rel / \"project.scala\"     -> s\"//> using scala ${crossScalaVersions.mkString(\" \")}\",\n          os.rel / s\"$mainClass.scala\" ->\n            s\"\"\"object $mainClass extends App { println(\"$message\") }\"\"\"\n        ).fromRoot { root =>\n          val r = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"package\",\n            extraOptions,\n            \".\",\n            packageOpts\n          ).call(cwd = root, stderr = os.Pipe)\n\n          val expectedFile = root / s\"$mainClass$extension\"\n          expect(os.isFile(expectedFile))\n\n          expect(r.err.trim().contains(s\"ignoring ${numberOfBuilds - 1} builds\"))\n          expect(r.err.trim().contains(s\"Defaulting to Scala $actualScalaVersion\"))\n        }\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PgpTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\nclass PgpTests extends ScalaCliSuite {\n  private val pubKeyInputs = TestInputs(\n    os.rel / \"key.pub\" ->\n      \"\"\"-----BEGIN PGP PUBLIC KEY BLOCK-----\n        |Version: BCPG v1.68\n        |\n        |mQENBGKDqDYDCACw/2akQ1h6NHmtcEKQThq7OMdExyJ4WUKWdr+iV3BFx/AZICRd\n        |zU/tbDzvhqnwgBQalHfkcO8d1zIhzHhb3Hq3zo3Z0Gd1DcevODU7v2/GJkV/ici2\n        |xScYqJAmxkIM8yANiHrDZ17XrZ9m44YcK7qV9Z+PP2SNh3WtDPRzR//vL2yZgYm/\n        |KfzTuOzHqmne6Z0dVnc9jZTR6e7kNTHqIPpKvqIxFoqn2t0T6QZafYD1DdDmonyj\n        |1ME33vEec2llgJmIGHIVN3EcmQnBtfRP3mEqRZHmUGSAIrdqtLBegum0q8rvEBI4\n        |VIbg+xx7/5qnlfCIaCji4r/C2FlkrSiQgr+VABEBAAG0H2FsZXhhbmRyZS5hcmNo\n        |YW1iYXVsdEBnbWFpbC5jb22JAS4EEwMCABgFAmKDqDYCGwMECwkIBwYVCAIJCgsC\n        |HgEACgkQkU0pjfj6TSARjggAgbTFf1vjvHb1lwYP6jA5g3WFKHFSbh/fid/uOygq\n        |FqdD29KoE2tqmC9sE0koYMBpSJK4as5RDBKhliw8j60eBoYjRJZIDoc5oQQ7N7ul\n        |n0t5KMwpJ1CJs8G1BnU747Efg89blb3tY6LG4L+vziQ9Uw0d/0qizT1OWxGshqpA\n        |PJk4tHwQ3aujGbmVWttietdFVndcB4BB1s7UlGHFtwcAQyz5R8B3dfnaU4czyzM8\n        |zp+ltPXjLkuGvI/Bz3f15TZiUr1zEw0/ZAMmDdZvf8tGt8hhAp8n3DXL7HV4azdv\n        |iwZUribQnr1MHadTlKmUxz7+VfYNm8gkSRmGBl4YO5dl0rkBDQRig6g2AggApW+O\n        |XrTJrWM8asyur3UDOyXpvBZ1PeLIXyTrjVOHSiqqFjI8/P3faG6MQWv9YUN3cYIL\n        |SnIX9rAgchRpEWivqaYpj2hfyLOMkDUhw0owPo7vFXAX8uUXK5Fg/krSwGukY9+Z\n        |HcjzC9+CTEYZrBcxtziOWoGyOOk1Wepi1EqTWVB/xuY+hHWrhI5ff9bOdQmfcQL3\n        |Z9r2nJR3ahe+Qu5ppNGiX2FTLBMl5XwL0pOjonLyd39EsdbCbQvMnLgYwxwKMOht\n        |OPyXJAeOolchht50FWerTdf4r8fLndCiYg7hi5i0+GxWZwBj3MXNnkyQ0jjwiO/y\n        |p64RL2gJMrFwn7bv5wARAQABiQEfBBgDAgAJBQJig6g3AhsMAAoJEJFNKY34+k0g\n        |t1QH/06YazNGeuLzc62Mamnr8kA0AakGJxPZ83rGXcdahBRd9Enga32pcEks6YPI\n        |OZBbayEoIf4CasSaz9H/Bn1l91L60AEYeBwj8CFYx2ZZrC+ywdFkgbrVFP0N1Doj\n        |TMxim2rAJ8OFH2kczmhaG9HRL6V7kjGpb/tGdVpjgdt4V4NMDGQc4AWTWVCBcQKa\n        |3cHnXDgKrCE1inUej1bJ5g2SHm+gMyF7WAgbVi9r1/suu4d1WjJuUEQ28FmjpeEd\n        |CXxI+5gGgs4H7rUbTb1DScYsb4/j/Y/7SsOqnmz/SFA9Ej0scSaVviFwS06Q5LZN\n        |mnSk2Og334LRowks7+/8CEofK/w=\n        |=RTOn\n        |-----END PGP PUBLIC KEY BLOCK-----\n        |\"\"\".stripMargin\n  )\n\n  def pgpKeyIdTest(useSigningJvmLauncher: Boolean) =\n    pubKeyInputs.fromRoot { root =>\n      val signingCliArgs = if (useSigningJvmLauncher) Seq(\"--force-jvm-signing-cli\") else Seq.empty\n      val res            =\n        os.proc(TestUtil.cli, \"--power\", \"pgp\", \"key-id\", signingCliArgs, \"key.pub\").call(cwd =\n          root\n        )\n      val output         = res.out.trim()\n      val expectedOutput = \"914d298df8fa4d20\"\n      expect(output == expectedOutput)\n    }\n\n  test(\"pgp key-id\") {\n    pgpKeyIdTest(false)\n  }\n\n  if (TestUtil.isNativeCli)\n    test(\"pgp key-id - use jvm launcher of signing cli for native Scala CLI\") {\n      pgpKeyIdTest(true)\n    }\n\n  test(\"pgp pull\") {\n    // random key that I pushed to the default ker server at some point\n    val res = os.proc(TestUtil.cli, \"--power\", \"pgp\", \"pull\", \"0x914d298df8fa4d20\")\n      .call()\n    val output        = res.out.trim()\n    val start         = \"-----BEGIN PGP PUBLIC KEY BLOCK-----\"\n    val end           = \"-----END PGP PUBLIC KEY BLOCK-----\"\n    val expectedLines = Seq(\n      \"xsBNBGKDqDYDCACw/2akQ1h6NHmtcEKQThq7OMdExyJ4WUKWdr+iV3BFx/AZICRd\",\n      \"t1QH/06YazNGeuLzc62Mamnr8kA0AakGJxPZ83rGXcdahBRd9Enga32pcEks6YPI\",\n      \"mnSk2Og334LRowks7+/8CEofK/w=\"\n    )\n    expect(output.startsWith(start))\n    expect(output.endsWith(end))\n    val outputLines = output.linesIterator.toSet\n    for (expectedLine <- expectedLines)\n      expect(outputLines.contains(expectedLine))\n  }\n\n  test(\"pgp push\") {\n    pubKeyInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"pgp\", \"push\", \"key.pub\").call(cwd = root)\n    }\n  }\n\n  test(\"pgp push with binary\") {\n    pubKeyInputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"push\",\n        \"key.pub\",\n        \"--force-signing-externally\",\n        \"-v\",\n        \"-v\",\n        \"-v\"\n      ).call(\n        cwd = root,\n        stderr = os.Pipe\n      )\n      val errOutput = res.err.trim()\n\n      expect(errOutput.contains(\n        \"Getting https://github.com/VirtusLab/scala-cli-signing/releases/download/\"\n      ))\n      expect(\n        !errOutput.contains(\"coursier.cache.ArtifactError$NotFound\") ||\n        errOutput.contains(\n          \"\"\"Could not fetch binary, fetching JVM dependencies:\n            |  org.virtuslab.scala-cli-signing\"\"\".stripMargin\n        )\n      )\n    }\n  }\n\n  test(\"pgp push with external JVM process, java version too low\") {\n    pubKeyInputs.fromRoot { root =>\n      val java8Home =\n        os.Path(os.proc(TestUtil.cs, \"java-home\", \"--jvm\", \"zulu:8\").call().out.trim(), os.pwd)\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"push\",\n        \"key.pub\",\n        \"--force-signing-externally\",\n        \"--force-jvm-signing-cli\",\n        \"-v\",\n        \"-v\",\n        \"-v\"\n      ).call(\n        cwd = root,\n        env = Map(\n          \"JAVA_HOME\" -> java8Home.toString,\n          \"PATH\"      -> ((java8Home / \"bin\").toString + File.pathSeparator + System.getenv(\"PATH\"))\n        )\n      )\n    }\n  }\n\n  test(\"pgp create\") {\n    pubKeyInputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        \"test@test.com\",\n        \"--password\",\n        \"value:1234\",\n        \"--dest\",\n        \"new_key\"\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val tmpFile    = root / \"test-file\"\n      val tmpFileAsc = root / \"test-file.asc\"\n      os.write(tmpFile, \"Hello\")\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"sign\",\n        \"--password\",\n        \"value:1234\",\n        \"--secret-key\",\n        \"file:new_key.skr\",\n        tmpFile\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val verifyProc =\n        os.proc(TestUtil.cli, \"--power\", \"pgp\", \"verify\", \"--key\", \"new_key.pub\", tmpFileAsc)\n          .call(cwd = root, mergeErrIntoOut = true)\n\n      expect(verifyProc.out.text().contains(s\"$tmpFileAsc: valid signature\"))\n    }\n  }\n\n  test(\"pgp create no password\") {\n    pubKeyInputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        \"test@test.com\",\n        \"--dest\",\n        \"new_key\"\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val tmpFile    = root / \"test-file\"\n      val tmpFileAsc = root / \"test-file.asc\"\n      os.write(tmpFile, \"Hello\")\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"sign\",\n        \"--secret-key\",\n        \"file:new_key.skr\",\n        tmpFile\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val verifyProc =\n        os.proc(TestUtil.cli, \"--power\", \"pgp\", \"verify\", \"--key\", \"new_key.pub\", tmpFileAsc)\n          .call(cwd = root, mergeErrIntoOut = true)\n\n      expect(verifyProc.out.text().contains(s\"$tmpFileAsc: valid signature\"))\n    }\n  }\n\n  test(\"pgp sign wrong password\") {\n    pubKeyInputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        \"test@test.com\",\n        \"--password\",\n        \"value:1234\",\n        \"--dest\",\n        \"new_key\"\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n\n      val tmpFile = root / \"test-file\"\n      root / \"test-file.asc\"\n      os.write(tmpFile, \"Hello\")\n\n      val signProcWrongPassword = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"sign\",\n        \"--password\",\n        \"value:WRONG_PASSWORD\",\n        \"--secret-key\",\n        \"file:new_key.skr\",\n        tmpFile\n      )\n        .call(cwd = root, check = false, mergeErrIntoOut = true)\n\n      expect(signProcWrongPassword.exitCode != 0)\n      expect(signProcWrongPassword.out.text().contains(\n        \"Failed to decrypt the PGP secret key, make sure the provided password is correct!\"\n      ))\n\n      val signProcNoPassword = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"sign\",\n        \"--secret-key\",\n        \"file:new_key.skr\",\n        tmpFile\n      )\n        .call(cwd = root, check = false, mergeErrIntoOut = true)\n\n      expect(signProcNoPassword.exitCode != 0)\n      expect(signProcNoPassword.out.text().contains(\n        \"Failed to decrypt the PGP secret key, provide a password!\"\n      ))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishLocalTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nabstract class PublishLocalTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  protected def extraOptions: Seq[String] =\n    scalaVersionArgs ++ TestUtil.extraOptions ++ Seq(\"--suppress-experimental-feature-warning\")\n\n  def testedPublishedScalaVersion: String =\n    if (actualScalaVersion.startsWith(\"3\"))\n      actualScalaVersion.split('.').take(1).mkString\n    else actualScalaVersion.split('.').take(2).mkString(\".\")\n\n  def testPublishVersion: String = \"1.5.6\"\n\n  protected object PublishTestInputs {\n    def testOrg: String  = \"test-local-org.sth\"\n    def testName: String = \"my-proj\"\n    def projFile(\n      message: String,\n      exclude: Boolean = false,\n      useTestScope: Boolean = false,\n      crossVersions: Option[Seq[String]] = None\n    ): String =\n      s\"\"\"//> using scala ${crossVersions.map(\n          _.mkString(\" \")\n        ).getOrElse(testedPublishedScalaVersion)}\n         |${if (useTestScope) \"//> using target.scope test\" else \"\"}\n         |//> using dep com.lihaoyi::os-lib:0.11.3${Some(\",exclude=com.lihaoyi%%geny\").filter(_ =>\n          exclude\n        ).getOrElse(\"\")}\n         |\n         |object Project {\n         |  def message = \"$message\"\n         |\n         |  def main(args: Array[String]): Unit = {\n         |    os.pwd\n         |    println(message)\n         |  }\n         |}\n         |\"\"\".stripMargin\n\n    private def publishConfFile(includePublishVersion: Boolean): String = {\n      val publishVersionDirective =\n        if (includePublishVersion) s\"//> using publish.version $testPublishVersion\"\n        else \"\"\n      s\"\"\"//> using publish.organization $testOrg\n         |//> using publish.name $testName\n         |$publishVersionDirective\n         |\"\"\".stripMargin\n    }\n\n    lazy val projectFilePath: os.RelPath = os.rel / \"src\" / \"project.scala\"\n    lazy val projectConfPath: os.RelPath = os.rel / \"src\" / \"publish-conf.scala\"\n    def inputs(\n      message: String = \"Hello\",\n      includePublishVersion: Boolean = true,\n      excludeGeny: Boolean = false,\n      useTestScope: Boolean = false,\n      crossVersions: Option[Seq[String]] = None\n    ): TestInputs =\n      TestInputs(\n        projectFilePath -> projFile(message, excludeGeny, useTestScope, crossVersions),\n        projectConfPath -> publishConfFile(includePublishVersion)\n      )\n  }\n\n  for (includePublishVersion <- Seq(true, false)) {\n    val withPublishVersionString =\n      if (includePublishVersion) \" with publish.version\"\n      else \" without explicit publish.version, reading it from git:tag\"\n    test(s\"publish local$withPublishVersionString\") {\n      val expectedFiles = {\n        val modName   = s\"${PublishTestInputs.testName}_$testedPublishedScalaVersion\"\n        val base      = os.rel / PublishTestInputs.testOrg / modName / testPublishVersion\n        val baseFiles = Seq(\n          base / \"jars\" / s\"$modName.jar\",\n          base / \"docs\" / s\"$modName-javadoc.jar\",\n          base / \"srcs\" / s\"$modName-sources.jar\",\n          base / \"poms\" / s\"$modName.pom\",\n          base / \"ivys\" / \"ivy.xml\"\n        )\n        baseFiles\n          .flatMap { f =>\n            val md5  = f / os.up / s\"${f.last}.md5\"\n            val sha1 = f / os.up / s\"${f.last}.sha1\"\n            Seq(f, md5, sha1)\n          }\n          .toSet\n      }\n\n      PublishTestInputs.inputs(includePublishVersion = includePublishVersion)\n        .fromRoot { root =>\n          val ciOptions =\n            if (!includePublishVersion) {\n              TestUtil.initializeGit(cwd = root, tag = testPublishVersion)\n              Seq(\"--ci=false\") // when running on CI, version wouldn't be read from a git tag\n            }\n            else Seq.empty\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"publish\",\n            \"local\",\n            \".\",\n            \"--ivy2-home\",\n            os.rel / \"ivy2\",\n            extraOptions,\n            ciOptions\n          )\n            .call(cwd = root)\n          val ivy2Local  = root / \"ivy2\" / \"local\"\n          val foundFiles = os.walk(ivy2Local)\n            .filter(os.isFile(_))\n            .map(_.relativeTo(ivy2Local))\n            .toSet\n          val missingFiles    = expectedFiles -- foundFiles\n          val unexpectedFiles = foundFiles -- expectedFiles\n          if (missingFiles.nonEmpty)\n            pprint.err.log(missingFiles)\n          if (unexpectedFiles.nonEmpty)\n            pprint.err.log(unexpectedFiles)\n          expect(missingFiles.isEmpty)\n          expect(unexpectedFiles.isEmpty)\n        }\n    }\n  }\n\n  test(\"publish local twice\") {\n    PublishTestInputs.inputs().fromRoot { root =>\n      def publishLocal(): os.CommandResult =\n        os.proc(\n          TestUtil.cli,\n          \"publish\",\n          \"local\",\n          \".\",\n          \"--ivy2-home\",\n          os.rel / \"ivy2\",\n          \"--power\", // Test --power placed after subcommand name\n          \"--working-dir\",\n          os.rel / \"work-dir\",\n          extraOptions\n        )\n          .call(cwd = root)\n\n      def output(): String =\n        os.proc(\n          TestUtil.cs,\n          s\"-J-Divy.home=${root / \"ivy2\"}\",\n          \"launch\",\n          s\"${PublishTestInputs.testOrg}:${PublishTestInputs.testName}_$testedPublishedScalaVersion:$testPublishVersion\"\n        )\n          .call(cwd = root)\n          .out.trim()\n\n      val expectedMessage1 = \"Hello\"\n      val expectedMessage2 = \"olleH\"\n      publishLocal()\n      val output1 = output()\n      expect(output1 == expectedMessage1)\n\n      os.write.over(\n        root / PublishTestInputs.projectFilePath,\n        PublishTestInputs.projFile(expectedMessage2)\n      )\n      publishLocal()\n      val output2 = output()\n      expect(output2 == expectedMessage2)\n    }\n  }\n\n  test(\"publish local with ivy.home\") {\n    PublishTestInputs.inputs().fromRoot { root =>\n      def publishLocal(): os.CommandResult =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          \"--working-dir\",\n          os.rel / \"work-dir\",\n          extraOptions\n        )\n          .call(\n            cwd = root,\n            env = Map(\n              \"JAVA_OPTS\" -> s\"-Divy.home=${root / \"ivyhome\"} -Duser.home${root / \"userhome\"}\"\n            ) // ivy.home takes precedence\n          )\n\n      def output(): String =\n        os.proc(\n          TestUtil.cs,\n          s\"-J-Divy.home=${root / \"ivyhome\"}\",\n          \"launch\",\n          s\"${PublishTestInputs.testOrg}:${PublishTestInputs.testName}_$testedPublishedScalaVersion:$testPublishVersion\"\n        )\n          .call(cwd = root)\n          .out.trim()\n\n      publishLocal()\n      expect(output() == \"Hello\")\n    }\n  }\n\n  test(\"publish local with user.home\") {\n    PublishTestInputs.inputs().fromRoot { root =>\n      def publishLocal(): os.CommandResult =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          \"--working-dir\",\n          os.rel / \"work-dir\",\n          extraOptions\n        )\n          .call(\n            cwd = root,\n            env = Map(\n              \"JAVA_OPTS\" -> s\"-Duser.home=${root / \"userhome\"}\"\n            )\n          )\n\n      def output(): String =\n        os.proc(\n          TestUtil.cs,\n          s\"-J-Divy.home=${root / \"userhome\" / \".ivy2\"}\",\n          \"launch\",\n          s\"${PublishTestInputs.testOrg}:${PublishTestInputs.testName}_$testedPublishedScalaVersion:$testPublishVersion\"\n        )\n          .call(cwd = root)\n          .out.trim()\n\n      publishLocal()\n      expect(output() == \"Hello\")\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"publish local excluding a transitive dependency\") {\n      PublishTestInputs.inputs(excludeGeny = true).fromRoot { root =>\n        val failPublishAsGenyIsntProvided =\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"publish\",\n            \"local\",\n            \".\",\n            extraOptions\n          )\n            .call(cwd = root, check = false)\n        expect(failPublishAsGenyIsntProvided.exitCode == 1)\n        val genyDep = \"com.lihaoyi::geny:1.1.1\"\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          \"--compile-dep\",\n          genyDep,\n          extraOptions\n        )\n          .call(cwd = root)\n        val publishedDep =\n          s\"${PublishTestInputs.testOrg}:${PublishTestInputs.testName}_$testedPublishedScalaVersion:$testPublishVersion\"\n        val failRunAsGenyIsntProvided = os.proc(TestUtil.cli, \"run\", \"--dep\", publishedDep)\n          .call(cwd = root, check = false)\n        expect(failRunAsGenyIsntProvided.exitCode == 1)\n        os.proc(TestUtil.cli, \"run\", \"--dep\", publishedDep, \"--dep\", genyDep).call(cwd = root)\n      }\n    }\n\n    test(\"publish local without docs\") {\n      val expectedFiles = {\n        val modName   = s\"${PublishTestInputs.testName}_$testedPublishedScalaVersion\"\n        val base      = os.rel / PublishTestInputs.testOrg / modName / testPublishVersion\n        val baseFiles = Seq(\n          base / \"jars\" / s\"$modName.jar\",\n          base / \"srcs\" / s\"$modName-sources.jar\",\n          base / \"poms\" / s\"$modName.pom\",\n          base / \"ivys\" / \"ivy.xml\"\n        )\n        baseFiles\n          .flatMap { f =>\n            val md5  = f / os.up / s\"${f.last}.md5\"\n            val sha1 = f / os.up / s\"${f.last}.sha1\"\n            Seq(f, md5, sha1)\n          }\n          .toSet\n      }\n\n      PublishTestInputs.inputs()\n        .fromRoot { root =>\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"publish\",\n            \"local\",\n            \".\",\n            \"--ivy2-home\",\n            os.rel / \"ivy2\",\n            extraOptions,\n            \"--doc=false\"\n          )\n            .call(cwd = root)\n          val ivy2Local  = root / \"ivy2\" / \"local\"\n          val foundFiles = os.walk(ivy2Local)\n            .filter(os.isFile(_))\n            .map(_.relativeTo(ivy2Local))\n            .toSet\n          val missingFiles    = expectedFiles -- foundFiles\n          val unexpectedFiles = foundFiles -- expectedFiles\n          if (missingFiles.nonEmpty)\n            pprint.err.log(missingFiles)\n          if (unexpectedFiles.nonEmpty)\n            pprint.err.log(unexpectedFiles)\n          expect(missingFiles.isEmpty)\n          expect(unexpectedFiles.isEmpty)\n        }\n    }\n\n  test(\"publish local with test scope\") {\n    val expectedMessage = \"Hello\"\n    PublishTestInputs.inputs(message = expectedMessage, useTestScope = true).fromRoot { root =>\n      val scalaVersionArgs =\n        if (actualScalaVersion == Constants.scala3Next)\n          Seq(\"-S\", actualScalaVersion)\n        else Nil\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"local\",\n        \".\",\n        \"--test\",\n        scalaVersionArgs,\n        extraOptions\n      )\n        .call(cwd = root)\n      val publishedDep =\n        s\"${PublishTestInputs.testOrg}:${PublishTestInputs.testName}_$testedPublishedScalaVersion:$testPublishVersion\"\n      val r = os.proc(TestUtil.cli, \"run\", \"--dep\", publishedDep, extraOptions).call(cwd = root)\n      expect(r.out.trim() == expectedMessage)\n    }\n  }\n\n  test(\"publish local --m2\") {\n    val expectedFiles = {\n      val modName = s\"${PublishTestInputs.testName}_$testedPublishedScalaVersion\"\n      val base    =\n        os.rel / PublishTestInputs.testOrg.split('.').toSeq / modName / testPublishVersion\n      val baseFiles = Seq(\n        base / s\"$modName-$testPublishVersion.jar\",\n        base / s\"$modName-$testPublishVersion.pom\",\n        base / s\"$modName-$testPublishVersion-sources.jar\",\n        base / s\"$modName-$testPublishVersion-javadoc.jar\"\n      )\n      baseFiles\n        .flatMap { f =>\n          val md5  = f / os.up / s\"${f.last}.md5\"\n          val sha1 = f / os.up / s\"${f.last}.sha1\"\n          Seq(f, md5, sha1)\n        }\n        .toSet\n    }\n\n    PublishTestInputs.inputs()\n      .fromRoot { root =>\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          \"--m2\",\n          \"--m2-home\",\n          (root / \"m2repo\").toString,\n          extraOptions\n        )\n          .call(cwd = root)\n        val m2Local    = root / \"m2repo\"\n        val foundFiles = os.walk(m2Local)\n          .filter(os.isFile(_))\n          .map(_.relativeTo(m2Local))\n          .toSet\n        val missingFiles    = expectedFiles -- foundFiles\n        val unexpectedFiles = foundFiles -- expectedFiles\n        if (missingFiles.nonEmpty)\n          pprint.err.log(missingFiles)\n        if (unexpectedFiles.nonEmpty)\n          pprint.err.log(unexpectedFiles)\n        expect(missingFiles.isEmpty)\n        expect(unexpectedFiles.isEmpty)\n      }\n  }\n\n  test(\"publish local --m2 twice\") {\n    PublishTestInputs.inputs().fromRoot { root =>\n      val m2Repo  = root / \"m2repo\"\n      val modName = s\"${PublishTestInputs.testName}_$testedPublishedScalaVersion\"\n      val jarPath = m2Repo /\n        PublishTestInputs.testOrg.split('.').toSeq /\n        modName / testPublishVersion / s\"$modName-$testPublishVersion.jar\"\n\n      def publishLocal(): os.CommandResult =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          \"--m2\",\n          \"--m2-home\",\n          m2Repo.toString,\n          \"--working-dir\",\n          os.rel / \"work-dir\",\n          extraOptions\n        )\n          .call(cwd = root)\n\n      lazy val depsCp: String =\n        os.proc(\n          TestUtil.cs,\n          \"fetch\",\n          \"--classpath\",\n          s\"com.lihaoyi:os-lib_$testedPublishedScalaVersion:0.11.3\"\n        )\n          .call(cwd = root)\n          .out.trim()\n\n      def output(): String =\n        os.proc(\n          \"java\",\n          \"-cp\",\n          s\"$jarPath${java.io.File.pathSeparator}$depsCp\",\n          \"Project\"\n        )\n          .call(cwd = root)\n          .out.trim()\n\n      val expectedMessage1 = \"Hello\"\n      val expectedMessage2 = \"olleH\"\n      publishLocal()\n      val output1 = output()\n      expect(output1 == expectedMessage1)\n\n      os.write.over(\n        root / PublishTestInputs.projectFilePath,\n        PublishTestInputs.projFile(expectedMessage2)\n      )\n      publishLocal()\n      val output2 = output()\n      expect(output2 == expectedMessage2)\n    }\n  }\n\n  test(\"publish local ivy.xml includes license scm and developers\") {\n    val licenseId  = \"MIT\"\n    val licenseUrl = \"https://spdx.org/licenses/MIT.html\"\n    val vcsOrg     = \"it-integ\"\n    val vcsProj    = \"ivy-desc-test\"\n    val devId      = \"itest\"\n    val devName    = \"Integration Tester\"\n    val devUrl     = \"https://itest.example\"\n\n    val modName        = s\"${PublishTestInputs.testName}_$testedPublishedScalaVersion\"\n    val pomProjectName = \"Ivy metadata integration display name\"\n\n    val scmUrl             = s\"https://github.com/$vcsOrg/$vcsProj.git\"\n    val scmConnection      = s\"scm:git:github.com/$vcsOrg/$vcsProj.git\"\n    val scmDevConnection   = s\"scm:git:git@github.com:$vcsOrg/$vcsProj.git\"\n    val developerDirective = s\"$devId|$devName|$devUrl\"\n\n    val publishConf =\n      s\"\"\"//> using publish.organization ${PublishTestInputs.testOrg}\n         |//> using publish.moduleName $modName\n         |//> using publish.name \"$pomProjectName\"\n         |//> using publish.version $testPublishVersion\n         |//> using publish.license $licenseId:$licenseUrl\n         |//> using publish.scm github:$vcsOrg/$vcsProj\n         |//> using publish.developer \"$developerDirective\"\n         |\"\"\".stripMargin\n\n    TestInputs(\n      PublishTestInputs.projectFilePath -> PublishTestInputs.projFile(\"Hello\"),\n      PublishTestInputs.projectConfPath -> publishConf\n    ).fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"local\",\n        \".\",\n        \"--ivy2-home\",\n        os.rel / \"ivy2\",\n        extraOptions\n      )\n        .call(cwd = root)\n\n      val ivyPath = root / \"ivy2\" / \"local\" / PublishTestInputs.testOrg / modName /\n        testPublishVersion / \"ivys\" / \"ivy.xml\"\n      val pomPath = root / \"ivy2\" / \"local\" / PublishTestInputs.testOrg / modName /\n        testPublishVersion / \"poms\" / s\"$modName.pom\"\n      expect(os.exists(ivyPath))\n      expect(os.exists(pomPath))\n      val ivyXml = os.read(ivyPath)\n      val pomXml = os.read(pomPath)\n\n      expect(ivyXml.contains(s\"\"\"<license name=\"$licenseId\"\"\"\"))\n      expect(ivyXml.contains(s\"\"\"url=\"$licenseUrl\"\"\"\"))\n\n      expect(ivyXml.contains(\"\"\"xmlns:m=\"http://maven.apache.org/POM/4.0.0\"\"\"\"))\n      expect(ivyXml.contains(s\"<m:name>$pomProjectName</m:name>\"))\n      expect(ivyXml.contains(\"<m:scm>\"))\n      expect(ivyXml.contains(\"<m:developers>\"))\n\n      expect(ivyXml.contains(s\"<m:url>$scmUrl</m:url>\"))\n      expect(ivyXml.contains(s\"<m:connection>$scmConnection</m:connection>\"))\n      expect(ivyXml.contains(s\"<m:developerConnection>$scmDevConnection</m:developerConnection>\"))\n\n      expect(ivyXml.contains(s\"<m:id>$devId</m:id>\"))\n      expect(ivyXml.contains(s\"<m:name>$devName</m:name>\"))\n      expect(ivyXml.contains(s\"<m:url>$devUrl</m:url>\"))\n\n      expect(pomXml.contains(s\"<name>$pomProjectName</name>\"))\n    }\n  }\n\n  if actualScalaVersion.startsWith(\"3\") then\n    test(\"publish local with compileOnly.dep\") {\n      TestInputs(\n        os.rel / \"project.scala\" ->\n          s\"\"\"//> using compileOnly.dep org.springframework.boot:spring-boot:3.5.6\n             |//> using test.dep org.springframework.boot:spring-boot:3.5.6\n             |\n             |//> using publish.organization my.org\n             |//> using publish.name scala-cli-publish-bug\n             |//> using publish.version 1.0.0\n             |\"\"\".stripMargin,\n        os.rel / \"RootLoggerConfigurer.scala\" ->\n          s\"\"\"import org.springframework.beans.factory.annotation.Autowired\n             |import scala.compiletime.uninitialized\n             |\n             |class RootLoggerConfigurer:\n             |  @Autowired var sentryClient: String = uninitialized\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val scalaVersionArgs =\n          if (actualScalaVersion == Constants.scala3Next)\n            Seq(\"-S\", actualScalaVersion)\n          else Nil\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          scalaVersionArgs,\n          extraOptions\n        )\n          .call(cwd = root)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishLocalTests212.scala",
    "content": "package scala.cli.integration\n\nclass PublishLocalTests212 extends PublishLocalTestDefinitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishLocalTests213.scala",
    "content": "package scala.cli.integration\n\nclass PublishLocalTests213 extends PublishLocalTestDefinitions with Test213\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishLocalTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass PublishLocalTests3Lts extends PublishLocalTestDefinitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishLocalTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass PublishLocalTests3NextRc extends PublishLocalTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishLocalTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass PublishLocalTestsDefault extends PublishLocalTestDefinitions with TestDefault {\n  test(\n    s\"publish local --cross $actualScalaVersion with ${Constants.scala213} and ${Constants.scala212}\"\n  ) {\n    val expectedMessage = \"Hello\"\n    val crossVersions   = Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n    PublishTestInputs.inputs(message = expectedMessage, crossVersions = Some(crossVersions))\n      .fromRoot { root =>\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"local\",\n          \".\",\n          extraOptions,\n          \"--cross\"\n        )\n          .call(cwd = root)\n        def publishedDep(scalaVersionSuffix: String) =\n          s\"${PublishTestInputs.testOrg}:${PublishTestInputs.testName}_$scalaVersionSuffix:$testPublishVersion\"\n        val r3 =\n          os.proc(TestUtil.cli, \"run\", \"--dep\", publishedDep(\"3\"), extraOptions).call(cwd = root)\n        expect(r3.out.trim() == expectedMessage)\n        val r213 = os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--dep\",\n          publishedDep(\"2.13\"),\n          \"-S\",\n          Constants.scala213,\n          extraOptions\n        ).call(cwd = root)\n        expect(r213.out.trim() == expectedMessage)\n        val r212 = os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--dep\",\n          publishedDep(\"2.12\"),\n          \"-S\",\n          Constants.scala212,\n          extraOptions\n        ).call(cwd = root)\n        expect(r212.out.trim() == expectedMessage)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishSetupTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport com.virtuslab.using_directives.UsingDirectivesProcessor\nimport com.virtuslab.using_directives.reporter.ConsoleReporter\nimport org.eclipse.jgit.api.Git\nimport org.eclipse.jgit.transport.URIish\n\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\nimport scala.util.matching.Regex\n\nclass PublishSetupTests extends ScalaCliSuite {\n  private def ghUserName = \"foo\"\n  private def projName   = \"project-name\"\n  private def devName    = \"Alex Test\"\n  private def devMail    = \"alex@alex.me\"\n  private def devUrl     = \"https://alex.me\"\n  private def password   = \"password\"\n\n  private def configSetup(configFile: os.Path, root: os.Path): Unit = {\n    val envs = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n    os.proc(TestUtil.cli, \"--power\", \"config\", \"publish.user.name\", devName)\n      .call(cwd = root, stdout = os.Inherit, env = envs, stderr = os.Pipe)\n    os.proc(TestUtil.cli, \"--power\", \"config\", \"publish.user.email\", devMail)\n      .call(cwd = root, stdout = os.Inherit, env = envs, stderr = os.Pipe)\n    os.proc(TestUtil.cli, \"--power\", \"config\", \"publish.user.url\", devUrl)\n      .call(cwd = root, stdout = os.Inherit, env = envs, stderr = os.Pipe)\n    os.proc(\n      TestUtil.cli,\n      \"--power\",\n      \"config\",\n      \"publish.credentials\",\n      \"s01.oss.sonatype.org\",\n      \"value:uSeR\",\n      \"value:1234\"\n    )\n      .call(cwd = root, stdout = os.Inherit, env = envs, stderr = os.Pipe)\n    os.proc(\n      TestUtil.cli,\n      \"--power\",\n      \"config\",\n      \"publish.credentials\",\n      \"maven.pkg.github.com\",\n      \"value:uSeR\",\n      \"value:1234\"\n    )\n      .call(cwd = root, stdout = os.Inherit, env = envs, stderr = os.Pipe)\n    os.proc(TestUtil.cli, \"--power\", \"config\", \"--create-pgp-key\", \"--pgp-password\", \"random\")\n      .call(cwd = root, stdout = os.Inherit, env = envs, stderr = os.Pipe)\n  }\n\n  private val projDir    = os.rel / projName\n  private val testInputs = TestInputs(\n    os.rel / projDir / \"Foo.scala\" ->\n      \"\"\"object Foo\n        |\"\"\".stripMargin\n  )\n  private val configFile = os.rel / \"config\" / \"config.json\"\n  private val envs       = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString)\n\n  private def gitInit(dir: os.Path): Git = {\n    val git = Git.init().setDirectory(dir.toIO).call()\n    git\n      .remoteAdd()\n      .setName(\"origin\")\n      .setUri(new URIish(s\"https://github.com/$ghUserName/tests.git\"))\n      .call()\n    git\n  }\n\n  private def directives(content: String): Map[String, Seq[String]] = {\n    val reporter  = new ConsoleReporter\n    val processor = new UsingDirectivesProcessor(reporter)\n\n    val usedDirectives = processor\n      .extract(content.toCharArray)\n      .asScala\n      .head\n\n    usedDirectives\n      .getFlattenedMap\n      .asScala\n      .toSeq\n      .map {\n        case (k, l) =>\n          (k.getPath.asScala.mkString(\".\"), l.asScala.toSeq.map(_.toString))\n      }\n      .toMap\n  }\n\n  override def munitFlakyOK = TestUtil.isCI && TestUtil.isNativeCli && Properties.isMac\n\n  test(\"local\") {\n    val expectedDirectives = Map(\n      \"publish.versionControl\" -> Seq(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"   -> Seq(s\"io.github.$ghUserName\"),\n      \"publish.developer\"      -> Seq(s\"$devName|$devMail|$devUrl\"),\n      \"publish.repository\"     -> Seq(\"central-s01\"),\n      \"publish.url\"            -> Seq(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.name\"           -> Seq(projName),\n      \"publish.computeVersion\" -> Seq(\"git:tag\"),\n      \"publish.license\"        -> Seq(\"Apache-2.0\")\n    )\n    val expectedGhSecrets = Set.empty[String]\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"-R\",\n        \"central-s01\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      )\n\n      val ghSecrets = res.out.text()\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets == expectedGhSecrets)\n      expect(res.out.text().contains(\"found keys in config\"))\n    }\n  }\n\n  test(\"CI\".flaky) {\n    val expectedDirectives = Map(\n      \"publish.versionControl\"    -> List(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"      -> List(s\"io.github.$ghUserName\"),\n      \"publish.developer\"         -> List(s\"$devName|$devMail|$devUrl\"),\n      \"publish.name\"              -> List(projName),\n      \"publish.license\"           -> List(\"Apache-2.0\"),\n      \"publish.url\"               -> List(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.ci.secretKey\"      -> List(\"env:PUBLISH_SECRET_KEY\"),\n      \"publish.ci.user\"           -> List(\"env:PUBLISH_USER\"),\n      \"publish.ci.password\"       -> List(\"env:PUBLISH_PASSWORD\"),\n      \"publish.ci.publicKey\"      -> List(\"env:PUBLISH_PUBLIC_KEY\"),\n      \"publish.ci.repository\"     -> List(\"central-s01\"),\n      \"publish.ci.computeVersion\" -> List(\"git:tag\")\n    )\n    val expectedGhSecrets =\n      Set(\n        \"PUBLISH_USER\",\n        \"PUBLISH_PASSWORD\",\n        \"PUBLISH_SECRET_KEY\",\n        \"PUBLISH_PUBLIC_KEY\"\n      )\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n      val res =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          \"setup\",\n          \"--ci\",\n          \"--dummy\",\n          \"-R\",\n          \"central-s01\",\n          projDir\n        ).call(\n          cwd = root,\n          mergeErrIntoOut = true,\n          env = envs\n        )\n\n      val ghSecrets = res.out.text()\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets == expectedGhSecrets)\n    }\n  }\n\n  test(\"CI repository default\".flaky) {\n\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n      val res =\n        os.proc(TestUtil.cli, \"--power\", \"publish\", \"setup\", \"--ci\", \"--dummy\", projDir).call(\n          cwd = root,\n          mergeErrIntoOut = true,\n          check = false,\n          env = envs\n        )\n\n      expect(res.exitCode == 1)\n      expect(res.out.text().trim().contains(\"Missing repository for publishing\"))\n    }\n\n    val expectedDirectives = Map(\n      \"publish.versionControl\"    -> List(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"      -> List(s\"io.github.$ghUserName\"),\n      \"publish.developer\"         -> List(s\"$devName|$devMail|$devUrl\"),\n      \"publish.name\"              -> List(projName),\n      \"publish.license\"           -> List(\"Apache-2.0\"),\n      \"publish.url\"               -> List(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.repository\"        -> List(\"github\"),\n      \"publish.ci.secretKey\"      -> List(\"env:PUBLISH_SECRET_KEY\"),\n      \"publish.ci.user\"           -> List(\"env:PUBLISH_USER\"),\n      \"publish.ci.password\"       -> List(\"env:PUBLISH_PASSWORD\"),\n      \"publish.ci.publicKey\"      -> List(\"env:PUBLISH_PUBLIC_KEY\"),\n      \"publish.ci.repository\"     -> List(\"github\"),\n      \"publish.ci.computeVersion\" -> List(\"git:tag\")\n    )\n    val expectedGhSecrets =\n      Set(\n        \"PUBLISH_USER\",\n        \"PUBLISH_PASSWORD\",\n        \"PUBLISH_SECRET_KEY\",\n        \"PUBLISH_PUBLIC_KEY\"\n      )\n\n    testInputs.add(\n      os.rel / projDir / \"publish-conf.scala\" ->\n        \"\"\"//> using publish.repository github\n          |\"\"\".stripMargin\n    )\n      .fromRoot { root =>\n        configSetup(root / configFile, root)\n        gitInit(root / projDir)\n        val res =\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"publish\",\n            \"setup\",\n            \"-v\",\n            \"-v\",\n            \"-v\",\n            \"--ci\",\n            \"--dummy\",\n            projDir\n          ).call(\n            cwd = root,\n            mergeErrIntoOut = true,\n            env = envs\n          )\n\n        expect(res.exitCode == 0)\n        val ghSecrets = res.out.text()\n          .linesIterator\n          .filter(_.startsWith(\"Would have set GitHub secret \"))\n          .map(_.stripPrefix(\"Would have set GitHub secret \"))\n          .toSet\n        val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n        expect(directives0 == expectedDirectives)\n        expect(ghSecrets == expectedGhSecrets)\n      }\n  }\n\n  test(\"local GitHub\") {\n    val expectedDirectives = Map(\n      \"publish.versionControl\" -> Seq(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"   -> Seq(s\"io.github.$ghUserName\"),\n      \"publish.developer\"      -> Seq(s\"$devName|$devMail|$devUrl\"),\n      \"publish.repository\"     -> Seq(\"github\"),\n      \"publish.url\"            -> Seq(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.name\"           -> Seq(projName),\n      \"publish.computeVersion\" -> Seq(\"git:tag\"),\n      \"publish.license\"        -> List(\"Apache-2.0\")\n    )\n    val expectedGhSecrets = Set.empty[String]\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"--publish-repository\",\n        \"github\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      )\n\n      val ghSecrets = res.out.text()\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets == expectedGhSecrets)\n    }\n  }\n\n  test(\"CI GitHub\".flaky) {\n    val expectedDirectives = Map(\n      \"publish.versionControl\"    -> List(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"      -> List(s\"io.github.$ghUserName\"),\n      \"publish.ci.user\"           -> List(\"env:PUBLISH_USER\"),\n      \"publish.developer\"         -> List(s\"$devName|$devMail|$devUrl\"),\n      \"publish.ci.password\"       -> List(\"env:PUBLISH_PASSWORD\"),\n      \"publish.name\"              -> List(projName),\n      \"publish.license\"           -> List(\"Apache-2.0\"),\n      \"publish.url\"               -> List(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.ci.repository\"     -> List(\"github\"),\n      \"publish.ci.computeVersion\" -> List(\"git:tag\")\n    )\n    val expectedGhSecrets = Set(\"PUBLISH_USER\", \"PUBLISH_PASSWORD\")\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--ci\",\n        \"--publish-repository\",\n        \"github\",\n        \"--dummy\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      )\n\n      val ghSecrets = res.out.text()\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets == expectedGhSecrets)\n    }\n  }\n\n  test(\"local upload key\") {\n    val expectedDirectives = Map(\n      \"publish.versionControl\"    -> Seq(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"      -> Seq(s\"io.github.$ghUserName\"),\n      \"publish.developer\"         -> Seq(s\"$devName|$devMail|$devUrl\"),\n      \"publish.repository\"        -> Seq(\"central-s01\"),\n      \"publish.url\"               -> Seq(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.name\"              -> Seq(projName),\n      \"publish.computeVersion\"    -> Seq(\"git:tag\"),\n      \"publish.license\"           -> Seq(\"Apache-2.0\"),\n      \"publish.secretKey\"         -> Seq(\"file:key.skr\"),\n      \"publish.secretKeyPassword\" -> Seq(\"file:whatever_not_checked\"),\n      \"publish.publicKey\"         -> Seq(\"file:key.pub\")\n    )\n    val expectedGhSecrets = Set.empty[String]\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n\n      val pgpCreateOutput = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        devMail,\n        \"--password\",\n        s\"value:$password\"\n      )\n        .call(\n          cwd = root,\n          mergeErrIntoOut = true,\n          env = envs\n        )\n\n      val publicKeyRegex: Regex = \"Wrote public key (\\\\w+) .*\".r\n\n      val publicKeyIdOpt = pgpCreateOutput.out.text()\n        .linesIterator\n        .toSeq\n        .collect {\n          case publicKeyRegex(publicKeyId) => publicKeyId\n        }\n        .headOption\n\n      expect(publicKeyIdOpt.isDefined)\n\n      val publicKeyId: String = publicKeyIdOpt.get\n\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"-R\",\n        \"central-s01\",\n        \"--secret-key\",\n        \"file:key.skr\",\n        \"--secret-key-password\",\n        s\"file:whatever_not_checked\",\n        \"--public-key\",\n        \"file:key.pub\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      ).out.text()\n\n      val ghSecrets = res\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets == expectedGhSecrets)\n      expect(!res.contains(\"found keys in config\"))\n      expect(res.contains(s\"Would upload key 0x$publicKeyId\"))\n    }\n  }\n\n  test(\"local add public key and password\") {\n    val expectedDirectives = Map(\n      \"publish.versionControl\" -> Seq(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"   -> Seq(s\"io.github.$ghUserName\"),\n      \"publish.developer\"      -> Seq(s\"$devName|$devMail|$devUrl\"),\n      \"publish.repository\"     -> Seq(\"central-s01\"),\n      \"publish.url\"            -> Seq(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.name\"           -> Seq(projName),\n      \"publish.computeVersion\" -> Seq(\"git:tag\"),\n      \"publish.license\"        -> Seq(\"Apache-2.0\"),\n      \"publish.secretKey\"      -> Seq(\"file:key.skr\")\n    )\n    val expectedGhSecrets = Set.empty[String]\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n\n      val pgpCreateOutput = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        devMail,\n        \"--password\",\n        s\"value:$password\"\n      )\n        .call(\n          cwd = root,\n          mergeErrIntoOut = true,\n          env = envs\n        )\n\n      val publicKeyRegex: Regex = \"Wrote public key (\\\\w+) .*\".r\n\n      val publicKeyIdOpt = pgpCreateOutput.out.text()\n        .linesIterator\n        .toSeq\n        .collect {\n          case publicKeyRegex(publicKeyId) => publicKeyId\n        }\n        .headOption\n\n      expect(publicKeyIdOpt.isDefined)\n\n      val publicKeyId: String = publicKeyIdOpt.get\n\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"-R\",\n        \"central-s01\",\n        \"--secret-key\",\n        \"file:key.skr\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      ).out.text()\n\n      val ghSecrets = res\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets == expectedGhSecrets)\n      expect(!res.contains(\"found keys in config\"))\n      expect(res.contains(\"Warning: no public key passed, not checking\"))\n\n      val res2 = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"--secret-key-password\",\n        s\"file:whatever_not_checked\",\n        \"--public-key\",\n        \"file:key.pub\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      )\n\n      val ghSecrets2 = res2.out.text()\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives2             = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      val expectedDirectivesAdded = Map(\n        \"publish.secretKeyPassword\" -> Seq(\"file:whatever_not_checked\"),\n        \"publish.publicKey\"         -> Seq(\"file:key.pub\")\n      )\n\n      expect(directives2 == (expectedDirectives ++ expectedDirectivesAdded))\n      expect(ghSecrets2 == expectedGhSecrets)\n      expect(!res2.out.text().contains(\"found keys in config\"))\n      expect(res2.out.text().contains(s\"Would upload key 0x$publicKeyId\"))\n    }\n  }\n\n  test(\"local secret value NOT written\") {\n    val expectedDirectives = Map(\n      \"publish.versionControl\" -> Seq(s\"github:$ghUserName/tests\"),\n      \"publish.organization\"   -> Seq(s\"io.github.$ghUserName\"),\n      \"publish.developer\"      -> Seq(s\"$devName|$devMail|$devUrl\"),\n      \"publish.repository\"     -> Seq(\"central-s01\"),\n      \"publish.url\"            -> Seq(s\"https://github.com/$ghUserName/tests\"),\n      \"publish.name\"           -> Seq(projName),\n      \"publish.computeVersion\" -> Seq(\"git:tag\"),\n      \"publish.license\"        -> Seq(\"Apache-2.0\")\n      // SHOULD NOT BE HERE \"publish.secretKey\"      -> Seq(\"value:whatever\")\n    )\n\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"-R\",\n        \"central-s01\",\n        \"--secret-key\",\n        \"value:whatever\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        env = envs\n      ).out.text()\n\n      expect(res.contains(\n        s\"The value of PGP secret key ${Console.BOLD}will not${Console.RESET} be written to a potentially public file!\"\n      ))\n\n      val ghSecrets = res\n        .linesIterator\n        .filter(_.startsWith(\"Would have set GitHub secret \"))\n        .map(_.stripPrefix(\"Would have set GitHub secret \"))\n        .toSet\n      val directives0 = directives(os.read(root / projDir / \"publish-conf.scala\"))\n      expect(directives0 == expectedDirectives)\n      expect(ghSecrets.isEmpty)\n      expect(!res.contains(\"found keys in config\"))\n      expect(res.contains(\"Warning: no public key passed, not checking\"))\n    }\n  }\n\n  test(\"Mistyped repo\") {\n    testInputs.fromRoot { root =>\n      configSetup(root / configFile, root)\n      gitInit(root / projDir)\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"setup\",\n        \"--dummy\",\n        \"-R\",\n        \"central-s02\",\n        projDir\n      ).call(\n        cwd = root,\n        mergeErrIntoOut = true,\n        check = false,\n        env = envs\n      )\n      expect(res.out.text().contains(\"Missing publish user for central-s02 for publishing\"))\n      expect(res.out.text().contains(\"Missing publish password for central-s02 for publishing\"))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\nimport java.nio.file.Paths\nimport java.util.zip.ZipFile\n\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nabstract class PublishTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  protected def extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n\n  protected object TestCase {\n    val expectedMessage    = \"Hello\"\n    val org                = \"org.virtuslab.scalacli.test\"\n    val name               = \"simple\"\n    val version            = \"0.2.0-SNAPSHOT\"\n    val url                = \"https://github.com/VirtusLab/scala-cli\"\n    val license            = \"Apache 2.0:http://opensource.org/licenses/Apache-2.0\"\n    val developer          = \"someone|Someone||https://github.com/someone\"\n    val testScopeMainClass = \"HelloFromTestScope\"\n    def testInputs(useTestScope: Boolean = false): TestInputs = {\n      val inputs = TestInputs(\n        os.rel / \"project\" / \"foo\" / \"Hello.scala\" ->\n          s\"\"\"//> using publish.organization $org\n             |//> using publish.name $name\n             |//> using publish.version $version\n             |//> using publish.url $url\n             |//> using publish.license \"$license\"\n             |//> using publish.developer $developer\n             |\n             |package foo\n             |\n             |object Hello {\n             |  def main(args: Array[String]): Unit =\n             |    println(Messages.hello)\n             |}\n             |\"\"\".stripMargin,\n        os.rel / \"project\" / \"foo\" / \"Messages.scala\" ->\n          s\"\"\"package foo\n             |\n             |object Messages {\n             |  def hello = \"$expectedMessage\"\n             |}\n             |\"\"\".stripMargin\n      )\n      if (useTestScope)\n        inputs.add(os.rel / \"project\" / \"foo\" / \"test\" / s\"$testScopeMainClass.scala\" ->\n          s\"\"\"object $testScopeMainClass {\n             |  def main(args: Array[String]): Unit =\n             |    println(foo.Messages.hello)\n             |}\n             |\"\"\".stripMargin)\n      else inputs\n    }\n    val scalaSuffix: String =\n      if (actualScalaVersion.startsWith(\"3.\")) \"_3\"\n      else \"_\" + actualScalaVersion.split('.').take(2).mkString(\".\")\n    val dependency                       = s\"$org:$name$scalaSuffix:$version\"\n    val expectedArtifactsDir: os.RelPath =\n      os.rel / \"org\" / \"virtuslab\" / \"scalacli\" / \"test\" / s\"simple$scalaSuffix\" / \"0.2.0-SNAPSHOT\"\n    val expectedJsArtifactsDir: os.RelPath =\n      os.rel / \"org\" / \"virtuslab\" / \"scalacli\" / \"test\" / s\"simple_sjs1$scalaSuffix\" /\n        \"0.2.0-SNAPSHOT\"\n  }\n\n  val baseExpectedArtifacts: Set[String] = Set(\n    s\"simple${TestCase.scalaSuffix}-0.2.0-SNAPSHOT.pom\",\n    s\"simple${TestCase.scalaSuffix}-0.2.0-SNAPSHOT.jar\",\n    s\"simple${TestCase.scalaSuffix}-0.2.0-SNAPSHOT-javadoc.jar\",\n    s\"simple${TestCase.scalaSuffix}-0.2.0-SNAPSHOT-sources.jar\"\n  )\n\n  val expectedArtifacts: Set[os.RelPath] = baseExpectedArtifacts\n    .flatMap { n =>\n      Seq(n, n + \".asc\")\n    }\n    .flatMap { n =>\n      Seq(\"\", \".md5\", \".sha1\").map(n + _)\n    }\n    .map(os.rel / _)\n\n  def expectedSourceEntries(withTestScope: Boolean = false): Set[String] = Set(\n    \"foo/Hello.scala\",\n    \"foo/Messages.scala\"\n  ) ++ (if (withTestScope) Set(\"foo/test/HelloFromTestScope.scala\") else Set.empty)\n\n  for {\n    useTestScope <- Seq(true, false)\n    scopeOpts        = if (useTestScope) Seq(\"--test\") else Nil\n    scopeDescription = scopeOpts.headOption.getOrElse(\"main\")\n  }\n    test(s\"simple ($scopeDescription)\") {\n      val publicKey = {\n        val uri = Thread.currentThread().getContextClassLoader\n          .getResource(\"test-keys/key.asc\")\n          .toURI\n        os.Path(Paths.get(uri))\n      }\n      val secretKey = {\n        val uri = Thread.currentThread().getContextClassLoader\n          .getResource(\"test-keys/key.skr\")\n          .toURI\n        os.Path(Paths.get(uri))\n      }\n\n      val signingOptions = Seq(\n        \"--secret-key\",\n        s\"file:$secretKey\",\n        \"--secret-key-password\",\n        \"value:1234\",\n        \"--signer\",\n        \"bc\"\n      )\n\n      TestCase.testInputs(useTestScope).fromRoot { root =>\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          extraOptions,\n          signingOptions,\n          \"project\",\n          \"-R\",\n          \"test-repo\",\n          scopeOpts\n        ).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit\n        )\n\n        val files = os.walk(root / \"test-repo\")\n          .filter(os.isFile(_))\n          .map(_.relativeTo(root / \"test-repo\"))\n        val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir))\n        expect(notInDir.isEmpty)\n\n        val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet\n\n        expect((files0 -- expectedArtifacts).isEmpty)\n        expect((expectedArtifacts -- files0).isEmpty)\n        expect(files0 == expectedArtifacts) // just in case…\n\n        val repoArgs =\n          Seq[os.Shellable](\"-r\", \"!central\", \"-r\", (root / \"test-repo\").toNIO.toUri.toASCIIString)\n        val mainClassOptions =\n          if (useTestScope) Seq(\"-M\", TestCase.testScopeMainClass) else Nil\n        val dep = s\"org.virtuslab.scalacli.test:simple${TestCase.scalaSuffix}:0.2.0-SNAPSHOT\"\n        val res = os.proc(TestUtil.cs, \"launch\", repoArgs, dep, mainClassOptions).call(cwd = root)\n        val output = res.out.trim()\n        expect(output == TestCase.expectedMessage)\n\n        val sourceJarViaCsStr =\n          os.proc(TestUtil.cs, \"fetch\", repoArgs, \"--sources\", \"--intransitive\", dep)\n            .call(cwd = root)\n            .out.trim()\n        val sourceJarViaCs = os.Path(sourceJarViaCsStr, os.pwd)\n        val zf             = new ZipFile(sourceJarViaCs.toIO)\n        val entries        = zf.entries().asScala.toVector.map(_.getName).toSet\n        expect(entries == expectedSourceEntries(useTestScope))\n\n        val signatures = expectedArtifacts.filter(_.last.endsWith(\".asc\"))\n        assert(signatures.nonEmpty)\n        val verifyProc = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"pgp\",\n          \"verify\",\n          \"--key\",\n          publicKey,\n          signatures.map(os.rel / \"test-repo\" / TestCase.expectedArtifactsDir / _)\n        )\n          .call(cwd = root, mergeErrIntoOut = true)\n        expect(!verifyProc.out.text().contains(s\"invalid signature\"))\n\n        val r = os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--dep\",\n          TestCase.dependency,\n          \"-r\",\n          (root / \"test-repo\").toNIO.toUri.toASCIIString,\n          mainClassOptions\n        ).call(cwd = root)\n        expect(r.out.trim() == TestCase.expectedMessage)\n      }\n    }\n\n  if !isScala38OrNewer then {\n    // I'm not sure this test is even relevant anymore, but in the current shape it won't pass for 3.8+\n    // TODO potentially consider if we want to port this to 3.8\n    test(\"simple sign with external JVM process, java version too low\") {\n      TestUtil.retryOnCi() {\n        val publicKey = {\n          val uri = Thread.currentThread().getContextClassLoader\n            .getResource(\"test-keys/key.asc\")\n            .toURI\n          os.Path(Paths.get(uri))\n        }\n        val secretKey = {\n          val uri = Thread.currentThread().getContextClassLoader\n            .getResource(\"test-keys/key.skr\")\n            .toURI\n          os.Path(Paths.get(uri))\n        }\n\n        val signingOptions = Seq(\n          \"--secret-key\",\n          s\"file:$secretKey\",\n          \"--secret-key-password\",\n          \"value:1234\",\n          \"--signer\",\n          \"bc\",\n          \"--force-signing-externally\",\n          \"--force-jvm-signing-cli\"\n        )\n\n        val java8Home =\n          os.Path(os.proc(TestUtil.cs, \"java-home\", \"--jvm\", \"zulu:8\").call().out.trim(), os.pwd)\n\n        val extraEnv = Map(\n          \"JAVA_HOME\" -> java8Home.toString,\n          \"PATH\"      -> ((java8Home / \"bin\").toString + File.pathSeparator + System.getenv(\"PATH\"))\n        )\n\n        TestCase.testInputs().fromRoot { root =>\n          val publishRes = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"publish\",\n            extraOptions,\n            signingOptions,\n            \"project\",\n            \"-R\",\n            \"test-repo\",\n            \"-v\",\n            \"-v\",\n            \"-v\"\n          ).call(\n            cwd = root,\n            mergeErrIntoOut = true,\n            env = extraEnv\n          )\n\n          val javaCommandOpt = publishRes.out.text()\n            .linesIterator\n            .find(_.contains(\"Running command \"))\n\n          expect(javaCommandOpt.isDefined)\n          expect(javaCommandOpt.get.contains(\" -cp,\"))\n          expect(javaCommandOpt.get.split(\" -cp,\").headOption.exists(_.contains(\"17\")))\n\n          val files = os.walk(root / \"test-repo\")\n            .filter(os.isFile(_))\n            .map(_.relativeTo(root / \"test-repo\"))\n          val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir))\n          expect(notInDir.isEmpty)\n\n          val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet\n\n          expect((files0 -- expectedArtifacts).isEmpty)\n          expect((expectedArtifacts -- files0).isEmpty)\n          expect(files0 == expectedArtifacts) // just in case…\n\n          val repoArgs =\n            Seq[os.Shellable](\n              \"-r\",\n              \"!central\",\n              \"-r\",\n              (root / \"test-repo\").toNIO.toUri.toASCIIString\n            )\n          val dep    = s\"org.virtuslab.scalacli.test:simple${TestCase.scalaSuffix}:0.2.0-SNAPSHOT\"\n          val res    = os.proc(TestUtil.cs, \"launch\", repoArgs, dep).call(cwd = root)\n          val output = res.out.trim()\n          expect(output == \"Hello\")\n\n          val sourceJarViaCsStr =\n            os.proc(TestUtil.cs, \"fetch\", repoArgs, \"--sources\", \"--intransitive\", dep)\n              .call(cwd = root)\n              .out.trim()\n          val sourceJarViaCs = os.Path(sourceJarViaCsStr, os.pwd)\n          val zf             = new ZipFile(sourceJarViaCs.toIO)\n          val entries        = zf.entries().asScala.toVector.map(_.getName).toSet\n          expect(entries == expectedSourceEntries())\n\n          val signatures = expectedArtifacts.filter(_.last.endsWith(\".asc\"))\n          assert(signatures.nonEmpty)\n          val verifyProc = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"pgp\",\n            \"verify\",\n            \"--key\",\n            publicKey,\n            signatures.map(os.rel / \"test-repo\" / TestCase.expectedArtifactsDir / _)\n          )\n            .call(cwd = root, mergeErrIntoOut = true)\n\n          expect(!verifyProc.out.text().contains(s\"invalid signature\"))\n        }\n      }\n    }\n  }\n\n  test(\"artifacts name for scalajs\") {\n    val baseExpectedArtifacts = Seq(\n      s\"simple_sjs1${TestCase.scalaSuffix}-0.2.0-SNAPSHOT.pom\",\n      s\"simple_sjs1${TestCase.scalaSuffix}-0.2.0-SNAPSHOT.jar\",\n      s\"simple_sjs1${TestCase.scalaSuffix}-0.2.0-SNAPSHOT-javadoc.jar\",\n      s\"simple_sjs1${TestCase.scalaSuffix}-0.2.0-SNAPSHOT-sources.jar\"\n    )\n    val expectedArtifacts = baseExpectedArtifacts\n      .flatMap { n =>\n        Seq(\"\", \".md5\", \".sha1\").map(n + _)\n      }\n      .map(os.rel / _)\n      .toSet\n\n    TestCase.testInputs().fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        \"project\",\n        \"--js\",\n        \"-R\",\n        \"test-repo\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val files = os.walk(root / \"test-repo\")\n        .filter(os.isFile(_))\n        .map(_.relativeTo(root / \"test-repo\"))\n      val notInDir = files.filter(!_.startsWith(TestCase.expectedJsArtifactsDir))\n      expect(notInDir.isEmpty)\n\n      val files0 = files.map(_.relativeTo(TestCase.expectedJsArtifactsDir)).toSet\n\n      expect((files0 -- expectedArtifacts).isEmpty)\n      expect((expectedArtifacts -- files0).isEmpty)\n    }\n  }\n\n  test(\"custom checksums\") {\n    val baseExpectedArtifacts = Seq(\n      s\"simple${TestCase.scalaSuffix}-0.2.0-SNAPSHOT.pom\",\n      s\"simple${TestCase.scalaSuffix}-0.2.0-SNAPSHOT.jar\"\n    )\n    val expectedArtifacts = baseExpectedArtifacts\n      .flatMap { n =>\n        Seq(\"\", \".sha1\").map(n + _)\n      }\n      .map(os.rel / _)\n      .toSet\n\n    TestCase.testInputs().fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        \"--with-sources=false\",\n        \"--doc=false\",\n        \"--checksum\",\n        \"sha-1\",\n        \"project\",\n        \"-R\",\n        \"test-repo\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val files = os.walk(root / \"test-repo\")\n        .filter(os.isFile(_))\n        .map(_.relativeTo(root / \"test-repo\"))\n      val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir))\n      expect(notInDir.isEmpty)\n\n      val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet\n\n      expect((files0 -- expectedArtifacts).isEmpty)\n      expect((expectedArtifacts -- files0).isEmpty)\n      expect(files0 == expectedArtifacts) // just in case…\n    }\n  }\n\n  test(\"correctly list main classes\") {\n    val (scalaFile1, scalaFile2, scriptName) = (\"ScalaMainClass1\", \"ScalaMainClass2\", \"ScalaScript\")\n    val scriptsDir                           = \"scripts\"\n    val inputs                               = TestInputs(\n      os.rel / s\"$scalaFile1.scala\"           -> s\"object $scalaFile1 extends App { println() }\",\n      os.rel / s\"$scalaFile2.scala\"           -> s\"object $scalaFile2 extends App { println() }\",\n      os.rel / scriptsDir / s\"$scriptName.sc\" -> \"println()\"\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        \".\",\n        \"--list-main-classes\"\n      )\n        .call(cwd = root)\n      val output   = res.out.trim()\n      val resLocal = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"local\",\n        extraOptions,\n        \".\",\n        \"--list-main-classes\"\n      )\n        .call(cwd = root)\n      val outputLocal = resLocal.out.trim()\n      expect(output == outputLocal)\n      val mainClasses = output.linesIterator.toSeq.last.split(\" \").toSet\n\n      val scriptMainClassName = if (actualScalaVersion.startsWith(\"3\"))\n        s\"$scriptsDir.${scriptName}_sc\"\n      else\n        s\"$scriptsDir.$scriptName\"\n\n      expect(mainClasses == Set(scalaFile1, scalaFile2, scriptMainClassName))\n    }\n  }\n\n  test(\"no secret key password\") {\n    val signingOptions = Seq(\n      \"--secret-key\",\n      s\"file:key.skr\",\n      \"--signer\",\n      \"bc\"\n    )\n\n    TestCase.testInputs().fromRoot { root =>\n      val confDir  = root / \"config\"\n      val confFile = confDir / \"test-config.json\"\n\n      os.write(confFile, \"{}\", createFolders = true)\n\n      if (!Properties.isWin)\n        os.perms.set(confDir, \"rwx------\")\n\n      val extraEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        \"some_email\"\n      ).call(cwd = root, env = extraEnv)\n\n      val publicKey = os.Path(\"key.pub\", root)\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        signingOptions,\n        \"project\",\n        \"-R\",\n        \"test-repo\"\n      ).call(cwd = root, env = extraEnv)\n\n      val files = os.walk(root / \"test-repo\")\n        .filter(os.isFile(_))\n        .map(_.relativeTo(root / \"test-repo\"))\n      val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir))\n      expect(notInDir.isEmpty)\n\n      val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet\n\n      expect((files0 -- expectedArtifacts).isEmpty)\n      expect((expectedArtifacts -- files0).isEmpty)\n      expect(files0 == expectedArtifacts) // just in case…\n\n      val repoArgs =\n        Seq[os.Shellable](\"-r\", \"!central\", \"-r\", (root / \"test-repo\").toNIO.toUri.toASCIIString)\n      val dep    = s\"org.virtuslab.scalacli.test:simple${TestCase.scalaSuffix}:0.2.0-SNAPSHOT\"\n      val res    = os.proc(TestUtil.cs, \"launch\", repoArgs, dep).call(cwd = root)\n      val output = res.out.trim()\n      expect(output == \"Hello\")\n\n      val sourceJarViaCsStr =\n        os.proc(TestUtil.cs, \"fetch\", repoArgs, \"--sources\", \"--intransitive\", dep)\n          .call(cwd = root)\n          .out.trim()\n      val sourceJarViaCs = os.Path(sourceJarViaCsStr, os.pwd)\n      val zf             = new ZipFile(sourceJarViaCs.toIO)\n      val entries        = zf.entries().asScala.toVector.map(_.getName).toSet\n      expect(entries == expectedSourceEntries())\n\n      val signatures = expectedArtifacts.filter(_.last.endsWith(\".asc\"))\n      assert(signatures.nonEmpty)\n      val verifyProc = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"verify\",\n        \"--key\",\n        publicKey,\n        signatures.map(os.rel / \"test-repo\" / TestCase.expectedArtifactsDir / _)\n      ).call(cwd = root, env = extraEnv, mergeErrIntoOut = true)\n\n      expect(!verifyProc.out.text().contains(s\"invalid signature\"))\n    }\n  }\n\n  if (!Properties.isWin) // TODO: fix intermittent failures on Windows\n    test(\"secret keys in config\") {\n\n      TestCase.testInputs().fromRoot { root =>\n        val confDir  = root / \"config\"\n        val confFile = confDir / \"test-config.json\"\n\n        os.write(confFile, \"{}\", createFolders = true)\n\n        if (!Properties.isWin)\n          os.perms.set(confDir, \"rwx------\")\n\n        val extraEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"config\",\n          \"--create-pgp-key\",\n          \"--email\",\n          \"some_email\",\n          \"--pgp-password\",\n          \"none\"\n        ).call(cwd = root, env = extraEnv)\n\n        val secretKeyConfig = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"config\",\n          \"pgp.secret-key\"\n        ).call(cwd = root, env = extraEnv)\n          .out.text()\n\n        expect(secretKeyConfig.contains(\"value:-----BEGIN PGP PRIVATE KEY BLOCK-----\"))\n        expect(secretKeyConfig.contains(\"-----END PGP PRIVATE KEY BLOCK-----\"))\n\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"publish\",\n          extraOptions,\n          \"--signer\",\n          \"bc\",\n          \"project\",\n          \"-R\",\n          \"test-repo\"\n        ).call(\n          cwd = root,\n          stdin = os.Inherit,\n          stdout = os.Inherit,\n          env = extraEnv\n        )\n\n        val files = os.walk(root / \"test-repo\")\n          .filter(os.isFile(_))\n          .map(_.relativeTo(root / \"test-repo\"))\n        val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir))\n        expect(notInDir.isEmpty)\n\n        val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet\n\n        expect((files0 -- expectedArtifacts).isEmpty)\n        expect((expectedArtifacts -- files0).isEmpty)\n        expect(files0 == expectedArtifacts) // just in case…\n\n        val repoArgs =\n          Seq[os.Shellable](\n            \"-r\",\n            \"!central\",\n            \"-r\",\n            (root / \"test-repo\").toNIO.toUri.toASCIIString\n          )\n        val dep    = s\"org.virtuslab.scalacli.test:simple${TestCase.scalaSuffix}:0.2.0-SNAPSHOT\"\n        val res    = os.proc(TestUtil.cs, \"launch\", repoArgs, dep).call(cwd = root)\n        val output = res.out.trim()\n        expect(output == \"Hello\")\n\n        val sourceJarViaCsStr =\n          os.proc(TestUtil.cs, \"fetch\", repoArgs, \"--sources\", \"--intransitive\", dep)\n            .call(cwd = root)\n            .out.trim()\n        val sourceJarViaCs = os.Path(sourceJarViaCsStr, os.pwd)\n        val zf             = new ZipFile(sourceJarViaCs.toIO)\n        val entries        = zf.entries().asScala.toVector.map(_.getName).toSet\n        expect(entries == expectedSourceEntries())\n\n        val publicKey = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"config\",\n          \"pgp.public-key\"\n        ).call(cwd = root, env = extraEnv)\n          .out.trim()\n          .stripPrefix(\"value:\")\n\n        os.write(os.Path(\"key.pub\", root), publicKey)\n\n        val signatures = expectedArtifacts.filter(_.last.endsWith(\".asc\"))\n        assert(signatures.nonEmpty)\n        val verifyProc = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"pgp\",\n          \"verify\",\n          \"--key\",\n          s\"key.pub\",\n          signatures.map(os.rel / \"test-repo\" / TestCase.expectedArtifactsDir / _)\n        )\n          .call(cwd = root, env = extraEnv, mergeErrIntoOut = true)\n\n        expect(!verifyProc.out.text().contains(s\"invalid signature\"))\n      }\n    }\n\n  test(\"signer=none overrides other options\") {\n\n    TestCase.testInputs().fromRoot { root =>\n      val confDir  = root / \"config\"\n      val confFile = confDir / \"test-config.json\"\n\n      os.write(confFile, \"{}\", createFolders = true)\n\n      if (!Properties.isWin)\n        os.perms.set(confDir, \"rwx------\")\n\n      val extraEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"config\",\n        \"--create-pgp-key\",\n        \"--email\",\n        \"some_email\",\n        \"--pgp-password\",\n        \"random\"\n      ).call(cwd = root, env = extraEnv)\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        \"--secret-key\",\n        \"value:INCORRECT_KEY\",\n        \"--signer\",\n        \"none\",\n        \"project\",\n        \"-R\",\n        \"test-repo\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit,\n        env = extraEnv\n      )\n\n      val files = os.walk(root / \"test-repo\")\n        .filter(os.isFile(_))\n        .map(_.relativeTo(root / \"test-repo\"))\n      val notInDir = files.filter(!_.startsWith(TestCase.expectedArtifactsDir))\n      expect(notInDir.isEmpty)\n\n      val files0 = files.map(_.relativeTo(TestCase.expectedArtifactsDir)).toSet\n\n      val expectedArtifactsNotSigned = expectedArtifacts.filterNot(_.last.contains(\".asc\"))\n\n      expect((files0 -- expectedArtifactsNotSigned).isEmpty)\n      expect((expectedArtifactsNotSigned -- files0).isEmpty)\n      expect(files0 == expectedArtifactsNotSigned) // just in case…\n    }\n  }\n\n  test(\"incorrect or missing secret key password\") {\n\n    TestCase.testInputs().fromRoot { root =>\n      val confDir  = root / \"config\"\n      val confFile = confDir / \"test-config.json\"\n\n      os.write(confFile, \"{}\", createFolders = true)\n\n      if (!Properties.isWin)\n        os.perms.set(confDir, \"rwx------\")\n\n      val extraEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"pgp\",\n        \"create\",\n        \"--email\",\n        \"test@test.com\",\n        \"--password\",\n        \"value:1234\",\n        \"--dest\",\n        \"new_key\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit,\n        env = extraEnv\n      )\n\n      val wrongPasswordProc = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        \"--secret-key\",\n        \"file:new_key.skr\",\n        \"--secret-key-password\",\n        \"value:WRONG_PASSWORD\",\n        \"project\",\n        \"-R\",\n        \"test-repo\"\n      ).call(\n        cwd = root,\n        check = false,\n        env = extraEnv,\n        mergeErrIntoOut = true\n      )\n\n      expect(wrongPasswordProc.exitCode != 0)\n      expect(wrongPasswordProc.out.text().contains(\n        \"Failed to decrypt the PGP secret key, make sure the provided password is correct!\"\n      ))\n\n      val noPasswordProc = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        \"--secret-key\",\n        \"file:new_key.skr\",\n        \"project\",\n        \"-R\",\n        \"test-repo\"\n      ).call(\n        cwd = root,\n        check = false,\n        env = extraEnv,\n        mergeErrIntoOut = true\n      )\n\n      expect(noPasswordProc.exitCode != 0)\n      expect(noPasswordProc.out.text().contains(\n        \"Failed to decrypt the PGP secret key, provide a password!\"\n      ))\n\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishTests212.scala",
    "content": "package scala.cli.integration\n\nclass PublishTests212 extends PublishTestDefinitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishTests213.scala",
    "content": "package scala.cli.integration\n\nclass PublishTests213 extends PublishTestDefinitions with Test213\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass PublishTests3Lts extends PublishTestDefinitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass PublishTests3NextRc extends PublishTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.file.Paths\n\nclass PublishTestsDefault extends PublishTestDefinitions with TestDefault {\n  test(\"Pure Java\") {\n    val testOrg     = \"test-org.foo\"\n    val testName    = \"foo\"\n    val testVersion = \"0.3.1\"\n    val inputs      = TestInputs(\n      os.rel / \"Foo.java\" ->\n        s\"\"\"//> using publish.organization $testOrg\n           |//> using publish.name $testName\n           |//> using publish.version $testVersion\n           |\n           |package foo;\n           |\n           |public class Foo {\n           |  private static boolean checkClass(String clsName) {\n           |    try {\n           |      Thread.currentThread().getContextClassLoader().loadClass(clsName);\n           |      return true;\n           |    } catch (ClassNotFoundException ex) {\n           |      return false;\n           |    }\n           |  }\n           |\n           |  public static void main(String[] args) {\n           |    boolean hasJuList = checkClass(\"java.util.List\");\n           |    boolean hasScalaArray = checkClass(\"scala.Array\");\n           |    if (!hasJuList) {\n           |      System.out.println(\"Error: java.util.List not found\");\n           |      System.exit(1);\n           |    }\n           |    if (hasScalaArray) {\n           |      System.out.println(\"Error: unexpectedly found scala.Array\");\n           |      System.exit(1);\n           |    }\n           |    System.out.println(\"Hello from \" + \"foo\");\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n\n    val repoRelPath = os.rel / \"test-repo\"\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"--power\", \"publish\", extraOptions, \".\", \"-R\", repoRelPath)\n        .call(stdin = os.Inherit, stdout = os.Inherit, cwd = root)\n      val repoRoot = root / repoRelPath\n      val baseDir  = repoRoot / testOrg.split('.').toSeq / testName / testVersion\n      expect(os.isDir(baseDir))\n\n      val res = os.proc(\n        TestUtil.cs,\n        \"launch\",\n        s\"$testOrg:$testName:$testVersion\",\n        \"-r\",\n        repoRoot.toNIO.toUri.toASCIIString\n      )\n        .call(stdin = os.Inherit, cwd = root)\n      val output = res.out.trim()\n      expect(output == \"Hello from foo\")\n    }\n  }\n\n  test(\"scalapy\") {\n\n    def maybeScalapyPrefix =\n      if (actualScalaVersion.startsWith(\"2.13.\")) \"\"\n      else \"import me.shadaj.scalapy.py\" + System.lineSeparator()\n\n    val sbv =\n      if (actualScalaVersion.startsWith(\"3.\")) \"3\"\n      else actualScalaVersion.split('.').take(2).mkString(\".\")\n\n    val org  = \"test-org\"\n    val name = \"test-name\"\n    val ver  = \"0.3.6\"\n\n    val inputs = TestInputs(\n      os.rel / \"src\" / \"Hello.scala\" ->\n        s\"\"\"$maybeScalapyPrefix\n           |object Hello {\n           |  def main(args: Array[String]): Unit = {\n           |    py.Dynamic.global.print(\"Hello from Python\", flush = true)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n\n    val publishArgs = Seq(\n      \"--organization\",\n      org,\n      \"--name\",\n      name,\n      \"--project-version\",\n      ver\n    )\n\n    inputs.fromRoot { root =>\n      val repoRoot = root / \"tmp-repo\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"--python\",\n        \"--publish-repo\",\n        repoRoot,\n        \"src\",\n        publishArgs\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)\n      val res = os.proc(\n        TestUtil.cs,\n        \"launch\",\n        \"--python\",\n        \"--no-default\",\n        \"-r\",\n        \"central\",\n        \"-r\",\n        repoRoot.toNIO.toUri.toASCIIString,\n        s\"$org:${name}_$sbv:$ver\"\n      ).call(cwd = root)\n      val output = res.out.trim()\n      expect(output == \"Hello from Python\")\n    }\n  }\n\n  test(\"missing org and version\") {\n    // Missing org and missing version should be reported at the same time,\n    // rather than one at a time.\n    val inputs = TestInputs(\n      os.rel / \"Messages.scala\" ->\n        \"\"\"package messages\n          |\n          |object Messages {\n          |  def hello = \"Hello\"\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val tmpDir = os.temp.dir(prefix = \"scala-cli-publish-test\")\n      for (f <- os.list(root))\n        os.copy.into(f, tmpDir)\n      val publishRepo = root / \"the-repo\"\n      val res = os.proc(TestUtil.cli, \"--power\", \"publish\", \"--publish-repo\", publishRepo, tmpDir)\n        .call(cwd = root, check = false, mergeErrIntoOut = true)\n      val output = res.out.text()\n      expect(output.contains(\"Missing organization\"))\n      expect(output.contains(\"Missing version\"))\n    }\n  }\n\n  test(\"missing sonatype requirements\") {\n    val inputs = TestInputs(\n      os.rel / \"messages\" / \"Messages.scala\" ->\n        \"\"\"//> using publish.repository central\n          |//> using publish.organization test-org\n          |//> using publish.name test-name\n          |//> using publish.version 0.1.0\n          |package messages\n          |object Messages {\n          |  def hello = \"Hello\"\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"publish-conf.scala\" ->\n        \"\"\"//> using publish.url https://github.com/me/my-project\n          |//> using publish.license Apache-2.0\n          |//> using publish.scm github:test-org/test-name\n          |//> using publish.developer me|Me|https://me.me\n          |\"\"\".stripMargin\n    )\n    def checkWarnings(output: String, hasWarnings: Boolean): Unit = {\n      val lines = Seq(\n        \"project URL is empty\",\n        \"license is empty\",\n        \"SCM details are empty\",\n        \"developer details are empty\"\n      )\n      for (line <- lines)\n        if (hasWarnings)\n          expect(output.contains(line))\n        else\n          expect(!output.contains(line))\n    }\n    def checkCredentialsWarning(output: String): Unit =\n      expect(\n        output.contains(\n          \"Publishing to a repository that needs authentication, but no credentials are available.\"\n        )\n      )\n    inputs.fromRoot { root =>\n      val failRes =\n        os.proc(TestUtil.cli, \"--power\", \"publish\", \"--dummy\", \"--signer\", \"none\", \"messages\")\n          .call(cwd = root, mergeErrIntoOut = true)\n      checkWarnings(failRes.out.text(), hasWarnings = true)\n      checkCredentialsWarning(failRes.out.text())\n\n      val okRes = os.proc(TestUtil.cli, \"--power\", \"publish\", \"--dummy\", \"--signer\", \"none\", \".\")\n        .call(cwd = root, mergeErrIntoOut = true)\n      checkWarnings(okRes.out.text(), hasWarnings = false)\n      checkCredentialsWarning(okRes.out.text())\n    }\n  }\n\n  test(s\"simple failed upload\") {\n    val secretKey = {\n      val uri = Thread.currentThread().getContextClassLoader\n        .getResource(\"test-keys/key.skr\")\n        .toURI\n      os.Path(Paths.get(uri))\n    }\n\n    val signingOptions = Seq(\n      \"--secret-key\",\n      s\"file:$secretKey\",\n      \"--secret-key-password\",\n      \"value:1234\",\n      \"--signer\",\n      \"bc\"\n    )\n\n    TestCase.testInputs().fromRoot { root =>\n      val r = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        extraOptions,\n        signingOptions,\n        \"project\",\n        \"--publish-repository\",\n        \"sonatype:central\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit,\n        check = false\n      )\n      expect(r.exitCode != 0)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplAmmoniteTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.{normalizeArgsForWindows, removeAnsiColors}\n\ntrait ReplAmmoniteTestDefinitions { this: ReplTestDefinitions =>\n  protected val ammonitePrefix: String        = \"Running in Ammonite REPL:\"\n  def expectedScalaVersionForAmmonite: String =\n    actualScalaVersion match {\n      case s\n          if s.startsWith(\"2.12\") &&\n          Constants.maxAmmoniteScala212Version.coursierVersion < s.coursierVersion =>\n        Constants.maxAmmoniteScala212Version\n      case s\n          if s.startsWith(\"2.13\") &&\n          Constants.maxAmmoniteScala213Version.coursierVersion < s.coursierVersion =>\n        Constants.maxAmmoniteScala213Version\n      case s\n          if s.startsWith(Constants.scala3LtsPrefix) &&\n          Constants.maxAmmoniteScala3LtsVersion.coursierVersion < s.coursierVersion =>\n        Constants.maxAmmoniteScala3LtsVersion\n      case s\n          if s.startsWith(\"3\") &&\n          Constants.maxAmmoniteScala3Version.coursierVersion < s.coursierVersion =>\n        Constants.maxAmmoniteScala3Version\n      case s => s\n    }\n\n  def actualMaxAmmoniteScalaVersion: String =\n    if actualScalaVersion.startsWith(Constants.scala3LtsPrefix) then\n      Constants.maxAmmoniteScala3LtsVersion\n    else if actualScalaVersion.startsWith(Constants.scala3NextPrefix) then\n      Constants.maxAmmoniteScala3Version\n    else if actualScalaVersion.startsWith(\"2.13\") then Constants.maxAmmoniteScala213Version\n    else Constants.maxAmmoniteScala212Version\n\n  def shouldUseMaxAmmoniteScalaVersion: Boolean =\n    actualScalaVersion.coursierVersion > actualMaxAmmoniteScalaVersion.coursierVersion\n\n  def ammoniteExtraOptions: Seq[String] =\n    if !shouldUseMaxAmmoniteScalaVersion then extraOptions\n    else Seq(\"--scala\", actualMaxAmmoniteScalaVersion) ++ TestUtil.extraOptions\n\n  def runInAmmoniteRepl(\n    codeToRunInRepl: String = \"\",\n    testInputs: TestInputs = TestInputs.empty,\n    cliOptions: Seq[String] = Seq.empty,\n    extraAmmoniteOptions: Seq[String] = Seq.empty,\n    shouldPipeStdErr: Boolean = false,\n    check: Boolean = true,\n    env: Map[String, String] = Map.empty\n  )(\n    runAfterRepl: os.CommandResult => Unit,\n    runBeforeReplAndGetExtraCliOpts: () => Seq[os.Shellable] = () => Seq.empty\n  ): Unit = {\n    val ammArgs =\n      if codeToRunInRepl.nonEmpty then\n        (Seq(\"-c\", codeToRunInRepl) ++ extraAmmoniteOptions)\n          .normalizeArgsForWindows\n          .flatMap(arg => Seq(\"--ammonite-arg\", arg))\n      else Seq.empty\n    testInputs.fromRoot { root =>\n      val potentiallyExtraCliOpts = runBeforeReplAndGetExtraCliOpts()\n      runAfterRepl(\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"repl\",\n          \".\",\n          \"--ammonite\",\n          ammArgs,\n          ammoniteExtraOptions,\n          cliOptions,\n          potentiallyExtraCliOpts\n        ).call(\n          cwd = root,\n          env = env,\n          check = check,\n          stderr = if shouldPipeStdErr then os.Pipe else os.Inherit\n        )\n      )\n    }\n  }\n\n  def ammoniteTest(): Unit = {\n    TestInputs.empty.fromRoot { _ =>\n      val code =\n        if actualScalaVersion.startsWith(\"3\") then\n          \"dotty.tools.dotc.config.Properties.simpleVersionString\"\n        else retrieveScalaVersionCode\n      runInAmmoniteRepl(\n        codeToRunInRepl = s\"\"\"println(\"Hello\" + \" from Scala \" + $code)\"\"\",\n        shouldPipeStdErr = true\n      ) { res =>\n        val output     = res.out.trim()\n        val expectedSv =\n          if shouldUseMaxAmmoniteScalaVersion then actualMaxAmmoniteScalaVersion\n          else expectedScalaVersionForAmmonite\n        expect(output == s\"Hello from Scala $expectedSv\")\n        if shouldUseMaxAmmoniteScalaVersion then {\n          // the maximum Scala version supported by ammonite is being used, so we shouldn't downgrade\n          val errOutput = res.err.trim()\n          expect(!errOutput.contains(\"not yet supported with this version of Ammonite\"))\n        }\n      }\n    }\n  }\n\n  def ammoniteTestScope(): Unit = {\n    val message = \"something something ammonite\"\n    runInAmmoniteRepl(\n      codeToRunInRepl = \"println(example.TestScopeExample.message)\",\n      testInputs =\n        TestInputs(\n          os.rel / \"example\" / \"TestScopeExample.test.scala\" ->\n            s\"\"\"package example\n               |\n               |object TestScopeExample {\n               |  def message: String = \"$message\"\n               |}\n               |\"\"\".stripMargin\n        ),\n      cliOptions = Seq(\"--test\"),\n      shouldPipeStdErr = true\n    ) { res =>\n      val output = res.out.trim()\n      expect(output == message)\n      if shouldUseMaxAmmoniteScalaVersion then {\n        // the maximum Scala version supported by ammonite is being used, so we shouldn't downgrade\n        val errOutput = res.err.trim()\n        expect(!errOutput.contains(\"not yet supported with this version of Ammonite\"))\n      }\n    }\n  }\n\n  def ammoniteScalapyTest(): Unit = {\n    val code =\n      if actualScalaVersion.startsWith(\"3\") then\n        \"dotty.tools.dotc.config.Properties.simpleVersionString\"\n      else retrieveScalaVersionCode\n    val codeToRunInRepl =\n      s\"\"\"println(\"Hello\" + \" from Scala \" + $code)\n         |val sth = py.module(\"foo.something\")\n         |py.Dynamic.global.applyDynamicNamed(\"print\")(\"\" -> sth.messageStart, \"\" -> sth.messageEnd, \"flush\" -> py.Any.from(true))\n         |\"\"\".stripMargin\n    val inputs =\n      TestInputs(\n        os.rel / \"foo\" / \"something.py\" ->\n          \"\"\"messageStart = 'Hello from'\n            |messageEnd = 'ScalaPy'\n            |\"\"\".stripMargin\n      )\n    runInAmmoniteRepl(\n      codeToRunInRepl = codeToRunInRepl,\n      testInputs = inputs,\n      cliOptions = Seq(\"--python\"),\n      shouldPipeStdErr = true,\n      check = false,\n      env = Map(\"PYTHONSAFEPATH\" -> \"foo\")\n    ) { errorRes =>\n      expect(errorRes.exitCode != 0)\n      val errorOutput = errorRes.err.trim() + errorRes.out.trim()\n      expect(errorOutput.contains(\"No module named 'foo'\"))\n    }\n    runInAmmoniteRepl(\n      codeToRunInRepl = codeToRunInRepl,\n      testInputs = inputs,\n      cliOptions = Seq(\"--python\"),\n      shouldPipeStdErr = true\n    ) { res =>\n      val expectedSv =\n        if shouldUseMaxAmmoniteScalaVersion then actualMaxAmmoniteScalaVersion\n        else expectedScalaVersionForAmmonite\n      val lines = res.out.trim().linesIterator.toVector\n      expect(lines == Seq(s\"Hello from Scala $expectedSv\", \"Hello from ScalaPy\"))\n      if shouldUseMaxAmmoniteScalaVersion then\n        // the maximum Scala version supported by ammonite is being used, so we shouldn't downgrade\n        expect(!res.err.trim().contains(\"not yet supported with this version of Ammonite\"))\n    }\n  }\n\n  def ammoniteMaxVersionString: String =\n    if actualScalaVersion <= actualMaxAmmoniteScalaVersion then s\" with Scala $actualScalaVersion\"\n    else s\" with Scala $actualMaxAmmoniteScalaVersion (the latest supported version)\"\n\n  test(s\"$ammonitePrefix simple $ammoniteMaxVersionString\")(ammoniteTest())\n  test(s\"$ammonitePrefix scalapy$ammoniteMaxVersionString\")(ammoniteScalapyTest())\n  test(s\"$ammonitePrefix with test scope sources$ammoniteMaxVersionString\")(ammoniteTestScope())\n\n  test(s\"$ammonitePrefix ammonite version in help$ammoniteMaxVersionString\") {\n    runInAmmoniteRepl(cliOptions = Seq(\"--help\")) { res =>\n      val lines          = removeAnsiColors(res.out.trim()).linesIterator.toVector\n      val ammVersionHelp = lines.find(_.contains(\"--ammonite-ver\")).getOrElse(\"\")\n      expect(ammVersionHelp.contains(s\"(${Constants.ammoniteVersion} by default)\"))\n    }\n  }\n\n  def ammoniteWithExtraJarTest(): Unit = {\n    runInAmmoniteRepl(codeToRunInRepl =\n      \"\"\"import shapeless._; println(\"Here's an HList: \" + (2 :: true :: \"a\" :: HNil))\"\"\"\n    )(\n      runBeforeReplAndGetExtraCliOpts = () =>\n        val shapelessJar =\n          os.proc(TestUtil.cs, \"fetch\", \"--intransitive\", \"com.chuusai:shapeless_2.13:2.3.7\")\n            .call()\n            .out\n            .text()\n            .trim\n        Seq(\"--jar\", shapelessJar)\n      ,\n      runAfterRepl = res => expect(res.out.trim() == \"Here's an HList: 2 :: true :: a :: HNil\")\n    )\n  }\n\n  if actualScalaVersion.startsWith(\"2.13\") then\n    test(s\"$ammonitePrefix with extra JAR$ammoniteMaxVersionString\") {\n      ammoniteWithExtraJarTest()\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplAmmoniteTests3StableDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\ntrait ReplAmmoniteTests3StableDefinitions {\n  this: ReplTestDefinitions & ReplAmmoniteTestDefinitions =>\n  test(s\"$ammonitePrefix https://github.com/scala/scala3/issues/21229$ammoniteMaxVersionString\") {\n    // FIXME: test this on standard Scala 3 REPL, rather than just Ammonite\n    runInAmmoniteRepl(\n      codeToRunInRepl = \"println(stuff.baz)\",\n      testInputs = TestInputs(\n        os.rel / \"Pprint.scala\" ->\n          \"\"\"//> using dep com.lihaoyi::pprint::0.9.0\n            |package stuff\n            |import scala.quoted.*\n            |def foo = pprint(1)\n            |inline def bar = pprint(1)\n            |inline def baz = ${ bazImpl }\n            |def bazImpl(using Quotes) = '{ pprint(1) }\n            |\"\"\".stripMargin\n      )\n    )(res => expect(res.out.trim().nonEmpty))\n  }\n\n  test(s\"$ammonitePrefix as jar$ammoniteMaxVersionString\") {\n    val inputs = TestInputs(\n      os.rel / \"CheckCp.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |package checkcp\n          |object CheckCp {\n          |  def hasDir: Boolean =\n          |    sys.props(\"java.class.path\")\n          |      .split(java.io.File.pathSeparator)\n          |      .toVector\n          |      .map(os.Path(_, os.pwd))\n          |      .exists(os.isDir(_))\n          |}\n          |\"\"\".stripMargin\n    )\n    val code = \"\"\"println(\"hasDir=\" + checkcp.CheckCp.hasDir)\"\"\"\n    runInAmmoniteRepl(codeToRunInRepl = code, testInputs = inputs) {\n      res => expect(res.out.trim() == \"hasDir=true\")\n    }\n    runInAmmoniteRepl(codeToRunInRepl = code, testInputs = inputs, cliOptions = Seq(\"--as-jar\")) {\n      res => expect(res.out.trim() == \"hasDir=false\")\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.removeAnsiColors\nimport scala.util.Properties\n\nabstract class ReplTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n\n  protected lazy val canRunInRepl: Boolean =\n    (actualScalaVersion.startsWith(\"3.3\") &&\n      actualScalaVersion.coursierVersion >= \"3.3.7\".coursierVersion) ||\n    actualScalaVersion.startsWith(\"3.7\") ||\n    actualScalaVersion.coursierVersion >= \"3.7.0-RC1\".coursierVersion\n\n  protected val dryRunPrefix: String    = \"Dry run:\"\n  protected val runInReplPrefix: String = \"Running in Scala REPL:\"\n\n  def runInRepl(\n    codeToRunInRepl: String,\n    testInputs: TestInputs = TestInputs.empty,\n    cliOptions: Seq[String] = Seq.empty,\n    shouldPipeStdErr: Boolean = false,\n    check: Boolean = true,\n    skipScalaVersionArgs: Boolean = false,\n    env: Map[String, String] = Map.empty\n  )(\n    runAfterRepl: os.CommandResult => Unit,\n    runBeforeReplAndGetExtraCliOpts: () => Seq[os.Shellable] = () => Seq.empty\n  ): Unit = {\n    testInputs.fromRoot { root =>\n      val potentiallyExtraCliOpts = runBeforeReplAndGetExtraCliOpts()\n      runAfterRepl(\n        os.proc(\n          TestUtil.cli,\n          \"repl\",\n          \".\",\n          \"--repl-quit-after-init\",\n          \"--repl-init-script\",\n          codeToRunInRepl,\n          if skipScalaVersionArgs then TestUtil.extraOptions else extraOptions,\n          cliOptions,\n          potentiallyExtraCliOpts\n        )\n          .call(\n            cwd = root,\n            stderr = if shouldPipeStdErr then os.Pipe else os.Inherit,\n            env = env,\n            check = check\n          )\n      )\n    }\n  }\n\n  def dryRun(\n    testInputs: TestInputs = TestInputs.empty,\n    cliOptions: Seq[String] = Seq.empty,\n    useExtraOptions: Boolean = true\n  ): Unit = {\n    testInputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"repl\",\n        \"--repl-dry-run\",\n        cliOptions,\n        if useExtraOptions then extraOptions else Seq.empty\n      )\n        .call(cwd = root)\n    }\n  }\n\n  test(s\"$dryRunPrefix default\")(dryRun())\n\n  test(s\"$dryRunPrefix with main scope sources\") {\n    dryRun(\n      TestInputs(\n        os.rel / \"Example.scala\" ->\n          \"\"\"object Example extends App {\n            |  println(\"Hello\")\n            |}\n            |\"\"\".stripMargin\n      )\n    )\n  }\n\n  test(s\"$dryRunPrefix with main and test scope sources, and the --test flag\") {\n    dryRun(\n      TestInputs(\n        os.rel / \"Example.scala\" ->\n          \"\"\"object Example extends App {\n            |  println(\"Hello\")\n            |}\n            |\"\"\".stripMargin,\n        os.rel / \"Example.test.scala\" ->\n          s\"\"\"//> using dep org.scalameta::munit::${Constants.munitVersion}\n             |\n             |class Example extends munit.FunSuite {\n             |  test(\"is true true\") { assert(true) }\n             |}\n             |\"\"\".stripMargin\n      )\n    )\n  }\n\n  test(s\"$dryRunPrefix calling repl with a directory with no scala artifacts\") {\n    dryRun(TestInputs(os.rel / \"Testing.java\" -> \"public class Testing {}\"))\n  }\n\n  test(\"default scala version in help\") {\n    TestInputs.empty.fromRoot { root =>\n      val res              = os.proc(TestUtil.cli, \"repl\", extraOptions, \"--help\").call(cwd = root)\n      val lines            = removeAnsiColors(res.out.trim()).linesIterator.toVector\n      val scalaVersionHelp = lines.find(_.contains(\"--scala-version\")).getOrElse(\"\")\n      expect(scalaVersionHelp.contains(s\"(${Constants.defaultScala} by default)\"))\n    }\n  }\n\n  test(\"calling repl with -Xshow-phases flag\") {\n    val cmd = Seq[os.Shellable](\n      TestUtil.cli,\n      \"repl\",\n      \"-Xshow-phases\",\n      extraOptions\n    )\n\n    val res = os.proc(cmd).call(mergeErrIntoOut = true)\n    expect(res.exitCode == 0)\n    val output = res.out.text()\n    expect(output.contains(\"parser\"))\n    expect(output.contains(\"typer\"))\n  }\n\n  if canRunInRepl then {\n    test(s\"$runInReplPrefix simple\") {\n      val expectedMessage = \"1337\"\n      runInRepl(s\"\"\"println($expectedMessage)\"\"\")(r =>\n        expect(r.out.trim() == expectedMessage)\n      )\n    }\n\n    test(s\"$runInReplPrefix verify Scala version from the REPL\") {\n      val opts = if actualScalaVersion.startsWith(\"3\") && !isScala38OrNewer then\n        Seq(\"--with-compiler\")\n      else Seq.empty\n      runInRepl(\n        codeToRunInRepl = s\"\"\"println($retrieveScalaVersionCode)\"\"\",\n        cliOptions = opts\n      )(r => expect(r.out.trim() == actualScalaVersion))\n    }\n\n    test(s\"$runInReplPrefix test scope\") {\n      val message = \"something something something REPL\"\n      runInRepl(\n        codeToRunInRepl = \"println(example.TestScopeExample.message)\",\n        testInputs = TestInputs(\n          os.rel / \"example\" / \"TestScopeExample.test.scala\" ->\n            s\"\"\"package example\n               |\n               |object TestScopeExample {\n               |  def message: String = \"$message\"\n               |}\n               |\"\"\".stripMargin\n        ),\n        cliOptions = Seq(\"--test\")\n      )(r => expect(r.out.trim() == message))\n    }\n\n    test(s\"$runInReplPrefix https://github.com/scala/scala3/issues/21229\") {\n      runInRepl(\n        codeToRunInRepl = \"println(stuff.baz)\",\n        testInputs = TestInputs(\n          os.rel / \"Pprint.scala\" ->\n            \"\"\"//> using dep com.lihaoyi::pprint::0.9.0\n              |package stuff\n              |import scala.quoted.*\n              |def foo = pprint(1)\n              |inline def bar = pprint(1)\n              |inline def baz = ${ bazImpl }\n              |def bazImpl(using Quotes) = '{ pprint(1) }\n              |\"\"\".stripMargin\n        )\n      )(res => expect(res.out.trim().nonEmpty))\n    }\n\n    if !Properties.isWin then {\n      test(s\"$runInReplPrefix ScalaPy\") {\n        val opts =\n          if actualScalaVersion.startsWith(\"3\") && !isScala38OrNewer then Seq(\"--with-compiler\")\n          else Seq.empty\n        runInRepl(\n          codeToRunInRepl =\n            s\"\"\"import me.shadaj.scalapy.py\n               |println(\"Hello\" + \" from Scala \" + $retrieveScalaVersionCode)\n               |val sth = py.module(\"foo.something\")\n               |py.Dynamic.global.applyDynamicNamed(\"print\")(\"\" -> sth.messageStart, \"\" -> sth.messageEnd, \"flush\" -> py.Any.from(true))\n               |\"\"\".stripMargin,\n          testInputs = TestInputs(\n            os.rel / \"foo\" / \"something.py\" ->\n              \"\"\"messageStart = 'Hello from'\n                |messageEnd = 'ScalaPy'\n                |\"\"\".stripMargin\n          ),\n          cliOptions = Seq(\"--python\", \"--power\") ++ opts,\n          shouldPipeStdErr = true\n        ) { res =>\n          val output = res.out.trim().linesIterator.toVector.take(2).mkString(\"\\n\")\n          expect(output ==\n            s\"\"\"Hello from Scala $actualScalaVersion\n               |Hello from ScalaPy\"\"\".stripMargin)\n        }\n      }\n\n      test(s\"$runInReplPrefix ScalaPy with PYTHONSAFEPATH\") {\n        val opts =\n          if actualScalaVersion.startsWith(\"3\") && !isScala38OrNewer then Seq(\"--with-compiler\")\n          else Seq.empty\n        runInRepl(\n          codeToRunInRepl =\n            s\"\"\"import me.shadaj.scalapy.py\n               |println(\"Hello\" + \" from Scala \" + $retrieveScalaVersionCode)\n               |val sth = py.module(\"foo.something\")\n               |py.Dynamic.global.applyDynamicNamed(\"print\")(\"\" -> sth.messageStart, \"\" -> sth.messageEnd, \"flush\" -> py.Any.from(true))\n               |\"\"\".stripMargin,\n          testInputs = TestInputs(\n            os.rel / \"foo\" / \"something.py\" ->\n              \"\"\"messageStart = 'Hello from'\n                |messageEnd = 'ScalaPy'\n                |\"\"\".stripMargin\n          ),\n          cliOptions = Seq(\"--python\", \"--power\") ++ opts,\n          shouldPipeStdErr = true,\n          // check = false, // technically should be an error, but the REPL itself doesn't return it as such.\n          env = Map(\"PYTHONSAFEPATH\" -> \"foo\")\n        ) { errorRes =>\n          // expect(errorRes.exitCode != 0) // technically should be an error, but the REPL itself doesn't return it as such.\n          val errorOutput = TestUtil.removeAnsiColors(errorRes.err.trim() + errorRes.out.trim())\n          expect(errorOutput.contains(\"No module named 'foo'\"))\n        }\n      }\n\n      test(s\"$runInReplPrefix with extra JAR\") {\n        runInRepl(codeToRunInRepl =\n          \"\"\"import shapeless._; println(\"Here's an HList: \" + (2 :: true :: \"a\" :: HNil))\"\"\"\n        )(\n          runBeforeReplAndGetExtraCliOpts = () =>\n            val shapelessJar =\n              os.proc(TestUtil.cs, \"fetch\", \"--intransitive\", \"com.chuusai:shapeless_2.13:2.3.7\")\n                .call()\n                .out\n                .text()\n                .trim\n            Seq(\"--jar\", shapelessJar)\n          ,\n          runAfterRepl = res => expect(res.out.trim() == \"Here's an HList: 2 :: true :: a :: HNil\")\n        )\n      }\n\n      if !isScala38OrNewer then\n        // TODO rewrite this test to work with Scala 3.8+ once 3.8.0 stable is out\n        test(s\"$runInReplPrefix as jar\") {\n          val inputs = TestInputs(\n            os.rel / \"CheckCp.scala\" ->\n              \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n                |package checkcp\n                |class CheckCp\n                |object CheckCp {\n                |  def hasDir: Boolean = {\n                |    val uri: java.net.URI = classOf[checkcp.CheckCp].getProtectionDomain.getCodeSource.getLocation.toURI\n                |    os.isDir(os.Path(java.nio.file.Paths.get(uri)))\n                |  }\n                |}\n                |\"\"\".stripMargin\n          )\n          val code = \"\"\"println(\"hasDir=\" + checkcp.CheckCp.hasDir)\"\"\"\n          runInRepl(codeToRunInRepl = code, testInputs = inputs) {\n            res => expect(res.out.trim().contains(\"hasDir=true\"))\n          }\n          runInRepl(\n            codeToRunInRepl = code,\n            testInputs = inputs,\n            cliOptions = Seq(\"--as-jar\", \"--power\")\n          ) {\n            res => expect(res.out.trim().contains(\"hasDir=false\"))\n          }\n\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplTests212.scala",
    "content": "package scala.cli.integration\n\nclass ReplTests212 extends ReplTestDefinitions with ReplAmmoniteTestDefinitions with Test212\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplTests213.scala",
    "content": "package scala.cli.integration\n\nclass ReplTests213 extends ReplTestDefinitions with ReplAmmoniteTestDefinitions with Test213 {\n  for {\n    withExplicitScala2SnapshotRepo <- Seq(true, false)\n    nightlyVersion      = \"2.13.nightly\"\n    scalaVersionOptions = Seq(\"--scala\", nightlyVersion)\n    repoOptions         =\n      if withExplicitScala2SnapshotRepo then\n        Seq(\n          \"--repository\",\n          \"https://scala-ci.typesafe.com/artifactory/scala-2.13-snapshots\"\n        )\n      else\n        Seq.empty\n    repoString = if withExplicitScala2SnapshotRepo then \" with Scala 2 snapshot repo\" else \"\"\n  }\n    test(s\"$dryRunPrefix repl Scala 2 snapshots: $nightlyVersion$repoString\") {\n      dryRun(\n        cliOptions = scalaVersionOptions ++ repoOptions,\n        useExtraOptions = false\n      )\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplTests3Lts.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass ReplTests3Lts extends ReplTestDefinitions with Test3Lts\n    with ReplAmmoniteTestDefinitions\n    with ReplAmmoniteTests3StableDefinitions {\n  import Constants.scala3LtsPrefix\n  if canRunInRepl then\n    for { ltsNightlyTag <- List(\"3.lts.nightly\", \"lts.nightly\") }\n      test(\n        s\"$runInReplPrefix $ltsNightlyTag returns the same Scala version as $scala3LtsPrefix.nightly\"\n      ) {\n        val code = s\"\"\"println($retrieveScalaVersionCode)\"\"\"\n        runInRepl(\n          code,\n          cliOptions = Seq(\"-S\", ltsNightlyTag, \"--with-compiler\"),\n          skipScalaVersionArgs = true\n        ) { r1 =>\n          val version1 = r1.out.trim()\n          System.err.println(s\"$ltsNightlyTag returns the following nightly: $version1\")\n          runInRepl(\n            code,\n            cliOptions = Seq(\"-S\", s\"$scala3LtsPrefix.nightly\", \"--with-compiler\"),\n            skipScalaVersionArgs = true\n          ) { r2 =>\n            val version2 = r2.out.trim()\n            expect(version1 == version2)\n            val major = version1.split('.').take(1).head.toInt\n            expect(major == 3)\n            val minor = version1.split('.').take(2).last.toInt\n            expect(minor >= scala3LtsPrefix.split('.').last.toInt)\n            val patch = version1.split('.').take(3).last.takeWhile(_.isDigit).toInt\n            if minor == 3 then expect(patch >= 8) // new nightly repo\n          }\n        }\n      }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass ReplTests3NextRc extends ReplTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ReplTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass ReplTestsDefault extends ReplTestDefinitions\n    with ReplAmmoniteTestDefinitions\n    with ReplAmmoniteTests3StableDefinitions\n    with TestDefault {\n  if canRunInRepl then\n    for { nightlyTag <- List(\"3.nightly\", \"nightly\") }\n      test(\n        s\"$runInReplPrefix $nightlyTag returns the same Scala version as <latest-minor>.nightly\"\n      ) {\n        val code = \"\"\"println(scala.util.Properties.versionNumberString)\"\"\"\n        runInRepl(code, cliOptions = Seq(\"-S\", nightlyTag)) { r1 =>\n          val version1 = r1.out.trim()\n          System.err.println(s\"$nightlyTag returns the following nightly: $version1\")\n          val nightlyPrefix = version1.split('.').take(2).mkString(\".\")\n          runInRepl(code, cliOptions = Seq(\"-S\", s\"$nightlyPrefix.nightly\")) { r2 =>\n            val version2 = r2.out.trim()\n            System.err.println(s\"$nightlyPrefix.nightly returns the following nightly: $version2\")\n            expect(version1 == version2)\n            val major = version1.split('.').take(1).head.toInt\n            expect(major == 3)\n            val minor = version1.split('.').take(2).last.toInt\n            expect(minor >= Constants.scala3NextPrefix.split('.').last.toInt)\n          }\n        }\n      }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunGistTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\ntrait RunGistTestDefinitions { this: RunTestDefinitions =>\n  def escapedUrls(url: String): String =\n    if (Properties.isWin) \"\\\"\" + url + \"\\\"\"\n    else url\n\n  for {\n    useFileDirective <- Seq(true, false)\n    useFileDirectiveMessage = if (useFileDirective) \" (//> using file)\" else \"\"\n  } {\n    def testInputs(url: String) =\n      if (useFileDirective) TestInputs(os.rel / \"input.scala\" -> s\"//> using file $url\")\n      else emptyInputs\n    def args(url: String) = if (useFileDirective) Seq(\".\") else Seq(escapedUrls(url))\n    test(s\"Script URL$useFileDirectiveMessage\") {\n      val url =\n        \"https://gist.github.com/alexarchambault/f972d941bc4a502d70267cfbbc4d6343/raw/b0285fa0305f76856897517b06251970578565af/test.sc\"\n      val expectedMessage = \"Hello from GitHub Gist\"\n      testInputs(url).fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"run\", extraOptions, args(url))\n          .call(cwd = root)\n          .out.trim()\n        expect(output == expectedMessage)\n      }\n    }\n\n    test(s\"Scala URL$useFileDirectiveMessage\") {\n      val url =\n        \"https://gist.github.com/alexarchambault/f972d941bc4a502d70267cfbbc4d6343/raw/2691c01984c9249936a625a42e29a822a357b0f6/Test.scala\"\n      val message = \"Hello from Scala GitHub Gist\"\n      testInputs(url).fromRoot { root =>\n        val output = os.proc(TestUtil.cli, extraOptions, args(url))\n          .call(cwd = root)\n          .out.trim()\n        expect(output == message)\n      }\n    }\n\n    test(s\"Java URL$useFileDirectiveMessage\") {\n      val url =\n        \"https://gist.github.com/alexarchambault/f972d941bc4a502d70267cfbbc4d6343/raw/2691c01984c9249936a625a42e29a822a357b0f6/Test.java\"\n      val message = \"Hello from Java GitHub Gist\"\n      testInputs(url).fromRoot { root =>\n        val output = os.proc(TestUtil.cli, extraOptions, args(url))\n          .call(cwd = root)\n          .out.trim()\n        expect(output == message)\n      }\n    }\n\n    if (!useFileDirective) // TODO: add support for gists in using file directives\n      test(s\"Github Gists Script URL$useFileDirectiveMessage\") {\n        TestUtil.retryOnCi() {\n          val url =\n            \"https://gist.github.com/alexarchambault/7b4ec20c4033690dd750ffd601e540ec\"\n          val message = \"Hello\"\n          testInputs(url).fromRoot { root =>\n            val output = os.proc(TestUtil.cli, extraOptions, args(url))\n              .call(cwd = root)\n              .out.trim()\n            expect(output == message)\n          }\n        }\n      }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunJdkTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.ProcOps\nimport scala.util.{Properties, Try}\n\ntrait RunJdkTestDefinitions { this: RunTestDefinitions =>\n  def canUseScalaInstallationWrapper: Boolean =\n    actualScalaVersion.startsWith(\"3\") && actualScalaVersion.split('.').drop(1).head.toInt >= 5\n\n  for {\n    javaVersion <- isScala38OrNewer -> TestUtil.isJvmCli match {\n      case (false, true) =>\n        Constants.allJavaVersions.filter(_ >= Constants.minimumLauncherJavaVersion)\n      case (true, _) => Constants.allJavaVersions.filter(_ >= Constants.defaultJvmVersion)\n      case _         => Constants.allJavaVersions\n    }\n    index = javaVersion\n    useScalaInstallationWrapper <-\n      if (canUseScalaInstallationWrapper) Seq(false, true) else Seq(false)\n    launcherString = if (useScalaInstallationWrapper) \"coursier scala installation\" else \"Scala CLI\"\n    scalaRunnerWrapperVersion = actualScalaVersion match {\n      case v if v == Constants.scala3NextRc => Constants.scala3NextRcAnnounced\n      case v if v == Constants.scala3Next   => Constants.scala3NextAnnounced\n      case v                                => v\n    }\n    withLauncher = (root: os.Path) =>\n      (f: Seq[os.Shellable] => Unit) =>\n        if (useScalaInstallationWrapper)\n          withScalaRunnerWrapper(\n            root = root,\n            localBin = root / \"local-bin\",\n            scalaVersion = scalaRunnerWrapperVersion,\n            shouldCleanUp = false\n          )(launcher => f(Seq(launcher)))\n        else\n          f(Seq(TestUtil.cli))\n  } {\n    test(s\"correct JVM is picked up by $launcherString when JAVA_HOME set to $index\") {\n      TestUtil.retryOnCi() {\n        TestInputs(\n          os.rel / \"check_java_home.sc\" ->\n            s\"\"\"assert(\n               |  System.getProperty(\"java.version\").startsWith(\"$javaVersion\") ||\n               |  System.getProperty(\"java.version\").startsWith(\"1.$javaVersion\")\n               |)\n               |println(System.getProperty(\"java.home\"))\"\"\".stripMargin\n        ).fromRoot { root =>\n          val javaHome =\n            os.Path(\n              os.proc(TestUtil.cs, \"java-home\", \"--jvm\", index).call().out.trim(),\n              os.pwd\n            )\n          withLauncher(root) { launcher =>\n            val res = os.proc(launcher, \"run\", \".\", extraOptions)\n              .call(cwd = root, env = Map(\"JAVA_HOME\" -> javaHome.toString))\n            expect(res.out.trim().contains(javaHome.toString))\n          }\n        }\n      }\n    }\n\n    test(s\"hello world with $launcherString and --jvm $index\") {\n      TestUtil.retryOnCi() {\n        val expectedMessage = \"Hello, world!\"\n        TestInputs(\n          os.rel / \"hello_world.sc\" -> s\"println(\\\"$expectedMessage\\\")\"\n        ).fromRoot { root =>\n          withLauncher(root) { launcher =>\n            val res = os.proc(launcher, \"run\", \".\", extraOptions, \"--jvm\", index)\n              .call(cwd = root)\n            expect(res.out.trim() == expectedMessage)\n          }\n        }\n      }\n    }\n\n    if (!Properties.isWin || !useScalaInstallationWrapper) // TODO make this pass on Windows\n      test(\n        s\"correct JVM is picked up by $launcherString when Java $index is passed with --java-home\"\n      ) {\n        TestUtil.retryOnCi() {\n          TestInputs(\n            os.rel / \"check_java_home.sc\" ->\n              s\"\"\"assert(\n                 |  System.getProperty(\"java.version\").startsWith(\"$javaVersion\") ||\n                 |  System.getProperty(\"java.version\").startsWith(\"1.$javaVersion\")\n                 |)\n                 |println(System.getProperty(\"java.home\"))\"\"\".stripMargin\n          ).fromRoot { root =>\n            val javaHome =\n              os.Path(\n                os.proc(TestUtil.cs, \"java-home\", \"--jvm\", index).call().out.trim(),\n                os.pwd\n              )\n            withLauncher(root) { launcher =>\n              val res =\n                os.proc(launcher, \"run\", \".\", extraOptions, \"--java-home\", javaHome.toString)\n                  .call(cwd = root)\n              expect(res.out.trim().contains(javaHome.toString))\n            }\n          }\n        }\n      }\n\n    if (javaVersion >= Constants.bloopMinimumJvmVersion)\n      test(s\"Bloop runs correctly with $launcherString on JVM $index\") {\n        TestUtil.retryOnCi() {\n          val expectedMessage = \"Hello, world!\"\n          TestInputs(os.rel / \"check_java_home.sc\" -> s\"\"\"println(\"$expectedMessage\")\"\"\")\n            .fromRoot { root =>\n              os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n              withLauncher(root) { launcher =>\n                val res = os.proc(\n                  launcher,\n                  \"run\",\n                  \".\",\n                  extraOptions,\n                  \"--bloop-jvm\",\n                  index,\n                  \"--jvm\",\n                  index\n                )\n                  .call(cwd = root, stderr = os.Pipe)\n                expect(res.err.trim().contains(javaVersion.toString))\n                expect(res.out.trim() == expectedMessage)\n              }\n            }\n        }\n      }\n\n    // the warnings were introduced in JDK 24, so we only test this for JDKs >= 24\n    // the issue never affected Scala 2.12, so we skip it for that version\n    if (\n      !actualScalaVersion.startsWith(\"2.12\") &&\n      !useScalaInstallationWrapper &&\n      Try(index.toInt).map(_ >= 24).getOrElse(false)\n    )\n      // TODO: test with Scala installation wrapper when the fix gets propagated there\n      test(s\"REPL does not warn about restricted java.lang.System API called on JDK $index\") {\n        TestInputs.empty.fromRoot { root =>\n          TestUtil.withProcessWatching(\n            proc = os.proc(TestUtil.cli, \"repl\", extraOptions, \"--jvm\", index)\n              .spawn(cwd = root, stderr = os.Pipe)\n          ) { (proc, _, ec) =>\n            proc.printStderrUntilJlineRevertsToDumbTerminal(proc) { s =>\n              expect(!s.contains(\"A restricted method in java.lang.System has been called\"))\n            }(ec)\n          }\n        }\n      }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunPipedSourcesTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\ntrait RunPipedSourcesTestDefinitions { this: RunTestDefinitions =>\n  def piping(): Unit = {\n    emptyInputs.fromRoot { root =>\n      val cliCmd         = (TestUtil.cli ++ extraOptions).mkString(\" \")\n      val cmd            = s\"\"\" echo 'println(\"Hello\" + \" from pipe\")' | $cliCmd _.sc \"\"\"\n      val res            = os.proc(\"bash\", \"-c\", cmd).call(cwd = root)\n      val expectedOutput = \"Hello from pipe\" + System.lineSeparator()\n      expect(res.out.text() == expectedOutput)\n    }\n  }\n\n  if (!Properties.isWin) {\n    test(\"piping\") {\n      piping()\n    }\n    test(\"Scripts accepted as piped input\") {\n      val message = \"Hello\"\n      val input   = s\"println(\\\"$message\\\")\"\n      emptyInputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"-\", extraOptions)\n          .call(cwd = root, stdin = input)\n          .out.trim()\n        expect(output == message)\n      }\n    }\n    test(\"Scala code accepted as piped input\") {\n      val expectedOutput = \"Hello\"\n      val pipedInput     = s\"object Test extends App { println(\\\"$expectedOutput\\\") }\"\n      emptyInputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"_.scala\", extraOptions)\n          .call(cwd = root, stdin = pipedInput)\n          .out.trim()\n        expect(output == expectedOutput)\n      }\n    }\n    test(\"Scala code with references to existing files accepted as piped input\") {\n      val expectedOutput = \"Hello\"\n      val pipedInput     =\n        s\"\"\"object Test extends App {\n           |  val data = SomeData(value = \"$expectedOutput\")\n           |  println(data.value)\n           |}\"\"\".stripMargin\n      val inputs = TestInputs(os.rel / \"SomeData.scala\" -> \"case class SomeData(value: String)\")\n      inputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \".\", \"_.scala\", extraOptions)\n          .call(cwd = root, stdin = pipedInput)\n          .out.trim()\n        expect(output == expectedOutput)\n      }\n    }\n    test(\"Java code accepted as piped input\") {\n      val expectedOutput = \"Hello\"\n      val pipedInput     =\n        s\"\"\"public class Main {\n           |    public static void main(String[] args) {\n           |        System.out.println(\"$expectedOutput\");\n           |    }\n           |}\n           |\"\"\".stripMargin\n      emptyInputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"_.java\", extraOptions)\n          .call(cwd = root, stdin = pipedInput)\n          .out.trim()\n        expect(output == expectedOutput)\n      }\n    }\n    test(\"Java code with multiple classes accepted as piped input\") {\n      val expectedOutput = \"Hello\"\n      val pipedInput     =\n        s\"\"\"class OtherClass {\n           |    public String message;\n           |    public OtherClass(String message) {\n           |      this.message = message;\n           |    }\n           |}\n           |\n           |public class Main {\n           |    public static void main(String[] args) {\n           |        OtherClass obj = new OtherClass(\"$expectedOutput\");\n           |        System.out.println(obj.message);\n           |    }\n           |}\n           |\"\"\".stripMargin\n      emptyInputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"_.java\", extraOptions)\n          .call(cwd = root, stdin = pipedInput)\n          .out.trim()\n        expect(output == expectedOutput)\n      }\n    }\n    test(\n      \"snippets mixed with piped Scala code and existing sources allow for cross-references\"\n    ) {\n      val hello          = \"Hello\"\n      val comma          = \", \"\n      val world          = \"World\"\n      val exclamation    = \"!\"\n      val expectedOutput = hello + comma + world + exclamation\n      val scriptSnippet  = s\"def world = \\\"$world\\\"\"\n      val scalaSnippet   = \"case class ScalaSnippetData(value: String)\"\n      val javaSnippet    =\n        s\"public class JavaSnippet { public static String exclamation = \\\"$exclamation\\\"; }\"\n      val pipedInput = s\"def hello = \\\"$hello\\\"\"\n      val inputs     =\n        TestInputs(os.rel / \"Main.scala\" ->\n          s\"\"\"object Main extends App {\n             |  val hello = stdin.hello\n             |  val comma = ScalaSnippetData(value = \"$comma\").value\n             |  val world = snippet.world\n             |  val exclamation = JavaSnippet.exclamation\n             |  println(hello + comma + world + exclamation)\n             |}\n             |\"\"\".stripMargin)\n      inputs.fromRoot { root =>\n        val output =\n          os.proc(\n            TestUtil.cli,\n            \".\",\n            \"_.sc\",\n            \"--script-snippet\",\n            scriptSnippet,\n            \"--scala-snippet\",\n            scalaSnippet,\n            \"--java-snippet\",\n            javaSnippet,\n            extraOptions\n          )\n            .call(cwd = root, stdin = pipedInput)\n            .out.trim()\n        expect(output == expectedOutput)\n      }\n    }\n    test(\"pick .scala main class over in-context scripts, including piped ones\") {\n      val inputs = TestInputs(\n        os.rel / \"Hello.scala\" ->\n          \"\"\"object Hello extends App {\n            |  println(s\"${stdin.hello} ${scripts.Script.world}\")\n            |}\n            |\"\"\".stripMargin,\n        os.rel / \"scripts\" / \"Script.sc\" -> \"\"\"def world: String = \"world\"\"\"\"\n      )\n      val pipedInput = \"\"\"def hello: String = \"Hello\"\"\"\"\n      inputs.fromRoot { root =>\n        val res = os.proc(\n          TestUtil.cli,\n          \"run\",\n          extraOptions,\n          \".\",\n          \"_.sc\"\n        )\n          .call(cwd = root, stdin = pipedInput)\n        expect(res.out.trim() == \"Hello world\")\n      }\n    }\n    test(\"pick piped .scala main class over in-context scripts\") {\n      val inputs = TestInputs(\n        os.rel / \"Hello.scala\" ->\n          \"\"\"object Hello {\n            |  def hello: String = \"Hello\"\n            |}\n            |\"\"\".stripMargin,\n        os.rel / \"scripts\" / \"Script.sc\" -> \"\"\"def world: String = \"world\"\"\"\"\n      )\n      val pipedInput =\n        \"\"\"object Main extends App {\n          |  println(s\"${Hello.hello} ${scripts.Script.world}\")\n          |}\n          |\"\"\".stripMargin\n      inputs.fromRoot { root =>\n        val res = os.proc(\n          TestUtil.cli,\n          \"run\",\n          extraOptions,\n          \".\",\n          \"_.scala\"\n        )\n          .call(cwd = root, stdin = pipedInput)\n        expect(res.out.trim() == \"Hello world\")\n      }\n    }\n  }\n\n  test(\"Markdown code with a scala snippet accepted as piped input\") {\n    val expectedOutput = \"Hello\"\n    val pipedInput     =\n      s\"\"\"# Piped Markdown\n         |A simple `scala` snippet\n         |```scala\n         |println(\"$expectedOutput\")\n         |```\n         |\"\"\".stripMargin\n    emptyInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"_.md\", extraOptions)\n        .call(cwd = root, stdin = pipedInput)\n        .out.trim()\n      expect(output == expectedOutput)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunScalaJsTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.removeAnsiColors\n\ntrait RunScalaJsTestDefinitions { this: RunTestDefinitions =>\n  def simpleJsTestOutput(extraArgs: String*): String = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, fileName, \"--js\", extraArgs).call(\n        cwd = root,\n        mergeErrIntoOut = true\n      ).out.trim()\n      expect(output.linesIterator.toSeq.last == message)\n      output\n    }\n  }\n\n  test(\"simple script JS\") {\n    simpleJsTestOutput()\n  }\n\n  test(s\"simple script JS in fullLinkJS mode\") {\n    val output = simpleJsTestOutput(\"--js-mode\", \"fullLinkJS\", \"-v\", \"-v\", \"-v\")\n    expect(output.contains(\"--fullOpt\"))\n\n    expect(!output.contains(\"--fastOpt\"))\n    expect(!output.contains(\"--noOpt\"))\n  }\n\n  test(s\"simple script JS in fastLinkJS mode\") {\n    val output = simpleJsTestOutput(\"--js-mode\", \"fastLinkJS\", \"-v\", \"-v\", \"-v\")\n    expect(output.contains(\"--fastOpt\"))\n\n    expect(!output.contains(\"--fullOpt\"))\n    expect(!output.contains(\"--noOpt\"))\n  }\n\n  test(s\"simple script JS with noOpt\") {\n    val output = simpleJsTestOutput(\"--js-mode\", \"fullLinkJS\", \"--js-no-opt\", \"-v\", \"-v\", \"-v\")\n    expect(output.contains(\"--noOpt\"))\n\n    expect(!output.contains(\"--fastOpt\"))\n    expect(!output.contains(\"--fullOpt\"))\n  }\n\n  test(\"without node on the PATH\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val thrown = os.proc(TestUtil.cli, extraOptions, fileName, \"--js\", \"--server=false\")\n        .call(\n          cwd = root,\n          env = Map(\"PATH\" -> \"\", \"PATHEXT\" -> \"\"),\n          check = false,\n          mergeErrIntoOut = true\n        )\n      val output = thrown.out.trim()\n\n      assert(thrown.exitCode == 1)\n      assert(output.contains(\"Node was not found on the PATH\"))\n    }\n  }\n\n  test(\"JS arguments\") {\n    val inputs = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"// FIXME Ideally, 'args' should contain 'argv' here out-of-the-box.\n           |import scala.scalajs.js\n           |import scala.scalajs.js.Dynamic.global\n           |val process = global.require(\"process\")\n           |val argv = Option(process.argv)\n           |  .filterNot(js.isUndefined)\n           |  .map(_.asInstanceOf[js.Array[String]].drop(2).toSeq)\n           |  .getOrElse(Nil)\n           |val console = global.console\n           |console.log(argv.mkString(\" \"))\n           |\"\"\".stripMargin\n    )\n    val messageArgs = Seq(\"Hello\", \"foo\")\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\", \"--js\", \"--\", messageArgs)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == messageArgs.mkString(\" \"))\n    }\n  }\n\n  test(\"simple script JS command\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(\n        TestUtil.cli,\n        extraOptions,\n        fileName,\n        \"--js\",\n        \"--command\",\n        \"--scratch-dir\",\n        root / \"stuff\"\n      )\n        .call(cwd = root).out.trim()\n      val command      = output.linesIterator.toVector\n      val actualOutput = os.proc(command).call(cwd = root).out.trim()\n      expect(actualOutput.linesIterator.toSeq.last == message)\n    }\n  }\n\n  test(\"esmodule import JS\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"//> using jsModuleKind es\n           |import scala.scalajs.js\n           |import scala.scalajs.js.annotation._\n           |\n           |@js.native\n           |@JSImport(\"console\", JSImport.Namespace)\n           |object console extends js.Object {\n           |  def log(msg: js.Any): Unit = js.native\n           |}\n           |\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, fileName, \"--js\")\n        .call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"simple script JS via config file\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"//> using platform scala-js\n           |import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\").call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"simple script JS via platform option\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"//> using platform scala-native\n           |import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |val msg = \"$message\"\n           |console.log(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, extraOptions, \".\", \"--platform\", \"js\").call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"Multiple scripts JS\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"messages.sc\" ->\n        s\"\"\"def msg = \"$message\"\n           |\"\"\".stripMargin,\n      os.rel / \"print.sc\" ->\n        s\"\"\"import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |console.log(messages.msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"print.sc\", \"messages.sc\", \"--js\").call(cwd =\n        root\n      ).out.trim()\n      expect(output == message)\n    }\n  }\n\n  def jsDomTest(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"JsDom.scala\" ->\n        s\"\"\"|//> using dep org.scala-js::scalajs-dom::2.1.0\n            |\n            |import org.scalajs.dom.document\n            |\n            |object JsDom extends App {\n            |  val pSize = document.querySelectorAll(\"p\")\n            |  println(\"Hello from js dom\")\n            |}\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      // install jsdom library\n      val npmPath = TestUtil.fromPath(\"npm\").getOrElse(\"npm\")\n      os.proc(npmPath, \"init\", \"private\").call(cwd = root)\n      os.proc(npmPath, \"install\", \"jsdom\").call(cwd = root)\n\n      val output = os.proc(TestUtil.cli, extraOptions, \".\", \"--js\", \"--js-dom\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from js dom\"))\n    }\n  }\n\n  if (TestUtil.isCI)\n    // FIXME: figure out why this started failing on the CI: https://github.com/VirtusLab/scala-cli/issues/3335\n    test(\"Js DOM\".flaky) {\n      jsDomTest()\n    }\n\n  test(\"help js\") {\n    val helpJsOption  = \"--help-js\"\n    val helpJs        = os.proc(TestUtil.cli, \"run\", helpJsOption).call(check = false)\n    val lines         = removeAnsiColors(helpJs.out.trim()).linesIterator.toVector\n    val jsVersionHelp = lines.find(_.contains(\"--js-version\")).getOrElse(\"\")\n    expect(jsVersionHelp.contains(s\"(${Constants.scalaJsVersion} by default)\"))\n    expect(lines.exists(_.contains(\"Scala.js options\")))\n    expect(!lines.exists(_.contains(\"Scala Native options\")))\n  }\n\n  test(\"Directory JS\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"dir\" / \"messages.sc\" ->\n        s\"\"\"def msg = \"$message\"\n           |\"\"\".stripMargin,\n      os.rel / \"dir\" / \"print.sc\" ->\n        s\"\"\"import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |console.log(messages.msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val mainClassName = if (actualScalaVersion.startsWith(\"3\")) \"print_sc\" else \"print\"\n\n      val output = os.proc(TestUtil.cli, extraOptions, \"dir\", \"--js\", \"--main-class\", mainClassName)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n  test(\"set es version to scala-js-cli\") {\n    val inputs = TestInputs(\n      os.rel / \"run.sc\" ->\n        s\"\"\"//> using jsEsVersionStr es2018\n           |\n           |import scala.scalajs.js\n           |val console = js.Dynamic.global.console\n           |console.log(\\\"\\\"\\\"(?m).\\\"\\\"\\\".r.findFirstIn(\"Hi\").get)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"run.sc\", \"--js\").call(cwd = root).out.trim()\n      expect(output == \"H\")\n    }\n  }\n\n  test(\"Emit Wasm\") {\n    val outDir = \"out\"\n\n    val inputs = TestInputs(\n      os.rel / \"run.scala\" ->\n        s\"\"\"//> using jsEmitWasm true\n           |//> using jsModuleKind es\n           |//> using jsModuleSplitStyleStr fewestmodules\n           |\n           |object Foo {\n           |  def main(args: Array[String]): Unit = {\n           |    println(\"Hello\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val absOutDir = root / outDir\n\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \"run.scala\",\n        \"--js\",\n        \"-o\",\n        absOutDir.toString(),\n        \"-f\",\n        extraOptions\n      )\n        .call(cwd = root).out.trim()\n      val path = absOutDir / \"main.wasm\"\n      expect(os.exists(path))\n\n      // TODO : Run WASM using node. Requires node 22.\n    }\n  }\n\n  test(\"remap imports directive\") {\n    val importmapFile = \"importmap.json\"\n    val outDir        = \"out\"\n    val fileName      = os.rel / \"run.scala\"\n\n    val inputs = TestInputs(\n      fileName ->\n        s\"\"\"//> using jsEsModuleImportMap $importmapFile\n           | //> using jsModuleKind es\n           | //> using jsMode fastLinkJS\n           | //> using platform js\n           |\n           |import scala.scalajs.js\n           |import scala.scalajs.js.annotation.JSImport\n           |import scala.scalajs.js.typedarray.Float64Array\n           |\n           |object Foo {\n           |  def main(args: Array[String]): Unit = {\n           |    println(Array(-10.0, 10.0, 10).mkString(\", \"))\n           |    println(linspace(0, 10, 10).mkString(\", \"))\n           |  }\n           |}\n           |\n           |@js.native\n           |@JSImport(\"@stdlib/linspace\", JSImport.Default)\n           |object linspace extends js.Object {\n           |  def apply(start: Double, stop: Double, num: Int): Float64Array = js.native\n           |}\"\"\".stripMargin,\n      os.rel / importmapFile ->\n        \"\"\"{\"imports\": {\"@stdlib/linspace\": \"https://cdn.skypack.dev/@stdlib/linspace\"}}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val absOutDir = root / outDir\n      val outFile   = absOutDir / \"main.js\"\n      os.makeDir.all(absOutDir)\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        fileName,\n        \"--js\",\n        \"--js-module-kind\",\n        \"ESModule\",\n        \"-o\",\n        outFile,\n        \"-f\"\n      ).call(cwd = root).out.trim()\n      expect(os.read(outFile).contains(\"https://cdn.skypack.dev/@stdlib/linspace\"))\n    }\n  }\n\n  test(\"remap imports directive error\") {\n    val fileName = os.rel / \"run.scala\"\n    val notexist = \"I_DONT_EXIST.json\"\n    val inputs   = TestInputs(\n      fileName ->\n        s\"\"\"//> using jsEsModuleImportMap $notexist\n           | //> using jsModuleKind es\n           | //> using jsMode fastLinkJS\n           | //> using platform js\n           |\n           |import scala.scalajs.js\n           |import scala.scalajs.js.annotation.JSImport\n           |import scala.scalajs.js.typedarray.Float64Array\n           |\n           |object Foo {\n           |  def main(args: Array[String]): Unit = {\n           |    println(Array(-10.0, 10.0, 10).mkString(\", \"))\n           |    println(linspace(0, 10, 10).mkString(\", \"))\n           |  }\n           |}\n           |\n           |@js.native\n           |@JSImport(\"@stdlib/linspace\", JSImport.Default)\n           |object linspace extends js.Object {\n           |  def apply(start: Double, stop: Double, num: Int): Float64Array = js.native\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val absOutDir = root / \"outDir\"\n      val outFile   = absOutDir / \"main.js\"\n      os.makeDir.all(absOutDir)\n      val result = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        fileName,\n        \"--js\",\n        \"--js-module-kind\",\n        \"ESModule\",\n        \"-o\",\n        outFile,\n        \"-f\"\n      ).call(cwd = root, check = false, mergeErrIntoOut = true).out.trim()\n      expect(result.contains(notexist))\n      expect(result.contains(\"Invalid path to EsImportMap.\"))\n    }\n  }\n\n  test(\"remap imports cmd\") {\n    val importmapFile = \"importmap.json\"\n    val outDir        = \"out\"\n    val fileName      = os.rel / \"run.scala\"\n\n    val inputs = TestInputs(\n      fileName ->\n        s\"\"\"\n           | //> using jsModuleKind es\n           | //> using jsMode fastLinkJS\n           | //> using platform js\n           |\n           |import scala.scalajs.js\n           |import scala.scalajs.js.annotation.JSImport\n           |import scala.scalajs.js.typedarray.Float64Array\n           |\n           |object Foo {\n           |  def main(args: Array[String]): Unit = {\n           |    println(Array(-10.0, 10.0, 10).mkString(\", \"))\n           |    println(linspace(0, 10, 10).mkString(\", \"))\n           |  }\n           |}\n           |\n           |@js.native\n           |@JSImport(\"@stdlib/linspace\", JSImport.Default)\n           |object linspace extends js.Object {\n           |  def apply(start: Double, stop: Double, num: Int): Float64Array = js.native\n           |}\"\"\".stripMargin,\n      os.rel / importmapFile ->\n        \"\"\"{\"imports\": {\"@stdlib/linspace\": \"https://cdn.skypack.dev/@stdlib/linspace\"}}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val absOutDir = root / outDir\n      val outFile   = absOutDir / \"main.js\"\n      os.makeDir.all(absOutDir)\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        fileName,\n        \"--js\",\n        \"--js-module-kind\",\n        \"ESModule\",\n        \"-o\",\n        outFile,\n        \"-f\",\n        \"--js-es-module-import-map\",\n        importmapFile\n      ).call(cwd = root, stdout = os.Inherit).out.trim()\n      expect(os.read(outFile).contains(\"https://cdn.skypack.dev/@stdlib/linspace\"))\n    }\n  }\n\n  if (!actualScalaVersion.startsWith(\"2.12\"))\n    test(\"js defaults & toolkit default\") {\n      val msg = \"Hello\"\n      TestInputs(\n        os.rel / \"toolkit.scala\" ->\n          s\"\"\"//> using toolkit default\n             |//> using toolkit typelevel:default\n             |\n             |//> using platform scala-js\n             |\n             |import cats.effect._\n             |import scala.scalajs.js\n             |\n             |object Hello extends IOApp.Simple {\n             |\n             |  def run =  IO {\n             |    val console = js.Dynamic.global.console\n             |    val jsonString = \"{\\\\\"msg\\\\\": \\\\\"$msg\\\\\"}\"\n             |    val json: ujson.Value  = ujson.read(jsonString)\n             |    console.log(json(\"msg\").str)\n             |  }\n             |}\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val result = os.proc(TestUtil.cli, \"run\", \"toolkit.scala\", extraOptions).call(cwd = root)\n        expect(result.out.trim() == msg)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunScalaNativeTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.removeAnsiColors\nimport scala.util.Properties\n\ntrait RunScalaNativeTestDefinitions { this: RunTestDefinitions =>\n\n  def simpleNativeScriptCode(\n    message: String,\n    wrapMessage: String => String = m => s\"c\\\"$m\\\"\"\n  ): String =\n    if (actualScalaVersion.startsWith(\"3\"))\n      s\"\"\"import scala.scalanative.libc._\n         |import scala.scalanative.unsafe._\n         |\n         |Zone {\n         |   val io = StdioHelpers(stdio)\n         |   io.printf(c\"%s$platformNl\", ${wrapMessage(message)})\n         |}\n         |\"\"\".stripMargin\n    else\n      s\"\"\"import scala.scalanative.libc._\n         |import scala.scalanative.unsafe._\n         |\n         |Zone.acquire { implicit z =>\n         |   val io = StdioHelpers(stdio)\n         |   io.printf(c\"%s$platformNl\", ${wrapMessage(message)})\n         |}\n         |\"\"\".stripMargin\n\n  def simpleNativeTests(): Unit = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    TestInputs(os.rel / fileName -> simpleNativeScriptCode(message))\n      .fromRoot { root =>\n        val output =\n          os.proc(TestUtil.cli, extraOptions, fileName, \"--native\")\n            .call(cwd = root)\n            .out.trim()\n        expect(output == message)\n      }\n  }\n\n  test(\"simple script native\") {\n    TestUtil.retryOnCi() {\n      simpleNativeTests()\n    }\n  }\n\n  def scalaNativeLtoTests(): Unit = {\n    val fileName = \"hello.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"//> using nativeLto full\n           |println(\"$message\")\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, extraOptions, fileName, \"--native\")\n          .call(cwd = root)\n          .out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"scala native with lto optimisation\") {\n    TestUtil.retryOnCi()(scalaNativeLtoTests())\n  }\n\n  test(\"simple script native command\") {\n    TestUtil.retryOnCi() {\n      val fileName = \"simple.sc\"\n      val message  = \"Hello\"\n      TestInputs(os.rel / fileName -> simpleNativeScriptCode(message))\n        .fromRoot { root =>\n          val output =\n            os.proc(TestUtil.cli, extraOptions, fileName, \"--native\", \"--command\")\n              .call(cwd = root)\n              .out.trim()\n          val command      = output.linesIterator.toVector.filter(!_.startsWith(\"[\"))\n          val actualOutput = os.proc(command).call(cwd = root).out.trim()\n          expect(actualOutput == message)\n        }\n    }\n  }\n\n  test(\"Resource embedding in Scala Native\") {\n    TestUtil.retryOnCi() {\n      val projectDir       = \"nativeres\"\n      val resourceContent  = \"resource contents\"\n      val resourceFileName = \"embeddedfile.txt\"\n      val inputs           = TestInputs(\n        os.rel / projectDir / \"main.scala\" ->\n          s\"\"\"|//> using platform scala-native\n              |//> using resourceDir resources\n              |\n              |import java.nio.charset.StandardCharsets\n              |import java.io.{BufferedReader, InputStreamReader}\n              |\n              |object Main {\n              |  def main(args: Array[String]): Unit = {\n              |    val inputStream = getClass().getResourceAsStream(\"/$resourceFileName\")\n              |    val nativeResourceText = new BufferedReader(\n              |      new InputStreamReader(inputStream, StandardCharsets.UTF_8)\n              |    ).readLine()\n              |    println(nativeResourceText)\n              |  }\n              |}\n              |\"\"\".stripMargin,\n        os.rel / projectDir / \"resources\" / resourceFileName -> resourceContent\n      )\n      inputs.fromRoot { root =>\n        val output =\n          os.proc(TestUtil.cli, extraOptions, projectDir, \"-q\")\n            .call(cwd = root)\n            .out.trim()\n        println(output)\n        expect(output == resourceContent)\n      }\n    }\n  }\n\n  test(\"Scala Native C Files are correctly handled as a regular Input\") {\n    TestUtil.retryOnCi() {\n      val projectDir      = \"native-interop\"\n      val interopFileName = \"bindings.c\"\n      val interopMsg      = \"Hello C!\"\n      val inputs          = TestInputs(\n        os.rel / projectDir / \"main.scala\" ->\n          s\"\"\"|//> using platform scala-native\n              |\n              |import scala.scalanative.unsafe._\n              |\n              |@extern\n              |object Bindings {\n              |  @name(\"scalanative_print\")\n              |  def print(): Unit = extern\n              |}\n              |\n              |object Main {\n              |  def main(args: Array[String]): Unit = {\n              |    Bindings.print()\n              |  }\n              |}\n              |\"\"\".stripMargin,\n        os.rel / projectDir / interopFileName ->\n          s\"\"\"|#include <stdio.h>\n              |\n              |void scalanative_print() {\n              |    printf(\"$interopMsg\\\\n\");\n              |}\n              |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val output =\n          os.proc(TestUtil.cli, extraOptions, projectDir, \"-q\")\n            .call(cwd = root)\n            .out.trim()\n        expect(output == interopMsg)\n\n        os.move(root / projectDir / interopFileName, root / projectDir / \"bindings2.c\")\n        val output2 =\n          os.proc(TestUtil.cli, extraOptions, projectDir, \"-q\")\n            .call(cwd = root)\n            .out.trim()\n\n        // LLVM throws linking errors if scalanative_print is internally repeated.\n        // This can happen if a file containing it will be removed/renamed in src,\n        // but somehow those changes will not be reflected in the output directory,\n        // causing symbols inside linked files to be doubled.\n        // Because of that, the removed file should not be passed to linker,\n        // otherwise this test will fail.\n        expect(output2 == interopMsg)\n      }\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3.2\"))\n    test(\"Scala 3 in Scala Native\") {\n      TestUtil.retryOnCi() {\n        val message  = \"using Scala 3 Native\"\n        val fileName = \"scala3native.scala\"\n        val inputs   = TestInputs(\n          os.rel / fileName ->\n            s\"\"\"import scala.scalanative.libc._\n               |import scala.scalanative.unsafe._\n               |\n               |@main def main() =\n               |  val message = \"$message\"\n               |  Zone { implicit z =>\n               |    stdio.printf(toCString(message))\n               |  }\n               |\"\"\".stripMargin\n        )\n        inputs.fromRoot { root =>\n          val output =\n            os.proc(TestUtil.cli, extraOptions, fileName, \"--native\", \"-q\")\n              .call(cwd = root)\n              .out.trim()\n          expect(output == message)\n        }\n      }\n    }\n\n  def multipleScriptsNative(): Unit = {\n    val message = \"Hello\"\n    TestInputs(os.rel / \"print.sc\" ->\n      simpleNativeScriptCode(\"messages.msg\", m => s\"toCString($m)\"))\n      .add(os.rel / \"messages.sc\" ->\n        s\"\"\"def msg = \"$message\"\n           |\"\"\".stripMargin)\n      .fromRoot { root =>\n        val output =\n          os.proc(TestUtil.cli, extraOptions, \"print.sc\", \"messages.sc\", \"--native\")\n            .call(cwd = root)\n            .out.trim()\n        expect(output == message)\n      }\n  }\n\n  test(\"Multiple scripts native\") {\n    TestUtil.retryOnCi()(multipleScriptsNative())\n  }\n\n  def directoryNative(): Unit = {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"dir\" / \"messages.sc\" ->\n        s\"\"\"def msg = \"$message\"\n           |\"\"\".stripMargin,\n      os.rel / \"dir\" / \"print.sc\" ->\n        s\"\"\"import scala.scalanative.libc._\n           |import scala.scalanative.unsafe._\n           |\n           |Zone { implicit z =>\n           |   val io = StdioHelpers(stdio)\n           |   io.printf(c\"%s$platformNl\", toCString(messages.msg))\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, extraOptions, \"dir\", \"--native\", \"--main-class\", \"print\", \"-q\")\n          .call(cwd = root)\n          .out.trim()\n      expect(output == message)\n    }\n  }\n\n  // TODO: make nice messages that the scenario is unsupported with 2.12\n  if (actualScalaVersion.startsWith(\"2.13\"))\n    test(\"Directory native\") {\n      TestUtil.retryOnCi()(directoryNative())\n    }\n\n  test(\"help native\") {\n    val helpNativeOption  = \"--help-native\"\n    val helpNative        = os.proc(TestUtil.cli, \"run\", helpNativeOption).call(check = false)\n    val lines             = removeAnsiColors(helpNative.out.trim()).linesIterator.toVector\n    val nativeVersionHelp = lines.find(_.contains(\"--native-version\")).getOrElse(\"\")\n    expect(nativeVersionHelp.contains(s\"(${Constants.scalaNativeVersion} by default)\"))\n    expect(lines.exists(_.contains(\"Scala Native options\")))\n    expect(!lines.exists(_.contains(\"Scala.js options\")))\n  }\n\n  test(\"Take into account interactive main class when caching binaries\") {\n    TestUtil.retryOnCi() {\n      val inputs = TestInputs(\n        os.rel / \"Main1.scala\" ->\n          \"\"\"package foo\n            |\n            |object Main1 {\n            |  def main(args: Array[String]): Unit =\n            |    println(\"Hello from Main1\")\n            |}\n            |\"\"\".stripMargin,\n        os.rel / \"Main2.scala\" ->\n          \"\"\"package foo\n            |\n            |object Main2 {\n            |  def main(args: Array[String]): Unit =\n            |    println(\"Hello from Main2\")\n            |}\n            |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val configDir = root / \"config\"\n        os.makeDir.all(configDir)\n        if (!Properties.isWin)\n          os.perms.set(configDir, \"rwx------\")\n        val configEnv = Map(\"SCALA_CLI_CONFIG\" -> (configDir / \"config.json\").toString)\n        os.proc(TestUtil.cli, \"config\", \"interactive\", \"true\")\n          .call(cwd = root, env = configEnv)\n        val output1 = os.proc(TestUtil.cli, \"run\", \"--native\", \".\", extraOptions)\n          .call(cwd = root, env = configEnv ++ Seq(\"SCALA_CLI_INTERACTIVE_INPUTS\" -> \"foo.Main1\"))\n          .out.lines().last\n        expect(output1 == \"Hello from Main1\")\n        val output2 = os.proc(TestUtil.cli, \"run\", \"--native\", \".\", extraOptions)\n          .call(cwd = root, env = configEnv ++ Seq(\"SCALA_CLI_INTERACTIVE_INPUTS\" -> \"foo.Main2\"))\n          .out.lines().last\n        expect(output2 == \"Hello from Main2\")\n      }\n    }\n  }\n\n  if (!actualScalaVersion.startsWith(\"2.12\"))\n    for {\n      useDirectives <- Seq(true, false)\n      titleStr = if (useDirectives) \"with directives\" else \"with command line args\"\n      explicitNativeVersion <-\n        Seq(Some(Constants.scalaNativeVersion04), Some(Constants.scalaNativeVersion05), None)\n      actualNativeVersion = explicitNativeVersion.getOrElse(Constants.scalaNativeVersion)\n      nativeVersionStr    = explicitNativeVersion.map(v => s\"explicit: $v\").getOrElse(\"default\")\n      nativeVersionOpts   = explicitNativeVersion.toSeq.flatMap(v => Seq(\"--native-version\", v))\n      nativeVersionDirectiveStr =\n        explicitNativeVersion\n          .map(v => s\"\"\"//> using nativeVersion $v\n                       |\"\"\".stripMargin)\n          .getOrElse(\"\")\n      scalaToolkitVersion =\n        if (explicitNativeVersion.contains(Constants.scalaNativeVersion04))\n          Constants.toolkitVersionForNative04\n        else \"default\"\n      typelevelToolkitVersion = \"default\"\n    } {\n      // for the time being, typelevel toolkit does nto support Scala Native 0.5.x\n      if (!explicitNativeVersion.contains(Constants.scalaNativeVersion05))\n        test(\n          s\"native ($nativeVersionStr) & typelevel toolkit ($typelevelToolkitVersion) $titleStr\"\n        ) {\n          TestUtil.retryOnCi() {\n            val expectedMessage = \"Hello\"\n            val cmdLineOpts     =\n              if (useDirectives) Nil\n              else Seq(\n                \"--toolkit\",\n                s\"typelevel:$typelevelToolkitVersion\",\n                \"--native\"\n              ) ++ nativeVersionOpts\n            val directivesStr =\n              if (useDirectives)\n                s\"\"\"//> using toolkit typelevel:$typelevelToolkitVersion\n                   |//> using platform native\n                   |$nativeVersionDirectiveStr\n                   |\"\"\".stripMargin\n              else \"\"\n            TestInputs(\n              os.rel / \"toolkit.scala\" ->\n                s\"\"\"$directivesStr\n                   |import cats.effect._\n                   |\n                   |object Hello extends IOApp.Simple {\n                   |  def run =  IO.println(\"$expectedMessage\")\n                   |}\n                   |\"\"\".stripMargin\n            ).fromRoot { root =>\n              val result = os.proc(TestUtil.cli, \"run\", \"toolkit.scala\", cmdLineOpts, extraOptions)\n                .call(cwd = root, stderr = os.Pipe)\n              expect(result.out.trim() == expectedMessage)\n              if (actualNativeVersion != Constants.typelevelToolkitMaxScalaNative) {\n                val err = result.err.trim()\n                expect(\n                  err.contains(\n                    s\"Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build\"\n                  )\n                )\n                expect(err.contains(s\"Using ${Constants.typelevelToolkitMaxScalaNative} instead.\"))\n                expect(err.contains(\n                  s\"TypeLevel Toolkit ${Constants.typelevelToolkitVersion} does not support Scala Native ${Constants.scalaNativeVersion}\"\n                ))\n              }\n            }\n          }\n        }\n\n      test(s\"native ($nativeVersionStr) & scala toolkit ($scalaToolkitVersion) $titleStr\") {\n        TestUtil.retryOnCi() {\n          val cmdLineOpts =\n            if (useDirectives) Nil\n            else Seq(\"--toolkit\", scalaToolkitVersion, \"--native\") ++ nativeVersionOpts\n          val directivesStr =\n            if (useDirectives)\n              s\"\"\"//> using toolkit $scalaToolkitVersion\n                 |//> using platform native\n                 |$nativeVersionDirectiveStr\n                 |\"\"\".stripMargin\n            else \"\"\n          TestInputs(\n            os.rel / \"toolkit.scala\" ->\n              s\"\"\"$directivesStr\n                 |object Hello extends App {\n                 |  println(os.pwd)\n                 |}\n                 |\"\"\".stripMargin\n          ).fromRoot { root =>\n            val result = os.proc(TestUtil.cli, \"run\", \"toolkit.scala\", cmdLineOpts, extraOptions)\n              .call(cwd = root, stderr = os.Pipe)\n            expect(result.out.trim() == root.toString)\n            if (Constants.scalaNativeVersion != Constants.toolkiMaxScalaNative) {\n              val err = result.err.trim()\n              expect(\n                err.contains(\n                  s\"Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build\"\n                )\n              )\n              expect(err.contains(s\"Using ${Constants.toolkiMaxScalaNative} instead.\"))\n              expect(err.contains(\n                s\"Scala Toolkit does not support Scala Native ${Constants.scalaNativeVersion}\"\n              ))\n            }\n          }\n        }\n      }\n    }\n\n  for {\n    expectedMultithreadingState <- Seq(true, false)\n    // multithreading should be enabled by Scala Native by default\n    setExplicitly <- if (expectedMultithreadingState) Seq(true, false) else Seq(true)\n    useDirective  <- if (setExplicitly) Seq(true, false) else Seq(false)\n    directive =\n      if (useDirective && setExplicitly)\n        s\"//> using nativeMultithreading $expectedMultithreadingState\"\n      else \"\"\n    cliOptions =\n      if (!useDirective && setExplicitly)\n        Seq(s\"--native-multithreading=$expectedMultithreadingState\")\n      else Nil\n    testDescriptionString = useDirective -> setExplicitly match {\n      case (_, false)    => \"(implicitly)\"\n      case (true, true)  => \"with directive\"\n      case (false, true) => \"with command line option\"\n    }\n  }\n    test(\n      s\"Scala Native multithreading set to $expectedMultithreadingState $testDescriptionString\"\n    ) {\n      TestUtil.retryOnCi() {\n        val fileName       = \"multithreading.sc\"\n        val expectedOutput = \"42\"\n        val threadSleep    = \"100\"\n        val threadAwait    = \"2.seconds\"\n        val inputs         = TestInputs(\n          os.rel / fileName ->\n            s\"\"\"$directive\n               |import scala.concurrent._\n               |import scala.concurrent.duration._\n               |import ExecutionContext.Implicits.global\n               |val promise = Promise[Int]()\n               |val thread = new Thread(new Runnable {\n               |    def run(): Unit = {\n               |      Thread.sleep($threadSleep)\n               |      promise.success($expectedOutput)\n               |    }\n               |  })\n               |thread.start()\n               |val result = Await.result(promise.future, $threadAwait)\n               |println(result)\n               |\"\"\".stripMargin\n        )\n        inputs.fromRoot { root =>\n          val r = os.proc(TestUtil.cli, extraOptions, fileName, \"--native\", cliOptions)\n            .call(cwd = root, stderr = os.Pipe, check = expectedMultithreadingState)\n          if (!expectedMultithreadingState) expect(r.exitCode == 1)\n          else {\n            expect(r.exitCode == 0)\n            expect(r.out.trim() == expectedOutput)\n          }\n          val outputMultithreadingState =\n            if setExplicitly then expectedMultithreadingState.toString else \"detect\"\n          expect(r.err.trim().contains(s\"multithreadingEnabled=$outputMultithreadingState\"))\n        }\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\ntrait RunScalaPyTestDefinitions { this: RunTestDefinitions =>\n  private def maybeScalapyPrefix =\n    if (actualScalaVersion.startsWith(\"2.13.\")) \"\"\n    else \"import me.shadaj.scalapy.py\" + System.lineSeparator()\n\n  def scalapyTest(useDirective: Boolean): Unit = {\n    val maybeDirective =\n      if (useDirective)\n        \"\"\"//> using python\n          |\"\"\".stripMargin\n      else\n        \"\"\n    val maybeCliArg =\n      if (useDirective) Nil\n      else Seq(\"--python\")\n    val inputs = TestInputs(\n      os.rel / \"helloscalapy.sc\" -> {\n        maybeDirective +\n          s\"\"\"$maybeScalapyPrefix\n             |import py.SeqConverters\n             |val len = py.Dynamic.global.len(List(0, 2, 3).toPythonProxy)\n             |println(s\"Length is $$len\")\n             |\"\"\".stripMargin\n      }\n    )\n\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, \"--power\", \"run\", extraOptions, \".\", maybeCliArg).call(cwd = root)\n      val output         = res.out.trim()\n      val expectedOutput = \"Length is 3\"\n      expect(output == expectedOutput)\n    }\n  }\n\n  test(\"scalapy from CLI\") {\n    scalapyTest(useDirective = false)\n  }\n  test(\"scalapy via directive\") {\n    scalapyTest(useDirective = true)\n  }\n\n  def scalapyNativeTest(useDirectives: Boolean): Unit = {\n    val maybeDirectives =\n      if (useDirectives)\n        \"\"\"//> using python\n          |//> using platform native\n          |\"\"\".stripMargin\n      else \"\"\n    val maybeCliArg =\n      if (useDirectives) Nil\n      else Seq(\"--python\", \"--native\")\n    val inputs = TestInputs(\n      os.rel / \"helloscalapy.sc\" ->\n        s\"\"\"$maybeDirectives\n           |$maybeScalapyPrefix\n           |import py.SeqConverters\n           |py.local {\n           |  val len = py.Dynamic.global.len(List(0, 2, 3).toPythonProxy)\n           |  println(s\"Length is $$len\")\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, \"--power\", \"run\", extraOptions, \".\", maybeCliArg)\n          .call(cwd = root, stderr = os.Pipe)\n      val output = res.out.trim()\n        .linesIterator\n        .filter { l =>\n          // filter out scala-native-cli garbage output\n          !l.startsWith(\"[info] \")\n        }\n        .mkString(System.lineSeparator())\n      val expectedOutput = \"Length is 3\"\n      expect(output == expectedOutput)\n      val err = res.err.trim()\n      if (Constants.scalaNativeVersion != Constants.scalaPyMaxScalaNative) {\n        expect(\n          err.contains(\n            s\"Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build\"\n          )\n        )\n        expect(err.contains(s\"Using ${Constants.scalaPyMaxScalaNative} instead.\"))\n        expect(\n          err.contains(s\"ScalaPy does not support Scala Native ${Constants.scalaNativeVersion}\")\n        )\n      }\n    }\n  }\n\n  // disabled on Windows for now, for context, see\n  // https://github.com/VirtusLab/scala-cli/pull/1270#issuecomment-1237904394\n  if (!Properties.isWin) {\n    test(\"scalapy native with directives\") {\n      scalapyNativeTest(useDirectives = true)\n    }\n\n    test(\"scalapy native with CLI args\") {\n      scalapyNativeTest(useDirectives = false)\n    }\n  }\n\n  def pythonAndScalaSourcesTest(native: Boolean): Unit = {\n    val tq     = \"\\\"\\\"\\\"\"\n    val inputs = TestInputs(\n      os.rel / \"src\" / \"helpers.py\" ->\n        s\"\"\"class Helper:\n           |    ${tq}Helper class$tq\n           |\n           |    def message(self):\n           |        return 'Hello from Python'\n           |\"\"\".stripMargin,\n      os.rel / \"src\" / \"Hello.scala\" ->\n        s\"\"\"//> using python\n           |$maybeScalapyPrefix\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    py.local {\n           |      val helpers = py.module(\"helpers\")\n           |      println(helpers.Helper().message())\n           |    }\n           |}\n           |\"\"\".stripMargin\n    )\n    val nativeOpt = if (native) Seq(\"--native\") else Nil\n    inputs.fromRoot { root =>\n\n      // Script dir shouldn't be added to PYTHONPATH if PYTHONSAFEPATH is non-empty\n      val errorRes = os.proc(TestUtil.cli, \"--power\", \"run\", extraOptions, nativeOpt, \"src\")\n        .call(\n          cwd = root,\n          env = Map(\"PYTHONSAFEPATH\" -> \"foo\"),\n          mergeErrIntoOut = true,\n          check = false\n        )\n      expect(errorRes.exitCode != 0)\n      val errorOutput = errorRes.out.text()\n      expect(errorOutput.contains(\"No module named 'helpers'\"))\n\n      val res = os.proc(TestUtil.cli, \"--power\", \"run\", extraOptions, nativeOpt, \"src\")\n        .call(cwd = root)\n      val output = res.out.trim()\n      if (native)\n        expect(output.linesIterator.toVector.endsWith(Seq(\"Hello from Python\")))\n      else\n        expect(output == \"Hello from Python\")\n    }\n  }\n\n  test(\"Python and Scala sources\") {\n    pythonAndScalaSourcesTest(native = false)\n  }\n  // disabled on Windows for now, for context, see\n  // https://github.com/VirtusLab/scala-cli/pull/1270#issuecomment-1237904394\n  // disabled on Scala 3.8 and above, behavior seems to have changed here\n  if !Properties.isWin && !isScala38OrNewer then\n    test(\"Python and Scala sources (native)\") {\n      pythonAndScalaSourcesTest(native = true)\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunScalacCompatTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\n/** For the `compile` counterpart, refer to [[CompileScalacCompatTestDefinitions]] */\ntrait RunScalacCompatTestDefinitions {\n  this: RunTestDefinitions =>\n\n  final val smithyVersion     = \"1.50.0\"\n  private def shutdownBloop() =\n    os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(mergeErrIntoOut = true)\n\n  def commandLineScalacXOption(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"Test.scala\" ->\n        \"\"\"object Test {\n          |  def main(args: Array[String]): Unit = {\n          |    val msg = \"Hello\"\n          |    val foo = List(\"Not printed\", 2, true, new Object)\n          |    println(msg)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      def run(warnAny: Boolean) = {\n        val cmd = Seq[os.Shellable](\n          TestUtil.cli,\n          extraOptions,\n          \".\",\n          if (warnAny) Seq(\"-Xlint:infer-any\") else Nil\n        )\n        os.proc(cmd).call(\n          cwd = root,\n          stderr = os.Pipe\n        )\n      }\n\n      val expectedWarning =\n        \"a type was inferred to be `Any`; this may indicate a programming error.\"\n\n      val baseRes       = run(warnAny = false)\n      val baseOutput    = baseRes.out.trim()\n      val baseErrOutput = baseRes.err.text()\n      expect(baseOutput == \"Hello\")\n      expect(!baseErrOutput.contains(expectedWarning))\n\n      val res       = run(warnAny = true)\n      val output    = res.out.trim()\n      val errOutput = res.err.text()\n      expect(output == \"Hello\")\n      expect(errOutput.contains(expectedWarning))\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2.12.\"))\n    test(\"Command-line -X scalac options\") {\n      commandLineScalacXOption()\n    }\n\n  def commandLineScalacYOption(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"Delambdafy.scala\" ->\n        \"\"\"object Delambdafy {\n          |  def main(args: Array[String]): Unit = {\n          |    val l = List(0, 1, 2)\n          |    println(l.map(_ + 1).mkString)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      // FIXME We don't really use the run command here, in spite of being in RunTests…\n      def classNames(inlineDelambdafy: Boolean): Seq[String] = {\n        val cmd = Seq[os.Shellable](\n          TestUtil.cli,\n          \"compile\",\n          extraOptions,\n          \"--print-class-path\",\n          \".\",\n          if (inlineDelambdafy) Seq(\"-Ydelambdafy:inline\") else Nil\n        )\n        val res = os.proc(cmd).call(cwd = root)\n        val cp  = res.out.trim().split(File.pathSeparator).toVector.map(os.Path(_, os.pwd))\n        cp\n          .filter(os.isDir(_))\n          .flatMap(os.list(_))\n          .filter(os.isFile(_))\n          .map(_.last)\n          .filter(_.startsWith(\"Delambdafy\"))\n          .filter(_.endsWith(\".class\"))\n          .map(_.stripSuffix(\".class\"))\n      }\n\n      val baseClassNames = classNames(inlineDelambdafy = false)\n      expect(baseClassNames.nonEmpty)\n      expect(!baseClassNames.exists(_.contains(\"$anonfun$\")))\n\n      val classNames0 = classNames(inlineDelambdafy = true)\n      expect(classNames0.exists(_.contains(\"$anonfun$\")))\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2.\"))\n    test(\"Command-line -Y scalac options\") {\n      commandLineScalacYOption()\n    }\n\n  test(\"-X.. options passed to the child app\") {\n    val inputs = TestInputs(os.rel / \"Hello.scala\" -> \"object Hello extends App {}\")\n    inputs.fromRoot { root =>\n      // Binaries generated with Graal's native-image are run under SubstrateVM\n      // that cuts some -X.. java options, so they're not passed\n      // to the application's main method. This test ensures it is not\n      // cut. \"--java-opt\" option requires a value, so it would fail\n      // if -Xmx1g is cut\n      val res = os.proc(TestUtil.cli, \"Hello.scala\", \"--java-opt\", \"-Xmx1g\").call(\n        cwd = root,\n        check = false\n      )\n      assert(res.exitCode == 0, clues(res.out.text(), res.err.text()))\n    }\n  }\n\n  test(\"scalac help\") {\n    emptyInputs.fromRoot { root =>\n      val res1 = os.proc(\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \"--scalac-help\"\n      )\n        .call(cwd = root, mergeErrIntoOut = true)\n      expect(res1.out.text().contains(\"scalac <options> <source files>\"))\n\n      val res2 = os.proc(\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \"--scalac-option\",\n        \"-help\"\n      )\n        .call(cwd = root, mergeErrIntoOut = true)\n      expect(res1.out.text() == res2.out.text())\n    }\n  }\n\n  for {\n    printOption <- {\n      val printOptionsForAllVersions   = Seq(\"-X\", \"-Xshow-phases\", \"-Xplugin-list\", \"-Y\")\n      val printOptionsScala213OrHigher = Seq(\"-V\", \"-Vphases\", \"-W\", \"-Xsource:help\")\n      val printOptionsScala38Help      = Seq(\"-opt-inline:help\")\n      val printOptionsScala2 = Seq(\"-Xlint:help\", \"-opt:help\", \"-Xmixin-force-forwarders:help\")\n      actualScalaVersion match {\n        case v if v.startsWith(\"3\") =>\n          val scala38Help =\n            if v.coursierVersion >= \"3.8.3\".coursierVersion then printOptionsScala38Help\n            else Nil\n          printOptionsForAllVersions ++ printOptionsScala213OrHigher ++ scala38Help\n        case v if v.startsWith(\"2.13\") =>\n          printOptionsForAllVersions ++ printOptionsScala213OrHigher ++ printOptionsScala2\n        case v if v.startsWith(\"2.12\") => printOptionsForAllVersions ++ printOptionsScala2\n      }\n    }\n    explicitSubcommand <- Seq(true, false)\n    explicitSubcommandString =\n      if (explicitSubcommand) \"(explicit run subcommand)\" else \"(default subcommand)\"\n  } test(s\"scalac print option: $printOption $explicitSubcommandString\") {\n    emptyInputs.fromRoot { root =>\n      val res =\n        (\n          if (explicitSubcommand) os.proc(TestUtil.cli, \"run\", printOption, extraOptions)\n          else os.proc(TestUtil.cli, printOption, extraOptions)\n        ).call(cwd = root, mergeErrIntoOut = true)\n      expect(res.exitCode == 0)\n      expect(res.out.text().nonEmpty)\n    }\n  }\n\n  test(\"-classpath allows to run with scala-cli compile -d option pre-compiled classes\") {\n    val preCompileDir    = \"PreCompileDir\"\n    val preCompiledInput = \"Message.scala\"\n    val runDir           = \"RunDir\"\n    val mainInput        = \"Main.scala\"\n    val expectedOutput   = \"Hello\"\n    TestInputs(\n      os.rel / preCompileDir / preCompiledInput -> \"case class Message(value: String)\",\n      os.rel / runDir / mainInput               ->\n        s\"\"\"object Main extends App { println(Message(\"$expectedOutput\").value) }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val preCompileOutputDir = os.rel / \"outParentDir\" / \"out\"\n\n      // first, precompile to an explicitly specified output directory with -d\n      os.proc(\n        TestUtil.cli,\n        \"compile\",\n        preCompiledInput,\n        \"-d\",\n        preCompileOutputDir.toString,\n        extraOptions\n      ).call(cwd = root / preCompileDir)\n\n      // next, run while relying on the pre-compiled class, specifying the path with -classpath\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"run\",\n        mainInput,\n        \"-classpath\",\n        (os.rel / os.up / preCompileDir / preCompileOutputDir).toString,\n        extraOptions\n      ).call(cwd = root / runDir)\n      expect(runRes.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"-O -classpath allows to run with scala-cli compile -O -d option pre-compiled classes\") {\n    val preCompileDir    = \"PreCompileDir\"\n    val preCompiledInput = \"Message.scala\"\n    val runDir           = \"RunDir\"\n    val mainInput        = \"Main.scala\"\n    val expectedOutput   = \"Hello\"\n    TestInputs(\n      os.rel / preCompileDir / preCompiledInput -> \"case class Message(value: String)\",\n      os.rel / runDir / mainInput               ->\n        s\"\"\"object Main extends App { println(Message(\"$expectedOutput\").value) }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val preCompileOutputDir = os.rel / \"outParentDir\" / \"out\"\n\n      // first, precompile to an explicitly specified output directory with -O -d\n      val compileRes = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        preCompiledInput,\n        \"-O\",\n        \"-d\",\n        \"-O\",\n        preCompileOutputDir.toString,\n        extraOptions\n      ).call(cwd = root / preCompileDir, stderr = os.Pipe)\n      expect(!compileRes.err.trim().contains(\"Warning: Flag -d set repeatedly\"))\n\n      // next, run while relying on the pre-compiled class, specifying the path with -O -classpath\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"run\",\n        mainInput,\n        \"-O\",\n        \"-classpath\",\n        \"-O\",\n        (os.rel / os.up / preCompileDir / preCompileOutputDir).toString,\n        extraOptions\n      ).call(cwd = root / runDir, stderr = os.Pipe)\n      expect(!runRes.err.trim().contains(\"Warning: Flag -classpath set repeatedly\"))\n      expect(runRes.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run main class from -classpath even when no explicit inputs are passed\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(\n      os.rel / \"Main.scala\" -> s\"\"\"object Main extends App { println(\"$expectedOutput\") }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val compilationOutputDir = os.rel / \"compilationOutput\"\n      // first, precompile to an explicitly specified output directory with -d\n      os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \".\",\n        \"-d\",\n        compilationOutputDir,\n        extraOptions\n      ).call(cwd = root)\n\n      // next, run while relying on the pre-compiled class instead of passing inputs\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"run\",\n        \"-classpath\",\n        (os.rel / compilationOutputDir).toString,\n        extraOptions\n      ).call(cwd = root)\n      expect(runRes.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run main class from --dep even when no explicit inputs are passed\") {\n    shutdownBloop()\n    val output = os.proc(\n      TestUtil.cli,\n      \"--dep\",\n      s\"software.amazon.smithy:smithy-cli:$smithyVersion\",\n      \"--main-class\",\n      \"software.amazon.smithy.cli.SmithyCli\",\n      \"--\",\n      \"--version\"\n    ).call()\n    assert(output.exitCode == 0)\n    assert(output.out.text().contains(smithyVersion))\n\n    // assert bloop wasn't started\n    assertNoDiff(shutdownBloop().out.text(), \"No running Bloop server found.\")\n  }\n\n  test(\"find and run main class from --dep even when no explicit inputs are passed\") {\n    shutdownBloop()\n    val output = os.proc(\n      TestUtil.cli,\n      \"run\",\n      \"--dep\",\n      s\"software.amazon.smithy:smithy-cli:$smithyVersion\",\n      \"--\",\n      \"--version\"\n    ).call()\n    assert(output.exitCode == 0)\n    assert(output.out.text().contains(smithyVersion))\n\n    // assert bloop wasn't started\n    assertNoDiff(shutdownBloop().out.text(), \"No running Bloop server found.\")\n  }\n\n  test(\"dont clear output dir\") {\n    val expectedOutput = \"Hello\"\n    val `lib.scala`    = os.rel / \"lib.scala\"\n    val `utils.scala`  = os.rel / \"utils.scala\"\n    TestInputs(\n      `lib.scala`   -> s\"\"\"object lib { def foo = \"$expectedOutput\" }\"\"\",\n      `utils.scala` -> s\"\"\"object utils { def bar = lib.foo }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      val compilationOutputDir = os.rel / \"compilationOutput\"\n      // first, precompile to an explicitly specified output directory with -d\n      os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \"-d\",\n        compilationOutputDir,\n        `lib.scala`,\n        extraOptions\n      ).call(cwd = root)\n\n      val outputFiles = os.list(root / compilationOutputDir)\n      expect(outputFiles.exists(_.endsWith(os.rel / \"lib$.class\")))\n      expect(outputFiles.exists(_.endsWith(os.rel / \"lib.class\")))\n\n      os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \"-d\",\n        compilationOutputDir,\n        \"-cp\",\n        compilationOutputDir,\n        `utils.scala`,\n        extraOptions\n      ).call(cwd = root)\n\n      val outputFlies2 = os.list(root / compilationOutputDir)\n      expect(outputFlies2.exists(_.endsWith(os.rel / \"utils$.class\")))\n      expect(outputFlies2.exists(_.endsWith(os.rel / \"utils.class\")))\n      expect(outputFlies2.exists(_.endsWith(os.rel / \"lib$.class\")))\n      expect(outputFlies2.exists(_.endsWith(os.rel / \"lib.class\")))\n    }\n  }\n\n  test(\"run main class from a jar even when no explicit inputs are passed\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(\n      os.rel / \"Main.scala\" -> s\"\"\"object Main extends App { println(\"$expectedOutput\") }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      // first, package the code to a jar with a main class\n      val jarPath = os.rel / \"Main.jar\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \".\",\n        \"--library\",\n        \"-o\",\n        jarPath,\n        extraOptions\n      ).call(cwd = root)\n\n      // next, run while relying on the jar instead of passing inputs\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"run\",\n        \"-classpath\",\n        jarPath,\n        extraOptions\n      ).call(cwd = root)\n      expect(runRes.out.trim() == expectedOutput)\n    }\n  }\n\n  test(\"run main class from a jar in a directory even when no explicit inputs are passed\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(\n      os.rel / \"Main.scala\" -> s\"\"\"object Main extends App { println(\"$expectedOutput\") }\"\"\"\n    ).fromRoot { (root: os.Path) =>\n      // first, package the code to a jar with a main class\n      val jarParentDirectory = os.rel / \"out\"\n      val jarPath            = jarParentDirectory / \"Main.jar\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \".\",\n        \"--library\",\n        \"-o\",\n        jarPath,\n        extraOptions\n      ).call(cwd = root)\n\n      // next, run while relying on the jar instead of passing inputs\n      val runRes = os.proc(\n        TestUtil.cli,\n        \"run\",\n        \"-cp\",\n        jarParentDirectory,\n        extraOptions\n      ).call(cwd = root)\n      expect(runRes.out.trim() == expectedOutput)\n    }\n  }\n\n  def verifyScala212VersionCompiles(scalaPatchVersion: String): Unit = {\n    TestInputs(os.rel / \"s.sc\" -> \"println(util.Properties.versionNumberString)\").fromRoot {\n      root =>\n        val scala212VersionString = s\"2.12.$scalaPatchVersion\"\n        val res                   =\n          os.proc(TestUtil.cli, \"run\", \".\", \"-S\", scala212VersionString, TestUtil.extraOptions)\n            .call(cwd = root)\n        expect(res.out.trim() == scala212VersionString)\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2.12.\"))\n    (1 until 4).map(_.toString).foreach { scalaPatchVersion =>\n      // FIXME this shouldn't be flaky on Mac\n      if (TestUtil.isCI && Properties.isMac && TestUtil.isNativeCli) test(\n        s\"verify that Scala version 2.12.$scalaPatchVersion is respected and compiles correctly\".flaky\n      )(verifyScala212VersionCompiles(scalaPatchVersion))\n      else test(\n        s\"verify that Scala version 2.12.$scalaPatchVersion is respected and compiles correctly\"\n      )(verifyScala212VersionCompiles(scalaPatchVersion))\n    }\n\n  test(\"scalac verbose\") {\n    val expectedOutput = \"Hello\"\n    val mainClass      = \"Main\"\n    val inputRelPath   = os.rel / s\"$mainClass.scala\"\n    TestInputs(inputRelPath -> s\"\"\"object $mainClass extends App { println(\"$expectedOutput\") }\"\"\")\n      .fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \".\", \"--scalac-verbose\", extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        val errLines = res.err.trim().lines.toList.asScala\n        // there should be a lot of logs, but different stuff is logged depending on the Scala version\n        expect(errLines.length > 100)\n        expect(errLines.exists(_.startsWith(\"[loaded package loader scala\")))\n        expect(errLines.exists(_.contains(s\"$mainClass.scala\")))\n        expect(res.out.trim() == expectedOutput)\n      }\n  }\n\n  if (!Properties.isWin)\n    test(\"-encoding CP1252 should be handled correctly in .scala files\") {\n      TestInputs(\n        charsetName = \"Windows-1252\",\n        os.rel / \"s.scala\" -> \"\"\"object Main extends App { println(\"€\") }\"\"\"\n      )\n        .fromRoot { root =>\n          val res = os.proc(\n            TestUtil.cli,\n            \"s.scala\",\n            \"-encoding\",\n            \"cp1252\",\n            extraOptions\n          ).call(cwd = root)\n          expect(res.out.trim() == \"€\")\n        }\n    }\n\n  if (actualScalaVersion.startsWith(\"3\") || actualScalaVersion.startsWith(\"2.13\")) {\n    val fileName       = \"Main.scala\"\n    val expectedOutput = \"Hello\"\n    val oldSyntaxCode  =\n      s\"\"\"object Main extends App {\n         |  if (true) println(\"$expectedOutput\") else println(\"Error\")\n         |}\n         |\"\"\".stripMargin\n    val newSyntaxCode =\n      s\"\"\"object Main extends App {\n         |  if true then println(\"$expectedOutput\") else println(\"Error\")\n         |}\n         |\"\"\".stripMargin\n\n    test(\"rewrite code to new syntax and then run it correctly (no -O required)\") {\n      TestInputs(os.rel / fileName -> oldSyntaxCode)\n        .fromRoot { root =>\n          val res = os.proc(\n            TestUtil.cli,\n            fileName,\n            \"-new-syntax\",\n            \"-rewrite\",\n            \"-source:3.2-migration\"\n          ).call(cwd = root, stderr = os.Pipe)\n          val filePath = root / fileName\n          expect(res.err.trim().contains(s\"[patched file $filePath]\"))\n          expect(os.read(filePath) == newSyntaxCode)\n          expect(res.out.trim() == expectedOutput)\n        }\n    }\n\n    test(\"rewrite code to old syntax and then run it correctly (no -O required)\") {\n      TestInputs(os.rel / fileName -> newSyntaxCode)\n        .fromRoot { root =>\n          val res = os.proc(\n            TestUtil.cli,\n            fileName,\n            \"-old-syntax\",\n            \"-rewrite\",\n            \"-source:3.2-migration\"\n          ).call(cwd = root, stderr = os.Pipe)\n          val filePath = root / fileName\n          expect(res.err.trim().contains(s\"[patched file $filePath]\"))\n          expect(os.read(filePath) == oldSyntaxCode)\n          expect(res.out.trim() == expectedOutput)\n        }\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"ensure -with-compiler is supported for Scala 3\") {\n      TestInputs(\n        os.rel / \"s.sc\" ->\n          \"println(dotty.tools.dotc.config.Properties.simpleVersionString)\"\n      )\n        .fromRoot { root =>\n          val res =\n            os.proc(TestUtil.cli, \"-with-compiler\", \"s.sc\", extraOptions)\n              .call(cwd = root)\n          expect(res.out.trim() == actualScalaVersion)\n        }\n    }\n\n  if (actualScalaVersion.startsWith(\"2.13\"))\n    test(\"ensure -with-compiler is supported for Scala 2.13\") {\n      TestInputs(os.rel / \"s.sc\" -> \"println(scala.tools.nsc.Properties.versionString)\")\n        .fromRoot { root =>\n          val res =\n            os.proc(TestUtil.cli, \"-with-compiler\", \"s.sc\", extraOptions)\n              .call(cwd = root)\n          expect(res.out.trim() == s\"version $actualScalaVersion\")\n        }\n    }\n\n  for {\n    useDirective <- Seq(true, false)\n    if !Properties.isWin\n    optionsSource = if (useDirective) \"using directive\" else \"command line\"\n    if actualScalaVersion == Constants.scala3Next || actualScalaVersion == Constants.scala3NextRc\n  }\n    test(s\"consecutive -Xmacro-settings:* flags are not ignored (passed via $optionsSource)\") {\n      val sourceFileName                                                   = \"example.scala\"\n      val macroFileName                                                    = \"macro.scala\"\n      val macroSettings @ Seq(macroSetting1, macroSetting2, macroSetting3) =\n        Seq(\"one\", \"two\", \"three\")\n      val macroSettingOptions  = macroSettings.map(s => s\"-Xmacro-settings:$s\")\n      val maybeDirectiveString =\n        if (useDirective) s\"//> using options ${macroSettingOptions.mkString(\" \")}\" else \"\"\n      TestInputs(\n        os.rel / macroFileName ->\n          \"\"\"package x\n            |import scala.quoted.*\n            |object M:\n            |  inline def settingsContains(inline x:String): Boolean = ${\n            |     settingsContainsImpl('x)\n            |  }\n            |  def settingsContainsImpl(x:Expr[String])(using Quotes): Expr[Boolean] =\n            |     import quotes.reflect.*\n            |     val v = x.valueOrAbort\n            |     val r = CompilationInfo.XmacroSettings.contains(v)\n            |     Expr(r)\n            |\"\"\".stripMargin,\n        os.rel / sourceFileName ->\n          s\"\"\"$maybeDirectiveString\n             |import x.M\n             |@main def main(): Unit = {\n             |  val output = Seq(\n             |    if M.settingsContains(\"$macroSetting1\") then Seq(\"$macroSetting1\") else Nil,\n             |    if M.settingsContains(\"$macroSetting2\") then Seq(\"$macroSetting2\") else Nil,\n             |    if M.settingsContains(\"$macroSetting3\") then Seq(\"$macroSetting3\") else Nil,\n             |    if M.settingsContains(\"dummy\") then Seq(\"dummy\") else Nil,\n             |  )\n             |  println(output.flatten.mkString(\", \"))\n             |}\n             |\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val r = os.proc(\n          TestUtil.cli,\n          \"run\",\n          \".\",\n          if (useDirective) Nil else macroSettingOptions,\n          \"-S\",\n          Constants.scala3NextRcAnnounced,\n          \"-experimental\"\n        )\n          .call(cwd = root, stderr = os.Pipe)\n        expect(r.out.trim() == macroSettings.mkString(\", \"))\n      }\n    }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"-Ysafe-init-global doesnt crash the CLI when a dependency produces a warning\") {\n      val (org, name, version) = (\"hello-world\", \"test-safe-init-global-works\", \"0.0.1-SNAPSHOT\")\n      val (depDir, mainDir)    = (\"dep\", \"main\")\n      val (depPackage, depClass, depMethod) = (\"hello\", \"DepMain\", \"x\")\n      val expectedMessage                   = \"Hello, world!\"\n      TestInputs(\n        os.rel / depDir / \"Main.scala\" ->\n          s\"\"\"//> using publish.organization $org\n             |//> using publish.name $name\n             |//> using publish.version $version\n             |package $depPackage\n             |\n             |object $depClass {\n             |  val $depMethod: Int = y\n             |  val y: Int = $depMethod\n             |}\n             |\"\"\".stripMargin,\n        os.rel / mainDir / \"Main.scala\" ->\n          s\"\"\"//> using option -Ysafe-init-global\n             |//> using dep $org::$name:$version\n             |\n             |import $depPackage.$depClass\n             |\n             |object Main extends App {\n             |  val a = $depClass.$depMethod\n             |  println(\"$expectedMessage\")\n             |}\n             |\"\"\".stripMargin\n      ).fromRoot { root =>\n        os.proc(TestUtil.cli, \"--power\", \"publish\", \"local\", depDir, extraOptions).call(cwd = root)\n        val res = os.proc(TestUtil.cli, \"run\", mainDir, extraOptions).call(cwd = root)\n        expect(res.out.trim() == expectedMessage)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\nimport scala.cli.integration.TestUtil.normalizeConsoleOutput\nimport scala.util.Properties\n\ntrait RunScriptTestDefinitions { this: RunTestDefinitions =>\n  def simpleScriptTest(ignoreErrors: Boolean = false, extraArgs: Seq[String] = Nil): Unit = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, extraOptions, extraArgs, fileName).call(cwd = root).out.trim()\n      if (!ignoreErrors)\n        expect(output == message)\n    }\n  }\n\n  test(\"simple script\") {\n    simpleScriptTest()\n  }\n\n  test(\"verbosity\") {\n    simpleScriptTest(extraArgs = Seq(\"-v\"))\n  }\n\n  test(\"Multiple scripts\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"messages.sc\" ->\n        s\"\"\"def msg = \"$message\"\n           |\"\"\".stripMargin,\n      os.rel / \"print.sc\" ->\n        s\"\"\"println(messages.msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"print.sc\", \"messages.sc\").call(cwd =\n        root\n      ).out.trim()\n      expect(output == message)\n      expect(\n        !output.contains(\"Script file named 'main.sc' detected, keep in mind that accessing it\")\n      )\n    }\n  }\n\n  test(\"main.sc is not a special case\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"println(\"$message\")\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"main.sc\").call(cwd =\n        root\n      ).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"main.sc has an object with a main method\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|\n            |object Main {\n            |  def main(args: Array[String]): Unit = println(\"$message\")\n            |}\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"main.sc\").call(cwd =\n        root\n      ).out.trim()\n      expect(output == message)\n    }\n  }\n  test(\"main.sc has an object that extends App\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|\n            |object Main extends App{\n            |   println(\"$message\")\n            |}\n            |\n            |object Other {}\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"main.sc\").call(cwd =\n        root\n      ).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"main.sc has an object with a main method and an object wrapper\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|//> using objectWrapper\n            |object Main {\n            |  def main(args: Array[String]): Unit = println(\"$message\")\n            |}\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"--power\", \"main.sc\").call(cwd =\n        root\n      ).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"main.sc has multiple main methods\") {\n    val inputs = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|//> using objectWrapper\n            |object Main {\n            |  def main(args: Array[String]): Unit = println(\"1\")\n            |}\n            |object AnotherMain {\n            |  def main(args: Array[String]): Unit = println(\"2\")\n            |}\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, extraOptions, \"--power\", \"main.sc\").call(\n        cwd = root,\n        stderr = os.Pipe\n      )\n      val output = result.out.trim()\n      val err    = result.err.trim()\n      expect(output == \"\")\n      expect(err.contains(\n        \"Only a single main is allowed within scripts. Multiple main classes were found in the script: Main, AnotherMain\"\n      ))\n    }\n  }\n  test(\"main.sc has multiple main methods and top-level definitions\") {\n    val inputs = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|//> using objectWrapper\n            |object Main {\n            |  def main(args: Array[String]): Unit = println(\"1\")\n            |}\n            |object AnotherMain {\n            |  def main(args: Array[String]): Unit = println(\"2\")\n            |}\n            |\n            |println(\"3\")\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, extraOptions, \"--power\", \"main.sc\").call(\n        cwd = root,\n        stderr = os.Pipe\n      )\n      val output = result.out.trim()\n      val err    = result.err.trim()\n      expect(output == \"3\")\n      expect(err.contains(\n        \"Only a single main is allowed within scripts. Multiple main classes were found in the script: Main, AnotherMain\"\n      ))\n      expect(err.contains(\n        \"Script contains objects with main methods and top-level statements, only the latter will be run.\"\n      ))\n    }\n  }\n\n  test(\"main.sc has both an object with a main method as well as top-level definitions\") {\n    val message1 = \"Hello\"\n    val message2 = \"Another hello\"\n    val inputs   = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|object Main {\n            |  def main(args: Array[String]): Unit = println(\"$message1\")\n            |}\n            |println(\"$message2\")\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val result = os.proc(TestUtil.cli, extraOptions, \"main.sc\").call(\n        cwd = root,\n        stderr = os.Pipe\n      )\n      val output = result.out.trim()\n      val err    = result.err.trim()\n      expect(output == message2)\n      expect(err.contains(\n        \"Script contains objects with main methods and top-level statements, only the latter will be run.\"\n      ))\n      expect(output == message2)\n    }\n  }\n\n  test(\n    \"main.sc has both an object with a main method and an object wrapper as well as top-level calls\"\n  ) {\n    val message1 = \"Hello\"\n    val message2 = \"Another hello\"\n    val inputs   = TestInputs(\n      os.rel / \"main.sc\" ->\n        s\"\"\"|//> using objectWrapper\n            |object Main {\n            |  def main(args: Array[String]): Unit = println(\"$message1\")\n            |}\n            |println(\"$message2\")\n            |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"--power\", \"main.sc\")\n        .call(cwd = root).out.trim()\n      expect(output == message2)\n    }\n  }\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"use method from main.sc file\") {\n      val message = \"Hello\"\n      val inputs  = TestInputs(\n        os.rel / \"message.sc\" ->\n          s\"\"\"println(main.msg)\n             |\"\"\".stripMargin,\n        os.rel / \"main.sc\" ->\n          s\"\"\"def msg = \"$message\"\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, extraOptions, \"message.sc\", \"main.sc\").call(cwd =\n          root\n        ).out.trim()\n        expect(output == message)\n        expect(\n          !output.contains(\"Script file named 'main.sc' detected, keep in mind that accessing it\")\n        )\n      }\n    }\n  else\n    test(\"warn when main.sc file is used together with other scripts\") {\n      val message = \"Hello\"\n      val inputs  = TestInputs(\n        os.rel / \"message.sc\" ->\n          s\"\"\"println(main.msg)\n             |\"\"\".stripMargin,\n        os.rel / \"main.sc\" ->\n          s\"\"\"def msg = \"$message\"\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val res = os.proc(TestUtil.cli, extraOptions, \"message.sc\", \"main.sc\")\n          .call(cwd = root, check = false, mergeErrIntoOut = true)\n\n        expect(res.exitCode == 1)\n        val output = res.out.trim()\n        expect(\n          output.contains(\"Script file named 'main.sc' detected, keep in mind that accessing it\")\n        )\n      }\n    }\n\n  test(\"Directory\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"dir\" / \"messages.sc\" ->\n        s\"\"\"def msg = \"$message\"\n           |\"\"\".stripMargin,\n      os.rel / \"dir\" / \"print.sc\" ->\n        s\"\"\"println(messages.msg)\n           |\"\"\".stripMargin\n    )\n\n    val mainClassName = if (actualScalaVersion.startsWith(\"3\")) \"print_sc\" else \"print\"\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, extraOptions, \"dir\", \"--main-class\", mainClassName).call(cwd =\n          root\n        ).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"sub-directory\") {\n    val fileName          = \"script.sc\"\n    val expectedClassName =\n      if (actualScalaVersion.startsWith(\"3.\"))\n        fileName.stripSuffix(\".sc\") + \"$_\"\n      else\n        fileName.stripSuffix(\".sc\") + \"$\"\n    val scriptPath = os.rel / \"something\" / fileName\n    val inputs     = TestInputs(\n      scriptPath ->\n        s\"\"\"println(getClass.getName)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, scriptPath.toString)\n        .call(cwd = root)\n        .out.text()\n        .trim\n      expect(output == expectedClassName)\n    }\n  }\n\n  test(\"sub-directory and script\") {\n    val fileName          = \"script.sc\"\n    val expectedClassName =\n      if (actualScalaVersion.startsWith(\"3.\"))\n        fileName.stripSuffix(\".sc\") + \"$_\"\n      else\n        fileName.stripSuffix(\".sc\") + \"$\"\n    val scriptPath = os.rel / \"something\" / fileName\n    val inputs     = TestInputs(\n      os.rel / \"dir\" / \"Messages.scala\" ->\n        s\"\"\"object Messages {\n           |  def msg = \"Hello\"\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"dir\" / \"Print.scala\" ->\n        s\"\"\"object Print {\n           |  def main(args: Array[String]): Unit =\n           |    println(Messages.msg)\n           |}\n           |\"\"\".stripMargin,\n      scriptPath ->\n        s\"\"\"println(getClass.getName)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"dir\", scriptPath.toString)\n        .call(cwd = root)\n        .out.text()\n        .trim\n      expect(output == expectedClassName)\n    }\n  }\n\n  def stackTraceInScriptScala2(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"throws.sc\" ->\n        s\"\"\"def something(): String =\n           |  sys.error(\"nope\")\n           |try something()\n           |catch {\n           |  case e: Exception =>\n           |    throw new Exception(\"Caught exception during processing\", e)\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val cmd = Seq[os.Shellable](\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\",\n        \"--java-prop\",\n        \"scala.colored-stack-traces=false\"\n      )\n      val res            = os.proc(cmd).call(cwd = root, check = false, mergeErrIntoOut = true)\n      val output         = res.out.lines()\n      val exceptionLines = output.dropWhile(!_.startsWith(\"Exception in thread \"))\n      val tab            = \"\\t\"\n      val expectedLines  =\n        if (actualScalaVersion.startsWith(\"2.12.\"))\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:6)\n             |${tab}at throws$$delayedInit$$body.apply(throws.sc:65534)\n             |${tab}at scala.Function0.apply$$mcV$$sp(Function0.scala:39)\n             |${tab}at scala.Function0.apply$$mcV$$sp$$(Function0.scala:39)\n             |${tab}at scala.runtime.AbstractFunction0.apply$$mcV$$sp(AbstractFunction0.scala:17)\n             |${tab}at scala.App.$$anonfun$$main$$1$$adapted(App.scala:80)\n             |${tab}at scala.collection.immutable.List.foreach(List.scala:431)\n             |${tab}at scala.App.main(App.scala:80)\n             |${tab}at scala.App.main$$(App.scala:78)\n             |${tab}at throws$$.main(throws.sc:65534)\n             |${tab}at throws.main(throws.sc)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:30)\n             |${tab}at throws$$.something(throws.sc:2)\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:3)\n             |$tab... 10 more\"\"\".stripMargin.linesIterator.toVector\n        else if (\n          actualScalaVersion.coursierVersion >= \"2.13.13\".coursierVersion &&\n          actualScalaVersion.coursierVersion < \"2.13.17\".coursierVersion\n        )\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:6)\n             |${tab}at throws$$delayedInit$$body.apply(throws.sc:65534)\n             |${tab}at scala.Function0.apply$$mcV$$sp(Function0.scala:42)\n             |${tab}at scala.Function0.apply$$mcV$$sp$$(Function0.scala:42)\n             |${tab}at scala.runtime.AbstractFunction0.apply$$mcV$$sp(AbstractFunction0.scala:17)\n             |${tab}at scala.App.$$anonfun$$main$$1(App.scala:98)\n             |${tab}at scala.App.$$anonfun$$main$$1$$adapted(App.scala:98)\n             |${tab}at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:619)\n             |${tab}at scala.collection.IterableOnceOps.foreach$$(IterableOnce.scala:617)\n             |${tab}at scala.collection.AbstractIterable.foreach(Iterable.scala:935)\n             |${tab}at scala.App.main(App.scala:98)\n             |${tab}at scala.App.main$$(App.scala:96)\n             |${tab}at throws$$.main(throws.sc:65534)\n             |${tab}at throws.main(throws.sc)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:27)\n             |${tab}at throws$$.something(throws.sc:2)\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:3)\n             |$tab... 13 more\n             |\"\"\".stripMargin.linesIterator.toVector\n        else if (actualScalaVersion.coursierVersion >= \"2.13.17\".coursierVersion)\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:6)\n             |${tab}at throws$$delayedInit$$body.apply(throws.sc:65534)\n             |${tab}at scala.Function0.apply$$mcV$$sp(Function0.scala:42)\n             |${tab}at scala.Function0.apply$$mcV$$sp$$(Function0.scala:42)\n             |${tab}at scala.runtime.AbstractFunction0.apply$$mcV$$sp(AbstractFunction0.scala:17)\n             |${tab}at scala.App.$$anonfun$$main$$1(App.scala:98)\n             |${tab}at scala.App.$$anonfun$$main$$1$$adapted(App.scala:98)\n             |${tab}at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:630)\n             |${tab}at scala.collection.IterableOnceOps.foreach$$(IterableOnce.scala:628)\n             |${tab}at scala.collection.AbstractIterable.foreach(Iterable.scala:936)\n             |${tab}at scala.App.main(App.scala:98)\n             |${tab}at scala.App.main$$(App.scala:96)\n             |${tab}at throws$$.main(throws.sc:65534)\n             |${tab}at throws.main(throws.sc)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:27)\n             |${tab}at throws$$.something(throws.sc:2)\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:3)\n             |$tab... 13 more\n             |\"\"\".stripMargin.linesIterator.toVector\n        else\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:6)\n             |${tab}at throws$$delayedInit$$body.apply(throws.sc:65534)\n             |${tab}at scala.Function0.apply$$mcV$$sp(Function0.scala:42)\n             |${tab}at scala.Function0.apply$$mcV$$sp$$(Function0.scala:42)\n             |${tab}at scala.runtime.AbstractFunction0.apply$$mcV$$sp(AbstractFunction0.scala:17)\n             |${tab}at scala.App.$$anonfun$$main$$1(App.scala:98)\n             |${tab}at scala.App.$$anonfun$$main$$1$$adapted(App.scala:98)\n             |${tab}at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\n             |${tab}at scala.collection.IterableOnceOps.foreach$$(IterableOnce.scala:574)\n             |${tab}at scala.collection.AbstractIterable.foreach(Iterable.scala:933)\n             |${tab}at scala.App.main(App.scala:98)\n             |${tab}at scala.App.main$$(App.scala:96)\n             |${tab}at throws$$.main(throws.sc:65534)\n             |${tab}at throws.main(throws.sc)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:27)\n             |${tab}at throws$$.something(throws.sc:2)\n             |${tab}at throws$$.delayedEndpoint$$throws$$1(throws.sc:3)\n             |$tab... 13 more\n             |\"\"\".stripMargin.linesIterator.toVector\n      if (exceptionLines != expectedLines) {\n        println(exceptionLines.mkString(\"\\n\"))\n        println(expectedLines)\n      }\n      assert(\n        exceptionLines.length == expectedLines.length,\n        clues(output, exceptionLines.length, expectedLines.length)\n      )\n      for (i <- exceptionLines.indices)\n        assert(\n          exceptionLines(i) == expectedLines(i),\n          clues(output, exceptionLines(i), expectedLines(i))\n        )\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2.\"))\n    test(\"stack traces in script\") {\n      stackTraceInScriptScala2()\n    }\n\n  def scriptStackTraceScala3(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"throws.sc\" ->\n        s\"\"\"def something(): String =\n           |  val message = \"nope\"\n           |  sys.error(message)\n           |\n           |try something()\n           |catch {\n           |  case e: Exception =>\n           |    throw new Exception(\"Caught exception during processing\", e)\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val cmd = Seq[os.Shellable](\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\"\n      )\n      val res    = os.proc(cmd).call(cwd = root, check = false, mergeErrIntoOut = true)\n      val output = res.out.lines()\n      val exceptionLines: Vector[String] = output\n        .map(stripAnsi)\n        .dropWhile(!_.startsWith(\"Exception in thread \"))\n      val tab = \"\\t\"\n\n      val (caughtLines, causedLines) = exceptionLines.span(!_.startsWith(\"Caused by:\"))\n\n      assert(caughtLines.length > 1)\n      assert(caughtLines.contains(s\"${tab}at throws$$_.<init>(throws.sc:8)\"), clues(caughtLines))\n\n      assert(causedLines.length > 1)\n      assert(\n        causedLines.contains(s\"Caused by: java.lang.RuntimeException: nope\") &&\n        causedLines.contains(s\"${tab}at throws$$_.something(throws.sc:3)\") &&\n        causedLines.contains(s\"${tab}at throws$$_.<init>(throws.sc:5)\"),\n        clues(causedLines)\n      )\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3.\"))\n    test(\"stack traces in script in Scala 3\") {\n      scriptStackTraceScala3()\n    }\n\n  test(\"pick .scala main class over in-context scripts\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"object Hello extends App {\n          |  println(s\"Hello ${scripts.`Script-1`.world}\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"scripts\" / \"Script-1.sc\" -> \"\"\"def world: String = \"world\"\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\"\n      )\n        .call(cwd = root)\n      expect(res.out.trim() == \"Hello world\")\n    }\n  }\n\n  test(\"interconnection between scripts\") {\n    val inputs = TestInputs(\n      os.rel / \"f.sc\"     -> \"def f(x: String) = println(x + x + x)\",\n      os.rel / \"main0.sc\" -> \"f.f(args(0))\"\n    )\n    inputs.fromRoot { root =>\n      val p =\n        os.proc(TestUtil.cli, \"main0.sc\", \"f.sc\", \"--\", \"20\").call(cwd = root)\n      val res = p.out.trim()\n      expect(res == \"202020\")\n    }\n  }\n  test(\"CLI args passed to script\") {\n    val inputs = TestInputs(os.rel / \"f.sc\" -> \"println(args(0))\")\n    inputs.fromRoot { root =>\n      val p = os.proc(TestUtil.cli, \"f.sc\", \"--\", \"16\").call(cwd = root)\n      expect(p.out.trim() == \"16\")\n    }\n  }\n  test(\"print the name of script\") {\n    val inputs = TestInputs(os.rel / \"hello.sc\" -> \"println(scriptPath)\")\n    inputs.fromRoot { root =>\n      val p = os.proc(TestUtil.cli, \"hello.sc\").call(cwd = root)\n      expect(p.out.trim() == \"hello.sc\")\n    }\n  }\n  test(\"print the name of nested script\") {\n    val inputs = TestInputs(os.rel / \"dir\" / \"hello.sc\" -> \"println(scriptPath)\")\n    inputs.fromRoot { root =>\n      val p = os.proc(TestUtil.cli, \"dir/hello.sc\").call(cwd = root)\n      expect(p.out.trim() == \"dir/hello.sc\")\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"CLI args passed to shebang script\") {\n      val inputs = TestInputs(\n        os.rel / \"f.sc\" ->\n          s\"\"\"|#!/usr/bin/env -S ${TestUtil.cli.mkString(\" \")} shebang -S 2.13\n              |//> using scala $actualScalaVersion\n              |println(args.toList)\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        os.perms.set(root / \"f.sc\", os.PermSet.fromString(\"rwx------\"))\n        val p = os.proc(\"./f.sc\", \"1\", \"2\", \"3\", \"-v\").call(cwd = root)\n        expect(p.out.trim() == \"List(1, 2, 3, -v)\")\n      }\n    }\n\n  test(\"script file with shebang header and no extension run with scala-cli\") {\n    val expected   = \"success\"\n    val scriptName = \"scriptWithShebang\"\n    val inputs     = TestInputs(\n      os.rel / scriptName ->\n        s\"\"\"|#!/usr/bin/env -S ${TestUtil.cli.mkString(\" \")} shebang\n            |//> using scala $actualScalaVersion\n            |println(s\"$expected\")\"\"\".stripMargin\n    )\n    val cmd = TestUtil.cli ++ Seq(scriptName)\n    inputs.fromRoot { root =>\n      val actual = os.proc(cmd)\n        .call(cwd = root).out.trim()\n      expect(actual == expected)\n    }\n  }\n\n  test(\"script file with shebang header and no extension run with scala-cli shebang\") {\n    val inputs = TestInputs(\n      os.rel / \"script-with-shebang\" ->\n        s\"\"\"|#!/usr/bin/env -S ${TestUtil.cli.mkString(\" \")} shebang -S 2.13\n            |//> using scala $actualScalaVersion\n            |println(args.toList)\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = if (!Properties.isWin) {\n        os.perms.set(root / \"script-with-shebang\", os.PermSet.fromString(\"rwx------\"))\n        os.proc(\"./script-with-shebang\", \"1\", \"2\", \"3\", \"-v\").call(cwd = root).out.trim()\n      }\n      else\n        os.proc(TestUtil.cli, \"shebang\", \"script-with-shebang\", \"1\", \"2\", \"3\", \"-v\")\n          .call(cwd = root).out.trim()\n      expect(output == \"List(1, 2, 3, -v)\")\n    }\n  }\n  test(\"script file with NO shebang header and no extension run with scala-cli shebang\") {\n    val inputs = TestInputs(\n      os.rel / \"script-no-shebang\" ->\n        s\"\"\"//> using scala $actualScalaVersion\n           |println(args.toList)\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = if (!Properties.isWin) {\n        os.perms.set(root / \"script-no-shebang\", os.PermSet.fromString(\"rwx------\"))\n        os.proc(TestUtil.cli, \"shebang\", \"script-no-shebang\", \"1\", \"2\", \"3\", \"-v\")\n          .call(cwd = root, check = false, stderr = os.Pipe).err.trim()\n      }\n      else\n        os.proc(TestUtil.cli, \"shebang\", \"script-no-shebang\", \"1\", \"2\", \"3\", \"-v\")\n          .call(cwd = root, check = false, stderr = os.Pipe).err.trim()\n\n      expect(output.contains(\n        \"unrecognized source type (expected .scala or .sc extension, or a directory)\"\n      ))\n\n      if (TestUtil.isShebangCapableShell)\n        expect(output.contains(\"shebang header\"))\n    }\n  }\n\n  test(\"shebang run does not produce update-dependency warnings\") {\n    val dependencyOsLib = \"com.lihaoyi::os-lib:0.7.8\"\n\n    val inputs = TestInputs(\n      os.rel / \"script.sc\" ->\n        s\"\"\"//> using scala $actualScalaVersion\n           |//> using dep $dependencyOsLib\n           |\n           |println(args.toList)\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val proc = os.proc(TestUtil.cli, \"shebang\", \"script.sc\", \"1\", \"2\", \"3\", \"-v\")\n        .call(cwd = root, mergeErrIntoOut = true)\n\n      expect(!proc.out.text().contains(\"[hint] \\\"os-lib is outdated\"))\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3.\")) {\n    test(\"no deadlock when running background threads\") {\n      val inputs = TestInputs(\n        os.rel / \"script.sc\" ->\n          s\"\"\"//> using scala $actualScalaVersion\n             |\n             |import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor, Future}\n             |import scala.concurrent.duration._\n             |\n             |implicit val ec: ExecutionContextExecutor = ExecutionContext.global\n             |\n             |val future =\n             |  for {\n             |    message <- Future(\"Hello world\")\n             |    _ <- Future(println(message))\n             |  } yield ()\n             |\n             |Await.ready(future, Duration(5, SECONDS))\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        os.proc(TestUtil.cli, \"script.sc\")\n          .call(cwd = root, mergeErrIntoOut = true)\n      }\n    }\n\n    test(\"user readable error when @main is used\") {\n      val inputs = TestInputs(\n        os.rel / \"script.sc\" ->\n          \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n            |/*ignore this while regexing*/ @main def main(args: Strings*): Unit = println(\"Hello\")\n            |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"script.sc\")\n          .call(cwd = root, mergeErrIntoOut = true, check = false, stdout = os.Pipe)\n\n        val outputNormalized: String = normalizeConsoleOutput(res.out.text())\n\n        expect(outputNormalized.contains(\"Not found: type Strings\"))\n        expect(outputNormalized.contains(\n          \"[warn]  Annotation @main in .sc scripts is not supported, use .scala format instead\"\n        ))\n\n        val snippetRes = os.proc(\n          TestUtil.cli,\n          \"--script-snippet\",\n          \"\"\"@main def main(args: Strings*): Unit = println(\"Hello\")\"\"\"\n        )\n          .call(cwd = root, mergeErrIntoOut = true, check = false, stdout = os.Pipe)\n\n        val snippetOutputNormalized: String = normalizeConsoleOutput(snippetRes.out.text())\n\n        expect(snippetOutputNormalized.contains(\"Not found: type Strings\"))\n        expect(snippetOutputNormalized.contains(\n          \"[warn]  Annotation @main in .sc scripts is not supported, use .scala format instead\"\n        ))\n\n        val noBloopRes = os.proc(TestUtil.cli, \"--server=false\", \"script.sc\")\n          .call(cwd = root, mergeErrIntoOut = true, check = false, stdout = os.Pipe)\n\n        val noBloopOutputNormalized: String = normalizeConsoleOutput(noBloopRes.out.text())\n\n        expect(noBloopOutputNormalized.contains(\n          \"[warn]  Annotation @main in .sc scripts is not supported\"\n        ))\n\n        val scala2Res = os.proc(TestUtil.cli, \"--server=false\", \"script.sc\", \"-S\", \"2\")\n          .call(cwd = root, mergeErrIntoOut = true, check = false, stdout = os.Pipe)\n\n        val scala2OutputNormalized: String = normalizeConsoleOutput(scala2Res.out.text())\n\n        expect(scala2OutputNormalized.contains(\n          \"[warn]  Annotation @main in .sc scripts is not supported, it will be ignored, use .scala format instead\"\n        ))\n      }\n    }\n\n    test(\"@main error unchanged in .scala\") {\n      val inputs = TestInputs(\n        os.rel / \"main.scala\" ->\n          \"\"\"class Main {\n            | @main def main(args: String*): Unit = println(\"Hello\")\n            |}\n            |\"\"\".stripMargin,\n        os.rel / \"script.sc\" ->\n          \"\"\"@main def main(args: String*): Unit = println(\"Hello\")\"\"\"\n      )\n      inputs.fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"main.scala\")\n          .call(cwd = root, mergeErrIntoOut = true, check = false)\n\n        expect(!normalizeConsoleOutput(res.out.text())\n          .contains(\"Annotation @main in .sc scripts is not supported\"))\n\n        val noBloopRes = os.proc(TestUtil.cli, \"--server=false\", \"main.scala\")\n          .call(cwd = root, mergeErrIntoOut = true, check = false)\n\n        expect(!normalizeConsoleOutput(noBloopRes.out.text())\n          .contains(\"Annotation @main in .sc scripts is not supported\"))\n\n        val noBloopScalaSnippetRes = os.proc(\n          TestUtil.cli,\n          \"--server=false\",\n          \"--scala-snippet\",\n          \"class Main { @main def main(args: Strings*): Unit = println(\\\"Hello\\\")}\"\n        )\n          .call(cwd = root, mergeErrIntoOut = true, check = false)\n\n        expect(!normalizeConsoleOutput(noBloopScalaSnippetRes.out.text())\n          .contains(\"Annotation @main in .sc scripts is not supported\"))\n      }\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    for {\n      useObjectWrapper <- Seq(true, false)\n      wrapperType = if (useObjectWrapper) \"object\" else \"class\"\n    }\n      test(s\"$wrapperType script wrapper satisfies strict compiler flags\") {\n        val expectedMessage      = \"Hello\"\n        val sourceFileName       = \"strictClassWrapper.sc\"\n        val versionDependentOpts =\n          if (\n            actualScalaVersion.coursierVersion >= \"3.5.0\".coursierVersion\n            || actualScalaVersion.startsWith(\"3.5\")\n          )\n            Seq(\"-Wsafe-init\")\n          else Seq(\"-Yno-experimental\", \"-Ysafe-init\")\n        val compilerOpts = Seq(\n          \"-Werror\",\n          \"-Wnonunit-statement\",\n          \"-Wunused:all\",\n          \"-Wvalue-discard\",\n          \"-deprecation\",\n          \"-feature\",\n          \"-language:strictEquality\",\n          \"-new-syntax\",\n          \"-old-syntax\",\n          \"-unchecked\",\n          \"-no-indent\"\n        ) ++ versionDependentOpts\n        TestInputs(\n          os.rel / sourceFileName ->\n            s\"\"\"//> using options ${compilerOpts.mkString(\" \")}\n               |\n               |println(\"$expectedMessage\")\n               |\"\"\".stripMargin\n        )\n          .fromRoot { root =>\n            val wrapperOptions = if (useObjectWrapper) Seq(\"--power\", \"--object-wrapper\") else Nil\n            val r = os.proc(TestUtil.cli, \"run\", sourceFileName, wrapperOptions, extraOptions)\n              .call(cwd = root)\n            expect(r.out.trim() == expectedMessage)\n          }\n      }\n\n  if (actualScalaVersion.startsWith(\"2\"))\n    for {\n      useObjectWrapper <- Seq(true, false)\n      wrapperType = if (useObjectWrapper) \"object\" else \"App\"\n    }\n      test(s\"$wrapperType script wrapper satisfies strict compiler flags\") {\n        val expectedMessage = \"Hello\"\n        val sourceFileName  = \"strictClassWrapper.sc\"\n        val warningOptions  =\n          if (actualScalaVersion.startsWith(\"2.13\"))\n            Seq(\"-Werror\", \"-Wdead-code\", \"-Wextra-implicit\")\n          else Seq(\"-Werror\")\n        TestInputs(\n          os.rel / sourceFileName ->\n            s\"\"\"//> using options ${warningOptions.mkString(\" \")}\n               |//> using options -deprecation -feature\n               |//> using options -unchecked\n               |\n               |println(\"$expectedMessage\")\n               |\"\"\".stripMargin\n        )\n          .fromRoot { root =>\n            val wrapperOptions = if (useObjectWrapper) Seq(\"--power\", \"--object-wrapper\") else Nil\n            val r = os.proc(TestUtil.cli, \"run\", sourceFileName, wrapperOptions, extraOptions)\n              .call(cwd = root)\n            expect(r.out.trim() == expectedMessage)\n          }\n      }\n\n  test(\"verify drive-relative JAVA_HOME works\") {\n    TestUtil.retryOnCi() {\n      val jvmIndex =\n        if TestUtil.isJvmCli && !isScala38OrNewer then Constants.minimumLauncherJavaVersion\n        else if isScala38OrNewer then Constants.defaultJvmVersion\n        else 8\n      val oldJavaHome =\n        os.Path(os.proc(TestUtil.cs, \"java-home\", \"--jvm\", jvmIndex).call().out.trim(), os.pwd)\n\n      val dr = os.Path.driveRoot\n\n      // forward slash is legal in `Windows`\n      val javaHome = oldJavaHome.toString.replace('\\\\', '/')\n      expect(javaHome.drop(dr.length).startsWith(\"/\"))\n\n      val sysPath: String = System.getenv(\"PATH\").replace('\\\\', '/')\n      val newPath: String = s\"$javaHome/bin\" + File.pathSeparator + sysPath\n\n      val extraEnv = Map(\n        \"JAVA_HOME\" -> oldJavaHome.toString,\n        \"PATH\"      -> newPath\n      )\n\n      val inputs = TestInputs(\n        os.rel / \"script-with-shebang\" ->\n          s\"\"\"|#!/usr/bin/env -S ${TestUtil.cli.mkString(\" \")} shebang -S 2.13\n              |//> using scala $actualScalaVersion\n              |println(args.toList)\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        printf(\n          \"TestUtil.cli: [%s]\\njavaHome: [%s]\\nnewPath: [%s]\\n\",\n          TestUtil.cli,\n          javaHome,\n          newPath\n        )\n        val proc = if (!Properties.isWin) {\n          os.perms.set(root / \"script-with-shebang\", os.PermSet.fromString(\"rwx------\"))\n          os.proc(\"./script-with-shebang\", \"1\", \"2\", \"3\", \"-v\")\n        }\n        else\n          os.proc(TestUtil.cli, \"shebang\", \"script-with-shebang\", \"1\", \"2\", \"3\", \"-v\")\n\n        val output = proc.call(cwd = root, env = extraEnv).out.trim()\n\n        val expectedOutput = \"List(1, 2, 3, -v)\"\n\n        expect(output == expectedOutput)\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunSnippetTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\ntrait RunSnippetTestDefinitions { this: RunTestDefinitions =>\n  test(\"correctly run a script snippet\") {\n    emptyInputs.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--script-snippet\",\n          s\"println($quotation$msg$quotation)\",\n          extraOptions\n        )\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"correctly run a scala snippet\") {\n    emptyInputs.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(\n          TestUtil.cli,\n          \"run\",\n          \"--scala-snippet\",\n          s\"object Hello extends App { println($quotation$msg$quotation) }\",\n          extraOptions\n        )\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"correctly run a java snippet\") {\n    emptyInputs.fromRoot { root =>\n      val quotation = TestUtil.argQuotationMark\n      val msg       = \"Hello world\"\n      val res       = os.proc(\n        TestUtil.cli,\n        \"run\",\n        \"--java-snippet\",\n        s\"public class Main { public static void main(String[] args) { System.out.println($quotation$msg$quotation); } }\",\n        extraOptions\n      )\n        .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"correctly run a markdown snippet\") {\n    emptyInputs.fromRoot { root =>\n      val msg       = \"Hello world\"\n      val quotation = TestUtil.argQuotationMark\n      val res       =\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"run\",\n          \"--markdown-snippet\",\n          s\"\"\"# A Markdown snippet\n             |With some scala code\n             |```scala\n             |println($quotation$msg$quotation)\n             |```\"\"\".stripMargin,\n          extraOptions\n        )\n          .call(cwd = root)\n      expect(res.out.trim() == msg)\n    }\n  }\n\n  test(\"correctly run multiple snippets\") {\n    emptyInputs.fromRoot { root =>\n      val quotation = TestUtil.argQuotationMark\n\n      val scriptSnippetOption                                      = \"--script-snippet\"\n      val scriptMessages @ Seq(scriptMsg1, scriptMsg2, scriptMsg3) =\n        Seq(\"hello script 1\", \"hello script 2\", \"hello script 3\")\n      val script0 =\n        s\"\"\"def printAll(): Unit = println(\n           |  Seq(\n           |    snippet1.scriptMsg1, snippet2.scriptMsg2, snippet3.scriptMsg3,\n           |    ScalaSnippet1.msg, ScalaSnippet2.msg, ScalaSnippet3.msg,\n           |    JavaSnippet1.msg, JavaSnippet2.msg, JavaSnippet3.msg\n           |  ).mkString($quotation;$quotation)\n           |)\"\"\".stripMargin\n      val script1 = s\"def scriptMsg1: String = $quotation$scriptMsg1$quotation\"\n      val script2 = s\"def scriptMsg2: String = $quotation$scriptMsg2$quotation\"\n      val script3 = s\"def scriptMsg3: String = $quotation$scriptMsg3$quotation\"\n\n      val scalaSnippetOption                                   = \"--scala-snippet\"\n      val scalaMessages @ Seq(scalaMsg1, scalaMsg2, scalaMsg3) =\n        Seq(\"hello scala 1\", \"hello scala 2\", \"hello scala 3\")\n      val scala0 = \"object SnippetMain extends App { snippet.printAll() }\"\n      val scala1 = s\"object ScalaSnippet1 { def msg: String = $quotation$scalaMsg1$quotation }\"\n      val scala2 = s\"object ScalaSnippet2 { def msg: String = $quotation$scalaMsg2$quotation }\"\n      val scala3 = s\"object ScalaSnippet3 { def msg: String = $quotation$scalaMsg3$quotation }\"\n\n      val javaSnippetOption                                = \"--java-snippet\"\n      val javaMessages @ Seq(javaMsg1, javaMsg2, javaMsg3) =\n        Seq(\"hello scala 1\", \"hello scala 2\", \"hello scala 3\")\n      val java1 =\n        s\"public class JavaSnippet1 { public static String msg = $quotation$javaMsg1$quotation; }\"\n      val java2 =\n        s\"public class JavaSnippet2 { public static String msg = $quotation$javaMsg2$quotation; }\"\n      val java3 =\n        s\"public class JavaSnippet3 { public static String msg = $quotation$javaMsg3$quotation; }\"\n\n      val expectedOutput = (scriptMessages ++ scalaMessages ++ javaMessages).mkString(\";\")\n\n      val res = os.proc(\n        TestUtil.cli,\n        \"run\",\n        scriptSnippetOption,\n        script0,\n        scriptSnippetOption,\n        script1,\n        scriptSnippetOption,\n        script2,\n        scriptSnippetOption,\n        script3,\n        scalaSnippetOption,\n        scala0,\n        scalaSnippetOption,\n        scala1,\n        scalaSnippetOption,\n        scala2,\n        scalaSnippetOption,\n        scala3,\n        javaSnippetOption,\n        java1,\n        javaSnippetOption,\n        java2,\n        javaSnippetOption,\n        java3,\n        extraOptions\n      )\n        .call(cwd = root)\n      expect(res.out.trim() == expectedOutput)\n    }\n  }\n  test(\"running a script snippet should not create the workspace dir in cwd\") {\n    emptyInputs.fromRoot { root =>\n      val quotation = TestUtil.argQuotationMark\n      val msg       = \"Hello World from snippet\"\n      os.proc(TestUtil.cli, \"-e\", s\"println($quotation$msg$quotation)\", extraOptions)\n        .call(cwd = root)\n        .out.trim()\n      expect(!os.exists(root / Constants.workspaceDirName))\n    }\n  }\n  test(\"running a script snippet with one source file should create the workspace dir in cwd\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello {\n           | val hello = \"Hello World\"\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      os.proc(TestUtil.cli, \"-e\", s\"println(Hello.hello)\", \".\", extraOptions)\n        .call(cwd = root)\n        .out.trim()\n      expect(os.exists(root / Constants.workspaceDirName))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.{ByteArrayOutputStream, File}\nimport java.nio.charset.Charset\n\nimport scala.cli.integration.util.DockerServer\nimport scala.io.Codec\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nabstract class RunTestDefinitions\n    extends WithWarmUpScalaCliSuite\n    with TestScalaVersionArgs\n    with RunScriptTestDefinitions\n    with RunScalaJsTestDefinitions\n    with RunScalaNativeTestDefinitions\n    with RunPipedSourcesTestDefinitions\n    with RunGistTestDefinitions\n    with RunScalacCompatTestDefinitions\n    with RunSnippetTestDefinitions\n    with RunScalaPyTestDefinitions\n    with RunZipTestDefinitions\n    with RunJdkTestDefinitions\n    with CoursierScalaInstallationTestHelper { this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n  protected val emptyInputs: TestInputs        = TestInputs(os.rel / \".placeholder\" -> \"\")\n\n  override def warmUpExtraTestOptions: Seq[String] = extraOptions\n\n  protected val ciOpt: Seq[String] =\n    Option(System.getenv(\"CI\")).map(v => Seq(\"-e\", s\"CI=$v\")).getOrElse(Nil)\n\n  protected def getScalaVersion(\n    scalaVersionIndex: String,\n    root: os.Path,\n    check: Boolean = true,\n    mergeErrIntoOut: Boolean = false\n  ): String =\n    os.proc(\n      TestUtil.cli,\n      \"run\",\n      \"-e\",\n      \"println(dotty.tools.dotc.config.Properties.simpleVersionString)\",\n      \"-S\",\n      scalaVersionIndex,\n      \"--with-compiler\",\n      TestUtil.extraOptions\n    )\n      .call(cwd = root, check = check, mergeErrIntoOut = mergeErrIntoOut)\n      .out\n      .trim()\n\n  test(\"print command\") {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, extraOptions, fileName, \"--command\").call(cwd = root).out.trim()\n      val command      = output.linesIterator.toVector\n      val actualOutput = os.proc(command).call(cwd = root).out.trim()\n      expect(actualOutput == message)\n    }\n  }\n\n  test(\"manifest\") {\n    val message    = \"Hello\"\n    val converters =\n      if (actualScalaVersion.startsWith(\"2.12.\")) \"scala.collection.JavaConverters._\"\n      else \"scala.jdk.CollectionConverters._\"\n    val inputs = TestInputs(\n      os.rel / \"Simple.scala\" ->\n        s\"\"\"import java.io.File\n           |import java.util.zip.ZipFile\n           |import $converters\n           |\n           |object Simple {\n           |  private def manifestClassPathCheck(): Unit = {\n           |    val cp = sys.props(\"java.class.path\")\n           |    assert(!cp.contains(File.pathSeparator), s\"Expected single entry in class path, got $$cp\")\n           |    val zf = new ZipFile(new File(cp))\n           |    val entries = zf.entries.asScala.map(_.getName).toVector\n           |    zf.close()\n           |    assert(entries == Seq(\"META-INF/MANIFEST.MF\"), s\"Expected only META-INF/MANIFEST.MF entry, got $$entries\")\n           |  }\n           |  def main(args: Array[String]): Unit = {\n           |    manifestClassPathCheck()\n           |    val msg = \"$message\"\n           |    println(msg)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \"--use-manifest\", \".\")\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n\n  def platformNl: String = if (Properties.isWin) \"\\\\r\\\\n\" else \"\\\\n\"\n\n  test(\"No default inputs when the `run` sub-command is launched with no args\") {\n    val inputs = TestInputs(\n      os.rel / \"dir\" / \"print.sc\" ->\n        s\"\"\"println(\"Foo\")\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"run\", extraOptions, \"--main-class\", \"print\")\n        .call(cwd = root / \"dir\", check = false, mergeErrIntoOut = true)\n      val output = res.out.trim()\n      expect(res.exitCode != 0)\n      expect(output.contains(\"No inputs provided\"))\n    }\n  }\n\n  test(\"Debugging\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"object Foo {\n           |  def main(args: Array[String]): Unit = {\n           |    println(\"foo\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val out1 = os.proc(TestUtil.cli, \"run\", extraOptions, \".\", \"--debug\", \"--command\")\n        .call(cwd = root).out.trim().lines.toList.asScala\n      val out2 = os.proc(\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\",\n        \"--debug-port\",\n        \"5006\",\n        \"--debug-mode\",\n        \"listen\",\n        \"--command\"\n      ).call(cwd = root).out.trim().lines.toList.asScala\n\n      def debugString(server: String, port: String) =\n        s\"-agentlib:jdwp=transport=dt_socket,server=$server,suspend=y,address=$port\"\n\n      assert(out1.contains(debugString(\"y\", \"5005\")))\n      assert(out2.contains(debugString(\"n\", \"5006\")))\n    }\n  }\n\n  test(\"Pass arguments\") {\n    val inputs = TestInputs(\n      os.rel / \"Test.scala\" ->\n        s\"\"\"object Test {\n           |  def main(args: Array[String]): Unit = {\n           |    println(args(0))\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    val message = \"Hello\"\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"run\", extraOptions, \".\", \"--\", message)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n\n  def passArgumentsScala3(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"Test.scala\" ->\n        s\"\"\"object Test:\n           |  def main(args: Array[String]): Unit =\n           |    val message = args(0)\n           |    println(message)\n           |\"\"\".stripMargin\n    )\n    val message = \"Hello\"\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"run\", extraOptions, \".\", \"--\", message)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3.\"))\n    test(\"Pass arguments - Scala 3\") {\n      passArgumentsScala3()\n    }\n\n  test(\"setting root dir with virtual input\") {\n    val url = \"https://gist.github.com/alexarchambault/7b4ec20c4033690dd750ffd601e540ec\"\n    emptyInputs.fromRoot { root =>\n      os.proc(TestUtil.cli, extraOptions, escapedUrls(url)).call(cwd = root)\n      val path = root / \".scala-build\"\n      expect(!os.exists(path)) // virtual source should not create workspace dir in cwd\n    }\n  }\n\n  private lazy val ansiRegex = \"\\u001B\\\\[[;\\\\d]*m\".r\n\n  protected def stripAnsi(s: String): String = ansiRegex.replaceAllIn(s, \"\")\n\n  test(\"stack traces\") {\n    val inputs = TestInputs(\n      os.rel / \"Throws.scala\" ->\n        s\"\"\"object Throws {\n           |  def something(): String =\n           |    sys.error(\"nope\")\n           |  def main(args: Array[String]): Unit =\n           |    try something()\n           |    catch {\n           |      case e: Exception =>\n           |        throw new Exception(\"Caught exception during processing\", e)\n           |    }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val cmd = Seq[os.Shellable](\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\",\n        \"--java-prop\",\n        \"scala.colored-stack-traces=false\"\n      )\n      val res = os.proc(cmd).call(cwd = root, check = false, mergeErrIntoOut = true)\n      val output: Vector[String] = TestUtil.fullStableOutputLines(res)\n      // FIXME We need to have the pretty-stacktraces stuff take scala.colored-stack-traces into account\n      val exceptionLines =\n        output.map(stripAnsi).dropWhile(!_.startsWith(\"Exception in thread \"))\n      val tab = \"\\t\"\n\n      val expectedLines =\n        if actualScalaVersion.startsWith(\"2.12.\") then\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at Throws$$.main(Throws.scala:8)\n             |${tab}at Throws.main(Throws.scala)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:30)\n             |${tab}at Throws$$.something(Throws.scala:3)\n             |${tab}at Throws$$.main(Throws.scala:5)\n             |$tab... 1 more\n             |\"\"\".stripMargin.linesIterator.toVector\n        else if isScala38OrNewer then\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at Throws$$.main(Throws.scala:8)\n             |${tab}at Throws.main(Throws.scala)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:28)\n             |${tab}at Throws$$.something(Throws.scala:3)\n             |${tab}at Throws$$.main(Throws.scala:5)\n             |$tab... 1 more\n             |\"\"\".stripMargin.linesIterator.toVector\n        else if actualScalaVersion.startsWith(\"3.\") || actualScalaVersion.startsWith(\"2.13.\") then\n          s\"\"\"Exception in thread \"main\" java.lang.Exception: Caught exception during processing\n             |${tab}at Throws$$.main(Throws.scala:8)\n             |${tab}at Throws.main(Throws.scala)\n             |Caused by: java.lang.RuntimeException: nope\n             |${tab}at scala.sys.package$$.error(package.scala:27)\n             |${tab}at Throws$$.something(Throws.scala:3)\n             |${tab}at Throws$$.main(Throws.scala:5)\n             |$tab... 1 more\n             |\"\"\".stripMargin.linesIterator.toVector\n        else sys.error(s\"Unexpected Scala version: $actualScalaVersion\")\n      if exceptionLines != expectedLines then {\n        pprint.log(exceptionLines)\n        pprint.log(expectedLines)\n      }\n      assert(exceptionLines == expectedLines, clues(output))\n    }\n  }\n\n  def fd(): Unit = {\n    emptyInputs.fromRoot { root =>\n      val cliCmd         = (TestUtil.cli ++ extraOptions).mkString(\" \")\n      val cmd            = s\"\"\" $cliCmd <(echo 'println(\"Hello\" + \" from fd\")') \"\"\"\n      val res            = os.proc(\"bash\", \"-c\", cmd).call(cwd = root)\n      val expectedOutput = \"Hello from fd\" + System.lineSeparator()\n      expect(res.out.text() == expectedOutput)\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"fd\") {\n      fd()\n    }\n\n  def compileTimeOnlyJars(): Unit = {\n    val cmd = Seq[os.Shellable](\n      TestUtil.cs,\n      \"fetch\",\n      \"--intransitive\",\n      \"com.chuusai::shapeless:2.3.9\",\n      \"--scala\",\n      actualScalaVersion\n    )\n    val shapelessJar = os.proc(cmd).call().out.trim()\n    expect(os.isFile(os.Path(shapelessJar, os.pwd)))\n\n    val inputs = TestInputs(\n      os.rel / \"test.sc\" ->\n        \"\"\"val shapelessFound =\n          |  try Thread.currentThread().getContextClassLoader.loadClass(\"shapeless.HList\") != null\n          |  catch { case _: ClassNotFoundException => false }\n          |println(if (shapelessFound) \"Hello with \" + \"shapeless\" else \"Hello from \" + \"test\")\n          |\"\"\".stripMargin,\n      os.rel / \"Other.scala\" ->\n        \"\"\"object Other {\n          |  import shapeless._\n          |  val l = 2 :: \"a\" :: HNil\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val baseOutput = os.proc(TestUtil.cli, extraOptions, \".\", \"--extra-jar\", shapelessJar)\n        .call(cwd = root)\n        .out.trim()\n      expect(baseOutput == \"Hello with shapeless\")\n      val output = os.proc(TestUtil.cli, extraOptions, \".\", \"--compile-only-jar\", shapelessJar)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == \"Hello from test\")\n    }\n  }\n\n  // TODO Adapt this test to Scala 3\n  if (actualScalaVersion.startsWith(\"2.\"))\n    test(\"Compile-time only JARs\") {\n      compileTimeOnlyJars()\n    }\n\n  test(\"compile-time only for jsoniter macros\") {\n    val inputs = TestInputs(\n      os.rel / \"hello.sc\" ->\n        \"\"\"|//> using dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:2.23.2\n           |//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\n           |\n           |import com.github.plokhotnyuk.jsoniter_scala.core._\n           |import com.github.plokhotnyuk.jsoniter_scala.macros._\n           |\n           |case class User(name: String, friends: Seq[String])\n           |implicit val codec: JsonValueCodec[User] = JsonCodecMaker.make\n           |\n           |val user = readFromString[User](\"{\\\"name\\\":\\\"John\\\",\\\"friends\\\":[\\\"Mark\\\"]}\")\n           |System.out.println(user.name)\n           |val classPath = System.getProperty(\"java.class.path\").split(java.io.File.pathSeparator).iterator.toList\n           |System.out.println(classPath)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\")\n        .call(cwd = root)\n        .out.trim()\n      expect(output.contains(\"John\"))\n      expect(!output.contains(\"jsoniter-scala-macros\"))\n    }\n  }\n\n  def compileTimeOnlyDep(): Unit = {\n\n    def inputs(compileOnly: Boolean) = {\n      val directiveName = if (compileOnly) \"compileOnly.dep\" else \"dep\"\n      TestInputs(\n        os.rel / \"test.sc\" ->\n          s\"\"\"//> using $directiveName com.chuusai::shapeless:2.3.10\n             |val shapelessFound =\n             |  try Thread.currentThread().getContextClassLoader.loadClass(\"shapeless.HList\") != null\n             |  catch { case _: ClassNotFoundException => false }\n             |println(if (shapelessFound) \"Hello with \" + \"shapeless\" else \"Hello from \" + \"test\")\n             |\"\"\".stripMargin,\n        os.rel / \"Other.scala\" ->\n          \"\"\"object Other {\n            |  import shapeless._\n            |  val l = 2 :: \"a\" :: HNil\n            |}\n            |\"\"\".stripMargin\n      )\n    }\n\n    inputs(compileOnly = false).fromRoot { root =>\n      val baseOutput = os.proc(TestUtil.cli, extraOptions, \".\")\n        .call(cwd = root)\n        .out.trim()\n      expect(baseOutput == \"Hello with shapeless\")\n    }\n    inputs(compileOnly = true).fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\")\n        .call(cwd = root)\n        .out.trim()\n      expect(output == \"Hello from test\")\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"2.\"))\n    test(\"Compile-time only dep\") {\n      compileTimeOnlyDep()\n    }\n\n  if (Properties.isLinux && TestUtil.isNativeCli)\n    // TODO: restore this test once it gets reliable again\n    test(\"no JVM installed\".ignore) {\n      val fileName = \"simple.sc\"\n      val message  = \"Hello\"\n      val inputs   = TestInputs(\n        os.rel / fileName ->\n          s\"\"\"val msg = \"$message\"\n             |println(msg)\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val baseImage =\n          if (TestUtil.cliKind == \"native-static\")\n            Constants.dockerAlpineTestImage\n          else\n            Constants.dockerTestImage\n        os.copy(os.Path(TestUtil.cli.head, os.pwd), root / \"scala\")\n        val script =\n          s\"\"\"#!/usr/bin/env sh\n             |set -e\n             |./scala ${extraOptions.mkString(\" \") /* meh escaping */} $fileName| tee -a output\n             |\"\"\".stripMargin\n        os.write(root / \"script.sh\", script)\n        os.perms.set(root / \"script.sh\", \"rwxr-xr-x\")\n        val termOpt = if (System.console() == null) Nil else Seq(\"-t\")\n        val cmd     = Seq[os.Shellable](\n          \"docker\",\n          \"run\",\n          \"--rm\",\n          termOpt,\n          \"-v\",\n          s\"$root:/data\",\n          \"-w\",\n          \"/data\",\n          ciOpt,\n          baseImage,\n          \"/data/script.sh\"\n        )\n        val res = os.proc(cmd).call(cwd = root)\n        System.err.println(res.out.text())\n        val output = os.read(root / \"output\").trim\n        expect(output == message)\n      }\n    }\n\n  test(\"Java options in config file\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"simple.sc\" ->\n        s\"\"\"//> using javaOpt -Dtest.message=$message\n           |val msg = sys.props(\"test.message\")\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\").call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"Main class in config file\") {\n    val inputs = TestInputs(\n      os.rel / \"simple.scala\" ->\n        s\"\"\"//> using main-class hello\n           |object hello extends App { println(\"hello\") }\n           |object world extends App { println(\"world\") }\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\").call(cwd = root).out.trim()\n      expect(output == \"hello\")\n    }\n  }\n\n  def simpleScriptDistrolessImage(): Unit = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin,\n      os.rel / \"Dockerfile\" -> os.read(os.Path(Constants.mostlyStaticDockerfile))\n    )\n    inputs.fromRoot { root =>\n      os.copy(os.Path(TestUtil.cli.head), root / \"scala-cli\")\n      os.proc(\"docker\", \"build\", \"-t\", \"scala-cli-distroless-it\", \".\").call(\n        cwd = root,\n        stdout = os.Inherit\n      )\n      os.remove(root / \"scala\")\n      os.remove(root / \"Dockerfile\")\n      val termOpt   = if (System.console() == null) Nil else Seq(\"-t\")\n      val rawOutput = new ByteArrayOutputStream\n      val cmd       = Seq[os.Shellable](\n        \"docker\",\n        \"run\",\n        \"--rm\",\n        termOpt,\n        \"-v\",\n        s\"$root:/data\",\n        \"-w\",\n        \"/data\",\n        ciOpt,\n        \"scala-cli-distroless-it\",\n        extraOptions,\n        fileName\n      )\n      os.proc(cmd).call(\n        cwd = root,\n        stdout = os.ProcessOutput { (b, len) =>\n          rawOutput.write(b, 0, len)\n          System.err.write(b, 0, len)\n        },\n        mergeErrIntoOut = true\n      )\n      val output = new String(rawOutput.toByteArray, Charset.defaultCharset())\n      expect(output.linesIterator.toVector.last == message)\n    }\n  }\n\n  if (Properties.isLinux && TestUtil.cliKind == \"native-mostly-static\")\n    test(\"simple distroless test\") {\n      simpleScriptDistrolessImage()\n    }\n\n  private def simpleDirInputs = TestInputs(\n    os.rel / \"dir\" / \"Hello.scala\" ->\n      \"\"\"object Hello {\n        |  def main(args: Array[String]): Unit = {\n        |    val p = java.nio.file.Paths.get(getClass.getProtectionDomain.getCodeSource.getLocation.toURI)\n        |    println(p)\n        |  }\n        |}\n        |\"\"\".stripMargin\n  )\n\n  private def nonWritableTest(): Unit = {\n    simpleDirInputs.fromRoot { root =>\n      def run(): String = {\n        val res = os.proc(TestUtil.cli, \"dir\").call(cwd = root)\n        res.out.trim()\n      }\n\n      val classDirBefore = os.Path(run(), os.pwd)\n      expect(classDirBefore.startsWith(root))\n\n      try {\n        os.perms.set(root / \"dir\", \"r-xr-xr-x\")\n        val classDirAfter = os.Path(run(), os.pwd)\n        expect(!classDirAfter.startsWith(root))\n      }\n      finally os.perms.set(root / \"dir\", \"rwxr-xr-x\")\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"no .scala in non-writable directory\") {\n      nonWritableTest()\n    }\n\n  private def forbiddenDirTest(): Unit = {\n    simpleDirInputs.fromRoot { root =>\n      def run(options: String*): String = {\n        val res = os.proc(TestUtil.cli, \"dir\", options).call(cwd = root)\n        res.out.trim()\n      }\n\n      val classDirBefore = os.Path(run(), os.pwd)\n      expect(classDirBefore.startsWith(root))\n\n      val classDirAfter = os.Path(run(\"--forbid\", \"./dir\"), os.pwd)\n      expect(!classDirAfter.startsWith(root))\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"no .scala in forbidden directory\") {\n      forbiddenDirTest()\n    }\n\n  protected def resourcesInputs(\n    directive: String = \"\",\n    resourceContent: String = \"Hello from resources\"\n  ): TestInputs =\n    TestInputs(\n      os.rel / \"src\" / \"proj\" / \"resources\" / \"test\" / \"data\" -> resourceContent,\n      os.rel / \"src\" / \"proj\" / \"Example.scala\"               ->\n        s\"\"\"$directive\n           |object Example {\n           |  def main(args: Array[String]): Unit = {\n           |    val cl = Thread.currentThread().getContextClassLoader\n           |    val is = cl.getResourceAsStream(\"test/data\")\n           |    val content = scala.io.Source.fromInputStream(is)(scala.io.Codec.UTF8).mkString\n           |    println(content)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n\n  test(\"resources via command line\") {\n    val expectedMessage = \"hello\"\n    resourcesInputs(resourceContent = expectedMessage).fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"run\", \"src\", \"--resource-dirs\", \"./src/proj/resources\")\n        .call(cwd = root)\n      expect(res.out.trim() == expectedMessage)\n    }\n  }\n  test(\"resources via directive\") {\n    val expectedMessage = \"hello\"\n    resourcesInputs(\n      directive = \"//> using resourceDirs ./resources\",\n      resourceContent = expectedMessage\n    )\n      .fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"run\", \".\").call(cwd = root)\n        expect(res.out.trim() == expectedMessage)\n      }\n  }\n  test(\"resources via test directive\") {\n    val expectedMessage = \"hello\"\n    resourcesInputs(\n      directive = \"//> using test.resourceDirs ./resources\",\n      resourceContent = expectedMessage\n    )\n      .fromRoot { root =>\n        val err = os.proc(TestUtil.cli, \"run\", \".\")\n          .call(cwd = root, check = false, stderr = os.Pipe)\n        expect(err.err.trim().contains(\"java.lang.NullPointerException\"))\n        expect(err.exitCode == 1)\n        val res = os.proc(TestUtil.cli, \"run\", \".\", \"--test\")\n          .call(cwd = root)\n        expect(res.out.trim() == expectedMessage)\n      }\n  }\n\n  def argsAsIsTest(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"MyScript.scala\" ->\n        \"\"\"#!/usr/bin/env -S scala-cli shebang\n          |object MyScript {\n          |  def main(args: Array[String]): Unit =\n          |    println(\"Hello\" + args.map(\" \" + _).mkString)\n          |}\n          |\"\"\".stripMargin\n    )\n    val launcherPath = TestUtil.cli match {\n      case Seq(cli) => os.Path(cli, os.pwd)\n      case other => sys.error(s\"Expected CLI command to be just a path to a launcher (got $other)\")\n    }\n    inputs.fromRoot { root =>\n      os.perms.set(root / \"MyScript.scala\", \"rwxrwxr-x\")\n      val binDir = root / \"bin\"\n      os.makeDir.all(binDir)\n      os.copy(launcherPath, binDir / \"scala-cli\")\n      val updatedPath =\n        binDir.toString + File.pathSeparator + Option(System.getenv(\"PATH\")).getOrElse(\"\")\n      val res = os.proc(\"/bin/bash\", \"-c\", \"./MyScript.scala from tests\")\n        .call(cwd = root, env = Map(\"PATH\" -> updatedPath))\n      expect(res.out.trim() == \"Hello from tests\")\n    }\n  }\n\n  if (TestUtil.isNativeCli && !Properties.isWin)\n    test(\"should pass arguments as is\") {\n      argsAsIsTest()\n    }\n\n  test(\"test scope\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::utest:0.7.10\n          |\n          |object Main {\n          |  val err = utest.compileError(\"pprint.log(2)\")\n          |  def message = \"Hello from \" + \"tests\"\n          |  def main(args: Array[String]): Unit = {\n          |    println(message)\n          |    println(err)\n          |  }\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"Tests.test.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::pprint:0.6.6\n          |\n          |import utest._\n          |\n          |object Tests extends TestSuite {\n          |  val tests = Tests {\n          |    test(\"message\") {\n          |      assert(Main.message.startsWith(\"Hello\"))\n          |    }\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, extraOptions, \".\").call(cwd = root)\n      pprint.log(res.out.text())\n      expect(res.out.text().contains(\"Hello from tests\"))\n    }\n  }\n\n  if (!Properties.isWin)\n    test(\"CLI args passed to shebang in Scala file\") {\n      val inputs = TestInputs(\n        os.rel / \"f.scala\" ->\n          s\"\"\"|#!/usr/bin/env -S ${TestUtil.cli.mkString(\" \")} shebang\n              |object Hello {\n              |    def main(args: Array[String]) = {\n              |        println(args.toList)\n              |    }\n              |}\n              |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        os.perms.set(root / \"f.scala\", os.PermSet.fromString(\"rwx------\"))\n        val p = os.proc(\"./f.scala\", \"1\", \"2\", \"3\", \"-v\").call(cwd = root)\n        expect(p.out.trim() == \"List(1, 2, 3, -v)\")\n      }\n    }\n\n  {\n    val jvm = Constants.scala38MinJavaVersion\n    test(s\"Runs with JVM $jvm\") {\n      val inputs =\n        TestInputs(\n          os.rel / \"run.scala\" ->\n            \"\"\"object Main extends App { println(System.getProperty(\"java.version\"))}\"\"\"\n        )\n      inputs.fromRoot { root =>\n        val p   = os.proc(TestUtil.cli, \"run.scala\", \"--jvm\", jvm).call(cwd = root)\n        val res = p.out.trim()\n        expect(res.startsWith(s\"1.$jvm\") || res.startsWith(s\"$jvm.\"))\n      }\n    }\n\n    test(s\"Runs with JVM $jvm with using directive\") {\n      val inputs =\n        TestInputs(os.rel / \"run.scala\" ->\n          s\"\"\"//> using jvm $jvm\n             |object Main extends App { println(System.getProperty(\"java.version\"))}\"\"\".stripMargin)\n      inputs.fromRoot { root =>\n        val p   = os.proc(TestUtil.cli, \"run.scala\").call(cwd = root)\n        val res = p.out.trim()\n        expect(res.startsWith(s\"1.$jvm\") || res.startsWith(s\"$jvm.\"))\n      }\n    }\n  }\n\n  test(\"workspace dir\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"|//> using dep com.lihaoyi::os-lib:0.7.8\n           |\n           |object Hello extends App {\n           |  println(os.pwd)\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val p = os.proc(TestUtil.cli, \"Hello.scala\").call(cwd = root)\n      expect(p.out.trim() == root.toString)\n    }\n  }\n\n  test(\"-D.. options passed to the child app\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"object ClassHello extends App {\n          |  print(System.getProperty(\"foo\"))\n          |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"Hello.scala\", \"--java-opt\", \"-Dfoo=bar\").call(\n        cwd = root\n      )\n      expect(res.out.trim() == \"bar\")\n    }\n  }\n\n  test(\"java style -Dproperty=value system properties\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        \"\"\"object Hello extends App {\n          |  print(System.getProperty(\"foo\"))\n          |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"Hello.scala\", \"-Dfoo=bar\").call(\n        cwd = root\n      )\n      expect(res.out.trim() == \"bar\")\n    }\n  }\n\n  test(\"add to class path sources from using directive\") {\n    val fileName       = \"Hello.scala\"\n    val (hello, world) = (\"Hello\", \"World\")\n    val inputs         = TestInputs(\n      os.rel / fileName ->\n        \"\"\"|//> using file Utils.scala helper\n           |\n           |object Hello extends App {\n           |   println(s\"${Utils.hello}${helper.Helper.world}\")\n           |}\"\"\".stripMargin,\n      os.rel / \"Utils.scala\" ->\n        s\"\"\"|object Utils {\n            |  val hello = \"$hello\"\n            |}\"\"\".stripMargin,\n      os.rel / \"helper\" / \"Helper.scala\" ->\n        s\"\"\"|package helper\n            |object Helper {\n            |  val world = \"$world\"\n            |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"Hello.scala\")\n        .call(cwd = root)\n      expect(res.out.trim() == s\"$hello$world\")\n    }\n  }\n\n  test(\"multiple using directives warning message\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using scala 3.2.0\n           |\n           |object Foo extends App {\n           |  println(\"Foo\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Bar.scala\"  -> \"\",\n      os.rel / \"Hello.java\" -> \"//> using jvm 11\"\n    )\n    inputs.fromRoot { root =>\n      val warningMessage =\n        \"\"\"Using directives detected in multiple files:\n          |- Foo.scala:1:1-22\n          |- Hello.java:1:1-17\"\"\".stripMargin\n      val output1 = os.proc(TestUtil.cli, \".\").call(cwd = root, stderr = os.Pipe).err.trim()\n      val output2 = os.proc(TestUtil.cli, \"Foo.scala\", \"Bar.scala\").call(\n        cwd = root,\n        stderr = os.Pipe\n      ).err.trim()\n      expect(output1.contains(warningMessage))\n      expect(!output2.contains(\"Using directives detected in multiple files\"))\n    }\n  }\n\n  test(\"suppress multiple using directives warning message\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using scala 3.2.0\n           |\n           |object Foo extends App {\n           |  println(\"Foo\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Bar.scala\"  -> \"\",\n      os.rel / \"Hello.java\" -> \"//> using jvm 11\"\n    )\n    inputs.fromRoot { root =>\n      val warningMessage = \"Using directives detected in\"\n      val output         =\n        os.proc(TestUtil.cli, \".\", \"--suppress-warning-directives-in-multiple-files\").call(\n          cwd = root,\n          stderr = os.Pipe\n        ).err.trim()\n      expect(!output.contains(warningMessage))\n    }\n  }\n\n  test(\"suppress multiple using directives warning message with global config\") {\n    val inputs = TestInputs(\n      os.rel / \"Foo.scala\" ->\n        s\"\"\"//> using scala 3.2.0\n           |\n           |object Foo extends App {\n           |  println(\"Foo\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Bar.scala\"  -> \"\",\n      os.rel / \"Hello.java\" -> \"//> using jvm 11\"\n    )\n    inputs.fromRoot { root =>\n      val warningMessage = \"Using directives detected in\"\n\n      os.proc(TestUtil.cli, \"config\", \"suppress-warning.directives-in-multiple-files\", \"true\")\n        .call(cwd = root)\n\n      val output =\n        os.proc(TestUtil.cli, \".\").call(\n          cwd = root,\n          stderr = os.Pipe\n        ).err.trim()\n      expect(!output.contains(warningMessage))\n\n      os.proc(TestUtil.cli, \"config\", \"suppress-warning.directives-in-multiple-files\", \"false\")\n        .call(cwd = root)\n    }\n  }\n\n  def sudoTest(): Unit = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val baseImage = Constants.dockerTestImage\n      os.copy(os.Path(TestUtil.cli.head, os.pwd), root / \"scala\")\n      val script =\n        s\"\"\"#!/usr/bin/env sh\n           |set -ev\n           |useradd --create-home --shell /bin/bash test\n           |apt update\n           |apt install -y sudo\n           |./scala ${extraOptions.mkString(\" \") /* meh escaping */} $fileName| tee output-root\n           |sudo -u test ./scala clean $fileName\n           |sudo -u test ./scala ${\n            extraOptions.mkString(\n              \" \"\n            ) /* meh escaping */\n          } $fileName| tee output-user\n           |\"\"\".stripMargin\n      os.write(root / \"script.sh\", script)\n      os.perms.set(root / \"script.sh\", \"rwxr-xr-x\")\n      val termOpt = if (System.console() == null) Nil else Seq(\"-t\")\n      val cmd     = Seq[os.Shellable](\n        \"docker\",\n        \"run\",\n        \"--rm\",\n        termOpt,\n        \"-v\",\n        s\"$root:/data\",\n        \"-w\",\n        \"/data\",\n        ciOpt,\n        baseImage,\n        \"/data/script.sh\"\n      )\n      os.proc(cmd).call(cwd = root, stdout = os.Inherit)\n      val rootOutput = os.read(root / \"output-root\").trim\n      expect(rootOutput == message)\n      val userOutput = os.read(root / \"output-user\").trim\n      expect(userOutput == message)\n    }\n  }\n\n  if (\n    Properties.isLinux && TestUtil.isNativeCli && TestUtil.cliKind != \"native-static\" &&\n    TestUtil.cliKind != \"native-mostly-static\"\n  )\n    // TODO: restore this test once it gets reliable again\n    test(\"sudo\".flaky) {\n      sudoTest()\n    }\n\n  def authProxyTest(legacySetup: Boolean): Unit = {\n    val okDir    = os.rel / \"ok\"\n    val wrongDir = os.rel / \"wrong\"\n    val inputs   = TestInputs(\n      Seq(okDir, wrongDir).flatMap { baseDir =>\n        Seq(\n          baseDir / \"Simple.scala\" ->\n            \"\"\"object Simple {\n              |  def main(args: Array[String]): Unit = {\n              |    println(\"Hello proxy\")\n              |  }\n              |}\n              |\"\"\".stripMargin\n        )\n      }*\n    )\n\n    def authProperties(host: String, port: Int, user: String, password: String): Seq[String] =\n      Seq(\"http\", \"https\").flatMap { scheme =>\n        Seq(\n          s\"-D$scheme.proxyHost=$host\",\n          s\"-D$scheme.proxyPort=$port\",\n          s\"-D$scheme.proxyUser=$user\",\n          s\"-D$scheme.proxyPassword=$password\",\n          s\"-D$scheme.proxyProtocol=http\"\n        )\n      }\n\n    val proxyArgs =\n      if (legacySetup) authProperties(\"localhost\", 9083, \"jack\", \"insecure\")\n      else Nil\n    val wrongProxyArgs =\n      if (legacySetup) authProperties(\"localhost\", 9084, \"wrong\", \"nope\")\n      else Nil\n\n    def setupProxyConfig(\n      cwd: os.Path,\n      env: Map[String, String],\n      host: String,\n      port: Int,\n      user: String,\n      password: String\n    ): Unit = {\n      os.proc(TestUtil.cli, \"--power\", \"config\", \"httpProxy.address\", s\"http://$host:$port\")\n        .call(cwd = cwd, env = env)\n      os.proc(TestUtil.cli, \"--power\", \"config\", \"httpProxy.user\", s\"value:$user\")\n        .call(cwd = cwd, env = env)\n      os.proc(TestUtil.cli, \"--power\", \"config\", \"httpProxy.password\", s\"value:$password\")\n        .call(cwd = cwd, env = env)\n    }\n\n    val image = Constants.authProxyTestImage\n    inputs.fromRoot { root =>\n      val configDir = root / \"configs\"\n      os.makeDir(configDir, \"rwx------\")\n      val configFile                  = configDir / \"config.json\"\n      val wrongConfigFile             = configDir / \"wrong-config.json\"\n      val (configEnv, wrongConfigEnv) =\n        if (legacySetup)\n          (Map.empty[String, String], Map.empty[String, String])\n        else {\n          val csEnv           = TestUtil.putCsInPathViaEnv(root / \"bin\")\n          val configEnv0      = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString) ++ csEnv\n          val wrongConfigEnv0 = Map(\"SCALA_CLI_CONFIG\" -> wrongConfigFile.toString) ++ csEnv\n          setupProxyConfig(root, configEnv0, \"localhost\", 9083, \"jack\", \"insecure\")\n          setupProxyConfig(root, wrongConfigEnv0, \"localhost\", 9084, \"wrong\", \"nope\")\n          (configEnv0, wrongConfigEnv0)\n        }\n      DockerServer.withServer(image, root.toString, 80 -> 9083) { _ =>\n        DockerServer.withServer(image, root.toString, 80 -> 9084) { _ =>\n\n          val okRes = os.proc(\n            TestUtil.cli,\n            proxyArgs,\n            \"-Dcoursier.cache.throw-exceptions=true\",\n            \"run\",\n            \".\",\n            \"--cache\",\n            os.rel / \"tmp-cache-ok\"\n          )\n            .call(cwd = root / okDir, env = configEnv)\n          val okOutput = okRes.out.trim()\n          expect(okOutput == \"Hello proxy\")\n\n          val wrongRes = os.proc(\n            TestUtil.cli,\n            wrongProxyArgs,\n            \"-Dcoursier.cache.throw-exceptions=true\",\n            \"run\",\n            \".\",\n            \"--cache\",\n            os.rel / \"tmp-cache-wrong\"\n          )\n            .call(\n              cwd = root / wrongDir,\n              env = wrongConfigEnv,\n              mergeErrIntoOut = true,\n              check = false\n            )\n          val wrongOutput = wrongRes.out.trim()\n          expect(wrongRes.exitCode == 1)\n          expect(wrongOutput.contains(\n            \"\"\"Unable to tunnel through proxy. Proxy returns \"HTTP/1.1 407 Proxy Authentication Required\"\"\"\"\n          ))\n        }\n      }\n    }\n  }\n\n  def runAuthProxyTests: Boolean =\n    (Properties.isLinux && !TestUtil.isAarch64) || (Properties.isMac && !TestUtil.isCI)\n\n  if (runAuthProxyTests) {\n    test(\"auth proxy (legacy)\") {\n      TestUtil.retry() {\n        authProxyTest(legacySetup = true)\n      }\n    }\n\n    test(\"auth proxy\") {\n      TestUtil.retry() {\n        authProxyTest(legacySetup = false)\n      }\n    }\n  }\n\n  test(\"UTF-8\") {\n    val message  = \"Hello from TestÅÄÖåäö\"\n    val fileName = \"TestÅÄÖåäö.scala\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"object TestÅÄÖåäö {\n           |  def main(args: Array[String]): Unit = {\n           |    println(\"$message\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"-Dtest.scala-cli.debug-charset-issue=true\",\n        \"run\",\n        extraOptions,\n        fileName\n      )\n        .call(cwd = root)\n      if (res.out.text(Codec.UTF8).trim != message) {\n        pprint.err.log(res.out.text(Codec.UTF8).trim)\n        pprint.err.log(message)\n      }\n      expect(res.out.text(Codec.UTF8).trim == message)\n    }\n  }\n\n  test(\"return relevant error if multiple .scala main classes are present\") {\n    TestUtil.retryOnCi() {\n      val (scalaFile1, scalaFile2, scriptName) =\n        (\"ScalaMainClass1\", \"ScalaMainClass2\", \"ScalaScript\")\n      val scriptsDir = \"scripts\"\n      val inputs     = TestInputs(\n        os.rel / s\"$scalaFile1.scala\"           -> s\"object $scalaFile1 extends App { println() }\",\n        os.rel / s\"$scalaFile2.scala\"           -> s\"object $scalaFile2 extends App { println() }\",\n        os.rel / scriptsDir / s\"$scriptName.sc\" -> \"println()\"\n      )\n      inputs.fromRoot { root =>\n        val res = os.proc(\n          TestUtil.cli,\n          \"run\",\n          \".\",\n          extraOptions\n        )\n          .call(cwd = root, mergeErrIntoOut = true, check = false)\n        expect(res.exitCode == 1)\n        val output       = res.out.trim()\n        val errorMessage =\n          output.linesWithSeparators.toSeq.takeRight(6).mkString // dropping compilation logs\n        val extraOptionsString  = extraOptions.mkString(\" \")\n        val scriptMainClassName = if (actualScalaVersion.startsWith(\"3\"))\n          s\"$scriptsDir.${scriptName}_sc\"\n        else\n          s\"$scriptsDir.$scriptName\"\n\n        val expectedMainClassNames = Seq(scalaFile1, scalaFile2, scriptMainClassName).sorted\n        val expectedErrorMessage   =\n          s\"\"\"[${Console.RED}error${Console.RESET}]  Found several main classes: ${\n              expectedMainClassNames.mkString(\n                \", \"\n              )\n            }\n             |You can run one of them by passing it with the --main-class option, e.g.\n             |  ${Console.BOLD}${TestUtil.detectCliPath} run . $extraOptionsString --main-class ${\n              expectedMainClassNames\n                .head\n            }${Console.RESET}\n             |\n             |You can pick the main class interactively by passing the --interactive option.\n             |  ${Console.BOLD}${\n              TestUtil\n                .detectCliPath\n            } run . $extraOptionsString --interactive${Console.RESET}\"\"\".stripMargin\n        expect(errorMessage == expectedErrorMessage)\n      }\n    }\n  }\n\n  test(\n    \"return relevant error when main classes list is requested, but no main classes are present\"\n  ) {\n    val inputs = TestInputs(os.rel / \"Main.scala\" -> \"object Main { println() }\")\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\",\n        \"--main-class-ls\"\n      )\n        .call(cwd = root, mergeErrIntoOut = true, check = false)\n      expect(res.exitCode == 1)\n      expect(res.out.trim().contains(\"No main class found\"))\n    }\n  }\n\n  test(\"correctly list main classes\") {\n    val (scalaFile1, scalaFile2, scriptName) = (\"ScalaMainClass1\", \"ScalaMainClass2\", \"ScalaScript\")\n    val scriptsDir                           = \"scripts\"\n    val inputs                               = TestInputs(\n      os.rel / s\"$scalaFile1.scala\"           -> s\"object $scalaFile1 extends App { println() }\",\n      os.rel / s\"$scalaFile2.scala\"           -> s\"object $scalaFile2 extends App { println() }\",\n      os.rel / scriptsDir / s\"$scriptName.sc\" -> \"println()\"\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"run\",\n        extraOptions,\n        \".\",\n        \"--list-main-classes\"\n      )\n        .call(cwd = root)\n      val output      = res.out.trim()\n      val mainClasses = output.split(\" \").toSet\n\n      val scriptMainClassName = if (actualScalaVersion.startsWith(\"3\"))\n        s\"$scriptsDir.${scriptName}_sc\"\n      else\n        s\"$scriptsDir.$scriptName\"\n\n      expect(mainClasses == Set(scalaFile1, scalaFile2, scriptMainClassName))\n    }\n  }\n\n  test(\"deleting resources after building\") {\n    val projectDir      = \"projectDir\"\n    val fileName        = \"main.scala\"\n    val resourceContent = \"hello world\"\n    val resourcePath    = os.rel / projectDir / \"resources\" / \"test.txt\"\n    val inputs          = TestInputs(\n      os.rel / projectDir / fileName ->\n        s\"\"\"\n           |//> using resourceDir resources\n           |\n           |object Main {\n           |  def main(args: Array[String]) = {\n           |    val inputStream = getClass().getResourceAsStream(\"/test.txt\")\n           |    if (inputStream == null) println(\"null\")\n           |    else println(\"non null\")\n           |  }\n           |}\n           |\"\"\".stripMargin,\n      resourcePath -> resourceContent\n    )\n\n    inputs.fromRoot { root =>\n      def runCli() =\n        os.proc(TestUtil.cli, extraOptions, projectDir)\n          .call(cwd = root)\n          .out.trim()\n\n      val output1 = runCli()\n      expect(output1 == \"non null\")\n\n      os.remove(root / resourcePath)\n      val output2 = runCli()\n      expect(output2 == \"null\")\n    }\n  }\n\n  test(\"run jar file\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(\"Hello World\")\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      // build jar\n      val helloJarPath = root / \"Hello.jar\"\n      os.proc(TestUtil.cli, \"--power\", \"package\", \".\", \"--library\", \"-o\", helloJarPath).call(cwd =\n        root\n      )\n\n      // run jar\n      val output = os.proc(TestUtil.cli, helloJarPath).call(cwd = root).out.trim()\n      expect(output == \"Hello World\")\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\"should throw exception for code compiled by scala 3.1.3\") {\n      val exceptionMsg = \"Throw exception in Scala\"\n      val inputs       = TestInputs(\n        os.rel / \"hello.sc\" ->\n          s\"\"\"//> using scala 3.1.3\n             |throw new Exception(\"$exceptionMsg\")\"\"\".stripMargin\n      )\n\n      inputs.fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"hello.sc\").call(cwd = root, mergeErrIntoOut = true, check = false)\n        val output = res.out.trim()\n        expect(output.contains(exceptionMsg))\n      }\n    }\n\n  test(\"should add toolkit to classpath\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(os.pwd) // os lib should be added to classpath by toolkit\n           |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \".\", \"--toolkit\", Constants.toolkitVersion)\n        .call(cwd = root).out.trim()\n\n      expect(output == root.toString())\n    }\n  }\n\n  test(\"should add typelevel toolkit to classpath\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"|import cats.effect.*\n            |import fs2.io.file.Files\n            |object Hello extends IOApp.Simple {\n            |  // IO should be added to classpath by typelevel toolkit\n            |  def run = Files[IO].currentWorkingDirectory.flatMap { cwd =>\n            |    IO.println(cwd.toString)\n            |  }\n            |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, \".\", \"--toolkit\", s\"typelevel:${Constants.typelevelToolkitVersion}\")\n          .call(cwd = root).out.trim()\n\n      expect(output == root.toString())\n    }\n  }\n\n  test(\"should add typelevel toolkit-test to classpath\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.test.scala\" ->\n        s\"\"\"|import cats.effect.*\n            |import munit.CatsEffectSuite\n            |class HelloSuite extends CatsEffectSuite {\n            |  // IO should be added to classpath by typelevel toolkit-test\n            |  test(\"warm hello from the sun is coming\") {\n            |    (IO(\"i love to live in the\") *> IO(\"sun\")).assertEquals(\"sun\")\n            |  }\n            |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output = os.proc(\n        TestUtil.cli,\n        \"test\",\n        \".\",\n        \"--toolkit\",\n        s\"typelevel:${Constants.typelevelToolkitVersion}\"\n      )\n        .call(cwd = root).out.text()\n\n      expect(output.contains(\"+\")) // test succeeded\n    }\n  }\n\n  test(s\"print error if workspace path contains a ${File.pathSeparator}\") {\n    val msg     = \"Hello\"\n    val relPath = os.rel / s\"weird${File.pathSeparator}directory\" / \"Hello.scala\"\n    TestInputs(\n      relPath ->\n        s\"\"\"object Hello extends App {\n           |  println(\"$msg\")\n           |}\n           |\"\"\".stripMargin\n    )\n      .fromRoot { root =>\n        val resWithColon =\n          os.proc(TestUtil.cli, \"run\", relPath.toString, extraOptions)\n            .call(cwd = root, check = false, stderr = os.Pipe)\n        expect(resWithColon.exitCode == 1)\n        expect(resWithColon.err.trim().contains(\n          \"you can force your workspace with the '--workspace' option:\"\n        ))\n        val resFixedWorkspace = // should run fine for a forced workspace with no classpath separator on path\n          os.proc(TestUtil.cli, \"run\", relPath.toString, \"--workspace\", \".\", extraOptions)\n            .call(cwd = root)\n        expect(resFixedWorkspace.out.trim() == msg)\n      }\n  }\n\n  val commands = Seq(\"\", \"run\", \"compile\")\n\n  for (command <- commands) {\n    test(\n      s\"error output for unrecognized source type for ${if (command == \"\") \"default\" else command}\"\n    ) {\n      val inputs = TestInputs(\n        os.rel / \"print.hehe\" ->\n          s\"\"\"println(\"Foo\")\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val proc = if (command == \"\") os.proc(TestUtil.cli, \"print.hehe\")\n        else os.proc(TestUtil.cli, command, \"print.hehe\")\n        val output = proc.call(cwd = root, check = false, stderr = os.Pipe)\n          .err.trim()\n\n        expect(output.contains(\"unrecognized source type\"))\n      }\n    }\n\n    test(s\"error output for nonexistent file for ${if (command == \"\") \"default\" else command}\") {\n      val inputs = TestInputs(\n        os.rel / \"print.hehe\" ->\n          s\"\"\"println(\"Foo\")\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val proc = if (command == \"\") os.proc(TestUtil.cli, \"nonexisten.no\")\n        else os.proc(TestUtil.cli, command, \"nonexisten.no\")\n        val output = proc.call(cwd = root, check = false, stderr = os.Pipe)\n          .err.trim()\n\n        expect(output.contains(\"file not found\"))\n      }\n    }\n\n    test(s\"error output for invalid sub-command for ${if (command == \"\") \"default\" else command}\") {\n      val inputs = TestInputs(\n        os.rel / \"print.hehe\" ->\n          s\"\"\"println(\"Foo\")\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val proc = if (command == \"\") os.proc(TestUtil.cli, \"invalid\")\n        else os.proc(TestUtil.cli, command, \"invalid\")\n        val output = proc.call(cwd = root, check = false, stderr = os.Pipe)\n          .err.trim()\n\n        if (command == \"\")\n          expect(output.contains(s\"is not a ${TestUtil.detectCliPath} sub-command\"))\n        else\n          expect(output.contains(\"file not found\"))\n      }\n    }\n  }\n\n  test(\"declare test scope dependencies from main scope\") {\n    val projectFile     = \"project.scala\"\n    val invalidMainFile = \"InvalidMain.scala\"\n    val validMainFile   = \"ValidMain.scala\"\n    val testFile        = \"Tests.test.scala\"\n    TestInputs(\n      os.rel / projectFile ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |//> using test.dep org.scalameta::munit::0.7.29\n          |//> using test.dep com.lihaoyi::pprint:0.9.6\n          |\"\"\".stripMargin,\n      os.rel / invalidMainFile ->\n        \"\"\"object InvalidMain extends App {\n          |  pprint.pprintln(\"Hello\")\n          |}\n          |\"\"\".stripMargin,\n      os.rel / validMainFile ->\n        \"\"\"object ValidMain extends App {\n          |  println(os.pwd)\n          |}\n          |\"\"\".stripMargin,\n      os.rel / testFile ->\n        \"\"\"class Tests extends munit.FunSuite {\n          |  test(\"foo\") {\n          |    pprint.pprintln(os.pwd)\n          |  }\n          |}\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      // running `invalidMainFile` should fail, as it's in the main scope and depends on test scope deps\n      val res1 = os.proc(TestUtil.cli, \"run\", projectFile, invalidMainFile)\n        .call(cwd = root, check = false)\n      expect(res1.exitCode == 1)\n      // running `validMainFile` should succeed, since it only depends on main scope deps\n      val res2 = os.proc(TestUtil.cli, \"run\", projectFile, validMainFile)\n        .call(cwd = root)\n      expect(res2.out.trim() == root.toString())\n      // test scope should have access to both main and test deps\n      os.proc(TestUtil.cli, \"test\", projectFile, testFile)\n        .call(cwd = root, stderr = os.Pipe)\n    }\n  }\n  test(\"declare test scope custom jar from main scope\") {\n    val projectFile                                                       = \"project.scala\"\n    val testMessageFile                                                   = \"TestMessage.scala\"\n    val mainMessageFile                                                   = \"MainMessage.scala\"\n    val validMainFile                                                     = \"ValidMain.scala\"\n    val invalidMainFile                                                   = \"InvalidMain.scala\"\n    val testFile                                                          = \"Tests.test.scala\"\n    val expectedMessage1                                                  = \"Hello\"\n    val expectedMessage2                                                  = \" world!\"\n    val jarPathsWithFiles @ Seq((mainMessageJar, _), (testMessageJar, _)) =\n      Seq(\n        os.rel / \"MainMessage.jar\" -> mainMessageFile,\n        os.rel / \"TestMessage.jar\" -> testMessageFile\n      )\n    TestInputs(\n      os.rel / projectFile ->\n        s\"\"\"//> using jar $mainMessageJar\n           |//> using test.jar $testMessageJar\n           |//> using test.dep org.scalameta::munit::0.7.29\n           |\"\"\".stripMargin,\n      os.rel / mainMessageFile ->\n        \"\"\"case class MainMessage(value: String)\n          |\"\"\".stripMargin,\n      os.rel / testMessageFile ->\n        \"\"\"case class TestMessage(value: String)\n          |\"\"\".stripMargin,\n      os.rel / invalidMainFile ->\n        s\"\"\"object InvalidMain extends App {\n           |  println(TestMessage(\"$expectedMessage1\").value)\n           |}\n           |\"\"\".stripMargin,\n      os.rel / validMainFile ->\n        s\"\"\"object ValidMain extends App {\n           |  println(MainMessage(\"$expectedMessage1\").value)\n           |}\n           |\"\"\".stripMargin,\n      os.rel / testFile ->\n        s\"\"\"class Tests extends munit.FunSuite {\n           |  val msg1 = MainMessage(\"$expectedMessage1\").value\n           |  val msg2 = TestMessage(\"$expectedMessage2\").value\n           |  val testName = msg1 + msg2\n           |  test(testName) {\n           |    assert(1 + 1 == 2)\n           |  }\n           |}\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      // package the MainMessage and TestMessage jars\n      for ((jarPath, sourcePath) <- jarPathsWithFiles)\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"package\",\n          sourcePath,\n          \"--library\",\n          \"-o\",\n          jarPath,\n          extraOptions\n        )\n          .call(cwd = root)\n      // running `invalidMainFile` should fail, as it's in the main scope and depends on the test scope jar\n      val res1 = os.proc(TestUtil.cli, \"run\", projectFile, invalidMainFile, extraOptions)\n        .call(cwd = root, check = false)\n      expect(res1.exitCode == 1)\n      // running `validMainFile` should succeed, since it only depends on the main scope jar\n      val res2 = os.proc(TestUtil.cli, \"run\", projectFile, validMainFile, extraOptions)\n        .call(cwd = root)\n      expect(res2.out.trim() == expectedMessage1)\n      // test scope should have access to both main and test deps\n      val res3 = os.proc(TestUtil.cli, \"test\", projectFile, testFile, extraOptions)\n        .call(cwd = root, stderr = os.Pipe)\n      expect(res3.out.trim().contains(s\"$expectedMessage1$expectedMessage2\"))\n    }\n  }\n  test(\"exclude file\") {\n    val message = \"Hello\"\n    val inputs  = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           | println(\"$message\")\n           |}\"\"\".stripMargin,\n      os.rel / \"Main.scala\" ->\n        \"\"\"object Main {\n          | val msg: String = 1 // compilation fails\n          |}\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(TestUtil.cli, extraOptions, \".\", \"--exclude\", \"*Main.scala\").call(cwd = root)\n      val output = res.out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"decoded classNames in interactive ask\") {\n    val fileName = \"watch.scala\"\n\n    val inputs = TestInputs(\n      os.rel / fileName ->\n        \"\"\"object `Run-1` extends App {println(\"Run-1 launched\")}\n          |object `Run-2` extends App {println(\"Run-2 launched\")}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val confDir  = root / \"config\"\n      val confFile = confDir / \"test-config.json\"\n\n      os.write(confFile, \"{\\\"interactive-was-suggested\\\":true}\", createFolders = true)\n\n      if (!Properties.isWin)\n        os.perms.set(confDir, \"rwx------\")\n\n      val configEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n      val proc = os.proc(TestUtil.cli, \"run\", \"--interactive\", fileName)\n        .call(\n          cwd = root,\n          mergeErrIntoOut = true,\n          env = Map(\"SCALA_CLI_INTERACTIVE_INPUTS\" -> \"Run-1\") ++ configEnv\n        )\n\n      expect(proc.out.trim().contains(\"[0] Run-1\"))\n      expect(proc.out.trim().contains(\"[1] Run-2\"))\n      expect(proc.out.trim().contains(\"Run-1 launched\"))\n    }\n  }\n\n  test(\"BuildInfo fields should be reachable\") {\n    val jvmId  = \"18\"\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n           |//> using option -Xasync\n           |//> using jvm $jvmId\n           |//> using mainClass Main\n           |//> using resourceDir ./resources\n           |//> using jar TEST1.jar TEST2.jar\n           |\n           |//> using buildInfo\n           |\n           |import scala.cli.build.BuildInfo\n           |\n           |object Main extends App {\n           |  assert(BuildInfo.scalaVersion == \"$actualScalaVersion\")\n           |  assert(BuildInfo.platform == \"JVM\")\n           |  assert(BuildInfo.jvmVersion == Some(\"$jvmId\"))\n           |  assert(BuildInfo.scalaJsVersion == None)\n           |  assert(BuildInfo.jsEsVersion == None)\n           |  assert(BuildInfo.scalaNativeVersion == None)\n           |  assert(BuildInfo.mainClass == Some(\"Main\"))\n           |  assert(BuildInfo.projectVersion == Some(\"1.0.0\"))\n           |\n           |  assert(BuildInfo.Main.sources.head.endsWith(\"Main.scala\"))\n           |  assert(BuildInfo.Main.scalacOptions == Seq(\"-Xasync\"))\n           |  assert(BuildInfo.Main.scalaCompilerPlugins.size == 0)\n           |  assert(BuildInfo.Main.dependencies.size == 1)\n           |  assert(BuildInfo.Main.dependencies.head.contains(\"com.lihaoyi:os-lib_\"))\n           |  assert(BuildInfo.Main.resolvers.size == 3)\n           |  assert(BuildInfo.Main.resourceDirs.size == 1)\n           |  assert(BuildInfo.Main.customJarsDecls.size == 2)\n           |   \n           |  assert(BuildInfo.Test.sources.head.endsWith(\"Test.scala\"))\n           |  assert(BuildInfo.Test.scalacOptions == Seq(\"-Xasync\"))\n           |  assert(BuildInfo.Test.scalaCompilerPlugins.size == 0)\n           |  assert(BuildInfo.Test.dependencies.size == 2)\n           |  assert(BuildInfo.Test.dependencies.exists(_.contains(\"com.lihaoyi:os-lib_\")))\n           |  assert(BuildInfo.Test.dependencies.exists(_.contains(\"org.scalameta:munit\")))\n           |  assert(BuildInfo.Test.resolvers.size == 3)\n           |  assert(BuildInfo.Test.resourceDirs.size == 1)\n           |  assert(BuildInfo.Test.customJarsDecls.size == 2)\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"test\" / \"Test.scala\" ->\n        \"\"\"//> using dep org.scalameta::munit::0.7.29\n          |\n          |class MyTests extends munit.FunSuite {\n          |  test(\"foo\") {\n          |    assert(2 + 2 == 4)\n          |    println(\"Hello from \" + \"tests\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      TestUtil.initializeGit(root, \"v1.0.0\")\n\n      val res =\n        os.proc(TestUtil.cli, \"--power\", extraOptions, \".\").call(cwd = root)\n      val output = res.out.trim()\n\n      val projectDir = os.list(root / \".scala-build\").filter(\n        _.baseName.startsWith(root.baseName + \"_\")\n      )\n      expect(projectDir.size == 1)\n      val buildInfoPath = projectDir.head / \"src_generated\" / \"main\" / \"BuildInfo.scala\"\n      expect(os.isFile(buildInfoPath))\n\n      expect(output == \"\")\n    }\n  }\n\n  test(\"BuildInfo should take into account --project-version\") {\n    TestUtil.retryOnCi() {\n      val inputs = TestInputs(\n        os.rel / \"Main.scala\" ->\n          s\"\"\"//> using buildInfo\n             |\n             |import scala.cli.build.BuildInfo\n             |\n             |object Main extends App {\n             |  assert(BuildInfo.projectVersion == Some(\"35.0.1\"))\n             |}\n             |\"\"\".stripMargin\n      )\n\n      inputs.fromRoot { root =>\n        TestUtil.initializeGit(root, \"v1.0.0\")\n\n        val res =\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            extraOptions,\n            \".\",\n            \"--compute-version\",\n            \"git\",\n            \"--project-version\",\n            \"35.0.1\"\n          ).call(cwd = root)\n        val output = res.out.trim()\n\n        val projectDir = os.list(root / \".scala-build\").filter(\n          _.baseName.startsWith(root.baseName + \"_\")\n        )\n        expect(projectDir.size == 1)\n        val buildInfoPath = projectDir.head / \"src_generated\" / \"main\" / \"BuildInfo.scala\"\n        expect(os.isFile(buildInfoPath))\n\n        expect(output == \"\")\n      }\n    }\n  }\n\n  // Credentials tests\n  test(\"repository credentials passed to coursier\") {\n    val testOrg     = \"test-org\"\n    val testName    = \"the-messages\"\n    val testVersion = \"0.1.2\"\n    val user        = \"username\"\n    val password    = \"1234\"\n    val realm       = \"Realm\"\n    val inputs      = TestInputs(\n      os.rel / \"messages\" / \"Messages.scala\" ->\n        \"\"\"package messages\n          |\n          |object Messages {\n          |  def hello(name: String): String =\n          |    s\"Hello $name\"\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"hello\" / \"Hello.scala\" ->\n        s\"\"\"//> using dep $testOrg::$testName:$testVersion\n           |import messages.Messages\n           |object Hello {\n           |  def main(args: Array[String]): Unit =\n           |    println(Messages.hello(args.headOption.getOrElse(\"Unknown\")))\n           |}\n           |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val configFile = {\n        val dir = root / \"conf\"\n        os.makeDir.all(dir, if (Properties.isWin) null else \"rwx------\")\n        dir / \"config.json\"\n      }\n      val extraEnv = Map(\n        \"SCALA_CLI_CONFIG\" -> configFile.toString\n      )\n      val repoPath = root / \"the-repo\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"publish\",\n        \"--publish-repo\",\n        repoPath.toNIO.toUri.toASCIIString,\n        \"messages\",\n        \"--organization\",\n        testOrg,\n        \"--name\",\n        testName,\n        \"--project-version\",\n        testVersion\n      )\n        .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = extraEnv)\n\n      TestUtil.serveFilesInHttpServer(repoPath, user, password, realm) { (host, port) =>\n        // This codeblock represents test(\"No repository credentials passed to coursier\")\n        {\n          val resWithNoCreds = os.proc(\n            TestUtil.cli,\n            \"run\",\n            \"--repository\",\n            s\"http://$host:$port\",\n            \"hello\",\n            \"--\",\n            \"TestUser\"\n          ).call(\n            cwd = root,\n            env = Map(\n              \"USER\"     -> user,\n              \"PASSWORD\" -> password\n            ),\n            check = false,\n            mergeErrIntoOut = true\n          )\n\n          expect(resWithNoCreds.exitCode == 1)\n        }\n\n        // This codeblock represents test(\"Repository credentials passed to coursier - environment variables\")\n        {\n          val resWithEnvVar = os.proc(\n            TestUtil.cli,\n            \"run\",\n            \"--repository\",\n            s\"http://$host:$port\",\n            \"hello\",\n            \"--\",\n            \"TestUser\"\n          ).call(\n            cwd = root,\n            env = Map(\n              \"USER\"                 -> user,\n              \"PASSWORD\"             -> password,\n              \"COURSIER_CREDENTIALS\" -> s\"$host $user:$password\"\n            ),\n            mergeErrIntoOut = true\n          )\n\n          expect(resWithEnvVar.exitCode == 0)\n        }\n\n        // This codeblock represents test(\"Repository credentials passed to coursier - config entry\")\n        {\n          os.write(\n            configFile,\n            s\"\"\"{\n               |\"repositories.credentials\": [\n               |{\"host\":\"$host\",\"user\":\"value:$user\",\"password\":\"value:$password\",\"matchHost\":true}\n               |]\n               |}\"\"\".stripMargin\n          )\n          val resWithConfig = os.proc(\n            TestUtil.cli,\n            \"run\",\n            \"--repository\",\n            s\"http://$host:$port\",\n            \"hello\",\n            \"--\",\n            \"TestUser\"\n          ).call(\n            cwd = root,\n            env = Map(\n              \"USER\"     -> user,\n              \"PASSWORD\" -> password\n            ) ++ extraEnv,\n            mergeErrIntoOut = true\n          )\n\n          expect(resWithConfig.exitCode == 0)\n        }\n\n        // This codeblock represents test(\"Repository credentials passed to coursier - java properties\")\n        {\n          os.write(root / \".scala-jvmopts\", s\"-Dcoursier.credentials=$host $user:$password\\n\")\n\n          val resWithProps = os.proc(\n            TestUtil.cli,\n            \"run\",\n            \"--repository\",\n            s\"http://$host:$port\",\n            \"hello\",\n            \"--\",\n            \"TestUser\"\n          ).call(\n            cwd = root,\n            env = Map(\n              \"USER\"     -> user,\n              \"PASSWORD\" -> password\n            ),\n            mergeErrIntoOut = true\n          )\n\n          expect(resWithProps.exitCode == 0)\n        }\n      }\n    }\n  }\n\n  test(\"warn about transitive `using file` directive\") {\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using file bar/Bar.scala\n          |//> using file abc/Abc.scala\n          |object Main extends App {\n          | println(Bar(42))\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"bar\" / \"Bar.scala\" ->\n        \"\"\"//> using file xyz/Xyz.scala\n          |//> using file xyz/NonExistent.scala\n          |case class Bar(x: Int)\n          |\"\"\".stripMargin,\n      os.rel / \"abc\" / \"Abc.scala\" ->\n        \"\"\"//> using file xyz/Xyz.scala\n          |//> using file xyz/NonExistent.scala\n          |case class Abc(x: Int)\n          |\"\"\".stripMargin,\n      os.rel / \"xyz\" / \"Xyz.scala\" ->\n        \"\"\"val xyz = 42\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \"Main.scala\",\n        \"--suppress-directives-in-multiple-files-warning\"\n      )\n        .call(cwd = root, mergeErrIntoOut = true)\n\n      val output = TestUtil.removeAnsiColors(res.out.trim())\n\n      expect(output.contains(\n        \"\"\"[warn] Chaining the 'using file' directive is not supported, the source won't be included in the build.\n          |[warn] //> using file xyz/Xyz.scala\n          |[warn]                ^^^^^^^^^^^^^\n          |\"\"\".stripMargin\n      ))\n\n      expect(output.contains(\n        \"\"\"[warn] Chaining the 'using file' directive is not supported, the source won't be included in the build.\n          |[warn] //> using file xyz/NonExistent.scala\n          |[warn]                ^^^^^^^^^^^^^^^^^^^^^\n          |\"\"\".stripMargin\n      ))\n\n      expect(output.contains(\n        \"\"\"[warn] Chaining the 'using file' directive is not supported, the source won't be included in the build.\n          |[warn] //> using file xyz/Xyz.scala\n          |[warn]                ^^^^^^^^^^^^^\n          |\"\"\".stripMargin\n      ))\n\n      expect(output.contains(\n        \"\"\"[warn] Chaining the 'using file' directive is not supported, the source won't be included in the build.\n          |[warn] //> using file xyz/NonExistent.scala\n          |[warn]                ^^^^^^^^^^^^^^^^^^^^^\n          |\"\"\".stripMargin\n      ))\n    }\n  }\n\n  if (\n    !actualScalaVersion.contains(\"RC\") &&\n    actualScalaVersion != \"3.6.0\" && actualScalaVersion != \"3.6.1\"\n  ) {\n    val actualAnnouncedScalaVersion = actualScalaVersion match {\n      case _\n          if actualScalaVersion == Constants.scala3Next &&\n          Constants.scala3Next != Constants.scala3NextAnnounced =>\n        // if the version isn't public yet, Coursier won't be able to install it\n        Constants.scala3NextAnnounced\n      case s => s\n    }\n\n    test(\n      s\"offline mode should fail on missing artifacts (with Scala $actualAnnouncedScalaVersion)\"\n    ) {\n      TestUtil.retryOnCi(maxAttempts = 5) {\n        // Kill bloop deamon to test scalac fallback\n        os.proc(TestUtil.cli, \"--power\", \"bloop\", \"exit\")\n          .call(cwd = os.pwd)\n\n        // ensure extra options use an announced Scala version\n        val customExtraOptions: Seq[String] =\n          if (\n            scalaVersionOpt.isEmpty &&\n            Constants.scala3Next != Constants.scala3NextAnnounced\n          )\n            extraOptions ++ Seq(\"--scala\", actualAnnouncedScalaVersion)\n          else if (\n            actualScalaVersion == Constants.scala3Next &&\n            actualScalaVersion != actualAnnouncedScalaVersion\n          )\n            extraOptions\n              .map {\n                case opt if opt == Constants.scala3Next => actualAnnouncedScalaVersion\n                case opt                                => opt\n              }\n          else extraOptions\n\n        val depScalaVersion = actualAnnouncedScalaVersion match {\n          case sv if sv.startsWith(\"2.12\") => \"2.12\"\n          case sv if sv.startsWith(\"2.13\") => \"2.13\"\n          case _                           => \"3\"\n        }\n\n        val dep    = s\"com.lihaoyi:os-lib_$depScalaVersion:0.10.6\"\n        val inputs = TestInputs(\n          os.rel / \"NoDeps.scala\" ->\n            \"\"\"//> using jvm zulu:17\n              |object NoDeps extends App {\n              |  println(\"Hello from NoDeps\")\n              |}\n              |\"\"\".stripMargin,\n          os.rel / \"WithDeps.scala\" ->\n            s\"\"\"//> using jvm zulu:17\n               |//> using dep $dep\n               |\n               |object WithDeps extends App {\n               |  println(\"Hello from WithDeps\")\n               |}\n               |\"\"\".stripMargin\n        )\n        inputs.fromRoot { root =>\n          val cachePath = root / \".cache\"\n          os.makeDir(cachePath)\n\n          val extraEnv = Map(\"COURSIER_CACHE\" -> cachePath.toString)\n\n          val emptyCacheWalkSize = os.walk(cachePath).size\n\n          val noArtifactsRes = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"NoDeps.scala\",\n            customExtraOptions,\n            \"--offline\",\n            \"--cache\",\n            cachePath.toString\n          )\n            .call(cwd = root, check = false, mergeErrIntoOut = true)\n          expect(noArtifactsRes.exitCode == 1)\n\n          // Cache unchanged\n          expect(emptyCacheWalkSize == os.walk(cachePath).size)\n\n          // Download the artifacts for scala\n          os.proc(TestUtil.cs, \"install\", s\"scala:$actualAnnouncedScalaVersion\")\n            .call(cwd = root, env = extraEnv)\n          os.proc(TestUtil.cs, \"install\", s\"scalac:$actualAnnouncedScalaVersion\")\n            .call(cwd = root, env = extraEnv)\n          (if (actualAnnouncedScalaVersion.startsWith(\"3\")) Some(\"scala3-sbt-bridge\")\n           else if (\n             actualAnnouncedScalaVersion.startsWith(\"2.13.\") &&\n             actualAnnouncedScalaVersion.coursierVersion >= \"2.13.12\".coursierVersion\n           )\n             Some(\"scala2-sbt-bridge\")\n           else None)\n            .foreach { bridgeArtifactName =>\n              os.proc(\n                TestUtil.cs,\n                \"fetch\",\n                s\"org.scala-lang:$bridgeArtifactName:$actualAnnouncedScalaVersion\"\n              )\n                .call(cwd = root, env = extraEnv)\n            }\n\n          // Download JVM that won't suit Bloop, also no Bloop artifacts are present\n          os.proc(TestUtil.cs, \"java-home\", \"--jvm\", \"zulu:17\")\n            .call(cwd = root, env = extraEnv)\n\n          val scalaJvmCacheWalkSize = os.walk(cachePath).size\n\n          val scalaAndJvmRes = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"NoDeps.scala\",\n            customExtraOptions,\n            \"--offline\",\n            \"--cache\",\n            cachePath.toString\n          )\n            .call(cwd = root, mergeErrIntoOut = true)\n          expect(scalaAndJvmRes.exitCode == 0)\n          expect(scalaAndJvmRes.out.trim().contains(\n            \"Offline mode is ON and Bloop could not be fetched from the local cache, using scalac as fallback\"\n          ))\n          expect(scalaAndJvmRes.out.trim().contains(\"Hello from NoDeps\"))\n\n          // Cache unchanged\n          expect(scalaJvmCacheWalkSize == os.walk(cachePath).size)\n\n          // Missing dependencies\n          for {\n            (cliOption, extraEnvMode) <- Seq(\n              \"--offline\"               -> Map.empty[String, String],\n              \"-Dcoursier.mode=offline\" -> Map.empty[String, String],\n              \"\"                        -> Map(\"COURSIER_MODE\" -> \"offline\")\n            )\n          } {\n            val missingDepsRes = os.proc(\n              TestUtil.cli,\n              \"--power\",\n              cliOption,\n              \"WithDeps.scala\",\n              customExtraOptions,\n              \"--cache\",\n              cachePath.toString\n            )\n              .call(cwd = root, check = false, mergeErrIntoOut = true, env = extraEnvMode)\n            expect(missingDepsRes.exitCode == 1)\n            expect(missingDepsRes.out.trim().contains(\"Error downloading com.lihaoyi:os-lib\"))\n\n            // Cache unchanged\n            expect(scalaJvmCacheWalkSize == os.walk(cachePath).size)\n          }\n\n          // Download dependencies\n          os.proc(TestUtil.cs, \"fetch\", dep)\n            .call(cwd = root, env = extraEnv)\n\n          val withDependencyCacheWalkSize = os.walk(cachePath).size\n\n          val depsRes = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"WithDeps.scala\",\n            customExtraOptions,\n            \"--offline\",\n            \"--cache\",\n            cachePath.toString,\n            \"-v\",\n            \"-v\"\n          )\n            .call(cwd = root, mergeErrIntoOut = true)\n          expect(depsRes.exitCode == 0)\n          expect(\n            depsRes.out.trim().contains(\n              \"Offline mode is ON and Bloop could not be fetched from the local cache, using scalac as fallback\"\n            )\n          )\n          expect(depsRes.out.trim().contains(\"Hello from WithDeps\"))\n\n          // Cache changed\n          expect(withDependencyCacheWalkSize == os.walk(cachePath).size)\n        }\n      }\n    }\n  }\n\n  test(\"JVM id is printed with compilation info correctly\") {\n    val msg   = \"Hello\"\n    val input = \"jvm.sc\"\n    val jvmId = \"18\"\n    TestInputs(os.rel / input ->\n      s\"\"\"//> using jvm $jvmId\n         |println(\"$msg\")\n         |\"\"\".stripMargin).fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"run\", extraOptions, input).call(cwd = root, stderr = os.Pipe)\n      expect(res.out.trim() == msg)\n      expect(res.err.trim().contains(s\"JVM ($jvmId)\"))\n    }\n  }\n\n  def getCoursierCacheRelPath: os.RelPath =\n    if (Properties.isWin) os.rel / \"Coursier\" / \"Cache\"\n    else if (Properties.isMac) os.rel / \"Library\" / \"Caches\" / \"Coursier\"\n    else os.rel / \".cache\" / \"coursier\"\n\n  if (TestUtil.isJvmCli) // can't reproduce on native image launchers\n    test(\"doesn't fail on invalid user.home\") {\n      val customCall =\n        Seq(\"java\", \"-Xmx512m\", \"-Xms128m\", \"-Duser.home=?\", \"-jar\", TestUtil.cliPath)\n      val msg   = \"Hello\"\n      val input = \"script.sc\"\n      TestInputs(os.rel / input -> s\"println(\\\"$msg\\\")\")\n        .fromRoot { root =>\n          val res = os.proc(customCall, \"run\", extraOptions, \"--server=false\", input)\n            .call(\n              cwd = root,\n              stderr = os.Pipe\n            )\n          expect(res.out.trim() == msg)\n        }\n    }\n\n  test(\"toolkit default\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using toolkit default\n          |//> using toolkit typelevel:default\n          |//> using toolkit org.typelevel:default\n          |\n          |import cats.effect.IOApp\n          |import cats.effect.IO\n          |\n          |object Hello extends IOApp.Simple {\n          |  def run =  IO.println(os.pwd)\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val result =\n        os.proc(TestUtil.cli, extraOptions, \".\").call(cwd = root, stderr = os.Pipe, check = false)\n      if (actualScalaVersion.startsWith(\"2.12\")) {\n        expect(result.exitCode == 1)\n        expect(result.err.trim().contains(\"Toolkits do not support Scala 2.12\"))\n      }\n      else {\n        expect(result.exitCode == 0)\n        expect(result.out.trim() == root.toString)\n      }\n    }\n  }\n\n  for {\n    suppressDeprecatedWarnings <- Seq(true, false)\n    useDirective               <- Seq(true, false)\n    toolkitDirective      = if (useDirective) \"//> using toolkit latest\" else \"\"\n    toolkitOptions        = if (useDirective) Nil else Seq(\"--toolkit\", \"latest\")\n    useDirectiveText      = if (useDirective) \"with directive\" else \"with command line option\"\n    suppressedWarningText = if (suppressDeprecatedWarnings) \"suppressed\" else \"reported\"\n  }\n    test(s\"warning about using toolkit latest $useDirectiveText should be $suppressedWarningText\") {\n      val inputs = TestInputs(\n        os.rel / \"Main.scala\" ->\n          s\"\"\"$toolkitDirective\n             |object Main {\n             |  def main(args: Array[String]): Unit = {\n             |    println(os.pwd)\n             |  }\n             |}\n             |\"\"\".stripMargin\n      )\n\n      inputs.fromRoot { root =>\n        val suppressWarningOptions =\n          if (suppressDeprecatedWarnings) Seq(\"--suppress-deprecated-warnings\")\n          else Seq.empty\n        val resLatest =\n          os.proc(TestUtil.cli, extraOptions, \".\", toolkitOptions, suppressWarningOptions)\n            .call(cwd = root, mergeErrIntoOut = true)\n        val expectedWarning = \"Using 'latest' for toolkit is deprecated\"\n        if (suppressDeprecatedWarnings) expect(!resLatest.out.text().contains(expectedWarning))\n        else {\n          expect(resLatest.out.text().contains(expectedWarning))\n          val warningCount =\n            resLatest.out.text().sliding(expectedWarning.length).count(_ == expectedWarning)\n          expect(warningCount == 1)\n        }\n      }\n    }\n\n  test(\"running a .scala file several times doesn't produce Bloop errors\") {\n    val msg   = \"Hello\"\n    val input = \"Main.scala\"\n    TestInputs(\n      os.rel / input ->\n        s\"\"\"object Main {\n           |  def main(args: Array[String]): Unit = {\n           |    println(\"$msg\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      // ensure the test will be run on a fresh Bloop instance\n      os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n      (0 to 2).foreach { _ =>\n        val res = os.proc(TestUtil.cli, \"run\", input, extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        expect(res.out.trim() == msg)\n        expect(!res.err.trim().toLowerCase.contains(\"error\"))\n      }\n    }\n  }\n\n  test(s\"warn about invalid values present in JAVA_OPTS\") {\n    val expectedOutput = \"Hello\"\n    TestInputs(os.rel / \"example.sc\" -> s\"println(\\\"$expectedOutput\\\")\")\n      .fromRoot { root =>\n        val invalidOpt = \"--invalid\"\n        val validOpt   = \"-Dfoo=bar\"\n        val res        = os.proc(TestUtil.cli, \"run\", \"example.sc\", \"--server=false\", extraOptions)\n          .call(cwd = root, env = Map(\"JAVA_OPTS\" -> s\"$invalidOpt $validOpt\"), stderr = os.Pipe)\n        val errOutput = res.err.trim()\n        expect(errOutput.contains(\n          s\"Only java properties are supported in JAVA_OPTS\"\n        ))\n        expect(errOutput.contains(s\"Other options are ignored: $invalidOpt\"))\n        expect(!errOutput.contains(validOpt))\n        expect(res.out.trim() == expectedOutput)\n      }\n  }\n\n  test(s\"warn about invalid values present in .scala-jvmopts\") {\n    val expectedOutput = \"Hello\"\n    val invalidOpt     = \"--invalid\"\n    val validOpt       = \"-Dfoo=bar\"\n    TestInputs(\n      os.rel / \"example.sc\"     -> s\"println(\\\"$expectedOutput\\\")\",\n      os.rel / \".scala-jvmopts\" ->\n        s\"\"\"$invalidOpt\n           |$validOpt\n           |\"\"\".stripMargin\n    )\n      .fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"run\", \"example.sc\", extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n        val errOutput = res.err.trim()\n        expect(errOutput.contains(s\"Only java properties are supported in .scala-jvmopts file\"))\n        expect(errOutput.contains(s\"Other options are ignored: $invalidOpt\"))\n        expect(!errOutput.contains(validOpt))\n        expect(res.out.trim() == expectedOutput)\n      }\n  }\n\n  {\n    val expectedMessage = \"Hello\"\n    for {\n      (actualInputPath, inputPathToCall, inputs) <- {\n        val scalaInputPath  = os.rel / \"Main.scala\"\n        val scriptInputPath = os.rel / \"script.sc\"\n        val scalaInputs     = TestInputs(\n          scalaInputPath -> s\"\"\"object Main extends App { println(\"$expectedMessage\") }\"\"\"\n        )\n        val scriptInputs = TestInputs(scriptInputPath -> s\"\"\"println(\"$expectedMessage\")\"\"\")\n        Seq(\n          (scalaInputPath, \".\", scalaInputs),\n          (scalaInputPath, scalaInputPath.toString, scalaInputs),\n          (scriptInputPath, \".\", scriptInputs),\n          (scriptInputPath, scriptInputPath.toString, scriptInputs)\n        )\n      }\n      inputExtension = \".\" + actualInputPath.last.split('.').last\n    }\n      test(\n        s\"prioritise main class in a $inputExtension file passed as $inputPathToCall over main classes in dependencies on the classpath\"\n      ) {\n        inputs.fromRoot { root =>\n          val localCache        = root / \"local-cache\"\n          val dependencyVersion = \"42.7.4\"\n          val csRes             = os.proc(\n            TestUtil.cs,\n            \"fetch\",\n            \"--cache\",\n            localCache,\n            s\"org.postgresql:postgresql:$dependencyVersion\"\n          )\n            .call(cwd = root)\n          val dependencyJar = csRes.out.trim().linesIterator.toSeq.head\n\n          // pass classpath via -cp\n          val res =\n            os.proc(TestUtil.cli, \"run\", inputPathToCall, extraOptions, \"-cp\", dependencyJar)\n              .call(cwd = root)\n          expect(res.out.trim() == expectedMessage)\n\n          // pass classpath via args file\n          val argsFileName = \"args.txt\"\n          os.write(root / argsFileName, s\"-cp $dependencyJar\")\n          val res2 = os.proc(TestUtil.cli, \"run\", inputPathToCall, extraOptions, s\"@$argsFileName\")\n            .call(cwd = root)\n          expect(res2.out.trim() == expectedMessage)\n        }\n      }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\")) test(\n    \"fail with a valid error when multiple main classes are present and a dependency doesn't define main classes in the manifest\"\n  ) {\n    val (main1, main2) = \"main1\" -> \"main2\"\n    val input          = \"example.scala\"\n    TestInputs(\n      os.rel / input ->\n        s\"\"\"//> using dep io.get-coursier:coursier_2.13:2.1.24\n           |@main def $main1() = println(\"$main1\")\n           |@main def $main2() = println(\"$main2\")\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"run\", input, extraOptions)\n        .call(cwd = root, stderr = os.Pipe, check = false)\n      val err = res.err.trim()\n      expect(err.contains(\"Found several main classes\"))\n      expect(err.contains(main1))\n      expect(err.contains(main2))\n    }\n  }\n\n  for {\n    (input, code) <- Seq(\n      os.rel / \"script.sc\" -> \"\"\"println(args.mkString(\" \"))\"\"\",\n      os.rel / \"raw.scala\" ->\n        \"\"\"object Main { def main(args: Array[String]) = println(args.mkString(\" \")) }\"\"\"\n    )\n    testInputs = TestInputs(input -> code)\n    shouldRestartBloop <- Seq(true, false)\n    restartBloopString = if (shouldRestartBloop) \"with\" else \"without\"\n    parallelInstancesCount <-\n      TestUtil.isCI -> shouldRestartBloop match {\n        case (true, false) => Seq(2, 5)\n        case (true, true)  => Seq(2)\n        case _             => Seq(5, 10, 20)\n      }\n  }\n    test(\n      s\"run $parallelInstancesCount instances of $input in parallel ($restartBloopString restarting Bloop)\"\n    ) {\n      TestUtil.retryOnCi() {\n        testInputs.fromRoot {\n          root =>\n            if (shouldRestartBloop)\n              os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\")\n                .call(cwd = root)\n            val processes: Seq[(os.SubProcess, Int)] = (0 until parallelInstancesCount).map { i =>\n              os.proc(\n                TestUtil.cli,\n                \"run\",\n                input.toString(),\n                extraOptions,\n                \"--\",\n                \"iteration\",\n                i.toString\n              )\n                .spawn(cwd = root, env = Map(\"SCALA_CLI_EXTRA_TIMEOUT\" -> \"120 seconds\"))\n            }.zipWithIndex\n            processes.foreach { case (p, _) => p.waitFor() }\n            processes.foreach { case (p, _) => expect(p.exitCode() == 0) }\n            processes.foreach { case (p, i) => expect(p.stdout.trim() == s\"iteration $i\") }\n        }\n      }\n\n    }\n\n  for (\n    (platformDescription, platformOpts) <- Seq(\n      \"JVM\"    -> Nil,\n      \"JS\"     -> Seq(\"--js\"),\n      \"Native\" -> Seq(\"--native\")\n    )\n  )\n    test(s\"run a main method from the test scope ($platformDescription)\") {\n      val expectedMessage = \"Hello from the test scope\"\n      TestInputs(\n        os.rel / \"example.test.scala\" ->\n          s\"\"\"object Main extends App { println(\"$expectedMessage\") }\"\"\"\n      ).fromRoot { root =>\n        val res = os.proc(TestUtil.cli, \"run\", \".\", \"--test\", extraOptions, platformOpts)\n          .call(cwd = root)\n        expect(res.out.trim().contains(expectedMessage))\n      }\n    }\n\n  test(s\"--list-main-classes includes test scope main methods when --test is enabled\") {\n    val expectedMains @ Seq(expectedMain1, expectedMain2) = Seq(\"Main\", \"AnotherMain\")\n    val expectedOutput                                    = expectedMains.mkString(\" \")\n    TestInputs(\n      os.rel / \"example.scala\" ->\n        s\"\"\"object $expectedMain1 extends App { println(\"Hello from the main scope\") }\"\"\",\n      os.rel / \"example.test.scala\" ->\n        s\"\"\"object $expectedMain2 extends App { println(\"Hello from the test scope\") }\"\"\"\n    ).fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"run\",\n        \".\",\n        \"--test\",\n        \"--list-main-classes\",\n        extraOptions\n      )\n        .call(cwd = root)\n      expect(res.out.trim() == expectedOutput)\n    }\n  }\n\n  for {\n    javaVersion <-\n      if isScala38OrNewer then\n        Constants.allJavaVersions.filter(_ >= Constants.scala38MinJavaVersion)\n      else Constants.allJavaVersions.filter(_ < 24)\n  }\n    test(\n      s\"run a simple hello world with the runner module on the classpath, Scala $actualScalaVersion and Java $javaVersion\"\n    ) {\n      val expectedMessage     = \"Hello, world!\"\n      val legacyRunnerWarning = \"Defaulting to a legacy runner module version\"\n      TestInputs(os.rel / \"script.sc\" -> s\"\"\"println(\"$expectedMessage\")\"\"\")\n        .fromRoot { root =>\n          val res =\n            os.proc(TestUtil.cli, \"run\", \".\", \"--runner\", extraOptions, \"--jvm\", javaVersion)\n              .call(cwd = root, stderr = os.Pipe)\n          expect(res.out.trim() == expectedMessage)\n          val legacyWarningCheck = {\n            val check       = res.err.trim().contains(legacyRunnerWarning)\n            val shouldCheck =\n              javaVersion < Constants.scala38MinJavaVersion || actualScalaVersion.startsWith(\"2\")\n            if shouldCheck then check else !check\n          }\n          expect(legacyWarningCheck)\n        }\n    }\n\n  for (parallelInstancesCount <- Seq(2, 5, 10))\n    test(\n      s\"run $parallelInstancesCount instances in parallel without local repo\"\n    ) {\n      TestInputs.empty.fromRoot { root =>\n        val localRepoPath =\n          os.Path(os.proc(\n            TestUtil.cli,\n            \"directories\",\n            \"--power\"\n          ).call().out.text().linesIterator.find(_.startsWith(\"Local repository:\")).getOrElse(\n            throw new RuntimeException(\"Local repository line not found in directories output\")\n          ).split(\":\", 2).last.trim())\n        os.remove.all(localRepoPath)\n\n        val processes = (0 until parallelInstancesCount).map { _ =>\n          os.proc(TestUtil.cli, \"--version\")\n            .spawn(cwd = root)\n        }.zipWithIndex\n        processes.foreach { case (p, _) => p.waitFor() }\n        processes.foreach { case (p, _) => expect(p.exitCode() == 0) }\n      }\n    }\n\n  test(\"sbt file in directory does not break run\") {\n    val message = \"Hello from run\"\n    TestInputs(\n      os.rel / \"Main.scala\" ->\n        s\"\"\"object Main {\n           |  def main(args: Array[String]): Unit = println(\"$message\")\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"build.sbt\" -> \"\"\"name := \"my-project\"\"\"\"\n    ).fromRoot { root =>\n      val output = os.proc(TestUtil.cli, extraOptions, \".\").call(cwd = root).out.trim()\n      expect(output == message)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunTests212.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass RunTests212 extends RunTestDefinitions with Test212 {\n  test(\"Descriptive error message for unsupported native/script configurations\") {\n    val inputs        = TestInputs(os.rel / \"a.sc\" -> \"println(1)\")\n    val nativeVersion = \"0.4.2\"\n    inputs.fromRoot { root =>\n      val output = os.proc(\n        TestUtil.cli,\n        extraOptions,\n        \"--native\",\n        \"a.sc\",\n        \"--native-version\",\n        nativeVersion\n      ).call(\n        cwd = root,\n        check = false,\n        stderr = os.Pipe\n      ).err.trim()\n      expect(\n        output.contains(\n          s\"Used Scala Native version $nativeVersion is incompatible with Scala $actualScalaVersion.\"\n        )\n      )\n    }\n  }\n  test(\"2.12.nightly\") {\n    TestInputs(os.rel / \"sample.sc\" -> \"println(util.Properties.versionNumberString)\").fromRoot {\n      root =>\n        val res =\n          os.proc(\n            TestUtil.cli,\n            \"run\",\n            \".\",\n            \"-S\",\n            \"2.12.nightly\",\n            TestUtil.extraOptions\n          )\n            .call(cwd = root)\n        val version = res.out.trim()\n        expect(version.startsWith(\"2.12\"))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunTests213.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass RunTests213 extends RunTestDefinitions with Test213 {\n  test(\"ensure typesafe PR validation snapshot Scala versions are supported\".flaky) {\n    TestInputs(os.rel / \"sample.sc\" -> \"println(util.Properties.versionNumberString)\").fromRoot {\n      root =>\n        val snapshotScalaVersion = \"2.13.11-bin-89f3de5-SNAPSHOT\"\n        val res                  =\n          os.proc(\n            TestUtil.cli,\n            \"run\",\n            \".\",\n            \"--repository\",\n            \"https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots\",\n            \"-S\",\n            snapshotScalaVersion,\n            TestUtil.extraOptions\n          )\n            .call(cwd = root)\n        expect(res.out.trim() == \"2.13.11-20221128-143922-89f3de5\")\n    }\n  }\n  test(\"2.13.nightly\") {\n    TestInputs(os.rel / \"sample.sc\" -> \"println(util.Properties.versionNumberString)\").fromRoot {\n      root =>\n        val res =\n          os.proc(\n            TestUtil.cli,\n            \"run\",\n            \".\",\n            \"-S\",\n            \"2.13.nightly\",\n            TestUtil.extraOptions\n          )\n            .call(cwd = root)\n        val version = res.out.trim()\n        expect(version.startsWith(\"2.13\"))\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunTests3Lts.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass RunTests3Lts extends RunTestDefinitions with Test3Lts {\n  import Constants.scala3LtsPrefix\n  for (ltsNightlyAlias <- List(\"lts.nightly\", \"3.lts.nightly\"))\n    test(s\"Scala $ltsNightlyAlias & $scala3LtsPrefix.nightly point to the same version\") {\n      TestInputs.empty.fromRoot { root =>\n        val version1      = getScalaVersion(ltsNightlyAlias, root)\n        val nightlyPrefix = version1.split('.').take(2).mkString(\".\")\n        expect(nightlyPrefix == Constants.scala3LtsPrefix)\n        val nightlyPatch = version1.split('.').take(3).last.takeWhile(_.isDigit).toInt\n        if nightlyPrefix == \"3.3\" then expect(nightlyPatch >= 8) // new nightly repo\n        val version2 = getScalaVersion(s\"${Constants.scala3LtsPrefix}.nightly\", root)\n        expect(version1 == version2)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass RunTests3NextRc extends RunTestDefinitions with Test3NextRc {\n  for { nightlyTag <- List(\"3.nightly\", \"nightly\") }\n    test(s\"Scala $nightlyTag & 3.<latest-minor>.nightly point to the same version\") {\n      TestInputs.empty.fromRoot { root =>\n        val version1     = getScalaVersion(nightlyTag, root)\n        val nightlyMinor = version1.split('.').take(2).last\n        val version2     = getScalaVersion(s\"3.$nightlyMinor.nightly\", root)\n        expect(version1 == version2)\n      }\n    }\n\n  for {\n    label <- List(\"rc\", \"3.rc\", \"3.lts.rc\", \"lts.rc\", s\"${Constants.scala3LtsPrefix}.rc\", \"3.7.rc\")\n  }\n    test(s\"$label is valid and works as expected\") {\n      TestInputs.empty.fromRoot { root =>\n        val latestRcVersion = getScalaVersion(label, root)\n        if latestRcVersion == actualScalaVersion then {\n          expect(\n            label == \"rc\" || label == \"3.rc\"\n          ) // this should only be the case for *latest* labels\n          System.err.println(s\"RC version $latestRcVersion is the same as the hardcoded latest RC\")\n        }\n        expect(latestRcVersion.startsWith(\"3.\"))\n        expect(latestRcVersion.contains(\"-RC\"))\n        expect(!latestRcVersion.contains(\"SNAPSHOT\"))\n        expect(!latestRcVersion.contains(\"NIGHTLY\"))\n      }\n    }\n\n  for { label <- List(\"2.rc\", \"2.12.rc\", \"2.13.rc\") }\n    test(s\"$label produces a reasonable error\") {\n      TestInputs.empty.fromRoot { root =>\n        val result = getScalaVersion(label, root, check = false, mergeErrIntoOut = true)\n        expect(result.contains(\"Invalid Scala version\"))\n        expect(result.contains(\n          \"In the case of Scala 2, a particular nightly version serves as a release candidate.\"\n        ))\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass RunTestsDefault extends RunTestDefinitions\n    with RunWithWatchTestDefinitions\n    with TestDefault {\n  def archLinuxTest(): Unit = {\n    val message = \"Hello from Scala CLI on Arch Linux\"\n    val inputs  = TestInputs(\n      os.rel / \"hello.sc\" ->\n        s\"\"\"println(\"$message\")\n           |\"\"\".stripMargin\n    )\n    val extraOptsStr = extraOptions.mkString(\" \") /* meh escaping */\n    inputs.fromRoot { root =>\n      os.copy(os.Path(TestUtil.cli.head, os.pwd), root / \"scala\")\n      val script =\n        s\"\"\"#!/usr/bin/env sh\n           |set -e\n           |./scala --server=false $extraOptsStr . | tee -a output\n           |\"\"\".stripMargin\n      os.write(root / \"script.sh\", script)\n      os.perms.set(root / \"script.sh\", \"rwxr-xr-x\")\n      val termOpt = if (System.console() == null) Nil else Seq(\"-t\")\n      val cmd     = Seq[os.Shellable](\n        \"docker\",\n        \"run\",\n        \"--rm\",\n        termOpt,\n        \"-e\",\n        \"SCALA_CLI_VENDORED_ZIS=true\",\n        \"-v\",\n        s\"$root:/data\",\n        \"-w\",\n        \"/data\",\n        ciOpt,\n        Constants.dockerArchLinuxImage,\n        \"/data/script.sh\"\n      )\n      val res = os.proc(cmd).call(cwd = root)\n      System.err.println(res.out.text())\n      val output = os.read(root / \"output\").trim\n      expect(output == message)\n    }\n  }\n\n  if (Properties.isLinux && TestUtil.isNativeCli)\n    // TODO: restore this test once it gets reliable again\n    test(\"arch linux\".ignore) {\n      archLinuxTest()\n    }\n\n  test(\"3.nightly\") { // should run code using scala 3 nightly version\n    TestInputs(os.rel / \"sample.sc\" -> \"\"\"println(\"Hello World\")\"\"\").fromRoot {\n      root =>\n        val res =\n          os.proc(\n            TestUtil.cli,\n            \"run\",\n            \".\",\n            \"-S\",\n            \"3.nightly\",\n            TestUtil.extraOptions\n          )\n            .call(cwd = root)\n        expect(res.out.trim() == \"Hello World\")\n    }\n  }\n\n  test(\"as jar\") {\n    val inputs = TestInputs(\n      os.rel / \"CheckCp.scala\" ->\n        \"\"\"//> using dep com.lihaoyi::os-lib:0.9.1\n          |object CheckCp {\n          |  def main(args: Array[String]): Unit = {\n          |    val cp = sys.props(\"java.class.path\")\n          |      .split(java.io.File.pathSeparator)\n          |      .toVector\n          |      .map(os.Path(_, os.pwd))\n          |    assert(cp.forall(os.isFile(_)), \"Not only files\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"run\", extraOptions, \".\")\n        .call(cwd = root, mergeErrIntoOut = true, check = false)\n      expect(res.exitCode != 0)\n      val output = res.out.text()\n      expect(output.contains(\"java.lang.AssertionError: assertion failed: Not only files\"))\n\n      os.proc(TestUtil.cli, \"--power\", \"run\", extraOptions, \".\", \"--as-jar\")\n        .call(cwd = root)\n    }\n  }\n\n  test(\"meaningful commas dont have to be escaped in using directive values\") {\n    val inputPath = os.rel / \"example.scala\"\n    TestInputs(inputPath ->\n      \"\"\"//> using dep tabby:tabby:0.2.3,url=https://github.com/bjornregnell/tabby/releases/download/v0.2.3/tabby_3-0.2.3.jar\n        |import tabby.Grid\n        |@main def main = println(Grid(\"a\", \"b\", \"c\")(1, 2, 3))\n        |\"\"\".stripMargin).fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"run\", extraOptions, inputPath)\n        .call(cwd = root)\n      val out = res.out.trim()\n      expect(out.contains(\"a, b, c\"))\n    }\n  }\n\n  for {\n    suppressDeprecatedWarnings <- Seq(true, false)\n    suppressByConfig           <- if (suppressDeprecatedWarnings) Seq(true, false) else Seq(false)\n    suppressWarningOptions =\n      if (suppressDeprecatedWarnings && !suppressByConfig) Seq(\"--suppress-deprecated-warnings\")\n      else Seq.empty\n    suppressedWarningText =\n      suppressDeprecatedWarnings -> suppressByConfig match {\n        case (true, true)  => \"suppressed (by config)\"\n        case (true, false) => \"suppressed (by command line option)\"\n        case (false, _)    => \"reported\"\n      }\n  }\n    test(\n      s\"a deprecation warning should be $suppressedWarningText when using commas with space as separators in using directives\"\n    ) {\n      val configFile = os.rel / \"config\" / \"config.json\"\n      val configEnvs = Map(\"SCALA_CLI_CONFIG\" -> configFile.toString())\n      val inputPath  = os.rel / \"example.sc\"\n      TestInputs(inputPath ->\n        \"\"\"//> using options -Werror, -Wconf:cat=deprecation:e, -Wconf:cat=unused:e\n          |println(\"Deprecation warnings should have been printed\")\n          |\"\"\".stripMargin)\n        .fromRoot { root =>\n          if (suppressByConfig)\n            os.proc(TestUtil.cli, \"config\", \"suppress-warning.deprecated-features\", \"true\")\n              .call(cwd = root, env = configEnvs)\n          val res = os.proc(TestUtil.cli, \"run\", extraOptions, inputPath, suppressWarningOptions)\n            .call(cwd = root, stderr = os.Pipe, env = configEnvs)\n          val err             = res.err.trim()\n          val expectedWarning = \"Use of commas as separators is deprecated\"\n          if (suppressDeprecatedWarnings) expect(!err.contains(\"deprecated\"))\n          else {\n            expect(err.contains(\"deprecated\"))\n            expect(err.linesIterator.count(_.contains(expectedWarning)) == 2)\n          }\n        }\n    }\n\n  for {\n    useTestScope <- Seq(true, false)\n    fileName        = if (useTestScope) \"example.test.scala\" else \"example.scala\"\n    scopeOptions    = if (useTestScope) Seq(\"--test\") else Seq.empty\n    scopeDesc       = if (useTestScope) \"test\" else \"main\"\n    expectedMessage = \"Hello, World!\"\n    platformOptions <- Seq(Seq(\"--native\"), Seq(\"--js\"), Nil)\n    platformDesc = platformOptions.headOption.map {\n      case \"--native\" => \"Native\"\n      case \"--js\"     => \"JS\"\n      case other      => other\n    }.getOrElse(\"JVM\")\n    crossScalaVersions = Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n    numberOfBuilds     = crossScalaVersions.size\n    testInputs         = TestInputs(\n      os.rel / \"project.scala\" -> s\"//> using scala ${crossScalaVersions.mkString(\" \")}\",\n      os.rel / fileName        ->\n        s\"\"\"object Main extends App {\n           |  println(\"$expectedMessage\")\n           |}\n           |\"\"\".stripMargin\n    )\n  } {\n    test(\n      s\"run --cross platform $platformDesc Scala ${crossScalaVersions.mkString(\" \")} ($scopeDesc scope)\"\n    ) {\n      TestUtil.retryOnCi() {\n        testInputs.fromRoot { root =>\n          val r =\n            os.proc(\n              TestUtil.cli,\n              \"run\",\n              \".\",\n              \"--cross\",\n              \"--power\",\n              extraOptions,\n              scopeOptions,\n              platformOptions\n            )\n              .call(cwd = root)\n          expect(r.out.trim().linesIterator.count(_.trim() == expectedMessage) == numberOfBuilds)\n        }\n      }\n    }\n\n    test(\n      s\"run without --cross $platformDesc ${crossScalaVersions.mkString(\" \")} ($scopeDesc scope)\"\n    ) {\n      TestUtil.retryOnCi() {\n        testInputs.fromRoot { root =>\n          val r =\n            os.proc(\n              TestUtil.cli,\n              \"run\",\n              \".\",\n              extraOptions,\n              scopeOptions,\n              platformOptions\n            )\n              .call(cwd = root, stderr = os.Pipe)\n          println(r.err.trim())\n          expect(r.err.trim().contains(s\"Defaulting to Scala $actualScalaVersion, $platformDesc\"))\n          expect(r.err.trim().contains(s\"ignoring ${numberOfBuilds - 1} builds\"))\n          expect(r.out.trim() == expectedMessage)\n        }\n      }\n    }\n  }\n\n  for {\n    scalaVersion <- TestUtil.legacyScalaVersionsOnePerMinor\n    expectedMessage = \"Hello, world!\"\n    expectedWarning =\n      s\"Defaulting to a legacy runner module version: ${Constants.runnerScala30LegacyVersion}\"\n  }\n    test(\n      s\"run a simple hello world with the runner module on the classpath and Scala $scalaVersion (legacy)\"\n    ) {\n      TestInputs(os.rel / \"script.sc\" -> s\"\"\"println(\"$expectedMessage\")\"\"\").fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"run\", \".\", \"-S\", scalaVersion, TestUtil.extraOptions, \"--runner\")\n            .call(cwd = root, stderr = os.Pipe)\n        expect(res.out.trim() == expectedMessage)\n        expect(res.err.trim().contains(expectedWarning))\n      }\n    }\n\n  for {\n    buildServerOptions <- Seq(Nil, Seq(\"--server=false\"))\n    buildServerDesc =\n      if buildServerOptions.isEmpty then \"with build server\" else \"without build server\"\n  }\n    test(s\"pure Java run has no Scala on classpath $buildServerDesc\") {\n      TestInputs(\n        os.rel / \"Main.java\" ->\n          \"\"\"public class Main {\n            |  public static void main(String[] args) {\n            |    try {\n            |      Class.forName(\"scala.Predef\");\n            |      throw new RuntimeException(\"Scala should not be on the classpath\");\n            |    } catch (ClassNotFoundException e) {\n            |      System.out.println(\"No Scala on classpath!\");\n            |    }\n            |  }\n            |}\n            |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"run\", buildServerOptions, extraOptions, \".\").call(cwd = root)\n        expect(res.out.text().contains(\"No Scala on classpath!\"))\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunWithWatchTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.ProcOps\nimport scala.concurrent.duration.DurationInt\nimport scala.util.{Properties, Try}\n\ntrait RunWithWatchTestDefinitions { this: RunTestDefinitions =>\n  // TODO make this pass reliably on Mac CI\n  if (!Properties.isMac || !TestUtil.isCI) {\n    val expectedMessage1 = \"Hello\"\n    val expectedMessage2 = \"World\"\n    for {\n      (inputPath, inputs, codeToWriteOver) <-\n        Seq(\n          {\n            val inputPath             = os.rel / \"raw.scala\"\n            def code(message: String) = s\"\"\"object Smth extends App { println(\"$message\") }\"\"\"\n            (\n              inputPath,\n              TestInputs(inputPath -> code(expectedMessage1)),\n              code(expectedMessage2)\n            )\n          }, {\n            val inputPath             = os.rel / \"script.sc\"\n            def code(message: String) = s\"\"\"println(\"$message\")\"\"\"\n            (\n              inputPath,\n              TestInputs(inputPath -> code(expectedMessage1)),\n              code(expectedMessage2)\n            )\n          }, {\n            val inputPath             = os.rel / \"Main.java\"\n            def code(message: String) = s\"\"\"public class Main {\n                                           |  public static void main(String[] args) {\n                                           |    System.out.println(\"$message\");\n                                           |  }\n                                           |}\"\"\".stripMargin\n            (\n              inputPath,\n              TestInputs(inputPath -> code(expectedMessage1)),\n              code(expectedMessage2)\n            )\n          }, {\n            val inputPath             = os.rel / \"markdown.md\"\n            def code(message: String) =\n              s\"\"\"# Some random docs with a Scala snippet\n                 |```scala\n                 |println(\"$message\")\n                 |```\n                 |The snippet prints the message, of course.\n                 |\"\"\".stripMargin\n            (\n              inputPath,\n              TestInputs(inputPath -> code(expectedMessage1)),\n              code(expectedMessage2)\n            )\n          }\n        )\n    }\n      test(s\"simple --watch ${inputPath.last}\") {\n        inputs.fromRoot { root =>\n          TestUtil.withProcessWatching(\n            proc = os.proc(TestUtil.cli, \"run\", inputPath.toString(), \"--watch\", extraOptions)\n              .spawn(cwd = root, stderr = os.Pipe),\n            timeout = 120.seconds\n          ) { (proc, timeout, ec) =>\n            val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n            expect(output1 == expectedMessage1)\n            proc.printStderrUntilRerun(timeout)(ec)\n            os.write.over(root / inputPath, codeToWriteOver)\n            val output2 = TestUtil.readLine(proc.stdout, ec, timeout)\n            expect(output2 == expectedMessage2)\n          }\n        }\n      }\n  }\n\n  if (!Properties.isMac || !TestUtil.isCI) {\n    test(\"--watching with CLI option triggers re-run on external file change\") {\n      val sourceFile   = os.rel / \"app.scala\"\n      val externalFile = os.rel / \"data\" / \"input.txt\"\n      val code         =\n        \"\"\"object App {\n          |  def main(args: Array[String]): Unit = {\n          |    val content = scala.io.Source.fromFile(\"data/input.txt\").mkString.trim\n          |    println(content)\n          |  }\n          |}\n          |\"\"\".stripMargin\n\n      TestInputs(\n        sourceFile   -> code,\n        externalFile -> \"Hello\"\n      ).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          proc = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"run\",\n            \".\",\n            \"--watch\",\n            \"--watching\",\n            \"data\",\n            extraOptions\n          )\n            .spawn(cwd = root, stderr = os.Pipe),\n          timeout = 120.seconds\n        ) { (proc, timeout, ec) =>\n          val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output1 == \"Hello\")\n          proc.printStderrUntilRerun(timeout)(ec)\n          Thread.sleep(2000L)\n          os.write.over(root / externalFile, \"World\")\n          val output2 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output2 == \"World\")\n        }\n      }\n    }\n\n    test(\"//> using watching directive triggers re-run on external file change\") {\n      val sourceFile   = os.rel / \"app.scala\"\n      val externalFile = os.rel / \"data\" / \"input.txt\"\n      val code         =\n        \"\"\"//> using watching ./data\n          |object App {\n          |  def main(args: Array[String]): Unit = {\n          |    val content = scala.io.Source.fromFile(\"data/input.txt\").mkString.trim\n          |    println(content)\n          |  }\n          |}\n          |\"\"\".stripMargin\n\n      TestInputs(\n        sourceFile   -> code,\n        externalFile -> \"Hello\"\n      ).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          proc = os.proc(TestUtil.cli, \"--power\", \"run\", \".\", \"--watch\", extraOptions)\n            .spawn(cwd = root, stderr = os.Pipe),\n          timeout = 120.seconds\n        ) { (proc, timeout, ec) =>\n          val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output1 == \"Hello\")\n          proc.printStderrUntilRerun(timeout)(ec)\n          Thread.sleep(2000L)\n          os.write.over(root / externalFile, \"World\")\n          val output2 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output2 == \"World\")\n        }\n      }\n    }\n\n    test(\"--watching CLI + //> using watching directive union\") {\n      val sourceFile         = os.rel / \"app.scala\"\n      val directiveWatchFile = os.rel / \"data1\" / \"input1.txt\"\n      val cliWatchFile       = os.rel / \"data2\" / \"input2.txt\"\n      val code               =\n        \"\"\"//> using watching ./data1\n          |object App {\n          |  def main(args: Array[String]): Unit = {\n          |    val fromDirective = scala.io.Source.fromFile(\"data1/input1.txt\").mkString.trim\n          |    val fromCli = scala.io.Source.fromFile(\"data2/input2.txt\").mkString.trim\n          |    println(s\"$fromDirective|$fromCli\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n\n      TestInputs(\n        sourceFile         -> code,\n        directiveWatchFile -> \"Hello\",\n        cliWatchFile       -> \"World\"\n      ).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          proc =\n            os.proc(\n              TestUtil.cli,\n              \"--power\",\n              \"run\",\n              \".\",\n              \"--watch\",\n              \"--watching\",\n              \"data2\",\n              extraOptions\n            )\n              .spawn(cwd = root, stderr = os.Pipe),\n          timeout = 120.seconds\n        ) { (proc, timeout, ec) =>\n          val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output1 == \"Hello|World\")\n\n          proc.printStderrUntilRerun(timeout)(ec)\n          Thread.sleep(2000L)\n          os.write.over(root / directiveWatchFile, \"Bonjour\")\n          val output2 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output2 == \"Bonjour|World\")\n\n          proc.printStderrUntilRerun(timeout)(ec)\n          Thread.sleep(2000L)\n          os.write.over(root / cliWatchFile, \"Universe\")\n          val output3 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output3 == \"Bonjour|Universe\")\n        }\n      }\n    }\n  }\n\n  for {\n    (platformDescription, platformOpts) <- Seq(\n      \"JVM\"    -> Nil,\n      \"JS\"     -> Seq(\"--js\"),\n      \"Native\" -> Seq(\"--native\")\n    )\n    // TODO make this pass reliably on Mac CI https://github.com/VirtusLab/scala-cli/issues/2517\n    if !Properties.isMac || !TestUtil.isCI\n  }\n    test(s\"--watch --test ($platformDescription)\") {\n      TestUtil.retryOnCi() {\n        val expectedMessage1 = \"Hello from the test scope 1\"\n        val expectedMessage2 = \"Hello from the test scope 2\"\n        val inputPath        = os.rel / \"example.test.scala\"\n\n        def code(expectedMessage: String) =\n          s\"\"\"object Main extends App { println(\"$expectedMessage\") }\"\"\"\n\n        TestInputs(\n          inputPath -> code(expectedMessage1)\n        ).fromRoot { root =>\n          TestUtil.withProcessWatching(\n            proc =\n              os.proc(\n                TestUtil.cli,\n                \"run\",\n                inputPath.toString(),\n                \"--watch\",\n                \"--test\",\n                extraOptions,\n                platformOpts\n              )\n                .spawn(cwd = root, stderr = os.Pipe),\n            timeout = 300.seconds\n          ) { (proc, timeout, ec) =>\n            val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n            expect(output1 == expectedMessage1)\n            proc.printStderrUntilRerun(timeout)(ec)\n            os.write.over(root / inputPath, code(expectedMessage2))\n            val output2 = TestUtil.readLine(proc.stdout, ec, timeout)\n            expect(output2 == expectedMessage2)\n          }\n        }\n      }\n    }\n\n  test(\"watch with interactive, with multiple main classes\") {\n    val fileName = \"watch.scala\"\n    TestInputs(\n      os.rel / fileName ->\n        \"\"\"object Run1 extends App {println(\"Run1 launched\")}\n          |object Run2 extends App {println(\"Run2 launched\")}\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val confDir  = root / \"config\"\n      val confFile = confDir / \"test-config.json\"\n\n      os.write(confFile, \"{}\", createFolders = true)\n\n      if (!Properties.isWin)\n        os.perms.set(confDir, \"rwx------\")\n\n      val configEnv = Map(\"SCALA_CLI_CONFIG\" -> confFile.toString)\n\n      TestUtil.withProcessWatching(\n        proc = os.proc(TestUtil.cli, \"run\", \"--watch\", \"--interactive\", fileName)\n          .spawn(\n            cwd = root,\n            mergeErrIntoOut = true,\n            stdout = os.Pipe,\n            stdin = os.Pipe,\n            env = Map(\"SCALA_CLI_INTERACTIVE\" -> \"true\") ++ configEnv\n          ),\n        timeout = 60.seconds\n      ) { (proc, timeout, ec) =>\n        def lineReaderIter: Iterator[String] = Iterator.continually {\n          val line = TestUtil.readLine(proc.stdout, ec, timeout)\n          println(s\"Line read: $line\")\n          line\n        }\n\n        def checkLinesForError(lines: Seq[String]): Unit = munit.Assertions.assert(\n          !lines.exists { line =>\n            TestUtil.removeAnsiColors(line).contains(\"[error]\")\n          },\n          clues(lines.toSeq)\n        )\n\n        def answerInteractivePrompt(id: Int): Unit = {\n          val interactivePromptLines = lineReaderIter\n            .takeWhile(!_.startsWith(\"[1]\" /* probably [1] Run2  or [1] No*/ ))\n            .toList\n          expect(interactivePromptLines.nonEmpty)\n          checkLinesForError(interactivePromptLines)\n          proc.stdin.write(s\"$id\\n\")\n          proc.stdin.flush()\n        }\n\n        def analyzeRunOutput(restart: Boolean): Unit = {\n          val runResultLines = lineReaderIter\n            .takeWhile(!_.contains(\"press Enter to re-run\"))\n            .toList\n          expect(runResultLines.nonEmpty)\n          checkLinesForError(runResultLines)\n          if (restart)\n            proc.stdin.write(\"\\n\")\n          proc.stdin.flush()\n        }\n\n        // You have run the current scala-cli command with the --interactive mode turned on.\n        // Would you like to leave it on permanently?\n        answerInteractivePrompt(0)\n\n        // Found several main classes. Which would you like to run?\n        answerInteractivePrompt(0)\n        expect(TestUtil.readLine(proc.stdout, ec, timeout) == \"Run1 launched\")\n\n        analyzeRunOutput( /* restart */ true)\n\n        answerInteractivePrompt(1)\n        expect(TestUtil.readLine(proc.stdout, ec, timeout) == \"Run2 launched\")\n\n        analyzeRunOutput( /* restart */ false)\n      }\n    }\n  }\n\n  if (!Properties.isMac || !TestUtil.isNativeCli || !TestUtil.isCI)\n    // TODO make this pass reliably on Mac CI\n    test(\"watch artifacts\") {\n      val libSourcePath            = os.rel / \"lib\" / \"Messages.scala\"\n      def libSource(hello: String) =\n        s\"\"\"//> using publish.organization test-org\n           |//> using publish.name messages\n           |//> using publish.version 0.1.0\n           |\n           |package messages\n           |\n           |object Messages {\n           |  def hello(name: String) = s\"$hello $$name\"\n           |}\n           |\"\"\".stripMargin\n      TestInputs(\n        libSourcePath                    -> libSource(\"Hello\"),\n        os.rel / \"app\" / \"TestApp.scala\" ->\n          \"\"\"//> using lib test-org::messages:0.1.0\n            |\n            |package testapp\n            |\n            |import messages.Messages\n            |\n            |@main\n            |def run(): Unit =\n            |  println(Messages.hello(\"user\"))\n            |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val testRepo = root / \"test-repo\"\n\n        def publishLib(): Unit =\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"publish\",\n            \"--offline\",\n            \"--publish-repo\",\n            testRepo,\n            \"lib\"\n          )\n            .call(cwd = root)\n\n        publishLib()\n\n        TestUtil.withProcessWatching(\n          os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"run\",\n            \"--offline\",\n            \"app\",\n            \"-w\",\n            \"-r\",\n            testRepo.toNIO.toUri.toASCIIString\n          ).spawn(cwd = root)\n        ) { (proc, timeout, ec) =>\n          val output = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output == \"Hello user\")\n\n          os.write.over(root / libSourcePath, libSource(\"Hola\"))\n          publishLib()\n\n          val secondOutput = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(secondOutput == \"Hola user\")\n        }\n      }\n    }\n\n  test(\"watch test - no infinite loop\") {\n    val fileName = \"watch.scala\"\n    TestInputs(\n      os.rel / fileName ->\n        \"\"\"//> using dep org.scalameta::munit::0.7.29\n          |\n          |class MyTests extends munit.FunSuite {\n          |    test(\"is true true\") { assert(true) }\n          |}\n          |\"\"\".stripMargin\n    ).fromRoot { root =>\n      TestUtil.withProcessWatching(\n        proc = os.proc(TestUtil.cli, \"test\", \"-w\", \"watch.scala\")\n          .spawn(cwd = root, mergeErrIntoOut = true),\n        timeout = 10.seconds\n      ) { (proc, timeout, ec) =>\n        val watchingMsg = \"Watching sources, press Ctrl+C to exit, or press Enter to re-run.\"\n        val testingMsg  = \"MyTests:\"\n\n        def lineReadIter = Iterator.continually(Try(TestUtil.readLine(proc.stdout, ec, timeout)))\n          .takeWhile(_.isSuccess)\n          .map(_.get)\n\n        val beforeAppendOut = lineReadIter.toSeq\n        expect(beforeAppendOut.count(_.contains(testingMsg)) == 1)\n        expect(beforeAppendOut.count(_.contains(watchingMsg)) == 1)\n        expect(beforeAppendOut.last.contains(watchingMsg))\n\n        os.write.append(root / fileName, \"\\n//comment\")\n\n        val afterAppendOut = lineReadIter.toSeq\n        expect(afterAppendOut.count(_.contains(testingMsg)) == 1)\n        expect(afterAppendOut.count(_.contains(watchingMsg)) == 1)\n        expect(afterAppendOut.last.contains(watchingMsg))\n      }\n    }\n  }\n\n  // TODO make this pass reliably on Mac CI\n  if (!Properties.isMac || !TestUtil.isCI)\n    test(\"--watch .scala source with changing directives\") {\n      val inputPath = os.rel / \"smth.scala\"\n\n      def code(includeDirective: Boolean) = {\n        val directive = if (includeDirective) \"//> using toolkit default\" else \"\"\n        s\"\"\"$directive\n           |object Smth {\n           |  def main(args: Array[String]) = println(os.pwd)\n           |}\n           |\"\"\".stripMargin\n      }\n\n      TestInputs(inputPath -> code(includeDirective = true)).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          os.proc(TestUtil.cli, \"run\", \".\", \"--watch\", extraOptions)\n            .spawn(cwd = root, stderr = os.Pipe)\n        ) { (proc, timeout, ec) =>\n          val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output1 == root.toString)\n          proc.printStderrUntilRerun(timeout)(ec)\n          os.write.over(root / inputPath, code(includeDirective = false))\n          TestUtil.readLine(proc.stderr, ec, timeout)\n          val output2 = TestUtil.readLine(proc.stderr, ec, timeout)\n          expect(output2.toLowerCase.contains(\"error\"))\n          proc.printStderrUntilRerun(timeout)(ec)\n          os.write.over(root / inputPath, code(includeDirective = true))\n          val output3 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output3 == root.toString)\n        }\n      }\n    }\n\n  for {\n    useDirective <- Seq(false, true)\n    testScope    <- if (useDirective) Seq(false, true) else Seq(false)\n    scopeString = if (testScope) \"test\" else \"main\"\n    // TODO make this pass reliably on Mac CI\n    if !Properties.isMac || !TestUtil.isCI\n    directive =\n      useDirective -> testScope match {\n        case (true, true)  => \"//> using test.resourceDirs ./resources\"\n        case (true, false) => \"//> using resourceDirs ./resources\"\n        case _             => \"\"\n      }\n    resourceOptions = if (useDirective) Nil else Seq(\"--resource-dirs\", \"./src/proj/resources\")\n    scopeOptions    = if (testScope) Seq(\"--test\") else Nil\n    title           = if (useDirective) \"directive\" else \"command line\"\n  } test(s\"resources via $title with --watch ($scopeString)\") {\n    val expectedMessage1 = \"Hello\"\n    val expectedMessage2 = \"world\"\n    resourcesInputs(directive = directive, resourceContent = expectedMessage1)\n      .fromRoot { root =>\n        TestUtil.withProcessWatching(\n          os.proc(\n            TestUtil.cli,\n            \"run\",\n            \"src\",\n            \"--watch\",\n            resourceOptions,\n            scopeOptions,\n            extraOptions\n          )\n            .spawn(cwd = root, stderr = os.Pipe)\n        ) { (proc, timeout, ec) =>\n          val output1 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output1 == expectedMessage1)\n          proc.printStderrUntilRerun(timeout)(ec)\n          val (resourcePath, newResourceContent) =\n            resourcesInputs(directive = directive, resourceContent = expectedMessage2)\n              .files\n              .find(_._1.toString.contains(\"resources\"))\n              .get\n          os.write.over(root / resourcePath, newResourceContent)\n          val output2 = TestUtil.readLine(proc.stdout, ec, timeout)\n          expect(output2 == expectedMessage2)\n        }\n      }\n  }\n\n  def testRepeatedRerunsWithWatch(): Unit = {\n    def expectedMessage(number: Int) = s\"Hello $number\"\n\n    def content(number: Int) =\n      s\"@main def main(): Unit = println(\\\"${expectedMessage(number)}\\\")\"\n\n    TestUtil.retryOnCi() {\n      val inputPath = os.rel / \"example.scala\"\n      TestInputs(inputPath -> content(0)).fromRoot { root =>\n        os.proc(TestUtil.cli, \"--power\", \"bloop\", \"exit\").call(cwd = root)\n        TestUtil.withProcessWatching(\n          proc = os.proc(TestUtil.cli, \".\", \"--watch\", extraOptions)\n            .spawn(cwd = root, stderr = os.Pipe)\n        ) { (proc, timeout, ec) =>\n          for (num <- 1 to 10) {\n            val output = TestUtil.readLine(proc.stdout, ec, timeout)\n            expect(output == expectedMessage(num - 1))\n            proc.printStderrUntilRerun(timeout)(ec)\n            Thread.sleep(200L)\n            if (num < 10) {\n              val newContent = content(num)\n              os.write.over(root / inputPath, newContent)\n            }\n          }\n        }\n      }\n    }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\") && Properties.isMac && TestUtil.isCI)\n    // TODO make this pass reliably on Mac CI (seems to work fine locally)\n    test(\"watch mode doesnt hang on Bloop when rebuilding repeatedly\".flaky) {\n      testRepeatedRerunsWithWatch()\n    }\n  else if (actualScalaVersion.startsWith(\"3\"))\n    test(\"watch mode doesnt hang on Bloop when rebuilding repeatedly\") {\n      testRepeatedRerunsWithWatch()\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/RunZipTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.nio.charset.{Charset, StandardCharsets}\n\nimport scala.cli.integration.TestInputs.compress\n\ntrait RunZipTestDefinitions { this: RunTestDefinitions =>\n  test(\"Zip with multiple Scala files\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"object Hello extends App {\n           |  println(Messages.hello)\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"Messages.scala\" ->\n        s\"\"\"object Messages {\n           |  def hello: String = \"Hello\"\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.asZip { (root, zipPath) =>\n      val message = \"Hello\"\n      val output  = os.proc(TestUtil.cli, extraOptions, zipPath.toString)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n  test(\"load virtual data with UTF_16 encoding\") {\n    val zipInputs: Seq[(os.RelPath, String, Charset)] = Seq(\n      (\n        os.rel / \"Hello.scala\",\n        s\"\"\"//> using resourceDir ./\n           |import scala.io.Source\n           |import java.nio.charset.StandardCharsets\n           |import java.io.{BufferedReader, InputStreamReader}\n           |import java.util.stream.Collectors\n           |\n           |object Hello extends App {\n           |    val inputStream = getClass().getResourceAsStream(\"input\")\n           |    val nativeResourceText = new BufferedReader(\n           |      new InputStreamReader(inputStream, StandardCharsets.UTF_16)\n           |    ).lines().collect(Collectors.joining(\"\\\\n\"));\n           |    println(nativeResourceText)\n           |}\n           |\"\"\".stripMargin,\n        StandardCharsets.UTF_8\n      ),\n      (\n        os.rel / \"input\",\n        s\"\"\"1\n           |2\n           |\"\"\".stripMargin,\n        StandardCharsets.UTF_16\n      )\n    )\n    TestInputs().fromRoot { root =>\n      val zipArchivePath = root / \"hello.zip\"\n      compress(zipArchivePath, zipInputs)\n\n      val output = os.proc(TestUtil.cli, extraOptions, zipArchivePath.toString)\n        .call(cwd = root)\n        .out.trim()\n\n      val expectedOutput = \"1\\n2\"\n\n      expect(output == expectedOutput)\n    }\n  }\n\n  test(\"Zip with Scala containing resource directive\") {\n    val inputs = TestInputs(\n      os.rel / \"Hello.scala\" ->\n        s\"\"\"//> using resourceDir ./\n           |import scala.io.Source\n           |\n           |object Hello extends App {\n           |    val inputs = Source.fromResource(\"input\").getLines.map(_.toInt).toSeq\n           |    println(inputs.mkString(\",\"))\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"input\" ->\n        s\"\"\"1\n           |2\n           |\"\"\".stripMargin\n    )\n    inputs.asZip { (root, zipPath) =>\n      val message = \"1,2\"\n\n      val output = os.proc(TestUtil.cli, extraOptions, zipPath.toString)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"Zip with Scala Script containing resource directive\") {\n    val inputs = TestInputs(\n      os.rel / \"hello.sc\" ->\n        s\"\"\"//> using resourceDir ./\n           |import scala.io.Source\n           |\n           |val inputs = Source.fromResource(\"input\").getLines.map(_.toInt).toSeq\n           |println(inputs.mkString(\",\"))\n           |\"\"\".stripMargin,\n      os.rel / \"input\" ->\n        s\"\"\"1\n           |2\n           |\"\"\".stripMargin\n    )\n    inputs.asZip { (root, zipPath) =>\n      val message = \"1,2\"\n\n      val output = os.proc(TestUtil.cli, extraOptions, zipPath.toString)\n        .call(cwd = root)\n        .out.trim()\n      expect(output == message)\n    }\n  }\n\n  test(\"Zip with Markdown containing resource directive\") {\n    val (inputA, inputB) = \"1\" -> \"2\"\n    val expectedMessage  = s\"$inputA,$inputB\"\n    val inputs           = TestInputs(\n      os.rel / \"Hello.md\" ->\n        s\"\"\"# Example Markdown file\n           |A snippet for printing inputs from resources\n           |```scala raw\n           |//> using resourceDir ./\n           |import scala.io.Source\n           |object Hello extends App {\n           |  val inputs = Source.fromResource(\"input\").getLines.map(_.toInt).toSeq\n           |  println(inputs.mkString(\",\"))\n           |}\n           |```\n           |\"\"\".stripMargin,\n      os.rel / \"input\" ->\n        s\"\"\"$inputA\n           |$inputB\n           |\"\"\".stripMargin\n    )\n    inputs.asZip { (root, zipPath) =>\n      val result = os.proc(TestUtil.cli, \"--power\", extraOptions, zipPath, \"--md\").call(cwd = root)\n      expect(result.out.trim() == expectedMessage)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SbtTestHelper.scala",
    "content": "package scala.cli.integration\n\ntrait SbtTestHelper {\n  protected lazy val sbtLaunchJar: os.Path = {\n    val res =\n      os.proc(TestUtil.cs, \"fetch\", \"--intransitive\", \"org.scala-sbt:sbt-launch:1.5.5\").call()\n    val rawPath = res.out.trim()\n    val path    = os.Path(rawPath, os.pwd)\n    if (os.isFile(path)) path\n    else sys.error(s\"Something went wrong (invalid sbt launch JAR path '$rawPath')\")\n  }\n\n  protected lazy val sbt: os.Shellable =\n    Seq[os.Shellable](\n      \"java\",\n      \"-Xmx512m\",\n      \"-Xms128m\",\n      \"-Djline.terminal=jline.UnsupportedTerminal\",\n      \"-Dsbt.log.noformat=true\",\n      \"-jar\",\n      sbtLaunchJar\n    )\n\n  protected def sbtCommand(args: String*): os.proc = os.proc(sbt, args)\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ScalaCliSuite.scala",
    "content": "package scala.cli.integration\n\nimport java.util.concurrent.TimeUnit\n\nimport scala.concurrent.duration.{Duration, FiniteDuration}\nimport scala.util.Properties\n\nabstract class ScalaCliSuite extends munit.FunSuite {\n  implicit class BeforeEachOpts(munitContext: BeforeEach) {\n    def locationAbsolutePath: os.Path = os.Path(munitContext.test.location.path)\n  }\n\n  implicit class AfterEachOpts(munitContext: AfterEach) {\n    def locationAbsolutePath: os.Path = os.Path(munitContext.test.location.path)\n  }\n  val testStartEndLogger: Fixture[Unit] = new Fixture[Unit](\"files\") {\n    def apply(): Unit = ()\n\n    override def beforeEach(context: BeforeEach): Unit = {\n      val fileName = context.locationAbsolutePath.baseName\n      System.err.println(\n        s\">==== ${Console.CYAN}Running '${context.test.name}' from $fileName${Console.RESET}\"\n      )\n    }\n\n    override def afterEach(context: AfterEach): Unit = {\n      val fileName = context.locationAbsolutePath.baseName\n      System.err.println(\n        s\"X==== ${Console.CYAN}Finishing '${context.test.name}' from $fileName${Console.RESET}\"\n      )\n    }\n\n    override def afterAll(): Unit = {\n      super.afterAll()\n      // Clean up cached JDKs after all tests have run on Linux native CI runners\n      if isCI && Properties.isLinux then TestUtil.cleanCachedJdks()\n      else System.err.println(\"Skipping cached JDKs cleanup\")\n    }\n  }\n\n  override def munitTimeout: Duration = new FiniteDuration(300, TimeUnit.SECONDS)\n\n  override def munitFixtures: List[Fixture[Unit]] = List(testStartEndLogger)\n  def group: ScalaCliSuite.TestGroup              = ScalaCliSuite.TestGroup.Third\n\n  override def munitIgnore: Boolean =\n    Option(System.getenv(\"SCALA_CLI_IT_GROUP\"))\n      .flatMap(_.toIntOption)\n      .exists(_ != group.idx)\n\n  override def munitFlakyOK: Boolean = TestUtil.isCI\n}\n\nobject ScalaCliSuite {\n  sealed abstract class TestGroup(val idx: Int) extends Product with Serializable\n  object TestGroup {\n    case object First  extends TestGroup(1) // Scala 3 Next / default\n    case object Second extends TestGroup(2) // Scala 2.13\n    case object Third  extends TestGroup(3) // Scala 2.12\n    case object Fourth extends TestGroup(4) // Scala 3.3 LTS\n    case object Fifth  extends TestGroup(5) // Scala 3 Next RC\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/ScriptWrapperTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.await\nimport scala.concurrent.{ExecutionContext, Future}\nimport scala.jdk.CollectionConverters.*\n\ntrait ScriptWrapperTestDefinitions extends ScalaCliSuite { this: BspTestDefinitions =>\n  private def appWrapperSnippet(wrapperName: String)    = s\"object $wrapperName extends App {\"\n  private def classWrapperSnippet(wrapperName: String)  = s\"final class $wrapperName$$_\"\n  private def objectWrapperSnippet(wrapperName: String) = s\"object $wrapperName {\"\n  def expectScriptWrapper(\n    path: os.Path,\n    containsCheck: String => Boolean,\n    doesNotContainCheck: String => Boolean\n  ): Unit = {\n    val generatedFileContent = os.read(path)\n    assert(\n      containsCheck(generatedFileContent),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n    assert(\n      doesNotContainCheck(generatedFileContent),\n      clue(s\"Generated file content: $generatedFileContent\")\n    )\n  }\n  def expectAppWrapper(wrapperName: String, path: os.Path): Unit =\n    expectScriptWrapper(\n      path,\n      _.contains(appWrapperSnippet(wrapperName)),\n      content =>\n        !content.contains(classWrapperSnippet(wrapperName)) &&\n        !content.contains(objectWrapperSnippet(wrapperName))\n    )\n\n  def expectObjectWrapper(wrapperName: String, path: os.Path): Unit =\n    expectScriptWrapper(\n      path,\n      _.contains(objectWrapperSnippet(wrapperName)),\n      content =>\n        !content.contains(classWrapperSnippet(wrapperName)) &&\n        !content.contains(appWrapperSnippet(wrapperName))\n    )\n\n  def expectClassWrapper(wrapperName: String, path: os.Path): Unit =\n    expectScriptWrapper(\n      path,\n      _.contains(classWrapperSnippet(wrapperName)),\n      content =>\n        !content.contains(appWrapperSnippet(wrapperName)) &&\n        !content.contains(objectWrapperSnippet(wrapperName))\n    )\n\n  def testScriptWrappers(\n    inputs: TestInputs,\n    bspOptions: Seq[String] = Nil,\n    extraOptionsOverride: Seq[String] = extraOptions\n  )(expectWrapperFunction: (String, os.Path) => Unit)(implicit ec: ExecutionContext): Unit = {\n    withBsp(\n      inputs,\n      inputs.fileNames ++ Seq(\"--power\") ++ bspOptions,\n      extraOptionsOverride = extraOptionsOverride\n    ) {\n      (root, _, remoteServer) =>\n        Future {\n          val buildTargetsResp   = remoteServer.workspaceBuildTargets().asScala.await\n          val targets            = buildTargetsResp.getTargets.asScala.map(_.getId).asJava\n          val compileParams      = new b.CompileParams(targets)\n          val buildResp          = remoteServer.buildTargetCompile(compileParams).asScala.await\n          val expectedStatusCode = b.StatusCode.OK\n          expect(buildResp.getStatusCode == expectedStatusCode)\n          val projectDir = os.list(root / Constants.workspaceDirName).filter(\n            _.baseName.startsWith(root.baseName + \"_\")\n          )\n          expect(projectDir.size == 1)\n          inputs.fileNames.map(_.stripSuffix(\".sc\")).foreach {\n            scriptName =>\n              expectWrapperFunction(\n                scriptName,\n                projectDir.head / \"src_generated\" / \"main\" / s\"$scriptName.scala\"\n              )\n          }\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SemanticDbTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\ntrait SemanticDbTestDefinitions { this: CompileTestDefinitions =>\n  def javaHelloWorld(packageName: String, mainClassName: String): String =\n    s\"\"\"package $packageName;\n       |\n       |public class $mainClassName {\n       |  public static void main(String[] args) {\n       |    System.err.println(\"Hello\");\n       |  }\n       |}\n       |\"\"\".stripMargin\n\n  def scalaHelloWorld(packageName: String, mainClassName: String): String =\n    s\"\"\"package $packageName\n       |\n       |object $mainClassName {\n       |  def main(args: Array[String]): Unit = {\n       |    println(\"Hello\")\n       |  }\n       |}\n       |\"\"\".stripMargin\n\n  lazy val scalaScriptHelloWorld: String = \"\"\"#!/usr/bin/env scala\n                                             |println(\"Hello\")\n                                             |\"\"\".stripMargin\n\n  test(\"Java SemanticDB (manual)\") {\n    TestInputs(os.rel / \"foo\" / \"Test.java\" -> javaHelloWorld(\"foo\", \"Test\"))\n      .fromRoot { root =>\n        val compilerPackages = Seq(\n          \"com.sun.tools.javac.api\",\n          \"com.sun.tools.javac.code\",\n          \"com.sun.tools.javac.model\",\n          \"com.sun.tools.javac.tree\",\n          \"com.sun.tools.javac.util\"\n        )\n        val exports = compilerPackages\n          .flatMap { pkg =>\n            Seq(\"-J--add-exports\", s\"-Jjdk.compiler/$pkg=ALL-UNNAMED\")\n          }\n          .flatMap(opt => List(\"--javac-opt\", opt))\n        val javaSemDbOptions = Seq(\n          \"--javac-plugin\",\n          s\"com.sourcegraph:semanticdb-javac:${Constants.semanticDbJavacPluginVersion}\",\n          \"--javac-opt\",\n          s\"-Xplugin:semanticdb -sourceroot:$root -targetroot:javac-classes-directory\"\n        ) ++ exports\n        os.proc(TestUtil.cli, \"compile\", extraOptions, javaSemDbOptions, \".\")\n          .call(cwd = root)\n\n        val files      = os.walk(root / Constants.workspaceDirName)\n        val semDbFiles = files\n          .filter(_.last.endsWith(\".semanticdb\"))\n          .filter(!_.segments.exists(_ == \"bloop-internal-classes\"))\n        expect(semDbFiles.length == 1)\n        val semDbFile = semDbFiles.head\n        val path      = os.rel / \"META-INF\" / \"semanticdb\" / \"foo\" / \"Test.java.semanticdb\"\n        expect(semDbFile.endsWith(path))\n      }\n  }\n\n  for {\n    inputType           <- Seq(\"Java\", \"Scala\", \"ScalaScript\")\n    semanticDbTargetDir <- Seq(None, Some(\"semanticdb-target\"))\n    inputTypeString = inputType match {\n      case \"ScalaScript\" => \"Scala script\"\n      case it            => it\n    }\n  } {\n    test(\n      s\"$inputTypeString SemanticDB${semanticDbTargetDir.map(_ => \" with forced target root\").getOrElse(\"\")}\"\n    ) {\n      val sourceFileName =\n        if (inputType == \"Java\") \"Test.java\"\n        else if (inputType == \"Scala\") \"Test.scala\"\n        else \"Test.sc\"\n      (if (inputType == \"Java\")\n         TestInputs(os.rel / \"foo\" / sourceFileName -> javaHelloWorld(\"foo\", \"Test\"))\n       else if (inputType == \"Scala\")\n         TestInputs(os.rel / \"foo\" / sourceFileName -> scalaHelloWorld(\"foo\", \"Test\"))\n       else\n         TestInputs(os.rel / \"foo\" / sourceFileName -> scalaScriptHelloWorld)).fromRoot {\n        root =>\n          val targetDirOptions =\n            semanticDbTargetDir match {\n              case Some(targetDir) => Seq(\"--semantic-db-target-root\", targetDir)\n              case None            => Nil\n            }\n          os.proc(TestUtil.cli, \"compile\", extraOptions, \"--semantic-db\", \".\", targetDirOptions)\n            .call(cwd = root)\n          val files      = os.walk(root)\n          val semDbFiles = files\n            .filter(_.last.endsWith(\".semanticdb\"))\n            .filter(!_.segments.exists(_ == \"bloop-internal-classes\"))\n          expect(semDbFiles.length == 1)\n          val semDbFile              = semDbFiles.head\n          val expectedSemanticDbPath =\n            if (semanticDbTargetDir.isDefined)\n              os.rel /\n                semanticDbTargetDir\n                  .get / \"META-INF\" / \"semanticdb\" / \"foo\" / s\"$sourceFileName.semanticdb\"\n            else\n              os.rel / \"META-INF\" / \"semanticdb\" / \"foo\" / s\"$sourceFileName.semanticdb\"\n          expect(semDbFile.endsWith(expectedSemanticDbPath))\n      }\n    }\n\n    for {\n      workspaceDir <- Seq(None, Some(\"custom-workspace\"))\n    }\n      test(\n        s\"$inputTypeString SemanticDB with spread source dirs, forced source root ${\n            semanticDbTargetDir.map(_ => \"and target root \").getOrElse(\"\")\n          }${workspaceDir.map(_ => \"and custom workspace directory\").getOrElse(\"\")}\"\n      ) {\n        val (className1, className2)           = s\"Test1$inputType\" -> s\"Test2$inputType\"\n        val (sourceFileName1, sourceFileName2) =\n          if (inputType == \"Java\") s\"$className1.java\" -> s\"$className2.java\"\n          else if (inputType == \"Scala\") s\"$className1.scala\" -> s\"$className2.scala\"\n          else s\"$className1.sc\"                              -> s\"$className2.sc\"\n        val (package1, package2)     = \"foo\"                 -> \"bar\"\n        val (sourceDir1, sourceDir2) = (os.rel / \"sources1\") -> (os.rel / \"sources2\")\n        val (code1, code2)           =\n          if (inputType == \"Java\")\n            javaHelloWorld(package1, className1) -> javaHelloWorld(package2, className2)\n          else if (inputType == \"Scala\")\n            scalaHelloWorld(package1, className1) -> scalaHelloWorld(package2, className2)\n          else scalaScriptHelloWorld              -> scalaScriptHelloWorld\n        TestInputs(\n          os.rel / sourceDir1 / package1 / sourceFileName1 -> code1,\n          os.rel / sourceDir2 / package2 / sourceFileName2 -> code2\n        ).fromRoot { (root: os.Path) =>\n          val targetDirOptions =\n            semanticDbTargetDir match {\n              case Some(targetDir) => Seq(\"--semanticdb-targetroot\", targetDir)\n              case None            => Nil\n            }\n          val workspaceDirOptions =\n            workspaceDir match {\n              case Some(workspaceDir) => Seq(\"--workspace\", workspaceDir)\n              case None               => Nil\n            }\n          val semanticDbOptions: Seq[String] =\n            targetDirOptions ++ Seq(\n              \"--semanticdb\",\n              \"--semanticdb-sourceroot\",\n              root.toString\n            )\n          os.proc(\n            TestUtil.cli,\n            \"compile\",\n            extraOptions,\n            workspaceDirOptions,\n            semanticDbOptions,\n            sourceDir1,\n            sourceDir2\n          )\n            .call(cwd = root)\n          val files = os.walk(semanticDbTargetDir.map(root / _)\n            .orElse(workspaceDir.map(root / _))\n            .getOrElse(root / sourceDir1 / Constants.workspaceDirName))\n          val semDbFiles = files\n            .filter(_.last.endsWith(\".semanticdb\"))\n            .filter(!_.segments.exists(_ == \"bloop-internal-classes\"))\n          expect(semDbFiles.length == 2)\n          val semDbFile1 = semDbFiles.find(_.last == s\"$sourceFileName1.semanticdb\").get\n          val relPath1   =\n            os.rel / \"META-INF\" / \"semanticdb\" / sourceDir1 / package1 /\n              s\"$sourceFileName1.semanticdb\"\n          expect(semDbFile1.endsWith(relPath1))\n          val semDbFile2 = semDbFiles.find(_.last == s\"$sourceFileName2.semanticdb\").get\n          val relPath2   =\n            os.rel / \"META-INF\" / \"semanticdb\" / sourceDir2 / package2 /\n              s\"$sourceFileName2.semanticdb\"\n          expect(semDbFile2.endsWith(relPath2))\n        }\n      }\n  }\n\n  if (actualScalaVersion.startsWith(\"3\"))\n    test(\n      \"Scala script SemanticDB with forced source root and custom workspace directory outside of root\"\n    ) {\n      TestInputs(os.rel / \"projectRoot\" / \"foo\" / \"Test.sc\" -> scalaScriptHelloWorld).fromRoot {\n        (root: os.Path) =>\n          val customWorkspace = root / \"custom-workspace\"\n          os.proc(\n            TestUtil.cli,\n            \"compile\",\n            extraOptions,\n            \"--workspace\",\n            customWorkspace.toString(),\n            \"--semanticdb-sourceroot\",\n            \"projectRoot\",\n            \"--semanticdb\",\n            \"projectRoot\"\n          ).call(cwd = root)\n          val semDbFiles = os.walk(customWorkspace / Constants.workspaceDirName)\n            .filter(_.last.endsWith(\".semanticdb\"))\n            .filter(!_.segments.exists(_ == \"bloop-internal-classes\"))\n          expect(semDbFiles.length == 1)\n          val relPath = os.rel / \"META-INF\" / \"semanticdb\" / \"foo\" / \"Test.sc.semanticdb\"\n          expect(semDbFiles.head.endsWith(relPath))\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SharedRunTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nclass SharedRunTests extends ScalaCliSuite {\n  val printScalaVersionInputs: TestInputs = TestInputs(\n    os.rel / \"print.sc\" ->\n      s\"\"\"println(scala.util.Properties.versionNumberString)\n         |\"\"\".stripMargin\n  )\n  val printScalaVersionInputs3: TestInputs = TestInputs(\n    os.rel / \"print.sc\" ->\n      s\"\"\"def printStuff(): Unit =\n         |  val toPrint = scala.util.Properties.versionNumberString\n         |  println(toPrint)\n         |printStuff()\n         |\"\"\".stripMargin\n  )\n  test(\"Scala version 2.12\") {\n    printScalaVersionInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, TestUtil.extraOptions, \".\", \"--scala\", \"2.12\")\n        .call(cwd = root)\n        .out.trim()\n      expect(output.startsWith(\"2.12.\"))\n    }\n  }\n  test(\"Scala version 2.13\") {\n    printScalaVersionInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, TestUtil.extraOptions, \".\", \"--scala\", \"2.13\")\n        .call(cwd = root)\n        .out.trim()\n      expect(output.startsWith(\"2.13.\"))\n    }\n  }\n  test(\"Scala version 2\") {\n    printScalaVersionInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, TestUtil.extraOptions, \".\", \"--scala\", \"2\")\n        .call(cwd = root)\n        .out.trim()\n      expect(output.startsWith(\"2.13.\"))\n    }\n  }\n  test(\"Scala version 3\") {\n    printScalaVersionInputs3.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, TestUtil.extraOptions, \".\", \"--scala\", \"3.0.2\")\n        .call(cwd = root)\n        .out.trim()\n      // Scala 3.0 uses the 2.13 standard library\n      expect(output.startsWith(\"2.13.\"))\n    }\n  }\n\n  test(\"Scala version in config file\") {\n    val confSv = \"2.13.13\"\n    val inputs = TestInputs(\n      os.rel / \"test.sc\" ->\n        s\"\"\"//> using scala $confSv\n           |println(scala.util.Properties.versionNumberString)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, TestUtil.extraOptions, \".\").call(cwd = root).out.trim()\n      expect(output == confSv)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SipScalaTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport os.CommandResult\n\nimport scala.util.Properties\n\nclass SipScalaTests extends ScalaCliSuite\n    with SbtTestHelper\n    with MillTestHelper\n    with CoursierScalaInstallationTestHelper {\n  implicit class StringEnrichment(s: String) {\n    def containsExperimentalWarningOf(featureNameAndType: String): Boolean =\n      s.contains(s\"The $featureNameAndType is experimental\") ||\n      s.linesIterator\n        .dropWhile(!_.endsWith(\"are marked as experimental:\"))\n        .takeWhile(_ != \"Please bear in mind that non-ideal user experience should be expected.\")\n        .contains(s\" - $featureNameAndType\")\n  }\n  override def munitFlakyOK: Boolean = TestUtil.isCI\n\n  implicit class BinaryNameOps(binaryName: String) {\n    def prepareBinary(root: os.Path): os.Path = {\n      val cliPath    = os.Path(TestUtil.cliPath, os.pwd)\n      val ext        = if (Properties.isWin) \".exe\" else \"\"\n      val newCliPath = root / s\"$binaryName$ext\"\n      os.copy(cliPath, newCliPath)\n      if (!Properties.isWin) os.perms.set(newCliPath, \"rwxr-xr-x\")\n      newCliPath\n    }\n  }\n  def powerArgs(isPowerMode: Boolean): Seq[String] = if (isPowerMode) Seq(\"--power\") else Nil\n  def suppressExperimentalWarningArgs(areWarningsSuppressed: Boolean): Seq[String] =\n    if (areWarningsSuppressed) Seq(\"--suppress-experimental-feature-warning\") else Nil\n\n  def testWithGlobalConfig(\n    configKey: String,\n    testWhenDisabled: (os.Path, Map[String, String]) => Any,\n    testWhenEnabled: (os.Path, Map[String, String]) => Any\n  ): Any =\n    TestInputs.empty.fromRoot { root =>\n      val homeEnv = Map(\"SCALA_CLI_CONFIG\" -> (root / \"config\" / \"config.json\").toString())\n      for (disableSetting <- Seq(\"false\", \"--unset\")) {\n        os.proc(TestUtil.cli, \"config\", configKey, disableSetting)\n          .call(cwd = root, env = homeEnv)\n        testWhenDisabled(root, homeEnv)\n        os.proc(TestUtil.cli, \"config\", configKey, \"true\", \"-f\")\n          .call(cwd = root, env = homeEnv)\n        testWhenEnabled(root, homeEnv)\n      }\n    }\n\n  def testDirectoriesCommand(isPowerMode: Boolean): Unit =\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, powerArgs(isPowerMode), \"directories\").call(\n        cwd = root,\n        check = false,\n        mergeErrIntoOut = true\n      )\n      if (!isPowerMode) {\n        expect(res.exitCode == 1)\n        val output = res.out.text()\n        expect(output.contains(\"The `directories` sub-command is restricted.\"))\n      }\n      else expect(res.exitCode == 0)\n    }\n\n  def testExportCommand(isPowerMode: Boolean, areWarningsSuppressed: Boolean): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"HelloWorld.scala\" ->\n        \"\"\"//> using scala 3.0.0\n          |object HelloWorld extends App { println(\\\"Hello World\\\") }\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        powerArgs(isPowerMode),\n        \"export\",\n        suppressExperimentalWarningArgs(areWarningsSuppressed),\n        \".\"\n      ).call(\n        cwd = root,\n        check = false,\n        stderr = os.Pipe\n      )\n      val errOutput = res.err.trim()\n      isPowerMode -> areWarningsSuppressed match {\n        case (false, _) =>\n          expect(errOutput.contains(\"The `export` sub-command is experimental.\"))\n          expect(res.exitCode == 1)\n        case (true, false) =>\n          expect(errOutput.containsExperimentalWarningOf(\"`export` sub-command\"))\n        case (true, true) =>\n          expect(!errOutput.containsExperimentalWarningOf(\"`export` sub-command\"))\n      }\n    }\n  }\n\n  def testConfigCommand(isPowerMode: Boolean, areWarningsSuppressed: Boolean): Unit =\n    TestInputs.empty.fromRoot { root =>\n      val homeEnv = Map(\"SCALA_CLI_CONFIG\" -> (root / \"config\" / \"config.json\").toString())\n      def callConfig(key: String, value: String): CommandResult =\n        os.proc(\n          TestUtil.cli,\n          powerArgs(isPowerMode),\n          \"config\",\n          suppressExperimentalWarningArgs(areWarningsSuppressed),\n          key,\n          value\n        ).call(cwd = root, check = false, stderr = os.Pipe, env = homeEnv)\n\n      val configProxyResult    = callConfig(\"repositories.default\", \"https://example.address/maven\")\n      val configProxyErrOutput = configProxyResult.err.trim()\n      val configPublishUserResult    = callConfig(\"publish.user.name\", \"exampleUser\")\n      val configPublishUserErrOutput = configPublishUserResult.err.trim()\n      isPowerMode -> areWarningsSuppressed match {\n        case (false, _) =>\n          expect(configProxyResult.exitCode == 1)\n          expect(configProxyErrOutput.contains(\n            \"The `repositories.default` configuration key is restricted.\"\n          ))\n          expect(configPublishUserResult.exitCode == 1)\n          expect(configPublishUserErrOutput.containsExperimentalWarningOf(\n            \"`publish.user.name` configuration key\"\n          ))\n        case (true, false) =>\n          expect(configProxyResult.exitCode == 0)\n          expect(!configProxyErrOutput.contains(\n            \"The `repositories.default` configuration key is restricted.\"\n          ))\n          expect(configPublishUserResult.exitCode == 0)\n          expect(configPublishUserErrOutput.containsExperimentalWarningOf(\n            \"`publish.user.name` configuration key\"\n          ))\n        case (true, true) =>\n          expect(configProxyResult.exitCode == 0)\n          expect(!configProxyErrOutput.contains(\n            \"The `repositories.default` configuration key is restricted.\"\n          ))\n          expect(configPublishUserResult.exitCode == 0)\n          expect(!configPublishUserErrOutput.contains(\n            \"`publish.user.name` configuration key\"\n          ))\n      }\n    }\n\n  def testExportCommandHelp(isPowerMode: Boolean): Unit =\n    TestInputs.empty.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, powerArgs(isPowerMode), \"export\", \"-h\").call(\n        cwd = root,\n        stderr = os.Pipe\n      )\n      val output = res.out.trim()\n      if (!isPowerMode) expect(output.contains(\"The `export` sub-command is experimental.\"))\n      else expect(output.contains(\"The `export` sub-command is experimental.\"))\n    }\n\n  def testConfigCommandHelp(isPowerMode: Boolean, isFullHelp: Boolean): Unit =\n    TestInputs.empty.fromRoot { root =>\n      val helpParams = if (isFullHelp) Seq(\"--full-help\") else Seq(\"-h\")\n      val helpOutput = os.proc(TestUtil.cli, powerArgs(isPowerMode), \"config\", helpParams)\n        .call(cwd = root, stderr = os.Pipe)\n        .out.trim()\n      if (isPowerMode) {\n        expect(helpOutput.contains(\"(power)\"))\n        expect(helpOutput.contains(\"repositories.mirrors\"))\n      }\n      if (isFullHelp) {\n        expect(helpOutput.contains(\"(hidden)\"))\n        expect(helpOutput.contains(\"interactive-was-suggested\"))\n      }\n      if (isPowerMode && isFullHelp) {\n        expect(helpOutput.contains(\"(experimental)\"))\n        expect(helpOutput.contains(\"publish.user.name\"))\n      }\n    }\n\n  def testExperimentalDirectives(isPowerMode: Boolean, areWarningsSuppressed: Boolean): Unit =\n    TestInputs.empty.fromRoot { root =>\n      val code =\n        \"\"\"//> using publish.name my-library\n          |//> using python\n          |class A\n          |\"\"\".stripMargin\n\n      val source = root / \"A.scala\"\n      os.write(source, code)\n\n      val res = os.proc(\n        TestUtil.cli,\n        powerArgs(isPowerMode),\n        \"compile\",\n        suppressExperimentalWarningArgs(areWarningsSuppressed),\n        source\n      ).call(\n        cwd = root,\n        check = false,\n        stderr = os.Pipe\n      )\n\n      val errOutput = res.err.trim()\n\n      isPowerMode -> areWarningsSuppressed match {\n        case (false, _) =>\n          expect(res.exitCode == 1)\n          expect(errOutput.contains(s\"directive is experimental\"))\n        case (true, false) =>\n          expect(res.exitCode == 0)\n          expect(errOutput.containsExperimentalWarningOf(\n            \"`//> using publish.name my-library`\"\n          ))\n          expect(errOutput.containsExperimentalWarningOf(\"`//> using python`\"))\n        case (true, true) =>\n          expect(res.exitCode == 0)\n          expect(!errOutput.containsExperimentalWarningOf(\n            \"`//> using publish.name my-library`\"\n          ))\n          expect(!errOutput.containsExperimentalWarningOf(\"`//> using python`\"))\n      }\n    }\n\n  def testMarkdownOptions(isPowerMode: Boolean, areWarningsSuppressed: Boolean): Unit =\n    TestInputs.empty.fromRoot { root =>\n      val code =\n        \"\"\"\n          | println(\"ala\")\n          |\"\"\".stripMargin\n\n      val source = root / \"A.sc\"\n      os.write(source, code)\n\n      val res =\n        os.proc(\n          TestUtil.cli,\n          powerArgs(isPowerMode),\n          suppressExperimentalWarningArgs(areWarningsSuppressed),\n          \"--scala\",\n          \"3\",\n          \"--markdown\",\n          source\n        ).call(\n          cwd = root,\n          check = false,\n          stderr = os.Pipe\n        )\n      val errOutput = res.err.trim()\n      isPowerMode -> areWarningsSuppressed match {\n        case (false, _) =>\n          expect(res.exitCode == 1)\n          expect(\n            errOutput.contains(\"Unrecognized argument: The `--markdown` option is experimental.\")\n          )\n        case (true, false) =>\n          expect(res.exitCode == 0)\n          expect(errOutput.containsExperimentalWarningOf(\"`--markdown` option\"))\n        case (true, true) =>\n          expect(res.exitCode == 0)\n          expect(!errOutput.containsExperimentalWarningOf(\"`--markdown` option\"))\n      }\n    }\n\n  if (TestUtil.isNativeCli)\n    test(\n      s\"usage instruction should point to scala when installing by cs\"\n    ) { // https://github.com/VirtusLab/scala-cli/issues/1662\n      TestInputs.empty.fromRoot {\n        root => // cs installs binaries under .app-name.aux and scala-cli should drop .aux from progName\n          val binary       = \"scala\".prepareBinary(root)\n          val csBinaryName = root / \".scala.aux\"\n          os.move(binary, csBinaryName)\n          val output = os.proc(csBinaryName, \"test\", \"--usage\").call(check = false).out.text().trim\n          val usageMsg = TestUtil.removeAnsiColors(output)\n          expect(usageMsg == \"Usage: scala test [options]\")\n      }\n    }\n\n  def testDefaultHelpOutput(isPowerMode: Boolean): Unit = TestInputs.empty.fromRoot { root =>\n    for (helpOptions <- HelpTests.variants) {\n      val output =\n        os.proc(TestUtil.cli, powerArgs(isPowerMode), helpOptions).call(cwd = root).out.trim()\n      val restrictedFeaturesMentioned = output.contains(\"package\")\n      if (!isPowerMode) expect(!restrictedFeaturesMentioned)\n      else expect(restrictedFeaturesMentioned)\n    }\n  }\n\n  def testReplHelpOutput(isPowerMode: Boolean): Unit = TestInputs.empty.fromRoot { root =>\n    val output =\n      os.proc(TestUtil.cli, powerArgs(isPowerMode), \"repl\", \"--help-full\").call(cwd =\n        root\n      ).out.trim()\n    val restrictedFeaturesMentioned   = output.contains(\"--amm\")\n    val experimentalFeaturesMentioned = output.contains(\"--python\")\n    if (!isPowerMode) expect(!restrictedFeaturesMentioned && !experimentalFeaturesMentioned)\n    else expect(restrictedFeaturesMentioned && experimentalFeaturesMentioned)\n  }\n\n  def testConfigSuppressingExperimentalFeatureWarnings(featureType: String)(\n    callExperimentalFeature: (\n      os.Path,\n      Map[String, String]\n    ) => CommandResult\n  ): Unit = {\n    testWithGlobalConfig(\n      \"suppress-warning.experimental-features\",\n      testWhenDisabled =\n        (root, homeEnv) => {\n          val errOutput = callExperimentalFeature(root, homeEnv).err.trim()\n          expect(errOutput.containsExperimentalWarningOf(featureType))\n        },\n      testWhenEnabled = (root, homeEnv) => {\n        val errOutput = callExperimentalFeature(root, homeEnv).err.trim()\n        expect(!errOutput.containsExperimentalWarningOf(featureType))\n      }\n    )\n  }\n\n  for {\n    isPowerMode <- Seq(false, true)\n    powerModeString = if (isPowerMode) \"enabled\" else \"disabled\"\n  } {\n    test(s\"test directories command when power mode is $powerModeString\") {\n      testDirectoriesCommand(isPowerMode)\n    }\n    test(s\"test default help when power mode is $powerModeString\") {\n      testDefaultHelpOutput(isPowerMode)\n    }\n    test(s\"test repl help when power mode is $powerModeString\") {\n      testReplHelpOutput(isPowerMode)\n    }\n    for {\n      warningsSuppressed <- Seq(true, false)\n      warningsSuppressedString = if (warningsSuppressed) \"suppressed\" else \"not suppressed\"\n    } {\n      test(\n        s\"test experimental directives when power mode is $powerModeString and experimental warnings are $warningsSuppressedString\"\n      ) {\n        testExperimentalDirectives(isPowerMode, warningsSuppressed)\n      }\n      test(\n        s\"test markdown options when power mode is $powerModeString and experimental warnings are $warningsSuppressedString\"\n      ) {\n        testMarkdownOptions(isPowerMode, warningsSuppressed)\n      }\n      test(\n        s\"test export command when power mode is $powerModeString and experimental warnings are $warningsSuppressedString\"\n      ) {\n        testExportCommand(isPowerMode, warningsSuppressed)\n      }\n      test(\n        s\"test config command when power mode is $powerModeString and experimental warnings are $warningsSuppressedString\"\n      ) {\n        testConfigCommand(isPowerMode, warningsSuppressed)\n      }\n    }\n    test(s\"test export command help output when power mode is $powerModeString\") {\n      testExportCommandHelp(isPowerMode)\n    }\n    for {\n      isFullHelp <- Seq(true, false)\n      helpTypeString = if (isFullHelp) \"full help\" else \"short help\"\n    }\n      test(s\"test config command $helpTypeString output when power mode is $powerModeString\") {\n        testConfigCommandHelp(isPowerMode, isFullHelp)\n      }\n  }\n\n  test(\"test global config suppressing warnings for an experimental sub-command\") {\n    testConfigSuppressingExperimentalFeatureWarnings(\"`export` sub-command\") {\n      (root: os.Path, homeEnv: Map[String, String]) =>\n        val res = os.proc(TestUtil.cli, \"--power\", \"export\")\n          .call(cwd = root, check = false, env = homeEnv, stderr = os.Pipe)\n        expect(res.exitCode == 1)\n        res\n    }\n  }\n  test(\"test global config suppressing warnings for an experimental option\") {\n    testConfigSuppressingExperimentalFeatureWarnings(\"`--md` option\") {\n      (root: os.Path, homeEnv: Map[String, String]) =>\n        os.proc(TestUtil.cli, \"--power\", \"-e\", \"println()\", \"--md\")\n          .call(cwd = root, env = homeEnv, stderr = os.Pipe)\n    }\n  }\n  test(\"test global config suppressing warnings for an experimental directive\") {\n    testConfigSuppressingExperimentalFeatureWarnings(\n      \"`//> using publish.name my-library` directive\"\n    ) {\n      (root: os.Path, homeEnv: Map[String, String]) =>\n        val quote = TestUtil.argQuotationMark\n        os.proc(TestUtil.cli, \"--power\", \"-e\", s\"//> using publish.name ${quote}my-library$quote\")\n          .call(cwd = root, env = homeEnv, stderr = os.Pipe)\n    }\n  }\n  test(\"test global config suppressing warnings for an experimental configuration key\") {\n    testConfigSuppressingExperimentalFeatureWarnings(\"`publish.user.name` configuration key\") {\n      (root: os.Path, homeEnv: Map[String, String]) =>\n        os.proc(TestUtil.cli, \"--power\", \"config\", \"publish.user.name\")\n          .call(cwd = root, env = homeEnv, stderr = os.Pipe)\n    }\n  }\n\n  for ((restrictionType, subCommand) <- Seq(\"restricted\" -> \"package\", \"experimental\" -> \"export\"))\n    test(s\"power config enables $restrictionType sub-command: $subCommand\") {\n      testWithGlobalConfig(\n        configKey = \"power\",\n        testWhenDisabled = { (root, homeEnv) =>\n          val res = os.proc(TestUtil.cli, subCommand)\n            .call(cwd = root, check = false, env = homeEnv, stderr = os.Pipe)\n          val errOutput = res.err.trim()\n          expect(res.exitCode == 1)\n          expect(errOutput.contains(s\"The `$subCommand` sub-command is $restrictionType.\"))\n        },\n        testWhenEnabled = { (root, homeEnv) =>\n          val res = os.proc(TestUtil.cli, subCommand)\n            .call(cwd = root, check = false, env = homeEnv, stderr = os.Pipe)\n          expect(res.exitCode == 1)\n          expect(res.err.text().trim().contains(\"No inputs provided\"))\n        }\n      )\n    }\n\n  test(\"test multiple sources of experimental features\") {\n    val inputs = TestInputs(\n      os.rel / \"Main.scala\" ->\n        \"\"\"//> using target.scope main\n          |//> using target.platform jvm\n          |//> using publish.name my-library\n          |\n          |object Main {\n          |  def main(args: Array[String]): Unit = {\n          |    println(\"Hello World!\")\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"--power\", \"export\", \".\", \"--object-wrapper\", \"--md\")\n        .call(cwd = root, mergeErrIntoOut = true)\n\n      val output = res.out.trim()\n\n      assertNoDiff(\n        output,\n        s\"\"\"Some utilized features are marked as experimental:\n           | - `export` sub-command\n           | - `--object-wrapper` option\n           | - `--md` option\n           |Please bear in mind that non-ideal user experience should be expected.\n           |If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n           |Exporting to a sbt project...\n           |Some utilized directives are marked as experimental:\n           | - `//> using publish.name my-library`\n           | - `//> using target.platform jvm`\n           | - `//> using target.scope main`\n           |Please bear in mind that non-ideal user experience should be expected.\n           |If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n           |Exported to: ${root / \"dest\"}\n           |\"\"\".stripMargin\n      )\n    }\n  }\n\n  test(s\"code using scala-continuations should compile for Scala 2.12.2\".flaky) {\n    val sourceFileName = \"example.scala\"\n    TestInputs(os.rel / sourceFileName ->\n      \"\"\"import scala.util.continuations._\n        |\n        |object ContinuationsExample extends App {\n        |  def generator(init: Int): Int @cps[Unit] = {\n        |    shift { k: (Int => Unit) =>\n        |      for (i <- init to 10) k(i)\n        |    }\n        |    0 // We never reach this point, but it enables the function to compile.\n        |  }\n        |\n        |  reset {\n        |    val result = generator(1)\n        |    println(result)\n        |  }\n        |}\n        |\"\"\".stripMargin).fromRoot { root =>\n      val continuationsVersion = \"1.0.3\"\n      val res                  = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        sourceFileName,\n        \"--compiler-plugin\",\n        s\"org.scala-lang.plugins:::scala-continuations-plugin:$continuationsVersion\",\n        \"--dependency\",\n        s\"org.scala-lang.plugins::scala-continuations-library:$continuationsVersion\",\n        \"-P:continuations:enable\",\n        \"-S\",\n        \"2.12.2\"\n      )\n        .call(cwd = root)\n      expect(res.exitCode == 0)\n    }\n  }\n\n  for {\n    sv <- Seq(Constants.scala212, Constants.scala213, Constants.scala3NextRc)\n    code =\n      if (sv.startsWith(\"3\")) \"println(dotty.tools.dotc.config.Properties.simpleVersionString)\"\n      else \"println(scala.util.Properties.versionNumberString)\"\n    anotherVersion =\n      if (sv.startsWith(\"2.13\")) Constants.scala212\n      else if (sv.startsWith(\"2.12\")) Constants.scala213\n      else Constants.scala3Lts\n  } {\n    test(\n      s\"default Scala version overridden with $sv by a launcher parameter is respected when running a script\"\n    ) {\n      TestInputs(os.rel / \"simple.sc\" -> code)\n        .fromRoot { root =>\n          val r = os.proc(\n            TestUtil.cli,\n            \"--cli-default-scala-version\",\n            sv,\n            \"run\",\n            \"simple.sc\",\n            \"--with-compiler\"\n          )\n            .call(cwd = root)\n          expect(r.out.trim() == sv)\n        }\n    }\n    test(\n      s\"default Scala version overridden with $sv by a launcher parameter is overridable by -S passing $anotherVersion\"\n    ) {\n      TestInputs(os.rel / \"simple.sc\" -> code)\n        .fromRoot { root =>\n          val r = os.proc(\n            TestUtil.cli,\n            \"--cli-default-scala-version\",\n            sv,\n            \"run\",\n            \"simple.sc\",\n            \"--with-compiler\",\n            \"-S\",\n            anotherVersion\n          )\n            .call(cwd = root)\n          expect(r.out.trim() == anotherVersion)\n        }\n    }\n\n    test(\n      s\"default Scala version overridden with $sv by a launcher parameter is respected when printing Scala version\"\n    ) {\n      TestInputs.empty.fromRoot { root =>\n        val r =\n          os.proc(TestUtil.cli, \"--cli-default-scala-version\", sv, \"version\", \"--scala-version\")\n            .call(cwd = root)\n        expect(r.out.trim() == sv)\n      }\n    }\n\n    test(\n      s\"default Scala version overridden with $sv by a launcher parameter is respected when printing versions\"\n    ) {\n      TestInputs.empty.fromRoot { root =>\n        val r = os.proc(TestUtil.cli, \"--cli-default-scala-version\", sv, \"version\")\n          .call(cwd = root)\n        expect(r.out.trim().contains(sv))\n      }\n    }\n  }\n\n  test(s\"default Scala version override launcher option can only be passed once\") {\n    TestInputs.empty.fromRoot { root =>\n      val (sv1, sv2)  = (Constants.scala212, Constants.scala213)\n      val launcherOpt = \"--cli-default-scala-version\"\n      val r           = os.proc(TestUtil.cli, launcherOpt, sv1, launcherOpt, sv2, \"version\")\n        .call(cwd = root, check = false, stderr = os.Pipe)\n      expect(r.exitCode == 1)\n      expect(r.err.trim().contains(launcherOpt))\n      expect(r.err.trim().contains(\"already specified\"))\n    }\n  }\n\n  for {\n    withBloop <- Seq(true, false)\n    withBloopString = if (withBloop) \"with Bloop\" else \"with --server=false\"\n    sv3             = if (Properties.isWin) \"3.5.0-RC1\" else \"3.5.0-RC1-fakeversion-bin-SNAPSHOT\"\n    sv2             = \"2.13.15-bin-ccdcde3\"\n  } {\n    test(\n      s\"default Scala version ($sv3) coming straight from a predefined local repository $withBloopString\"\n    ) {\n      TestInputs(\n        os.rel / \"simple.sc\" -> \"println(dotty.tools.dotc.config.Properties.versionNumberString)\"\n      )\n        .fromRoot { root =>\n          val localRepoPath = root / \"local-repo\"\n          val sv            = sv3\n          if (Properties.isWin) {\n            // 3.5.0-RC1-fakeversion-bin-SNAPSHOT has too long filenames for Windows.\n            // Yes, seriously. Which is why we can't use it there.\n            val artifactNames =\n              Seq(\"scala3-compiler_3\", \"scala3-staging_3\", \"scala3-tasty-inspector_3\") ++\n                (if (withBloop) Seq(\"scala3-sbt-bridge\") else Nil)\n            for { artifactName <- artifactNames } {\n              val csRes = os.proc(\n                TestUtil.cs,\n                \"fetch\",\n                \"--cache\",\n                localRepoPath,\n                s\"org.scala-lang:$artifactName:$sv\"\n              )\n                .call(cwd = root)\n              expect(csRes.exitCode == 0)\n            }\n          }\n          else {\n            TestUtil.initializeGit(root)\n            os.proc(\n              \"git\",\n              \"clone\",\n              \"https://github.com/dotty-staging/maven-test-repo.git\",\n              localRepoPath.toString\n            ).call(cwd = root)\n          }\n          val buildServerOptions =\n            if (withBloop) Nil else Seq(\"--server=false\")\n\n          val predefinedRepository =\n            if (Properties.isWin)\n              (localRepoPath / \"https\" / \"repo1.maven.org\" / \"maven2\").toNIO.toUri.toASCIIString\n            else\n              (localRepoPath / \"thecache\" / \"https\" / \"repo1.maven.org\" /\n                \"maven2\").toNIO.toUri.toASCIIString\n          val r = os.proc(\n            TestUtil.cli,\n            \"--cli-default-scala-version\",\n            sv,\n            \"--predefined-repository\",\n            predefinedRepository,\n            \"run\",\n            \"simple.sc\",\n            \"--with-compiler\",\n            \"--offline\",\n            \"--power\",\n            buildServerOptions\n          )\n            .call(cwd = root)\n          expect(r.out.trim() == sv)\n        }\n    }\n\n    test(\n      s\"default Scala version ($sv2) coming straight from a predefined local repository $withBloopString\".flaky\n    ) {\n      TestInputs(\n        os.rel / \"simple.sc\" -> \"println(scala.util.Properties.versionNumberString)\"\n      )\n        .fromRoot { root =>\n          val localRepoPath = root / \"local-repo\"\n          val sv            = sv2\n          val artifactNames =\n            Seq(\"scala-compiler\") ++ (if (withBloop) Seq(\"scala2-sbt-bridge\") else Nil)\n          for { artifactName <- artifactNames } {\n            val csRes = os.proc(\n              TestUtil.cs,\n              \"fetch\",\n              \"--cache\",\n              localRepoPath,\n              \"-r\",\n              \"https://scala-ci.typesafe.com/artifactory/scala-integration\",\n              s\"org.scala-lang:$artifactName:$sv\"\n            )\n              .call(cwd = root)\n            expect(csRes.exitCode == 0)\n          }\n          val buildServerOptions = if (withBloop) Nil else Seq(\"--server=false\")\n          os.proc(TestUtil.cli, \"bloop\", \"exit\", \"--power\").call(cwd = root)\n          val r = os.proc(\n            TestUtil.cli,\n            \"--cli-default-scala-version\",\n            sv,\n            \"--predefined-repository\",\n            (localRepoPath / \"https\" / \"repo1.maven.org\" / \"maven2\").toNIO.toUri.toASCIIString,\n            \"--predefined-repository\",\n            (localRepoPath / \"https\" / \"scala-ci.typesafe.com\" / \"artifactory\" /\n              \"scala-integration\")\n              .toNIO.toUri.toASCIIString,\n            \"run\",\n            \"simple.sc\",\n            \"--with-compiler\",\n            \"--offline\",\n            \"--power\",\n            buildServerOptions\n          )\n            .call(cwd = root)\n          expect(r.out.trim() == sv)\n        }\n    }\n  }\n\n  test(s\"default Scala version override launcher option is respected by the SBT export\") {\n    val input     = \"printVersion.sc\"\n    val code      = \"\"\"println(s\"Default version: ${scala.util.Properties.versionNumberString}\")\"\"\"\n    val outputDir = \"sbt-project\"\n    TestInputs(os.rel / input -> code).fromRoot { root =>\n      val defaultSv       = Constants.scala213\n      val expectedMessage = s\"Default version: $defaultSv\"\n      val launcherOpt     = \"--cli-default-scala-version\"\n      val exportRes       = os.proc(\n        TestUtil.cli,\n        launcherOpt,\n        defaultSv,\n        \"export\",\n        input,\n        \"--sbt\",\n        \"--power\",\n        \"-o\",\n        outputDir\n      ).call(cwd = root)\n      expect(exportRes.exitCode == 0)\n      val sbtRes = sbtCommand(\"run\").call(cwd = root / outputDir)\n      val output = sbtRes.out.trim()\n      expect(output.contains(expectedMessage))\n    }\n  }\n\n  test(\"prog name override launcher arg allows to change the identified launcher name\") {\n    TestInputs.empty.fromRoot { root =>\n      val progName              = \"some-weird-launcher-name-some-dev-thought-of\"\n      val invalidSubCommandName = \"foo\"\n      val res = os.proc(TestUtil.cli, \"--prog-name\", progName, invalidSubCommandName)\n        .call(cwd = root, stderr = os.Pipe, check = false)\n      expect(res.exitCode == 1)\n      expect(res.err.trim().contains(progName))\n      expect(res.err.trim().contains(invalidSubCommandName))\n    }\n  }\n\n  test(s\"default Scala version override launcher option is respected by the Mill export\") {\n    val input     = \"printVersion.sc\"\n    val code      = \"\"\"println(s\"Default version: ${scala.util.Properties.versionNumberString}\")\"\"\"\n    val outputDir = millOutputDir\n    TestInputs(os.rel / input -> code).fromRoot { root =>\n      val defaultSv       = Constants.scala213\n      val expectedMessage = s\"Default version: $defaultSv\"\n      val launcherOpt     = \"--cli-default-scala-version\"\n      val exportRes       = os.proc(\n        TestUtil.cli,\n        launcherOpt,\n        defaultSv,\n        \"export\",\n        input,\n        \"--mill\",\n        \"--power\",\n        \"-o\",\n        outputDir\n      ).call(cwd = root)\n      expect(exportRes.exitCode == 0)\n      val millRes = millCommand(root, s\"$millDefaultProjectName.run\").call(cwd = root / outputDir)\n      val output  = millRes.out.trim()\n      expect(output.contains(expectedMessage))\n    }\n  }\n\n  test(\"--with-compiler option includes scala3-staging & scala3-tasty-inspector artifacts\") {\n    TestInputs(os.rel / \"example.sc\" ->\n      \"\"\"import scala.quoted.staging.Compiler\n        |import scala.tasty.inspector.TastyInspector\n        |\"\"\".stripMargin).fromRoot { root =>\n      val res = os.proc(\n        TestUtil.cli,\n        \"compile\",\n        \"example.sc\",\n        \"--with-compiler\"\n      ).call(cwd = root)\n      expect(res.exitCode == 0)\n    }\n  }\n\n  test(s\"default Scala version override launcher option is respected by the json export\") {\n    val input = \"printVersion.sc\"\n    val code  = \"\"\"println(s\"Default version: ${scala.util.Properties.versionNumberString}\")\"\"\"\n    TestInputs(os.rel / input -> code).fromRoot { root =>\n      val defaultSv   = Constants.scala213\n      val launcherOpt = \"--cli-default-scala-version\"\n      val exportRes   = os.proc(\n        TestUtil.cli,\n        launcherOpt,\n        defaultSv,\n        \"export\",\n        input,\n        \"--json\",\n        \"--power\"\n      ).call(cwd = root)\n      expect(exportRes.exitCode == 0)\n      expect(exportRes.out.trim().contains(s\"\"\"\"scalaVersion\": \"$defaultSv\"\"\"\"))\n    }\n  }\n\n  test(\"no warnings about TASTY when using the latest nightly with scripts\") {\n    val scriptName = \"script.sc\"\n    TestInputs(os.rel / \"script.sc\" -> \"println(1)\").fromRoot { root =>\n      val scala3Nightly =\n        os.proc(\n          TestUtil.cli,\n          \"-e\",\n          \"println(dotty.tools.dotc.config.Properties.versionNumberString)\",\n          \"-S\",\n          \"3.nightly\",\n          \"--with-compiler\"\n        )\n          .call(cwd = root)\n          .out.trim()\n      val res =\n        os.proc(TestUtil.cli, \"--cli-default-scala-version\", scala3Nightly, \"run\", scriptName)\n          .call(cwd = root, stderr = os.Pipe)\n      expect(res.exitCode == 0)\n      expect(!res.err.trim().contains(\"TASTY\"))\n    }\n  }\n\n  test(\"--cli-version and --cli-default-scala-version can be passed in tandem\") {\n    TestUtil.retryOnCi() {\n      TestInputs.empty.fromRoot { root =>\n        val cliVersion   = \"1.3.1\"\n        val scalaVersion = \"3.5.1-RC1-bin-20240522-e0c030c-NIGHTLY\"\n        val res          = os.proc(\n          TestUtil.cli,\n          \"--cli-version\",\n          cliVersion,\n          \"--cli-default-scala-version\",\n          scalaVersion,\n          \"version\"\n        ).call(cwd = root)\n        expect(res.out.trim().contains(cliVersion))\n        expect(res.out.trim().contains(scalaVersion))\n      }\n    }\n  }\n\n  test(\"coursier scala installation works in --offline mode\") {\n    TestInputs.empty.fromRoot { root =>\n      val localCache   = root / \"local-cache\"\n      val localBin     = root / \"local-bin\"\n      val scalaVersion = Constants.scala3NextRcAnnounced\n      withScalaRunnerWrapper(\n        root = root,\n        localBin = localBin,\n        scalaVersion = scalaVersion,\n        localCache = Some(localCache)\n      ) { launchScalaPath =>\n        val r =\n          os.proc(\n            launchScalaPath,\n            \"--offline\",\n            \"--power\",\n            \"--with-compiler\",\n            \"-e\",\n            \"println(dotty.tools.dotc.config.Properties.versionNumberString)\"\n          ).call(\n            cwd = root,\n            env = Map(\"COURSIER_CACHE\" -> localCache.toString),\n            check = false // need to clean up even on failure\n          )\n        expect(r.exitCode == 0)\n        expect(r.out.trim() == scalaVersion)\n      }\n    }\n  }\n\n  // this check is just to ensure this isn't being run for LTS RC jobs\n  // should be adjusted when a new LTS line is released\n  if (!Constants.scala3NextRc.startsWith(Constants.scala3LtsPrefix))\n    test(\"scalac help respects --cli-default-scala-version\") {\n      TestInputs.empty.fromRoot { root =>\n        val sv                          = Constants.scala3NextRc\n        val launcherVersionOverrideHelp =\n          os.proc(TestUtil.cli, \"--cli-default-scala-version\", sv, \"--scalac-help\")\n            .call(cwd = root).out.trim()\n        val standardVersionOverrideHelp =\n          os.proc(TestUtil.cli, \"--scalac-help\", \"-S\", sv)\n            .call(cwd = root).out.trim()\n        val migrationPrefix = sv.take(2) + (sv.charAt(2).asDigit + 1).toString\n        expect(launcherVersionOverrideHelp.contains(s\"$migrationPrefix-migration\"))\n        expect(launcherVersionOverrideHelp == standardVersionOverrideHelp)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SparkTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\nimport java.util.Locale\n\nimport scala.util.Properties\n\nobject SparkTestDefinitions {\n  def lightweightSparkDistribVersionOpt: Option[String] = Option(\"0.0.5\")\n\n  final class Spark(val sparkVersion: String, val scalaVersion: String) {\n    private def sbv             = scalaVersion.split('.').take(2).mkString(\".\")\n    private var toDeleteOpt     = Option.empty[os.Path]\n    lazy val sparkHome: os.Path = {\n      val url = lightweightSparkDistribVersionOpt match {\n        case Some(lightweightSparkDistribVersion) =>\n          s\"https://github.com/VirtusLab/lightweight-spark-distrib/releases/download/v$lightweightSparkDistribVersion/spark-$sparkVersion-bin-hadoop2.7-scala$sbv.tgz\"\n        case None =>\n          // original URL (too heavyweight, often fails / times out…)\n          s\"https://archive.apache.org/dist/spark/spark-$sparkVersion/spark-$sparkVersion-bin-hadoop2.7.tgz\"\n      }\n      val baseDir =\n        os.Path(os.proc(TestUtil.cs, \"get\", \"--archive\", url).call().out.trim(), os.pwd)\n      val home = os.list(baseDir) match {\n        case Seq(dir) if os.isDir(dir) => dir\n        case _                         => baseDir\n      }\n      if (lightweightSparkDistribVersionOpt.nonEmpty) {\n        val copy = os.temp.dir(prefix = home.last) / \"home\"\n        toDeleteOpt = Some(copy)\n        System.err.println(s\"Copying $home over to $copy\")\n        os.copy(home, copy)\n        val fetchJarsScript0       = copy / \"fetch-jars.sh\"\n        val cmd: Seq[os.Shellable] =\n          if (Properties.isWin) Seq(\"\"\"C:\\Program Files\\Git\\bin\\bash.EXE\"\"\", fetchJarsScript0)\n          else Seq(fetchJarsScript0)\n\n        System.err.println(s\"Running $cmd\")\n        os.proc(cmd).call(stdin = os.Inherit, stdout = os.Inherit)\n        System.err.println(s\"Spark home $copy ready\")\n        copy\n      }\n      else\n        home\n    }\n    def cleanUp(): Unit =\n      toDeleteOpt.foreach(os.remove.all(_))\n  }\n\n}\n\nabstract class SparkTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  import SparkTestDefinitions.*\n\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n\n  protected def defaultMaster                                                     = \"local[4]\"\n  protected def simpleJobInputs(spark: Spark, withTestScope: Boolean): TestInputs = TestInputs(\n    os.rel / (if (withTestScope) \"SparkJob.test.scala\" else \"SparkJob.scala\") ->\n      s\"\"\"//> using dep org.apache.spark::spark-sql:${spark.sparkVersion}\n         |//> using dep com.chuusai::shapeless:2.3.10\n         |//> using dep com.lihaoyi::pprint:0.7.3\n         |\n         |import org.apache.spark._\n         |import org.apache.spark.sql._\n         |\n         |object SparkJob {\n         |  def main(args: Array[String]): Unit = {\n         |    val spark = SparkSession.builder()\n         |      .appName(\"Test job\")\n         |      .getOrCreate()\n         |    import spark.implicits._\n         |    def sc    = spark.sparkContext\n         |    val accum = sc.longAccumulator\n         |    sc.parallelize(1 to 10).foreach { x =>\n         |      import shapeless._\n         |      val l = x :: HNil\n         |      accum.add(l.head)\n         |    }\n         |    pprint.err.log(accum.value)\n         |    println(\"Result: \" + accum.value)\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  private def maybeSetupWinutils(hadoopHome: os.Path): Unit =\n    if (Properties.isWin) {\n      val bin = hadoopHome / \"bin\"\n      os.makeDir.all(bin)\n      val res = os.proc(\n        TestUtil.cs,\n        \"get\",\n        \"--archive\",\n        \"https://github.com/steveloughran/winutils/releases/download/tag_2017-08-29-hadoop-2.8.1-native/hadoop-2.8.1.zip\"\n      )\n        .call(cwd = hadoopHome)\n      val dataDir     = os.Path(res.out.trim())\n      val binStuffDir = os.list(dataDir)\n        .filter(os.isDir(_))\n        .filter(_.last.startsWith(\"hadoop-\"))\n        .headOption\n        .getOrElse {\n          sys.error(s\"No hadoop-* directory found under $dataDir\")\n        }\n      for (elem <- os.list(binStuffDir))\n        os.copy.into(elem, bin)\n    }\n\n  private def maybeHadoopHomeForWinutils(hadoopHome: os.Path): Map[String, String] =\n    if (Properties.isWin) {\n      // FIXME Maybe Scala CLI should handle that itself, when on Windows,\n      // for Spark >= 3.3.0 (maybe 3.3.0, > 3.0 for sure)\n      maybeSetupWinutils(hadoopHome)\n      val (pathVarName, currentPath) =\n        sys.env.find(_._1.toLowerCase(Locale.ROOT) == \"path\").getOrElse((\"PATH\", \"\"))\n      Map(\n        pathVarName   -> s\"${hadoopHome / \"bin\"}${File.pathSeparator}$currentPath\",\n        \"HADOOP_HOME\" -> hadoopHome.toString\n      )\n    }\n    else\n      Map.empty[String, String]\n\n  def simpleRunStandaloneSparkJobTest(\n    scalaVersion: String,\n    sparkVersion: String,\n    needsWinUtils: Boolean = false,\n    withTestScope: Boolean\n  ): Unit =\n    simpleJobInputs(new Spark(sparkVersion, scalaVersion), withTestScope).fromRoot { root =>\n      val extraEnv =\n        if (needsWinUtils) maybeHadoopHomeForWinutils(root / \"hadoop-home\")\n        else Map.empty[String, String]\n      val scopeOptions = if (withTestScope) Seq(\"--test\") else Nil\n      val res          = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"run\",\n        extraOptions,\n        \"--spark-standalone\",\n        \".\",\n        scopeOptions\n      )\n        .call(cwd = root, env = extraEnv)\n\n      val expectedOutput = \"Result: 55\"\n\n      val output = res.out.trim().linesIterator.toVector\n\n      expect(output.contains(expectedOutput))\n    }\n\n  for {\n    withTestScope <- Seq(true, false)\n    scopeDescription = if (withTestScope) \"test scope\" else \"main scope\"\n    if !Properties.isMac // TODO: https://github.com/VirtusLab/scala-cli/issues/3841\n  } test(s\"run spark 3.3 standalone ($scopeDescription)\") {\n    simpleRunStandaloneSparkJobTest(\n      actualScalaVersion,\n      \"3.3.0\",\n      needsWinUtils = true,\n      withTestScope = withTestScope\n    )\n  }\n\n  if (!Properties.isMac) // TODO: https://github.com/VirtusLab/scala-cli/issues/3841\n    test(\"run spark spark-submit args\") {\n      val jobName = \"the test spark job\"\n      val inputs  = TestInputs(\n        os.rel / \"SparkJob.scala\" ->\n          s\"\"\"//> using dep org.apache.spark::spark-sql:3.3.0\n             |\n             |import org.apache.spark._\n             |import org.apache.spark.sql._\n             |\n             |object SparkJob {\n             |  def main(args: Array[String]): Unit = {\n             |    val spark = SparkSession.builder().getOrCreate()\n             |    val name = spark.conf.get(\"spark.app.name\")\n             |    assert(name == \"$jobName\")\n             |    import spark.implicits._\n             |    def sc    = spark.sparkContext\n             |    val accum = sc.longAccumulator\n             |    sc.parallelize(1 to 10).foreach(x => accum.add(x))\n             |    println(\"Result: \" + accum.value)\n             |  }\n             |}\n             |\"\"\".stripMargin\n      )\n      inputs.fromRoot { root =>\n        val extraEnv = maybeHadoopHomeForWinutils(root / \"hadoop-home\")\n        val res      = os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"run\",\n          extraOptions,\n          \"--spark-standalone\",\n          \".\",\n          \"--submit-arg\",\n          \"--name\",\n          \"--submit-arg\",\n          jobName\n        )\n          .call(cwd = root, env = extraEnv)\n\n        val expectedOutput = \"Result: 55\"\n\n        val output = res.out.trim().linesIterator.toVector\n\n        expect(output.contains(expectedOutput))\n      }\n    }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SparkTests212.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\nimport java.util.Locale\n\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Properties\n\nclass SparkTests212 extends SparkTestDefinitions with Test212 {\n  import SparkTestDefinitions.*\n\n  private val spark30 = new Spark(\n    \"3.0.3\",\n    // The spark distribution actually ships with Scala 2.12.10, but we run into #1092 if we use it here\n    \"2.12.15\"\n  )\n\n  private val spark24 = new Spark(\n    \"2.4.2\",\n    // The spark distribution actually ships with Scala 2.12.8, but we run into #1092 if we use it here\n    \"2.12.15\"\n  )\n\n  override def afterAll(): Unit = {\n    spark30.cleanUp()\n    spark24.cleanUp()\n  }\n\n  def simplePackageSparkJobTest(spark: Spark): Unit =\n    simpleJobInputs(spark, withTestScope = false).fromRoot { root =>\n      val dest = os.rel / \"SparkJob.jar\"\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        extraOptions,\n        \"--spark\",\n        \"--jvm\",\n        \"8\",\n        \".\",\n        \"-o\",\n        dest\n      )\n        .call(cwd = root)\n\n      val java8Home =\n        os.Path(os.proc(TestUtil.cs, \"java-home\", \"--jvm\", \"zulu:8\").call().out.trim(), os.pwd)\n\n      val ext = if (Properties.isWin) \".cmd\" else \"\"\n      val res =\n        os.proc(\n          spark.sparkHome / \"bin\" / s\"spark-submit$ext\",\n          \"--master\",\n          defaultMaster,\n          dest\n        ).call(\n          cwd = root,\n          env = Map(\n            \"JAVA_HOME\" -> java8Home.toString,\n            \"PATH\" -> ((java8Home / \"bin\").toString + File.pathSeparator + System.getenv(\"PATH\"))\n          )\n        )\n\n      val expectedOutput = \"Result: 55\"\n\n      expect(res.out.trim() == expectedOutput)\n    }\n\n  private def addToPath(dir: os.Path): Map[String, String] = {\n    // On Windows, trying to preserve the case of the PATH entry\n    def default             = \"PATH\" -> Option(System.getenv(\"PATH\")).getOrElse(\"\")\n    val (key, currentValue) =\n      if (Properties.isWin)\n        System.getenv().asScala.find(_._1.toLowerCase(Locale.ROOT) == \"path\").getOrElse(default)\n      else\n        default\n\n    Map(key -> s\"$dir${File.pathSeparator}$currentValue\")\n  }\n\n  def simpleRunSparkJobTest(\n    spark: Spark,\n    usePath: Boolean = false,\n    withTestScope: Boolean = false\n  ): Unit =\n    simpleJobInputs(spark, withTestScope).fromRoot { root =>\n      val env =\n        if (usePath) addToPath(spark.sparkHome / \"bin\")\n        else Map(\"SPARK_HOME\" -> spark.sparkHome.toString)\n      val scopeOptions = if (withTestScope) Seq(\"--test\") else Nil\n      val res          = os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"run\",\n        extraOptions,\n        \"--spark\",\n        \"--jvm\",\n        \"8\",\n        \".\",\n        scopeOptions\n      )\n        .call(cwd = root, env = env)\n\n      val expectedOutput = \"Result: 55\"\n\n      val output = res.out.trim().linesIterator.toVector\n\n      expect(output.contains(expectedOutput))\n    }\n\n  if (!Properties.isMac) { // TODO: https://github.com/VirtusLab/scala-cli/issues/3841\n    test(\"package spark 2.4\") {\n      simplePackageSparkJobTest(spark24)\n    }\n\n    test(\"package spark 3.0\") {\n      simplePackageSparkJobTest(spark30)\n    }\n  }\n\n  for {\n    withTestScope <- Seq(true, false)\n    scopeDescription = if (withTestScope) \"test scope\" else \"main scope\"\n    if !Properties.isMac // TODO: https://github.com/VirtusLab/scala-cli/issues/3841\n  } {\n    test(s\"run spark 2.4 ($scopeDescription)\") {\n      simpleRunSparkJobTest(spark24, withTestScope = withTestScope)\n    }\n\n    test(s\"run spark 2.4 standalone ($scopeDescription)\") {\n      simpleRunStandaloneSparkJobTest(\n        spark24.scalaVersion,\n        spark24.sparkVersion,\n        withTestScope = withTestScope\n      )\n    }\n\n    test(s\"run spark 3.0 standalone ($scopeDescription)\") {\n      simpleRunStandaloneSparkJobTest(\n        spark30.scalaVersion,\n        spark30.sparkVersion,\n        withTestScope = withTestScope\n      )\n    }\n\n    for {\n      usePath <- Seq(false, true)\n      pathDescription = if (usePath) \"via PATH \" else \"\"\n    }\n      test(s\"run spark 3.0 $pathDescription($scopeDescription)\") {\n        simpleRunSparkJobTest(spark30, usePath = usePath, withTestScope = withTestScope)\n      }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/SparkTests213.scala",
    "content": "package scala.cli.integration\n\nclass SparkTests213 extends SparkTestDefinitions with Test213\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestBspClient.scala",
    "content": "package scala.cli.integration\n\nimport ch.epfl.scala.bsp4j as b\nimport org.eclipse.lsp4j.jsonrpc\n\nimport java.io.{InputStream, OutputStream}\nimport java.util.concurrent.{CompletableFuture, ExecutorService}\n\nimport scala.cli.integration.bsp.WrappedSourcesResult\nimport scala.collection.mutable\nimport scala.concurrent.{ExecutionContext, Future, Promise}\nimport scala.util.{Failure, Success}\n\nclass TestBspClient extends b.BuildClient {\n  private val lock                             = new Object\n  private val messages0                        = new mutable.ListBuffer[Object]\n  private def addMessage(params: Object): Unit =\n    lock.synchronized {\n      messages0.append(params)\n    }\n\n  private val didChangePromises = new mutable.ListBuffer[Promise[b.DidChangeBuildTarget]]\n  private val didChangeLock     = new Object\n  def buildTargetDidChange(): Future[b.DidChangeBuildTarget] = {\n    val p = Promise[b.DidChangeBuildTarget]()\n    didChangeLock.synchronized {\n      didChangePromises.append(p)\n    }\n    p.future\n  }\n\n  def logMessages(): Seq[b.LogMessageParams] =\n    lock.synchronized {\n      messages0.reverseIterator.collect {\n        case p: b.LogMessageParams => p\n      }.toSeq\n    }\n  def diagnostics(): Seq[b.PublishDiagnosticsParams] =\n    lock.synchronized {\n      messages0.reverseIterator.collect {\n        case p: b.PublishDiagnosticsParams => p\n      }.toSeq\n    }\n\n  def latestDiagnostics(): Option[b.PublishDiagnosticsParams] =\n    lock.synchronized {\n      messages0.reverseIterator.collectFirst {\n        case p: b.PublishDiagnosticsParams => p\n      }\n    }\n\n  case class TaskStartFinishDiagnostics(\n    startParams: b.TaskStartParams,\n    finishParams: b.TaskFinishParams,\n    taskMessages: Seq[b.PublishDiagnosticsParams]\n  )\n\n  def getTaskDiagnostics(): Seq[TaskStartFinishDiagnostics] =\n    lock.synchronized {\n      messages0.foldRight(\n        Option.empty[TaskStartFinishDiagnostics],\n        List.empty[TaskStartFinishDiagnostics]\n      ) {\n        case (msg: b.TaskFinishParams, (None, acc)) =>\n          (Some(TaskStartFinishDiagnostics(null, msg, Nil)), acc)\n        case (msg: b.TaskStartParams, (Some(tsfm), acc))\n            if tsfm.finishParams.getTaskId == msg.getTaskId =>\n          (None, tsfm.copy(startParams = msg) :: acc)\n        case (msg: b.PublishDiagnosticsParams, (Some(tsfm), acc)) =>\n          (Some(tsfm.copy(taskMessages = msg +: tsfm.taskMessages)), acc)\n        case (_, accTuple) => accTuple\n      }._2\n    }\n\n  def onBuildLogMessage(params: b.LogMessageParams): Unit =\n    addMessage(params)\n  def onBuildShowMessage(params: b.ShowMessageParams): Unit =\n    addMessage(params)\n  def onBuildPublishDiagnostics(params: b.PublishDiagnosticsParams): Unit =\n    addMessage(params)\n\n  def onBuildTargetDidChange(params: b.DidChangeBuildTarget): Unit = {\n    val promises =\n      didChangeLock.synchronized {\n        val promises0 = didChangePromises.toList\n        didChangePromises.clear()\n        promises0\n      }\n    for (p <- promises)\n      p.success(params)\n    addMessage(params)\n  }\n\n  def onBuildTaskStart(params: b.TaskStartParams): Unit =\n    addMessage(params)\n  def onBuildTaskProgress(params: b.TaskProgressParams): Unit =\n    addMessage(params)\n  def onBuildTaskFinish(params: b.TaskFinishParams): Unit =\n    addMessage(params)\n}\n\nobject TestBspClient {\n\n  trait WrappedSourcesBuildServer {\n    @org.eclipse.lsp4j.jsonrpc.services.JsonRequest(\"buildTarget/wrappedSources\")\n    def buildTargetWrappedSources(params: b.SourcesParams)\n      : CompletableFuture[WrappedSourcesResult]\n  }\n\n  private trait BuildServer extends b.BuildServer with b.ScalaBuildServer with b.JavaBuildServer\n      with b.JvmBuildServer with WrappedSourcesBuildServer\n\n  def connect(\n    in: InputStream,\n    out: OutputStream,\n    es: ExecutorService\n  ): (\n    TestBspClient,\n    b.BuildServer & b.ScalaBuildServer & b.JavaBuildServer & b.JvmBuildServer &\n      WrappedSourcesBuildServer,\n    Future[Unit]\n  ) = {\n\n    val localClient = new TestBspClient\n\n    val launcher = new jsonrpc.Launcher.Builder[BuildServer]()\n      .setExecutorService(es)\n      .setInput(in)\n      .setOutput(out)\n      .setRemoteInterface(classOf[BuildServer])\n      .setLocalService(localClient)\n      .create()\n    val remoteServer = launcher.getRemoteProxy\n\n    val f  = launcher.startListening()\n    val f0 = naiveJavaFutureToScalaFuture(f).map(_ => ())(ExecutionContext.fromExecutor(es))\n\n    (localClient, remoteServer, f0)\n  }\n\n  // from https://github.com/com-lihaoyi/Ammonite/blob/7eb58c58ec8c252dc5bd1591b041fcae01cccf90/amm/interp/src/main/scala/ammonite/interp/script/AmmoniteBuildServer.scala#L550-L565\n  private def naiveJavaFutureToScalaFuture[T](\n    f: java.util.concurrent.Future[T]\n  ): Future[T] = {\n    val p = Promise[T]()\n    val t = new Thread {\n      setDaemon(true)\n      setName(\"bsp-wait-for-exit\")\n      override def run(): Unit =\n        p.complete {\n          try Success(f.get())\n          catch { case t: Throwable => Failure(t) }\n        }\n    }\n    t.start()\n    p.future\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestNativeImageOnScala3.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass TestNativeImageOnScala3 extends ScalaCliSuite {\n  override def group: ScalaCliSuite.TestGroup = ScalaCliSuite.TestGroup.First\n\n  def runTest(args: String*)(expectedLines: String*)(code: String): Unit = {\n    val dest =\n      if (Properties.isWin) \"testApp.exe\"\n      else \"testApp\"\n\n    val inputs = TestInputs(os.rel / \"Hello.scala\" -> code)\n    inputs.fromRoot { root =>\n      os.proc(\n        TestUtil.cli,\n        \"--power\",\n        \"package\",\n        \".\",\n        \"--native-image\",\n        \"-o\",\n        dest,\n        \"--\",\n        \"--no-fallback\"\n      ).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      expect(os.isFile(root / dest))\n\n      // FIXME Check that dest is indeed a binary?\n\n      val res         = os.proc(root / dest, args).call(cwd = root)\n      val outputLines = res.out.trim().linesIterator.to(Seq)\n      expect(expectedLines == outputLines)\n    }\n  }\n\n  for {\n    scalaVersion <- TestUtil.legacyScalaVersionsOnePerMinor.sorted ++ Seq(\n      Constants.scala3Lts,\n      Constants.scala3Next,\n      Constants.scala3NextRc\n    )\n  }\n    test(s\"lazy vals ($scalaVersion)\") {\n      runTest(\"1\")(\"2\") {\n        s\"\"\"//> using scala $scalaVersion\n           |class A(a: String) { lazy val b = a.toInt + 1 }\n           |@main def add1(i: String) = println(A(i).b)\n           |\"\"\".stripMargin\n      }\n    }\n\n  test(\"lazy vals and enums with default scala version\") {\n    runTest(\"1\")(\"2\", \"A\") {\n      \"\"\"class A(a: String) { lazy val b = a.toInt + 1 }\n        |enum Ala:\n        |  case A\n        |  case B\n        |@main def add1(i: String) =\n        | println(A(i).b)\n        | println(Ala.valueOf(\"A\"))\n        |\"\"\".stripMargin\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestScalaVersionArgs.scala",
    "content": "package scala.cli.integration\n\nimport scala.annotation.unused\n\ntrait TestScalaVersionArgs extends ScalaCliSuite { this: TestScalaVersion =>\n  override def group: ScalaCliSuite.TestGroup =\n    if (actualScalaVersion.startsWith(\"2.12.\")) ScalaCliSuite.TestGroup.Third\n    else if (actualScalaVersion.startsWith(\"2.13.\")) ScalaCliSuite.TestGroup.Second\n    else if (actualScalaVersion.startsWith(Constants.scala3LtsPrefix))\n      ScalaCliSuite.TestGroup.Fourth\n    else if (actualScalaVersion.startsWith(Constants.scala3NextRc))\n      ScalaCliSuite.TestGroup.Fifth\n    else ScalaCliSuite.TestGroup.First\n\n  lazy val scalaVersionArgs: Seq[String] = scalaVersionOpt match {\n    case None     => Nil\n    case Some(sv) => Seq(\"--scala\", sv)\n  }\n\n  lazy val actualScalaVersion: String = scalaVersionOpt.getOrElse(Constants.defaultScala)\n\n  def isScala38OrNewer: Boolean =\n    Constants.scala38Versions\n      .map(_.coursierVersion)\n      .exists(_ <= actualScalaVersion.coursierVersion)\n\n  def retrieveScalaVersionCode: String =\n    if actualScalaVersion.startsWith(\"2.\") || isScala38OrNewer then\n      \"scala.util.Properties.versionNumberString\"\n    else \"dotty.tools.dotc.config.Properties.simpleVersionString\"\n}\n\nsealed trait TestScalaVersion { this: TestScalaVersionArgs =>\n  def scalaVersionOpt: Option[String]\n}\ntrait Test213 extends TestScalaVersion { this: TestScalaVersionArgs =>\n  override lazy val scalaVersionOpt: Option[String] = Some(Constants.scala213)\n}\ntrait Test212 extends TestScalaVersion { this: TestScalaVersionArgs =>\n  override lazy val scalaVersionOpt: Option[String] = Some(Constants.scala212)\n}\ntrait Test3Lts extends TestScalaVersion { this: TestScalaVersionArgs =>\n  override lazy val scalaVersionOpt: Option[String] = Some(Constants.scala3Lts)\n}\ntrait Test3NextRc extends TestScalaVersion { this: TestScalaVersionArgs =>\n  override lazy val scalaVersionOpt: Option[String] = Some(Constants.scala3NextRc)\n}\n@unused // TestDefault should normally be mixed in instead\ntrait Test3Next extends TestScalaVersion { this: TestScalaVersionArgs =>\n  override lazy val scalaVersionOpt: Option[String] = Some(Constants.scala3Next)\n}\ntrait TestDefault extends TestScalaVersion { this: TestScalaVersionArgs =>\n  // this effectively should make the launcher default to 3.next\n  override lazy val scalaVersionOpt: Option[String] = None\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestTestDefinitions.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.annotation.tailrec\nimport scala.cli.integration.Constants.munitVersion\nimport scala.cli.integration.TestUtil.{ProcOps, StringOps}\nimport scala.concurrent.duration.DurationInt\nimport scala.util.Properties\n\nabstract class TestTestDefinitions extends ScalaCliSuite with TestScalaVersionArgs {\n  this: TestScalaVersion =>\n  protected lazy val extraOptions: Seq[String] = scalaVersionArgs ++ TestUtil.extraOptions\n  private val utestVersion                     = \"0.8.3\"\n  private val zioTestVersion                   = \"2.1.17\"\n\n  def successfulTestInputs(directivesString: String =\n    s\"//> using dep org.scalameta::munit::$munitVersion\"): TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"$directivesString\n         |\n         |class MyTests extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 4)\n         |    println(\"Hello from \" + \"tests\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val failingTestInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n         |\n         |class MyTests extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 5, \"Hello from \" + \"tests\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val successfulUtestInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n         |import utest._\n         |\n         |object MyTests extends TestSuite {\n         |  val tests = Tests {\n         |    test(\"foo\") {\n         |      assert(2 + 2 == 4)\n         |      println(\"Hello from \" + \"tests\")\n         |    }\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val successfulUtestJsInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n         |import utest._\n         |import scala.scalajs.js\n         |\n         |object MyTests extends TestSuite {\n         |  val tests = Tests {\n         |    test(\"foo\") {\n         |      assert(2 + 2 == 4)\n         |      val console = js.Dynamic.global.console\n         |      console.log(\"Hello from \" + \"tests\")\n         |    }\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val successfulUtestNativeInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n         |import utest._\n         |import scala.scalanative.libc._\n         |import scala.scalanative.unsafe._\n         |\n         |object MyTests extends TestSuite {\n         |  val tests = Tests {\n         |    test(\"foo\") {\n         |      assert(2 + 2 == 4)\n         |      Zone { ${if actualScalaVersion.startsWith(\"3\") then \"\" else \"implicit z =>\"}\n         |       val io = StdioHelpers(stdio)\n         |       io.printf(c\"%s %s\", c\"Hello from\", c\"tests\")\n         |      }\n         |    }\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val successfulScalaCheckFromCatsNativeInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      \"\"\"//> using scala 2.13\n        |//> using platform native\n        |//> using nativeVersion 0.4.17\n        |//> using dep org.typelevel::cats-kernel-laws::2.8.0\n        |\n        |import org.scalacheck._\n        |import Prop.forAll\n        |\n        |class TestSpec extends Properties(\"spec\") {\n        |  property(\"startsWith\") = forAll { (a: String, b: String) =>\n        |    (a+b).startsWith(a)\n        |  }\n        |  println(\"Hello from \" + \"tests\")\n        |}\"\"\".stripMargin\n  )\n\n  val successfulJunitInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      \"\"\"//> using dep com.novocode:junit-interface:0.11\n        |import org.junit.Test\n        |\n        |class MyTests {\n        |\n        |  @Test\n        |  def foo(): Unit = {\n        |    assert(2 + 2 == 4)\n        |    println(\"Hello from \" + \"tests\")\n        |  }\n        |}\n        |\"\"\".stripMargin\n  )\n\n  val severalTestsInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n         |\n         |class MyTests extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 4)\n         |    println(\"Hello from \" + \"tests1\")\n         |  }\n         |}\n         |\"\"\".stripMargin,\n    os.rel / \"OtherTests.test.scala\" ->\n      s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n         |\n         |class OtherTests extends munit.FunSuite {\n         |  test(\"bar\") {\n         |    assert(1 + 1 == 2)\n         |    println(\"Hello from \" + \"tests2\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val successfulWeaverInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      \"\"\"//> using deps com.disneystreaming::weaver-cats:0.8.2\n        |import weaver._\n        |import cats.effect.IO\n        |\n        |object MyTests extends SimpleIOSuite {\n        |  test(\"bar\") {\n        |    IO.println(\"Hello from \" + \"tests\").map(_ => expect(1 + 1 == 2))\n        |  }\n        |}\n        |\"\"\".stripMargin\n  )\n\n  val successfulESModuleTestInputs: TestInputs = TestInputs(\n    os.rel / \"MyTests.test.scala\" ->\n      s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n         |//> using jsModuleKind esmodule\n         |import scala.scalajs.js\n         |import scala.scalajs.js.annotation._\n         |\n         |@js.native\n         |@JSImport(\"console\", JSImport.Namespace)\n         |object console extends js.Object {\n         |  def log(msg: js.Any): Unit = js.native\n         |}\n         |\n         |class MyTests extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 4)\n         |    console.log(\"Hello from \" + \"tests\")\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  val successfulMarkdownTestInputs: TestInputs = TestInputs(\n    os.rel / \"Test.md\" ->\n      s\"\"\"# Example Markdown File\n         |This is an example for how Scala test code can be run from a Markdown file.\n         |\n         |## Example Snippet\n         |```scala test\n         |//> using dep org.scalameta::munit:$munitVersion\n         |\n         |class Test extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 4)\n         |    println(\"Hello from tests\")\n         |  }\n         |}\n         |```\n         |\n         |\"\"\".stripMargin\n  )\n\n  val failingMarkdownTestInputs: TestInputs = TestInputs(\n    os.rel / \"Test.md\" ->\n      s\"\"\"# Example Markdown File\n         |This is an example for how Scala test code can be run from a Markdown file.\n         |\n         |## Example Snippet\n         |```scala test\n         |//> using dep org.scalameta::munit:$munitVersion\n         |\n         |class Test extends munit.FunSuite {\n         |  test(\"foo\") {\n         |    assert(2 + 2 == 5, \"Hello from tests\")\n         |  }\n         |}\n         |```\n         |\n         |\"\"\".stripMargin\n  )\n\n  test(\"successful test\") {\n    successfulTestInputs().fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root).out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  if (!Properties.isMac || !TestUtil.isCI)\n    test(\"--watching with --watch re-runs tests on external file change\") {\n      val sourceFile   = os.rel / \"MyTests.test.scala\"\n      val externalFile = os.rel / \"data\" / \"input.txt\"\n      TestInputs(\n        sourceFile ->\n          s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n             |\n             |class MyTests extends munit.FunSuite {\n             |  test(\"watched input\") {\n             |    val content = scala.io.Source.fromFile(\"data/input.txt\").mkString.trim\n             |    println(content)\n             |    assert(content.nonEmpty)\n             |  }\n             |}\n             |\"\"\".stripMargin,\n        externalFile -> \"Hello\"\n      ).fromRoot { root =>\n        TestUtil.withProcessWatching(\n          proc = os.proc(\n            TestUtil.cli,\n            \"--power\",\n            \"test\",\n            \".\",\n            \"--watch\",\n            \"--watching\",\n            \"data\",\n            extraOptions\n          )\n            .spawn(cwd = root, mergeErrIntoOut = true),\n          timeout = 120.seconds\n        ) { (proc, timeout, ec) =>\n          implicit val ec0  = ec\n          val initialOutput = proc.readOutputUntilWatchingMessage(timeout)\n          expect(initialOutput.exists(_.contains(\"Hello\")))\n\n          Thread.sleep(2000L)\n          os.write.over(root / externalFile, \"World\")\n\n          val rerunOutput = proc.readOutputUntilWatchingMessage(timeout)\n          expect(rerunOutput.exists(_.contains(\"World\")))\n        }\n      }\n    }\n\n  if (actualScalaVersion.startsWith(\"2\"))\n    test(\"successful test JVM 8\") {\n      successfulTestInputs().fromRoot { root =>\n        val output =\n          os.proc(TestUtil.cli, \"test\", \"--jvm\", \"8\", extraOptions, \".\").call(cwd = root).out.text()\n        expect(output.contains(\"Hello from tests\"))\n      }\n    }\n\n  test(\"successful test JS\") {\n    successfulTestInputs().fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--js\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"successful test esmodule import JS\") {\n    successfulESModuleTestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--js\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"run only one test from munit\") {\n    val inputs: TestInputs = TestInputs(\n      os.rel / \"MyTests.scala\" ->\n        s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n           |package test\n           |\n           |class MyTests extends munit.FunSuite {\n           |  test(\"foo\") {\n           |    assert(2 + 2 == 5, \"foo\")\n           |  }\n           |  test(\"bar\") {\n           |    assert(2 + 3 == 5)\n           |    println(\"Hello from bar\")\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(\n          TestUtil.cli,\n          \"test\",\n          \".\",\n          extraOptions,\n          \"--test-only\",\n          \"test.MyTests\",\n          \"--\",\n          \"*bar*\"\n        ).call(cwd = root)\n      val output = res.out.text()\n      expect(res.exitCode == 0)\n      expect(output.contains(\"Hello from bar\"))\n    }\n  }\n  test(\"run only one test from utest\") {\n    val inputs: TestInputs = TestInputs(\n      os.rel / \"FooTests.test.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n           |package tests.foo\n           |import utest._\n           |\n           |object FooTests extends TestSuite {\n           |  val tests = Tests {\n           |    test(\"foo\") {\n           |      assert(2 + 2 == 5)\n           |    }\n           |  }\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"BarTests.test.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n           |package tests.bar\n           |import utest._\n           |\n           |object BarTests extends TestSuite {\n           |  val tests = Tests {\n           |    test(\"bar\") {\n           |      assert(2 + 2 == 4)\n           |      println(\"Hello from bar\")\n           |    }\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res =\n        os.proc(\n          TestUtil.cli,\n          \"test\",\n          \".\",\n          extraOptions,\n          \"--test-only\",\n          \"tests.b*\"\n        ).call(cwd = root)\n      val output = res.out.text()\n      expect(res.exitCode == 0)\n      expect(output.contains(\"Hello from bar\"))\n    }\n  }\n  def successfulNativeTest(): Unit =\n    successfulTestInputs().fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--native\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n\n  test(\"successful test native\") {\n    TestUtil.retryOnCi()(successfulNativeTest())\n  }\n\n  test(\"failing test\") {\n    failingTestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\")\n        .call(cwd = root, check = false)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"failing test JS\") {\n    failingTestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--js\")\n        .call(cwd = root, check = false)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  def failingNativeTest(): Unit =\n    failingTestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--native\")\n        .call(cwd = root, check = false)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n\n  test(\"failing test native\") {\n    TestUtil.retryOnCi()(failingNativeTest())\n  }\n\n  test(\"failing test return code\") {\n    failingTestInputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit,\n        check = false\n      )\n      expect(res.exitCode == 1)\n    }\n  }\n\n  test(\"failing test return code when compiling error\") {\n    val inputs = TestInputs(\n      os.rel / \"MyTests.test.scala\" ->\n        s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n           |\n           |class SomeTest extends munit.FunSuite {\n           |  test(\"failig\") {\n           |   val s: String = 1\n           |   assert(true == true)\n           | }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root, check = false)\n      expect(res.exitCode == 1)\n    }\n  }\n\n  test(\"utest\") {\n    successfulUtestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root).out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"utest JS\") {\n    TestUtil.retryOnCi() {\n      successfulUtestJsInputs.fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--js\")\n          .call(cwd = root)\n          .out.text()\n        expect(output.contains(\"Hello from tests\"))\n      }\n    }\n  }\n\n  def utestNative(): Unit =\n    successfulUtestNativeInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--native\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n\n  test(\"scalacheck from cats\") {\n    successfulScalaCheckFromCatsNativeInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--native\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"utest native\") {\n    TestUtil.retryOnCi()(utestNative())\n  }\n\n  test(\"junit\") {\n    successfulJunitInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root).out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"several tests\") {\n    severalTestsInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root).out.text()\n      expect(output.contains(\"Hello from tests1\"))\n      expect(output.contains(\"Hello from tests2\"))\n    }\n  }\n\n  test(\"weaver\") {\n    successfulWeaverInputs.fromRoot { root =>\n      val output =\n        os.proc(\n          TestUtil.cli,\n          \"test\",\n          extraOptions,\n          \".\"\n        ).call(cwd = root).out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  val platforms: Seq[(String, Seq[String])] = {\n    val maybeJs     = Seq(\"JS\" -> Seq(\"--js\"))\n    val maybeNative =\n      if (actualScalaVersion.startsWith(\"2.\"))\n        Seq(\"native\" -> Seq(\"--native\"))\n      else\n        Nil\n    Seq(\"JVM\" -> Nil) ++ maybeJs ++ maybeNative\n  }\n\n  for ((platformName, platformArgs) <- platforms)\n    test(s\"test framework arguments $platformName\") {\n      TestUtil.retryOnCi() {\n        val inputs = TestInputs(\n          os.rel / \"MyTests.test.scala\" ->\n            \"\"\"//> using dep org.scalatest::scalatest::3.2.18\n              |import org.scalatest._\n              |import org.scalatest.flatspec._\n              |import org.scalatest.matchers._\n              |\n              |class Tests extends AnyFlatSpec with should.Matchers {\n              |  \"A thing\" should \"thing\" in {\n              |    assert(2 + 2 == 4)\n              |  }\n              |}\n              |\"\"\".stripMargin\n        )\n        inputs.fromRoot { root =>\n          val scalaTestExtraArgs =\n            if (platformName == \"native\")\n              // FIXME: revert to using default Scala Native version when scalatest supports 0.5.x\n              Seq(\"--native-version\", \"0.4.17\")\n            else Nil\n          val baseRes =\n            os.proc(TestUtil.cli, \"test\", extraOptions, platformArgs, scalaTestExtraArgs, \".\")\n              .call(cwd = root, check = false)\n          if (baseRes.exitCode != 0) {\n            println(baseRes.out.text())\n            fail(\"scala-cli test falied\", clues(baseRes.exitCode))\n          }\n          val baseOutput = baseRes.out.text()\n          expect(baseOutput.contains(\"A thing\"))\n          expect(baseOutput.contains(\"should thing\"))\n          val baseShouldThingLine = baseRes.out\n            .lines()\n            .find(_.contains(\"should thing\"))\n            .getOrElse(???)\n          expect(!baseShouldThingLine.contains(\"millisecond\"))\n\n          val res = os.proc(\n            TestUtil.cli,\n            \"test\",\n            extraOptions,\n            platformArgs,\n            scalaTestExtraArgs,\n            \".\",\n            \"--\",\n            \"-oD\"\n          )\n            .call(cwd = root)\n          val output = res.out.text()\n          expect(output.contains(\"A thing\"))\n          expect(output.contains(\"should thing\"))\n          val shouldThingLine = res.out.lines().find(_.contains(\"should thing\")).getOrElse(???)\n          expect(shouldThingLine.contains(\"millisecond\"))\n        }\n      }\n    }\n\n  for ((platformName, platformArgs) <- platforms)\n    test(s\"custom test framework $platformName\") {\n      TestUtil.retryOnCi(maxAttempts = if (platformName.contains(\"native\")) 3 else 1) {\n        val inputs = TestInputs(\n          os.rel / \"MyTests.test.scala\" ->\n            s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n               |\n               |package mytests\n               |import utest._\n               |\n               |object MyTests extends TestSuite {\n               |  val tests = Tests {\n               |    test(\"foo\") {\n               |      assert(2 + 2 == 4)\n               |      println(\"Hello from \" + \"tests\")\n               |    }\n               |  }\n               |}\n               |\"\"\".stripMargin,\n          os.rel / \"CustomFramework.test.scala\" ->\n            \"\"\"package custom\n              |\n              |class CustomFramework extends utest.runner.Framework {\n              |  override def setup(): Unit =\n              |    println(\"Hello from CustomFramework\")\n              |}\n              |\"\"\".stripMargin\n        )\n        inputs.fromRoot { root =>\n          val baseRes = os.proc(TestUtil.cli, \"test\", extraOptions, platformArgs, \".\")\n            .call(cwd = root)\n          val baseOutput = baseRes.out.text()\n          expect(baseOutput.contains(\"Hello from tests\"))\n          expect(!baseOutput.contains(\"Hello from CustomFramework\"))\n\n          val cmd = Seq[os.Shellable](\n            TestUtil.cli,\n            \"test\",\n            extraOptions,\n            platformArgs,\n            \".\",\n            \"--test-framework\",\n            \"custom.CustomFramework\"\n          )\n          val res    = os.proc(cmd).call(cwd = root)\n          val output = res.out.text()\n          expect(output.contains(\"Hello from tests\"))\n          expect(output.contains(\"Hello from CustomFramework\"))\n        }\n      }\n    }\n\n  for ((platformName, platformArgs) <- platforms)\n    test(s\"Fail if no tests were run $platformName\") {\n      TestUtil.retryOnCi(maxAttempts = if (platformName.contains(\"native\")) 3 else 1) {\n        val inputs = TestInputs(\n          os.rel / \"MyTests.test.scala\" ->\n            s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n               |\n               |object MyTests\n               |\"\"\".stripMargin\n        )\n\n        inputs.fromRoot { root =>\n          val res =\n            os.proc(TestUtil.cli, \"test\", extraOptions, \"--require-tests\", platformArgs, \".\")\n              .call(cwd = root, stderr = os.Pipe, mergeErrIntoOut = true, check = false)\n          expect(res.exitCode != 0)\n          val output = res.out.text()\n          expect(\n            output.contains(\"Error: no tests were run\") || output.contains(\"No tests were run\")\n          )\n        }\n      }\n    }\n\n  private def countSubStrings(input: String, subString: String): Int = {\n    @tailrec\n    def helper(startIdx: Int, acc: Int): Int =\n      if (startIdx + subString.length > input.length) acc\n      else {\n        val idx = input.indexOf(subString, startIdx)\n        if (idx < 0) acc\n        else helper(idx + subString.length, acc + 1)\n      }\n\n    helper(0, 0)\n  }\n\n  test(\"Cross-tests\") {\n    TestUtil.retryOnCi() {\n      val supportsNative = actualScalaVersion.startsWith(\"2.\")\n      val platforms      = {\n        var pf = Seq(\"jvm\", \"js\")\n        if (supportsNative)\n          pf = pf :+ \"native\"\n        pf.mkString(\" \")\n      }\n      val inputs = {\n        var inputs0 = TestInputs(\n          os.rel / \"MyTests.scala\" ->\n            s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n               |//> using platform $platforms\n               |\n               |class MyTests extends munit.FunSuite {\n               |  test(\"shared\") {\n               |    println(\"Hello from \" + \"shared\")\n               |  }\n               |}\n               |\"\"\".stripMargin,\n          os.rel / \"MyJvmTests.scala\" ->\n            \"\"\"//> using target.platform jvm\n              |\n              |class MyJvmTests extends munit.FunSuite {\n              |  test(\"jvm\") {\n              |    println(\"Hello from \" + \"jvm\")\n              |  }\n              |}\n              |\"\"\".stripMargin,\n          os.rel / \"MyJsTests.scala\" ->\n            \"\"\"//> using target.platform js\n              |\n              |class MyJsTests extends munit.FunSuite {\n              |  test(\"js\") {\n              |    println(\"Hello from \" + \"js\")\n              |  }\n              |}\n              |\"\"\".stripMargin\n        )\n        if (supportsNative)\n          inputs0 = inputs0.add(\n            os.rel / \"MyNativeTests.scala\" ->\n              \"\"\"//> using target.platform native\n                |\n                |class MyNativeTests extends munit.FunSuite {\n                |  test(\"native\") {\n                |    println(\"Hello from \" + \"native\")\n                |  }\n                |}\n                |\"\"\".stripMargin\n          )\n        inputs0\n      }\n      inputs.fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"--power\", \"test\", extraOptions, \".\", \"--cross\").call(cwd = root)\n        val output        = res.out.text()\n        val expectedCount = 2 + (if (supportsNative) 1 else 0)\n        expect(countSubStrings(output, \"Hello from shared\") == expectedCount)\n        expect(output.contains(\"Hello from jvm\"))\n        expect(output.contains(\"Hello from js\"))\n        if (supportsNative)\n          expect(output.contains(\"Hello from native\"))\n      }\n    }\n  }\n\n  def jsDomTest(): Unit = {\n    val inputs = TestInputs(\n      os.rel / \"JsDom.test.scala\" ->\n        s\"\"\"//> using dep com.lihaoyi::utest::$utestVersion\n           |//> using dep org.scala-js::scalajs-dom::2.2.0\n           |\n           |import utest._\n           |\n           |import org.scalajs.dom.document\n           |\n           |object MyTests extends TestSuite {\n           |  val tests = Tests {\n           |    test(\"Hello World\") {\n           |      assert(document.querySelectorAll(\"p\").size == 0)\n           |      println(\"Hello from tests\")\n           |    }\n           |  }\n           |}\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      // install jsdom library\n      val npmPath = TestUtil.fromPath(\"npm\").getOrElse(\"npm\")\n      os.proc(npmPath, \"init\", \"private\").call(cwd = root)\n      os.proc(npmPath, \"install\", \"jsdom\").call(cwd = root)\n\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--js\", \"--js-dom\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  if (TestUtil.isCI)\n    // FIXME: figure out why this started failing on the CI: https://github.com/VirtusLab/scala-cli/issues/3335\n    test(\"Js DOM\".flaky) {\n      jsDomTest()\n    }\n\n  test(\"successful test Markdown with munit\") {\n    successfulMarkdownTestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \"Test.md\")\n        .call(cwd = root)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  test(\"failing test Markdown with munit\") {\n    failingMarkdownTestInputs.fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \"Test.md\")\n        .call(cwd = root, check = false)\n        .out.text()\n      expect(output.contains(\"Hello from tests\"))\n    }\n  }\n\n  if (!actualScalaVersion.startsWith(\"2.12\")) {\n    test(\"toolkit\") {\n      successfulTestInputs(s\"//> using toolkit ${Constants.toolkitVersion}\").fromRoot { root =>\n        val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root).out.text()\n        expect(output.contains(\"Hello from tests\"))\n      }\n    }\n\n    test(\"toolkit from command line\") {\n      successfulTestInputs(\"\").fromRoot { root =>\n        val output =\n          os.proc(\n            TestUtil.cli,\n            \"test\",\n            extraOptions,\n            \".\",\n            \"--toolkit\",\n            Constants.toolkitVersion\n          ).call(cwd =\n            root\n          ).out.text()\n        expect(output.contains(\"Hello from tests\"))\n      }\n    }\n  }\n\n  test(\"successful pure Java test with JUnit\") {\n    val expectedMessage = \"Hello from JUnit\"\n    TestInputs(\n      os.rel / \"test\" / \"MyTests.java\" ->\n        s\"\"\"//> using test.dependencies junit:junit:4.13.2\n           |//> using test.dependencies com.novocode:junit-interface:0.11\n           |import org.junit.Test;\n           |import static org.junit.Assert.assertEquals;\n           |\n           |public class MyTests {\n           |  @Test\n           |  public void foo() {\n           |    assertEquals(4, 2 + 2);\n           |    System.out.println(\"$expectedMessage\");\n           |  }\n           |}\n           |\"\"\".stripMargin\n    ).fromRoot { root =>\n      val res = os.proc(TestUtil.cli, \"test\", extraOptions, \".\").call(cwd = root)\n      expect(res.out.text().contains(expectedMessage))\n    }\n  }\n\n  test(s\"zio-test warning when zio-test-sbt was not passed\") {\n    TestUtil.retryOnCi() {\n      val expectedMessage = \"Hello from zio\"\n      TestInputs(os.rel / \"Zio.test.scala\" ->\n        s\"\"\"//> using test.dep dev.zio::zio-test::$zioTestVersion\n           |import zio._\n           |import zio.test._\n           |\n           |object SimpleSpec extends ZIOSpecDefault {\n           |  override def spec: Spec[TestEnvironment with Scope, Any] =\n           |    suite(\"SimpleSpec\")(\n           |      test(\"print hello and assert true\") {\n           |        for {\n           |          _ <- Console.printLine(\"$expectedMessage\")\n           |        } yield assertTrue(true)\n           |      }\n           |    )\n           |}\n           |\"\"\".stripMargin).fromRoot { root =>\n        val r = os.proc(TestUtil.cli, \"test\", \".\", extraOptions)\n          .call(cwd = root, check = false, stderr = os.Pipe)\n        expect(r.exitCode == 1)\n        val expectedWarning =\n          \"zio-test found in the class path, zio-test-sbt should be added to run zio tests\"\n        expect(r.err.trim().contains(expectedWarning))\n      }\n    }\n  }\n\n  for {\n    platformOptions <- Seq(\n      Nil, // JVM\n      Seq(\"--native\"),\n      Seq(\"--js\")\n    )\n    platformDescription = platformOptions.headOption.map(o => s\" ($o)\").getOrElse(\" (JVM)\")\n  } {\n    test(s\"zio-test$platformDescription\") {\n      TestUtil.retryOnCi() {\n        val expectedMessage = \"Hello from zio\"\n        TestInputs(os.rel / \"Zio.test.scala\" ->\n          s\"\"\"//> using test.dep dev.zio::zio-test::$zioTestVersion\n             |//> using test.dep dev.zio::zio-test-sbt::$zioTestVersion\n             |import zio._\n             |import zio.test._\n             |\n             |object SimpleSpec extends ZIOSpecDefault {\n             |  override def spec: Spec[TestEnvironment with Scope, Any] =\n             |    suite(\"SimpleSpec\")(\n             |      test(\"print hello and assert true\") {\n             |        for {\n             |          _ <- Console.printLine(\"$expectedMessage\")\n             |        } yield assertTrue(true)\n             |      }\n             |    )\n             |}\n             |\"\"\".stripMargin).fromRoot { root =>\n          val r = os.proc(TestUtil.cli, \"test\", \".\", extraOptions, platformOptions).call(cwd = root)\n          val output = r.out.trim()\n          expect(output.contains(expectedMessage))\n          expect(countSubStrings(output, expectedMessage) == 1)\n        }\n      }\n    }\n\n    test(s\"multiple test frameworks$platformDescription\") {\n      TestUtil.retryOnCi() {\n        val expectedMessages @ Seq(scalatestMessage, munitMessage, utestMessage, zioMessage) =\n          Seq(\"Hello from ScalaTest\", \"Hello from Munit\", \"Hello from utest\", \"Hello from zio\")\n        TestInputs(\n          os.rel / \"project.scala\" ->\n            s\"\"\"//> using test.dep org.scalatest::scalatest::3.2.19\n               |//> using test.dep org.scalameta::munit::$munitVersion\n               |//> using dep com.lihaoyi::utest::$utestVersion\n               |//> using test.dep dev.zio::zio-test::$zioTestVersion\n               |//> using test.dep dev.zio::zio-test-sbt::$zioTestVersion\n               |\"\"\".stripMargin,\n          os.rel / \"scalatest.test.scala\" ->\n            s\"\"\"import org.scalatest.flatspec.AnyFlatSpec\n               |\n               |class ScalaTestSpec extends AnyFlatSpec {\n               |    \"example\" should \"work\" in {\n               |      assertResult(1)(1)\n               |      println(\"$scalatestMessage\")\n               |    }\n               |}\n               |\"\"\".stripMargin,\n          os.rel / \"munit.test.scala\" ->\n            s\"\"\"import munit.FunSuite\n               |\n               |class Munit extends FunSuite {\n               |  test(\"foo\") {\n               |    assert(2 + 2 == 4)\n               |    println(\"$munitMessage\")\n               |  }\n               |}\n               |\"\"\".stripMargin,\n          os.rel / \"utest.test.scala\" ->\n            s\"\"\"import utest._\n               |\n               |object MyTests extends TestSuite {\n               |  val tests = Tests {\n               |    test(\"foo\") {\n               |      assert(2 + 2 == 4)\n               |      println(\"$utestMessage\")\n               |    }\n               |  }\n               |}\"\"\".stripMargin,\n          os.rel / \"Zio.test.scala\" ->\n            s\"\"\"import zio._\n               |import zio.test._\n               |\n               |object SimpleSpec extends ZIOSpecDefault {\n               |  override def spec: Spec[TestEnvironment with Scope, Any] =\n               |    suite(\"SimpleSpec\")(\n               |      test(\"print hello and assert true\") {\n               |        for {\n               |          _ <- Console.printLine(\"$zioMessage\")\n               |        } yield assertTrue(true)\n               |      }\n               |    )\n               |}\n               |\"\"\".stripMargin\n        ).fromRoot { root =>\n          val r =\n            os.proc(TestUtil.cli, \"test\", extraOptions, \".\", platformOptions, \"-v\", \"-v\").call(cwd =\n              root\n            )\n          val output = r.out.trim()\n          expect(output.nonEmpty)\n          expectedMessages.foreach { expectedMessage =>\n            expect(output.contains(expectedMessage))\n            expect(countSubStrings(output, expectedMessage) == 1)\n          }\n        }\n      }\n    }\n\n    test(s\"multiple explicitly preconfigured test frameworks$platformDescription\") {\n      TestUtil.retryOnCi() {\n        val expectedMessages @ Seq(scalatestMessage, munitMessage, customMessage) =\n          Seq(\"Hello from scalatest\", \"Hello from Munit\", \"Hello from custom framework\")\n        TestInputs(\n          os.rel / \"project.scala\" ->\n            s\"\"\"//> using test.dep org.scalatest::scalatest::3.2.19\n               |//> using dep com.lihaoyi::utest::$utestVersion\n               |//> using test.dep org.scalameta::munit::$munitVersion\n               |//> using test.frameworks org.scalatest.tools.Framework munit.Framework custom.CustomFramework\n               |\"\"\".stripMargin,\n          os.rel / \"scalatest.test.scala\" ->\n            s\"\"\"import org.scalatest.flatspec.AnyFlatSpec\n               |\n               |class ScalaTestSpec extends AnyFlatSpec {\n               |    \"example\" should \"work\" in {\n               |      assertResult(1)(1)\n               |      println(\"$scalatestMessage\")\n               |    }\n               |}\n               |\"\"\".stripMargin,\n          os.rel / \"munit.test.scala\" ->\n            s\"\"\"import munit.FunSuite\n               |\n               |class Munit extends FunSuite {\n               |  test(\"foo\") {\n               |    assert(2 + 2 == 4)\n               |    println(\"$munitMessage\")\n               |  }\n               |}\n               |\"\"\".stripMargin,\n          os.rel / \"custom.test.scala\" ->\n            s\"\"\"package custom\n               |\n               |class CustomFramework extends utest.runner.Framework {\n               |  override def setup(): Unit =\n               |    println(\"$customMessage\")\n               |}\"\"\".stripMargin\n        ).fromRoot { root =>\n          val r =\n            os.proc(\n              TestUtil.cli,\n              \"test\",\n              extraOptions,\n              \".\",\n              platformOptions\n            ).call(cwd = root)\n          val output = r.out.trim()\n          expect(output.nonEmpty)\n          expectedMessages.foreach { expectedMessage =>\n            expect(output.contains(expectedMessage))\n            expect(countSubStrings(output, expectedMessage) == 1)\n          }\n        }\n      }\n    }\n  }\n\n  for {\n    javaVersion <-\n      if isScala38OrNewer then\n        Constants.allJavaVersions.filter(_ >= Constants.scala38MinJavaVersion)\n      else Constants.allJavaVersions.filter(_ < 24)\n    expectedMessage = \"Hello, world!\"\n    expectedWarning = s\"Defaulting to a legacy test-runner module version\"\n  }\n    test(s\"run a simple test with Java $javaVersion\") {\n      TestInputs(os.rel / \"example.test.scala\" ->\n        s\"\"\"//> using dep com.novocode:junit-interface:0.11\n           |import org.junit.Test\n           |\n           |class MyTests {\n           |  @Test\n           |  def foo(): Unit = {\n           |    assert(2 + 2 == 4)\n           |    println(\"$expectedMessage\")\n           |  }\n           |}\n           |\"\"\".stripMargin).fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"test\", \".\", extraOptions, \"--jvm\", javaVersion)\n            .call(cwd = root, stderr = os.Pipe)\n        val out = res.out.trim()\n        expect(out.contains(expectedMessage))\n        if actualScalaVersion.startsWith(\"2\") || javaVersion < Constants.scala38MinJavaVersion then\n          val err = res.err.trim()\n          expect(err.contains(expectedWarning))\n          expect(err.countOccurrences(expectedWarning) == 1)\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestTests212.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.StringOps\n\nclass TestTests212 extends TestTestDefinitions with Test212 {\n  test(s\"run a simple test with Scala $actualScalaVersion (legacy)\") {\n    val expectedMessage = \"Hello, world!\"\n    TestInputs(os.rel / \"example.test.scala\" ->\n      s\"\"\"//> using dep com.novocode:junit-interface:0.11\n         |import org.junit.Test\n         |\n         |class MyTests {\n         |  @Test\n         |  def foo(): Unit = {\n         |    assert(2 + 2 == 4)\n         |    println(\"$expectedMessage\")\n         |  }\n         |}\n         |\"\"\".stripMargin).fromRoot { root =>\n      val expectedWarning =\n        s\"Defaulting to a legacy test-runner module version: ${Constants.runnerScala2LegacyVersion}\"\n      val res =\n        os.proc(TestUtil.cli, \"test\", \".\", extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n      val out = res.out.trim()\n      expect(out.contains(expectedMessage))\n      val err = res.err.trim()\n      expect(err.contains(expectedWarning))\n      expect(err.countOccurrences(expectedWarning) == 1)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestTests213.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.TestUtil.StringOps\n\nclass TestTests213 extends TestTestDefinitions with Test213 {\n  test(s\"run a simple test with Scala $actualScalaVersion (legacy)\") {\n    val expectedMessage = \"Hello, world!\"\n    TestInputs(os.rel / \"example.test.scala\" ->\n      s\"\"\"//> using dep com.novocode:junit-interface:0.11\n         |import org.junit.Test\n         |\n         |class MyTests {\n         |  @Test\n         |  def foo(): Unit = {\n         |    assert(2 + 2 == 4)\n         |    println(\"$expectedMessage\")\n         |  }\n         |}\n         |\"\"\".stripMargin).fromRoot { root =>\n      val expectedWarning =\n        s\"Defaulting to a legacy test-runner module version: ${Constants.runnerScala2LegacyVersion}\"\n      val res =\n        os.proc(TestUtil.cli, \"test\", \".\", extraOptions)\n          .call(cwd = root, stderr = os.Pipe)\n      val out = res.out.trim()\n      expect(out.contains(expectedMessage))\n      val err = res.err.trim()\n      expect(err.contains(expectedWarning))\n      expect(err.countOccurrences(expectedWarning) == 1)\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestTests3Lts.scala",
    "content": "package scala.cli.integration\n\nclass TestTests3Lts extends TestTestDefinitions with Test3Lts\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestTests3NextRc.scala",
    "content": "package scala.cli.integration\n\nclass TestTests3NextRc extends TestTestDefinitions with Test3NextRc\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestTestsDefault.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport java.io.File\n\nimport scala.cli.integration.Constants.munitVersion\nimport scala.cli.integration.TestUtil.StringOps\n\nclass TestTestsDefault extends TestTestDefinitions with TestDefault {\n  test(\"Pure Java with Scala tests\") {\n    val inputs = TestInputs(\n      os.rel / \"Messages.java\" ->\n        \"\"\"package messages;\n          |\n          |public final class Messages {\n          |  public final static String HELLO = \"Hello\";\n          |}\n          |\"\"\".stripMargin,\n      os.rel / \"test\" / \"MessagesTests.scala\" ->\n        \"\"\"//> using scala 2.13\n          |//> using dep com.lihaoyi::utest::0.7.10\n          |package messages\n          |package tests\n          |import utest._\n          |\n          |object MessagesTests extends TestSuite {\n          |  val tests = Tests {\n          |    test(\"hello\") {\n          |      assert(Messages.HELLO == \"Hello\")\n          |    }\n          |  }\n          |}\n          |\"\"\".stripMargin\n    )\n\n    inputs.fromRoot { root =>\n      val compileRes = os.proc(TestUtil.cli, \"compile\", \"--print-class-path\", extraOptions, \".\")\n        .call(cwd = root)\n      val cp = compileRes.out.trim().split(File.pathSeparator)\n      expect(cp.length == 1) // only class dir, no scala JARs\n      os.proc(TestUtil.cli, \"test\", extraOptions, \".\")\n        .call(cwd = root, stdout = os.Inherit)\n    }\n  }\n\n  test(\n    s\"successful test --cross $actualScalaVersion with ${Constants.scala213} and ${Constants.scala212}\"\n  ) {\n    val crossVersions   = Seq(actualScalaVersion, Constants.scala213, Constants.scala212)\n    val expectedMessage = \"Hello\"\n    TestInputs(\n      os.rel / \"Cross.test.scala\" ->\n        s\"\"\"//> using dep org.scalameta::munit::$munitVersion\n           |class MyTests extends munit.FunSuite {\n           |  test(\"foo\") {\n           |    assert(2 + 2 == 4)\n           |    println(\"$expectedMessage\")\n           |  }\n           |}\n           |\"\"\".stripMargin,\n      os.rel / \"project.scala\" -> s\"//> using scala ${crossVersions.mkString(\" \")}\"\n    ).fromRoot { root =>\n      val output = os.proc(TestUtil.cli, \"test\", extraOptions, \".\", \"--cross\", \"--power\")\n        .call(cwd = root).out.text()\n      expect(output.contains(expectedMessage))\n      expect(output.countOccurrences(expectedMessage) == crossVersions.length)\n    }\n  }\n\n  for {\n    scalaVersion <- TestUtil.legacyScalaVersionsOnePerMinor\n    expectedMessage = \"Hello, world!\"\n    expectedWarning =\n      s\"Defaulting to a legacy test-runner module version: ${Constants.runnerScala30LegacyVersion}\"\n  }\n    test(s\"run a simple test with Scala $scalaVersion (legacy)\") {\n      TestInputs(os.rel / \"example.test.scala\" ->\n        // using JUnit to work around TASTy and macro incompatibilities\n        s\"\"\"//> using dep com.novocode:junit-interface:0.11\n           |import org.junit.Test\n           |\n           |class MyTests {\n           |  @Test\n           |  def foo(): Unit = {\n           |    assert(2 + 2 == 4)\n           |    println(\"$expectedMessage\")\n           |  }\n           |}\n           |\"\"\".stripMargin).fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"test\", \".\", \"-S\", scalaVersion, TestUtil.extraOptions)\n            .call(cwd = root, stderr = os.Pipe)\n        val out = res.out.trim()\n        expect(out.contains(expectedMessage))\n        val err = res.err.trim()\n        expect(err.contains(expectedWarning))\n        expect(err.countOccurrences(expectedWarning) == 1)\n      }\n    }\n\n  for {\n    buildServerOptions <- Seq(Nil, Seq(\"--server=false\"))\n    buildServerDesc =\n      if buildServerOptions.isEmpty then \"with build server\" else \"without build server\"\n  }\n    test(s\"pure Java test with JUnit has no Scala on classpath $buildServerDesc\") {\n      TestInputs(\n        os.rel / \"test\" / \"MyTests.java\" ->\n          \"\"\"//> using test.dep junit:junit:4.13.2\n            |//> using test.dep com.novocode:junit-interface:0.11\n            |import org.junit.Test;\n            |import static org.junit.Assert.assertEquals;\n            |\n            |public class MyTests {\n            |  @Test\n            |  public void foo() {\n            |    try {\n            |      Class.forName(\"scala.Predef\");\n            |      throw new AssertionError(\"Scala should not be on the classpath\");\n            |    } catch (ClassNotFoundException e) {\n            |      // expected\n            |    }\n            |    assertEquals(4, 2 + 2);\n            |    System.out.println(\"No Scala on classpath!\");\n            |  }\n            |}\n            |\"\"\".stripMargin\n      ).fromRoot { root =>\n        val res =\n          os.proc(TestUtil.cli, \"test\", extraOptions, buildServerOptions, \".\").call(cwd = root)\n        expect(res.out.text().contains(\"No Scala on classpath!\"))\n      }\n    }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/TestUtil.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\nimport coursier.paths.CoursierPaths\n\nimport java.io.File\nimport java.net.ServerSocket\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.concurrent.{ExecutorService, Executors, ScheduledExecutorService, ThreadFactory}\nimport java.util.{Locale, UUID}\n\nimport scala.Console.*\nimport scala.annotation.tailrec\nimport scala.concurrent.duration.{Duration, DurationInt, FiniteDuration}\nimport scala.concurrent.{Await, ExecutionContext, Future}\nimport scala.jdk.CollectionConverters.*\nimport scala.util.{Properties, Try}\n\nobject TestUtil {\n\n  val cliKind: String               = sys.props(\"test.scala-cli.kind\")\n  val isNativeCli: Boolean          = cliKind.startsWith(\"native\")\n  val isNativeStaticCli: Boolean    = cliKind.startsWith(\"native-static\")\n  val isJvmCli: Boolean             = cliKind.startsWith(\"jvm\")\n  val isJvmBootstrappedCli: Boolean = cliKind.startsWith(\"jvmBootstrapped\")\n  val isCI: Boolean                 = System.getenv(\"CI\") != null\n  val isAarch64: Boolean            = sys.props.get(\"os.arch\").contains(\"aarch64\")\n  val cliPath: String               = sys.props(\"test.scala-cli.path\")\n  val debugPortOpt: Option[String]  = sys.props.get(\"test.scala-cli.debug.port\")\n  val detectCliPath: String         = if (TestUtil.isNativeCli) TestUtil.cliPath else \"scala-cli\"\n  val cli: Seq[String]              = cliCommand(cliPath)\n  val ltsEqualsNext: Boolean        = Constants.scala3Lts equals Constants.scala3Next\n\n  lazy val legacyScalaVersionsOnePerMinor: Seq[String] =\n    Constants.legacyScala3Versions.sorted.reverse.distinctBy(_.split('.').take(2).mkString(\".\"))\n\n  def cliCommand(cliPath: String): Seq[String] =\n    if (isNativeCli)\n      Seq(cliPath)\n    else\n      debugPortOpt match {\n        case Some(port) => Seq(\n            \"java\",\n            \"-Xmx512m\",\n            \"-Xms128m\",\n            s\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$port,quiet=y\",\n            \"-jar\",\n            cliPath\n          )\n        case _ => Seq(\"java\", \"-Xmx512m\", \"-Xms128m\", \"-jar\", cliPath)\n      }\n\n  val extraOptions: List[String] = List(\n    \"--bloop-startup-timeout\",\n    \"2min\",\n    \"--bloop-bsp-timeout\",\n    \"1min\"\n  )\n\n  def fromPath(app: String): Option[String] = {\n\n    val pathExt =\n      if (Properties.isWin)\n        Option(System.getenv(\"PATHEXT\"))\n          .toSeq\n          .flatMap(_.split(File.pathSeparator).toSeq)\n      else\n        Seq(\"\")\n    val path = Seq(new File(\"\").getAbsoluteFile) ++\n      Option(System.getenv(\"PATH\"))\n        .toSeq\n        .flatMap(_.split(File.pathSeparator))\n        .map(new File(_))\n\n    def candidates =\n      for {\n        dir <- path.iterator\n        ext <- pathExt.iterator\n      } yield new File(dir, app + ext)\n\n    candidates\n      .filter(_.canExecute)\n      .take(1)\n      .toList\n      .headOption\n      .map(_.getAbsolutePath)\n  }\n\n  def cs: String = Constants.cs\n\n  def threadPool(prefix: String, size: Int): ExecutorService =\n    Executors.newFixedThreadPool(size, daemonThreadFactory(prefix))\n  def withThreadPool[T](prefix: String, size: Int)(f: ExecutorService => T): T = {\n    var pool: ExecutorService = null\n    try {\n      pool = threadPool(prefix, size)\n      f(pool)\n    }\n    finally\n      if (pool != null)\n        pool.shutdown()\n  }\n\n  def scheduler(prefix: String): ScheduledExecutorService =\n    Executors.newSingleThreadScheduledExecutor(daemonThreadFactory(prefix))\n\n  private def daemonThreadFactory(prefix: String): ThreadFactory =\n    new ThreadFactory {\n      val counter                        = new AtomicInteger\n      def threadNumber(): Int            = counter.incrementAndGet()\n      def newThread(r: Runnable): Thread =\n        new Thread(r, s\"$prefix-thread-${threadNumber()}\") {\n          setDaemon(true)\n          setPriority(Thread.NORM_PRIORITY)\n        }\n    }\n\n  def normalizeUri(uri: String): String =\n    if (uri.startsWith(\"file:///\")) \"file:/\" + uri.stripPrefix(\"file:///\")\n    else uri\n\n  def removeAnsiColors(str: String): String = str.replaceAll(\"\\\\e\\\\[[0-9]+m\", \"\")\n\n  def fullStableOutput(result: os.CommandResult): String =\n    removeAnsiColors(result.toString).trim().linesIterator.filterNot { str =>\n      // these lines are not stable and can easily change\n      val shouldNotContain =\n        Set(\n          \"Starting compilation server\",\n          \"hint\",\n          \"Download\",\n          \"Result of\",\n          \"Checking\",\n          \"Checked\",\n          \"Failed to download\"\n        )\n      shouldNotContain.exists(str.contains)\n    }.mkString(System.lineSeparator())\n\n  def fullStableOutputLines(result: os.CommandResult): Vector[String] =\n    fullStableOutput(result).lines().toList.asScala.toVector\n\n  def retry[T](\n    maxAttempts: Int = 3,\n    waitDuration: FiniteDuration = 5.seconds\n  )(\n    run: => T\n  ): T = {\n    @tailrec\n    def helper(count: Int): T =\n      try run\n      catch {\n        case t: Throwable =>\n          if (count >= maxAttempts) {\n            System.err.println(s\"$maxAttempts attempts failed, caught $t. Giving up.\")\n            throw new Exception(t)\n          }\n          else {\n            val remainingAttempts = maxAttempts - count\n            System.err.println(\n              s\"Caught $t, $remainingAttempts attempts remaining, trying again in $waitDuration…\"\n            )\n            Thread.sleep(waitDuration.toMillis)\n            System.err.println(s\"Trying attempt $count out of $maxAttempts...\")\n            helper(count + 1)\n          }\n      }\n    helper(1)\n  }\n\n  def retryOnCi[T](maxAttempts: Int = 3, waitDuration: FiniteDuration = 5.seconds)(\n    run: => T\n  ): T = retry(if (isCI) maxAttempts else 1, waitDuration)(run)\n\n  // Same as os.RelPath.toString, but for the use of File.separator instead of \"/\"\n  def relPathStr(relPath: os.RelPath): String =\n    (Seq.fill(relPath.ups)(\"..\") ++ relPath.segments).mkString(File.separator)\n\n  def kill(pid: Int): os.CommandResult =\n    if (Properties.isWin)\n      os.proc(\"taskkill\", \"/F\", \"/PID\", pid).call()\n    else\n      os.proc(\"kill\", pid).call()\n\n  def pwd: os.Path =\n    if (Properties.isWin)\n      os.Path(os.pwd.toIO.getCanonicalFile)\n    else\n      os.pwd\n\n  /** @return\n    *   2 quotation marks (\") when run for Windows, or a single one otherwise. This is necessary to\n    *   escape the quotation marks passed in args for the Windows command line.\n    */\n  def argQuotationMark: String = if (Properties.isWin) \"\\\"\\\"\" else \"\\\"\"\n\n  def serveFilesInHttpServer[T](\n    path: os.Path,\n    user: String,\n    password: String,\n    realm: String\n  )(f: (String, Int) => T): T = {\n    val host = \"127.0.0.1\"\n    val port = {\n      val s = new ServerSocket(0)\n      try s.getLocalPort()\n      finally s.close()\n    }\n    val proc = os.proc(\n      cs,\n      \"launch\",\n      \"io.get-coursier:http-server_2.12:1.0.1\",\n      \"--\",\n      \"--user\",\n      user,\n      \"--password\",\n      password,\n      \"--realm\",\n      realm,\n      \"--directory\",\n      \".\",\n      \"--host\",\n      host,\n      \"--port\",\n      port,\n      \"-v\"\n    )\n      .spawn(cwd = path, mergeErrIntoOut = true)\n    try {\n\n      // a timeout around this would be great…\n      System.err.println(s\"Waiting for local HTTP server to get started on $host:$port…\")\n      var lineOpt = Option.empty[String]\n      while (\n        proc.isAlive() && {\n          lineOpt = Some(proc.stdout.readLine())\n          !lineOpt.exists(_.startsWith(\"Listening on \"))\n        }\n      )\n        for (l <- lineOpt)\n          System.err.println(l)\n\n      // Seems required, especially when using native launchers\n      val waitFor = Duration(2L, \"s\")\n      System.err.println(s\"Waiting $waitFor\")\n      Thread.sleep(waitFor.toMillis)\n\n      val t = new Thread(\"test-http-server-output\") {\n        setDaemon(true)\n        override def run(): Unit = {\n          var line = \"\"\n          while (\n            proc.isAlive() && {\n              line = proc.stdout.readLine()\n              line != null\n            }\n          )\n            System.err.println(line)\n        }\n      }\n      t.start()\n      f(host, port)\n    }\n    finally {\n      proc.destroy()\n      Thread.sleep(100L)\n      if (proc.isAlive()) {\n        Thread.sleep(1000L)\n        proc.destroy(shutdownGracePeriod = 0)\n      }\n    }\n  }\n\n  def putCsInPathViaEnv(binDir: os.Path): Map[String, String] = {\n\n    val (pathVarName, currentPath) = sys.env\n      .find(_._1.toLowerCase(Locale.ROOT) == \"path\")\n      .getOrElse((\"PATH\", \"\"))\n    if (Properties.isWin) {\n      val dest = binDir / \"cs.bat\"\n      if (!os.exists(dest)) {\n        val script =\n          s\"\"\"@echo off\n             |\"${TestUtil.cs}\" %*\n             |\"\"\".stripMargin\n        os.write(dest, script, createFolders = true)\n      }\n    }\n    else {\n      val dest = binDir / \"cs\"\n      if (!os.exists(dest)) {\n        val script =\n          s\"\"\"#!/usr/bin/env bash\n             |exec \"${TestUtil.cs}\" \"$$@\"\n             |\"\"\".stripMargin\n        os.write(dest, script, \"rwxr-xr-x\", createFolders = true)\n      }\n    }\n    Map(pathVarName -> s\"$binDir${File.pathSeparator}$currentPath\")\n  }\n\n  // Copied from https://github.com/scalacenter/bloop/blob/a249e0a710ce169ca05d0606778f96f44a398680/shared/src/main/scala/bloop/io/Environment.scala\n  private lazy val shebangCapableShells = Seq(\n    \"/bin/sh\",\n    \"/bin/ash\",\n    \"/bin/bash\",\n    \"/bin/dash\",\n    \"/bin/mksh\",\n    \"/bin/pdksh\",\n    \"/bin/posh\",\n    \"/bin/tcsh\",\n    \"/bin/zsh\",\n    \"/bin/fish\"\n  )\n\n  def isShebangCapableShell = Option(System.getenv(\"SHELL\")) match {\n    case Some(currentShell) if shebangCapableShells.exists(sh => currentShell.contains(sh)) => true\n    case _                                                                                  => false\n  }\n\n  def readLine(\n    stream: os.SubProcess.OutputStream,\n    ec: ExecutionContext,\n    timeout: Duration\n  ): String = {\n    implicit val ec0 = ec\n    val readLineF    = Future {\n      stream.readLine()\n    }\n    Await.result(readLineF, timeout)\n  }\n\n  def normalizeConsoleOutput(text: String) = {\n    val allColors =\n      Set(BOLD, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, /* GREY */ \"\\u001b[90m\")\n    allColors.+(Console.RESET).fold(text) { (textAcc, colorStr) =>\n      textAcc.replace(colorStr, \"\")\n    }\n  }\n\n  def initializeGit(\n    cwd: os.Path,\n    tag: String = \"test-inputs\",\n    gitUserName: String = \"testUser\",\n    gitUserEmail: String = \"testUser@scala-cli-tests.com\"\n  ): Unit = {\n    println(s\"Initializing git in $cwd...\")\n    os.proc(\"git\", \"init\").call(cwd = cwd)\n    println(s\"Setting git user.name to $gitUserName\")\n    os.proc(\"git\", \"config\", \"--local\", \"user.name\", gitUserName).call(cwd = cwd)\n    println(s\"Setting git user.email to $gitUserEmail\")\n    os.proc(\"git\", \"config\", \"--local\", \"user.email\", gitUserEmail).call(cwd = cwd)\n    println(s\"Adding $cwd to git...\")\n    os.proc(\"git\", \"add\", \".\").call(cwd = cwd)\n    println(s\"Doing an initial commit...\")\n    os.proc(\"git\", \"commit\", \"-m\", \"git init test inputs\").call(cwd = cwd)\n    println(s\"Tagging as $tag...\")\n    os.proc(\"git\", \"tag\", tag).call(cwd = cwd)\n    println(s\"Git initialized at $cwd\")\n  }\n\n  def maybeUseBash(cmd: os.Shellable*)(cwd: os.Path = null): os.CommandResult = {\n    val res = os.proc(cmd*).call(cwd = cwd, check = false)\n    if (Properties.isLinux && res.exitCode == 127)\n      // /bin/sh seems to have issues with '%' signs in PATH, that coursier can leave\n      // in the JVM path entry (https://unix.stackexchange.com/questions/126955/percent-in-path-environment-variable)\n      os.proc(((\"/bin/bash\": os.Shellable) +: cmd)*).call(cwd = cwd)\n    else {\n      expect(res.exitCode == 0)\n      res\n    }\n  }\n\n  def withProcessWatching(\n    proc: os.SubProcess,\n    threadName: String = UUID.randomUUID().toString,\n    poolSize: Int = 2,\n    timeout: Duration = 90.seconds\n  )(f: (os.SubProcess, Duration, ExecutionContext) => Unit): Unit =\n    try withThreadPool(threadName, poolSize) { pool =>\n        f(proc, timeout, ExecutionContext.fromExecutorService(pool))\n      }\n    finally if (proc.isAlive()) {\n        proc.destroy()\n        Thread.sleep(200L)\n        if (proc.isAlive()) proc.destroy(shutdownGracePeriod = 0)\n      }\n\n  implicit class StringOps(a: String) {\n    def countOccurrences(b: String): Int =\n      if (b.isEmpty) 0 // Avoid infinite splitting\n      else a.sliding(b.length).count(_ == b)\n  }\n\n  def printStderrUntilCondition(\n    proc: os.SubProcess,\n    timeout: Duration = 90.seconds\n  )(condition: String => Boolean)(\n    f: String => Unit = _ => ()\n  )(implicit ec: ExecutionContext): Unit = {\n    def revertTriggered(): Boolean = {\n      val stderrOutput = TestUtil.readLine(proc.stderr, ec, timeout)\n      println(stderrOutput)\n      f(stderrOutput)\n      condition(stderrOutput)\n    }\n\n    while (!revertTriggered()) Thread.sleep(100L)\n  }\n\n  def readLinesUntil(\n    stream: os.SubProcess.OutputStream,\n    ec: ExecutionContext,\n    timeout: Duration\n  )(condition: String => Boolean): Seq[String] = {\n    val lines = scala.collection.mutable.ListBuffer.empty[String]\n    var done  = false\n    while (!done) {\n      val line = TestUtil.readLine(stream, ec, timeout)\n      if (line == null) done = true\n      else {\n        lines += line\n        done = condition(line)\n      }\n    }\n    lines.toSeq\n  }\n\n  private val watchingSourcesCondition: String => Boolean = _.contains(\"Watching sources\")\n\n  implicit class ProcOps(proc: os.SubProcess) {\n    def printStderrUntilJlineRevertsToDumbTerminal(proc: os.SubProcess)(\n      f: String => Unit\n    )(implicit ec: ExecutionContext): Unit =\n      TestUtil.printStderrUntilCondition(proc)(_.contains(\"creating a dumb terminal\"))(f)\n\n    def printStderrUntilRerun(timeout: Duration)(implicit ec: ExecutionContext): Unit =\n      TestUtil.printStderrUntilCondition(proc, timeout)(_.contains(\"re-run\"))()\n\n    def readStderrUntilWatchingMessage(timeout: Duration)(implicit\n      ec: ExecutionContext\n    ): Seq[String] =\n      TestUtil.readLinesUntil(proc.stderr, ec, timeout)(watchingSourcesCondition)\n\n    def readOutputUntilWatchingMessage(timeout: Duration)(implicit\n      ec: ExecutionContext\n    ): Seq[String] =\n      TestUtil.readLinesUntil(proc.stdout, ec, timeout)(watchingSourcesCondition)\n  }\n\n  // based on the implementation from bloop-rifle:\n  // https://github.com/scalacenter/bloop/blob/65b0b290fddd6d4256665014a7d16531e29ded4f/bloop-rifle/src/main/scala/bloop/rifle/VersionUtil.scala#L13-L30\n  def parseJavaVersion(input: String): Option[Int] = {\n    val jvmReleaseRegex                             = \"(1[.])?(\\\\d+)\"\n    def jvmRelease(jvmVersion: String): Option[Int] = for {\n      regexMatch    <- jvmReleaseRegex.r.findFirstMatchIn(jvmVersion)\n      versionString <- Option(regexMatch.group(2))\n      versionInt    <- Try(versionString.toInt).toOption\n    } yield versionInt\n    for {\n      firstMatch         <- s\"\"\".*version .($jvmReleaseRegex).*\"\"\".r.findFirstMatchIn(input)\n      versionNumberGroup <- Option(firstMatch.group(1))\n      versionInt         <- jvmRelease(versionNumberGroup)\n    } yield versionInt\n  }\n\n  extension [T](f: scala.concurrent.Future[T]) {\n    def await: T                         = Await.result(f, Duration.Inf)\n    def awaitWithTimeout(d: Duration): T = Await.result(f, d)\n  }\n\n  extension (args: Seq[String]) {\n    def normalizeArgsForWindows: Seq[String] =\n      if Properties.isWin then\n        args.map(a => if a.contains(\" \") then \"\\\"\" + a.replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\" else a)\n      else args\n  }\n\n  def cleanCoursierCache(pathToCleanContainsAnyKeyword: Seq[String] = Seq.empty): Unit = {\n    val csCache = os.Path(CoursierPaths.cacheDirectoryPath())\n    if pathToCleanContainsAnyKeyword.isEmpty && os.exists(csCache) && os.isDir(csCache) then\n      System.err.println(s\"Cleaning Coursier cache at $csCache\")\n      os.remove.all(csCache)\n    else if pathToCleanContainsAnyKeyword.nonEmpty then\n      os.walk(csCache)\n        .filter(d => !os.isDir(d)) // skip containing dirs\n        .filter(p => pathToCleanContainsAnyKeyword.exists(p.toString.contains))\n        .foreach { p =>\n          System.err.println(s\"Cleaning from Coursier cache: $p\")\n          os.remove(p)\n        }\n    else System.err.println(\"Nothing to clean\")\n  }\n\n  def cleanCachedJdks(): Unit = {\n    System.err.println(\"Cleaning cached JDKs in Coursier cache…\")\n    cleanCoursierCache(Seq(\"zulu\", \"temurin\", \"adoptium\", \"corretto\", \"liberica\", \"graalvm\"))\n  }\n\n  def substedDrives: Set[Char] =\n    if Properties.isWin then\n      os.proc(\"cmd\", \"/c\", \"subst\").call().out.text()\n        .linesIterator\n        .flatMap { line =>\n          // lines look like: \"I:\\: => C:\\path\"\n          if (line.length >= 2 && line(1) == ':') Some(line(0))\n          else None\n        }\n        .toSet\n    else Set.empty[Char]\n\n  def availableDriveLetter(): Char = {\n    import scala.annotation.tailrec\n    @tailrec\n    def helper(from: Char): Char =\n      if (from > 'Z') sys.error(\"Cannot find free drive letter\")\n      // neither physical drives nor SUBSTed drives are free\n      else if (mountedDrives.contains(from) || substedDrives.contains(from))\n        helper((from + 1).toChar)\n      else\n        from\n\n    helper('D')\n  }\n\n  lazy val mountedDrives: String = {\n    val str         = \"HKEY_LOCAL_MACHINE/SYSTEM/MountedDevices\".replace('/', '\\\\')\n    val queryDrives = s\"reg query $str\"\n    val lines       = os.proc(\"cmd\", \"/c\", queryDrives).call().out.lines()\n    val dosDevices  = lines.filter { s =>\n      s.contains(\"DosDevices\")\n    }.map { s =>\n      s.replaceAll(\".DosDevices.\", \"\").replaceAll(\":.*\", \"\")\n    }\n    dosDevices.mkString\n  }\n\n  def aliasDriveLetter(driveLetter: Char, from: String): Unit = {\n    val setupCommand = s\"\"\"subst $driveLetter: \"$from\"\"\"\"\n    os.proc(\"cmd\", \"/c\", setupCommand).call(stdin = os.Inherit, stdout = os.Inherit)\n  }\n\n  def unaliasDriveLetter(driveLetter: Char): Int = {\n    val (exit, _) = execWindowsCmd(\"cmd.exe\", \"/c\", s\"subst $driveLetter: /d\")\n    exit\n  }\n\n  def setCodePage(cp: String): Int =\n    val (exit, _) = execWindowsCmd(\"cmd\", \"/c\", s\"chcp $cp\")\n    exit\n\n  def getCodePage: String = {\n    val (_, output) = execWindowsCmd(\"cmd\", \"/c\", \"chcp\")\n    output\n  }\n\n  private def execWindowsCmd(cmd: String*): (Int, String) =\n    val pb = new ProcessBuilder(cmd*)\n    pb.redirectInput(ProcessBuilder.Redirect.INHERIT)\n    pb.redirectError(ProcessBuilder.Redirect.INHERIT)\n    pb.redirectOutput(ProcessBuilder.Redirect.PIPE)\n    val p = pb.start()\n    // read stdout fully\n    val output   = scala.io.Source.fromInputStream(p.getInputStream, \"UTF-8\").mkString\n    val exitCode = p.waitFor()\n    (exitCode, output)\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/UpdateTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.util.Properties\n\nclass UpdateTests extends ScalaCliSuite {\n  val firstVersion           = \"0.0.1\"\n  val dummyScalaCliFirstName = \"DummyScalaCli-1.scala\"\n  val dummyScalaCliBinName   = \"scala-cli-dummy-test\"\n  val testInputs: TestInputs = TestInputs(\n    os.rel / dummyScalaCliFirstName ->\n      s\"\"\"\n         |object DummyScalaCli extends App {\n         |  println(\\\"$firstVersion\\\")\n         |}\"\"\".stripMargin\n  )\n\n  private def packageDummyScalaCli(root: os.Path, dummyScalaCliFileName: String, output: String) = {\n    val cmd = Seq[os.Shellable](\n      TestUtil.cli,\n      \"--power\",\n      \"package\",\n      dummyScalaCliFileName,\n      \"-o\",\n      output\n    )\n    os.proc(cmd).call(\n      cwd = root,\n      stdin = os.Inherit,\n      stdout = os.Inherit\n    )\n  }\n\n  private def installScalaCli(\n    root: os.Path,\n    binVersion: String,\n    binDirPath: os.Path\n  ) = {\n    val cmdInstallVersion = Seq[os.Shellable](\n      TestUtil.cli,\n      \"install-home\",\n      \"--env\",\n      \"--scala-cli-binary-path\",\n      binVersion,\n      \"--binary-name\",\n      dummyScalaCliBinName,\n      \"--bin-dir\",\n      binDirPath,\n      \"--force\"\n    )\n    os.proc(cmdInstallVersion).call(\n      cwd = root,\n      stdin = os.Inherit,\n      stdout = os.Inherit\n    )\n  }\n\n  def runUpdate(): Unit = {\n\n    testInputs.fromRoot { root =>\n      val binDirPath = root / Constants.workspaceDirName / \"scala-cli\"\n\n      val binDummyScalaCliFirst = dummyScalaCliFirstName.stripSuffix(\".scala\").toLowerCase\n\n      packageDummyScalaCli(root, dummyScalaCliFirstName, binDummyScalaCliFirst)\n\n      // install 1 version\n      installScalaCli(root, binDummyScalaCliFirst, binDirPath)\n\n      val dummyScalaCliCommand =\n        Seq[os.Shellable](\"/usr/bin/env\", \"bash\", binDirPath / dummyScalaCliBinName)\n\n      val v1Install = os.proc(dummyScalaCliCommand).call(\n        cwd = root,\n        stdin = os.Inherit\n      ).out.trim()\n      expect(v1Install == firstVersion)\n\n      val tokenOptions =\n        if (System.getenv(\"UPDATE_GH_TOKEN\") == null) Nil\n        else Seq(\"--gh-token\", \"env:UPDATE_GH_TOKEN\")\n      // update to newest version\n      val cmdUpdate = Seq[os.Shellable](\n        TestUtil.cli,\n        \"update\",\n        \"--binary-name\",\n        dummyScalaCliBinName,\n        \"--bin-dir\",\n        binDirPath,\n        \"--force\",\n        tokenOptions\n      )\n      os.proc(cmdUpdate).call(\n        cwd = root,\n        stdin = os.Inherit,\n        stdout = os.Inherit\n      )\n\n      val nextVersion = os.proc(binDirPath / dummyScalaCliBinName, \"version\").call(\n        cwd = root,\n        stdin = os.Inherit\n      ).out.trim()\n\n      expect(firstVersion != nextVersion)\n    }\n  }\n\n  if (\n    !Properties.isWin && !Properties.isMac && Constants.ghOrg == \"VirtusLab\" &&\n    Constants.ghName == \"scala-cli\"\n  )\n    test(\"updating dummy scala-cli using update command\") {\n      TestUtil.retryOnCi()(runUpdate())\n    }\n\n  test(\"run update before run/test/compile should not return exit code\") {\n    val res = os.proc(TestUtil.cli, \"update\", \"--is-internal-run\").call(cwd = os.pwd)\n    expect(res.exitCode == 0)\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/VersionTests.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nimport scala.cli.integration.VersionTests.variants\n\nclass VersionTests extends ScalaCliSuite {\n\n  for (versionOption <- variants) {\n    test(versionOption) {\n      // tests if the format is correct instead of comparing to a version passed via Constants\n      // in order to catch errors in Mill configuration, too\n      val versionRegex = \".*\\\\d+[.]\\\\d+[.]\\\\d+.*\".r\n      val version      = os.proc(TestUtil.cli, versionOption).call(check = false)\n      assert(\n        versionRegex.findFirstMatchIn(version.out.text()).isDefined,\n        clues(version.exitCode, version.out.text(), version.err.text())\n      )\n      expect(version.exitCode == 0)\n    }\n\n    test(s\"$versionOption --offline\") {\n      TestInputs.empty.fromRoot { root =>\n        // TODO: --power should not be necessary here\n        os.proc(TestUtil.cli, versionOption, \"--offline\", \"--power\").call(cwd = root)\n      }\n    }\n  }\n\n  // given the 30 days snapshot retention, this may prove unreliable on the CI\n  // but might still be valuable for local testing\n  test(\"CLI nightly on new Maven Central Snapshots repo\".flaky) {\n    TestInputs.empty.fromRoot {\n      root =>\n        val res =\n          os.proc(\n            TestUtil.cli,\n            \"--cli-version\",\n            \"nightly\",\n            \"version\",\n            \"--cli-version\"\n          )\n            .call(cwd = root)\n        expect(res.out.trim().coursierVersion >= \"1.8.4\".coursierVersion)\n    }\n  }\n\n}\n\nobject VersionTests {\n  val variants = Seq(\"version\", \"-version\", \"--version\")\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/WithWarmUpScalaCliSuite.scala",
    "content": "package scala.cli.integration\n\nimport com.eed3si9n.expecty.Expecty.expect\n\nabstract class WithWarmUpScalaCliSuite extends ScalaCliSuite {\n  def warmUpExtraTestOptions: Seq[String]\n\n  // warm-up run that downloads compiler bridges\n  // The \"Downloading compiler-bridge (from bloop?) pollute the output, and would make the first test fail.\n  lazy val warmupTest: Unit = {\n    System.err.println(\"Running warmup test…\")\n    warmUpTests(ignoreErrors = true)\n    System.err.println(\"Done running warmup test.\")\n  }\n\n  def warmUpTests(ignoreErrors: Boolean): Unit = {\n    val fileName = \"simple.sc\"\n    val message  = \"Hello\"\n    val inputs   = TestInputs(\n      os.rel / fileName ->\n        s\"\"\"val msg = \"$message\"\n           |println(msg)\n           |\"\"\".stripMargin\n    )\n    inputs.fromRoot { root =>\n      val output =\n        os.proc(TestUtil.cli, warmUpExtraTestOptions, fileName).call(cwd = root).out.trim()\n      if (!ignoreErrors)\n        expect(output == message)\n    }\n  }\n\n  override def test(name: String)(body: => Any)(implicit loc: munit.Location): Unit =\n    super.test(name) { warmupTest; body }(loc)\n\n  override def test(name: munit.TestOptions)(body: => Any)(implicit loc: munit.Location): Unit =\n    super.test(name) { warmupTest; body }(loc)\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/package.scala",
    "content": "package scala.cli\n\nimport coursier.version.Version\n\nimport java.util.concurrent.CompletableFuture\n\nimport scala.concurrent.{Future, Promise}\nimport scala.util.{Failure, Success}\n\npackage object integration {\n\n  implicit class CompletableFutureToScala[T](private val cf: CompletableFuture[T]) extends AnyVal {\n    def asScala: Future[T] = {\n      val p = Promise[T]()\n      cf.handle { (t, ex) =>\n        val res =\n          if (ex == null) Success(t)\n          else Failure(ex)\n        p.complete(res)\n      }\n      p.future\n    }\n  }\n\n  implicit class VersionString(private val s: String) {\n    def coursierVersion: Version = Version(s)\n  }\n\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/util/BloopUtil.scala",
    "content": "package scala.cli.integration.util\n\nimport coursier.core.Version\n\nimport scala.cli.integration.TestUtil\nimport scala.util.Properties\n\nobject BloopUtil {\n\n  def bloopDaemonDir(directoriesCommandOutput: String): os.Path = {\n    val dir = directoriesCommandOutput\n      .linesIterator\n      .map(_.trim)\n      .filter(_.startsWith(\"Bloop daemon directory: \"))\n      .map(_.stripPrefix(\"Bloop daemon directory: \"))\n      .map(os.Path(_, os.pwd))\n      .take(1)\n      .toList\n      .headOption\n      .getOrElse {\n        sys.error(\n          s\"Cannot get Bloop daemon directory in 'scala-cli directories' output '$directoriesCommandOutput'\"\n        )\n      }\n    if (!os.exists(dir)) {\n      os.makeDir.all(dir)\n      if (!Properties.isWin)\n        os.perms.set(dir, \"rwx------\")\n    }\n    dir\n  }\n\n  private def bloopOrg(currentBloopVersion: String): String =\n    currentBloopVersion.split(\"[-.]\") match {\n      case Array(majStr, minStr, patchStr, _*) =>\n        import scala.math.Ordering.Implicits.*\n        val maj              = majStr.toInt\n        val min              = minStr.toInt\n        val patch            = patchStr.toInt\n        val useBloopMainLine =\n          Seq(maj, min, patch) < Seq(1, 4, 11) ||\n          (Seq(maj, min, patch) == Seq(1, 4, 11) && !currentBloopVersion.endsWith(\"-SNAPSHOT\"))\n        if (useBloopMainLine)\n          \"ch.epfl.scala\"\n        else\n          \"io.github.alexarchambault.bleep\"\n      case _ =>\n        \"ch.epfl.scala\"\n    }\n  def bloop(\n    currentBloopVersion: String,\n    bloopDaemonDir: os.Path,\n    jvm: Option[String] = None\n  ): Seq[String] => os.proc = {\n\n    val bloopOrg0 = bloopOrg(currentBloopVersion)\n\n    // no more bloopgun for the Bloop fork from version 1.5.3-sc-1\n    val useScalaCliBloopCommand =\n      bloopOrg0 != \"ch.epfl.scala\" && Version(currentBloopVersion) >= Version(\"1.5.3-sc-1\")\n\n    if (useScalaCliBloopCommand) {\n      val jvmArgs = jvm.toList.flatMap(name => Seq(\"--bloop-java-opt\", name))\n      args =>\n        os.proc(\n          TestUtil.cli,\n          \"--power\",\n          \"bloop\",\n          jvmArgs,\n          \"--bloop-version\",\n          currentBloopVersion.toString,\n          \"--bloop-daemon-dir\",\n          bloopDaemonDir,\n          args\n        )\n    }\n    else {\n      val daemonArgs =\n        if (Properties.isWin)\n          Seq(\"--nailgun-server\", \"127.0.0.1\", \"--nailgun-port\", \"8212\")\n        else\n          Seq(\"--daemon-dir\", bloopDaemonDir.toString)\n      val jvmArgs = jvm.toList.flatMap(name => Seq(\"--jvm\", name))\n      args =>\n        os.proc(\n          TestUtil.cs,\n          \"launch\",\n          jvmArgs,\n          s\"$bloopOrg0:bloopgun_2.12:$currentBloopVersion\",\n          \"--\",\n          daemonArgs,\n          args\n        )\n    }\n  }\n  def killBloop(): Unit = {\n    val javaProcesses = os.proc(\"jps\", \"-l\").call().out.text().linesIterator\n    val bloopPidReg   = \"(\\\\d+).*bloop[.]Bloop\".r\n    val bloopPids     = javaProcesses.flatMap { l =>\n      l match {\n        case bloopPidReg(pid) => Some(pid.toInt)\n        case _                => None\n      }\n    }.toList\n    bloopPids.foreach(TestUtil.kill)\n  }\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/util/CompilerPluginUtil.scala",
    "content": "package scala.cli.integration.util\n\nimport scala.cli.integration.TestInputs\n\nobject CompilerPluginUtil {\n  def compilerPluginForScala2(pluginName: String, pluginErrorMsg: String): TestInputs = TestInputs(\n    os.rel / \"resources\" / \"scalac-plugin.xml\" ->\n      s\"\"\"<plugin>\n         |    <name>$pluginName</name>\n         |    <classname>localhost.DivByZero</classname>\n         |</plugin>\n         |\"\"\".stripMargin,\n    os.rel / s\"$pluginName.scala\" ->\n      s\"\"\"//> using resourceDir ./resources/\n         |\n         |package localhost\n         |\n         |import scala.tools.nsc\n         |import nsc.Global\n         |import nsc.Phase\n         |import nsc.plugins.Plugin\n         |import nsc.plugins.PluginComponent\n         |\n         |class DivByZero(val global: Global) extends Plugin {\n         |  import global._\n         |\n         |  val name = \"$pluginName\"\n         |  val description = \"checks for division by zero\"\n         |  val components = List[PluginComponent](Component)\n         |\n         |  private object Component extends PluginComponent {\n         |    val global: DivByZero.this.global.type = DivByZero.this.global\n         |    val runsAfter = List[String](\"refchecks\")\n         |    val phaseName = DivByZero.this.name\n         |    def newPhase(_prev: Phase) = new DivByZeroPhase(_prev)\n         |    class DivByZeroPhase(prev: Phase) extends StdPhase(prev) {\n         |      override def name = DivByZero.this.name\n         |      def apply(unit: CompilationUnit): Unit = {\n         |        for ( tree @ Apply(Select(rcvr, nme.DIV), List(Literal(Constant(0)))) <- unit.body\n         |              if rcvr.tpe <:< definitions.IntClass.tpe)\n         |        {\n         |          global.reporter.error(tree.pos, \"$pluginErrorMsg\")\n         |        }\n         |      }\n         |    }\n         |  }\n         |}\n         |\"\"\".stripMargin\n  )\n\n  def compilerPluginForScala3(pluginName: String, pluginErrorMsg: String): TestInputs = TestInputs(\n    os.rel / \"resources\" / \"plugin.properties\" ->\n      s\"\"\"pluginClass=localhost.DivideZero\n         |\"\"\".stripMargin,\n    os.rel / s\"$pluginName.scala\" ->\n      s\"\"\"//> using resourceDir ./resources/\n         |\n         |package localhost\n         |\n         |import dotty.tools.dotc.ast.Trees.*\n         |import dotty.tools.dotc.ast.tpd\n         |import dotty.tools.dotc.core.Constants.Constant\n         |import dotty.tools.dotc.core.Contexts.Context\n         |import dotty.tools.dotc.core.Decorators.*\n         |import dotty.tools.dotc.core.StdNames.*\n         |import dotty.tools.dotc.core.Symbols.*\n         |import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin}\n         |import dotty.tools.dotc.transform.{Pickler, Staging}\n         |import dotty.tools.dotc.report\n         |\n         |class DivideZero extends StandardPlugin:\n         |  val name: String = \"$pluginName\"\n         |  override val description: String = \"checks for division by zero\"\n         |\n         |  override def init(options: List[String]): List[PluginPhase] =\n         |    (new DivideZeroPhase) :: Nil\n         |\n         |class DivideZeroPhase extends PluginPhase:\n         |  import tpd.*\n         |\n         |  val phaseName = \"$pluginName\"\n         |\n         |  override val runsAfter = Set(Pickler.name)\n         |  override val runsBefore = Set(Staging.name)\n         |\n         |  override def transformApply(tree: Apply)(implicit ctx: Context): Tree =\n         |    tree match\n         |      case Apply(Select(rcvr, nme.DIV), List(Literal(Constant(0))))\n         |      if rcvr.tpe <:< defn.IntType =>\n         |        report.error(\"$pluginErrorMsg\", tree.sourcePos)\n         |      case _ =>\n         |        ()\n         |    tree\n         |end DivideZeroPhase\n         |\"\"\".stripMargin\n  )\n}\n"
  },
  {
    "path": "modules/integration/src/test/scala/scala/cli/integration/util/DockerServer.scala",
    "content": "package scala.cli.integration.util\n\n// adapted from https://github.com/coursier/coursier/blob/2cb552ea934a100f52ce79f94278304c90d808a4/modules/proxy-tests/src/it/scala/coursier/test/DockerServer.scala\n\nimport com.spotify.docker.client.DefaultDockerClient\nimport com.spotify.docker.client.messages.{ContainerConfig, HostConfig, PortBinding}\n\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Try\n\nfinal case class DockerServer(\n  base: String,\n  shutdown: () => Unit,\n  address: String\n)\n\nobject DockerServer {\n  def withServer[T](\n    image: String,\n    basePath: String,\n    // can't find a way to get back a randomly assigned port (even following https://github.com/spotify/docker-client/issues/625)\n    // so that one has to be specified\n    portMapping: (Int, Int)\n  )(f: DockerServer => T): T = {\n\n    val (imagePort, hostPort) = portMapping\n    val addr                  = s\"localhost:$hostPort\"\n\n    def log(s: String): Unit =\n      Console.err.println(s\"[$image @ $addr] $s\")\n\n    val docker = DefaultDockerClient.fromEnv().build()\n    docker.pull(image)\n\n    val portBindings = Map(imagePort.toString -> Seq(PortBinding.of(\"0.0.0.0\", hostPort)).asJava)\n\n    val hostConfig = HostConfig.builder().portBindings(portBindings.asJava).build()\n\n    val containerConfig = ContainerConfig.builder()\n      .hostConfig(hostConfig)\n      .image(image)\n      .exposedPorts(portBindings.keys.toSeq*)\n      .build()\n\n    var idOpt = Option.empty[String]\n\n    def shutdown(): Unit =\n      for (id <- idOpt) {\n        Try(docker.killContainer(id))\n        docker.removeContainer(id)\n        docker.close()\n      }\n\n    try {\n      val creation = docker.createContainer(containerConfig)\n\n      val id = creation.id()\n      idOpt = Some(id)\n\n      log(s\"starting container $id\")\n      docker.startContainer(id)\n\n      val base = s\"http://localhost:$hostPort/$basePath\"\n\n      log(s\"waiting for $image server to be up-and-running\")\n\n      Thread.sleep(2000L)\n\n      val server = DockerServer(base, () => shutdown(), addr)\n      f(server)\n    }\n    finally\n      shutdown()\n  }\n}\n"
  },
  {
    "path": "modules/java-test-runner/src/main/java/scala/build/testrunner/JavaAsmTestRunner.java",
    "content": "package scala.build.testrunner;\n\nimport org.objectweb.asm.*;\nimport sbt.testing.*;\n\nimport java.io.*;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.*;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Stream;\nimport java.util.zip.*;\n\npublic class JavaAsmTestRunner {\n\n    public static class ParentInspector {\n        private final List<Path> classPath;\n        private final JavaTestLogger logger;\n        private final ConcurrentHashMap<String, List<String>> cache = new ConcurrentHashMap<>();\n\n        public ParentInspector(List<Path> classPath, JavaTestLogger logger) {\n            this.classPath = classPath;\n            this.logger = logger;\n        }\n\n        private List<String> parents(String className) {\n            return cache.computeIfAbsent(className, name -> {\n                byte[] byteCode = findInClassPath(classPath, name + \".class\", logger);\n                if (byteCode == null) return Collections.emptyList();\n                TestClassChecker checker = new TestClassChecker();\n                ClassReader reader = new ClassReader(byteCode);\n                reader.accept(checker, 0);\n                return checker.getImplements();\n            });\n        }\n\n        public List<String> allParents(String className) {\n            List<String> result = new ArrayList<>();\n            Set<String> done = new HashSet<>();\n            Deque<String> todo = new ArrayDeque<>();\n            todo.add(className);\n            while (!todo.isEmpty()) {\n                String current = todo.poll();\n                if (!done.add(current)) continue;\n                result.add(current);\n                todo.addAll(parents(current));\n            }\n            return result;\n        }\n    }\n\n    public static Optional<Fingerprint> matchFingerprints(\n        String className,\n        InputStream byteCodeStream,\n        List<Fingerprint> fingerprints,\n        ParentInspector parentInspector,\n        ClassLoader loader,\n        JavaTestLogger logger\n    ) throws IOException {\n        TestClassChecker checker = new TestClassChecker();\n        ClassReader reader = new ClassReader(byteCodeStream);\n        reader.accept(checker, 0);\n\n        boolean isModule = className.endsWith(\"$\");\n        boolean hasPublicConstructors = checker.getPublicConstructorCount() > 0;\n        boolean definitelyNoTests = checker.isAbstract() ||\n            checker.isInterface() ||\n            checker.getPublicConstructorCount() > 1 ||\n            isModule == hasPublicConstructors;\n\n        if (definitelyNoTests) return Optional.empty();\n\n        for (Fingerprint fp : fingerprints) {\n            if (fp instanceof SubclassFingerprint) {\n                SubclassFingerprint sf = (SubclassFingerprint) fp;\n                if (sf.isModule() != isModule) continue;\n                String superName = sf.superclassName().replace('.', '/');\n                if (parentInspector.allParents(checker.getName()).contains(superName)) {\n                    return Optional.of(fp);\n                }\n            } else if (fp instanceof AnnotatedFingerprint) {\n                AnnotatedFingerprint af = (AnnotatedFingerprint) fp;\n                if (af.isModule() != isModule) continue;\n                // Use classloader-based reflection for annotation matching (proven approach)\n                if (loader != null) {\n                    try {\n                        String rawName = className.replace('/', '.').replace('\\\\', '.');\n                        String clsNameForLoad = rawName.endsWith(\"$\") ? rawName.substring(0, rawName.length() - 1) : rawName;\n                        Class<?> cls = loader.loadClass(clsNameForLoad);\n                        Optional<Fingerprint> result =\n                            JavaFrameworkUtils.matchFingerprints(loader, cls, new Fingerprint[]{fp}, logger);\n                        if (result.isPresent()) return Optional.of(fp);\n                    } catch (ClassNotFoundException | NoClassDefFoundError |\n                             UnsupportedClassVersionError | IncompatibleClassChangeError e) {\n                        // Expected: class may not be loadable during scanning\n                        logger.debug(\n                            \"Could not load class for annotation matching: \" + className + \" (\" + e + \")\");\n                    }\n                }\n            }\n        }\n        return Optional.empty();\n    }\n\n    public static List<String> findFrameworkServices(List<Path> classPath, JavaTestLogger logger) {\n        List<String> result = new ArrayList<>();\n        byte[] content = findInClassPath(classPath, \"META-INF/services/sbt.testing.Framework\", logger);\n        if (content != null) {\n            parseServiceFileContent(new String(content, StandardCharsets.UTF_8), result);\n        }\n        return result;\n    }\n\n    private static void parseServiceFileContent(String content, List<String> result) {\n        for (String line : content.split(\"[\\r\\n]+\")) {\n            String trimmed = line.trim();\n            if (!trimmed.isEmpty() && !trimmed.startsWith(\"#\")) {\n                result.add(trimmed);\n            }\n        }\n    }\n\n    public static List<String> findFrameworks(\n        List<Path> classPath,\n        List<String> preferredClasses,\n        ParentInspector parentInspector,\n        JavaTestLogger logger\n    ) {\n        List<String> result = new ArrayList<>();\n        // first check preferred classes\n        for (String preferred : preferredClasses) {\n            String resourceName = preferred.replace('.', '/') + \".class\";\n            byte[] bytes = findInClassPath(classPath, resourceName, logger);\n            if (bytes != null) {\n                TestClassChecker checker = new TestClassChecker();\n                new ClassReader(bytes).accept(checker, 0);\n                if (!checker.isAbstract() && checker.getPublicConstructorCount() == 1) {\n                    String internalName = preferred.replace('.', '/');\n                    if (parentInspector.allParents(internalName).contains(\"sbt/testing/Framework\")) {\n                        result.add(internalName);\n                    }\n                }\n            }\n        }\n        if (!result.isEmpty()) return result;\n\n        // scan all classes in classpath\n        for (Map.Entry<String, byte[]> entry : listClassesByteCode(classPath, true, logger).entrySet()) {\n            String name = entry.getKey();\n            if (name.contains(\"module-info\")) continue;\n            TestClassChecker checker = new TestClassChecker();\n            new ClassReader(entry.getValue()).accept(checker, 0);\n            if (!checker.isAbstract() && checker.getPublicConstructorCount() == 1) {\n                if (parentInspector.allParents(name).contains(\"sbt/testing/Framework\")) {\n                    result.add(name);\n                }\n            }\n        }\n        return result;\n    }\n\n    public static List<TaskDef> taskDefs(\n        List<Path> classPath,\n        boolean keepJars,\n        List<Fingerprint> fingerprints,\n        ParentInspector parentInspector,\n        ClassLoader loader,\n        JavaTestLogger logger\n    ) {\n        List<TaskDef> result = new ArrayList<>();\n        for (Map.Entry<String, byte[]> entry : listClassesByteCode(classPath, keepJars, logger).entrySet()) {\n            String name = entry.getKey();\n            if (name.contains(\"module-info\")) continue;\n            try {\n                Optional<Fingerprint> fp = matchFingerprints(\n                    name,\n                    new ByteArrayInputStream(entry.getValue()),\n                    fingerprints,\n                    parentInspector,\n                    loader,\n                    logger\n                );\n                if (fp.isPresent()) {\n                    String stripped = name.endsWith(\"$\") ? name.substring(0, name.length() - 1) : name;\n                    String clsName = stripped.replace('/', '.').replace('\\\\', '.');\n                    result.add(new TaskDef(clsName, fp.get(), false, new Selector[]{new SuiteSelector()}));\n                }\n            } catch (IOException e) {\n                logger.debug(\"Could not read bytecode for \" + name + \": \" + e.getMessage());\n            }\n        }\n        return result;\n    }\n\n    private static Map<String, byte[]> listClassesByteCode(\n        List<Path> classPath, boolean keepJars, JavaTestLogger logger\n    ) {\n        Map<String, byte[]> result = new LinkedHashMap<>();\n        for (Path entry : classPath) {\n            result.putAll(listClassesByteCode(entry, keepJars, logger));\n        }\n        return result;\n    }\n\n    private static Map<String, byte[]> listClassesByteCode(\n        Path entry, boolean keepJars, JavaTestLogger logger\n    ) {\n        Map<String, byte[]> result = new LinkedHashMap<>();\n        if (Files.isDirectory(entry)) {\n            try (Stream<Path> stream = Files.walk(entry, Integer.MAX_VALUE)) {\n                stream.filter(p -> p.getFileName().toString().endsWith(\".class\"))\n                    .forEach(p -> {\n                        String rel = entry.relativize(p).toString().replace('\\\\', '/');\n                        String name = rel.endsWith(\".class\") ? rel.substring(0, rel.length() - 6) : rel;\n                        try {\n                            result.put(name, Files.readAllBytes(p));\n                        } catch (IOException e) {\n                            logger.debug(\"Could not read class file \" + p + \": \" + e.getMessage());\n                        }\n                    });\n            } catch (IOException e) {\n                logger.log(\"Could not walk directory \" + entry + \": \" + e.getMessage());\n            }\n        } else if (keepJars && Files.isRegularFile(entry)) {\n            byte[] buf = new byte[16384];\n            try (ZipFile zf = new ZipFile(entry.toFile())) {\n                Enumeration<? extends ZipEntry> entries = zf.entries();\n                while (entries.hasMoreElements()) {\n                    ZipEntry ze = entries.nextElement();\n                    if (!ze.getName().endsWith(\".class\")) continue;\n                    String name = ze.getName();\n                    name = name.substring(0, name.length() - 6);\n                    ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                    try (InputStream is = zf.getInputStream(ze)) {\n                        int read;\n                        while ((read = is.read(buf)) >= 0) {\n                            baos.write(buf, 0, read);\n                        }\n                    }\n                    result.put(name, baos.toByteArray());\n                }\n            } catch (IOException e) {\n                logger.log(\"Could not read JAR \" + entry + \": \" + e.getMessage());\n            }\n        }\n        return result;\n    }\n\n    static byte[] findInClassPath(List<Path> classPath, String name, JavaTestLogger logger) {\n        for (Path entry : classPath) {\n            byte[] found = findInClassPathEntry(entry, name, logger);\n            if (found != null) return found;\n        }\n        return null;\n    }\n\n    private static byte[] findInClassPathEntry(Path entry, String name, JavaTestLogger logger) {\n        if (Files.isDirectory(entry)) {\n            Path p = entry.resolve(name);\n            if (Files.isRegularFile(p)) {\n                try {\n                    return Files.readAllBytes(p);\n                } catch (IOException e) {\n                    logger.debug(\"Could not read \" + p + \": \" + e.getMessage());\n                    return null;\n                }\n            }\n        } else if (Files.isRegularFile(entry)) {\n            byte[] buf = new byte[16384];\n            try (ZipFile zf = new ZipFile(entry.toFile())) {\n                ZipEntry ze = zf.getEntry(name);\n                if (ze == null) return null;\n                ByteArrayOutputStream baos = new ByteArrayOutputStream();\n                try (InputStream is = zf.getInputStream(ze)) {\n                    int read;\n                    while ((read = is.read(buf)) >= 0) {\n                        baos.write(buf, 0, read);\n                    }\n                }\n                return baos.toByteArray();\n            } catch (IOException e) {\n                logger.debug(\"Could not read \" + name + \" from \" + entry + \": \" + e.getMessage());\n                return null;\n            }\n        }\n        return null;\n    }\n\n    public static class TestClassChecker extends ClassVisitor {\n        private String name;\n        private int publicConstructorCount = 0;\n        private boolean isInterface = false;\n        private boolean isAbstract = false;\n        private List<String> implementsList = new ArrayList<>();\n\n        public TestClassChecker() {\n            super(Opcodes.ASM9);\n        }\n\n        @Override\n        public void visit(int version, int access, String name, String signature,\n                          String superName, String[] interfaces) {\n            this.name = name;\n            this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0;\n            this.isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;\n            if (superName != null) implementsList.add(superName);\n            if (interfaces != null) {\n                for (String iface : interfaces) {\n                    implementsList.add(iface);\n                }\n            }\n        }\n\n        @Override\n        public MethodVisitor visitMethod(int access, String name, String descriptor,\n                                         String signature, String[] exceptions) {\n            if (\"<init>\".equals(name) && (access & Opcodes.ACC_PUBLIC) != 0) {\n                publicConstructorCount++;\n            }\n            return null;\n        }\n\n        public String getName() { return name; }\n        public int getPublicConstructorCount() { return publicConstructorCount; }\n        public boolean isInterface() { return isInterface; }\n        public boolean isAbstract() { return isAbstract; }\n        public List<String> getImplements() { return implementsList; }\n    }\n}\n"
  },
  {
    "path": "modules/java-test-runner/src/main/java/scala/build/testrunner/JavaDynamicTestRunner.java",
    "content": "package scala.build.testrunner;\n\nimport sbt.testing.*;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.regex.Pattern;\n\npublic class JavaDynamicTestRunner {\n\n    /**\n     * Based on junit-interface GlobFilter.compileGlobPattern:\n     * https://github.com/sbt/junit-interface/blob/f8c6372ed01ce86f15393b890323d96afbe6d594/src/main/java/com/novocode/junit/GlobFilter.java#L37\n     *\n     * Converts a glob expression (only * supported) into a regex Pattern.\n     */\n    private static Pattern globPattern(String expr) {\n        String[] parts = expr.split(\"\\\\*\", -1);\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < parts.length; i++) {\n            if (i != 0) sb.append(\".*\");\n            if (!parts[i].isEmpty()) sb.append(Pattern.quote(parts[i].replace(\"\\n\", \"\\\\n\")));\n        }\n        return Pattern.compile(sb.toString());\n    }\n\n    public static void main(String[] args) {\n        List<String> testFrameworks = new ArrayList<>();\n        List<String> remainingArgs = new ArrayList<>();\n        boolean requireTests = false;\n        int verbosity = 0;\n        Optional<String> testOnly = Optional.empty();\n\n        boolean pastDashDash = false;\n        for (String arg : args) {\n            if (pastDashDash) {\n                remainingArgs.add(arg);\n            } else if (\"--\".equals(arg)) {\n                pastDashDash = true;\n            } else if (arg.startsWith(\"--test-framework=\")) {\n                testFrameworks.add(arg.substring(\"--test-framework=\".length()));\n            } else if (arg.startsWith(\"--test-only=\")) {\n                testOnly = Optional.of(arg.substring(\"--test-only=\".length()));\n            } else if (arg.startsWith(\"--verbosity=\")) {\n                try {\n                    verbosity = Integer.parseInt(arg.substring(\"--verbosity=\".length()));\n                } catch (NumberFormatException e) {\n                    System.err.println(\"Warning: malformed --verbosity value: \" + arg);\n                }\n            } else if (\"--require-tests\".equals(arg)) {\n                requireTests = true;\n            } else {\n                remainingArgs.add(arg);\n            }\n        }\n\n        JavaTestLogger logger = new JavaTestLogger(verbosity, System.err);\n\n        if (!testFrameworks.isEmpty()) {\n            logger.debug(\"Directly passed \" + testFrameworks.size() + \" test frameworks:\\n  - \" +\n                String.join(\"\\n  - \", testFrameworks));\n        }\n\n        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n        java.util.List<java.nio.file.Path> classPath0 = JavaTestRunner.classPath(classLoader, logger);\n\n        List<Framework> frameworks;\n        if (!testFrameworks.isEmpty()) {\n            frameworks = new ArrayList<>();\n            for (String fw : testFrameworks) {\n                try {\n                    frameworks.add(JavaFrameworkUtils.loadFramework(classLoader, fw));\n                } catch (Exception e) {\n                    logger.error(\"Could not load test framework: \" + fw);\n                    logger.error(e.toString());\n                    System.exit(1);\n                }\n            }\n        } else {\n            List<Framework> frameworkServices = JavaFrameworkUtils.findFrameworkServices(classLoader);\n            List<Framework> scannedFrameworks = JavaFrameworkUtils.findFrameworks(\n                classPath0, classLoader, JavaTestRunner.commonTestFrameworks(), logger\n            );\n            List<Framework> toRun = JavaFrameworkUtils.getFrameworksToRun(\n                frameworkServices, scannedFrameworks, logger\n            );\n            if (toRun.isEmpty()) {\n                if (verbosity >= 2) {\n                    throw new RuntimeException(\"No test framework found\");\n                } else {\n                    System.err.println(\"No test framework found\");\n                    System.exit(1);\n                }\n            }\n            frameworks = toRun;\n        }\n\n        String[] runnerArgs = remainingArgs.toArray(new String[0]);\n        final Optional<String> testOnlyFinal = testOnly;\n        final boolean requireTestsFinal = requireTests;\n\n        boolean anyFailed = false;\n        for (Framework framework : frameworks) {\n            logger.log(\"Running test framework: \" + framework.name());\n            Fingerprint[] fingerprints = framework.fingerprints();\n            Runner runner = framework.runner(runnerArgs, new String[0], classLoader);\n\n            List<Class<?>> classes = new ArrayList<>();\n            for (String name : JavaFrameworkUtils.listClasses(classPath0, false, logger)) {\n                try {\n                    classes.add(classLoader.loadClass(name));\n                } catch (ClassNotFoundException | NoClassDefFoundError |\n                         UnsupportedClassVersionError | IncompatibleClassChangeError e) {\n                    // Expected: not every .class file on the classpath is loadable\n                    logger.debug(\"Could not load class \" + name + \": \" + e);\n                }\n            }\n\n            List<TaskDef> taskDefs = new ArrayList<>();\n            for (Class<?> cls : classes) {\n                Optional<Fingerprint> fp = JavaFrameworkUtils.matchFingerprints(\n                    classLoader, cls, fingerprints, logger\n                );\n                if (!fp.isPresent()) continue;\n                String clsName = cls.getName().endsWith(\"$\")\n                    ? cls.getName().substring(0, cls.getName().length() - 1)\n                    : cls.getName();\n                if (testOnlyFinal.isPresent()) {\n                    Pattern pat = globPattern(testOnlyFinal.get());\n                    if (!pat.matcher(clsName).matches()) continue;\n                }\n                taskDefs.add(new TaskDef(clsName, fp.get(), false, new Selector[]{new SuiteSelector()}));\n            }\n\n            Task[] initialTasks = runner.tasks(taskDefs.toArray(new TaskDef[0]));\n            List<Event> events = JavaTestRunner.runTasks(Arrays.asList(initialTasks), System.out);\n\n            boolean failed = events.stream().anyMatch(ev ->\n                ev.status() == Status.Error ||\n                ev.status() == Status.Failure ||\n                ev.status() == Status.Canceled\n            );\n\n            String doneMsg = runner.done();\n            if (doneMsg != null && !doneMsg.isEmpty()) System.out.println(doneMsg);\n\n            if (requireTestsFinal && events.isEmpty()) {\n                logger.error(\"Error: no tests were run for \" + framework.name() + \".\");\n                anyFailed = true;\n            } else if (failed) {\n                logger.error(\"Error: \" + framework.name() + \" tests failed.\");\n                anyFailed = true;\n            } else {\n                logger.log(framework.name() + \" tests ran successfully.\");\n            }\n        }\n\n        System.exit(anyFailed ? 1 : 0);\n    }\n}\n"
  },
  {
    "path": "modules/java-test-runner/src/main/java/scala/build/testrunner/JavaFrameworkUtils.java",
    "content": "package scala.build.testrunner;\n\nimport sbt.testing.*;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Modifier;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.*;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.stream.Stream;\n\npublic class JavaFrameworkUtils {\n\n    public static List<Framework> findFrameworkServices(ClassLoader loader) {\n        List<Framework> result = new ArrayList<>();\n        ServiceLoader<Framework> serviceLoader = ServiceLoader.load(Framework.class, loader);\n        for (Framework f : serviceLoader) {\n            result.add(f);\n        }\n        return result;\n    }\n\n    public static Framework loadFramework(ClassLoader loader, String className) throws Exception {\n        Class<?> cls = loader.loadClass(className);\n        return (Framework) cls.getConstructor().newInstance();\n    }\n\n    public static List<Framework> findFrameworks(\n        List<Path> classPath,\n        ClassLoader loader,\n        List<String> preferredClasses,\n        JavaTestLogger logger\n    ) {\n        Class<?> frameworkCls = Framework.class;\n        List<Framework> result = new ArrayList<>();\n        Set<String> seen = new LinkedHashSet<>();\n\n        // first try preferred classes, then scan classpath\n        List<String> candidates = new ArrayList<>(preferredClasses);\n        for (String name : listClasses(classPath, true, logger)) {\n            if (!seen.contains(name)) {\n                candidates.add(name);\n            }\n        }\n\n        for (String name : candidates) {\n            if (!seen.add(name)) continue;\n            Class<?> cls;\n            try {\n                cls = loader.loadClass(name);\n            } catch (ClassNotFoundException | UnsupportedClassVersionError |\n                     NoClassDefFoundError | IncompatibleClassChangeError e) {\n                // Expected: most classpath entries aren't test frameworks\n                continue;\n            }\n            if (!frameworkCls.isAssignableFrom(cls)) continue;\n            if (Modifier.isAbstract(cls.getModifiers())) continue;\n            long publicNoArgCtors = Arrays.stream(cls.getConstructors())\n                .filter(c -> Modifier.isPublic(c.getModifiers()) && c.getParameterCount() == 0)\n                .count();\n            if (publicNoArgCtors != 1) continue;\n            try {\n                Framework instance = (Framework) cls.getConstructor().newInstance();\n                result.add(instance);\n            } catch (Exception e) {\n                logger.log(\"Could not instantiate framework \" + name + \": \" + e);\n            }\n        }\n        return result;\n    }\n\n    public static Optional<Fingerprint> matchFingerprints(\n        ClassLoader loader,\n        Class<?> cls,\n        Fingerprint[] fingerprints,\n        JavaTestLogger logger\n    ) {\n        boolean isModule = cls.getName().endsWith(\"$\");\n        long publicCtorCount = Arrays.stream(cls.getConstructors())\n            .filter(c -> Modifier.isPublic(c.getModifiers()))\n            .count();\n        boolean noPublicConstructors = publicCtorCount == 0;\n        boolean definitelyNoTests = Modifier.isAbstract(cls.getModifiers()) ||\n            cls.isInterface() ||\n            publicCtorCount > 1 ||\n            isModule != noPublicConstructors;\n        if (definitelyNoTests) return Optional.empty();\n\n        for (Fingerprint fp : fingerprints) {\n            if (fp instanceof SubclassFingerprint) {\n                SubclassFingerprint sf = (SubclassFingerprint) fp;\n                if (sf.isModule() != isModule) continue;\n                try {\n                    Class<?> superCls = loader.loadClass(sf.superclassName());\n                    if (superCls.isAssignableFrom(cls)) return Optional.of(fp);\n                } catch (ClassNotFoundException e) {\n                    logger.debug(\n                        \"Superclass not found for fingerprint matching: \" + sf.superclassName());\n                }\n            } else if (fp instanceof AnnotatedFingerprint) {\n                AnnotatedFingerprint af = (AnnotatedFingerprint) fp;\n                if (af.isModule() != isModule) continue;\n                try {\n                    @SuppressWarnings(\"unchecked\")\n                    Class<? extends Annotation> annotationCls =\n                        (Class<? extends Annotation>) loader.loadClass(af.annotationName());\n                    boolean matches =\n                        cls.isAnnotationPresent(annotationCls) ||\n                        Arrays.stream(cls.getDeclaredMethods())\n                            .anyMatch(m -> m.isAnnotationPresent(annotationCls)) ||\n                        Arrays.stream(cls.getMethods())\n                            .anyMatch(m -> m.isAnnotationPresent(annotationCls) &&\n                                          Modifier.isPublic(m.getModifiers()));\n                    if (matches) return Optional.of(fp);\n                } catch (ClassNotFoundException e) {\n                    logger.debug(\n                        \"Annotation class not found for fingerprint matching: \" + af.annotationName());\n                }\n            }\n        }\n        return Optional.empty();\n    }\n\n    public static List<Framework> getFrameworksToRun(\n        List<Framework> frameworkServices,\n        List<Framework> frameworks,\n        JavaTestLogger logger\n    ) {\n        List<Framework> all = new ArrayList<>(frameworkServices);\n        all.addAll(frameworks);\n        return getFrameworksToRun(all, logger);\n    }\n\n    public static List<Framework> getFrameworksToRun(\n        List<Framework> allFrameworks,\n        JavaTestLogger logger\n    ) {\n        // dedup by name\n        Map<String, Framework> byName = new LinkedHashMap<>();\n        for (Framework f : allFrameworks) {\n            byName.putIfAbsent(f.name(), f);\n        }\n        List<Framework> distinct = new ArrayList<>(byName.values());\n\n        // filter out frameworks that are superclasses of another framework in the list\n        List<Framework> finalFrameworks = new ArrayList<>();\n        for (Framework f1 : distinct) {\n            boolean isInherited = distinct.stream()\n                .filter(f2 -> f2 != f1)\n                .anyMatch(f2 -> f1.getClass().isAssignableFrom(f2.getClass()));\n            if (!isInherited) finalFrameworks.add(f1);\n        }\n        return finalFrameworks;\n    }\n\n    public static List<String> listClasses(List<Path> classPath, boolean keepJars, JavaTestLogger logger) {\n        List<String> result = new ArrayList<>();\n        for (Path entry : classPath) {\n            result.addAll(listClasses(entry, keepJars, logger));\n        }\n        return result;\n    }\n\n    public static List<String> listClasses(Path entry, boolean keepJars, JavaTestLogger logger) {\n        List<String> result = new ArrayList<>();\n        if (Files.isDirectory(entry)) {\n            try (Stream<Path> stream = Files.walk(entry, Integer.MAX_VALUE)) {\n                stream.filter(p -> p.getFileName().toString().endsWith(\".class\"))\n                    .map(entry::relativize)\n                    .map(p -> {\n                        StringBuilder sb = new StringBuilder();\n                        for (int i = 0; i < p.getNameCount(); i++) {\n                            if (i > 0) sb.append(\".\");\n                            sb.append(p.getName(i).toString());\n                        }\n                        String name = sb.toString();\n                        return name.endsWith(\".class\") ? name.substring(0, name.length() - 6) : name;\n                    })\n                    .forEach(result::add);\n            } catch (Exception e) {\n                logger.log(\"Could not walk directory \" + entry + \": \" + e.getMessage());\n            }\n        } else if (keepJars && Files.isRegularFile(entry)) {\n            try (ZipFile zf = new ZipFile(entry.toFile())) {\n                Enumeration<? extends ZipEntry> entries = zf.entries();\n                while (entries.hasMoreElements()) {\n                    ZipEntry ze = entries.nextElement();\n                    String name = ze.getName();\n                    if (name.endsWith(\".class\")) {\n                        result.add(name.substring(0, name.length() - 6).replace(\"/\", \".\"));\n                    }\n                }\n            } catch (Exception e) {\n                logger.log(\"Could not read JAR \" + entry + \": \" + e.getMessage());\n            }\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "modules/java-test-runner/src/main/java/scala/build/testrunner/JavaTestLogger.java",
    "content": "package scala.build.testrunner;\n\nimport java.io.PrintStream;\n\npublic class JavaTestLogger {\n    private final int verbosity;\n    private final PrintStream out;\n\n    public JavaTestLogger(int verbosity, PrintStream out) {\n        this.verbosity = verbosity;\n        this.out = out;\n    }\n\n    public void error(String message) {\n        out.println(message);\n    }\n\n    public void message(String message) {\n        if (verbosity >= 0) out.println(message);\n    }\n\n    public void log(String message) {\n        if (verbosity >= 1) out.println(message);\n    }\n\n    public void debug(String message) {\n        if (verbosity >= 2) out.println(message);\n    }\n}\n"
  },
  {
    "path": "modules/java-test-runner/src/main/java/scala/build/testrunner/JavaTestRunner.java",
    "content": "package scala.build.testrunner;\n\nimport sbt.testing.*;\n\nimport java.io.File;\nimport java.io.PrintStream;\nimport java.net.URLClassLoader;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Deque;\nimport java.util.List;\n\npublic class JavaTestRunner {\n\n    public static List<String> commonTestFrameworks() {\n        // Only pure-Java-compatible frameworks belong here.\n        // Scala-only frameworks (munit, utest, ScalaCheck, ZIO Test, ScalaTest, weaver)\n        // live in the Scala test-runner's TestRunner.commonTestFrameworks instead.\n        return Arrays.asList(\"com.novocode.junit.JUnitFramework\");\n    }\n\n    public static List<Path> classPath(ClassLoader loader, JavaTestLogger logger) {\n        List<Path> result = new ArrayList<>();\n        collectClassPath(loader, result, logger);\n        return result;\n    }\n\n    private static void collectClassPath(ClassLoader loader, List<Path> result, JavaTestLogger logger) {\n        if (loader == null) return;\n        if (loader instanceof URLClassLoader) {\n            URLClassLoader urlLoader = (URLClassLoader) loader;\n            for (java.net.URL url : urlLoader.getURLs()) {\n                if (\"file\".equals(url.getProtocol())) {\n                    try {\n                        result.add(Paths.get(url.toURI()).toAbsolutePath());\n                    } catch (Exception e) {\n                        logger.debug(\n                            \"Could not convert URL to path: \" + url + \" (\" + e.getMessage() + \")\");\n                    }\n                }\n            }\n        } else if (loader.getClass().getName().equals(\"jdk.internal.loader.ClassLoaders$AppClassLoader\")) {\n            String cp = System.getProperty(\"java.class.path\", \"\");\n            for (String entry : cp.split(File.pathSeparator)) {\n                if (!entry.isEmpty()) {\n                    result.add(Paths.get(entry));\n                }\n            }\n        }\n        collectClassPath(loader.getParent(), result, logger);\n    }\n\n    public static List<Event> runTasks(List<Task> initialTasks, PrintStream out) {\n        Deque<Task> tasks = new ArrayDeque<>(initialTasks);\n        List<Event> events = new ArrayList<>();\n\n        sbt.testing.Logger logger = new sbt.testing.Logger() {\n            public boolean ansiCodesSupported() { return true; }\n            public void error(String msg) { out.println(msg); }\n            public void warn(String msg) { out.println(msg); }\n            public void info(String msg) { out.println(msg); }\n            public void debug(String msg) { out.println(msg); }\n            public void trace(Throwable t) { t.printStackTrace(out); }\n        };\n\n        EventHandler eventHandler = event -> events.add(event);\n        sbt.testing.Logger[] loggers = new sbt.testing.Logger[]{logger};\n\n        while (!tasks.isEmpty()) {\n            Task task = tasks.poll();\n            Task[] newTasks = task.execute(eventHandler, loggers);\n            for (Task t : newTasks) {\n                tasks.add(t);\n            }\n        }\n\n        return events;\n    }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/Artifacts.scala",
    "content": "package scala.build\n\nimport coursier.cache.FileCache\nimport coursier.core.{Classifier, Module, ModuleName, Organization, Repository, Version}\nimport coursier.error.ResolutionError\nimport coursier.util.Task\nimport coursier.version.VersionConstraint\nimport coursier.{\n  Dependency as CsDependency,\n  Fetch,\n  Resolution,\n  core as csCore,\n  util as csUtil\n}\nimport dependency.*\n\nimport java.net.URL\n\nimport scala.build.CoursierUtils.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Ops.*\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  CoursierDependencyError,\n  FetchingDependenciesError,\n  NoScalaVersionProvidedError,\n  ToolkitVersionError\n}\nimport scala.build.internal.Constants\nimport scala.build.internal.Constants.*\nimport scala.build.internal.CsLoggerUtil.*\nimport scala.build.internal.Util.{PositionedScalaDependencyOps, safeFullDetailedArtifacts}\nimport scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix\nimport scala.collection.mutable\n\nfinal case class Artifacts(\n  javacPluginDependencies: Seq[(AnyDependency, String, os.Path)],\n  extraJavacPlugins: Seq[os.Path],\n  defaultDependencies: Seq[AnyDependency],\n  extraDependencies: Seq[AnyDependency],\n  userCompileOnlyDependencies: Seq[AnyDependency],\n  internalDependencies: Seq[AnyDependency],\n  detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)],\n  detailedRuntimeArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)],\n  extraClassPath: Seq[os.Path],\n  extraCompileOnlyJars: Seq[os.Path],\n  extraRuntimeClassPath: Seq[os.Path],\n  extraSourceJars: Seq[os.Path],\n  scalaOpt: Option[ScalaArtifacts],\n  hasJvmRunner: Boolean,\n  hasJavaTestRunner: Boolean,\n  resolution: Option[Resolution]\n) {\n\n  def userDependencies                  = defaultDependencies ++ extraDependencies\n  lazy val jarsForUserExtraDependencies = {\n    val extraDependenciesMap =\n      extraDependencies.map(dep => dep.module.name -> dep.version).toMap\n    detailedArtifacts\n      .iterator\n      .collect {\n        case (dep, pub, _, path)\n            if pub.classifier != Classifier.sources &&\n            extraDependenciesMap.get(dep.module.name.value).contains(\n              dep.versionConstraint.asString\n            ) => path\n      }\n      .toVector\n      .distinct\n  }\n\n  lazy val artifacts: Seq[(String, os.Path)] =\n    detailedArtifacts\n      .iterator\n      .collect {\n        case (_, pub, a, f) if pub.classifier != Classifier.sources =>\n          (a.url, f)\n      }\n      .toVector\n      .distinct\n  lazy val runtimeArtifacts: Seq[(String, os.Path)] =\n    detailedRuntimeArtifacts\n      .iterator\n      .collect {\n        case (_, pub, a, f) if pub.classifier != Classifier.sources =>\n          (a.url, f)\n      }\n      .toVector\n      .distinct\n  lazy val sourceArtifacts: Seq[(String, os.Path)] =\n    detailedArtifacts\n      .iterator\n      .collect {\n        case (_, pub, a, f) if pub.classifier == Classifier.sources =>\n          (a.url, f)\n      }\n      .toVector\n      .distinct\n  lazy val classPath: Seq[os.Path] =\n    runtimeArtifacts.map(_._2) ++ extraClassPath ++ extraRuntimeClassPath\n  lazy val compileClassPath: Seq[os.Path] =\n    artifacts.map(_._2) ++ extraClassPath ++ extraCompileOnlyJars\n  lazy val sourcePath: Seq[os.Path] =\n    sourceArtifacts.map(_._2) ++ extraSourceJars\n}\n\nobject Artifacts {\n\n  final case class ScalaArtifactsParams(\n    params: ScalaParameters,\n    compilerPlugins: Seq[Positioned[AnyDependency]],\n    addJsTestBridge: Option[String],\n    addNativeTestInterface: Option[String],\n    scalaJsVersion: Option[String],\n    scalaJsCliVersion: Option[String],\n    scalaNativeCliVersion: Option[String],\n    addScalapy: Option[String]\n  )\n\n  def apply(\n    scalaArtifactsParamsOpt: Option[ScalaArtifactsParams],\n    javacPluginDependencies: Seq[Positioned[AnyDependency]],\n    extraJavacPlugins: Seq[os.Path],\n    defaultDependencies: Seq[Positioned[AnyDependency]],\n    extraDependencies: Seq[Positioned[AnyDependency]],\n    compileOnlyDependencies: Seq[Positioned[AnyDependency]],\n    extraClassPath: Seq[os.Path],\n    extraCompileOnlyJars: Seq[os.Path],\n    extraSourceJars: Seq[os.Path],\n    fetchSources: Boolean,\n    jvmVersion: Int,\n    addJvmRunner: Option[Boolean],\n    addJvmTestRunner: Boolean,\n    addJvmJavaTestRunner: Boolean,\n    addJmhDependencies: Option[String],\n    extraRepositories: Seq[Repository],\n    keepResolution: Boolean,\n    includeBuildServerDeps: Boolean,\n    cache: FileCache[Task],\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  ): Either[BuildException, Artifacts] = either {\n    val dependencies = defaultDependencies ++ extraDependencies\n\n    val scalaVersion = (for {\n      scalaArtifactsParams <- scalaArtifactsParamsOpt\n      scalaParams  = scalaArtifactsParams.params\n      scalaVersion = scalaParams.scalaVersion\n    } yield scalaVersion).getOrElse(defaultScalaVersion)\n\n    val shouldUseLegacyJava8Runners  = jvmVersion < Constants.scala38MinJavaVersion\n    val shouldUseLegacyScala3Runners =\n      scalaVersion.startsWith(\"3\") &&\n      scalaVersion.coursierVersion < s\"$scala3LtsPrefix.0\".coursierVersion\n    val shouldUseLegacyScala2Runners = scalaVersion.startsWith(\"2\")\n    val shouldUseLegacyScalaRunners  = shouldUseLegacyScala3Runners || shouldUseLegacyScala2Runners\n    val shouldUseLegacyRunners       = shouldUseLegacyScalaRunners || shouldUseLegacyJava8Runners\n\n    val jvmTestRunnerDependencies =\n      if addJvmTestRunner then {\n        val runnerLegacyVersion =\n          if scalaVersion.startsWith(\"3\")\n          then runnerScala30LegacyVersion\n          else runnerScala2LegacyVersion\n        val testRunnerVersion0 =\n          if shouldUseLegacyRunners then {\n            if shouldUseLegacyScalaRunners then\n              logger.message(\n                s\"$warnPrefix Scala $scalaVersion is no longer supported by the test-runner module.\"\n              )\n            if shouldUseLegacyJava8Runners then\n              logger.message(\n                s\"$warnPrefix Java $jvmVersion is no longer supported by the test-runner module.\"\n              )\n            logger.message(\n              s\"$warnPrefix Defaulting to a legacy test-runner module version: $runnerLegacyVersion.\"\n            )\n            if shouldUseLegacyScalaRunners then\n              logger.message(\n                s\"$warnPrefix To use the latest test-runner, upgrade Scala to at least $scala3LtsPrefix.\"\n              )\n            if shouldUseLegacyJava8Runners then\n              logger.message(\n                s\"$warnPrefix To use the latest test-runner, upgrade Java to at least ${Constants.defaultJavaVersion}.\"\n              )\n            runnerLegacyVersion\n          }\n          else testRunnerVersion\n        Seq(dep\"$testRunnerOrganization::$testRunnerModuleName:$testRunnerVersion0\")\n      }\n      else Nil\n\n    val jvmJavaTestRunnerDependencies =\n      if addJvmJavaTestRunner then\n        Seq(\n          dep\"${Constants.javaTestRunnerOrganization}:${Constants.javaTestRunnerModuleName}:${Constants.javaTestRunnerVersion}\"\n        )\n      else Nil\n\n    val jmhDependencies = addJmhDependencies.toSeq\n      .map(version => dep\"${Constants.jmhOrg}:${Constants.jmhGeneratorBytecodeModule}:$version\")\n\n    val maybeSnapshotRepo = {\n      val hasSnapshots = jvmTestRunnerDependencies.exists(_.version.endsWith(\"SNAPSHOT\")) ||\n        jvmJavaTestRunnerDependencies.exists(_.version.endsWith(\"SNAPSHOT\")) ||\n        scalaArtifactsParamsOpt.flatMap(_.scalaNativeCliVersion).exists(_.endsWith(\"SNAPSHOT\"))\n      val hasNightlies = scalaArtifactsParamsOpt.exists(a =>\n        a.params.scalaVersion.endsWith(\"-NIGHTLY\") ||\n        a.params.scalaBinaryVersion.endsWith(\"-NIGHTLY\")\n      )\n      if hasSnapshots || hasNightlies then\n        Seq(\n          RepositoryUtils.snapshotsRepository,\n          RepositoryUtils.scala3NightlyRepository\n        )\n      else Nil\n    }\n\n    val allExtraRepositories = (maybeSnapshotRepo ++ extraRepositories).distinct\n\n    val scalaOpt = scalaArtifactsParamsOpt match {\n      case Some(scalaArtifactsParams) =>\n        val compilerDependencies =\n          if (scalaArtifactsParams.params.scalaVersion.startsWith(\"3.\"))\n            Seq(\n              dep\"org.scala-lang::scala3-compiler:${scalaArtifactsParams.params.scalaVersion}\"\n            )\n          else\n            Seq(\n              dep\"org.scala-lang:scala-compiler:${scalaArtifactsParams.params.scalaVersion}\"\n            )\n        val compilerDependenciesMessage =\n          s\"Downloading Scala ${scalaArtifactsParams.params.scalaVersion} compiler\"\n\n        val compilerPlugins0 = value {\n          scalaArtifactsParams.compilerPlugins\n            .map { posDep =>\n              val posDep0 =\n                posDep.map(dep =>\n                  dep.copy(userParams = dep.userParams ++ Seq(\"intransitive\" -> None))\n                )\n              artifacts(\n                Seq(posDep0),\n                allExtraRepositories,\n                Some(scalaArtifactsParams.params),\n                logger,\n                cache.withMessage(s\"Downloading compiler plugin ${posDep.value.render}\")\n              ).map(_.map { case (url, path) => (posDep0.value, url, path) })\n            }\n            .sequence\n            .left.flatMap {\n              CompositeBuildException(_)\n                .maybeRecoverWithDefault(Seq.empty, maybeRecoverOnError)\n            }\n            .map(_.flatten)\n        }\n\n        val compilerArtifacts = value {\n          artifacts(\n            compilerDependencies.map(Positioned.none),\n            allExtraRepositories,\n            Some(scalaArtifactsParams.params),\n            logger,\n            cache.withMessage(compilerDependenciesMessage)\n          ).left.flatMap(_.maybeRecoverWithDefault(Seq.empty, maybeRecoverOnError))\n        }\n\n        val bridgeJarsOpt =\n          (scalaArtifactsParams.params.scalaVersion -> includeBuildServerDeps match {\n            case (sv, true) if sv.startsWith(\"3.\") =>\n              Some(Seq(\n                dep\"org.scala-lang:scala3-sbt-bridge:${scalaArtifactsParams.params.scalaVersion}\"\n              ))\n            case (sv, true)\n                if Version(sv) >= Version(\"2.13.12\") ||\n                sv.startsWith(Constants.defaultScala213Version) =>\n              Some(Seq(\n                dep\"org.scala-lang:scala2-sbt-bridge:${scalaArtifactsParams.params.scalaVersion}\"\n              ))\n            case _ => None\n          })\n            .map { bridgeDependencies =>\n              value {\n                artifacts(\n                  bridgeDependencies.map(Positioned.none),\n                  (allExtraRepositories ++ Seq(RepositoryUtils.scala3NightlyRepository)).distinct,\n                  Some(scalaArtifactsParams.params),\n                  logger,\n                  cache.withMessage(\n                    s\"Downloading Scala ${scalaArtifactsParams.params.scalaVersion} bridge\"\n                  )\n                ).left.flatMap(_.maybeRecoverWithDefault(Seq.empty, maybeRecoverOnError))\n              }.map(_._2)\n            }\n\n        def fetchedArtifactToPath(fetched: Fetch.Result): Either[BuildException, Seq[os.Path]] =\n          either {\n            value(fetched.fullDetailedArtifacts0.safeFullDetailedArtifacts)\n              .collect { case (_, _, _, Some(f)) => os.Path(f, Os.pwd) }\n          }\n\n        val scalaJsCliDependency =\n          scalaArtifactsParams.scalaJsCliVersion.map { scalaJsCliVersion =>\n            val mod = Module(\n              Organization(\"org.virtuslab.scala-cli\"),\n              ModuleName(s\"scalajscli_2.13\"),\n              Map.empty\n            )\n            Seq(coursier.Dependency(mod, VersionConstraint(s\"$scalaJsCliVersion+\")))\n          }\n\n        val fetchedScalaJsCli = scalaJsCliDependency match {\n          case Some(dependency) =>\n            val forcedVersions = Seq(\n              cmod\"org.scala-js:scalajs-linker_2.13\" -> VersionConstraint(scalaJsVersion)\n            )\n            Some {\n              val (_, res) = value {\n                fetchCsDependencies(\n                  dependencies = dependency.map(Positioned.none),\n                  extraRepositories = allExtraRepositories,\n                  forceScalaVersionOpt = None,\n                  forcedVersions = forcedVersions,\n                  logger = logger,\n                  cache = cache.withMessage(\"Downloading Scala.js CLI\"),\n                  classifiersOpt = None\n                )\n              }\n              res\n            }\n          case None =>\n            None\n        }\n\n        val scalaJsCli = value {\n          fetchedScalaJsCli.toSeq\n            .map(fetchedArtifactToPath)\n            .sequence\n            .map(_.flatten)\n            .left\n            .map(CompositeBuildException(_))\n        }\n\n        val scalaNativeCliDependency =\n          scalaArtifactsParams.scalaNativeCliVersion.map { version =>\n            val module = cmod\"org.scala-native:scala-native-cli_2.12\"\n            Seq(coursier.Dependency(module, VersionConstraint(version)))\n          }\n\n        val fetchedScalaNativeCli: Option[Fetch.Result] = scalaNativeCliDependency match {\n          case Some(dependency) =>\n            Some {\n              val (_, res) = value {\n                fetchCsDependencies(\n                  dependencies = dependency.map(Positioned.none),\n                  extraRepositories = allExtraRepositories,\n                  forceScalaVersionOpt = None,\n                  forcedVersions = Nil,\n                  logger = logger,\n                  cache = cache.withMessage(\"Downloading Scala Native CLI\"),\n                  classifiersOpt = None\n                )\n              }\n              res\n            }\n          case None =>\n            None\n        }\n\n        val scalaNativeCli = value {\n          fetchedScalaNativeCli.toSeq\n            .map(fetchedArtifactToPath)\n            .sequence\n            .map(_.flatten)\n            .left\n            .map(CompositeBuildException(_))\n        }\n\n        val jsTestBridgeDependencies =\n          scalaArtifactsParams.addJsTestBridge.toSeq.map { scalaJsVersion =>\n            if (scalaArtifactsParams.params.scalaVersion.startsWith(\"2.\"))\n              dep\"org.scala-js::scalajs-test-bridge:$scalaJsVersion\"\n            else\n              dep\"org.scala-js:scalajs-test-bridge_2.13:$scalaJsVersion\"\n          }\n        val nativeTestInterfaceDependencies =\n          scalaArtifactsParams.addNativeTestInterface.toSeq.map { scalaNativeVersion =>\n            dep\"org.scala-native::test-interface::$scalaNativeVersion\"\n          }\n\n        val scalapyDependencies = scalaArtifactsParams.addScalapy match {\n          case Some(scalaPyVersion) =>\n            Seq(dep\"${scalaPyOrganization(scalaPyVersion)}::scalapy-core::$scalaPyVersion\")\n          case None =>\n            Nil\n        }\n\n        val internalDependencies =\n          jsTestBridgeDependencies ++\n            nativeTestInterfaceDependencies\n\n        val scala = ScalaArtifacts(\n          compilerDependencies,\n          compilerArtifacts,\n          compilerPlugins0,\n          scalaJsCli,\n          scalaNativeCli,\n          internalDependencies,\n          scalapyDependencies,\n          scalaArtifactsParams.params,\n          bridgeJarsOpt\n        )\n        Some(scala)\n\n      case None =>\n        None\n    }\n\n    val internalDependencies =\n      jvmTestRunnerDependencies.map(Positioned.none) ++\n        jvmJavaTestRunnerDependencies.map(Positioned.none) ++\n        scalaOpt.toSeq.flatMap(_.internalDependencies).map(Positioned.none) ++\n        jmhDependencies.map(Positioned.none)\n    val updatedDependencies = dependencies ++\n      scalaOpt.toSeq.flatMap(_.extraDependencies).map(Positioned.none) ++\n      internalDependencies\n    val allUpdatedDependencies = compileOnlyDependencies ++ updatedDependencies\n\n    val updatedDependenciesMessage = {\n      val b           = new mutable.StringBuilder(\"Downloading \")\n      val depLen      = dependencies.length\n      val extraDepLen = allUpdatedDependencies.length - depLen\n      depLen match {\n        case 1          => b.append(\"one dependency\")\n        case n if n > 1 => b.append(s\"$n dependencies\")\n        case _          =>\n      }\n\n      if (depLen > 0 && extraDepLen > 0)\n        b.append(\" and \")\n\n      extraDepLen match {\n        case 1          => b.append(\"one internal dependency\")\n        case n if n > 1 => b.append(s\"$n internal dependencies\")\n        case _          =>\n      }\n\n      b.result()\n    }\n\n    val (fetcher: Fetch[Task], fetchRes: Fetch.Result) = value {\n      fetchAnyDependenciesWithResult(\n        allUpdatedDependencies,\n        allExtraRepositories,\n        scalaArtifactsParamsOpt.map(_.params),\n        logger,\n        cache.withMessage(updatedDependenciesMessage),\n        classifiersOpt = Some(Set(\"_\") ++ (if (fetchSources) Set(\"sources\") else Set.empty)),\n        maybeRecoverOnError\n      )\n    }\n\n    val updatedDependencies0 = value {\n      coursierDeps(\n        updatedDependencies,\n        scalaArtifactsParamsOpt.map(_.params),\n        maybeRecoverOnError\n      )\n    }\n    val runtimeRes = value {\n      val resolution = value {\n        fetchRes.resolution.subset0(updatedDependencies0.map(_._2._1))\n          .left.map(CoursierDependencyError(_))\n      }\n      // this is actually fetcher.artifacts, which is a private field…\n      val artifacts = coursier.Artifacts()\n        .withCache(fetcher.cache)\n        .withClassifiers(fetcher.classifiers)\n        .withMainArtifactsOpt(fetcher.mainArtifactsOpt)\n        .withArtifactTypesOpt(fetcher.artifactTypesOpt)\n        .withExtraArtifactsSeq(fetcher.extraArtifactsSeq)\n        .withClasspathOrder(fetcher.classpathOrder)\n        .withTransformArtifacts(fetcher.transformArtifacts)\n      artifacts\n        .withResolution(resolution)\n        .runResult()\n        .fullDetailedArtifacts0\n        .safeFullDetailedArtifacts\n    }\n\n    val (hasRunner, extraRunnerJars) =\n      if scalaOpt.nonEmpty then {\n        val addJvmRunner0 = addJvmRunner.getOrElse(false)\n        val runnerJars    =\n          if addJvmRunner0 then {\n            val maybeSnapshotRepo =\n              if runnerVersion.endsWith(\"SNAPSHOT\") then\n                Seq(\n                  RepositoryUtils.snapshotsRepository,\n                  RepositoryUtils.scala3NightlyRepository\n                )\n              else Nil\n            val runnerVersion0 =\n              if shouldUseLegacyRunners then {\n                val runnerLegacyVersion =\n                  if shouldUseLegacyScala3Runners\n                  then runnerScala30LegacyVersion\n                  else runnerScala2LegacyVersion\n                if shouldUseLegacyScalaRunners then\n                  logger.message(\n                    s\"$warnPrefix Scala $scalaVersion is no longer supported by the runner module.\"\n                  )\n                if shouldUseLegacyJava8Runners then\n                  logger.message(\n                    s\"$warnPrefix Java $jvmVersion is no longer supported by the runner module.\"\n                  )\n                logger.message(\n                  s\"$warnPrefix Defaulting to a legacy runner module version: $runnerLegacyVersion.\"\n                )\n                if shouldUseLegacyScalaRunners then\n                  logger.message(\n                    s\"$warnPrefix To use the latest runner, upgrade Scala to at least $scala3LtsPrefix.\"\n                  )\n                if shouldUseLegacyJava8Runners then\n                  logger.message(\n                    s\"$warnPrefix To use the latest runner, upgrade Java to at least ${Constants.defaultJavaVersion}.\"\n                  )\n                logger.message(\n                  s\"\"\"$warnPrefix Scala $scalaVersion is no longer supported by the runner module.\n                     |$warnPrefix Defaulting to a legacy runner module version: $runnerLegacyVersion.\n                     |$warnPrefix To use the latest runner, upgrade Scala to at least $scala3LtsPrefix.\"\"\"\n                    .stripMargin\n                )\n                runnerLegacyVersion\n              }\n              else runnerVersion\n            value {\n              artifacts(\n                Seq(Positioned.none(\n                  dep\"$runnerOrganization::$runnerModuleName:$runnerVersion0,intransitive\"\n                )),\n                extraRepositories ++ maybeSnapshotRepo,\n                scalaArtifactsParamsOpt.map(_.params),\n                logger,\n                cache.withMessage(\"Downloading runner dependency\")\n              ).map(_.map(_._2))\n            }\n          }\n          else\n            Nil\n\n        (addJvmRunner0, runnerJars)\n      }\n      else\n        (false, Nil)\n\n    val javacPlugins0 = value {\n      javacPluginDependencies\n        .map { posDep =>\n          val cache0 = cache.withMessage(s\"Downloading javac plugin ${posDep.value.render}\")\n          artifacts(\n            Seq(posDep),\n            allExtraRepositories,\n            scalaArtifactsParamsOpt.map(_.params),\n            logger,\n            cache0\n          )\n            .map(_.map { case (url, path) => (posDep.value, url, path) })\n        }\n        .sequence\n        .left.map(CompositeBuildException(_))\n        .map(_.flatten)\n    }\n\n    val detailedArtifacts =\n      value(fetchRes.fullDetailedArtifacts0.safeFullDetailedArtifacts)\n        .collect { case (d, p, a, Some(f)) => (d, p, a, os.Path(f, Os.pwd)) }\n    Artifacts(\n      javacPlugins0,\n      extraJavacPlugins,\n      defaultDependencies.map(_.value),\n      extraDependencies.map(_.value) ++ scalaOpt.toSeq.flatMap(_.extraDependencies),\n      compileOnlyDependencies.map(_.value),\n      internalDependencies.map(_.value),\n      detailedArtifacts,\n      runtimeRes.collect { case (d, p, a, Some(f)) =>\n        (d, p, a, os.Path(f, Os.pwd))\n      },\n      extraClassPath,\n      extraCompileOnlyJars,\n      extraRunnerJars,\n      extraSourceJars,\n      scalaOpt,\n      hasRunner,\n      addJvmJavaTestRunner,\n      if (keepResolution) Some(fetchRes.resolution) else None\n    )\n  }\n\n  def scalaPyOrganization(version: String): String = {\n    def sortAfterPlus(v: String) = coursier.core.Version(v.replace(\"+\", \"-\"))\n    if (sortAfterPlus(version).compareTo(sortAfterPlus(\"0.5.2+9-623f0807\")) < 0)\n      \"me.shadaj\"\n    else\n      \"dev.scalapy\"\n  }\n\n  private[build] def artifacts(\n    dependencies: Seq[Positioned[AnyDependency]],\n    extraRepositories: Seq[Repository],\n    paramsOpt: Option[ScalaParameters],\n    logger: Logger,\n    cache: FileCache[Task],\n    classifiersOpt: Option[Set[String]] = None\n  ): Either[BuildException, Seq[(String, os.Path)]] = either {\n    val res =\n      value(fetchAnyDependencies(\n        dependencies,\n        extraRepositories,\n        paramsOpt,\n        logger,\n        cache,\n        classifiersOpt\n      ))\n    val result = res\n      .artifacts\n      .iterator\n      .map { case (a, f) => (a.url, os.Path(f, Os.pwd)) }\n      .toList\n    logger.debug {\n      val elems = Seq(s\"Found ${result.length} artifacts:\") ++\n        result.map(\"  \" + _._2) ++\n        Seq(\"\")\n      elems.mkString(System.lineSeparator())\n    }\n    result\n  }\n\n  def coursierDeps(\n    dependencies: Seq[Positioned[AnyDependency]],\n    paramsOpt: Option[ScalaParameters],\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  ): Either[BuildException, Seq[Positioned[(\n    CsDependency,\n    Option[((Module, VersionConstraint), (URL, Boolean))]\n  )]]] =\n    dependencies\n      .map(dep =>\n        dep.toCs(paramsOpt)\n          .map { case Positioned(pos, csDep) => Positioned(pos, (dep.value, csDep)) }\n      )\n      .map(_.left.map(maybeRecoverOnError))\n      .flatMap {\n        case Left(Some(e: NoScalaVersionProvidedError)) => Some(Left(e))\n        case Left(_)                                    => None\n        case Right(depTuple)                            => Some(Right(depTuple))\n      }\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map(positionedDepTupleSeq =>\n        positionedDepTupleSeq.map {\n          case Positioned(positions, (dep, csDep)) =>\n            val maybeUrl = dep.userParams.find(_._1 == \"url\").flatMap(_._2.map(new URL(_)))\n            val fallback = maybeUrl.map(url =>\n              (csDep.module -> csDep.versionConstraint) -> (url -> true)\n            )\n            Positioned(positions, (csDep, fallback))\n        }\n      )\n\n  def fetchAnyDependencies(\n    dependencies: Seq[Positioned[AnyDependency]],\n    extraRepositories: Seq[Repository],\n    paramsOpt: Option[ScalaParameters],\n    logger: Logger,\n    cache: FileCache[Task],\n    classifiersOpt: Option[Set[String]],\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)\n  ): Either[BuildException, Fetch.Result] = either {\n    val (_, res) = value {\n      fetchAnyDependenciesWithResult(\n        dependencies,\n        extraRepositories,\n        paramsOpt,\n        logger,\n        cache,\n        classifiersOpt,\n        maybeRecoverOnError\n      )\n    }\n    res\n  }\n\n  private def fetchAnyDependenciesWithResult(\n    dependencies: Seq[Positioned[AnyDependency]],\n    extraRepositories: Seq[Repository],\n    paramsOpt: Option[ScalaParameters],\n    logger: Logger,\n    cache: FileCache[Task],\n    classifiersOpt: Option[Set[String]],\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  ): Either[BuildException, (coursier.Fetch[Task], coursier.Fetch.Result)] = either {\n    val coursierDependenciesWithFallbacks: Seq[Positioned[(\n      CsDependency,\n      Option[((Module, VersionConstraint), (URL, Boolean))]\n    )]] = value {\n      coursierDeps(dependencies, paramsOpt, maybeRecoverOnError)\n    }\n\n    val coursierDependencies: Seq[Positioned[CsDependency]] =\n      coursierDependenciesWithFallbacks.map(_.map(_._1))\n    val fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)] =\n      coursierDependenciesWithFallbacks.map(_.value)\n        .flatMap(_._2)\n        .toMap\n\n    value {\n      fetchCsDependencies(\n        dependencies = coursierDependencies,\n        extraRepositories = extraRepositories,\n        forceScalaVersionOpt = paramsOpt.map(_.scalaVersion),\n        forcedVersions = Nil,\n        logger = logger,\n        cache = cache,\n        classifiersOpt = classifiersOpt,\n        fallbacks = fallbacks\n      ).left.flatMap(_.maybeRecoverWithDefault(\n        (\n          fetcher(\n            dependencies = coursierDependencies,\n            extraRepositories = extraRepositories,\n            forceScalaVersionOpt = paramsOpt.map(_.scalaVersion),\n            forcedVersions = Nil,\n            cache = cache,\n            classifiersOpt = classifiersOpt,\n            fallbacks = fallbacks\n          ),\n          Fetch.Result()\n        ),\n        maybeRecoverOnError\n      ))\n    }\n  }\n\n  private def fetcher(\n    dependencies: Seq[Positioned[coursier.Dependency]],\n    extraRepositories: Seq[Repository],\n    forceScalaVersionOpt: Option[String],\n    forcedVersions: Seq[(coursier.Module, VersionConstraint)],\n    cache: FileCache[Task],\n    classifiersOpt: Option[Set[String]],\n    fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)]\n  ): coursier.Fetch[Task] = {\n\n    val fallbackRepository            = TemporaryInMemoryRepository(fallbacks)\n    val extraRepositoriesWithFallback = extraRepositories :+ fallbackRepository\n\n    val forceScalaVersions = forceScalaVersionOpt match {\n      case None     => Nil\n      case Some(sv) =>\n        val svc = VersionConstraint(sv)\n        if (sv.startsWith(\"2.\"))\n          Seq(\n            cmod\"org.scala-lang:scala-library\"  -> svc,\n            cmod\"org.scala-lang:scala-compiler\" -> svc,\n            cmod\"org.scala-lang:scala-reflect\"  -> svc\n          )\n        else\n          // FIXME Shouldn't we force the org.scala-lang:scala-library version too?\n          // (to a 2.13.x version)\n          Seq(\n            cmod\"org.scala-lang:scala3-library_3\"         -> svc,\n            cmod\"org.scala-lang:scala3-compiler_3\"        -> svc,\n            cmod\"org.scala-lang:scala3-interfaces_3\"      -> svc,\n            cmod\"org.scala-lang:scala3-tasty-inspector_3\" -> svc,\n            cmod\"org.scala-lang:tasty-core_3\"             -> svc\n          )\n    }\n\n    val forceVersion = forceScalaVersions ++ forcedVersions\n\n    // FIXME Many parameters that we could allow to customize here\n    val defaultFetcher = coursier.Fetch()\n    var fetcher        = defaultFetcher\n      .withCache(cache)\n      // repository order matters here, since in some cases coursier resolves only the head\n      .withRepositories(extraRepositoriesWithFallback ++ defaultFetcher.repositories)\n      .addDependencies(dependencies.map(_.value)*)\n      .mapResolutionParams(_.addForceVersion0(forceVersion*))\n    for (classifiers <- classifiersOpt) {\n      if (classifiers(\"_\"))\n        fetcher = fetcher.withMainArtifacts()\n      fetcher = fetcher\n        .addClassifiers(classifiers.toSeq.filter(_ != \"_\").map(coursier.Classifier(_))*)\n    }\n    fetcher\n  }\n\n  def fetchCsDependencies(\n    dependencies: Seq[Positioned[coursier.Dependency]],\n    extraRepositories: Seq[Repository],\n    forceScalaVersionOpt: Option[String],\n    forcedVersions: Seq[(coursier.Module, VersionConstraint)],\n    logger: Logger,\n    cache: FileCache[Task],\n    classifiersOpt: Option[Set[String]],\n    fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)] = Map.empty\n  ): Either[BuildException, (coursier.Fetch[Task], coursier.Fetch.Result)] = either {\n    logger.debug {\n      s\"Fetching ${dependencies.map(_.value)}\" +\n        (if (extraRepositories.isEmpty) \"\" else s\", adding $extraRepositories\")\n    }\n\n    val fetcher0 = fetcher(\n      dependencies = dependencies,\n      extraRepositories = extraRepositories,\n      forceScalaVersionOpt = forceScalaVersionOpt,\n      forcedVersions = forcedVersions,\n      cache = cache,\n      classifiersOpt = classifiersOpt,\n      fallbacks = fallbacks\n    )\n\n    val res = cache.logger.use {\n      fetcher0.eitherResult()\n    }\n    value {\n      res.left.map {\n        case ex: ResolutionError.Several =>\n          CompositeBuildException(\n            ex.errors.map(toFetchingDependenciesError(dependencies, _))\n          )\n        case ex: ResolutionError.Simple =>\n          toFetchingDependenciesError(dependencies, ex)\n        case ex => new FetchingDependenciesError(ex, dependencies.flatMap(_.positions))\n      }.map((fetcher0, _))\n    }\n  }\n\n  def toFetchingDependenciesError(\n    dependencies: Seq[Positioned[coursier.Dependency]],\n    resolutionError: coursier.error.ResolutionError.Simple\n  ) = resolutionError match {\n    case ex: ResolutionError.CantDownloadModule\n        if ex.module.name.value == s\"${Constants.toolkitName}_2.12\" ||\n        ex.module.name\n          .value == s\"${Constants.toolkitTestName}_2.12\" =>\n      val errorPositions = dependencies.collect {\n        case Positioned(pos, dep)\n            if ex.module == dep.module => pos\n      }.flatten\n      new ToolkitVersionError(\n        \"Toolkits do not support Scala 2.12\",\n        errorPositions\n      )\n    case ex: ResolutionError.CantDownloadModule =>\n      val errorPositions = dependencies.collect {\n        case Positioned(pos, dep)\n            if ex.module == dep.module => pos\n      }.flatten\n      new FetchingDependenciesError(ex, errorPositions)\n    case ex => new FetchingDependenciesError(ex, dependencies.flatMap(_.positions))\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/CoursierUtils.scala",
    "content": "package scala.build\n\nimport coursier.core.Module\nimport coursier.parse.ModuleParser\n\nimport scala.build.internal.Constants\nimport scala.quoted.*\n\ndef noArgs(args: Expr[Seq[Any]])(using Quotes): Unit = {} // TODO\n\ndef extractString(cs: Expr[StringContext])(using Quotes): String =\n  cs.value match {\n    // without Seq it brokes compiler! - check this!\n    case Some(StringContext(Seq(part: String))) =>\n      part\n    case _ =>\n      quotes.reflect.report.error(\"StringContext args must be statically known\")\n      ???\n  }\n\nobject CoursierUtils {\n  def parseModule(cs: Expr[StringContext], args: Expr[Seq[Any]])(using Quotes): Expr[Module] =\n    noArgs(args)\n    val modString = extractString(cs)\n    ModuleParser.module(modString, Constants.defaultScalaVersion) match {\n      case Left(error) =>\n        quotes.reflect.report.error(error)\n        ???\n      case Right(_) =>\n        '{\n          ModuleParser.module(\n            ${ Expr(modString) },\n            Constants.defaultScalaVersion\n          ).getOrElse(???)\n        }\n    }\n\n  extension (inline ctx: StringContext)\n    inline def cmod(inline args: Any*): Module =\n      ${ parseModule('ctx, 'args) }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/Positioned.scala",
    "content": "package scala.build\n\nimport scala.build.options.{ConfigMonoid, HashedType, ShadowingSeq}\n\nfinal case class Positioned[+T](\n  positions: Seq[Position],\n  value: T\n) {\n  def addPosition(position: Position*): Positioned[T] =\n    copy(positions = this.positions ++ position)\n  def addPositions(positions: Seq[Position]): Positioned[T] =\n    copy(positions = this.positions ++ positions)\n\n  def map[U](f: T => U): Positioned[U] =\n    copy(value = f(value))\n  def flatMap[U](f: T => Positioned[U]): Positioned[U] = {\n    val pos0 = f(value)\n    Positioned(\n      positions ++ pos0.positions,\n      pos0.value\n    )\n  }\n\n  def eitherSequence[L, R](using ev: T <:< Either[L, R]): Either[L, Positioned[R]] =\n    ev(value) match {\n      case Left(l)  => Left(l)\n      case Right(r) => Right(copy(value = r))\n    }\n}\n\nobject Positioned {\n  def apply[T](position: Position, value: T): Positioned[T] = Positioned(List(position), value)\n\n  def none[T](value: T): Positioned[T] =\n    Positioned(Nil, value)\n\n  def commandLine[T](value: T): Positioned[T] =\n    Positioned(List(Position.CommandLine()), value)\n\n  def sequence[T](seq: Seq[Positioned[T]]): Positioned[Seq[T]] = {\n    val allPositions = seq.flatMap(_.positions)\n    val value        = seq.map(_.value)\n    Positioned(allPositions, value)\n  }\n\n  implicit def hashedType[T](implicit underlying: HashedType[T]): HashedType[Positioned[T]] = {\n    t =>\n      underlying.hashedValue(t.value)\n  }\n\n  implicit def monoid[T](implicit underlying: ConfigMonoid[T]): ConfigMonoid[Positioned[T]] =\n    ConfigMonoid.instance(Positioned.none(underlying.zero)) {\n      (a, b) =>\n        Positioned(a.positions ++ b.positions, underlying.orElse(a.value, b.value))\n    }\n\n  implicit def ordering[T](implicit underlying: Ordering[T]): Ordering[Positioned[T]] =\n    Ordering.by(_.value)\n\n  implicit def keyOf[T](implicit\n    underlying: ShadowingSeq.KeyOf[T]\n  ): ShadowingSeq.KeyOf[Positioned[T]] =\n    ShadowingSeq.KeyOf(\n      pSeq => underlying.makeKey(pSeq.map(_.value)),\n      seq => underlying.groups(seq.map(_.value))\n    )\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/ScalaArtifacts.scala",
    "content": "package scala.build\n\nimport dependency.{AnyDependency, ScalaParameters}\n\nfinal case class ScalaArtifacts(\n  compilerDependencies: Seq[AnyDependency],\n  compilerArtifacts: Seq[(String, os.Path)],\n  compilerPlugins: Seq[(AnyDependency, String, os.Path)],\n  scalaJsCli: Seq[os.Path],\n  scalaNativeCli: Seq[os.Path],\n  internalDependencies: Seq[AnyDependency],\n  extraDependencies: Seq[AnyDependency],\n  params: ScalaParameters,\n  bridgeJarsOpt: Option[Seq[os.Path]]\n) {\n\n  lazy val compilerClassPath: Seq[os.Path] =\n    compilerArtifacts.map(_._2)\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/TemporaryInMemoryRepository.scala",
    "content": "package scala.build\n\nimport coursier.Type\nimport coursier.cache.{ConnectionBuilder, FileCache}\nimport coursier.core.*\nimport coursier.core.Repository.Fetch\nimport coursier.util.{Artifact, EitherT, Monad}\nimport coursier.version.{Version, VersionConstraint}\n\nimport java.io.{File, FileNotFoundException, IOException}\nimport java.net.{HttpURLConnection, URL, URLConnection}\n\nimport scala.util.Try\n\n/** Copied over from [[https://github.com/coursier/sbt-coursier]]. Should probably be removed in the\n  * future, when (and if) the source lands in coursier itself.\n  *\n  * [[https://github.com/coursier/sbt-coursier/blob/master/modules/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala]]\n  */\nobject TemporaryInMemoryRepository {\n\n  def closeConn(conn: URLConnection): Unit = {\n    Try(conn.getInputStream).toOption.filter(_ != null).foreach(_.close())\n    conn match {\n      case conn0: HttpURLConnection =>\n        Try(conn0.getErrorStream).toOption.filter(_ != null).foreach(_.close())\n        conn0.disconnect()\n      case _ =>\n    }\n  }\n\n  def exists(\n    url: URL,\n    localArtifactsShouldBeCached: Boolean\n  ): Boolean =\n    exists(url, localArtifactsShouldBeCached, None)\n\n  def exists(\n    url: URL,\n    localArtifactsShouldBeCached: Boolean,\n    cacheOpt: Option[FileCache[Nothing]]\n  ): Boolean = {\n\n    // Sometimes HEAD attempts fail even though standard GETs are fine.\n    // E.g. https://github.com/NetLogo/NetLogo/releases/download/5.3.1/NetLogo.jar\n    // returning 403s. Hence the second attempt below.\n\n    val protocolSpecificAttemptOpt = {\n\n      def ifFile: Option[Boolean] =\n        if (localArtifactsShouldBeCached && !new File(url.toURI).exists()) {\n          val cachePath = coursier.cache.CacheDefaults.location\n          // 'file' here stands for the protocol (e.g. it's https instead for https:// URLs)\n          Some(new File(cachePath, s\"file/${url.getPath}\").exists())\n        }\n        else\n          Some(new File(url.toURI).exists()) // FIXME Escaping / de-escaping needed here?\n\n      def ifHttp: Option[Boolean] = {\n        // HEAD request attempt, adapted from http://stackoverflow.com/questions/22541629/android-how-can-i-make-an-http-head-request/22545275#22545275\n\n        var conn: URLConnection = null\n        try {\n          conn = ConnectionBuilder(url.toURI.toASCIIString)\n            .withFollowHttpToHttpsRedirections(\n              cacheOpt.fold(false)(_.followHttpToHttpsRedirections)\n            )\n            .withFollowHttpsToHttpRedirections(\n              cacheOpt.fold(false)(_.followHttpsToHttpRedirections)\n            )\n            .withSslSocketFactoryOpt(cacheOpt.flatMap(_.sslSocketFactoryOpt))\n            .withHostnameVerifierOpt(cacheOpt.flatMap(_.hostnameVerifierOpt))\n            .withMethod(\"HEAD\")\n            .withMaxRedirectionsOpt(cacheOpt.flatMap(_.maxRedirections))\n            .connection()\n          // Even though the finally clause handles this too, this has to be run here, so that we return Some(true)\n          // iff this doesn't throw.\n          conn.getInputStream.close()\n          Some(true)\n        }\n        catch {\n          case _: FileNotFoundException => Some(false)\n          case _: IOException           => None // error other than not found\n        }\n        finally\n          if (conn != null)\n            closeConn(conn)\n      }\n\n      url.getProtocol match {\n        case \"file\"           => ifFile\n        case \"http\" | \"https\" => ifHttp\n        case _                => None\n      }\n    }\n\n    def genericAttempt: Boolean = {\n      var conn: URLConnection = null\n      try {\n        conn = url.openConnection()\n        // NOT setting request type to HEAD here.\n        conn.getInputStream.close()\n        true\n      }\n      catch {\n        case _: IOException => false\n      }\n      finally\n        if (conn != null)\n          closeConn(conn)\n    }\n\n    protocolSpecificAttemptOpt\n      .getOrElse(genericAttempt)\n  }\n\n  def apply(fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)])\n    : TemporaryInMemoryRepository =\n    new TemporaryInMemoryRepository(fallbacks, localArtifactsShouldBeCached = false, None)\n\n  def apply(\n    fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)],\n    localArtifactsShouldBeCached: Boolean\n  ): TemporaryInMemoryRepository =\n    new TemporaryInMemoryRepository(fallbacks, localArtifactsShouldBeCached, None)\n\n  def apply[F[_]](\n    fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)],\n    cache: FileCache[F]\n  ): TemporaryInMemoryRepository =\n    new TemporaryInMemoryRepository(\n      fallbacks,\n      localArtifactsShouldBeCached = cache.localArtifactsShouldBeCached,\n      Some(cache.asInstanceOf[FileCache[Nothing]])\n    )\n\n}\n\nfinal class TemporaryInMemoryRepository private (\n  val fallbacks: Map[(Module, VersionConstraint), (URL, Boolean)],\n  val localArtifactsShouldBeCached: Boolean,\n  val cacheOpt: Option[FileCache[Nothing]]\n) extends Repository {\n\n  @deprecated\n  override def find[F[_]](\n    module: Module,\n    version: String,\n    fetch: Repository.Fetch[F]\n  )(implicit\n    F: Monad[F]\n  ): EitherT[F, String, (ArtifactSource, Project)] =\n    find0(module, Version(version), fetch)\n\n  override def find0[F[_]](module: Module, version: Version, fetch: Fetch[F])(implicit\n    F: Monad[F]\n  ): EitherT[F, String, (ArtifactSource, Project)] = {\n\n    def res = fallbacks\n      .get((module, VersionConstraint.fromVersion(version)))\n      .fold[Either[String, (ArtifactSource, Project)]](Left(\"No fallback URL found\")) {\n        case (url, _) =>\n          val urlStr = url.toExternalForm\n          val idx    = urlStr.lastIndexOf('/')\n\n          if (idx < 0 || urlStr.endsWith(\"/\"))\n            Left(s\"$url doesn't point to a file\")\n          else {\n            val (dirUrlStr, fileName) = urlStr.splitAt(idx + 1)\n\n            if (TemporaryInMemoryRepository.exists(url, localArtifactsShouldBeCached, cacheOpt)) {\n              val proj = Project(\n                module = module,\n                version0 = version,\n                dependencies0 = Nil,\n                configurations = Map.empty,\n                parent0 = None,\n                dependencyManagement0 = Nil,\n                properties = Nil,\n                profiles = Nil,\n                versions = None,\n                snapshotVersioning = None,\n                packagingOpt = None,\n                relocated = false,\n                actualVersionOpt0 = None,\n                publications0 = Nil,\n                info = Info.empty,\n                overrides = Overrides.empty,\n                variants = Map.empty,\n                variantPublications = Map.empty\n              )\n\n              Right((this, proj))\n            }\n            else\n              Left(s\"$fileName not found under $dirUrlStr\")\n          }\n      }\n\n    // EitherT(F.bind(F.point(()))(_ => F.point(res)))\n    EitherT(F.map(F.point(()))(_ => res))\n  }\n\n  override def artifacts(\n    dependency: Dependency,\n    project: Project,\n    overrideClassifiers: Option[Seq[Classifier]]\n  ): Seq[(Publication, Artifact)] =\n    fallbacks\n      .get {\n        dependency.moduleVersionConstraint match\n          case (m, vc) => m -> vc\n      }\n      .toSeq\n      .map {\n        case (url, changing) =>\n          val url0 = url.toString\n          val ext  = url0.substring(url0.lastIndexOf('.') + 1)\n          val pub  = Publication(\n            dependency.module.name.value, // ???\n            Type(ext),\n            Extension(ext),\n            Classifier.empty\n          )\n          (pub, Artifact(url0, Map.empty, Map.empty, changing, optional = false, None))\n      }\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/actionable/ActionableDependencyHandler.scala",
    "content": "package scala.build.actionable\nimport coursier.cache.FileCache\nimport coursier.core.Repository\nimport coursier.util.Task\nimport coursier.version.{Latest, Version}\nimport dependency.*\n\nimport scala.build.EitherCps.*\nimport scala.build.actionable.ActionableDiagnostic.*\nimport scala.build.errors.{BuildException, Severity}\nimport scala.build.internal.Constants\nimport scala.build.internal.Util.*\nimport scala.build.options.BuildOptions\nimport scala.build.options.ScalaVersionUtil.versions\nimport scala.build.{Logger, Positioned}\n\ncase object ActionableDependencyHandler\n    extends ActionableHandler[ActionableDependencyUpdateDiagnostic] {\n  type Setting = Positioned[AnyDependency]\n\n  override def extractSettings(options: BuildOptions): Seq[Positioned[AnyDependency]] =\n    if (options.suppressWarningOptions.suppressOutdatedDependencyWarning.getOrElse(false))\n      Nil\n    else\n      options.classPathOptions.extraDependencies.toSeq\n\n  override def actionableDiagnostic(\n    setting: Positioned[AnyDependency],\n    buildOptions: BuildOptions,\n    loggerOpt: Option[Logger]\n  ): Either[BuildException, Option[ActionableDependencyUpdateDiagnostic]] = either {\n    val dependency       = setting.value\n    val currentVersion   = dependency.version\n    val latestVersionOpt = value(findLatestVersion(buildOptions, setting, loggerOpt))\n\n    for {\n      latestVersion <- latestVersionOpt\n      if Version(latestVersion) > Version(currentVersion) &&\n      !isLatestSyntaxVersion(currentVersion)\n      // filtering out toolkit-test to prevent double-update-diagnostic\n      if !(dependency.userParams.exists(_._1 == Constants.toolkitName) &&\n      dependency.module.name == Constants.toolkitTestName)\n    } yield\n      if dependency.userParams.exists(_._1 == Constants.toolkitName)\n      then\n        val toolkitSuggestion =\n          if dependency.module.organization == Constants.toolkitOrganization then latestVersion\n          else if dependency.module.organization == Constants.typelevelOrganization then\n            s\"typelevel:$latestVersion\"\n          else s\"${dependency.module.organization}:$latestVersion\"\n        ActionableDependencyUpdateDiagnostic(\n          setting.positions,\n          currentVersion,\n          latestVersion,\n          dependencyModuleName = Constants.toolkitName,\n          suggestion = toolkitSuggestion\n        )\n      else\n        ActionableDependencyUpdateDiagnostic(\n          setting.positions,\n          currentVersion,\n          latestVersion,\n          dependencyModuleName = dependency.module.name,\n          suggestion = dependency.copy(version = latestVersion).render\n        )\n  }\n\n  /** Versions like 'latest.*': 'latest.release', 'latest.integration', 'latest.stable'\n    */\n  private def isLatestSyntaxVersion(version: String): Boolean = Latest(version).nonEmpty\n  private def findLatestVersion(\n    buildOptions: BuildOptions,\n    setting: Positioned[AnyDependency],\n    loggerOpt: Option[Logger]\n  ): Either[BuildException, Option[String]] = either {\n    val dependency: AnyDependency            = setting.value\n    val scalaParams: Option[ScalaParameters] = value(buildOptions.scalaParams)\n    val cache: FileCache[Task]               = buildOptions.finalCache\n    val csModule: coursier.core.Module       = value(dependency.toCs(scalaParams)).module\n    val repositories: Seq[Repository]        = value(buildOptions.finalRepositories)\n\n    val latestVersionOpt = cache.versions(csModule, repositories)\n      .versions\n      .latest(Latest.Stable)\n\n    if (latestVersionOpt.isEmpty)\n      loggerOpt.foreach(_.diagnostic(\n        s\"No latest version found for ${dependency.render}\",\n        Severity.Warning,\n        setting.positions\n      ))\n\n    latestVersionOpt.map(_.asString)\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/actionable/ActionableDiagnostic.scala",
    "content": "package scala.build.actionable\nimport scala.build.Position\nimport scala.build.errors.Diagnostic.TextEdit\nimport scala.build.errors.{Diagnostic, Severity}\n\nobject ActionableDiagnostic {\n\n  case class ActionableDependencyUpdateDiagnostic(\n    positions: Seq[Position],\n    currentVersion: String,\n    newVersion: String,\n    dependencyModuleName: String,\n    suggestion: String\n  ) extends Diagnostic {\n    override def message: String =\n      s\"\"\"|\"$dependencyModuleName is outdated, update to $newVersion\"\n          |     $dependencyModuleName $currentVersion -> $suggestion\"\"\".stripMargin\n\n    override def textEdit: Option[TextEdit] = Some(TextEdit(message, suggestion))\n\n    override def severity: Severity = Severity.Hint\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/actionable/ActionableHandler.scala",
    "content": "package scala.build.actionable\n\nimport scala.build.Logger\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException, Diagnostic}\nimport scala.build.options.BuildOptions\n\ntrait ActionableHandler[A <: Diagnostic] {\n\n  /** Type of setting used to generate actionable diagnostic\n    */\n  type Setting\n\n  /** Extract settings on the basis of which actionable diagnostics will be generated\n    *\n    * @param options\n    *   the Build Options to extract settings\n    * @return\n    *   the list of settings on the basis of which actionable diagnostics will be generated\n    */\n  def extractSettings(options: BuildOptions): Seq[Setting]\n\n  /** The setting on the basis of which the Actionable Diagnostic is generated\n    *\n    * @param option\n    *   this option is used to generate an actionable diagnostic\n    * @param buildOptions\n    *   used to extract additional parameter from buildOptions, such as \"ScalaParams\" or \"Coursier\n    *   Cache\" See [[ActionableDependencyHandler]]\n    */\n  def actionableDiagnostic(\n    setting: Setting,\n    buildOptions: BuildOptions,\n    loggerOpt: Option[Logger]\n  ): Either[BuildException, Option[A]]\n\n  final def createActionableDiagnostics(\n    buildOptions: BuildOptions,\n    loggerOpt: Option[Logger] = None\n  ): Either[BuildException, Seq[A]] =\n    extractSettings(buildOptions)\n      .map(v => actionableDiagnostic(v, buildOptions, loggerOpt))\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map(_.flatten)\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/actionable/ActionablePreprocessor.scala",
    "content": "package scala.build.actionable\n\nimport scala.build.Ops.*\nimport scala.build.errors.{BuildException, CompositeBuildException, Diagnostic}\nimport scala.build.options.BuildOptions\n\nobject ActionablePreprocessor {\n  val actionableHandlers = Seq[ActionableHandler[?]](\n    ActionableDependencyHandler\n  )\n\n  def generateActionableDiagnostics(\n    options: BuildOptions\n  ): Either[BuildException, Seq[Diagnostic]] =\n    actionableHandlers\n      .map(handler => handler.createActionableDiagnostics(options))\n      .sequence\n      .left.map(CompositeBuildException(_))\n      .map(_.flatten)\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/actionable/errors/ActionableHandlerError.scala",
    "content": "package scala.build.actionable.errors\n\nimport scala.build.errors.BuildException\n\nfinal class ActionableHandlerError(message: String)\n    extends BuildException(message)\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/info/BuildInfo.scala",
    "content": "package scala.build.info\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.errors.{BuildException, BuildInfoGenerationError}\nimport scala.build.info.BuildInfo.escapeBackslashes\nimport scala.build.internal.Constants\nimport scala.build.options.*\n\nfinal case class BuildInfo(\n  projectVersion: Option[String] = None,\n  scalaVersion: Option[String] = None,\n  platform: Option[String] = None,\n  jvmVersion: Option[String] = None,\n  scalaJsVersion: Option[String] = None,\n  jsEsVersion: Option[String] = None,\n  scalaNativeVersion: Option[String] = None,\n  mainClass: Option[String] = None,\n  scopes: Map[String, ScopedBuildInfo] = Map.empty,\n  scalaCliVersion: Option[String] = None\n) {\n  def +(other: BuildInfo): BuildInfo =\n    BuildInfo.monoid.orElse(this, other)\n\n  def withScope(scopeName: String, scopedBuildInfo: ScopedBuildInfo): BuildInfo =\n    if (scopedBuildInfo.sources.isEmpty)\n      this.copy(\n        scopes = this.scopes + (scopeName -> ScopedBuildInfo.empty)\n      )\n    else\n      this.copy(\n        scopes = this.scopes + (scopeName -> scopedBuildInfo)\n      )\n\n  def generateContents(): String = {\n    val nl         = System.lineSeparator()\n    val indent     = \" \" * 2\n    val stringVals = Seq(\n      \"/** version of Scala used to compile this project */\",\n      s\"val scalaVersion = \\\"${escapeBackslashes(scalaVersion.getOrElse(Constants.defaultScalaVersion))}\\\"\",\n      \"/** target platform of this project, it can be \\\"JVM\\\" or \\\"JS\\\" or \\\"Native\\\" */\",\n      s\"val platform = \\\"${escapeBackslashes(platform.getOrElse(Platform.JVM.repr))}\\\"\"\n    )\n\n    val optionVals = Seq(\n      Seq(\n        \"/** version of JVM, if it's the target platform */\",\n        \"val jvmVersion =\"\n      ) -> jvmVersion,\n      Seq(\n        \"/** version of Scala.js, if it's the target platform */\",\n        \"val scalaJsVersion =\"\n      ) -> scalaJsVersion,\n      Seq(\n        \"/** Scala.js ECMA Script version, if Scala.js is the target platform */\",\n        \"val jsEsVersion =\"\n      ) -> jsEsVersion,\n      Seq(\n        \"/** version of Scala Native, if it's the target platform */\",\n        \"val scalaNativeVersion =\"\n      ) -> scalaNativeVersion,\n      Seq(\n        \"/** Main class specified for the project */\",\n        \"val mainClass =\"\n      ) -> mainClass,\n      Seq(\n        \"/** Project version */\",\n        \"val projectVersion =\"\n      ) -> projectVersion,\n      Seq(\n        \"/** Scala CLI version used for the compilation */\",\n        \"val scalaCliVersion =\"\n      ) -> scalaCliVersion\n    ).flatMap {\n      case (Seq(scaladoc, prefix), Some(v)) =>\n        Seq(scaladoc, s\"$prefix Some(\\\"${escapeBackslashes(v)}\\\")\")\n      case (Seq(scaladoc, prefix), None) =>\n        Seq(scaladoc, s\"$prefix None\")\n      case other => other._1\n    }\n\n    val allVals = stringVals ++ optionVals\n\n    val scopesContents =\n      for ((scopeName, scopedBuildInfo) <- scopes)\n        yield {\n          val scopedBuildInfoVals = scopedBuildInfo.generateContentLines()\n            .mkString(indent, nl + indent * 2, \"\")\n          s\"\"\"$indent/** Information about the ${scopeName.capitalize} scope */\n             |${indent}object ${scopeName.capitalize} {\n             |$indent$scopedBuildInfoVals\n             |$indent}\"\"\".stripMargin\n        }\n\n    s\"\"\"package scala.cli.build\n       |\n       |/** Information about the build gathered by Scala CLI */\n       |object BuildInfo {\n       |${allVals.mkString(indent, nl + indent, nl)}\n       |${scopesContents.mkString(nl * 2)}\n       |}\n       |\"\"\".stripMargin\n  }\n}\n\nobject BuildInfo {\n  def apply(\n    options: BuildOptions,\n    workspace: os.Path\n  ): Either[BuildException, BuildInfo] = either[Exception] {\n    Seq(\n      BuildInfo(\n        mainClass = options.mainClass,\n        projectVersion = options.sourceGeneratorOptions.projectVersion.orElse(\n          options.sourceGeneratorOptions.computeVersion\n            .map(cv => value(cv.get(workspace)))\n            .orElse(\n              ComputeVersion.GitTag(os.rel, dynVer = false, positions = Nil).get(workspace).toOption\n            )\n        )\n      ),\n      scalaVersionSettings(options),\n      platformSettings(options),\n      scalaCliSettings\n    )\n      .reduceLeft(_ + _)\n  }.left.map {\n    case e: BuildException =>\n      BuildInfoGenerationError(e.message, positions = e.positions, cause = e)\n    case e => BuildInfoGenerationError(e.getMessage, Nil, e)\n  }\n\n  def escapeBackslashes(s: String): String =\n    s.replace(\"\\\\\", \"\\\\\\\\\")\n\n  private def scalaVersionSettings(options: BuildOptions): BuildInfo = {\n    val sv = options.scalaParams.toOption.flatten\n      .map(_.scalaVersion)\n      .orElse(options.scalaOptions.defaultScalaVersion)\n      .getOrElse(Constants.defaultScalaVersion)\n\n    BuildInfo(scalaVersion = Some(sv))\n  }\n\n  private def scalaJsSettings(options: ScalaJsOptions): BuildInfo = {\n\n    val scalaJsVersion = Some(options.version.getOrElse(Constants.scalaJsVersion))\n\n    BuildInfo(\n      platform = Some(Platform.JS.repr),\n      scalaJsVersion = scalaJsVersion,\n      jsEsVersion = options.esVersionStr\n    )\n  }\n\n  private def scalaNativeSettings(options: ScalaNativeOptions): BuildInfo =\n    BuildInfo(\n      platform = Some(Platform.Native.repr),\n      scalaNativeVersion = Some(options.finalVersion)\n    )\n\n  private def jvmSettings(options: BuildOptions): BuildInfo =\n    BuildInfo(\n      platform = Some(Platform.JVM.repr),\n      jvmVersion = options.javaOptions.jvmIdOpt.map(_.value)\n        .orElse(Some(options.javaHome().value.version.toString))\n    )\n\n  private def scalaCliSettings: BuildInfo =\n    BuildInfo(scalaCliVersion = Some(Constants.version))\n\n  private def platformSettings(options: BuildOptions): BuildInfo =\n    options.scalaOptions.platform.map(_.value) match {\n      case Some(Platform.JS) =>\n        scalaJsSettings(options.scalaJsOptions)\n      case Some(Platform.Native) =>\n        scalaNativeSettings(options.scalaNativeOptions)\n      case _ => jvmSettings(options)\n    }\n\n  implicit val monoid: ConfigMonoid[BuildInfo] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/info/ScopedBuildInfo.scala",
    "content": "package scala.build.info\n\nimport coursier.ivy.IvyRepository\nimport coursier.maven.MavenRepository\nimport coursier.{Dependency, LocalRepositories, Repositories}\nimport dependency.AnyDependency\n\nimport scala.build.options.{BuildOptions, ConfigMonoid}\n\nfinal case class ScopedBuildInfo(\n  sources: Seq[String] = Nil,\n  scalacOptions: Seq[String] = Nil,\n  scalaCompilerPlugins: Seq[ExportDependencyFormat] = Nil,\n  dependencies: Seq[ExportDependencyFormat] = Nil,\n  compileOnlyDependencies: Seq[ExportDependencyFormat] = Nil,\n  resolvers: Seq[String] = Nil,\n  resourceDirs: Seq[String] = Nil,\n  customJarsDecls: Seq[String] = Nil\n) {\n  def +(other: ScopedBuildInfo): ScopedBuildInfo =\n    ScopedBuildInfo.monoid.orElse(this, other)\n\n  def generateContentLines(): Seq[String] =\n    Seq(\n      \"/** sources found for the scope */\"  -> \"val sources = \"       -> sources,\n      \"/** scalac options for the scope */\" -> \"val scalacOptions = \" -> scalacOptions,\n      \"/** compiler plugins used in this scope */\" -> \"val scalaCompilerPlugins = \" ->\n        scalaCompilerPlugins.map(_.toString()),\n      \"/** dependencies used in this scope */\" -> \"val dependencies = \" ->\n        dependencies.map(_.toString()),\n      \"/** dependency resolvers used in this scope */\" -> \"val resolvers = \"    -> resolvers,\n      \"/** resource directories used in this scope */\" -> \"val resourceDirs = \" -> resourceDirs,\n      \"/** custom jars added to this scope */\" -> \"val customJarsDecls = \" -> customJarsDecls\n    ).flatMap {\n      case ((scaladoc, prefix), values) =>\n        val sb = new StringBuilder\n        sb.append(prefix)\n        if values.isEmpty\n        then sb.append(\"Nil\")\n        else\n          sb.append {\n            values.map(str => s\"\\\"${BuildInfo.escapeBackslashes(str)}\\\"\")\n              .mkString(\"Seq(\", \", \", \")\")\n          }\n        Seq(scaladoc, sb.toString())\n    }\n}\n\nobject ScopedBuildInfo {\n  def empty: ScopedBuildInfo = ScopedBuildInfo()\n\n  def apply(options: BuildOptions, sourcePaths: Seq[String]): ScopedBuildInfo =\n    Seq(\n      ScopedBuildInfo(sources = sourcePaths),\n      scalaCompilerPlugins(options),\n      scalacOptionsSettings(options),\n      dependencySettings(options),\n      repositorySettings(options),\n      customResourcesSettings(options),\n      customJarsSettings(options)\n    )\n      .reduceLeft(_ + _)\n\n  private def scalacOptionsSettings(options: BuildOptions): ScopedBuildInfo =\n    ScopedBuildInfo(scalacOptions = options.scalaOptions.scalacOptions.toSeq.map(_.value.value))\n\n  private def scalaCompilerPlugins(options: BuildOptions): ScopedBuildInfo =\n    val compilerPlugins = options.scalaOptions.compilerPlugins.map(_.value)\n      .map(ExportDependencyFormat(_, options.scalaParams.getOrElse(None)))\n\n    ScopedBuildInfo(scalaCompilerPlugins = compilerPlugins)\n\n  private def dependencySettings(options: BuildOptions): ScopedBuildInfo = {\n    val directDeps = options.classPathOptions.extraDependencies.toSeq.map(_.value)\n      .map(ExportDependencyFormat(_, options.scalaParams.getOrElse(None)))\n    val compileDeps = options.classPathOptions.extraCompileOnlyDependencies.toSeq.map(_.value)\n      .map(ExportDependencyFormat(_, options.scalaParams.getOrElse(None)))\n\n    ScopedBuildInfo(\n      dependencies = directDeps,\n      compileOnlyDependencies = compileDeps\n    )\n  }\n\n  private def repositorySettings(options: BuildOptions): ScopedBuildInfo = {\n    val resolvers = options.finalRepositories\n      .getOrElse(Nil)\n      .appended(Repositories.central)\n      .appended(LocalRepositories.ivy2Local)\n      .collect {\n        case repo: MavenRepository => repo.root\n        case repo: IvyRepository   => s\"ivy:${repo.pattern.string}\"\n      }\n      .distinct\n\n    ScopedBuildInfo(resolvers = resolvers)\n  }\n\n  private def customResourcesSettings(options: BuildOptions): ScopedBuildInfo =\n    ScopedBuildInfo(resourceDirs = options.classPathOptions.resourcesDir.map(_.toNIO.toString))\n\n  private def customJarsSettings(options: BuildOptions): ScopedBuildInfo = {\n\n    val customCompileOnlyJarsDecls =\n      options.classPathOptions.extraCompileOnlyJars.map(_.toNIO.toString)\n\n    val customJarsDecls = options.classPathOptions.extraClassPath.map(_.toNIO.toString)\n\n    ScopedBuildInfo(\n      customJarsDecls = customCompileOnlyJarsDecls ++ customJarsDecls\n    )\n  }\n\n  implicit val monoid: ConfigMonoid[ScopedBuildInfo] = ConfigMonoid.derive\n}\n\nfinal case class ExportDependencyFormat(groupId: String, artifactId: ArtifactId, version: String) {\n  override def toString(): String = {\n    val sb = new StringBuilder\n    sb.append(groupId)\n    sb.append(':')\n    sb.append(artifactId.fullName)\n    sb.append(':')\n    sb.append(version)\n    sb.toString()\n  }\n}\n\nfinal case class ArtifactId(name: String, fullName: String)\n\nobject ExportDependencyFormat {\n  def apply(dep: Dependency): ExportDependencyFormat = {\n    val scalaVersionStartIndex = dep.module.name.value.lastIndexOf('_')\n    val shortDepName           = if (scalaVersionStartIndex == -1)\n      dep.module.name.value\n    else\n      dep.module.name.value.take(scalaVersionStartIndex)\n    new ExportDependencyFormat(\n      dep.module.organization.value,\n      ArtifactId(shortDepName, dep.module.name.value),\n      dep.versionConstraint.asString\n    )\n  }\n\n  def apply(\n    dep: AnyDependency,\n    scalaParamsOpt: Option[dependency.ScalaParameters]\n  ): ExportDependencyFormat = {\n    import scala.build.internal.Util.*\n    dep.toCs(scalaParamsOpt)\n      .map(ExportDependencyFormat.apply)\n      .getOrElse(\n        ExportDependencyFormat(\n          dep.module.organization,\n          ArtifactId(dep.module.name, dep.module.name),\n          dep.version\n        )\n      )\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/interactive/Interactive.scala",
    "content": "package scala.build.interactive\n\nimport scala.build.internal.StdInConcurrentReader\nimport scala.build.internals.EnvVar\n\nsealed abstract class Interactive extends Product with Serializable {\n  def confirmOperation(msg: String): Option[Boolean]                = None\n  def chooseOne(msg: String, options: List[String]): Option[String] = None\n}\n\nobject Interactive {\n\n  private var interactiveInputsOpt =\n    EnvVar.ScalaCli.interactiveInputs.valueOpt.map(_.linesIterator.toList)\n\n  private def readLine(): String =\n    interactiveInputsOpt match {\n      case None =>\n        StdInConcurrentReader.waitForLine().getOrElse(\"\")\n      case Some(interactiveInputs) =>\n        synchronized {\n          interactiveInputs match {\n            case Nil    => \"\"\n            case h :: t =>\n              interactiveInputsOpt = Some(t)\n              h\n          }\n        }\n    }\n\n  case object InteractiveNop extends Interactive\n\n  case object InteractiveAsk extends Interactive {\n\n    private sealed abstract class Action[V] extends Product with Serializable {\n      def msg: String\n      def action: Option[V]\n      final def run: Option[V] =\n        if (\n          interactiveInputsOpt.nonEmpty ||\n          coursier.paths.Util.useAnsiOutput() ||\n          EnvVar.ScalaCli.interactive.valueOpt.nonEmpty\n        )\n          action\n        else None\n    }\n\n    private case class ConfirmOperation(msg: String) extends Action[Boolean] {\n      override def action: Option[Boolean] = {\n        System.err.println(s\"$msg [Y/n]\")\n        val response = readLine()\n        if (response.toLowerCase == \"y\")\n          Some(true)\n        else {\n          System.err.println(\"Abort\")\n          Some(false)\n        }\n      }\n    }\n\n    private case class ChooseOne(msg: String, options: List[String])\n        extends Action[String] {\n      override def action: Option[String] = {\n        System.err.println(msg)\n        options.zipWithIndex.foreach {\n          case (option, index) => System.err.println(s\"[$index] $option\")\n        }\n        val response = readLine()\n        parseIndexInput(response, options.length - 1)\n      }\n\n      private def parseIndexInput(input: String, range: Int): Option[String] =\n        input.toIntOption match {\n          case Some(index) =>\n            val isInRange = index <= range && index >= 0\n            if (isInRange) Some(options(index))\n            else {\n              System.err.println(\n                s\"The input index number is invalid, integer value from 0 to $range is expected.\"\n              )\n              None\n            }\n          case None =>\n            if (options.contains(input))\n              Some(input)\n            else {\n              System.err.println(\n                s\"Unable to parse input: integer value from 0 to $range is expected.\"\n              )\n              None\n            }\n        }\n    }\n\n    override def confirmOperation(msg: String): Option[Boolean] = ConfirmOperation(msg).run\n\n    override def chooseOne(msg: String, options: List[String]): Option[String] =\n      ChooseOne(msg, options).run\n\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/interactive/InteractiveFileOps.scala",
    "content": "package scala.build.interactive\n\nobject InteractiveFileOps {\n\n  def erasingPath(\n    interactive: Interactive,\n    printableDest: String,\n    destPath: os.Path\n  )(fallbackAction: () => Unit) = {\n    val msg = s\"\"\"|$printableDest already exists.\n                  |Do you want to erase $printableDest?\"\"\".stripMargin\n    val response = interactive.confirmOperation(msg)\n    response match {\n      case Some(true) => os.remove.all(destPath)\n      case _          => fallbackAction()\n    }\n  }\n\n  def appendToFile(\n    interactive: Interactive,\n    msg: String,\n    filePath: os.Path,\n    entry: String\n  )(fallbackAction: () => Unit) = {\n    val response = interactive.confirmOperation(msg)\n    response match {\n      case Some(true) => os.write.append(filePath, entry)\n      case _          => fallbackAction()\n    }\n  }\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/internal/ExternalBinary.scala",
    "content": "package scala.build.internal\n\nimport java.io.File\n\nsealed abstract class ExternalBinary extends Product with Serializable {\n  def command: Seq[String]\n}\n\nobject ExternalBinary {\n  final case class Native(path: os.Path) extends ExternalBinary {\n    def command: Seq[String] =\n      Seq(path.toString)\n  }\n  final case class ClassPath(\n    java: String,\n    classPath: Seq[os.Path],\n    mainClass: String\n  ) extends ExternalBinary {\n    def command: Seq[String] =\n      Seq(java, \"-cp\", classPath.map(_.toString).mkString(File.pathSeparator), mainClass)\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/internal/ExternalBinaryParams.scala",
    "content": "package scala.build.internal\n\nimport coursier.core.Repository\n\nfinal case class ExternalBinaryParams(\n  binaryUrl: String,\n  changing: Boolean,\n  launcherPrefix: String,\n  dependencies: Seq[dependency.Dependency],\n  mainClass: String,\n  forcedVersions: Seq[(dependency.Module, String)] = Nil,\n  extraRepos: Seq[Repository] = Nil\n)\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/internal/FetchExternalBinary.scala",
    "content": "package scala.build.internal\n\nimport coursier.cache.{ArchiveCache, ArtifactError, CacheLogger}\nimport coursier.error.FetchError\nimport coursier.util.{Artifact, Task}\nimport coursier.version.VersionConstraint\n\nimport java.util.Locale\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.Logger\nimport scala.build.errors.{BuildException, FetchingDependenciesError}\nimport scala.build.internal.Util.{DependencyOps, ModuleOps}\nimport scala.util.Properties\n\nobject FetchExternalBinary {\n\n  def fetch(\n    params: ExternalBinaryParams,\n    archiveCache: ArchiveCache[Task],\n    logger: Logger,\n    javaCommand: () => String\n  ): Either[BuildException, ExternalBinary] = either {\n\n    val binaryOpt: Option[os.Path] = value {\n      fetchLauncher(\n        params.binaryUrl,\n        params.changing,\n        archiveCache,\n        logger,\n        params.launcherPrefix\n      )\n    }\n\n    binaryOpt match {\n      case Some(binary) =>\n        logger.debug(s\"Fetched binary: $binary\")\n        ExternalBinary.Native(binary)\n      case None =>\n        logger.debug(\n          s\"\"\"Could not fetch binary, fetching JVM dependencies:\n             |  ${params.dependencies.map(_.toString).mkString(s\"${System.lineSeparator()}  \")}\n             |\"\"\".stripMargin\n        )\n        val classPath = coursier.Fetch()\n          .withCache(archiveCache.cache)\n          .addDependencies(params.dependencies.map(_.toCs)*)\n          .mapResolutionParams { params0 =>\n            params0.addForceVersion0(\n              params.forcedVersions.map { case (m, v) => m.toCs -> VersionConstraint(v) }*\n            )\n          }\n          .addRepositories(params.extraRepos*)\n          .run()(using archiveCache.cache.ec)\n          .map(os.Path(_, os.pwd))\n        ExternalBinary.ClassPath(javaCommand(), classPath, params.mainClass)\n    }\n  }\n\n  def fetchLauncher(\n    url: String,\n    changing: Boolean,\n    archiveCache: ArchiveCache[Task],\n    logger: Logger,\n    launcherPrefix: String,\n    launcherPathOpt: Option[os.RelPath] = None,\n    makeExecutable: Boolean = true\n  ): Either[BuildException, Option[os.Path]] = either {\n\n    val artifact = Artifact(url).withChanging(changing)\n    val res      = archiveCache.cache.loggerOpt.getOrElse(CacheLogger.nop).use {\n      logger.log(s\"Getting $url\")\n      archiveCache.get(artifact)\n        .unsafeRun()(using archiveCache.cache.ec)\n    }\n    val fileOpt = res match {\n      case Left(nf: ArtifactError.NotFound) =>\n        logger.debug(s\"$url not found ($nf)\") // FIXME Log the whole stack trace of nf\n        None\n      case Left(err) =>\n        val err0 = new FetchError.DownloadingArtifacts(Seq((artifact, err)))\n        value(Left(new FetchingDependenciesError(err0, Nil)))\n      case Right(f) => Some(os.Path(f, os.pwd))\n    }\n\n    fileOpt.map { f =>\n      logger.debug(s\"$url is available locally at $f\")\n\n      val launcher = launcherPathOpt match {\n        case Some(launcherPath) =>\n          f / launcherPath\n        case None =>\n          if (os.isDir(f)) {\n            val dirContent = os.list(f)\n            if (dirContent.length == 1) dirContent.head\n            else dirContent.filter(_.last.startsWith(launcherPrefix)).head\n          }\n          else\n            f\n      }\n\n      if (makeExecutable && !Properties.isWin)\n        os.perms.set(launcher, \"rwxr-xr-x\")\n\n      launcher\n    }\n  }\n\n  def maybePlatformSuffix(supportsMusl: Boolean = true): Either[String, String] = {\n    val arch = sys.props(\"os.arch\").toLowerCase(Locale.ROOT) match {\n      case \"amd64\" => \"x86_64\"\n      case other   => other\n    }\n    val maybeOs =\n      if (Properties.isWin) Right(\"pc-win32\")\n      else if (Properties.isLinux)\n        Right {\n          if (supportsMusl && OsLibc.isMusl.getOrElse(false))\n            \"pc-linux-static\"\n          else\n            \"pc-linux\"\n        }\n      else if (Properties.isMac) Right(\"apple-darwin\")\n      else Left(s\"Unrecognized OS: ${sys.props(\"os.name\")}\")\n    maybeOs.map(os => s\"$arch-$os\")\n  }\n\n  def platformSuffix(supportsMusl: Boolean = true): String =\n    maybePlatformSuffix(supportsMusl) match {\n      case Left(err)    => sys.error(err)\n      case Right(value) => value\n    }\n// Warning: somehow also in settings.sc in the build\n  lazy val condaPlatform = {\n    val mambaOs =\n      if (Properties.isWin) \"win\"\n      else if (Properties.isMac) \"osx\"\n      else if (Properties.isLinux) \"linux\"\n      else sys.error(s\"Unsupported mamba OS: ${sys.props(\"os.name\")}\")\n    val arch      = sys.props(\"os.arch\").toLowerCase(Locale.ROOT)\n    val mambaArch = arch match {\n      case \"x86_64\" | \"amd64\"  => \"64\"\n      case \"arm64\" | \"aarch64\" => \"arm64\"\n      case \"ppc64le\"           => \"ppc64le\"\n      case _                   =>\n        sys.error(s\"Unsupported mamba architecture: $arch\")\n    }\n    s\"$mambaOs-$mambaArch\"\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/internal/ScalaJsLinkerConfig.scala",
    "content": "package scala.build.internal\n\nfinal case class ScalaJsLinkerConfig(\n  // trying to have the same defaults as org.scalajs.linker.interface.StandardConfig here\n  moduleKind: String = ScalaJsLinkerConfig.ModuleKind.NoModule,\n  checkIR: Boolean = false,\n  sourceMap: Boolean = true,\n  moduleSplitStyle: String = ScalaJsLinkerConfig.ModuleSplitStyle.FewestModules,\n  smallModuleForPackage: List[String] = Nil,\n  esFeatures: ScalaJsLinkerConfig.ESFeatures = ScalaJsLinkerConfig.ESFeatures(),\n  jsHeader: Option[String] = None,\n  prettyPrint: Boolean = false,\n  relativizeSourceMapBase: Option[String] = None,\n  remapEsModuleImportMap: Option[os.Path] = None,\n  emitWasm: Boolean = false\n) {\n  def linkerCliArgs: Seq[String] = {\n    val moduleKindArgs            = Seq(\"--moduleKind\", moduleKind)\n    val moduleSplitStyleArgs      = Seq(\"--moduleSplitStyle\", moduleSplitStyle)\n    val smallModuleForPackageArgs =\n      if (smallModuleForPackage.nonEmpty)\n        Seq(\"--smallModuleForPackages\", smallModuleForPackage.mkString(\",\"))\n      else\n        Nil\n    val esFeaturesArgs              = Seq(\"--esVersion\", esFeatures.esVersion)\n    val checkIRArgs                 = if (checkIR) Seq(\"--checkIR\") else Nil\n    val sourceMapArgs               = if (sourceMap) Seq(\"--sourceMap\") else Nil\n    val relativizeSourceMapBaseArgs =\n      relativizeSourceMapBase.toSeq\n        .flatMap(uri => Seq(\"--relativizeSourceMap\", uri))\n    val prettyPrintArgs =\n      if (prettyPrint) Seq(\"--prettyPrint\")\n      else Nil\n    val jsHeaderArg = if (jsHeader.nonEmpty) Seq(\"--jsHeader\", jsHeader.getOrElse(\"\")) else Nil\n    val jsEsModuleImportMap = if (remapEsModuleImportMap.nonEmpty)\n      Seq(\"--importmap\", remapEsModuleImportMap.getOrElse(os.pwd / \"importmap.json\").toString)\n    else Nil\n    val jsEmitWasm = if (emitWasm) Seq(\"--emitWasm\") else Nil\n\n    val configArgs = Seq[os.Shellable](\n      moduleKindArgs,\n      moduleSplitStyleArgs,\n      smallModuleForPackageArgs,\n      esFeaturesArgs,\n      checkIRArgs,\n      sourceMapArgs,\n      relativizeSourceMapBaseArgs,\n      jsHeaderArg,\n      prettyPrintArgs,\n      jsEsModuleImportMap,\n      jsEmitWasm\n    )\n\n    configArgs.flatMap(_.value)\n  }\n}\n\nobject ScalaJsLinkerConfig {\n  object ModuleKind {\n    val NoModule       = \"NoModule\"\n    val ESModule       = \"ESModule\"\n    val CommonJSModule = \"CommonJSModule\"\n  }\n\n  object ModuleSplitStyle {\n    val FewestModules   = \"FewestModules\"\n    val SmallestModules = \"SmallestModules\"\n    val SmallModulesFor = \"SmallModulesFor\"\n  }\n\n  final case class ESFeatures(\n    allowBigIntsForLongs: Boolean = false,\n    avoidClasses: Boolean = true,\n    avoidLetsAndConsts: Boolean = true,\n    esVersion: String = ESVersion.default\n  )\n\n  object ESVersion {\n    val ES5_1  = \"ES5_1\"\n    val ES2015 = \"ES2015\"\n    val ES2016 = \"ES2016\"\n    val ES2017 = \"ES2017\"\n    val ES2018 = \"ES2018\"\n    val ES2019 = \"ES2019\"\n    val ES2020 = \"ES2020\"\n    val ES2021 = \"ES2021\"\n\n    def default = ES2015\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/internal/StdInConcurrentReader.scala",
    "content": "package scala.build.internal\n\nimport java.util.concurrent.atomic.AtomicReference\n\nimport scala.concurrent.duration.Duration\nimport scala.concurrent.{Await, ExecutionContext, Future}\nimport scala.io.StdIn\n\n/** Allows for reading StdIn concurrently, in a way that it can be interrupted. It was introduced in\n  * [[https://github.com/VirtusLab/scala-cli/pull/2168 #2168]] to fix input conflicts when watch and\n  * interactive modes are used together. <br>\n  *\n  * Two scenarios are possible when a new process uses [[waitforLine]] to read StdIn:\n  *   - if there is no ongoing reads taking place a future reading StdIn is started and the process\n  *     waits until there's a new input line or until it is interrupted\n  *   - if there is an ongoing read, the process waits for the result of the ongoing future or until\n  *     it is interrupted. <br>\n  *\n  * __Effectively, if used in parallel, the potential input is copied and distributed among the\n  * callers of [[waitForLine]]__\n  */\nobject StdInConcurrentReader {\n  private implicit val ec: ExecutionContext                           = ExecutionContext.global\n  private val readLineFuture: AtomicReference[Future[Option[String]]] =\n    new AtomicReference(Future.successful(None))\n\n  /** Wait for a line to be read from StdIn\n    *\n    * @param atMost\n    *   duration to wait before timeout\n    * @return\n    *   a line from StdIn wrapped in Some or None if end of stream was reached\n    */\n  def waitForLine(atMost: Duration = Duration.Inf): Option[String] = {\n    val updatedFuture = readLineFuture.updateAndGet { f =>\n      if f.isCompleted then Future(Option(StdIn.readLine())) else f\n    }\n\n    Await.result(updatedFuture, atMost)\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/internals/Util.scala",
    "content": "package scala.build.internal\nimport coursier.core.{Dependency, MinimizedExclusions, Publication, VariantPublication}\nimport coursier.util.Artifact\nimport coursier.version.VersionConstraint\nimport dependency.NoAttributes\n\nimport java.io.{File, PrintStream}\nimport java.util.concurrent.ThreadFactory\nimport java.util.concurrent.atomic.AtomicInteger\n\nimport scala.build.Ops.EitherSeqOps\nimport scala.build.errors.{\n  BuildException,\n  CompositeBuildException,\n  NoScalaVersionProvidedError,\n  UnsupportedGradleModuleVariantError\n}\nimport scala.build.{Os, Positioned}\n\nobject Util {\n\n  def printException(t: Throwable, out: PrintStream = System.err): Unit =\n    if (t != null) {\n      out.println(t)\n      for (l <- t.getStackTrace)\n        out.println(s\"  $l\")\n      printException(t.getCause, out)\n    }\n  def printException(t: Throwable, out: => String => Unit): Unit =\n    if (t != null) {\n      out(t.toString)\n      for (l <- t.getStackTrace)\n        out(s\"  $l\")\n      printException(t.getCause, out)\n    }\n\n  def daemonThreadFactory(prefix: String): ThreadFactory =\n    new ThreadFactory {\n      val counter                = new AtomicInteger\n      def threadNumber()         = counter.incrementAndGet()\n      def newThread(r: Runnable) =\n        new Thread(r, s\"$prefix-thread-${threadNumber()}\") {\n          setDaemon(true)\n          setPriority(Thread.NORM_PRIORITY)\n        }\n    }\n\n  implicit class ModuleOps(private val mod: dependency.Module) extends AnyVal {\n    def toCs: coursier.Module =\n      coursier.Module(\n        coursier.Organization(mod.organization),\n        coursier.ModuleName(mod.name),\n        mod.attributes\n      )\n  }\n  implicit class ScalaModuleOps(private val mod: dependency.AnyModule) extends AnyVal {\n    def toCs(params: dependency.ScalaParameters): coursier.Module =\n      mod.applyParams(params).toCs\n    def toCs(paramsOpt: Option[dependency.ScalaParameters])\n      : Either[NoScalaVersionProvidedError, coursier.Module] =\n      paramsOpt match {\n        case Some(params) => Right(toCs(params))\n        case None         =>\n          val isJavaMod = mod.nameAttributes == NoAttributes\n          if (isJavaMod)\n            Right(mod.asInstanceOf[dependency.Module].toCs)\n          else\n            Left(new NoScalaVersionProvidedError(Left(mod)))\n      }\n  }\n\n  implicit class DependencyOps(private val dep: dependency.Dependency) extends AnyVal {\n    def toCs: coursier.Dependency = {\n      val mod  = dep.module.toCs\n      var dep0 = coursier.Dependency(mod, VersionConstraint(dep.version))\n      if (dep.exclude.nonEmpty)\n        dep0 = dep0.withMinimizedExclusions {\n          MinimizedExclusions {\n            dep.exclude.toSet[dependency.Module].map { mod =>\n              (coursier.Organization(mod.organization), coursier.ModuleName(mod.name))\n            }\n          }\n        }\n      for (clOpt <- dep.userParams.find(_._1 == \"classifier\").map(_._2); cl <- clOpt)\n        dep0 = dep0.withPublication(dep0.publication.withClassifier(coursier.core.Classifier(cl)))\n      for (tpeOpt <- dep.userParams.find(_._1 == \"type\").map(_._2); tpe <- tpeOpt)\n        dep0 = dep0.withPublication(dep0.publication.withType(coursier.core.Type(tpe)))\n      for (extOpt <- dep.userParams.find(_._1 == \"ext\").map(_._2); ext <- extOpt)\n        dep0 = dep0.withPublication(dep0.publication.withExt(coursier.core.Extension(ext)))\n      for (_ <- dep.userParams.find(_._1 == \"intransitive\"))\n        dep0 = dep0.withTransitive(false)\n      dep0\n    }\n  }\n  implicit class ScalaDependencyOps(private val dep: dependency.AnyDependency) extends AnyVal {\n    def toCs(params: dependency.ScalaParameters): coursier.Dependency =\n      dep.applyParams(params).toCs\n    def toCs(paramsOpt: Option[dependency.ScalaParameters])\n      : Either[NoScalaVersionProvidedError, coursier.Dependency] =\n      paramsOpt match {\n        case Some(params) => Right(toCs(params))\n        case None         =>\n          val isJavaDep = dep.module.nameAttributes == NoAttributes && dep.exclude.forall(\n            _.nameAttributes == NoAttributes\n          )\n          if (isJavaDep)\n            Right(dep.asInstanceOf[dependency.Dependency].toCs)\n          else\n            Left(new NoScalaVersionProvidedError(Right(dep)))\n      }\n  }\n  implicit class PositionedScalaDependencyOps(\n    private val posDep: Positioned[dependency.AnyDependency]\n  ) extends AnyVal {\n    def toCs(paramsOpt: Option[dependency.ScalaParameters])\n      : Either[NoScalaVersionProvidedError, Positioned[coursier.Dependency]] = {\n      val res = posDep.map(_.toCs(paramsOpt))\n      res.value\n        .left.map(_ => new NoScalaVersionProvidedError(Right(posDep.value), posDep.positions))\n        .map(Positioned(res.positions, _))\n    }\n  }\n\n  def isFullScalaVersion(sv: String): Boolean =\n    sv.count(_ == '.') >= 2 && !sv.endsWith(\".\")\n\n  def printablePath(p: os.Path): String =\n    printablePath(p, Os.pwd, File.separator)\n  def printablePath(p: os.Path, cwd: os.Path, sep: String): String =\n    if (p.startsWith(cwd)) (Iterator(\".\") ++ p.relativeTo(cwd).segments.iterator).mkString(sep)\n    else p.toString\n\n  def printablePath(p: Either[String, os.Path]): String =\n    p.fold(identity, printablePath)\n\n  extension (fullDetailedArtifacts: Seq[(\n    Dependency,\n    Either[VariantPublication, Publication],\n    Artifact,\n    Option[File]\n  )]) {\n    def safeFullDetailedArtifacts: Either[BuildException, Seq[(\n      Dependency,\n      Publication,\n      Artifact,\n      Option[File]\n    )]] =\n      fullDetailedArtifacts\n        .map {\n          case (dependency, Right(publication), artifact, maybeFile) =>\n            Right((dependency, publication, artifact, maybeFile))\n          case (_, Left(variantPublication), _, _) =>\n            // TODO: Add support for Gradle Module variants\n            Left(UnsupportedGradleModuleVariantError(variantPublication))\n        }\n        .sequence\n        .left\n        .map(CompositeBuildException(_))\n  }\n\n  extension (artifacts: Seq[(Dependency, Either[VariantPublication, Publication], Artifact)]) {\n    def safeArtifacts: Either[BuildException, Seq[(\n      Dependency,\n      Publication,\n      Artifact\n    )]] =\n      artifacts\n        .map { case (d, p, a) => (d, p, a, None) }\n        .safeFullDetailedArtifacts\n        .map(_.map { case (d, p, a, _) => (d, p, a) })\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/BuildOptions.scala",
    "content": "package scala.build.options\nimport coursier.cache.{ArchiveCache, FileCache}\nimport coursier.core.{Repository, Version}\nimport coursier.jvm.JavaHome\nimport coursier.parse.RepositoryParser\nimport coursier.util.{Artifact, Task}\nimport dependency.*\n\nimport java.io.File\nimport java.math.BigInteger\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\n\nimport scala.build.EitherCps.{either, value}\nimport scala.build.actionable.ActionablePreprocessor\nimport scala.build.errors.*\nimport scala.build.interactive.Interactive\nimport scala.build.interactive.Interactive.*\nimport scala.build.internal.Constants.*\nimport scala.build.internal.Regexes.{\n  scala3NightlyNicknameRegex,\n  scala3RcNicknameRegex,\n  scala3RcRegex\n}\nimport scala.build.internal.{Constants, OsLibc, Util}\nimport scala.build.internals.EnvVar\nimport scala.build.options.validation.BuildOptionsRule\nimport scala.build.{Artifacts, Logger, Os, Position, Positioned, RepositoryUtils}\nimport scala.collection.immutable.Seq\nimport scala.concurrent.Await\nimport scala.concurrent.duration.*\nimport scala.util.Properties\nimport scala.util.control.NonFatal\n\nfinal case class BuildOptions(\n  suppressWarningOptions: SuppressWarningOptions = SuppressWarningOptions(),\n  scalaOptions: ScalaOptions = ScalaOptions(),\n  scalaJsOptions: ScalaJsOptions = ScalaJsOptions(),\n  scalaNativeOptions: ScalaNativeOptions = ScalaNativeOptions(),\n  internalDependencies: InternalDependenciesOptions = InternalDependenciesOptions(),\n  javaOptions: JavaOptions = JavaOptions(),\n  jmhOptions: JmhOptions = JmhOptions(),\n  classPathOptions: ClassPathOptions = ClassPathOptions(),\n  scriptOptions: ScriptOptions = ScriptOptions(),\n  internal: InternalOptions = InternalOptions(),\n  mainClass: Option[String] = None,\n  testOptions: TestOptions = TestOptions(),\n  notForBloopOptions: PostBuildOptions = PostBuildOptions(),\n  watchOptions: WatchOptions = WatchOptions(),\n  sourceGeneratorOptions: SourceGeneratorOptions = SourceGeneratorOptions(),\n  useBuildServer: Option[Boolean] = None\n) {\n\n  import BuildOptions.JavaHomeInfo\n\n  lazy val platform: Positioned[Platform] =\n    scalaOptions.platform.getOrElse(Positioned(List(Position.Custom(\"DEFAULT\")), Platform.JVM))\n\n  lazy val projectParams: Either[BuildException, Seq[String]] = either {\n    value(scalaParams) match {\n      case Some(scalaParams0) =>\n        val platform0 = platform.value match {\n          case Platform.JVM =>\n            val jvmIdSuffix =\n              javaOptions.jvmIdOpt.map(_.value)\n                .orElse(Some(javaHome().value.version.toString))\n                .map(\" (\" + _ + \")\").getOrElse(\"\")\n            s\"JVM$jvmIdSuffix\"\n          case Platform.JS =>\n            val scalaJsVersion = scalaJsOptions.version.getOrElse(Constants.scalaJsVersion)\n            s\"Scala.js $scalaJsVersion\"\n          case Platform.Native =>\n            s\"Scala Native ${scalaNativeOptions.finalVersion}\"\n        }\n        Seq(s\"Scala ${scalaParams0.scalaVersion}\", platform0)\n      case None =>\n        Seq(\"Java\")\n    }\n  }\n\n  lazy val scalaVersionIsExotic: Boolean = scalaParams.toOption.flatten.exists { scalaParameters =>\n    scalaParameters.scalaVersion.startsWith(\"2\") && scalaParameters.scalaVersion.exists(_.isLetter)\n  }\n\n  def addRunnerDependency: Option[Boolean] =\n    notForBloopOptions.addRunnerDependencyOpt\n      .orElse {\n        if (platform.value == Platform.JVM && !scalaVersionIsExotic) None\n        else Some(false)\n      }\n\n  private def scalaLibraryDependencies: Either[BuildException, Seq[AnyDependency]] = either {\n    value(scalaParams).toSeq.flatMap { scalaParams0 =>\n      if (platform.value != Platform.Native && scalaOptions.addScalaLibrary.getOrElse(true))\n        Seq(\n          if (scalaParams0.scalaVersion.startsWith(\"3.\"))\n            dep\"org.scala-lang::scala3-library::${scalaParams0.scalaVersion}\"\n          else\n            dep\"org.scala-lang:scala-library:${scalaParams0.scalaVersion}\"\n        )\n      else Nil\n    }\n  }\n\n  private def scalaCompilerDependencies: Either[BuildException, Seq[AnyDependency]] = either {\n    value(scalaParams)\n      .map(_ -> scalaOptions.addScalaCompiler.getOrElse(false))\n      .toSeq\n      .flatMap {\n        case (sp, true) if sp.scalaVersion.startsWith(\"3\") =>\n          Seq(\n            dep\"org.scala-lang::scala3-compiler::${sp.scalaVersion}\",\n            dep\"org.scala-lang::scala3-staging::${sp.scalaVersion}\",\n            dep\"org.scala-lang::scala3-tasty-inspector::${sp.scalaVersion}\"\n          )\n        case (sp, true) => Seq(dep\"org.scala-lang:scala-compiler:${sp.scalaVersion}\")\n        case _          => Nil\n      }\n  }\n\n  private def maybeJsDependencies: Either[BuildException, Seq[AnyDependency]] = either {\n    if (platform.value == Platform.JS)\n      value(scalaParams).toSeq.flatMap { scalaParams0 =>\n        scalaJsOptions.jsDependencies(scalaParams0.scalaVersion)\n      }\n    else Nil\n  }\n  private def maybeNativeDependencies: Either[BuildException, Seq[AnyDependency]] = either {\n    if (platform.value == Platform.Native)\n      value(scalaParams).toSeq.flatMap { scalaParams0 =>\n        scalaNativeOptions.nativeDependencies(scalaParams0.scalaVersion)\n      }\n    else Nil\n  }\n  private def defaultDependencies: Either[BuildException, Seq[Positioned[AnyDependency]]] = either {\n    value(maybeJsDependencies).map(Positioned.none(_)) ++\n      value(maybeNativeDependencies).map(Positioned.none(_)) ++\n      value(scalaLibraryDependencies).map(Positioned.none(_)) ++\n      value(scalaCompilerDependencies).map(Positioned.none(_))\n  }\n\n  private def semanticDbPlugins(logger: Logger): Either[BuildException, Seq[AnyDependency]] =\n    either {\n      val scalaVersion: Option[String] = value(scalaParams).map(_.scalaVersion)\n      val generateSemDbs = scalaOptions.semanticDbOptions.generateSemanticDbs.getOrElse(false)\n      scalaVersion match {\n        case Some(sv) if sv.startsWith(\"2.\") && generateSemDbs =>\n          val semanticDbVersion = findSemanticDbVersion(sv, logger)\n          Seq(\n            dep\"$semanticDbPluginOrganization:::$semanticDbPluginModuleName:$semanticDbVersion\"\n          )\n        case _ => Nil\n      }\n    }\n\n  /** Find the latest supported semanticdb version for @scalaVersion\n    */\n  def findSemanticDbVersion(scalaVersion: String, logger: Logger): String = {\n    val versionsFuture =\n      finalCache.logger.use {\n        coursier.complete.Complete(finalCache)\n          .withScalaVersion(scalaVersion)\n          .withScalaBinaryVersion(scalaVersion.split('.').take(2).mkString(\".\"))\n          .withInput(s\"org.scalameta:semanticdb-scalac_$scalaVersion:\")\n          .complete()\n          .future()(using finalCache.ec)\n      }\n\n    val versions =\n      try\n        Await.result(versionsFuture, FiniteDuration(10, \"s\"))._2\n      catch {\n        case NonFatal(e) =>\n          logger.debug(s\"Error while looking up semanticdb versions for scala $scalaVersion\")\n          Util.printException(e, logger.debug(_: String))\n          Nil\n      }\n\n    versions.lastOption.getOrElse(semanticDbPluginVersion)\n  }\n\n  private def maybeJsCompilerPlugins: Either[BuildException, Seq[AnyDependency]] = either {\n    if (platform.value == Platform.JS)\n      value(scalaParams).toSeq.flatMap { scalaParams0 =>\n        scalaJsOptions.compilerPlugins(scalaParams0.scalaVersion)\n      }\n    else Nil\n  }\n  private def maybeNativeCompilerPlugins: Seq[AnyDependency] =\n    if (platform.value == Platform.Native) scalaNativeOptions.compilerPlugins\n    else Nil\n  def compilerPlugins(logger: Logger): Either[BuildException, Seq[Positioned[AnyDependency]]] =\n    either {\n      value(maybeJsCompilerPlugins).map(Positioned.none) ++\n        maybeNativeCompilerPlugins.map(Positioned.none) ++\n        value(semanticDbPlugins(logger)).map(Positioned.none) ++\n        scalaOptions.compilerPlugins\n    }\n\n  private def semanticDbJavacPlugins: Either[BuildException, Seq[AnyDependency]] = either {\n    val generateSemDbs = scalaOptions.semanticDbOptions.generateSemanticDbs.getOrElse(false)\n    if (generateSemDbs)\n      Seq(\n        dep\"$semanticDbJavacPluginOrganization:$semanticDbJavacPluginModuleName:$semanticDbJavacPluginVersion\"\n      )\n    else\n      Nil\n  }\n\n  def javacPluginDependencies: Either[BuildException, Seq[Positioned[AnyDependency]]] = either {\n    value(semanticDbJavacPlugins).map(Positioned.none(_)) ++\n      javaOptions.javacPluginDependencies\n  }\n\n  def allExtraJars: Seq[os.Path] =\n    classPathOptions.extraClassPath\n  def allExtraCompileOnlyJars: Seq[os.Path] =\n    classPathOptions.extraCompileOnlyJars\n  def allExtraSourceJars: Seq[os.Path] =\n    classPathOptions.extraSourceJars\n\n  private def addJvmTestRunner: Boolean =\n    platform.value == Platform.JVM &&\n    internalDependencies.addTestRunnerDependency\n\n  private def addJvmJavaTestRunner: Boolean =\n    platform.value == Platform.JVM &&\n    internalDependencies.addTestRunnerDependency\n  private def addJsTestBridge: Option[String] =\n    if (platform.value == Platform.JS && internalDependencies.addTestRunnerDependency)\n      Some(scalaJsOptions.finalVersion)\n    else None\n  private def addNativeTestInterface: Option[String] = {\n    val doAdd =\n      platform.value == Platform.Native &&\n      internalDependencies.addTestRunnerDependency &&\n      Version(\"0.4.3\").compareTo(Version(scalaNativeOptions.finalVersion)) <= 0\n    if (doAdd) Some(scalaNativeOptions.finalVersion)\n    else None\n  }\n\n  lazy val finalCache: FileCache[Task] = internal.cache.getOrElse(FileCache())\n  // This might download a JVM if --jvm … is passed or no system JVM is installed\n\n  lazy val archiveCache: ArchiveCache[Task] = ArchiveCache().withCache(finalCache)\n\n  private lazy val javaCommand0: Positioned[JavaHomeInfo] =\n    javaHomeLocation().map(JavaHomeInfo(_))\n\n  def javaHomeLocationOpt(): Option[Positioned[os.Path]] =\n    javaOptions.javaHomeLocationOpt(archiveCache, finalCache, internal.verbosityOrDefault)\n\n  def javaHomeLocation(): Positioned[os.Path] =\n    javaOptions.javaHomeLocation(archiveCache, finalCache, internal.verbosityOrDefault)\n\n  def javaHome(): Positioned[JavaHomeInfo] = javaCommand0\n\n  lazy val javaHomeManager: JavaHome =\n    javaOptions.javaHomeManager(archiveCache, finalCache, internal.verbosityOrDefault)\n\n  private val scala2NightlyRepo = Seq(coursier.Repositories.scalaIntegration.root)\n  private val scala3NightlyRepo = Seq(RepositoryUtils.scala3NightlyRepository.root)\n\n  def finalRepositories: Either[BuildException, Seq[Repository]] = either {\n    val maybeSv = scalaOptions.scalaVersion\n      .map(_.asString)\n      .orElse(scalaOptions.defaultScalaVersion)\n    val nightlyRepos =\n      if maybeSv.exists(ScalaVersionUtil.isScala2Nightly) then scala2NightlyRepo\n      else if maybeSv.exists(ScalaVersionUtil.isScala3Nightly) then scala3NightlyRepo\n      else Nil\n    val snapshotRepositories =\n      if classPathOptions.extraRepositories.contains(\"snapshots\")\n      then\n        Seq(\n          RepositoryUtils.snapshotsRepository,\n          RepositoryUtils.scala3NightlyRepository\n        )\n      else Nil\n    val extraRepositories = classPathOptions.extraRepositories.filterNot(_ == \"snapshots\")\n\n    val repositories = nightlyRepos ++\n      extraRepositories ++\n      internal.localRepository.toSeq\n\n    val parseRepositories = value {\n      RepositoryParser.repositories(repositories)\n        .either\n        .left.map(errors => new RepositoryFormatError(errors))\n    }\n\n    (parseRepositories ++ snapshotRepositories).distinct\n  }\n\n  lazy val scalaParams: Either[BuildException, Option[ScalaParameters]] = either {\n    val params =\n      if EnvVar.Internal.ci.valueOpt.isEmpty then\n        computeScalaParams(\n          cache = finalCache,\n          repositories = value(finalRepositories)\n        ).orElse {\n          // when the passed scala version is missed in the cache, we always force a cache refresh\n          // https://github.com/VirtusLab/scala-cli/issues/1090\n          computeScalaParams(\n            cache = finalCache.withTtl(0.seconds),\n            repositories = value(finalRepositories)\n          )\n        }\n      else\n        computeScalaParams(\n          cache = finalCache.withTtl(0.seconds),\n          repositories = value(finalRepositories)\n        )\n    value(params)\n  }\n\n  private[build] def computeScalaParams(\n    cache: FileCache[Task] = finalCache,\n    repositories: Seq[Repository] = Nil\n  ): Either[BuildException, Option[ScalaParameters]] = either {\n\n    val defaultVersions = Set(\n      Constants.defaultScalaVersion,\n      Constants.defaultScala212Version,\n      Constants.defaultScala213Version,\n      scalaOptions.defaultScalaVersion.getOrElse(Constants.defaultScalaVersion)\n    )\n\n    val svOpt: Option[String] =\n      scalaOptions.scalaVersion -> scalaOptions.defaultScalaVersion match {\n        case (Some(MaybeScalaVersion(None)), _) =>\n          None\n        // Do not validate Scala version in offline mode\n        case (Some(MaybeScalaVersion(Some(svInput))), _) if internal.offline.getOrElse(false) =>\n          Some(svInput)\n        // Do not validate Scala version if it is a default one\n        case (Some(MaybeScalaVersion(Some(svInput))), _) if defaultVersions.contains(svInput) =>\n          Some(svInput)\n        case (Some(MaybeScalaVersion(Some(svInput))), Some(predefinedScalaVersion))\n            if predefinedScalaVersion.startsWith(svInput) &&\n            (svInput == \"3\" || svInput == Constants.scala3NextPrefix ||\n            svInput == \"2.13\" || svInput == \"2.12\") =>\n          Some(predefinedScalaVersion)\n        case (Some(MaybeScalaVersion(Some(svInput))), None)\n            if svInput == \"3\" || svInput == Constants.scala3NextPrefix =>\n          Some(Constants.defaultScalaVersion)\n        case (Some(MaybeScalaVersion(Some(svInput))), None) if svInput == \"2.13\" =>\n          Some(Constants.defaultScala213Version)\n        case (Some(MaybeScalaVersion(Some(svInput))), None) if svInput == \"2.12\" =>\n          Some(Constants.defaultScala212Version)\n        case (Some(MaybeScalaVersion(Some(svInput))), _) =>\n          val sv = value {\n            svInput match {\n              case sv if ScalaVersionUtil.scala3Lts.contains(sv) =>\n                ScalaVersionUtil.validateStable(Constants.scala3LtsPrefix, cache, repositories)\n              case sv if ScalaVersionUtil.scala3LatestRc.contains(sv.toLowerCase) =>\n                ScalaVersionUtil.validateRc(\"3\", cache, repositories)\n              case sv if ScalaVersionUtil.scala3LtsLatestRc.contains(sv.toLowerCase) =>\n                ScalaVersionUtil.validateRc(Constants.scala3LtsPrefix, cache, repositories)\n              case scala3RcRegex(threeSubBinarySuffix) =>\n                ScalaVersionUtil.validateRc(s\"3.$threeSubBinarySuffix\", cache, repositories)\n              case scala3RcNicknameRegex(threeSubBinarySuffix) =>\n                ScalaVersionUtil.validateRc(s\"3.$threeSubBinarySuffix\", cache, repositories)\n              case sv if ScalaVersionUtil.scala2Lts.contains(sv) =>\n                Left(new ScalaVersionError(\n                  s\"Invalid Scala version: $sv. There is no official LTS version for Scala 2.\"\n                ))\n              case sv if ScalaVersionUtil.scala2LatestRc.contains(sv) =>\n                Left(new ScalaVersionError(\n                  s\"Invalid Scala version: $sv. In the case of Scala 2, a particular nightly version serves as a release candidate.\"\n                ))\n              case sv if ScalaVersionUtil.scala3Nightly.contains(sv.toLowerCase) =>\n                ScalaVersionUtil.GetNightly.scala3(cache)\n              case sv if ScalaVersionUtil.scala3LtsNightly.contains(sv.toLowerCase) =>\n                ScalaVersionUtil.GetNightly.scala3X(\n                  Constants.scala3LtsPrefix.split('.').last,\n                  cache\n                )\n              case scala3NightlyNicknameRegex(threeSubBinaryNum) =>\n                ScalaVersionUtil.GetNightly.scala3X(threeSubBinaryNum, cache)\n              case vs if ScalaVersionUtil.scala213Nightly.contains(vs) =>\n                ScalaVersionUtil.GetNightly.scala2(\"2.13\", cache)\n              case sv if sv == ScalaVersionUtil.scala212Nightly =>\n                ScalaVersionUtil.GetNightly.scala2(\"2.12\", cache)\n              case versionString if ScalaVersionUtil.isScala3Nightly(versionString) =>\n                ScalaVersionUtil.CheckNightly.scala3(\n                  versionString,\n                  cache,\n                  repositories\n                )\n                  .map(_ => versionString)\n              case versionString if ScalaVersionUtil.isScala2Nightly(versionString) =>\n                ScalaVersionUtil.CheckNightly.scala2(\n                  versionString,\n                  cache\n                )\n                  .map(_ => versionString)\n              case versionString if versionString.exists(_.isLetter) =>\n                ScalaVersionUtil.validateExactVersion(\n                  versionString,\n                  cache,\n                  repositories\n                )\n              case versionString =>\n                ScalaVersionUtil.validateStable(\n                  versionString,\n                  cache,\n                  repositories\n                )\n            }\n          }\n          Some(sv)\n        case (None, Some(predefinedScalaVersion)) => Some(predefinedScalaVersion)\n        case _                                    => Some(Constants.defaultScalaVersion)\n      }\n\n    svOpt match {\n      case Some(scalaVersion) =>\n        val scalaBinaryVersion = scalaOptions.scalaBinaryVersion.getOrElse {\n          ScalaVersion.binary(scalaVersion)\n        }\n\n        val maybePlatformSuffix = platform.value match {\n          case Platform.JVM    => None\n          case Platform.JS     => Some(scalaJsOptions.platformSuffix)\n          case Platform.Native => Some(scalaNativeOptions.platformSuffix)\n        }\n\n        Some(ScalaParameters(scalaVersion, scalaBinaryVersion, maybePlatformSuffix))\n      case None =>\n        None\n    }\n  }\n\n  def artifacts(\n    logger: Logger,\n    scope: Scope,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)\n  ): Either[BuildException, Artifacts] = either {\n    val isTests                 = scope == Scope.Test\n    val scalaArtifactsParamsOpt = value(scalaParams) match {\n      case Some(scalaParams0) =>\n        val params = Artifacts.ScalaArtifactsParams(\n          params = scalaParams0,\n          compilerPlugins = value(compilerPlugins(logger)),\n          addJsTestBridge = addJsTestBridge.filter(_ => isTests),\n          addNativeTestInterface = addNativeTestInterface.filter(_ => isTests),\n          scalaJsVersion =\n            if (platform.value == Platform.JS) Some(scalaJsOptions.finalVersion) else None,\n          scalaJsCliVersion =\n            if (platform.value == Platform.JS)\n              Some(notForBloopOptions.scalaJsLinkerOptions.finalScalaJsCliVersion)\n            else None,\n          scalaNativeCliVersion =\n            if (platform.value == Platform.Native) {\n              val scalaNativeFinalVersion = scalaNativeOptions.finalVersion\n              if scalaNativeOptions.version.isEmpty &&\n                scalaNativeFinalVersion != Constants.scalaNativeVersion\n              then\n                scalaNativeOptions.maxDefaultNativeVersions.map(_._2).distinct\n                  .map(reason => s\"[${Console.YELLOW}warn${Console.RESET}] $reason\")\n                  .foreach(reason => logger.message(reason))\n                logger.message(\n                  s\"[${Console.YELLOW}warn${Console.RESET}] Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build. Using $scalaNativeFinalVersion instead.\"\n                )\n              Some(scalaNativeFinalVersion)\n            }\n            else None,\n          addScalapy =\n            if (notForBloopOptions.doSetupPython.getOrElse(false))\n              Some(notForBloopOptions.scalaPyVersion.getOrElse(Constants.scalaPyVersion))\n            else\n              None\n        )\n        Some(params)\n      case None =>\n        None\n    }\n    val addRunnerDependency0 = addRunnerDependency.orElse {\n      if (scalaArtifactsParamsOpt.isDefined) None\n      else Some(false) // no runner in pure Java mode\n    }\n    val isJavaBuild                        = scalaArtifactsParamsOpt.isEmpty\n    val extraRepositories: Seq[Repository] = value(finalRepositories)\n    val maybeArtifacts                     = Artifacts(\n      scalaArtifactsParamsOpt = scalaArtifactsParamsOpt,\n      javacPluginDependencies = value(javacPluginDependencies),\n      extraJavacPlugins = javaOptions.javacPlugins.map(_.value),\n      defaultDependencies = value(defaultDependencies),\n      extraDependencies = classPathOptions.extraDependencies.toSeq,\n      compileOnlyDependencies = classPathOptions.extraCompileOnlyDependencies.toSeq,\n      extraClassPath = allExtraJars,\n      extraCompileOnlyJars = allExtraCompileOnlyJars,\n      extraSourceJars = allExtraSourceJars,\n      fetchSources = classPathOptions.fetchSources.getOrElse(false),\n      jvmVersion = javaHome().value.version,\n      addJvmRunner = addRunnerDependency0,\n      addJvmTestRunner = isTests && addJvmTestRunner && !isJavaBuild,\n      addJvmJavaTestRunner = isTests && addJvmJavaTestRunner && isJavaBuild,\n      addJmhDependencies = jmhOptions.finalJmhVersion,\n      extraRepositories = extraRepositories,\n      keepResolution = internal.keepResolution,\n      includeBuildServerDeps = useBuildServer.getOrElse(true),\n      cache = finalCache,\n      logger = logger,\n      maybeRecoverOnError = maybeRecoverOnError\n    )\n    value(maybeArtifacts)\n  }\n\n  private def allCrossScalaVersionOptions: Seq[BuildOptions] = {\n    val scalaOptions0            = scalaOptions.normalize\n    val sortedExtraScalaVersions = scalaOptions0\n      .extraScalaVersions\n      .toVector\n      .map(coursier.version.Version(_))\n      .sorted\n      .map(_.repr)\n      .reverse\n    this +: sortedExtraScalaVersions.map { sv =>\n      copy(\n        scalaOptions = scalaOptions0.copy(\n          scalaVersion = Some(MaybeScalaVersion(sv)),\n          extraScalaVersions = Set.empty\n        )\n      )\n    }\n  }\n\n  private def allCrossScalaPlatformOptions: Seq[BuildOptions] = {\n    val scalaOptions0        = scalaOptions.normalize\n    val sortedExtraPlatforms = scalaOptions0\n      .extraPlatforms\n      .toVector\n    this +: sortedExtraPlatforms.map { case (pf, pos) =>\n      copy(\n        scalaOptions = scalaOptions0.copy(\n          platform = Some(Positioned(pos.positions, pf)),\n          extraPlatforms = Map.empty\n        )\n      )\n    }\n  }\n\n  def crossOptions: Seq[BuildOptions] = {\n    val allOptions = for {\n      svOpt   <- allCrossScalaVersionOptions\n      svPfOpt <- svOpt.allCrossScalaPlatformOptions\n    } yield svPfOpt\n    allOptions.drop(1) // First one if basically 'this', dropping it\n  }\n\n  private def clearJsOptions: BuildOptions =\n    copy(scalaJsOptions = ScalaJsOptions())\n  private def clearNativeOptions: BuildOptions =\n    copy(scalaNativeOptions = ScalaNativeOptions())\n  private def normalize: BuildOptions = {\n    var opt = this\n\n    if (platform.value != Platform.JS)\n      opt = opt.clearJsOptions\n    if (platform.value != Platform.Native)\n      opt = opt.clearNativeOptions\n\n    opt.copy(\n      scalaOptions = opt.scalaOptions.normalize\n    )\n  }\n\n  lazy val hash: Option[String] = {\n    val md = MessageDigest.getInstance(\"SHA-1\")\n\n    var hasAnyOverride = false\n\n    BuildOptions.hasHashData.add(\n      \"\",\n      normalize,\n      s => {\n        val bytes = s.getBytes(StandardCharsets.UTF_8)\n        if (bytes.nonEmpty) {\n          hasAnyOverride = true\n          md.update(bytes)\n        }\n      }\n    )\n\n    if (hasAnyOverride) {\n      val digest        = md.digest()\n      val calculatedSum = new BigInteger(1, digest)\n      val hash          = String.format(s\"%040x\", calculatedSum).take(10)\n      Some(hash)\n    }\n    else None\n  }\n\n  def orElse(other: BuildOptions): BuildOptions =\n    BuildOptions.monoid.orElse(this, other)\n\n  def validate: Seq[Diagnostic] = BuildOptionsRule.validateAll(this)\n\n  def logActionableDiagnostics(logger: Logger): Unit = {\n    val actionableDiagnostics = ActionablePreprocessor.generateActionableDiagnostics(this)\n    actionableDiagnostics match {\n      case Left(e) =>\n        logger.debug(e)\n      case Right(diagnostics) =>\n        logger.log(diagnostics)\n    }\n  }\n\n  lazy val downloader: BuildOptions.Download = BuildOptions.Download(finalCache)\n\n  lazy val interactive: Either[BuildException, Interactive] =\n    internal.interactive.map(_()).getOrElse(Right(InteractiveNop))\n}\n\nobject BuildOptions {\n  def empty: BuildOptions = BuildOptions()\n\n  final case class CrossKey(\n    scalaVersion: String,\n    platform: Platform\n  )\n\n  final case class JavaHomeInfo(\n    javaHome: os.Path,\n    javaCommand: String,\n    version: Int\n  ) {\n    def envUpdates(currentEnv: Map[String, String]): Map[String, String] = {\n      // On Windows, AFAIK, env vars are \"case-insensitive but case-preserving\".\n      // If PATH was defined as \"Path\", we need to update \"Path\", not \"PATH\".\n      // Same for JAVA_HOME\n      def keyFor(name: String) =\n        if (Properties.isWin)\n          currentEnv.keys.find(_.equalsIgnoreCase(name)).getOrElse(name)\n        else\n          name\n      val javaHomeKey = keyFor(EnvVar.Java.javaHome.name)\n      val pathKey     = keyFor(EnvVar.Misc.path.name)\n      val updatedPath = {\n        val valueOpt = currentEnv.get(pathKey)\n        val entry    = (javaHome / \"bin\").toString\n        valueOpt.fold(entry)(entry + File.pathSeparator + _)\n      }\n      Map(\n        javaHomeKey -> javaHome.toString,\n        pathKey     -> updatedPath\n      )\n    }\n  }\n\n  object JavaHomeInfo {\n    def apply(javaHome: os.Path): JavaHomeInfo = {\n      val ext         = if (Properties.isWin) \".exe\" else \"\"\n      val javaCmd     = (javaHome / \"bin\" / s\"java$ext\").toString\n      val javaVersion = OsLibc.javaVersion(javaCmd)\n      JavaHomeInfo(javaHome, javaCmd, javaVersion)\n    }\n  }\n\n  type Download = String => Either[String, Array[Byte]]\n  object Download {\n    def apply(\n      cache: FileCache[Task],\n      toArtifact: String => Artifact = Artifact.fromUrl\n    ): Download = {\n      import scala.build.options.ScalaVersionUtil.fileWithTtl0\n      url =>\n        cache.fileWithTtl0(toArtifact(url))\n          .left\n          .map(_.describe)\n          .map(f => os.read.bytes(os.Path(f, Os.pwd)))\n    }\n    def changing(cache: FileCache[Task]): Download = apply(cache, Artifact(_).withChanging(true))\n    val notSupported: Download                     = _ => Left(\"URL not supported\")\n  }\n\n  implicit val hasHashData: HasHashData[BuildOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[BuildOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/BuildRequirements.scala",
    "content": "package scala.build.options\n\nfinal case class BuildRequirements(\n  scalaVersion: Seq[BuildRequirements.VersionRequirement] = Nil,\n  platform: Seq[BuildRequirements.PlatformRequirement] = Nil,\n  scope: Option[BuildRequirements.ScopeRequirement] = None\n) {\n  def needsScalaVersion: Boolean = scalaVersion.nonEmpty\n  def withScalaVersion(sv: MaybeScalaVersion): Either[String, BuildRequirements] = {\n    val dontPass = scalaVersion.filter(!_.valid(sv))\n    if (dontPass.isEmpty)\n      Right(copy(scalaVersion = Nil))\n    else\n      Left(dontPass.map(_.failedMessage).mkString(\", \"))\n  }\n  def withPlatform(pf: Platform): Either[String, BuildRequirements] =\n    BuildRequirements.PlatformRequirement.merge(platform) match {\n      case None            => Right(this)\n      case Some(platform0) =>\n        if (platform0.valid(pf)) Right(copy(platform = Nil))\n        else Left(platform0.failedMessage)\n    }\n  def isEmpty: Boolean =\n    this == BuildRequirements()\n  def nonEmpty: Boolean                                   = !isEmpty\n  def orElse(other: BuildRequirements): BuildRequirements =\n    BuildRequirements.monoid.orElse(this, other)\n}\n\nobject BuildRequirements {\n\n  sealed trait VersionRequirement extends Product with Serializable {\n    def valid(version: MaybeScalaVersion): Boolean\n    def failedMessage: String\n  }\n\n  final case class VersionEquals(requiredVersion: String, loose: Boolean)\n      extends VersionRequirement {\n    def looselyValid(version: MaybeScalaVersion): Boolean =\n      version.versionOpt.contains(requiredVersion) ||\n      version.versionOpt.exists(_.startsWith(requiredVersion + \".\")) ||\n      version.versionOpt.exists(_.startsWith(requiredVersion + \"-\"))\n    def strictlyValid(version: MaybeScalaVersion): Boolean =\n      version.versionOpt.exists { version =>\n        val cmp = coursier.core.Version(requiredVersion).compare(coursier.core.Version(version))\n        cmp == 0\n      }\n    def valid(version: MaybeScalaVersion): Boolean =\n      (loose && looselyValid(version)) || strictlyValid(version)\n    def failedMessage: String = s\"Expected version $requiredVersion\"\n  }\n  final case class VersionLowerThan(maxVersion: String, orEqual: Boolean)\n      extends VersionRequirement {\n    def valid(version: MaybeScalaVersion): Boolean =\n      version.versionOpt.exists { version =>\n        val cmp = coursier.core.Version(version).compare(coursier.core.Version(maxVersion))\n        cmp < 0 || (orEqual && cmp == 0)\n      }\n    def failedMessage: String =\n      if (orEqual) s\"Expected version lower than or equal to $maxVersion\"\n      else s\"Expected version lower than $maxVersion\"\n  }\n  final case class VersionHigherThan(minVersion: String, orEqual: Boolean)\n      extends VersionRequirement {\n    def valid(version: MaybeScalaVersion): Boolean =\n      version.versionOpt.exists { version =>\n        val cmp = coursier.core.Version(minVersion).compare(coursier.core.Version(version))\n        cmp < 0 || (orEqual && cmp == 0)\n      }\n    def failedMessage: String =\n      if (orEqual) s\"Expected version higher than or equal to $minVersion\"\n      else s\"Expected version higher than $minVersion\"\n  }\n\n  final case class PlatformRequirement(platforms: Set[Platform]) {\n    def valid(pf: Platform): Boolean =\n      platforms.contains(pf)\n    def failedMessage: String =\n      \"Expected platform: \" + platforms.toVector.map(_.repr).sorted.mkString(\" or \")\n  }\n\n  object PlatformRequirement {\n    def merge(requirements: Seq[PlatformRequirement]): Option[PlatformRequirement] =\n      if (requirements.isEmpty) None\n      else if (requirements.lengthCompare(1) == 0) Some(requirements.head)\n      else {\n        val platforms = requirements.tail.foldLeft(requirements.head.platforms) { (acc, req) =>\n          acc.intersect(req.platforms)\n        }\n        Some(PlatformRequirement(platforms))\n      }\n  }\n\n  final case class ScopeRequirement(scope: Scope)\n\n  implicit val monoid: ConfigMonoid[BuildRequirements] = ConfigMonoid.derive\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ClassPathOptions.scala",
    "content": "package scala.build.options\n\nimport dependency.*\n\nimport scala.build.Positioned\n\nfinal case class ClassPathOptions(\n  extraRepositories: Seq[String] = Nil,\n  extraClassPath: Seq[os.Path] = Nil,\n  extraCompileOnlyJars: Seq[os.Path] = Nil,\n  extraSourceJars: Seq[os.Path] = Nil,\n  fetchSources: Option[Boolean] = None,\n  extraDependencies: ShadowingSeq[Positioned[AnyDependency]] = ShadowingSeq.empty,\n  extraCompileOnlyDependencies: ShadowingSeq[Positioned[AnyDependency]] = ShadowingSeq.empty,\n  resourcesDir: Seq[os.Path] = Nil,\n  resourcesVirtualDir: Seq[os.SubPath] = Nil,\n  scalafixDependencies: ShadowingSeq[Positioned[AnyDependency]] = ShadowingSeq.empty\n) {\n  def allExtraDependencies: ShadowingSeq[Positioned[AnyDependency]] =\n    extraDependencies ++ extraCompileOnlyDependencies.toSeq\n}\n\nobject ClassPathOptions {\n  implicit val hasHashData: HasHashData[ClassPathOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[ClassPathOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ComputeVersion.scala",
    "content": "package scala.build.options\nimport org.eclipse.jgit.api.Git\nimport org.eclipse.jgit.lib.{Constants, Ref}\n\nimport scala.build.errors.{BuildException, MalformedInputError}\nimport scala.build.{Position, Positioned}\nimport scala.jdk.CollectionConverters.*\nimport scala.util.Using\n\nsealed abstract class ComputeVersion extends Product with Serializable {\n  def get(workspace: os.Path): Either[BuildException, String]\n\n  val positions: Seq[Position]\n}\n\nobject ComputeVersion {\n  final case class GitTag(\n    repo: os.FilePath,\n    dynVer: Boolean,\n    positions: Seq[Position],\n    defaultFirstVersion: String = \"0.1.0-SNAPSHOT\"\n  ) extends ComputeVersion {\n    import GitTag.GitTagError\n    private def versionOf(tag: String): Option[String] = {\n      val tag0 = tag.stripPrefix(Constants.R_TAGS)\n      if (tag0.isEmpty) None\n      else if (tag0.head.isDigit) Some(tag0)\n      else if (tag0.length >= 2 && tag0(0) == 'v' && tag0(1).isDigit) Some(tag0.drop(1))\n      else None\n    }\n    def get(workspace: os.Path): Either[BuildException, String] = {\n      val repo0 = repo.resolveFrom(workspace)\n      if (os.exists(repo0 / \".git\")) Using.resource(Git.open(repo0.toIO)) { git =>\n        val hasHead = git.getRepository.resolve(Constants.HEAD) != null\n        if (hasHead) {\n          val (lastTagOpt, lastStableTagOpt) = {\n            val tagMap = git.tagList()\n              .call()\n              .asScala\n              .iterator\n              .flatMap { tag =>\n                Option(git.getRepository.getRefDatabase.peel(tag).getPeeledObjectId)\n                  .orElse(Option(tag.getObjectId))\n                  .orElse(Option(tag.getPeeledObjectId))\n                  .iterator\n                  .map(id => (id.name, tag))\n              }\n              .toMap\n            val tagsIt = git.log()\n              .call()\n              .asScala\n              .iterator\n              .flatMap(c => tagMap.get(c.name()).iterator)\n              .flatMap(r => versionOf(r.getName).map((r, _)).iterator)\n              .scanLeft((Option.empty[(Ref, String)], Option.empty[(Ref, String)])) {\n                case ((acc, stableAcc), v @ (_, name)) =>\n                  val acc0       = acc.orElse(Some(v))\n                  val stableAcc0 = stableAcc.orElse {\n                    if (name.forall(c => c == '.' || c.isDigit)) Some(v)\n                    else None\n                  }\n                  (acc0, stableAcc0)\n              }\n            var lastTagOpt0       = Option.empty[(Ref, String)]\n            var lastStableTagOpt0 = Option.empty[(Ref, String)]\n            while (tagsIt.hasNext && (lastTagOpt0.isEmpty || lastStableTagOpt0.isEmpty)) {\n              val v = tagsIt.next()\n              if (lastTagOpt0.isEmpty)\n                lastTagOpt0 = v._1\n              if (lastStableTagOpt0.isEmpty)\n                lastStableTagOpt0 = v._2\n            }\n            (lastTagOpt0, lastStableTagOpt0)\n          }\n          val headCommit = git.log().call().asScala.iterator.next()\n\n          (lastTagOpt, lastStableTagOpt) match {\n            case (None, _) =>\n              Right(defaultFirstVersion)\n            case (Some((tag, name)), _)\n                if Option(git.getRepository.getRefDatabase.peel(tag).getPeeledObjectId)\n                  .exists(_.name == headCommit.name) ||\n                Option(tag.getObjectId).exists(_.name == headCommit.name) =>\n              Right(name)\n            case (Some((tag, _)), _) if dynVer =>\n              val tagOrNull = git.describe()\n                .setMatch(\"v[0-9]*\", \"[0-9]*\")\n                .setTags(true)\n                .setTarget(headCommit)\n                .call()\n              Option(tagOrNull) match {\n                case None =>\n                  Left(new GitTagError(\n                    positions,\n                    s\"Unexpected error when running git describe from Git repository $repo0 (git describe doesn't find back tag $tag)\"\n                  ))\n                case Some(tag) =>\n                  versionOf(tag).map(_ + \"-SNAPSHOT\").toRight(\n                    new GitTagError(\n                      positions,\n                      s\"Unexpected error when running git describe from Git repository $repo0 (git describe-provided tag $tag doesn't have the expected shape)\"\n                    )\n                  )\n              }\n            case (Some(_), None) =>\n              Left(new GitTagError(positions, s\"No stable tag found in Git repository $repo0\"))\n            case (_, Some((tag, name))) =>\n              val idx = name.lastIndexOf('.')\n              if (\n                idx >= 0 && idx < name.length - 1 && name.iterator.drop(idx + 1).forall(_.isDigit)\n              )\n                Right(name.take(idx + 1) + (name.drop(idx + 1).toInt + 1).toString + \"-SNAPSHOT\")\n              else\n                Left(new GitTagError(\n                  positions,\n                  s\"Don't know how to bump version in tag $tag in Git repository $repo0\"\n                ))\n          }\n        }\n        else\n          Right(defaultFirstVersion)\n      }\n      else\n        Left(new GitTagError(positions, s\"$repo0 doesn't look like a Git repository\"))\n    }\n  }\n  object GitTag {\n    final class GitTagError(positions: Seq[Position], message: String)\n        extends BuildException(message, positions)\n  }\n\n  def parse(input: Positioned[String]): Either[BuildException, ComputeVersion] =\n    if (input.value == \"git\" || input.value == \"git:tag\")\n      Right(ComputeVersion.GitTag(os.rel, dynVer = false, positions = input.positions))\n    else if (input.value.startsWith(\"git:tag:\"))\n      Right(ComputeVersion.GitTag(\n        os.FilePath(input.value.stripPrefix(\"git:tag:\")),\n        dynVer = false,\n        positions = input.positions\n      ))\n    else if (input.value == \"git:dynver\")\n      Right(ComputeVersion.GitTag(os.rel, dynVer = true, positions = input.positions))\n    else if (input.value.startsWith(\"git:dynver:\"))\n      Right(ComputeVersion.GitTag(\n        os.FilePath(input.value.stripPrefix(\"git:dynver:\")),\n        dynVer = true,\n        positions = input.positions\n      ))\n    else\n      Left(\n        new MalformedInputError(\n          \"compute-version\",\n          input.value,\n          \"git|git:tag|git:dynver\",\n          input.positions\n        )\n      )\n\n  def defaultComputeVersion(mayDefaultToGitTag: Boolean): Option[ComputeVersion] =\n    if mayDefaultToGitTag then Some(ComputeVersion.GitTag(os.rel, dynVer = false, positions = Nil))\n    else None\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ConfigMonoid.scala",
    "content": "package scala.build.options\n\nimport scala.compiletime.*\nimport scala.deriving.*\n\ntrait ConfigMonoid[T]:\n  def zero: T\n  def orElse(main: T, defaults: T): T\n\n  def sum(values: Seq[T]): T =\n    values.foldLeft(zero)(orElse(_, _))\n\ncase class ConfigMonoidImpl[T](override val zero: T)(orElseFun: (T, T) => T)\n    extends ConfigMonoid[T]:\n  def orElse(main: T, defaults: T) = orElseFun(main, defaults)\n\nobject ConfigMonoid:\n  def apply[T](implicit instance: ConfigMonoid[T]): ConfigMonoid[T] = instance\n\n  def instance[T](zeroValue: => T)(orElseFn: (T, T) => T): ConfigMonoid[T] =\n    new ConfigMonoid[T] {\n      def zero                         = zeroValue\n      def orElse(main: T, defaults: T) = orElseFn(main, defaults)\n    }\n\n  def sum[T](values: Seq[T])(implicit monoid: ConfigMonoid[T]): T =\n    monoid.sum(values)\n\n  given seq[T]: ConfigMonoid[Seq[T]]       = ConfigMonoidImpl(Nil)(_ ++ _)\n  given list[T]: ConfigMonoid[List[T]]     = ConfigMonoidImpl(Nil)(_ ++ _)\n  given set[T]: ConfigMonoid[Set[T]]       = ConfigMonoidImpl(Set.empty)(_ ++ _)\n  given option[T]: ConfigMonoid[Option[T]] = ConfigMonoidImpl(None)(_ orElse _)\n  given boolean: ConfigMonoid[Boolean]     = ConfigMonoidImpl(false)(_ || _)\n  given unit: ConfigMonoid[Unit]           = ConfigMonoidImpl[Unit](())((_, _) => ())\n  given map[K, V](using valueMonoid: ConfigMonoid[V]): ConfigMonoid[Map[K, V]] =\n    ConfigMonoidImpl(Map.empty[K, V]) {\n      (main, defaults) =>\n        (main.keySet ++ defaults.keySet).map {\n          key =>\n            val mainVal     = main.getOrElse(key, valueMonoid.zero)\n            val defaultsVal = defaults.getOrElse(key, valueMonoid.zero)\n            key -> valueMonoid.orElse(mainVal, defaultsVal)\n        }.toMap\n    }\n\n  inline def zeroTuple[C <: Tuple]: Tuple =\n    inline erasedValue[C] match\n      case _: EmptyTuple => EmptyTuple\n      case _: (t *: ts)  =>\n        summonInline[ConfigMonoid[t]].zero *: zeroTuple[ts]\n\n  inline def valueTuple[C <: Tuple, T](index: Int, main: T, defaults: T): Tuple =\n    inline erasedValue[C] match\n      case _: EmptyTuple => EmptyTuple\n      case _: (t *: ts)  =>\n        def get(v: T) = v.asInstanceOf[Product].productElement(index).asInstanceOf[t]\n        summonInline[ConfigMonoid[t]].orElse(get(main), get(defaults)) *: valueTuple[ts, T](\n          index + 1,\n          main,\n          defaults\n        )\n\n  inline given derive[T](using m: Mirror.ProductOf[T]): ConfigMonoid[T] =\n    inline m match\n      case p: Mirror.ProductOf[T] =>\n        new ConfigMonoid[T]:\n          def zero: T =\n            p.fromProduct(zeroTuple[m.MirroredElemTypes])\n\n          def orElse(main: T, defaults: T): T =\n            p.fromProduct(valueTuple[m.MirroredElemTypes, T](0, main, defaults))\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/HasHashData.scala",
    "content": "package scala.build.options\n\nimport scala.compiletime.*\nimport scala.deriving.*\n\ntrait HasHashData[T]:\n  def add(prefix: String, t: T, update: String => Unit): Unit\n\nobject HasHashData:\n  def nop[T]: HasHashData[T] = (_, _, _) => ()\n\n  inline def doAdd[C <: Tuple, T](\n    index: Int,\n    main: T,\n    prefix: String,\n    indexes: Seq[String],\n    update: String => Unit\n  ): Unit =\n    inline erasedValue[C] match\n      case _: EmptyTuple => ()\n      case _: (t *: ts)  =>\n        val hasher     = summonInline[HasHashData[t]]\n        val newPrefix  = prefix + \".\" + indexes(index) // in 2.x we were not adding '.'\n        val childValue = main.asInstanceOf[Product].productElement(index).asInstanceOf[t]\n        hasher.add(newPrefix, childValue, update)\n\n        doAdd[ts, T](index + 1, main, prefix, indexes, update)\n\n  inline given derive[T](using m: Mirror.ProductOf[T]): HasHashData[T] =\n    inline m match\n      case p: Mirror.ProductOf[T] =>\n        (prefix: String, main: T, update: String => Unit) =>\n          val labels =\n            constValueTuple[p.MirroredElemLabels].productIterator.toList.map(_.toString)\n          doAdd[m.MirroredElemTypes, T](0, main, prefix, labels, update)\n\n  given asIs[T](using hasher: HashedType[T]): HasHashData[T] =\n    (prefix, t, update) => update(s\"$prefix=${hasher.hashedValue(t)}\")\n\n  given seq[T](using hasher: HashedType[T]): HasHashData[Seq[T]] = // shouldn't we hash index here?\n    (name, seq, update) => seq.foreach(t => update(s\"$name+=${hasher.hashedValue(t)}\"))\n\n  given list[T](using\n    hasher: HashedType[T]\n  ): HasHashData[List[T]] = // shouldn't we hash index here?\n    (name, list, update) => list.foreach(t => update(s\"$name+=${hasher.hashedValue(t)}\"))\n\n  given listOfTuple[T](using\n    hasher: HashedType[T]\n  ): HasHashData[List[(T, T)]] =\n    (name, list, update) =>\n      list.foreach((t1, t2) => update(s\"$name+=${hasher.hashedValue(t1) + hasher.hashedValue(t2)}\"))\n\n  given option[T](using hasher: HashedType[T]): HasHashData[Option[T]] =\n    (name, opt, update) => opt.foreach(t => update(s\"$name=${hasher.hashedValue(t)}\"))\n\n  given set[T](using hasher: HashedType[T], ordering: Ordering[T]): HasHashData[Set[T]] =\n    (name, opt, update) =>\n      opt.toVector.sorted(using ordering)\n        .foreach(t => update(s\"$name=${hasher.hashedValue(t)}\"))\n\n  given map[K, V](using\n    hasherK: HashedType[K],\n    hasherV: HashedType[V],\n    ordering: Ordering[K]\n  ): HasHashData[Map[K, V]] =\n    (name, map0, update) =>\n      for ((k, v) <- map0.toVector.sortBy(_._1)(using ordering))\n        update(\n          s\"$name+=${hasherK.hashedValue(k)}${hasherV.hashedValue(v)}\"\n        ) // should't we seperate key and value here?\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/HasScope.scala",
    "content": "package scala.build.options\n\nfinal case class HasScope[+T](\n  scope: Scope,\n  value: T\n) {\n  def valueFor(currentScope: Scope): Option[T] =\n    if (currentScope == scope) Some(value)\n    else None\n  def valueForInheriting(currentScope: Scope): Option[T] =\n    if (currentScope.allScopes.contains(scope)) Some(value)\n    else None\n  def map[U](f: T => U): HasScope[U] =\n    copy(value = f(value))\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/HashedType.scala",
    "content": "package scala.build.options\n\nimport dependency.AnyDependency\n\nimport scala.build.internal.CodeWrapper\n\ntrait HashedType[T] {\n  def hashedValue(t: T): String\n}\n\nobject HashedType {\n  def apply[T](implicit instance: HashedType[T]): HashedType[T] = instance\n\n  implicit val string: HashedType[String] = {\n    str => str\n  }\n\n  implicit val int: HashedType[Int] = {\n    i => i.toString\n  }\n\n  implicit val path: HashedType[os.Path] = {\n    path => path.toString\n  }\n\n  implicit val subPath: HashedType[os.SubPath] = {\n    path => path.toString\n  }\n\n  implicit val boolean: HashedType[Boolean] = {\n    bool => bool.toString\n  }\n\n  implicit val anyDependency: HashedType[AnyDependency] = {\n    dep => dep.render\n  }\n\n  implicit val packageType: HashedType[PackageType] = {\n    tpe => tpe.toString\n  }\n\n  implicit val codeWrapper: HashedType[CodeWrapper] = {\n    wrapper => wrapper.toString\n  }\n\n  implicit val platform: HashedType[Platform] = {\n    pf => pf.repr\n  }\n\n  implicit val unit: HashedType[Unit] = {\n    _ => \"\"\n  }\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/InternalDependenciesOptions.scala",
    "content": "package scala.build.options\n\nfinal case class InternalDependenciesOptions(\n  addTestRunnerDependencyOpt: Option[Boolean] = None\n) {\n  def addTestRunnerDependency: Boolean =\n    addTestRunnerDependencyOpt.getOrElse(false)\n}\n\nobject InternalDependenciesOptions {\n  implicit val hasHashData: HasHashData[InternalDependenciesOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[InternalDependenciesOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/InternalOptions.scala",
    "content": "package scala.build.options\n\nimport coursier.cache.FileCache\nimport coursier.util.Task\n\nimport scala.build.Positioned\nimport scala.build.errors.BuildException\nimport scala.build.interactive.Interactive\n\ntype CodeFile = os.Path | java.net.URI\n\nfinal case class InternalOptions(\n  keepDiagnostics: Boolean = false,\n  cache: Option[FileCache[Task]] = None,\n  localRepository: Option[String] = None,\n  verbosity: Option[Int] = None,\n  // FIXME Should be removed, not a real option (not meant to be set from using directives)\n  strictBloopJsonCheck: Option[Boolean] = None,\n  interactive: Option[() => Either[BuildException, Interactive]] = None,\n  javaClassNameVersionOpt: Option[String] = None,\n  /** Whether to keep the coursier.Resolution instance in [[scala.build.Artifacts]]\n    *\n    * Resolutions can be quite heavy in memory (I don't recall the exact numbers, but 10s of MB if\n    * not more is not unseen when Spark modules are involves for example), so we only keep them when\n    * really needed.\n    */\n  keepResolution: Boolean = false,\n  extraSourceFiles: Seq[Positioned[CodeFile]] = Nil,\n  exclude: Seq[Positioned[String]] = Nil,\n  offline: Option[Boolean] = None\n) {\n  def verbosityOrDefault: Int                = verbosity.getOrElse(0)\n  def strictBloopJsonCheckOrDefault: Boolean =\n    strictBloopJsonCheck.getOrElse(InternalOptions.defaultStrictBloopJsonCheck)\n}\n\nobject InternalOptions {\n\n  def defaultStrictBloopJsonCheck = true\n\n  implicit val hasHashData: HasHashData[InternalOptions] = HasHashData.nop\n  implicit val monoid: ConfigMonoid[InternalOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/JavaOpt.scala",
    "content": "package scala.build.options\n\nfinal case class JavaOpt(value: String) {\n  def key: Option[String] =\n    JavaOpt.optionPrefixes.find(value.startsWith)\n      .orElse {\n        if (value.startsWith(\"-\"))\n          Some(value.takeWhile(_ != ':'))\n            .filterNot(key => JavaOpt.repeatingKeys.exists(_.startsWith(key)))\n        else if (value.startsWith(\"@\")) Some(\"@\")\n        else None\n      }\n\n}\n\nobject JavaOpt {\n  private val repeatingKeys = Set(\n    \"--add-exports\",\n    \"--add-modules\",\n    \"--add-opens\",\n    \"--add-reads\",\n    \"--patch-module\",\n    \"-XX\"\n  )\n\n  /* Hardcoded prefixes for java options */\n  private val optionPrefixes = Set(\"-Xmn\", \"-Xms\", \"-Xmx\", \"-Xss\")\n\n  implicit val hashedType: HashedType[JavaOpt] = {\n    opt => opt.value\n  }\n  implicit val keyOf: ShadowingSeq.KeyOf[JavaOpt] =\n    ShadowingSeq.KeyOf(\n      opts => opts.headOption.flatMap(_.key).orElse(Some(opts.map(_.value).mkString(\":\"))),\n      seq => ScalacOpt.groupCliOptions(seq.map(_.value))\n    )\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/JavaOptions.scala",
    "content": "package scala.build.options\n\nimport bloop.rifle.VersionUtil.jvmRelease\nimport coursier.cache.{ArchiveCache, FileCache}\nimport coursier.jvm.{JavaHome, JvmCache, JvmChannel, JvmIndex}\nimport coursier.util.Task\nimport dependency.AnyDependency\n\nimport scala.build.internal.CsLoggerUtil.*\nimport scala.build.internal.OsLibc\nimport scala.build.options.BuildOptions.JavaHomeInfo\nimport scala.build.{Os, Position, Positioned}\nimport scala.concurrent.ExecutionContextExecutorService\nimport scala.util.control.NonFatal\nimport scala.util.{Properties, Try}\n\n/** @javaOpts\n  *   java options which will be passed when compiling sources.\n  * @javacOptions\n  *   java options which will be passed when running an application.\n  */\nfinal case class JavaOptions(\n  javaHomeOpt: Option[Positioned[os.Path]] = None,\n  jvmIdOpt: Option[Positioned[String]] = None,\n  jvmIndexOpt: Option[String] = None,\n  jvmIndexOs: Option[String] = None,\n  jvmIndexArch: Option[String] = None,\n  javaOpts: ShadowingSeq[Positioned[JavaOpt]] = ShadowingSeq.empty,\n  javacPluginDependencies: Seq[Positioned[AnyDependency]] = Nil,\n  javacPlugins: Seq[Positioned[os.Path]] = Nil,\n  javacOptions: Seq[Positioned[String]] = Nil\n) {\n\n  private def finalJvmIndexOs = jvmIndexOs.getOrElse(OsLibc.jvmIndexOs)\n\n  def javaHomeManager(\n    archiveCache: ArchiveCache[Task],\n    cache: FileCache[Task],\n    verbosity: Int\n  ): JavaHome = {\n    val indexUrl  = jvmIndexOpt.getOrElse(JvmChannel.gitHubIndexUrl)\n    val indexTask = {\n      val msg    = if (verbosity > 0) \"Downloading JVM index\" else \"\"\n      val cache0 = cache.withMessage(msg)\n      cache0.logger.using {\n        JvmIndex.load(cache0, indexUrl)\n      }\n    }\n    val jvmCache = JvmCache()\n      .withIndex(indexTask)\n      .withArchiveCache(\n        archiveCache.withCache(\n          cache.withMessage(\"Downloading JVM\")\n        )\n      )\n      .withOs(finalJvmIndexOs)\n      .withArchitecture(jvmIndexArch.getOrElse(JvmChannel.defaultArchitecture()))\n    JavaHome().withCache(jvmCache)\n  }\n\n  def javaHomeLocationOpt(\n    archiveCache: ArchiveCache[Task],\n    cache: FileCache[Task],\n    verbosity: Int\n  ): Option[Positioned[os.Path]] =\n    lazy val javaHomeManager0 = javaHomeManager(archiveCache, cache, verbosity)\n\n    implicit val ec: ExecutionContextExecutorService = cache.ec\n\n    def isJvmVersion(jvmId: String): Boolean =\n      jvmId.forall(c => c.isDigit || c == '.' || c == '-')\n\n    javaHomeOpt\n      .orElse {\n        if (jvmIdOpt.isEmpty)\n          findLocalDefaultJava()\n        else if (\n          Properties.isMac && jvmIdOpt.exists(jvmId =>\n            isJvmVersion(jvmId.value.stripPrefix(\"system|\"))\n          )\n        )\n          val jvmVersionOpt = for {\n            jvmId <- jvmIdOpt\n            maybeJvmVersion = jvmId.value.stripPrefix(\"system|\")\n            jvmVersion <- jvmRelease(maybeJvmVersion)\n          } yield jvmVersion.toString\n\n          findLocalJavaOnMacOs(jvmVersionOpt)\n        else None\n      }\n      .orElse {\n        jvmIdOpt.map(_.value).map { jvmId =>\n\n          cache.logger.use {\n            val enforceLiberica =\n              finalJvmIndexOs == \"linux-musl\" &&\n              isJvmVersion(jvmId)\n            val enforceZulu =\n              Os.isArmArchitecture &&\n              isJvmVersion(jvmId)\n            val jvmId0 =\n              if (enforceLiberica)\n                s\"liberica:$jvmId\" // FIXME Workaround, until this is automatically handled by coursier-jvm\n              else if (enforceZulu) // default jvmId adoptium doesn't support java 8 for M1\n                s\"zulu:$jvmId\"\n              else\n                jvmId\n\n            val path =\n              try javaHomeManager0\n                  .withMessage(s\"Downloading JVM $jvmId0\")\n                  .get(jvmId0).unsafeRun()\n              catch {\n                case NonFatal(e) => throw new Exception(e)\n              }\n            Positioned(Position.CommandLine(\"--jvm\"), os.Path(path))\n          }\n        }\n      }\n\n  def javaHomeLocation(\n    archiveCache: ArchiveCache[Task],\n    cache: FileCache[Task],\n    verbosity: Int\n  ): Positioned[os.Path] =\n    javaHomeLocationOpt(archiveCache, cache, verbosity).getOrElse {\n      val jvmId            = OsLibc.defaultJvm(finalJvmIndexOs)\n      val javaHomeManager0 = javaHomeManager(archiveCache, cache, verbosity)\n        .withMessage(s\"Downloading JVM $jvmId\")\n      implicit val ec: ExecutionContextExecutorService = cache.ec\n      cache.logger.use {\n        val path =\n          try javaHomeManager0.get(jvmId).unsafeRun()\n          catch {\n            case NonFatal(e) => throw new Exception(e)\n          }\n        Positioned(Position.Custom(\"OsLibc.defaultJvm\"), os.Path(path))\n      }\n    }\n\n  def javaHome(\n    archiveCache: ArchiveCache[Task],\n    cache: FileCache[Task],\n    verbosity: Int\n  ): Positioned[JavaHomeInfo] =\n    javaHomeLocation(archiveCache, cache, verbosity).map { javaHome =>\n      val (javaVersion, javaCmd) = OsLibc.javaHomeVersion(javaHome)\n      JavaHomeInfo(javaHome, javaCmd, javaVersion)\n    }\n\n  private def findLocalDefaultJava(): Option[Positioned[os.Path]] =\n    Option(System.getenv(\"JAVA_HOME\")).filter(_.nonEmpty).map(p =>\n      Positioned(Position.Custom(\"JAVA_HOME env\"), os.Path(p, os.pwd))\n    ).orElse(\n      sys.props.get(\"java.home\").map(p =>\n        Positioned(Position.Custom(\"java.home prop\"), os.Path(p, os.pwd))\n      )\n    ).orElse(\n      if (Properties.isMac)\n        findLocalJavaOnMacOs(None)\n      else None\n    )\n  private def findLocalJavaOnMacOs(jvmIdOpt: Option[String]): Option[Positioned[os.Path]] =\n    Try {\n      jvmIdOpt.fold(os.proc(\"/usr/libexec/java_home\")) { jvmId =>\n        os.proc(\n          \"/usr/libexec/java_home\",\n          \"-v\",\n          jvmId.stripPrefix(\"system|\"),\n          \"--failfast\"\n        )\n      }.call(os.pwd, check = true, mergeErrIntoOut = true)\n        .out.text().trim()\n    }\n      .toOption\n      .flatMap(p => Try(os.Path(p, os.pwd)).toOption)\n      .filter(os.exists(_))\n      .map(p => Positioned(Position.Custom(\"/usr/libexec/java_home -v\"), p))\n}\n\nobject JavaOptions {\n  implicit val hasHashData: HasHashData[JavaOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[JavaOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/JmhOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.internal.Constants\n\n/** @param jmhVersion\n  *   the version of JMH to be used in the build\n  * @param enableJmh\n  *   toggle for enabling JMH dependency handling in the build (overrides [[runJmh]] when disabled)\n  * @param runJmh\n  *   toggle for whether JMH should actually be runnable from this build (this value gets changed in\n  *   JMH builds to detect which main class is to be called as benchmarks are being run)\n  */\nfinal case class JmhOptions(\n  jmhVersion: Option[String] = None,\n  enableJmh: Option[Boolean] = None,\n  runJmh: Option[Boolean] = None\n) {\n  def finalJmhVersion: Option[String] = jmhVersion.orElse(enableJmh match {\n    case Some(true) => Some(Constants.jmhVersion)\n    case _          => None\n  })\n\n  def canRunJmh: Boolean =\n    (for { enabled <- enableJmh; runnable <- runJmh } yield enabled && runnable)\n      .getOrElse(false)\n}\n\nobject JmhOptions {\n  implicit val hasHashData: HasHashData[JmhOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[JmhOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/MaybeScalaVersion.scala",
    "content": "package scala.build.options\n\nfinal case class MaybeScalaVersion(versionOpt: Option[String] = None) {\n  def asString = versionOpt.getOrElse(MaybeScalaVersion.noneStr)\n}\n\nobject MaybeScalaVersion {\n\n  private def noneStr = \"none\"\n\n  def none: MaybeScalaVersion =\n    MaybeScalaVersion(noneStr)\n\n  def apply(s: String): MaybeScalaVersion =\n    if (s == noneStr) MaybeScalaVersion(None)\n    else MaybeScalaVersion(Some(s))\n\n  implicit lazy val hashedType: HashedType[MaybeScalaVersion] = { v =>\n    HashedType.string.hashedValue(v.versionOpt.getOrElse(\"no-scala-version\"))\n  }\n  implicit lazy val hasHashData: HasHashData[MaybeScalaVersion] =\n    HasHashData.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/PackageOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.internal.Constants\nimport scala.build.options.packaging.*\n\nfinal case class PackageOptions(\n  standalone: Option[Boolean] = None,\n  version: Option[String] = None,\n  launcherApp: Option[String] = None,\n  maintainer: Option[String] = None,\n  description: Option[String] = None,\n  output: Option[String] = None,\n  packageTypeOpt: Option[PackageType] = None,\n  logoPath: Option[os.Path] = None,\n  macOSidentifier: Option[String] = None,\n  debianOptions: DebianOptions = DebianOptions(),\n  windowsOptions: WindowsOptions = WindowsOptions(),\n  redHatOptions: RedHatOptions = RedHatOptions(),\n  dockerOptions: DockerOptions = DockerOptions(),\n  nativeImageOptions: NativeImageOptions = NativeImageOptions(),\n  useDefaultScaladocOptions: Option[Boolean] = None,\n  provided: Seq[dependency.AnyModule] = Nil\n) {\n\n  def packageVersion: String =\n    version\n      .map(_.trim)\n      .filter(_.nonEmpty)\n      .getOrElse(Constants.version.stripSuffix(\"-SNAPSHOT\"))\n\n  def isDockerEnabled: Boolean = dockerOptions.isDockerEnabled.getOrElse(false)\n\n  // default behaviour for building docker image is building standalone JARs\n  def isStandalone: Boolean = standalone.getOrElse(isDockerEnabled)\n\n}\n\nobject PackageOptions {\n  implicit val monoid: ConfigMonoid[PackageOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/PackageType.scala",
    "content": "package scala.build.options\n\nsealed abstract class PackageType extends Product with Serializable {\n  def runnable: Option[Boolean] = Some(false)\n  def sourceBased: Boolean      = false\n}\n\nobject PackageType {\n  sealed abstract class NativePackagerType extends PackageType\n\n  case object Bootstrap extends PackageType {\n    override def runnable = Some(true)\n  }\n  case object LibraryJar extends PackageType\n  case object SourceJar  extends PackageType {\n    override def sourceBased = true\n  }\n  case object DocJar extends PackageType\n  final case class Assembly(\n    addPreamble: Boolean,\n    mainClassInManifest: Option[Boolean]\n  ) extends PackageType {\n    override def runnable = Some(addPreamble)\n  }\n  case object Spark extends PackageType {\n    override def runnable = Some(false)\n  }\n  case object Js      extends PackageType\n  sealed trait Native extends PackageType\n  object Native {\n    case object Application extends Native {\n      override def runnable = Some(true)\n    }\n    case object LibraryDynamic extends Native {\n      override def runnable = Some(false)\n    }\n    case object LibraryStatic extends Native {\n      override def runnable = Some(false)\n    }\n  }\n  case object Docker extends PackageType {\n    override def runnable = None\n  }\n  case object GraalVMNativeImage extends PackageType {\n    override def runnable = Some(true)\n  }\n  case object Debian extends NativePackagerType\n  case object Dmg    extends NativePackagerType\n  case object Pkg    extends NativePackagerType\n  case object Rpm    extends NativePackagerType\n  case object Msi    extends NativePackagerType\n\n  val mapping = Seq(\n    \"assembly\"     -> Assembly(true, None),\n    \"raw-assembly\" -> Assembly(false, Some(false)),\n    \"bootstrap\"    -> Bootstrap,\n    \"library\"      -> LibraryJar,\n    \"source\"       -> SourceJar,\n    \"doc\"          -> DocJar,\n    \"spark\"        -> Spark,\n    \"js\"           -> Js,\n    \"native\"       -> Native.Application,\n    \"docker\"       -> Docker,\n    \"graalvm\"      -> GraalVMNativeImage,\n    \"deb\"          -> Debian,\n    \"dmg\"          -> Dmg,\n    \"pkg\"          -> Pkg,\n    \"rpm\"          -> Rpm,\n    \"msi\"          -> Msi\n  )\n  private lazy val map                          = mapping.toMap\n  def parse(input: String): Option[PackageType] =\n    map.get(input)\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/Platform.scala",
    "content": "package scala.build.options\n\nimport java.util.Locale\n\nsealed abstract class Platform(val repr: String) extends Product with Serializable\n\nobject Platform {\n  case object JVM    extends Platform(\"JVM\")\n  case object JS     extends Platform(\"JS\")\n  case object Native extends Platform(\"Native\")\n\n  def normalize(p: String): String =\n    p.toLowerCase(Locale.ROOT) match {\n      case \"scala.js\" | \"scala-js\" | \"scalajs\" | \"js\" => \"js\"\n      case \"scala-native\" | \"scalanative\" | \"native\"  => \"native\"\n      case \"jvm\"                                      => \"jvm\"\n      case _                                          => p\n    }\n  def parse(p: String): Option[Platform] =\n    p match {\n      case \"jvm\"    => Some(JVM)\n      case \"js\"     => Some(JS)\n      case \"native\" => Some(Native)\n      case _        => None\n    }\n\n  private def parseSpec0(\n    l: List[String],\n    acc: Set[Platform]\n  ): Option[Set[Platform]] =\n    l match {\n      case Nil       => None\n      case p :: Nil  => parse(p).map(p0 => acc + p0)\n      case p :: tail =>\n        parse(p) match {\n          case Some(p0) => parseSpec0(tail, acc + p0)\n          case None     => None\n        }\n    }\n\n  def parseSpec(input: Seq[String]): Option[Set[Platform]] =\n    parseSpec0(input.toList, Set.empty)\n\n  implicit val ordering: Ordering[Platform] =\n    Ordering.by {\n      case Platform.JVM    => 0\n      case Platform.JS     => 1\n      case Platform.Native => 2\n    }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/PostBuildOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.options.scalajs.ScalaJsLinkerOptions\n\nfinal case class PostBuildOptions(\n  packageOptions: PackageOptions = PackageOptions(),\n  replOptions: ReplOptions = ReplOptions(),\n  publishOptions: PublishOptions = PublishOptions(),\n  scalaJsLinkerOptions: ScalaJsLinkerOptions = ScalaJsLinkerOptions(),\n  runWithManifest: Option[Boolean] = None,\n  pythonSetup: Option[Boolean] = None,\n  python: Option[Boolean] = None,\n  scalaPyVersion: Option[String] = None,\n  addRunnerDependencyOpt: Option[Boolean] = None\n) {\n\n  def doSetupPython: Option[Boolean] =\n    pythonSetup.orElse(python)\n}\n\nobject PostBuildOptions {\n  /* Using HasHashData.nop here (PostBuildOptions values are not used during compilation) */\n  implicit val hasHashData: HasHashData[PostBuildOptions] = HasHashData.nop\n  implicit val monoid: ConfigMonoid[PostBuildOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/PublishContextualOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.options.publish.{ConfigPasswordOption, Signer}\nimport scala.cli.signing.shared.PasswordOption\n\n/** Publishing-related options, that can have different values locally and on CIs */\nfinal case class PublishContextualOptions(\n  repository: Option[String] = None,\n  repositoryIsIvy2LocalLike: Option[Boolean] = None,\n  sourceJar: Option[Boolean] = None,\n  docJar: Option[Boolean] = None,\n  gpgSignatureId: Option[String] = None,\n  gpgOptions: List[String] = Nil,\n  signer: Option[Signer] = None,\n  secretKey: Option[ConfigPasswordOption] = None,\n  secretKeyPassword: Option[ConfigPasswordOption] = None,\n  publicKey: Option[ConfigPasswordOption] = None,\n  repoUser: Option[PasswordOption] = None,\n  repoPassword: Option[PasswordOption] = None,\n  repoRealm: Option[String] = None,\n  computeVersion: Option[ComputeVersion] = None,\n  checksums: Option[Seq[String]] = None,\n  connectionTimeoutRetries: Option[Int] = None,\n  connectionTimeoutSeconds: Option[Int] = None,\n  responseTimeoutSeconds: Option[Int] = None,\n  stagingRepoRetries: Option[Int] = None,\n  stagingRepoWaitTimeMilis: Option[Int] = None\n)\n\nobject PublishContextualOptions {\n  implicit val monoid: ConfigMonoid[PublishContextualOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/PublishOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.Positioned\nimport scala.build.options.publish.{Developer, License, Vcs}\n\nfinal case class PublishOptions(\n  organization: Option[Positioned[String]] = None,\n  name: Option[Positioned[String]] = None,\n  moduleName: Option[Positioned[String]] = None,\n  version: Option[Positioned[String]] = None,\n  url: Option[Positioned[String]] = None,\n  license: Option[Positioned[License]] = None,\n  versionControl: Option[Vcs] = None,\n  description: Option[String] = None,\n  developers: Seq[Developer] = Nil,\n  scalaVersionSuffix: Option[String] = None,\n  scalaPlatformSuffix: Option[String] = None,\n  local: PublishContextualOptions = PublishContextualOptions(),\n  ci: PublishContextualOptions = PublishContextualOptions(),\n  signingCli: ScalaSigningCliOptions = ScalaSigningCliOptions()\n) {\n  def retained(isCi: Boolean): PublishContextualOptions =\n    if (isCi) ci\n    else local\n  def contextual(isCi: Boolean): PublishContextualOptions =\n    if (isCi) PublishContextualOptions.monoid.orElse(ci, local)\n    else local\n}\n\nobject PublishOptions {\n  implicit val monoid: ConfigMonoid[PublishOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ReplOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.Logger\nimport scala.build.internal.Constants\n\nfinal case class ReplOptions(\n  useAmmoniteOpt: Option[Boolean] = None,\n  ammoniteVersionOpt: Option[String] = None,\n  ammoniteArgs: Seq[String] = Nil\n) {\n  def useAmmonite: Boolean =\n    useAmmoniteOpt.getOrElse(false)\n  def ammoniteVersion(scalaVersion: String, logger: Logger): String =\n    ammoniteVersionOpt.getOrElse {\n      if scalaVersion.startsWith(\"3.3\") then {\n        val ammoniteVersionForLts = Constants.ammoniteVersionForScala3Lts\n        logger.debug(s\"Using the default Ammonite version for Scala 3 LTS: $ammoniteVersionForLts\")\n        ammoniteVersionForLts\n      }\n      else Constants.ammoniteVersion\n    }\n}\n\nobject ReplOptions {\n  implicit val monoid: ConfigMonoid[ReplOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/SNNumeralVersion.scala",
    "content": "package scala.build.options\n\ncase class SNNumeralVersion(major: Int, minor: Int, patch: Int) {\n  def <(that: SNNumeralVersion): Boolean =\n    if (this.major == that.major)\n      if (this.minor == that.minor) this.patch < that.patch\n      else this.minor < that.minor\n    else this.major < that.major\n\n  def <=(that: SNNumeralVersion) = this < that || this == that\n\n  def >(that: SNNumeralVersion) = !(this <= that)\n\n  def >=(that: SNNumeralVersion) = !(this < that)\n}\n\nobject SNNumeralVersion {\n  implicit val ordering: Ordering[SNNumeralVersion] = Ordering.by(v => (v.major, v.minor, v.patch))\n  private val VersionPattern                        = raw\"(\\d+)\\.(\\d+)\\.(\\d+)(\\-.*)?\".r\n\n  // tags/suffixes are not included or compared since they usually\n  // should offer no feature compatibility improvements\n  def parse(v: String): Option[SNNumeralVersion] = v match {\n    case VersionPattern(major, minor, patch, _) =>\n      Some(SNNumeralVersion(major.toInt, minor.toInt, patch.toInt))\n    case _ =>\n      None\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScalaJsOptions.scala",
    "content": "package scala.build.options\n\nimport bloop.config.Config as BloopConfig\nimport dependency.*\n\nimport java.util.Locale\n\nimport scala.build.Logger\nimport scala.build.errors.{BuildException, UnrecognizedJsOptModeError}\nimport scala.build.internal.{Constants, ScalaJsLinkerConfig}\n\nfinal case class ScalaJsOptions(\n  version: Option[String] = None,\n  mode: ScalaJsMode = ScalaJsMode(),\n  moduleKindStr: Option[String] = None,\n  checkIr: Option[Boolean] = None,\n  emitSourceMaps: Boolean = false,\n  sourceMapsDest: Option[os.Path] = None,\n  remapEsModuleImportMap: Option[os.Path] = None,\n  dom: Option[Boolean] = None,\n  header: Option[String] = None,\n  allowBigIntsForLongs: Option[Boolean] = None,\n  avoidClasses: Option[Boolean] = None,\n  avoidLetsAndConsts: Option[Boolean] = None,\n  moduleSplitStyleStr: Option[String] = None,\n  smallModuleForPackage: List[String] = Nil,\n  esVersionStr: Option[String] = None,\n  noOpt: Option[Boolean] = None,\n  jsEmitWasm: Boolean = false\n) {\n  def fullOpt: Either[UnrecognizedJsOptModeError, Boolean] =\n    if (mode.isValid)\n      if (noOpt.contains(true))\n        Right(false)\n      else\n        Right(mode.nameOpt.exists(ScalaJsMode.validFullLinkAliases.contains))\n    else\n      Left(UnrecognizedJsOptModeError(\n        mode.nameOpt.getOrElse(\"None\"), // shouldn't happen since None is valid\n        ScalaJsMode.validFullLinkAliases.toSeq,\n        ScalaJsMode.validFastLinkAliases.toSeq\n      ))\n  def platformSuffix: String =\n    \"sjs\" + ScalaVersion.jsBinary(finalVersion).getOrElse(finalVersion)\n  def jsDependencies(scalaVersion: String): Seq[AnyDependency] =\n    if (scalaVersion.startsWith(\"2.\"))\n      Seq(dep\"org.scala-js::scalajs-library:$finalVersion\")\n    else\n      Seq(dep\"org.scala-js:scalajs-library_2.13:$finalVersion\")\n  def compilerPlugins(scalaVersion: String): Seq[AnyDependency] =\n    if (scalaVersion.startsWith(\"2.\"))\n      Seq(dep\"org.scala-js:::scalajs-compiler:$finalVersion\")\n    else\n      Nil\n\n  def moduleKind(logger: Logger): String =\n    moduleKindStr\n      .map(_.trim.toLowerCase(Locale.ROOT))\n      .map {\n        case \"commonjs\" | \"common\" => ScalaJsLinkerConfig.ModuleKind.CommonJSModule\n        case \"esmodule\" | \"es\"     => ScalaJsLinkerConfig.ModuleKind.ESModule\n        case \"nomodule\" | \"none\"   => ScalaJsLinkerConfig.ModuleKind.NoModule\n        case unknown               =>\n          logger.message(\n            s\"Warning: unrecognized argument: $unknown for --js-module-kind parameter, using default value: nomodule\"\n          )\n          ScalaJsLinkerConfig.ModuleKind.NoModule\n      }\n      .getOrElse(ScalaJsLinkerConfig.ModuleKind.NoModule)\n\n  def moduleSplitStyle(logger: Logger): String =\n    moduleSplitStyleStr\n      .map(_.trim.toLowerCase(Locale.ROOT))\n      .map {\n        case \"fewestmodules\"   => ScalaJsLinkerConfig.ModuleSplitStyle.FewestModules\n        case \"smallestmodules\" => ScalaJsLinkerConfig.ModuleSplitStyle.SmallestModules\n        case \"smallmodulesfor\" => ScalaJsLinkerConfig.ModuleSplitStyle.SmallModulesFor\n        case unknown           =>\n          logger.message(\n            s\"Warning: unrecognized argument: $unknown for --js-module-split-style parameter, use default value: fewestmodules\"\n          )\n          ScalaJsLinkerConfig.ModuleSplitStyle.FewestModules\n      }\n      .getOrElse(ScalaJsLinkerConfig.ModuleSplitStyle.FewestModules)\n\n  def esVersion(logger: Logger): String =\n    esVersionStr\n      .map(_.trim.toLowerCase(Locale.ROOT))\n      .map {\n        case \"es5_1\"  => ScalaJsLinkerConfig.ESVersion.ES5_1\n        case \"es2015\" => ScalaJsLinkerConfig.ESVersion.ES2015\n        case \"es2016\" => ScalaJsLinkerConfig.ESVersion.ES2016\n        case \"es2017\" => ScalaJsLinkerConfig.ESVersion.ES2017\n        case \"es2018\" => ScalaJsLinkerConfig.ESVersion.ES2018\n        case \"es2019\" => ScalaJsLinkerConfig.ESVersion.ES2019\n        case \"es2020\" => ScalaJsLinkerConfig.ESVersion.ES2020\n        case \"es2021\" => ScalaJsLinkerConfig.ESVersion.ES2021\n        case unknown  =>\n          val default = ScalaJsLinkerConfig.ESVersion.default\n          logger.message(\n            s\"Warning: unrecognized argument: $unknown for --js-es-version parameter, use default value: $default\"\n          )\n          default\n      }\n      .getOrElse(ScalaJsLinkerConfig.ESVersion.default)\n\n  def finalVersion = version.map(_.trim).filter(_.nonEmpty).getOrElse(Constants.scalaJsVersion)\n\n  private def configUnsafe(\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException]\n  ): Either[BuildException, BloopConfig.JsConfig] = for {\n    isFullOpt <- {\n      fullOpt match {\n        case Left(be) if maybeRecoverOnError(be).isEmpty => Right(false)\n        case otherwise                                   => otherwise\n      }\n    }\n  } yield {\n    val kind = moduleKind(logger) match {\n      case ScalaJsLinkerConfig.ModuleKind.CommonJSModule => BloopConfig.ModuleKindJS.CommonJSModule\n      case ScalaJsLinkerConfig.ModuleKind.ESModule       => BloopConfig.ModuleKindJS.ESModule\n      case ScalaJsLinkerConfig.ModuleKind.NoModule       => BloopConfig.ModuleKindJS.NoModule\n      // shouldn't happen\n      case _ => BloopConfig.ModuleKindJS.NoModule\n    }\n    BloopConfig.JsConfig(\n      version = finalVersion,\n      mode =\n        if isFullOpt then BloopConfig.LinkerMode.Release\n        else BloopConfig.LinkerMode.Debug,\n      kind = kind,\n      emitSourceMaps = emitSourceMaps,\n      jsdom = dom,\n      output = None,\n      nodePath = None,\n      toolchain = Nil\n    )\n  }\n\n  def config(\n    logger: Logger,\n    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)\n  ): Either[BuildException, BloopConfig.JsConfig] =\n    configUnsafe(logger, maybeRecoverOnError)\n\n  def linkerConfig(logger: Logger): ScalaJsLinkerConfig = {\n    val esFeatureDefaults = ScalaJsLinkerConfig.ESFeatures()\n    val esFeatures        = ScalaJsLinkerConfig.ESFeatures(\n      allowBigIntsForLongs =\n        allowBigIntsForLongs.getOrElse(esFeatureDefaults.allowBigIntsForLongs),\n      avoidClasses = avoidClasses.getOrElse(esFeatureDefaults.avoidClasses),\n      avoidLetsAndConsts = avoidLetsAndConsts.getOrElse(esFeatureDefaults.avoidLetsAndConsts),\n      esVersion = esVersion(logger)\n    )\n\n    ScalaJsLinkerConfig(\n      moduleKind = moduleKind(logger),\n      checkIR = checkIr.getOrElse(false), // meh\n      sourceMap = emitSourceMaps,\n      moduleSplitStyle = moduleSplitStyle(logger),\n      smallModuleForPackage = smallModuleForPackage,\n      esFeatures = esFeatures,\n      jsHeader = header,\n      remapEsModuleImportMap = remapEsModuleImportMap,\n      emitWasm = jsEmitWasm\n    )\n  }\n}\n\ncase class ScalaJsMode(nameOpt: Option[String] = None) {\n  lazy val isValid: Boolean = nameOpt.isEmpty || nameOpt.exists(ScalaJsMode.allAliases.contains)\n}\nobject ScalaJsMode {\n  val validFullLinkAliases = Set(\n    \"release\",\n    \"fullLinkJs\",\n    \"fullLinkJS\",\n    \"full\"\n  )\n  val validFastLinkAliases = Set(\n    \"dev\",\n    \"fastLinkJs\",\n    \"fastLinkJS\",\n    \"fast\"\n  )\n  def allAliases: Set[String] =\n    ScalaJsMode.validFullLinkAliases.union(ScalaJsMode.validFastLinkAliases)\n}\n\nobject ScalaJsOptions {\n  implicit val hasHashData: HasHashData[ScalaJsOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[ScalaJsOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala",
    "content": "package scala.build.options\n\nimport _root_.bloop.config.Config as BloopConfig\nimport dependency.*\n\nimport java.nio.file.Paths\n\nimport scala.build.internal.Constants\nimport scala.scalanative.build as sn\nimport scala.scalanative.build.LTO\n\nenum ScalaNativeTarget:\n  case Application, LibraryDynamic, LibraryStatic\n\n  def toBuildTarget: sn.BuildTarget =\n    this match\n      case Application    => sn.BuildTarget.application\n      case LibraryDynamic => sn.BuildTarget.libraryDynamic\n      case _              => sn.BuildTarget.libraryStatic\n\nobject ScalaNativeTarget:\n  def fromString(str: String): Option[ScalaNativeTarget] =\n    str match\n      case \"application\" | \"app\"                    => Some(Application)\n      case \"library-dynamic\" | \"dynamic\" | \"shared\" => Some(LibraryDynamic)\n      case \"library-static\" | \"static\"              => Some(LibraryStatic)\n      case _                                        => None\n\nfinal case class ScalaNativeOptions(\n  version: Option[String] = None,\n  modeStr: Option[String] = None,\n  ltoStr: Option[String] = None,\n  gcStr: Option[String] = None,\n  clang: Option[String] = None,\n  clangpp: Option[String] = None,\n  linkingOptions: List[String] = Nil,\n  linkingDefaults: Option[Boolean] = None,\n  compileOptions: List[String] = Nil,\n  cCompileOptions: List[String] = Nil,\n  cppCompileOptions: List[String] = Nil,\n  compileDefaults: Option[Boolean] = None,\n  embedResources: Option[Boolean] = None,\n  buildTargetStr: Option[String] = None,\n  multithreading: Option[Boolean] = None,\n  maxDefaultNativeVersions: List[(String, String)] = Nil\n) {\n\n  def defaultMaxVersion: Option[String] =\n    maxDefaultNativeVersions\n      .map(_._1)\n      .map(v => v -> SNNumeralVersion.parse(v))\n      .minByOption(_._2)\n      .map(_._1)\n\n  def finalVersion: String = version.map(_.trim).filter(_.nonEmpty)\n    .orElse(defaultMaxVersion)\n    .getOrElse(Constants.scalaNativeVersion)\n\n  def numeralVersion: Option[SNNumeralVersion] = SNNumeralVersion.parse(finalVersion)\n\n  def target(): Option[ScalaNativeTarget] =\n    buildTargetStr.flatMap(ScalaNativeTarget.fromString)\n\n  private def targetCliOption(): List[String] =\n    import ScalaNativeTarget.*\n    val targ = target().map {\n      case Application    => \"application\"\n      case LibraryDynamic => \"library-dynamic\"\n      case LibraryStatic  => \"library-static\"\n    }\n\n    targ.toList.flatMap(opt => List(\"--build-target\", opt))\n\n  private def gc(): sn.GC =\n    gcStr.map(_.trim).filter(_.nonEmpty) match {\n      case Some(\"default\") | None => sn.Discover.GC()\n      case Some(other)            => sn.GC(other)\n    }\n  private def gcCliOption(): List[String] =\n    List(\"--gc\", gc().name)\n\n  private def mode(): sn.Mode =\n    modeStr.map(_.trim).filter(_.nonEmpty) match {\n      case Some(\"default\") | None => sn.Discover.mode()\n      case Some(other)            => sn.Mode(other)\n    }\n  private def modeCliOption(): List[String] =\n    List(\"--mode\", mode().name)\n\n  private def clangPath() = clang\n    .filter(_.nonEmpty)\n    .map(Paths.get(_))\n    .getOrElse(sn.Discover.clang())\n  private def clangCliOption(): List[String] =\n    List(\"--clang\", clangPath().toString())\n\n  private def clangppPath() = clangpp\n    .filter(_.nonEmpty)\n    .map(Paths.get(_))\n    .getOrElse(sn.Discover.clangpp())\n  private def clangppCliOption(): List[String] =\n    List(\"--clang-pp\", clangppPath().toString())\n\n  private def finalLinkingOptions(): List[String] =\n    linkingOptions ++ (if (linkingDefaults.getOrElse(true)) sn.Discover.linkingOptions() else Nil)\n  private def finalCompileOptions(): List[String] =\n    compileOptions ++ (if (compileDefaults.getOrElse(true)) sn.Discover.compileOptions() else Nil)\n\n  private def linkingCliOptions(): List[String] =\n    finalLinkingOptions().flatMap(option => List(\"--linking-option\", option))\n\n  private def compileCliOptions(): List[String] =\n    finalCompileOptions().flatMap(option => List(\"--compile-option\", option))\n  private def cCompileCliOptions(): List[String] =\n    cCompileOptions.flatMap(option => List(\"--c-compile-option\", option))\n  private def cppCompileCliOptions(): List[String] =\n    cppCompileOptions.flatMap(option => List(\"--cpp-compile-option\", option))\n  private def ltoOptions(): List[String] =\n    ltoStr.map(_.trim).filter(_.nonEmpty)\n      .map(lto => LTO.apply(lto))\n      .map(lto => List(\"--lto\", lto.name)).getOrElse(Nil)\n  private def resourcesCliOptions(resourcesExist: Boolean): List[String] =\n    if (embedResources.getOrElse(true))\n      (numeralVersion, resourcesExist) match {\n        case (Some(numeralVersion), true) if numeralVersion >= SNNumeralVersion(0, 4, 4) =>\n          List(\"--embed-resources\")\n        case _ => Nil\n      }\n    else Nil\n\n  private def multithreadingCliOption(): List[String] =\n    multithreading.toList.flatMap(m => List(\"--multithreading\", m.toString))\n\n  def platformSuffix: String =\n    \"native\" + ScalaVersion.nativeBinary(finalVersion).getOrElse(finalVersion)\n\n  def nativeDependencies(scalaVersion: String): Seq[AnyDependency] = {\n    // https://github.com/scala-native/scala-native/pull/3326\n    val scalalibVersion =\n      if (finalVersion.startsWith(\"0.4.\")) finalVersion\n      else s\"$scalaVersion+$finalVersion\"\n    // Since 0.5.x Scala Native requires explicit dependency on javalib\n    // See https://github.com/scala-native/scala-native/pull/3566\n    val javalib = dep\"org.scala-native::javalib::$finalVersion\"\n    if (scalaVersion.startsWith(\"2.\"))\n      Seq(dep\"org.scala-native::scalalib::$scalalibVersion\", javalib)\n    else\n      Seq(dep\"org.scala-native::scala3lib::$scalalibVersion\", javalib)\n  }\n\n  def compilerPlugins: Seq[AnyDependency] =\n    Seq(dep\"org.scala-native:::nscplugin:$finalVersion\")\n\n  def bloopConfig(): BloopConfig.NativeConfig =\n    BloopConfig.NativeConfig(\n      version = finalVersion,\n      // there are more modes than bloop allows, but that setting here shouldn't end up being used anyway\n      mode =\n        if (mode() == sn.Mode.releaseFast || mode() == sn.Mode.releaseFull)\n          BloopConfig.LinkerMode.Release\n        else BloopConfig.LinkerMode.Debug,\n      gc = gc().name,\n      targetTriple = None,\n      clang = clangPath(),\n      clangpp = clangppPath(),\n      toolchain = Nil,\n      options = BloopConfig.NativeOptions(\n        linker = finalLinkingOptions(),\n        compiler = finalCompileOptions()\n      ),\n      linkStubs = false,\n      check = false,\n      dump = false,\n      output = None\n    )\n\n  def configCliOptions(resourcesExist: Boolean): List[String] =\n    gcCliOption() ++\n      modeCliOption() ++\n      ltoOptions() ++\n      clangCliOption() ++\n      clangppCliOption() ++\n      linkingCliOptions() ++\n      compileCliOptions() ++\n      cCompileCliOptions() ++\n      cppCompileCliOptions() ++\n      resourcesCliOptions(resourcesExist) ++\n      targetCliOption() ++\n      multithreadingCliOption()\n\n}\n\nobject ScalaNativeOptions {\n  implicit val hasHashData: HasHashData[ScalaNativeOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[ScalaNativeOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScalaOptions.scala",
    "content": "package scala.build.options\n\nimport dependency.AnyDependency\n\nimport scala.build.Positioned\n\nfinal case class ScalaOptions(\n  scalaVersion: Option[MaybeScalaVersion] = None,\n  scalaBinaryVersion: Option[String] = None,\n  addScalaLibrary: Option[Boolean] = None,\n  addScalaCompiler: Option[Boolean] = None,\n  semanticDbOptions: SemanticDbOptions = SemanticDbOptions(),\n  scalacOptions: ShadowingSeq[Positioned[ScalacOpt]] = ShadowingSeq.empty,\n  extraScalaVersions: Set[String] = Set.empty,\n  compilerPlugins: Seq[Positioned[AnyDependency]] = Nil,\n  platform: Option[Positioned[Platform]] = None,\n  extraPlatforms: Map[Platform, Positioned[Unit]] = Map.empty,\n  defaultScalaVersion: Option[String] = None\n) {\n  def orElse(other: ScalaOptions): ScalaOptions =\n    ScalaOptions.monoid.orElse(this, other)\n\n  def normalize: ScalaOptions = {\n    var opt = this\n    for (sv <- opt.scalaVersion.map(_.asString) if opt.extraScalaVersions.contains(sv))\n      opt = opt.copy(\n        extraScalaVersions = opt.extraScalaVersions - sv\n      )\n    for (pf <- opt.platform.map(_.value) if opt.extraPlatforms.keySet.contains(pf))\n      opt = opt.copy(\n        extraPlatforms = opt.extraPlatforms - pf\n      )\n    opt\n  }\n}\n\nobject ScalaOptions {\n  implicit val hasHashData: HasHashData[ScalaOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[ScalaOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScalaSigningCliOptions.scala",
    "content": "package scala.build.options\n\nfinal case class ScalaSigningCliOptions(\n  javaArgs: Seq[String] = Nil,\n  forceExternal: Option[Boolean] = None,\n  forceJvm: Option[Boolean] = None,\n  signingCliVersion: Option[String] = None\n)\n\nobject ScalaSigningCliOptions {\n  implicit val hasHashData: HasHashData[ScalaSigningCliOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[ScalaSigningCliOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScalaVersionUtil.scala",
    "content": "package scala.build.options\n\nimport com.github.plokhotnyuk.jsoniter_scala.core.*\nimport com.github.plokhotnyuk.jsoniter_scala.macros.*\nimport coursier.Versions\nimport coursier.cache.{ArtifactError, FileCache}\nimport coursier.core.{Module, Repository, Versions as CoreVersions}\nimport coursier.util.{Artifact, Task}\nimport coursier.version.Version\n\nimport java.io.File\n\nimport scala.build.CoursierUtils.*\nimport scala.build.EitherCps.{either, value}\nimport scala.build.RepositoryUtils\nimport scala.build.errors.{\n  BuildException,\n  InvalidBinaryScalaVersionError,\n  NoValidScalaVersionFoundError,\n  ScalaVersionError,\n  UnsupportedScalaVersionError\n}\nimport scala.build.internal.Regexes.scala2NightlyRegex\nimport scala.build.internal.{Constants, Util}\nimport scala.concurrent.duration.{Duration, DurationInt}\nimport scala.util.control.NonFatal\n\nobject ScalaVersionUtil {\n\n  private def scala2Library           = cmod\"org.scala-lang:scala-library\"\n  private def scala3Library           = cmod\"org.scala-lang:scala3-library_3\"\n  def scala212Nightly                 = \"2.12.nightly\"\n  def scala213Nightly                 = List(\"2.13.nightly\", \"2.nightly\")\n  def scala3Nightly                   = List(\"nightly\", \"3.nightly\")\n  def scala3LtsNightly                = List(\"lts.nightly\", \"3.lts.nightly\")\n  private def rcAlias(prefix: String) = s\"$prefix.rc\"\n  def scala2LatestRc                  = List(rcAlias(\"2\"), rcAlias(\"2.12\"), rcAlias(\"2.13\"))\n  def scala3LatestRc                  = List(\"rc\", rcAlias(\"3\"))\n  def scala3LtsLatestRc = List(rcAlias(\"lts\"), rcAlias(\"3.lts\"), rcAlias(Constants.scala3LtsPrefix))\n  def scala3Lts         = List(\"3.lts\", \"lts\")\n  // not valid versions, defined only for informative error messages\n  def scala2Lts = List(\"2.13.lts\", \"2.12.lts\", \"2.lts\")\n  extension (cache: FileCache[Task]) {\n    def fileWithTtl0(artifact: Artifact): Either[ArtifactError, File] =\n      cache.logger.use {\n        try cache.withTtl(0.seconds).file(artifact).run.unsafeRun()(using cache.ec)\n        catch {\n          case NonFatal(e) => throw new Exception(e)\n        }\n      }\n\n    def versions(\n      module: Module,\n      repositories: Seq[Repository] = Seq.empty,\n      ttl: Option[Duration] = None\n    ): Versions.Result =\n      val cacheWithTtl = ttl.map(cache.withTtl).getOrElse(cache)\n      cacheWithTtl.logger.use {\n        Versions(cacheWithTtl)\n          .withModule(module)\n          .addRepositories(repositories*)\n          .result()\n          .unsafeRun()(using cacheWithTtl.ec)\n      }\n    def versionsWithTtl0(\n      module: Module,\n      repositories: Seq[Repository] = Seq.empty\n    ): Versions.Result = versions(module, repositories, Some(0.seconds))\n  }\n\n  extension (versionsResult: Versions.Result) {\n    def verify(versionString: String): Either[BuildException, Unit] =\n      if versionsResult.versions.available0.exists(_.asString == versionString)\n      then Right(())\n      else Left(NoValidScalaVersionFoundError(versionString))\n  }\n\n  object GetNightly {\n\n    private object Scala2Repo {\n      final case class ScalaVersion(name: String, lastModified: Long)\n      final case class ScalaVersionsMetaData(repo: String, children: List[ScalaVersion])\n\n      val codec: JsonValueCodec[ScalaVersionsMetaData] = JsonCodecMaker.make\n    }\n\n    private object GitHubScalaRepo {\n      final case class Commit(sha: String)\n      val codec: JsonValueCodec[List[Commit]] = JsonCodecMaker.make\n    }\n\n    private def fetchScalaRepoCommitHashes(\n      versionPrefix: String,\n      cache: FileCache[Task]\n    ): Either[BuildException, List[String]] = either {\n      val branch = s\"$versionPrefix.x\"\n      val url    =\n        s\"https://api.github.com/repos/scala/scala/commits?sha=$branch&per_page=20\"\n      val artifact = Artifact(url).withChanging(true)\n      val file     = value {\n        cache.fileWithTtl0(artifact).left.map { err =>\n          new ScalaVersionError(\n            s\"Unable to fetch commits from GitHub for scala/scala branch $branch\",\n            cause = err\n          )\n        }\n      }\n      val content = os.read.bytes(os.Path(file, os.pwd))\n      readFromArray(content)(using GitHubScalaRepo.codec).map(_.sha)\n    }\n\n    private def extractNightlyHash(version: String): Option[String] =\n      version.split(\"-bin-\") match {\n        case Array(_, hash) => Some(hash)\n        case _              => None\n      }\n\n    private def downloadScala2RepoPage(cache: FileCache[Task])\n      : Either[BuildException, Array[Byte]] =\n      either {\n        val scala2NightlyRepo =\n          \"https://scala-ci.typesafe.com/ui/api/v1/ui/nativeBrowser/scala-integration/org/scala-lang/scala-compiler\"\n        val artifact = Artifact(scala2NightlyRepo).withChanging(true)\n        val res      = cache.fileWithTtl0(artifact)\n          .left.map { err =>\n            val msg =\n              \"\"\"|Unable to compute the latest Scala 2 nightly version.\n                 |Throws error during downloading web page repository for Scala 2.\"\"\".stripMargin\n            new ScalaVersionError(msg, cause = err)\n          }\n\n        val res0    = value(res)\n        val content = os.read.bytes(os.Path(res0, os.pwd))\n        content\n      }\n\n    def scala2(\n      versionPrefix: String,\n      cache: FileCache[Task]\n    ): Either[BuildException, String] = either {\n      val webPageScala2Repo = value(downloadScala2RepoPage(cache))\n      val maybeScala2Repo   =\n        try Right(readFromArray(webPageScala2Repo)(using Scala2Repo.codec))\n        catch { case e: JsonReaderException => Left(e) }\n      maybeScala2Repo match {\n        case Right(scala2Repo) =>\n          val versions      = scala2Repo.children\n          val sortedVersion =\n            versions\n              .filter(_.name.startsWith(versionPrefix))\n              .filterNot(_.name.contains(\"pre\"))\n              .sortBy(_.lastModified)\n\n          val latestNightly = sortedVersion.lastOption.map(_.name)\n          latestNightly.getOrElse {\n            val msg = s\"\"\"|Unable to compute the latest Scala $versionPrefix nightly version.\n                          |Pass explicitly full Scala 2 nightly version.\"\"\".stripMargin\n            throw new ScalaVersionError(msg)\n          }\n        case Left(jsonReaderException) =>\n          // in case the Scala 2 repo page is down or the JSON format gets truncated/changed, we fall back to\n          // fetching it with coursier from the maven repository/cache\n          val allVersions = cache\n            .versionsWithTtl0(scala2Library, Seq(coursier.Repositories.scalaIntegration))\n            .versions.available0\n            .map(_.asString)\n          val nightlyVersions = allVersions\n            .filter(_.startsWith(versionPrefix))\n            .filterNot(_.contains(\"pre\"))\n\n          // the only way to figure out which version is actually the latest there in the case of nightlies\n          // is to check the order of commit hashes in the actual scala/scala repository\n          // sorting by version itself is insufficient\n          val latestByCommitOrder = fetchScalaRepoCommitHashes(versionPrefix, cache)\n            .toOption\n            .flatMap { commitHashes =>\n              commitHashes.iterator.flatMap { fullHash =>\n                nightlyVersions.find(v =>\n                  extractNightlyHash(v).exists(fullHash.startsWith)\n                )\n              }.nextOption()\n            }\n\n          latestByCommitOrder.getOrElse {\n            val msg =\n              s\"\"\"|Unable to compute the latest Scala 2 nightly version.\n                  |An error was thrown during parsing web page repository for Scala 2.\n                  |${jsonReaderException.getMessage}\n                  |\n                  |No appropriate Scala 2 nightly versions were found in cache, either.\"\"\".stripMargin\n            throw new ScalaVersionError(msg, cause = jsonReaderException)\n          }\n      }\n    }\n\n    /** @return\n      *   Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple\n      */\n    def scala3X(\n      threeSubBinaryNum: String,\n      cache: FileCache[Task]\n    ): Either[BuildException, String] = {\n      val repositories = Seq(RepositoryUtils.scala3NightlyRepository)\n      val res          = cache.versionsWithTtl0(scala3Library, repositories)\n        .versions.available0.filter(_.asString.endsWith(\"-NIGHTLY\"))\n\n      val threeXNightlies = res.filter(_.asString.startsWith(s\"3.$threeSubBinaryNum.\"))\n      if threeXNightlies.nonEmpty then Right(threeXNightlies.max.repr)\n      else Left(NoValidScalaVersionFoundError())\n    }\n\n    /** @return\n      *   Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple\n      */\n    def scala3(cache: FileCache[Task]): Either[BuildException, String] = {\n      val repositories = Seq(RepositoryUtils.scala3NightlyRepository)\n      val versions     = cache\n        .versionsWithTtl0(scala3Library, repositories)\n        .versions\n      latestScalaVersionFrom(\n        versions = versions,\n        desc = \"latest Scala 3 nightly build\"\n      )\n    }\n\n    private def latestScalaVersionFrom(\n      versions: CoreVersions,\n      desc: String\n    ): Either[scala.build.errors.ScalaVersionError, String] =\n      versions.latest(coursier.version.Latest.Release) match {\n        case Some(versionString) => Right(versionString.asString)\n        case None                =>\n          val availableVersionsString = versions.available0.map(_.asString).mkString(\", \")\n          val msg                     =\n            s\"Unable to find matching version for $desc in available version: $availableVersionsString. \" +\n              \"This error may indicate a network or other problem accessing repository.\"\n          Left(new ScalaVersionError(msg))\n      }\n\n  }\n\n  object CheckNightly {\n\n    def scala2(\n      versionString: String,\n      cache: FileCache[Task]\n    ): Either[BuildException, Unit] =\n      cache.versionsWithTtl0(scala2Library, Seq(coursier.Repositories.scalaIntegration))\n        .verify(versionString)\n\n    def scala3(\n      versionString: String,\n      cache: FileCache[Task],\n      repositories: Seq[Repository] = Seq.empty\n    ): Either[BuildException, Unit] =\n      cache.versionsWithTtl0(scala3Library, repositories).verify(versionString)\n  }\n\n  def validate(\n    scalaVersionStringArg: String,\n    cache: FileCache[Task],\n    repositories: Seq[Repository],\n    isExactVersion: Boolean\n  )(validationFunction: String => Boolean): Either[ScalaVersionError, String] = {\n    val versionPool =\n      ScalaVersionUtil.allMatchingVersions(Some(scalaVersionStringArg), cache, repositories)\n        .filter(validationFunction)\n\n    val prefix =\n      if Util.isFullScalaVersion(scalaVersionStringArg) then scalaVersionStringArg\n      else if scalaVersionStringArg.endsWith(\".\") then scalaVersionStringArg\n      else scalaVersionStringArg + \".\"\n    val matchingVersions = versionPool.filter(_.startsWith(prefix)).map(Version(_))\n    if matchingVersions.isEmpty ||\n      (isExactVersion && !matchingVersions.exists(_.repr == scalaVersionStringArg))\n    then Left(new InvalidBinaryScalaVersionError(scalaVersionStringArg))\n    else {\n      val supportedMatchingVersions = matchingVersions.filter(v => isSupportedVersion(v.repr))\n      supportedMatchingVersions.find(_.repr == scalaVersionStringArg) match {\n        case Some(v)                                                       => Right(v.repr)\n        case None if supportedMatchingVersions.nonEmpty && !isExactVersion =>\n          Right(supportedMatchingVersions.max.repr)\n        case _ => Left(new UnsupportedScalaVersionError(scalaVersionStringArg))\n      }\n    }\n  }\n\n  def validateExactVersion(\n    scalaVersionStringArg: String,\n    cache: FileCache[Task],\n    repositories: Seq[Repository]\n  ): Either[ScalaVersionError, String] =\n    validate(scalaVersionStringArg, cache, repositories, isExactVersion = true)(_ => true)\n\n  def validateStable(\n    scalaVersionStringArg: String,\n    cache: FileCache[Task],\n    repositories: Seq[Repository]\n  ): Either[ScalaVersionError, String] =\n    validate(scalaVersionStringArg, cache, repositories, isExactVersion = false)(isStable)\n\n  def validateRc(\n    scalaVersionStringArg: String,\n    cache: FileCache[Task],\n    repositories: Seq[Repository]\n  ): Either[ScalaVersionError, String] =\n    validate(scalaVersionStringArg, cache, repositories, isExactVersion = false)(isRc)\n\n  private def isSupportedVersion(version: String): Boolean =\n    version.startsWith(\"2.12.\") || version.startsWith(\"2.13.\") || version.startsWith(\"3.\")\n\n  def isScala2Nightly(version: String): Boolean =\n    scala2NightlyRegex.unapplySeq(version).isDefined\n    || (scala212Nightly +: scala213Nightly).contains(version)\n\n  def isScala3Nightly(version: String): Boolean =\n    (version.startsWith(\"3\") && version.toLowerCase.endsWith(\"nightly\")) ||\n    scala3Nightly.contains(version.toLowerCase) || scala3LtsNightly.contains(version)\n  def isStable(version: String): Boolean =\n    !version.exists(_.isLetter)\n  def isRc(version: String): Boolean = {\n    val lowerCasedVersion = version.toLowerCase\n    lowerCasedVersion.contains(\"rc\") &&\n    !lowerCasedVersion.contains(\"-nightly\") &&\n    !lowerCasedVersion.contains(\"-snapshot\")\n  }\n\n  def allMatchingVersions(\n    maybeScalaVersionArg: Option[String],\n    cache: FileCache[Task],\n    repositories: Seq[Repository]\n  ): Seq[String] = {\n\n    val modules =\n      if (maybeScalaVersionArg.contains(\"2\") || maybeScalaVersionArg.exists(_.startsWith(\"2.\")))\n        Seq(scala2Library)\n      else if (\n        maybeScalaVersionArg.contains(\"3\") || maybeScalaVersionArg.exists(_.startsWith(\"3.\"))\n      )\n        Seq(scala3Library)\n      else\n        Seq(scala2Library, scala3Library)\n\n    modules\n      .flatMap { mod =>\n        val versions = cache.logger.use {\n          try Versions(cache)\n              .withModule(mod)\n              .addRepositories(repositories*)\n              .result()\n              .unsafeRun()(using cache.ec)\n          catch {\n            case NonFatal(e) => throw new Exception(e)\n          }\n        }\n        versions.versions.available0.map(_.asString)\n      }\n      .distinct\n  }\n\n  extension (sv: String) {\n    def asVersion: Version = Version(sv)\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScalacOpt.scala",
    "content": "package scala.build.options\n\nimport scala.build.options.ScalacOpt.noDashPrefixes\n\nfinal case class ScalacOpt(value: String) {\n\n  /** @return raw key for the option (if valid) */\n  private[options] def key: Option[String] =\n    if value.startsWith(\"-\") || value.startsWith(\"--\") then Some(value.takeWhile(_ != ':'))\n    else Some(\"@\").filter(value.startsWith)\n\n  /** @return raw key for the option (only if the key can be shadowed from the CLI) */\n  private[options] def shadowableKey: Option[String] = key match\n    case Some(key)\n        if ScalacOpt.repeatingKeys\n          .exists(rKey =>\n            rKey.startsWith(key.noDashPrefixes + \":\") || rKey == key.noDashPrefixes\n          ) => None\n    case otherwise => otherwise\n}\n\nobject ScalacOpt {\n  extension (opt: String) {\n    def noDashPrefixes: String = opt.stripPrefix(\"--\").stripPrefix(\"-\")\n  }\n  private val repeatingKeys = Set(\n    \"coverage-exclude-classlikes\",\n    \"coverage-exclude-files\",\n    \"language\",\n    \"P\", // plugin options\n    \"Wconf\",\n    \"Wunused\",\n    \"Wshadow\",\n    \"Xlint\",\n    \"Xmacro-settings\",\n    \"Xplugin\",\n    \"Xplugin-disable\",\n    \"Xplugin-require\",\n    \"Yimports\",\n    \"Yfrom-tasty-ignore-list\"\n  )\n\n  implicit val hashedType: HashedType[ScalacOpt] = {\n    opt => opt.value\n  }\n  implicit val keyOf: ShadowingSeq.KeyOf[ScalacOpt] =\n    ShadowingSeq.KeyOf(\n      opts =>\n        opts.headOption.flatMap(_.shadowableKey).orElse(Some(opts.map(_.value).mkString(\":\"))),\n      seq => groupCliOptions(seq.map(_.value))\n    )\n\n  // Groups options (starting with `-`, `--` or `@`) with option arguments that follow\n  def groupCliOptions(opts: Seq[String]): Seq[Int] =\n    opts\n      .zipWithIndex\n      .collect {\n        case (opt, idx) if opt.startsWith(\"-\") || opt.startsWith(\"--\") || opt.startsWith(\"@\") =>\n          idx\n      }\n\n  extension (opts: ShadowingSeq[ScalacOpt]) {\n    def filterScalacOptionKeys(f: String => Boolean): ShadowingSeq[ScalacOpt] =\n      opts.filterKeys(_.key.exists(f))\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/Scope.scala",
    "content": "package scala.build.options\n\nsealed abstract class Scope(val name: String, private val index: Int) extends Product\n    with Serializable {\n  def inherits: Seq[Scope]                                   = Nil\n  lazy val allScopes: Set[Scope]                             = inherits.toSet + this\n  def asScopeRequirement: BuildRequirements.ScopeRequirement =\n    BuildRequirements.ScopeRequirement(this)\n}\nobject Scope {\n  case object Main extends Scope(\"main\", 0)\n  case object Test extends Scope(\"test\", 1) {\n    override def inherits: Seq[Scope] =\n      Seq(Main)\n  }\n\n  val all: Seq[Scope] = Seq(Main, Test)\n\n  implicit val ordering: Ordering[Scope] =\n    Ordering.Int.on(_.index)\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ScriptOptions.scala",
    "content": "package scala.build.options\n\nfinal case class ScriptOptions(\n  forceObjectWrapper: Option[Boolean] = None\n)\n\nobject ScriptOptions {\n  implicit val hasHashData: HasHashData[ScriptOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[ScriptOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/SemanticDbOptions.scala",
    "content": "package scala.build.options\n\ncase class SemanticDbOptions(\n  generateSemanticDbs: Option[Boolean] = None,\n  semanticDbTargetRoot: Option[os.Path] = None,\n  semanticDbSourceRoot: Option[os.Path] = None\n)\n\nobject SemanticDbOptions {\n  implicit val hasHashData: HasHashData[SemanticDbOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[SemanticDbOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/ShadowingSeq.scala",
    "content": "package scala.build.options\n\nimport dependency.AnyDependency\n\nimport scala.build.options.ScalacOpt.noDashPrefixes\nimport scala.collection.mutable\n\n/** Seq ensuring some of its values are unique according to some key */\nfinal case class ShadowingSeq[T] private (values: Seq[Seq[T]]) {\n  lazy val toSeq: Seq[T]                                                      = values.flatten\n  def map[U](f: T => U)(implicit key: ShadowingSeq.KeyOf[U]): ShadowingSeq[U] =\n    ShadowingSeq.empty[U] ++ toSeq.map(f)\n  def mapSubSeq[U](f: Seq[T] => Seq[U]): ShadowingSeq[U] =\n    ShadowingSeq[U](values.map(f))\n  def filter(f: T => Boolean)(implicit key: ShadowingSeq.KeyOf[T]): ShadowingSeq[T] =\n    ShadowingSeq.empty ++ toSeq.filter(f)\n  def filterSubSeq(f: Seq[T] => Boolean): ShadowingSeq[T] =\n    ShadowingSeq(values.filter(f))\n  def filterKeys(f: T => Boolean): ShadowingSeq[T] =\n    filterSubSeq {\n      case Seq(head, _*) => f(head)\n      case _             => true\n    }\n  def keys: Seq[T]                                                            = values.map(_.head)\n  def ++(other: Seq[T])(implicit key: ShadowingSeq.KeyOf[T]): ShadowingSeq[T] =\n    addGroups(ShadowingSeq.groups(other, key.groups(other)))\n  private def addGroups(other: Seq[Seq[T]])(implicit key: ShadowingSeq.KeyOf[T]): ShadowingSeq[T] =\n    if (other.isEmpty) this\n    else {\n      val l    = new mutable.ListBuffer[Seq[T]]\n      val seen = new mutable.HashSet[String]\n      for (group <- values.iterator ++ other.iterator) {\n        assert(group.nonEmpty)\n        val keyOpt = key.makeKey(group)\n        if !keyOpt.exists(k => seen.contains(k.noDashPrefixes))\n        then {\n          l += group\n          for (key <- keyOpt)\n            seen += key.noDashPrefixes\n        }\n      }\n\n      ShadowingSeq(l.toList)\n    }\n  def keyValueMap: Map[T, Seq[T]] =\n    values\n      .flatMap {\n        case Seq(head, tail*) => Some(head -> tail)\n        case _                => None\n      }\n      .toMap\n\n  def get(key: T): Seq[T] =\n    keyValueMap\n      .get(key)\n      .toSeq.flatten\n}\n\nobject ShadowingSeq {\n\n  final case class KeyOf[T](\n    makeKey: Seq[T] => Option[String],\n    /** The indices at which sub-groups of values start */\n    groups: Seq[T] => Seq[Int]\n  )\n  object KeyOf {\n    implicit val keyOfAnyDependency: KeyOf[AnyDependency] =\n      KeyOf(deps => deps.headOption.map(_.module.render), _.indices)\n  }\n\n  implicit def monoid[T](implicit key: KeyOf[T]): ConfigMonoid[ShadowingSeq[T]] =\n    ConfigMonoid.instance(ShadowingSeq.empty[T]) { (a, b) =>\n      a.addGroups(b.values)\n    }\n  implicit def hashedField[T](implicit\n    hasher: HashedType[T]\n  ): HasHashData[ShadowingSeq[T]] = {\n    (name, seq, update) =>\n      for (t <- seq.toSeq)\n        update(s\"$name+=${hasher.hashedValue(t)}\")\n  }\n\n  def empty[T]: ShadowingSeq[T] = ShadowingSeq(Nil)\n\n  def from[T](values: Seq[T])(implicit key: KeyOf[T]): ShadowingSeq[T] =\n    empty[T] ++ values\n\n  private def groups[T](values: Seq[T], indices: Seq[Int]): Seq[Seq[T]] = {\n    val safeIndices = Seq(0) ++ indices ++ Seq(values.length)\n    safeIndices\n      .sliding(2)\n      .map {\n        case Seq(start, end) =>\n          values.slice(start, end)\n      }\n      .filter(_.nonEmpty)\n      .toVector\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/SourceGeneratorOptions.scala",
    "content": "package scala.build.options\n\nfinal case class SourceGeneratorOptions(\n  useBuildInfo: Option[Boolean] = None,\n  projectVersion: Option[String] = None,\n  computeVersion: Option[ComputeVersion] = None\n)\n\nobject SourceGeneratorOptions {\n  /* Using HasHashData.nop here (SourceGeneratorOptions values are not used during compilation) */\n  implicit val hasHashData: HasHashData[SourceGeneratorOptions] = HasHashData.nop\n  implicit val monoid: ConfigMonoid[SourceGeneratorOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/SuppressWarningOptions.scala",
    "content": "package scala.build.options\n\nfinal case class SuppressWarningOptions(\n  suppressDirectivesInMultipleFilesWarning: Option[Boolean] = None,\n  suppressOutdatedDependencyWarning: Option[Boolean] = None,\n  suppressExperimentalFeatureWarning: Option[Boolean] = None,\n  suppressDeprecatedFeatureWarning: Option[Boolean] = None\n)\n\nobject SuppressWarningOptions {\n  implicit val hasHashData: HasHashData[SuppressWarningOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[SuppressWarningOptions]     = ConfigMonoid.derive\n\n  val suppressAll = SuppressWarningOptions(\n    suppressDirectivesInMultipleFilesWarning = Some(true),\n    suppressOutdatedDependencyWarning = Some(true),\n    suppressExperimentalFeatureWarning = Some(true)\n  )\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/TestOptions.scala",
    "content": "package scala.build.options\n\nimport scala.build.Positioned\n\nfinal case class TestOptions(\n  frameworks: Seq[Positioned[String]] = Nil,\n  testOnly: Option[String] = None\n)\n\nobject TestOptions {\n  implicit val hasHashData: HasHashData[TestOptions] = HasHashData.derive\n  implicit val monoid: ConfigMonoid[TestOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/WatchOptions.scala",
    "content": "package scala.build.options\n\nfinal case class WatchOptions(\n  extraWatchPaths: Seq[os.Path] = Nil\n)\n\nobject WatchOptions {\n  implicit val hasHashData: HasHashData[WatchOptions] = HasHashData.nop\n  implicit val monoid: ConfigMonoid[WatchOptions]     = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/WithBuildRequirements.scala",
    "content": "package scala.build.options\n\nimport scala.build.options.BuildRequirements.ScopeRequirement\n\nfinal case class WithBuildRequirements[+T](\n  requirements: BuildRequirements,\n  value: T\n) {\n  def needsScalaVersion: Boolean =\n    requirements.needsScalaVersion\n  def withScalaVersion(sv: MaybeScalaVersion): Either[String, WithBuildRequirements[T]] =\n    requirements.withScalaVersion(sv).map { updatedRequirements =>\n      copy(requirements = updatedRequirements)\n    }\n  def withPlatform(pf: Platform): Either[String, WithBuildRequirements[T]] =\n    requirements.withPlatform(pf).map { updatedRequirements =>\n      copy(requirements = updatedRequirements)\n    }\n  def scopedValue(defaultScope: Scope): HasScope[T] =\n    HasScope(requirements.scope.map(_.scope).getOrElse(defaultScope), value)\n  def map[U](f: T => U): WithBuildRequirements[U] =\n    copy(value = f(value))\n}\n\nobject WithBuildRequirements {\n  extension [T](t: T) {\n    def withBuildRequirements(buildRequirements: BuildRequirements): WithBuildRequirements[T] =\n      WithBuildRequirements(buildRequirements, t)\n\n    def withEmptyRequirements: WithBuildRequirements[T] =\n      t.withBuildRequirements(BuildRequirements())\n\n    def withScopeRequirement(scope: Scope): WithBuildRequirements[T] =\n      t.withBuildRequirements(BuildRequirements(scope = Some(ScopeRequirement(scope = scope))))\n  }\n  extension [T](t: WithBuildRequirements[Seq[T]]) {\n    def flatten: Seq[WithBuildRequirements[T]] =\n      t.value.map { v =>\n        WithBuildRequirements(t.requirements, v)\n      }\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/packaging/DebianOptions.scala",
    "content": "package scala.build.options.packaging\n\nimport scala.build.options.ConfigMonoid\n\nfinal case class DebianOptions(\n  conflicts: List[String] = Nil,\n  dependencies: List[String] = Nil,\n  architecture: Option[String] = None,\n  priority: Option[String] = None,\n  section: Option[String] = None\n)\n\nobject DebianOptions {\n  implicit val monoid: ConfigMonoid[DebianOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/packaging/DockerOptions.scala",
    "content": "package scala.build.options.packaging\n\nimport scala.build.options.ConfigMonoid\n\nfinal case class DockerOptions(\n  from: Option[String] = None,\n  imageRegistry: Option[String] = None,\n  imageRepository: Option[String] = None,\n  imageTag: Option[String] = None,\n  cmd: Option[String] = None,\n  isDockerEnabled: Option[Boolean] = None,\n  extraDirectories: Seq[os.Path] = Nil\n)\n\nobject DockerOptions {\n\n  implicit val monoid: ConfigMonoid[DockerOptions] = ConfigMonoid.derive\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/packaging/NativeImageOptions.scala",
    "content": "package scala.build.options.packaging\n\nimport scala.build.Positioned\nimport scala.build.internal.Constants\nimport scala.build.options.ConfigMonoid\n\nfinal case class NativeImageOptions(\n  graalvmJvmId: Option[String] = None,\n  graalvmJavaVersion: Option[Int] = None,\n  graalvmVersion: Option[String] = None,\n  graalvmArgs: Seq[Positioned[String]] = Nil\n) {\n  lazy val jvmId: String =\n    graalvmJvmId.getOrElse {\n      val javaVersion = graalvmJavaVersion.getOrElse(Constants.defaultGraalVMJavaVersion)\n      val version     = graalvmVersion.getOrElse(Constants.defaultGraalVMVersion)\n      s\"graalvm-java$javaVersion:$version\"\n    }\n}\n\nobject NativeImageOptions {\n  implicit val monoid: ConfigMonoid[NativeImageOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/packaging/RedHatOptions.scala",
    "content": "package scala.build.options.packaging\n\nimport scala.build.options.ConfigMonoid\n\nfinal case class RedHatOptions(\n  license: Option[String] = None,\n  release: Option[String] = None,\n  architecture: Option[String] = None\n)\n\nobject RedHatOptions {\n  implicit val monoid: ConfigMonoid[RedHatOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/packaging/WindowsOptions.scala",
    "content": "package scala.build.options.packaging\n\nimport scala.build.options.ConfigMonoid\n\nfinal case class WindowsOptions(\n  licensePath: Option[os.Path] = None,\n  productName: Option[String] = None,\n  exitDialog: Option[String] = None,\n  suppressValidation: Option[Boolean] = None,\n  extraConfig: List[String] = Nil,\n  is64Bits: Option[Boolean] = None,\n  installerVersion: Option[String] = None,\n  wixUpgradeCodeGuid: Option[String] = None\n)\n\nobject WindowsOptions {\n  implicit val monoid: ConfigMonoid[WindowsOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/publish/ConfigPasswordOption.scala",
    "content": "package scala.build.options.publish\n\nimport scala.cli.signing.shared.PasswordOption\n\n/** Can be either a [[PasswordOption]], or something like \"config:…\" pointing at a config entry */\nsealed abstract class ConfigPasswordOption extends Product with Serializable\n\nobject ConfigPasswordOption {\n  final case class ActualOption(option: PasswordOption) extends ConfigPasswordOption\n  final case class ConfigOption(fullName: String)       extends ConfigPasswordOption {\n    private lazy val split  = fullName.split('.')\n    def prefix: Seq[String] = split.dropRight(1).toSeq\n    def name: String        = split.last\n  }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/publish/Developer.scala",
    "content": "package scala.build.options.publish\n\nimport scala.build.Positioned\nimport scala.build.errors.{BuildException, MalformedInputError}\n\nfinal case class Developer(\n  id: String,\n  name: String,\n  url: String,\n  mail: Option[String] = None\n)\n\nobject Developer {\n\n  def parse(input: Positioned[String]): Either[BuildException, Developer] =\n    input.value.split(\"\\\\|\", 4) match {\n      case Array(id, name, url) =>\n        Right(Developer(id, name, url))\n      case Array(id, name, url, mail) =>\n        Right(Developer(id, name, url, Some(mail).map(_.trim).filter(_.nonEmpty)))\n      case _ =>\n        Left(\n          new MalformedInputError(\"developer\", input.value, \"id|name|URL\", input.positions)\n        )\n    }\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/publish/License.scala",
    "content": "package scala.build.options.publish\n\nimport scala.build.Positioned\nimport scala.build.errors.{BuildException, MalformedInputError}\nimport scala.build.internal.Licenses\n\nfinal case class License(name: String, url: String)\n\nobject License {\n\n  def parse(input: Positioned[String]): Either[BuildException, Positioned[License]] =\n    input.value.split(\":\", 2) match {\n      case Array(name) =>\n        Licenses.map.get(name) match {\n          case None =>\n            Left(new MalformedInputError(\n              \"license\",\n              input.value,\n              \"license-id|license-id:url\",\n              input.positions\n            ))\n          case Some(license) =>\n            Right(input.map(_ => License(name, license.url)))\n        }\n      case Array(name, url) =>\n        Right(input.map(_ => License(name, url)))\n    }\n\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/publish/Signer.scala",
    "content": "package scala.build.options.publish\n\nimport scala.build.Positioned\nimport scala.build.errors.MalformedInputError\n\nsealed abstract class Signer extends Product with Serializable\n\nobject Signer {\n  case object Gpg          extends Signer\n  case object BouncyCastle extends Signer\n  case object Nop          extends Signer\n\n  def parse(input: Positioned[String]): Either[MalformedInputError, Signer] =\n    input.value match {\n      case \"gpg\"                 => Right(Signer.Gpg)\n      case \"bc\" | \"bouncycastle\" => Right(Signer.BouncyCastle)\n      case \"nop\" | \"none\"        => Right(Signer.Nop)\n      case _ => Left(new MalformedInputError(\"signer\", input.value, \"gpg|bc\", input.positions))\n    }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/publish/Vcs.scala",
    "content": "package scala.build.options.publish\n\nimport scala.build.Positioned\nimport scala.build.errors.{BuildException, MalformedInputError}\n\nfinal case class Vcs(\n  url: String,\n  connection: String,\n  developerConnection: String\n)\n\nobject Vcs {\n\n  def parse(input: Positioned[String]): Either[BuildException, Vcs] =\n    if (input.value.startsWith(\"github:\"))\n      input.value.stripPrefix(\"github:\").split('/') match {\n        case Array(org, project) =>\n          val vcs = Vcs(\n            s\"https://github.com/$org/$project.git\",\n            s\"scm:git:github.com/$org/$project.git\",\n            s\"scm:git:git@github.com:$org/$project.git\"\n          )\n          Right(vcs)\n        case _ =>\n          Left(\n            new MalformedInputError(\n              \"github-vcs\",\n              input.value,\n              \"github:org/project\",\n              input.positions\n            )\n          )\n      }\n    else\n      input.value.split('|') match {\n        case Array(url, conn, devConn) =>\n          val vcs = Vcs(url, conn, devConn)\n          Right(vcs)\n        case _ =>\n          Left(\n            new MalformedInputError(\n              \"vcs\",\n              input.value,\n              \"url|connection|developer-connection\",\n              input.positions\n            )\n          )\n      }\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/scalajs/ScalaJsLinkerOptions.scala",
    "content": "package scala.build.options.scalajs\n\nimport scala.build.internal.{Constants, FetchExternalBinary}\nimport scala.build.options.ConfigMonoid\n\nfinal case class ScalaJsLinkerOptions(\n  javaArgs: Seq[String] = Nil,\n  /** If right, use JVM, if left, use the value as architecture */\n  useJvm: Option[Either[String, Unit]] = None,\n  scalaJsVersion: Option[String] = None,\n  scalaJsCliVersion: Option[String] = None,\n  linkerPath: Option[os.Path] = None\n) {\n  def finalScalaJsCliVersion = scalaJsCliVersion.orElse(scalaJsVersion).getOrElse {\n    Constants.scalaJsCliVersion\n  }\n\n  /** If right, use JVM, if left, use the value as architecture */\n  lazy val finalUseJvm: Either[String, Unit] = useJvm.getOrElse {\n    FetchExternalBinary.maybePlatformSuffix() match {\n      case Left(_) =>\n        // FIXME Log error?\n        Right(())\n      case Right(osArch) =>\n        Left(osArch)\n    }\n  }\n}\n\nobject ScalaJsLinkerOptions {\n  implicit val monoid: ConfigMonoid[ScalaJsLinkerOptions] = ConfigMonoid.derive\n}\n"
  },
  {
    "path": "modules/options/src/main/scala/scala/build/options/validation/BuildOptionsRule.scala",
    "content": "package scala.build.options.validation\nimport scala.build.errors.{BuildException, Diagnostic, Severity}\nimport scala.build.options.BuildOptions\n\ntrait BuildOptionsRule {\n  def validate(options: BuildOptions): Seq[Diagnostic]\n}\n\nobject BuildOptionsRule {\n  def validateAll(options: BuildOptions): Seq[Diagnostic] =\n    List(JvmOptionsForNonJvmBuild).flatMap(_.validate(options))\n}\n\nclass ValidationException(\n  diagnostic: Diagnostic\n) extends BuildException(diagnostic.message, diagnostic.positions)\n\nobject JvmOptionsForNonJvmBuild extends BuildOptionsRule {\n  def validate(options: BuildOptions): List[Diagnostic] = {\n    val jvmOptions = options.javaOptions.javaOpts.toSeq\n    if (jvmOptions.nonEmpty && options.platform.value != scala.build.options.Platform.JVM)\n      List(Diagnostic(\n        \"Conflicting options. Jvm Options are valid only for jvm platform.\",\n        Severity.Warning,\n        options.platform.positions ++ jvmOptions.flatMap(_.positions)\n      ))\n    else Nil\n  }\n}\n"
  },
  {
    "path": "modules/options/src/test/scala/scala/build/options/ConfigMonoidTest.scala",
    "content": "package scala.build.options\n\ncase class Inner(\n  foo: Boolean = false,\n  bar: Seq[String] = Nil,\n  baz: Set[Double] = Set()\n)\n\nobject Inner {\n  implicit def monoid: ConfigMonoid[Inner] = ConfigMonoid.derive\n}\n\ncase class Outer(\n  name: Option[String] = None,\n  inner: Inner = Inner()\n)\n\nobject Outer {\n  implicit def monoid: ConfigMonoid[Outer] = ConfigMonoid.derive\n}\n\nclass ConfigMonoidTest extends munit.FunSuite {\n  test(\"Basic Config Monoid\") {\n    val inner1 = Inner(foo = true)\n\n    assertEquals(false, Inner.monoid.zero.foo)\n    assertEquals(true, Inner.monoid.orElse(inner1, Inner()).foo)\n    assertEquals(true, Inner.monoid.orElse(Inner(), inner1).foo)\n\n    val outer = Outer(inner = inner1)\n\n    assertEquals(false, Outer.monoid.zero.inner.foo)\n    assertEquals(true, Outer.monoid.orElse(outer, Outer()).inner.foo)\n    assertEquals(true, Outer.monoid.orElse(Outer(), outer).inner.foo)\n  }\n\n  test(\"Merging sets\") {\n    val inner1 = Inner(bar = Seq(\"v1\"))\n    val inner2 = Inner(bar = Seq(\"v2\"))\n\n    assertEquals(Seq(\"v1\"), Inner.monoid.orElse(inner1, Inner()).bar)\n    assertEquals(Seq(\"v1\", \"v2\"), Inner.monoid.orElse(inner1, inner2).bar)\n    assertEquals(Seq(\"v2\", \"v1\"), Inner.monoid.orElse(inner2, inner1).bar)\n\n    val outer1 = Outer(Some(\"o1\"), inner1)\n    val outer2 = Outer(Some(\"o2\"), inner2)\n\n    assertEquals(Seq(\"v1\"), Outer.monoid.orElse(outer1, Outer()).inner.bar)\n    assertEquals(Seq(\"v1\", \"v2\"), Outer.monoid.orElse(outer1, outer2).inner.bar)\n    assertEquals(Seq(\"v2\", \"v1\"), Outer.monoid.orElse(outer2, outer1).inner.bar)\n\n    assertEquals(Outer.monoid.orElse(outer1, outer2).name, Some(\"o1\"))\n    assertEquals(Outer.monoid.orElse(outer2, outer1).name, Some(\"o2\"))\n  }\n}\n"
  },
  {
    "path": "modules/runner/src/main/scala/scala/cli/runner/Runner.scala",
    "content": "package scala.cli.runner\n\nimport java.lang.reflect.InvocationTargetException\n\nobject Runner {\n  def main(args: Array[String]): Unit = {\n    assert(args.nonEmpty)\n    val mainClass = args.head\n    val verbosity = args.tail.head.toInt\n    val args0     = args.drop(2)\n\n    val loader = Thread.currentThread().getContextClassLoader\n    val cls    = loader.loadClass(mainClass)\n    val method = cls.getMethod(\"main\", classOf[Array[String]])\n    try method.invoke(null, args0)\n    catch {\n      case e: InvocationTargetException if e.getCause != null =>\n        val printer = StackTracePrinter(\n          loader = loader,\n          callerClass = Some(getClass.getName),\n          cutInvoke = true\n        )\n        printer.printException(e.getCause, verbosity)\n        System.exit(1)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/runner/src/main/scala/scala/cli/runner/StackTracePrinter.scala",
    "content": "package scala.cli.runner\n\nimport java.util.Locale\n\nimport scala.annotation.tailrec\n\nfinal case class StackTracePrinter(\n  loader: ClassLoader = Thread.currentThread().getContextClassLoader,\n  callerClass: Option[String] = None,\n  cutInvoke: Boolean = false,\n  colored: Boolean = StackTracePrinter.coloredStackTraces\n) {\n\n  private def bold  = if (colored) Console.BOLD else \"\"\n  private def gray  = if (colored) \"\\u001b[90m\" else \"\"\n  private def reset = if (colored) Console.RESET else \"\"\n\n  private def truncateStackTrace(ex: Throwable): Unit = {\n    val noCallerStackTrace = callerClass match {\n      case None         => ex.getStackTrace\n      case Some(caller) =>\n        ex.getStackTrace\n          .takeWhile(_.getClassName.stripSuffix(\"$\") != caller.stripSuffix(\"$\"))\n    }\n    val drop =\n      if (cutInvoke)\n        noCallerStackTrace\n          .reverseIterator\n          .takeWhile { elem =>\n            def isJdkClass =\n              elem.getClassName.startsWith(\"java.\") ||\n              elem.getClassName.startsWith(\"jdk.\") ||\n              elem.getClassName.startsWith(\"sun.\")\n            elem.getMethodName.startsWith(\"invoke\") && isJdkClass\n          }\n          .length\n      else\n        0\n    val truncated = noCallerStackTrace.dropRight(drop)\n    if (truncated.length != ex.getStackTrace.length)\n      ex.setStackTrace(truncated)\n  }\n\n  @tailrec\n  private def printCause(\n    ex: Throwable,\n    causedStackTrace: Array[StackTraceElement],\n    verbosity: Int\n  ): Unit =\n    if (ex != null) {\n      truncateStackTrace(ex)\n      System.err.println(s\"Caused by: $ex\")\n      printStackTrace(ex.getStackTrace, causedStackTrace)\n      printCause(ex.getCause, ex.getStackTrace, verbosity)\n    }\n  private def printStackTrace(trace: Array[StackTraceElement]): Unit =\n    printStackTrace(trace, Array.empty)\n  private def printStackTrace(\n    trace: Array[StackTraceElement],\n    causedStackTrace: Array[StackTraceElement]\n  ): Unit = {\n    val cut = causedStackTrace\n      .reverseIterator\n      .zip(trace.reverseIterator)\n      .takeWhile { case (a, b) => a == b }\n      .length\n    for (elem <- trace.take(trace.length - cut)) {\n      val clsName     = elem.getClassName\n      val resource    = clsName.replace('.', '/') + \".class\"\n      val resourceUrl = loader.getResource(resource)\n      val highlight   = resourceUrl != null && resourceUrl.getProtocol == \"file\"\n      if (highlight) {\n        val location =\n          if (elem.isNativeMethod) \"Native Method\"\n          else if (elem.getFileName == null) \"Unknown Source\"\n          else if (elem.getLineNumber >= 0) s\"${elem.getFileName}:${elem.getLineNumber}\"\n          else elem.getFileName\n        val str = s\"$bold${elem.getClassName}.${elem.getMethodName}$reset\" +\n          s\"$gray($reset$location$gray)$reset\"\n        System.err.println(s\"\\t${gray}at$reset $str\")\n      }\n      else\n        System.err.println(s\"\\t${gray}at $elem\")\n    }\n    if (cut > 0)\n      System.err.println(s\"\\t$gray... $cut more$reset\")\n  }\n\n  def printException(ex: Throwable, verbosity: Int): Unit = {\n    val q          = \"\\\"\"\n    val threadName = Thread.currentThread().getName\n    truncateStackTrace(ex)\n    System.err.println(s\"Exception in thread $q$threadName$q $ex\")\n    printStackTrace(ex.getStackTrace)\n    printCause(ex.getCause, ex.getStackTrace, verbosity)\n  }\n}\n\nobject StackTracePrinter {\n  lazy val coloredStackTraces: Boolean =\n    sys.props.get(\"scala.colored-stack-traces\")\n      .map(_.toLowerCase(Locale.ROOT))\n      .forall(_ == \"true\")\n}\n"
  },
  {
    "path": "modules/scala-cli-bsp/src/main/java/scala/build/bsp/ScalaScriptBuildServer.java",
    "content": "package scala.build.bsp;\n\nimport org.eclipse.lsp4j.jsonrpc.services.JsonRequest;\n\nimport java.util.concurrent.CompletableFuture;\n\npublic interface ScalaScriptBuildServer {\n\n    @JsonRequest(\"buildTarget/wrappedSources\")\n    CompletableFuture<WrappedSourcesResult> buildTargetWrappedSources(WrappedSourcesParams params);\n\n}\n"
  },
  {
    "path": "modules/scala-cli-bsp/src/main/java/scala/build/bsp/WrappedSourceItem.java",
    "content": "package scala.build.bsp;\n\nimport org.eclipse.lsp4j.jsonrpc.validation.NonNull;\nimport org.eclipse.lsp4j.util.Preconditions;\nimport org.eclipse.xtext.xbase.lib.Pure;\nimport org.eclipse.xtext.xbase.lib.util.ToStringBuilder;\n\npublic class WrappedSourceItem {\n  @NonNull\n  private String uri;\n  @NonNull\n  private String generatedUri;\n\n  private String topWrapper;\n  private String bottomWrapper;\n\n  public WrappedSourceItem(@NonNull final String uri, @NonNull final String generatedUri) {\n    this.uri = uri;\n    this.generatedUri = generatedUri;\n  }\n\n  @Pure\n  @NonNull\n  public String getUri() {\n    return this.uri;\n  }\n\n  public void setUri(@NonNull final String uri) {\n    this.uri = Preconditions.checkNotNull(uri, \"uri\");\n  }\n\n  @Pure\n  @NonNull\n  public String getGeneratedUri() {\n    return this.generatedUri;\n  }\n\n  public void setGeneratedUri(@NonNull final String generatedUri) {\n    this.generatedUri = Preconditions.checkNotNull(generatedUri, \"generatedUri\");\n  }\n\n  @Pure\n  public String getTopWrapper() {\n    return this.topWrapper;\n  }\n\n  public void setTopWrapper(final String topWrapper) {\n    this.topWrapper = topWrapper;\n  }\n\n  @Pure\n  public String getBottomWrapper() {\n    return this.bottomWrapper;\n  }\n\n  public void setBottomWrapper(final String bottomWrapper) {\n    this.bottomWrapper = bottomWrapper;\n  }\n\n  @Override\n  @Pure\n  public String toString() {\n    ToStringBuilder b = new ToStringBuilder(this);\n    b.add(\"uri\", this.uri);\n    b.add(\"generatedUri\", this.generatedUri);\n    b.add(\"topWrapper\", this.topWrapper);\n    b.add(\"bottomWrapper\", this.bottomWrapper);\n    return b.toString();\n  }\n\n  @Override\n  @Pure\n  public boolean equals(final Object obj) {\n    if (this == obj)\n      return true;\n    if (obj == null)\n      return false;\n    if (getClass() != obj.getClass())\n      return false;\n    WrappedSourceItem other = (WrappedSourceItem) obj;\n    if (this.uri == null) {\n      if (other.uri != null)\n        return false;\n    } else if (!this.uri.equals(other.uri))\n      return false;\n    if (this.generatedUri == null) {\n      if (other.generatedUri != null)\n        return false;\n    } else if (!this.generatedUri.equals(other.generatedUri))\n      return false;\n    if (this.topWrapper == null) {\n      if (other.topWrapper != null)\n        return false;\n    } else if (!this.topWrapper.equals(other.topWrapper))\n      return false;\n    if (this.bottomWrapper == null) {\n      if (other.bottomWrapper != null)\n        return false;\n    } else if (!this.bottomWrapper.equals(other.bottomWrapper))\n      return false;\n    return true;\n  }\n\n  @Override\n  @Pure\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((this.uri== null) ? 0 : this.uri.hashCode());\n    result = prime * result + ((this.generatedUri== null) ? 0 : this.generatedUri.hashCode());\n    result = prime * result + ((this.topWrapper== null) ? 0 : this.topWrapper.hashCode());\n    return prime * result + ((this.bottomWrapper== null) ? 0 : this.bottomWrapper.hashCode());\n  }\n}\n"
  },
  {
    "path": "modules/scala-cli-bsp/src/main/java/scala/build/bsp/WrappedSourcesItem.java",
    "content": "package scala.build.bsp;\n\nimport java.util.List;\nimport ch.epfl.scala.bsp4j.BuildTargetIdentifier;\nimport org.eclipse.lsp4j.jsonrpc.validation.NonNull;\nimport org.eclipse.lsp4j.util.Preconditions;\nimport org.eclipse.xtext.xbase.lib.Pure;\nimport org.eclipse.xtext.xbase.lib.util.ToStringBuilder;\n\npublic class WrappedSourcesItem {\n  @NonNull\n  private BuildTargetIdentifier target;\n\n  @NonNull\n  private List<WrappedSourceItem> sources;\n\n  public WrappedSourcesItem(@NonNull final BuildTargetIdentifier target, @NonNull final List<WrappedSourceItem> sources) {\n    this.target = target;\n    this.sources = sources;\n  }\n\n  @Pure\n  @NonNull\n  public BuildTargetIdentifier getTarget() {\n    return this.target;\n  }\n\n  public void setTarget(@NonNull final BuildTargetIdentifier target) {\n    this.target = Preconditions.checkNotNull(target, \"target\");\n  }\n\n  @Pure\n  @NonNull\n  public List<WrappedSourceItem> getSources() {\n    return this.sources;\n  }\n\n  public void setSources(@NonNull final List<WrappedSourceItem> sources) {\n    this.sources = Preconditions.checkNotNull(sources, \"sources\");\n  }\n\n  @Override\n  @Pure\n  public String toString() {\n    ToStringBuilder b = new ToStringBuilder(this);\n    b.add(\"target\", this.target);\n    b.add(\"sources\", this.sources);\n    return b.toString();\n  }\n\n  @Override\n  @Pure\n  public boolean equals(final Object obj) {\n    if (this == obj)\n      return true;\n    if (obj == null)\n      return false;\n    if (getClass() != obj.getClass())\n      return false;\n    WrappedSourcesItem other = (WrappedSourcesItem) obj;\n    if (this.target == null) {\n      if (other.target != null)\n        return false;\n    } else if (!this.target.equals(other.target))\n      return false;\n    if (this.sources == null) {\n      if (other.sources != null)\n        return false;\n    } else if (!this.sources.equals(other.sources))\n      return false;\n    return true;\n  }\n\n  @Override\n  @Pure\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((this.target== null) ? 0 : this.target.hashCode());\n    return prime * result + ((this.sources== null) ? 0 : this.sources.hashCode());\n  }\n}\n"
  },
  {
    "path": "modules/scala-cli-bsp/src/main/java/scala/build/bsp/WrappedSourcesParams.java",
    "content": "package scala.build.bsp;\n\nimport ch.epfl.scala.bsp4j.BuildTargetIdentifier;\nimport java.util.List;\nimport org.eclipse.lsp4j.jsonrpc.validation.NonNull;\nimport org.eclipse.xtext.xbase.lib.Pure;\nimport org.eclipse.xtext.xbase.lib.util.ToStringBuilder;\n\npublic class WrappedSourcesParams {\n  @NonNull\n  private List<BuildTargetIdentifier> targets;\n\n  public WrappedSourcesParams(@NonNull final List<BuildTargetIdentifier> targets) {\n    this.targets = targets;\n  }\n\n  @Pure\n  @NonNull\n  public List<BuildTargetIdentifier> getTargets() {\n    return this.targets;\n  }\n\n  @Override\n  @Pure\n  public String toString() {\n    ToStringBuilder b = new ToStringBuilder(this);\n    b.add(\"targets\", this.targets);\n    return b.toString();\n  }\n\n  @Override\n  @Pure\n  public boolean equals(final Object obj) {\n    if (this == obj)\n      return true;\n    if (obj == null)\n      return false;\n    if (getClass() != obj.getClass())\n      return false;\n    WrappedSourcesParams other = (WrappedSourcesParams) obj;\n    if (this.targets == null) {\n      if (other.targets != null)\n        return false;\n    } else if (!this.targets.equals(other.targets))\n      return false;\n    return true;\n  }\n\n  @Override\n  @Pure\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    return prime * result + ((this.targets== null) ? 0 : this.targets.hashCode());\n  }\n}\n"
  },
  {
    "path": "modules/scala-cli-bsp/src/main/java/scala/build/bsp/WrappedSourcesResult.java",
    "content": "package scala.build.bsp;\n\nimport java.util.List;\nimport org.eclipse.lsp4j.jsonrpc.validation.NonNull;\nimport org.eclipse.lsp4j.util.Preconditions;\nimport org.eclipse.xtext.xbase.lib.Pure;\nimport org.eclipse.xtext.xbase.lib.util.ToStringBuilder;\n\n@SuppressWarnings(\"all\")\npublic class WrappedSourcesResult {\n  @NonNull\n  private List<WrappedSourcesItem> items;\n\n  public WrappedSourcesResult(@NonNull final List<WrappedSourcesItem> items) {\n    this.items = items;\n  }\n\n  @Pure\n  @NonNull\n  public List<WrappedSourcesItem> getItems() {\n    return this.items;\n  }\n\n  public void setItems(@NonNull final List<WrappedSourcesItem> items) {\n    this.items = Preconditions.checkNotNull(items, \"items\");\n  }\n\n  @Override\n  @Pure\n  public String toString() {\n    ToStringBuilder b = new ToStringBuilder(this);\n    b.add(\"items\", this.items);\n    return b.toString();\n  }\n\n  @Override\n  @Pure\n  public boolean equals(final Object obj) {\n    if (this == obj)\n      return true;\n    if (obj == null)\n      return false;\n    if (getClass() != obj.getClass())\n      return false;\n    WrappedSourcesResult other = (WrappedSourcesResult) obj;\n    if (this.items == null) {\n      if (other.items != null)\n        return false;\n    } else if (!this.items.equals(other.items))\n      return false;\n    return true;\n  }\n\n  @Override\n  @Pure\n  public int hashCode() {\n    return 31 * 1 + ((this.items== null) ? 0 : this.items.hashCode());\n  }\n}\n"
  },
  {
    "path": "modules/scalaparse/src/main/scala/build/internal/ImportTree.scala",
    "content": "package scala.build.internal\n\n// adapted from https://github.com/com-lihaoyi/Ammonite/blob/9be39debc367abad5f5541ef58f4b986b2a8d045/amm/util/src/main/scala/ammonite/util/Model.scala#L256-L266\n\ncase class ImportTree(\n  prefix: Seq[String],\n  mappings: Option[ImportTree.ImportMapping],\n  start: Int,\n  end: Int\n) {\n  lazy val strippedPrefix: Seq[String] =\n    prefix.takeWhile(_(0) == '$').map(_.stripPrefix(\"$\"))\n}\n\nobject ImportTree {\n  type ImportMapping = Seq[(String, Option[String])]\n}\n"
  },
  {
    "path": "modules/scalaparse/src/main/scala/build/internal/ScalaParse.scala",
    "content": "package scala.build.internal\n\nimport fastparse.ScalaWhitespace._\nimport fastparse._\nimport scalaparse._\n\nobject ScalaParse {\n\n  import Scala._\n\n  // from https://github.com/com-lihaoyi/Ammonite/blob/0f0d597f04e62e86cbf76d3bd16deb6965331470/amm/compiler/src/main/scala/ammonite/compiler/Parsers.scala#L162-L176\n  def formatFastparseError(fileName: String, rawCode: String, f: Parsed.Failure) = {\n\n    val newLine        = System.lineSeparator()\n    val lineColIndex   = f.extra.input.prettyIndex(f.index)\n    val expected       = f.trace().failure.label\n    val locationString = {\n      val (first, last) = rawCode.splitAt(f.index)\n      val lastSnippet   = last.split(newLine).headOption.getOrElse(\"\")\n      val firstSnippet  = first.reverse\n        .split(newLine.reverse)\n        .lift(0).getOrElse(\"\").reverse\n      firstSnippet + lastSnippet + newLine + (\" \" * firstSnippet.length) + \"^\"\n    }\n    s\"$fileName:$lineColIndex expected $expected$newLine$locationString\"\n  }\n\n  def Header[X: P]: P[Seq[(Int, Int)]] = {\n    def PkgAsEmptyList = P(TopPkgSeq).map(_ => List.empty[(Int, Int)])\n    def ImportStartEnd = P(Index ~ Import ~ Index).map(List(_))\n    def TopStat        = P(PkgAsEmptyList | ImportStartEnd)\n    P(Semis.? ~ TopStat.repX(0, Semis))\n      .map(_.flatten)\n  }\n\n  // For some reason Scala doesn't import this by default\n  private def `_`[X: P] = scalaparse.Scala.`_`\n\n  def ImportSplitter[X: P]: P[Seq[ImportTree]] = {\n    def IdParser   = P((Id | `_`).!).map(s => if (s(0) == '`') s.drop(1).dropRight(1) else s)\n    def Selector   = P(IdParser ~ (`=>` ~/ IdParser).?)\n    def Selectors  = P(\"{\" ~/ Selector.rep(sep = \",\"./) ~ \"}\")\n    def BulkImport = P(`_`).map(_ => Seq(\"_\" -> None))\n    def Prefix     = P(IdParser.rep(1, sep = \".\"))\n    def Suffix     = P(\".\" ~/ (BulkImport | Selectors))\n    def ImportExpr: P[ImportTree] =\n      // Manually use `WL0` parser here, instead of relying on WhitespaceApi, as\n      // we do not want the whitespace to be consumed even if the WL0 parser parses\n      // to the end of the input (which is the default behavior for WhitespaceApi)\n      P(Index ~~ Prefix ~~ (WL0 ~~ Suffix).? ~~ Index).map {\n        case (start, idSeq, selectors, end) =>\n          ImportTree(idSeq, selectors, start, end)\n      }\n    P(`import` ~/ ImportExpr.rep(1, sep = \",\"./))\n  }\n\n}\n"
  },
  {
    "path": "modules/specification-level/src/main/scala/scala/cli/commands/SpecificationLevel.scala",
    "content": "package scala.cli.commands\n\nsealed trait SpecificationLevel extends Product with Serializable {\n  def md: String = toString + \" have\"\n}\n\n/** Specification levels in the context of Scala CLI runner specification. For more refer to\n  * [SIP-46](https://github.com/scala/improvement-proposals/pull/46)\n  *\n  * Levels are also used to mark if given option, directive or command is part of stable API.\n  */\nobject SpecificationLevel {\n\n  /** Marks option, directive or command that MUST be a part of any Scala Runner Specification (in\n    * RFC meaning). Annotated thing will be included in a new `scala` command.\n    *\n    * This also means that that thing should be sable and we need to support it.\n    */\n  case object MUST extends SpecificationLevel\n\n  /** Marks option, directive or command that SHOULD be a part of any Scala Runner Specification (in\n    * RFC meaning). Annotated thing will be included in a new `scala` command.\n    *\n    * This also means that that thing should be sable and we need to support it.\n    */\n  case object SHOULD extends SpecificationLevel\n\n  /** Marks option, directive or command that is an implementation details of Scala CLI and will not\n    * be a part of any Scala Runner Specification. Annotated thing will be included in a new `scala`\n    * command.\n    *\n    * This also means that that thing should be sable and we need to support it.\n    */\n  case object IMPLEMENTATION extends SpecificationLevel {\n    override def md: String = toString + \" specific\"\n  }\n\n  /** Annotated option, directive or command will not be a part of the Scala Runner Specification\n    * and will not be avialiable in the new `scala` command.\n    *\n    * This also means that that thing should be sable and we need to support it.\n    */\n  case object RESTRICTED extends SpecificationLevel {\n    override def md = \"Scala CLI specific\"\n  }\n\n  /** Annotated option, directive or command will not be a part of the Scala Runner Specification\n    * and will not be avialiable in the new `scala` command.\n    *\n    * Experimental option are not guarantee to be supported in upcoming versions of Scala CLI and\n    * all new options should be experimental.\n    */\n  case object EXPERIMENTAL extends SpecificationLevel {\n    override def md: String = toString\n  }\n\n  val inSpecification = Seq(MUST, SHOULD)\n}\n\nobject tags {\n  val valueSeparator: String = \":\" // separates values in a tag\n\n  // specification level tags\n  val experimental: String   = SpecificationLevel.EXPERIMENTAL.toString\n  val restricted: String     = SpecificationLevel.RESTRICTED.toString\n  val implementation: String = SpecificationLevel.IMPLEMENTATION.toString\n  val must: String           = SpecificationLevel.MUST.toString // included in --help by default\n  val should: String         = SpecificationLevel.SHOULD.toString\n\n  // other tags\n  // the `inShortHelp` tag whitelists options to be included in --help\n  // this is in contrast to blacklisting options in --help with the @Hidden annotation\n  val inShortHelp: String              = \"inShortHelp\" // included in --help by default\n  val deprecatedPrefix: String         = \"deprecated\"\n  def deprecated(name: String): String =\n    s\"$deprecatedPrefix$valueSeparator$name\" // produces a deprecated warning for the given name\n\n  def levelFor(name: String): Option[SpecificationLevel] = name match {\n    case `experimental`   => Some(SpecificationLevel.EXPERIMENTAL)\n    case `restricted`     => Some(SpecificationLevel.RESTRICTED)\n    case `implementation` => Some(SpecificationLevel.IMPLEMENTATION)\n    case `must`           => Some(SpecificationLevel.MUST)\n    case `should`         => Some(SpecificationLevel.SHOULD)\n    case _                => None\n  }\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyBuffer.scala",
    "content": "package scala.build.tastylib\n\n// Originally adapted from https://github.com/lampepfl/dotty/blob/d09981141ea16a98c654b98ca582b9626bf0ff0b/tasty/src/dotty/tools/tasty/TastyBuffer.scala\n\nobject TastyBuffer {\n\n  /** An address pointing to an index in a Tasty buffer's byte array */\n  case class Addr(index: Int) extends AnyVal {\n    def +(delta: Int): Addr = Addr(this.index + delta)\n  }\n\n  /** An address referring to a serialized name */\n  case class NameRef(index: Int) extends AnyVal\n\n  /** An array twice the size of given array, with existing elements copied over */\n  private def dble(arr: Array[Byte]): Array[Byte] = {\n    val arr1 = new Array[Byte](arr.length * 2)\n    System.arraycopy(arr, 0, arr1, 0, arr.length)\n    arr1\n  }\n\n}\n\n/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, and that\n  * supports reading and patching addresses represented as natural numbers.\n  */\nclass TastyBuffer(initialSize: Int) {\n  import TastyBuffer._\n\n  /** The current byte array, will be expanded as needed */\n  var bytes: Array[Byte] = new Array(initialSize)\n\n  /** The number of bytes written */\n  var length: Int = 0\n\n  // -- Output routines --------------------------------------------\n\n  /** Write a byte of data. */\n  def writeByte(b: Int): Unit = {\n    if (length >= bytes.length)\n      bytes = dble(bytes)\n    bytes(length) = b.toByte\n    length += 1\n  }\n\n  /** Write the first `n` bytes of `data`. */\n  def writeBytes(data: Array[Byte], n: Int): Unit = {\n    while (bytes.length < length + n) bytes = dble(bytes)\n    System.arraycopy(data, 0, bytes, length, n)\n    length += n\n  }\n\n  /** Write a natural number in big endian format, base 128. All but the last digits have bit 0x80\n    * set.\n    */\n  def writeNat(x: Int): Unit =\n    writeLongNat(x.toLong & 0x00000000ffffffffL)\n\n  /** Like writeNat, but for longs. Note that the binary representation of LongNat is identical to\n    * Nat if the long value is in the range Int.MIN_VALUE to Int.MAX_VALUE.\n    */\n  private def writeLongNat(x: Long): Unit = {\n    def writePrefix(x: Long): Unit = {\n      val y = x >>> 7\n      if (y != 0L) writePrefix(y)\n      writeByte((x & 0x7f).toInt)\n    }\n    val y = x >>> 7\n    if (y != 0L) writePrefix(y)\n    writeByte(((x & 0x7f) | 0x80).toInt)\n  }\n\n  // -- Address handling --------------------------------------------\n\n  /** The byte at given address */\n  def getByte(at: Addr): Int = bytes(at.index)\n\n  /** The natural number at address `at` */\n  def getNat(at: Addr): Int = getLongNat(at).toInt\n\n  /** The long natural number at address `at` */\n  private def getLongNat(at: Addr): Long = {\n    var b   = 0L\n    var x   = 0L\n    var idx = at.index\n    while ({\n      b = bytes(idx)\n      x = (x << 7) | (b & 0x7f)\n      idx += 1\n      (b & 0x80) == 0\n    })\n      ()\n    x\n  }\n\n  /** The address referring to the end of data written so far */\n  def currentAddr: Addr = Addr(length)\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyData.scala",
    "content": "package scala.build.tastylib\n\nimport java.io.ByteArrayOutputStream\nimport java.nio.charset.StandardCharsets\nimport java.util.UUID\n\nimport scala.build.tastylib.TastyFormat.NameTags\nimport scala.build.tastylib.TastyReader.Bytes\n\nfinal case class TastyData(\n  header: TastyData.Header,\n  names: TastyData.Names,\n  sections: TastyData.Sections\n) {\n  def mapNames(f: String => String): TastyData =\n    copy(\n      names = names.mapNames(f)\n    )\n}\n\nobject TastyData {\n  final case class Header(\n    id: UUID,\n    bytes: Bytes\n  )\n  final case class Names(\n    preambleBytes: Bytes,\n    nameAtRef: Seq[(Option[TastyName], Bytes)]\n  ) {\n    def simpleNames: Seq[String] =\n      nameAtRef.collect {\n        case (Some(s: TastyName.SimpleName), _) =>\n          s.raw\n      }\n    def mapNames(f: String => String): Names = {\n      val updatedNameAtRef = nameAtRef.map {\n        case elem @ (Some(s: TastyName.SimpleName), _) =>\n          val updatedName = f(s.raw)\n          if (updatedName == s.raw) elem\n          else {\n            val buf = new TastyBuffer(1 + 1 + updatedName.length)\n            buf.writeByte(NameTags.UTF8)\n            val strBytes = updatedName.getBytes(StandardCharsets.UTF_8)\n            buf.writeNat(strBytes.length)\n            buf.writeBytes(strBytes, strBytes.length)\n            (Some(TastyName.SimpleName(updatedName)), new Bytes(buf.bytes, 0, buf.length))\n          }\n        case other => other\n      }\n      val totalLength = updatedNameAtRef.iterator.map(_._2.length).sum\n      val preambleBuf = new TastyBuffer(1)\n      preambleBuf.writeNat(totalLength)\n      Names(Bytes(preambleBuf.bytes, 0, preambleBuf.length), updatedNameAtRef)\n    }\n  }\n  final case class Sections(\n    bytes: Bytes\n  )\n\n  def read(bytes: Array[Byte]): Either[UnpickleException, TastyData] = {\n\n    val headerReader = new TastyReader(bytes)\n    new TastyHeaderUnpickler(headerReader).readHeader().map { id =>\n      val header = Header(id, headerReader.read)\n\n      val nameReader         = headerReader.readerFromCurrentPos\n      val pickler            = new TastyUnpickler(nameReader)\n      val namesPreambleBytes = pickler.readNames()\n      val names              = Names(namesPreambleBytes, pickler.nameAtRef.toSeq)\n\n      val sections = Sections(nameReader.toRead)\n\n      TastyData(header, names, sections)\n    }\n  }\n\n  def write(data: TastyData): Array[Byte] = {\n    val baos = new ByteArrayOutputStream\n    data.header.bytes.writeTo(baos)\n    data.names.preambleBytes.writeTo(baos)\n    for ((_, b) <- data.names.nameAtRef)\n      b.writeTo(baos)\n    data.sections.bytes.writeTo(baos)\n    baos.toByteArray\n  }\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyFormat.scala",
    "content": "/*\n * Scala (https://www.scala-lang.org)\n *\n * Copyright EPFL and Lightbend, Inc.\n *\n * Licensed under Apache License 2.0\n * (http://www.apache.org/licenses/LICENSE-2.0).\n *\n * See the NOTICE file distributed with this work for\n * additional information regarding copyright ownership.\n */\n\n// Originally adapted from https://github.com/scala/scala/blob/20ac944346a93ba747811e80f8f67a09247cb987/src/compiler/scala/tools/tasty/TastyFormat.scala\n\npackage scala.build.tastylib\n\nobject TastyFormat {\n\n  final val header: Array[Int] = Array(0x5c, 0xa1, 0xab, 0x1f)\n\n  final val MajorVersion: Int = 28\n\n  object NameTags {\n    final val UTF8 = 1\n  }\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyHeaderUnpickler.scala",
    "content": "/*\n * Scala (https://www.scala-lang.org)\n *\n * Copyright EPFL and Lightbend, Inc.\n *\n * Licensed under Apache License 2.0\n * (http://www.apache.org/licenses/LICENSE-2.0).\n *\n * See the NOTICE file distributed with this work for\n * additional information regarding copyright ownership.\n */\n\n// Originally adapted from https://github.com/scala/scala/blob/20ac944346a93ba747811e80f8f67a09247cb987/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala\n\npackage scala.build.tastylib\n\nimport java.util.UUID\n\nclass TastyHeaderUnpickler(reader: TastyReader) {\n\n  def this(bytes: Array[Byte]) = this(new TastyReader(bytes))\n\n  /** reads and verifies the TASTy version, extracting the UUID */\n  def readHeader(): Either[UnpickleException, UUID] = {\n\n    val isTasty = TastyFormat.header.forall { b =>\n      val read = reader.readByte()\n      read == b\n    }\n\n    if (isTasty) {\n      val fileMajor = reader.readNat()\n      if (fileMajor <= 27) { // old behavior before `tasty-core` 3.0.0-RC1\n        reader.readNat()     // fileMinor\n        val signature = TastyHeaderUnpickler.signatureString(fileMajor)\n        Left(new UnpickleException(signature + TastyHeaderUnpickler.backIncompatAddendum))\n      }\n      else {\n        reader.readNat() // fileMinor\n        reader.readNat() // fileExperimental\n        val toolingLength = reader.readNat()\n        val toolingStart  = {\n          val start = reader.currentAddr\n          val end   = start + toolingLength\n          reader.goto(end)\n          start\n        }\n\n        val validVersion = fileMajor == TastyFormat.MajorVersion\n\n        if (validVersion)\n          Right(new UUID(reader.readUncompressedLong(), reader.readUncompressedLong()))\n        else {\n          val signature          = TastyHeaderUnpickler.signatureString(fileMajor)\n          val toolingVersion     = new String(reader.bytes, toolingStart.index, toolingLength)\n          val producedByAddendum =\n            s\"\\nThe TASTy file was produced by $toolingVersion.\"\n          val msg =\n            if (fileMajor < TastyFormat.MajorVersion) TastyHeaderUnpickler.backIncompatAddendum\n            else TastyHeaderUnpickler.forwardIncompatAddendum\n          Left(new UnpickleException(signature + msg + producedByAddendum))\n        }\n      }\n    }\n    else\n      Left(new UnpickleException(\"not a TASTy file\"))\n  }\n}\n\nobject TastyHeaderUnpickler {\n\n  private def signatureString(fileMajor: Int) =\n    s\"\"\"TASTy signature has wrong version.\n       | expected: {majorVersion: ${TastyFormat.MajorVersion}}\n       | found   : {majorVersion: $fileMajor}\n       |\n       |\"\"\".stripMargin\n\n  private def backIncompatAddendum =\n    \"\"\"This TASTy file was produced by an earlier release that is not supported anymore.\n      |Please recompile this TASTy with a later version.\"\"\".stripMargin\n\n  private def forwardIncompatAddendum =\n    \"\"\"This TASTy file was produced by a more recent, forwards incompatible release.\n      |To read this TASTy file, please upgrade your tooling.\"\"\".stripMargin\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyName.scala",
    "content": "/*\n * Scala (https://www.scala-lang.org)\n *\n * Copyright EPFL and Lightbend, Inc.\n *\n * Licensed under Apache License 2.0\n * (http://www.apache.org/licenses/LICENSE-2.0).\n *\n * See the NOTICE file distributed with this work for\n * additional information regarding copyright ownership.\n */\n\n// Originally adapted from https://github.com/scala/scala/blob/20ac944346a93ba747811e80f8f67a09247cb987/src/compiler/scala/tools/tasty/TastyName.scala\n\npackage scala.build.tastylib\n\nobject TastyName {\n  final case class SimpleName(raw: String) extends TastyName {\n    override def toString: String = raw\n  }\n}\n\nsealed abstract class TastyName extends Product with Serializable\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyReader.scala",
    "content": "/*\n * Scala (https://www.scala-lang.org)\n *\n * Copyright EPFL and Lightbend, Inc.\n *\n * Licensed under Apache License 2.0\n * (http://www.apache.org/licenses/LICENSE-2.0).\n *\n * See the NOTICE file distributed with this work for\n * additional information regarding copyright ownership.\n */\n\n// Originally adapted from https://github.com/scala/scala/blob/20ac944346a93ba747811e80f8f67a09247cb987/src/compiler/scala/tools/tasty/TastyReader.scala\n\npackage scala.build.tastylib\n\nimport java.io.OutputStream\n\nimport scala.build.tastylib.TastyBuffer.*\nimport scala.collection.mutable\n\nclass TastyReader(val bytes: Array[Byte], val start: Int, val end: Int, val base: Int = 0) {\n\n  def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length)\n\n  private var bp: Int = start\n\n  def pos: Int                  = bp\n  def read: TastyReader.Bytes   = TastyReader.Bytes(bytes, start, bp)\n  def toRead: TastyReader.Bytes = TastyReader.Bytes(bytes, bp, end)\n\n  def readerFromCurrentPos: TastyReader =\n    new TastyReader(bytes, bp, end, base)\n\n  def addr(idx: Int): Addr   = Addr(idx - base)\n  def index(addr: Addr): Int = addr.index + base\n\n  def currentAddr: Addr = addr(bp)\n\n  def endAddr: Addr = addr(end)\n\n  def isAtEnd: Boolean = bp == end\n\n  def readByte(): Int = {\n    val result = bytes(bp) & 0xff\n    bp += 1\n    result\n  }\n\n  def readNat(): Int = readLongNat().toInt\n  def readInt(): Int = readLongInt().toInt\n\n  private def readLongNat(): Long = {\n    var b = 0L\n    var x = 0L\n    while ({\n      b = bytes(bp)\n      x = (x << 7) | (b & 0x7f)\n      bp += 1\n      (b & 0x80) == 0\n    }) ()\n    x\n  }\n\n  private def readLongInt(): Long = {\n    var b       = bytes(bp)\n    var x: Long = (b << 1).toByte >> 1\n    bp += 1\n    while ((b & 0x80) == 0) {\n      b = bytes(bp)\n      x = (x << 7) | (b & 0x7f)\n      bp += 1\n    }\n    x\n  }\n\n  def readUncompressedLong(): Long = {\n    var x: Long = 0\n    for (_ <- 0 to 7)\n      x = (x << 8) | (readByte() & 0xff)\n    x\n  }\n\n  def readNameRef(): NameRef = NameRef(readNat())\n\n  def readEnd(): Addr = addr(readNat() + bp)\n\n  def goto(addr: Addr): Unit =\n    bp = index(addr)\n\n  def until[T](end: Addr)(op: => T): List[T] = {\n    val buf = new mutable.ListBuffer[T]\n    doUntil(end)(buf += op)\n    buf.toList\n  }\n\n  def doUntil(end: Addr)(op: => Unit): Unit = {\n    // noinspection LoopVariableNotUpdated\n    while (bp < index(end)) op\n    assert(bp == index(end))\n  }\n}\n\nobject TastyReader {\n  final class Bytes(val buf: Array[Byte], val start: Int, val end: Int) {\n    def length: Int                     = end - start\n    def writeTo(os: OutputStream): Unit =\n      os.write(buf, start, end - start)\n  }\n  object Bytes {\n    def apply(values: (Array[Byte], Int, Int)): Bytes = {\n      val (buf, start, end) = values\n      new Bytes(buf, start, end)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyUnpickler.scala",
    "content": "/*\n * Scala (https://www.scala-lang.org)\n *\n * Copyright EPFL and Lightbend, Inc.\n *\n * Licensed under Apache License 2.0\n * (http://www.apache.org/licenses/LICENSE-2.0).\n *\n * See the NOTICE file distributed with this work for\n * additional information regarding copyright ownership.\n */\n\n// Originally adapted from https://github.com/scala/scala/blob/20ac944346a93ba747811e80f8f67a09247cb987/src/compiler/scala/tools/nsc/tasty/TastyUnpickler.scala\n\npackage scala.build.tastylib\n\nimport scala.build.tastylib.TastyBuffer.NameRef\nimport scala.build.tastylib.TastyFormat.NameTags\nimport scala.build.tastylib.TastyName.*\nimport scala.build.tastylib.TastyReader.Bytes\nimport scala.collection.mutable\n\nobject TastyUnpickler {\n\n  final class NameTable {\n    private val names = new mutable.ArrayBuffer[(Option[TastyName], Bytes)]\n    def add(\n      name: Option[TastyName],\n      bytes: Bytes\n    ): mutable.ArrayBuffer[(Option[TastyName], Bytes)] =\n      names += (name -> bytes)\n    def apply(ref: NameRef): Option[TastyName] = names(ref.index)._1\n    def size: Int                              = names.size\n    def toSeq: Seq[(Option[TastyName], Bytes)] = names.toArray.toSeq\n  }\n}\n\nimport TastyUnpickler._\n\nprivate class TastyUnpickler(reader: TastyReader) { self =>\n\n  private val nameTable = new NameTable\n\n  def nameAtRef: NameTable = nameTable\n\n  private def readNameContents(): (Option[TastyName], Bytes) = {\n    val initialPos = reader.pos\n    val tag        = reader.readByte()\n    val length     = reader.readNat()\n    val start      = reader.currentAddr\n    val end        = start + length\n    val result     = tag match {\n      case NameTags.UTF8 =>\n        Some(SimpleName(new String(reader.bytes.slice(start.index, start.index + length), \"UTF-8\")))\n      case _ =>\n        None\n    }\n    reader.goto(end)\n    (result, new Bytes(reader.bytes, initialPos, reader.index(end)))\n  }\n\n  def readHeader(): Unit = new TastyHeaderUnpickler(reader).readHeader()\n\n  def readNames(): Bytes = {\n    val preambleStart = reader.pos\n    val endAddr       = reader.readEnd()\n    val preambleEnd   = reader.pos\n    val preambleBytes = Bytes((reader.bytes, preambleStart, preambleEnd))\n    reader.doUntil(endAddr) {\n      val (n, b) = readNameContents()\n      nameTable.add(n, b)\n    }\n    preambleBytes\n  }\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/TastyVersions.scala",
    "content": "package scala.build.tastylib\n\nimport scala.build.tastylib.internal.Constants\nimport scala.util.Try\n\nobject TastyVersions {\n  implicit class VersionOps(version: String) {\n    def majorVersion: Int               = version.split('.')(0).toInt\n    def minorVersion: Int               = version.split('.')(1).toInt\n    def minorVersionOption: Option[Int] = Try(minorVersion).toOption\n    def isLatestSupportedMajorVersion(latestSupportedScalaVersion: String): Boolean = {\n      val latestSupportedMajor = latestSupportedScalaVersion.majorVersion.toString\n      version.startsWith(s\"$latestSupportedMajor.\") || version == latestSupportedMajor\n    }\n  }\n\n  def shouldRunPreprocessor(\n    scalaVersion: String,\n    scalaCliVersion: String,\n    defaultScalaVersion: Option[String]\n  ): Either[String, Boolean] = {\n    val scalaDefault = defaultScalaVersion.getOrElse(Constants.defaultScalaVersion)\n    if (!scalaVersion.isLatestSupportedMajorVersion(scalaDefault)) Right(false)\n    else scalaVersion.minorVersionOption match {\n      case Some(scalaMinor) if scalaMinor > scalaDefault.minorVersion =>\n        Left(\n          s\"\"\"Scala CLI (v$scalaCliVersion) cannot post process TASTY files from Scala $scalaVersion.\n             |This is not a fatal error since post processing only cleans up source paths in TASTY file.\n             |It should not affect your application.\n             |You may be getting this warning because you are using a newer version of Scala than the one supported by Scala CLI (v$scalaCliVersion).\n             |Make sure your Scala CLI is up-to-date.\n             |You may need to wait for $scalaVersion support in a future version of Scala CLI.\n             |\"\"\".stripMargin\n        )\n      case _ => Right(true)\n    }\n  }\n}\n"
  },
  {
    "path": "modules/tasty-lib/src/main/scala/scala/build/tastylib/UnpickleException.scala",
    "content": "/*\n * Scala (https://www.scala-lang.org)\n *\n * Copyright EPFL and Lightbend, Inc.\n *\n * Licensed under Apache License 2.0\n * (http://www.apache.org/licenses/LICENSE-2.0).\n *\n * See the NOTICE file distributed with this work for\n * additional information regarding copyright ownership.\n */\n\n// Adapted from https://github.com/scala/scala/blob/20ac944346a93ba747811e80f8f67a09247cb987/src/compiler/scala/tools/tasty/UnpickleException.scala\n\npackage scala.build.tastylib\n\nclass UnpickleException(msg: String) extends Exception(msg)\n"
  },
  {
    "path": "modules/test-runner/src/main/scala/scala/build/testrunner/AsmTestRunner.scala",
    "content": "package scala.build.testrunner\n\nimport org.objectweb.asm\nimport sbt.testing.{Logger as _, *}\n\nimport java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream}\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.{Files, Path}\nimport java.util.concurrent.ConcurrentHashMap\n\nimport scala.jdk.CollectionConverters.*\n\nobject AsmTestRunner {\n\n  class ParentInspector(classPath: Seq[Path], logger: Logger) {\n\n    private val cache = new ConcurrentHashMap[String, Seq[String]]\n\n    private def parents(className: String): Seq[String] =\n      Option(cache.get(className)) match {\n        case Some(value) => value\n        case None        =>\n          val byteCodeOpt =\n            findInClassPath(classPath, className + \".class\", logger)\n              .take(1)\n              .toList\n              .headOption\n          val parents = byteCodeOpt match {\n            case None    => Nil\n            case Some(b) =>\n              val reader  = new asm.ClassReader(new ByteArrayInputStream(b))\n              val checker = new TestClassChecker\n              reader.accept(checker, 0)\n              checker.implements\n          }\n\n          cache.put(className, parents)\n          parents\n      }\n\n    def allParents(className: String): LazyList[String] = {\n\n      def helper(done: Set[String], todo: List[String]): LazyList[String] =\n        todo match {\n          case Nil    => LazyList.empty\n          case h :: t =>\n            if (done(h)) helper(done, t)\n            else h #:: helper(done + h, parents(h).toList ::: t)\n        }\n\n      helper(Set.empty, className :: Nil)\n    }\n\n  }\n\n  // originally adapted from https://github.com/com-lihaoyi/mill/blob/ab4d61a50da24fb7fac97c4453dd8a770d8ac62b/scalalib/src/Lib.scala#L156-L172\n  def matchFingerprints(\n    className: String,\n    byteCode: () => InputStream,\n    fingerprints: Seq[Fingerprint],\n    parentInspector: ParentInspector\n  ): Option[Fingerprint] = {\n\n    val checker          = new TestClassChecker\n    var is0: InputStream = null\n    try {\n      is0 = byteCode()\n      val reader = new asm.ClassReader(is0)\n      reader.accept(checker, 0)\n    }\n    finally if (is0 != null) is0.close()\n\n    val isModule              = className.endsWith(\"$\")\n    val hasPublicConstructors = checker.publicConstructorCount > 0\n    val definitelyNoTests     = checker.isAbstract ||\n      checker.isInterface ||\n      checker.publicConstructorCount > 1 ||\n      isModule == hasPublicConstructors\n    if (definitelyNoTests)\n      None\n    else\n      fingerprints.find {\n        case f: SubclassFingerprint =>\n          f.isModule == isModule &&\n          parentInspector.allParents(checker.name)\n            .contains(f.superclassName().replace('.', '/'))\n\n        case _: AnnotatedFingerprint =>\n          // val annotationCls = loader.loadClass(f.annotationName())\n          //   .asInstanceOf[Class[Annotation]]\n          // f.isModule == isModule && (\n          //   cls.isAnnotationPresent(annotationCls) ||\n          //   cls.getDeclaredMethods.exists(_.isAnnotationPresent(annotationCls)) ||\n          //   cls.getMethods.exists(m => m.isAnnotationPresent(annotationCls) && Modifier.isPublic(m.getModifiers()))\n          // )\n          ??? // TODO: this is necessary to support JUnit, check https://github.com/VirtusLab/scala-cli/issues/3627\n      }\n  }\n\n  private def listClassesByteCode(\n    classPathEntry: Path,\n    keepJars: Boolean,\n    logger: Logger\n  ): Iterator[(String, () => InputStream)] =\n    if (Files.isDirectory(classPathEntry)) {\n      var stream: java.util.stream.Stream[Path] = null\n      try {\n        stream = Files.walk(classPathEntry, Int.MaxValue)\n        stream\n          .iterator\n          .asScala\n          .filter(_.getFileName.toString.endsWith(\".class\"))\n          .map(_.toAbsolutePath)\n          .map { p =>\n            val clsName      = classPathEntry.relativize(p).toString.stripSuffix(\".class\")\n            def openStream() = Files.newInputStream(p)\n            (clsName, () => openStream())\n          }\n          .toVector // fully consume stream before closing it\n          .iterator\n      }\n      catch {\n        case e: Exception =>\n          logger.log(s\"Could not walk directory $classPathEntry: ${e.getMessage}\")\n          Iterator.empty\n      }\n      finally if (stream != null) stream.close()\n    }\n    else if (keepJars && Files.isRegularFile(classPathEntry)) {\n      import java.util.zip._\n      val buf         = Array.ofDim[Byte](16384)\n      var zf: ZipFile = null\n      try {\n        zf = new ZipFile(classPathEntry.toFile)\n        zf.entries\n          .asScala\n          // FIXME Check if these are files too\n          .filter(_.getName.endsWith(\".class\"))\n          .map { ent =>\n            val baos            = new ByteArrayOutputStream\n            var is: InputStream = null\n            try {\n              is = zf.getInputStream(ent)\n              var read = -1\n              while ({\n                read = is.read(buf)\n                read >= 0\n              }) baos.write(buf, 0, read)\n              val clsName      = ent.getName.stripSuffix(\".class\")\n              def openStream() = new ByteArrayInputStream(baos.toByteArray)\n              (clsName, () => openStream())\n            }\n            finally if (is != null) is.close()\n          }\n          .toVector // fully consume ZipFile before closing it\n          .iterator\n      }\n      catch {\n        case e: Exception =>\n          logger.log(s\"Could not read JAR $classPathEntry: ${e.getMessage}\")\n          Iterator.empty\n      }\n      finally if (zf != null) zf.close()\n    }\n    else Iterator.empty\n\n  private def listClassesByteCode(\n    classPath: Seq[Path],\n    keepJars: Boolean,\n    logger: Logger\n  ): Iterator[(String, () => InputStream)] =\n    classPath.iterator.flatMap(listClassesByteCode(_, keepJars, logger))\n\n  private def findInClassPath(\n    classPathEntry: Path,\n    name: String,\n    logger: Logger\n  ): Option[Array[Byte]] =\n    if (Files.isDirectory(classPathEntry)) {\n      val p = classPathEntry.resolve(name)\n      if (Files.isRegularFile(p))\n        try Some(Files.readAllBytes(p))\n        catch {\n          case e: java.io.IOException =>\n            logger.debug(s\"Could not read $p: ${e.getMessage}\")\n            None\n        }\n      else None\n    }\n    else if (Files.isRegularFile(classPathEntry)) {\n      import java.util.zip._\n      val buf         = Array.ofDim[Byte](16384)\n      var zf: ZipFile = null\n      try {\n        zf = new ZipFile(classPathEntry.toFile)\n        Option(zf.getEntry(name)).map { ent =>\n          val baos            = new ByteArrayOutputStream\n          var is: InputStream = null\n          try {\n            is = zf.getInputStream(ent)\n            var read = -1\n            while ({\n              read = is.read(buf)\n              read >= 0\n            }) baos.write(buf, 0, read)\n            baos.toByteArray\n          }\n          finally if (is != null) is.close()\n        }\n      }\n      catch {\n        case e: java.io.IOException =>\n          logger.debug(s\"Could not read $name from $classPathEntry: ${e.getMessage}\")\n          None\n      }\n      finally if (zf != null) zf.close()\n    }\n    else None\n\n  private def findInClassPath(\n    classPath: Seq[Path],\n    name: String,\n    logger: Logger\n  ): Iterator[Array[Byte]] =\n    classPath\n      .iterator\n      .flatMap(findInClassPath(_, name, logger).iterator)\n\n  /** Parse Java ServiceLoader format: one class name per line; # comments and empty lines ignored.\n    */\n  private def parseServiceFileContent(content: String): Seq[String] =\n    content\n      .split(\"[\\r\\n]+\")\n      .iterator\n      .map(_.trim)\n      .filter(line => line.nonEmpty && !line.startsWith(\"#\"))\n      .toSeq\n\n  def findFrameworkServices(classPath: Seq[Path], logger: Logger): Seq[String] =\n    findInClassPath(classPath, \"META-INF/services/sbt.testing.Framework\", logger)\n      .flatMap(b => parseServiceFileContent(new String(b, StandardCharsets.UTF_8)))\n      .toSeq\n\n  def findFrameworks(\n    classPath: Seq[Path],\n    preferredClasses: Seq[String],\n    parentInspector: ParentInspector,\n    logger: Logger\n  ): List[String] = {\n    // first check preferred classes\n    val preferredClassesByteCode = preferredClasses\n      .map(_.replace('.', '/'))\n      .flatMap { name =>\n        findInClassPath(classPath, name + \".class\", logger)\n          .map { b =>\n            def openStream() = new ByteArrayInputStream(b)\n            (name, () => openStream())\n          }\n      }\n    // scan all classes in classpath\n    (preferredClassesByteCode.iterator ++ listClassesByteCode(classPath, true, logger))\n      .flatMap {\n        case (moduleInfo, _) if moduleInfo.contains(\"module-info\") => Iterator.empty\n        case (name, is)                                            =>\n          val checker          = new TestClassChecker\n          var is0: InputStream = null\n          try {\n            is0 = is()\n            val reader = new asm.ClassReader(is0)\n            reader.accept(checker, 0)\n          }\n          finally if (is0 != null) is0.close()\n          val isFramework = parentInspector.allParents(name).contains(\"sbt/testing/Framework\")\n          if (isFramework && !checker.isAbstract && checker.publicConstructorCount == 1)\n            Iterator(name)\n          else\n            Iterator.empty\n      }\n      .take(math.max(preferredClassesByteCode.length, 1))\n      .toList\n  }\n\n  private class TestClassChecker extends asm.ClassVisitor(asm.Opcodes.ASM9) {\n    private var nameOpt                 = Option.empty[String]\n    private var publicConstructorCount0 = 0\n    private var isInterfaceOpt          = Option.empty[Boolean]\n    private var isAbstractOpt           = Option.empty[Boolean]\n    private var implements0             = List.empty[String]\n    def name: String                    = nameOpt.getOrElse(sys.error(\"Class not visited\"))\n    def publicConstructorCount: Int     = publicConstructorCount0\n    def implements: Seq[String]         = implements0\n    def isAbstract: Boolean             = isAbstractOpt.getOrElse(sys.error(\"Class not visited\"))\n    def isInterface: Boolean            = isInterfaceOpt.getOrElse(sys.error(\"Class not visited\"))\n    override def visit(\n      version: Int,\n      access: Int,\n      name: String,\n      signature: String,\n      superName: String,\n      interfaces: Array[String]\n    ): Unit = {\n      isInterfaceOpt = Some((access & asm.Opcodes.ACC_INTERFACE) != 0)\n      isAbstractOpt = Some((access & asm.Opcodes.ACC_ABSTRACT) != 0)\n      nameOpt = Some(name)\n      implements0 = Option(superName).toList ::: implements0\n      if (interfaces.nonEmpty)\n        implements0 = interfaces.toList ::: implements0\n    }\n    override def visitMethod(\n      access: Int,\n      name: String,\n      descriptor: String,\n      signature: String,\n      exceptions: Array[String]\n    ): asm.MethodVisitor = {\n      def isPublic = (access & asm.Opcodes.ACC_PUBLIC) != 0\n      if (name == \"<init>\" && isPublic)\n        publicConstructorCount0 += 1\n      null\n    }\n  }\n\n  def taskDefs(\n    classPath: Seq[Path],\n    keepJars: Boolean,\n    fingerprints: Seq[Fingerprint],\n    parentInspector: ParentInspector,\n    logger: Logger\n  ): Iterator[TaskDef] =\n    listClassesByteCode(classPath, keepJars = keepJars, logger)\n      .flatMap {\n        case (name, is) =>\n          try\n            matchFingerprints(name, is, fingerprints, parentInspector)\n              .map((name.stripSuffix(\"$\"), _))\n              .iterator\n          catch {\n            case e: java.io.IOException =>\n              logger.debug(s\"Could not read bytecode for $name: ${e.getMessage}\")\n              Iterator.empty\n          }\n      }\n      .map {\n        case (clsName, fp) =>\n          new TaskDef(\n            clsName.replace('/', '.').replace('\\\\', '.'),\n            fp,\n            false,\n            Array(new SuiteSelector)\n          )\n      }\n\n  def main(args: Array[String]): Unit = {\n\n    val logger = Logger(0)\n\n    val classLoader = Thread.currentThread().getContextClassLoader\n    val classPath   = TestRunner.classPath(classLoader, logger)\n\n    val parentCache = new ParentInspector(classPath, logger)\n\n    val frameworkClassName =\n      findFrameworkServices(classPath, logger).headOption // TODO handle multiple\n        .orElse(findFrameworks(\n          classPath,\n          TestRunner.commonTestFrameworks,\n          parentCache,\n          logger\n        ).headOption)\n        .getOrElse(sys.error(\"No test framework found\"))\n        .replace('/', '.')\n        .replace('\\\\', '.')\n\n    val framework = classLoader\n      .loadClass(frameworkClassName)\n      .getConstructor()\n      .newInstance()\n      .asInstanceOf[Framework]\n\n    val out = System.out\n\n    val taskDefs0 =\n      taskDefs(\n        classPath,\n        keepJars = false,\n        framework.fingerprints().toIndexedSeq,\n        parentCache,\n        logger\n      ).toArray\n\n    val runner       = framework.runner(Array(), Array(), classLoader)\n    val initialTasks = runner.tasks(taskDefs0).toSeq\n    val events       = TestRunner.runTasks(initialTasks, out)\n\n    val doneMsg = runner.done()\n    if (doneMsg.nonEmpty)\n      out.println(doneMsg)\n\n    val failed = events.exists { ev =>\n      ev.status == Status.Error ||\n      ev.status == Status.Failure ||\n      ev.status == Status.Canceled\n    }\n    if (failed)\n      sys.exit(1)\n  }\n}\n\nabstract class AsmTestRunner\n"
  },
  {
    "path": "modules/test-runner/src/main/scala/scala/build/testrunner/DynamicTestRunner.scala",
    "content": "package scala.build.testrunner\n\nimport sbt.testing.{Logger as _, *}\n\nimport java.util.regex.Pattern\n\nimport scala.annotation.tailrec\nimport scala.build.testrunner.FrameworkUtils.*\n\nobject DynamicTestRunner {\n\n  /** Based on junit-interface [GlobFilter.\n    * compileGlobPattern](https://github.com/sbt/junit-interface/blob/f8c6372ed01ce86f15393b890323d96afbe6d594/src/main/java/com/novocode/junit/GlobFilter.java#L37)\n    *\n    * @return\n    *   Pattern allows to regex input which contains only *, for example `*foo*` match to\n    *   `MyTests.foo`\n    */\n  private def globPattern(expr: String): Pattern = {\n    val a = expr.split(\"\\\\*\", -1)\n    val b = new StringBuilder()\n    for (i <- 0 until a.length) {\n      if (i != 0) b.append(\".*\")\n      if (a(i).nonEmpty) b.append(Pattern.quote(a(i).replaceAll(\"\\n\", \"\\\\n\")))\n    }\n    Pattern.compile(b.toString)\n  }\n\n  def main(args: Array[String]): Unit = {\n\n    val (testFrameworks, requireTests, verbosity, testOnly, args0) = {\n      @tailrec\n      def parse(\n        testFrameworks: List[String],\n        reverseTestArgs: List[String],\n        requireTests: Boolean,\n        verbosity: Int,\n        testOnly: Option[String],\n        args: List[String]\n      ): (List[String], Boolean, Int, Option[String], List[String]) =\n        args match {\n          case Nil => (testFrameworks, requireTests, verbosity, testOnly, reverseTestArgs.reverse)\n          case \"--\" :: t =>\n            (testFrameworks, requireTests, verbosity, testOnly, reverseTestArgs.reverse ::: t)\n          case h :: t if h.startsWith(\"--test-framework=\") =>\n            parse(\n              testFrameworks ++ List(h.stripPrefix(\"--test-framework=\")),\n              reverseTestArgs,\n              requireTests,\n              verbosity,\n              testOnly,\n              t\n            )\n          case h :: t if h.startsWith(\"--test-only=\") =>\n            parse(\n              testFrameworks,\n              reverseTestArgs,\n              requireTests,\n              verbosity,\n              Some(h.stripPrefix(\"--test-only=\")),\n              t\n            )\n          case h :: t if h.startsWith(\"--verbosity=\") =>\n            val v =\n              try h.stripPrefix(\"--verbosity=\").toInt\n              catch {\n                case _: NumberFormatException =>\n                  System.err.println(s\"Warning: malformed --verbosity value: $h\")\n                  0\n              }\n            parse(\n              testFrameworks,\n              reverseTestArgs,\n              requireTests,\n              v,\n              testOnly,\n              t\n            )\n          case \"--require-tests\" :: t =>\n            parse(testFrameworks, reverseTestArgs, true, verbosity, testOnly, t)\n          case h :: t =>\n            parse(testFrameworks, h :: reverseTestArgs, requireTests, verbosity, testOnly, t)\n        }\n\n      parse(Nil, Nil, false, 0, None, args.toList)\n    }\n\n    val logger = Logger(verbosity)\n\n    if (testFrameworks.nonEmpty) logger.debug(\n      s\"\"\"Directly passed ${testFrameworks.length} test frameworks:\n         |  - ${testFrameworks.mkString(\"\\n  - \")}\"\"\".stripMargin\n    )\n\n    val classLoader = Thread.currentThread().getContextClassLoader\n    val classPath0  = TestRunner.classPath(classLoader, logger)\n    val frameworks  =\n      Option(testFrameworks)\n        .filter(_.nonEmpty)\n        .map(_.map(loadFramework(classLoader, _)).toSeq)\n        .getOrElse {\n          getFrameworksToRun(\n            frameworkServices = findFrameworkServices(classLoader),\n            frameworks =\n              findFrameworks(classPath0, classLoader, TestRunner.commonTestFrameworks, logger)\n          )(logger) match {\n            case f if f.nonEmpty     => f\n            case _ if verbosity >= 2 => sys.error(\"No test framework found\")\n            case _                   =>\n              System.err.println(\"No test framework found\")\n              sys.exit(1)\n          }\n        }\n    def classes = {\n      val keepJars = false // look into dependencies, much slower\n      listClasses(classPath0, keepJars, logger).flatMap { name =>\n        try Iterator(classLoader.loadClass(name))\n        catch {\n          case _: ClassNotFoundException | _: NoClassDefFoundError |\n              _: UnsupportedClassVersionError | _: IncompatibleClassChangeError =>\n            // Expected: not every .class file on the classpath is loadable\n            logger.debug(s\"Could not load class $name\")\n            Iterator.empty\n        }\n      }\n    }\n    val out = System.out\n\n    val exitCodes =\n      frameworks\n        .map { framework =>\n          logger.log(s\"Running test framework: ${framework.name}\")\n          val fingerprints = framework.fingerprints()\n          val runner       = framework.runner(args0.toArray, Array(), classLoader)\n\n          def clsFingerprints = classes.flatMap { cls =>\n            matchFingerprints(classLoader, cls, fingerprints, logger)\n              .map((cls, _))\n              .iterator\n          }\n\n          val taskDefs = clsFingerprints\n            .filter {\n              case (cls, _) =>\n                testOnly.forall(pattern =>\n                  globPattern(pattern).matcher(cls.getName.stripSuffix(\"$\")).matches()\n                )\n            }\n            .map {\n              case (cls, fp) =>\n                new TaskDef(cls.getName.stripSuffix(\"$\"), fp, false, Array(new SuiteSelector))\n            }\n            .toVector\n          val initialTasks = runner.tasks(taskDefs.toArray).toSeq\n          val events       = TestRunner.runTasks(initialTasks, out)\n          val failed       = events.exists { ev =>\n            ev.status == Status.Error ||\n            ev.status == Status.Failure ||\n            ev.status == Status.Canceled\n          }\n          val doneMsg = runner.done()\n          if (doneMsg.nonEmpty) out.println(doneMsg)\n          if (requireTests && events.isEmpty) {\n            logger.error(s\"Error: no tests were run for ${framework.name()}.\")\n            1\n          }\n          else if (failed) {\n            logger.error(s\"Error: ${framework.name()} tests failed.\")\n            1\n          }\n          else {\n            logger.log(s\"${framework.name()} tests ran successfully.\")\n            0\n          }\n        }\n    if (exitCodes.contains(1)) sys.exit(1)\n    else sys.exit(0)\n  }\n}\n\nabstract class DynamicTestRunner\n"
  },
  {
    "path": "modules/test-runner/src/main/scala/scala/build/testrunner/FrameworkUtils.scala",
    "content": "package scala.build.testrunner\n\nimport sbt.testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint}\n\nimport java.lang.annotation.Annotation\nimport java.lang.reflect.Modifier\nimport java.nio.file.{Files, Path}\nimport java.util.ServiceLoader\n\nimport scala.jdk.CollectionConverters.*\n\nobject FrameworkUtils {\n  // needed for Scala 2.12\n  def distinctBy[A, B](seq: Seq[A])(f: A => B): Seq[A] = {\n    @annotation.tailrec\n    def loop(remaining: Seq[A], seen: Set[B], acc: Vector[A]): Vector[A] =\n      if (remaining.isEmpty) acc\n      else {\n        val head = remaining.head\n        val tail = remaining.tail\n        val key  = f(head)\n        if (seen(key)) loop(tail, seen, acc)\n        else loop(tail, seen + key, acc :+ head)\n      }\n\n    loop(seq, Set.empty, Vector.empty)\n  }\n\n  implicit class TestFrameworkOps(val framework: Framework) {\n    def description: String =\n      s\"${framework.name()} (${Option(framework.getClass.getCanonicalName).getOrElse(framework.toString)})\"\n  }\n\n  def getFrameworksToRun(allFrameworks: Seq[Framework])(logger: Logger): Seq[Framework] = {\n    val distinctFrameworks = distinctBy(allFrameworks)(_.name())\n    if (distinctFrameworks.nonEmpty)\n      logger.debug(\n        s\"\"\"Distinct test frameworks found (by framework name):\n           |  - ${distinctFrameworks.map(_.description).mkString(\"\\n  - \")}\n           |\"\"\".stripMargin\n      )\n\n    val finalFrameworks =\n      distinctFrameworks\n        .filter(f1 =>\n          !distinctFrameworks\n            .filter(_ != f1)\n            .exists(f2 =>\n              f1.getClass.isAssignableFrom(f2.getClass)\n            )\n        )\n    if (finalFrameworks.nonEmpty)\n      logger.log(\n        s\"\"\"Final list of test frameworks found:\n           |  - ${finalFrameworks.map(_.description).mkString(\"\\n  - \")}\n           |\"\"\".stripMargin\n      )\n\n    val skippedInheritedFrameworks = distinctFrameworks.diff(finalFrameworks)\n    if (skippedInheritedFrameworks.nonEmpty)\n      logger.log(\n        s\"\"\"The following test frameworks have been filtered out, as they're being inherited from by others:\n           |  - ${skippedInheritedFrameworks.map(_.description).mkString(\"\\n  - \")}\n           |\"\"\".stripMargin\n      )\n\n    finalFrameworks\n  }\n\n  def getFrameworksToRun(\n    frameworkServices: Seq[Framework],\n    frameworks: Seq[Framework]\n  )(logger: Logger): Seq[Framework] = {\n    if (frameworkServices.nonEmpty)\n      logger.debug(\n        s\"\"\"Found test framework services:\n           |  - ${frameworkServices.map(_.description).mkString(\"\\n  - \")}\n           |\"\"\".stripMargin\n      )\n    if (frameworks.nonEmpty)\n      logger.debug(\n        s\"\"\"Found test frameworks:\n           |  - ${frameworks.map(_.description).mkString(\"\\n  - \")}\n           |\"\"\".stripMargin\n      )\n\n    getFrameworksToRun(allFrameworks = frameworkServices ++ frameworks)(logger)\n  }\n\n  def findFrameworkServices(loader: ClassLoader): Seq[Framework] =\n    ServiceLoader.load(classOf[Framework], loader)\n      .iterator()\n      .asScala\n      .toSeq\n\n  def loadFramework(\n    loader: ClassLoader,\n    className: String\n  ): Framework = {\n    val cls         = loader.loadClass(className)\n    val constructor = cls.getConstructor()\n    constructor.newInstance().asInstanceOf[Framework]\n  }\n\n  def findFrameworks(\n    classPath: Seq[Path],\n    loader: ClassLoader,\n    preferredClasses: Seq[String],\n    logger: Logger\n  ): Seq[Framework] = {\n    val frameworkCls = classOf[Framework]\n    // first try preferred classes, then scan classpath\n    (preferredClasses.iterator ++ listClasses(classPath, true, logger))\n      .flatMap { name =>\n        val it: Iterator[Class[?]] =\n          try Iterator(loader.loadClass(name))\n          catch {\n            case _: ClassNotFoundException | _: UnsupportedClassVersionError | _: NoClassDefFoundError | _: IncompatibleClassChangeError =>\n              // Expected: most classpath entries aren't test frameworks\n              Iterator.empty\n          }\n        it\n      }\n      .flatMap { cls =>\n        def isAbstract = Modifier.isAbstract(cls.getModifiers)\n\n        def publicConstructorCount =\n          cls.getConstructors.count { c =>\n            Modifier.isPublic(c.getModifiers) && c.getParameterCount == 0\n          }\n\n        val it: Iterator[Class[?]] =\n          if (frameworkCls.isAssignableFrom(cls) && !isAbstract && publicConstructorCount == 1)\n            Iterator(cls)\n          else\n            Iterator.empty\n        it\n      }\n      .flatMap { cls =>\n        try {\n          val constructor = cls.getConstructor()\n          Iterator(constructor.newInstance().asInstanceOf[Framework])\n        }\n        catch {\n          case e: Exception =>\n            logger.log(s\"Could not instantiate framework ${cls.getName}: $e\")\n            Iterator.empty\n        }\n      }\n      .toSeq\n  }\n\n  def listClasses(classPath: Seq[Path], keepJars: Boolean, logger: Logger): Iterator[String] =\n    classPath.iterator.flatMap(listClasses(_, keepJars, logger))\n\n  def listClasses(classPathEntry: Path, keepJars: Boolean, logger: Logger): Iterator[String] =\n    if (Files.isDirectory(classPathEntry)) {\n      var stream: java.util.stream.Stream[Path] = null\n      try {\n        stream = Files.walk(classPathEntry, Int.MaxValue)\n        stream\n          .iterator\n          .asScala\n          .filter(_.getFileName.toString.endsWith(\".class\"))\n          .map(classPathEntry.relativize)\n          .map { p =>\n            val count = p.getNameCount\n            (0 until count).map(p.getName).mkString(\".\")\n          }\n          .map(_.stripSuffix(\".class\"))\n          .toVector // fully consume stream before closing it\n          .iterator\n      }\n      catch {\n        case e: Exception =>\n          logger.log(s\"Could not walk directory $classPathEntry: ${e.getMessage}\")\n          Iterator.empty\n      }\n      finally if (stream != null) stream.close()\n    }\n    else if (keepJars && Files.isRegularFile(classPathEntry)) {\n      import java.util.zip._\n      var zf: ZipFile = null\n      try {\n        zf = new ZipFile(classPathEntry.toFile)\n        zf.entries\n          .asScala\n          // FIXME Check if these are files too\n          .filter(_.getName.endsWith(\".class\"))\n          .map(ent => ent.getName.stripSuffix(\".class\").replace(\"/\", \".\"))\n          .toVector // full consume ZipFile before closing it\n          .iterator\n      }\n      catch {\n        case e: Exception =>\n          logger.log(s\"Could not read JAR $classPathEntry: ${e.getMessage}\")\n          Iterator.empty\n      }\n      finally if (zf != null) zf.close()\n    }\n    else Iterator.empty\n\n  // adapted from https://github.com/com-lihaoyi/mill/blob/ab4d61a50da24fb7fac97c4453dd8a770d8ac62b/scalalib/src/Lib.scala#L156-L172\n  def matchFingerprints(\n    loader: ClassLoader,\n    cls: Class[?],\n    fingerprints: Array[Fingerprint],\n    logger: Logger\n  ): Option[Fingerprint] = {\n    val isModule               = cls.getName.endsWith(\"$\")\n    val publicConstructorCount = cls.getConstructors.count(c => Modifier.isPublic(c.getModifiers))\n    val noPublicConstructors   = publicConstructorCount == 0\n    val definitelyNoTests      = Modifier.isAbstract(cls.getModifiers) ||\n      cls.isInterface ||\n      publicConstructorCount > 1 ||\n      isModule != noPublicConstructors\n    if (definitelyNoTests)\n      None\n    else\n      fingerprints.find {\n        case f: SubclassFingerprint =>\n          f.isModule == isModule && {\n            try\n              loader.loadClass(f.superclassName())\n                .isAssignableFrom(cls)\n            catch {\n              case _: ClassNotFoundException =>\n                logger.debug(\n                  s\"Superclass not found for fingerprint matching: ${f.superclassName()}\"\n                )\n                false\n            }\n          }\n\n        case f: AnnotatedFingerprint =>\n          f.isModule == isModule && {\n            try {\n              val annotationCls = loader.loadClass(f.annotationName())\n                .asInstanceOf[Class[Annotation]]\n              cls.isAnnotationPresent(annotationCls) ||\n              cls.getDeclaredMethods.exists(_.isAnnotationPresent(annotationCls)) ||\n              cls.getMethods.exists { m =>\n                m.isAnnotationPresent(annotationCls) &&\n                Modifier.isPublic(m.getModifiers)\n              }\n            }\n            catch {\n              case _: ClassNotFoundException =>\n                logger.debug(\n                  s\"Annotation class not found for fingerprint matching: ${f.annotationName()}\"\n                )\n                false\n            }\n          }\n      }\n  }\n}\n"
  },
  {
    "path": "modules/test-runner/src/main/scala/scala/build/testrunner/Logger.scala",
    "content": "package scala.build.testrunner\n\nimport java.io.PrintStream\n\nclass Logger(val verbosity: Int, out: PrintStream) {\n  def error(message: String): Unit = out.println(message)\n\n  def message(message: => String): Unit = if (verbosity >= 0) out.println(message)\n\n  def log(message: => String): Unit = if (verbosity >= 1) out.println(message)\n  def log(message: => String, debugMessage: => String): Unit =\n    if (verbosity >= 2) out.println(debugMessage)\n    else if (verbosity >= 1) out.println(message)\n\n  def debug(message: => String): Unit = if (verbosity >= 2) out.println(message)\n}\n\nobject Logger {\n  def apply(verbosity: Int, out: PrintStream) = new Logger(verbosity, out)\n  def apply(verbosity: Int)                   = new Logger(verbosity, out = System.err)\n}\n"
  },
  {
    "path": "modules/test-runner/src/main/scala/scala/build/testrunner/TestRunner.scala",
    "content": "package scala.build.testrunner\n\nimport sbt.testing.{Logger as SbtTestLogger, *}\n\nimport java.io.{File, PrintStream}\nimport java.nio.file.{Path, Paths}\n\nimport scala.collection.mutable\n\nobject TestRunner {\n\n  def commonTestFrameworks: Seq[String] = Seq(\n    \"munit.Framework\",\n    \"utest.runner.Framework\",\n    \"org.scalacheck.ScalaCheckFramework\",\n    \"zio.test.sbt.ZTestFramework\",\n    \"org.scalatest.tools.Framework\",\n    \"com.novocode.junit.JUnitFramework\",\n    \"org.scalajs.junit.JUnitFramework\",\n    \"weaver.framework.CatsEffect\"\n  )\n\n  def classPath(loader: ClassLoader, logger: Logger): Seq[Path] = {\n    def helper(loader: ClassLoader): LazyList[Path] =\n      if (loader == null) LazyList.empty\n      else {\n        val paths = loader match {\n          case u: java.net.URLClassLoader =>\n            u.getURLs\n              .flatMap {\n                case url if url.getProtocol == \"file\" =>\n                  Seq(Paths.get(url.toURI).toAbsolutePath)\n                case url =>\n                  logger.debug(s\"Skipping non-file URL in classloader: $url\")\n                  Nil\n              }\n              .to(LazyList)\n          case cl if cl.getClass.getName == \"jdk.internal.loader.ClassLoaders$AppClassLoader\" =>\n            // Required with JDK-11\n            sys.props.getOrElse(\"java.class.path\", \"\")\n              .split(File.pathSeparator)\n              .to(LazyList)\n              .map(Paths.get(_))\n          case cl =>\n            logger.log(s\"Unknown classloader type: ${cl.getClass.getName}\")\n            LazyList.empty\n        }\n        paths #::: helper(loader.getParent)\n      }\n    helper(loader).toVector\n  }\n\n  // initially based on https://github.com/com-lihaoyi/mill/blob/e4c838cf9347ec3659d487af2121c9960d5842e8/scalalib/src/TestRunner.scala#L218-L248\n  def runTasks(initialTasks: Seq[Task], out: PrintStream): Seq[Event] = {\n\n    val tasks = new mutable.Queue[Task]\n    tasks ++= initialTasks\n\n    val events = mutable.Buffer.empty[Event]\n\n    val logger: SbtTestLogger =\n      new SbtTestLogger {\n        def error(msg: String): Unit      = out.println(msg)\n        def warn(msg: String): Unit       = out.println(msg)\n        def info(msg: String): Unit       = out.println(msg)\n        def debug(msg: String): Unit      = out.println(msg)\n        def trace(t: Throwable): Unit     = t.printStackTrace(out)\n        def ansiCodesSupported(): Boolean = true\n      }\n\n    val eventHandler: EventHandler = (event: Event) => events.append(event)\n\n    while (tasks.nonEmpty) {\n      val task     = tasks.dequeue()\n      val newTasks = task.execute(eventHandler, Array(logger))\n      tasks ++= newTasks\n    }\n\n    events.toVector\n  }\n\n}\n"
  },
  {
    "path": "project/deps/package.mill",
    "content": "package build.project.deps\n\nimport coursier.version.Version\nimport mill.*\nimport mill.api.BuildInfo\nimport scalalib.*\n\nobject Cli {\n  def runnerScala30LegacyVersion =\n    \"1.7.1\" // last runner version to support pre-LTS Scala 3 versions\n  def runnerScala2LegacyVersion = \"1.9.1\" // last runner version to support pre-Scala-3 versions\n  def scala3GraalLegacyVersion  = \"1.9.1\" // last version to support pre-3.3 processing for Graal\n}\n\nobject Scala {\n  def scala212         = \"2.12.21\"\n  def scala213         = \"2.13.18\"\n  def scala3LtsPrefix  = \"3.3\"                  // used for the LTS version tags\n  def scala3Lts        = s\"$scala3LtsPrefix.7\"  // the LTS version currently used in the build\n  def runnerScala3     = scala3Lts\n  def scala3NextPrefix = \"3.8\"\n  def scala3Next       = s\"$scala3NextPrefix.3\" // the newest/next version of Scala\n  def scala3NextAnnounced   = s\"$scala3NextPrefix.2\"  // the newest/next version of Scala that's been announced\n  def scala3NextRc          = \"3.8.4-RC1\" // the latest RC version of Scala Next\n  def scala3NextRcAnnounced = \"3.8.3-RC3\" // the latest announced RC version of Scala Next\n\n  // The Scala version used to build the CLI itself.\n  def defaultInternal = sys.props.get(\"scala.version.internal\").getOrElse(scala3Lts)\n\n  // The Scala version used by default to compile user input.\n  def defaultUser = sys.props.get(\"scala.version.user\").getOrElse(scala3Next)\n\n  // The latest Scala version supporting scala_legacy runner command\n  def scalaLegacyRunnerVersion = \"3.7.4\"\n\n  // as Scala 3.8 changes a lot of compatibilities,\n  // these are the cutoff versions for the stable, RC and nightly series,\n  // respectively\n  def scala38Versions = Seq(\"3.8.0\", \"3.8.0-RC1\", \"3.8.0-RC1-bin-20250901-ca400bd-NIGHTLY\")\n\n  val allScala2           = Seq(scala213, scala212)\n  val defaults            = Seq(defaultInternal, defaultUser).distinct\n  val allScala3           = Seq(scala3Lts, scala3Next, scala3NextAnnounced, scala3NextRc).distinct\n  val all                 = (allScala2 ++ allScala3 ++ defaults).distinct\n  val scala3MainVersions  = (defaults ++ allScala3).distinct\n  val runnerScalaVersions = (Seq(runnerScala3) ++ scala3MainVersions).distinct\n\n  def scalaJs    = \"1.20.2\"\n  def scalaJsCli = scalaJs // this must be compatible with the Scala.js version\n\n  private def patchVer(sv: String): Int =\n    sv.split('.').drop(2).head.takeWhile(_.isDigit).toInt\n\n  private def minorVer(sv: String): Int =\n    sv.split('.').drop(1).head.takeWhile(_.isDigit).toInt\n\n  def listAll: Seq[String] = {\n    val max212 = patchVer(scala212)\n    val max213 = patchVer(scala213)\n    val max30  = 2\n    val max31  = 3\n    val max32  = 2\n    val max33  = patchVer(scala3Lts)\n    val max34  = 3\n    val max35  = 2\n    val max36  = 4\n    val max37  = 4\n    val max38  = patchVer(scala3Next)\n    (8 until max212).map(i => s\"2.12.$i\") ++ Seq(scala212) ++\n      (0 until max213).map(i => s\"2.13.$i\") ++ Seq(scala213) ++\n      (0 to max30).map(i => s\"3.0.$i\") ++\n      (0 to max31).map(i => s\"3.1.$i\") ++\n      (0 to max32).map(i => s\"3.2.$i\") ++\n      (0 to max33).map(i => s\"3.3.$i\") ++\n      (0 to max34).map(i => s\"3.4.$i\") ++\n      (0 to max35).map(i => s\"3.5.$i\") ++\n      (0 to max36).map(i => s\"3.6.$i\") ++\n      (0 to max37).map(i => s\"3.7.$i\") ++\n      (0 until max38).map(i => s\"3.8.$i\") ++ Seq(scala3Next)\n  }\n\n  def legacyScala3Versions: Seq[String] =\n    listAll\n      .filter(_.startsWith(\"3\"))\n      .distinct\n      .filter(minorVer(_) < minorVer(scala3Lts))\n\n  def maxAmmoniteScala212Version                    = \"2.12.21\"\n  def maxAmmoniteScala213Version                    = \"2.13.18\"\n  def maxAmmoniteScala3Version                      = \"3.8.1\"\n  def maxAmmoniteScala3LtsVersion                   = \"3.3.7\"\n  lazy val listMaxAmmoniteScalaVersion: Seq[String] =\n    Seq(maxAmmoniteScala212Version, maxAmmoniteScala213Version, maxAmmoniteScala3Version)\n}\n\nobject Java {\n  def minimumJavaLauncherJava  = 11\n  def minimumBloopJava: Int    = 17\n  def minimumScala38Java: Int  = 17\n  def minimumInternalJava: Int = 16\n  def defaultJava: Int         = minimumBloopJava\n  def cliKeyJavaVersions       = Seq(\n    minimumBloopJava,\n    minimumInternalJava,\n    defaultJava,\n    minimumJavaLauncherJava\n  ).distinct\n  def mainJavaVersions: Seq[Int] = Seq(8, 11, 17, 21, 23, 24, 25, 26)\n  def allJavaVersions: Seq[Int]  = (mainJavaVersions ++ cliKeyJavaVersions).distinct\n}\n\n// Dependencies used in integration test fixtures\nobject TestDeps {\n  def pprint: Dep = Deps.pprint\n  def munit: Dep  = Deps.munit\n\n  def archLinuxImage: String =\n    \"archlinux@sha256:b15db21228c7cd5fd3ab364a97193ba38abfad0e8b9593c15b71850b74738153\"\n}\n\nobject Deps {\n  object Versions {\n    def ammonite             = \"3.0.8\"\n    def ammoniteForScala3Lts = ammonite\n    def argonautShapeless    = \"1.3.1\"\n    // jni-utils version may need to be sync-ed when bumping the coursier version\n    def coursierDefault                   = \"2.1.25-M24\"\n    def coursier                          = coursierDefault\n    def coursierCli                       = coursierDefault\n    def coursierPublish                   = \"0.4.4\"\n    def jmh                               = \"1.37\"\n    def jsoniterScala                     = \"2.38.8\"\n    def jsoup                             = \"1.22.1\"\n    def scalaMeta                         = \"4.15.2\"\n    def scalafmt                          = \"3.10.7\"\n    def scalaNative04                     = \"0.4.17\"\n    def scalaNative05                     = \"0.5.10\"\n    def scalaNative                       = scalaNative05\n    def maxScalaNativeForToolkit          = scalaNative05\n    def maxScalaNativeForTypelevelToolkit = scalaNative04\n    def maxScalaNativeForScalaPy          = scalaNative04\n    def maxScalaNativeForMillExport       = scalaNative05\n    def scalaPackager                     = \"0.2.1\"\n    def signingCli                        = \"0.2.13\"\n    def signingCliJvmVersion              = Java.defaultJava\n    def javaSemanticdb                    = \"0.10.0\"\n    def javaClassName                     = \"0.1.9\"\n    def bloop                             = \"2.0.19\"\n    def sbtVersion                        = \"1.12.4\"\n    def mill012Version                    = \"0.12.17\"\n    def mill1Version                      = BuildInfo.millVersion\n    def mavenVersion                      = \"3.8.1\"\n    def mavenScalaCompilerPluginVersion   = \"4.9.1\"\n    def mavenExecPluginVersion            = \"3.3.0\"\n    def mavenAppArtifactId                = \"maven-app\"\n    def mavenAppGroupId                   = \"com.example\"\n    def mavenAppVersion                   = \"0.1-SNAPSHOT\"\n    def scalafix                          = \"0.14.5\"\n  }\n\n  // DO NOT hardcode a Scala version in this dependency string\n  // This dependency is used to ensure that Ammonite is available for Scala versions\n  // that Scala CLI supports.\n  def ammonite             = mvn\"com.lihaoyi:::ammonite:${Versions.ammonite}\"\n  def ammoniteForScala3Lts = mvn\"com.lihaoyi:::ammonite:${Versions.ammoniteForScala3Lts}\"\n  def argonautShapeless    =\n    mvn\"com.github.alexarchambault:argonaut-shapeless_6.3_2.13:${Versions.argonautShapeless}\"\n  def asm = mvn\"org.ow2.asm:asm:9.9.1\"\n  // Force using of 2.13 - is there a better way?\n  def bloopConfig = mvn\"ch.epfl.scala:bloop-config_2.13:2.3.3\"\n    .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-core_2.13\"))\n  def bloopRifle       = mvn\"ch.epfl.scala:bloop-rifle_2.13:${Versions.bloop}\"\n  def bsp4j            = mvn\"ch.epfl.scala:bsp4j:2.1.1\"\n  def caseApp          = mvn\"com.github.alexarchambault::case-app:2.1.0\"\n  def collectionCompat = mvn\"org.scala-lang.modules::scala-collection-compat:2.14.0\"\n  // Force using of 2.13 - is there a better way?\n  def coursier             = mvn\"io.get-coursier:coursier_2.13:${Versions.coursier}\"\n  def coursierArchiveCache = mvn\"io.get-coursier:coursier-archive-cache_2.13:${Versions.coursier}\"\n    .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"*\"))\n  def coursierCli = mvn\"io.get-coursier::coursier-cli:${Versions.coursierCli}\"\n  def coursierJvm = mvn\"io.get-coursier:coursier-jvm_2.13:${Versions.coursier}\"\n    .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-core_2.13\"))\n    .exclude(\"io.get-coursier\" -> \"dependency_2.13\")\n  def coursierLauncher = mvn\"io.get-coursier:coursier-launcher_2.13:${Versions.coursier}\"\n    .exclude((\"ai.kien\", \"python-native-libs_2.13\"))\n    .exclude((\"org.scala-lang.modules\", \"scala-collection-compat_2.13\"))\n  def coursierProxySetup = mvn\"io.get-coursier:coursier-proxy-setup:${Versions.coursier}\"\n  def coursierPublish    = mvn\"io.get-coursier.publish::publish:${Versions.coursierPublish}\"\n    .exclude((\"org.scala-lang.modules\", \"scala-collection-compat_2.13\"))\n    .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-core_2.13\"))\n  def dependency    = mvn\"io.get-coursier::dependency:0.3.2\"\n  def dockerClient  = mvn\"com.spotify:docker-client:8.16.0\"\n  def expecty       = mvn\"com.eed3si9n.expecty::expecty:0.17.1\"\n  def fansi         = mvn\"com.lihaoyi::fansi:0.5.1\"\n  def giter8        = mvn\"org.foundweekends.giter8:giter8:0.18.0\"\n  def guava         = mvn\"com.google.guava:guava:33.5.0-jre\"\n  def javaClassName =\n    mvn\"org.virtuslab.scala-cli.java-class-name:java-class-name_3:${Versions.javaClassName}\"\n      .exclude(\n        \"org.jline\" -> \"jline-reader\",\n        \"org.jline\" -> \"jline-terminal\",\n        \"org.jline\" -> \"jline-terminal-jna\"\n      )\n  def jgit                 = mvn\"org.eclipse.jgit:org.eclipse.jgit:7.5.0.202512021534-r\"\n  def jimfs                = mvn\"com.google.jimfs:jimfs:1.3.1\"\n  def jmhGeneratorBytecode = mvn\"org.openjdk.jmh:jmh-generator-bytecode:${Versions.jmh}\"\n  def jmhCore              = mvn\"org.openjdk.jmh:jmh-core:${Versions.jmh}\"\n  def jniUtils             = mvn\"io.get-coursier.jniutils:windows-jni-utils:0.3.3\"\n  def jsoniterCore         =\n    mvn\"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${Versions.jsoniterScala}\"\n  def jsoniterMacros =\n    mvn\"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${Versions.jsoniterScala}\"\n  def jsoup              = mvn\"org.jsoup:jsoup:${Versions.jsoup}\"\n  def libsodiumjni       = mvn\"org.virtuslab.scala-cli:libsodiumjni:0.0.4\"\n  def metaconfigTypesafe =\n    mvn\"org.scalameta::metaconfig-typesafe-config:0.18.2\"\n      .exclude((\"org.scala-lang\", \"scala-compiler\"))\n  def munit              = mvn\"org.scalameta::munit:1.2.2\"\n  def nativeTestRunner   = mvn\"org.scala-native::test-runner:${Versions.scalaNative}\"\n  def nativeTools        = mvn\"org.scala-native::tools:${Versions.scalaNative}\"\n  def osLib              = mvn\"com.lihaoyi::os-lib:0.11.8\"\n  def pprint             = mvn\"com.lihaoyi::pprint:0.9.5\"\n  def pythonInterface    = mvn\"io.github.alexarchambault.python:interface:0.1.0\"\n  def pythonNativeLibs   = mvn\"ai.kien::python-native-libs:0.2.5\"\n  def scalac(sv: String) = mvn\"org.scala-lang:scala-compiler:$sv\"\n  def scala3Graal = mvn\"org.virtuslab.scala-cli::scala3-graal:${Cli.scala3GraalLegacyVersion}\"\n  def scala3GraalProcessor =\n    mvn\"org.virtuslab.scala-cli::scala3-graal-processor:${Cli.scala3GraalLegacyVersion}\"\n  def scalafmtCli = mvn\"org.scalameta:scalafmt-cli_2.13:${Versions.scalafmt}\"\n  // Force using of 2.13 - is there a better way?\n  def scalaJsEnvJsdomNodejs =\n    mvn\"org.scala-js:scalajs-env-jsdom-nodejs_2.13:1.1.1\"\n      .exclude(\"org.scala-js\" -> \"scalajs-env-nodejs_2.13\")\n      .exclude(\"org.scala-js\" -> \"scalajs-js-envs_2.13\")\n  // Force using of 2.13 - is there a better way?\n  def scalaJsEnvNodeJs = mvn\"org.scala-js::scalajs-env-nodejs:1.5.0\"\n  def scalaJsLogging   = mvn\"org.scala-js::scalajs-logging:1.2.0\"\n  // Force using of 2.13 - is there a better way?\n  def scalaJsTestAdapter = mvn\"org.scala-js:scalajs-sbt-test-adapter_2.13:${Scala.scalaJs}\"\n    .exclude(\"org.scala-js\" -> \"scalajs-js-envs_2.13\")\n  def scalaPackager             = mvn\"org.virtuslab::scala-packager:${Versions.scalaPackager}\"\n  def scalaPackagerCli          = mvn\"org.virtuslab::scala-packager-cli:${Versions.scalaPackager}\"\n  def scalaPy                   = mvn\"dev.scalapy::scalapy-core::0.5.3\"\n  def scalaReflect(sv: String)  = mvn\"org.scala-lang:scala-reflect:$sv\"\n  def semanticDbJavac           = mvn\"com.sourcegraph:semanticdb-javac:${Versions.javaSemanticdb}\"\n  def semanticDbScalac          = mvn\"org.scalameta:::semanticdb-scalac:${Versions.scalaMeta}\"\n  def scalametaSemanticDbShared =\n    mvn\"org.scalameta:semanticdb-shared_2.13:${Versions.scalaMeta}\"\n      .exclude(\"org.jline\" -> \"jline\") // to prevent incompatibilities with GraalVM <23\n      .exclude(\"com.lihaoyi\" -> \"sourcecode_2.13\")\n      .exclude(\"org.scala-lang.modules\" -> \"scala-collection-compat_2.13\")\n  def signingCliShared =\n    mvn\"org.virtuslab.scala-cli-signing::shared:${Versions.signingCli}\"\n      // to prevent collisions with scala-cli's case-app version\n      .exclude((\"com.github.alexarchambault\", \"case-app_3\"))\n      .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-core_3\"))\n      .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-macros_3\"))\n      .exclude((\"com.lihaoyi\", \"os-lib_3\"))\n      .exclude((\"com.lihaoyi\", \"os-lib_2.13\"))\n      .exclude((\"org.virtuslab.scala-cli\", \"config_3\"))\n  def signingCli =\n    mvn\"org.virtuslab.scala-cli-signing::cli:${Versions.signingCli}\"\n      // to prevent collisions with scala-cli's case-app version\n      .exclude((\"com.github.alexarchambault\", \"case-app_3\"))\n      .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-core_3\"))\n      .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-macros_3\"))\n      .exclude((\"com.github.plokhotnyuk.jsoniter-scala\", \"jsoniter-scala-core_2.13\"))\n      .exclude((\"io.get-coursier.publish\", \"publish_2.13\"))\n      .exclude((\"org.scala-lang.modules\", \"scala-collection-compat_2.13\"))\n      .exclude((\"com.lihaoyi\", \"os-lib_3\"))\n      .exclude((\"com.lihaoyi\", \"os-lib_2.13\"))\n  def slf4jNop                  = mvn\"org.slf4j:slf4j-nop:2.0.17\"\n  def sttp                      = mvn\"com.softwaremill.sttp.client3::core:3.11.0\"\n  def svm                       = mvn\"org.graalvm.nativeimage:svm:$graalSvmVersion\"\n  def swoval                    = mvn\"com.swoval:file-tree-views:2.1.12\"\n  def testInterface             = mvn\"org.scala-sbt:test-interface:1.0\"\n  val toolkitVersion            = \"0.8.0\"\n  val toolkitVersionForNative04 = \"0.3.0\"\n  val toolkitVersionForNative05 = toolkitVersion\n  def toolkit                   = mvn\"org.scala-lang:toolkit:$toolkitVersion\"\n  def toolkitTest               = mvn\"org.scala-lang:toolkit-test:$toolkitVersion\"\n  val typelevelToolkitVersion   = \"0.1.29\"\n  def typelevelToolkit          = mvn\"org.typelevel:toolkit:$typelevelToolkitVersion\"\n  def usingDirectives           = mvn\"org.virtuslab:using_directives:1.1.4\"\n  // Lives at https://github.com/VirtusLab/no-crc32-zip-input-stream, see #865\n  // This provides a ZipInputStream that doesn't verify CRC32 checksums, that users\n  // can enable by setting SCALA_CLI_VENDORED_ZIS=true in the environment, to workaround\n  // some bad GraalVM / zlib issues (see #828 and linked issues for more details).\n  def zipInputStream     = mvn\"org.virtuslab.scala-cli.zip-input-stream:zip-input-stream:0.1.3\"\n  def scalafixInterfaces = mvn\"ch.epfl.scala:scalafix-interfaces:${Versions.scalafix}\"\n}\n\ndef graalVmJavaVersion      = Java.defaultJava\ndef graalVmCommunityVersion = s\"$graalVmJavaVersion.0.9\"\ndef graalSvmVersion         = \"22.3.1\"\ndef graalVmJvmId            = s\"graalvm-community:$graalVmCommunityVersion\"\n\ndef csDockerVersion = Deps.Versions.coursierCli\n\ndef buildCsVersion = Deps.Versions.coursierCli\n\n// Native library used to encrypt GitHub secrets\ndef libsodiumVersion       = \"1.0.21\"\ndef alpineLibsodiumVersion = \"1.0.20\"\ndef condaLibsodiumVersion  = \"1.0.18\" // used in integration tests\n// Using the libsodium static library from this Alpine version (in the static launcher)\ndef alpineVersion = \"3.23\"\ndef ubuntuVersion = \"24.04\"\n\nobject Docker {\n  def customMuslBuilderImageName = \"scala-cli-base-musl\"\n  def muslBuilder                =\n    s\"$customMuslBuilderImageName:latest\"\n\n  def testImage          = s\"ubuntu:$ubuntuVersion\"\n  def alpineTestImage    = s\"alpine:$alpineVersion\"\n  def authProxyTestImage =\n    \"bahamat/authenticated-proxy@sha256:568c759ac687f93d606866fbb397f39fe1350187b95e648376b971e9d7596e75\"\n}\n\ndef customRepositories =\n  Seq(\n    coursier.Repositories.sonatype(\"snapshots\"),\n    coursier.MavenRepository(\"https://s01.oss.sonatype.org/content/repositories/snapshots\"),\n    coursier.MavenRepository(\"https://central.sonatype.com/repository/maven-snapshots\")\n    // Uncomment for local development\n    // coursier.LocalRepositories.Dangerous.maven2Local\n  )\n"
  },
  {
    "path": "project/musl-image/Dockerfile",
    "content": "FROM messense/rust-musl-cross@sha256:47a3721b3e186abfd705feb1e03bf1d5212357ea26762cceef11530e0a2f2c7c\nADD setup.sh /setup.sh\nRUN /setup.sh\n"
  },
  {
    "path": "project/musl-image/setup.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\ncd /usr/local/musl/bin\n\nfor i in x86_64-unknown-linux-musl-*; do\n  dest=\"$(echo \"$i\" | sed 's/-unknown//')\"\n  ln -s \"$i\" \"$dest\"\ndone\n"
  },
  {
    "path": "project/package.mill",
    "content": "package build.project\n"
  },
  {
    "path": "project/publish/package.mill",
    "content": "package build.project.publish\n\nimport build.project.settings\nimport mill.javalib.publish.{PublishingType, SonatypeCredentials}\nimport settings.{PublishLocalNoFluff, workspaceDirName}\nimport mill.*\nimport mill.javalib.publish.Artifact\nimport mill.util.{Tasks, VcsVersion}\nimport scalalib.*\nimport org.eclipse.jgit.api.Git\nimport mill.api.{BuildCtx, ModuleCtx, Task}\n\nimport java.nio.charset.Charset\nimport scala.concurrent.duration.*\nimport scala.jdk.CollectionConverters.*\n\ndef gh: (ghOrg: String, ghName: String) = {\n  def default = (\"VirtusLab\", \"scala-cli\")\n  val isCI    = System.getenv(\"CI\") != null\n  if (isCI) {\n    val repos = {\n      var git: Git = null\n      try {\n        git = Git.open(os.Path(sys.env(\"MILL_WORKSPACE_ROOT\")).toIO)\n        git.remoteList().call().asScala.toVector\n      }\n      finally\n        if (git != null)\n          git.close()\n    }\n    val reposByName = repos.flatMap(r =>\n      r.getURIs.asScala.headOption.map(_.toASCIIString).toSeq.map((r.getName, _))\n    )\n    val repoUrlOpt =\n      if (reposByName.length == 1)\n        reposByName.headOption.map(_._2)\n      else {\n        val m = reposByName.toMap\n        m.get(\"origin\").orElse(m.get(\"upstream\"))\n      }\n    repoUrlOpt match {\n      case Some(url) if url.startsWith(\"https://github.com/\") =>\n        url.stripPrefix(\"https://github.com/\").stripSuffix(\".git\").split(\"/\", 2) match {\n          case Array(org, name) => (org, name)\n          case Array(_)         =>\n            System.err.println(\n              s\"Warning: git remote URL $url doesn't look like a GitHub URL, using default GitHub org and name\"\n            )\n            default\n        }\n      case Some(url) =>\n        System.err.println(\n          s\"Warning: git remote URL $url doesn't look like a GitHub URL, using default GitHub org and name\"\n        )\n        default\n      case _ =>\n        System.err.println(\"Warning: no git remote found, using default GitHub org and name\")\n        default\n    }\n  }\n  else\n    default\n}\n\nlazy val (ghOrg: String, ghName: String) = gh\n\nprivate def computePublishVersion(state: VcsVersion.State, simple: Boolean): String =\n  if (state.commitsSinceLastTag > 0)\n    if (simple) {\n      val versionOrEmpty = state.lastTag\n        .filter(_ != \"latest\")\n        .filter(_ != \"nightly\")\n        .map(_.stripPrefix(\"v\"))\n        .flatMap { tag =>\n          if (simple) {\n            val idx = tag.lastIndexOf(\".\")\n            if (idx >= 0)\n              Some(\n                tag.take(idx + 1) +\n                  (tag.drop(idx + 1).replaceAll(\"-RC.\", \"\").toInt + 1).toString +\n                  \"-SNAPSHOT\"\n              )\n            else\n              None\n          }\n          else {\n            val idx = tag.indexOf(\"-\")\n            if (idx >= 0) Some(tag.take(idx) + \"+\" + tag.drop(idx + 1) + \"-SNAPSHOT\")\n            else None\n          }\n        }\n        .getOrElse(\"0.0.1-SNAPSHOT\")\n      Some(versionOrEmpty)\n        .filter(_.nonEmpty)\n        .getOrElse(state.format())\n    }\n    else {\n      val rawVersion = os.proc(\"git\", \"describe\", \"--tags\").call().out.trim()\n        .stripPrefix(\"v\")\n        .replace(\"latest\", \"0.0.0\")\n        .replace(\"nightly\", \"0.0.0\")\n      val idx = rawVersion.indexOf(\"-\")\n      if (idx >= 0) rawVersion.take(idx) + \"-\" + rawVersion.drop(idx + 1) + \"-SNAPSHOT\"\n      else rawVersion\n    }\n  else\n    state\n      .lastTag\n      .getOrElse(state.format())\n      .stripPrefix(\"v\")\n\ndef finalPublishVersion: T[String] = {\n  val isCI = System.getenv(\"CI\") != null\n  if (isCI)\n    Task(persistent = true) {\n      val state = VcsVersion.vcsState()\n      computePublishVersion(state, simple = false)\n    }\n  else\n    Task {\n      val state = VcsVersion.vcsState()\n      computePublishVersion(state, simple = true)\n    }\n}\n\ndef organization = \"org.virtuslab.scala-cli\"\n\ntrait ScalaCliPublishModule extends SonatypeCentralPublishModule with PublishLocalNoFluff {\n  import mill.scalalib.publish._\n  override def pomSettings: T[PomSettings] = PomSettings(\n    description = artifactName(),\n    organization = organization,\n    url = s\"https://github.com/$ghOrg/$ghName\",\n    licenses = Seq(License.`Apache-2.0`),\n    versionControl = VersionControl.github(ghOrg, ghName),\n    developers = Seq(\n      Developer(\"alexarchambault\", \"Alex Archambault\", \"https://github.com/alexarchambault\"),\n      Developer(\"lwronski\", \"Łukasz Wroński\", \"https://github.com/lwronski\"),\n      Developer(\"romanowski\", \"Krzysztof Romanowski\", \"https://github.com/romanowski\"),\n      Developer(\"Gedochao\", \"Piotr Chabelski\", \"https://github.com/Gedochao\"),\n      Developer(\"MaciejG604\", \"Maciej Gajek\", \"https://github.com/MaciejG604\")\n    )\n  )\n  override def publishVersion: T[String] = finalPublishVersion()\n\n  override def sourceJar: T[PathRef] = Task {\n    PathRef {\n      import mill.util.Jvm.createJar\n      val allSources0 = allSources().map(_.path).filter(os.exists)\n      createJar(\n        jar = Task.dest / \"out.jar\",\n        inputPaths = allSources0 ++ resources().map(_.path).filter(os.exists),\n        manifest = manifest(),\n        fileFilter = (input, relPath) =>\n          !allSources0.toSet(input) ||\n          (!relPath.segments.contains(\".scala\") && !relPath.segments.contains(workspaceDirName))\n      )\n    }\n  }\n}\n\ndef publishSonatype(\n  data: Seq[PublishModule.PublishData],\n  log: mill.api.Logger,\n  workspace: os.Path,\n  env: Map[String, String],\n  bundleName: String\n): Unit = {\n  val credentials = SonatypeCredentials(\n    username = sys.env(\"SONATYPE_USERNAME\"),\n    password = sys.env(\"SONATYPE_PASSWORD\")\n  )\n  val pgpPassword = sys.env(\"PGP_PASSWORD\")\n  val timeout     = 10.minutes\n\n  System.err.println(\"Actual artifacts included in the bundle:\")\n  val artifacts: Seq[(Seq[(os.Path, String)], Artifact)] = data.map {\n    case PublishModule.PublishData(a, s) =>\n      System.err.println(s\"  ${a.group}:${a.id}:${a.version}\")\n      (s.map { case (p, f) => (p.path, f) }, a)\n  }\n\n  val isRelease: Boolean = {\n    val versions: Set[String] = artifacts.map(_._2.version).toSet\n    val set: Set[Boolean]     = versions.map(!_.endsWith(\"-SNAPSHOT\"))\n    assert(\n      set.size == 1,\n      s\"Found both snapshot and non-snapshot versions: ${versions.toVector.sorted.mkString(\", \")}\"\n    )\n    set.head\n  }\n  System.err.println(s\"Is release: $isRelease\")\n  val publisher = new SonatypeCentralPublisher(\n    credentials = credentials,\n    gpgArgs = Seq(\n      \"--detach-sign\",\n      \"--batch=true\",\n      \"--yes\",\n      \"--pinentry-mode\",\n      \"loopback\",\n      \"--passphrase\",\n      pgpPassword,\n      \"--armor\",\n      \"--use-agent\"\n    ),\n    readTimeout = timeout.toMillis.toInt,\n    connectTimeout = timeout.toMillis.toInt,\n    log = log,\n    workspace = workspace,\n    env = env,\n    awaitTimeout = timeout.toMillis.toInt\n  )\n  val publishingType = if (isRelease) PublishingType.AUTOMATIC else PublishingType.USER_MANAGED\n  System.err.println(s\"Publishing type: $publishingType\")\n  val finalBundleName = if (bundleName.nonEmpty) Some(bundleName) else None\n  System.err.println(s\"Final bundle name: $finalBundleName\")\n  publisher.publishAll(\n    publishingType = publishingType,\n    singleBundleName = finalBundleName,\n    artifacts = artifacts*\n  )\n}\n\n// from https://github.com/sbt/sbt-ci-release/blob/35b3d02cc6c247e1bb6c10dd992634aa8b3fe71f/plugin/src/main/scala/com/geirsson/CiReleasePlugin.scala#L33-L39\ndef isTag: Boolean =\n  Option(System.getenv(\"TRAVIS_TAG\")).exists(_.nonEmpty) ||\n  Option(System.getenv(\"CIRCLE_TAG\")).exists(_.nonEmpty) ||\n  Option(System.getenv(\"CI_COMMIT_TAG\")).exists(_.nonEmpty) ||\n  Option(System.getenv(\"BUILD_SOURCEBRANCH\"))\n    .orElse(Option(System.getenv(\"GITHUB_REF\")))\n    .exists(_.startsWith(\"refs/tags\"))\ndef shouldPublish = Task(persistent = true) {\n  val isSnapshot = finalPublishVersion().endsWith(\"SNAPSHOT\")\n  // not a snapshot, not a tag: let the tag job do the publishing\n  isSnapshot || isTag\n}\ndef setShouldPublish() = Task.Command {\n  val envFile = System.getenv(\"GITHUB_ENV\")\n  if (envFile == null)\n    sys.error(\"GITHUB_ENV not set\")\n  val charSet = Charset.defaultCharset()\n  val nl      = System.lineSeparator()\n  os.write.append(\n    os.Path(envFile, BuildCtx.workspaceRoot),\n    s\"SHOULD_PUBLISH=${shouldPublish()}$nl\".getBytes(charSet)\n  )\n}\n"
  },
  {
    "path": "project/settings/package.mill",
    "content": "package build.project.settings\n\nimport build.project.deps\nimport deps.{\n  Deps,\n  Docker,\n  alpineVersion,\n  alpineLibsodiumVersion,\n  buildCsVersion,\n  libsodiumVersion,\n  ubuntuVersion\n}\nimport build.project.utils\nimport utils.isArmArchitecture\nimport com.goyeau.mill.scalafix.ScalafixModule\nimport coursier.Repository\nimport io.github.alexarchambault.millnativeimage.NativeImage\nimport mill.*\nimport mill.scalalib.*\nimport upickle.default.*\n\nimport java.io.File\nimport java.nio.charset.StandardCharsets\nimport java.util.Locale\nimport scala.annotation.unused\nimport scala.util.Properties\nimport mill.util.{Tasks, VcsVersion}\nimport mill.api.{BuildCtx, Task}\n\ndef isCI = System.getenv(\"CI\") != null\n\ndef fromPath(name: String): String =\n  if (Properties.isWin) {\n    val pathExt = Option(System.getenv(\"PATHEXT\"))\n      .toSeq\n      .flatMap(_.split(File.pathSeparator).toSeq)\n    val path = Seq(new File(\"\").getAbsoluteFile) ++\n      Option(System.getenv(\"PATH\"))\n        .toSeq\n        .flatMap(_.split(File.pathSeparator))\n        .map(new File(_))\n\n    def candidates: Iterator[File] =\n      for {\n        dir <- path.iterator\n        ext <- pathExt.iterator\n      } yield new File(dir, name + ext)\n\n    candidates\n      .filter(_.canExecute)\n      .to(LazyList)\n      .headOption\n      .map(_.getAbsolutePath)\n      .getOrElse {\n        System.err.println(s\"Warning: could not find $name in PATH.\")\n        name\n      }\n  }\n  else\n    name\n\ndef cs: T[String] = Task(persistent = true) {\n  val arch      = sys.props.getOrElse(\"os.arch\", \"\").toLowerCase(Locale.ROOT)\n  val ext       = if (Properties.isWin) \".exe\" else \"\"\n  val csVersion = buildCsVersion\n  val dest      = Task.dest / s\"cs-$csVersion$ext\"\n\n  def downloadOpt(): Option[String] = {\n    val urlOpt = arch match {\n      case \"x86_64\" | \"amd64\" =>\n        if (Properties.isWin)\n          Some(\n            s\"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-x86_64-pc-win32.zip\"\n          )\n        else if (Properties.isMac)\n          Some(\n            s\"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-x86_64-apple-darwin.gz\"\n          )\n        else if (Properties.isLinux)\n          Some(\n            s\"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-x86_64-pc-linux.gz\"\n          )\n        else None\n      case \"aarch64\" =>\n        if (Properties.isLinux)\n          Some(\n            s\"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-aarch64-pc-linux.gz\"\n          )\n        else if (Properties.isMac)\n          Some(\n            s\"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-aarch64-apple-darwin.gz\"\n          )\n        else None\n      case _ =>\n        None\n    }\n\n    urlOpt.map { url =>\n      val cache        = coursier.cache.FileCache()\n      val archiveCache = coursier.cache.ArchiveCache().withCache(cache)\n      val task         = cache.logger.using(archiveCache.get(coursier.util.Artifact(url)))\n      val maybeFile    =\n        try task.unsafeRun()(using cache.ec)\n        catch {\n          case t: Throwable =>\n            throw new Exception(s\"Error getting and extracting $url\", t)\n        }\n      val f    = maybeFile.fold(ex => throw new Exception(ex), os.Path(_, BuildCtx.workspaceRoot))\n      val exec =\n        if (Properties.isWin && os.isDir(f) && f.last.endsWith(\".zip\"))\n          os.list(f).find(_.last.endsWith(\".exe\")).getOrElse(\n            sys.error(s\"No .exe found under $f\")\n          )\n        else\n          f\n\n      if (!Properties.isWin)\n        exec.toIO.setExecutable(true)\n\n      exec.toString\n    }\n  }\n\n  if (os.isFile(dest)) dest.toString\n  else downloadOpt().getOrElse(fromPath(\"cs\")): String\n}\n\ndef platformExecutableJarExtension: String = if (Properties.isWin) \".bat\" else \"\"\n\nlazy val arch = sys.props(\"os.arch\").toLowerCase(java.util.Locale.ROOT) match {\n  case \"amd64\" => \"x86_64\"\n  case other   => other\n}\ndef platformSuffix: String = {\n  val os =\n    if (Properties.isWin) \"pc-win32\"\n    else if (Properties.isLinux) \"pc-linux\"\n    else if (Properties.isMac) \"apple-darwin\"\n    else sys.error(s\"Unrecognized OS: ${sys.props(\"os.name\")}\")\n  s\"$arch-$os\"\n}\n\ndef localRepoResourcePath = \"local-repo.zip\"\n\ntrait LocatedInModules extends Module {\n  override def moduleDir: os.Path = {\n    val oldModuleDir: os.Path = super.moduleDir\n    oldModuleDir / os.up / \"modules\" / oldModuleDir.last\n  }\n}\n\ntrait CliLaunchers extends SbtModule { self =>\n\n  def launcherTypeResourcePath: os.RelPath =\n    os.rel / \"scala\" / \"cli\" / \"internal\" / \"launcher-type.txt\"\n  def defaultFilesResourcePath: os.RelPath = os.rel / \"scala\" / \"cli\" / \"commands\" / \"publish\"\n\n  trait CliNativeImage extends NativeImage {\n    override def generateNativeImageWithFileSystemChecker: Boolean = false\n\n    def writeDefaultNativeImageScript(scriptDest: String): Command[Unit] =\n      Task.Command(super.writeNativeImageScript(scriptDest, \"\")())\n\n    def launcherKind: String\n    override def nativeImageCsCommand: T[Seq[String]] = Seq(cs())\n    override def nativeImagePersist: Boolean          = System.getenv(\"CI\") != null\n    override def nativeImageGraalVmJvmId: T[String]   = deps.graalVmJvmId\n    override def nativeImageOptions: T[Seq[String]]   = Task {\n      val usesDocker = nativeImageDockerParams().nonEmpty\n      val cLibPath   =\n        if (usesDocker) s\"/data/$staticLibDirName\"\n        else staticLibDir().path.toString\n      super.nativeImageOptions() ++ Seq(\n        \"--no-fallback\",\n        \"-march=compatibility\",\n        s\"-H:IncludeResources=$localRepoResourcePath\",\n        s\"-H:IncludeResources=$launcherTypeResourcePath\",\n        s\"-H:IncludeResources=$defaultFilesResourcePath/.*\",\n        \"-H:-ParseRuntimeOptions\",\n        s\"-H:CLibraryPath=$cLibPath\"\n      ) ++\n        (if (Properties.isLinux && isArmArchitecture) // https://stackoverflow.com/a/61325264\n           Seq(\"-Djdk.lang.Process.launchMechanism=vfork\", \"-H:PageSize=65536\")\n         else Nil)\n    }\n    override def nativeImageName: T[String]            = \"scala-cli\"\n    override def nativeImageClassPath: T[Seq[PathRef]] = Task {\n      val launcherKindResourceDir = Task.dest / \"resources\"\n      os.write(\n        launcherKindResourceDir / launcherTypeResourcePath,\n        launcherKind,\n        createFolders = true\n      )\n      PathRef(launcherKindResourceDir) +: self.nativeImageClassPath()\n    }\n    override def nativeImageMainClass: T[String] = self.nativeImageMainClass()\n\n    private def staticLibDirName = \"native-libs\"\n\n    private def copyCsjniutilTo(cs: String, destDir: os.Path, workspace: os.Path): Unit = {\n      val jniUtilsVersion = Deps.jniUtils.dep.versionConstraint.asString\n      val libRes          = os.proc(\n        cs,\n        \"fetch\",\n        \"--intransitive\",\n        s\"io.get-coursier.jniutils:windows-jni-utils:$jniUtilsVersion,classifier=x86_64-pc-win32,ext=lib,type=lib\",\n        \"-A\",\n        \"lib\"\n      ).call()\n      val libPath = os.Path(libRes.out.trim(), workspace)\n      os.copy.over(libPath, destDir / \"csjniutils.lib\")\n    }\n    private def copyLibsodiumjniTo(cs: String, destDir: os.Path, workspace: os.Path): Unit = {\n      val libsodiumjniVersion = Deps.libsodiumjni.dep.versionConstraint.asString\n      val (classifier, ext)   = sys.props.get(\"os.arch\") match {\n        case Some(\"x86_64\" | \"amd64\") =>\n          if (Properties.isWin) (\"x86_64-pc-win32\", \"lib\")\n          else if (Properties.isLinux) (\"x86_64-pc-linux\", \"a\")\n          else if (Properties.isMac && isArmArchitecture) (\"aarch64-apple-darwin\", \"a\")\n          else if (Properties.isMac) (\"x86_64-apple-darwin\", \"a\")\n          else sys.error(s\"Unsupported OS for x86_64 platform: ${sys.props(\"os.name\")}\")\n        case Some(\"aarch64\") =>\n          if (Properties.isLinux) (\"aarch64-pc-linux\", \"a\")\n          else sys.error(s\"Unsupported OS for aarch64 platform: ${sys.props(\"os.name\")}\")\n        case Some(arch) =>\n          sys.error(s\"Unsupported architecture: $arch\")\n        case None =>\n          sys.error(\"Cannot determine CPU architecture\")\n      }\n      val libRes = os.proc(\n        cs,\n        \"fetch\",\n        \"--intransitive\",\n        s\"org.virtuslab.scala-cli:libsodiumjni:$libsodiumjniVersion,classifier=$classifier,ext=$ext,type=$ext\",\n        \"-A\",\n        ext\n      ).call()\n      val libPath = os.Path(libRes.out.trim(), workspace)\n      val prefix  =\n        if (Properties.isWin) \"\"\n        else \"lib\"\n      os.copy.over(libPath, destDir / s\"${prefix}sodiumjni.$ext\")\n    }\n    private def copyLibsodiumStaticTo(cs: String, destDir: os.Path, workspace: os.Path): Unit = {\n      val dirRes = os.proc(\n        cs,\n        \"get\",\n        \"--archive\",\n        s\"https://download.libsodium.org/libsodium/releases/libsodium-$libsodiumVersion-stable-msvc.zip\"\n      ).call()\n      val dir = os.Path(dirRes.out.trim(), workspace)\n      os.copy.over(\n        dir / \"libsodium\" / \"x64\" / \"Release\" / \"v143\" / \"static\" / \"libsodium.lib\",\n        destDir / \"sodium.lib\"\n      )\n    }\n    private def copyAlpineLibsodiumTo(\n      cs: String,\n      destDir: os.Path,\n      workspace: os.Path\n    ): Unit = {\n      val libsodiumAlpineAddress =\n        s\"https://dl-cdn.alpinelinux.org/alpine/v$alpineVersion/main/x86_64/libsodium-static-$alpineLibsodiumVersion-r1.apk\"\n      System.err.println(\n        s\"Sourcing libsodium from alpine $alpineVersion apk... ($libsodiumAlpineAddress)\"\n      )\n      val arcPath = os.proc(cs, \"get\", libsodiumAlpineAddress).call().out.trim()\n      System.err.println(s\"Downloaded alpine libsodium apk to $arcPath\")\n      val tmpDir = os.temp.dir(prefix = \"libsodium-static\")\n      System.err.println(s\"Extracting libsodium static lib to $tmpDir...\")\n      try {\n        os.proc(\"tar\", \"-zxf\", os.Path(arcPath, workspace))\n          .call(cwd = tmpDir, stdout = os.Inherit)\n        System.err.println(s\"Copying libsodium.a to $destDir...\")\n        os.copy.over(tmpDir / \"usr\" / \"lib\" / \"libsodium.a\", destDir / \"libsodium.a\")\n        System.err.println(s\"Copied libsodium.a to $destDir\")\n      }\n      finally\n        os.remove.all(tmpDir)\n    }\n    def staticLibDir: T[PathRef] = Task {\n      BuildCtx.withFilesystemCheckerDisabled {\n        val dir = nativeImageDockerWorkingDir() / staticLibDirName\n        os.makeDir.all(dir)\n\n        if (Properties.isWin) {\n          copyLibsodiumStaticTo(cs(), dir, BuildCtx.workspaceRoot)\n          copyLibsodiumjniTo(cs(), dir, BuildCtx.workspaceRoot)\n          copyCsjniutilTo(cs(), dir, BuildCtx.workspaceRoot)\n        }\n\n        if (launcherKind == \"static\") {\n          copyAlpineLibsodiumTo(cs(), dir, BuildCtx.workspaceRoot)\n          copyLibsodiumjniTo(cs(), dir, BuildCtx.workspaceRoot)\n        }\n\n        PathRef(dir)\n      }\n    }\n  }\n\n  object `base-image` extends CliNativeImage {\n    override def launcherKind = \"default\"\n  }\n\n  private def maybePassNativeImageJpmsOption =\n    Option(System.getenv(\"USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM\"))\n      .fold(\"\") { value =>\n        \"export USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM=\" + value + System.lineSeparator()\n      }\n\n  object `linux-docker-image` extends CliNativeImage {\n    override def launcherKind: String = `base-image`.launcherKind\n    override def nativeImageDockerParams: T[Option[NativeImage.DockerParams]] = Some(\n      NativeImage.DockerParams(\n        imageName = s\"ubuntu:$ubuntuVersion\",\n        prepareCommand =\n          maybePassNativeImageJpmsOption +\n            \"\"\"apt-get update -q -y &&\\\n              |apt-get install -q -y build-essential libz-dev locales\n              |locale-gen en_US.UTF-8\n              |export LANG=en_US.UTF-8\n              |export LANGUAGE=en_US:en\n              |export LC_ALL=en_US.UTF-8\"\"\".stripMargin,\n        csUrl =\n          s\"https://github.com/coursier/coursier/releases/download/v${deps.csDockerVersion}/cs-x86_64-pc-linux.gz\",\n        extraNativeImageArgs = Nil\n      )\n    )\n  }\n\n  private def setupLocaleAndOptions(params: NativeImage.DockerParams): NativeImage.DockerParams =\n    params.copy(\n      prepareCommand = maybePassNativeImageJpmsOption +\n        params.prepareCommand +\n        \"\"\"\n          |set -v\n          |apt-get update\n          |apt-get install -q -y locales\n          |locale-gen en_US.UTF-8\n          |export LANG=en_US.UTF-8\n          |export LANGUAGE=en_US:en\n          |export LC_ALL=en_US.UTF-8\"\"\".stripMargin\n    )\n\n  object `static-image` extends CliNativeImage {\n    override def launcherKind                       = \"static\"\n    override def nativeImageOptions: T[Seq[String]] = Task {\n      super.nativeImageOptions() ++ Seq(\n        \"-J-Dscala-cli.static-launcher=true\"\n      )\n    }\n    override def nativeImageDockerParams: T[Option[NativeImage.DockerParams]] = Task {\n      val baseDockerParams = NativeImage.linuxStaticParams(\n        Docker.muslBuilder,\n        s\"https://github.com/coursier/coursier/releases/download/v${deps.csDockerVersion}/cs-x86_64-pc-linux.gz\"\n      )\n      val dockerParams = setupLocaleAndOptions(baseDockerParams)\n      buildHelperImage()\n      Some(dockerParams)\n    }\n    def buildHelperImage: T[Unit] = Task {\n      os.proc(\"docker\", \"build\", \"-t\", Docker.customMuslBuilderImageName, \".\")\n        .call(cwd = BuildCtx.workspaceRoot / \"project\" / \"musl-image\", stdout = os.Inherit)\n      ()\n    }\n    override def writeDefaultNativeImageScript(scriptDest: String): Command[Unit] =\n      Task.Command {\n        buildHelperImage()\n        super.writeDefaultNativeImageScript(scriptDest)()\n      }\n  }\n\n  object `mostly-static-image` extends CliNativeImage {\n    override def launcherKind                                                 = \"mostly-static\"\n    override def nativeImageDockerParams: T[Option[NativeImage.DockerParams]] = Task {\n      val baseDockerParams = NativeImage.linuxMostlyStaticParams(\n        s\"ubuntu:$ubuntuVersion\",\n        s\"https://github.com/coursier/coursier/releases/download/v${deps.csDockerVersion}/cs-x86_64-pc-linux.gz\"\n      )\n      val dockerParams = setupLocaleAndOptions(baseDockerParams)\n      Some(dockerParams)\n    }\n  }\n\n  def localRepoJar: T[PathRef]\n\n  def nativeImageMainClass: T[String] = Task {\n    mainClass().getOrElse(sys.error(\"Don't know what main class to use\"))\n  }\n\n  def transitiveJarsSeq: T[Seq[PathRef]] = {\n    def allModuleDeps(todo: List[JavaModule]): List[JavaModule] =\n      todo match {\n        case Nil    => Nil\n        case h :: t =>\n          h :: allModuleDeps(h.moduleDeps.toList ::: t)\n      }\n\n    Task {\n      Task.traverse(allModuleDeps(this :: Nil).distinct)(m =>\n        Task.Anon(m.jar())\n      )()\n    }\n  }\n\n  def nativeImageClassPath: T[Seq[PathRef]] = Task {\n    val localRepoJar0 = localRepoJar()\n    runClasspath() :+ localRepoJar0 // isn't localRepoJar already there?\n  }\n\n  def nativeImage: T[PathRef] =\n    if (Properties.isLinux && arch == \"x86_64\" && isCI)\n      `linux-docker-image`.nativeImage\n    else\n      `base-image`.nativeImage\n\n  def nativeImageStatic: T[PathRef] =\n    `static-image`.nativeImage\n  def nativeImageMostlyStatic: T[PathRef] =\n    `mostly-static-image`.nativeImage\n\n  def runWithAssistedConfig(args: String*): Command[Unit] = Task.Command {\n    val cp          = jarClassPath().map(_.path).mkString(File.pathSeparator)\n    val mainClass0  = mainClass().getOrElse(sys.error(\"No main class\"))\n    val graalVmHome = Option(System.getenv(\"GRAALVM_HOME\")).getOrElse {\n      import sys.process._\n      Seq(cs(), \"java-home\", \"--jvm\", deps.graalVmJvmId).!!.trim\n    }\n    val outputDir = Task.ctx().dest / \"config\"\n    val command   = Seq(\n      s\"$graalVmHome/bin/java\",\n      s\"-agentlib:native-image-agent=config-output-dir=$outputDir\",\n      \"-cp\",\n      cp,\n      mainClass0\n    ) ++ args\n    os.proc(command.map(x => x: os.Shellable)*).call(\n      stdin = os.Inherit,\n      stdout = os.Inherit\n    )\n    Task.log.streams.out.println(\n      s\"Config generated in ${outputDir.relativeTo(BuildCtx.workspaceRoot)}\"\n    )\n  }\n\n  @unused\n  def runFromJars(args: String*): Command[os.CommandResult] = Task.Command {\n    val cp         = jarClassPath().map(_.path).mkString(File.pathSeparator)\n    val mainClass0 = mainClass().getOrElse(sys.error(\"No main class\"))\n    val command    = Seq(\"java\", \"-cp\", cp, mainClass0) ++ args\n    os.proc(command.map(x => x: os.Shellable)*).call(\n      stdin = os.Inherit,\n      stdout = os.Inherit\n    )\n  }\n\n  override def runClasspath: T[Seq[PathRef]] = Task {\n    super.runClasspath() ++ Seq(localRepoJar())\n  }\n\n  def jarClassPath: T[Seq[PathRef]] = Task {\n    val cp = runClasspath() ++ transitiveJarsSeq()\n    cp.filter(ref => os.exists(ref.path) && !os.isDir(ref.path))\n  }\n\n  override def launcher: T[PathRef] = Task {\n    import coursier.launcher.{BootstrapGenerator, ClassPathEntry, Parameters, Preamble}\n\n    import scala.util.Properties.isWin\n    val cp         = jarClassPath().map(_.path)\n    val mainClass0 = mainClass().getOrElse(sys.error(\"No main class\"))\n\n    val dest = Task.ctx().dest / (if (isWin) \"launcher.bat\" else \"launcher\")\n\n    val preamble = Preamble()\n      .withOsKind(isWin)\n      .callsItself(isWin)\n    val entries       = cp.map(path => ClassPathEntry.Url(path.toNIO.toUri.toASCIIString))\n    val loaderContent = coursier.launcher.ClassLoaderContent(entries)\n    val params        = Parameters.Bootstrap(Seq(loaderContent), mainClass0)\n      .withDeterministic(true)\n      .withPreamble(preamble)\n\n    BootstrapGenerator.generate(params, dest.toNIO)\n\n    PathRef(dest)\n  }\n\n  def standaloneLauncher: T[PathRef] = Task {\n    val cachePath = os.Path(coursier.cache.FileCache().location, BuildCtx.workspaceRoot)\n    def urlOf(path: os.Path): Option[String] =\n      if (path.startsWith(cachePath)) {\n        val segments = path.relativeTo(cachePath).segments\n        val url      = segments.head + \"://\" + segments.tail.mkString(\"/\")\n        Some(url)\n      }\n      else None\n\n    import coursier.launcher.{BootstrapGenerator, ClassPathEntry, Parameters, Preamble}\n\n    import scala.util.Properties.isWin\n    val cp         = jarClassPath().map(_.path)\n    val mainClass0 = mainClass().getOrElse(sys.error(\"No main class\"))\n\n    val dest = Task.ctx().dest / (if (isWin) \"launcher.bat\" else \"launcher\")\n\n    val preamble = Preamble()\n      .withOsKind(isWin)\n      .callsItself(isWin)\n    val entries = cp.map { path =>\n      urlOf(path) match {\n        case None =>\n          val content = os.read.bytes(path)\n          val name    = path.last\n          ClassPathEntry.Resource(name, os.mtime(path), content)\n        case Some(url) => ClassPathEntry.Url(url)\n      }\n    }\n    val loaderContent = coursier.launcher.ClassLoaderContent(entries)\n    val params        = Parameters.Bootstrap(Seq(loaderContent), mainClass0)\n      .withDeterministic(true)\n      .withPreamble(preamble)\n      .withJavaProperties(Seq(\"scala-cli.kind\" -> \"jvm.standaloneLauncher\"))\n\n    BootstrapGenerator.generate(params, dest.toNIO)\n\n    PathRef(dest)\n  }\n\n}\n\ntrait HasTests extends SbtModule {\n  override def scalacOptions: T[Seq[String]] = Task {\n    val sv           = scalaVersion()\n    val isScala213   = sv.startsWith(\"2.13.\")\n    val extraOptions =\n      if (isScala213) Seq(\"-Xsource:3\")\n      else Nil\n    super.scalacOptions() ++ extraOptions\n  }\n  trait ScalaCliTests extends ScalaCliModule with super.SbtTests with TestModule.Munit {\n    override def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq(\n      Deps.expecty,\n      Deps.munit\n    )\n    override def forkArgs: T[Seq[String]] = super.forkArgs() ++ Seq(\"-Xmx512m\", \"-Xms128m\")\n\n    override def repositoriesTask: Task[Seq[Repository]] =\n      Task.Anon(super.repositoriesTask() ++ deps.customRepositories)\n\n    override def testFramework: T[String] = super.testFramework\n  }\n}\n\ntrait PublishLocalNoFluff extends SonatypeCentralPublishModule {\n  def emptyZip: T[PathRef] = Task {\n    import java.io._\n    import java.util.zip._\n    val dest = Task.dest / \"empty.zip\"\n    val baos = new ByteArrayOutputStream\n    val zos  = new ZipOutputStream(baos)\n    zos.finish()\n    zos.close()\n    os.write(dest, baos.toByteArray)\n    PathRef(dest)\n  }\n  // adapted from https://github.com/com-lihaoyi/mill/blob/fea79f0515dda1def83500f0f49993e93338c3de/scalalib/src/PublishModule.scala#L70-L85\n  // writes empty zips as source and doc JARs\n  def publishLocalNoFluff(localIvyRepo: String = null): Command[PathRef] = Task.Command {\n\n    import mill.scalalib.publish.{LocalIvyPublisher, PublishInfo}\n    import mill.api.PathRef\n    val publisher = localIvyRepo match {\n      case null => LocalIvyPublisher\n      case repo =>\n        new LocalIvyPublisher(os.Path(\n          repo.replace(\"{VERSION}\", publishVersion()),\n          BuildCtx.workspaceRoot\n        ))\n    }\n\n    // PublishInfo parameters: file, classifier, ext, ivyConfig, ivyType\n    val mainArtifacts = Seq(\n      PublishInfo(PathRef(jar().path), None, \"jar\", \"compile\", \"jar\"),                 // jar\n      PublishInfo(PathRef(emptyZip().path), Some(\"sources\"), \"jar\", \"compile\", \"src\"), // sources\n      PublishInfo(PathRef(emptyZip().path), Some(\"javadoc\"), \"jar\", \"compile\", \"doc\")  // doc\n    )\n\n    val artifact  = artifactMetadata()\n    val jarPath   = jar().path\n    val pomPath   = pom().path\n    val ivyPath   = ivy().path\n    val emptyPath = emptyZip().path\n\n    val contents = Map[os.SubPath, Array[Byte]](\n      os.sub / \"jars\" / s\"${artifact.id}.jar\"         -> os.read.bytes(jarPath),\n      os.sub / \"srcs\" / s\"${artifact.id}-sources.jar\" -> os.read.bytes(emptyPath),\n      os.sub / \"docs\" / s\"${artifact.id}-javadoc.jar\" -> os.read.bytes(emptyPath),\n      os.sub / \"poms\" / s\"${artifact.id}.pom\"         -> os.read.bytes(pomPath),\n      os.sub / \"ivys\" / \"ivy.xml\"                     -> os.read.bytes(ivyPath)\n    )\n\n    publisher.publishLocal(artifact, contents)\n\n    jar()\n  }\n}\n\ntrait LocalRepo extends Module {\n  def stubsModules: Seq[PublishLocalNoFluff]\n  def version: T[String]\n\n  def localRepo: T[Seq[PathRef]] = {\n    val repoRoot                     = os.rel / \"out\" / \"repo\" / \"{VERSION}\"\n    val tasks: Seq[Command[PathRef]] = stubsModules.map(_.publishLocalNoFluff(repoRoot.toString))\n    Task.sequence(tasks)()\n  }\n\n  private def vcsState: T[VcsVersion.State] =\n    if (isCI)\n      Task(persistent = true) {\n        VcsVersion.vcsState()\n      }\n    else\n      Task {\n        VcsVersion.vcsState()\n      }\n  def localRepoZip: T[PathRef] = Task {\n    BuildCtx.withFilesystemCheckerDisabled {\n      val repoVer = vcsState().format()\n      val ver     = version()\n      localRepo()\n      val repoDir = BuildCtx.workspaceRoot / \"out\" / \"repo\" / ver\n      val destDir = Task.dest / ver / \"repo.zip\"\n      val dest    = destDir / \"repo.zip\"\n\n      import java.io._\n      import java.util.zip._\n      os.makeDir.all(destDir)\n      var fos: FileOutputStream = null\n      var zos: ZipOutputStream  = null\n      try {\n        fos = new FileOutputStream(dest.toIO)\n        zos = new ZipOutputStream(new BufferedOutputStream(fos))\n\n        val versionEntry = new ZipEntry(\"version\")\n        versionEntry.setTime(0L)\n        zos.putNextEntry(versionEntry)\n        zos.write(repoVer.getBytes(StandardCharsets.UTF_8))\n        zos.flush()\n\n        os.walk(repoDir).filter(_ != repoDir).foreach { p =>\n          val isDir = os.isDir(p)\n          val name  = p.relativeTo(repoDir).toString + (if (isDir) \"/\" else \"\")\n          val entry = new ZipEntry(name)\n          entry.setTime(os.mtime(p))\n          zos.putNextEntry(entry)\n          if (!isDir) {\n            zos.write(os.read.bytes(p))\n            zos.flush()\n          }\n          zos.closeEntry()\n        }\n        zos.finish()\n      }\n      finally {\n        if (zos != null) zos.close()\n        if (fos != null) fos.close()\n      }\n\n      PathRef(dest)\n    }\n  }\n\n  def localRepoJar: T[PathRef] = Task {\n    val zip  = localRepoZip().path\n    val dest = Task.dest / \"repo.jar\"\n\n    import java.io._\n    import java.util.zip._\n    var fos: FileOutputStream = null\n    var zos: ZipOutputStream  = null\n    try {\n      fos = new FileOutputStream(dest.toIO)\n      zos = new ZipOutputStream(new BufferedOutputStream(fos))\n\n      val entry = new ZipEntry(localRepoResourcePath)\n      entry.setTime(os.mtime(zip))\n      zos.putNextEntry(entry)\n      zos.write(os.read.bytes(zip))\n      zos.flush()\n      zos.closeEntry()\n\n      zos.finish()\n    }\n    finally {\n      if (zos != null) zos.close()\n      if (fos != null) fos.close()\n    }\n\n    PathRef(dest)\n  }\n\n}\n\nprivate def doFormatNativeImageConf(dir: os.Path, format: Boolean): List[os.Path] = {\n  val sortByName = Set(\"jni-config.json\", \"reflect-config.json\")\n  val files      = Seq(\n    \"jni-config.json\",\n    \"proxy-config.json\",\n    \"reflect-config.json\",\n    \"resource-config.json\"\n  )\n  var needsFormatting = List.empty[os.Path]\n  for (name <- files) {\n    val file = dir / name\n    if (os.isFile(file)) {\n      val content     = os.read(file)\n      val json        = ujson.read(content)\n      val updatedJson =\n        if (name == \"reflect-config.json\")\n          json.arrOpt.fold(json) { arr =>\n            val values =\n              arr.toVector.groupBy(_(\"name\").str).toVector.sortBy(_._1).map(_._2).map { t =>\n                val entries = t.map(_.obj).reduce(_ addAll _)\n                if (entries.get(\"allDeclaredFields\").contains(ujson.Bool(true)))\n                  entries -= \"fields\"\n                if (entries.get(\"allDeclaredMethods\").contains(ujson.Bool(true)))\n                  entries -= \"methods\"\n                ujson.Obj(entries)\n              }\n            ujson.Arr(values*)\n          }\n        else if (sortByName(name))\n          json.arrOpt.fold(json) { arr =>\n            val values = arr.toVector.sortBy(_(\"name\").str)\n            ujson.Arr(values*)\n          }\n        else\n          json\n      val updatedContent = updatedJson.render(indent = 2)\n        .linesIterator\n        .filter(_.trim.nonEmpty)\n        .map(_ + \"\\n\")\n        .mkString\n      if (updatedContent != content) {\n        needsFormatting = file :: needsFormatting\n        if (format)\n          os.write.over(file, updatedContent)\n      }\n    }\n  }\n  needsFormatting\n}\n\ntrait FormatNativeImageConf extends JavaModule {\n  def nativeImageConfDirs: T[Seq[os.Path]] = Task {\n    resources()\n      .map(_.path / \"META-INF\" / \"native-image\")\n      .filter(os.exists(_))\n      .flatMap { path =>\n        os.walk(path)\n          .filter(_.last.endsWith(\"-config.json\"))\n          .filter(os.isFile(_))\n          .map(_ / os.up)\n      }\n      .distinct\n  }\n  def checkNativeImageConfFormat(): Command[Unit] = Task.Command {\n    var needsFormatting = List.empty[os.Path]\n    for (dir <- nativeImageConfDirs())\n      needsFormatting = doFormatNativeImageConf(dir, format = false) ::: needsFormatting\n    if (needsFormatting.nonEmpty) {\n      val msg = s\"Error: ${needsFormatting.length} file(s) needs formatting\"\n      System.err.println(msg)\n      for (f <- needsFormatting)\n        System.err.println(\n          s\"  ${\n              if (f.startsWith(BuildCtx.workspaceRoot)) f.relativeTo(BuildCtx.workspaceRoot).toString\n              else f.toString\n            }\"\n        )\n      System.err.println(\n        \"\"\"Run\n          |  ./mill -i __.formatNativeImageConf\n          |to format them.\"\"\".stripMargin\n      )\n      sys.error(msg)\n    }\n    ()\n  }\n  def formatNativeImageConf(): Command[Unit] = Task.Command {\n    var formattedCount = 0\n    for (dir <- nativeImageConfDirs())\n      formattedCount += doFormatNativeImageConf(dir, format = true).length\n    System.err.println(s\"Formatted $formattedCount file(s).\")\n    ()\n  }\n}\n\ntrait ScalaCliScalafixModule extends ScalafixModule {\n\n  override def semanticDbVersion: T[String] = Deps.Versions.scalaMeta\n\n  def scalafixConfig: T[Option[os.Path]] = Task {\n    if (scalaVersion().startsWith(\"2.\")) super.scalafixConfig()\n    else Some(BuildCtx.workspaceRoot / \".scalafix3.conf\")\n  }\n  def scalacPluginMvnDeps: T[Seq[Dep]] = super.scalacPluginMvnDeps() ++ {\n    if (scalaVersion().startsWith(\"2.\")) Seq(Deps.semanticDbScalac)\n    else Nil\n  }\n  // Explicitly setting sourceroot, so that Scala CLI doesn't use a wrong one.\n  // Using BuildCtx.workspaceRoot is more or less required, for scalafix stuff to work fine.\n  def scalacOptions: T[Seq[String]] = Task {\n    val sv            = scalaVersion()\n    val isScala2      = sv.startsWith(\"2.\")\n    val sourceRoot    = BuildCtx.workspaceRoot\n    val parentOptions = {\n      val l = super.scalacOptions()\n      if (isScala2) l.filterNot(_.startsWith(\"-P:semanticdb:sourceroot:\"))\n      else {\n        val len = l.length\n        val idx = l.indexWhere(_.startsWith(\"-sourceroot\"))\n        if (idx >= 0 && idx < len - 1) l.take(idx) ++ l.drop(idx + 2)\n        else l\n      }\n    }\n    val semDbOptions =\n      if (isScala2) Seq(s\"-P:semanticdb:sourceroot:$sourceRoot\")\n      else Seq(s\"-sourceroot\", sourceRoot.toString)\n    val warnUnusedOptions =\n      if (!parentOptions.contains(\"-Ywarn-unused\") && scalaVersion().startsWith(\"2.12\"))\n        Seq(\"-Ywarn-unused\")\n      else if (!parentOptions.contains(\"-Wunused\") && scalaVersion().startsWith(\"2.13\"))\n        Seq(\"-Wunused\")\n      else if (!parentOptions.contains(\"-Wunused:all\") && scalaVersion().startsWith(\"3\"))\n        Seq(\"-Wunused:all\")\n      else Nil\n    parentOptions ++ semDbOptions ++ warnUnusedOptions\n  }\n}\n\ntrait ScalaCliCrossSbtModule extends CrossSbtModule with ScalaCliModule\n\ntrait ScalaCliModule extends ScalaModule {\n  def javacOptions: T[Seq[String]] = super.javacOptions() ++ Seq(\n    \"--release\",\n    \"16\"\n  )\n  def scalacOptions: T[Seq[String]] = Task {\n    val sv           = scalaVersion()\n    val isScala213   = sv.startsWith(\"2.13.\")\n    val extraOptions =\n      if (isScala213) Seq(\"-Xsource:3\", \"-Ytasty-reader\")\n      else Nil\n    super.scalacOptions() ++ Seq(\"-feature\", \"-deprecation\") ++ extraOptions\n  }\n}\n\ndef workspaceDirName      = \".scala-build\"\ndef projectFileName       = \"project.scala\"\ndef jvmPropertiesFileName = \".scala-jvmopts\"\n\ncase class License(licenseId: String, name: String, reference: String)\nobject License {\n  implicit val rw: ReadWriter[License] = macroRW\n}\ncase class Licenses(licenses: List[License])\nobject Licenses {\n  implicit val rw: ReadWriter[Licenses] = macroRW\n}\n"
  },
  {
    "path": "project/utils/package.mill",
    "content": "package build.project.utils\nimport java.util.Locale\n\nlazy val isArmArchitecture: Boolean =\n  sys.props.getOrElse(\"os.arch\", \"\").toLowerCase(Locale.ROOT) == \"aarch64\"\n"
  },
  {
    "path": "project/website/package.mill",
    "content": "package build.project.website\nimport build.project.deps, deps.Scala\n\nprivate def lastTableLine(path: os.Path, colCount: Int): Seq[String] = {\n  val content   = os.read(path)\n  val lines     = content.linesIterator.toVector\n  val (line, _) = lines\n    .zipWithIndex\n    .filter(_._1.count(_ == '|') == colCount + 1)\n    .lastOption\n    .getOrElse {\n      sys.error(s\"No table of expected shape found in $path\")\n    }\n  val cells = line\n    .split(\"\\\\|\", -1)\n    .toSeq\n    .map(_.trim)\n    .drop(1)\n    .dropRight(1)\n  assert(cells.length == colCount)\n  cells\n}\n\ndef checkMainScalaVersions(path: os.Path): Unit = {\n  val cells     = lastTableLine(path, 4)\n  val lastCells = cells.drop(1)\n  assert(lastCells.length == 3)\n  val expectedLastCells = Seq(Scala.scala3Next, Scala.scala213, Scala.scala212)\n  if (lastCells != expectedLastCells)\n    sys.error(\n      s\"Unexpected last line in Scala version table in $path \" +\n        s\"(expected ${expectedLastCells.mkString(\", \")}, got ${lastCells.mkString(\", \")})\"\n    )\n}\n\ndef checkScalaJsVersions(path: os.Path): Unit = {\n  val cells     = lastTableLine(path, 2)\n  val lastCells = cells.drop(1)\n  assert(lastCells.length == 1)\n  val expectedLastCells = Seq(Scala.scalaJs)\n  if (lastCells != expectedLastCells)\n    sys.error(\n      s\"Unexpected last line in Scala.js version table in $path \" +\n        s\"(expected ${expectedLastCells.mkString(\", \")}, got ${lastCells.mkString(\", \")})\"\n    )\n}\n"
  },
  {
    "path": "scala-cli",
    "content": "#!/usr/bin/env bash\nset -e\n\nREADLINK=\"readlink\"\nif [ \"$(uname)\" = \"Darwin\" ]; then\n  READLINK=\"greadlink\"\n  if ! which \"$READLINK\" >/dev/null 2>&1; then\n    echo \"greadlink not found. Install it with\"\n    echo \"  brew install coreutils\"\n    exit 1\n  fi\nfi\nDIR=\"$(dirname \"$(\"$READLINK\" -f \"${BASH_SOURCE[0]}\")\")\"\nLAUNCHER_DIR=\"$DIR/out/cli/base-image/nativeImage.dest\"\n\nif [ ! -x \"$LAUNCHER_DIR/scala-cli\" ]; then\n  echo \"native-image launcher not built yet.\" 1>&2\n  echo \"In order to build it, go in $DIR, and run\" 1>&2\n  echo 1>&2\n  echo \"  ./mill 'cli[]'.nativeImage\" 1>&2\n  exit 1\nfi\n\nexport PATH=\"$LAUNCHER_DIR:$PATH\"\nexec scala-cli \"$@\"\n"
  },
  {
    "path": "scala-cli-src",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\nSCRIPT_DIR=\"$( cd \"$( dirname \"$0\" )\" && pwd )\"\nLAUNCHER=\"$(cd \"$SCRIPT_DIR\" && ./mill show 'cli[]'.launcher </dev/null | jq -r . | sed 's/ref:[a-z0-9]*:[a-z0-9]*://')\"\nexec \"$LAUNCHER\" \"$@\"\n"
  },
  {
    "path": "scala-cli.bat",
    "content": "@echo off\r\n\r\nrem This is the launcher script of Scala CLI (https://github.com/VirtusLab/scala-cli).\r\nrem This script downloads and runs the Scala CLI version set by SCALA_CLI_VERSION below.\r\nrem\r\nrem Download the latest version of this script at https://github.com/VirtusLab/scala-cli/raw/main/scala-cli.bat\r\n\r\nsetlocal enabledelayedexpansion\r\n\r\nset \"SCALA_CLI_VERSION=1.12.5\"\r\n\r\nset SCALA_CLI_URL=https://github.com/VirtusLab/scala-cli/releases/download/v%SCALA_CLI_VERSION%/scala-cli.bat\r\nset CACHE_BASE=%localappdata%/Coursier/v1\r\n\r\nset CACHE_DEST=%CACHE_BASE%/https/github.com/VirtusLab/scala-cli/releases/download/v%SCALA_CLI_VERSION%\r\nset SCALA_CLI_BIN_PATH=%CACHE_DEST%/scala-cli.bat\r\n\r\nif not exist \"%SCALA_CLI_BIN_PATH%\" (\r\n\r\n    if not exist \"%CACHE_DEST%\" mkdir \"%CACHE_DEST%\"\r\n     \r\n    where /Q curl\r\n    if %ERRORLEVEL% EQU 0 (\r\n        curl -fLo \"%SCALA_CLI_BIN_PATH%\" \"%SCALA_CLI_URL%\"\r\n    ) else (\r\n        echo Could not download scala-cli %SCALA_CLI_VERSION%. Please, install curl and run './scala-cli.bat' again.\r\n        exit 1\r\n    )\r\n)\r\n\r\n%SCALA_CLI_BIN_PATH% %*\r\n"
  },
  {
    "path": "scala-cli.sh",
    "content": "#!/usr/bin/env bash\n\n# This is the launcher script of Scala CLI (https://github.com/VirtusLab/scala-cli).\n# This script downloads and runs the Scala CLI version set by SCALA_CLI_VERSION below.\n#\n# Download the latest version of this script at https://github.com/VirtusLab/scala-cli/raw/main/scala-cli.sh\n\nset -eu\n\nSCALA_CLI_VERSION=\"1.12.5\"\n\nGH_ORG=\"VirtusLab\"\nGH_NAME=\"scala-cli\"\n\nif [ \"$SCALA_CLI_VERSION\" == \"nightly\" ]; then\n  TAG=\"nightly\"\nelse\n  TAG=\"v$SCALA_CLI_VERSION\"\nfi\n\nif [ \"$(expr substr $(uname -s) 1 5 2>/dev/null)\" == \"Linux\" ]; then\n  arch=$(uname -m)\n  if [[ \"$arch\" == \"aarch64\" ]] || [[ \"$arch\" == \"x86_64\" ]]; then\n    SCALA_CLI_URL=\"https://github.com/$GH_ORG/$GH_NAME/releases/download/$TAG/scala-cli-${arch}-pc-linux.gz\"\n  else\n    echoerr \"scala-cli is not supported on $arch\"\n    exit 2\n  fi\n  CACHE_BASE=\"$HOME/.cache/coursier/v1\"\nelif [ \"$(uname)\" == \"Darwin\" ]; then\n  arch=$(uname -m)\n  CACHE_BASE=\"$HOME/Library/Caches/Coursier/v1\"\n  if [[ \"$arch\" == \"x86_64\" ]]; then\n    SCALA_CLI_URL=\"https://github.com/$GH_ORG/$GH_NAME/releases/download/$TAG/scala-cli-x86_64-apple-darwin.gz\"\n  elif [[ \"$arch\" == \"arm64\" ]]; then\n    SCALA_CLI_URL=\"https://github.com/$GH_ORG/$GH_NAME/releases/download/$TAG/scala-cli-aarch64-apple-darwin.gz\"\n  else\n    echoerr \"scala-cli is not supported on $arch\"\n    exit 2\n  fi\nelse\n  echo \"This standalone scala-cli launcher is supported only in Linux and macOS. If you are using Windows, please use the dedicated launcher scala-cli.bat\"\n  exit 1\nfi\n\nCACHE_DEST=\"$CACHE_BASE/$(echo \"$SCALA_CLI_URL\" | sed 's@://@/@')\"\nSCALA_CLI_BIN_PATH=${CACHE_DEST%.gz}\n\nif [ ! -f \"$CACHE_DEST\" ]; then\n  mkdir -p \"$(dirname \"$CACHE_DEST\")\"\n  TMP_DEST=\"$CACHE_DEST.tmp-setup\"\n  echo \"Downloading $SCALA_CLI_URL\"\n  curl -fLo \"$TMP_DEST\" \"$SCALA_CLI_URL\"\n  mv \"$TMP_DEST\" \"$CACHE_DEST\"\nfi\n\nif [ ! -f \"$SCALA_CLI_BIN_PATH\" ]; then\n  gunzip -k \"$CACHE_DEST\"\nfi\n\nif [ ! -x \"$SCALA_CLI_BIN_PATH\" ]; then\n  chmod +x \"$SCALA_CLI_BIN_PATH\"\nfi\n\nexec \"$SCALA_CLI_BIN_PATH\" \"$@\"\n"
  },
  {
    "path": "website/.gitignore",
    "content": "# Dependencies\n/node_modules\n\n# Production\n/build\n\n# Generated files\n.docusaurus\n.cache-loader\n\n# Misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "website/README.md",
    "content": "# Website\n\nThis website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.\n\n## Installation\n\n```console\nyarn install\n```\n\n## Local Development\n\n```console\nyarn start\n```\n\nThis command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.\n\n## Build\n\n```console\nyarn build\n```\n\nThis command generates static content into the `build` directory and can be served using any static contents hosting service.\n\n## Deployment\n\n```console\nGIT_USER=<Your GitHub username> USE_SSH=true yarn deploy\n```\n\nIf you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.\n"
  },
  {
    "path": "website/babel.config.js",
    "content": "module.exports = {\n  presets: [require.resolve('@docusaurus/core/lib/babel/preset')],\n};\n"
  },
  {
    "path": "website/docs/_advanced_install.mdx",
    "content": "import Tabs from '@theme/Tabs';\nimport TabItem from '@theme/TabItem';\nimport SectionAbout from \"../src/components/SectionAbout\"\nimport DownloadButton from \"../src/components/DownloadButton\"\nimport {currentOs} from \"../src/components/osUtils\";\n\n\n<SectionAbout title=\"Scala 3 installation\"/>\n<div className=\"row\">\n    <div className=\"col col--9 col--offset-1 padding--lg\">\n        If you install Scala 3.5.0 or newer on your machine, you can use Scala CLI under the `scala` command. <br/>\n        For Scala 3 installation instructions refer <a  className=\"no_monospace\" href=\"https://www.scala-lang.org/download/\">here</a>.\n\n        Note that even when installing the latest Scala 3 version, its `scala` command may refer to an older Scala CLI version.\n        For the latest stable Scala CLI release it usually is necessary to install it separately.\n    </div>\n</div>\n<SectionAbout title=\"Advanced Installation\">\n    <div className=\"margin--lg\"/>\n    <Tabs\n        groupId=\"operating-systems-specific\"\n        defaultValue={currentOs()}\n        values={[\n        {label: 'Windows', value: 'windows'},\n        {label: 'MacOs', value: 'mac'},\n        {label: 'Linux', value: 'linux'},\n        {label: 'BSD (experimental)', value: 'bsd'},\n        ]}>\n     <TabItem value=\"windows\"></TabItem>\n     <TabItem value=\"mac\"></TabItem>\n     <TabItem value=\"linux\"></TabItem>\n     <TabItem value=\"bsd\"></TabItem>\n    </Tabs>\n</SectionAbout>\n\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\nPick the installation method:\n\n<Tabs\ngroupId=\"operating-systems-specific\"\ndefaultValue={currentOs()}\nvalues={[\n{label: 'Windows', value: 'windows'},\n{label: 'MacOs', value: 'mac'},\n{label: 'Linux', value: 'linux'},\n{label: 'BSD', value: 'bsd'},\n]}\n>\n<TabItem value=\"linux\">\n\n<Tabs\ngroupId=\"linux\"\ndefaultValue=\"manual\"\nvalues={[\n{label: 'Manual', value: 'manual'},\n{label: 'Apt', value: 'apt'},\n{label: 'Deb', value: 'deb'},\n{label: 'Yum', value: 'yum'},\n{label: 'Rpm', value: 'rpm'},\n{label: 'Alpine', value: 'alpine'},\n{label: 'Nix', value: 'nix'},\n{label: 'SDKMAN', value: 'sdkman'},\n]}\n>\n<TabItem value=\"manual\">\n\nDownload the launcher from GitHub release assets with\n```bash\ncurl -fL https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux.gz | gzip -d > scala-cli\nchmod +x scala-cli\nsudo mv scala-cli /usr/local/bin/scala-cli\n```\n\nCheck that it runs fine by running its `version` command:\n```bash\nscala-cli version\n```\n</TabItem>\n\n<TabItem value=\"apt\">\n\nScala CLI can be installed via [apt](https://wiki.debian.org/Apt) packager tool.\n\n```bash\nsudo mkdir -p /etc/apt/keyrings\ncurl -sS \"https://virtuslab.github.io/scala-cli-packages/scala-cli-archive-keyring.gpg\" | sudo tee /etc/apt/keyrings/scala-cli-archive-keyring.gpg > /dev/null\nsudo curl -s --compressed -o /etc/apt/sources.list.d/scala_cli_packages.list \"https://virtuslab.github.io/scala-cli-packages/debian/scala_cli_packages.list\"\nsudo apt update\nsudo apt install scala-cli\n```\n</TabItem>\n<TabItem value=\"deb\">\n\nThe Debian package can be downloaded at [this address](https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux.deb).\n\nAlternatively, you can download it and install it manually with:\n\n```bash\ncurl -fLo scala-cli.deb https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux.deb\nsudo dpkg -i scala-cli.deb\n```\n</TabItem>\n\n<TabItem value=\"yum\">\n\nScala CLI can be installed via [yum](http://yum.baseurl.org) packager tool.\n\n```bash\ncat << EOF | sudo tee /etc/yum.repos.d/virtuslab.repo\n[virtuslab-repo]\nname=VirtusLab Repo\nbaseurl=https://virtuslab.github.io/scala-cli-packages/CentOS/Packages\nenabled=1\ngpgcheck=1\ngpgkey=https://virtuslab.github.io/scala-cli-packages/KEY.gpg\nEOF\nsudo yum repo-pkgs virtuslab-repo list\nsudo yum install scala-cli\n```\n</TabItem>\n<TabItem value=\"rpm\">\n\nThe RPM package can be downloaded at [this address](https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux.rpm).\n\nAlternatively, you can download it and install it manually with:\n```bash\ncurl -fLo scala-cli.rpm https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux.rpm\nsudo rpm -i scala-cli.rpm\n```\n</TabItem>\n<TabItem value=\"alpine\">\n\nDownload the launcher from GitHub release assets with\n\n```bash\nwget -q -O scala-cli.gz  https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux-static.gz && gunzip scala-cli.gz\nchmod +x scala-cli\nmv scala-cli /usr/bin/\n```\n\nCheck that it runs fine by running its `version` command:\n```bash\nscala-cli version\n```\n</TabItem>\n\n<TabItem value=\"nix\">\n\n:::info\nThis method is provided and supported by the community, not the core team of Scala CLI\n:::\n\nScala CLI can be installed with [Nix](https://nixos.org) with\n\n```bash\nnix-env -iA nixpkgs.scala-cli\n```\n\n</TabItem>\n<TabItem value=\"sdkman\">\n\nScala CLI can be installed via [SDKMAN](https://sdkman.io) with\n\n```bash\nsdk install scalacli\n```\n</TabItem>\n\n</Tabs>\n\n</TabItem>\n<TabItem value=\"windows\">\n\n<Tabs\ngroupId=\"windows\"\ndefaultValue=\"manual\"\nvalues={[\n{label: 'Manual', value: 'manual'},\n{label: 'Installer', value: 'installer'},\n{label: 'SDKMAN', value: 'sdkman'},\n{label: 'Chocolatey', value: 'choco'},\n{label: 'Scoop', value: 'scoop'},\n{label: 'WinGet', value: 'winget'},\n]}\n>\n\n<TabItem value=\"manual\">\n\nNote that the Windows manual installation requires [Visual C++ redistributable](https://support.microsoft.com/en-us/topic/the-latest-supported-visual-c-downloads-2647da03-1eea-4433-9aff-95f26a218cc0)\nto be installed. See below for how to install it.\n\nDownload the launcher from GitHub release assets with\n```bash\ncurl -fLo scala-cli.zip https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-win32.zip\njar -xf scala-cli.zip\n```\n\nCheck that it runs fine by running its `version` command:\n```bash\nscala-cli version\n```\n\nIf you get an error about `MSVCR100.dll` being missing, you have to install\n[Visual C++ redistributable](https://support.microsoft.com/en-us/topic/the-latest-supported-visual-c-downloads-2647da03-1eea-4433-9aff-95f26a218cc0). A valid version is distributed with the Scala CLI launchers.\nYou can download it [here](https://github.com/Virtuslab/scala-cli/releases/latest/download/vc_redist.x64.exe),\nand install it by double-clicking on it. Once the Visual C++ redistributable runtime is installed,\ncheck that the Scala CLI runs fine by running its `version` command:\n```bash\nscala-cli version\n```\n\nNote that the commands above don't put the `scala-cli` command in the `PATH`. For that, you can create a directory, move the\nlauncher there, and add the directory to the `PATH` with\n```bash\nmd \"%USERPROFILE%/scala-cli\"\nscala-cli add-path \"%USERPROFILE%/scala-cli\"\nmove scala-cli.exe \"%USERPROFILE%/scala-cli\"\n```\n</TabItem>\n<TabItem value=\"installer\">\n\nDownload MSI installer with Scala CLI for Windows\n\n<DownloadButton desc= 'Scala CLI for Windows' href='https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-win32.msi' width = '190px'></DownloadButton>\n\n</TabItem>\n<TabItem value=\"sdkman\">\n\nScala CLI can be installed via [SDKMAN](https://sdkman.io) with\n\n```bash\nsdk install scalacli\n```\n</TabItem>\n<TabItem value=\"choco\">\n\nTo install Scala CLI via [Chocolatey](https://chocolatey.org), run the following command from the command line or from PowerShell:\n\n```bash\nchoco install scala-cli\n```\n:::note\nThird-party Chocolatey packages may not provide the latest version.\n:::\n\n</TabItem>\n<TabItem value=\"scoop\">\n\nTo install Scala CLI via [Scoop](https://scoop.sh), run the following command from the command line or from PowerShell:\n\n```bash\nscoop install scala-cli\n```\n\n</TabItem>\n<TabItem value=\"winget\">\n\nTo install Scala CLI via [WinGet](https://learn.microsoft.com/en-gb/windows/package-manager/), run the following command from the command line or from PowerShell:\n\n```bash\nwinget install virtuslab.scalacli\n```\n\n</TabItem>\n</Tabs>\n\n</TabItem>\n\n<TabItem value=\"mac\">\n\n<Tabs\ngroupId=\"mac\"\ndefaultValue=\"brew\"\nvalues={[\n{label: 'Manual', value: 'manual'},\n{label: 'Installer', value: 'installer'},\n{label: 'Brew', value: 'brew'},\n{label: 'Nix', value: 'nix'},\n{label: 'SDKMAN', value: 'sdkman'},\n]}\n>\n<TabItem value=\"manual\">\n\nFor a Mac with the **arm64** architecture run the following commands:\n```bash\ncurl -fL https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-aarch64-apple-darwin.gz | gzip -d > scala-cli\nchmod +x scala-cli\nmv scala-cli /usr/local/bin/scala-cli\n```\n\nOtherwise, for a Mac with non-**arm64** architecture (pre-M1) run the following commands:\n```bash\ncurl -fL https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-apple-darwin.gz | gzip -d > scala-cli\nchmod +x scala-cli\nmv scala-cli /usr/local/bin/scala-cli\n```\n\nCheck that it runs fine by running its `version` command:\n```bash\nscala-cli version\n```\n</TabItem>\n<TabItem value=\"installer\">\n\nDownload the PKG installer with Scala CLI for MacOS\n<DownloadButton desc= 'Scala CLI for arm64' href='https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-aarch64-apple-darwin.pkg'></DownloadButton>\n<br/>\n<br/>\n<DownloadButton desc= 'Scala CLI for x86_64' href='https://github.com/Virtuslab/scala-cli/releases/latest/download/scala-cli-x86_64-apple-darwin.pkg'></DownloadButton>\n<br/>\n<br/>\n\nOnce downloaded, right-click on the downloaded file from Finder and then choose \"Open\".\n\n</TabItem>\n<TabItem value=\"brew\">\n\nScala CLI can be installed via [homebrew](https://brew.sh) with\n\n```bash\nbrew install Virtuslab/scala-cli/scala-cli\n```\n</TabItem>\n<TabItem value=\"nix\">\n\n:::info\nThis method is provided and supported by the community, not the core team of Scala CLI\n:::\n\nScala CLI can be installed via [Nix](https://nixos.org) with:\n\n```bash\nnix-env -iA nixpkgs.scala-cli\n```\n\n</TabItem>\n<TabItem value=\"sdkman\">\n\nScala CLI can be installed via [SDKMAN](https://sdkman.io) with\n\n```bash\nsdk install scalacli\n```\n</TabItem>\n</Tabs>\n\n</TabItem>\n\n<TabItem value=\"bsd\">\n<Tabs\ngroupId=\"bsd\"\ndefaultValue=\"manual\"\nvalues={[\n{label: 'Manual', value: 'manual'},\n]}\n>\n\n<TabItem value=\"manual\">\nFreeBSD support (and support for other BSDs) is currently experimental and not stable. Scala CLI is not being distributed for FreeBSD, but you can install it via Coursier's standalone JAR.\n\nBefore running Coursier and installing Scala CLI, make sure you have Java 17 or higher installed and in your $PATH:\n```bash\npkg install openjdk17\n```\nNote: Java 17 has to be installed manually on BSD systems at this time.\n\nDownload Coursier via the [Any JAR option](https://get-coursier.io/docs/cli-installation#latest-launchers).\n\nAfter downloading Coursier, you can use it to install Scala CLI:\n```console\nchmod +x coursier\n./coursier install scala-cli\n```\nScala CLI is now installed on your system. It is installed as a JAR rather than a native binary, and so the startup time is slightly longer.\n\nTo run scala-cli anywhere on your system, you need to add its binary path to the $PATH variable. For a given **user-directory**:\n```bash\nexport PATH=\"$PATH:/home/user-directory/.local/share/coursier/bin\"\n```\nAdd this command to your shell's RC file to make the setting permanent.\n\nDepending on the Java distribution you are using, it has been observed that Bloop (the build server used by Scala CLI) may have trouble starting on FreeBSD. You may try to customise Bloop JVM options with the `BLOOP_JAVA_OPTS` environment variable. Alternatively, you may skip using the build server altogether and use the compiler directly by passing the `--server=false` option. Please note that doing this will noticably extend project build times.\n\n```bash\nscala-cli run -e 'println(\"Hello FreeBSD\")' --server=false\n```\n\nAny features relying on third-party JVMs (such as ```--jvm``` and ```package --native-image```) may not work on FreeBSD, as those JVMs may not have BSD distributions provided. They may be made to work when a compatible JVM path is provided directly (refer to individual sub-commands' `--help`).\n</TabItem>\n\n</Tabs>\n</TabItem>\n\n</Tabs>\n\n</div></div>\n\n\n<SectionAbout title=\"Standalone launcher\">\n    <div className=\"margin--lg\"/>\n    <Tabs\n        defaultValue={currentOs() == 'windows' ? 'windows' : 'macOS/Linux'}\n        groupId=\"specific-standalone-launcher\"\n        values={[\n        {label: 'macOS/Linux', value: 'macOS/Linux'},\n        {label: 'Windows', value: 'windows'},\n        ]}>\n     <TabItem value=\"macOS/Linux\"></TabItem>\n     <TabItem value=\"windows\"></TabItem>\n    </Tabs>\n</SectionAbout>\n\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\nScript to automatically download and cache standalone Scala CLI launcher.\n\n<Tabs groupId=\"specific-standalone-launcher\"\n      defaultValue=\"macOS/Linux\"\n      values={[\n        {label: 'macOS/Linux', value: 'macOS/Linux'},\n        {label: 'Windows', value: 'windows'}\n        ]}>\n<TabItem value=\"macOS/Linux\">\n\nDownload the Scala CLI launcher in your project directory:\n```bash\ncurl https://raw.githubusercontent.com/VirtusLab/scala-cli/refs/heads/main/scala-cli.sh > scala && chmod +x scala\n```\nWe also recommend downloading the corresponding launcher for Windows users:\n```bash\ncurl https://raw.githubusercontent.com/VirtusLab/scala-cli/refs/heads/main/scala-cli.bat > scala.bat\n```\n\n<DownloadButton desc= 'Scala CLI launcher for macOS/Linux' href='https://github.com/VirtusLab/scala-cli/blob/main/scala-cli.sh' width = '250px' ></DownloadButton>\n\n</TabItem>\n<TabItem value=\"windows\">\n\nDownload the Scala CLI launcher in your project directory:\n```bash\nInvoke-WebRequest \"https://raw.githubusercontent.com/VirtusLab/scala-cli/refs/heads/main/scala-cli.bat\" -OutFile \"scala.bat”\n```\nWe also recommend downloading the corresponding launcher for macOS and Linux users:\n```bash\nInvoke-WebRequest \"https://raw.githubusercontent.com/VirtusLab/scala-cli/refs/heads/main/scala-cli.sh\" > scala\ngit update-index --chmod=+x scala\n```\n\n<DownloadButton desc= 'Scala CLI launcher for Windows' href='https://github.com/VirtusLab/scala-cli/blob/main/scala-cli.bat' width = '250px' ></DownloadButton>\n\n</TabItem>\n</Tabs>\n</div></div>\n\n<SectionAbout title=\"Bootstrapped standalone fat JAR\" colBigTitle=\"7\"></SectionAbout>\n\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\nIn certain restricted environments, jar files may only be accessible through Nexus or a specific Artifactory,\nmaking it cumbersome to manually load each dependency and construct the classpath.\nTo simplify this process, run the bootstrapped Scala CLI standalone fat jar using Coursier, follow the command below:\n\n```bash\ncs launch org.virtuslab.scala-cli:cliBootstrapped:latest.release -M scala.cli.ScalaCli\n```\n\nAlternatively, you can directly download it from the Maven repository [here](https://repo1.maven.org/maven2/org/virtuslab/scala-cli/cliBootstrapped).\n\n</div></div>\n\n\n<SectionAbout title=\"Shell completions\">\n    <div className=\"margin--lg\"/>\n    <Tabs\n        defaultValue={currentOs() == 'mac' ? 'zsh' : 'bash'}\n        groupId=\"shell-specific\"\n        values={[\n        {label: 'Bash', value: 'bash'},\n        {label: 'zsh', value: 'zsh'},\n        {label: 'fish', value: 'fish'},\n        ]}>\n     <TabItem value=\"bash\"></TabItem>\n     <TabItem value=\"zsh\"></TabItem>\n     <TabItem value=\"fish\"></TabItem>\n    </Tabs>\n</SectionAbout>\n\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\n\nTry the completions with\n\n<Tabs groupId=\"shell-specific\"\n      defaultValue=\"bash\"\n      values={[\n        {label: 'Bash', value: 'bash'},\n        {label: 'zsh', value: 'zsh'},\n        {label: 'fish', value: 'fish'},\n        ]}>\n<TabItem value=\"bash\">\n\n```\neval \"$(scala-cli install completions --env --shell bash)\"\nscala-cli --<TAB>\n```\n\n</TabItem>\n<TabItem value=\"zsh\">\n\n```\neval \"$(scala-cli install completions --env --shell zsh)\"\nscala-cli --<TAB>\n```\n\n</TabItem>\n<TabItem value=\"fish\">\n\n```\neval \"$(scala-cli install completions --env --shell fish)\"\nfish\nscala-cli --<TAB>\n```\n\n</TabItem>\n</Tabs>\n\nInstall them on your system with\n```bash\nscala-cli install completions\n```\n\nIf any of the `scala-cli install completions` command complained that your shell cannot be determined, specify it\nwith `--shell`\n<Tabs groupId=\"shell-specific\"\n      values={[\n        {label: 'Bash', value: 'bash'},\n        {label: 'zsh', value: 'zsh'},\n        {label: 'fish', value: 'fish'},\n        ]}>\n<TabItem value=\"bash\">\n\n```bash\nscala-cli install completions --shell bash\n```\n\n</TabItem>\n<TabItem value=\"zsh\">\n\n```bash\nscala-cli install completions --shell zsh\n```\n\n</TabItem>\n<TabItem value=\"fish\">\n\n```bash\nscala-cli install completions --shell fish\n```\n\n</TabItem>\n</Tabs>\n</div></div>\n\n<div id=\"scala-js\"/>\n<SectionAbout title=\"Scala.js\"/>\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\nTo run Scala.js applications [Node.js](https://nodejs.org/) needs to be installed. Scala CLI at this moment does not manage Node.js however it may change in the future.\n\n</div></div>\n\n<div id=\"scala-native\"/>\n<SectionAbout title=\"Scala Native\"/>\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\n[Clang](https://llvm.org/docs/GettingStarted.html#requirements) is required to compile and run Scala Native applications. Using some functionalities known from JDK (like using `java.util.zip` package) require additional packages to be installed.\n\nScala Native page contains detailed [installation guide](https://scala-native.readthedocs.io/en/latest/user/setup.html#installing-clang-and-runtime-dependencies).\n\n</div></div>\n\n\n<SectionAbout title=\"Uninstall Scala CLI\">\n    <div className=\"margin--lg\"/>\n    <Tabs\n        groupId=\"uninstall-specific\"\n        defaultValue={currentOs()}\n        values={[\n        {label: 'Windows', value: 'windows'},\n        {label: 'MacOs', value: 'mac'},\n        {label: 'Linux', value: 'linux'},\n        ]}>\n     <TabItem value=\"windows\"></TabItem>\n     <TabItem value=\"mac\"></TabItem>\n     <TabItem value=\"linux\"></TabItem>\n    </Tabs>\n</SectionAbout>\n\n<div className=\"row\"><div className=\"col col--9 col--offset-1 padding--lg advanced_install_methods\">\n\nPick the uninstallation method:\n\n<Tabs\ngroupId=\"uninstall-specific\"\ndefaultValue={currentOs()}\nvalues={[\n{label: 'Windows', value: 'windows'},\n{label: 'MacOs', value: 'mac'},\n{label: 'Linux', value: 'linux'},\n]}\n>\n<TabItem value=\"linux\">\n\n<Tabs\ngroupId=\"linux\"\ndefaultValue=\"installation-script-u\"\nvalues={[\n{label: 'Installation script', value: 'installation-script-u' },\n{label: 'Manual', value: 'manual-u'},\n{label: 'Apt', value: 'apt-u'},\n{label: 'Deb', value: 'deb-u'},\n{label: 'Yum', value: 'yum-u'},\n{label: 'Rpm', value: 'rpm-u'},\n{label: 'Alpine', value: 'alpine-u'},\n]}\n>\n<TabItem value=\"installation-script-u\">\n\nIf Scala CLI was installed via the installation script, you can uninstall it with:\n\n```bash\nscala-cli uninstall\n```\n\nIf you have installed Scala CLI completions into your shell, `uninstall` command uninstalls them by running [`uninstall-completions`](/docs/commands/completions) command under the hood.\n</TabItem>\n\n<TabItem value=\"manual-u\">\n\nIf Scala CLI was installed manually you can uninstall it with:\n\n```bash\nrm /usr/local/bin/scala-cli\n```\n</TabItem>\n\n<TabItem value=\"apt-u\">\n\nIf Scala CLI was installed via [apt](https://wiki.debian.org/Apt) you can uninstall it with:\n\n```bash\nsudo apt purge scala-cli\n```\n</TabItem>\n<TabItem value=\"deb-u\">\n\nIf Scala CLI was installed via [dpkg](https://en.wikipedia.org/wiki/Dpkg) you can uninstall it with:\n\n```bash\nsudo dpkg --remove scala-cli\n```\n</TabItem>\n<TabItem value=\"yum-u\">\n\nIf Scala CLI was installed via [yum](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/c1-yum) you can uninstall it with:\n\n```bash\nyum remove scala-cli\n```\n</TabItem>\n<TabItem value=\"rpm-u\">\n\nIf Scala CLI was installed via [rpm](https://rpm.org/) you can uninstall it with:\n\n``` bash\nrpm -e scala-cli\n```\n</TabItem>\n<TabItem value=\"alpine-u\">\n\nFor Alpine Linux you can uninstall scala-cli with:\n\n```bash\nrm /usr/bin/scala-cli\n```\n\n</TabItem>\n\n</Tabs>\n\n</TabItem>\n<TabItem value=\"windows\">\n\n<Tabs\ngroupId=\"windows\"\ndefaultValue=\"manual-u\"\nvalues={[\n{label: 'Manual', value: 'manual-u'},\n{label: 'Installer', value: 'installer-u'},\n{label: 'Chocolatey', value: 'choco'},\n]}\n>\n\n<TabItem value=\"manual-u\">\n\nIf Scala CLI was installed manually you can uninstall it with:\n\n```bash\nrmdir \"%USERPROFILE%/scala-cli\"\n```\n</TabItem>\n<TabItem value=\"installer-u\">\n\nIf Scala CLI was installed via the installer, you can uninstall it in the Control Panel.\n\n</TabItem>\n<TabItem value=\"choco\">\n\nTo uninstall Scala CLI via [Chocolatey](https://chocolatey.org), run the following command from the command line or from PowerShell:\n\n```bash\nchoco uninstall scala-cli\n```\n\n</TabItem>\n</Tabs>\n\n</TabItem>\n<TabItem value=\"mac\">\n\n<Tabs\ngroupId=\"mac\"\ndefaultValue=\"installation-script-u\"\nvalues={[\n{label: 'Installation script', value: 'installation-script-u' },\n{label: 'Manual', value: 'manual-u'},\n{label: 'Brew', value: 'brew-u'},\n]}\n>\n<TabItem value=\"installation-script-u\">\n\nIf Scala CLI was installed via the installation script, you can uninstall it with:\n\n```bash\nscala-cli uninstall\n```\n\nIf you have installed Scala CLI completions into your shell, `uninstall` command uninstalls them by running [`uninstall-completions`](/docs/commands/completions) command under the hood.\n</TabItem>\n\n<TabItem value=\"manual-u\">\n\nIf Scala CLI was installed manually you can uninstall it with:\n\n```bash\nrm /usr/local/bin/scala-cli\n```\n\n</TabItem>\n<TabItem value=\"brew-u\">\n\nIf Scala CLI was installed via [homebrew](https://brew.sh) you can uninstall it with:\n\n```bash\nbrew uninstall scala-cli\n```\n</TabItem>\n</Tabs>\n\n</TabItem>\n</Tabs>\n\n</div></div>\n"
  },
  {
    "path": "website/docs/_misc/_category_.json",
    "content": "{\n  \"label\": \"Miscellaneous\",\n}\n"
  },
  {
    "path": "website/docs/_misc/benchmarking.md",
    "content": "---\ntitle: Running benchmarks\nsidebar_position: 1\n---\n\nWrite and run JMH benchmarks with `scala run --jmh`\n"
  },
  {
    "path": "website/docs/_misc/browse.md",
    "content": "---\ntitle: Navigate dependencies\nsidebar_position: 2\n---\n\nFor now, only on macOS\n\nOnly possible with the Scala version of the Scala CLI itself (2.12.14)\n\n\n"
  },
  {
    "path": "website/docs/_scala-ecosystem.md",
    "content": "---\ntitle: Scala CLI within Scala ecosystem\nsidebar_position: 31\n---\n\n:::note\nThis page concerns the future of the Scala ecosystem.\n\nIf you just want to learn Scala CLI just head out to the [Commands section](./commands/basics.md)\n:::\n\n:::warning\nPage under construction!\n:::\n\nProject evolution and the area before build tools.\n\n## Why not better sbt or mill?\n\nFundamental problems and over-complication.\n\n## Scala CLI: the modern `scala` command?\n\nScala CLI as new `scala` command.\n\n\n\n"
  },
  {
    "path": "website/docs/commands/_category_.json",
    "content": "{\n  \"label\": \"Commands\",\n  \"position\": 10\n}\n"
  },
  {
    "path": "website/docs/commands/basics.md",
    "content": "---\ntitle: Basics\nsidebar_position: 3\n---\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\n\nScala CLI is a command line tool that executes a given sub-command on the inputs it’s provided with, using a\ngiven [configuration](../guides/introduction/configuration.md) to produce a result.\n\nThe most important sub-commands are:\n\n- [compile](./compile.md) compiles your code (excluding tests)\n- [run](./run.md) runs your code using the provided arguments (it’s also used when no other command is provided)\n- [test](./test.md) compiles and runs the tests defined in your code\n- [package](./package.md) packages your code into a jar or other format\n- [repl](./repl.md) / [console](./repl.md) runs the interactive Scala shell\n- [fmt](./fmt.md) formats your code\n\nScala CLI can also be run without passing any explicit sub-command,\nin which case it defaults to one of the sub-commands based on context:\n\n- if the `--version` option is passed, it prints the `version` command output (unmodified by any other options)\n- if any inputs were passed, it defaults to the `run` sub-command\n    - and so, `scala-cli a.scala` runs your `a.scala` file\n- additionally, when no inputs were passed, it defaults to the `run` sub-command in the following scenarios:\n    - if a snippet was passed with `-e`, `--execute-script`, `--execute-scala`, `--execute-java` or `--execute-markdown`\n    - if a main class was passed with the `--main-class` option alongside an extra `--classpath`\n- otherwise if no inputs were passed, it defaults to the `repl` sub-command\n\n## Input formats\n\nThe Scala CLI commands accept input in a number of ways, most notably:\n\n- as source files\n- as one or several directories that contain source files\n- as URLs pointing to sources\n- by processing source code via piping or process substitution\n\nNote that all of these input formats can be used alongside each other.\n\n## Source files\n\nScala CLI accepts the following types of source code:\n\n- `.scala` files, containing Scala code\n- `.sc` files, containing Scala scripts (see more in the [Scripts guide](../guides/scripting/scripts.md))\n- `.java` files, containing Java code\n- `.md` files, containing Markdown code (experimental, see more in the [Markdown guide](../guides/power/markdown.md))\n- `.c` and `.h` files, containing C code (only as resources for Scala Native, see more in\n  the [Scala Native guide](../guides/advanced/scala-native.md))\n- `.jar` files, (see more in the [Run docs](run#jar))\n\nThe following example shows the simplest input format.\nFirst, create a source file:\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello from Scala\")\n}\n```\n\nThen run it by passing it to Scala CLI:\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.scala\n```\n\n```text\nHello from Scala\n```\n\n</ChainedSnippets>\n\nYou can also split your code into multiple files:\n\n```scala title=Messages.scala\nobject Messages {\n  def hello = \"Hello from Scala\"\n}\n```\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(Messages.hello)\n}\n```\n\nand the run them with Scala CLI:\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.scala Messages.scala\n```\n\n```text\nHello from Scala\n```\n\n</ChainedSnippets>\n\n:::note\nScala CLI compiles only the provided inputs.\nFor example, if we provide only one of the files above:\n\n```bash fail\nscala-cli Hello.scala\n```\n\ncompilation will fail. Scala CLI compiles only the files it’s given.\n:::\n\nWhile this is *very* convenient for projects with just a few files, passing many files this way can be cumbersome and\nerror-prone.\nIn the case of larger projects, passing whole directories can help.\n\n## Directories\n\nScala CLI accepts whole directories as input.\n\nThis is convenient when you have many `.scala` files, and passing them all one-by-one on the command line isn't\npractical:\n\n```scala title=my-app/Messages.scala\nobject Messages {\n  def hello = \"Hello from Scala\"\n}\n```\n\n```scala title=my-app/Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(Messages.hello)\n}\n```\n\nIn this case, you can run all the source code files in `my-app` by supplying the directory name:\n\n<ChainedSnippets>\n\n```bash\nscala-cli my-app\n```\n\n```text\nHello from Scala\n```\n\n</ChainedSnippets>\n\nIn our experience, `scala-cli .` is the most used command; it compiles and runs all sources in the current directory.\n\n:::note\nScala CLI process all files within the specified directories and all of its subdirectories.\n\nScala CLI ignores all subdirectories that start with `.` like `.scala-build` or `.vscode`.\nSuch directories needs to be explicitly provided as inputs.\n:::\n\n## URLs\n\n:::warning\nRunning unverified code from the internet can be very handy for *trusted* sources, but it can also be really dangerous,\nsince Scala CLI does not provide any sandboxing at this moment.\n\nMake sure that you trust the code that you are about to run.\n:::\n\nScala CLI accepts input via URLs pointing at `.scala` files.\nIt downloads their content, and runs them:\n\n<ChainedSnippets>\n\n```bash\nscala-cli https://gist.github.com/alexarchambault/f972d941bc4a502d70267cfbbc4d6343/raw/2691c01984c9249936a625a42e29a822a357b0f6/Test.scala\n```\n\n```text\nHello from Scala GitHub Gist\n```\n\n</ChainedSnippets>\n\n### GitHub Gist\n\nScala CLI accepts input via Github Gist’s urls.\nIt downloads the gist zip archive and runs it:\n\n<ChainedSnippets>\n\n```bash\nscala-cli https://gist.github.com/alexarchambault/7b4ec20c4033690dd750ffd601e540ec\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nMore details in the [GitHub gists cookbook](../cookbooks/introduction/gists.md).\n\n### Zip archive\n\nScala CLI accepts inputs via a `zip` archive path.\nIt unpacks the archive and runs it:\n\n```scala titleHello.scala\nobject Hello extends App {\n  println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash ignore\nunzip -l hello.zip\n```\n\n```text\nArchive:  hello.zip\n  Length      Date    Time    Name\n---------  ---------- -----   ----\n       49  12-07-2021 00:06   Hello.scala\n---------                     -------\n       49                     1 file\n```\n\n```bash ignore\nscala-cli hello.zip\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n## Piping\n\nYou can also pipe code to Scala CLI for execution:\n\n- scripts\n\n  <ChainedSnippets>\n\n  ```bash\n  echo 'println(\"Hello\")' | scala-cli _.sc\n  ```\n\n  ```text\n  Hello\n  ```\n\n  </ChainedSnippets>\n\n- Scala code\n\n  <ChainedSnippets>\n\n  ```bash\n  echo '@main def hello() = println(\"Hello\")' | scala-cli _.scala\n  ```\n\n  ```text\n  Hello\n  ```\n\n  </ChainedSnippets>\n\n- Java code\n\n  <ChainedSnippets>\n\n  ```bash\n  echo 'class Hello { public static void main(String args[]) { System.out.println(\"Hello\"); } }' | scala-cli _.java\n  ```\n\n  ```text\n  Hello\n  ```\n\n  </ChainedSnippets>\n\n- Markdown code (experimental)\n\n  <ChainedSnippets>\n\n  ```bash\n  echo '# Example Snippet\n  ```scala\n  println(\"Hello\")\n  ```' | scala-cli _.md\n  ```\n\n  ```text\n  Hello\n  ```\n\n  </ChainedSnippets>\n\nMore details in the [Piping guide](../guides/advanced/piping.md).\n\n## Scala CLI version\n\nScala CLI can also run another launcher version, which can be helpful to test unreleased Scala CLI functionalities.\n:::warning\nRunning another Scala CLI version might be slower because it uses JVM-based Scala CLI launcher.\n:::\n\nTo run another Scala CLI version, specify it with `--cli-version` before any other argument:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli --cli-version 0.1.17-62-g21e1cf44-SNAPSHOT version\n```\n\n```text\nScala CLI version: 0.1.17-62-g21e1cf44-SNAPSHOT\nScala version (default): 3.2.1\n```\n\n</ChainedSnippets>\n\nTo use the latest Scala CLI nightly build, pass `nightly` to `--cli-version` parameter:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli --cli-version nightly version\n```\n\n```text\nFetching Scala CLI 0.1.17-62-g21e1cf44-SNAPSHOT\nScala CLI version: 0.1.17-62-g21e1cf44-SNAPSHOT\nScala version (default): 3.2.1\n```\n\n</ChainedSnippets>\n\n## Process substitution\n\nLastly, Scala CLI also accepts input via shell process substitution:\n\n<ChainedSnippets>\n\n```bash\nscala-cli <(echo 'println(\"Hello\")')\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello\n-->\n\n## Logging\n\nTo turn off logging in the application, pass the `-q` flag once. This will suppress all logging output except for\nerrors:\n\n```scala title=Hello.scala\nobject Hello extends App {\n  println(\"Hello\"\n}\n```\n\n<ChainedSnippets>\n\n```bash run-fail\nscala-cli Hello.scala -q  \n```\n\n```text\n[error] ./Hello.scala:6:3: ')' expected, but '}' found\n[error]   }\n[error]   ^\nCompilation failed\n```\n\n<!-- Expected:\n')' expected, but '}' found\nCompilation failed\n-->\n\n</ChainedSnippets>\n\nNote that this will also suppress any logging related to tasks such as downloading dependencies, logs\nabout the start of compilation, and so on.\n\n## Warning suppression\n\nSome particular warning logs can be suppressed, either for a particular command by passing an option or with a global\nconfig.\nMore details can be found in the [verbosity guide](../guides/advanced/verbosity.md)"
  },
  {
    "path": "website/docs/commands/clean.md",
    "content": "---\ntitle: Clean\nsidebar_position: 16\n---\n\nThe `clean` command deletes all of the files generated by Scala CLI. This includes the `.scala-build` directory where Scala CLI writes outputs and stores its caches, and `.bsp/scala-cli.json` file which contains details for the BSP connection (necessary for importing a Scala CLI build to your IDE of choice).\n"
  },
  {
    "path": "website/docs/commands/compile.md",
    "content": "---\ntitle: Compile\nsidebar_position: 5\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nScala CLI compiles your code with its `compile` command:\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n```bash\nscala-cli compile Hello.scala\n```\n\nNote that most Scala CLI commands automatically compile your code, if necessary.\nThe `compile` command is useful if you want to check that your code compiles\n(or to see the compilation warnings, if any occur) without running it or packaging it.\n\nThe most common `compile` options are shown below.\nFor a full list of options, run `scala-cli compile --help`, or check the options linked in the\n[reference documentation](../reference/commands.md#compile).\n\n## Test scope\n\n`--test` makes Scala CLI compile main and test scopes:\n\n```scala title=Sample.test.scala\n//> using dep org.scalameta::munit:1.0.2\nclass Test extends munit.FunSuite {\n  test(\"sample\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n```bash\nscala-cli compile --test Sample.test.scala\n```\n\n## Watch mode\n\n`--watch` makes Scala CLI watch your code for changes, and re-compiles it upon any change:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli compile --watch Hello.scala\n```\n\n```text\nCompiling project-cef76d561e (1 Scala source)\nCompiled 'project-cef76d561e'\nWatching sources, press Ctrl+C to exit.\nCompiling project-cef76d561e (1 Scala source)\nCompiled 'project-cef76d561e'\nWatching sources, press Ctrl+C to exit.\n```\n\n</ChainedSnippets>\n\n### Watching additional paths\n\nUse `--watching` to re-trigger compilation when files outside your Scala sources change:\n\n```bash ignore\nscala-cli compile --watch --watching ./data Hello.scala\n```\n\nYou can also configure this from sources with:\n\n```scala\n//> using watching ./data\n```\n\nIf you use both, Scala CLI watches every path from both the command line and the directive.\n\n## Scala version\n\nScala CLI uses the latest stable version of Scala which was tested in Scala CLI (see our list\nof [Supported Scala Versions](../reference/scala-versions.md)). You can specify the Scala version you'd like to use\nwith `--scala`:\n\n```bash\nscala-cli compile --scala 2.13.15 Hello.scala\n```\n\nScala CLI works with all major `2.12.x`, `2.13.x`, and `3.x` Scala versions.\n\n`--scala` also accepts \"short\" Scala versions, such as `2.12`, `2`, or `3`. In this\ncase, it picks the highest corresponding stable Scala version:\n\n```bash\nscala-cli compile --scala 2.12 Hello.scala\nscala-cli compile --scala 2 Hello.scala\nscala-cli compile --scala 3 Hello.scala\n```\n\n## Scala Nightlies\n\nThe nightly builds of Scala compiler are the unstable ones which are published on a nightly basis.\n\nTo use the latest Scala 2 and Scala 3 nightly builds, pass `2.nightly` and `3.nightly`, respectively.\nYou can also request the last `2.12.nightly` and `2.13.nightly` versions. `2.13.nightly` is the same as `2.nightly`.\nMoreover, passing in the `3.{sub binary number}.nightly` format, such as `3.0.nightly` or `3.1.nightly` is accepted,\ntoo.\n\nScala CLI takes care of fetching the nightly builds of Scala 2 and Scala 3 from different repositories, without you\nhaving to pass their addresses as input after the `--repo` flag.\n\nFor compiling with the latest Scala 2 nightly build:\n\n```bash ignore\nscala-cli Hello.scala -S 2.nightly\n```\n\nFor compiling with the latest Scala 3 nightly build:\n\n```bash\nscala-cli Hello.scala -S 3.nightly\n```\n\nFor compiling with a specific nightly build you have the full version:\n\n```bash ignore\nscala-cli Hello.scala -S 2.13.9-bin-4505094\n```\n\nFor setting this inside scala files, use [`using` directives](../guides/introduction/using-directives.md):\n\n```scala\n//> using scala 2.nightly\n```\n\n```scala compile\n//> using scala 3.nightly\n```\n\n```scala\n//> using scala 2.13.9-bin-4505094\n```\n\n## Dependencies\n\nYou can add dependencies on the command-line with `--dependency`:\n\n```bash\nscala-cli compile Hello.scala \\\n  --dependency org.scala-lang.modules::scala-parallel-collections:1.0.4\n```\n\nNote that `--dependency` is only meant as a convenience. You should favor\nadding dependencies in the source files themselves via [`using` directives](../guides/introduction/configuration.md#special-imports).\n\nYou can also add simple JAR files — those that don’t have transitive dependencies — as dependencies, with `--jar`:\n\n```bash\nscala-cli compile Hello.scala --jar /path/to/library.jar\n```\n\nSee the [Dependency management](../guides/introduction/dependencies.md) guide for more details.\n\n## Scala compiler options\n\n### Passing compiler options with `-O`\n\nAll [Scala compiler options](https://docs.scala-lang.org/overviews/compiler-options) can be passed to Scala CLI\nwith `-O`:\n\n<ChainedSnippets>\n\n```bash\nscala-cli compile Hello.scala -O -deprecation -O -Xlint:infer-any\n```\n\n```text\n[warn] ./Hello.scala:3:7: method x in class Some is deprecated (since 2.12.0): Use .value instead.\n[warn]   opt.x\n[warn]       ^\n```\n\n</ChainedSnippets>\n\nPassing a value to a compiler option requires another `-O`:\n\n```bash\nscala-cli -O -Xshow-phases -O -color -O never\n```\n\n:::note\nScala CLI uses `bloop` by default, which at times gets in the way of getting the full compiler output.\nIn the case of some compiler options it may be necessary to turn `bloop` off with `--server=false`.\nThe Scala CLI team is actively trying to minimize such cases, but for the time being it's a useful workaround.\n:::\n\n### Passing compiler options with `using` directives\n\nIt is also possible to pass compiler options with the appropriate `using` directives.\n\nA single option can be passed like this:\n\n```scala compile\n//> using option -new-syntax\n@main def hello = if true then println(\"Hello\")\n```\n\nIt's also possible to pass a value to the option with the same directive:\n\n```scala compile\n//> using option -release 17\n\nobject Main {\n  java.util.HexFormat.of().toHexDigits(255)\n}\n```\n\nThere's a separate directive for passing multiple options at one time:\n\n```scala compile\n//> using options -new-syntax -rewrite -source:3.2-migration\n\n@main def hello = if (true) println(\"Hello\")\n```\n\n### Compiler options recognised even when passed without `-O`\n\nFor ease of use many compiler options can be passed as-is to Scala CLI, without the need of passing after `-O`:\n\n<ChainedSnippets>\n\n```bash\nscala-cli compile Hello.scala -Xlint:infer-any\n\n```\n\n```text\nCompiling project (1 Scala source)\n[warn] ./Hello.scala:2:11: a type was inferred to be `Any`; this may indicate a programming error.\n[warn]   val l = List(\"a\", true, 2, new Object)\n[warn]           ^\nCompiled project\n```\n\n</ChainedSnippets>\n\nThose include:\n\n- all options which start with:\n    - `-g`\n    - `-language`\n    - `-opt`\n    - `-P`\n    - `-target`\n    - `-source`\n    - `-V`\n    - `-W`\n    - `-X`\n    - `-Y`\n- the following flags:\n    - `-nowarn`\n    - `-feature`\n    - `-deprecation`\n    - `-rewrite`\n    - `-old-syntax`\n    - `-new-syntax`\n    - `-indent`\n    - `-no-indent`\n- the following options which accept values (which can be passed similarly to any regular Scala CLI option values)\n    - `-encoding`\n    - `-release`\n    - `-color`\n    - `-classpath`\n    - `-d`\n\nAll options mentioned above are assumed to be Scala compiler options and are being passed as such to the compiler even\nwithout `-O`. (However, they can still be passed with `-O`, regardless.)\n\n:::note\nSome compiler options (e.g. `-new-syntax`) may be Scala-version-specific.\nThus, it may happen that Scala CLI will pass those to the compiler,\nbut they will not be recognised if they're not supported in a given Scala version.\nIn such a case, refer to the `--scalac-help` output while passing the appropriate version with `-S`.\n:::\n\n### Java options for the compiler\n\nThere are two ways to pass Java options to the compiler:\n- `--bloop-java-opt` when using the build server, which is the default, e.g. `--bloop-java-opt -XX:MaxHeapSize=8g`\n- `//> using options` or `--scalac-opt` with arguments prefixed by `-J`, e.g. `//> using options -J-XX:MaxHeapSize=8g`,\nthis will work only when the build server is disabled (with `--server=false`).\n\n### Compiler options redirected to Scala CLI alternatives\n\nIn a few cases, certain compiler options are being auto-redirected to a corresponding Scala CLI-specific option for\nbetter integration with other functionalities of the tool.\nThe redirection happens even when the options are passed with `-O`, making them effectively aliases for their\nScala CLI counterparts.\n\nThose include:\n\n- `-classpath` being redirected to `--classpath`\n- `-d` being redirected to `--compilation-output`\n\n### Scala compiler help\n\nCertain compiler options allow to view relevant help. Inputs aren't required when those are passed.\n(since they would be disregarded anyway)\n\nThose include:\n\n- `-help`\n- `-V`\n- `-W`\n- `-X`\n- `-Y`\n\n<ChainedSnippets>\n\n```bash\nscala-cli -S 2.12.17 -Xshow-phases\n```\n\n```text\n\n     phase name  id  description\n     ----------  --  -----------\n         parser   1  parse source into ASTs, perform simple desugaring\n          namer   2  resolve names, attach symbols to named trees\n packageobjects   3  load package objects\n          typer   4  the meat and potatoes: type the trees\n         patmat   5  translate match expressions\n superaccessors   6  add super accessors in traits and nested classes\n     extmethods   7  add extension methods for inline classes\n        pickler   8  serialize symbol tables\n      refchecks   9  reference/override checking, translate nested objects\n        uncurry  10  uncurry, translate function values to anonymous classes\n         fields  11  synthesize accessors and fields, add bitmaps for lazy vals\n      tailcalls  12  replace tail calls by jumps\n     specialize  13  @specialized-driven class and method specialization\n  explicitouter  14  this refs to outer pointers\n        erasure  15  erase types, add interfaces for traits\n    posterasure  16  clean up erased inline classes\n     lambdalift  17  move nested functions to top level\n   constructors  18  move field definitions into constructors\n        flatten  19  eliminate inner classes\n          mixin  20  mixin composition\n        cleanup  21  platform-specific cleanups, generate reflective calls\n     delambdafy  22  remove lambdas\n            jvm  23  generate JVM bytecode\n       terminal  24  the last phase during a compilation run\n```\n\n</ChainedSnippets>\n\nYou can also view the Scala compiler help for a particular Scala version with `--scalac-help`, which is just an alias\nfor `-O -help`.\nPlease note that `-help` passed without `-O` will show the Scala CLI help instead.\n\n<ChainedSnippets>\n\n```bash\nscala-cli -S 2.13.8 --scalac-help\n```\n```text\nUsage: scalac <options> <source files>\n\nStandard options:\n  -Dproperty=value             Pass -Dproperty=value directly to the runtime system.\n  -J<flag>                     Pass <flag> directly to the runtime system.\n  -P:<plugin>:<opt>            Pass an option to a plugin\n  -V                           Print a synopsis of verbose options. [false]\n  -W                           Print a synopsis of warning options. [false]\n  -Werror                      Fail the compilation if there are any warnings. [false]\n  -X                           Print a synopsis of advanced options. [false]\n  -Y                           Print a synopsis of private options. [false]\n  -bootclasspath <path>        Override location of bootstrap class files.\n  -classpath <path>            Specify where to find user class files.\n  -d <directory|jar>           destination for generated classfiles.\n  -dependencyfile <file>       Set dependency tracking file.\n  -deprecation                 Emit warning and location for usages of deprecated APIs. See also -Wconf. [false]\n  -encoding <encoding>         Specify character encoding used by source files.\n  -explaintypes                Explain type errors in more detail. [false]\n  -extdirs <path>              Override location of installed extensions.\n  -feature                     Emit warning and location for usages of features that should be imported explicitly. See also -Wconf. [false]\n  -g:<level>                   Set level of generated debugging info. (none,source,line,[vars],notailcalls)\n  -help                        Print a synopsis of standard options [false]\n  -javabootclasspath <path>    Override java boot classpath.\n  -javaextdirs <path>          Override java extdirs classpath.\n  -language:<features>         Enable or disable language features\n  -no-specialization           Ignore @specialize annotations. [false]\n  -nobootcp                    Do not use the boot classpath for the scala jars. [false]\n  -nowarn                      Generate no warnings. [false]\n  -opt:<optimizations>         Enable optimizations, `help` for details.\n  -opt-inline-from:<patterns>  Patterns for classfile names from which to allow inlining, `help` for details.\n  -opt-warnings:<warnings>     Enable optimizer warnings, `help` for details.\n  -print                       Print program with Scala-specific features removed. [false]\n  -release <release>           Compile for a specific version of the Java platform. Supported targets: 6, 7, 8, 9\n  -rootdir <path>              The absolute path of the project root directory, usually the git/scm checkout. Used by -Wconf.\n  -sourcepath <path>           Specify location(s) of source files.\n  -target:<target>             Target platform for object files. ([8],9,10,11,12,13,14,15,16,17,18)\n  -toolcp <path>               Add to the runner classpath.\n  -unchecked                   Enable additional warnings where generated code depends on assumptions. See also -Wconf. [false]\n  -uniqid                      Uniquely tag all identifiers in debugging output. [false]\n  -usejavacp                   Utilize the java.class.path in classpath resolution. [false]\n  -usemanifestcp               Utilize the manifest in classpath resolution. [false]\n  -verbose                     Output messages about what the compiler is doing. [false]\n  -version                     Print product version and exit. [false]\n  @<file>                      A text file containing compiler arguments (options and source files) [false]\n\nDeprecated settings:\n  -optimize                    Enables optimizations. [false]\n                               deprecated: Since 2.12, enables -opt:l:inline -opt-inline-from:**. See -opt:help.\n```\n\n</ChainedSnippets>\n\n## Scala compiler plugins\n\nUse `--compiler-plugin` to add compiler plugin dependencies:\n\n```bash\nscala-cli compile Hello.scala --compiler-plugin org.typelevel:::kind-projector:0.13.3 --scala 2.13.13\n```\n\n## Printing a class path\n\n`--print-class-path` makes `scala-cli compile` print a class path:\n\n<ChainedSnippets>\n\n```bash\nscala-cli compile --print-class-path Hello.scala\n```\n\n```text\n/work/.scala/project-cef76d561e/classes:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.14/scala-library-2.12.14.jar:~/Library/Caches/ScalaCli/local-repo/0.1.0/org.virtuslab.scala-cli/runner_2.12/0.0.1-SNAPSHOT/jars/runner_2.12.jar:~/Library/Caches/ScalaCli/local-repo/0.1.0/org.virtuslab.scala-cli/stubs/0.0.1-SNAPSHOT/jars/stubs.jar\n```\n\n</ChainedSnippets>\n\nThis is handy when working with other tools.\nFor example, you can pass this class path to `java -cp`:\n\n<ChainedSnippets>\n\n```bash\njava -cp \"$(scala-cli compile --print-class-path Hello.scala)\" Hello\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nNote that you should favor the [`run`](./run.md) command to run your code, rather than running `java -cp`.\nThe class path obtained this way is only meant for scenarios where Scala CLI doesn't offer a more convenient option.\n\nIf you need the class path to consist only of JAR files, pass `--as-jar`. This packages the Scala CLI project\nbyte code in a JAR file, rather than leaving it in a directory:\n\n```bash ignore\nscala-cli compile --print-class-path Hello.scala --as-jar\n```\n\n```text\n/work/.scala-build/project_103be31561-475e1607f5/jar/library.jar:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.2.2/scala3-library_3-3.2.2.jar:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar\n```\n\n### JVM options\n\n`--javac-opt` lets you add `javac` options which will be passed when compiling sources.\n\n```bash\nscala-cli Hello.scala --javac-opt source --javac-opt 1.8 --javac-opt target --javac-opt 1.8\n```\n\nYou can also add javac options with the using directive `//> using javacOpt`:\n\n```scala compile\n//> using javacOpt source 1.8 target 1.8\n```\n\n## Exclude sources\n\nTo exclude specific source files or entire directories from a Scala CLI project, use the `exclude` directive or command\nline parameter `--exclude` along with a pattern:\n\n- an absolute path: `/root/path/to/your/project/Main.scala`\n- a relative path: `src/main/scala/Main.scala`\n- a glob pattern: `*.sc`\n\n:::note\nThe `exclude` directive should be placed in your `project.scala` file, which Scala CLI uses to determine the project\nroot directory.\nFor more details on `project.file`, see [the `Project root directory` reference](../reference/root-dir.md).\n:::\n\nFor example, to exclude all files in the `example/scala` directory, add the following directive to your\n `project.file` file:\n\n```scala title=project.scala\n//> using exclude example/scala\n```\n\n## Compile-Only Dependencies\n\nCompile-only dependencies, allow to include certain libraries exclusively at the time of the compilation. These\ndependencies are added to the class path during compilation, but won't be included when the application is run.\n\nTo declare a compile-only dependency, you should use the `compileOnly.dep` directive or `--compile-lib` command line\noption. For instance, to include the `jsoniter-scala-macros` library at compile-time, you would use:\n\n```scala title=CompileOnly.scala\n//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\n```\n\nor by using the `--compile-lib` command line option:\n\n```bash\nscala-cli Hello.scala --compile-lib \"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\"\n```"
  },
  {
    "path": "website/docs/commands/completions.md",
    "content": "---\ntitle: Completions\nsidebar_position: 19\n---\n\nThe completions commands can be used to install Scala CLI completions into your shell and uninstall them.\n\n## Installing completions\nTo install completions run the `install-completions` command:\n\n```bash ignore\nscala-cli install-completions\n```\n\n## Uninstalling completions\nTo uninstall completions run the `uninstall-completions` command:\n\n```bash ignore\nscala-cli uninstall-completions\n```\n"
  },
  {
    "path": "website/docs/commands/config.md",
    "content": "---\ntitle: Config\nsidebar_position: 17\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nThe `config` sub-command makes it possible to get and set various configuration values, used by\nother Scala CLI sub-commands.\n\nThe full list of the available configuration keys is available in [the reference docs](../reference/commands.md#config).\n\nExamples of use:\n<ChainedSnippets>\n\n```bash ignore\nscala-cli config power true\nscala-cli config power\n```\n\n```text\ntrue\n```\n\n</ChainedSnippets>\n\n:::caution\nEven though the `config` command is not restricted, some available configuration keys may be, and thus may\nrequire setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n```bash ignore\nscala-cli config power true\n```\n:::\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power config publish.user.name \"Alex Me\"\nscala-cli --power config publish.user.name\n```\n\n```text\nAlex Me\n```\n\n</ChainedSnippets>\n\nThe `--dump` option allows to print all config entries in JSON format:\n<ChainedSnippets>\n\n```bash\nscala-cli config --dump | jq .\n```\n\n```json\n{\n  \"github\": {\n    \"token\": \"value:qWeRtYuIoP\"\n  },\n  \"pgp\": {\n    \"public-key\": \"value:-----BEGIN PGP PUBLIC KEY BLOCK-----\\nVersion: BCPG v1.68\\n\\n…\\n-----END PGP PUBLIC KEY BLOCK-----\\n\",\n    \"secret-key\": \"value:…\",\n    \"secret-key-password\": \"value:1234\"\n  },\n  \"user\": {\n    \"email\": \"alex@alex.me\",\n    \"name\": \"Alex Me\",\n    \"url\": \"https://alex.me\"\n  }\n}\n```\n\n</ChainedSnippets>\n\nUse `--password-value` to get the value of a password entry:\n\n<ChainedSnippets>\n\n```bash\nexport MY_GITHUB_TOKEN=1234\nscala-cli --power config github.token \"env:MY_GITHUB_TOKEN\"\nscala-cli --power config github.token\n```\n\n```text\nenv:MY_GITHUB_TOKEN\n```\n\n```bash\nexport MY_GITHUB_TOKEN=1234\nscala-cli --power config --password-value github.token\n```\n\n```text\n1234\n```\n\n</ChainedSnippets>\n\nUse `--create-pgp-key` to create a PGP key pair, protected by a randomly-generated password, to\nbe used by the `publish setup` sub-command:\n\n```sh\nscala-cli --power config --create-pgp-key --pgp-password MY_CHOSEN_PASSWORD --email \"some_email\"\n```\n\nIt's not mandatory, although recomended, to use a password to encrypt your keychains.\nTo store the private keychain in an unencrypted form use `--pgp-password none`.\nTo randomly generate a pasword, use `--pgp-password random` instead.\nAlso, the `--email` option or `publish.user.email` has to be specified for this subcommand to work properly.\n\nConfiguration values are stored in a directory under your home directory, with restricted permissions:\n\n- on macOS: `~/Library/Application Support/ScalaCli/secrets/config.json`\n- on Linux: `~/.config/scala-cli/secrets/config.json`\n- on Windows: `%LOCALAPPDATA%\\ScalaCli\\data\\secrets\\config.json` (\n  typically `C:\\Users\\username\\AppData\\Local\\ScalaCli\\data\\secrets\\config.json`)\n"
  },
  {
    "path": "website/docs/commands/doc.md",
    "content": "---\ntitle: Doc\nsidebar_position: 18\n---\n\nScala CLI can generate the API documentation of your Scala 2, Scala 3, and Java projects. It provides features similar\nto `javadoc`.\nThe API documentation is generated in a directory whose files make up a static website:\n\n```scala title=Hello.scala\npackage hello\n\n/** Hello object for running main method\n */\nobject Hello {\n  /**\n   * Main method\n   * @param args The command line arguments.\n   * */\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n```bash\nscala-cli doc Hello.scala -o scala-doc\n# Wrote Scaladoc to ./scala-doc\n```\n\n<!-- Expected\nWrote Scaladoc to ./scala-doc\n-->\n\nThe output directory `scala-doc` contains the static site files with your documentation.\n\n## Cross-building documentation ⚡️\n\n:::caution\nThe `--cross` option is experimental and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n\n:::\n\nUse `--cross` (with `--power`) to build and generate Scaladoc for **every** Scala version and platform combination\nconfigured for your project—the same behavior as `run` and `package` with `--cross`. This is useful when you have\nmultiple Scala versions or platforms and want documentation for each.\n\nExample: a library that supports both Scala 2.13 and 3.3 LTS:\n\n```scala title=Example.scala\n//> using scala 2.13 3.3.7\npackage lib\n\n/** Example class for cross-built documentation. */\nclass Example {\n  /** Returns a greeting. */\n  def greet: String = \"Hello\"\n}\n```\n\nWhen `--cross` produces multiple cross builds, the output directory is split into one subdirectory per combination: by\ndefault a subdirectory per Scala version (e.g. `doc-out/2.13.18`, `doc-out/3.3.7`), and when targeting multiple\nplatforms, each subdirectory name includes the platform (e.g. `doc-out/3.3.7_jvm`). This avoids overwriting docs from\ndifferent builds.\n\n```bash\nscala-cli --power doc --cross . -o doc-out\n# Wrote Scaladoc to doc-out/2.13.18\n# Wrote Scaladoc to doc-out/3.3.7\n```\n\nWithout `--cross`, only a single build (the default Scala version and platform) is documented and written to the given\noutput path.\n\nAfter opening the generated static documentation (you have to open `scala-doc/index.html` in your browser),\nyou will see the generated scaladoc documentation. The following screen shows the definition of the `main` method:\n\nimport ScalaDocMainMethod from '@site/static/img/scala-doc-main-method.png';\n\n<img src={ScalaDocMainMethod} />"
  },
  {
    "path": "website/docs/commands/export.md",
    "content": "---\ntitle: Export ⚡️\nsidebar_position: 27\n---\n\nIn case your project outgrows the capabilities of Scala CLI (e.g support for modules) it may be beneficial\nto switch to a build tool such as SBT or Mill.\nThe `export` sub-command allows to do that by converting a Scala CLI project into an SBT or Mill configuration.\nAdditionally the sub-command supports the JSON format for custom analysis of projects.\n\nResults of running the with `--mill` or `--sbt` sub-command are, by default, put in `./dest/`,\nthat behaviour can be modified by specifying a path with the `--output` option.\n\nThe default behaviour of exporting with the `--json` option is printing to standard output.\n\n:::caution\nThe `export` sub-command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\n:::caution\nThe `export` sub-command is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nThe project configuration is read both from information specified in source files\nas well as options passed to the `export` sub-command.\n\nLet's take a simple one-file project as an example:\n```scala title=Hello.scala\n//> using scala 3.1.3\n//> using option -Xasync\n//> using dep com.lihaoyi::os-lib:0.9.0\n\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(os.pwd)\n}\n```\n\n# Exporting to SBT:\n\n```bash\nscala-cli --power export Hello.scala --sbt\n```\nNote that `--sbt` is not required here since it's the default.\n\nThe result is an sbt-compliant project created in the `dest/` directory:\n\n```\ndest\n├── project\n│   └── build.properties\n├── src\n│   └── main\n│       └── scala\n│           └── Hello.scala\n└── build.sbt\n```\n\nAll the project's configuration resides now in `dest/build.sbt`:\n\n\n```scala title=dest/build.sbt\nscalaVersion := \"3.1.3\"\n\nscalacOptions ++= Seq(\"-Xasync\")\n\nlibraryDependencies += \"com.lihaoyi\" %% \"os-lib\" % \"0.9.0\" \n\nlibraryDependencies += \"com.lihaoyi\" %% \"os-lib\" % \"0.9.0\" % Test\n\n```\n\n\nTo configure the version of SBT used in the new project provide the `--sbtVersion` option to the `export` sub-command.\n\n# Exporting to Mill:\n\n```bash\nscala-cli --power export Hello.scala --mill --output=dest_mill\n```\nMill is not the default `export` format, so passing the `--mill` option is required.\n\nBy specifying the path with `--output` option the results are now created in `dest_mill/` directory:\n\n```\ndest_mill\n├── project\n│   └── src\n│       └── Hello.scala\n├── .mill-version\n├── build.sc\n├── mill\n└── mill.bat\n```\n\nAnd all the project's configuration resides now in `dest_mill/build.sc`:\n\n```scala title=dest_mill/build.sc\nimport mill._\nimport mill.scalalib._\nobject project extends ScalaModule {\n  def scalaVersion = \"3.1.3\"\n  def scalacOptions = super.scalacOptions() ++ Seq(\"-Xasync\")\n  def ivyDeps = super.ivyDeps() ++ Seq(\n    ivy\"com.lihaoyi::os-lib:0.9.0\"\n  )\n\n  object test extends Tests {\n    def ivyDeps = super.ivyDeps() ++ Seq(\n      ivy\"com.lihaoyi::os-lib:0.9.0\"\n    )\n  }\n}\n\n```\n\nThe script files `mill` and `mill.bat` are mill wrappers fetched from [the Mill repository](https://github.com/com-lihaoyi/mill).\nTo change the build tool version used override the contents of `dest_mill/.mill-version`.\n\n\n# Exporting to JSON:\n\nTo export project information in a human-comprehensible format, use the `--json` flag.\nBy default, exporting with the `--json` option prints to standard output, this can be changed\nwith `--output` parameter by specifying a directory where to create the `export.json` file.\n\n```bash\nscala-cli --power export Hello.scala --json --output=dest_json\n```\n\nThe result is the `dest_json/export.json` file:\n\n```json title=dest_json/export.json\n{\n \"scalaVersion\": \"3.1.3\",\n \"platform\": \"JVM\",\n \"scopes\": {\n  \"main\": {\n   \"sources\": [\n    \"Foo.scala\"\n   ],\n   \"scalacOptions\": [\n    \"-Xasync\"\n   ],\n   \"dependencies\": [\n    {\n     \"groupId\": \"com.lihaoyi\",\n     \"artifactId\": {\n      \"name\": \"os-lib\",\n      \"fullName\": \"os-lib_3\"\n     },\n     \"version\": \"0.9.0\"\n    }\n   ],\n   \"resolvers\": [\n    \"https://repo1.maven.org/maven2\",\n    \"ivy:file:///Users/mgajek/Library/Caches/ScalaCli/local-repo/v0.1.20-111-648755-DIRTY2ba64fdc//[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]\",\n    \"ivy:file:/Users/mgajek/.ivy2/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]\"\n   ]\n  }\n }\n}\n```\n"
  },
  {
    "path": "website/docs/commands/fix.md",
    "content": "---\ntitle: Fix ⚡️\nsidebar_position: 28\n---\n\n:::caution\nThe Fix command is experimental and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\nThe `fix` command is used to check, lint, rewrite or otherwise rearrange code in a Scala CLI project.\n\nCurrently, the following sets of rules are supported:\n- built-in rules (enabled automatically and controlled with the `--enable-built-in` flag)\n- `scalafix`, running [Scalafix](https://scalacenter.github.io/scalafix/) under the hood (enabled automatically and controlled with `--enable-scalafix` flag).\n\nYou can disable unnecessary rule sets when needed.\nFor example, to disable built-in rules, you can run:\n```bash\nscala-cli fix . --power --enable-built-in=false\n```\n\n## Built-in rules\n\nCurrently, the only built-in rule is extraction of `using` directives into the `project.scala` configuration file.\nThis allows to fix warnings tied to having `using` directives present in multiple files and eliminate duplicate directives.\nFiles containing (experimental) `using target` directives, e.g. `//> using target.scala 3.0.0` will not be changed by `fix`.\nThe original scope (`main` or `test`) of each extracted directive is respected. `main` scope directives are transformed \nthem into their `test.*` equivalent when needed.\n\nExceptions:\n- directives won't be extracted for single-file projects;\n- directives in test inputs with no test scope equivalents won't be extracted to preserve their initial scope.\n\n## `scalafix` integration\n\nScala CLI is capable of running [Scalafix](https://scalacenter.github.io/scalafix/) (a refactoring and linting tool for Scala) on your project.\nBefore using this command you need to provide the configuration at `.scalafix.conf`.\nFor example:\n``` text title=.scalafix.conf\n// .scalafix.conf\nrules = [\n  DisableSyntax\n]\n```\n\nThen you can run it:\n```bash\nscala-cli fix . --power\n```\n\nIf you’re setting up a continuous integration (CI) server, Scala CLI also has you covered.\nYou can run linter using the `--check` flag:\n```bash fail\nscala-cli fix --check . --power\n```\n\nRead more about Scalafix:\n- [Configuration](https://scalacenter.github.io/scalafix/docs/users/configuration.html)\n- [Rules](https://scalacenter.github.io/scalafix/docs/rules/overview.html)\n\n### Using external rules\n\nAdding an [external scalafix rule](https://scalacenter.github.io/scalafix/docs/rules/external-rules.html) to Scala CLI can be done with the [`scalafix.dep`](./compile.md#compile-only-dependencies) directive:\n```scala compile power\n//> using scalafix.dep com.github.xuwei-k::scalafix-rules:0.5.1\n```"
  },
  {
    "path": "website/docs/commands/fmt.md",
    "content": "---\ntitle: Format\nsidebar_position: 15\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nScala CLI supports formatting your code using [Scalafmt](https://scalameta.org/scalafmt/):\n\n```bash\nscala-cli fmt .\n```\n\nUnder the hood, Scala CLI downloads and runs Scalafmt on your code.\n\nIf you’re setting up a continuous integration (CI) server, Scala CLI also has you covered.\nYou can check formatting correctness using a `--check` flag:\n\n```bash fail\nscala-cli fmt --check .\n```\n\n### Scalafmt version and dialect\n\nScala CLI `fmt` command supports passing the `scalafmt` **version** and **dialect** directly from the command line, using the `--scalafmt-dialect` and `--scalafmt-version` options respectively:\n```\nscala-cli fmt . --scalafmt-dialect scala3 --scalafmt-version 3.5.8\n```\nYou can skip passing either of those, which will make Scala CLI infer a default value:\n- If a `.scalafmt.conf` file is present in the workspace and it has the field defined, the value will be read from there, unless explicitly specified with Scala CLI options.\n- Otherwise, the default `scalafmt` **version** will be the latest one used by your Scala CLI version (so it is subject to change when updating Scala CLI). The default **dialect** will be inferred based on the Scala version (defined explicitly by `-S` option, or default version if the option is not passed).\n\nIt is possible to pass the configuration as a string directly from the command line, using `--scalafmt-conf-str` option. If the configuration is passed this way, Scala CLI will behave exactly the same as if it found the specified configuration in the `.scalafmt.conf` file in the workspace.\n\n#### Example 1\n\n``` text title=.scalafmt.conf\nversion = \"3.5.8\"\nrunner.dialect = scala212\n```\n\n```bash\nscala-cli fmt --scalafmt-dialect scala213 .\n```\n\nFor the setup above, `fmt` will use:\n- `version=\"3.5.8\"` from the file\n- `dialect=scala213`, because passed `--scalafmt-dialect` option overrides dialect found in the file\n\n#### Example 2\n\n``` text title=.scalafmt.conf\nversion = \"2.7.5\"\n```\n\n```bash\nscala-cli fmt --scalafmt-version 3.5.8 .\n```\n\nFor the setup above, `fmt` will use:\n- `version=\"3.5.8\"`, because passed `--scalafmt-version` option overrides version from the file\n- `dialect=scala3`, because dialect is neither passed as an option nor is it present in the configuration file, so it is inferred based on the Scala version; the Scala version wasn't explicitly specified in the command either, so it falls back to the default Scala version - the latest one, thus the resulting dialect is `scala3`. \n\n### Scalafmt options\n\nIt is possible to pass native `scalafmt` options with the `-F` (short for `--scalafmt-arg`), for example:\n\n<ChainedSnippets>\n\n```bash\nscala-cli fmt -F --version\n```\n\n```text\nscalafmt 3.5.8\n```\n\n</ChainedSnippets>\n\nFor the available options please refer to `scalafmt` help, which can be viewed with the `--scalafmt-help` option (which\nis just an alias for `-F --help`):\n\n<ChainedSnippets>\n\n```bash\nscala-cli fmt --scalafmt-help\n```\n\n```text\nscalafmt 3.5.8\nUsage: scalafmt [options] [<file>...]\n\n  -h, --help               prints this usage text\n  -v, --version            print version \n(...)\n```\n\n</ChainedSnippets>\n\n### Excluding sources\n\nBecause of the way Scala CLI invokes `scalafmt` under the hood, sources are always being passed to it explicitly. This\nin turn means that regardless of how the sources were passed, `scalafmt` exclusion paths (the `project.excludePaths`)\nwould be ignored. In order to prevent that from happening, the `--respect-project-filters` option is set to `true` by\ndefault.\n\n```text title=.scalafmt.conf\nversion = \"3.5.8\"\nrunner.dialect = scala3\nproject {\n  includePaths = [\n    \"glob:**.scala\",\n    \"regex:.*\\\\.sc\"\n  ]\n  excludePaths = [\n    \"glob:**/should/not/format/**.scala\"\n  ]\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli fmt . --check\n```\n\n```text\nAll files are formatted with scalafmt :)\n```\n\n</ChainedSnippets>\n\nYou can explicitly set it to false if you want to disregard any filters configured in the `project.excludePaths` setting\nin your `.scalafmt.conf` for any reason.\n\n<ChainedSnippets>\n\n```bash\nscala-cli fmt . --check --respect-project-filters=false\n```\n\n```text\n--- a/.../should/not/format/ShouldNotFormat.scala\n+++ b/.../should/not/format/ShouldNotFormat.scala\n@@ -1,3 +1,3 @@\n class ShouldNotFormat {\n-                       println()\n+  println()\n }\n```\n\n</ChainedSnippets>\n\n### How `.scalafmt.conf` file is generated\n\nThe Scala CLI `fmt` command runs `scalafmt` under the hood, which *normally* requires `.scalafmt.conf` configuration file with explicitly specified **version** and **dialect** fields. The way it is handled by Scala CLI is as follows:\n\nAt the beginning `fmt` looks for the configuration inside the file specified in the `--scalafmt-conf` option. If the option is not passed or the file doesn't exist, `fmt` looks for the existing configuration file inside **current workspace** directory. If the file is still not found, `fmt` looks for it inside **git root** directory. There are 3 possible cases:\n\n1. Configuration file with the specified version and dialect is found.\n2. Configuration file is found, but it doesn't have specified version or dialect.\n3. Configuration file is not found.\n\n- In the **first** case `fmt` uses the found `.scalafmt.conf` file to run `scalafmt`.\n- In the **second** case `fmt` creates a `.scalafmt.conf` file inside the `.scala-build` directory. Content of the previously found file is copied into the newly created file, missing parameters are [inferred](#scalafmt-version-and-dialect) and written into the same file. Created file is used to run `scalafmt`. \n- In the **third** case `fmt` creates a `.scalafmt.conf` file inside the `.scala-build` directory, writes [inferred](#scalafmt-version-and-dialect) version and dialect into it and uses it to run `scalafmt`.\n\nIf the `--save-scalafmt-conf` option is passed, then `fmt` command behaves as follows:\n- In the **first** case `fmt` uses the found `.scalafmt.conf` file to run `scalafmt`.\n- In the **second** case `fmt` [infers](#scalafmt-version-and-dialect) missing parameters, writes them directly into the previously found file and then uses this file to run `scalafmt`.\n- In the **third** case `fmt` creates a `.scalafmt.conf` file in the current workspace directory, writes [inferred](#scalafmt-version-and-dialect) version and dialect into it and uses it to run `scalafmt`.\n\n:::note\nIf the configuration is passed in the `--scalafmt-conf-str` option, Scala CLI will behave exactly the same as if it found the specified configuration in a `.scalafmt.conf` file in the workspace.\n:::"
  },
  {
    "path": "website/docs/commands/misc/_category_.json",
    "content": "{\n  \"label\": \"Miscellaneous ⚡️\",\n  \"position\": 31\n}\n"
  },
  {
    "path": "website/docs/commands/misc/bloop.md",
    "content": "---\ntitle: Bloop ⚡️\nsidebar_position: 10\n---\n\nScala CLI by default uses Bloop as a build server for compiling code. This approach has its advantages over the `scalac` compiler such as advanced caching and fast compile times, but the process is more complex.\nFortunately for the users, Scala CLI fully manages the Bloop build server. This includes its whole lifecycle, which starts with fetching the artifacts.\nThis document showcases the `bloop` subcommand that allows you manually manage the Bloop server.\nIt also goes through the server's lifecycle and the interactions that Scala CLI has with it.\n\n:::caution\nThe `bloop` sub-command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\n### Starting the server\n\nWhenever the code is compiled using Bloop, the first step is checking if the server is online, as it is launched as a daemon thread there's a chance it may have been launched during a past compilation.\nHowever, if the server is offline then Scala CLI needs to start and configure it.\n\nThe configuration file for Bloop is created after analyzing the options collected from command line flags and using directives.\nThe default location of the file is `.scala-build/bloop/project_name.json`.\n\nThe last thing before launching the server is downloading its artifacts from Maven Central via Coursier if they are not already present in the local cache.\n:::tip\nWhen working in an environment with restricted access to the web, using Bloop can be disabled with the `--server=false` flag. Also, see the [section about the Offline mode](../../guides/power/offline.md).\n:::\n\nBloop is started as a separate JVM process, parameters of this process can be configured using arguments passed to the invoked subcommand ([see compilation server options](../../reference/cli-options.md#compilation-server-options)).\nThey also depend on the JVM version chosen for building the project, it cannot be higher than the version of the JVM running Bloop. If such a case is detected, the build server has to be restarted with a sufficiently high JVM.\nNote that the default version of the JVM for Bloop is 17, so if your `JAVA_HOME` refers to an older version of Java, Scala CLI will fetch the one you need. You can also override the JVM version Bloop runs on with the `--bloop-jvm` option.\nTo start the Bloop server manually you can use the `bloop start` subcommand:\n```bash\nscala-cli --power bloop start\n```\n\n### Communicating with the server\n\nDuring the communication process, Scala CLI acts mostly as an intermediary between Bloop and the build client.\nThe build client can be either the user invoking the tool from the command line or the IDE seeing Scala CLI as a build server.\nThe behavior is mostly the same in both cases and is based on forwarding the messages. The messages being forwarded need to sometimes be edited as a result of preprocessing Scala CLI does, e.g. generating script sources.\n\nThe main difference between running on the command line and serving an IDE is the information that gets through to the client.\nWhile an IDE receives all the messages that Bloop sends, the user only receives the relevant information, like warnings and errors coming from the compilation process.\n\n### Killing the server\n\nIn general, the Bloop server is started as a daemon process that sticks around even after Scala CLI exits.\nThe server can sometimes be automatically killed and restarted if a configuration change requires that, e.g. JVM version requested by the build is too high.\n\nHowever, sometimes it is needed to restart the Bloop server manually, for that the `bloop exit` subcommand can be used:\n```bash\nscala-cli --power bloop exit\n```\n"
  },
  {
    "path": "website/docs/commands/misc/default-file.md",
    "content": "---\ntitle: Default File ⚡️\nsidebar_position: 20\n---\n\n:::caution\nThe Default File is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\nThe `default-file` sub-command provides sensible default content for files\nsuch as `.gitignore` or for GitHub actions workflows, for Scala CLI projects.\n\nTo list the available files, pass it `--list`:\n```text\n$ scala-cli default-file --list\n.gitignore\n.github/workflows/ci.yml\n```\n\nGet the content of a default file with\n```text\n$ scala-cli default-file .gitignore\n/.bsp/\n/.scala-build/\n```\n\nOptionally, write the content of one or more default files by passing `--write`:\n```text\n$ scala-cli default-file --write .gitignore .github/workflows/ci.yml\nWrote .gitignore\nWrote .github/workflows/ci.yml\n```\n"
  },
  {
    "path": "website/docs/commands/misc/pgp.md",
    "content": "---\ntitle: PGP ⚡️\nsidebar_position: 30\n---\n\n:::caution\nThe PGP command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\nThe `pgp` sub-commands are low-level commands, exposing the PGP capabilities of\nScala CLI. These capabilities are used in the `publish` and `publish setup` commands\nin particular.\n\nThese commands make it possible to\n- create PGP keys with `pgp create`\n- get a key fingerprint with `pgp key-id`\n- push them to / pull them from key servers with `pgp push` / `pgp pull`\n- sign files with `pgp sign`\n- verify signatures with `pgp verify`\n\nThese capabilities rely on the [Bouncy Castle library](https://www.bouncycastle.org).\nNote that sub-commands relying on signing, such as `publish`, also allow signing\nto be handled using `gpg`.\n\n## Create key pairs\n\nIt's not mandatory, although recomended, to use a password to encrypt your keychains.\n\n```text\n$ scala-cli pgp create --email alex@alex.me --password env:MY_PASSWORD\nWrote public key e259e7e8a23475b3 to key.pub\nWrote secret key to key.skr\n```\n\nSee [the dedicated page](../../reference/password-options.md) for the various formats\naccepted by the `--password` option.\n\n## Get the fingerprint of a public key\n\n```text\n$ scala-cli pgp key-id ./key.pub\ne259e7e8a23475b3\n```\n\n## Push public keys to key servers\n\n```text\n$ scala-cli pgp push key.pub\nKey 0xe259e7e8a23475b3 uploaded to http://keyserver.ubuntu.com:11371\n```\n\n## Pull public keys from key servers\n\n```text\n$ scala-cli pgp pull 0x914d298df8fa4d20\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n…\n-----END PGP PUBLIC KEY BLOCK-----\n```\n\n## Sign files\n\n```text\n$ scala-cli pgp sign --secret-key file:./key.skr --password value:1234 ./foo\n$ cat ./foo.asc\n-----BEGIN PGP MESSAGE-----\n…\n-----END PGP MESSAGE-----\n\n$ scala-cli pgp sign --secret-key file:./key.skr --password value:1234 ./foo --stdout\n-----BEGIN PGP MESSAGE-----\n…\n-----END PGP MESSAGE-----\n```\n\n## Verify signatures\n\n```text\n$ scala-cli pgp verify --key key.pub foo.asc\nfoo.asc: valid signature\n```\n"
  },
  {
    "path": "website/docs/commands/package.md",
    "content": "---\ntitle: Package ⚡️\nsidebar_position: 29\n---\n\n:::caution\nThe Package command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nThe `package` command can package your Scala code in various formats, such as:\n- [lightweight launcher JARs](#default-package-format)\n- [standard library JARs](#library-jars)\n- so called [\"assemblies\" or \"fat JARs\"](#assemblies)\n- [docker container](#docker-container)\n- [JavaScript files](#scalajs) for Scala.js code\n- [GraalVM native image executables](#native-image)\n- [native executables](#scala-native) for Scala Native code\n- [OS-specific formats](#os-specific-packages), such as deb or rpm (Linux), pkg (macOS), or MSI (Windows)\n\n## Default package format\n\nThe default package format writes a *lightweight launcher JAR*, like the \"bootstrap\" JAR files [generated by coursier](https://get-coursier.io/docs/cli-bootstrap#bootstraps).\nThese JARs tend to have a small size (mostly containing only the byte code from your own sources),\ncan be generated fast,\nand download their dependencies upon first launch via [coursier](https://get-coursier.io).\n\nSuch JARs can be copied to other machines, and will run fine there.\nTheir only requirement is that the `java` command needs to be available in the `PATH`:\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package Hello.scala -o hello\n./hello\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected\nHello\n-->\n\n## Watch mode\n\nUse `--watch` to rebuild the package whenever sources change:\n\n```bash ignore\nscala-cli --power package --watch Hello.scala -o hello\n```\n\nYou can watch additional inputs too:\n\n```bash ignore\nscala-cli --power package --watch --watching ./data Hello.scala -o hello\n```\n\nThis also works from sources:\n\n```scala\n//> using watching ./data\n```\n\nWhen both are present, Scala CLI watches all of the configured paths.\n\n## Library JARs\n\n*Library JARs* are suitable if you plan to put the resulting JAR in a class path, rather than running it as is.\nThese follow the same format as the JARs of libraries published to Maven Central:\n\n```scala title=MyLibrary.scala\npackage mylib\n\nclass MyLibrary {\n  def message = \"Hello\"\n}\n```\n\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package MyLibrary.scala -o my-library.jar --library\njavap -cp my-library.jar mylib.MyLibrary\n```\n\n```text\nCompiled from \"MyLibrary.scala\"\npublic class mylib.MyLibrary {\n  public java.lang.String message();\n  public mylib.MyLibrary();\n}\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nMyLibrary.scala\npublic class mylib.MyLibrary\npublic java.lang.String message();\npublic mylib.MyLibrary();\n-->\n\n## Assemblies\n\n*Assemblies* blend your dependencies and your sources' byte code together in a single JAR file.\nAs a result, assemblies can be run as is, just like [bootstraps](#default-package-format), but don't need to download\nanything upon first launch.\nBecause of that, assemblies also tend to be bigger, and somewhat slower to generate:\n\n<!-- clear -->\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package Hello.scala -o hello --assembly\n./hello\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello\n-->\n\nBy default assemblies are self-executable, just like the default package format. With the `--preamble=false` option, you can build an assembly that just contains the JAR and does not contain any built-in Bash code, and therefore can be launched directly with Java and is more portable:\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package Hello.scala -o hello.jar --assembly --preamble=false\njava -jar hello.jar\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello\n-->\n\n## Docker container\n\nScala CLI can create an executable application and package it into a docker image.\n\nFor example, here’s an application that will be executed in a docker container:\n\n```scala title=HelloDocker.scala\nobject HelloDocker extends App {\n  println(\"Hello from Docker\")\n}\n```\n\nPassing `--docker` to the `package` sub-command generates a docker image.\nThe docker image name parameter `--docker-image-repository` is mandatory.\n\nThe following command generates a `hello-docker` image with the `latest` tag:\n\n```bash ignore\nscala-cli --power package --docker HelloDocker.scala --docker-image-repository hello-docker\n```\n\n<ChainedSnippets>\n\n```bash ignore\ndocker run hello-docker\n```\n\n```text\nHello from Docker\n```\n\n</ChainedSnippets>\n\nYou can also create Docker images for Scala.js and Scala Native applications.\nThe following command shows how to create a Docker image (`--docker`) for a Scala.js (`--js`) application:\n\n```bash ignore\nscala-cli --power package --js --docker HelloDocker.scala --docker-image-repository hello-docker\n```\n\nPackaging Scala Native applications to a Docker image is only supported on Linux.\n\nThe following command shows how to do that:\n\n```bash ignore\nscala-cli --power package --native --docker HelloDocker.scala --docker-image-repository hello-docker\n```\n\n### Building Docker container from base image\n\n`--docker-from` lets you specify your base docker image.\n\nThe following command generate a `hello-docker` image using base image `openjdk:11`\n\n```bash ignore\nscala-cli --power package --docker HelloDocker.scala --docker-from openjdk:11 --docker-image-repository hello-docker\n```\n\n## Scala.js\n\nPackaging Scala.js applications results in a `.js` file, which can be run with `node`:\n\n<!-- TODO: add something js specific -->\n\n```scala title=HelloJs.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package --js HelloJs.scala -o hello.js\nnode hello.js\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello\n-->\n\n\nNote that Scala CLI doesn't offer the ability to link the resulting JavaScript with linkers, such as Webpack (yet).\n\n## Native image\n\n[GraalVM native image](https://www.graalvm.org/22.0/reference-manual/native-image/)\nmakes it possible to build native executables out of JVM applications. It can\nbe used from Scala CLI to build native executables for Scala applications.\n\n<!-- clear -->\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package Hello.scala -o hello --native-image\n./hello\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello\n-->\n\nNote that Scala CLI automatically downloads and unpacks a GraalVM distribution\nusing the [JVM management capabilities of coursier](https://get-coursier.io/docs/cli-java).\n\nSeveral options can be passed to adjust the GraalVM version used by Scala CLI:\n- `--graalvm-jvm-id` accepts a JVM identifier, such as `graalvm-community:17.0.9` or `graalvm-java25:25.0.1`.\n- `--graalvm-version` makes it possible to specify only a GraalVM version, such as `22.0.0` or `21` (short versions accepted)\n- `--graalvm-args` makes it possible to pass args to GraalVM version\n\n## Scala Native\n\nPackaging a Scala Native application results in a native executable:\n\n<!-- clear -->\n\n<!-- TODO: add something native specific -->\n\n```scala title=HelloNative.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package --native HelloNative.scala -S 2.13 -o hello\nfile hello\n```\n\n```text\nhello: Mach-O 64-bit executable x86_64\n```\n\n```bash\n./hello\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello\n-->\n\n## OS-specific packages\n\nScala CLI also lets you package Scala code as OS-specific packages.\nThis feature is somewhat experimental, and supports the following formats, provided they're compatible with the operating system you're running Scala CLI on:\n\n- [DEB](#debian) (Linux)\n- [RPM](#redhat) (Linux)\n- [PKG](#macos-pkg) (macOS)\n- [MSI](#windows) (Windows)\n\n```scala Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli --power package --deb Hello.scala -o hello.deb\nfile hello\n```\n\n```text\nhello: Mach-O 64-bit executable x86_64\n```\n\n```bash ignore\n./hello\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n### Debian\n\nDEB is the package format for the Debian Linux distribution.\nTo build a Debian package, you will need to have [`dpkg-deb`](http://manpages.ubuntu.com/manpages/trusty/pl/man1/dpkg-deb.1.html) installed.\n\nExample:\n\n```bash ignore\nscala-cli --power package --deb --output 'path.deb' Hello.scala\n```\n\n#### Mandatory arguments\n* version\n* maintainer\n* description\n* output-path\n\n#### Optional arguments\n* force\n* launcher-app\n* debian-conflicts\n* debian-dependencies\n* architecture\n\n### RedHat\n\nRPM is the software package format for RedHat distributions.\nTo build a RedHat Package, you will need to have [`rpmbuild`](https://linux.die.net/man/8/rpmbuild) installed.\n\nExample:\n\n```bash ignore\nscala-cli --power package --rpm --output 'path.rpm' Hello.scala\n```\n\n#### Mandatory arguments\n* version\n* description\n* license\n* output-path\n\n#### Optional arguments\n* force\n* launcher-app\n* release\n* rpm-architecture\n\n### macOS (PKG)\n\nPKG is a software package format for macOS.\nTo build a PKG you will need to have [`pkgbuild`](https://www.unix.com/man-page/osx/1/pkgbuild/) installed.\n\nExample:\n\n```bash ignore\n`scala-cli --power package --pkg --output 'path.pkg` Hello.scala\n```\n\n#### Mandatory arguments\n* version\n* identifier\n* output-path\n\n#### Optional arguments\n* force\n* launcher-app\n\n### Windows\n\nMSI is a software package format for Windows.\nTo build an MSI installer, you will need to have [`WIX Toolset`](https://wixtoolset.org/) installed.\n\nExample:\n\n```cmd\nscala-cli --power package --msi --output path.msi Hello.scala\n```\n\n#### Mandatory arguments\n* version\n* maintainer\n* licence-path\n* product-name\n* output-path\n\n#### Optional arguments\n* force\n* launcher-app\n* exit-dialog\n* logo-path\n\n## Using directives\n\nInstead of passing the `package` options directly from bash, it is possible to pass some of them with [using directives](../guides/introduction/using-directives.md).\n\n### packaging.packageType\n\nThis using directive makes it possible to define the type of the package generated by the `package` command. For example:\n\n```scala compile power\n//> using packaging.packageType assembly\n```\n\nAvailable types: `assembly`, `raw-assembly`, `bootstrap`, `library`, `source`, `doc`, `spark`, `js`, `native`, `docker`, `graalvm`, `deb`, `dmg`, `pkg`, `rpm`, `msi`.\n\n### packaging.output\n\nThis using directive makes it possible to define the destination path of the package generated by the `package` command. For example:\n\n```scala compile power\n//> using packaging.output foo\n```\n\nThe using directive above makes it possible to create a package named `foo` inside the current directory.\n\n### packaging.graalvmArgs\n\nThis using directive makes it possible to pass args to GraalVM:\n\n```scala compile power\n//> using packaging.graalvmArgs --no-fallback --enable-url-protocols=http,https\n```\n\n### Docker\n\n#### packaging.dockerFrom\n\nThe using directive allows you to define the base Docker image that is used to run your application.\n\n```scala compile power\n//> using packaging.dockerFrom openjdk:11\n```\n\n#### packaging.dockerFrom\n\nThe using directive allows you to define the generated Docker image tag.\n\n```scala compile power\n//> using packaging.dockerImageTag 1.0.0\n```\n\n#### packaging.dockerImageRegistry\n\nThe using directive allows you to define the image registry.\n\n```scala compile power\n//> using packaging.dockerImageRegistry virtuslab\n```\n\n#### packaging.dockerImageRepository\n\nThe using directive allows you to define the image repository.\n\n```scala compile power\n//> using packaging.dockerImageRepository scala-cli\n```\n\n#### packaging.dockerCmd\n\nThe using directive allows you to override the executable used to run the application in docker, \notherwise it defaults to `sh` for the JVM platform and `node` for the JS platform\n\n```scala compile power\n//> using packaging.dockerCmd node\n```\n"
  },
  {
    "path": "website/docs/commands/publishing/_category_.json",
    "content": "{\n  \"label\": \"Publishing ⚡️\",\n  \"position\": 30\n}\n"
  },
  {
    "path": "website/docs/commands/publishing/publish-local.md",
    "content": "---\ntitle: Publish Local ⚡️\nsidebar_position: 21\n---\n\n:::caution\nThe Publish Local command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\n:::caution\nThe `publish local` sub-command is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nThe `publish local` sub-command publishes a Scala CLI project in the local Ivy2\nrepository, just like how `sbt publishLocal` or `mill __.publishLocal` do. This\nrepository usually lives under `~/.ivy2/local`, and is taken into account most of\nthe time by most Scala tools when fetching artifacts.\n\n## Usage\n\nTo publish locally a Scala CLI project, run\n\n<ChainedSnippets>\n\n```sh\nscala-cli publish local .\n```\n\n```text\nPublishing io.github.scala-cli:hello-scala-cli_3:0.1.0-SNAPSHOT\n ✔ Computed 10 checksums\n 🚚 Wrote 15 files\n\n 👀 Check results at\n  ~/.ivy2/local/io.github.scala-cli/hello-scala-cli_3/0.1.0-SNAPSHOT/\n```\n\n:::caution\nThe `publish local` sub-command does not currently support publishing of the test scope.\nThis includes any file that is placed in `test` directory or with the `.test.scala` suffix.\n\nRead more about test sources in [testing documentation](../test.md#test-sources).\n:::\n\n</ChainedSnippets>\n\n## Required settings\n\nThe `publish local` command needs the [same required settings as the `publish` command](publish.md#required-settings). Like for `publish`, Scala CLI might already be able to compute sensible defaults\nfor those.\n"
  },
  {
    "path": "website/docs/commands/publishing/publish-setup.md",
    "content": "---\ntitle: Publish Setup ⚡️\nsidebar_position: 19\n---\n\n:::caution\nThe Publish Setup command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\n:::caution\nThe `publish setup` sub-command is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nThe `publish setup` sub-command configures your project for publishing to Maven repositories,\nsuch as Maven Central or GitHub Packages. It checks that all required parameters for publishing are set, and tries\nto infer many of them from the environment. It writes configuration as using directives, appended\nto a file named `publish-conf.scala` at the root of the workspace.\n\n`publish setup` can configure publishing, so that you can publish from your local machine,\nbut also from GitHub actions.\nIn particular, it can upload secrets as GitHub repository secrets, so that only\nminimal effort is required to setup publishing from GitHub actions.\n\nRunning `publish setup` works fine on brand new machines or projects, but it works better when:\n- user details, credentials for publishing, and a PGP key are configured (user-wide)\n- the current project is already pushed to GitHub\n\n## Configuration\n\nConfiguring Scala CLI for publishing makes `publish setup` works more smoothly later on.\n\nIn particular, one can configure:\n- details about oneself (name, email, website - written in the developers section of POM files)\n- a PGP key pair (to sign artifacts)\n- Sonatype credentials (to upload artifacts to Maven Central)\n- a GitHub token (to upload repository secrets to GitHub, and artifacts to Maven repositories of GitHub Packages)\n\nUser-wide configuration in Scala CLI is handled by the [`config` command](/docs/commands/config.md), and\nthe sections below show how to use it to configure things for `publish setup`.\n\n:::caution\nEven though the `config` command is not restricted, some available configuration keys may be, and thus may\nrequire setting the `--power` option to be used.\nThat includes configuration keys tied to publishing, like `publish.user.name` and others.\nYou can pass the `--power` option explicitly or set it globally by running:\n```bash ignore\nscala-cli config power true\n```\n:::\n\n### User details\n\nSet details with\n```bash\nscala-cli --power config publish.user.name \"Alex Me\"\nscala-cli --power config publish.user.email \"alex@alex.me\"\nscala-cli --power config publish.user.url \"https://alex.me\"\n```\n\n\\<!-- clear -->\n\nThe email can be left empty if you'd rather not put your email in POM files:\n```bash\nscala-cli --power config publish.user.email \"\"\n```\n\n### PGP key pair\n\nGenerate a PGP key pair for publishing with\n```bash\nscala-cli --power config --create-pgp-key --pgp-password MY_CHOSEN_PASSWORD\n```\n\nThis sets 2 entries in the Scala CLI configuration, that you can print with\n```bash\nscala-cli --power config pgp.public-key\nscala-cli --power config pgp.secret-key\n```\n\nIt's not mandatory, although recommended, to use a password to encrypt your keychains.\nTo store the private keychain in an unencrypted form use `--pgp-password none`.\nTo randomly generate a password, use `--pgp-password random` instead.\n\n:::caution\nIf a password is used, it should be kept safe, as it is needed to later decrypt and use the generated keychains.\n:::\n\n### Sonatype credentials\n\nPublishing to Maven Central requires a Sonatype account, and requesting the right to publish\nunder specific organizations.\nYou can follow the\n[sbt-ci-release Sonatype instructions](https://github.com/sbt/sbt-ci-release#sonatype)\nto create an account there. Either your real Sonatype username and password, or Sonatype tokens, can be used\nin Scala CLI (via the `publish.credentials` config key in both cases).\n\nThese can be written in the Scala CLI configuration the following way:\n```bash ignore\nscala-cli config publish.credentials s01.oss.sonatype.org env:SONATYPE_USER env:SONATYPE_PASSWORD --password-value\n```\n\nNote that both user and password arguments are assumed to be secrets, and\naccept the format documented [here](/docs/reference/password-options.md). Beyond environment\nvariables, commands or paths to files can provide those values. They can also be passed\nas is on the command line, although this is not recommended for security reasons.\n\nIn the example above, we pass the username and password via the environment, and\nask the `config` sub-command to read environment variables and persist the password values\n(via `--password-value`).\n\nIf you'd rather persist the environment variable names in the Scala CLI configuration, rather than\ntheir values, you can do\n```bash ignore\nscala-cli --power config publish.credentials s01.oss.sonatype.org env:SONATYPE_USER env:SONATYPE_PASSWORD\n```\n\nNote that in this case, both `SONATYPE_USER` and `SONATYPE_PASSWORD` will need to be available\nin the environment when using those credentials in the `publish` sub-command.\n\n### GitHub token\n\n`publish setup` uses a GitHub token in order to:\n- upload secrets as GitHub repository secrets\n- upload artifacts to GitHub packages, when publishing to it\n\nTo setup a token for Scala CLI, you need to generate a token first.\nFor that, head to your [Personal access tokens page](https://github.com/settings/tokens),\nand click \"Generate new token\". The \"public_repo\" scope is required to upload\nrepository secrets, and the \"write:packages\" scope is required to upload artifacts\nto GitHub packages.\n\nOnce created, copy the token in your clipboard, and run\n```bash ignore\n# macOS\nscala-cli config github.token command:pbpaste --password-value\n# Linux\nscala-cli config github.token \"command:xclip -selection clipboard -o\" --password-value\n```\n\n## Pushing project to GitHub\n\n`publish setup` infers some publishing parameters from the GitHub URL of your project.\nIt also uploads repository secrets there, when setting up publishing on GitHub actions.\n\nTo create a new repository from a project, head to `https://repo.new`, pick a name\nfor your project and create the repository. Note its URL, and do\n```bash ignore\nscala-cli default-file .gitignore --write # if you don't have a .gitignore already\ngit init # if git isn't set up already\ngit remote add origin https://github.com/org/name # replace org/name with your freshly created repository values\n```\n\n## Local setup\n\nTo setup publishing in order to publish from your local machine, you can run\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli publish setup .\n```\n\n```text\n9 options need to be set\n\norganization:\n  computing io.github.scala-cli from GitHub account scala-cli\nname:\n  using workspace directory name hello-scala-cli\ncomputeVersion:\n  assuming versions are computed from git tags\nrepository:\n  using Maven Central via its s01 server\nlicense:\n  using Apache-2.0 (default)\nurl:\n  computing from GitHub repository scala-cli/hello-scala-cli\nvcs:\n  using GitHub repository scala-cli/hello-scala-cli\ndevelopers:\n  using Alex Me <alex@alex.me> (https://github.com/scala-cli) from config\n\nWrote ./publish-conf.scala\n\nProject is ready for publishing!\nTo publish your project, run\n  scala-cli publish .\n```\n\n</ChainedSnippets>\n\nYou can then publish your project from your local machine with\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli publish .\n```\n\n```text\nPublishing io.github.scala-cli:hello-scala-cli_3:0.1.0-SNAPSHOT\n ✔ Computed 8 checksums\n 🚚 Wrote 12 files\n\n 👀 Check results at\n  https://s01.oss.sonatype.org/content/repositories/snapshots/io/github/scala-cli/hello-scala-cli_3/0.1.0-SNAPSHOT\n```\n\n</ChainedSnippets>\n\n## GitHub actions setup\n\nTo setup publishing from GitHub actions, you can run\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli publish setup . --ci\n```\n\n```text\n11 options need to be set\n\norganization:\n  computing io.github.scala-cli from GitHub account scala-cli\nname:\n  using workspace directory name hello-scala-cli\ncomputeVersion:\n  assuming versions are computed from git tags\nrepository:\n  using Maven Central via its s01 server\npublish.user:\n  using publish.credentials from Scala CLI configuration\npublish.password:\n  using publish.credentials from Scala CLI configuration\nlicense:\n  using Apache-2.0 (default)\nurl:\n  using GitHub repository https://github.com/scala-cli/hello-scala-cli\nvcs:\n  using GitHub repository scala-cli/hello-scala-cli\ndevelopers:\n  using Alex Me <alex@alex.me> (https://github.com/scala-cli) from config\n\nUploading 4 GitHub repository secrets\n  updated PUBLISH_USER\n  updated PUBLISH_PASSWORD\n  updated PUBLISH_SECRET_KEY\n  updated PUBLISH_SECRET_KEY_PASSWORD\n\nUploaded key 0xe58386629a30f5c5 to http://keyserver.ubuntu.com:11371\n\nWrote ./publish-conf.scala\nWrote workflow in ./.github/workflows/ci.yml\n\nCommit and push ./publish-conf.scala, ./.github/workflows/ci.yml, to enable publishing from CI\n```\n\n</ChainedSnippets>\n\nThen committing and pushing the suggested files `publish-conf.scala` and `.github/workflows/ci.yml`\nshould trigger a workflow pushing snapshot artifacts to Sonatype Snapshots.\n\nTo publish a non-snapshot version, either push a tag like `v0.1.0` (or any other version with a `v`\nprefix), or create a release with a tag with the same name from the GitHub UI.\n\n## GitHub Packages\n\nIn order to setup publishing to GitHub packages, pass `--publish-repository github` to the\n`publish setup` commands above, like\n```bash ignore\nscala-cli --power publish setup . --publish-repository github\n```\n"
  },
  {
    "path": "website/docs/commands/publishing/publish.md",
    "content": "---\ntitle: Publish ⚡️\nsidebar_position: 20\n---\n\n:::caution\nThe Publish command is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\n:::caution\nThe `publish` sub-command is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nThe `publish` sub-command allows to publish Scala CLI projects to Maven repositories.\n\nWe recommend running [the `publish setup` sub-command](./publish-setup.md) once prior to\nrunning `publish`, in order to set missing `using` directives for publishing, but this is not\nmandatory.\n\n## Required settings\n\n`scala-cli publish` and `scala-cli publish local` might complain about missing settings.\nAn organization, a name (or a module name), and (a way to compute) a version are needed, but Scala CLI may\nbe able to compute sensible defaults for them.\n\nWe recommend setting the settings below via using directives rather than on the command-line,\nso that commands such as `scala publish .` or `scala publish local .` work fine for your\nproject. Command-line options for those settings take over the using directive values, and are\nprovided as a convenience.\n\nThis table lists settings allowing to specify those. See the sub-sections right after for more details.\n\n|          | `using` directive | Command-line option | Example values | Notes |\n|----------|-------------------|---------------------|----------------|-------|\n| Organization | `publish.organization` | `--organization` | `org.virtuslab.scala-cli` | |\n| Name | `publish.name` | `--name` | `scala-cli` | |\n| Module Name | `publish.moduleName` | `--module-name` | `scala-cli_3` | Module Name includes the Scala prefix, such as `_2.13` or `_3`. Specifying Name should be favored over Module Name |\n| Compute Version | `publish.computeVersion` | `--compute-version` | `git:tag` | |\n| Version | `publish.version` | `--project-version` | `0.1.0`, `0.1.1-SNAPSHOT` | As much as possible, Compute Version (describing how to compute the version) should be favored over Version |\n\n### Organization\n\nIf your Scala CLI project lives in a git repository having a GitHub remote, Scala CLI\nwill infer an organization from it: if your project lives in GitHub organization `foo`\n(that is, lives somewhere under `https://github.com/foo/`), Scala CLI will use\n`io.github.foo` as default Maven organization.\n\nTo override this default value, set the `publish.organization` directive, like\n```scala\n//> using publish.organization io.github.foo\n```\n\n### Name\n\nScala CLI will use the project directory name as default Maven name. That is, if your\nScala CLI project lives in a directory named `something`, it will be published as\n`something` (pure Java project) or `something_3` (Scala 3 project) for example.\n\nTo override this default value, set the `publish.name` directive, like\n```scala\n//> using publish.name something\n```\n\n### Version\n\nIf your Scala CLI project lives in a git repository, Scala CLI will infer a way to compute\nversions from it: if the current commit has a tag `v1.2.3`, version `1.2.3` is assumed.\nElse, if it has such a tag earlier in the git history, version `1.2.4-SNAPSHOT` is assumed.\n\nTo override this default value, set the `publish.computeVersion` directive, like\n```scala\n//> using publish.computeVersion git:tag\n```\n\nPlease note that only tags that follow the semantic versioning are taken into consideration.\n\nValues available for project version configuration are:\n- `git:tag` or `git`: use the latest stable git tag, if it is older than HEAD then try to increment it\n  and add a suffix `-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`\n- `git:dynver`: use the latest (stable or unstable) git tag, if it is older than HEAD then use the output of\n  `-{distance from last tag}-g{shortened version of HEAD commit hash}-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`\n\nThe difference between stable and unstable tags are, that the latter can contain letters, e.g. `v0.1.0-RC1`.\nIt is also possible to specify the path to the repository, e.g. `git:tag:../my-repo`, `git:dynver:../my-repo`.\n\n## Repository settings\n\nA repository is required for the `publish` command, and might need other settings to work fine\n(to pass credentials for example). See [Repositories](#repositories) for more information.\n\nWhen publishing from you CI, we recommend letting `scala-cli publish setup`\nsetting those settings via using directives. When publishing from your local machine to Maven Central,\nwe recommend setting the repository via a `publish.repository` directive, and keeping your\nSonatype credentials in the Scala CLI settings, via commands such as\n```bash ignore\nscala-cli config publish.credentials s01.oss.sonatype.org env:SONATYPE_USER env:SONATYPE_PASSWORD\n```\n\n<!-- TODO Automatically generate that? -->\n|          | `using` directive | Command-line option | Example values | Notes |\n|----------|-------------------|---------------------|----------------|-------|\n| Repository | `publish.repository` | `--publish-repository` | `central`, `central-s01`, `github`, `https://artifacts.company.com/maven` | |\n\n## Other settings\n\nA number of metadata can be set either by `using` directives, or from the command-line. These\nmetadata are optional in the `publish local` command, but might be mandatory for some repositories\nin the `publish` command, like Maven Central for non-snapshot versions.\n\nWe recommend setting the settings below via using directives rather than on the command-line,\nso that these don't have to be recalled for each `scala-cli publish` or `scala-cli publish local`\ninvocation. Command-line options for those settings take over the using directive values, and are\nprovided as a convenience.\n\n<!-- TODO Automatically generate that? -->\n|          | `using` directive | Command-line option | Example values | Notes |\n|----------|-------------------|---------------------|----------------|-------|\n| License | `publish.license` | `--license` | `Apache-2.0`, `MIT`, `Foo:https://foo.com/license.txt`, … | Run `scala-cli publish --license list` to list pre-defined licenses |\n| URL | `publish.url` | `--url` | | |\n| VCS | `publish.vcs` | `--vcs` | `github:VirtusLab/scala-cli`, `https://github.com/VirtusLab/scala-cli.git` |scm:git:github.com/VirtusLab/scala-cli.git|scm:git:git@github.com:VirtusLab/scala-cli.git` | |\n| Developers | `publish.developer` | `--developer` | <code>alexme&vert;Alex Me&vert;https://alex.me</code> | Can be specified multiple times, using directives and CLI values add up |\n| Docs       | `publish.doc`       | `--doc`             | `--doc=false`, `//> using doc false`                                       | Use to disable publishing docs jar.                                     |\n\n### Signing\n\nScala CLI can sign the artifacts it publishes with PGP signatures. Signing in Scala CLI can be\nhandled by either\n- the [Bouncy Castle library](https://www.bouncycastle.org) (default, recommended)\n- the local `gpg` binary on your machine\n\nA signing mechanism will be chosen based on options and directives specified,\nit can also be overriden with `--signer` with one of the values:\n- `bc` - Bouncy Castle library will be used for signing, PGP secret key is required\n- `gpg` - a local `gpg` binary will be used for signing, GPG key ID is required\n- `none` - NO signing will take place\n\n#### Bouncy Castle\n\nBouncy Castle library is the quickest way of signing artifacts with Scala CLI. \nA benefit of using it is that it has no external dependencies,\nScala CLI is able to sign things with Bouncy Castle without further setup on your side.\nHowever, it does not provide a complex PGP handling functionality as e.g. GPG does.\n\nWhen the `--signer` option is not specified Bouncy Castle library will be used for signing\nif one of these conditions occur:\n- the `--secret-key` option has been passed\n- target repository requires signing (e.g. `central`)\n\nTo succesfully use PGP signing with Bouncy Castle a PGP key pair is required.\nScala CLI can generate and keep PGP keys for you by using:\n```bash ignore\nscala-cli --power config --create-pgp-key --pgp-password MY_CHOSEN_PASSWORD\n```\n\nIt's not mandatory, although recomended, to use a password to encrypt your keychains.\nTo store the private keychain in an unencrypted form use `--pgp-password none`.\nTo randomly generate a pasword, use `--pgp-password random` instead.\n\nThe generated values are kept in the `config` and will be used by default unless specified otherwise:\n- with directives:\n    ```scala\n    //> using publish.secretKey env:PGP_SECRET\n    //> using publish.secretKeyPassword command:get_my_password\n    ```\n\n- with options:\n    ```bash ignore\n    scala-cli --power publish \\\n      --secret-key env:PGP_SECRET \\\n      --secret-key-password file:pgp_password.txt \\\n      …\n    ```\n\nSince these values should be kept secret, the options and directives accept the format documented [here](/docs/reference/password-options.md).\n\n#### GPG\n\nUsing GPG to sign artifacts requires the `gpg` binary to be installed on your system.\nA benefit of using `gpg` to sign artifacts over Bouncy Castle is: you can use keys from\nyour GPG key ring, or from external devices that GPG may support.\n\nTo get started, consult the [documentation on the library's website](https://gnupg.org/documentation/guides.html) and be sure to read about\n[Protecting code integrity with PGP guide from the Linux Foundation](https://github.com/lfit/itpol/blob/master/protecting-code-integrity.md#target-audience).\n\nTo enable signing with GPG, pass `--gpg-key *key_id*` on the command line\nor specify it with a `using` directive: `//>using publish.gpgKey key_id`.\nIf needed, you can specify arguments meant to be passed to `gpg`,\nwith `--gpg-option` or `//>using publish.gpgOptions --opt1 --opt2`, like\n```text\n--gpg-key 1234567890ABCDEF --gpg-option --foo --gpg-option --bar\n```\n\n### Checksums\n\nScala CLI can generate checksums of the artifacts it publishes.\n\nBy default, Scala CLI generates SHA-1 and MD5 checksums. To disable checksums,\npass `--checksum none`. To generate checksum formats to generate, pass them via\n`--checksum`, separating the checksum values with `,` or using `--checksum` multiple\ntimes:\n```text\n--checksum sha1,md5\n--checksum sha1 --checksum md5\n```\n\nTo list supported checksum types, pass `--checksum list`.\n\n### CI overrides\n\nScala CLI allows some publishing-related settings to have different values on your local machine and\non CIs. In particular, this can be convenient to handle credentials and signing parameters, as these can\nbe read from different locations on developers' machines and on CIs.\n\nOn CIs (when `CI` is set in the environment, whatever its value), the CI override is\nused if it's there. Else the main directive is used.\n\n<!-- TODO Automatically generate that? -->\n| Settings | Directive | CI override directive |\n|----------|-------------------|-----------------------|\n| Compute Version | `publish.computeVersion` | `publish.ci.computeVersion` |\n| Repository | `publish.repository` | `publish.ci.repository` |\n| Repository User | `publish.user` | `publish.ci.user` |\n| Repository Password | `publish.password` | `publish.ci.password` |\n| Repository Realm | `publish.realm` | `publish.ci.realm` |\n| Secret Key | `publish.secretKey` | `publish.ci.secretKey` |\n| Secret Key Password | `publish.secretKeyPassword` | `publish.ci.secretKeyPassword` |\n| GPG key | `publish.gpgKey` | `publish.ci.gpgKey` |\n| GPG options | `publish.gpgOptions` | `publish.ci.gpgOptions` |\n\n## Repositories\n\n### Maven Central\n\nRight now the easiest way to publish to Maven Central Repository is to use\nSonatype repositories - `s01.oss.sonatype.org` or `oss.sonatype.org`\nSince 25.02.2021 `s01` is the default server for new users, if your account is older than that\nyou probably need to use the legacy `oss.sonatype.org`. More about this [here](https://central.sonatype.org/news/20210223_new-users-on-s01/#question).\n\nUse `central` as repository to push artifacts to Maven Central via `oss.sonatype.org`.\nTo push to it via `s01.oss.sonatype.org`, use `central-s01`.\n\nWhen using `central` or `central-s01` as repository, artifacts are pushed\neither to `https://oss.sonatype.org/content/repositories/snapshots` (versions\nending in `SNAPSHOT`) or to `https://oss.sonatype.org/staging/deploy/maven2`\n(in that case, Sonatype API endpoints are called to \"close\" and \"release\"\nartifacts, which later syncs them to `https://repo1.maven.org/maven2`).\n\n### GitHub Packages\n\nUse `github` (GitHub organization and name computed from the git remotes)\nor `github:org/name` (replace `org` and `name` by the GitHub organization and name\nof your repository, like `github:VirtusLab/scala-cli`)\nto push artifacts to GitHub Packages.\n\nNote that, as of writing this, this disables parallel uploading of artifacts,\nchecksums, and signing (all not supported by GitHub Packages as of writing this).\n\n### Ivy2 Local\n\nUse `ivy2Local` to put artifacts in the local Ivy2 repository, just like how\n[`publish local`](./publish-local.md) does.\n\n### Other pre-defined repositories\n\nAll pre-defined repositories accepted by coursier, such as `jitpack` or `sonatype:snapshots`, are accepted as repositories for publishing.\n\n### Generic Maven repositories\n\nPass a URL (beginning with `http://` or `https://`) to push to custom\nHTTP servers. Pushing to such repositories relies on HTTP PUT requests\n(just like for the pre-defined repositories above).\n\nYou can also pass a path to a local directory, absolute (recommended)\nor relative (beware of name clashes with pre-defined repositories above).\n\n### Authentication\n\nSpecify publish repository authentication either on the command-line or via\nusing directives. See user / password / realm in the [settings table](#other-settings)\nand the [CI overrides](#ci-overrides).\n\n### Connection parameters configuration\n\nWhen publishing large packages or when the internet connection is spotty one may use the following options to configure the connection parameters:\n- `--connection-timeout-seconds` - the connection timeout in seconds\n- `--response-timeout-seconds` - the response timeout in seconds\n- `--connection-timeout-retries` - the number of times to retry the connection on timeout\n\nPublishing to Sonatype uses a staging repository which may sometimes cause problems when transitioning through states.\nIf a publishing process fails with status `500` and message `\"Staging repository is already transitioning\"` you can try to tweak the following parameters taht are Sonatype specific:\n- `--staging-repo-retries` - the number of times to retry the staging repository transition\n- `--staging-repo-wait-time-milis` - the base time to wait between retries in milliseconds\n\n## Publishing\n\nOnce all the necessary settings are set, publish a Scala CLI project with a command\nsuch as this one:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli --power publish .\n```\n(`.` is for the Scala CLI project in the current directory)\n\n```text\nPublishing io.github.scala-cli:hello-scala-cli_3:0.1.0-SNAPSHOT\n ✔ Computed 8 checksums\n 🚚 Wrote 12 files\n\n 👀 Check results at\n  https://s01.oss.sonatype.org/content/repositories/snapshots/io/github/scala-cli/hello-scala-cli_3/0.1.0-SNAPSHOT\n```\n\n</ChainedSnippets>\n\n:::caution\nThe `publish local` sub-command does not currently support publishing of the test scope.\nThis includes any file that is placed in `test` directory or with the `.test.scala` suffix.\n\nRead more about test sources in [testing documentation](../test.md#test-sources).\n:::\n"
  },
  {
    "path": "website/docs/commands/repl.md",
    "content": "---\ntitle: REPL\nsidebar_position: 8\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nThe `repl` command starts a Scala REPL, which lets you interactively run your code and inspect its results:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli repl\n```\n\n```text\nscala> println(\"Hello Scala\")\nHello Scala\n\nscala> :exit\n```\n\n</ChainedSnippets>\n\nScala CLI by default uses the normal Scala REPL.\n\nIf you prefer to use the [Ammonite REPL](https://ammonite.io/#Ammonite-REPL), specify `--amm` to launch it rather than the default REPL:\n\n:::caution\nUsing the Ammonite REPL is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli --power repl --amm\n```\n\n```text\nLoading...\nWelcome to the Ammonite Repl 2.4.0-23-76673f7f (Scala 3.0.2 Java 11.0.11)\n@ println(\"Hello ammonite\")\nHello ammonite\n@ exit\nBye!\n```\n\n</ChainedSnippets>\n\nThe `repl` command accepts the same arguments as the [compile](compile.md) command. It first compiles any provided sources, and then exposes those results and any provided dependencies to the REPL session:\n\n```scala title=mylibrary/Messages.scala\npackage mylibrary\n\nobject Messages {\n  def message = \"Hello\"\n  def print(): Unit = println(message)\n}\n```\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli repl mylibrary/Messages.scala\n```\n\n```text\nCompiling project (Scala 3.0.2, JVM)\nCompiled project (Scala 3.0.2, JVM)\nscala> import mylibrary._\n\nscala> Messages.print()\nHello\n\nscala> :quit\n```\n\n</ChainedSnippets>\n\n## Watch mode\n\nUse `--watch` to recompile your inputs and restart the REPL session when sources change:\n\n```bash ignore\nscala-cli repl --watch Main.scala\n```\n\n`--watching` lets you include additional files or directories:\n\n```bash ignore\nscala-cli repl --watch --watching ./data Main.scala\n```\n\nYou can also configure extra watched paths in sources:\n\n```scala\n//> using watching ./data\n```\n\n## Passing REPL options\nIt is also possible to manually pass REPL-specific options.\nIt can be done in a couple ways:\n- after the `--` separator, as the REPL itself is the launched app, so its options are app arguments\n\n<ChainedSnippets>\n\n```bash ignore\nscala repl -S 3.6.4-RC1 -- --repl-init-script 'println(\"Hello\")'\n```\n\n```\nHello\nWelcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n                                                                                                                 \nscala> \n```\n</ChainedSnippets>\n\n\n- with the `-O`, effectively passing them as compiler options:\n\n<ChainedSnippets>\n\n```bash ignore\nscala repl -S 3.6.4-RC1 -O --repl-init-script -O 'println(\"Hello\")'\n```\n\n```\nHello\nWelcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n                                                                                                                 \nscala> \n```\n\n</ChainedSnippets>\n\n- via a using directive, treating them as Scala compiler options:\n\n<ChainedSnippets>\n\n```scala compile title=repl-options.scala\n//> using toolkit default\n//> using options --repl-init-script \"import os.*; println(pwd)\"\n```\n\n```bash ignore\nscala repl repl-options.scala\n```\n\n```\n/current/directory/path\nWelcome to Scala 3.7.4 (17, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n                                                                         \nscala> pwd\nval res0: os.Path = /current/directory/path\nscala> :quit\n```\n\n</ChainedSnippets>\n\n- directly, as a Scala CLI option (do note that newly added options from an RC version or a snapshot may not be supported this way just yet):\n\n<ChainedSnippets>\n\n```bash ignore\nscala repl --repl-init-script 'println(\"Hello\")'\n```\n\n```\nHello\nWelcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n                                                                         \nscala> pwd\nval res0: os.Path = /current/directory/path\nscala> :quit\n```\n\n</ChainedSnippets>\n\n## Using Toolkit in REPL\nIt is also possible to start the scala-cli REPL with [toolkit](https://scala-cli.virtuslab.org/docs/guides/introduction/toolkit/) enabled\n\n<ChainedSnippets>\n    \n```bash ignore\nscala-cli repl --toolkit default\n```\n\n```text\nWelcome to Scala 3.7.4 (17, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n                                                                                \nscala> import os.*\n                                                                                \nscala> os.pwd\nval res0: os.Path = /current/directory/path\nscala> :quit\n\n```\n</ChainedSnippets>\n\nSince we started the repl with toolkit enabled, we can use the libraries included in the toolkit directly. In the above example, the `os-lib` library from the toolkit is used to print the current path. \n\n### Running snippets directly via the REPL\nIt is possible to run code snippets via the REPL, with all the internal quirks of the REPL, rather than using standard runner execution.\nWhile the difference is subtle and should generally be invisible to users, it is useful for investigating REPL behavior.\nThe key to doing that is to use the both `--repl-init-script` and `--repl-quit-after-init` options together.\n\n<ChainedSnippets>\n\n\n```bash\nscala repl --repl-init-script 'println(42)' --repl-quit-after-init # runs in the REPL and quits immediately\n```\n\n```\n42\n```\n\n```bash\nscala repl -e '//> using options --repl-init-script \"println(42)\" --repl-quit-after-init' # same, but via using directive\n```\n\n```\n42\n```\n\n```bash\nscala -e 'println(42)' # standard runner equivalent\n```\n\n```\n42\n```\n\n</ChainedSnippets>\n\n## Inject code as JAR file in class path\n\nIf your application inspects its class path, and requires only JAR files in it, use `--as-jar` to\nput the Scala CLI project in the class path as a JAR file rather than as a directory:\n\n```bash ignore\nscala-cli repl Foo.scala --as-jar\n```\n"
  },
  {
    "path": "website/docs/commands/run.md",
    "content": "---\ntitle: Run\nsidebar_position: 6\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nThe `run` command runs your Scala code:\n\n```scala title=Hello.scala\nobject Hello {\n  def main(args: Array[String]): Unit =\n    println(\"Hello\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli run Hello.scala\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nThis is the default command, so you don’t have to specify it explicitly:\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.scala\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n## Passing arguments\n\nYou can pass arguments to the application or script you're launching after `--`:\n\n```scala title=app.sc\nprintln(args.mkString(\"App called with arguments: \", \", \", \"\"))\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli app.sc -- first-arg second-arg\n```\n\n```text\nApp called with arguments: first-arg, second-arg\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nApp called with arguments: first-arg, second-arg\n-->\n\n## Main class\n\nIf your application has multiple main classes, the `--main-class` option lets you explicitly specify the main class you\nwant to run:\n\n```scala title=hi.sc\nprintln(\"Hi\")\n```\n\n```bash\nscala-cli Hello.scala hi.sc --main-class hi_sc\n```\n\n## Custom JVM\n\n`--jvm` lets you run your application with a custom JVM:\n\n```bash\nscala-cli Hello.scala --jvm zulu:21\n```\n\nYou can also specify custom JVM with the using directive `//> using jvm`:\n\n```scala compile\n//> using jvm zulu:21\n```\n\nJVMs are [managed by coursier](https://get-coursier.io/docs/cli-java#managed-jvms), and are read from\nthe [coursier JVM index](https://github.com/coursier/jvm-index).\n(New JVM versions are automatically checked daily, and updates for those are - manually - merged\nswiftly.)\n\n### JVM options\n\n`--java-opt` lets you add `java` options which will be passed when running an application:\n\n```bash\nscala-cli Hello.scala --java-opt -Xmx1g --java-opt -Dfoo=bar\n```\n\nYou can also add java options with the using directive `//> using javaOpt`:\n\n```scala compile\n//> using javaOpt -Xmx1g -Dfoo=bar\n```\n\nAdditionally, Java properties can be passed to the executed code without `--java-prop`:\n\n```bash\nscala-cli Hello.scala -Dfoo=bar\n```\n\n### JAR\n\nScala CLI lets you run JAR files just like any other input.\n\n```bash ignore\nscala-cli Hello.jar\n```\n\n```text\nHello World\n```\n\nWhen you provide a JAR file as input to Scala CLI, it will be added to the `classPath`.\n\n## Define source files in using directives\n\nYou can also add source files with the using directive `//> using file`:\n\n```scala title=Main.scala\n//> using file Utils.scala\n\nobject Main extends App {\n  println(Utils.message)\n}\n```\n\n```scala title=Utils.scala\nobject Utils {\n  val message = \"Hello World\"\n}\n```\n\nScala CLI takes it into account and compiles `Utils.scala`.\n\n<ChainedSnippets>\n\n```bash\nscala-cli Main.scala\n```\n\n```text\nHello World\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello World\n-->\n\nIt is also possible to pass multiple paths to source files in a single using directive:\n\n```scala title=Multiple.scala\n//> using files Utils.scala Main.scala\n```\n\n```bash\nscala-cli run Multiple.scala\n```\n\nNote that the `//> using file` using directive only supports `.java`, `.scala`, `.sc` files or a directory.\n\n## Watch mode\n\n`--watch` makes Scala CLI watch your code for changes, and re-runs it upon any change\nor when the `ENTER` key is passed from the command line:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli run Hello.scala  --watch\n```\n\n```text\nHello\nProgram exited with return code 0.\nWatching sources, press Ctrl+C to exit, or press Enter to re-run.\nCompiling project (Scala 3.2.2, JVM)\nCompiled project (Scala 3.2.2, JVM)\nHello World\nProgram exited with return code 0.\nWatching sources, press Ctrl+C to exit, or press Enter to re-run.\n```\n\n</ChainedSnippets>\n\n### Watch mode (restart)\n\nThe `--restart` option works very similarly to `--watch`, but instead of waking the sleeping thread,\nit kills the process and restarts the app whenever sources change or the `ENTER` key is passed from the command line.\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli run Hello.scala --restart\n```\n\n```text\nWatching sources while your program is running.\nHello\nProgram exited with return code 0.\nWatching sources while your program is running.\nCompiling project (Scala 3.2.2, JVM)\nCompiled project (Scala 3.2.2, JVM)\nHello World\nProgram exited with return code 0.\nWatching sources while your program is running.\n```\n\n</ChainedSnippets>\n\n### Watching additional paths\n\n`--watching` lets you specify additional files or directories to watch while using `--watch` or `--restart`:\n\n```bash ignore\nscala-cli run --watch --watching ./data --watching ./templates Hello.scala\n```\n\nYou can also declare extra watched paths from your sources:\n\n```scala\n//> using watching ./data\n```\n\nWhen both `--watching` and `//> using watching` are used, Scala CLI watches all of the specified paths.\n\n## Scala.js\n\nScala.js applications can also be compiled and run with the `--js` option.\nNote that this requires `node` to be [installed](/install#scala-js) on your system:\n\n```bash\nscala-cli Hello.scala --js\n```\n\nIt is also possible to achieve it using `--platform` option:\n\n```bash\nscala-cli Hello.scala --platform js\n```\n\nSee our dedicated [Scala.js guide](../guides/advanced/scala-js.md) for more information.\n\n## Scala Native\n\nScala Native applications can be compiled and run with the `--native` option.\nNote that\nthe [Scala Native requirements](https://scala-native.readthedocs.io/en/latest/user/setup.html#installing-clang-and-runtime-dependencies)\nneed to be [installed](/install#scala-native) for this to work:\n\n```bash\nscala-cli Hello.scala --native -S 2.13\n```\n\nIt is also possible to achieve it using `--platform` option:\n\n```bash\nscala-cli Hello.scala --platform native\n```\n\nWe have a dedicated [Scala Native guide](../guides/advanced/scala-native.md) as well.\n\n## Platform\n\nThe `--platform` option can be used to choose the platform, which should be used to compile and run application.\nAvailable platforms are:\n\n* JVM (`jvm`)\n* Scala.js (`scala.js` | `scala-js` | `scalajs` | `js`)\n* Scala Native (`scala-native` | `scalanative` | `native`)\n\nPassing the `--platform` along with `--js` or `--native` is not recommended. If two different types of platform are\npassed, Scala CLI throws an error.\n\n## Scala Scripts\n\nScala CLI can also compile and run Scala scripts:\n\n```scala title=HelloScript.sc\n#!/usr/bin/env -S scala shebang\n\nprintln(\"Hello world from scala script\")\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli run HelloScript.sc\n```\n\n```text\nHello world from scala script\n```\n\n</ChainedSnippets>\n\nOur [scripts guide](../guides/scripting/scripts.md) provides many more details.\n\n## Scala CLI from docker\n\nScala applications can also be compiled and run using a [docker](https://docs.docker.com/get-started/) image\nwith Scala CLI, without needing to install Scala CLI manually:\n\n```bash\ndocker run virtuslab/scala-cli:latest version\n```\n\n```scala title=HelloWorld.scala\nobject HelloWorld extends App {\n  println(\"Hello world\")\n}\n```\n\n<ChainedSnippets>\n\n```bash ignore\ndocker run  -v $(pwd)/HelloWorld.scala:/HelloWorld.scala virtuslab/scala-cli /HelloWorld.scala\n```\n\n```text\nHello world\n```\n\n</ChainedSnippets>\n\n## Debugging\n\nIt is possible to debug code by passing `--debug` flag.\n\nAdditional debug options:\n\n* `--debug-mode` (attach by default)\n* `--debug-port` (5005 by default)\n\nAvailable debug modes:\n\n* Attach (`attach` | `att` | `a`)\n* Listen (`listen` | `lis` | `l`)\n\nExample debugging with scala-cli:\n\n```bash ignore\nscala-cli Foo.scala --debug --debug-mode l --debug-port 5006\n```\n\n## Inject code as JAR file in class path\n\nIf your application inspects its class path, and requires only JAR files in it, use `--as-jar` to\nput the Scala CLI project in the class path as a JAR file rather than as a directory:\n\n```bash ignore\nscala-cli Foo.scala --as-jar\n```\n"
  },
  {
    "path": "website/docs/commands/setup-ide.md",
    "content": "---\ntitle: IDE Setup\nsidebar_position: 14\n---\n\nWhether it's VS Code or IntelliJ, Scala CLI can help you setup your IDE of choice by generating the files that are necessary for it, providing you with full-blown IDE support.\n\nUsing Scala CLI should be as simple as possible, so under the hood this command is run before every `run`, `compile`, or `test` command.\nAs a result, in most cases you don't need to run this command manually.\n\nBut if you want to, invoke `setup-ide` like:\n\n```bash\nscala-cli setup-ide . --scala 2.13\n```\n\nKeep in mind that if you change any of these options, you may need to restart your IDE, or re-import your project.\n\n### IDE support internals\n\nAfter invoking `setup-ide`, two files should be generated:\n- `.bsp/scala-cli.json`\n- `.scala-build/ide-options-v2.json`\n\nThe first file is specifically created for Build Server Protocol (BSP) support in your IDE.\nBSP is supported by VS Code (via the Metals extension) and IntelliJ (with the Scala plugin), and defines the way in which IDEs gather information about the project you are working on.\n\nThe second file is designed to store settings used by the Scala CLI while generating BSP configuration.\nThis includes all options, such as the Scala version, custom arguments, and more, but fortunately you shouldn't need to edit it.\n"
  },
  {
    "path": "website/docs/commands/shebang.md",
    "content": "---\ntitle: Shebang\nsidebar_position: 26\n---\n\nThis command is equivalent to `run`, but it changes the way Scala CLI parses options (used to configure the tool) and\ninputs (the sources of your project) in order to be compatible with `shebang` scripts.\n\nThe command `shebang` also allows script files to be executed even if they have no file extension,\nprovided they start with the [`shebang` header](../guides/scripting/shebang.md#shebang-script-headers).\nNote that those files are always run as scripts even though they may contain e.g. valid `.scala` program.\n\nNormally, inputs and Scala CLI options can be mixed. Program arguments (to be passed to your app) have to be specified\nafter `--` (double dash) separator.\n\n```bash ignore\nscala-cli [command] [scala_cli_options | input]... -- [program_arguments]...\n```\n\nFor the `shebang` command, only a single input can be set. All Scala CLI options must be set before\nthe input, while everything after the input is considered a program argument.\n\n```bash ignore\nscala-cli shebang [scala_cli_options]... input [program_arguments]...\n```\n\nMore details can be found in [Shebang guide](../guides/scripting/shebang.md).\n\n"
  },
  {
    "path": "website/docs/commands/test.md",
    "content": "---\ntitle: Test\nsidebar_position: 7\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nThe `test` command runs test suites in the test sources.\nTest sources are compiled separately (after the 'main' sources), and may use different dependencies, compiler options,\nand other configurations.\n\nBy default, all command line options apply to both the main and test sources,\nso [using directives](../guides/introduction/using-directives.md) can be used to provide test-specific configurations.\n\n## Test sources\n\nA source file is treated as test source if:\n\n- the file name ends with `.test.scala`, or\n- the file comes from a directory that is provided as input, and the relative path from that file to its original\n  directory contains a `test` directory, or\n- it contains the `//> using target.scope test` directive\n\n:::caution\nThe `using target` directives are an experimental feature, and may change in future versions of Scala CLI.\n:::\n\nThe second rule may sound a bit complicated, so let's explain it using following directory structure:\n\n<ChainedSnippets>\n\n```bash ignore\ntree example\n```\n\n```text\nexample\n├── a.scala\n├── a.test.scala\n└── src\n    ├── main\n    │   └── scala\n    │       └── d.scala\n    ├── test\n    │   └── scala\n    │       └── b.scala\n    └── test_unit\n        └── scala\n            └── e.scala\n```\n\n</ChainedSnippets>\n\nGiven that directory structure, let's analyze what file(s) will be treated as tests based on the provided inputs.\n\n`scala-cli example` results in the following files being treated as test sources:\n\n- `a.test.scala`, since it ends with `.test.scala`\n- `src/test/scala/b.scala`, since the path to that directory contains a directory named `test`\n\nNote that `e.scala` is not treated as a test source since it lacks a parent directory in its relative path that is\nexactly named `test` (the name`test_unit` starts with `test`, but Scala CLI only looks for parent directories on the\nrelative path with the exact name `test`).\n\n`scala-cli example/src` results in `src/test/scala/b.scala` being treated as a test file since its relative\npath (`test/scala/b.scala`) contains a directory named `test`.\n\nConversely, `scala-cli example/src/test` results in no test sources, since the relative path to `b.scala` does not\ncontain `test` (the fact that the directory provided as input is named `test` does not make its content a test source).\n\nDirectives take precedence over file or path names, so `using target.scope main` can be used to force `test/a.scala`\nor `a.test.scala` to not be treated as tests.\n\nAs a rule of thumb, we recommend naming all of your test files with the `.test.scala` suffix.\n\n## Test directives\n\nWhen configuring your tests with `using` directives, it's usually advised to use their test scope equivalents, so that\nonly tests are affected.\n\nFor example, when declaring a test framework dependency, in most cases you wouldn't need it\nwhen running your whole app, you only need it in tests. So rather than declare it globally with `using dep`, you can use\nthe `test.dep` directive:\n\n```scala compile\n//> using test.dep org.scalameta::munit::1.0.2\n```\n\nFor more details on test directives,\nsee [the `using` directives guide](../guides/introduction/using-directives.md#directives-with-a-test-scope-equivalent).\n\n## Test framework\n\nIn order to run tests with a test framework, add the framework dependency to your application.\nSome of the most popular test frameworks in Scala are:\n\n- [munit](https://scalameta.org/munit): `org.scalameta::munit::1.0.2`\n- [utest](https://github.com/com-lihaoyi/utest): `com.lihaoyi::utest::0.8.4`\n- [ScalaTest](https://www.scalatest.org): `org.scalatest::scalatest::3.2.19`\n- [JUnit 4](https://junit.org/junit4), which can be used via\n  a [dedicated interface](https://github.com/sbt/junit-interface): `com.github.sbt:junit-interface:0.13.3`\n- [Weaver](https://disneystreaming.github.io/weaver-test/): `com.disneystreaming::weaver-cats:0.8.3`. You may need to\n  specify weaver's test framework with `//> using testFramework weaver.framework.CatsEffect` if you had other test\n  framework in your dependencies.\n\nThe following example shows how to run an munit-based test suite:\n\n```scala title=MyTests.test.scala\n//> using test.dep org.scalameta::munit::1.0.2\n\nclass MyTests extends munit.FunSuite {\n  test(\"foo\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli test MyTests.test.scala\n```\n\n```text\nCompiling project (1 Scala source)\nCompiled project\nMyTests:\n  + foo 0.143s\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nMyTests\nfoo\n-->\n\n## Filter test suite\n\nPassing the `--test-only` option to the `test` sub-command filters the test suites to be run:\n\n```scala title=BarTests.test.scala\n//> using test.dep org.scalameta::munit::1.0.2\npackage tests.only\n\nclass BarTests extends munit.FunSuite {\n  test(\"bar\") {\n    assert(2 + 3 == 5)\n  }\n}\n```\n\n```scala title=HelloTests.test.scala\npackage tests\n\nclass HelloTests extends munit.FunSuite {\n  test(\"hello\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n```bash\nscala-cli test . --test-only 'tests.only*' \n# tests.only.BarTests:\n#   + bar 0.045s\n```\n\n<!-- Expected:\ntests.only.BarTests:\n+ bar\n-->\n\n## Watch mode\n\nUse `--watch` to re-run your tests whenever sources change:\n\n```bash ignore\nscala-cli test --watch MyTests.test.scala\n```\n\n`--watching` can extend that to files or directories outside your Scala sources:\n\n```bash ignore\nscala-cli test --watch --watching ./data MyTests.test.scala\n```\n\nYou can declare the same extra watched paths from sources:\n\n```scala\n//> using watching ./data\n```\n\nIf both are used, Scala CLI watches all of the configured paths.\n\n## Filter test case\n\n### Munit\n\nTo run a specific test case inside the unit test suite pass `*exact-test-name*` as an argument to scala-cli:\n\n```scala title=BarTests.test.scala\n//> using test.dep org.scalameta::munit::1.0.2\npackage tests.only\n\nclass Tests extends munit.FunSuite {\n  test(\"bar\") {\n    assert(2 + 2 == 5)\n  }\n  test(\"foo\") {\n    assert(2 + 3 == 5)\n  }\n  test(\"foo-again\") {\n    assert(2 + 3 == 5)\n  }\n}\n```\n\n```bash\nscala-cli test . --test-only 'tests.only*'  -- '*foo*'\n# tests.only.Tests:\n#   + foo 0.045s\n#   + foo-again 0.001s\n```\n\n<!-- Expected:\ntests.only.Tests:\n+ foo\n+ foo-again\n-->\n\n## Test arguments\n\nYou can pass test arguments to your test framework by passing them after `--`:\n\n```scala title=MyTests.test.scala\n//> using test.dep org.scalatest::scalatest::3.2.19\n\nimport org.scalatest._\nimport org.scalatest.flatspec._\nimport org.scalatest.matchers._\n\nclass Tests extends AnyFlatSpec with should.Matchers {\n  \"A thing\" should \"thing\" in {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli test MyTests.test.scala -- -oD\n```\n\n```text\nCompiling project (1 Scala source)\nCompiled project\nTests:\nA thing\n- should thing (22 milliseconds)\nRun completed in 359 milliseconds.\nTotal number of tests run: 1\nSuites: completed 1, aborted 0\nTests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0\nAll tests passed.\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nTests:\nA thing\nshould thing\nAll tests passed.\n-->\n"
  },
  {
    "path": "website/docs/commands/version.md",
    "content": "---\ntitle: Version\nsidebar_position: 25\n---\n\nimport {ChainedSnippets} from \"../../src/components/MarkdownComponents.js\";\n\nThe `version` sub-command prints the currently used Scala CLI version and the associated Scala version.\n\n<ChainedSnippets>\n\n```bash\nscala-cli version\n```\n\n```text\nScala CLI version: 1.5.4\nScala version (default): 3.5.2\n```\n\n</ChainedSnippets>\n\nIt is also possible to print the same output with the `-version` option passed to the default sub-command.\nThis way doesn't allow to use the other options relevant to `version`, however.\n\n<ChainedSnippets>\n\n```bash\nscala-cli -version\n```\n\n```text\nScala CLI version: 1.5.4\nScala version (default): 3.5.2\n```\n\n</ChainedSnippets>\n\nWhen `version` is called, Scala CLI will automatically check if it's up to date.\nIf your version is outdated, you will get a warning.\n\n```text\nYour Scala CLI. version is outdated. The newest version is 0.1.19\nIt is recommended that you update Scala CLI through the same tool or method you used for its initial installation for avoiding the creation of outdated duplicates.\n```\n\nYou can skip checking if Scala CLI is up to date by passing the `--offline` option.\n\n```bash\nscala-cli version --offline\n```\n\nIt's also possible to just print the raw Scala CLI version with the `--cli-version` option.\nThis won't check if the app is outdated, so the `--offline` option is unnecessary in this context.\n\n<ChainedSnippets>\n\n```bash\nscala-cli version --cli-version\n```\n\n```text\n1.5.4\n```\n\n</ChainedSnippets>\n\n:::note\nDo not confuse the `version` sub-command's `--cli-version` option with the launcher option under the same name, as they\ndo different things. The former prints the raw Scala CLI version, while the latter allows to change the Scala CLI\nlauncher version. In fact, both of them can be used at one time.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --cli-version 0.1.18 version --cli-version\n```\n\n```text\n0.1.18\n```\n\n</ChainedSnippets>\n\nLauncher options have to be passed before the sub-command is specified, which allows to differentiate between them.\n:::\n\nSimilarly, it's possible to just print the raw default Scala version.\nOnce more, this won't check if the app is outdated, so the `--offline` option is unnecessary in this context as well.\n\n<ChainedSnippets>\n\n```bash\nscala-cli version --scala-version\n```\n\n```text\n3.5.2\n```\n\n</ChainedSnippets>\n"
  },
  {
    "path": "website/docs/cookbooks/_category_.json",
    "content": "{\n  \"label\": \"Cookbook\",\n  \"position\": 15\n}\n"
  },
  {
    "path": "website/docs/cookbooks/ide/_category_.json",
    "content": "{\n  \"label\": \"IDE\",\n  \"position\": 20\n}\n"
  },
  {
    "path": "website/docs/cookbooks/ide/emacs.md",
    "content": "---\ntitle: Emacs\nsidebar_position: 14\n---\n\nEmacs users can make it easier to use Scala CLI from within their editor by\nloading an extension: [https://github.com/ag91/scala-cli-repl](https://github.com/ag91/scala-cli-repl).\n\nThat lets you send Scala code directly from your buffer to the Scala REPL.\n\n![scala-cli-repl-demo](/img/scala-cli-repl.jpg)\n\nThe extension also facilitates [literate\nprogramming](https://en.wikipedia.org/wiki/Literate_programming) using\n[Org Mode](https://orgmode.org/), by letting the user experiment with\nsource blocks looking like the following.\n\n``` org\n#+begin_src scala :scala-version 3.0.0 :dep '(\"com.lihaoyi::os-lib:0.9.0\")\nprintln(\"This is:\" + os.pwd)\n#+end_src\n#+end_src\n```\n\nIn the above you can see that you can select the Scala version and\ndependencies you need for your code.\n\nThe users who use [lsp-metals](https://github.com/emacs-lsp/lsp-metals)\ncan also enable lsp support within a source block to access utilities as\ncompletion and navigation from within the Org Mode file.\n"
  },
  {
    "path": "website/docs/cookbooks/ide/intellij-multi-bsp.md",
    "content": "---\ntitle: Setup multiple projects in IDEA IntelliJ as separate modules\nsidebar_position: 13\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nIf you've read through [the basic IDEA IntelliJ cookbook](intellij.md), then you already know how to import a Scala CLI\nproject using `BSP`. However, in some cases importing a single project just does not fit the bill.\n\nHere's a walk-through for a slightly more advanced scenario.\n\nLet's say we keep the sources for 2 separate Scala apps in one repository. Each has its own subdirectory, to keep things\nclean. Additionally, you have another one for scripts alongside them.\n\nIt looks somewhat similar to this:\n\n<ChainedSnippets>\n\n```bash\ntree -a\n```\n\n```text\n.\n├── app1\n│   ├── src\n│   │   └── HelloWorld1.scala\n│   └── test\n│       └── MyTests1.test.scala\n├── app2\n│   ├── src\n│   │   └── HelloWorld2.scala\n│   └── test\n│       └── MyTests2.test.scala\n└── scripts\n    ├── AnotherScript.sc\n    └── SomeScript.sc\n\n7 directories, 6 files\n```\n\n```scala title=app1/src/HelloWorld1.scala\n@main def hello: Unit = println(\"hello1\")\n```\n\n```scala title=app1/test/MyTests1.scala\n//> using dep org.scalameta::munit:1.0.2\nclass MyTests1 extends munit.FunSuite {\n  test(\"my test 1\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n```scala title=app2/src/HelloWorld2.scala\n@main def hello: Unit = println(\"hello2\")\n```\n\n```scala title=app2/test/MyTests2.scala\n//> using dep com.lihaoyi::utest::0.8.4\n\nimport utest.*\n\nobject MessagesTests extends TestSuite {\n  val tests = Tests {\n    test(\"my test 2\") {\n      assert(2 + 2 == 4)\n    }\n  }\n}\n```\n\n```scala title=scripts/SomeScript.sc\nprintln(\"some script\")\n```\n\n```scala title=scripts/AnotherScript.sc\nprintln(\"another script\")\n```\n\n</ChainedSnippets>\n\nWhen running these apps, you'd like to run them separately. `app1` and `app2` may have conflicting dependencies, or it\nmay just not feel hygienic to share their classpath long term.\n\nHowever, you keep those in one repository because of business relevance (or whatever other reasons why they are tied\ntogether), and so, you'd like to see them all at once in your IDE, with all the syntax coloring, completions and\ndebugging\nyour code straight from the IDE, the whole shebang.\n\nIt's tempting to just run:\n\n```bash\nscala-cli setup-ide .\n```\n\nUnfortunately, in this case that won't really do the trick. Even if you run and package the apps & scripts from the\nterminal separately, when importing everything together to your IDE like this, the single `BSP` project will make them\nshare their classpath. This in turn means that things will break.\n\nThe only way to solve this is for each to have its own `BSP` configuration, really.\nAnd so:\n\n```bash\nscala-cli setup-ide app1\nscala-cli setup-ide app2\nscala-cli setup-ide scripts\n```\n\nAs a result, a separate `.bsp` directory was created in `app1`, `app2` and `scripts`, respectively.\n\n<ChainedSnippets>\n\n```bash\ntree -a\n```\n\n```text\n.\n├── app1\n│   ├── .bsp\n│   │   └── scala-cli.json\n│   ├── .scala-build\n│   │   ├── ide-inputs.json\n│   │   └── ide-options-v2.json\n│   ├── src\n│   │   └── HelloWorld1.scala\n│   └── test\n│       └── MyTests1.test.scala\n├── app2\n│   ├── .bsp\n│   │   └── scala-cli.json\n│   ├── .scala-build\n│   │   ├── ide-inputs.json\n│   │   └── ide-options-v2.json\n│   ├── src\n│   │   └── HelloWorld2.scala\n│   └── test\n│       └── MyTests2.test.scala\n└── scripts\n    ├── .bsp\n    │   └── scala-cli.json\n    ├── .scala-build\n    │   ├── ide-inputs.json\n    │   └── ide-options-v2.json\n    ├── AnotherScript.sc\n    └── SomeScript.sc\n\n13 directories, 15 files\n\n\n\n```\n\n</ChainedSnippets>\n\n\nAfter opening the root directory in `IntelliJ` (`File` -> `Open...`), the 3 `BSP` setups should be successfully\ndetected.\n\n![IntelliJ noticed the 3 BSP configs](/img/intellij_bsp_build_scripts_found.png)\n\nHowever, since there are 3 different setups, `IntelliJ` doesn't know what to import. And so, we have to set it up\nourselves.\n\nRight-click on your project root directory in `Intellij` and go into `Module Settings`.\n\n![Go into Module Settings](/img/intellij_module_settings.png)\n\nThen, under `Project Structure` -> `Modules` press the `+` button and then `Import Module`.\n\n![Import a module](/img/intellij_module_settings_import_module.png)\n\nNavigate to each of the subdirectories from there and add them as a `BSP` module (`BSP` should be an available choice,\nif the `setup-ide` was run correctly).\n\n![Import from BSP as external model](/img/intellij_import_bsp_module.png)\n\nYou have to import each of the subdirectories separately (`app1`, `app2` and `scripts`, in the example).\n\nThe end result should look like this:\n\n![End result multi-BSP setup](/img/intellij_multi_bsp_setup.png)\n\nNow each of the subdirectories uses its own `BSP` connection, which in turn means a separate classpath. And all of that\nin a single `IntelliJ` project!\n\nUpon closer inspection, you may notice that `IntelliJ` stores this as separate sub-project configurations. Each\nsubdirectory gets its own `.idea` folder with the relevant settings.\n\n<ChainedSnippets>\n\n```bash\ntree -a\n```\n\n```text\n.\n├── .idea\n│   ├── .gitignore\n│   ├── bsp.xml\n│   ├── codeStyles\n│   │   ├── Project.xml\n│   │   └── codeStyleConfig.xml\n│   ├── intellij-multi-bsp.iml\n│   ├── misc.xml\n│   ├── modules.xml\n│   ├── sbt.xml\n│   ├── vcs.xml\n│   └── workspace.xml\n├── app1\n│   ├── .bsp\n│   │   └── scala-cli.json\n│   ├── .idea\n│   │   └── modules\n│   │       └── app1-root.iml\n│   ├── .scala-build\n│   │   ├── ide-inputs.json\n│   │   └── ide-options-v2.json\n│   ├── src\n│   │   └── HelloWorld1.scala\n│   └── test\n│       └── MyTests1.test.scala\n├── app2\n│   ├── .bsp\n│   │   └── scala-cli.json\n│   ├── .idea\n│   │   └── modules\n│   │       └── app2-root.iml\n│   ├── .scala-build\n│   │   ├── ide-inputs.json\n│   │   └── ide-options-v2.json\n│   ├── src\n│   │   └── HelloWorld2.scala\n│   └── test\n│       └── MyTests2.test.scala\n└── scripts\n    ├── .bsp\n    │   └── scala-cli.json\n    ├── .idea\n    │   └── modules\n    │       └── scripts-root.iml\n    ├── .scala-build\n    │   ├── ide-inputs.json\n    │   └── ide-options-v2.json\n    ├── AnotherScript.sc\n    └── SomeScript.sc\n\n21 directories, 28 files\n```\n\n</ChainedSnippets>\n"
  },
  {
    "path": "website/docs/cookbooks/ide/intellij-sbt-with-bsp.md",
    "content": "---\ntitle: Scala CLI project in IntelliJ alongside your existing SBT project\nsidebar_position: 12\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nIf you've read through [the basic IDEA IntelliJ cookbook](intellij.md), then you already know how to import a Scala CLI\nproject using `BSP`. However, did you know that it's possible to import one alongside an `SBT` project? (Or any other\nbuild tool's project, for that matter.)\n\nHere's a walk-through for a simple example.\n\nLet's say you have an existing `SBT` project that you're working with for a while now. You have imported it in IntelliJ\nand the integration works nicely.\nThe project's structure looks roughly like this:\n\n<ChainedSnippets>\n\n```bash\ntree -a\n```\n\n```text\n.\n├── .bsp\n│   └── sbt.json\n├── .idea\n│   ├── .gitignore\n│   ├── codeStyles\n│   │   ├── Project.xml\n│   │   └── codeStyleConfig.xml\n│   ├── libraries\n│   │   ├── sbt__junit_junit_4_13_2_jar.xml\n│   │   ├── sbt__org_hamcrest_hamcrest_core_1_3_jar.xml\n│   │   ├── sbt__org_scala_lang_scala3_library_3_3_1_3_jar.xml\n│   │   ├── sbt__org_scala_lang_scala_library_2_13_8_jar.xml\n│   │   ├── sbt__org_scala_sbt_test_interface_1_0_jar.xml\n│   │   ├── sbt__org_scalameta_junit_interface_1_0_0_M6_jar.xml\n│   │   └── sbt__org_scalameta_munit_3_1_0_0_M6_jar.xml\n│   ├── misc.xml\n│   ├── modules\n│   │   ├── intellij-sbt-with-scala-cli-bsp-build.iml\n│   │   └── intellij-sbt-with-scala-cli-bsp.iml\n│   ├── modules.xml\n│   ├── sbt.xml\n│   ├── scala_compiler.xml\n│   ├── vcs.xml\n│   └── workspace.xml\n├── build.sbt\n├── project\n│   └── build.properties\n├── scripts\n│   ├── AnotherScript.sc\n│   └── SomeScript.sc\n├── src\n│   ├── main\n│   │   └── scala\n│   │       └── main.scala\n│   └── test\n│       └── scala\n│           └── MyTests.test.scala\n└── target\n    └── scala-3.1.3\n        ├── classes\n        │   ├── main$package$.class\n        │   ├── main$package.class\n        │   ├── main$package.tasty\n        │   ├── main.class\n        │   └── main.tasty\n        └── test-classes\n            ├── MyTests.class\n            └── MyTests.tasty\n\n16 directories, 32 files\n```\n\n</ChainedSnippets>\n\nNow, let's say that at some point you decide you need to occasionally run some scripts relevant to this project. You run\nthose scripts with Scala CLI and decide it'd be convenient to keep them in the same repository.\n\n<ChainedSnippets>\n```bash ignore\ntree scripts\n```\n\n```text\nscripts\n├── AnotherScript.sc\n└── SomeScript.sc\n\n0 directories, 2 files\n```\n\n</ChainedSnippets>\n\nHowever, you already import this repo as an `SBT` project, so what can you do?\nWell, you can import the Scala CLI scripts as a `BSP` module **alongside** your `SBT` project.\n\nMake sure you setup the `BSP` configuration for the `scripts` directory first:\n\n```bash ignore\nscala-cli setup-ide scripts\n```\n\nAs a result, a `scripts/.bsp` directory should be created.\nNow, right-click on your project root directory in `IntelliJ` and go into `Module Settings`\n\n![Go into Module Settings](/img/intellij_sbt_module_settings.png)\n\nThen, under `Project Structure` -> `Modules` press the `+` button and then `Import Module`.\n\n![Import a module](/img/intellij_module_settings_import_module.png)\n\nNavigate to the `scripts` directory from there and add it as a `BSP` module (`BSP` should be an available choice,\nif the `setup-ide` command was run correctly).\n\n![Import from BSP as external model](/img/intellij_import_bsp_module.png)\n\nNow the `scripts` `BSP` module should be imported and you should be able to run the scripts from your IDE.\nThe end result should look like this:\n\n![Import from BSP as external model](/img/intellij_sbt_alongside_bsp.png)\n"
  },
  {
    "path": "website/docs/cookbooks/ide/intellij.md",
    "content": "---\ntitle: IntelliJ IDEA setup\nsidebar_position: 11\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nIt is possible to import a Scala CLI project into IDEA IntelliJ. The import is done\nthrough [BSP](https://build-server-protocol.github.io/) and the relevant files can be seen in the hidden `.bsp`\ndirectory, generated after running the `setup-ide` command (or implicitly the `run`|`compile`|`test` commands as well).\n\nHere's a walk-through for a simple import scenario.\n\n```scala title=src/HelloWorld.scala\n@main\ndef hello() = println(\"Hello, world\")\n```\n\n```scala title=test/MyTests.test.scala\n//> using dep org.scalameta::munit::1.0.2\n\nclass MyTests extends munit.FunSuite {\n  test(\"test\") {\n    val x = 2\n    assertEquals(x, 2)\n  }\n}\n```\n\n<ChainedSnippets>\n\n```bash\ntree -a\n```\n\n```text\n.\n├── src\n│   └── HelloWorld.scala\n└── test\n    └── MyTests.test.scala\n```\n\n</ChainedSnippets>\n\nThe following command generates all the relevant configurations for IDE support:\n\n```bash\nscala-cli setup-ide .\n```\n\nAlternatively, the first time you run the `run`|`compile`|`test` commands, the relevant IDE configuration will be\ngenerated as well.\n\nIn fact it is entirely sufficient to just run:\n\n<ChainedSnippets>\n\n```bash\nscala-cli .\n```\n\n```text\nHello, world\n```\n\n</ChainedSnippets>\n\nNext, we need to launch IDEA IntelliJ.\nTo import the project, you can import it, `File` -> `New` -> `Project from Existing Sources...`\n\n![Project from Existing Sources...](/img/intellij_project_from_existing_sources.png)\n\nAnd then pick `BSP` as the external model (if `BSP` doesn't show up at this step, it means that the `.bsp` folder is\nabsent and should be generated with the `scala-cli setup-ide` subcommand).\n\n![BSP external model](/img/intellij_bsp_external_model.png)\n\nAlternatively, you can directly call `File` -> `Open` and pick the directory, allowing `IntelliJ` to figure things out\nby itself (which it definitely should, if the `.bsp` folder is in place). Just make sure the `.bsp` folder is present in\nthe project root directory.\n\n![just open the directory](/img/intellij_open_dir.png)\n\nYou should now be able to see the active `BSP` connection icon in the lower right corner of your `IDEA IntelliJ` window.\n\n![BSP icon](/img/intellij_bsp_icon.png)\n\nThe run buttons, syntax completions & coloring should now be available when opening source files.\nIntelliJ should also be identifying the main sources(blue) and test sources (green) directories.\n\n![imported project layout](/img/intellij_imported_project_layout.png)\n\nIDEA IntelliJ will now call Scala CLI's `bsp` command to handle running, testing and debugging your code in this\nproject.\n\n![run your code in IntelliJ](/img/intellij_run_code_with_bsp.png)\n\nAlso, please do note, that the project structure comes directly from Scala CLI and you shouldn't really have to control\nit from IntelliJ. Instead, being a CLI tool, we have a terminal-first policy, and so, if you want to update the project\nstructure to include an extra directory, just run the proper command to update the `.bsp` directory.\n\n```bash ignore\nscala-cli setup-ide . ../extra-directory\n```\n\nNow, after waiting for a bit, the extra directory should be picked up by `IntelliJ`.\n\n![BSP project with 2 directories](/img/intellij_project_layout_with_extra_dir.png)\n\nAnd if for whatever reason you want to reload the project manually, you can do it from `IntelliJ`'s `BSP` panel, just\nclick `Refresh` there.\n\n![Refresh BSP manually](/img/intellij_refresh_bsp.png)\n\nNote: this example scenario assumes the sources are put in separate subdirectories, 1 per scope. This is because that's\nwhat is encouraged by IDEA IntelliJ, which assumes by default that tests should have its own directory. However, nothing\nreally forces you to bother with that, you can put everything in the root directory (or anywhere else, really), and it\nshould (mostly) work fine:\n\n<ChainedSnippets>\n\n```bash\ntree -a\n```\n\n```text\n.\n├── HelloWorld.scala\n└── MyTests.test.scala\n```\n\n</ChainedSnippets>\n\n![Scala CLI flat project structure imported to IntelliJ](/img/intellij_flat_sources_layout.png)\n\n"
  },
  {
    "path": "website/docs/cookbooks/ide/vscode.md",
    "content": "---\ntitle: VSCode setup\nsidebar_position: 10\n---\n\nScala CLI can generate the files that are necessary for providing IDE support in Visual Studio Code.\n\nFor example, here is a simple project in scala-cli which contains only one main and one test class.\n\n```scala title=HelloWorld.scala\n@main\ndef hello() = println(\"Hello, world\")\n```\n\n```scala title=MyTests.test.scala\n//> using dep org.scalameta::munit::1.0.2\n\nclass MyTests extends munit.FunSuite {\n  test(\"test\") {\n    val x = 2\n    assertEquals(x, 2)\n  }\n}\n```\n\nThe following command generates configuration files for VSCode support:\n\n```bash\nscala-cli setup-ide .\n```\n\nThere is also another way. The first time you run the `run`|`compile`|`test` commands, the configuration files for the\nVSCode will be also generated.\n\n```bash\nscala-cli run .\n# \"Hello, world\"\n```\n\nand then, we launch Visual Studio Code\n\n```bash ignore\ncode .\n```\n\nAfter starting metals, you will see the `run/debug` buttons in `HelloWorld.scala` and `test/debug`\nin `MyTests.test.scala` (assuming the following directory layout).\n\n![layout](/img/source_layout.png)\n\nPressing the `run` button will run the `Main.scala`, the output will be visible in `DebugConsole`.\n\nimport VSCodeRun from '@site/static/img/vscode-run.png';\n\n<img src={VSCodeRun} />\n\nAnd the similar effect after pressing the `test` button.\n\nimport VSCodeTest from '@site/static/img/vscode-test.png';\n\n<img src={VSCodeTest} />\n"
  },
  {
    "path": "website/docs/cookbooks/intro.md",
    "content": "---\ntitle: Index\nsidebar_position: 1\n---\n\n# Cookbooks\n\nThis section of the documentation contains a set of recipes that show how to use Scala CLI in particular situations.\nThe recipes are intended to provide a solution to the task at hand, but also without going into great detail.\n\nFor a more in-depth analysis, please check out our [Guides](../guides/intro.md).\n\nTo get started, try one of the cookbooks below:\n\n## Introductory cookbooks\n\n- [Picking the Scala version](introduction/scala-versions.md)\n- [Picking the Java version](introduction/scala-jvm.md)\n- [Debugging with Scala CLI](introduction/debugging.md)\n- [Filter the test suites to run](introduction/test-only.md)\n- [Running scripts](introduction/scala-scripts.md)\n- [Scripts with instant startup](introduction/instant-startup-scala-scripts.md)\n- [Sharing and testing code with GitHub gists](introduction/gists.md)\n- [Use Scala CLI in GitHub Actions](introduction/gh-action.md)\n\n## Working with Scala CLI in IDEs\n\n- [Metals with VS Code](ide/vscode.md)\n- [IDEA IntelliJ](ide/intellij.md)\n- [Scala CLI alongside SBT in IDEA IntelliJ](ide/intellij-sbt-with-bsp.md)\n- [Multiple Scala CLI projects as separate modules in IDEA IntelliJ](ide/intellij-multi-bsp.md)\n- [Scala CLI within Emacs](ide/emacs.md)\n\n## Packaging ⚡️\n\n- [Packaging Scala applications as executable files](package/scala-package.md)\n- [Packaging Scala applications as Docker images](package/scala-docker.md)\n- [Packaging Scala applications as GraalVM native images](package/native-images.md)\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/_category_.json",
    "content": "{\n  \"label\": \"Introduction\",\n  \"position\": 1\n}\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/debugging.md",
    "content": "---\ntitle: Debugging\nsidebar_position: 4\n---\n\nDebugging with Scala CLI is very simple. All one needs to do is to pass the `--debug` option, which is available for the `run` and `test` sub-commands.\n\n## Preparing files to debug\n\nLet's start with creating a few example files, which we will run and debug later on:\n\n```scala title=MyClass.scala\nobject MyClass extends App  {\n  println(\"Line 1\")\n  println(\"Line 2\")\n  println(\"Line 3\")\n}\n```\n\n```scala title=MyTests.test.scala\n//> using dep org.scalameta::munit::1.0.2\n\nclass MyTests extends munit.FunSuite {\n  test(\"foo\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n## VS Code with Metals\n\n### Configuration\n\nIf you are using **VS Code with Metals**, you will have to define **launch configurations** in the `launch.json` file inside the `.vscode` directory.\n\nWithin each configuration you will have to define the following [configuration attributes](https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes): `type`, `request`, `name`, `buildTarget`, `hostName` and `port`.\n\nIf you don't know what are the exact **build target** names of your project, you can check them in [Metals Doctor](https://scalameta.org/metals/docs/editors/vscode/#run-doctor) in the `Build target` column:\n\n![Metals Doctor view](/img/debugging_run_doctor_view.png)\n\nIf **no build targets** have been found, perform the following steps:\n- run `scala-cli compile .` in the command line.\n- when the compilation is complete, run `Connect to build server` in the Metals **build commands** section.\n\nAfter these steps, build targets should be visible in the Metals Doctor view.\n\nExample `launch.json` configuration file:\n\n```scala title=.vscode/launch.json\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"scala\",\n      \"request\": \"attach\",\n      \"name\": \"project\",\n      \"buildTarget\": \"project_183d125c5c\",\n      \"hostName\": \"localhost\",\n      \"port\": 5005\n    },\n    {\n      \"type\": \"scala\",\n      \"request\": \"attach\",\n      \"name\": \"project-test\",\n      \"buildTarget\": \"project_183d125c5c-test\",\n      \"hostName\": \"localhost\",\n      \"port\": 5005\n    }\t\n  ]\n}\n```\n\nAfter defining configurations in the `launch.json` file, you should be able to see them in **Configuration dropdown** in the **Run and Debug** view:\n\n![Configuration dropdown](/img/debugging_configuration_dropdown.png)\n\nAfter setting up the configuration you can proceed to debugging.\n\n### Debugging\n\nSet [breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints) for the file you would like to debug:\n\n![Setting breakpoints](/img/debugging_setting_breakpoints_vs_code.png)\n\nRun one of the following commands depending on which file you would like to debug:\n- run `scala-cli MyClass.scala --debug` if you would like to debug `MyClass.scala` file\n- run `scala-cli test MyTests.test.scala --debug` if you would like to debug `MyTests.test.scala` file\n\nAfter compilation is completed, Scala CLI should stop and **listen for transport dt_socket at port 5005**.\n\n:::info\nPlease note that 5005 is the default port for debugging with scala-cli. You can always change it by passing `--debug-port` option.\n:::\n\nAt this moment go to the **Run and Debug** view, select proper configuration from the **Configuration dropdown** and run debugger by clicking **green arrow** on the side:\n\n![Running debugger](/img/debugging_running_debugger.png)\n\nAfter all these steps, the debugger should stop at the first breakpoint and you can proceed to **debugging** your code using all features delivered by VS Code. For more information check [this guide](https://code.visualstudio.com/docs/editor/debugging).\n\n## IntelliJ IDEA\n\n### Debugging in the attach mode\n\nThe first thing that you need to do to start debugging is [setting breakpoints](https://www.jetbrains.com/help/idea/debugging-your-first-java-application.html#setting-breakpoints) for the files you want to debug:\n\n![Setting breakpoints](/img/debugging_setting_breakpoints_intellij.png)\n\nRun one of the following commands depending on which file you would like to debug:\n- run `scala-cli MyClass.scala --debug` if you would like to debug `MyClass.scala` file\n- run `scala-cli test MyTests.test.scala --debug` if you would like to debug `MyTests.test.scala` file\n\nAfter compilation is completed, Scala CLI should stop and **listen for transport dt_socket at port 5005**.\n\n:::info\nPlease note that 5005 is the default port for debugging with scala-cli. You can always change it by passing `--debug-port` option.\n:::\n\nAt this moment, you can attach to process by clicking **Run -> Attach to Process** and choosing process, which is running at port **5005**:\n\n![Attach to Process](/img/debugging_attach_to_process.png)\n\nAfter all these steps, the debugger should stop at the first breakpoint and you can proceed to **debug** your code using all features delivered by IntelliJ IDEA. For more information check [this guide](https://www.jetbrains.com/help/idea/debugging-your-first-java-application.html#analyzing-state).\n\n### Debugging in the listen mode\n\nIf you would like to debug in listen mode, add a new **Remote JVM Debug** [configuration](https://www.jetbrains.com/help/idea/run-debug-configuration.html) with the following setup:\n\n![Listen mode configuration](/img/debugging_listen_mode_config.png)\n\n[Set breakpoints](https://www.jetbrains.com/help/idea/debugging-your-first-java-application.html#setting-breakpoints) for the files you want to debug:\n\n![Setting breakpoints](/img/debugging_setting_breakpoints_intellij.png)\n\nRun the previously set configuration by clicking on the **green debug button** on the side:\n\n![Running debug configuration](/img/debugging_running_debug_configuration.png)\n\nRun one of the following commands depending on which file you would like to debug:\n- run `scala-cli MyClass.scala --debug-mode listen` if you would like to debug `MyClass.scala` file\n- run `scala-cli test MyTests.test.scala --debug-mode listen` if you would like to debug `MyTests.test.scala` file\n\n:::info\n`attach` is the default mode for debugging with Scala CLI. You can always change it by passing `--debug-mode` option. Available modes are: `attach` and `listen`.\n:::\n\nAfter all these steps the debugger should stop at the first breakpoint and you can proceed to **debug** your code using all features delivered by IntelliJ IDEA. For more information check [this guide](https://www.jetbrains.com/help/idea/debugging-your-first-java-application.html#analyzing-state)."
  },
  {
    "path": "website/docs/cookbooks/introduction/formatting.md",
    "content": "---\ntitle: Formatting\nsidebar_position: 5\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nScala CLI also lets you format your code using the [Scalafmt](https://scalameta.org/scalafmt/) formatter.\n\n\n## Basic usage\n\nThis section describes how to use Scala CLI to format your code using default rules. For advanced usage and all available flags, please see next section.\n\n### Check\n\nTo check the format of all your sources in the current directory:\n```bash\nscala-cli fmt --check .\n```\n\nThis command will check if the code is formatted correctly according to the default rules.\nIf it is not, it will return a diff of the changes that need to be made to match formatting rules.\n\n### Format\n\nIf you want to format all your code, you can use the following command:\n```bash\nscala-cli fmt .\n```\nThis command will format all your sources in the current directory according to the default rules.\n\n### Example\n\nLet's take a simple `HelloWorld` application:\n```scala title=FormattingExample.scala reset\n// use scala 3.3.0\n@main def hello()={println(\"Hello, World!\")}\ndef someLongMethodMoreThan80Characters():Unit={println(\"This is a long method\")\nprintln(\"Another print here?\")}\n```\n\nYou can notice that this implementation is written with long lines, no indentation, no spaces and no new lines.\nIt is hard to distinguish where one statement ends and another begins, which makes it hard to read.\n\n<ChainedSnippets>\n\nLet's check the format of this code:\n```bash fail\nscala-cli fmt --check .\n```\nHere is the output:\n```diff\n--- a/private/tmp/formatting-example/FormattingExample.scala\n+++ b/private/tmp/formatting-example/FormattingExample.scala\n@@ -1,4 +1,7 @@\n // use scala 3.3.0\n-@main def hello()={println(\"Hello, World!\")}\n-def someLongMethodMoreThan80Characters():Unit={println(\"This is a long method\")\n-println(\"Another print here?\")}\n+@main def hello() = { println(\"Hello, World!\") }\n+def someLongMethodMoreThan80Characters(): Unit = {\n+  println(\"This is a long method\")\n+  println(\"Another print here?\")\n+}\n+\nerror: --test failed\n```\n</ChainedSnippets>\n\nAs we can see, the code is not correctly formatted according to the default rules and [Scalafmt](https://scalameta.org/scalafmt/) formatter provides a diff of the changes that need to be made to match them.\n\n<ChainedSnippets>\nTo apply the suggested changes to our code, we can run:\n\n```bash\nscala-cli fmt .\n```\nAnd there will be no output, which means that the formatting was successful. Let's see the code after formatting:\n\n```scala title=FormattingExample2.scala reset\n// use scala 3.3.0\n@main def hello() = { println(\"Hello, World!\") }\ndef someLongMethodMoreThan80Characters(): Unit = {\n  println(\"This is a long method\")\n  println(\"Another print here?\")\n}\n```\n</ChainedSnippets>\n\nNow the code is formatted according to the default rules.\n\n<ChainedSnippets>\nLet's check it again:\n\n```bash\nscala-cli fmt --check .\n```\n\nAnd now we see `All files are formatted with scalafmt :)` message, which means that the code is formatted correctly.\n\n</ChainedSnippets>\n\n---\n\n## Advanced usage\n\nThis section describes how to use Scala CLI to format your code using custom parameters and the `.scalafmt.conf` file.\n\n\n### Default \nBy default, Scala CLI creates a `.scalafmt.conf` file in the `.scala-build/.scalafmt.conf` directory with default rules. The default rules in this case are:\n```hocon\nversion = 3.7.12\nrunner.dialect = scala3\n```\n\n:::note\nThe default version of `scalafmt` is linked to the Scala CLI version you are using. In case you want to use a different version, you can override it with the `--fmt-version` option. The defaults might also change after doing a Scala CLI version update.\n:::\n\n### Custom config\n\nThe custom configuration will be used if the `.scalafmt.conf` file is present in the root directory of the project. \nYou can create a `.scalafmt.conf` file in the root directory of your project with the desired `Scalafmt` configuration.\n\n\nScala CLI also allows you to pass the relative path to the desired config with `--scalafmt-conf`, `--scalafmt-config` or `--fmt-config`. For example:\n\n```bash fail\nscala-cli fmt --check --scalafmt-conf=super.scalafmt.conf FormattingExample.scala\n```\n\nThis command will check the format of the `FormattingExample.scala` file using the custom configuration from the `super.scalafmt.conf` file.\n\n### Custom dialect\n\nYou can specify the dialect of Scala using `--dialect` or `--scalafmt-dialect` parameter. For example:\n\n```bash\nscala-cli fmt --dialect=scala3 .\n```\n\nThis command will run check using Scala 3 dialect. All dialects can be found in [Scalafmt documentation](https://scalameta.org/scalafmt/docs/configuration.html#scala-dialects) in section *Scala Dialects*.\n\n### Custom version\n\nYou can specify the version of Scalafmt using `--fmt-version` or `--scalafmt-version` parameter. For example:\n\n```bash\nscala-cli fmt --fmt-version=3.7.12 .\n```\n\nThe version of Scalafmt can be found in [Scalafmt releases](https://github.com/scalameta/scalafmt/releases) on Github.\n\n### Saving the configuration\n\nThe configuration can be saved in the `.scalafmt.conf` file in the root directory of the project automatically by running the following command:\n\n```bash\nscala-cli fmt --save-scalafmt-conf .\n```\n\n### Direct `scalafmt` arguments\n\nYou can pass any `scalafmt` arguments directly to the `scalafmt` command using `-F` or `--scalafmt-arg` parameter. For example:\n\n```bash\nscala-cli fmt -F --help\n```\nThe full list of `scalafmt` arguments can be found in the [Scalafmt CLI documentation](https://scalameta.org/scalafmt/docs/installation.html#--help) in section *`--help`*.\n\n\n---\n\n## See also\n\nPlease see the our [Github Actions Cookbook](/docs/cookbooks/introduction/gh-action#check-your-scala-code-format) to learn how to setup checking your Scala code formating using Github Actions. And for more information about Scalafmt, please see the [Scalafmt documentation](https://scalameta.org/scalafmt/)."
  },
  {
    "path": "website/docs/cookbooks/introduction/gh-action.md",
    "content": "---\ntitle: GitHub Actions\nsidebar_position: 9\n---\n\n## Preparing simple application\n\nScala CLI lets you run, test, and package Scala code in various environments, including GitHub CI. \nTo use Scala CLI features in a simple way you can use the GitHub Actions [scala-cli-setup](https://github.com/VirtusLab/scala-cli-setup) that installs everything necessary to run your Scala CLI application and more.\n\nFor example, here's a simple `ls` application printing the files in a given directory:\n```scala compile title=Ls.scala\n//> using scala 3\n//> using dep com.lihaoyi::os-lib:0.11.3\n\n@main def hello(args: String*): Unit =\n  val path = args.headOption match\n    case Some(p) => os.Path(p, os.pwd)\n    case _       => os.pwd\n\n  if (os.isDir(path)) println(os.list(path).mkString(\",\"))\n  else System.err.println(\"Expected directory path as an input\")\n```\n\nand some tests for `ls` application:\n\n```scala compile title=TestsLs.test.scala\n//> using dep org.scalameta::munit::1.0.2\nimport scala.util.Properties\n\nclass TestsLs extends munit.FunSuite {\n  test(\"ls\") {\n    // prepare test directory\n    val tempDir = os.temp.dir()\n    // create files\n    val expectedFiles = Seq(\"Ls\", \"Hello\").map(tempDir / _)\n    expectedFiles.foreach(os.write(_, \"Hello\"))\n\n    // check\n    val scalaCLILauncher = if (Properties.isWin) \"scala-cli.bat\" else \"scala-cli\"\n    val foundFiles =\n      os.proc(scalaCLILauncher, \"Ls.scala\", \"--\", tempDir).call().out.trim()\n\n    expectedFiles.map(_.toString).foreach { file =>\n      assert(foundFiles.contains(file))\n    }\n  }\n}\n\n```\n\n## Run tests in Github CI\n\nThe following configuration of `ci.yml` contains a definition of job that runs tests using Scala CLI for every platform defined in `matrix.OS`.\n\n```yaml\njobs:\n  build:\n    runs-on: ${{ matrix.OS }}\n    strategy:\n      matrix:\n        OS: [\"ubuntu-latest\", \"macos-latest\", \"windows-latest\"]\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        fetch-depth: 0\n    - uses: coursier/cache-action@v6.4\n    - uses: VirtusLab/scala-cli-setup@v1.5\n    - run: scala-cli test .\n```\n\n## Check your Scala code format\n\nTo check the code style of your sources, you can use [Scalafmt](https://scalameta.org/scalafmt/). \n\n\nTo check your code format in GitHub CI by adding new job `format`:\n```yaml\n  format:\n    runs-on: \"ubuntu-latest\"\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        fetch-depth: 0\n    - uses: coursier/cache-action@v6.4\n    - uses: VirtusLab/scala-cli-setup@v1.5\n    - name: Scalafmt check\n      run: |\n        scala-cli fmt --check . || (\n          echo \"To format code run\"\n          echo \"  scala-cli fmt .\"\n          exit 1\n        )\n```\n\nIf the `scala-cli fmt --check .` command fails, it can be easily fixed by running `scala-cli fmt .`, which correctly formats your code.\n\n## Package your application\n\nScala CLI allows to build native executable applications using [GraalVM](https://www.graalvm.org), which can be uploaded as GitHub release artifacts.\n\n```yaml\n    - name: Package app\n      run: scala-cli .github/scripts/package.sc\n```\n\nGiven this simple Scala Script `package.sc` to package application to every platform:\n```scala compile title=package.sc\n//> using scala 3\n//> using dep com.lihaoyi::os-lib:0.11.3\nimport scala.util.Properties\n\nval platformSuffix: String = {\n  val os =\n    if (Properties.isWin) \"pc-win32\"\n    else if (Properties.isLinux) \"pc-linux\"\n    else if (Properties.isMac) \"apple-darwin\"\n    else sys.error(s\"Unrecognized OS: ${sys.props(\"os.name\")}\")\n  os\n}\nval artifactsPath = os.Path(\"artifacts\", os.pwd)\nval destPath =\n  if (Properties.isWin) artifactsPath / s\"ls-$platformSuffix.exe\"\n  else artifactsPath / s\"ls-$platformSuffix\"\nval scalaCLILauncher =\n  if (Properties.isWin) \"scala-cli.bat\" else \"scala-cli\"\n\nos.makeDir(artifactsPath)\nos.proc(scalaCLILauncher,\"--power\",  \"package\", \".\", \"-o\", destPath, \"--native-image\")\n  .call(cwd = os.pwd)\n  .out\n  .text()\n  .trim\n```\n\n\n## Distribute generated native application\n\nTo upload generated native executable applications to artifacts you can use [upload-artifact](https://github.com/actions/upload-artifact) GitHub Actions.\n\n```yaml\n    - uses: actions/upload-artifact@v4\n      with:\n        name: launchers\n        path: artifacts\n        if-no-files-found: error\n        retention-days: 2\n```\n\nWhen release CI pass, you should be able to download artifacts that contain native launchers of your applications.\n\n[Here](https://github.com/lwronski/ls-scala-cli-demo/actions/runs/2376334882) you can find examples of a CI that contains generated launcher based on this cookbook.\n\nYou can find the code of this cookbook [here](https://github.com/lwronski/ls-scala-cli-demo).\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/gists.md",
    "content": "---\ntitle: GitHub gists\nsidebar_position: 8\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n## Running code from gists\n\nScala CLI lets you run Scala code straight from GitHub gists, without the need to manually download them first.\nThis is done by passing the link to a gist as an argument to Scala CLI:\n\nFor example, given the gist `https://gist.github.com/alexarchambault/7b4ec20c4033690dd750ffd601e540ec`, which contains these two files:\n```scala title=Messages.scala\nobject Messages {\n  def hello = \"Hello\"\n}\n```\n```scala title=run.sc\nprintln(Messages.hello)\n```\n\nYou can run them with Scala CLI like this:\n```bash\nscala-cli https://gist.github.com/alexarchambault/7b4ec20c4033690dd750ffd601e540ec\n```\n<!-- Expected:\nHello\n-->\n\nThis example prints `Hello` to the standard output.\n\n:::note\nAs shown in this example, the gist isn't limited to just one file.\nScala CLI downloads the gist's archive and unzips it, so the gist can contain multiple files that depend on each other.\n\nScala CLI also caches the project sources using Coursier's cache.\n:::\n\n## Sharing code snippets\n\nTogether with the GitHub CLI (`gh`), it becomes really easy to share Scala code.\nIf you want to share a code file named `file.scala`, just run this command to create the gist:\n\n```sh\ngh gist create file.scala\n```\n\nThen you (and others) can run it quickly, using the Scala CLI approach shown above.\n\n\n## Resources from gists\n\nYou can also use resources from gists archive. This is done by passing `resourceDir` in using directives.\n\nFor example, given the gist `https://gist.github.com/lwronski/7ee12fa4b8b8bac3211841273df82080` which containing Scala code and text file:\n\n```scala title=Hello.scala\n//> using resourceDir ./\nimport scala.io.Source\n\nobject Hello extends App {\n    val inputs = Source.fromResource(\"input\").getLines.map(_.toInt).toSeq\n    println(inputs.mkString(\",\"))\n}\n```\n\n```scala title=input\n1\n2\n3\n4\n```\n\nand run them:\n\n<ChainedSnippets>\n\n```bash\nscala-cli https://gist.github.com/lwronski/7ee12fa4b8b8bac3211841273df82080\n```\n\n```text\n1,2,3,4\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\n1,2,3,4\n-->\n\nit will print `1,2,3,4` to the standard output.\n\n## Gists and Markdown code\n\n:::note\nThis feature is a work in progress and should currently be treated as experimental.\nMarkdown sources are ignored by default unless passed explicitly as inputs.\nYou can enable including non-explicit `.md` inputs by passing the `--enable-markdown` option.\n:::\n\nIt is possible to run markdown sources from a GitHub gist. \nThe gist is technically treated as a zipped archive (which it is downloaded as), so it is necessary to pass\nthe `--enable-markdown` option alongside the gist URL to run any contained Markdown sources.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power https://gist.github.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839 --enable-markdown\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nYou can find more information on working with Markdown in the [Markdown guide](../../guides/power/markdown.md).\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/instant-startup-scala-scripts.md",
    "content": "---\ntitle: Scripts with instant startup\nsidebar_position: 7\n---\n\nimport {ChainedSnippets, GiflikeVideo} from \"../../../src/components/MarkdownComponents.js\";\n\nScala CLI allows to easly compile and run Scala Scripts.\nIt also allows for straightforward compilation with Scala Native. \nScala Native is an ahead-of-time compiler to native binary allowing \nfor instant startup times, meaning that along with scala-cli, it should \nperfectly suit the needs of a fast scripting tool.\n\n## Using Scala Native\n\nAs an example, let’s build a script printing files from\na directory with sizes bigger than a passed value.\n\n```scala title=size-higher-than.scala\n//> using scala 3\n//> using dep com.lihaoyi::os-lib::0.11.3\n \n@main\ndef sizeHigherThan(dir: String, minSizeMB: Int) =\n  val wd = os.pwd / dir\n  val files = os.walk.attrs(wd).collect{\n    case (p, attrs) if attrs.size > minSizeMB * 10E6 => p\n  }\n  files.foreach(println(_))\n```\n\nRunning this for a `dir` directory and 20 MB as a lower limit with\n`scala-cli size-higher-than.scala – dir 20` can give us for example:\n\n\n```text title=dir/large-file.txt\n(>20MB of text)\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli size-higher-than.scala -- dir 20\n```\n\n```text\nCompiling project (Scala 3.5.2, JVM)\nCompiled project (Scala 3.5.2, JVM)\n/Users/user/Documents/workspace/dir/large-file.txt\n```\n</ChainedSnippets>\n\nA keen eye will notice that we have not yet compiled to Scala Native. We are still running on the JVM!\nWe can fix that by either running with a `—-native` option, or,\nin this case, by including an additional using directive:\n\n```scala compile title=size-higher-than.scala\n//> using dep com.lihaoyi::os-lib::0.10.0\n//> using platform scala-native\n \n@main\ndef sizeHigherThan(dir: String, minSizeMB: Int) =\n  val wd = os.pwd / dir\n  val files = os.walk.attrs(wd).collect{\n    case (p, attrs) if attrs.size > minSizeMB * 10E6 => p\n  }\n  files.foreach(println(_))\n```\n\nAfter rerunning, you may notice that while the initial compilation took a little longer,\nsubsequent runs will severely cut on the startup time compared to the JVM.\n\n## Optimization options\n\nWe can make the runtime itself even faster, using various Scala Native optimization options:\n* `debug` - what was used by default up to this point, fast compilation with a slower runtime \n* `release-fast` - moderate compilation time with a faster runtime\n* `release-full` - slow compilation time with the fastest runtime\n\nWe pass these using a `-–native-mode` scala-cli option or, like previously, by adding a using directive:\n\n```scala compile title=size-higher-than.scala\n//> using dep com.lihaoyi::os-lib::0.11.3\n//> using platform scala-native\n//> using nativeMode release-full\n \n@main\ndef sizeHigherThan(dir: String, minSizeMB: Int) =\n  val wd = os.pwd / dir\n  val files = os.walk.attrs(wd).collect{\n    case (p, attrs) if attrs.size > minSizeMB * 10E6 => p\n  }\n  files.foreach(println(_))\n```\n\nWe can also package this script into a separate binary with the `package` command,\nuseful especially on Windows where typically shebangs won’t work:\n\n## Additional considerations\n\nSome things to look out for when working with Scala Native:\n * dependencies - libraries have to be published separately for Scala Native. Notice the `org::project::version` double colon syntax used for os-lib - it basically hides `org::project_native[Scala Native binary version]:version` underneath. Fortunately, many libraries are already available for Scala Native. However, Java dependencies will not work altogether.\n * some [differences](https://scala-native.readthedocs.io/en/stable/user/lang.html) exist when compared to Scala on the JVM.\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/scala-jvm.md",
    "content": "---\ntitle: Changing Java versions\nsidebar_position: 3\n---\n\nYou can use Scala CLI to test your code compatibility with various versions of `java`, with a key point being that manual installation of a JDK/SDK is not required(!).\nScala CLI automatically downloads the Java version you specify.\n\nAs an example, the following snippet uses the new method `Files.writeString` from Java 11:\n\n```scala title=Main.scala\nimport java.nio.file.Files\nimport java.nio.file.Paths\n\nobject Main extends App {\n  val dest = Files.createTempDirectory(\"scala-cli-demo\").resolve(\"hello.txt\")\n  val filePath = Files.writeString(dest, \"Hello from ScalaCli\")\n  val fileContent: String = Files.readString(filePath)\n  println(fileContent)\n}\n```\n\nTo use Java 11 to run this application, pass the following `--jvm` option to the Scala CLI command:\n\n```bash ignore\nscala-cli --jvm temurin:11 Main.scala\n```\n\n<!-- ignored Expected:\nHello from ScalaCli\n-->\n\nTo attempt to compile the application with Java 8, change the value of the `--jvm` parameter:\n```bash ignore fail\nscala-cli --jvm 8 Main.scala\n# In this case, it raises an error because the `Files.writeString` and `Files.readString` methods are not available in java 8\n#\n# [error] ./Main.scala:6:18\n# [error] value writeString is not a member of object java.nio.file.Files\n# [error]   val filePath = Files.writeString(dest, \"Hello from ScalaCli\")\n# [error]                  ^^^^^^^^^^^^^^^^^\n# [error] ./Main.scala:7:29\n# [error] value readString is not a member of object java.nio.file.Files\n# [error]   val fileContent: String = Files.readString(filePath)\n# [error]                             ^^^^^^^^^^^^^^^^\n```\n\n<!-- ignored Expected:\njava.lang.NoSuchMethodError\njava.nio.file.Files.writeString\n-->\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/scala-scripts.md",
    "content": "---\ntitle: Running Scala Scripts\nsidebar_position: 6\n---\n\n## Scala Scripts\n\nScala scripts are files that contain Scala code without a main method.\nThese source code files don't require build-tool configurations.\nTo run Scala scripts very quickly without waiting the need for build tools, use Scala CLI.\n\n### Run\n\nFor example, given this simple script:\n\n```scala title=HelloScript.sc\nval sv = scala.util.Properties.versionNumberString\n\nval message = s\"Hello from Scala ${sv}, Java ${System.getProperty(\"java.version\")}\"\nprintln(message)\n```\n\nYou can run it directly with Scala CLI — there's no need for a build tool or additional configuration:\n\n```bash\nscala-cli run HelloScript.sc\n```\n\n<!-- Expected-regex:\nHello from Scala .*, Java .*\n-->\n\nAlternatively, you can add a \"shebang\" header to your script, make it executable, and execute it directly with Scala CLI. For example, given this script with a header that invokes Scala CLI:\n\n```scala title=HelloScriptSheBang.sc\n#!/usr/bin/env -S scala-cli shebang\n\nval sv = scala.util.Properties.versionNumberString\n\ndef printMessage(): Unit =\n  val message = s\"Hello from Scala ${sv}, Java ${System.getProperty(\"java.version\")}\"\n  println(message)\n\nprintMessage()\n```\n\nYou can make it executable and then run it like this:\n\n```bash\nchmod +x HelloScriptSheBang.sc\n./HelloScriptSheBang.sc\n# Hello from Scala 2.13.6, Java 16.0.1\n```\n\n<!-- Expected-regex:\nHello from Scala .*, Java .*\n-->\n\nYou can also pass command line arguments to Scala scripts:\n\n```scala title=ScriptArguments.sc\n#!/usr/bin/env -S scala-cli shebang\nprintln(args(1))\n```\n\n```bash\nchmod +x ScriptArguments.sc\n./ScriptArguments.sc foo bar\n# bar\n```\n\n<!-- Expected-regex:\nbar\n-->\n\nAs shown, command line arguments are accessed through the special `args` variable.\n\n\n## Features\n\nAll of the features shown for non-scripts work for Scala scripts as well, such as waiting for changes (watch mode), dependency menagement, packaging, compiling, etc.\n\n### Package\n\nFor example, run the `package` sub-command to package your script as a lightweight executable JAR file:\n\n```bash\nscala-cli --power package HelloScript.sc\n./HelloScript\n```\n\n<!-- Expected-regex:\nHello from Scala .*, Java .*\n-->\n\n### Watch mode\n\nAs another example, pass `--watch` to Scala CLI to watch all source files for changes, and then re-run them when there is a change:\n\n```bash ignore\nscala-cli --watch HelloScript.sc\n```\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/scala-versions.md",
    "content": "---\ntitle: Picking the Scala version\nsidebar_position: 2\n---\n\nBy default, Scala CLI runs the latest supported scala version. See our list of [Supported Scala Versions](../../reference/scala-versions.md) in Scala CLI.\n\nTo demonstrate how this works, here’s a universal piece of code that detects the Scala version at runtime.\nThe code is a bit complicated, so we suggest that you skip reading the whole file, and just focus on what it prints:\n\n```scala title=ScalaVersion.scala\nobject ScalaVersion extends App {\n  def props(url: java.net.URL): java.util.Properties = {\n    val properties = new java.util.Properties()\n    val is = url.openStream()\n    try {\n      properties.load(is)\n      properties\n    } finally is.close()\n  }\n\n  def scala2Version: String =\n    props(getClass.getResource(\"/library.properties\")).getProperty(\"version.number\")\n\n  def checkScala3(res: java.util.Enumeration[java.net.URL]): String =\n    if (!res.hasMoreElements) scala2Version else {\n      val manifest = props(res.nextElement)\n      manifest.getProperty(\"Specification-Title\") match {\n        case \"scala3-library-bootstrapped\" =>\n          manifest.getProperty(\"Implementation-Version\")\n        case _ => checkScala3(res)\n      }\n    }\n  val manifests = getClass.getClassLoader.getResources(\"META-INF/MANIFEST.MF\")\n\n  val scalaVersion = checkScala3(manifests)\n  val javaVersion = System.getProperty(\"java.version\")\n\n  println(s\"Scala: $scalaVersion\")\n}\n```\n\nWhen this application is run without specifying a Scala version, it uses the latest stable release of Scala — 3.1.0 at the time of writing this doc:\n\n```bash\nscala-cli ScalaVersion.scala\n```\n\n<!-- Expected-regex:\nScala: 3\\..*\n-->\n\nWhen you want to control the Scala version, you can control it from the command line using the `--scala` option (with `-S` and `--scala-version` aliases):\n\n```bash\nscala-cli -S 2.13.15 ScalaVersion.scala\n```\n<!-- Expected-regex:\nScala: 2\\.13\\.15\n-->\n\nIn many cases you won't care for a precise Scala version and will want \"any Scala 2\" or \"any 2.13 release.\"\nFor this situation, Scala CLI accepts version prefixes like this:\n\n```bash\nscala-cli -S 2 ScalaVersion.scala\n```\n<!-- Expected-regex:\nScala: 2\\..+\n-->\n\nand this:\n\n```bash\nscala-cli -S 2.12 ScalaVersion.scala\n```\n<!-- Expected-regex:\nScala: 2\\.12\\.\n-->\n\nIn the first example (`-S 2`), the application picks up the latest Scala 2 stable release (`2.13.9` at the time of this writing).\nIn the second example, the application picks up the latest stable release of `2.12` (which is `2.12.18` at the time of this writing).\n\nYou can also pin the version of the language within a `.scala` file with `using` directives.\n\n:::info\nThe `using` directives syntax is still experimental, and may change in future versions of Scala CLI.\n:::\n\nHere’s an example of a source code file named `version.scala` that contains a `using` directive:\n\n```scala title=version.scala\n//> using scala 2.12\n\nobject OldCode\n//rest of the config\n```\n\nNow when you compile that code along with the previous `ScalaVersion.scala` file:\n\n```bash\nscala-cli ScalaVersion.scala version.scala\n```\n\n<!-- Expected-regex:\nScala: 2\\.12\\.\n-->\n\nThe output at the time of this writing is \"`2.12.21`\".\n\nThe Scala CLI philosophy is “command line first,” so any configuration information that’s passed to the command line will override `using` directives. So when you run this command with the `-S` option:\n\n```bash\nscala-cli -S 2.13.15 ScalaVersion.scala version.scala\n```\n\nthe result is \"`2.13.15`\" (as opposed to \"`2.12.21`\" in the previous example).\n\n<!-- Expected-regex:\nScala: 2\\.13\\.15\n-->\n\n:::note\nSee our [Using Directives Guide](../../guides/introduction/using-directives.md) for more details on `using` directives.\n:::\n\n\n## When should I provide a full version of Scala?\n\nFor prototyping, scripting, and other use cases that won’t need to be run multiple times in the future, providing a Scala version generally isn’t necessary.\n\nScala is source and binary compatible within each major version (e.g., `2.12.x` or `3.1.x`) so specifying the version in `epoch.major` form (e.g., `2.12`, `2.13`, or `3.1`) should be perfectly fine for most use cases. When your Scala code contains more advanced features that may be more sensitive for changes in minor version (e.g., from `2.13.4` to `2.13.5`) we recommend specifying the complete Scala version.\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/show-sources.md",
    "content": "---\ntitle: Accessing Source File Paths\nsidebar_position: 10\n---\n\n## Source Names and Paths\n\nSystem properties provide source file paths, separated by `java.io.File.pathSeparator`.\n- `scala.source.names` filenames without a path.\n- `scala.sources`  full paths to source files\n\n### System property `scala.source.names`\n\nThis simple script reports the script file name:\n\n```scala title=reportNames.sc\nval name = sys.props(\"scala.source.names\")\nprintln(s\"scriptName: $name\")\n```\n\n```bash\nscala-cli reportNames.sc\n# scriptName: reportNames.sc\n```\n\n<!-- Expected-regex:\nscriptName: reportNames.sc\n-->\n\nLikewise, for this `.scala` program:\n\n```scala title=SourceNames.scala\nobject Main {\n  def main(args: Array[String]): Unit =\n    val names = sys.props(\"scala.source.names\")\n    println(names)\n}\n```\n\n```bash\nscala-cli SourceNames.scala\n# SourceNames.scala\n```\n\n<!-- Expected-regex:\nSourceNames[.]scala\n-->\n\nThis java program prints a comma-separated list of source names:\n\n```java title=NamesLister.java\npublic class NamesLister {\n  public static void main(String[] args) {\n    String sourceNames = System.getProperty(\"scala.source.names\");\n    String[] names = sourceNames.split(java.io.File.pathSeparator);\n    String list = String.join(\",\", names);\n    System.out.printf(\"%s\\n\", list);\n  }\n}\n\n```\n\n```bash\nscala-cli NamesLister.java\n# NamesLister.java\n```\n\n<!-- Expected-regex:\nNamesLister.java\n-->\n\n\nIf multiple sources are provided, all are displayed:\n\n```bash\nscala-cli NamesLister.java reportNames.sc SourceNames.scala --main-class NamesLister\n# NamesLister.java,reportNames.sc,SourceNames.scala\n```\n\n<!-- Expected-regex:\nNamesLister[.]java,reportNames[.]sc,SourceNames[.]scala\n-->\n\n### System property `scala.sources`\n\n```java title=PathsLister.java\npublic class PathsLister {\n  public static void main(String[] args) {\n    String paths = System.getProperty(\"scala.sources\");\n    String list = String.join(\",\", paths.split(java.io.File.pathSeparator));\n    System.out.printf(\"%s\\n\", list);\n  }\n}\n```\nThe property \"scala.sources\" displays full source file paths:\n\n```bash\nscala-cli PathsLister.java reportNames.sc SourceNames.scala --main-class PathsLister\n# /tmp/workdir/PathsLister.java,/tmp/workdir/reportNames.sc,/tmp/workdir/SourceNames.scala\n```\n\n<!-- Expected-regex:\n.*/PathsLister[.]java,.*/reportNames[.]sc,.*/SourceNames[.]scala\n-->\n\n### Other examples\n\nWhen multiple `.md` files with plain `scala` snippets are being run, names and paths can refer to '.md' files:\n\n````markdown title=MainA.md\n# Main class A\n```scala\nprintln(s\"A: ${sys.props(\"scala.source.names\")}\")\n```\n````\n\n\n````markdown title=MainB.md\n# Main class 2\n```scala\nprintln(s\"2: ${sys.props(\"scala.source.names\")}\")\n```\n````\n\nWhen multiple such sources are passed as inputs, the main class has to be passed explicitly with the `--main-class`\noption.\n\n```bash\nscala-cli --power MainA.md MainB.md --main-class MainA_md\n# A: MainA.md:MainB.md\n```\n\n```text\n1: MainA.md:MainB.md\n```\n<!-- Expected-regex:\nA: MainA[.]md:MainB[.]md\n-->\n"
  },
  {
    "path": "website/docs/cookbooks/introduction/test-only.md",
    "content": "---\ntitle: Filter the test suites to run\nsidebar_position: 5\n---\n\nThe `--test-only` option is useful for when you only want to run a specific subset of tests.\n\nIn this cookbook, we will walk through how to use the `--test-only` option with the `test` sub-command in Scala CLI,\nspecifically when using the `munit` and `utest` test frameworks.\n\n## Filter the test suites\n\nThe `--test-only` option in Scala CLI supports using glob patterns to filter test suites to run. A glob pattern is a\nstring which contains asterisk `*` characters to match a set of test suites.\n\nHere are three examples of glob patterns for how to filter test suites with `--test-only`:\n\n- start with `test` - `test*`\n- end with `test` - `*test`\n- contains `test` -  `*test*`\n\n:::note\nThe `--test-only` option is supported for every test framework running with Scala CLI.\n:::\n\nFor example, passing `tests.only*` to the `--test-only` option runs only the test suites which start with `tests.only`:\n\n```scala title=BarTests.scala\n//> using dep org.scalameta::munit::1.0.2\npackage tests.only\n\nclass BarTests extends munit.FunSuite {\n  test(\"bar\") {\n    assert(2 + 3 == 5)\n  }\n}\n```\n\n```scala title=HelloTests.scala\npackage tests\n\nclass HelloTests extends munit.FunSuite {\n  test(\"hello\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n```bash\nscala-cli test . --test-only 'tests.only*' \n# tests.only.BarTests:\n#   + bar 0.045s\n```\n\n<!-- Expected:\ntests.only.BarTests:\n+ bar\n-->\n\n## Running a single test case by name\n\n:::note\nTest frameworks may have their own specific API for specifying the test cases to run aside from the test-only input.\n:::\n\n### Munit\n\nTo run a specific test case inside a test suite pass `*test-name*` as an argument to Scala CLI:\n\n<!-- clear -->\n\n```scala title=MunitTests.scala\n//> using dep org.scalameta::munit::1.0.2\npackage tests.only\n\nclass Tests extends munit.FunSuite {\n  test(\"bar\") {\n    assert(2 + 2 == 5)\n  }\n  test(\"foo\") {\n    assert(2 + 3 == 5)\n  }\n  test(\"foo-again\") {\n    assert(2 + 3 == 5)\n  }\n}\n```\n\n```bash\nscala-cli test .  -- '*foo*'\n# tests.only.Tests:\n#   + foo 0.045s\n#   + foo-again 0.001s\n```\n\n<!-- Expected:\ntests.only.Tests:\n+ foo\n+ foo-again\n-->\n\nThis will only run the test which contains the name `foo`, any other tests in your test suite will be skipped.\n\n### Utest\n\nUnfortunately, the `utest` test framework doesn't support using glob pattern `*` to filter the test cases to run. In\norder to run a specific test case you will need to specify the exact name of the test you want to run.\n\n<!-- clear -->\n\n```scala title=MyTests.scala\n//> using dep com.lihaoyi::utest::0.8.4\n\nimport utest.*\n\nobject MyTests extends TestSuite {\n  val tests = Tests {\n    test(\"foo\") {\n      assert(2 + 2 == 4)\n      println(\"Hello from \" + \"tests\")\n    }\n    test(\"bar\") {\n      assert(2 + 2 == 4)\n      println(\"Hello from \" + \"tests\")\n    }\n  }\n}\n```\n\n```bash\nscala-cli test .  -- 'MyTests.foo'\n# -------------------------- Running Tests MyTests.foo --------------------------\n# Hello from tests\n# + MyTests.foo 8ms  \n# Tests: 1, Passed: 1, Failed: 0\n```\n\n<!-- Expected:\nRunning Tests MyTests.foo\nHello from tests\n+ MyTests.foo\nTests: 1, Passed: 1, Failed: 0\n-->\n\nThis will run only the test case with the name `MyTests.foo`.\n"
  },
  {
    "path": "website/docs/cookbooks/package/_category_.json",
    "content": "{\n  \"label\": \"Packaging ⚡\",\n  \"position\": 30\n}\n"
  },
  {
    "path": "website/docs/cookbooks/package/native-images.md",
    "content": "---\ntitle: Packaging as GraalVM native images ⚡\nsidebar_position: 16\n---\n\nScala CLI lets you package your applications as native executables\nusing GraalVM native images.\n\nAs an example, let's package the following application as a native executable\nusing GraalVM native image:\n```scala title=Echo.scala\nobject Echo {\n  def main(args: Array[String]): Unit =\n    println(args.mkString(\" \"))\n}\n```\n\nThe following command packages this application as a native executable:\n```bash\nscala-cli --power package --native-image Echo.scala -o echo\n```\n\n<!-- Expected-regex:\nWrote .*echo\n.*\\/echo\n-->\n\n```bash\n# Run echo on macOS\n./echo a b\n# a b\n```\n\n<!--\n```bash\nrm ./echo\n```\n-->\n\nYou can pass custom options to GraalVM native image by passing them after `--`, like\n```bash\nscala-cli --power package --native-image Echo.scala -o echo -- --no-fallback\n```\n\n<!-- Expected-regex:\nWrote .*echo, run it with\n  .*\\/echo\n-->\n"
  },
  {
    "path": "website/docs/cookbooks/package/scala-docker.md",
    "content": "---\ntitle: Packaging as Docker images ⚡\nsidebar_position: 15\n---\n\nScala CLI can create an executable application and package it into a Docker image.\n\nFor example, here's a simple piece of code that will be executed in a Docker container:\n\n```scala title=HelloDocker.scala\nobject HelloDocker extends App {\n    println(\"Hello from Docker\")\n}\n```\n\nPassing `--docker` to the `package` sub-command generates a Docker image. When creating a Docker image, the `--docker-image-repository` parameter is mandatory.\n\nThe following command generates a `hello-docker` image with the `latest` tag:\n\n```bash ignore\nscala-cli --power package --docker HelloDocker.scala --docker-image-repository hello-docker\n```\n\n```bash ignore\ndocker run hello-docker\n# Hello from Docker\n```\n\nYou can also package your app in the Scala.js or Scala Native environments.\nFor example, this command creates a Scala.js Docker image:\n\n```bash ignore\nscala-cli --power package --js --docker HelloDocker.scala --docker-image-repository hello-docker\n```\n\nThis command creates a Scala Native Docker image:\n\n```bash ignore\nscala-cli --power package --native --docker HelloDocker.scala --docker-image-repository hello-docker\n```\n\n:::note\nPackaging a Scala Native application to a Docker image is supported only on Linux.\n:::\n"
  },
  {
    "path": "website/docs/cookbooks/package/scala-package.md",
    "content": "---\ntitle: Packaging as executable files ⚡\nsidebar_position: 14\n---\n\nScala CLI lets you package your application into a lightweight JAR file that can be easily run.\nThe JAR file only contains the byte code that’s generated from your source code files, and automatically downloads its dependencies on its first run.\n\nAs an example, the following snippet contains a short application to detect the OS:\n```scala title=DetectOsApp.scala\nobject DetectOsApp extends App  {\n    def getOperatingSystem(): String = {\n        val os: String = System.getProperty(\"os.name\")\n        os\n    }\n    println(s\"os: ${getOperatingSystem()}\")\n}\n```\n\n### Default format (lightweight launcher)\n\nBy default, the `package` sub-command generates a lightweight JAR that contains only your bytecode. This is how you create a lightweight JAR named `DetectOsApp.jar`:\n\n```bash\nscala-cli --power package DetectOsApp.scala\n```\n\n<!-- Expected-regex:\nWrote .*DetectOsApp, run it with\n  .*\\/DetectOsApp\n-->\n\nLightweight JARs require the `java` command to be available, and access to the internet, if dependencies need to be downloaded. This is how you run it on macOS:\n\n```bash\n# Run DetectOsApp on macOS\n./DetectOsApp\n# os: Mac OS X\n```\n\nThe lightweight JAR that was just built on macOS can also run on Linux:\n\n```bash\n# Run DetectOsApp on Linux\n./DetectOsApp\n# os: Linux\n```\n\nScala CLI supports building Lightweight JARs in the macOS, Linux, and Windows environments.\nJARs built on macOS and Linux are portable between these two operating systems.\nLightweight JARs built on Windows can only be run on Windows.\n\n\n### Assemblies\nPassing `--assembly` to the `package` sub-command generates so-called \"assemblies,\" or \"fat JARs\":\n\n```bash\nscala-cli --power package --assembly DetectOsApp.scala\n```\n\nAssemblies also require the `java` command to be available in the `PATH`. But in this case, all of the dependencies that are needed are packaged into the assembly, so nothing gets downloaded upon the first run, and no internet access is required.\n\n```bash\n# Run DetectOsApp on macOS\n./DetectOsApp\n# os: Mac OS X\n```\n"
  },
  {
    "path": "website/docs/getting_started.md",
    "content": "---\ntitle: Getting started\nsidebar_position: 2\n---\n\nimport {ChainedSnippets, GiflikeVideo} from \"../src/components/MarkdownComponents.js\";\n\n:::info\nThis article requires knowledge of the Scala language (how to define a class or method) as well as Scala tooling (the REPL, and basics of dependency management and unit tests).\n:::\n\nIn this article we show how to use Scala CLI to create a basic script, followed by small project with features like dependencies, tests, and IDE support. We aim to provide you with a knowledge of how to create and develop your first projects using Scala CLI.\n\nFirst, let's verify if Scala CLI is properly [installed](/install) with a simple \"hello world\" test:\n\n<ChainedSnippets>\n\n```bash\necho 'println(\"Hello\")' | scala-cli -\n```\n\n```\nHello\n```\n\n<!-- Expected:\nHello\n-->\n\n</ChainedSnippets>\n\nRunning this command the first time may take a bit longer than usual and prints a fair number of logging output because Scala CLI needs to download all the artifacts it needs to compile and run the code.\n\n## Scripting\n\nIn that example we actually just created a Scala Script. To demonstrate this more fully, let's create a script in a `hello.sc` file that greets more properly:\n\n```scala title=hello.sc\ndef helloMessage(names: Seq[String]) = names match\n  case Nil =>\n    \"Hello!\"\n  case names =>\n    names.mkString(\"Hello: \", \", \", \"!\")\n\nprintln(helloMessage(args.toSeq))\n```\n\nWhen that script is given no names, it prints `\"Hello!\"`, and when it’s given one or more names it prints the string that's created in the second `case` statement. With Scala CLI we run the script like this:\n\n<ChainedSnippets>\n\n```bash\nscala-cli hello.sc\n```\n\n```\nHello\n```\n</ChainedSnippets>\n\nTo provide arguments to the script we add them after `--`:\n\n<ChainedSnippets>\n\n```bash\nscala-cli hello.sc -- Jenny Jake\n```\n\n```\nHello Jenny, Jake!\n```\n\n</ChainedSnippets>\n\nYou may wonder what kind of Scala version was used under the hood. The answer is the latest stable version which was tested in Scala CLI. If you want to specify the Scala version you can use `-S` or `--scala` option. More about setting Scala version in the dedicated [cookbook](./cookbooks/introduction/scala-versions.md).\n\nScala CLI offers many more features dedicated for scripting, as described in the [dedicated guide](guides/scripting/scripts.md).\n\n## Dependencies\n\nNow let's build something more serious. For this example, it's best to start with some prototyping inside the REPL. We can start a REPL session by running `scala-cli repl`. (If desired, you can also set the Scala version with `-S` or `--scala`.)\n\n:::note\nScala CLI reuses most of its options across all of its commands.\n:::\n\nOne of the main strengths of Scala is its ecosystem. Scala CLI is designed in a way to expose the Scala ecosystem to all usages of Scala, and running the REPL is no exception.\n\nTo demonstrate this, let's start prototyping with [os-lib](https://github.com/com-lihaoyi/os-lib) — a Scala interface to common OS filesystem and subprocess methods. To experiment with `os-lib` in the REPL, we simply need to add the parameter `--dep com.lihaoyi::os-lib:0.9.0`, as shown here:\n\n<ChainedSnippets>\n\n```bash ignore\nscala-cli repl --dep com.lihaoyi::os-lib:0.11.3\n```\n\n```scala ignore\nscala> os.pwd\nval res0: os.Path = ...\n\nscala> os.walk(os.pwd)\nval res1: IndexedSeq[os.Path] = ArraySeq(...)\n```\n\n</ChainedSnippets>\n\n## A project\n\nNow it's time to write some logic, based on the prototyping we just did. We'll create a filter function to display all files with the given filename extension in the current directory.\n\nFor the consistency of our results, let's create a new directory and `cd` to it:\n\n```bash\nmkdir scala-cli-getting-started\ncd scala-cli-getting-started\n```\n<!-- clear -->\n\nNow we can write our logic in a file named `files.scala`:\n\n```scala title=files.scala\n//> using dep com.lihaoyi::os-lib:0.11.3\n\ndef filesByExtension(\n  extension: String,\n  dir: os.Path = os.pwd): Seq[os.Path] =\n    os.walk(dir).filter { f =>\n      f.last.endsWith(s\".$extension\") && os.isFile(f)\n    }\n```\n\nAs you may have noticed, we specified a dependency within `files.scala` using the `//> using dep com.lihaoyi::os-lib:0.9.0` syntax. With Scala CLI, you can provide configuration information with `using` directives — a dedicated syntax that can be embedded in any `.scala` file. For more details, see our dedicated [guide for `using` directives](guides/introduction/using-directives.md).\n\nNow let's check if our code compiles. We do that by running:\n\n```bash\nscala-cli compile .\n```\n\nNotice that this time we didn’t provide a path to single files, but rather used a directory; in this case, the current directory. For project-like use cases, we recommend providing directories rather than individual files. For most cases, specifying the current directory (`.`) is a best choice.\n\n## IDE support\n\nSome people are fine using the command line only, but most Scala developers use an IDE. To demonstrate this, let's open Metals with your favorite editor inside `scala-cli-getting-started` directory:\n\n<GiflikeVideo url='/img/scala-cli-getting-started-1.mp4'/>\n\nAt the present moment, support for IntelliJ is often problematic. But know that we are working on making it as rock-solid as Metals.\n\nActually, in this case, we cheated a bit by running the compilation first. In order for Metals or IntelliJ to pick up a Scala CLI project, we need to generate a BSP connection detail file. Scala CLI generates these details by default every time `compile`, `run`, or `test` are run. We also expose a `setup-ide` command to manually control creation of the connection details file. For more information on this, see our [IDE guide](guides/introduction/ide.md).\n\n## Tests\n\nWith our IDE in place, how can we test if our code works correctly? The best way is to create a unit test. The simplest way to add a test using scala-cli is by creating a file whose name ends with `.test.scala`, such as `files.test.scala`. (There are also other ways to mark source code files as containing a test, as described in [tests guide](./commands/test.md#test-sources).)\n\nWe also need to add a test framework. Scala CLI support most popular test frameworks, and for this guide we will stick with [munit](https://scalameta.org/munit/). To add a test framework, we just need an ordinary dependency, and once again we'll add that with the `using` directive:\n\n```scala title=files.test.scala\n//> using dep org.scalameta::munit:1.0.2\n\nclass TestSuite extends munit.FunSuite {\n  test(\"hello\") {\n    val expected = Set(\"files.scala\", \"files.test.scala\")\n    val obtained = filesByExtension(\"scala\").map(_.last).toSet\n    assertEquals(obtained, expected)\n  }\n}\n```\n\nNow we can run our tests at the command line:\n\n<ChainedSnippets>\n\n```bash\nscala-cli test .\n```\n\n```\nCompiling project (test, Scala 3.5.2, JVM)\nCompiled project (test, Scala 3.5.2, JVM)\nTestSuite:\n  + hello 0.058s\n```\n\n</ChainedSnippets>\n\nor directly within Metals:\n\n<GiflikeVideo url='/img/scala-cli-getting-started-2.mp4'/>\n\n## A project, vol 2\n\nWith our code ready and tested, now it's time to turn it into a command-line tool that counts files by their extension. For this we can write a simple script. A great feature of Scala CLI is that scripts and Scala sources can be mixed:\n\n```scala title=countByExtension.sc\nval (ext, directory) = args.toSeq match\n  case Seq(ext) => (ext, os.pwd)\n  case Seq(ext, directory) => (ext, os.Path(directory))\n  case other =>\n    println(s\"Expected: `extension [directory]` but got: `${other.mkString(\" \")}`\")\n    sys.exit(1)\n\nval files = filesByExtension(ext, directory)\nfiles.map(_.relativeTo(directory)).foreach(println)\n```\n\nAs you probably noticed, we are using `os-lib` in our script without any `using` directive, How is that possible? The way it works is that configuration details provided by `using` directives are global, and apply to all files. Since `files.scala` and `countByExtension.sc` are compiled together, the `using` directives in `files.scala` are used when compiling both files. (Note that defining a library dependency in more than one file is an anti-pattern.)\n\n<!-- TODO add piece about scala-cli warnings in such case -->\n\nNow let's run our code, looking for all files that end with the `.scala` extension:\n\n<ChainedSnippets>\n\n```bash\nscala-cli . -- scala\n```\n\n```\nfiles.scala\n.scala-build/project_940fb43dce/src_generated/main/countByExtension.scala\nfiles.test.scala\n```\n\n</ChainedSnippets>\n\nSeeing that output, you may wonder, why do we have an additional `.scala` file under the `.scala-build` dir? The way this works is that under the hood, Scala CLI sometimes needs to preprocess source code files — such as scripts. So these preprocessed files are created under the `.scala-build` directory, and then compiled from there.\n\n## Packaging\n\nWe could stop here and call Scala CLI on our set of sources every time. Scala CLI uses caches aggressively, so rollup runs are reasonably fast — less than 1,500 milliseconds on tested machine — but sometimes this isn't fast enough, or shipping sources and compiling them may be not convenient.\n\nFor these use cases, Scala CLI offers means to package your project. For example, we can run this command to generate a thin, executable jar file, with the compiled code inside:\n\n```bash\nscala-cli --power package . -o countByExtension\n```\n\nThe default binary name is `app`, so in this example we provide the `-o` flag to make the name of the binary `countByExtension`. Now we can run our project like this:\n\n```bash\n./countByExtension scala\n```\n\nThis time it only took 350 milliseconds, so this is a big improvement. When you create a binary file (a runnable jar) like this, it's self-contained, and can be shipped to your colleagues or deployed.\n\nWe can reduce the startup time even further using [Scala Native](guides/advanced/scala-native.md), or by packaging our application to other formats like [Docker container](./commands/package.md#docker-container), [assembly](./commands/package.md#assemblies), or even [OS-specific packages](./commands/package.md#os-specific-packages) (.dep, .pkg, etc.). See those resources for more information.\n\n\n## Summary\n\nWith this guide we've only scratched the surface of what Scala CLI can do. For many more details, we've prepared a set of [cookbooks](./cookbooks/intro.md) that showcase solutions to common problems, as well as a detailed set of [guides](./guides/intro.md) for our [commands](./commands/basics.md).\n\nWe also have a dedicated [room on Scala discord](https://discord.gg/KzQdYkZZza) where you can ask for help or discuss anything that's related to Scala CLI. For more in-depth discussions, we're using [Github discussions in our repo](https://github.com/VirtusLab/scala-cli/discussions); this is the best place to suggest a new feature or any improvements.\n"
  },
  {
    "path": "website/docs/guides/_category_.json",
    "content": "{\n  \"label\": \"Guides\",\n  \"position\": 14\n}\n"
  },
  {
    "path": "website/docs/guides/advanced/_category_.json",
    "content": "{\n  \"label\": \"Advanced\",\n  \"position\": 30\n}\n"
  },
  {
    "path": "website/docs/guides/advanced/custom-toolkit.md",
    "content": "---\ntitle: Custom Toolkit\nsidebar_position: 44\n---\nSimilar to the Scala Toolkit and Typelevel toolkit, it is possible to create your own, custom toolkit. \nHaving a custom toolkit with common libraries can speed up the development using scala-cli. \n\nLet's look at how we can create a new toolkit. \n\nFor example, to create a LiHaoyi ecosystem toolkit, we can name the file as `LiHaoyiToolkit.scala` and add the required libraries as dependency directives:\n\n```scala title=LiHaoyiToolkit.scala\n//> using scala 2.13 3\n//> using publish.name toolkit\n//> using dep com.lihaoyi::upickle::4.0.2\n//> using dep com.lihaoyi::os-lib::0.11.3\n//> using dep com.lihaoyi::requests::0.9.0\n//> using dep com.lihaoyi::fansi::0.5.0\n``` \nThis toolkit is a combination of 4 libraries from `com.lihaoyi` organization as defined before. The key `publish.name` must have the value `toolkit` to be used as a toolkit. \n\nSimilarly, define the scalajs version of toolkit in `LiHaoyiToolkit.js.scala` file. Notice the `js.scala` extension. It should also have `publish.name` as `toolkit`. \n\nIf testkit is supported, it can also be added as another file, `LiHaoyiToolkitTest.scala` with `publish.name` as `toolkit-test`:\n```scala title=LiHaoyiToolkitTest.scala\n//> using scala 2.13 3\n//> using publish.name toolkit-test\n//> using dep com.lihaoyi::utest::0.8.4\n```\n\nAdditionally, more configurations needed for publishing the toolkit can be kept in a conf file, for example, `publish-conf.scala`:\n```scala title=publish-conf.scala \n//> using publish.organization com.yadavan88\n//> using publish.version 0.1.1-SNAPSHOT\n//> using publish.url https://github.com/yadavan88/lihaoyi-toolkit\n//> using publish.license Apache-2.0\n//> using publish.repository central\n//> using publish.developer \"yadavan88|Yadu Krishnan|https://github.com/yadavan88\"\n//> using repository sonatype:public\n```\n\nThe toolkit can be published locally using the command:\n```bash\nscala-cli --power publish local --cross LiHaoyiToolkit.scala publish-conf.scala\n```\n\nSimilarly, it is also possible to publish to a central repository. Refer to the [GitHub Action workflow](https://github.com/scala/toolkit/blob/main/.github/workflows/deploy.yaml) for more details.\n\nOnce it is published, it can be accessed using the org-name with which it got published. For example, with the published toolkit under the organization `com.yadavan88`, it can be accessed as:\n\n```scala\n//> using toolkit com.yadavan88:0.1.1-SNAPSHOT\n\n@main\ndef main() = {\n  println(fansi.Color.Blue(\"Hello world!\"))\n  println(\"path is: \" + os.pwd)\n}\n\n```\nThis brings in all the dependencies mentioned in the custom toolkit file.\n"
  },
  {
    "path": "website/docs/guides/advanced/internals.md",
    "content": "---\ntitle: Internals\nsidebar_position: 49\n---\n\nEven though Scala CLI exposes a simple interface to users, quite a number of steps happen when compiling or running even a single source file.\nThis page describes what happens under the hood when you run a Scala CLI command.\n\n\n### Bloop\n\nScala CLI uses Bloop to compile code.\nThat way, it doesn't interface directly with `scalac`, and newly released Scala versions work out of the box: there's no need to update Scala CLI itself.\n\nScala CLI connects to Bloop on the local machine using a domain socket. That domain socket\nlives under the \"Bloop daemon directory\", that is OS-dependent, and whose path is printed\nby `scala-cli directories`.\nIf no Bloop instance is running, Scala CLI fetches Bloop if necessary (via Coursier), and starts it.\n\nOnce it’s connected to Bloop, Scala CLI writes a Bloop project file under a `.scala/.bloop` directory. This file describes the current Scala CLI project, including its Scala version, dependencies, compiler plugins and options, etc.\n\nIt then initiates a [BSP](https://github.com/build-server-protocol/build-server-protocol) connection with Bloop.\nBSP communication happens on a domain socket too, different than the one above.\n\nThat BSP connection then allows Scala CLI to ask Bloop to compile sources, and get diagnostics (warnings / errors) and the compiled byte code.\n\n### `.scala-build` directory\n\nIn the directory where you run your Scala CLI commands, Scala CLI creates a subdirectory named `.scala-build`, where it writes:\n- [Bloop project files](#bloop)\n- [generated sources](#preprocessing)\n- byte code and TASTy files that result from compiling the user sources\n\nThe typical content of the `.scala-build` directory looks like this:\n```text\n.scala-build\n├── .bloop\n│   ├── project_940fb43dce\n│   │   ├── bloop-internal-classes\n│   │   │   └── main-ZWP3jgllS6y93V4HoGYa2g==\n│   │   │       ├── test$.class\n│   │   │       ├── test.class\n│   │   │       ├── test.tasty\n│   │   │       ├── test_sc$.class\n│   │   │       ├── test_sc.class\n│   │   │       └── test_sc.tasty\n│   │   └── project_940fb43dce-analysis.bin\n│   ├── project_940fb43dce.json\n│   └── project_f643cb0bc2-test.json\n└── project_940fb43dce\n    ├── classes\n    │   └── main\n    │       ├── test$.class\n    │       ├── test.class\n    │       ├── test.tasty\n    │       ├── test_sc$.class\n    │       ├── test_sc.class\n    │       └── test_sc.tasty\n    └── src_generated\n        └── main\n            └── test.scala\n```\n\nIn particular, `.scala-build/.bloop` contains Bloop project files and Bloop's own working directories, and `.scala-build/project_*` contains byte code, TASTy files, and generated sources. \n\n## Home directory for scala-cli\n\nBy default, Scala CLI uses the home directory to store Coursier caches, the config database, the working directory\nfor Bloop, and other internal files. To change this default behavior, set the `SCALA_CLI_HOME` environment variable to point\nto an existing directory.\n\n## Preprocessing\n\nSome source code files that Scala CLI accepts cannot be passed as-is to `scalac`.\nThis is the case for:\n- `.sc` files, which can contain top-level definitions not accepted by `scalac`\n- `.scala` files that have uncommented `using` directives\n\nIn all of those cases, Scala CLI parses the top of those files, and looks for `using` directives.\nIt then replaces the non-commented `using` directives with space characters.\n\nAs described in [Scripts](../scripting/scripts.md), `.sc` files are also \"wrapped\" in an `object`, and a `main` class is added to them, so that `.sc` files can be run as-is, and can access arguments via a special `args` variable.\n\nIn all cases, the resulting processed sources are written in the `.scala-build/project_…/src_generated` directory, and passed to Bloop from there.\n\n## Postprocessing\n\nBecause of [preprocessing](#preprocessing), some outputs we get from `scalac` might not match the original sources.\nProcessed sources might have shifted line numbers (for `.sc` files, because\nof the wrapping in an `object`), or wrong relative paths (as they're written in `src_generated`).\n\nFor those files, most outputs from `scalac` are postprocessed, so they match the original sources.\nThat includes:\n- diagnostics (errors/warnings, whose file names and line/column numbers are adjusted)\n- byte code (whole line numbers, reported in exception stack traces or used by debuggers, needs to be shifted)\n- semantic DBs (used for IDE support, whose path fields and positions need to be adjusted)\n- TASTy files (whose path fields need to be adjusted)\n\n## Runner\n\nWhen running your code, if the code crashes, Scala CLI processes the stack traces of the exception to make them more readable.\nThis is achieved by adding a module (called `runner`) to the class path, and this module is actually used as the entry point of your application.\nThe [`Runner` class](https://github.com/VirtusLab/scala-cli/blob/60eae701abc74bdd634efa5157740578bd6c4162/modules/runner/src/main/scala/scala/cli/runner/Runner.scala)\nof the `runner` module starts your main class, catches any exceptions it might throw, and prints it.\n## Logging\n\nTo get a glimpse at what Scala CLI is doing, increase its verbosity with `-v`.\nThe `-v` option can be specified up to 3 times, which increases its verbosity level.\n\nUsing this option can be a good way to learn how Scala CLI works, though it's mostly meant to help debug issues.\nWhen reporting bugs, increasing the verbosity to its maximum level can be helpful.\n\nHere's some example output for the first verbosity level:\n\n```text\n$ scala-cli . -v\nRunning /Users/alexandre/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home/bin/java -cp /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar:/Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/runner_3/0.0.5+43-g60eae701-SNAPSHOT/jars/runner_3.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/oss.sonatype.org/content/repositories/snapshots/org/virtuslab/pretty-stacktraces_3/0.0.0%2B27-b9d69198-SNAPSHOT/pretty-stacktraces_3-0.0.0%2B27-b9d69198-SNAPSHOT.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-tasty-inspector_3/3.0.0/scala3-tasty-inspector_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.0/scala3-compiler_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.0/scala3-interfaces-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.0/tasty-core_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar:/Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar scala.cli.runner.Runner test_sc\nHello\n```\n\nNext, this output shows how much more detail is available when `-v` is specified twice:\n\n```text\n$ scala-cli . -v -v\nFetching List(ch.epfl.scala:bloop-frontend_2.12:1.4.8-124-49a6348a)\nFound 127 artifacts:\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-frontend_2.12/1.4.8-124-49a6348a/bloop-frontend_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.13/scala-library-2.12.13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/sockets/1.4.8-124-49a6348a/sockets-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-shared_2.12/1.4.8-124-49a6348a/bloop-shared_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-backend_2.12/1.4.8-124-49a6348a/bloop-backend_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-config_2.12/1.4.8-124-49a6348a/bloop-config_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalaz/scalaz-core_2.12/7.2.20/scalaz-core_2.12-7.2.20.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix_2.12/2.3.3/monix_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/case-app_2.12/2.0.6/case-app_2.12-2.0.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/scala-debug-adapter_2.12/1.1.3/scala-debug-adapter_2.12-1.1.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.8.0/jna-5.8.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna-platform/5.8.0/jna-platform-5.8.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bsp4s_2.12/2.0.0-M13/bsp4s_2.12-2.0.0-M13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc_2.12/1.3.0-M4%2B46-edbe573e/zinc_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/github/soc/directories/10/directories-10.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/test-agent/1.4.4/test-agent-1.4.4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/pprint_2.12/0.5.3/pprint_2.12-0.5.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/com-microsoft-java-debug-core/0.21.0%2B1-7f1080f1/com-microsoft-java-debug-core-0.21.0%2B1-7f1080f1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/nailgun-server/ee3c4343/nailgun-server-ee3c4343.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalaz/scalaz-concurrent_2.12/7.2.20/scalaz-concurrent_2.12-7.2.20.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier_2.12/2.0.16/coursier_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier-cache_2.12/2.0.16/coursier-cache_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/librarymanagement-ivy_2.12/1.0.0/librarymanagement-ivy_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.12/0.1.4/sourcecode_2.12-0.1.4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/directory-watcher/0.8.0%2B6-f651bd93/directory-watcher-0.8.0%2B6-f651bd93.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/zeroturnaround/zt-zip/1.13/zt-zip-1.13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/brave/brave/5.6.1/brave-5.6.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/reporter2/zipkin-sender-urlconnection/2.7.15/zipkin-sender-urlconnection-2.7.15.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm/9.2/asm-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/plokhotnyuk/jsoniter-scala/jsoniter-scala-core_2.12/2.4.0/jsoniter-scala-core_2.12-2.4.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-types_2.12/2.3.3/monix-types_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-execution_2.12/2.3.3/monix-execution_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-eval_2.12/2.3.3/monix-eval_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-reactive_2.12/2.3.3/monix-reactive_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/case-app-annotations_2.12/2.0.6/case-app-annotations_2.12-2.0.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/case-app-util_2.12/2.0.6/case-app-util_2.12-2.0.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-core_2.12/0.9.3/circe-core_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-derivation_2.12/0.9.0-M4/circe-derivation_2.12-0.9.0-M4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalameta/lsp4s_2.12/0.2.0/lsp4s_2.12-0.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-core_2.12/1.3.0-M4%2B46-edbe573e/zinc-core_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-persist_2.12/1.3.0-M4%2B46-edbe573e/zinc-persist_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-compile-core_2.12/1.3.0-M4%2B46-edbe573e/zinc-compile-core_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-classfile_2.12/1.3.0-M4%2B46-edbe573e/zinc-classfile_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fansi_2.12/0.2.5/fansi_2.12-0.2.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/code/gson/gson/2.7/gson-2.7.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/reactivex/rxjava2/rxjava/2.1.1/rxjava-2.1.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.0/reactive-streams-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/commons-io/commons-io/2.5/commons-io-2.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.26/slf4j-api-1.7.26.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalaz/scalaz-effect_2.12/7.2.20/scalaz-effect_2.12-7.2.20.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier-core_2.12/2.0.16/coursier-core_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/argonaut-shapeless_6.2_2.12/1.2.0/argonaut-shapeless_6.2_2.12-1.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier-util_2.12/2.0.16/coursier-util_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/github/alexarchambault/windows-ansi/windows-ansi/0.0.3/windows-ansi-0.0.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/librarymanagement-core_2.12/1.0.0/librarymanagement-core_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/sjson-new-core_2.12/0.8.2/sjson-new-core_2.12-0.8.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/ivy/ivy/2.3.0-sbt-a3314352b638afbf0dca19f127e8263ed6f898bd/ivy-2.3.0-sbt-a3314352b638afbf0dca19f127e8263ed6f898bd.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/zipkin2/zipkin/2.12.1/zipkin-2.12.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/reporter2/zipkin-reporter/2.7.15/zipkin-reporter-2.7.15.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jctools/jctools-core/2.0.1/jctools-core-2.0.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.12/2.3.3/shapeless_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-numbers_2.12/0.9.3/circe-numbers_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-core_2.12/1.1.0/cats-core_2.12-1.1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalameta/jsonrpc_2.12/0.2.0/jsonrpc_2.12-0.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/beachape/enumeratum_2.12/1.5.13/enumeratum_2.12-1.5.13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/beachape/enumeratum-circe_2.12/1.5.17/enumeratum-circe_2.12-1.5.17.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-apiinfo_2.12/1.3.0-M4%2B46-edbe573e/zinc-apiinfo_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-classpath_2.12/1.3.0-M4%2B46-edbe573e/zinc-classpath_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/compiler-interface/1.3.0-M4%2B46-edbe573e/compiler-interface-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/thesamet/scalapb/scalapb-runtime_2.12/0.8.0-RC1/scalapb-runtime_2.12-0.8.0-RC1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/io_2.12/1.2.0/io_2.12-1.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-logging_2.12/1.2.2/util-logging_2.12-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-relation_2.12/1.2.2/util-relation_2.12-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/sbinary_2.12/0.5.0/sbinary_2.12-0.5.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/launcher-interface/1.0.0/launcher-interface-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-parser-combinators_2.12/1.0.5/scala-parser-combinators_2.12-1.0.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-control_2.12/1.2.2/util-control_2.12-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/github/alexarchambault/concurrent-reference-hash-map/1.0.0/concurrent-reference-hash-map-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-xml_2.12/1.3.0/scala-xml_2.12-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/argonaut/argonaut_2.12/6.2.5/argonaut_2.12-6.2.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-collection-compat_2.12/2.2.0/scala-collection-compat_2.12-2.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.18/jansi-1.18.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-compiler/2.12.11/scala-compiler-2.12.11.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/jcraft/jsch/0.1.46/jsch-0.1.46.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-reflect/2.12.11/scala-reflect-2.12.11.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/gigahorse-okhttp_2.12/0.3.0/gigahorse-okhttp_2.12-0.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-urlconnection/3.7.0/okhttp-urlconnection-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-position_2.12/1.0.0/util-position_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-cache_2.12/1.0.0/util-cache_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/macro-compat_2.12/1.1.1/macro-compat_2.12-1.1.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-macros_2.12/1.1.0/cats-macros_2.12-1.1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-kernel_2.12/1.1.0/cats-kernel_2.12-1.1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/machinist_2.12/0.6.2/machinist_2.12-0.6.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/scribe_2.12/2.5.0/scribe_2.12-2.5.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-parser_2.12/0.9.3/circe-parser_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/beachape/enumeratum-macros_2.12/1.5.9/enumeratum-macros_2.12-1.5.9.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/compiler-bridge_2.12/1.3.0-M4%2B46-edbe573e/compiler-bridge_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.2.2/util-interface-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/thesamet/scalapb/lenses_2.12/0.8.0-RC1/lenses_2.12-0.8.0-RC1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse_2.12/1.0.0/fastparse_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/swoval/apple-file-events/1.3.2/apple-file-events-1.3.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/jline/jline/2.14.4/jline-2.14.4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.8.1/log4j-api-2.8.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.8.1/log4j-core-2.8.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lmax/disruptor/3.3.6/disruptor-3.3.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/sjson-new-scalajson_2.12/0.8.2/sjson-new-scalajson_2.12-0.8.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/gigahorse-core_2.12/0.3.0/gigahorse-core_2.12-0.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp/3.7.0/okhttp-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/sjson-new-murmurhash_2.12/0.8.0/sjson-new-murmurhash_2.12-0.8.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/scribe-macros_2.12/2.5.0/scribe-macros_2.12-2.5.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/perfolation_2.12/1.0.2/perfolation_2.12-1.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-jawn_2.12/0.9.3/circe-jawn_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse-utils_2.12/1.0.0/fastparse-utils_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/shaded-scalajson_2.12/1.0.0-M4/shaded-scalajson_2.12-1.0.0-M4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/spire-math/jawn-parser_2.12/0.11.1/jawn-parser_2.12-0.11.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/typesafe/ssl-config-core_2.12/0.2.2/ssl-config-core_2.12-0.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/squareup/okio/okio/1.12.0/okio-1.12.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/perfolation-macros_2.12/1.0.2/perfolation-macros_2.12-1.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/typesafe/config/1.2.0/config-1.2.0.jar\n\nFetching List(org.scala-lang::scala3-compiler:3.0.2), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 13 artifacts:\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.2/scala3-compiler_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.2/scala3-interfaces-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.2/tasty-core_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar\n\nFetching List(org.scala-lang::scala3-library::3.0.2, org.virtuslab.scala-cli::runner:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFetching List(org.virtuslab.scala-cli:stubs:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 1 artifacts:\n  /Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar\n\nWriting bloop project in /Users/alexandre/projects/scala-cli/test/.scala/.bloop/project_940fb43dce.json\nListing BSP build targets\nCompiling project_940fb43dce with Bloop\nReceived onBuildTaskStart from bloop: TaskStartParams [\n  taskId = TaskId [\n    id = \"1\"\n    parents = null\n  ]\n  eventTime = 1634309123019\n  message = \"Compiling project_940fb43dce (1 Scala source)\"\n  dataKind = \"compile-task\"\n  data = {\"target\":{\"uri\":\"file:/Users/alexandre/projects/scala-cli/test/.scala/?id=project_940fb43dce\"}}\n]\nCompiling project (Scala 3.0.2, JVM)\nReceived onBuildTaskFinish from bloop: TaskFinishParams [\n  taskId = TaskId [\n    id = \"1\"\n    parents = null\n  ]\n  eventTime = 1634309127394\n  message = \"Compiled 'project_940fb43dce'\"\n  status = OK\n  dataKind = \"compile-report\"\n  data = {\"target\":{\"uri\":\"file:/Users/alexandre/projects/scala-cli/test/.scala/?id=project_940fb43dce\"},\"originId\":null,\"errors\":0,\"warnings\":0,\"time\":null,\"isNoOp\":false,\"isLastCycle\":true,\"clientDir\":\"file:///Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/\",\"analysisOut\":\"file:///Users/alexandre/projects/scala-cli/test/.scala/.bloop/project_940fb43dce/project_940fb43dce-analysis.bin\"}\n]\nCompiled project (Scala 3.0.2, JVM)\nCompilation succeeded\nPost-processing class files of pre-processed sources\nOverwriting .scala/project_940fb43dce/classes/main/test$.class\nOverwriting .scala/project_940fb43dce/classes/main/test.class\nOverwriting .scala/project_940fb43dce/classes/main/test_sc$.class\nOverwriting .scala/project_940fb43dce/classes/main/test_sc.class\nMoving semantic DBs around\nReading TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test.tasty\nParsed TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test.tasty\nOverwriting .scala/project_940fb43dce/classes/main/test.tasty\nReading TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test_sc.tasty\nParsed TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test_sc.tasty\nOverwriting .scala/project_940fb43dce/classes/main/test_sc.tasty\nFetching List(org.scala-lang::scala3-compiler:3.0.2), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 13 artifacts:\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.2/scala3-compiler_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.2/scala3-interfaces-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.2/tasty-core_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar\n\nFetching List(org.scala-lang::scala3-library::3.0.2, org.virtuslab.scala-cli::runner:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFetching List(org.virtuslab.scala-cli:stubs:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 1 artifacts:\n  /Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar\n\nWriting bloop project in /Users/alexandre/projects/scala-cli/test/.scala/.bloop/project_f643cb0bc2-test.json\nListing BSP build targets\nCompiling project_f643cb0bc2-test with Bloop\nCompilation succeeded\nPost-processing class files of pre-processed sources\nMoving semantic DBs around\n  Running\n/Users/alexandre/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home/bin/java\n-cp\n/Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar:/Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/runner_3/0.0.5+43-g60eae701-SNAPSHOT/jars/runner_3.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/oss.sonatype.org/content/repositories/snapshots/org/virtuslab/pretty-stacktraces_3/0.0.0%2B27-b9d69198-SNAPSHOT/pretty-stacktraces_3-0.0.0%2B27-b9d69198-SNAPSHOT.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-tasty-inspector_3/3.0.0/scala3-tasty-inspector_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.0/scala3-compiler_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.0/scala3-interfaces-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.0/tasty-core_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar:/Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar\nscala.cli.runner.Runner\ntest_sc\n\nexecve available\nHello\n```\n\nFinally, this example shows the detail that's available when `-v` is specified three times:\n\n```text\n$ scala-cli . -v -v -v\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nNo bloop daemon found on 127.0.0.1:8212\nStarting bloop server\nFetching List(ch.epfl.scala:bloop-frontend_2.12:1.4.8-124-49a6348a)\nFound 127 artifacts:\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-frontend_2.12/1.4.8-124-49a6348a/bloop-frontend_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.13/scala-library-2.12.13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/sockets/1.4.8-124-49a6348a/sockets-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-shared_2.12/1.4.8-124-49a6348a/bloop-shared_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-backend_2.12/1.4.8-124-49a6348a/bloop-backend_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bloop-config_2.12/1.4.8-124-49a6348a/bloop-config_2.12-1.4.8-124-49a6348a.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalaz/scalaz-core_2.12/7.2.20/scalaz-core_2.12-7.2.20.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix_2.12/2.3.3/monix_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/case-app_2.12/2.0.6/case-app_2.12-2.0.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/scala-debug-adapter_2.12/1.1.3/scala-debug-adapter_2.12-1.1.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.8.0/jna-5.8.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna-platform/5.8.0/jna-platform-5.8.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/bsp4s_2.12/2.0.0-M13/bsp4s_2.12-2.0.0-M13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc_2.12/1.3.0-M4%2B46-edbe573e/zinc_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/github/soc/directories/10/directories-10.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/test-agent/1.4.4/test-agent-1.4.4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/pprint_2.12/0.5.3/pprint_2.12-0.5.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/com-microsoft-java-debug-core/0.21.0%2B1-7f1080f1/com-microsoft-java-debug-core-0.21.0%2B1-7f1080f1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/nailgun-server/ee3c4343/nailgun-server-ee3c4343.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalaz/scalaz-concurrent_2.12/7.2.20/scalaz-concurrent_2.12-7.2.20.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier_2.12/2.0.16/coursier_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier-cache_2.12/2.0.16/coursier-cache_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/librarymanagement-ivy_2.12/1.0.0/librarymanagement-ivy_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.12/0.1.4/sourcecode_2.12-0.1.4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/directory-watcher/0.8.0%2B6-f651bd93/directory-watcher-0.8.0%2B6-f651bd93.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/zeroturnaround/zt-zip/1.13/zt-zip-1.13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/brave/brave/5.6.1/brave-5.6.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/reporter2/zipkin-sender-urlconnection/2.7.15/zipkin-sender-urlconnection-2.7.15.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm/9.2/asm-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/plokhotnyuk/jsoniter-scala/jsoniter-scala-core_2.12/2.4.0/jsoniter-scala-core_2.12-2.4.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-types_2.12/2.3.3/monix-types_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-execution_2.12/2.3.3/monix-execution_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-eval_2.12/2.3.3/monix-eval_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/monix/monix-reactive_2.12/2.3.3/monix-reactive_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/case-app-annotations_2.12/2.0.6/case-app-annotations_2.12-2.0.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/case-app-util_2.12/2.0.6/case-app-util_2.12-2.0.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-core_2.12/0.9.3/circe-core_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-derivation_2.12/0.9.0-M4/circe-derivation_2.12-0.9.0-M4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalameta/lsp4s_2.12/0.2.0/lsp4s_2.12-0.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-core_2.12/1.3.0-M4%2B46-edbe573e/zinc-core_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-persist_2.12/1.3.0-M4%2B46-edbe573e/zinc-persist_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-compile-core_2.12/1.3.0-M4%2B46-edbe573e/zinc-compile-core_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-classfile_2.12/1.3.0-M4%2B46-edbe573e/zinc-classfile_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fansi_2.12/0.2.5/fansi_2.12-0.2.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/code/gson/gson/2.7/gson-2.7.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/reactivex/rxjava2/rxjava/2.1.1/rxjava-2.1.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.0/reactive-streams-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/commons-io/commons-io/2.5/commons-io-2.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.26/slf4j-api-1.7.26.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalaz/scalaz-effect_2.12/7.2.20/scalaz-effect_2.12-7.2.20.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier-core_2.12/2.0.16/coursier-core_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/github/alexarchambault/argonaut-shapeless_6.2_2.12/1.2.0/argonaut-shapeless_6.2_2.12-1.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/get-coursier/coursier-util_2.12/2.0.16/coursier-util_2.12-2.0.16.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/github/alexarchambault/windows-ansi/windows-ansi/0.0.3/windows-ansi-0.0.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/librarymanagement-core_2.12/1.0.0/librarymanagement-core_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/sjson-new-core_2.12/0.8.2/sjson-new-core_2.12-0.8.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/ivy/ivy/2.3.0-sbt-a3314352b638afbf0dca19f127e8263ed6f898bd/ivy-2.3.0-sbt-a3314352b638afbf0dca19f127e8263ed6f898bd.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/zipkin2/zipkin/2.12.1/zipkin-2.12.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/zipkin/reporter2/zipkin-reporter/2.7.15/zipkin-reporter-2.7.15.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jctools/jctools-core/2.0.1/jctools-core-2.0.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.12/2.3.3/shapeless_2.12-2.3.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-numbers_2.12/0.9.3/circe-numbers_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-core_2.12/1.1.0/cats-core_2.12-1.1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalameta/jsonrpc_2.12/0.2.0/jsonrpc_2.12-0.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/beachape/enumeratum_2.12/1.5.13/enumeratum_2.12-1.5.13.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/beachape/enumeratum-circe_2.12/1.5.17/enumeratum-circe_2.12-1.5.17.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-apiinfo_2.12/1.3.0-M4%2B46-edbe573e/zinc-apiinfo_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/zinc-classpath_2.12/1.3.0-M4%2B46-edbe573e/zinc-classpath_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/compiler-interface/1.3.0-M4%2B46-edbe573e/compiler-interface-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/thesamet/scalapb/scalapb-runtime_2.12/0.8.0-RC1/scalapb-runtime_2.12-0.8.0-RC1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/io_2.12/1.2.0/io_2.12-1.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-logging_2.12/1.2.2/util-logging_2.12-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-relation_2.12/1.2.2/util-relation_2.12-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/sbinary_2.12/0.5.0/sbinary_2.12-0.5.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/launcher-interface/1.0.0/launcher-interface-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-parser-combinators_2.12/1.0.5/scala-parser-combinators_2.12-1.0.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-control_2.12/1.2.2/util-control_2.12-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/github/alexarchambault/concurrent-reference-hash-map/1.0.0/concurrent-reference-hash-map-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-xml_2.12/1.3.0/scala-xml_2.12-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/argonaut/argonaut_2.12/6.2.5/argonaut_2.12-6.2.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-collection-compat_2.12/2.2.0/scala-collection-compat_2.12-2.2.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.18/jansi-1.18.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-compiler/2.12.11/scala-compiler-2.12.11.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/jcraft/jsch/0.1.46/jsch-0.1.46.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-reflect/2.12.11/scala-reflect-2.12.11.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/gigahorse-okhttp_2.12/0.3.0/gigahorse-okhttp_2.12-0.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-urlconnection/3.7.0/okhttp-urlconnection-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-position_2.12/1.0.0/util-position_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-cache_2.12/1.0.0/util-cache_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/macro-compat_2.12/1.1.1/macro-compat_2.12-1.1.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-macros_2.12/1.1.0/cats-macros_2.12-1.1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-kernel_2.12/1.1.0/cats-kernel_2.12-1.1.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/typelevel/machinist_2.12/0.6.2/machinist_2.12-0.6.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/scribe_2.12/2.5.0/scribe_2.12-2.5.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-parser_2.12/0.9.3/circe-parser_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/beachape/enumeratum-macros_2.12/1.5.9/enumeratum-macros_2.12-1.5.9.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ch/epfl/scala/compiler-bridge_2.12/1.3.0-M4%2B46-edbe573e/compiler-bridge_2.12-1.3.0-M4%2B46-edbe573e.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.2.2/util-interface-1.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/thesamet/scalapb/lenses_2.12/0.8.0-RC1/lenses_2.12-0.8.0-RC1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse_2.12/1.0.0/fastparse_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/swoval/apple-file-events/1.3.2/apple-file-events-1.3.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/jline/jline/2.14.4/jline-2.14.4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.8.1/log4j-api-2.8.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.8.1/log4j-core-2.8.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lmax/disruptor/3.3.6/disruptor-3.3.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/sjson-new-scalajson_2.12/0.8.2/sjson-new-scalajson_2.12-0.8.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/gigahorse-core_2.12/0.3.0/gigahorse-core_2.12-0.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp/3.7.0/okhttp-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/sjson-new-murmurhash_2.12/0.8.0/sjson-new-murmurhash_2.12-0.8.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/scribe-macros_2.12/2.5.0/scribe-macros_2.12-2.5.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/perfolation_2.12/1.0.2/perfolation_2.12-1.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/io/circe/circe-jawn_2.12/0.9.3/circe-jawn_2.12-0.9.3.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse-utils_2.12/1.0.0/fastparse-utils_2.12-1.0.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/eed3si9n/shaded-scalajson_2.12/1.0.0-M4/shaded-scalajson_2.12-1.0.0-M4.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/spire-math/jawn-parser_2.12/0.11.1/jawn-parser_2.12-0.11.1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/typesafe/ssl-config-core_2.12/0.2.2/ssl-config-core_2.12-0.2.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/squareup/okio/okio/1.12.0/okio-1.12.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/outr/perfolation-macros_2.12/1.0.2/perfolation-macros_2.12-1.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/typesafe/config/1.2.0/config-1.2.0.jar\n\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nUnable to load nailgun-version.properties.\nNGServer [UNKNOWN] started on address /127.0.0.1 port 8212.\nAttempting a connection to bloop server 127.0.0.1:8212 ...\nBloop server started\nOpening BSP connection with bloop\nBloop BSP connection waiting at local:/Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nnailgun debug: Sending arguments '--protocol local --socket /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511' to Nailgun server\nnailgun debug: Sending environment variables to Nailgun server\nnailgun debug: Sending working directory /Users/alexandre/projects/scala-cli/test/.scala to Nailgun server\nnailgun debug: Sending command to bsp Nailgun server\nnailgun debug: Finished sending command information to Nailgun server\nnailgun debug: Starting thread to read stdin...\n[W] Internal error in session\njava.io.EOFException\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\n\tat java.base/java.io.DataInputStream.readInt(DataInputStream.java:397)\n\tat com.martiansoftware.nailgun.NGCommunicator.readCommandContext(NGCommunicator.java:140)\n\tat com.martiansoftware.nailgun.NGSession.run(NGSession.java:197)\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 not found, waiting 100 milliseconds\nBSP connection at /Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511 opened\nConnected to Bloop via BSP at local:/Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511\nConnected to Bloop via BSP at local:/Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511\nnailgun debug: Received action Print([B@1c79f3a7) from Nailgun server\nThe server is listening for incoming connections at local:///Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511...\nnailgun debug: Received action Print([B@274c0297) from Nailgun server\nAccepted incoming BSP client connection at local:///Users/alexandre/Library/Caches/ScalaCli/bsp-sockets/proc-80511\nSending buildInitialize BSP command to Bloop\nnailgun debug: Received action Print([B@7af46130) from Nailgun server\nrequest received: build/initialize\nnailgun debug: Received action Print([B@29f9d46d) from Nailgun server\nBSP initialization handshake complete.\nFetching List(org.scala-lang::scala3-compiler:3.0.2), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 13 artifacts:\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.2/scala3-compiler_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.2/scala3-interfaces-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.2/tasty-core_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar\n\nFetching List(org.scala-lang::scala3-library::3.0.2, org.virtuslab.scala-cli::runner:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFetching List(org.virtuslab.scala-cli:stubs:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 1 artifacts:\n  /Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar\n\nWriting bloop project in /Users/alexandre/projects/scala-cli/test/.scala/.bloop/project_940fb43dce.json\nListing BSP build targets\nCompiling project_940fb43dce with Bloop\nReceived onBuildTaskStart from bloop: TaskStartParams [\n  taskId = TaskId [\n    id = \"1\"\n    parents = null\n  ]\n  eventTime = 1634309020072\n  message = \"Compiling project_940fb43dce (1 Scala source)\"\n  dataKind = \"compile-task\"\n  data = {\"target\":{\"uri\":\"file:/Users/alexandre/projects/scala-cli/test/.scala/?id=project_940fb43dce\"}}\n]\nCompiling project (Scala 3.0.2, JVM)\nReceived onBuildTaskFinish from bloop: TaskFinishParams [\n  taskId = TaskId [\n    id = \"1\"\n    parents = null\n  ]\n  eventTime = 1634309023968\n  message = \"Compiled 'project_940fb43dce'\"\n  status = OK\n  dataKind = \"compile-report\"\n  data = {\"target\":{\"uri\":\"file:/Users/alexandre/projects/scala-cli/test/.scala/?id=project_940fb43dce\"},\"originId\":null,\"errors\":0,\"warnings\":0,\"time\":null,\"isNoOp\":false,\"isLastCycle\":true,\"clientDir\":\"file:///Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/\",\"analysisOut\":\"file:///Users/alexandre/projects/scala-cli/test/.scala/.bloop/project_940fb43dce/project_940fb43dce-analysis.bin\"}\n]\nCompiled project (Scala 3.0.2, JVM)\nCompilation succeeded\nPost-processing class files of pre-processed sources\nOverwriting .scala/project_940fb43dce/classes/main/test$.class\nOverwriting .scala/project_940fb43dce/classes/main/test.class\nOverwriting .scala/project_940fb43dce/classes/main/test_sc$.class\nOverwriting .scala/project_940fb43dce/classes/main/test_sc.class\nMoving semantic DBs around\nReading TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test.tasty\nParsed TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test.tasty\nOverwriting .scala/project_940fb43dce/classes/main/test.tasty\nReading TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test_sc.tasty\nParsed TASTy file /Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main/test_sc.tasty\nOverwriting .scala/project_940fb43dce/classes/main/test_sc.tasty\nFetching List(org.scala-lang::scala3-compiler:3.0.2), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 13 artifacts:\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.2/scala3-compiler_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.2/scala3-interfaces-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.2/tasty-core_3-3.0.2.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar\n  /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar\n\nFetching List(org.scala-lang::scala3-library::3.0.2, org.virtuslab.scala-cli::runner:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFetching List(org.virtuslab.scala-cli:stubs:0.0.5+43-g60eae701-SNAPSHOT), adding List(https://oss.sonatype.org/content/repositories/snapshots, ivy:file:///Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7//[defaultPattern])\nFound 1 artifacts:\n  /Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar\n\nWriting bloop project in /Users/alexandre/projects/scala-cli/test/.scala/.bloop/project_f643cb0bc2-test.json\nListing BSP build targets\nCompiling project_f643cb0bc2-test with Bloop\nCompilation succeeded\nPost-processing class files of pre-processed sources\nMoving semantic DBs around\n  Running\n/Users/alexandre/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home/bin/java\n-cp\n/Users/alexandre/projects/scala-cli/test/.scala/project_940fb43dce/classes/main:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.0.2/scala3-library_3-3.0.2.jar:/Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/runner_3/0.0.5+43-g60eae701-SNAPSHOT/jars/runner_3.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.6/scala-library-2.13.6.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/oss.sonatype.org/content/repositories/snapshots/org/virtuslab/pretty-stacktraces_3/0.0.0%2B27-b9d69198-SNAPSHOT/pretty-stacktraces_3-0.0.0%2B27-b9d69198-SNAPSHOT.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-tasty-inspector_3/3.0.0/scala3-tasty-inspector_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.0.0/scala3-compiler_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.0.0/scala3-interfaces-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.0.0/tasty-core_3-3.0.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.1.0-scala-1/scala-asm-9.1.0-scala-1.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar:/Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar:/Users/alexandre/Library/Caches/ScalaCli/local-repo/v0.0.5-43-60eae7/org.virtuslab.scala-cli/stubs/0.0.5+43-g60eae701-SNAPSHOT/jars/stubs.jar\nscala.cli.runner.Runner\ntest_sc\n\nexecve available\nHello\nClient in /Users/alexandre/projects/scala-cli/test/.scala/.bloop disconnected with a 'SocketError' event. Cancelling tasks...\n```\n\nIf you want to understand how Scala CLI works, the `-v` option shows you the details of what's happening when your command is run.\n"
  },
  {
    "path": "website/docs/guides/advanced/java-properties.md",
    "content": "---\ntitle: Java properties\nsidebar_position: 45\n---\n\nAlthough the Scala CLI runner can be used as a native image and thus will not always be run on the JVM it still supports Java properties.\nThere are a couple ways to specify them.\n\n### On the Command line\nJava properties can be passed as arguments, before the sub-command name and sources, when invoking `scala-cli`, e.g.\n```bash ignore\n    scala-cli '-Dcoursier.credentials=maven.pkg.github.com Private_Token:gh_token1234' run .\n```\n:::note\n- `scala-cli run . -Dfoo=bar` would pass the java property into your Scala app\n- `scala-cli -Dfoo=bar run .` would pass the java property into `scala-cli`.\n  :::\n\n### File named `.scala-jvmopts` \nYou can also use a `.scala-jvmopts` file placed in the project's root, example file content:\n```text\n-Dcoursier.credentials=maven.pkg.github.com Private_Token:gh_token1234\n-Dhttp.proxy=4.4.4.4\n-Dhttp.user=User2\n```\n\n### Scala CLI's `config`\nIt's also possible to use the `config` subcommand to set the properties globally:\n```bash ignore\n    scala-cli --power config -i java.properties \"http.proxy=4.4.4.4\" \"http.user=User2\" \"coursier.credentials=...\"\n```\n:::note\nPlease note that if you need to modify the Java properties, you have to redefine all of them. It's not possible\nto update just a single value via the `config` command. Each update effectively replaces the entire Java properties\nlist.\n:::\n\n### `JAVA_OPTS` and `JDK_JAVA_OPTIONS`\nScala CLI will also read environment variables `JAVA_OPTS` and `JDK_JAVA_OPTIONS` and scan them for Java properties:\n```bash ignore\n    export JAVA_OPTS=\"-Dhttp.proxy=4.4.4.4 -Dhttp.user=User2\"\n```\n\n:::note\nThe `-D` prefix can only be dropped when writing the values to config.\n:::"
  },
  {
    "path": "website/docs/guides/advanced/piping.md",
    "content": "---\ntitle: Piping\nsidebar_position: 42\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n# Piping\n\nInstead of passing paths to your sources, you can also pipe your code via standard input:\n\n<ChainedSnippets>\n\n```bash\necho '@main def hello() = println(\"Hello\")' | scala-cli _\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n## Wildcards\n\nThe `_` wildcard implies that the piped code is a standard Scala app.\nIt is also possible to pass a script or Java code, when using the appropriate wildcard.\nThe available options are as follows:\n\n- for standard Scala code use `_`, `_.scala` or `-.scala`;\n- for Scala scripts use `-`, `_.sc` or `-.sc`;\n- for Java code use `_.java` or `-.java`;\n- for Markdown code use `_.md` or `-.md`.\n\n## Examples\n\n- scripts\n\n<ChainedSnippets>\n\n```bash\necho 'println(\"Hello\")' | scala-cli _.sc\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- Scala code\n\n<ChainedSnippets>\n\n```bash\necho '@main def hello() = println(\"Hello\")' | scala-cli _.scala\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- Java code\n\n<ChainedSnippets>\n\n```bash\necho 'class Hello { public static void main(String args[]) { System.out.println(\"Hello\"); } }' | scala-cli _.java\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- Markdown code (experimental)\n\n<ChainedSnippets>\n\n```bash\necho '# Example Snippet\n```scala\nprintln(\"Hello\")\n```' | scala-cli _.md\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n## Mixing piped sources with on-disk ones\n\nIt is also possible to pipe some code via standard input, while the rest of your code is on-disk.\n\n<ChainedSnippets>\n\n```bash\necho 'case class HelloMessage(msg: String)' > HelloMessage.scala\necho '@main def hello() = println(HelloMessage(msg = \"Hello\").msg)' | scala-cli _ HelloMessage.scala\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nYou can even refer to code from piped scripts, when needed. A piped script can be referred to by its wrapper\nname `stdin`, as in the example below.\n\n<ChainedSnippets>\n\n```bash\necho '@main def main() = println(stdin.message)' > PrintMessage.scala\necho 'def message: String = \"Hello\"' | scala-cli PrintMessage.scala _.sc\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n"
  },
  {
    "path": "website/docs/guides/advanced/scala-js.md",
    "content": "---\ntitle: Scala.js\nsidebar_position: 40\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nScala CLI can compile, run, test, and package Scala.js sources.\n\nOne caveat is that Scala CLI doesn't have support for JS bundlers (such as [webpack](https://webpack.js.org))\nlike [scalajs-bundler](https://github.com/scalacenter/scalajs-bundler) has.\nFor this, you'll have to handle bundling yourself, with the `.js` file generated by Scala CLI.\n\nIn order to run Scala.js sources, `node` is required.\n\n## Configuration\n\nEnable Scala.js support by passing `--js` to Scala CLI, such as:\n\n```bash ignore\nscala-cli Test.scala --js\n```\n\n## Dependencies\n\nThis section is currently a work in progress, but here are some initial notes:\n\n- Beware platform dependencies\n- `run` / `test` / `package` should all work\n\n## Package\n\nPackaging Scala.js applications results in a `.js` file, that can be run with `node`:\n\n```scala title=HelloJs.scala\nimport scala.scalajs.js\n\nobject Hello {\n  def main(args: Array[String]): Unit = {\n    val console = js.Dynamic.global.console\n    val msg = \"Hello World from Scala.js\"\n    console.log(msg)\n  }\n}\n```\n\n```bash\nscala-cli --power package --js HelloJs.scala -o hello.js\nnode hello.js\n# Hello World from Scala.js\n```\n\n<!-- Expected:\nHello World from Scala.js\n-->\n\n### Module Split Style\n\n### Smallest Modules\n\nPassing `--js-module-split-style smallestmodules` to the `package` sub-command creates js modules that are as small as possible.\nScala.js linker generates a lot of js modules, which are copied to the `output` directory.\n\n```scala title=SmallestModules.scala\nimport scala.scalajs.js\n\ncase class Foo(txt: String)\n\nobject Hello extends App {\n  println(Foo(\"Hello World from Scala.js\").txt)\n}\n```\n\n```bash\nscala-cli --power package --js SmallestModules.scala --js-module-split-style smallestmodules --js-module-kind es --output hello\necho \"{\\\"type\\\": \\\"module\\\"}\" >> package.json # enable ES module\nnode hello/main.js\n# Hello World from Scala.js\n```\n\n<!-- Expected:\nHello World from Scala.js\n-->\n\n### Small Modules For\n\nPassing `--js-module-split-style smallestmodules` to the `package` sub-command creates many small modules as possibles for the classes in the listed packages (and their subpackages). To define packages use `jsSmallModuleForPackage` parameter.\n\n```scala title=SmallestModules.scala\n//> using jsModuleKind es\n//> using jsModuleSplitStyleStr smallmodulesfor\n//> using jsSmallModuleForPackage com.example.test com.example.example\n\npackage com.example.test\n\nimport scala.scalajs.js\n\ncase class Foo(txt: String)\n\nobject Hello extends App {\n  println(Foo(\"Hello World from Scala.js\").txt)\n}\n```\n\n## Emit source maps\n\nPassing `--js-emit-source-maps` to the `package` sub-command emits source maps alongside js files. To set the destination path of source maps, pass `--js-source-maps-path` flag with the argument.\n\nThe following command emits a `main.js.map` alongside js files:\n\n```scala title=sample.sc\nprintln(\"sample\")\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power package sample.sc --js --js-emit-source-maps\n```\n\n```text\nEmitted js source maps to: ./Hello.js.map\nWrote Hello.js, run it with\n node ./Hello.js\n```\n\n</ChainedSnippets>\n\n## Scala.js DOM support\n\nPass `--js-dom` to `compile`, `run`, or `test` to simulate a DOM in Node.js\n\n:::note\nIf you see the following error, it means that you don't have the `jsdom` library installed to simulate the DOM.\n```\nError: Cannot find module 'jsdom'\n```\nTo fix it, install `jsdom` locally for your project. You can install install `jsdom` as follows:\n```\nnpm init private\nnpm install jsdom\n```\n:::\n\n```scala title=Hello.scala\n//> using dep org.scala-js::scalajs-dom::2.1.0\n//> using platform scala-js\n\nobject Hello {\n  def main(args: Array[String]): Unit = {\n      val parNode = document.createElement(\"p\")\n      parNode.textContent = \"Hello World\"\n      document.body.appendChild(parNode)\n  }\n}\n```\n\n```bash\nscala-cli sample.sc --js-dom\n```\n\n## Using Directives\n\nScala.js options are supported by using directives in Scala CLI:\n\n#### `--js-version`\n\nThe Scala.js version\n\n #### `--js-header`\n\n A header that will be added at the top of generated .js files\n\nFor more options, see our [`using` directive section](../../reference/directives.md#scalajs-options).\n\n## Supported Scala.js Versions\n\nIn the future, the Scala CLI will be able to support any version of Scala.js independently of the version of scala-cli. But for now, there are some limitations and it isn't possible to use each version of Scala.js.\n\nThe table below lists the last supported version of Scala.js in Scala CLI. If you want to use newer Scala.js, update scala-cli.\n\n| Scala CLI versions | Scala.js |\n|--------------------|:--------:|\n| 0.0.9              |  1.7.1   |\n| 0.1.0 - 0.1.2      |  1.8.0   |\n| 0.1.3              |  1.9.0   |\n| 0.1.4 - 0.1.8      |  1.10.0  |\n| 0.1.9 - 0.1.17     |  1.10.1  |\n| 0.1.18 - 0.2.0     |  1.12.0  |\n| 0.2.1 - 1.0.0-RC1  |  1.13.0  |\n| 1.0.0-RC2 - 1.0.2  |  1.13.1  |\n| 1.0.3 - 1.0.4      |  1.13.2  |\n| 1.0.5 - 1.1.1      |  1.14.0  |\n| 1.1.2 - 1.2.0      |  1.15.0  |\n| 1.2.1 - 1.5.0      |  1.16.0  |\n| 1.5.1 - 1.5.4      |  1.17.0  |\n| 1.6.0 - 1.6.1      |  1.18.1  |\n| 1.6.2 - 1.7.1      |  1.18.2  |\n| 1.8.0 - 1.9.0      |  1.19.0  |\n| 1.9.1 - 1.11.0     |  1.20.1  |\n| 1.12.0 - current   |  1.20.2  |\n\n"
  },
  {
    "path": "website/docs/guides/advanced/scala-native.md",
    "content": "---\ntitle: Scala Native\nsidebar_position: 40\n---\n\nScala Native works with Scala `3.1.x`, `2.13.x` and `2.12.x`. Scripts are unavailable for Scala `2.12.x`.\n\nScala Native requires the LLVM toolchain - see requirements on Scala Native website.\n## Configuration\n\nEnable Scala Native support by passing `--native` to Scala CLI, such as:\n\n```scala\nscala-cli Test.scala --native\n```\n\nA Scala Native version can be set by passing `--native-version` with an argument:\n\n```scala\nscala-cli Test.scala --native --native-version 0.4.3\n```\n\nThese can also be set in a file, or in a project (in your [`project.scala`](https://scala-cli.virtuslab.org/docs/reference/root-dir/) file) by [`using`](https://scala-cli.virtuslab.org/docs/reference/scala-command/directives/#platform) [directives](https://scala-cli.virtuslab.org/docs/reference/scala-command/directives/#scala-native-options):\n\n```scala\n//> using platform native\n//> using nativeVersion 0.4.3\n```\n\nPlatform compatibility and supported Scala language versions depend on this version.  \nIt is recommended to use the newest stable version.\n\n## Dependencies\n\nThis section is currently a work in progress, but here are some initial notes:\n\n- Beware platform dependencies\n- `compile` / `run` / `test` / `package` should all work\n"
  },
  {
    "path": "website/docs/guides/advanced/snippets.md",
    "content": "---\ntitle: Snippets\nsidebar_position: 43\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n# Snippets\n\nInstead of passing paths to your sources, you can also pass the code itself with the appropriate option.\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --scala-snippet '@main def hello() = println(\"Hello\")'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nYou can also divide your code into multiple snippets when passing it this way. Each snippet is then treated as a\nseparate input by Scala CLI.\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --scala-snippet '@main def main() = println(Messages.hello)' --scala-snippet 'object Messages { def hello = \"Hello\" }'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n## Examples\n\n- scripts\n\n<ChainedSnippets>\n\n```bash\nscala-cli run -e 'println(\"Hello\")'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- Scala code\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --scala-snippet '@main def hello() = println(\"Hello\")'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- Java code\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --java-snippet 'class Hello { public static void main(String args[]) { System.out.println(\"Hello\"); } }'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- Markdown code (experimental)\n\n<ChainedSnippets>\n\n````bash\nscala-cli run --markdown-snippet '# Markdown snippet\nwith a code block\n```scala\nprintln(\"Hello\")\n```'\n````\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n- a mix of Scala, Java and scripts\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --scala-snippet '@main def hello() = println(s\"${JavaSnippet.hello} ${snippet.world}\")' --java-snippet 'public class JavaSnippet { public static String hello = \"Hello\"; }' --script-snippet 'def world = \"world\"'\n```\n\n```text\nHello world\n```\n\n</ChainedSnippets>\n\n## Snippets and other kinds of inputs\n\nIt is also possible to mix snippets with on-disk sources.\n\n<ChainedSnippets>\n\n```scala title=Main.scala\nobject Main extends App {\n  val snippetData = SnippetData()\n  println(snippetData.value)\n}\n```\n\n```bash\nscala-cli Main.scala --scala-snippet 'case class SnippetData(value: String = \"Hello\")'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nOr even with piped ones, why not.\n\n<ChainedSnippets>\n\n```bash\necho 'println(SnippetData().value)' ||  scala-cli _.sc --scala-snippet 'case class SnippetData(value: String = \"Hello\")'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nNothing stops you from mixing everything all at once, really.\n\n<ChainedSnippets>\n\n```scala title=Main.scala\nobject Main extends App {\n  val scalaSnippetString = ScalaSnippet().value\n  val javaSnippetString = JavaSnippet.data\n  val scriptSnippetString = snippet.script\n  val pipedInputString = stdin.piped\n  val ondiskScriptString = ondisk.script\n  println(s\"Output: $scalaSnippetString $javaSnippetString $scriptSnippetString $pipedInputString\")\n}\n```\n\n```scala title=ondisk.sc\ndef script = \"on-disk-script\"\n```\n\n```bash\necho 'def piped = \"piped-script\"'|scala-cli . _.sc --scala-snippet 'case class ScalaSnippet(value: String = \"scala-snippet\")' --java-snippet 'public class JavaSnippet { public static String data = \"java-snippet\"; }' --script-snippet 'def script = \"script-snippet\"'\n```\n\n```text\nOutput: scala-snippet java-snippet script-snippet piped-script\n```\n\n</ChainedSnippets>\n\n## Referring to code from a snippet\n\nWhen referring to code coming from a script snippet passed with `--script-snippet` (or `-e`), you use its wrapper\nname: `snippet`\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --scala-snippet '@main def main() = println(snippet.hello)' --script-snippet 'def hello: String = \"Hello\"'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nWhen referring to code coming from multiple script snippets, you use their wrapper names according to the order they\nwere passed (starting from 0 for the first script snippet): `snippet${snippetNumber}`. The `snippetNumber` is omitted\nfor the first script snippet (0). In other words, the first passed snippet is just `snippet`, the second is `snippet1`,\nthen `snippet2` and so on, as in the example:\n\n<ChainedSnippets>\n\n```bash\nscala-cli run --scala-snippet '@main def main() = println(s\"${snippet.hello} ${snippet1.world}${snippet2.exclamation}\")' --script-snippet 'def hello: String = \"Hello\"' --script-snippet 'def world: String = \"world\"' --script-snippet 'def exclamation: String = \"!\"'\n```\n\n```text\nHello world!\n```\n\n</ChainedSnippets>\n\nThis is similar to how you refer to code from piped scripts through their wrapper name (`stdin`), more on which can be\nfound in [the scripts guide](../scripting/scripts.md).\n\nIn fact, you can refer to both kinds of scripts at one time, just keep in mind that you need to pick which script is to\nactually be run with the `--main-class` option when multiple scripts are present on the classpath (and no non-script\nmain class was passed).\n\n<ChainedSnippets>\n\n```scala title=ondisk.sc\nprintln(s\"${stdin.hello} ${snippet.world}${snippet1.exclamation}\")\n```\n\n```bash\necho 'def hello = \"Hello\"' | scala-cli _.sc ondisk.sc -e 'def world = \"world\"' -e 'def exclamation = \"!\"' --main-class ondisk_sc\n```\n\n```text\nHello world!\n```\n\n</ChainedSnippets>\n\nWhen in doubt on what main classes are available on the classpath, you can always refer to the output\nof `--list-main-classes`\n\n<ChainedSnippets>\n\n```bash\necho 'def hello = \"Hello\"' | scala-cli _.sc ondisk.sc -e 'def world = \"world\"' -e 'def exclamation = \"!\"' --list-main-classes\n```\n\n```text\nondisk_sc snippet_sc stdin_sc\n```\n\n</ChainedSnippets>\n"
  },
  {
    "path": "website/docs/guides/advanced/verbosity.md",
    "content": "---\ntitle: Verbosity\nsidebar_position: 44\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nLogging in Scala CLI can be controlled in a number of ways.\n\n```scala title=Hello.sc\nprintln(\"Hello\")\n```\n\nLogs, warnings and errors will be printed by default.\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.sc\n```\n\n```text  \nCompiling project (Scala 3.2.2, JVM)\nCompiled project (Scala 3.2.2, JVM)\nHello\n```\n\n</ChainedSnippets>\n\n## Silencing logs with `-q`\n\nAll logs except for errors can be silenced by passing the `-q` option.\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.sc -q\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n## Increasing verbosity with `-v`\n\nYou can increase verbosity by passing the `-v` option, to print debugging logs or gain extra context.\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.sc -v\n```\n\n```text\nCompiling project (Scala 3.2.2, JVM)\nCompiled project (Scala 3.2.2, JVM)\nRunning ~/Library/Java/JavaVirtualMachines/corretto-17.0.5/Contents/Home/bin/java -cp ~/IdeaProjects/scala-cli-tests/hello1/.scala-build/project_853f6d1dbb-28a878fa14/classes/main:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.2.2/scala3-library_3-3.2.2.jar:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar Hello_sc\nHello\n```\n\n</ChainedSnippets>\n\nYou can increase verbosity even further by passing the `-v` option multiple times.\n\n<ChainedSnippets>\n\n```bash\nscala-cli Hello.sc -v -v\n```\n\n```text\nFetching List(Dependency(org.scala-lang:scala3-compiler_3, 3.2.2, Configuration(), Set(), Publication(, Type(), Extension(), Classifier()), false, true)), adding List(IvyRepository(Pattern(List(Const(file://~/Library/Caches/ScalaCli/local-repo/v0.2.0//), Var(organisation), Const(/), Var(module), Const(/), Opt(List(Const(scala_), Var(scalaVersion), Const(/))), Opt(List(Const(sbt_), Var(sbtVersion), Const(/))), Var(revision), Const(/), Var(type), Const(s/), Var(artifact), Opt(List(Const(-), Var(classifier))), Const(.), Var(ext))), None, None, true, true, true, false, None, true))\nFound 13 artifacts:\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.2.2/scala3-compiler_3-3.2.2.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-interfaces/3.2.2/scala3-interfaces-3.2.2.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.2.2/scala3-library_3-3.2.2.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/tasty-core_3/3.2.2/tasty-core_3-3.2.2.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/9.3.0-scala-1/scala-asm-9.3.0-scala-1.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar\n  ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar\n(...)\nHello\n\n```\n\n</ChainedSnippets>\n\n## Warnings suppression\n\nSome specific warning logs can be suppressed individually.\nThat can be done by passing an appropriate option or by setting the appropriate global configuration key.\n\n### Warnings about `using` directives spread in multiple files\n\n```scala title=Deps1.sc\n//> using dep com.lihaoyi::os-lib:0.11.3\n```\n\n```scala title=Deps2.sc\n//> using dep com.lihaoyi::pprint:0.9.0\n```\n\nIt is generally advised to not spread the `using` directives in multiple files, and put them in the\noptional `project.scala` configuration file.\nThe relevant warnings can be suppressed with the `--suppress-warning-directives-in-multiple-files` option.\n\n```bash\nscala-cli Deps1.sc Deps2.sc --suppress-warning-directives-in-multiple-files\n```\n\nAlternatively, the global config key `suppress-warning.directives-in-multiple-files` can be used.\n\n```bash\nscala-cli config suppress-warning.directives-in-multiple-files true\n```\n\n### Warnings about experimental features' usage\n\nUsing experimental features produces warnings, which can be suppressed with the `--suppress-experimental-warning`\noption.\n\n````bash\nscala-cli --power run --suppress-experimental-warning --markdown-snippet '# Markdown snippet\nwith a code block\n```scala\nprintln(\"Hello\")\n```'\n````\n\nAlternatively, the global config key `suppress-warning.experimental-features` can be used.\n\n```bash\nscala-cli config suppress-warning.experimental-features true\n```\n\n### Warnings about having outdated dependencies\n\n```scala title=OldDeps.sc\n//> using dep com.lihaoyi::pprint:0.9.0\n```\n\nDepending on outdated libraries produces warnings, which can be suppressed with\nthe `--suppress-outdated-dependency-warning` option.\n\n````bash\nscala-cli OldDeps.sc --suppress-outdated-dependency-warning\n````\n\nAlternatively, the global config key `suppress-warning.outdated-dependencies-files` can be used.\n\n```bash\nscala-cli config suppress-warning.outdated-dependencies-files true\n```\n"
  },
  {
    "path": "website/docs/guides/intro.md",
    "content": "---\ntitle: Index\nsidebar_position: 1\n---\n\n# Guides\n\nThis section covers some aspects of Scala CLI that apply across various commands.\nWe have divided the available guides into categories, so that it's easier to find a relevant topic.\n\nFor concrete recipes that show you how to use Scala CLI in particular situations, refer to\nthe [cookbooks section](../cookbooks/intro.md).\n\n## Introductory guides\n\nThese few guides are a good starting point when learning how to use Scala CLI.\n\n- [Configuration](introduction/configuration.md) - learn how to configure various options, and what configuration styles are best\n  for each use case\n- [Dependencies](introduction/dependencies.md) - learn how to define dependencies within a Scala CLI project.\n- [Updating dependencies](introduction/update-dependencies.md) - learn about how to keep your dependencies up-to-date automatically with\n  Scala CLI.\n- [`using` directives](introduction/using-directives.md) - Scala CLI’s syntax that lets you store configuration information\n  directly in source files\n- [IDE support](introduction/ide.md) - how to import and use Scala CLI-based projects in your favorite IDE.\n- [Scala Toolkit](introduction/toolkit.md) - how to use the [Scala Toolkit](https://github.com/scala/toolkit) dependency batch (and other dependency batches) in a Scala CLI project.\n- [Migrating from the old `scala` runner](introduction/old-runner-migration.md) - an in-depth look at all the differences between Scala CLI and the old `scala` script.\n\n## Scripting guides\n\nGuides on how to get started with Scala scripting with Scala CLI.\n\n- [Scripting guide](scripting/scripts.md) - covers how Scala CLI allows for powerful scripting with Scala.\n- [Shebang](scripting/shebang.md) - explains how to use the `shebang` sub-command in a script's shebang header.\n\n## Advanced guides\n\nLess introductory guides on specific topics.\n\n- [Scala.js](advanced/scala-js.md) and [Scala Native](advanced/scala-native.md) - learn how Scala CLI supports these non-JVM platforms\n- [Piping](advanced/piping.md) - covers how Scala CLI allows to work with piped sources.\n- [Snippets](advanced/snippets.md) - learn how to use command line snippets with Scala CLI.\n- [Verbosity](advanced/verbosity.md) - learn how to control logs verbosity in Scala CLI.\n- [Java properties](advanced/java-properties.md) - learn how to pass Java properties to Scala CLI.\n- [Internals](advanced/internals.md) - learn about how Scala CLI works under the hood.\n- [Custom Toolkit](advanced/custom-toolkit.md) - learn how to create custom toolkits\n\n## ⚡️ `--power` mode guides\n\n- [SBT and Mill export](power/sbt-mill.md) - learn how to convert your Scala CLI project into an SBT or Mill project (when\n  you need a more powerful build tool).\n- [proxies](power/proxy.md) - learn how to configure Scala CLI to work with proxies.\n- [Markdown](power/markdown.md) - learn how to work with `.md` sources.\n- [Python/ScalaPy](power/python.md) - learn how to use Python libraries in Scala CLI projects.\n- [offline mode](power/offline.md) - learn how to use Scala CLI in offline mode.\n- [repositories](power/repositories.md) - learn how to configure Scala CLI to work with custom repositories."
  },
  {
    "path": "website/docs/guides/introduction/_category_.json",
    "content": "{\n  \"label\": \"Introduction\",\n  \"position\": 1\n}\n"
  },
  {
    "path": "website/docs/guides/introduction/configuration.md",
    "content": "---\ntitle: Configuration\nsidebar_position: 2\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nScala CLI can be configured in two ways:\n- on the command-line\n- directly in `.scala` and `.sc` files\n\nParameters on the command line take precedence over parameters in sources.\nThat way, you can quickly override parameters from the command line.\n\n:::warning\nThe configuration options and syntax in `.scala` (and `.sc`) files is likely to evolve in the future.\n:::\n\n## Command-line\n\nPass `--help` to any sub-command of Scala CLI to list its options:\n```bash\nscala-cli --help\nscala-cli --power package --help\n```\n\nAs an example of command line configuration, one thing you can do with Scala CLI command line options is to specify the Scala version:\n```scala title=Test.scala\n@main def test = println(\"test\")\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --scala 3.0.0 Test.scala\n```\n\n```text\ntest\n```\n\n</ChainedSnippets>\n\nAnother thing you can do is to specify dependencies:\n\n<ChainedSnippets>\n\n```bash\nscala-cli --dependency org.typelevel::cats-core:2.10.0 Test.scala\n```\n\n```text\ntest\n```\n\n</ChainedSnippets>\n\nThe reference documentation lists [all of the available options](../../reference/cli-options.md).\n\n\n## In .scala and .sc files\n\nConfiguration information can also be put in `.scala` and `.sc` files using special imports, and the `using` directive.\n\n### Using directives\n\nScala CLI can be configured inside `.scala` files.\nThis is achieved by specifying `using` directives inside comments at the top of a `.scala` file, \nbefore any `package` or `import` statement:\n\n```scala compile\n//> using scala 2.13\n//> using platform scala-js\n//> using options -Xasync\n\n// package and import statements follow here ...\n```\n\nThe reference documentation lists [all available using directives](/docs/reference/directives.md#using-directives).\n\nAlso, there are some directives which only target tests, like `using test.dep`. \nThose can be useful when defining configuration specific to your test runs.\n\n```scala compile\n//> using test.dep com.lihaoyi::utest::0.8.4\n```\n\nMore details can be found in the [`using` directives guide](using-directives.md#directives-with-a-test-scope-equivalent).\n\n### Special imports\n\nDependencies can be added right from `.scala` and `.sc` files with [`using` directives](#using-directives):\n\n```scala compile\n//> using dep com.lihaoyi::upickle::4.0.2\n//> using dep com.lihaoyi::pprint::0.9.0\nimport ujson.*\n```\n\nBoth `import $ivy` and `import $dep` are not supported.\n"
  },
  {
    "path": "website/docs/guides/introduction/dependencies.md",
    "content": "---\ntitle: Managing dependencies\nsidebar_position: 3\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n# Managing dependencies\n\n## Dependency syntax\n\nDependencies are declared in Scala CLI according to the following format:\n\n```text\ngroupID:artifactID:revision\n```\n\nThis is similar to how you declare dependencies in SBT with the `%` character.\nFor example:\n\n```text\norg.scala-lang.modules:scala-parallel-collections_2.13:1.0.4\n```\n\nYou can also skip explicitly stating the Scala version in the artifact name by repeating the `:` character after\nthe `groupID` (similarly to how you can do the same with `%%` in SBT). This is just a shortcut, Scala CLI will still add\nthe Scala version for you when fetching the dependency. Also, this only applies to Scala dependencies.\n\n```text\norg.scala-lang.modules::scala-parallel-collections:1.0.4\n```\n\nJava and other non-scala dependencies follow the same syntax (without the `::` for implicit Scala version, of course).\nFor example:\n\n```text\norg.postgresql:postgresql:42.2.8\n```\n\n### Repositories\n\nSometimes dependencies are published into non-standard repositories, like nightly builds published to Sonatype Snapshots. Scala CLI can use additional maven and ivy repositories with the `repository` directive or `--repository` command line options:\n\n```scala\n//> using repository sonatype:snapshots\n```\n\nor\n\n```bash ignore\nscala-cli --repository \"https://maven-central.storage-download.googleapis.com/maven2\"\n```\n\n\n\nBoth directive and command line option accept predefined repository definitions (see below) or a URL of the root of Maven repository.\n\nRepositories can also be resolved from the `COURSIER_REPOSITORIES` environment variable, but this is not recommended (more in [Coursier documentation](https://get-coursier.io/docs/other-repositories)).\n\n\n#### Predefined repositories\n\n| predefined repository  | kind                                                                                                                                                        | description                                                                                                                                                                           |\n|------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| central                | Maven [(root)](https://repo1.maven.org/maven2)                                                                                                              | Used by default, default repository for most Scala libraries                                                                                                                          |\n| sonatype:snapshots     | Maven [(root)](https://oss.sonatype.org/content/repositories/snapshots)                                                                                     | Repositories where most Scala libraries publish its snapshots / nightly builds. Used when `X.nightly` is used as Scala version e.g. `3.1.nightly`.                                    |\n| sonatype-s01:snapshots | Maven [(root)](https://s01.oss.sonatype.org/content/repositories/snapshots)                                                                                 | This repository is similar to the `sonatype:snapshots` repository but is dedicated for accounts that were created after February 2021 and which publish snapshots of their libraries. |\n| snapshots              | Maven [(root)](https://oss.sonatype.org/content/repositories/snapshots) and Maven S01 [(root)](https://s01.oss.sonatype.org/content/repositories/snapshots) | An alias for `sonatype:snapshots` and `sonatype-s01:snapshots`.                                                                                                                      \n| ivy2local              | Ivy                                                                                                                                                         | Local ivy repository, used to publish things locally (e.g. by `publishLocal`). Localized in `<ivy-home>/local`, usually `<user-home>/.ivy/local`.                                     |\n| m2Local                | Maven                                                                                                                                                       | Local maven repository, localized in `<user-home>/.m2/repository`                                                                                                                     |\n| jitpack | Maven | jitpack supports github repo as dependency. Syntax is `using repository \"jitpack\"`\n\nScala CLI delegates parsing of predefined repositories to Coursier and full details can be obtained from Coursier source code ([here](https://github.com/coursier/coursier/blob/2444eebcc151e0f6927e269137e8737c1f31cbe2/modules/coursier/jvm/src/main/scala/coursier/LocalRepositories.scala) and [here](https://github.com/coursier/coursier/blob/2444eebcc151e0f6927e269137e8737c1f31cbe2/modules/coursier/shared/src/main/scala/coursier/internal/SharedRepositoryParser.scala))\n\n### Excluding Transitive Dependencies\n\nTo exclude a transitive dependency from a Scala CLI project use the `exclude` parameter:\n\n- `exclude=org%%name` - for Scala modules\n- `exclude=org%name` - for Java modules\n\nIt requires passing the organization and module name of the dependency to be excluded. For example, let's say you have\nthe following Scala code:\n\n```scala compile\n//> using dep com.lihaoyi::pprint:0.9.0\nobject Main extends App {\n  println(\"Hello\")\n}\n```\n\nIf you want to compile it with the `pprint` library but exclude its `sourcecode` dependency, you can use\nthe `exclude` parameter as follows:\n\n```scala compile\n//> using dep com.lihaoyi::pprint:0.9.0,exclude=com.lihaoyi%%sourcecode\nobject Main extends App {\n  println(\"Hello\")\n}\n```\n\nTo exclude Scala modules, you can also use a single `%` but with the full name of the module name, like this:\n\n```scala compile\n//> using dep com.lihaoyi::pprint:0.9.0,exclude=com.lihaoyi%sourcecode_3\nobject Main extends App {\n  println(\"Hello\")\n}\n```\n\n### Dependency classifiers\n\nTo specify a classifier of a dependency in a Scala CLI project, use the `classifier` parameter:\n\n- `classifier={classifier_name}`\n\nIf you want to use the `pytorch` dependency with the classifier `linux-x86_64`, use the `classifier` parameter as\nfollows:\n\n```scala compile\n//> using dep org.bytedeco:pytorch:2.5.1-1.5.11,classifier=linux-x86_64\nobject Main extends App {\n  println(\"Hello\")\n}\n```\n\n:::caution\nWhen using the `classifier`, `exclude` or others parameters, it is necessary to wrap the value of dependency within double quotes `\"`.\nIf this is omitted, Scala CLI treats these parameters as dependencies, resulting in a dependency parsing error.\n:::\n\n### Test dependencies\n\nIt is possible to declare dependencies limited to the test scope with the `using test.dep` directive.\n\n```scala compile\n//> using test.dep org.scalameta::munit::1.0.2\n```\n\nMore details can be found in\nthe [`using` directives guide](using-directives.md#directives-with-a-test-scope-equivalent).\n\n### Compile-Only dependencies\n\nIt is possible to declare dependencies that are only used during the compilation phase with the `using compileOnly.dep` directive.\n\n```scala compile\n//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\n```\n\nMore details can be found in\nthe [`compile` command guide](../../commands/compile.md#compile-only-dependencies).\n\n## Specifying dependencies from the command line\n\nYou can add dependencies on the command line, with the `--dependency` option:\n\n```scala title=Sample.sc\nprintln(\"Hello\")\n```\n\n```bash\nscala-cli compile Sample.sc \\\n  --dependency org.scala-lang.modules::scala-parallel-collections:1.0.4\n```\n\nYou can also add a URL fallback for a JAR dependency, if it can't be fetched otherwise:\n\n```bash ignore\nscala-cli compile Sample.sc \\\n  --dependency \"org::name::version,url=https://url-to-the-jar\"\n```\n\nNote that `--dependency` is only meant as a convenience. You should favor adding dependencies in the sources themselves\nvia [using directives](configuration.md#special-imports). However, the `--dependency` CLI option takes\nprecedence over `using` directives, so it can be used to override a `using` directive, such as when you want to work\nwith a different dependency version.\n\nYou can also add repositories on the command-line, via `--repository` or `//> using repos`\n\n```bash ignore\nscala-cli compile Sample.sc \\\n  --dependency com.pany::util:33.1.0 --repo https://artifacts.pany.com/maven\n```\n\nLastly, you can also add simple JAR files as dependencies with `--jar`:\n\n```bash ignore\nscala-cli compile Sample.sc --jar /path/to/library.jar\n```\n\n## Adding local JARs as dependencies\nYou can pass local JARs from the command line with the `--extra-jar` option:\n\n```bash ignore\nscala-cli compile Sample.sc \\\n  --extra-jar \"./path/to/custom.jar\"\n```\n\nLocal sources JARs can also be passed in a similar manner:\n```bash ignore\nscala-cli compile Sample.sc \\\n  --extra-source-jar \"./path/to/custom-sources.jar\"\n```\n\nBoth can be handled with the appropriate `using` directives, too:\n\n```scala\n//> using jar ./path/to/custom.jar\n//> using sourceJar ./path/to/custom-sources.jar\n```\n\n:::caution\nLocal JARs with the `*-sources.jar` suffix are assumed to be sources JARs and are treated as such.\n:::\n"
  },
  {
    "path": "website/docs/guides/introduction/ide.md",
    "content": "---\ntitle: IDE support\nsidebar_position: 6\n---\n\nScala CLI currently integrates a build server using the [BSP protocol](https://build-server-protocol.github.io/).\nAt this moment Scala CLI is not automatically detected by IDEs, so we need to\nuse [Build Server Discovery](https://build-server-protocol.github.io/docs/server-discovery.html) from BSP protocol to\ngenerate a connection details file (`.bsp/scala-cli.json`).\n\n:::note\nIf none of these commands were run:\n\n- `compile`\n- `run`\n- `test`\n- `setup-ide`\n\nor a previously-generated connection detail file was deleted, your IDE will *not* use Scala CLI to configure your\nworkspace. (Although there are ongoing efforts to improve that situation.)\n\nIn this case, just run one of the commands above to recreate the connection details file.\n:::\n\nSince Scala CLI has a command-line-first approach, this is reflected in its IDE integration.\nBy default, Scala CLI stores options passed to the last `compile`, `run`, or `test` command, and uses those options to\nconfigure the IDE.\n\nFor more control we also expose the [`setup-ide` command](../../commands/setup-ide.md), which lets you fine-tune the\noptions that are passed to the IDE.\n\nBut note that once `setup-ide` is used, Scala CLI does not update the configuration based on latest command.\nTo enable automatic updates again, remove the `.bsp` directory and run `compile`, `run`, or `test` to recreate the\nconnection details file.\n\n## Specific IDEs supporting Scala CLI\n\nScala CLI has been tested with two main Scala IDEs:\n\n- [Metals](https://scalameta.org/metals/), which is an LSP server for Scala, and is used\n  with [Visual Studio Code](https://code.visualstudio.com/), [Vim](https://www.vim.org/) and many other editors\n- [IntelliJ IDEA](https://www.jetbrains.com/idea/), with\n  the [Scala Plugin](https://confluence.jetbrains.com/display/SCA/Scala+Plugin+for+IntelliJ+IDEA?_ga=2.54176744.1963952405.1634470110-410935139.1631638301)\n  installed\n\nIn an ideal world we would replace the rest of this guide with something along the lines of, “Scala CLI works within\nIDEs above as you would expect.” However, mainly due to how fresh Scala CLI is, and also due to our radical approach to\nthe project structure, using a Scala CLI project with your favourite IDE may not be as amazing as we would like. (That\nbeing said, proper IDE integration is our top priority at this moment!)\n\n### VS Code with Metals\n\nCheck the cookbook on [how to set up a Scala CLI project in VSCode with Metals](../../cookbooks/ide/vscode.md).\n\n### IntelliJ\n\nCookbooks on how to work with IntelliJ:\n\n- [set up a simple Scala CLI project in IDEA IntelliJ](../../cookbooks/ide/intellij.md)\n- [set up a Scala CLI project in IntelliJ alongside an existing SBT project](../../cookbooks/ide/intellij-sbt-with-bsp.md)\n- [set up multiple Scala CLI projects in IDEA IntelliJ as separate modules](../../cookbooks/ide/intellij-multi-bsp.md)\n\n## Directories vs single files when working with an IDE\n\nWhen working with Scala CLI in an IDE, it is generally suggested to use directories rather than single files.\n\n```shell\nscala-cli setup-ide some-directory\n```\n\nOf course, nothing is stopping you from working with whatever you like as normal,\nbut please do keep in mind that the IDE will import the exact build that you have set up,\nwithout second-guessing the user's intentions. In many IDEs, IDEA IntelliJ & Visual Studio Code included,\neverything within a given project root directory is at least implicitly treated as\na part of the project (and probably shown as part of your project structure).\n\nThis means that when you pass just a single source file to Scala CLI like this:\n\n```shell\nscala-cli setup-ide some-directory/A.scala\n```\n\nIf you open its surrounding directory as a project, any other files present in that directory will be visible\nin your IDE project's structure, but they will not be included in your builds.\n\nSo if you want to include another file in your build, let's say `some-directory/B.scala`\nalongside the previously configured `some-directory/A.scala`, it is probably not enough\nto create the file within the same directory in your IDE.\n\nWhat you need to do instead is add it to your build with Scala CLI from the command line:\n\n```shell\nscala-cli setup-ide some-directory/A.scala some-directory/B.scala\n```\n\nThere, now both `A.scala` and `B.scala` should be included in your builds when the IDE picks up the new structure.\n\nStill, if you want to add/remove files like this a lot while working in an IDE,\nit may be a lot simpler to work on the whole directory instead:\n\n```shell\ncd some-directory\nscala-cli setup-ide .\n```\n\nThat way all the contents of `some-directory` will be treated as a part of the project as you go,\nwithout the need to jump into the command line whenever you create a new file.\n\n## Remote and virtual inputs\n\nDo note that IDEs do not yet support working with Scala CLI's remote and virtual inputs. That includes:\n\n- [piped sources](../advanced/piping.md),\n- URLs and [GitHub gists](../../cookbooks/introduction/gists.md),\n- [code snippets](../advanced/snippets.md).\n\nBeyond that, IDE support for some non-standard (like `.c` and `.h` resources used\nwith [Scala Native](../advanced/scala-native.md)) and experimental inputs (like i.e. [`.md` sources](../power/markdown.md)) may not yet\nbe on par with on-disk Scala and Java source files.\n"
  },
  {
    "path": "website/docs/guides/introduction/old-runner-migration.md",
    "content": "---\ntitle: Migrating from the old Scala runner\nsidebar_position: 15\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n# Migrating from the old `scala` runner\n\nAs of [SIP-46](https://github.com/scala/improvement-proposals/pull/46), Scala CLI has been accepted as the new `scala`\ncommand.\n\nIn that context, the purpose of this guide is to highlight the key differences between the old `scala` script\nand Scala CLI to make the migration as smooth as possible for users.\n\n:::note\nIf you are looking for an overview of Scala CLI basics, refer to [the Basics page](../../commands/basics.md).\nIf you merely want to get started with Scala CLI, you might want to first look\nat [the Getting started page](../../getting_started.md).\n:::\n\n## How to start using Scala CLI as the new `scala` command?\n\nRefer to the [official instructions for installing Scala](https://www.scala-lang.org/download/). \nScala CLI is available as the `scala` command alongside the Scala distribution in Scala 3.5.0 and later.\n\n## Can I still use the old `scala` runner with Scala 3.5+?\n\nEven though its usage has been deprecated, it was still available under the `scala_legacy` command until Scala 3.7.4. \nIt has been removed since Scala 3.8.0.\n\n```bash\nscala_legacy -version\n# [warning] MainGenericRunner class is deprecated since Scala 3.5.0, and Scala CLI features will not work.\n# [warning] Please be sure to update to the Scala CLI launcher to use the new features.\n# [warning] Check the Scala 3.5.0 release notes to troubleshoot your installation.\n# [warning] The MainGenericRunner class and 'legacy_scala' command have been deprecated for removal in Scala 3.8.0.\n# [warning] Please be sure to migrate to the scala command (Scala CLI).\n# Scala code runner version 3.7.4 -- Copyright 2002-2025, LAMP/EPFL\n```\n\n## How has the passing of arguments been changed from the old `scala` runner to Scala CLI?\n\nLet us take a closer look on how the old runner handled arguments when compared to Scala CLI.\n\n### The old ways\n\nIn the old runner, the first argument was treated as the input source, while the second and following arguments\nwere considered program arguments.\n\n```scala title=Source.scala\n@main def main(args: String*): Unit = println(args.mkString(\" \"))\n```\n\n```bash\nscala_legacy Source.scala programArg1 programArg2\n```\n\nSince everything after the first argument had to be arbitrarily read as a program argument, regardless of format, all\nrunner options had to be passed before the source input.\n\n```bash\nscala_legacy -save Source.scala programArg1 programArg2\n```\n\n### The ways of Scala CLI\n\nWith Scala CLI's default way of handling arguments, inputs and program arguments have to be\ndivided by `--`. There is no limit for the number of either.\n\n```scala title=Source2.scala\ndef placeholder = println(\"Example extra source\")\n```\n\n```bash ignore\nscala Source.scala Source2.scala -- programArg1 programArg2\n```\n\nAdditionally, a Scala CLI sub-command can be passed before the inputs section.\nFor example, to call the above example specifying the `run` sub-command explicitly, pass it like this:\n\n```bash\nscala run Source.scala Source2.scala -- programArg1 programArg2\n```\n\nMore on sub-commands can be found [here](../../commands/basics.md).\n\nRunner options can be passed on whatever position in the inputs section (before `--`).\nFor example, all the following examples are correct ways to specify the Scala version explicitly as `3.2`\n\n```bash\nscala -S 3.2 Source.scala Source2.scala -- programArg1 programArg2\nscala Source.scala -S 3.2 Source2.scala -- programArg1 programArg2\nscala Source.scala Source2.scala -S 3.2 -- programArg1 programArg2\n```\n\n:::note\nThe exception to this rule are the launcher options, like `--cli-version` or `--cli-scala-version`.\nThose have to be passed before the inputs section (before any source inputs).\n\nFor example, to explicitly specify the launcher should run Scala CLI `v1.5.0`, pass it like this:\n\n```bash\nscala --cli-version 1.5.0 Source.scala Source2.scala -- programArg1 programArg2\n```\n\nAlso, if a Scala CLI sub-command is being passed explicitly, all launcher options have to be passed before the\nsub-command.\n\nFor example, to call [the `package` sub-command](../../commands/package.md) using the nightly CLI version, do it like this:\n\n```bash ignore\nscala --cli-version nightly package --help\n```\n\n:::\n\n### The Scala CLI `shebang` sub-command\n\nTo provide better support for shebang scripts, Scala CLI\nhas [a dedicated `shebang` sub-command](../../commands/shebang.md), which handles arguments similarly to the old `scala`\nscript.\n\n```bash\nscala shebang Source.scala programArg1 programArg2\n```\n\nThe purpose of the `shebang` sub-command is essentially to only be used in a shebang header (more\ndetails on that can be found [in a later section of this guide](#example-shebang-script-with-scala-cli) or in the\nseparate [shebang scripts' guide](../scripting/shebang.md)), but nothing is really stopping you from using it from the command\nline, if you're used to how the old `scala` runner handled arguments. Just bear in mind that it is not the intended user\nexperience.\n\n## How are the old `scala` runner options supported?\n\nFor backwards compatibility's sake, Scala CLI accepts all the old `scala` runner options, although many of them have\nbeen deprecated and are no longer supported in the new runner. This includes accepting all the Scala `2.13.x` and `3.x`\nrespective runners' specific options.\n\n### Fully supported old `scala` runner options\n\nThe following old `scala` runner options are fully supported by Scala CLI, meaning that they deliver similar or expanded\nfunctionalities with backwards-compatible syntax:\n\n- `-e`, which is an alias for Scala CLI's `--execute-script` and a close synonym\n  for [`--script-snippet`](../advanced/snippets.md#examples)\n- `-v` / `-verbose` / `--verbose`, which can be passed multiple times with Scala CLI, increasing the verbosity\n- `-cp` / `-classpath` / `--class-path`, which adds compiled classes and jars to the class path\n- `-version` / `--version`, which prints the currently run Scala CLI [version information](../../commands/version.md)\n- `-with-compiler`, which adds the Scala compiler dependency to the Scala CLI project\n- Scala compiler options (with some requiring to be passed with `-O`, more info\n  in [the section below](#scala-compiler-options))\n- `-J<arg>` Java options\n- `-Dname=prop` Java properties\n\n### Old `scala` runner options which have a different meaning in Scala CLI\n\nThe following old `scala` runner options not only are not supported with their old functionalities, but have a different\nmeaning in Scala CLI:\n\n- `-i`, which is now an alias for Scala CLI's [`--interactive` mode](../../reference/cli-options.md#--interactive)\n- `-h` / `-help`\n    - in the old Scala `2.13.x` `scala` runner, it used to print the help of the runner\n    - in the old Scala `3.x` `scala` runner however, it used to print the Scala compiler help instead\n    - Scala CLI takes an approach similar to the old Scala `2.13.x` runner, and it prints Scala CLI help\n    - to view the Scala compiler help with Scala CLI, pass\n      the [--scalac-help](../../commands/compile.md#scala-compiler-help) option instead\n\n### Deprecated and unsupported old `scala` runner options\n\nThe following old `scala` runner options have been deprecated and even though they are accepted by Scala CLI (passing\nthem will not cause an error), they are ignored with an appropriate warning:\n\n- `-save`, refer to [the `package` sub-command](../../commands/package.md#library-jars) on how to package a Scala CLI\n  project to a JAR\n- `-nosave`, a JAR file is now never saved unless [the `package` sub-command](../../commands/package.md) is called\n- `-howtorun` / `--how-to-run`\n    - Scala CLI assumes how a file is to be run based on its file extension (and optionally its shebang header). This\n      cannot be overridden with a command line option, so ensure your inputs use the correct file extension or have\n      the [shebang header](#example-shebang-script-with-scala-cli) defined. This is sort of the equivalent of the\n      old `-howtorun guess`.\n    - To run the `REPL`, refer to [the `repl` sub-command](../../commands/repl.md)\n    - This option has been largely replaced with Scala CLI's [sub-commands](../../commands/basics.md)\n- `-I`, to preload the extra files for the `REPL`, try passing them as inputs\n  for [the repl sub-command](../../commands/repl.md)\n- `-nc` / `nocompdaemon`, the underlying script runner class can no longer be picked explicitly, as with the old `scala`\n  runner\n- `-run` - Scala CLI does not support explicitly forcing the old run mode. Just pass your sources as inputs and ensure\n  they are in the correct format and extension.\n\n### Scala compiler options\n\nAll compiler options are supported when passed with the `--scalac-option` flag (or the `-O` alias for short).\nHowever, many compiler options can also be passed directly.\nFor more information, refer\nto [the Scala compiler options section of the `compile` sub-command doc](../../commands/compile.md#scala-compiler-options).\n\n## How does Scala CLI detect if it's running a script or a main method?\n\nTo answer this question, some disambiguation is necessary.\nThe most important thing to note is that this has been handled differently by the 2 old `scala` runners (for\nScala `2.13.x` and for `3.x`), so a\nconsistent behaviour hasn't really been established before Scala CLI.\n\nThe Scala `2.13.x` old `scala` runner was the most flexible, automatically detecting if what is being run is a script or\nan\nobject based on the source contents. This automatic detection was also possible to be overridden with the `-howtorun`\nrunner option (which has been\ndeprecated and is not supported in Scala CLI,\nas [noted in an earlier section](#deprecated-and-unsupported-old-scala-runner-options)).\nThis also means that the `2.13.x` old `scala` runner did not really care about file extensions much.\n\nIn contrast, the Scala `3.x` old `scala` runner always expects to find a main method, potentially but not necessarily\nusing [the Scala 3 idiomatic `@main` annotation](https://docs.scala-lang.org/scala3/book/methods-main-methods.html).\nThis means that the Scala `3.x` runner respected main methods defined in `.sc` files, but did not support script\nsyntax (top level definitions with no explicit main method).\n\nScala CLI's approach is perhaps the most restrictive here.\nIt accepts explicitly defined main methods in `.scala` sources and script syntax in `.sc` sources, without any\nadditional flexibility.\n\nThe only exception would be files with no file extension, but with a shebang header, ran with the `shebang` sub-command.\nThose are always treated as scripts (more details about this can be\nfound [in [the shebang scripts' guide](../scripting/shebang.md)]).\n\nNow, to give some examples.\n\n### Main class in a `.scala` input\n\nOf course, the simplest case is putting a main class into a `.scala` source, which is supported by both of the old\nrunners and by Scala CLI.\n\n```scala title=Main.scala\nobject Main {\n  def main(args: Array[String]): Unit = println(args.mkString(\" \"))\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala_legacy Main.scala Hello world\n```\n\n```bash\nscala Main.scala -- Hello world\n```\n\n```text\nHello world\n```\n\n</ChainedSnippets>\n\n### Main class in a `.sc` input\n\n```scala title=main-in-script.sc\nobject Main {\n  def main(args: Array[String]): Unit = println(args.mkString(\" \"))\n}\n```\n\nThis case has been supported by both of the old `scala` runners, but is not supported by Scala CLI, which expects a\nscript in a `.sc` input and wraps its contents in a main class of its own, not inspecting further for a nested one.\nIn other words, when explicitly declaring a main class when working with Scala CLI, you have to do it in a `.scala`\nfile.\n\n```bash\nscala main-in-script.sc -- Hello world \n# no output will be printed\n```\n\nRunning such an `.sc` file will not fail by the way, but neither will it print any output, since the appropriate method\nhasn't been called explicitly in the script.\n\n### Script syntax in an `.sc` file\n\n```scala title=script.sc\nprintln(args.mkString(\" \"))\n```\n\nThis syntax is supported by the old Scala `2.13.x` runner, but **not** by the old Scala `3.x` one.\nThe Scala `3.x` runner does not allow for top level definitions without an explicit main class.\n\nHowever, it is supported by Scala CLI.\n\n<ChainedSnippets>\n\n```bash\nscala script.sc -- Hello world\n```\n\n```text\nHello world\n```\n\n</ChainedSnippets>\n\n### Script syntax in a `.scala` file\n\nNow for the inverted case, where script-style top level definitions are put in a `.scala` input.\n\n```scala title=script.scala\nprintln(args.mkString(\" \"))\n```\n\nThis has actually been supported by the old Scala `2.13.x` runner.\nHowever, both the old Scala `3.x` runner as well as Scala CLI do not support it.\n\n<ChainedSnippets>\n\n```bash fail\nscala script.scala -- Hello world\n```\n\n```text\n[error] ./ScriptInScala.scala:1:1\n[error] Illegal start of toplevel definition\n[error] println(args.mkString(\" \"))\n[error] ^^^^^^^\nError compiling project (Scala 3.2.2, JVM)\nCompilation failed\n```\n\n</ChainedSnippets>\n\n### Inputs with no extension\n\n```scala title=no-extension-script\nprintln(args.mkString(\" \"))\n```\n\n```scala title=no-extension-main-class\nobject Main {\n  def main(args: Array[String]): Unit = println(args.mkString(\" \"))\n}\n```\n\nFiles with no extensions have been supported in the `2.13.x` old runner, but not in `3.x`.\n\nScript syntax in files with no extension (or with extensions not indicating other kinds of sources, like `.java`) are\nsupported in Scala CLI via the `shebang` sub-command (and not otherwise).\nHowever, a shebang header is necessary. An example is given\nin [a later section of this guide](#example-shebang-script-with-scala-cli).\n\n## How to migrate scripts with the old `scala` runner in the shebang header to Scala CLI?\n\nAs described\nin [an earlier section of this guide](#how-has-the-passing-of-arguments-been-changed-from-the-old-scala-runner-to-scala-cli),\nthe way the old `scala` runner handles arguments differs from Scala CLI.\n\nThe old `scala` script accepted arguments with syntax making it easy to use it in a shebang header.\nThat is, all arguments starting with the second were treated as program args, rather than input sources.\nThis is in contrast with the Scala CLI default way of handling arguments, where inputs and program arguments have to be\ndivided by `--`.\n\n```bash\nscala Source.scala Source2.scala -- programArg1 programArg2\n```\n\nTo better support shebang scripts, Scala CLI has a dedicated `shebang` sub-command, which handles arguments similarly to\nthe old `scala` script.\n\n```bash\nscala shebang Source.scala programArg1 programArg2\n```\n\nFor more concrete examples on how to change the shebang header in your existing scripts, look below.\n\n### Example shebang script with the Scala `2.13.x` old `scala` runner\n\nThis is how an example shebang script could have looked like for the old `scala` runner with Scala `2.13.x`\n\n```scala compile title=old-scala-shebang-213.sc\n#!/usr/bin/env scala\nprintln(\"Args: \" + args.mkString(\" \"))\n```\n\n### Example shebang script with the Scala `3.x` old `scala` runner\n\nThis in turn is the Scala `3.x` equivalent for its own old `scala` runner.\n\n```scala title=old-scala-shebang-3.sc\n#!/usr/bin/env scala\n@main def main(args: String*): Unit = println(\"Args: \" + args.mkString(\" \"))\n```\n\n### Example shebang script with Scala CLI\n\nThis is an example of how a Scala CLI script with a shebang header looks like.\n\n```scala compile title=scala-cli-shebang.sc\n#!/usr/bin/env -S scala-cli shebang\n  println(\"Args: \" + args.mkString(\" \"))\n```\n\nThe example above refers `scala-cli`, as per the current default Scala CLI distribution.\nIf you have Scala CLI installed as `scala`, then that should be changed to the following:\n\n```scala compile title=scala-cli-as-scala-shebang.sc\n#!/usr/bin/env -S scala shebang\nprintln(\"Args: \" + args.mkString(\" \"))\n```\n\nFor more information about the `shebang` sub-command, refer to [the appropriate doc](../../commands/shebang.md).\nFor more details on how to use Scala CLI in shebang scripts, refer to [the relevant guide](../scripting/shebang.md).\n\n## How to run a main class from compiled sources with Scala CLI?\n\nWith the old `scala` runner, running a main class from compiled sources was as simple as passing the main class name \nas an argument. The old runner would then assume that the current working directory is to be added to the classpath and could \nimplicitly run any compiled class files it would find.\n\n```scala title=hello.scala\n@main def hello = println(\"Hello\")\n```\n\nThis syntax has been dropped and is no longer supported with the new `scala` runner.\n```bash\nscalac hello.scala\nscala_legacy hello # NOTE: this syntax is not supported by Scala CLI\n# Hello\n```\n\nWith Scala CLI, all inputs have to be passed explicitly, so any compiled classes in the current working directory \nwould be ignored unless passed explicitly. \n```bash clean\nscalac hello.scala\nscala run -cp .\n# Hello\n```\n\n:::note\nIf only the classpath is passed with `-cp`, then the `run` sub-command can't be skipped, as otherwise Scala CLI \nwill default to the REPL (as there are no explicit source file inputs present).\n\n```bash ignore\nscalac hello.scala\nscala -cp .\n# Welcome to Scala 3.5.0 (17, Java OpenJDK 64-Bit Server VM).\n# Type in expressions for evaluation. Or try :help.\n#                                                                                                                  \n# scala> \n```\n:::\n\nIt is possible to explicitly specify the main class to be run (for example, if there are multiple main classes \nin the build). The `run` sub-command becomes optional then, as passing `-M` indicates the intention to run something.\n```bash clean\nscalac hello.scala\nscala -cp . -M hello\n# Hello\n```\n\n:::note\nIf you want to compile your sources with a separate command, and then run them later, you can also do it \nwith the `compile` sub-command, rather than the `scalac` script.\n\nYou don't have to specify the class files location, Scala CLI won't recompile them if they are up to date.\n```bash clean\nscala compile hello.scala\nscala hello.scala\n# Hello\n```\n\nAlternatively, you can also specify the location for the compiled classes explicitly, and then add them \nto the classpath, as you would with `scalac`.\n```bash\nscala compile hello.scala -d compiled_classes\nscala run -cp compiled_classes\n# Hello\n```\n:::\n\n"
  },
  {
    "path": "website/docs/guides/introduction/toolkit.md",
    "content": "---\ntitle: Scala Toolkit\nsidebar_position: 7\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n:::caution\nNewer versions of toolkits dropped support for Scala 2.12\n:::\n\n# Scala Toolkit\n\n[Scala Toolkit](https://github.com/scala/toolkit) is an ongoing\neffort by Scala Center and VirtusLab to compose a set of approachable libraries to solve\neveryday problems.\n\nYou can easily add it to your Scala CLI project with the `--toolkit` option:\n\n<ChainedSnippets>\n\n```scala title=UseOsLib.sc\nprintln(os.pwd)\n```\n\n```bash\nscala-cli UseOsLib.sc --toolkit default\n```\n\n</ChainedSnippets>\n\nSimilarly, you can achieve the same with the `using toolkit` directive:\n\n```scala compile\n//> using toolkit default\n@main def printPwd: Unit = println(os.pwd)\n```\n\n## Scala Toolkit and tests\n\nAdding Scala Toolkit to your project effectively adds 2 dependencies to your classpath:\n\n- `org.scala-lang:toolkit:<version>` for your main scope (usable everywhere in the project);\n- `org.scala-lang:toolkit-test:<version>` for your test scope (usable only in tests).\n\n`toolkit-test` includes a batch of libraries only relevant for testing (like i.e. `munit`), which you probably don't\nwant on your main scope\nclass path (which is why Scala CLI won't put it there).\nAnd so, you can use it like this:\n\n<ChainedSnippets>\n\n```scala title=Something.test.scala\n//> using toolkit default\nclass Something extends munit.FunSuite {\n  test(\"foo\") {\n    assert(true)\n  }\n}\n```\n\n```bash\nscala-cli test Something.test.scala\n```\n\n</ChainedSnippets>\n\nAlso, in case you only want Scala Toolkit to be added to the test scope (and not for the main scope in any capacity),\nyou can always use the `using test.toolkit` directive.\n\n<ChainedSnippets>\n\n```scala title=project.scala\n//> using test.toolkit default\n```\n\n```scala title=Another.test.scala\nclass Another extends munit.FunSuite {\n  test(\"foo\") {\n    assert(os.pwd.last.nonEmpty)\n  }\n}\n```\n\n```bash\nscala-cli test Another.test.scala project.scala\n```\n\n</ChainedSnippets>\n\nMore details about test scope directives can be found in\nthe [`using` directives guide](using-directives.md#directives-with-a-test-scope-equivalent).\n\n## Other toolkits\n\nScala CLI also supports adding other toolkits to your project in a similar manner. Those have to follow the same\nstructure of 2 dependencies with the names `toolkit` and `toolkit-test`.\nTo do so, you have to explicitly pass the organisation the toolkit was released under (or an alias if defined).\n\nFor example, to add the [Typelevel Toolkit](https://github.com/typelevel/toolkit) to your project, you can pass it with\nthe `--toolkit` option:\n\n<ChainedSnippets>\n\n```scala title=UseTypelevel.scala\nimport cats.effect.*\nimport fs2.io.file.Files\n\nobject Hello extends IOApp.Simple {\n  def run = Files[IO].currentWorkingDirectory.flatMap { cwd =>\n    IO.println(cwd.toString)\n  }\n}\n```\n\n```bash\nscala-cli UseTypelevel.scala --toolkit org.typelevel:default\nscala-cli UseTypelevel.scala --toolkit typelevel:default # typelevel has a shorter alias defined\n```\n\n</ChainedSnippets>\n\nSimilarly, you can achieve the same with the `using toolkit` directive:\n\n```scala compile\n//> using toolkit org.typelevel:default\n\nimport cats.effect.*\nimport fs2.io.file.Files\n\nobject Hello extends IOApp.Simple {\n  def run = Files[IO].currentWorkingDirectory.flatMap { cwd =>\n    IO.println(cwd.toString)\n  }\n}\n```\n\nOr with the alias:\n\n```scala compile\n//> using toolkit typelevel:default\n\nimport cats.effect.*\nimport fs2.io.file.Files\n\nobject Hello extends IOApp.Simple {\n  def run = Files[IO].currentWorkingDirectory.flatMap { cwd =>\n    IO.println(cwd.toString)\n  }\n}\n```\n\n\n\n\n"
  },
  {
    "path": "website/docs/guides/introduction/update-dependencies.md",
    "content": "---\ntitle: Updating dependencies\nsidebar_position: 4\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nTo check if dependencies in using directives are up-to-date, use `dependency-update` command:\n\n```scala title=Hello.scala\n//> using dep com.lihaoyi::os-lib:0.7.8\n//> using dep com.lihaoyi::utest:0.7.10\n\nobject Hello extends App {\n  println(\"Hello World\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power dependency-update Hello.scala\n```\n\n```text\nUpdates\n   * com.lihaoyi::os-lib:0.7.8 -> 0.11.3\n   * com.lihaoyi::utest:0.7.10 -> 0.8.4\nTo update all dependencies run: \n    scala-cli dependency-update --all\n```\n\n</ChainedSnippets>\n\nPassing `--all` to the `dependency-update` sub-command updates all dependencies in your sources.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power dependency-update Hello.scala --all\n```\n\n```text\nUpdated dependency to: com.lihaoyi::os-lib:0.11.3\nUpdated dependency to: com.lihaoyi::utest:0.8.4\n```\n\n</ChainedSnippets>\n\n"
  },
  {
    "path": "website/docs/guides/introduction/using-directives.md",
    "content": "---\ntitle: Using directives\nsidebar_position: 5\n---\n\nThe `using` directives mechanism lets you define configuration information within `.scala` source code files,\neliminating the need for build tools to define a dedicated configuration syntax.\n\n`using` directives are basically key-value pairs that let you provide multiple values to a single key. These directives\nneed to be put in comments with a special syntax. For instance, this command:\n\n```scala\n//> using foo bar baz\n```\n\n## Deprecated syntax\n\nAs a part of `0.0.x` series we experimented with different syntaxes for using directives. Based on feedback and\ndiscussions with the Scala compiler team, we decided to remove `@using` (using annotations), `// using` (using within\nplain comment) and `using` code directives. Those syntaxes will keep working in the `0.1.x` series and will be ignored starting from `1.0.x`.\n\n## Semantics\n\n`using` directives can be only declared **before any other Scala code**:\n\n```scala\n//> using scala 2.13\n//> using platform scala-js\n//> using options -Xasync\n\n// package statements, import statements and other code follows ...\n```\n\n`using` directives contribute settings to the whole compilation scope where a given `.scala` file is defined.\nThis means that a library or compiler option defined in one file applies to the whole application or test (depending on\nwhether the source file is a test, or not).\n\nThe only exceptions are `using target` directives, which only apply to the given file.\n`using target` is a marker to specify requirements for the file to be used (e.g. Scala version, platform, or scope).\n\n:::caution\nThe `using target` directives are an experimental feature, and may change in future versions of Scala CLI.\n:::\n\n**We believe that syntax similar to `using` directives should become a part of Scala in the future and will already be included within the Scala runner itself**\n\n## `using` directives in the Scala CLI\n\nBelow is a list of the most important `using` directives that Scala CLI supports. The full list can be found in\nthe [Reference section of this documentation](../../reference/directives.md).\n\n- `//> using scala <scala-version>` - defines version of Scala used\n- `//> using dep org::name:version` - defines dependency to a given\n  library [more in dedicated guide](../../guides/introduction/dependencies.md)\n- `//> using dep org:name:version`  - defines dependency to a given **java** library, note the `:` instead of `::`\n- `//> using dep org::name:version,url=url` - defines dependency to a given library with a fallback to its jar url\n- `//> using resourceDir dir` - marks directory as source of resources. Resources accessible at runtime and packaged\n  together with compiled code.\n- `//> using javaOpt opt` - use given java options when running application or tests\n- `//> using testFramework framework` - select test framework to use\n\nThere are several reasons that we believe `using` directives are a good solution:\n\n- One of the main Scala CLI use cases is prototyping, and the ability to ship one or more source code files with a\n  complete configuration is a game-changer for this use case.\n- Defining dependencies and other settings is common in Ammonite scripts as well.\n- From a teaching perspective, the ability to provide pre-configured pieces of code that fit into one slide is also\n  beneficial.\n- Having configuration close to the code is beneficial, since often — especially in small programs — the given\n  dependencies are only used within one source file.\n\nWe acknowledge that configuration distributed across many source files may be hard to maintain in the long term.\nTherefore, in the near feature we will introduce a set of lints to ensure that above a given project size or complexity,\nall configuration details will be centralized.\n\nHow can configuration that’s contained in source files be centralized?\n`using` directives can be placed in any `.scala` file, so it’s possible to create a `.scala` file that contains only\nconfiguration information.\nTherefore, when your project needs to centralize its configuration, we recommend creating a `project.scala` file, and\nplacing the configuration there.\nThe experimental [Fix command](../../commands/fix.md) can do this automatically.\n\nWe are aware that `using` directives may be a controversial topic, so we’ve created\na [dedicated space for discussing `using` directives](https://github.com/VirtusLab/scala-cli/discussions/categories/using-directives-and-cmd-configuration-options).\n\n### Explicit handling of paths in using directives\n\nThe `${.}` pattern in directive values will be replaced by the parent directory of the file containing the\ndirective. This makes it possible for example to generate coverage output files relative to the source file location.\n\n```scala\n//> using options -coverage-out:${.}\n```\n\nHowever, if you want to include the `${.}` pattern in the directive value without it being replaced, you can precede it\nwith two dollar signs (`$$`), like this:\n\n```scala\n//> using options -coverage-out:$${.}\n```\n\n## How to comment out using directives?\n\nUsing directives are part of the code so similarly, developers should be able to comment them out.\n\nCommenting out comment-based directives does not cause any problems. Below, some examples how to do it:\n\n```scala compile\n// //> using dep no::lib:123\n```\n\n```scala compile\n// // using dep no::lib:123\n```\n\n## Directives with a test scope equivalent\n\nSome directives have a test scope equivalent. For example, `using dep` has `using test.dep`, which allows to declare\ndependencies that are only used in tests outside test-specific sources.\n\nFor example, this way you can declare the dependency to `munit` in `project.scala` like this:\n\n```scala title=project.scala\n//> using test.dep org.scalameta::munit::1.0.2\n```\n\nThe dependency will then only be available in test sources.\nIt's effectively an equivalent to just `using dep` inside of a test source (except you can define it anywhere):\n\n```scala title=src/test/scala/Tests.scala\n//> using dep org.scalameta::munit::1.0.2\n```\n\nDirectives with a test scope equivalent:\n\n```scala compile\n//> using test.dep org.scalameta::munit::1.0.2\n//> using test.jar path/to/dep.jar\n//> using test.sourceJar path/to/some-sources.jar\n//> using test.javaOpt -Dfoo=bar\n//> using test.javacOpt source 1.8 target 1.8\n//> using test.javaProp foo1=bar1\n//> using test.option -Xfatal-warnings\n//> using test.resourceDir testResources\n//> using test.toolkit default\n```\n"
  },
  {
    "path": "website/docs/guides/power/_category_.json",
    "content": "{\n  \"label\": \"--power ⚡\",\n  \"position\": 40\n}"
  },
  {
    "path": "website/docs/guides/power/markdown.md",
    "content": "---\ntitle: Markdown ⚡️\nsidebar_position: 60\n---\n\n:::caution\nMarkdown support is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\nScala CLI can compile, run, test, and package markdown (`.md`) sources.\n\n:::note\nThis feature is a work in progress and should currently be treated as experimental.\nMarkdown sources are ignored by default unless passed explicitly as inputs.\nYou can enable including non-explicit `.md` inputs by passing the `--enable-markdown` option.\n:::\n\n## Markdown inputs\n\n### On-disk markdown sources\n\nYou can pass local `.md` inputs by passing their path to Scala CLI (as you would for any other kind of input).\n\n````markdown title=dir/hello.md\n# Simple snippet\n```scala\nprintln(\"Hello\")\n```\n````\n\n```bash\nscala-cli --power dir/hello.md\n```\n\n`.md` sources inside of directories are ignored by default, unless the `--enable-markdown` option is passed.\n\n```bash\nscala-cli --power dir --enable-markdown\n```\n\n### Zipped archives\n\nScala CLI can run `.md` sources inside a `.zip` archive.\nSame as with directories,  `.md` sources inside zipped archives are ignored by default, unless\nthe `--enable-markdown` option is passed.\n\n```bash ignore\nscala-cli --power archive-with-markdown.zip --enable-markdown\n```\n\n### Remote inputs\n\n:::warning\nRunning unverified code from the Internet can be very handy for *trusted* sources, but it can also be really dangerous,\nsince Scala CLI does not provide any sandboxing at this moment.\n\nMake sure that you trust the code that you are about to run.\n:::\n\n#### URLs\n\nYou can also pass a URL pointing to a `.md` file to run it with Scala CLI.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power https://gist.githubusercontent.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839/raw/4c5ce7593e19f1390555221e0d076f4b02f4b4fd/example.md\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\n#### Github Gist\n\nScala CLI accepts GitHub Gist URLs.\nThe gist is technically treated as a zipped archive (which it is downloaded as), so it is necessary to pass\nthe `--enable-markdown` option alongside the gist URL to run any contained Markdown sources.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power https://gist.github.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839 --enable-markdown\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nYou can find more information on running GitHub Gists in the [gists cookbook](../../cookbooks/introduction/gists.md).\n\n### Piped Markdown code\n\nInstead of passing paths to your Markdown sources, you can also pipe your code via standard input:\n\n<ChainedSnippets>\n\n```bash\necho '# Example Snippet\n```scala\nprintln(\"Hello\")\n```' | scala-cli --power _.md\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nYou can find more information on piped sources in the [piping guide](../advanced/piping.md).\n\n### Markdown code as a command line snippet\n\nIt is also possible to pass Markdown code as a snippet directly from the command line.\n\n<ChainedSnippets>\n\n````bash\nscala-cli --power run --markdown-snippet '# Markdown snippet\nwith a code block\n```scala\nprintln(\"Hello\")\n```'\n````\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nYou can find more information on command line snippets in the [snippets guide](../advanced/snippets.md).\n\n## Markdown code blocks\n\n### Plain `scala` snippets\n\n````markdown title=Example.md\n# Example\n\nThis is a simple example of an `.md` file with a Scala snippet.\n\n```scala\nval message = \"Hello from Markdown\"\nprintln(message)\n```\n````\n\nPlain `scala` snippets are treated similarly to `.sc` scripts in that any kind of statement is accepted at the\ntop-level.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power run Example.md\n```\n\n```text\nHello from Markdown\n```\n\n</ChainedSnippets>\n\nSimilarly to `.sc` scripts, when multiple `.md` files with plain `scala` snippets are being run, each of them will have\nits own main class, that can be run.\n\n````markdown title=Main1.md\n# Main class 1\n```scala\nprintln(\"1\")\n```\n````\n\n\n````markdown title=Main2.md\n# Main class 2\n```scala\nprintln(\"2\")\n```\n````\n\n<ChainedSnippets>\n\n```bash fail\nscala-cli --power Main1.md Main2.md\n```\n\n```text\n[error]  Found several main classes: Main1_md, Main2_md\n```\n\n</ChainedSnippets>\n\nWhen multiple such sources are passed as inputs, the main class has to be passed explicitly with the `--main-class`\noption.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power Main1.md Main2.md --main-class Main1_md\n```\n\n```text\n1\n```\n\n</ChainedSnippets>\n\nYou can always check what main classes are available in the context with the `--list-main-classes` option.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power Main1.md Main2.md --list-main-classes\n```\n\n```text\nMain1_md Main2_md\n```\n\n</ChainedSnippets>\n\n### `scala raw` snippets\n\nYou can mark a `scala` code block with the `raw` keyword, indicating that this snippet should not be wrapped as a script\nand should instead be treated as is. This is the equivalent of code in a `.scala` file. For a `raw` snippet to be\nrunnable a main class has to be included.\n\n````markdown title=RawExample.md\n# `raw` example\n\nThis is a simple example of an `.md` file with a raw Scala snippet.\n\n```scala raw\nobject Main extends App {\n  val message = \"Hello from Markdown\"\n  println(message) \n}\n```\n````\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power RawExample.md\n```\n\n```text\nHello from Markdown\n```\n\n</ChainedSnippets>\n\n### `scala test` snippets\n\nIt is possible to run tests from `scala` code blocks marked as `test`. This is similar to `raw` snippets in that the\ncode is not wrapped and is treated as is.\n\nYou can run `scala test` code blocks with the `test` sub-command.\n\n````markdown title=TestExample.md\n# `test` example\nThis is a simple example of an `.md` file with a test Scala snippet.\n\n```scala test\n//> using dep org.scalameta::munit:1.0.2\nclass Test extends munit.FunSuite {\n  test(\"example test\") {\n    assert(true)\n  }\n}\n```\n````\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power test TestExample.md\n```\n\n```text\nTest:\n  + example test\n```\n\n</ChainedSnippets>\n\n### `reset` scope for `scala` snippets\n\nWhen multiple plain `scala` snippets are used in a single `.md` file, by default they are actually treated as a single\nscript. They share context and when run, are executed one after another, as if they were all in a single `.sc` file.\n\nIf you want a snippet to use a fresh context instead, you can rely on the `reset` keyword. This allows you to start a\nfresh scope for the marked snippet (and any coming after it).\n\n````markdown title=ResetExample.md\n# `reset` scope\nThis is an example of an `.md` file with multiple `scala` snippets with separate scopes\n\n## Scope 1\n```scala\nval message = \"Hello\"\n```\n\n## Still scope 1, since `reset` wasn't used yet\n```scala\nprintln(message)\n```\n\n## Scope 2\n```scala reset\nval message = \"world\"\nprintln(message)\n```\n\n## Scope 3\n```scala reset\nval message = \"!\"\nprintln(message)\n```\n````\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power ResetExample.md\n```\n\n```text\nHello\nworld\n!\n```\n\n</ChainedSnippets>\n\n### `ignore` in `scala` snippets\n\nYou can mark a `scala` code block with the `ignore` keyword, indicating that this snippet should not be run. This block will be ignored completely and will not be compiled or executed.\n\nBelow is an example of an `.md` file with an ignored `scala` snippet:\n\n````markdown title=IgnoreExample.md\n# `ignore` example\n## Normal snippet\n```scala\nval message = \"Hello world!\"\nprintln(message)\n```\n\n## Ignored snippet\n```scala ignore\nval message = \"This must be ignored\"\nprintln(message)\n1 / 0\n```\n\n````\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power IgnoreExample.md\n```\n\nWe see output only from the `scala` snippet:\n\n```text\nHello world!\n```\n\n</ChainedSnippets>\n\nAfter execution of the above example, you can see that the ignored snippet was not run.\nAll errors and exceptions that would have been thrown by the `ignore` snippet are also absent.\n\n## `shebang` header and Markdown code blocks\nThe `shebang` line in `scala` code blocks inside a markdown input are always ignored.\nYou can use them (i.e. to give an example of their usage), but they do not change how the code is handled.\n\n````markdown\n## Self executable Scala script\n```scala\n#!/usr/bin/env -S scala-cli shebang\nprintln(\"Hello world\")\n```\n````\n\n## `using` directives and Markdown code blocks\n\nIt is possible to define `using` directives at the beginning of a `scala` code block inside a markdown input.\nThis is supported for all `scala` code block flavours.\n\n````markdown compile title=UsingDirectives.md\n# Using directives in `.md` inputs\n\n## `scala raw` example\n```scala raw\n//> using dep com.lihaoyi::pprint:0.9.0\nobject Printer {\n  def printHello(): Unit = pprint.pprintln(\"Hello\")\n}\n```\n\n## Plain `scala` example\n```scala\n//> using dep com.lihaoyi::os-lib:0.11.3\nprintln(os.pwd)\n```\n\n## `scala test` example\n```scala test\n//> using dep org.scalameta::munit:1.0.2\n\nclass Test extends munit.FunSuite {\n  test(\"foo\") {\n    assert(true)\n    println(\"Hello from tests\")\n  }\n}\n```\n## Relying on directives from other snippets\nDirectives from other snippets apply to the whole context.\nAs a result, nothing really stops you from using a dependency\nfrom an earlier code block.\n```scala\nPrinter.printHello()\npprint.pprintln(\"world\")\n```\n````\n\n:::note\n`scala` snippets inside of a Markdown input are not isolated. Each `using` directive applies to the whole project's\ncontext. A directive defined in a later snippet within the same source may override another defined in an earlier one.\n\n````markdown title=OverriddenDirective.md\n## 1\n\n```scala\n//> using scala 2.12.21\nprintln(util.Properties.versionNumberString)\n```\n\n## 2\n\n```scala\n//> using scala 2.13.18\nprintln(util.Properties.versionNumberString)\n```\n````\n\nIn this example, the directive from the second `scala` snippet will override the previous one and Scala `2.13.18` will\nbe used for both.\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power OverriddenDirective.md\n```\n\n```text\nCompiling project (Scala 2.13.10, JVM)\nCompiled project (Scala 2.13.10, JVM)\n2.13.10\n2.13.10\n```\n\n</ChainedSnippets>\n\n:::\n\n## Referring to code from Markdown\n\n### Plain `scala` code blocks\n\nReferring to code from plain `scala` snippets in markdown requires using their package name.\nSimilarly to scripts, the package is inferred based on the relative path to the source file in your project.\n\nYou also have to point to the Scope under which the code is located.\nScopes are numbered according to their order in a given `.md` file (starting from 0 for the first plain `scala`\nsnippet): `Scope{scopeNumber}`. The `snippetNumber` is omitted for the first script code block (0). In other words,\nthe first scope is just `Scope`, the second is `Scope1`, then `Scope2` and so on.\n\n````markdown title=src/markdown/Example.md\n## Scope 0\n```scala\ndef hello: String = \"Hello\"\n```\n\n## Still scope 0, since `reset` wasn't used yet\n```scala\ndef space: String = \" \"\n```\n\n## Scope 1\n```scala reset\ndef world: String = \"world\"\n```\n````\n\n```scala title=Main.scala\nobject Main extends App {\n  val hello = markdown.Example_md.Scope.hello\n  val space = markdown.Example_md.Scope.space\n  val world = markdown.Example_md.Scope1.world\n  println(s\"$hello$space$world\")\n}\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power src Main.scala --enable-markdown --main-class Main\n```\n\n```text\nHello world\n```\n\n</ChainedSnippets>\n\n### `scala raw` and `scala test` code blocks\n\nYou can refer to code from `scala raw` and `scala test` snippets as if they were the contents of a `.scala` file.\n\n````markdown title=RawSnippetToReferTo.md\n# `raw` snippet\n```scala raw\nobject Something {\n  def message: String = \"Hello\"\n}\n```\n````\n\n<ChainedSnippets>\n\n```bash\nscala-cli --power RawSnippetToReferTo.md -e 'println(Something.message)'\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n"
  },
  {
    "path": "website/docs/guides/power/offline.md",
    "content": "---\ntitle: Offline mode ⚡️\nsidebar_position: 51\n---\n\n:::caution\nOffline mode is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nThe offline mode for Scala CLI was introduced to be used in two situations:\n- you want to have more control over the artifacts being downloaded\n- your development environment has restricted access to the Internet or certain web domains\n\nIn this mode Scala CLI will only use local artifacts cached by coursier. Any attempts to download artifacts will fail unless they're available locally in cache or there is a known fallback.\nThis applies to everything that Scala CLI normally manages behind the scenes:\n- Scala language and compiler artifacts\n- JVM artifacts\n- Bloop artifacts\n- dependency artifacts\n\n## How to use the offline mode\n\nTo enable offline mode pass the `--offline` flag to Scala CLI, e.g.:\n\n```bash ignore\nscala-cli run Main.scala --offline\n```\n\nIt is also possible to use the `COURSIER_MODE` environment variable or `coursier.mode` java property.\n```bash ignore\nexport COURSIER_MODE=offline\n```\nor\n```bash ignore\nscala-cli -Dcoursier.mode=offline run Main.scala \n```\n\nFinally, it's possible to enable offline mode via global config:\n```bash ignore\nscala-cli --power config offline true\n```\n\n## Changes in behaviour\n\n### Scala artifacts\nIn offline mode Scala CLI will not perform any validation of the Scala version specified in the project, it will not be checked if such a version has been released.\n\n### JVM artifacts\nSystem JVM will be used or it will be fetched from local cache.\nIf a different JVM version than the system one is required, it is best to export it to the `JAVA_HOME` environment variable.\nIt is important to know, that currently if a version is specified with `--jvm` or `using jvm` Scala CLI will ignore the system JVM and try to fetch via coursier.\n\nTo start the Bloop server a JVM with version above 17 is required, if it can't be found compilation will fall back to using `scalac` instead.\n\n### Bloop artifacts\nIf no artifacts for Bloop are available compilation falls back to using `scalac` instead.\n\n### Dependency artifacts\nAny attempt to download a dependency will fail, so it is required to have all the dependencies cached locally before compiling.\n\nDependencies that reside in local repositories like `~/.ivy2/local` will be resolved as usual.\n\n## Setting up the environment\n\nThe easiest way to set up the environment is to use [Coursier](https://get-coursier.io).\n\nInstalling scala artifacts:\n```bash ignore\ncs install scala:3.3.0 scalac:3.3.0\n```\n\nInstalling a JVM:\n```bash ignore\ncs java --jvm 17\n```\n\nUsing the two commands above is already enough for running and compiling code using `scalac`.\nFor fetching code dependencies run:\n```bash ignore\ncs fetch com.lihaoyi::os-lib::0.9.1\n```\nNote that the dependency format is the same as for `--dep` and `using dep`. More information about it [here](../introduction/dependencies.md).\n\nIf you want to use Bloop, you can get it with:\n```bash ignore\ncs fetch io.github.alexarchambault.bleep:bloop-frontend_2.12:1.5.11-sc-3 \n```\nNote that Scala CLI uses a custom fork of Bloop, so simple `cs install bloop` won't work.\n\n### Setting up the environment manually\n\nIt is possible to copy the Scala language artifacts and dependencies to the local Coursier's cache manually.\nThis can be done by creating a directory structure like this:\n```text\nCOURSIER_CACHE_PATH\n└── https\n    └── repo1.maven.org\n        └── maven2\n            └── org\n                └── scala-lang\n                    └── scala-compiler\n                        └── 2.13.12\n                            ├── scala-compiler-2.13.12-sources.jar (OPTIONAL)\n                            ├── scala-compiler-2.13.12.jar\n                            └── scala-compiler-2.13.12.pom\n```\nPath on MacOs `~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-compiler/2.13.12`\n\nSame for a library:\n```text\nCOURSIER_CACHE_PATH\n└── https\n    └── repo1.maven.org\n        └── maven2\n            └── com\n                └── lihaoyi\n                    └── os-lib_3\n                        └── 0.9.1\n                            ├── os-lib_3-0.9.1-sources.jar (OPTIONAL)\n                            ├── os-lib_3-0.9.1.jar\n                            └── os-lib_3-0.9.1.pom\n```\nPath on MacOS `~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/os-lib_3/0.9.1`\n\nThe first segments after the `v1` directory are the address of the repository from which the artifact was downloaded.\nThis part can effectively be `https/repo1.maven.org/maven2` since maven central is the default repository to use.\nThe rest of the path is the artifact's organization (split by the '.' character) and version.\n\n## Testing offline mode\n\nTo perform a test of environment setup for offline mode, it may be useful to create a clean cache directory for coursier.\nTo do so, run:\n```bash ignore\nmkdir test-coursier-cache\nexport COURSIER_CACHE=`pwd`/test-coursier-cache\n```\nAnd proceed with setting up the environment as described above:\n```bash ignore\n# Should fail with:\n# [error]  Error downloading org.scala-lang:scala3-compiler_3:3.3.0\nscala-cli run Main.scala --jvm 11 --offline\ncs install scala:3.3.0 scalac:3.3.0\n\n# Could fail with:\n# Error while getting https://github.com/coursier/jvm-index/raw/master/index.json\n# But may also pass on MacOS ('/usr/libexec/java_home -v' is tried)\n# or if a JVM is cached in coursier's archive cache (this cache's location can't be overridden), you may want to clear it, see section below\nscala-cli run Main.scala --jvm 11 --offline\ncs java --jvm 11\n\n# Should pass with a warning:\n# [warn]  Offline mode is ON and Bloop could not be fetched from the local cache, using scalac as fallback\nscala-cli run Main.scala --jvm 11 --offline\ncs fetch io.github.alexarchambault.bleep:bloop-frontend_2.12:1.5.11-sc-3\n\n# Should pass with a warning:\n# [warn]  Offline mode is ON and a JVM for Bloop could not be fetched from the local cache, using scalac as fallback\nscala-cli run Main.scala --jvm 11 --offline\ncs java 17\n\nShould pass with no warnings\nscala-cli run Main.scala --jvm 11 --offline\n```\n\n## Clearing coursier's caches\nCiting [Coursier's docs](https://get-coursier.io/docs/cache#default-location): <br/>\nOn a system where only recent versions of coursier were ever run (>= 1.0.0-RC12-1, released on the 2017/10/31), the default cache location is platform-dependent:\n- on Linux, `~/.cache/coursier/v1`. This also applies to Linux-based CI environments, and FreeBSD too\n- on OS X, `~/Library/Caches/Coursier/v1`\n- on Windows, `%LOCALAPPDATA%\\Coursier\\Cache\\v1`, which, for user Alex, typically corresponds to `C:\\Users\\Alex\\AppData\\Local\\Coursier\\Cache\\v1`\n\nSo clearing the cache is just a matter of removing the `v1` directory corresponding to the platform you're on.\nHowever, Coursier does use a second archive cache, which should be located in the same place as the `v1` directory, e.g. `~/.cache/coursier/arc`,\nthis cache's location can't be overridden, so it may be necessary to clear it for proper testing."
  },
  {
    "path": "website/docs/guides/power/proxy.md",
    "content": "---\ntitle: HTTP Proxies ⚡️\nsidebar_position: 51\n---\n\nScala CLI can download dependencies via HTTP proxies. Proxies can be setup in several ways:\n- via Java properties\n- via the Maven configuration file (recommended for now)\n- via Scala CLI or coursier configuration files (soon)\n\n## Java properties\nIt is possible to specify the proxy settings using Java properties. There are several ways to pass those to Scala CLI, [more information here](../advanced/java-properties.md).\n\nThe most basic way is to pass the Java properties directly to Scala CLI on the command line.\nKeep in mind that properties, put before the sub-command name and sources, are only passed to Scala CLI and not to the JVM executing user's code.\n\nExample (notice the different name of the property depending on the protocol `http` or `https`):\n```\n$ scala-cli \\\n    -Dhttp.proxyProtocol=http -Dhttp.proxyHost=proxy.corp.com -Dhttp.proxyPort=8080 \\\n    -Dhttp.proxyUsername=alex -Dhttp.proxyPassword=1234 \\\n    -Dhttps.proxyProtocol=http -Dhttps.proxyHost=proxy.corp.com -Dhttps.proxyPort=8080 \\\n    -Dhttps.proxyUsername=alex -Dhttps.proxyPassword=1234 \\\n    run .\n```\n\nMore information about the Java properties used for configuring proxies can be found [here](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html).\n\n## Maven configuration file\n\nThis file lives at `~/.m2/settings.xml`\n\nExample configuration file, without authentication:\n```xml\n<settings>\n  <proxies>\n    <proxy>\n      <id>test-proxy</id>\n      <protocol>http</protocol>\n      <host>proxy.corp.com</host>\n      <port>8080</port>\n    </proxy>\n  </proxies>\n</settings>\n```\n\nExample configuration file, with authentication:\n```xml\n<settings>\n  <proxies>\n    <proxy>\n      <id>test-proxy</id>\n      <protocol>http</protocol>\n      <host>proxy.corp.com</host>\n      <port>8080</port>\n      <username>alex</username>\n      <password>1234</password>\n    </proxy>\n  </proxies>\n</settings>\n```\n\nThe value in `<protocol>…</protocol>` is assumed to be the protocol of the proxy itself\n(can be either `http` or `https`, `https` is assumed by default not to inadvertently leak\nproxy credentials).\n\nSuch a proxy is used for both http and https by Scala CLI.\n\nThe [coursier](https://github.com/coursier/coursier) command-line and library also pick those credentials, since version `2.1.0-M6-26-gcec901e9a` (2022/05/31).\n\n## Scala CLI configuration files\n:::caution\nEven though the `config` command is not restricted, some available configuration keys may be, and thus may\nrequire setting the `--power` option to be used.\nThat includes configuration keys tied to setting up proxies, like `httpProxy.address` and others.\nYou can pass the `--power` option explicitly or set it globally by running:\n```bash ignore\nscala-cli config power true\n```\n:::\n\n:::warning\nThis way of configuring proxies is not recommended, since it will set up a proxy not only for Scala CLI, but for Coursier itself, which is used by other build tools like SBT.\nThis may result in unexpected behavior.\n\nIf using Scala CLI config is preferred, it's recommended to put the [relevant Java properties](#java-properties) into the `config` with:\n```bash ignore\n    scala-cli --power config -i java.properties \"http.proxyProtocol=http\" \"http.proxyHost=proxy.corp.com\" \"http.proxyPort=8080\" \"https.proxyUsername=alex\" \"https.proxyPassword=1234\"\n````\nThe -D prefix can be dropped when writing properties to config.\n:::\n\nScala CLI configuration can also be used to configure proxies globally.\nTo do that use the `config` command:\n\n```bash ignore\nscala-cli --power config httpProxy.address http://proxy.company.com:8081\n```\n\nReplace `proxy.company.com` by the address of your proxy and append the port number with `:` if needed.\nAlso, change `http://` to `https://` in the address if your proxy is accessible via HTTPS.\n\nIf your proxy requires authentication, set your user and password with\n```bash ignore\nscala-cli --power config httpProxy.user value:_encoded_user_\nscala-cli --power config httpProxy.password value:_encoded_password_\n```\n\nReplace `_encoded_user_` and `_encoded_password_` by your actual user and password, following\nthe [password option format](../../reference/password-options.md). They should typically look like\n`env:ENV_VAR_NAME`, `file:/path/to/file`, or `command:command to run`.\n"
  },
  {
    "path": "website/docs/guides/power/python.md",
    "content": "---\ntitle: Python/ScalaPy ⚡️\nsidebar_position: 70\n---\n\n:::caution\nScalaPy support is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nScalaPy is a library that allows you to access the Python interpreter from Scala code. It boasts a simple API, automatic conversion between Scala and Python types, and optional static typing.\nIt makes it possible to integrate Python libraries into Scala CLI projects.\n\nScala CLI allows to configure the ScalaPy library with the `--python` flag and `//> using python` directive.\n\nMore information about ScalaPy can be found [here](https://scalapy.dev).\n\n## Example usage\n\nSome configuration might be needed before running the examples below:\n\n```bash ignore\n# install Python 3.11 (e.g. via an installer from the official Python website)\n# then download the packages with\npip3 install numpy matplotlib python-config\n```\n\n```scala\n//> using python\n//> using scala 2.13\n\nimport me.shadaj.scalapy.py\nimport me.shadaj.scalapy.py.SeqConverters\nimport py.PyQuote\n\npy.local {\n  val np = py.module(\"numpy\")\n\n  val rng = np.random.default_rng()\n\n  val randoms = rng.standard_normal(10).as[Seq[Double]]\n\n  randoms.foreach(println(_))\n}\n\nval numbers = py\"[x * 2 for x in ${Iterator.from(3).take(10).toList.toPythonCopy}]\"\n  .as[Seq[Int]]\n\nprintln(numbers)\n```\n\nYou can also use Scala Native to create a native binary with direct bindings to CPython. \n\n```scala\n//> using python\n\nimport me.shadaj.scalapy.py\nimport me.shadaj.scalapy.py.SeqConverters\n\nimport scala.util.Random\nimport scala.math.{Pi, sin, random}\n\nobject PlotDemo {\n  @main\n  def plot = {\n    val sequences = generate3DataSeqs\n\n    py.local {\n      val plt = py.module(\"matplotlib.pyplot\")\n\n      for {\n        (seq, color) <- sequences.zip(Seq(\"b\", \"r\", \"g\"))\n      } {\n        plt.plot(seq.toPythonProxy, color = color)\n        plt.show()\n      }\n    }\n  }\n\n  def generate3DataSeqs: Seq[Seq[Double]] = {\n    val amplitude = 1.0 // Amplitude of the sine wave\n    val numSamples = 1000\n    val numSequences = 3\n    val noiseAmplitude = 0.2 // Amplitude of noise\n\n    // Generate three sequences with varying numbers of cycles\n    val sequences = (1 to numSequences).map { seqIdx =>\n      val frequency = seqIdx // Varying frequency for each sequence\n      (1 to numSamples).map { sampleIdx =>\n        val noise = (random * 2 - 1) * noiseAmplitude // Generate random noise\n        val phase = 2 * Pi * frequency * sampleIdx / numSamples\n        amplitude * sin(phase) + noise\n      }\n    }\n    sequences\n  }\n}\n\n```\nRun:\n```bash ignore\nscala-cli --power package --native PlotDemo.scala -o plot\n./plot\n```\n\n"
  },
  {
    "path": "website/docs/guides/power/repositories.md",
    "content": "---\ntitle: Repositories ⚡️\nsidebar_position: 52\n---\n\nScala CLI downloads the dependencies declared in your projects using [Coursier](https://get-coursier.io/).\nThe default repositories being searched are the Maven Central and local Ivy repository on your machine.\nIf additional repositories are required it is possible to declare them:\n- on the command line with `--repository` or `--repo` or just `-r`\n- with the `//> using repositories` directive\n\nThe values can be names of predefined repositories accepted by Coursier, some of which are:\n- `sonatype:_value_` and `sonatype-s01:_value_` for Sonatype servers e.g. `sonatype:snapshots`\n    snapshots from both servers are searched when using `snapshots`\n- `jitpack`\n- `m2Local`\n\n## Custom repositories\n\nSupplying the address of custom repositories is also accepted when using `--repository` or `//> using repositories`.\nTo do so, provide the URL to the repository's root, e.g. `https://maven.pkg.github.com/USER/REPO` for GitHub Package Registry.\nBy default, custom repositories are treated as Maven repositories, to specify an Ivy repository, prefix the address with `ivy:` and supply the ivy pattern at the end e.g. `ivy:http://localhost:8081/repository/ivy-releases/[defaultPattern]`.\n\n:::tip\n`[defaultPattern]` gets expanded by Coursier to: \n```text\n    [organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]\n```\n:::\n\n## Repository Authentication\n\n:::caution\nEven though the `config` command is not restricted, some available configuration keys may be, and thus may\nrequire setting the `--power` option to be used.\nThat includes the configuration key tied to repositories settings, like `repositories.credentials` and others.\nYou can pass the `--power` option explicitly or set it globally by running:\n```bash ignore\nscala-cli config power true\n```\n:::\n\nRepository authentication is also supported and there are a couple ways of using it:\n- specifying credentials for each host in `COURSIER_CREDENTIALS` environment variable or in the `coursier.credentials` java property ([read more here](../../guides/advanced/java-properties.md)),\n    the supported format in this case is `host-address username:password`, e.g. `my_domain.com MyUserName:myPasswOrd`\n- adding config entries for each host, this can be done using `scala-cli --power config repositories.credentials host _username_ _password_`,\n    username and password values should follow the [password option format](../../reference/password-options.md), e.g. \n```bash ignore\n  scala-cli --power config repositories.credentials maven.pkg.github.com value:PrivateToken env:GH_TOKEN\n```\n\n## Default repositories\n\nYou can override the default Coursier repositories globally by invoking:\n```bash ignore\nscala-cli --power config repositories.default https://first-repo.company.com https://second-repo.company.com\n```\n\n## Mirrors\n\nIf you're fine directly downloading artifacts from the internet, but would rather have some\nrepositories requests go through a repository of yours, configure mirror repositories, like\n```bash ignore\nscala-cli --power config repositories.mirrors https://repo1.maven.org/maven2=https://repository.company.com/maven\n```\n\nTo have all requests to a Maven repository go through a repository of yours, do\n```bash ignore\nscala-cli --power config repositories.mirrors maven:*=https://repository.company.com/maven\n```"
  },
  {
    "path": "website/docs/guides/power/sbt-mill.md",
    "content": "---\ntitle: SBT and Mill ⚡️\nsidebar_position: 50\n---\n\n:::caution\nThe `export` sub-command is an experimental feature.\n\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team\non [GitHub](https://github.com/VirtusLab/scala-cli).\n:::\n\nScala CLI lets you export your current build into sbt or Mill.\nThis means that if your project needs something that Scala CLI doesn’t provide — such as a second module — you can export your project to your build tool of choice.\n\nWhy do we need this?\nBasically we don’t want to block the development of your project.\nBut at the same time, we don’t want to introduce the complexity that multi-module builds and tasks and plugin systems introduce — at least not until that complexity is needed.\n\nTo export a project, run this command to export to sbt:\n\n```sh\nscala-cli export --sbt <standard-options>\n```\n\nOr use this command to export to Mill:\n\n```sh\nscala-cli export --mill <standard-options>\n```\n\nThese commands create a copy of your sources, resources, and local JARs.\nThey also download gists and other non-local inputs.\nBy default the project is exported to a `dest` directory, but you can control that with the `-o` option.\n"
  },
  {
    "path": "website/docs/guides/scripting/_category_.json",
    "content": "{\n  \"label\": \"Scripting\",\n  \"position\": 10\n}"
  },
  {
    "path": "website/docs/guides/scripting/scripts.md",
    "content": "---\ntitle: Scripts\nsidebar_position: 30\n---\n\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n# Scripts\n\nScala CLI accepts Scala scripts as files that end in `.sc`.\nUnlike `.scala` files, in scripts, any kind of statement is accepted at the top-level:\n\n```scala title=hello.sc\nval message = \"Hello from Scala script\"\nprintln(message)\n```\n\nA script is run with the Scala CLI command:\n\n<ChainedSnippets>\n\n```bash\nscala-cli hello.sc\n```\n\n```text\nHello from Scala script\n```\n\n</ChainedSnippets>\n\n## Using multiple scripts together\n\nWhen you pass multiple scripts to Scala CLI at once ([or add them with `//> using file ...`](#define-source-files-in-using-directives), they are all compiled together and can reference each other.\nTheir names are inferred from the file name e.g. `hello.sc` becomes `hello` and `main.sc` becomes `main`.\n\n:::caution\nReferencing a script from `main.sc` is not always possible.\nMore in [Scala 2 scripts wrapper](#scala-2-scripts-wrapper).\n:::\n\n```scala title=message.sc\ndef msg = \"from Scala script\"\n```\n\n```scala title=hello.sc\nprintln(\"Hello \" + message.msg)\n```\n\n<ChainedSnippets>\n\n```bash\nscala-cli hello.sc message.sc\n```\n\n```text\nHello from Scala script\n```\n\n</ChainedSnippets>\n\nWhen a script is in a sub-directory, a package name is also inferred:\n\n```scala title=my-app/constants/message.sc\ndef msg = \"Hello from Scala scripts\"\n```\n\n```scala title=my-app/main.sc\nimport constants.message\nprintln(\"Hello \" + message.msg)\n```\n\nPlease note: when referring to code from another script, the actual relative path from the project root is used for the\npackage path. In the example above, as `message.sc` is located in the `my-app/constants/` directory, to use the `msg`\nfunction you have to call `constants.message.msg`.\n\nWhen referencing code from a piped script, just use `stdin`.\n\n<ChainedSnippets>\n\n```bash\necho '@main def main() = println(stdin.message)' > PrintMessage.scala\necho 'def message: String = \"Hello\"' | scala-cli PrintMessage.scala _.sc\n```\n\n```text\nHello\n```\n\n</ChainedSnippets>\n\nTo specify a main class when running a script, use this command:\n\n<ChainedSnippets>\n\n```bash\nscala-cli my-app --main-class main_sc\n```\n\n```text\nHello from Scala scripts\n```\n\n</ChainedSnippets>\n\n:::caution\nWhen specifying a main class from Scala 2 scripts, you need to use the script file name without the `_sc` suffix.\nMore in [Scala 2 scripts wrapper](#scala-2-scripts-wrapper).\n:::\n\nBoth of the previous scripts (`hello.sc` and `main.sc`) automatically get a main class, so this is required to\ndisambiguate them. If a main class coming from a regular `.scala` file is present in your app's context, that will be\nrun by default if the `--main-class` param is not explicitly specified.\n\nWhen in doubt, you can always list the main classes present in your app by passing `--list-main-classes`.\n\n<ChainedSnippets>\n\n```bash\necho '@main def main1() = println(\"main1\")' > main1.scala\necho '@main def main2() = println(\"main2\")' > main2.scala\necho 'println(\"on-disk script\")' > script.sc\necho 'println(\"piped script\")' | scala-cli --list-main-classes _.sc main1.scala main2.scala script.sc\n```\n\n```text\nstdin_sc script_sc main2 main1\n```\n\n</ChainedSnippets>\n\n## Define source files in using directives\n\nYou can also add source files with the using directive `//> using file` in Scala scripts:\n\n```scala title=main.sc\n//> using file Utils.scala\n\nprintln(Utils.message)\n```\n\n```scala title=Utils.scala\nobject Utils {\n  val message = \"Hello World\"\n}\n```\n\nScala CLI takes into account and compiles `Utils.scala`.\n\n<ChainedSnippets>\n\n```bash\nscala-cli main.sc\n```\n\n```text\nHello World\n```\n\n</ChainedSnippets>\n\n<!-- Expected:\nHello World\n-->\n\n## Self executable Scala Script\n\nYou can define a file with the “shebang” header to be self-executable. Please remember to use `scala-cli shebang`\ncommand, which makes Scala CLI compatible with Unix shebang interpreter directive. For example, given this script:\n\n```scala title=HelloScript.sc\n#!/usr/bin/env -S scala-cli shebang\nprintln(\"Hello world\")\n```\n\nYou can make it executable and run it, just like any other shell script:\n\n<ChainedSnippets>\n\n```bash\nchmod +x HelloScript.sc\n./HelloScript.sc\n```\n\n```text\nHello world\n```\n\n</ChainedSnippets>\n\nIt is also possible to set Scala CLI command-line options in the shebang line, for example\n\n```scala title=Shebang213.sc\n#!/usr/bin/env -S scala-cli shebang --scala-version 2.13\n```\n\nThe command `shebang` also allows script files to be executed even if they have no file extension,\nprovided they start with the [`shebang` header](shebang.md#shebang-script-headers).\nNote that those files are always run as scripts even though they may contain e.g. valid `.scala` program.\n\n## Arguments\n\nYou may also pass arguments to your script, and they are referenced with the special `args` variable:\n\n```scala title=p.sc\n#!/usr/bin/env -S scala-cli shebang\n\nprintln(args(1))\n```\n\n<ChainedSnippets>\n\n```bash\nchmod +x p.sc\n./p.sc hello world\n```\n\n```text\nworld\n```\n\n</ChainedSnippets>\n\n## The name of script\n\nYou can access the name of the running script inside the script itself using the special `scriptPath` variable:\n\n```scala title=script.sc\n#!/usr/bin/env -S scala-cli shebang\n\nprintln(scriptPath)\n```\n\n<ChainedSnippets>\n\n```bash\nchmod +x script.sc\n./script.sc\n```\n\n```text\n./script.sc\n```\n\n<!-- Expected:\n./script.sc\n-->\n\n</ChainedSnippets>\n\n## Script wrappers\n\nThe compilation and execution of a source file containing top-level definitions is possible due to the script's code being wrapper in an additional construct and given a `main` method.\nScala CLI as of version v1.1.0 uses three kinds of script wrappers depending on the project's configuration.\nThey each differ slightly and have different capabilities and limitations.\n\n### Scala 2 scripts wrapper\n\nFor scripts compiled with Scala 2.12 and 2.13 there's only a single wrapper available.\nIt uses an object extending the `App` trait to wrap the user's code.\n\n**Limitations**  \nThanks to the mechanics of `App` in Scala 2, this wrapper has no reported limitations when it comes to the code that can be run in it. \n\n**Differences in behaviour**  \n- It is not possible to reference contents of a script from a file called `main.sc`, as the name `main` clashes with a `main` method each wrapper contains.\n- The main class name is the name of the script file without the `.sc` suffix. For example, `hello.sc` becomes `hello`.\n\n### Scala 3 scripts wrappers\n\nFor Scala 3 there are two wrappers available:\n- Class Wrapper - default wrapper for Scala 3 scripts\n- Object Wrapper - extra wrapper that can be forced with `--object-wrapper` flag and `>// using  objectWrapper` directive\n\n#### Class Wrapper\nThis wrapper is the default for scripts in Scala 3, however, it cannot be used when the script is compiled for the JS platform, [Object Wrapper](#object-wrapper) is then used.\nDue to the usage of `export` keyword it is not possible to use it in Scala 2.\n\n**Limitations**  \n- Can't be used with scripts compiled for the JS platform\n- Can't be used in Scala 2\n- When referencing types defined in the script, the type's path can be different from expected and compilation may fail with:  \n`Error: Unexpected error when compiling project: 'assertion failed: asTerm called on not-a-Term val <none>'`\n\n**Differences in behaviour**  \nThe Class Wrapper's behaviour is the default described throughout the documentation.\n\n#### Object Wrapper\nThis wrapper is an alternative to the [Class Wrapper](#class-wrapper) and can be forced with `--object-wrapper` flag and `>// using  objectWrapper` directive.\nIt is used by default for Scala 3 scripts compiled for JS platform. Can suffer from deadlocks when using multithreaded code.\n\n**Limitations**  \n- When running background threads from the script and using e.g. `scala.concurrent.Await` on them may result in a deadlock due to unfinished initialization of the wrapper object.\n\n**Differences in behaviour**  \nThe Object Wrapper's behaviour is the default described throughout the documentation.\n\n### Summary\nThe wrapper type used according to the configuration used ((platform + forced type) X Scala version) is summarized in the table below:\n\n|                             | Scala 2.12  | Scala 2.13  | Scala 3        |\n|-----------------------------|-------------|-------------|----------------|\n| `>// using platform jvm`    | App Wrapper | App Wrapper | Class Wrapper  |\n| `>// using platform native` | App Wrapper | App Wrapper | Class Wrapper  |\n| `>// using platform js`     | App Wrapper | App Wrapper | Object Wrapper |\n| `>// using objectWrapper`   | App Wrapper | App Wrapper | Object Wrapper |\n\n## Differences with Ammonite scripts\n\n[Ammonite](http://ammonite.io) is a popular REPL for Scala that can also compile and run `.sc` files.\n\nScala CLI and Ammonite are similar, but differ significantly when your code is split in multiple scripts:\n\n- In Ammonite, a script needs to use `import $file` directives to use values defined in another script\n- With Scala CLI, all scripts passed can reference each other without such directives\n\nOn the other hand:\n\n- You can pass a single \"entry point\" script as input to Ammonite, and Ammonite finds the scripts it depends on via\n  the `import $file` directives\n- Scala CLI requires all scripts to be added with `//> using file ...` or to be passed beforehand, either one-by-one, or by putting them in a directory, and\n  passing the directory to Scala CLI\n"
  },
  {
    "path": "website/docs/guides/scripting/shebang.md",
    "content": "---\ntitle: Shebang\nsidebar_position: 31\n---\nimport {ChainedSnippets} from \"../../../src/components/MarkdownComponents.js\";\n\n\nThis guide explains the differences between the `run` and `shebang` sub-commands, mainly covering how each of them\nparses its arguments.\n\n### `shebang` script headers\n\nBefore proceeding, let's discuss how Scala CLI works in a script without the `shebang` command.\nHere is a simple `hello.sc` script with a `shebang` header:\n\n```scala title=hello.sc\n#!/usr/bin/env -S scala-cli -S 3\n\nprintln(args.size)\nprintln(args.headOption)\n```\n\nAnd it works correctly:\n\n<ChainedSnippets>\n\n```bash \nchmod +x hello.sc\n./hello.sc    \n```\n\n```text\n0\nNone\n```\n\n<!-- Expected:\n0\nNone\n-->\n\n</ChainedSnippets>\n\nAnd it also works:\n\n<ChainedSnippets>\n\n```bash\n./hello.sc -- Hello World\n```\n\n```text\n2\nSome(Hello)\n```\n\n<!-- Expected:\n2\nSome(Hello)\n-->\n\n</ChainedSnippets>\n\nNote that the extra `--` must be added to make it work. If it is not supplied, the result is:\n\n<ChainedSnippets>\n\n```bash run-fail\n./hello.sc Hello World\n```\n\n```text\n[error] Hello: input file not found\nWorld: input file not found\n```\n\n<!-- Expected:\nHello: input file not found\nWorld: input file not found\n-->\n\n</ChainedSnippets>\n\nIf we modify our script slightly and use the `shebang` sub-command in the header, we will get the following:\n\n```scala title=hello.sc\n#!/usr/bin/env -S scala-cli shebang -S 3\n\nprintln(args.size)\nprintln(args.headOption)\n```\n\n<ChainedSnippets>\n\n```bash\n./hello.sc Hello World\n```\n\n```text\n2\nSome(Hello)\n```\n<!-- Expected:\n2\nSome(Hello)\n-->\n\n</ChainedSnippets>\n\n\n### `shebang` and the command line\n\nLet's now see how the `shebang` command works straight from the command line.\n\n```scala title=Main.scala \nobject Main {\n  def main(args: Array[String]): Unit = println(args.mkString(\" \"))\n}  \n```\n\n<ChainedSnippets>\n\n```bash                                                                                                                                                                                                                                                                \nscala-cli shebang Main.scala Hello world\n```\n\n```text\nHello world\n```\n\n<!-- Expected:\nHello world\n-->\n\n</ChainedSnippets>\n\n\n:::note\nPlease note that `shebang` changing how arguments are parsed means that every option after the first input will be treated as\nan argument to the app.\n\n<ChainedSnippets>\n\n```bash\nscala-cli shebang Main.scala -S 2.13 #-S 2.13 is not recognised as an option, but as app arguments\n```\n\n```text\n-S 2.13\n```\n\n<!-- Expected:\n-S 2.13\n-->\n\n</ChainedSnippets>\n:::\n\nIf we try to do the same with the `run` sub-command, we get the following error:\n\n<ChainedSnippets>\n\n```bash run-fail\nscala-cli run Main.scala Hello world\n```\n\n```text\n[error]  Hello: input file not found\nworld: input file not found\n```\n\n<!-- Expected:\n[error]  Hello: input file not found\nworld: input file not found\n-->\n\n</ChainedSnippets>\n\n### Script files' extensions\n\nWhen running the `shebang` subcommand, script files don't need the `.sc` extension,\nbut they are then REQUIRED to start with a shebang line:\n\n```scala title=hello-with-shebang\n#!/usr/bin/env -S scala-cli shebang -S 3\n\nprintln(args.size)\nprintln(args.headOption)\n```\n\n<ChainedSnippets>\n\n```bash\nchmod +x hello-with-shebang\n./hello-with-shebang Hello World\n```\n\n```text\n2\nSome(Hello)\n```\n<!-- Expected:\n2\nSome(Hello)\n-->\n\n</ChainedSnippets>\n\n```scala title=hello-no-shebang\nprintln(args.size)\nprintln(args.headOption)\n```\n\n<ChainedSnippets>\n\n```bash run-fail\nchmod +x hello-no-shebang\nscala-cli shebang hello-no-shebang Hello World\n```\n\n```text\nhello-no-shebang: unrecognized source type (expected .scala or .sc extension, or a directory)\n```\n<!-- Expected:\nhello-no-shebang: unrecognized source type (expected .scala or .sc extension, or a directory)\n-->\n</ChainedSnippets>\n\n:::note\nFiles with no extensions are always run as scripts even though they may contain e.g. valid `.scala` program.\n:::\n"
  },
  {
    "path": "website/docs/overview.md",
    "content": "---\ntitle: Overview\nsidebar_position: 1\n---\n\nThe Scala CLI makes it easier to compile, run, test, and package Scala code.\n\nIt can:\n- [compile](commands/compile.md) Scala code\n- [run](commands/run.md) it\n- [package](commands/package.md) it as a JAR file, or in formats such as deb, rpm, MSI, ...\n- fire up a [REPL](commands/repl.md), letting you quickly play with the code\n- compile and run [tests](commands/test.md) suites\n\n... and more!\n\nScala CLI supports most recent Scala versions (`3.x`, `2.13.x` and `2.12.x`), and changing the Scala version as easy as providing the `--scala` parameter. (See [the cookbook](cookbooks/introduction/scala-versions.md) for more information.)\n\nAs well as compiling and running Scala code with the JVM (the default), Scala CLI also supports [Scala.js](guides/advanced/scala-js.md) and [Scala Native](guides/advanced/scala-native.md).\n\n## Installation\n\nimport BasicInstall from \"../src/components/BasicInstall\"\n\n<BasicInstall/>\n\nPrefer another way to install Scala CLI? See our [Advanced installation guide](/install#advanced-installation).\n\n\n## What’s next?\n\nScala-CLI documentation is split into three main sections:\n- [Getting started](getting_started.md), where you learn how to start with Scala CLI\n- [Commands](commands/basics.md), where you learn the most important Scala CLI commands\n- [Guides](guides/intro.md), where you can read about the core aspects of Scala CLI, and learn how Scala CLI interacts with other tools, like your IDE\n- Scala CLI [Cookbook](cookbooks/intro.md), where you can learn how to solve specific problems with Scala CLI\n\n**Happy hacking with Scala CLI!**\n\n![Demo](/img/dark/demo.gif)\n\n"
  },
  {
    "path": "website/docs/reference/_category_.json",
    "content": "{\n  \"label\": \"Reference\",\n  \"position\": 17\n}\n"
  },
  {
    "path": "website/docs/reference/build-info.md",
    "content": "---\ntitle: BuildInfo\nsidebar_position: 6\n---\n\n:::caution\nBuildInfo is a restricted feature and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\nDuring the building process Scala CLI collects information about the project's configuration,\nboth from the console options and `using directives` found in the project's sources.\nYou can access this information from your code using the `BuildInfo` object, that's automatically generated for your\nbuild on compile when that information changes.\n\nTo enable BuildInfo generation pass the `--build-info` option to Scala CLI or use a\n`//> using buildInfo` directive.\n\n## Usage\n\nThe generated BuildInfo object is available on the project's classpath. To access it you need to import it first.\nIt is available in the package `scala.cli.build` so use\n```scala\nimport scala.cli.build.BuildInfo\n```\nto import it.\n\nBelow you can find an example instance of the BuildInfo object, with all fields explained.\nSome of the values have been shortened for readability.\n\n```scala\npackage scala.cli.build\n\n/** Information about the build gathered by Scala CLI */\nobject BuildInfo {\n  /** version of Scala used to compile this project */\n  val scalaVersion = \"3.3.0\"\n  /** target platform of this project, it can be \"JVM\" or \"JS\" or \"Native\" */\n  val platform = \"JVM\"\n  /** version of JVM, if it's the target platform */\n  val jvmVersion = Some(\"11\")\n  /** version of Scala.js, if it's the target platform */\n  val scalaJsVersion = None\n  /** Scala.js ECMA Script version, if Scala.js is the target platform */\n  val jsEsVersion = None\n  /** version of Scala Native, if it's the target platform */\n  val scalaNativeVersion = None\n  /** Main class specified for the project */\n  val mainClass = Some(\"Main\")\n  /** Project version */\n  val projectVersion = None\n  /** Scala CLI version used for the compilation */\n  val scalaCliVersion = Some(\"1.7.0\")\n\n  /** Information about the Main scope */\n  object Main {\n    /** sources found for the scope */\n    val sources = Seq(\".../Main.scala\")\n    /** scalac options for the scope */\n    val scalacOptions = Seq(\"-Werror\")\n    /** compiler plugins used in this scope */\n    val scalaCompilerPlugins = Nil\n    /** dependencies used in this scope */\n    val dependencies = Seq(\"com.lihaoyi:os-lib_3:0.9.1\")\n    /** dependency resolvers used in this scope */\n    val resolvers = Seq(\"https://repo1.maven.org/maven2\", \"ivy:file:...\")\n    /** resource directories used in this scope */\n    val resourceDirs = Seq(\".../resources\")\n    /** custom jars added to this scope */\n    val customJarsDecls = Seq(\".../AwesomeJar1.jar\", \".../AwesomeJar2.jar\")\n  }\n\n  /** Information about the Test scope */\n  object Test {\n    /** sources found for the scope */\n    val sources = Seq(\".../MyTests.scala\")\n    /** scalac options for the scope */\n    val scalacOptions = Seq(\"-Vdebug\")\n    /** compiler plugins used in this scope */\n    val scalaCompilerPlugins = Nil\n    /** dependencies used in this scope */\n    val dependencies = Seq(\"org.scala-lang:toolkit_3:latest.release\")\n    /** dependency resolvers used in this scope */\n    val resolvers = Seq(\"https://repo1.maven.org/maven2\", \"ivy:file:...\")\n    /** resource directories used in this scope */\n    val resourceDirs = Seq(\".../test/resources\")\n    /** custom jars added to this scope */\n    val customJarsDecls = Nil\n  }\n}\n```\n\n## Project version\n\nA part of the BuildInfo object is the project version. By default, an attempt is made to deduce it using git tags\nof the workspace repository. If this fails (e.g. no git repository is present), the version is set to `0.1.0-SNAPSHOT`.\nYou can override this behaviour by passing the `--project-version` option to Scala CLI or by using a\n`//> using projectVersion` directive.\n\nPlease note that only tags that follow the semantic versioning are taken into consideration.\n\nValues available for project version configuration are:\n- `git:tag` or `git`: use the latest stable git tag, if it is older than HEAD then try to increment it\n    and add a suffix `-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`\n- `git:dynver`: use the latest (stable or unstable) git tag, if it is older than HEAD then use the output of\n    `-{distance from last tag}-g{shortened version of HEAD commit hash}-SNAPSHOT`, if no tag is available then use `0.1.0-SNAPSHOT`\n\nThe difference between stable and unstable tags are, that the latter can contain letters, e.g. `v0.1.0-RC1`.\nIt is also possible to specify the path to the repository, e.g. `git:tag:../my-repo`, `git:dynver:../my-repo`.\n\n\n\n"
  },
  {
    "path": "website/docs/reference/cli-options.md",
    "content": "---\ntitle: Command-line options\nsidebar_position: 1\n---\n\n\n\nThis is a summary of options that are available for each subcommand of the `scala-cli` command.\n\n## Scalac options forwarding\n\n All options that start with:\n\n\n- `-g`\n- `-language`\n- `-opt`\n- `-P`\n- `-target`\n- `-V`\n- `-W`\n- `-X`\n- `-Y`\n\nare assumed to be Scala compiler options and will be propagated to Scala Compiler. This applies to all commands that uses compiler directly or indirectly.\n\n\n ## Scalac options that are directly supported in scala CLI (so can be provided as is, without any prefixes etc.):\n\n - `-encoding`\n - `-release`\n - `-color`\n - `-nowarn`\n - `-feature`\n - `-deprecation`\n - `-indent`\n - `-no-indent`\n - `-unchecked`\n - `-rewrite`\n - `-old-syntax`\n - `-new-syntax`\n\n\n\n## Benchmarking options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--jmh`\n\nRun JMH benchmarks\n\n### `--jmh-version`\n\nSet JMH version (default: 1.37)\n\n## Compilation server options\n\nAvailable in commands:\n\n[`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--bloop-bsp-protocol`\n\n[Internal]\nProtocol to use to open a BSP connection with Bloop\n\n### `--bloop-bsp-socket`\n\n[Internal]\nSocket file to use to open a BSP connection with Bloop\n\n### `--bloop-host`\n\n[Internal]\nHost the compilation server should bind to\n\n### `--bloop-port`\n\n[Internal]\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n### `--bloop-daemon-dir`\n\n[Internal]\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n### `--bloop-version`\n\n[Internal]\nIf Bloop isn't already running, the version we should start\n\n### `--bloop-bsp-timeout`\n\n[Internal]\nMaximum duration to wait for the BSP connection to be opened\n\n### `--bloop-bsp-check-period`\n\n[Internal]\nDuration between checks of the BSP connection state\n\n### `--bloop-startup-timeout`\n\n[Internal]\nMaximum duration to wait for the compilation server to start up\n\n### `--bloop-default-java-opts`\n\n[Internal]\nInclude default JVM options for Bloop\n\n### `--bloop-java-opt`\n\n[Internal]\nPass java options to use by Bloop server\n\n### `--bloop-global-options-file`\n\n[Internal]\nBloop global options file\n\n### `--bloop-jvm`\n\n[Internal]\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n### `--bloop-working-dir`\n\n[Internal]\nWorking directory for Bloop, if it needs to be started\n\n### `--server`\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n## Compile options\n\nAvailable in commands:\n\n[`compile`](./commands.md#compile)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--print-class-path`\n\nAliases: `-p`, `--print-classpath`\n\nPrint the resulting class path\n\n## Config options\n\nAvailable in commands:\n\n[`config`](./commands.md#config)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--dump`\n\n[Internal]\nDump config DB as JSON\n\n### `--create-pgp-key`\n\nCreate PGP keychain in config\n\n### `--pgp-password`\n\nAliases: `--passphrase`\n\nA password used to encode the private PGP keychain\n\n### `--email`\n\nEmail used to create the PGP keychains in config\n\n### `--password-value`\n\nWhen accessing config's content print the password value rather than how to get the password\nWhen saving an entry in config save the password value rather than how to get the password\ne.g. print/save the value of environment variable ENV_VAR rather than \"env:ENV_VAR\"\n\n### `--unset`\n\nAliases: `--remove`\n\nRemove an entry from config\n\n### `--https-only`\n\nFor repository.credentials and publish.credentials, whether these credentials should be HTTPS only (default: true)\n\n### `--match-host`\n\nFor repository.credentials, whether to use these credentials automatically based on the host\n\n### `--optional`\n\nFor repository.credentials, whether to use these credentials are optional\n\n### `--pass-on-redirect`\n\nFor repository.credentials, whether to use these credentials should be passed upon redirection\n\n### `--force`\n\nAliases: `-f`\n\nForce overwriting values for key\n\n## Coursier options\n\nAvailable in commands:\n\n[`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--ttl`\n\n[Internal]\nSpecify a TTL for changing dependencies, such as snapshots\n\n### `--cache`\n\n[Internal]\nSet the coursier cache location\n\n### `--coursier-validate-checksums`\n\n[Internal]\nEnable checksum validation of artifacts downloaded by coursier\n\n### `--offline`\n\nDisable using the network to download artifacts, use the local cache only\n\n## Cross options\n\nAvailable in commands:\n\n[`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--cross`\n\nRun given command against all provided Scala versions and/or platforms\n\n## Debug options\n\nAvailable in commands:\n\n[`bloop`](./commands.md#bloop), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--debug`\n\nTurn debugging on\n\n### `--debug-port`\n\nDebug port (5005 by default)\n\n### `--debug-mode`\n\nDebug mode (attach by default)\n\n## Dependency options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--dependency`\n\nAliases: `--dep`\n\nAdd dependencies\n\n### `--compile-only-dependency`\n\nAliases: `--compile-dep`, `--compile-lib`\n\nAdd compile-only dependencies\n\n### `--repository`\n\nAliases: `-r`, `--repo`\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\n### `--compiler-plugin`\n\nAliases: `-P`, `--plugin`\n\nAdd compiler plugin dependencies\n\n## Dependency update options\n\nAvailable in commands:\n\n[`dependency-update`](./commands.md#dependency-update)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--all`\n\nUpdate all dependencies if a newer version was released\n\n## Doc options\n\nAvailable in commands:\n\n[`doc`](./commands.md#doc)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--output`\n\nAliases: `-o`\n\nSet the destination path\n\n### `--force`\n\nAliases: `-f`\n\nOverwrite the destination directory, if it exists\n\n### `--default-scaladoc-options`\n\nAliases: `--default-scaladoc-opts`\n\nControl if Scala CLI should use default options for scaladoc, true by default. Use `--default-scaladoc-opts:false` to not include default options.\n\n## Export options\n\nAvailable in commands:\n\n[`export`](./commands.md#export)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--sbt`\n\nSets the export format to SBT\n\n### `--maven`\n\nAliases: `--mvn`\n\nSets the export format to Maven\n\n### `--mill`\n\nSets the export format to Mill\n\n### `--json`\n\nSets the export format to Json\n\n### `--sbt-setting`\n\nAliases: `--setting`\n\n### `--project`\n\nAliases: `-p`\n\nProject name to be used on Mill build file\n\n### `--sbt-version`\n\nVersion of SBT to be used for the export (1.12.4 by default)\n\n### `--mill-version`\n\nVersion of Mill to be used for the export (1.1.5 by default)\n\n### `--mvn-version`\n\nVersion of Maven Compiler Plugin to be used for the export (3.8.1 by default)\n\n### `--mvn-scala-version`\n\nVersion of Maven Scala Plugin to be used for the export (4.9.1 by default)\n\n### `--mvn-exec-plugin-version`\n\nVersion of Maven Exec Plugin to be used for the export (3.3.0 by default)\n\n### `--mvn-app-artifact-id`\n\nArtifactId to be used for the maven export\n\n### `--mvn-app-group-id`\n\nGroupId to be used for the maven export\n\n### `--mvn-app-version`\n\nVersion to be used for the maven export\n\n### `--output`\n\nAliases: `-o`\n\n## Fix options\n\nAvailable in commands:\n\n[`fix`](./commands.md#fix)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--check`\n\nFail the invocation if rewrites are needed\n\n### `--enable-scalafix`\n\nAliases: `--scalafix`\n\nEnable running Scalafix rules (enabled by default)\n\n### `--enable-built-in-rules`\n\nAliases: `--built-in`, `--built-in-rules`, `--enable-built-in`\n\nEnable running built-in rules (enabled by default)\n\n## Fmt options\n\nAvailable in commands:\n\n[`fmt` , `format` , `scalafmt`](./commands.md#fmt)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--check`\n\nCheck if sources are well formatted\n\n### `--respect-project-filters`\n\nUse project filters defined in the configuration. Turned on by default, use `--respect-project-filters:false` to disable it.\n\n### `--save-scalafmt-conf`\n\nSaves .scalafmt.conf file if it was created or overwritten\n\n### `--os-arch-suffix`\n\n[Internal]\n### `--scalafmt-tag`\n\n[Internal]\n### `--scalafmt-github-org-name`\n\n[Internal]\n### `--scalafmt-extension`\n\n[Internal]\n### `--scalafmt-launcher`\n\n[Internal]\n### `--scalafmt-arg`\n\nAliases: `-F`\n\nPass an argument to scalafmt.\n\n### `--scalafmt-conf`\n\nAliases: `--scalafmt-config`\n\nCustom path to the scalafmt configuration file.\n\n### `--scalafmt-conf-str`\n\nAliases: `--scalafmt-conf-snippet`, `--scalafmt-config-str`\n\nPass configuration as a string.\n\n### `--scalafmt-dialect`\n\nAliases: `--dialect`\n\nPass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file or inferred based on Scala version used.\n\n### `--scalafmt-version`\n\nAliases: `--fmt-version`\n\nPass scalafmt version before running it (3.10.7 by default). If passed, this overrides whatever value is configured in the .scalafmt.conf file.\n\n## Global suppress warning options\n\nAvailable in commands:\n\n[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--suppress-experimental-feature-warning`\n\nAliases: `--suppress-experimental-warning`\n\nSuppress warnings about using experimental features\n\n### `--suppress-deprecated-feature-warning`\n\nAliases: `--suppress-deprecated-feature-warnings`, `--suppress-deprecated-warning`, `--suppress-deprecated-warnings`\n\nSuppress warnings about using deprecated features\n\n## Help options\n\nAvailable in commands:\n\n[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp create`](./commands.md#pgp-create), [`pgp key-id`](./commands.md#pgp-key-id), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`pgp sign`](./commands.md#pgp-sign), [`pgp verify`](./commands.md#pgp-verify), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--usage`\n\nPrint usage and exit\n\n### `--help`\n\nAliases: `-h`, `-help`\n\nPrint help message and exit\n\n### `--help-full`\n\nAliases: `--full-help`, `-full-help`, `-help-full`\n\nPrint help message, including hidden options, and exit\n\n## Help group options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--help-envs`\n\nAliases: `--env-help`, `--envs-help`, `--help-env`\n\nShow environment variable help\n\n### `--help-js`\n\nShow options for ScalaJS\n\n### `--help-native`\n\nShow options for ScalaNative\n\n### `--help-scaladoc`\n\nAliases: `--doc-help`, `--help-doc`, `--scaladoc-help`\n\nShow options for Scaladoc\n\n### `--help-repl`\n\nAliases: `--repl-help`\n\nShow options for Scala REPL\n\n### `--help-scalafmt`\n\nAliases: `--fmt-help`, `--help-fmt`, `--scalafmt-help`\n\nShow options for Scalafmt\n\n## Install completions options\n\nAvailable in commands:\n\n[`install completions` , `install-completions`](./commands.md#install-completions)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--format`\n\nAliases: `--shell`\n\nName of the shell, either zsh or bash\n\n### `--rc-file`\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n### `--output`\n\nAliases: `-o`\n\nCompletions output directory\n\n### `--banner`\n\n[Internal]\nCustom banner in comment placed in rc file\n\n### `--name`\n\n[Internal]\nCustom completions name\n\n### `--env`\n\nPrint completions to stdout\n\n## Java options\n\nAvailable in commands:\n\n[`package`](./commands.md#package), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--java-opt`\n\nAliases: `-J`\n\nSet Java options, such as `-Xmx1g`\n\n## Java prop options\n\nAvailable in commands:\n\n[`package`](./commands.md#package), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--java-prop-option`\n\nAliases: `--java-prop`\n\nAdd java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\n\n## Jvm options\n\nAvailable in commands:\n\n[`bloop`](./commands.md#bloop), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--java-home`\n\nSet the Java home directory\n\n### `--jvm`\n\nAliases: `-j`\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\n### `--jvm-index`\n\n[Internal]\nJVM index URL\n\n### `--jvm-index-os`\n\n[Internal]\nOperating system to use when looking up in the JVM index\n\n### `--jvm-index-arch`\n\n[Internal]\nCPU architecture to use when looking up in the JVM index\n\n### `--javac-plugin`\n\n[Internal]\nJavac plugin dependencies or files\n\n### `--javac-option`\n\nAliases: `--javac-opt`\n\n[Internal]\nJavac options\n\n### `--bsp-debug-port`\n\n[Internal]\nPort for BSP debugging\n\n## Logging options\n\nAvailable in commands:\n\n[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--quiet`\n\nAliases: `-q`\n\nDecrease logging verbosity\n\n### `--progress`\n\nUse progress bars\n\n## Main class options\n\nAvailable in commands:\n\n[`export`](./commands.md#export), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--main-class`\n\nAliases: `-M`\n\nSpecify which main class to run\n\n### `--main-class-ls`\n\nAliases: `--list-main-class`, `--list-main-classes`, `--list-main-method`, `--list-main-methods`, `--main-class-list`, `--main-method-list`, `--main-method-ls`\n\nList main classes available in the current context\n\n## Markdown options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--enable-markdown`\n\nAliases: `--markdown`, `--md`\n\nEnable markdown support.\n\n## Package options\n\nAvailable in commands:\n\n[`package`](./commands.md#package)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--output`\n\nAliases: `-o`\n\nSet the destination path\n\n### `--force`\n\nAliases: `-f`\n\nOverwrite the destination file, if it exists\n\n### `--library`\n\nGenerate a library JAR rather than an executable JAR\n\n### `--with-sources`\n\nAliases: `--jar-sources`, [deprecated] `--sources`, `--sources-jar`, [deprecated] `--src`\n\nGenerate a source JAR rather than an executable JAR\n\n### `--doc`\n\nAliases: `--javadoc`, `--scaladoc`\n\nGenerate a scaladoc JAR rather than an executable JAR\n\n### `--assembly`\n\nGenerate an assembly JAR\n\n### `--preamble`\n\nFor assembly JAR, whether to add a bash / bat preamble\n\n### `--main-class-in-manifest`\n\n[Internal]\nFor assembly JAR, whether to specify a main class in the JAR manifest\n\n### `--spark`\n\n[Internal]\nGenerate an assembly JAR for Spark (assembly that doesn't contain Spark, nor any of its dependencies)\n\n### `--standalone`\n\nPackage standalone JARs\n\n### `--deb`\n\nBuild Debian package, available only on Linux\n\n### `--dmg`\n\nBuild dmg package, available only on macOS\n\n### `--rpm`\n\nBuild rpm package, available only on Linux\n\n### `--msi`\n\nBuild msi package, available only on Windows\n\n### `--pkg`\n\nBuild pkg package, available only on macOS\n\n### `--docker`\n\nBuild Docker image\n\n### `--provided`\n\n[Internal]\nExclude modules *and their transitive dependencies* from the JAR to be packaged\n\n### `--default-scaladoc-options`\n\nAliases: `--default-scaladoc-opts`\n\nUse default scaladoc options\n\n### `--native-image`\n\nAliases: `--graal`\n\nBuild GraalVM native image\n\n## Packager options\n\nAvailable in commands:\n\n[`package`](./commands.md#package)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--version`\n\nSet the version of the generated package\n\n### `--logo-path`\n\nPath to application logo in PNG format, it will be used to generate icon and banner/dialog in msi installer\n\n### `--launcher-app`\n\nSet launcher app name, which will be linked to the PATH\n\n### `--description`\n\n### `--maintainer`\n\nAliases: `-m`\n\nThis should contain names and email addresses of co-maintainers of the package\n\n### `--debian-conflicts`\n\nThe list of Debian package that this package is not compatible with\n\n### `--debian-dependencies`\n\nThe list of Debian packages that this package depends on\n\n### `--deb-architecture`\n\nArchitectures that are supported by the repository (default: all)\n\n### `--priority`\n\nThis field represents how important it is that the user have the package installed\n\n### `--section`\n\nThis field specifies an application area into which the package has been classified\n\n### `--identifier`\n\nCF Bundle Identifier\n\n### `--license`\n\nLicenses that are supported by the repository (list of licenses: https://spdx.org/licenses/)\n\n### `--release`\n\nThe number of times this version of the software was released (default: 1)\n\n### `--rpm-architecture`\n\nArchitectures that are supported by the repository (default: noarch)\n\n### `--license-path`\n\nPath to the license file\n\n### `--product-name`\n\nName of product (default: Scala packager)\n\n### `--exit-dialog`\n\nText that will be displayed on the exit dialog\n\n### `--suppress-validation`\n\nSuppress Wix ICE validation (required for users that are neither interactive, not local administrators)\n\n### `--extra-config`\n\nPath to extra WIX configuration content\n\n### `--is64-bits`\n\nAliases: `--64`\n\nWhether a 64-bit executable is being packaged\n\n### `--installer-version`\n\nWIX installer version\n\n### `--wix-upgrade-code-guid`\n\nThe GUID to identify that the windows package can be upgraded.\n\n### `--docker-from`\n\nBuilding the container from base image\n\n### `--docker-image-registry`\n\nThe image registry; if empty, it will use the default registry\n\n### `--docker-image-repository`\n\nThe image repository\n\n### `--docker-image-tag`\n\nThe image tag; the default tag is `latest`\n\n### `--docker-cmd`\n\nAllows to override the executable used to run the application in docker, otherwise it defaults to sh for the JVM platform and node for the JS platform\n\n### `--docker-extra-directories`\n\nExtra directories to be added to the docker image\n\n### `--graalvm-java-version`\n\nGraalVM Java major version to use to build GraalVM native images (17 by default)\n\n### `--graalvm-version`\n\nGraalVM version to use to build GraalVM native images (17.0.9 by default)\n\n### `--graalvm-jvm-id`\n\nJVM id of GraalVM distribution to build GraalVM native images (like \"graalvm-java17:22.0.0\")\n\n### `--graalvm-args`\n\nPass args to GraalVM\n\n## Pgp push pull options\n\nAvailable in commands:\n\n[`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish setup`](./commands.md#publish-setup)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--key-server`\n\nKey server to push / pull keys from\n\n## Power options\n\nAvailable in commands:\n\n[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--power`\n\nAllows to use restricted & experimental features\n\n## Publish options\n\nAvailable in commands:\n\n[`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--working-dir`\n\n[Internal]\nDirectory where temporary files for publishing should be written\n\n### `--scala-version-suffix`\n\n[Internal]\nScala version suffix to append to the module name, like \"_2.13\" or \"_3\"\n\n### `--scala-platform-suffix`\n\n[Internal]\nScala platform suffix to append to the module name, like \"_sjs1\" or \"_native0.4\"\n\n### `--with-sources`\n\nAliases: `--jar-sources`, [deprecated] `--sources`, `--sources-jar`\n\nWhether to build and publish source JARs\n\n### `--doc`\n\nAliases: `--javadoc`, `--scaladoc`\n\nWhether to build and publish doc JARs\n\n### `--gpg-key`\n\nAliases: `-K`\n\nID of the GPG key to use to sign artifacts\n\n### `--signer`\n\nMethod to use to sign artifacts\n\n### `--gpg-option`\n\nAliases: `-G`, `--gpg-opt`\n\ngpg command-line options\n\n### `--ivy2-home`\n\nSet Ivy 2 home directory\n\n### `--checksum`\n\n[Internal]\n### `--dummy`\n\nProceed as if publishing, but do not upload / write artifacts to the remote repository\n\n### `--ivy2-local-like`\n\n[Internal]\n### `--parallel-upload`\n\n[Internal]\n## Publish local options\n\nAvailable in commands:\n\n[`publish local`](./commands.md#publish-local)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--m2`\n\nAliases: `--maven-local`\n\nPublish to the local Maven repository (defaults to ~/.m2/repository) instead of Ivy2 local\n\n### `--m2-home`\n\nSet the local Maven repository path (defaults to ~/.m2/repository)\n\n## Publish params options\n\nAvailable in commands:\n\n[`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--organization`\n\nOrganization to publish artifacts under\n\n### `--name`\n\nName to publish artifacts as\n\n### `--module-name`\n\nFinal name to publish artifacts as, including Scala version and platform suffixes if any\n\n### `--url`\n\nURL to put in publishing metadata\n\n### `--license`\n\nLicense to put in publishing metadata\n\n### `--vcs`\n\nVCS information to put in publishing metadata\n\n### `--description`\n\nDescription to put in publishing metadata\n\n### `--developer`\n\nDeveloper(s) to add in publishing metadata, like \"alex|Alex|https://alex.info\" or \"alex|Alex|https://alex.info|alex@alex.me\"\n\n### `--secret-key`\n\nSecret key to use to sign artifacts with Bouncy Castle\n\n### `--secret-key-password`\n\nAliases: `--secret-key-pass`\n\nPassword of secret key to use to sign artifacts with Bouncy Castle\n\n### `--ci`\n\nUse or setup publish parameters meant to be used on continuous integration\n\n## Publish repository options\n\nAvailable in commands:\n\n[`publish`](./commands.md#publish), [`publish setup`](./commands.md#publish-setup)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--publish-repository`\n\nAliases: `-R`, `--publish-repo`\n\nRepository to publish to\n\n### `--user`\n\nUser to use with publishing repository\n\n### `--password`\n\nPassword to use with publishing repository\n\n### `--realm`\n\nRealm to use when passing credentials to publishing repository\n\n## Publish setup options\n\nAvailable in commands:\n\n[`publish setup`](./commands.md#publish-setup)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--public-key`\n\nPublic key to use to verify artifacts (to be uploaded to a key server)\n\n### `--check`\n\nCheck if some options for publishing are missing, and exit with non-zero return code if that's the case\n\n### `--token`\n\nGitHub token to use to upload secrets to GitHub - password encoded\n\n### `--random-secret-key`\n\nGenerate a random key pair for publishing, with a secret key protected by a random password\n\n### `--random-secret-key-mail`\n\nWhen generating a random key pair, the mail to associate to it\n\n### `--checks`\n\nThe option groups to check - can be \"all\", or a comma-separated list of \"core\", \"signing\", \"repo\", \"extra\"\n\n### `--check-workflow`\n\nWhether to check if a GitHub workflow already exists (one for publishing is written if none is found)\n\n### `--check-gitignore`\n\nWhether to check if a .gitignore file already exists (one is written if none is found)\n\n### `--dummy`\n\nDummy mode - don't upload any secret to GitHub\n\n## Python options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--python-setup`\n\nSet Java options so that Python can be loaded\n\n### `--python`\n\nAliases: `--py`\n\nEnable Python support via ScalaPy\n\n### `--scala-py-version`\n\nAliases: `--scalapy-version`\n\nSet ScalaPy version (0.5.3 by default)\n\n## Repl options\n\nAvailable in commands:\n\n[`repl` , `console`](./commands.md#repl)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--ammonite`\n\nAliases: `-A`, `--amm`\n\nUse Ammonite (instead of the default Scala REPL)\n\n### `--ammonite-version`\n\nAliases: `--ammonite-ver`\n\nSet the Ammonite version (3.0.8 by default)\n\n### `--ammonite-arg`\n\nAliases: `-a`\n\n[Internal]\nProvide arguments for ammonite repl\n\n### `--repl-dry-run`\n\n[Internal]\nDon't actually run the REPL, just fetch it\n\n## Run options\n\nAvailable in commands:\n\n[`run`](./commands.md#run), [`shebang`](./commands.md#shebang)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--spark-submit`\n\nAliases: `--spark`\n\n[Internal]\nRun as a Spark job, using the spark-submit command\n\n### `--submit-argument`\n\nAliases: `--submit-arg`\n\n[Internal]\nSpark-submit arguments\n\n### `--standalone-spark`\n\nAliases: `--spark-standalone`\n\nRun as a Spark job, using a vanilla Spark distribution downloaded by Scala CLI\n\n### `--hadoop-jar`\n\nAliases: `--hadoop`\n\nRun as a Hadoop job, using the \"hadoop jar\" command\n\n### `--command`\n\nPrint the command that would have been run (one argument per line), rather than running it\n\n### `--scratch-dir`\n\nTemporary / working directory where to write generated launchers\n\n### `--use-manifest`\n\n[Internal]\nRun Java commands using a manifest-based class path (shortens command length)\n\n## Scala.js options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--js`\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n### `--js-version`\n\nThe Scala.js version (1.20.2 by default).\n\n### `--js-mode`\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n### `--js-no-opt`\n\n[Internal]\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n### `--js-module-kind`\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n### `--js-check-ir`\n\n### `--js-emit-source-maps`\n\nEmit source maps\n\n### `--js-source-maps-path`\n\nSet the destination path of source maps\n\n### `--js-es-module-import-map`\n\nA file relative to the root directory containing import maps for ES module imports\n\n### `--js-dom`\n\nEnable jsdom\n\n### `--js-emit-wasm`\n\nEmit WASM\n\n### `--js-header`\n\nA header that will be added at the top of generated .js files\n\n### `--js-allow-big-ints-for-longs`\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n### `--js-avoid-classes`\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n### `--js-avoid-lets-and-consts`\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n### `--js-module-split-style`\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n### `--js-small-module-for-package`\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n### `--js-es-version`\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n### `--js-linker-path`\n\n[Internal]\nPath to the Scala.js linker\n\n### `--js-cli-version`\n\n[Internal]\nScala.js CLI version to use for linking (1.20.2 by default).\n\n### `--js-cli-java-arg`\n\n[Internal]\nScala.js CLI Java options\n\n### `--js-cli-on-jvm`\n\n[Internal]\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n## Scala Native options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--native`\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n### `--native-version`\n\nSet the Scala Native version (0.5.10 by default).\n\n### `--native-mode`\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n### `--native-lto`\n\nLink-time optimisation mode (none by default): none, full, thin\n\n### `--native-gc`\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n### `--native-clang`\n\nPath to the Clang command\n\n### `--native-clangpp`\n\nPath to the Clang++ command\n\n### `--native-linking`\n\nExtra options passed to `clang` verbatim during linking\n\n### `--native-linking-defaults`\n\n[Internal]\nUse default linking settings\n\n### `--native-compile`\n\nList of compile options\n\n### `--native-c-compile`\n\nList of compile options (C files only)\n\n### `--native-cpp-compile`\n\nList of compile options (C++ files only)\n\n### `--native-compile-defaults`\n\n[Internal]\nUse default compile options\n\n### `--native-target`\n\nBuild target type\n\n### `--embed-resources`\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n### `--native-multithreading`\n\nEnable/disable Scala Native multithreading support\n\n## Scalac options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--args-file`\n\nFile with scalac options.\n\n### `--scalac-option`\n\nAliases: `-O`, `--scala-opt`, `--scala-option`\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\n## Scalac extra options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scalac-help`\n\nAliases: `--help-scalac`\n\nShow help for scalac. This is an alias for --scalac-option -help\n\n### `--scalac-verbose`\n\nAliases: `--verbose-scalac`\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\n## Scalafix options\n\nAvailable in commands:\n\n[`fix`](./commands.md#fix)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scalafix-conf`\n\nCustom path to the scalafix configuration file.\n\n### `--scalafix-arg`\n\nPass extra argument(s) to scalafix.\n\n### `--scalafix-rules`\n\nRun scalafix rule(s) explicitly, overriding the configuration file default.\n\n## Scope options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--test`\n\nAliases: `--test-scope`, `--with-test`, `--with-test-scope`\n\nInclude test scope\n\n## Secret options\n\nAvailable in commands:\n\n[`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--token`\n\n### `--repository`\n\nAliases: `--repo`\n\n## Secret create options\n\nAvailable in commands:\n\n[`github secret create` , `gh secret create`](./commands.md#github-secret-create)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--public-key`\n\nAliases: `--pub-key`\n\n### `--dummy`\n\nAliases: `-n`\n\n### `--print-request`\n\n[Internal]\n## Shared options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scala-version`\n\nAliases: `-S`, `--scala`\n\nSet the Scala version (3.8.3 by default)\n\n### `--scala-binary-version`\n\nAliases: `-B`, `--scala-bin`, `--scala-binary`\n\n[Internal]\nSet the Scala binary version\n\n### `--extra-jars`\n\nAliases: `--class`, `--class-path`, `--classes`, `-classpath`, `--classpath`, `-cp`, `--extra-class`, `--extra-class-path`, `--extra-classes`, `--extra-jar`, `--jar`, `--jars`\n\nAdd extra JARs and compiled classes to the class path\n\n### `--extra-compile-only-jars`\n\nAliases: `--compile-only-jar`, `--compile-only-jars`, `--extra-compile-only-jar`\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\n### `--extra-source-jars`\n\nAliases: `--extra-source-jar`, `--source-jar`, `--source-jars`\n\nAdd extra source JARs\n\n### `--resource-dirs`\n\nAliases: `--resource-dir`\n\nAdd a resource directory\n\n### `--as-jar`\n\n[Internal]\nPut project in class paths as a JAR rather than as a byte code directory\n\n### `--platform`\n\nSpecify platform\n\n### `--scala-library`\n\n[Internal]\n### `--with-compiler`\n\nAliases: `-with-compiler`, `--with-scala-compiler`\n\nAllows to include the Scala compiler artifacts on the classpath.\n\n### `--java`\n\n[Internal]\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n### `--runner`\n\n[Internal]\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n### `--strict-bloop-json-check`\n\n[Internal]\n### `--compilation-output`\n\nAliases: `--compile-out`, `--compile-output`, `-d`, `--destination`, `--output-directory`\n\nCopy compilation results to output directory using either relative or absolute path\n\n### `--with-toolkit`\n\nAliases: `--toolkit`\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\n### `--exclude`\n\nExclude sources\n\n### `--object-wrapper`\n\nForce object wrapper for scripts\n\n## Snippet options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--script-snippet`\n\nAllows to execute a passed string as a Scala script\n\n### `--execute-script`\n\nAliases: `-e`, `--execute-sc`, `--execute-scala-script`\n\n[Internal]\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n### `--scala-snippet`\n\nAllows to execute a passed string as Scala code\n\n### `--execute-scala`\n\n[Internal]\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n### `--java-snippet`\n\nAllows to execute a passed string as Java code\n\n### `--execute-java`\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n### `--markdown-snippet`\n\nAliases: `--md-snippet`\n\nAllows to execute a passed string as Markdown code\n\n### `--execute-markdown`\n\nAliases: `--execute-md`\n\n[Internal]\nA synonym to --markdown-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n## Source generator options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--use-build-info`\n\nAliases: `--build-info`\n\nGenerate BuildInfo for project\n\n## Suppress warning options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--suppress-directives-in-multiple-files-warning`\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\nSuppress warnings about using directives in multiple files\n\n### `--suppress-outdated-dependency-warning`\n\nSuppress warnings about outdated dependencies in project\n\n## Test options\n\nAvailable in commands:\n\n[`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--test-frameworks`\n\nAliases: `--test-framework`\n\nNames of the test frameworks' runner classes to use while running tests.\nSkips framework lookup and only runs passed frameworks.\n\n### `--require-tests`\n\nFail if no test suites were run\n\n### `--test-only`\n\nSpecify a glob pattern to filter the tests suite to be run.\n\n## Uninstall options\n\nAvailable in commands:\n\n[`uninstall`](./commands.md#uninstall)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--force`\n\nAliases: `-f`\n\nForce scala-cli uninstall\n\n### `--skip-cache`\n\n[Internal]\nDon't clear Scala CLI cache\n\n### `--binary-name`\n\n[Internal]\nBinary name\n\n### `--bin-dir`\n\n[Internal]\nBinary directory\n\n## Uninstall completions options\n\nAvailable in commands:\n\n[`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--rc-file`\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n### `--banner`\n\n[Internal]\nCustom banner in comment placed in rc file\n\n### `--name`\n\n[Internal]\nCustom completions name\n\n## Update options\n\nAvailable in commands:\n\n[`update`](./commands.md#update)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--binary-name`\n\n[Internal]\nBinary name\n\n### `--bin-dir`\n\n[Internal]\nBinary directory\n\n### `--force`\n\nAliases: `-f`\n\nForce update Scala CLI if it is outdated\n\n### `--is-internal-run`\n\n[Internal]\n### `--gh-token`\n\n[Internal]\nA github token used to access GitHub. Not needed in most cases.\n\n## Verbosity options\n\nAvailable in commands:\n\n[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--verbose`\n\nAliases: `-v`, `-verbose`\n\nIncrease verbosity (can be specified multiple times)\n\n### `--interactive`\n\nAliases: `-i`\n\nInteractive mode\n\n### `--actions`\n\nEnable actionable diagnostics\n\n## Version options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--compute-version`\n\nMethod used to compute the project version\n\n### `--project-version`\n\nSet the project version\n\n### `--cli-version`\n\nAliases: `--cli`\n\nShow plain Scala CLI version only\n\n### `--scala-version`\n\nAliases: `--scala`\n\nShow plain Scala version only\n\n### `--gh-token`\n\n[Internal]\nA github token used to access GitHub. Not needed in most cases.\n\n### `--offline`\n\nDon't check for the newest available Scala CLI version upstream\n\n## Watch options\n\nAvailable in commands:\n\n[`compile`](./commands.md#compile), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--watch`\n\nAliases: `-w`\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\n### `--restart`\n\nAliases: `--revolver`\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\n### `--watching`\n\nAliases: `--watching-path`\n\nWatch additional paths for changes (used together with --watch or --restart)\n\n## Internal options \n### Add path options\n\nAvailable in commands:\n\n[`add-path`](./commands.md#add-path)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--title`\n\n[Internal]\n### Bloop options\n\nAvailable in commands:\n\n[`bloop`](./commands.md#bloop)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--working-directory`\n\nAliases: `--dir`, `--working-dir`\n\n[Internal]\n### Bloop start options\n\nAvailable in commands:\n\n[`bloop start`](./commands.md#bloop-start)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--force`\n\nAliases: `-f`\n\n[Internal]\n### Bsp options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--json-options`\n\n[Internal]\nCommand-line options JSON file\n\n### `--json-launcher-options`\n\n[Internal]\nCommand-line launcher options JSON file\n\n### `--envs`\n\nAliases: `--envs-file`\n\n[Internal]\nCommand-line options environment variables file\n\n### Bsp file options\n\nAvailable in commands:\n\n[`clean`](./commands.md#clean), [`setup-ide`](./commands.md#setup-ide)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--bsp-directory`\n\nAliases: `--bsp-dir`\n\n[Internal]\nCustom BSP configuration location\n\n### `--bsp-name`\n\nAliases: `--name`\n\n[Internal]\nName of BSP\n\n### Default file options\n\nAvailable in commands:\n\n[`default-file`](./commands.md#default-file)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--write`\n\n[Internal]\nWrite result to files rather than to stdout\n\n### `--list`\n\n[Internal]\nList available default files\n\n### `--list-ids`\n\n[Internal]\nList available default file ids\n\n### `--force`\n\nAliases: `-f`\n\n[Internal]\nForce overwriting destination files\n\n### Input options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--default-forbidden-directories`\n\n[Internal]\n### `--forbid`\n\n[Internal]\n### Install home options\n\nAvailable in commands:\n\n[`install-home`](./commands.md#install-home)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scala-cli-binary-path`\n\n[Internal]\n### `--force`\n\nAliases: `-f`\n\n[Internal]\nOverwrite if it exists\n\n### `--binary-name`\n\n[Internal]\nBinary name\n\n### `--env`\n\n[Internal]\nPrint the update to `env` variable\n\n### `--bin-dir`\n\n[Internal]\nBinary directory\n\n### Pgp create options\n\nAvailable in commands:\n\n[`pgp create`](./commands.md#pgp-create)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--email`\n\n[Internal]\n### `--password`\n\n[Internal]\n### `--dest`\n\n[Internal]\n### `--pub-dest`\n\n[Internal]\n### `--secret-dest`\n\n[Internal]\n### `--verbose`\n\n[Internal]\n### `--quiet`\n\n[Internal]\n### Pgp key id options\n\nAvailable in commands:\n\n[`pgp key-id`](./commands.md#pgp-key-id)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--fingerprint`\n\n[Internal]\n### `--verbose`\n\nAliases: `-v`\n\n[Internal]\n### Pgp pull options\n\nAvailable in commands:\n\n[`pgp pull`](./commands.md#pgp-pull)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--allow-empty`\n\n[Internal]\nWhether to exit with code 0 if no key is passed\n\n### Pgp push options\n\nAvailable in commands:\n\n[`pgp push`](./commands.md#pgp-push)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--force`\n\nAliases: `-f`\n\n[Internal]\nTry to push the key even if Scala CLI thinks it's not a public key\n\n### `--allow-empty`\n\n[Internal]\nWhether to exit with code 0 if no key is passed\n\n### Pgp scala signing options\n\nAvailable in commands:\n\n[`config`](./commands.md#config), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--signing-cli-version`\n\n[Internal]\nscala-cli-signing version when running externally (0.2.13 by default)\n\n### `--signing-cli-java-arg`\n\n[Internal]\nPass arguments to the Java command when running scala-cli-singing externally on JVM\n\n### `--force-signing-externally`\n\n[Internal]\nWhen running Scala CLI on the JVM, force running scala-cli-singing externally\n\n### `--force-jvm-signing-cli`\n\n[Internal]\nWhen running Scala CLI on the JVM, force running scala-cli-singing using a native launcher or a JVM launcher\n\n### Pgp sign options\n\nAvailable in commands:\n\n[`pgp sign`](./commands.md#pgp-sign)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--password`\n\n[Internal]\n### `--secret-key`\n\n[Internal]\n### `--force`\n\nAliases: `-f`\n\n[Internal]\n### `--stdout`\n\n[Internal]\n### Pgp verify options\n\nAvailable in commands:\n\n[`pgp verify`](./commands.md#pgp-verify)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--key`\n\n[Internal]\n### Publish connection options\n\nAvailable in commands:\n\n[`publish`](./commands.md#publish)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--connection-timeout-seconds`\n\n[Internal]\nConnection timeout, in seconds.\n\n### `--connection-timeout-retries`\n\n[Internal]\nHow many times to retry establishing the connection on timeout.\n\n### `--response-timeout-seconds`\n\n[Internal]\nWaiting for response timeout, in seconds.\n\n### `--staging-repo-retries`\n\n[Internal]\nHow many times to retry the staging repository operations on failure.\n\n### `--staging-repo-wait-time-milis`\n\n[Internal]\nTime to wait between staging repository operation retries, in milliseconds.\n\n### Semantic db options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--semantic-db`\n\nAliases: `--semanticdb`\n\n[Internal]\nGenerate SemanticDBs\n\n### `--semantic-db-target-root`\n\nAliases: `--semanticdb-target-root`, `--semanticdb-targetroot`\n\n[Internal]\nSemanticDB target root (default to the compiled classes destination directory)\n\n### `--semantic-db-source-root`\n\nAliases: `--semanticdb-source-root`, `--semanticdb-sourceroot`\n\n[Internal]\nSemanticDB source root (default to the project root directory)\n\n### Setup IDE options\n\nAvailable in commands:\n\n[`setup-ide`](./commands.md#setup-ide)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--charset`\n\n[Internal]\n### Workspace options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--workspace`\n\n[Internal]\nDirectory where .scala-build is written\n\n"
  },
  {
    "path": "website/docs/reference/commands.md",
    "content": "---\ntitle: Commands\nsidebar_position: 3\n---\n\n\n\n\n## clean\n\nClean the workspace.\n\nPassed inputs will establish the Scala CLI project, for which the workspace will be cleaned.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/clean\n\nAccepts option groups: [bsp file](./cli-options.md#bsp-file-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options), [workspace](./cli-options.md#workspace-options)\n\n## compile\n\nCompile Scala code.\n\nSpecific compile configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## config\n\nConfigure global settings for Scala CLI.\n\nSyntax:\n```sh\n  scala-cli config key value\n```\nFor example, to globally set the interactive mode:\n```sh\n  scala-cli config interactive true\n```\n  \nAvailable keys:\n  - actions                                        Globally enables actionable diagnostics. Enabled by default.\n  - github.token                                   GitHub token.\n  - httpProxy.address                              HTTP proxy address.\n  - httpProxy.password                             HTTP proxy password (used for authentication).\n  - httpProxy.user                                 HTTP proxy user (used for authentication).\n  - interactive                                    Globally enables interactive mode (the '--interactive' flag).\n  - interactive-was-suggested                      Setting indicating if the global interactive mode was already suggested.\n  - java.properties                                Java properties for Scala CLI's execution.\n  - offline                                        Globally enables offline mode (the '--offline' flag).\n  - pgp.public-key                                 The PGP public key, used for signing.\n  - pgp.secret-key                                 The PGP secret key, used for signing.\n  - pgp.secret-key-password                        The PGP secret key password, used for signing.\n  - power                                          Globally enables power mode (the '--power' launcher flag).\n  - publish.credentials                            Publishing credentials, syntax: repositoryAddress value:user value:password [realm]\n  - publish.user.email                             The 'email' user detail, used for publishing.\n  - publish.user.name                              The 'name' user detail, used for publishing.\n  - publish.user.url                               The 'url' user detail, used for publishing.\n  - repositories.credentials                       Repository credentials, syntax: repositoryAddress value:user value:password [realm]\n  - repositories.default                           Default repository, syntax: https://first-repo.company.com https://second-repo.company.com\n  - repositories.mirrors                           Repository mirrors, syntax: repositories.mirrors maven:*=https://repository.company.com/maven\n  - suppress-warning.deprecated-features           Globally suppresses warnings about deprecated features.\n  - suppress-warning.directives-in-multiple-files  Globally suppresses warnings about directives declared in multiple source files.\n  - suppress-warning.experimental-features         Globally suppresses warnings about experimental features.\n  - suppress-warning.outdated-dependencies-files   Globally suppresses warnings about outdated dependencies.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/config\n\nAccepts option groups: [config](./cli-options.md#config-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n## dependency-update\n\nUpdate dependency directives in the project\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [dependency update](./cli-options.md#dependency-update-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## doc\n\nGenerate Scaladoc documentation.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## export\n\nExport current project to an external build tool (like SBT or Mill) or to JSON.\n\nThe whole Scala CLI project should get exported along with its dependencies configuration.\n\nUnless otherwise configured, the default export format is SBT.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nDetailed documentation can be found on our website: https://scala-cli.virtuslab.org\n\nThe `export` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [export](./cli-options.md#export-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## fix\n\nRun Scala CLI & Scalafix rules to lint, rewrite or otherwise rearrange a Scala CLI project.\n\n`scalafix` is used to check project code or rewrite it under the hood with use of specified rules.\n\nAll standard Scala CLI inputs are accepted, but only Scala sources will be refactored (.scala and .sc files).\n\nSpecific fix configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fix\n\nThe `fix` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fix](./cli-options.md#fix-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [Scalafix](./cli-options.md#scalafix-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## fmt\n\nAliases: `format`, `scalafmt`\n\nFormats Scala code.\n\n`scalafmt` is used to perform the formatting under the hood.\n\nThe `.scalafmt.conf` configuration file is optional.\nDefault configuration values will be assumed by Scala CLI.\n\nAll standard Scala CLI inputs are accepted, but only Scala sources will be formatted (.scala and .sc files).\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## help\n\nPrint help message\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n## install completions\n\nAliases: `install-completions`\n\nInstalls Scala CLI completions into your shell\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/completions\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [install completions](./cli-options.md#install-completions-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n## new\n\nNew giter8 template.\n\n Creates a new project from a giter8 template.\n\n\n\nThe `new` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n## repl\n\nAliases: `console`\n\nFire-up a Scala REPL.\n\nThe entire Scala CLI project's classpath is loaded to the repl.\n\nSpecific repl configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## package\n\nCompile and package Scala code.\n\nSpecific package configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/package\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [package](./cli-options.md#package-options), [packager](./cli-options.md#packager-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## publish\n\nPublishes build artifacts to Maven repositories.\n\nWe recommend running the `publish setup` sub-command once prior to\nrunning `publish` in order to set missing `using` directives for publishing.\n(but this is not mandatory)\n    scala-cli --power publish setup .\n\nSpecific publish configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/publishing/publish\n\nThe `publish` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish connection](./cli-options.md#publish-connection-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## publish local\n\nPublishes build artifacts to the local Ivy2 or Maven repository.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/publishing/publish-local\n\nThe `publish-local` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish](./cli-options.md#publish-options), [publish local](./cli-options.md#publish-local-options), [publish params](./cli-options.md#publish-params-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## publish setup\n\nConfigures the project for publishing.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/publishing/publish-setup\n\nThe `publish-setup` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [pgp push pull](./cli-options.md#pgp-push-pull-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [publish setup](./cli-options.md#publish-setup-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## run\n\nCompile and run Scala code.\n\nSpecific run configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nFor a run to be successful, a main method must be present on the classpath.\n.sc scripts are an exception, as a main class is provided in their wrapper.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nTo pass arguments to the actual application, just add them after `--`, like:\n```sh\n  scala-cli run Main.scala AnotherSource.scala -- first-arg second-arg\n```\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## github secret create\n\nAliases: `gh secret create`\n\nCreates or updates a GitHub repository secret.\n```sh\n  scala-cli --power github secret create --repo repo-org/repo-name SECRET_VALUE=value:secret\n```\n\nThe `secret-create` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [coursier](./cli-options.md#coursier-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [secret](./cli-options.md#secret-options), [secret create](./cli-options.md#secret-create-options), [verbosity](./cli-options.md#verbosity-options)\n\n## github secret list\n\nAliases: `gh secret list`\n\nLists secrets for a given GitHub repository.\n\nThe `secret-list` sub-command is experimental.\nPlease bear in mind that non-ideal user experience should be expected.\nIf you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [secret](./cli-options.md#secret-options), [verbosity](./cli-options.md#verbosity-options)\n\n## setup-ide\n\nGenerates a BSP file that you can import into your IDE.\n\nThe setup-ide sub-command allows to pre-configure a Scala CLI project to import to an IDE with BSP support.\nIt is also ran implicitly when `compile`, `run`, `shebang` or `test` sub-commands are called.\n\nThe pre-configuration should be saved in a BSP json connection file under the path:\n```sh\n    {project-root}/.bsp/scala-cli.json\n```\n\nSpecific setup-ide configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n## shebang\n\nLike `run`, but handier for shebang scripts.\n\nThis command is equivalent to the `run` sub-command, but it changes the way\nScala CLI parses its command-line arguments in order to be compatible\nwith shebang scripts.\n\nWhen relying on the `run` sub-command, inputs and scala-cli options can be mixed,\nwhile program args have to be specified after `--`\n```sh\n  scala-cli [command] [scala-cli_options | input]... -- [program_arguments]...\n```\n\nHowever, for the `shebang` sub-command, only a single input file can be set, while all scala-cli options\nhave to be set before the input file.\nAll inputs after the first are treated as program arguments, without the need for `--`\n```sh\n  scala-cli shebang [scala-cli_options]... input [program_arguments]...\n```\n\nUsing this, it is possible to conveniently set up Unix shebang scripts. For example:\n```scala\n  #!/usr/bin/env -S scala-cli shebang --scala-version 2.13\n  println(\"Hello, world\")\n```\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## test\n\nCompile and test Scala code.\n\nTest sources are compiled separately (after the 'main' sources), and may use different dependencies, compiler options, and other configurations.\nA source file is treated as a test source if:\n  - the file name ends with `.test.scala`\n  - the file comes from a directory that is provided as input, and the relative path from that file to its original directory contains a `test` directory\n  - it contains the `//> using target.scope test` directive (Experimental)\n\nSpecific test configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## uninstall\n\nUninstalls Scala CLI.\nWorks only when installed with the installation script.\nFor detailed installation instructions refer to our website: https://scala-cli.virtuslab.org/install\n\nAccepts option groups: [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [uninstall](./cli-options.md#uninstall-options), [uninstall completions](./cli-options.md#uninstall-completions-options), [verbosity](./cli-options.md#verbosity-options)\n\n## uninstall completions\n\nAliases: `uninstall-completions`\n\nUninstalls Scala CLI completions from your shell.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/completions\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [uninstall completions](./cli-options.md#uninstall-completions-options), [verbosity](./cli-options.md#verbosity-options)\n\n## update\n\nUpdates Scala CLI.\nWorks only when installed with the installation script.\nIf Scala CLI was installed with an external tool, refer to its update methods.\n\nFor detailed installation instructions refer to our website: https://scala-cli.virtuslab.org/install\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [update](./cli-options.md#update-options), [verbosity](./cli-options.md#verbosity-options)\n\n## version\n\nPrints the version of the Scala CLI and the default version of Scala. (which can be overridden in the project)\nIf network connection is available, this sub-command also checks if the installed Scala CLI is up-to-date.\n\nThe version of the Scala CLI is the version of the command-line tool that runs Scala programs, which\nis distinct from the Scala version of the compiler. We recommend to specify the version of the Scala compiler\nfor a project in its sources (via a using directive). Otherwise, Scala CLI falls back to the default\nScala version defined by the runner.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/version\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options)\n\n## Hidden commands\n\n### add-path\n\nAdd entries to the PATH environment variable.\n\nAccepts option groups: [add path](./cli-options.md#add-path-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### bloop\n\nInteract with Bloop (the build server) or check its status.\n\nThis sub-command allows to check the current status of Bloop.\nIf Bloop isn't currently running, it will be started.\n\nBloop is the build server used by Scala CLI.\nFor more information about Bloop, refer to https://scalacenter.github.io/bloop/\n\nAccepts option groups: [bloop](./cli-options.md#bloop-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### bloop exit\n\nStop Bloop if an instance is running.\n\nBloop is the build server used by Scala CLI.\nFor more information about Bloop, refer to https://scalacenter.github.io/bloop/\n\nAccepts option groups: [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### bloop output\n\nPrint Bloop output.\n\nBloop is the build server used by Scala CLI.\nFor more information about Bloop, refer to https://scalacenter.github.io/bloop/\n\nAccepts option groups: [compilation server](./cli-options.md#compilation-server-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### bloop start\n\nStarts a Bloop instance, if none is running.\n\nBloop is the build server used by Scala CLI.\nFor more information about Bloop, refer to https://scalacenter.github.io/bloop/\n\nAccepts option groups: [bloop start](./cli-options.md#bloop-start-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### bsp\n\nStart BSP server.\n\nBSP stands for Build Server Protocol.\nFor more information refer to https://build-server-protocol.github.io/\n\nThis sub-command is not designed to be used by a human.\nIt is normally supposed to be invoked by your IDE when a Scala CLI project is imported.\n\nDetailed documentation can be found on our website: https://scala-cli.virtuslab.org\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n### default-file\n\nGenerates default files for a Scala CLI project (i.e. .gitignore).\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/misc/default-file\n\nAccepts option groups: [default file](./cli-options.md#default-file-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### directories\n\nPrints directories used by Scala CLI.\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### install-home\n\nInstall Scala CLI in a sub-directory of the home directory\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [install home](./cli-options.md#install-home-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### pgp pull\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [pgp pull](./cli-options.md#pgp-pull-options), [pgp push pull](./cli-options.md#pgp-push-pull-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### pgp push\n\nAccepts option groups: [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [pgp push](./cli-options.md#pgp-push-options), [pgp push pull](./cli-options.md#pgp-push-pull-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### pgp create\n\nCreate PGP key pair\n\nAccepts option groups: [pgp create](./cli-options.md#pgp-create-options)\n\n### pgp key-id\n\nAccepts option groups: [pgp key id](./cli-options.md#pgp-key-id-options)\n\n### pgp sign\n\nSign files with PGP\n\nAccepts option groups: [pgp sign](./cli-options.md#pgp-sign-options)\n\n### pgp verify\n\nVerify PGP signatures\n\nAccepts option groups: [pgp verify](./cli-options.md#pgp-verify-options)\n\n"
  },
  {
    "path": "website/docs/reference/dependency.md",
    "content": "---\ntitle: Dependency format\nsidebar_position: 4\n---\n\n- Same as Mill\n\n- first `:` — scala version suffix\n  - single `:` — no scala version suffix\n  - double `::` — scala binary version suffix\n  - triple `:::` — full scala version suffix\n\n- second `:` — scala platform suffix\n  - single `:` — no platform suffix\n  - double `::` — platform suffix, if any"
  },
  {
    "path": "website/docs/reference/directives.md",
    "content": "---\ntitle: Directives\nsidebar_position: 2\n---\n\n## using directives\n\n### Benchmarking options\n\nAdd benchmarking options\n\n`//> using jmh` _value_\n\n`//> using jmhVersion` _value_\n\n#### Examples\n`//> using jmh`\n\n`//> using jmh true`\n\n`//> using jmhVersion 1.37`\n\n### BuildInfo\n\nGenerate BuildInfo for project\n\n`//> using buildInfo`\n\n#### Examples\n`//> using buildInfo`\n\n### Compiler options\n\nAdd Scala compiler options\n\n`//> using scalacOption` _option_\n\n`//> using option` _option_\n\n`//> using scalacOptions` _option1_ _option2_ …\n\n`//> using options` _option1_ _option2_ …\n\n`//> using test.scalacOption` _option_\n\n`//> using test.option` _option_\n\n`//> using test.scalacOptions` _option1_ _option2_ …\n\n`//> using test.options` _option1_ _option2_ …\n\n\n\n#### Examples\n`//> using option -Xasync`\n\n`//> using options -Xasync -Xfatal-warnings`\n\n`//> using test.option -Xasync`\n\n`//> using test.options -Xasync -Xfatal-warnings`\n\n### Compiler plugins\n\nAdds compiler plugins\n\n`using plugin` _org_`:`_name_`:`_ver_\n\n#### Examples\n`//> using plugin org.typelevel:::kind-projector:0.13.4`\n\n### Compute Version\n\nMethod used to compute the version for BuildInfo\n\n`//> using computeVersion` _method_\n\n#### Examples\n`//> using computeVersion git`\n\n`//> using computeVersion git:tag`\n\n`//> using computeVersion git:dynver`\n\n### Custom JAR\n\nManually add JAR(s) to the class path\n\n`//> using jar` _path_\n\n`//> using jars` _path1_ _path2_ …\n\n`//> using test.jar` _path_\n\n`//> using test.jars` _path1_ _path2_ …\n\n`//> using source.jar` _path_\n\n`//> using source.jars` _path1_ _path2_ …\n\n`//> using test.source.jar` _path_\n\n`//> using test.source.jars` _path1_ _path2_ …\n\n\n#### Examples\n`//> using jar /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.13/2.3.7/shapeless_2.13-2.3.7.jar`\n\n`//> using test.jar /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.13/2.3.7/shapeless_2.13-2.3.7.jar`\n\n`//> using sourceJar /path/to/custom-jar-sources.jar`\n\n`//> using sourceJars /path/to/custom-jar-sources.jar /path/to/another-jar-sources.jar`\n\n`//> using test.sourceJar /path/to/test-custom-jar-sources.jar`\n\n### Custom sources\n\nManually add sources to the project. Does not support chaining, sources are added only once, not recursively.\n\n`//> using file` _path_\n\n`//> using files` _path1_ _path2_ …\n\n\n#### Examples\n`//> using file utils.scala`\n\n`//> using file https://raw.githubusercontent.com/softwaremill/sttp/refs/heads/master/examples/src/main/scala/sttp/client4/examples/json/GetAndParseJsonCatsEffectCirce.scala`\n\n### Dependency\n\nAdd dependencies\n\n`//> using dep` _org_`:`name`:`ver\n\n`//> using deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using test.dep` _org_`:`name`:`ver\n\n`//> using test.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using test.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using compileOnly.dep` _org_`:`name`:`ver\n\n`//> using compileOnly.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using compileOnly.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using scalafix.dep` _org_`:`name`:`ver\n\n`//> using scalafix.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using scalafix.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n\n#### Examples\n`//> using dep com.lihaoyi::os-lib:0.9.1`\n\n`//> using dep tabby:tabby:0.2.3,url=https://github.com/bjornregnell/tabby/releases/download/v0.2.3/tabby_3-0.2.3.jar`\n\n`//> using test.dep org.scalatest::scalatest:3.2.10`\n\n`//> using test.dep org.scalameta::munit:0.7.29`\n\n`//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2`\n\n`//> using scalafix.dep com.github.xuwei-k::scalafix-rules:0.5.1`\n\n### Exclude sources\n\nExclude sources from the project\n\n`//> using exclude` _pattern_\n\n`//> using exclude` _pattern1_ _pattern2_ …\n\n\n#### Examples\n`//> using exclude utils.scala`\n\n`//> using exclude examples/* */resources/*`\n\n`//> using exclude *.sc`\n\n### JVM version\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\n`//> using jvm` _value_\n\n#### Examples\n`//> using jvm 11`\n\n`//> using jvm temurin:11`\n\n`//> using jvm graalvm:21`\n\n### Java home\n\nSets Java home used to run your application or tests\n\n`//> using javaHome` _path_\n\n#### Examples\n`//> using javaHome /Users/Me/jdks/11`\n\n### Java options\n\nAdd Java options which will be passed when running an application.\n\n`//> using javaOpt` _options_\n`//> using javaOptions` _options_`\n\n`//> using test.javaOpt` _options_\n`//> using test.javaOptions` _options_`\n\n\n#### Examples\n`//> using javaOpt -Xmx2g -Dsomething=a`\n\n`//> using test.javaOpt -Dsomething=a`\n\n### Java properties\n\nAdd Java properties\n\n`//> using javaProp` _key=value_\n\n`//> using javaProp` _key_\n\n`//> using test.javaProp` _key=value_\n\n`//> using test.javaProp` _key_\n\n\n#### Examples\n`//> using javaProp foo1=bar foo2`\n\n`//> using test.javaProp foo3=bar foo4`\n\n### Javac options\n\nAdd Javac options which will be passed when compiling sources.\n\n`//> using javacOpt` _options_\n\n`//> using javacOptions` _options_\n\n`//> using test.javacOpt` _options_\n\n`//> using test.javacOptions` _options_\n\n\n#### Examples\n`//> using javacOpt -source 1.8 -target 1.8`\n\n`//> using test.javacOpt -source 1.8 -target 1.8`\n\n### Main class\n\nSpecify default main class\n\n`//> using mainClass` _main-class_\n\n#### Examples\n`//> using mainClass HelloWorld`\n\n### ObjectWrapper\n\nSet the default code wrapper for scripts to object wrapper\n\n`//> using objectWrapper`\n\n#### Examples\n`//> using objectWrapper`\n\n### Packaging\n\nSet parameters for packaging\n\n`//> using packaging.packageType` _package-type_\n\n`//> using packaging.output` _destination-path_\n\n`//> using packaging.provided` _module_\n\n`//> using packaging.graalvmArgs` _args_\n\n`//> using packaging.dockerFrom` _base-docker-image_\n\n`//> using packaging.dockerImageTag` _image-tag_\n\n`//> using packaging.dockerImageRegistry` _image-registry_\n\n`//> using packaging.dockerImageRepository` _image-repository_\n\n`//> using packaging.dockerCmd` _docker-command_\n\n`//> using packaging.dockerExtraDirectories` _directories_\n`//> using packaging.dockerExtraDirectory` _directory_\n\n\n\n#### Examples\n`//> using packaging.packageType assembly`\n\n`//> using packaging.output foo`\n\n`//> using packaging.provided org.apache.spark::spark-sql`\n\n`//> using packaging.graalvmArgs --no-fallback`\n\n`//> using packaging.dockerFrom openjdk:11`\n\n`//> using packaging.dockerImageTag 1.0.0`\n\n`//> using packaging.dockerImageRegistry virtuslab`\n\n`//> using packaging.dockerImageRepository scala-cli`\n\n`//> using packaging.dockerCmd sh`\n\n`//> using packaging.dockerCmd node`\n\n`//> using packaging.dockerExtraDirectories path/to/directory1 path/to/directory2`\n\n`//> using packaging.dockerExtraDirectory path/to/directory`\n\n### Platform\n\nSet the default platform to Scala.js or Scala Native\n\n`//> using platform` (`jvm`|`scala-js`|`js`|`scala-native`|`native`)+\n\n`//> using platforms` (`jvm`|`scala-js`|`js`|`scala-native`|`native`)+\n\n\n#### Examples\n`//> using platform scala-js`\n\n`//> using platforms jvm scala-native`\n\n### Publish\n\nSet parameters for publishing\n\n`//> using publish.organization` value\n\n`//> using publish.name` value\n\n`//> using publish.moduleName` value\n\n`//> using publish.version` value\n\n`//> using publish.url` value\n\n`//> using publish.license` value\n\n`//> using publish.vcs` value\n\n`//> using publish.scm` value\n\n`//> using publish.versionControl` value\n\n`//> using publish.description` value\n\n`//> using publish.developer` value\n\n`//> using publish.developers` value1 value2\n\n`//> using publish.scalaVersionSuffix` value\n\n`//> using publish.scalaPlatformSuffix` value\n\n\n\n#### Examples\n`//> using publish.organization io.github.myself`\n\n`//> using publish.name my-library`\n\n`//> using publish.moduleName scala-cli_3`\n\n`//> using publish.version 0.1.1`\n\n`//> using publish.url https://github.com/VirtusLab/scala-cli`\n\n`//> using publish.license MIT`\n\n`//> using publish.vcs https://github.com/VirtusLab/scala-cli.git`\n\n`//> using publish.vcs github:VirtusLab/scala-cli`\n\n`//> using publish.description \"Lorem ipsum dolor sit amet\"`\n\n`//> using publish.developer alexme|Alex Me|https://alex.me`\n\n`//> using publish.developers alexme|Alex Me|https://alex.me Gedochao|Gedo Chao|https://github.com/Gedochao`\n\n`//> using publish.scalaVersionSuffix _2.13`\n\n`//> using publish.scalaVersionSuffix _3`\n\n`//> using publish.scalaPlatformSuffix _sjs1`\n\n`//> using publish.scalaPlatformSuffix _native0.4`\n\n### Publish (CI)\n\nSet CI parameters for publishing\n\n`//> using publish.ci.computeVersion` value\n\n`//> using publish.ci.repository` value\n\n`//> using publish.ci.secretKey` value\n\n\n\n#### Examples\n`//> using publish.ci.computeVersion git:tag`\n\n`//> using publish.ci.repository central-s01`\n\n`//> using publish.ci.secretKey env:PUBLISH_SECRET_KEY`\n\n### Publish (contextual)\n\nSet contextual parameters for publishing\n\n`//> using publish.computeVersion` value\n\n`//> using publish.repository` value\n\n`//> using publish.secretKey` value\n\n`//> using publish.doc` boolean\n\n\n\n#### Examples\n`//> using publish.computeVersion git:tag`\n\n`//> using publish.repository central-s01`\n\n`//> using publish.secretKey env:PUBLISH_SECRET_KEY`\n\n`//> using publish.doc false`\n\n### Python\n\nEnable Python support\n\n`//> using python`\n\n#### Examples\n`//> using python`\n\n### Repository\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\n`//> using repository` _repository_\n\n#### Examples\n`//> using repository jitpack`\n\n`//> using repository sonatype:snapshots`\n\n`//> using repository ivy2Local`\n\n`//> using repository m2Local`\n\n`//> using repository https://maven-central.storage-download.googleapis.com/maven2`\n\n### Resource directories\n\nManually add a resource directory to the class path\n\n`//> using resourceDir` _path_\n\n`//> using resourceDirs` _path1_ _path2_ …\n\n`//> using test.resourceDir` _path_\n\n`//> using test.resourceDirs` _path1_ _path2_ …\n\n\n\n#### Examples\n`//> using resourceDir ./resources`\n\n`//> using test.resourceDir ./resources`\n\n### Scala Native options\n\nAdd Scala Native options\n\n`//> using nativeGc` **immix**_|commix|boehm|none_\n\n`//> using nativeMode` **debug**_|release-fast|release-size|release-full_\n\n`//> using nativeLto` **none**_|full|thin_\n\n`//> using nativeVersion` _value_\n\n`//> using nativeCompile` _value1_ _value2_ …\n\n`//> using nativeCCompile` _value1_ _value2_ …\n\n`//> using nativeCppCompile` _value1_ _value2_ …\n\n`//> using nativeLinking` _value1_ _value2_ …\n\n`//> using nativeClang` _value_\n\n`//> using nativeClangPP` _value_\n\n`//> using nativeClangPp` _value_\n\n`//> using nativeEmbedResources` _true|false_\n\n`//> using nativeEmbedResources`\n\n`//> using nativeTarget` _application|library-dynamic|library-static_\n\n`//> using nativeMultithreading` _true|false_\n\n`//> using nativeMultithreading`\n\n#### Examples\n`//> using nativeGc immix`\n\n`//> using nativeMode debug`\n\n`//> using nativeLto full`\n\n`//> using nativeVersion 0.5.10`\n\n`//> using nativeCompile -flto=thin`\n\n`//> using nativeCCompile -std=c17`\n\n`//> using nativeCppCompile -std=c++17 -fcxx-exceptions`\n\n`//> using nativeLinking -flto=thin`\n\n`//> using nativeClang ./clang`\n\n`//> using nativeClangPP ./clang++`\n\n`//> using nativeEmbedResources`\n\n`//> using nativeEmbedResources true`\n\n`//> using nativeTarget library-dynamic`\n\n`//> using nativeMultithreading`\n\n`//> using nativeMultithreading false`\n\n### Scala version\n\nSet the default Scala version\n\n`//> using scala` _version_+\n\n#### Examples\n`//> using scala 3.0.2`\n\n`//> using scala 2.13`\n\n`//> using scala 2`\n\n`//> using scala 2.13.6 2.12.16`\n\n### Scala.js options\n\nAdd Scala.js options\n\n\n`//> using jsVersion` _value_\n\n`//> using jsMode` _value_\n\n`//> using jsNoOpt` _true|false_\n\n`//> using jsNoOpt`\n\n`//> using jsModuleKind` _value_\n\n`//> using jsCheckIr` _true|false_\n\n`//> using jsCheckIr`\n\n`//> using jsEmitSourceMaps` _true|false_\n\n`//> using jsEmitSourceMaps`\n\n`//> using jsEsModuleImportMap` _value_\n\n`//> using jsSmallModuleForPackage` _value1_ _value2_ …\n\n`//> using jsDom` _true|false_\n\n`//> using jsDom`\n\n`//> using jsHeader` _value_\n\n`//> using jsAllowBigIntsForLongs` _true|false_\n\n`//> using jsAllowBigIntsForLongs`\n\n`//> using jsAvoidClasses` _true|false_\n\n`//> using jsAvoidClasses`\n\n`//> using jsAvoidLetsAndConsts` _true|false_\n\n`//> using jsAvoidLetsAndConsts`\n\n`//> using jsModuleSplitStyleStr` _value_\n\n`//> using jsEsVersionStr` _value_\n    \n`//> using jsEmitWasm` _true|false_\n\n`//> using jsEmitWasm`\n\n\n#### Examples\n`//> using jsVersion 1.20.2`\n\n`//> using jsMode mode`\n\n`//> using jsNoOpt`\n\n`//> using jsModuleKind common`\n\n`//> using jsCheckIr`\n\n`//> using jsEmitSourceMaps`\n\n`//> using jsEsModuleImportMap importmap.json`\n\n`//> using jsSmallModuleForPackage test`\n\n`//> using jsDom`\n\n`//> using jsHeader \"#!/usr/bin/env node\n\"`\n\n`//> using jsAllowBigIntsForLongs`\n\n`//> using jsAvoidClasses`\n\n`//> using jsAvoidLetsAndConsts`\n\n`//> using jsModuleSplitStyleStr smallestmodules`\n\n`//> using jsEsVersionStr es2017`\n\n`//> using jsEmitWasm`\n\n### Test framework\n\nSet the test framework\n\n`//> using testFramework`  _class-name_\n\n#### Examples\n`//> using testFramework utest.runner.Framework`\n\n`//> using test.frameworks utest.runner.Framework munit.Framework`\n\n### Toolkit\n\nUse a toolkit as dependency (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\n`//> using toolkit` _version_\n\n`//> using test.toolkit` _version_\n\n\n#### Examples\n`//> using toolkit 0.8.0`\n\n`//> using toolkit default`\n\n`//> using test.toolkit default`\n\n### Watch additional inputs\n\nWatch additional files or directories when using watch mode\n\n`//> using watching` _path_\n\n`//> using watching` _path1_ _path2_ …\n\n\n\n#### Examples\n`//> using watching ./data`\n\n\n## target directives\n\n### Platform\n\nRequire a Scala platform for the current file\n\n`//> using target.platform` _platform_\n\n#### Examples\n`//> using target.platform scala-js`\n\n`//> using target.platform scala-js scala-native`\n\n`//> using target.platform jvm`\n\n### Scala version\n\nRequire a Scala version for the current file\n\n`//> using target.scala` _version_\n\n#### Examples\n`//> using target.scala 3`\n\n### Scala version bounds\n\nRequire a Scala version for the current file\n\n`//> using target.scala.>=` _version_\n\n#### Examples\n`//> using target.scala.>= 2.13`\n\n`//> using target.scala.< 3.0.2`\n\n### Scope\n\nRequire a scope for the current file\n\n`//> using target.scope` _scope_\n\n#### Examples\n`//> using target.scope test`\n\n"
  },
  {
    "path": "website/docs/reference/env-vars.md",
    "content": "---\ntitle: Environment variables\nsidebar_position: 7\n---\n\nScala CLI uses environment variables to configure its behavior.\nBelow you can find a list of environment variables used and recognized by Scala CLI.\n\nHowever, it should by no means be treated as an exhaustive list.\nSome tools and libraries Scala CLI integrates with may have their own, which may or may not be listed here.\n\n\n## Scala CLI\n  - `SCALA_CLI_CONFIG`: Scala CLI configuration file path\n  - `SCALA_CLI_HOME`: Scala CLI home directory\n  - `SCALA_CLI_INTERACTIVE`: Interactive mode toggle\n  - `SCALA_CLI_INTERACTIVE_INPUTS`: Interactive mode inputs\n  - `SCALA_CLI_POWER`: Power mode toggle\n  - `SCALA_CLI_PRINT_STACK_TRACES`: Print stack traces toggle\n  - `SCALA_CLI_SODIUM_JNI_ALLOW`: Allow to load libsodiumjni\n  - `SCALA_CLI_VENDORED_ZIS`: Toggle io.github.scala_cli.zip.ZipInputStream\n\n## Java\n  - `JAVA_HOME`: Java installation directory\n  - `JAVA_OPTS`: Java options\n  - `JDK_JAVA_OPTIONS`: JDK Java options\n\n## Bloop\n  - `BLOOP_COMPUTATION_CORES`: ⚡ Number of computation cores to be used\n  - `BLOOP_DAEMON_DIR`: ⚡ Bloop daemon directory\n  - `BLOOP_JAVA_OPTS`: ⚡ Bloop Java options\n  - `BLOOP_MODULE`: ⚡ Bloop default module\n  - `BLOOP_PORT`: ⚡ Bloop default port\n  - `BLOOP_SCALA_VERSION`: ⚡ Bloop default Scala version\n  - `BLOOP_VERSION`: ⚡ Bloop default version\n  - `BLOOP_SERVER`: ⚡ Bloop default host\n  - `SCALA_CLI_EXTRA_TIMEOUT`: ⚡ Extra timeout\n\n## Coursier\n  - `COURSIER_BIN_DIR`: Coursier app binaries directory\n  - `COURSIER_CACHE`: Coursier cache location\n  - `COURSIER_CONFIG_DIR`: Coursier configuration directory\n  - `COURSIER_CREDENTIALS`: Coursier credentials\n  - `INSIDE_EMACS`: Emacs toggle\n  - `COURSIER_EXPERIMENTAL`: Experimental mode toggle\n  - `COURSIER_JNI`: Coursier JNI toggle\n  - `COURSIER_MODE`: Coursier mode (can be set to 'offline')\n  - `COURSIER_NO_TERM`: Terminal toggle\n  - `COURSIER_PROGRESS`: Progress bar toggle\n  - `COURSIER_REPOSITORIES`: Coursier repositories\n  - `COURSIER_VENDORED_ZIS`: Toggle io.github.scala_cli.zip.ZipInputStream\n  - `CS_MAVEN_HOME`: Coursier Maven home directory\n\n## Spark\n  - `SPARK_HOME`: ⚡ Spark installation directory\n\n## Miscellaneous\n  - `PATH`: The app path variable\n  - `DYLD_LIBRARY_PATH`: Runtime library paths on Mac OS X\n  - `LD_LIBRARY_PATH`: Runtime library paths on Linux\n  - `PATHEXT`: Executable file extensions on Windows\n  - `SHELL`: The currently used shell\n  - `VCVARSALL`: Visual C++ Redistributable Runtimes\n  - `ZDOTDIR`: Zsh configuration directory\n\n## Internal\n  - `CI`: ⚡ Marker for running on the CI\n\n"
  },
  {
    "path": "website/docs/reference/password-options.md",
    "content": "---\ntitle: Password options ⚡️\nsidebar_position: 8\n---\n\n:::caution\nUsing password options is restricted and requires setting the `--power` option to be used.\nYou can pass it explicitly or set it globally by running:\n\n    scala-cli config power true\n:::\n\nSome Scala CLI options expect password / secret values. Passing passwords directly on the command-line\nposes security issues, so Scala CLI offers a few ways to work around that.\nPasswords / secrets can be passed: via environment variables, via a command printing the secret, via a file, or (not recommended)\ninline.\n\n## Environment variable\n\nPrefix the environment variable name with `env:`, like\n```text\n$ export MY_PASSWORD=1234\n$ scala-cli publish . --repo-password env:MY_PASSWORD\n```\n\n## Command printing the secret\n\nPrefix the command printing the secret with `command:`, like\n```text\n$ get-secret sonatype-s01 # command printing the secret\n1234\n$ scala-cli publish . --repo-password \"command:get-secret sonatype-s01\"\n```\n\nAlternatively, if some of the command arguments contain spaces, one can pass a JSON list:\n```text\n$ get-secret \"sonatype s01\" # command printing the secret\n1234\n$ scala-cli publish . --repo-password 'command:[\"get-secret\", \"sonatype s01\"]'\n```\n\n## File\n\nPrefix the file path with `file:`, like\n```text\n$ cat \"$HOME/.passwords/sonatype-s01\"\n1234\n$ scala-cli publish . --repo-password \"file:$HOME/.passwords/sonatype-s01\"\n```\n\n## Inline\n\nThis is the less secure way of passing secrets to Scala CLI, and should only be used\nfor debugging purposes, with non-sensitive secrets. Prefix the password / secret value\nwith `value:`, like\n```text\n$ scala-cli publish . --repo-password value:1234\n```\n"
  },
  {
    "path": "website/docs/reference/root-dir.md",
    "content": "---\ntitle: Project root directory\nsidebar_position: 5\n---\n\n## Usage\n\nScala CLI needs a root directory:\n  - to write mapped sources\n  - to write class files\n  - for Bloop\n\n## Setting root directory\n\nFirst of all, Scala CLI checks every passed input (in the same order in which inputs were passed) for the `project.scala` file:\n- If the `project.scala` file is passed explicitly as a **source**, Scala CLI sets its parent directory as the root directory.\n- If the input is a **directory**, Scala CLI looks for the `project.scala` inside this directory. If the file is found, Scala CLI sets the passed directory as the root directory.\n\nIf more than one `project.scala` file is found, Scala CLI uses only **the first one** to set the root directory and raises a **warning** saying which one was used.\n\nIf no `project.scala` files are found, Scala CLI sets the root directory based on the first file/directory input:\n- If the input is a **directory**, it is set as the root directory. \n- If the input is a **file**, Scala CLI sets its parent directory as the root directory. \n\nIf more than one file/directory input has been passed Scala CLI raises the warning saying which directory has been set as the project root directory.\n\nIf no `project.scala` files are found and no file/directory inputs have been passed, Scala CLI sets the current working directory (where Scala CLI was invoked from) as the project root directory.\n\n#### Example\n\nLet's say we have the following file structure:\n\n```\nproject\n│   project.scala\n│\n└───dir1\n│   │   file1.scala\n│   │\n│   └───dir2\n│       │   project.scala\n│       │   file2.scala\n│   \n└───dir3\n    │   project.scala\n    │   file3.scala\n```\n\nAnd the user runs the following command:\n```\nproject> scala-cli dir1/file1.scala dir1/dir2 dir3/project.scala\n```\n\nScala CLI will find 2 `project.scala` files:\n- inside `dir2`, since this directory was passed as an input and it has `project.scala` inside.\n- inside `dir3`, since `dir3/project.scala` was passed explicitly as a source\n\n`dir1/dir2` was passed before `dir3/project.scala`, so `dir2` will be set as the **root** directory for this build. \n\nSince more than one `project.scala` has been found, Scala CLI will raise the warning saying that more than one `project.scala` file has been found and `dir1/dir2` has been set as the project root directory.\n"
  },
  {
    "path": "website/docs/reference/scala-command/cli-options.md",
    "content": "---\ntitle: Command-line options\nsidebar_position: 1\n---\n\n**This document describes as scala-cli behaves if run as `scala` command. See more information in [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\n\nThis is a summary of options that are available for each subcommand of the `scala-cli` command.\n\n## Scalac options forwarding\n\n All options that start with:\n\n\n- `-g`\n- `-language`\n- `-opt`\n- `-P`\n- `-target`\n- `-V`\n- `-W`\n- `-X`\n- `-Y`\n\nare assumed to be Scala compiler options and will be propagated to Scala Compiler. This applies to all commands that uses compiler directly or indirectly.\n\n\n ## Scalac options that are directly supported in scala CLI (so can be provided as is, without any prefixes etc.):\n\n - `-encoding`\n - `-release`\n - `-color`\n - `-nowarn`\n - `-feature`\n - `-deprecation`\n - `-indent`\n - `-no-indent`\n - `-unchecked`\n - `-rewrite`\n - `-old-syntax`\n - `-new-syntax`\n\n\n\n## Benchmarking options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n*This section was automatically generated and may be empty if no options were available.*\n\n## Compilation server options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--bloop-bsp-protocol`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nProtocol to use to open a BSP connection with Bloop\n\n### `--bloop-bsp-socket`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSocket file to use to open a BSP connection with Bloop\n\n### `--bloop-host`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nHost the compilation server should bind to\n\n### `--bloop-port`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n### `--bloop-daemon-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n### `--bloop-version`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nIf Bloop isn't already running, the version we should start\n\n### `--bloop-bsp-timeout`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nMaximum duration to wait for the BSP connection to be opened\n\n### `--bloop-bsp-check-period`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDuration between checks of the BSP connection state\n\n### `--bloop-startup-timeout`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nMaximum duration to wait for the compilation server to start up\n\n### `--bloop-default-java-opts`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nInclude default JVM options for Bloop\n\n### `--bloop-java-opt`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPass java options to use by Bloop server\n\n### `--bloop-global-options-file`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBloop global options file\n\n### `--bloop-jvm`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n### `--bloop-working-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nWorking directory for Bloop, if it needs to be started\n\n### `--server`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n## Compile options\n\nAvailable in commands:\n\n[`compile`](./commands.md#compile)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--print-class-path`\n\nAliases: `-p`, `--print-classpath`\n\n`SHOULD have` per Scala Runner specification\n\nPrint the resulting class path\n\n## Config options\n\nAvailable in commands:\n\n[`config`](./commands.md#config)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--dump`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDump config DB as JSON\n\n### `--unset`\n\nAliases: `--remove`\n\n`SHOULD have` per Scala Runner specification\n\nRemove an entry from config\n\n### `--force`\n\nAliases: `-f`\n\n`SHOULD have` per Scala Runner specification\n\nForce overwriting values for key\n\n## Cross options\n\nAvailable in commands:\n\n[`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n*This section was automatically generated and may be empty if no options were available.*\n\n## Debug options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--debug`\n\n`SHOULD have` per Scala Runner specification\n\nTurn debugging on\n\n### `--debug-port`\n\n`SHOULD have` per Scala Runner specification\n\nDebug port (5005 by default)\n\n### `--debug-mode`\n\n`SHOULD have` per Scala Runner specification\n\nDebug mode (attach by default)\n\n## Dependency options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--dependency`\n\nAliases: `--dep`\n\n`MUST have` per Scala Runner specification\n\nAdd dependencies\n\n### `--compile-only-dependency`\n\nAliases: `--compile-dep`, `--compile-lib`\n\n`MUST have` per Scala Runner specification\n\nAdd compile-only dependencies\n\n### `--repository`\n\nAliases: `-r`, `--repo`\n\n`SHOULD have` per Scala Runner specification\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\n### `--compiler-plugin`\n\nAliases: `-P`, `--plugin`\n\n`MUST have` per Scala Runner specification\n\nAdd compiler plugin dependencies\n\n## Doc options\n\nAvailable in commands:\n\n[`doc`](./commands.md#doc)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--output`\n\nAliases: `-o`\n\n`MUST have` per Scala Runner specification\n\nSet the destination path\n\n### `--force`\n\nAliases: `-f`\n\n`MUST have` per Scala Runner specification\n\nOverwrite the destination directory, if it exists\n\n### `--default-scaladoc-options`\n\nAliases: `--default-scaladoc-opts`\n\n`SHOULD have` per Scala Runner specification\n\nControl if Scala CLI should use default options for scaladoc, true by default. Use `--default-scaladoc-opts:false` to not include default options.\n\n## Fmt options\n\nAvailable in commands:\n\n[`fmt` , `format` , `scalafmt`](./commands.md#fmt)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--check`\n\n`SHOULD have` per Scala Runner specification\n\nCheck if sources are well formatted\n\n### `--respect-project-filters`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nUse project filters defined in the configuration. Turned on by default, use `--respect-project-filters:false` to disable it.\n\n### `--save-scalafmt-conf`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSaves .scalafmt.conf file if it was created or overwritten\n\n### `--os-arch-suffix`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--scalafmt-tag`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--scalafmt-github-org-name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--scalafmt-extension`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--scalafmt-launcher`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--scalafmt-arg`\n\nAliases: `-F`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPass an argument to scalafmt.\n\n### `--scalafmt-conf`\n\nAliases: `--scalafmt-config`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCustom path to the scalafmt configuration file.\n\n### `--scalafmt-conf-str`\n\nAliases: `--scalafmt-conf-snippet`, `--scalafmt-config-str`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPass configuration as a string.\n\n### `--scalafmt-dialect`\n\nAliases: `--dialect`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file or inferred based on Scala version used.\n\n### `--scalafmt-version`\n\nAliases: `--fmt-version`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPass scalafmt version before running it (3.10.7 by default). If passed, this overrides whatever value is configured in the .scalafmt.conf file.\n\n## Global suppress warning options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--suppress-experimental-feature-warning`\n\nAliases: `--suppress-experimental-warning`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSuppress warnings about using experimental features\n\n### `--suppress-deprecated-feature-warning`\n\nAliases: `--suppress-deprecated-feature-warnings`, `--suppress-deprecated-warning`, `--suppress-deprecated-warnings`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSuppress warnings about using deprecated features\n\n## Help options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--usage`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPrint usage and exit\n\n### `--help`\n\nAliases: `-h`, `-help`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPrint help message and exit\n\n### `--help-full`\n\nAliases: `--full-help`, `-full-help`, `-help-full`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPrint help message, including hidden options, and exit\n\n## Help group options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--help-envs`\n\nAliases: `--env-help`, `--envs-help`, `--help-env`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow environment variable help\n\n### `--help-js`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow options for ScalaJS\n\n### `--help-native`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow options for ScalaNative\n\n### `--help-scaladoc`\n\nAliases: `--doc-help`, `--help-doc`, `--scaladoc-help`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow options for Scaladoc\n\n### `--help-repl`\n\nAliases: `--repl-help`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow options for Scala REPL\n\n### `--help-scalafmt`\n\nAliases: `--fmt-help`, `--help-fmt`, `--scalafmt-help`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow options for Scalafmt\n\n## Install completions options\n\nAvailable in commands:\n\n[`install completions` , `install-completions`](./commands.md#install-completions)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--format`\n\nAliases: `--shell`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nName of the shell, either zsh or bash\n\n### `--rc-file`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n### `--output`\n\nAliases: `-o`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCompletions output directory\n\n### `--banner`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCustom banner in comment placed in rc file\n\n### `--name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCustom completions name\n\n### `--env`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPrint completions to stdout\n\n## Java options\n\nAvailable in commands:\n\n[`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--java-opt`\n\nAliases: `-J`\n\n`MUST have` per Scala Runner specification\n\nSet Java options, such as `-Xmx1g`\n\n## Java prop options\n\nAvailable in commands:\n\n[`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--java-prop-option`\n\nAliases: `--java-prop`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nAdd java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\n\n## Jvm options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--java-home`\n\n`SHOULD have` per Scala Runner specification\n\nSet the Java home directory\n\n### `--jvm`\n\nAliases: `-j`\n\n`SHOULD have` per Scala Runner specification\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\n### `--jvm-index`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nJVM index URL\n\n### `--jvm-index-os`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nOperating system to use when looking up in the JVM index\n\n### `--jvm-index-arch`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCPU architecture to use when looking up in the JVM index\n\n### `--javac-plugin`\n\n`SHOULD have` per Scala Runner specification\n\nJavac plugin dependencies or files\n\n### `--javac-option`\n\nAliases: `--javac-opt`\n\n`SHOULD have` per Scala Runner specification\n\nJavac options\n\n### `--bsp-debug-port`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPort for BSP debugging\n\n## Logging options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--quiet`\n\nAliases: `-q`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDecrease logging verbosity\n\n### `--progress`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nUse progress bars\n\n## Main class options\n\nAvailable in commands:\n\n[`run`](./commands.md#run), [`shebang`](./commands.md#shebang)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--main-class`\n\nAliases: `-M`\n\n`MUST have` per Scala Runner specification\n\nSpecify which main class to run\n\n### `--main-class-ls`\n\nAliases: `--list-main-class`, `--list-main-classes`, `--list-main-method`, `--list-main-methods`, `--main-class-list`, `--main-method-list`, `--main-method-ls`\n\n`SHOULD have` per Scala Runner specification\n\nList main classes available in the current context\n\n## Markdown options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n*This section was automatically generated and may be empty if no options were available.*\n\n## Pgp scala signing options\n\nAvailable in commands:\n\n[`config`](./commands.md#config)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n*This section was automatically generated and may be empty if no options were available.*\n\n## Power options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--power`\n\n`MUST have` per Scala Runner specification\n\nAllows to use restricted & experimental features\n\n## Python options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n*This section was automatically generated and may be empty if no options were available.*\n\n## Run options\n\nAvailable in commands:\n\n[`run`](./commands.md#run), [`shebang`](./commands.md#shebang)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--command`\n\n`SHOULD have` per Scala Runner specification\n\nPrint the command that would have been run (one argument per line), rather than running it\n\n### `--scratch-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nTemporary / working directory where to write generated launchers\n\n### `--use-manifest`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nRun Java commands using a manifest-based class path (shortens command length)\n\n## Scala.js options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--js`\n\n`SHOULD have` per Scala Runner specification\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n### `--js-version`\n\n`SHOULD have` per Scala Runner specification\n\nThe Scala.js version (1.20.2 by default).\n\n### `--js-mode`\n\n`SHOULD have` per Scala Runner specification\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n### `--js-no-opt`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n### `--js-module-kind`\n\n`SHOULD have` per Scala Runner specification\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n### `--js-check-ir`\n\n`SHOULD have` per Scala Runner specification\n\n### `--js-emit-source-maps`\n\n`SHOULD have` per Scala Runner specification\n\nEmit source maps\n\n### `--js-source-maps-path`\n\n`SHOULD have` per Scala Runner specification\n\nSet the destination path of source maps\n\n### `--js-es-module-import-map`\n\n`SHOULD have` per Scala Runner specification\n\nA file relative to the root directory containing import maps for ES module imports\n\n### `--js-dom`\n\n`SHOULD have` per Scala Runner specification\n\nEnable jsdom\n\n### `--js-header`\n\n`SHOULD have` per Scala Runner specification\n\nA header that will be added at the top of generated .js files\n\n### `--js-allow-big-ints-for-longs`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n### `--js-avoid-classes`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n### `--js-avoid-lets-and-consts`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n### `--js-module-split-style`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n### `--js-small-module-for-package`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n### `--js-es-version`\n\n`SHOULD have` per Scala Runner specification\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n### `--js-linker-path`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPath to the Scala.js linker\n\n### `--js-cli-version`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n### `--js-cli-java-arg`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nScala.js CLI Java options\n\n### `--js-cli-on-jvm`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n## Scala Native options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--native`\n\n`SHOULD have` per Scala Runner specification\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n### `--native-version`\n\n`SHOULD have` per Scala Runner specification\n\nSet the Scala Native version (0.5.10 by default).\n\n### `--native-mode`\n\n`SHOULD have` per Scala Runner specification\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n### `--native-lto`\n\n`SHOULD have` per Scala Runner specification\n\nLink-time optimisation mode (none by default): none, full, thin\n\n### `--native-gc`\n\n`SHOULD have` per Scala Runner specification\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n### `--native-clang`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPath to the Clang command\n\n### `--native-clangpp`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPath to the Clang++ command\n\n### `--native-linking`\n\n`SHOULD have` per Scala Runner specification\n\nExtra options passed to `clang` verbatim during linking\n\n### `--native-linking-defaults`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nUse default linking settings\n\n### `--native-compile`\n\n`SHOULD have` per Scala Runner specification\n\nList of compile options\n\n### `--native-c-compile`\n\n`SHOULD have` per Scala Runner specification\n\nList of compile options (C files only)\n\n### `--native-cpp-compile`\n\n`SHOULD have` per Scala Runner specification\n\nList of compile options (C++ files only)\n\n### `--native-compile-defaults`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nUse default compile options\n\n### `--native-target`\n\n`SHOULD have` per Scala Runner specification\n\nBuild target type\n\n### `--embed-resources`\n\n`SHOULD have` per Scala Runner specification\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n### `--native-multithreading`\n\n`SHOULD have` per Scala Runner specification\n\nEnable/disable Scala Native multithreading support\n\n## Scalac options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--args-file`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nFile with scalac options.\n\n### `--scalac-option`\n\nAliases: `-O`, `--scala-opt`, `--scala-option`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\n## Scalac extra options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scalac-help`\n\nAliases: `--help-scalac`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow help for scalac. This is an alias for --scalac-option -help\n\n### `--scalac-verbose`\n\nAliases: `--verbose-scalac`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\n## Scope options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--test`\n\nAliases: `--test-scope`, `--with-test`, `--with-test-scope`\n\n`SHOULD have` per Scala Runner specification\n\nInclude test scope\n\n## Shared options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scala-version`\n\nAliases: `-S`, `--scala`\n\n`MUST have` per Scala Runner specification\n\nSet the Scala version (3.8.3 by default)\n\n### `--scala-binary-version`\n\nAliases: `-B`, `--scala-bin`, `--scala-binary`\n\n`MUST have` per Scala Runner specification\n\nSet the Scala binary version\n\n### `--extra-jars`\n\nAliases: `--class`, `--class-path`, `--classes`, `-classpath`, `--classpath`, `-cp`, `--extra-class`, `--extra-class-path`, `--extra-classes`, `--extra-jar`, `--jar`, `--jars`\n\n`MUST have` per Scala Runner specification\n\nAdd extra JARs and compiled classes to the class path\n\n### `--extra-compile-only-jars`\n\nAliases: `--compile-only-jar`, `--compile-only-jars`, `--extra-compile-only-jar`\n\n`SHOULD have` per Scala Runner specification\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\n### `--extra-source-jars`\n\nAliases: `--extra-source-jar`, `--source-jar`, `--source-jars`\n\n`SHOULD have` per Scala Runner specification\n\nAdd extra source JARs\n\n### `--resource-dirs`\n\nAliases: `--resource-dir`\n\n`MUST have` per Scala Runner specification\n\nAdd a resource directory\n\n### `--platform`\n\n`SHOULD have` per Scala Runner specification\n\nSpecify platform\n\n### `--scala-library`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--with-compiler`\n\nAliases: `-with-compiler`, `--with-scala-compiler`\n\n`MUST have` per Scala Runner specification\n\nAllows to include the Scala compiler artifacts on the classpath.\n\n### `--java`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n### `--runner`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n### `--strict-bloop-json-check`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--compilation-output`\n\nAliases: `--compile-out`, `--compile-output`, `-d`, `--destination`, `--output-directory`\n\n`MUST have` per Scala Runner specification\n\nCopy compilation results to output directory using either relative or absolute path\n\n### `--with-toolkit`\n\nAliases: `--toolkit`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\n### `--exclude`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nExclude sources\n\n## Snippet options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--script-snippet`\n\n`SHOULD have` per Scala Runner specification\n\nAllows to execute a passed string as a Scala script\n\n### `--execute-script`\n\nAliases: `-e`, `--execute-sc`, `--execute-scala-script`\n\n`SHOULD have` per Scala Runner specification\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n### `--scala-snippet`\n\n`SHOULD have` per Scala Runner specification\n\nAllows to execute a passed string as Scala code\n\n### `--execute-scala`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n### `--java-snippet`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nAllows to execute a passed string as Java code\n\n### `--execute-java`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n## Source generator options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n*This section was automatically generated and may be empty if no options were available.*\n\n## Suppress warning options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--suppress-directives-in-multiple-files-warning`\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSuppress warnings about using directives in multiple files\n\n### `--suppress-outdated-dependency-warning`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSuppress warnings about outdated dependencies in project\n\n## Test options\n\nAvailable in commands:\n\n[`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--test-frameworks`\n\nAliases: `--test-framework`\n\n`SHOULD have` per Scala Runner specification\n\nNames of the test frameworks' runner classes to use while running tests.\nSkips framework lookup and only runs passed frameworks.\n\n### `--require-tests`\n\n`SHOULD have` per Scala Runner specification\n\nFail if no test suites were run\n\n### `--test-only`\n\n`SHOULD have` per Scala Runner specification\n\nSpecify a glob pattern to filter the tests suite to be run.\n\n## Uninstall options\n\nAvailable in commands:\n\n[`uninstall`](./commands.md#uninstall)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--force`\n\nAliases: `-f`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nForce scala-cli uninstall\n\n### `--skip-cache`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDon't clear Scala CLI cache\n\n### `--binary-name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBinary name\n\n### `--bin-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBinary directory\n\n## Uninstall completions options\n\nAvailable in commands:\n\n[`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--rc-file`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n### `--banner`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCustom banner in comment placed in rc file\n\n### `--name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCustom completions name\n\n## Update options\n\nAvailable in commands:\n\n[`update`](./commands.md#update)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--binary-name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBinary name\n\n### `--bin-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBinary directory\n\n### `--force`\n\nAliases: `-f`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nForce update Scala CLI if it is outdated\n\n### `--is-internal-run`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--gh-token`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nA github token used to access GitHub. Not needed in most cases.\n\n## Verbosity options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--verbose`\n\nAliases: `-v`, `-verbose`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nIncrease verbosity (can be specified multiple times)\n\n### `--interactive`\n\nAliases: `-i`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nInteractive mode\n\n### `--actions`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nEnable actionable diagnostics\n\n## Version options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`version`](./commands.md#version)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--cli-version`\n\nAliases: `--cli`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow plain Scala CLI version only\n\n### `--scala-version`\n\nAliases: `--scala`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nShow plain Scala version only\n\n### `--gh-token`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nA github token used to access GitHub. Not needed in most cases.\n\n### `--offline`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDon't check for the newest available Scala CLI version upstream\n\n## Watch options\n\nAvailable in commands:\n\n[`compile`](./commands.md#compile), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--watch`\n\nAliases: `-w`\n\n`SHOULD have` per Scala Runner specification\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\n### `--restart`\n\nAliases: `--revolver`\n\n`SHOULD have` per Scala Runner specification\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\n## Internal options \n### Bsp options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--json-options`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCommand-line options JSON file\n\n### `--json-launcher-options`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCommand-line launcher options JSON file\n\n### `--envs`\n\nAliases: `--envs-file`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCommand-line options environment variables file\n\n### Bsp file options\n\nAvailable in commands:\n\n[`clean`](./commands.md#clean), [`setup-ide`](./commands.md#setup-ide)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--bsp-directory`\n\nAliases: `--bsp-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nCustom BSP configuration location\n\n### `--bsp-name`\n\nAliases: `--name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nName of BSP\n\n### Coursier options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--ttl`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n### `--cache`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nSet the coursier cache location\n\n### `--coursier-validate-checksums`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nEnable checksum validation of artifacts downloaded by coursier\n\n### Input options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--default-forbidden-directories`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--forbid`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### Install home options\n\nAvailable in commands:\n\n[`install-home`](./commands.md#install-home)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--scala-cli-binary-path`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### `--force`\n\nAliases: `-f`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nOverwrite if it exists\n\n### `--binary-name`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBinary name\n\n### `--env`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nPrint the update to `env` variable\n\n### `--bin-dir`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nBinary directory\n\n### Repl options\n\nAvailable in commands:\n\n[`repl` , `console`](./commands.md#repl)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--repl-dry-run`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDon't actually run the REPL, just fetch it\n\n### Semantic db options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--semantic-db`\n\nAliases: `--semanticdb`\n\n`SHOULD have` per Scala Runner specification\n\nGenerate SemanticDBs\n\n### `--semantic-db-target-root`\n\nAliases: `--semanticdb-target-root`, `--semanticdb-targetroot`\n\n`SHOULD have` per Scala Runner specification\n\nSemanticDB target root (default to the compiled classes destination directory)\n\n### `--semantic-db-source-root`\n\nAliases: `--semanticdb-source-root`, `--semanticdb-sourceroot`\n\n`SHOULD have` per Scala Runner specification\n\nSemanticDB source root (default to the project root directory)\n\n### Setup IDE options\n\nAvailable in commands:\n\n[`setup-ide`](./commands.md#setup-ide)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--charset`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\n### Workspace options\n\nAvailable in commands:\n\n[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`doc`](./commands.md#doc), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test)\n\n<!-- Automatically generated, DO NOT EDIT MANUALLY -->\n\n### `--workspace`\n\n`IMPLEMENTATION specific` per Scala Runner specification\n\nDirectory where .scala-build is written\n\n"
  },
  {
    "path": "website/docs/reference/scala-command/commands.md",
    "content": "---\ntitle: Commands\nsidebar_position: 3\n---\n\n**This document describes as scala-cli behaves if run as `scala` command. See more information in [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\n\n\n# `scala` commands\n\nThis document is a specification of the `scala` runner.\nFor now it uses documentation specific to Scala CLI but at some point it may be refactored to provide more abstract documentation.\nDocumentation is split into sections in the spirit of RFC keywords (`MUST`, `SHOULD`, `NICE TO HAVE`) including the `IMPLEMENTATION` category,\nthat is reserved for commands that need to be present for Scala CLI to work properly but should not be a part of the official API.\n\n## MUST have commands:\n\n### compile\n\nCompile Scala code.\n\nSpecific compile configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [compile](./cli-options.md#compile-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n### config\n\nConfigure global settings for Scala CLI.\n\nSyntax:\n```sh\n  scala-cli config key value\n```\nFor example, to globally set the interactive mode:\n```sh\n  scala-cli config interactive true\n```\n  \nAvailable keys:\n  - actions                                        Globally enables actionable diagnostics. Enabled by default.\n  - github.token                                   GitHub token.\n  - httpProxy.address                              HTTP proxy address.\n  - httpProxy.password                             HTTP proxy password (used for authentication).\n  - httpProxy.user                                 HTTP proxy user (used for authentication).\n  - interactive                                    Globally enables interactive mode (the '--interactive' flag).\n  - interactive-was-suggested                      Setting indicating if the global interactive mode was already suggested.\n  - java.properties                                Java properties for Scala CLI's execution.\n  - offline                                        Globally enables offline mode (the '--offline' flag).\n  - pgp.public-key                                 The PGP public key, used for signing.\n  - pgp.secret-key                                 The PGP secret key, used for signing.\n  - pgp.secret-key-password                        The PGP secret key password, used for signing.\n  - power                                          Globally enables power mode (the '--power' launcher flag).\n  - publish.credentials                            Publishing credentials, syntax: repositoryAddress value:user value:password [realm]\n  - publish.user.email                             The 'email' user detail, used for publishing.\n  - publish.user.name                              The 'name' user detail, used for publishing.\n  - publish.user.url                               The 'url' user detail, used for publishing.\n  - repositories.credentials                       Repository credentials, syntax: repositoryAddress value:user value:password [realm]\n  - repositories.default                           Default repository, syntax: https://first-repo.company.com https://second-repo.company.com\n  - repositories.mirrors                           Repository mirrors, syntax: repositories.mirrors maven:*=https://repository.company.com/maven\n  - suppress-warning.deprecated-features           Globally suppresses warnings about deprecated features.\n  - suppress-warning.directives-in-multiple-files  Globally suppresses warnings about directives declared in multiple source files.\n  - suppress-warning.experimental-features         Globally suppresses warnings about experimental features.\n  - suppress-warning.outdated-dependencies-files   Globally suppresses warnings about outdated dependencies.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/config\n\nAccepts option groups: [config](./cli-options.md#config-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### doc\n\nGenerate Scaladoc documentation.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [doc](./cli-options.md#doc-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n### repl\n\nAliases: `console`\n\nFire-up a Scala REPL.\n\nThe entire Scala CLI project's classpath is loaded to the repl.\n\nSpecific repl configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [repl](./cli-options.md#repl-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n### run\n\nCompile and run Scala code.\n\nSpecific run configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nFor a run to be successful, a main method must be present on the classpath.\n.sc scripts are an exception, as a main class is provided in their wrapper.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nTo pass arguments to the actual application, just add them after `--`, like:\n```sh\n  scala-cli run Main.scala AnotherSource.scala -- first-arg second-arg\n```\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n### shebang\n\nLike `run`, but handier for shebang scripts.\n\nThis command is equivalent to the `run` sub-command, but it changes the way\nScala CLI parses its command-line arguments in order to be compatible\nwith shebang scripts.\n\nWhen relying on the `run` sub-command, inputs and scala-cli options can be mixed,\nwhile program args have to be specified after `--`\n```sh\n  scala-cli [command] [scala-cli_options | input]... -- [program_arguments]...\n```\n\nHowever, for the `shebang` sub-command, only a single input file can be set, while all scala-cli options\nhave to be set before the input file.\nAll inputs after the first are treated as program arguments, without the need for `--`\n```sh\n  scala-cli shebang [scala-cli_options]... input [program_arguments]...\n```\n\nUsing this, it is possible to conveniently set up Unix shebang scripts. For example:\n```scala\n  #!/usr/bin/env -S scala-cli shebang --scala-version 2.13\n  println(\"Hello, world\")\n```\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [run](./cli-options.md#run-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n## SHOULD have commands:\n\n### fmt\n\nAliases: `format`, `scalafmt`\n\nFormats Scala code.\n\n`scalafmt` is used to perform the formatting under the hood.\n\nThe `.scalafmt.conf` configuration file is optional.\nDefault configuration values will be assumed by Scala CLI.\n\nAll standard Scala CLI inputs are accepted, but only Scala sources will be formatted (.scala and .sc files).\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [fmt](./cli-options.md#fmt-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n### test\n\nCompile and test Scala code.\n\nTest sources are compiled separately (after the 'main' sources), and may use different dependencies, compiler options, and other configurations.\nA source file is treated as a test source if:\n  - the file name ends with `.test.scala`\n  - the file comes from a directory that is provided as input, and the relative path from that file to its original directory contains a `test` directory\n  - it contains the `//> using target.scope test` directive (Experimental)\n\nSpecific test configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [java](./cli-options.md#java-options), [java prop](./cli-options.md#java-prop-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [test](./cli-options.md#test-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)\n\n### version\n\nPrints the version of the Scala CLI and the default version of Scala. (which can be overridden in the project)\nIf network connection is available, this sub-command also checks if the installed Scala CLI is up-to-date.\n\nThe version of the Scala CLI is the version of the command-line tool that runs Scala programs, which\nis distinct from the Scala version of the compiler. We recommend to specify the version of the Scala compiler\nfor a project in its sources (via a using directive). Otherwise, Scala CLI falls back to the default\nScala version defined by the runner.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/version\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options)\n\n## Implementation-specific commands\n\nCommands which are used within Scala CLI and should be a part of the `scala` command but aren't a part of the specification.\n\n### bsp\n\nStart BSP server.\n\nBSP stands for Build Server Protocol.\nFor more information refer to https://build-server-protocol.github.io/\n\nThis sub-command is not designed to be used by a human.\nIt is normally supposed to be invoked by your IDE when a Scala CLI project is imported.\n\nDetailed documentation can be found on our website: https://scala-cli.virtuslab.org\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp](./cli-options.md#bsp-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n### clean\n\nClean the workspace.\n\nPassed inputs will establish the Scala CLI project, for which the workspace will be cleaned.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/clean\n\nAccepts option groups: [bsp file](./cli-options.md#bsp-file-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options), [workspace](./cli-options.md#workspace-options)\n\n### help\n\nPrint help message\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### install completions\n\nAliases: `install-completions`\n\nInstalls Scala CLI completions into your shell\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/completions\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [install completions](./cli-options.md#install-completions-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### install-home\n\nInstall Scala CLI in a sub-directory of the home directory\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [install home](./cli-options.md#install-home-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options)\n\n### setup-ide\n\nGenerates a BSP file that you can import into your IDE.\n\nThe setup-ide sub-command allows to pre-configure a Scala CLI project to import to an IDE with BSP support.\nIt is also ran implicitly when `compile`, `run`, `shebang` or `test` sub-commands are called.\n\nThe pre-configuration should be saved in a BSP json connection file under the path:\n```sh\n    {project-root}/.bsp/scala-cli.json\n```\n\nSpecific setup-ide configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide\n\nAccepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [bsp file](./cli-options.md#bsp-file-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [setup IDE](./cli-options.md#setup-ide-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options)\n\n### uninstall\n\nUninstalls Scala CLI.\nWorks only when installed with the installation script.\nFor detailed installation instructions refer to our website: https://scala-cli.virtuslab.org/install\n\nAccepts option groups: [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [uninstall](./cli-options.md#uninstall-options), [uninstall completions](./cli-options.md#uninstall-completions-options), [verbosity](./cli-options.md#verbosity-options)\n\n### uninstall completions\n\nAliases: `uninstall-completions`\n\nUninstalls Scala CLI completions from your shell.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/completions\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [uninstall completions](./cli-options.md#uninstall-completions-options), [verbosity](./cli-options.md#verbosity-options)\n\n### update\n\nUpdates Scala CLI.\nWorks only when installed with the installation script.\nIf Scala CLI was installed with an external tool, refer to its update methods.\n\nFor detailed installation instructions refer to our website: https://scala-cli.virtuslab.org/install\n\nAccepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [update](./cli-options.md#update-options), [verbosity](./cli-options.md#verbosity-options)\n\n"
  },
  {
    "path": "website/docs/reference/scala-command/directives.md",
    "content": "---\ntitle: Directives\nsidebar_position: 2\n---\n\n**This document describes as scala-cli behaves if run as `scala` command. See more information in [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\n\nThis document is a specification of the `scala` runner.\nFor now it uses documentation specific to Scala CLI but at some point it may be refactored to provide more abstract documentation.\nDocumentation is split into sections in the spirit of RFC keywords (`MUST`, `SHOULD`).\n\n## MUST have directives:\n\n### Compiler options\n\nAdd Scala compiler options\n\n`//> using scalacOption` _option_\n\n`//> using option` _option_\n\n`//> using scalacOptions` _option1_ _option2_ …\n\n`//> using options` _option1_ _option2_ …\n\n`//> using test.scalacOption` _option_\n\n`//> using test.option` _option_\n\n`//> using test.scalacOptions` _option1_ _option2_ …\n\n`//> using test.options` _option1_ _option2_ …\n\n\n\n#### Examples\n`//> using option -Xasync`\n\n`//> using options -Xasync -Xfatal-warnings`\n\n`//> using test.option -Xasync`\n\n`//> using test.options -Xasync -Xfatal-warnings`\n\n### Compiler plugins\n\nAdds compiler plugins\n\n`using plugin` _org_`:`_name_`:`_ver_\n\n#### Examples\n`//> using plugin org.typelevel:::kind-projector:0.13.4`\n\n### Dependency\n\nAdd dependencies\n\n`//> using dep` _org_`:`name`:`ver\n\n`//> using deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using test.dep` _org_`:`name`:`ver\n\n`//> using test.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using test.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using compileOnly.dep` _org_`:`name`:`ver\n\n`//> using compileOnly.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using compileOnly.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using scalafix.dep` _org_`:`name`:`ver\n\n`//> using scalafix.deps` _org_`:`name`:`ver _org_`:`name`:`ver\n\n`//> using scalafix.dependencies` _org_`:`name`:`ver _org_`:`name`:`ver\n\n\n#### Examples\n`//> using dep com.lihaoyi::os-lib:0.9.1`\n\n`//> using dep tabby:tabby:0.2.3,url=https://github.com/bjornregnell/tabby/releases/download/v0.2.3/tabby_3-0.2.3.jar`\n\n`//> using test.dep org.scalatest::scalatest:3.2.10`\n\n`//> using test.dep org.scalameta::munit:0.7.29`\n\n`//> using compileOnly.dep com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2`\n\n`//> using scalafix.dep com.github.xuwei-k::scalafix-rules:0.5.1`\n\n### Java options\n\nAdd Java options which will be passed when running an application.\n\n`//> using javaOpt` _options_\n`//> using javaOptions` _options_`\n\n`//> using test.javaOpt` _options_\n`//> using test.javaOptions` _options_`\n\n\n#### Examples\n`//> using javaOpt -Xmx2g -Dsomething=a`\n\n`//> using test.javaOpt -Dsomething=a`\n\n### Java properties\n\nAdd Java properties\n\n`//> using javaProp` _key=value_\n\n`//> using javaProp` _key_\n\n`//> using test.javaProp` _key=value_\n\n`//> using test.javaProp` _key_\n\n\n#### Examples\n`//> using javaProp foo1=bar foo2`\n\n`//> using test.javaProp foo3=bar foo4`\n\n### Main class\n\nSpecify default main class\n\n`//> using mainClass` _main-class_\n\n#### Examples\n`//> using mainClass HelloWorld`\n\n### Scala version\n\nSet the default Scala version\n\n`//> using scala` _version_+\n\n#### Examples\n`//> using scala 3.0.2`\n\n`//> using scala 2.13`\n\n`//> using scala 2`\n\n`//> using scala 2.13.6 2.12.16`\n\n## SHOULD have directives:\n\n### Custom JAR\n\nManually add JAR(s) to the class path\n\n`//> using jar` _path_\n\n`//> using jars` _path1_ _path2_ …\n\n`//> using test.jar` _path_\n\n`//> using test.jars` _path1_ _path2_ …\n\n`//> using source.jar` _path_\n\n`//> using source.jars` _path1_ _path2_ …\n\n`//> using test.source.jar` _path_\n\n`//> using test.source.jars` _path1_ _path2_ …\n\n\n#### Examples\n`//> using jar /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.13/2.3.7/shapeless_2.13-2.3.7.jar`\n\n`//> using test.jar /Users/alexandre/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/chuusai/shapeless_2.13/2.3.7/shapeless_2.13-2.3.7.jar`\n\n`//> using sourceJar /path/to/custom-jar-sources.jar`\n\n`//> using sourceJars /path/to/custom-jar-sources.jar /path/to/another-jar-sources.jar`\n\n`//> using test.sourceJar /path/to/test-custom-jar-sources.jar`\n\n### Custom sources\n\nManually add sources to the project. Does not support chaining, sources are added only once, not recursively.\n\n`//> using file` _path_\n\n`//> using files` _path1_ _path2_ …\n\n\n#### Examples\n`//> using file utils.scala`\n\n`//> using file https://raw.githubusercontent.com/softwaremill/sttp/refs/heads/master/examples/src/main/scala/sttp/client4/examples/json/GetAndParseJsonCatsEffectCirce.scala`\n\n### Exclude sources\n\nExclude sources from the project\n\n`//> using exclude` _pattern_\n\n`//> using exclude` _pattern1_ _pattern2_ …\n\n\n#### Examples\n`//> using exclude utils.scala`\n\n`//> using exclude examples/* */resources/*`\n\n`//> using exclude *.sc`\n\n### JVM version\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\n`//> using jvm` _value_\n\n#### Examples\n`//> using jvm 11`\n\n`//> using jvm temurin:11`\n\n`//> using jvm graalvm:21`\n\n### Java home\n\nSets Java home used to run your application or tests\n\n`//> using javaHome` _path_\n\n#### Examples\n`//> using javaHome /Users/Me/jdks/11`\n\n### Javac options\n\nAdd Javac options which will be passed when compiling sources.\n\n`//> using javacOpt` _options_\n\n`//> using javacOptions` _options_\n\n`//> using test.javacOpt` _options_\n\n`//> using test.javacOptions` _options_\n\n\n#### Examples\n`//> using javacOpt -source 1.8 -target 1.8`\n\n`//> using test.javacOpt -source 1.8 -target 1.8`\n\n### Platform\n\nSet the default platform to Scala.js or Scala Native\n\n`//> using platform` (`jvm`|`scala-js`|`js`|`scala-native`|`native`)+\n\n`//> using platforms` (`jvm`|`scala-js`|`js`|`scala-native`|`native`)+\n\n\n#### Examples\n`//> using platform scala-js`\n\n`//> using platforms jvm scala-native`\n\n### Repository\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\n`//> using repository` _repository_\n\n#### Examples\n`//> using repository jitpack`\n\n`//> using repository sonatype:snapshots`\n\n`//> using repository ivy2Local`\n\n`//> using repository m2Local`\n\n`//> using repository https://maven-central.storage-download.googleapis.com/maven2`\n\n### Resource directories\n\nManually add a resource directory to the class path\n\n`//> using resourceDir` _path_\n\n`//> using resourceDirs` _path1_ _path2_ …\n\n`//> using test.resourceDir` _path_\n\n`//> using test.resourceDirs` _path1_ _path2_ …\n\n\n\n#### Examples\n`//> using resourceDir ./resources`\n\n`//> using test.resourceDir ./resources`\n\n### Scala Native options\n\nAdd Scala Native options\n\n`//> using nativeGc` **immix**_|commix|boehm|none_\n\n`//> using nativeMode` **debug**_|release-fast|release-size|release-full_\n\n`//> using nativeLto` **none**_|full|thin_\n\n`//> using nativeVersion` _value_\n\n`//> using nativeCompile` _value1_ _value2_ …\n\n`//> using nativeCCompile` _value1_ _value2_ …\n\n`//> using nativeCppCompile` _value1_ _value2_ …\n\n`//> using nativeLinking` _value1_ _value2_ …\n\n`//> using nativeClang` _value_\n\n`//> using nativeClangPP` _value_\n\n`//> using nativeClangPp` _value_\n\n`//> using nativeEmbedResources` _true|false_\n\n`//> using nativeEmbedResources`\n\n`//> using nativeTarget` _application|library-dynamic|library-static_\n\n`//> using nativeMultithreading` _true|false_\n\n`//> using nativeMultithreading`\n\n#### Examples\n`//> using nativeGc immix`\n\n`//> using nativeMode debug`\n\n`//> using nativeLto full`\n\n`//> using nativeVersion 0.5.10`\n\n`//> using nativeCompile -flto=thin`\n\n`//> using nativeCCompile -std=c17`\n\n`//> using nativeCppCompile -std=c++17 -fcxx-exceptions`\n\n`//> using nativeLinking -flto=thin`\n\n`//> using nativeClang ./clang`\n\n`//> using nativeClangPP ./clang++`\n\n`//> using nativeEmbedResources`\n\n`//> using nativeEmbedResources true`\n\n`//> using nativeTarget library-dynamic`\n\n`//> using nativeMultithreading`\n\n`//> using nativeMultithreading false`\n\n### Scala.js options\n\nAdd Scala.js options\n\n\n`//> using jsVersion` _value_\n\n`//> using jsMode` _value_\n\n`//> using jsNoOpt` _true|false_\n\n`//> using jsNoOpt`\n\n`//> using jsModuleKind` _value_\n\n`//> using jsCheckIr` _true|false_\n\n`//> using jsCheckIr`\n\n`//> using jsEmitSourceMaps` _true|false_\n\n`//> using jsEmitSourceMaps`\n\n`//> using jsEsModuleImportMap` _value_\n\n`//> using jsSmallModuleForPackage` _value1_ _value2_ …\n\n`//> using jsDom` _true|false_\n\n`//> using jsDom`\n\n`//> using jsHeader` _value_\n\n`//> using jsAllowBigIntsForLongs` _true|false_\n\n`//> using jsAllowBigIntsForLongs`\n\n`//> using jsAvoidClasses` _true|false_\n\n`//> using jsAvoidClasses`\n\n`//> using jsAvoidLetsAndConsts` _true|false_\n\n`//> using jsAvoidLetsAndConsts`\n\n`//> using jsModuleSplitStyleStr` _value_\n\n`//> using jsEsVersionStr` _value_\n    \n`//> using jsEmitWasm` _true|false_\n\n`//> using jsEmitWasm`\n\n\n#### Examples\n`//> using jsVersion 1.20.2`\n\n`//> using jsMode mode`\n\n`//> using jsNoOpt`\n\n`//> using jsModuleKind common`\n\n`//> using jsCheckIr`\n\n`//> using jsEmitSourceMaps`\n\n`//> using jsEsModuleImportMap importmap.json`\n\n`//> using jsSmallModuleForPackage test`\n\n`//> using jsDom`\n\n`//> using jsHeader \"#!/usr/bin/env node\n\"`\n\n`//> using jsAllowBigIntsForLongs`\n\n`//> using jsAvoidClasses`\n\n`//> using jsAvoidLetsAndConsts`\n\n`//> using jsModuleSplitStyleStr smallestmodules`\n\n`//> using jsEsVersionStr es2017`\n\n`//> using jsEmitWasm`\n\n### Test framework\n\nSet the test framework\n\n`//> using testFramework`  _class-name_\n\n#### Examples\n`//> using testFramework utest.runner.Framework`\n\n`//> using test.frameworks utest.runner.Framework munit.Framework`\n\n### Toolkit\n\nUse a toolkit as dependency (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\n`//> using toolkit` _version_\n\n`//> using test.toolkit` _version_\n\n\n#### Examples\n`//> using toolkit 0.8.0`\n\n`//> using toolkit default`\n\n`//> using test.toolkit default`\n\n"
  },
  {
    "path": "website/docs/reference/scala-command/env-vars.md",
    "content": "---\ntitle: Environment variables\nsidebar_position: 7\n---\n\nScala CLI uses environment variables to configure its behavior.\nBelow you can find a list of environment variables used and recognized by Scala CLI.\n\nHowever, it should by no means be treated as an exhaustive list.\nSome tools and libraries Scala CLI integrates with may have their own, which may or may not be listed here.\n\n\n## Scala CLI\n  - `SCALA_CLI_CONFIG`: Scala CLI configuration file path\n  - `SCALA_CLI_HOME`: Scala CLI home directory\n  - `SCALA_CLI_INTERACTIVE`: Interactive mode toggle\n  - `SCALA_CLI_INTERACTIVE_INPUTS`: Interactive mode inputs\n  - `SCALA_CLI_POWER`: Power mode toggle\n  - `SCALA_CLI_PRINT_STACK_TRACES`: Print stack traces toggle\n  - `SCALA_CLI_SODIUM_JNI_ALLOW`: Allow to load libsodiumjni\n  - `SCALA_CLI_VENDORED_ZIS`: Toggle io.github.scala_cli.zip.ZipInputStream\n\n"
  },
  {
    "path": "website/docs/reference/scala-command/index.md",
    "content": "---\ntitle: Scala CLI as scala\n---\n\n# Scala CLI as an implementation of the `scala` command\n\nAs of Scala 3.5.0, Scala CLI has become the official runner for the language,\nreplacing the old runner implementation under the `scala` script.\n\nSince Scala CLI is quite feature-packed, we do not want to expose all the features and options to all the Scala users\nfrom the very beginning. Why is that?\n\n- We want to make sure that the options / commands are stable.\n- We do not want to overwhelm users with multiple options and commands.\n\nThat is why we built in a mechanism to limit the commands, options, directives in Scala CLI by default. However, it's\nstill possible to enable all features by explicitly passing the `--power` flag on the command line, or by setting it\nglobally running:\n\n```bash ignore\nscala config power true\n```\n\nAlternatively, it is also possible to rely on the `SCALA_CLI_POWER` environment variable to achieve the same:\n\n```bash ignore\nexport SCALA_CLI_POWER=true\n```\n\nTo check which options, commands and directives are supported when running Scala CLI with limited functionalities, refer\nto [options](./cli-options.md), [commands](./commands.md) and [using directives](./directives.md), respectively.\n\n## Installing Scala CLI as `scala`\n\nRefer to the [official instructions for installing Scala](https://www.scala-lang.org/download/).\n\n:::note\nA given Scala version has a paired Scala CLI version which is used by the `scala` command installed alongside it, as per\nthe [official instructions](https://www.scala-lang.org/download/).\nThis means that even when installing the latest Scala version, its `scala` command may refer to an older Scala CLI\nversion.\n\nTo get the latest stable Scala CLI launcher, refer to the [separate\n`scala-cli` installation instructions](../../../install).\n\nAlternatively, you can use the `--cli-version` launcher option to specify the Scala CLI version to use.\nThis will run the JVM launcher of the specified Scala CLI version under the hood, so do keep in mind that it may be a\nbit slower than a native launcher.\nAlso, the specified version (and potentially any of its dependencies, if they are not already installed) would be\ndownloaded if it's not available in the local cache, so it may require additional setup for isolated environments.\n\n```bash\nscala --cli-version 1.5.0 version\n# Scala CLI version: 1.5.0\n# Scala version (default): 3.5.0\n```\n\nIf the bleeding edge is what you are after, you can use the nightly version this way.\nJust keep in mind that there are no guarantees about the stability of nightly versions.\n\n```bash ignore\nscala --cli-version nightly version\n# Scala CLI version: 1.5.0-17-g00e4c88c1-SNAPSHOT\n# Scala version (default): 3.5.0\n```\n\n:::\n\n## Migrating from the old `scala` runner to Scala CLI\n\nIf you have been using the old `scala` runner and want to migrate to Scala CLI, refer\nto [the migration guide](../../guides/introduction/old-runner-migration.md).\n\n## Using the old (deprecated) `scala` runner with Scala 3.5+\n\nYou can use the (deprecated as of Scala 3.5.0) legacy runner with Scala 3.5-3.7 installed. It is available under\nthe `scala_legacy` command (deprecated as of Scala 3.7.4).\n`scala_legacy` has been removed since Scala 3.8.0.\n\n:::caution\nEven though this enables usage of the old runner under a new alias, it is recommended to\nmigrate any existing scripts and automations to Scala CLI under either `scala` or `scala-cli`. \n`scala_legacy` has been deprecated in Scala 3.7.4 and removed in 3.8.0.\n:::\n\n```bash\nscala_legacy -version\n# [warning] MainGenericRunner class is deprecated since Scala 3.5.0, and Scala CLI features will not work.\n# [warning] Please be sure to update to the Scala CLI launcher to use the new features.\n# [warning] Check the Scala 3.5.0 release notes to troubleshoot your installation.\n# [warning] The MainGenericRunner class and 'legacy_scala' command have been deprecated for removal in Scala 3.8.0.\n# [warning] Please be sure to migrate to the scala command (Scala CLI).\n# Scala code runner version 3.7.4 -- Copyright 2002-2025, LAMP/EPFL\n```\n"
  },
  {
    "path": "website/docs/reference/scala-command/runner-specification.md",
    "content": "---\ntitle: Scala Runner specification\nsidebar_position: 1\n---\n      \n\n**This document describes proposed specification for Scala runner based on Scala CLI documentation as requested per [SIP-46](https://github.com/scala/improvement-proposals/pull/46)**\n\nCommands and options are marked with MUST and SHOULD (in the RFC style) for ones applicable for Scala Runner.\nOptions and commands marked as **Implementation** are needed for smooth running of Scala CLI.\nWe recommend for those options and commands to be supported by the `scala` command (when based on Scala CLI) but not to be a part of the Scala Runner specification.\n\nThe proposed Scala runner specification should also contain supported `Using directives` defined in the dedicated [document](./directives.md)]\n\n## Scalac options forwarding\n\n All options that start with:\n\n\n- `-g`\n- `-language`\n- `-opt`\n- `-P`\n- `-target`\n- `-V`\n- `-W`\n- `-X`\n- `-Y`\n\nare assumed to be Scala compiler options and will be propagated to Scala Compiler. This applies to all commands that uses compiler directly or indirectly.\n\n\n ## Scalac options that are directly supported in scala CLI (so can be provided as is, without any prefixes etc.):\n\n - `-encoding`\n - `-release`\n - `-color`\n - `-nowarn`\n - `-feature`\n - `-deprecation`\n - `-indent`\n - `-no-indent`\n - `-unchecked`\n - `-rewrite`\n - `-old-syntax`\n - `-new-syntax`\n\n\n\n# MUST have commands\n\n## `compile` command\n**MUST have for Scala Runner specification.**\n\nCompile Scala code.\n\nSpecific compile configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/compile\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--watch**\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\nAliases: `-w`\n\n**--restart**\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\nAliases: `--revolver`\n\n**--print-class-path**\n\nPrint the resulting class path\n\nAliases: `-p` ,`--print-classpath`\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n</details>\n\n---\n\n## `config` command\n**MUST have for Scala Runner specification.**\n\nConfigure global settings for Scala CLI.\n\nSyntax:\n```sh\n  scala-cli config key value\n```\nFor example, to globally set the interactive mode:\n```sh\n  scala-cli config interactive true\n```\n  \nAvailable keys:\n  - actions                                        Globally enables actionable diagnostics. Enabled by default.\n  - github.token                                   GitHub token.\n  - httpProxy.address                              HTTP proxy address.\n  - httpProxy.password                             HTTP proxy password (used for authentication).\n  - httpProxy.user                                 HTTP proxy user (used for authentication).\n  - interactive                                    Globally enables interactive mode (the '--interactive' flag).\n  - interactive-was-suggested                      Setting indicating if the global interactive mode was already suggested.\n  - java.properties                                Java properties for Scala CLI's execution.\n  - offline                                        Globally enables offline mode (the '--offline' flag).\n  - pgp.public-key                                 The PGP public key, used for signing.\n  - pgp.secret-key                                 The PGP secret key, used for signing.\n  - pgp.secret-key-password                        The PGP secret key password, used for signing.\n  - power                                          Globally enables power mode (the '--power' launcher flag).\n  - publish.credentials                            Publishing credentials, syntax: repositoryAddress value:user value:password [realm]\n  - publish.user.email                             The 'email' user detail, used for publishing.\n  - publish.user.name                              The 'name' user detail, used for publishing.\n  - publish.user.url                               The 'url' user detail, used for publishing.\n  - repositories.credentials                       Repository credentials, syntax: repositoryAddress value:user value:password [realm]\n  - repositories.default                           Default repository, syntax: https://first-repo.company.com https://second-repo.company.com\n  - repositories.mirrors                           Repository mirrors, syntax: repositories.mirrors maven:*=https://repository.company.com/maven\n  - suppress-warning.deprecated-features           Globally suppresses warnings about deprecated features.\n  - suppress-warning.directives-in-multiple-files  Globally suppresses warnings about directives declared in multiple source files.\n  - suppress-warning.experimental-features         Globally suppresses warnings about experimental features.\n  - suppress-warning.outdated-dependencies-files   Globally suppresses warnings about outdated dependencies.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/config\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n### SHOULD have options\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--unset**\n\nRemove an entry from config\n\nAliases: `--remove`\n\n**--force**\n\nForce overwriting values for key\n\nAliases: `-f`\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--dump**\n\nDump config DB as JSON\n\n</details>\n\n---\n\n## `doc` command\n**MUST have for Scala Runner specification.**\n\nGenerate Scaladoc documentation.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/doc\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n**--output**\n\nSet the destination path\n\nAliases: `-o`\n\n**--force**\n\nOverwrite the destination directory, if it exists\n\nAliases: `-f`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--default-scaladoc-options**\n\nControl if Scala CLI should use default options for scaladoc, true by default. Use `--default-scaladoc-opts:false` to not include default options.\n\nAliases: `--default-scaladoc-opts`\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n</details>\n\n---\n\n## `repl` command\n**MUST have for Scala Runner specification.**\n\nAliases: `console`\n\nFire-up a Scala REPL.\n\nThe entire Scala CLI project's classpath is loaded to the repl.\n\nSpecific repl configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/repl\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n**--java-opt**\n\nSet Java options, such as `-Xmx1g`\n\nAliases: `-J`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--watch**\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\nAliases: `-w`\n\n**--restart**\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\nAliases: `--revolver`\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--java-prop-option**\n\nAdd java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\n\nAliases: `--java-prop`\n\n**--repl-dry-run**\n\nDon't actually run the REPL, just fetch it\n\n</details>\n\n---\n\n## `run` command\n**MUST have for Scala Runner specification.**\n\nCompile and run Scala code.\n\nSpecific run configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nFor a run to be successful, a main method must be present on the classpath.\n.sc scripts are an exception, as a main class is provided in their wrapper.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nTo pass arguments to the actual application, just add them after `--`, like:\n```sh\n  scala-cli run Main.scala AnotherSource.scala -- first-arg second-arg\n```\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/run\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n**--java-opt**\n\nSet Java options, such as `-Xmx1g`\n\nAliases: `-J`\n\n**--main-class**\n\nSpecify which main class to run\n\nAliases: `-M`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--watch**\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\nAliases: `-w`\n\n**--restart**\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\nAliases: `--revolver`\n\n**--main-class-ls**\n\nList main classes available in the current context\n\nAliases: `--main-class-list` ,`--list-main-class` ,`--list-main-classes` ,`--list-main-methods` ,`--list-main-method` ,`--main-method-list` ,`--main-method-ls`\n\n**--command**\n\nPrint the command that would have been run (one argument per line), rather than running it\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--java-prop-option**\n\nAdd java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\n\nAliases: `--java-prop`\n\n**--scratch-dir**\n\nTemporary / working directory where to write generated launchers\n\n**--use-manifest**\n\nRun Java commands using a manifest-based class path (shortens command length)\n\n</details>\n\n---\n\n## `shebang` command\n**MUST have for Scala Runner specification.**\n\nLike `run`, but handier for shebang scripts.\n\nThis command is equivalent to the `run` sub-command, but it changes the way\nScala CLI parses its command-line arguments in order to be compatible\nwith shebang scripts.\n\nWhen relying on the `run` sub-command, inputs and scala-cli options can be mixed,\nwhile program args have to be specified after `--`\n```sh\n  scala-cli [command] [scala-cli_options | input]... -- [program_arguments]...\n```\n\nHowever, for the `shebang` sub-command, only a single input file can be set, while all scala-cli options\nhave to be set before the input file.\nAll inputs after the first are treated as program arguments, without the need for `--`\n```sh\n  scala-cli shebang [scala-cli_options]... input [program_arguments]...\n```\n\nUsing this, it is possible to conveniently set up Unix shebang scripts. For example:\n```scala\n  #!/usr/bin/env -S scala-cli shebang --scala-version 2.13\n  println(\"Hello, world\")\n```\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/shebang\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n**--java-opt**\n\nSet Java options, such as `-Xmx1g`\n\nAliases: `-J`\n\n**--main-class**\n\nSpecify which main class to run\n\nAliases: `-M`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--watch**\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\nAliases: `-w`\n\n**--restart**\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\nAliases: `--revolver`\n\n**--main-class-ls**\n\nList main classes available in the current context\n\nAliases: `--main-class-list` ,`--list-main-class` ,`--list-main-classes` ,`--list-main-methods` ,`--list-main-method` ,`--main-method-list` ,`--main-method-ls`\n\n**--command**\n\nPrint the command that would have been run (one argument per line), rather than running it\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--java-prop-option**\n\nAdd java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\n\nAliases: `--java-prop`\n\n**--scratch-dir**\n\nTemporary / working directory where to write generated launchers\n\n**--use-manifest**\n\nRun Java commands using a manifest-based class path (shortens command length)\n\n</details>\n\n---\n\n# SHOULD have commands\n\n## `fmt` command\n**SHOULD have for Scala Runner specification.**\n\nAliases: `format`, `scalafmt`\n\nFormats Scala code.\n\n`scalafmt` is used to perform the formatting under the hood.\n\nThe `.scalafmt.conf` configuration file is optional.\nDefault configuration values will be assumed by Scala CLI.\n\nAll standard Scala CLI inputs are accepted, but only Scala sources will be formatted (.scala and .sc files).\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/fmt\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--check**\n\nCheck if sources are well formatted\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--respect-project-filters**\n\nUse project filters defined in the configuration. Turned on by default, use `--respect-project-filters:false` to disable it.\n\n**--save-scalafmt-conf**\n\nSaves .scalafmt.conf file if it was created or overwritten\n\n**--os-arch-suffix**\n\n\n\n**--scalafmt-tag**\n\n\n\n**--scalafmt-github-org-name**\n\n\n\n**--scalafmt-extension**\n\n\n\n**--scalafmt-launcher**\n\n\n\n**--scalafmt-arg**\n\nPass an argument to scalafmt.\n\nAliases: `-F`\n\n**--scalafmt-conf**\n\nCustom path to the scalafmt configuration file.\n\nAliases: `--scalafmt-config`\n\n**--scalafmt-conf-str**\n\nPass configuration as a string.\n\nAliases: `--scalafmt-config-str` ,`--scalafmt-conf-snippet`\n\n**--scalafmt-dialect**\n\nPass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file or inferred based on Scala version used.\n\nAliases: `--dialect`\n\n**--scalafmt-version**\n\nPass scalafmt version before running it (3.10.7 by default). If passed, this overrides whatever value is configured in the .scalafmt.conf file.\n\nAliases: `--fmt-version`\n\n</details>\n\n---\n\n## `test` command\n**SHOULD have for Scala Runner specification.**\n\nCompile and test Scala code.\n\nTest sources are compiled separately (after the 'main' sources), and may use different dependencies, compiler options, and other configurations.\nA source file is treated as a test source if:\n  - the file name ends with `.test.scala`\n  - the file comes from a directory that is provided as input, and the relative path from that file to its original directory contains a `test` directory\n  - it contains the `//> using target.scope test` directive (Experimental)\n\nSpecific test configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nMultiple inputs can be passed at once.\nPaths to directories, URLs and supported file types are accepted as inputs.\nAccepted file extensions: .scala, .sc, .java, .jar, .md, .jar, .c, .h, .zip\nFor piped inputs use the corresponding alias: _.scala, _.java, _.sc, _.md\nAll supported types of inputs can be mixed with each other.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/test\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n**--java-opt**\n\nSet Java options, such as `-Xmx1g`\n\nAliases: `-J`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n**--watch**\n\nRun the application in the background, automatically wake the thread and re-run if sources have been changed\n\nAliases: `-w`\n\n**--restart**\n\nRun the application in the background, automatically kill the process and restart if sources have been changed\n\nAliases: `--revolver`\n\n**--test-frameworks**\n\nNames of the test frameworks' runner classes to use while running tests.\nSkips framework lookup and only runs passed frameworks.\n\nAliases: `--test-framework`\n\n**--require-tests**\n\nFail if no test suites were run\n\n**--test-only**\n\nSpecify a glob pattern to filter the tests suite to be run.\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--java-prop-option**\n\nAdd java properties. Note that options equal `-Dproperty=value` are assumed to be java properties and don't require to be passed after `--java-prop`.\n\nAliases: `--java-prop`\n\n</details>\n\n---\n\n## `version` command\n**SHOULD have for Scala Runner specification.**\n\nPrints the version of the Scala CLI and the default version of Scala. (which can be overridden in the project)\nIf network connection is available, this sub-command also checks if the installed Scala CLI is up-to-date.\n\nThe version of the Scala CLI is the version of the command-line tool that runs Scala programs, which\nis distinct from the Scala version of the compiler. We recommend to specify the version of the Scala compiler\nfor a project in its sources (via a using directive). Otherwise, Scala CLI falls back to the default\nScala version defined by the runner.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/version\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--cli-version**\n\nShow plain Scala CLI version only\n\nAliases: `--cli`\n\n**--scala-version**\n\nShow plain Scala version only\n\nAliases: `--scala`\n\n**--gh-token**\n\nA github token used to access GitHub. Not needed in most cases.\n\n**--offline**\n\nDon't check for the newest available Scala CLI version upstream\n\n</details>\n\n---\n\n# IMPLEMENTATION specific commands\n\n## `bsp` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nStart BSP server.\n\nBSP stands for Build Server Protocol.\nFor more information refer to https://build-server-protocol.github.io/\n\nThis sub-command is not designed to be used by a human.\nIt is normally supposed to be invoked by your IDE when a Scala CLI project is imported.\n\nDetailed documentation can be found on our website: https://scala-cli.virtuslab.org\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--json-options**\n\nCommand-line options JSON file\n\n**--json-launcher-options**\n\nCommand-line launcher options JSON file\n\n**--envs**\n\nCommand-line options environment variables file\n\nAliases: `--envs-file`\n\n</details>\n\n---\n\n## `clean` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nClean the workspace.\n\nPassed inputs will establish the Scala CLI project, for which the workspace will be cleaned.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/clean\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--bsp-directory**\n\nCustom BSP configuration location\n\nAliases: `--bsp-dir`\n\n**--bsp-name**\n\nName of BSP\n\nAliases: `--name`\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n</details>\n\n---\n\n## `help` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nPrint help message\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n</details>\n\n---\n\n## `install-completions` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nAliases: `install-completions`\n\nInstalls Scala CLI completions into your shell\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/completions\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--format**\n\nName of the shell, either zsh or bash\n\nAliases: `--shell`\n\n**--rc-file**\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n**--output**\n\nCompletions output directory\n\nAliases: `-o`\n\n**--banner**\n\nCustom banner in comment placed in rc file\n\n**--name**\n\nCustom completions name\n\n**--env**\n\nPrint completions to stdout\n\n</details>\n\n---\n\n## `install-home` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nInstall Scala CLI in a sub-directory of the home directory\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--scala-cli-binary-path**\n\n\n\n**--force**\n\nOverwrite if it exists\n\nAliases: `-f`\n\n**--binary-name**\n\nBinary name\n\n**--env**\n\nPrint the update to `env` variable\n\n**--bin-dir**\n\nBinary directory\n\n</details>\n\n---\n\n## `setup-ide` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nGenerates a BSP file that you can import into your IDE.\n\nThe setup-ide sub-command allows to pre-configure a Scala CLI project to import to an IDE with BSP support.\nIt is also ran implicitly when `compile`, `run`, `shebang` or `test` sub-commands are called.\n\nThe pre-configuration should be saved in a BSP json connection file under the path:\n```sh\n    {project-root}/.bsp/scala-cli.json\n```\n\nSpecific setup-ide configurations can be specified with both command line options and using directives defined in sources.\nCommand line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.\nUsing directives can be defined in all supported input source file types.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/setup-ide\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n**--dependency**\n\nAdd dependencies\n\nAliases: `--dep`\n\n**--compile-only-dependency**\n\nAdd compile-only dependencies\n\nAliases: `--compile-dep` ,`--compile-lib`\n\n**--compiler-plugin**\n\nAdd compiler plugin dependencies\n\nAliases: `-P` ,`--plugin`\n\n**--scala-version**\n\nSet the Scala version (3.8.3 by default)\n\nAliases: `-S` ,`--scala`\n\n**--scala-binary-version**\n\nSet the Scala binary version\n\nAliases: `-B` ,`--scala-binary` ,`--scala-bin`\n\n**--extra-jars**\n\nAdd extra JARs and compiled classes to the class path\n\nAliases: `--jar` ,`--jars` ,`--extra-jar` ,`--class` ,`--extra-class` ,`--classes` ,`--extra-classes` ,`-classpath` ,`-cp` ,`--classpath` ,`--class-path` ,`--extra-class-path`\n\n**--resource-dirs**\n\nAdd a resource directory\n\nAliases: `--resource-dir`\n\n**--with-compiler**\n\nAllows to include the Scala compiler artifacts on the classpath.\n\nAliases: `--with-scala-compiler` ,`-with-compiler`\n\n**--compilation-output**\n\nCopy compilation results to output directory using either relative or absolute path\n\nAliases: `-d` ,`--output-directory` ,`--destination` ,`--compile-output` ,`--compile-out`\n\n### SHOULD have options\n\n**--js**\n\nEnable Scala.js. To show more options for Scala.js pass `--help-js`\n\n**--js-version**\n\nThe Scala.js version (1.20.2 by default).\n\n**--js-mode**\n\nThe Scala.js mode, for `fastLinkJS` use one of [`dev`, `fastLinkJS` or `fast`], for `fullLinkJS` use one of [`release`, `fullLinkJS`, `full`]\n\n**--js-module-kind**\n\nThe Scala.js module kind: commonjs/common, esmodule/es, nomodule/none\n\n**--js-check-ir**\n\n\n\n**--js-emit-source-maps**\n\nEmit source maps\n\n**--js-source-maps-path**\n\nSet the destination path of source maps\n\n**--js-es-module-import-map**\n\nA file relative to the root directory containing import maps for ES module imports\n\n**--js-dom**\n\nEnable jsdom\n\n**--js-header**\n\nA header that will be added at the top of generated .js files\n\n**--js-es-version**\n\nThe Scala.js ECMA Script version: es5_1, es2015, es2016, es2017, es2018, es2019, es2020, es2021\n\n**--native**\n\nEnable Scala Native. To show more options for Scala Native pass `--help-native`\n\n**--native-version**\n\nSet the Scala Native version (0.5.10 by default).\n\n**--native-mode**\n\nSet Scala Native compilation mode (debug by default): debug, release-fast, release-size, release-full\n\n**--native-lto**\n\nLink-time optimisation mode (none by default): none, full, thin\n\n**--native-gc**\n\nSet the Scala Native garbage collector (immix by default): immix, commix, boehm, none\n\n**--native-linking**\n\nExtra options passed to `clang` verbatim during linking\n\n**--native-compile**\n\nList of compile options\n\n**--native-c-compile**\n\nList of compile options (C files only)\n\n**--native-cpp-compile**\n\nList of compile options (C++ files only)\n\n**--native-target**\n\nBuild target type\n\n**--embed-resources**\n\nEmbed resources into the Scala Native binary (can be read with the Java resources API)\n\n**--native-multithreading**\n\nEnable/disable Scala Native multithreading support\n\n**--repository**\n\nAdd repositories for dependency resolution.\n\nAccepts predefined repositories supported by Coursier (like `sonatype:snapshots`, `ivy2Local` or `m2Local`) or a URL of the root of Maven repository\n\nAliases: `-r` ,`--repo`\n\n**--debug**\n\nTurn debugging on\n\n**--debug-port**\n\nDebug port (5005 by default)\n\n**--debug-mode**\n\nDebug mode (attach by default)\n\n**--java-home**\n\nSet the Java home directory\n\n**--jvm**\n\nUse a specific JVM, such as `14`, `temurin:11`, or `graalvm:21`, or `system`. scala-cli uses [coursier](https://get-coursier.io/) to fetch JVMs, so you can use `cs java --available` to list the available JVMs.\n\nAliases: `-j`\n\n**--javac-plugin**\n\nJavac plugin dependencies or files\n\n**--javac-option**\n\nJavac options\n\nAliases: `--javac-opt`\n\n**--script-snippet**\n\nAllows to execute a passed string as a Scala script\n\n**--execute-script**\n\nA synonym to --script-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\nAliases: `-e` ,`--execute-scala-script` ,`--execute-sc`\n\n**--scala-snippet**\n\nAllows to execute a passed string as Scala code\n\n**--extra-compile-only-jars**\n\nAdd extra JARs in the compilaion class path. Mainly using to run code in managed environments like Spark not to include certain depenencies on runtime ClassPath.\n\nAliases: `--compile-only-jar` ,`--compile-only-jars` ,`--extra-compile-only-jar`\n\n**--extra-source-jars**\n\nAdd extra source JARs\n\nAliases: `--source-jar` ,`--source-jars` ,`--extra-source-jar`\n\n**--platform**\n\nSpecify platform\n\n**--semantic-db**\n\nGenerate SemanticDBs\n\nAliases: `--semanticdb`\n\n**--semantic-db-target-root**\n\nSemanticDB target root (default to the compiled classes destination directory)\n\nAliases: `--semanticdb-target-root` ,`--semanticdb-targetroot`\n\n**--semantic-db-source-root**\n\nSemanticDB source root (default to the project root directory)\n\nAliases: `--semanticdb-source-root` ,`--semanticdb-sourceroot`\n\n**--test**\n\nInclude test scope\n\nAliases: `--test-scope` ,`--with-test-scope` ,`--with-test`\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--suppress-directives-in-multiple-files-warning**\n\nSuppress warnings about using directives in multiple files\n\nAliases: `--suppress-warning-directives-in-multiple-files`\n\n**--suppress-outdated-dependency-warning**\n\nSuppress warnings about outdated dependencies in project\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--js-no-opt**\n\nDisable optimalisation for Scala.js, overrides `--js-mode`\n\n**--js-allow-big-ints-for-longs**\n\nPrimitive Longs *may* be compiled as primitive JavaScript bigints\n\n**--js-avoid-classes**\n\nAvoid class'es when using functions and prototypes has the same observable semantics.\n\n**--js-avoid-lets-and-consts**\n\nAvoid lets and consts when using vars has the same observable semantics.\n\n**--js-module-split-style**\n\nThe Scala.js module split style: fewestmodules, smallestmodules, smallmodulesfor\n\n**--js-small-module-for-package**\n\nCreate as many small modules as possible for the classes in the passed packages and their subpackages.\n\n**--js-linker-path**\n\nPath to the Scala.js linker\n\n**--js-cli-version**\n\nScala.js CLI version to use for linking (1.20.2 by default).\n\n**--js-cli-java-arg**\n\nScala.js CLI Java options\n\n**--js-cli-on-jvm**\n\nWhether to run the Scala.js CLI on the JVM or using a native executable\n\n**--native-clang**\n\nPath to the Clang command\n\n**--native-clangpp**\n\nPath to the Clang++ command\n\n**--native-linking-defaults**\n\nUse default linking settings\n\n**--native-compile-defaults**\n\nUse default compile options\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--args-file**\n\nFile with scalac options.\n\n**--scalac-option**\n\nAdd a `scalac` option. Note that options starting with `-g`, `-language`, `-opt`, `-P`, `-target`, `-V`, `-W`, `-X`, and `-Y` are assumed to be Scala compiler options and don't require to be passed after `-O` or `--scalac-option`.\n\nAliases: `--scala-opt` ,`-O` ,`--scala-option`\n\n**--jvm-index**\n\nJVM index URL\n\n**--jvm-index-os**\n\nOperating system to use when looking up in the JVM index\n\n**--jvm-index-arch**\n\nCPU architecture to use when looking up in the JVM index\n\n**--bsp-debug-port**\n\nPort for BSP debugging\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--workspace**\n\nDirectory where .scala-build is written\n\n**--scalac-help**\n\nShow help for scalac. This is an alias for --scalac-option -help\n\nAliases: `--help-scalac`\n\n**--scalac-verbose**\n\nTurn verbosity on for scalac. This is an alias for --scalac-option -verbose\n\nAliases: `--verbose-scalac`\n\n**--execute-scala**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--java-snippet**\n\nAllows to execute a passed string as Java code\n\n**--execute-java**\n\nA synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly\n\n**--scala-library**\n\n\n\n**--java**\n\nDo not add dependency to Scala Standard library. This is useful, when Scala CLI works with pure Java projects.\n\n**--runner**\n\nShould include Scala CLI runner on the runtime ClassPath. Runner is added by default for application running on JVM using standard Scala versions. Runner is used to make stack traces more readable in case of application failure.\n\n**--default-forbidden-directories**\n\n\n\n**--forbid**\n\n\n\n**--help-envs**\n\nShow environment variable help\n\nAliases: `--help-env` ,`--env-help` ,`--envs-help`\n\n**--help-js**\n\nShow options for ScalaJS\n\n**--help-native**\n\nShow options for ScalaNative\n\n**--help-scaladoc**\n\nShow options for Scaladoc\n\nAliases: `--help-doc` ,`--scaladoc-help` ,`--doc-help`\n\n**--help-repl**\n\nShow options for Scala REPL\n\nAliases: `--repl-help`\n\n**--help-scalafmt**\n\nShow options for Scalafmt\n\nAliases: `--help-fmt` ,`--scalafmt-help` ,`--fmt-help`\n\n**--strict-bloop-json-check**\n\n\n\n**--with-toolkit**\n\nAdd toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.8.0, 'default' version for typelevel toolkit: 0.1.29\n\nAliases: `--toolkit`\n\n**--exclude**\n\nExclude sources\n\n**--bsp-directory**\n\nCustom BSP configuration location\n\nAliases: `--bsp-dir`\n\n**--bsp-name**\n\nName of BSP\n\nAliases: `--name`\n\n**--charset**\n\n\n\n</details>\n\n---\n\n## `uninstall` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nUninstalls Scala CLI.\nWorks only when installed with the installation script.\nFor detailed installation instructions refer to our website: https://scala-cli.virtuslab.org/install\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--bloop-bsp-protocol**\n\nProtocol to use to open a BSP connection with Bloop\n\n**--bloop-bsp-socket**\n\nSocket file to use to open a BSP connection with Bloop\n\n**--bloop-host**\n\nHost the compilation server should bind to\n\n**--bloop-port**\n\nPort the compilation server should bind to (pass `-1` to pick a random port)\n\n**--bloop-daemon-dir**\n\nDaemon directory of the Bloop daemon (directory with lock, pid, and socket files)\n\n**--bloop-version**\n\nIf Bloop isn't already running, the version we should start\n\n**--bloop-bsp-timeout**\n\nMaximum duration to wait for the BSP connection to be opened\n\n**--bloop-bsp-check-period**\n\nDuration between checks of the BSP connection state\n\n**--bloop-startup-timeout**\n\nMaximum duration to wait for the compilation server to start up\n\n**--bloop-default-java-opts**\n\nInclude default JVM options for Bloop\n\n**--bloop-java-opt**\n\nPass java options to use by Bloop server\n\n**--bloop-global-options-file**\n\nBloop global options file\n\n**--bloop-jvm**\n\nJVM to use to start Bloop (e.g. 'system|17', 'temurin:21', …)\n\n**--bloop-working-dir**\n\nWorking directory for Bloop, if it needs to be started\n\n**--server**\n\nEnable / disable usage of Bloop compilation server. Bloop is used by default so use `--server=false` to disable it. Disabling compilation server allows to test compilation in more controlled mannter (no caching or incremental compiler) but has a detrimental effect of performance.\n\n**--ttl**\n\nSpecify a TTL for changing dependencies, such as snapshots\n\n**--cache**\n\nSet the coursier cache location\n\n**--coursier-validate-checksums**\n\nEnable checksum validation of artifacts downloaded by coursier\n\n**--rc-file**\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n**--banner**\n\nCustom banner in comment placed in rc file\n\n**--name**\n\nCustom completions name\n\n**--force**\n\nForce scala-cli uninstall\n\nAliases: `-f`\n\n**--skip-cache**\n\nDon't clear Scala CLI cache\n\n**--binary-name**\n\nBinary name\n\n**--bin-dir**\n\nBinary directory\n\n</details>\n\n---\n\n## `uninstall-completions` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nAliases: `uninstall-completions`\n\nUninstalls Scala CLI completions from your shell.\n\nFor detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/completions\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--rc-file**\n\nPath to `*rc` file, defaults to `.bashrc` or `.zshrc` depending on shell\n\n**--banner**\n\nCustom banner in comment placed in rc file\n\n**--name**\n\nCustom completions name\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n</details>\n\n---\n\n## `update` command\n**IMPLEMENTATION specific for Scala Runner specification.**\n\nUpdates Scala CLI.\nWorks only when installed with the installation script.\nIf Scala CLI was installed with an external tool, refer to its update methods.\n\nFor detailed installation instructions refer to our website: https://scala-cli.virtuslab.org/install\n\n### MUST have options\n\n**--power**\n\nAllows to use restricted & experimental features\n\n<details><summary>\n\n### Implementantation specific options\n\n</summary>\n\n**--usage**\n\nPrint usage and exit\n\n**--help**\n\nPrint help message and exit\n\nAliases: `-h` ,`-help`\n\n**--help-full**\n\nPrint help message, including hidden options, and exit\n\nAliases: `--full-help` ,`-help-full` ,`-full-help`\n\n**--verbose**\n\nIncrease verbosity (can be specified multiple times)\n\nAliases: `-v` ,`-verbose`\n\n**--interactive**\n\nInteractive mode\n\nAliases: `-i`\n\n**--actions**\n\nEnable actionable diagnostics\n\n**--quiet**\n\nDecrease logging verbosity\n\nAliases: `-q`\n\n**--progress**\n\nUse progress bars\n\n**--suppress-experimental-feature-warning**\n\nSuppress warnings about using experimental features\n\nAliases: `--suppress-experimental-warning`\n\n**--suppress-deprecated-feature-warning**\n\nSuppress warnings about using deprecated features\n\nAliases: `--suppress-deprecated-warning` ,`--suppress-deprecated-warnings` ,`--suppress-deprecated-feature-warnings`\n\n**--binary-name**\n\nBinary name\n\n**--bin-dir**\n\nBinary directory\n\n**--force**\n\nForce update Scala CLI if it is outdated\n\nAliases: `-f`\n\n**--is-internal-run**\n\n\n\n**--gh-token**\n\nA github token used to access GitHub. Not needed in most cases.\n\n</details>\n\n---\n\n"
  },
  {
    "path": "website/docs/reference/scala-versions.md",
    "content": "---\ntitle: Supported scala versions\nsidebar_position: 7\n---\n\nCurrently, Scala CLI supports Scala 3, 2.13 and 2.12. The table below lists the last supported version of Scala in Scala\nCLI. If you want to use a newer Scala version,\nit is recommended to update scala-cli.\n\n| Scala CLI versions | Scala 3 | Scala 2.13 | Scala 2.12 |\n|--------------------|:-------:|-----------:|-----------:|\n| 0.0.9              |  3.0.2  |     2.13.7 |     2.12.7 |\n| 0.1.0 - 0.1.3      |  3.1.1  |     2.13.8 |    2.12.15 |\n| 0.1.4 - 0.1.7      |  3.1.2  |     2.13.8 |    2.12.15 |\n| 0.1.8              |  3.1.2  |     2.13.8 |    2.12.16 |\n| 0.1.9 - 0.1.12     |  3.1.3  |     2.13.8 |    2.12.16 |\n| 0.1.13 - 0.1.15    |  3.2.0  |     2.13.8 |    2.12.16 |\n| 0.1.16 - 0.1.17    |  3.2.0  |    2.13.10 |    2.12.17 |\n| 0.1.18 - 0.1.19    |  3.2.1  |    2.13.10 |    2.12.17 |\n| 0.1.20 - 1.0.0-RC2 |  3.2.2  |    2.13.10 |    2.12.17 |\n| 1.0.0 - 1.0.1      |  3.3.0  |    2.13.10 |    2.12.17 |\n| 1.0.1 - 1.0.4      |  3.3.0  |    2.13.11 |    2.12.18 |\n| 1.0.5 - 1.1.3      |  3.3.1  |    2.13.12 |    2.12.18 |\n| 1.2.0 - 1.2.1      |  3.4.0  |    2.13.13 |    2.12.19 |\n| 1.2.1 - 1.3.0      |  3.4.1  |    2.13.13 |    2.12.19 |\n| 1.3.1              |  3.4.1  |    2.13.14 |    2.12.19 |\n| 1.3.2 - 1.4.3      |  3.4.2  |    2.13.14 |    2.12.19 |\n| 1.5.0              |  3.5.0  |    2.13.14 |    2.12.19 |\n| 1.5.1              |  3.5.1  |    2.13.15 |    2.12.20 |\n| 1.5.2 - 1.5.4      |  3.5.2  |    2.13.15 |    2.12.20 |\n| 1.6.0 - 1.7.0      |  3.6.3  |    2.13.16 |    2.12.20 |\n| 1.7.1              |  3.6.4  |    2.13.16 |    2.12.20 |\n| 1.8.0              |  3.7.0  |    2.13.16 |    2.12.20 |\n| 1.8.1 - 1.8.4      |  3.7.1  |    2.13.16 |    2.12.20 |\n| 1.8.5 - 1.9.0      |  3.7.2  |    2.13.16 |    2.12.20 |\n| 1.9.1              |  3.7.3  |    2.13.16 |    2.12.20 |\n| 1.10.0 - 1.10.1    |  3.7.4  |    2.13.17 |    2.12.20 |\n| 1.11.0             |  3.7.4  |    2.13.18 |    2.12.21 |\n| 1.12.0             |  3.8.0  |    2.13.18 |    2.12.21 |\n| 1.12.1 - 1.12.2    |  3.8.1  |    2.13.18 |    2.12.21 |\n| 1.12.3 - 1.12.5    |  3.8.2  |    2.13.18 |    2.12.21 |\n| 1.13.0 - current   |  3.8.3  |    2.13.18 |    2.12.21 |\n\n"
  },
  {
    "path": "website/docs/release_notes.md",
    "content": "---\ntitle: Release notes\nsidebar_position: 99\n---\nimport {ChainedSnippets} from \"../src/components/MarkdownComponents.js\";\nimport ReactPlayer from 'react-player'\n\n\n# Release notes\n\n## [v1.12.5](https://github.com/VirtusLab/scala-cli/releases/tag/v1.12.5)\n\n### `--cross` support for `run`, `package` and `doc` sub-commands (experimental ⚡️)\nIt is now possible to cross-`run`, cross-`package` and cross-generate docs (`doc`) with the `--cross` command line \noption.\n- `run` runs each configured combination of Scala version and platform (e.g. JVM, Native, JS) in sequence;\n- `package` produces one artifact per cross build, with the Scala version and platform in the artifact name;\n- `doc` generates Scaladoc for each cross target into separate output directories.\n\n```scala title=cross.scala \n//> using scala 3.3 3.8\n@main def main() = println(\"Hello\")\n```\n\n```bash\nscala-cli run cross.scala --cross --power\nscala-cli package cross.scala --cross --power\nscala-cli doc cross.scala --cross -o doc-out --power\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3808](https://github.com/VirtusLab/scala-cli/pull/3808), [#4171](https://github.com/VirtusLab/scala-cli/pull/4171) & [#4183](https://github.com/VirtusLab/scala-cli/pull/4183)\n\n### Global `--offline` config key\nYou can set offline mode globally with the `config` sub-command, so Scala CLI uses the cache and skips network access \nwithout passing `--offline` every time.\n\n```bash ignore\nscala-cli config offline true\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3216](https://github.com/VirtusLab/scala-cli/pull/3216)\n\n### Watch extra paths with `--watching` (experimental ⚡️)\nUse the `--watching` option or `//> using watching` to have `--watch` re-run when files or directories outside \nyour sources change (e.g. config or assets). \n\n```bash ignore\nscala-cli run . --watch --power --watching ./config --watching ./assets\n```\n\nOr in source:\n\n```scala compile power\n//> using watching ./config ./assets\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4174](https://github.com/VirtusLab/scala-cli/pull/4174)\n\n### Local `.m2` in `publish local` (experimental ⚡️)\n`publish local` now publishes to your local Maven repository (`~/.m2`), so other local projects can depend \non the published artifacts via Maven coordinates. \n\n```bash ignore\nscala-cli publish local . --m2 --power\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4179](https://github.com/VirtusLab/scala-cli/pull/4179)\n\n### Features\n* Run all cross builds when `--cross` is passed by [@Gedochao](https://github.com/Gedochao) in [#3808](https://github.com/VirtusLab/scala-cli/pull/3808)\n* Add a global `--offline` config key by [@Gedochao](https://github.com/Gedochao) in [#3216](https://github.com/VirtusLab/scala-cli/pull/3216)\n* Support `--cross` with the `package` sub-command by [@Gedochao](https://github.com/Gedochao) in [#4171](https://github.com/VirtusLab/scala-cli/pull/4171)\n* Allow to `--watch` extra paths with `--watching` by [@Gedochao](https://github.com/Gedochao) in [#4174](https://github.com/VirtusLab/scala-cli/pull/4174)\n* Add support for `--cross` in the `doc` sub-command by [@Gedochao](https://github.com/Gedochao) in [#4183](https://github.com/VirtusLab/scala-cli/pull/4183)\n* Add support for local `.m2` in `publish local` by [@Gedochao](https://github.com/Gedochao) in [#4179](https://github.com/VirtusLab/scala-cli/pull/4179)\n\n### Fixes\n* Use Java 17 mapping when generating docs with Scala 3.8+ with `doc` by [@Gedochao](https://github.com/Gedochao) in [#4180](https://github.com/VirtusLab/scala-cli/pull/4180)\n* Make test framework discovery on Native more resilient & with better errors by [@Gedochao](https://github.com/Gedochao) in [#4185](https://github.com/VirtusLab/scala-cli/pull/4185)\n* Warn when `.java` & `.scala` sources are used in a mixed compilation with `--server=false` by [@Gedochao](https://github.com/Gedochao) in [#4181](https://github.com/VirtusLab/scala-cli/pull/4181)\n\n### Build and internal changes\n* Add LLM policy & a PR template by [@Gedochao](https://github.com/Gedochao) in [#4177](https://github.com/VirtusLab/scala-cli/pull/4177)\n* Add `AGENTS.md` by [@Gedochao](https://github.com/Gedochao) in [#4178](https://github.com/VirtusLab/scala-cli/pull/4178)\n\n### Updates\n* Bump the npm-dependencies group in /website with 3 updates by @dependabot[bot] in [#4165](https://github.com/VirtusLab/scala-cli/pull/4165)\n* Bump the github-actions group with 3 updates by @dependabot[bot] in [#4164](https://github.com/VirtusLab/scala-cli/pull/4164)\n* Update scala-cli.sh launcher for 1.12.4 by @github-actions[bot] in [#4166](https://github.com/VirtusLab/scala-cli/pull/4166)\n* Bump svgo from 3.3.2 to 3.3.3 in /website by @dependabot[bot] in [#4168](https://github.com/VirtusLab/scala-cli/pull/4168)\n* Bump immutable from 5.1.4 to 5.1.5 in /website by @dependabot[bot] in [#4167](https://github.com/VirtusLab/scala-cli/pull/4167)\n* Bump Mill to 1.1.3 (was 1.1.2) by [@Gedochao](https://github.com/Gedochao) in [#4169](https://github.com/VirtusLab/scala-cli/pull/4169)\n* Bump @algolia/client-search from 5.49.1 to 5.49.2 in /website in the npm-dependencies group by @dependabot[bot] in [#4173](https://github.com/VirtusLab/scala-cli/pull/4173)\n* Bump the github-actions group with 4 updates by @dependabot[bot] in [#4172](https://github.com/VirtusLab/scala-cli/pull/4172)\n* Update Scala 3 Next RC to 3.8.3-RC2 by [@Gedochao](https://github.com/Gedochao) in [#4175](https://github.com/VirtusLab/scala-cli/pull/4175)\n* Bump undici from 7.18.2 to 7.24.1 in /website by @dependabot[bot] in [#4182](https://github.com/VirtusLab/scala-cli/pull/4182)\n* Bump webfactory/ssh-agent from 0.9.1 to 0.10.0 in the github-actions group by @dependabot[bot] in [#4187](https://github.com/VirtusLab/scala-cli/pull/4187)\n* Bump `coursier` to 2.1.25-M24 by [@Gedochao](https://github.com/Gedochao) in [#4184](https://github.com/VirtusLab/scala-cli/pull/4184)\n* Bump sass from 1.97.3 to 1.98.0 in /website in the npm-dependencies group by @dependabot[bot] in [#4188](https://github.com/VirtusLab/scala-cli/pull/4188)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.12.4...v1.12.5\n\n## [v1.12.4](https://github.com/VirtusLab/scala-cli/releases/tag/v1.12.4)\n\nThis is just a small patch fixing a bug ([#4152](https://github.com/VirtusLab/scala-cli/issues/4152)) breaking Metals support in Scala CLI v1.12.3.\n\n### Fixes\n* Fix BSP `buildTarget/wrappedSources` for GraalVM native image by [@Gedochao](https://github.com/Gedochao) in [#4153](https://github.com/VirtusLab/scala-cli/pull/4153)\n\n### Documentation changes\n* Fix docs' links with inconsistent routing by [@Gedochao](https://github.com/Gedochao) in [#4154](https://github.com/VirtusLab/scala-cli/pull/4154)\n\n### Updates\n* Update scala-cli.sh launcher for 1.12.3 by @github-actions[bot] in [#4151](https://github.com/VirtusLab/scala-cli/pull/4151)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.12.3...v1.12.4\n\n## [v1.12.3](https://github.com/VirtusLab/scala-cli/releases/tag/v1.12.3)\n\n### Change default Scala version to 3.8.2\nThis Scala CLI version switches the default Scala version to 3.8.2.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.12.3\n# Scala version (default): 3.8.2\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4143](https://github.com/VirtusLab/scala-cli/pull/4143)\n\n### Fixes\n* Restore Scala 2 nightlies by [@Gedochao](https://github.com/Gedochao) in [#4140](https://github.com/VirtusLab/scala-cli/pull/4140)\n* Fix Scala version validation to accept locally built compiler by [@Gedochao](https://github.com/Gedochao) in [#4141](https://github.com/VirtusLab/scala-cli/pull/4141)\n* Ensure Scala CLI correctly refers to the latest Scala 2.12/2.13 nightly and recovers from errors by [@Gedochao](https://github.com/Gedochao) in [#4142](https://github.com/VirtusLab/scala-cli/pull/4142)\n* Restore GraalVM image support for older CPUs by [@Gedochao](https://github.com/Gedochao) in [#4150](https://github.com/VirtusLab/scala-cli/pull/4150)\n\n### Build and internal changes\n* Cut down on integration test suites by [@Gedochao](https://github.com/Gedochao) in [#4123](https://github.com/VirtusLab/scala-cli/pull/4123)\n* Add `coursier/cache-action` on the CI by [@Gedochao](https://github.com/Gedochao) in [#4126](https://github.com/VirtusLab/scala-cli/pull/4126)\n* Don't install Bloop separately on the CI by [@Gedochao](https://github.com/Gedochao) in [#4130](https://github.com/VirtusLab/scala-cli/pull/4130)\n* Migrate to Mill 1.1.2 (was 1.0.6) by [@Gedochao](https://github.com/Gedochao) in [#4086](https://github.com/VirtusLab/scala-cli/pull/4086)\n\n### Documentation changes\n* docs: Add release notes for 1.12.2 by [@tgodzik](https://github.com/tgodzik) in [#4111](https://github.com/VirtusLab/scala-cli/pull/4111)\n* Fix shebang in Scala script example by [@sake92](https://github.com/sake92) in [#4117](https://github.com/VirtusLab/scala-cli/pull/4117)\n* Cherry pick #4073, #4072 & #4123 to the `stable` branch by [@Gedochao](https://github.com/Gedochao) & [@andrzejressel](https://github.com/andrzejressel) in [#4129](https://github.com/VirtusLab/scala-cli/pull/4129)\n* Back port of documentation changes to main  by [@Gedochao](https://github.com/Gedochao) in [#4134](https://github.com/VirtusLab/scala-cli/pull/4134)\n\n### Updates\n* Update scala-cli.sh launcher for 1.12.2 by @github-actions[bot] in [#4116](https://github.com/VirtusLab/scala-cli/pull/4116)\n* Bump webpack from 5.103.0 to 5.105.0 in /website by @dependabot[bot] in [#4119](https://github.com/VirtusLab/scala-cli/pull/4119)\n* Update sbt, scripted-plugin to 1.12.2 by @scala-steward in [#4110](https://github.com/VirtusLab/scala-cli/pull/4110)\n* Bump @algolia/client-search from 5.47.0 to 5.48.0 in /website by @dependabot[bot] in [#4121](https://github.com/VirtusLab/scala-cli/pull/4121)\n* Bump Ammonite to 3.0.8 (was 3.0.7) by [@Gedochao](https://github.com/Gedochao) in [#4083](https://github.com/VirtusLab/scala-cli/pull/4083)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.10.7 by @scala-steward in [#4125](https://github.com/VirtusLab/scala-cli/pull/4125)\n* Update Scala 3 Next RC to 3.8.2-RC2 by @scala-steward in [#4124](https://github.com/VirtusLab/scala-cli/pull/4124)\n* Update os-lib to 0.11.8 by @scala-steward in [#4101](https://github.com/VirtusLab/scala-cli/pull/4101)\n* Update munit to 1.2.2 by @scala-steward in [#4097](https://github.com/VirtusLab/scala-cli/pull/4097)\n* Update semanticdb-shared_2.13 to 4.14.7 by @scala-steward in [#4103](https://github.com/VirtusLab/scala-cli/pull/4103)\n* Update pprint to 0.9.6 by @scala-steward in [#4092](https://github.com/VirtusLab/scala-cli/pull/4092)\n* Update asm to 9.9.1 by @scala-steward in [#4094](https://github.com/VirtusLab/scala-cli/pull/4094)\n* Update semanticdb-shared_2.13 to 4.15.2 by @scala-steward in [#4132](https://github.com/VirtusLab/scala-cli/pull/4132)\n* Update metaconfig-typesafe-config to 0.18.2 by @scala-steward in [#4096](https://github.com/VirtusLab/scala-cli/pull/4096)\n* Update jsoniter-scala-core, ... to 2.38.8 by @scala-steward in [#4089](https://github.com/VirtusLab/scala-cli/pull/4089)\n* Update scalafix-interfaces to 0.14.5 by @scala-steward in [#4088](https://github.com/VirtusLab/scala-cli/pull/4088)\n* Update jsoup to 1.22.1 by @scala-steward in [#4093](https://github.com/VirtusLab/scala-cli/pull/4093)\n* Bump dependencies of the docs website (dependabot sync + cleanup) by [@Gedochao](https://github.com/Gedochao) in [#4135](https://github.com/VirtusLab/scala-cli/pull/4135)\n* Bump @svta/cml-utils from 1.0.1 to 1.4.0 in /website by @dependabot[bot] in [#4137](https://github.com/VirtusLab/scala-cli/pull/4137)\n* Migrate to Mill 1.1.2 (was 1.0.6) by [@Gedochao](https://github.com/Gedochao) in [#4086](https://github.com/VirtusLab/scala-cli/pull/4086)\n* Update Bloop to 2.0.19 (was 2.0.17) by @scala-steward in [#4109](https://github.com/VirtusLab/scala-cli/pull/4109)\n* Bump Scala 3 Next RC to 3.8.2-RC3 by [@Gedochao](https://github.com/Gedochao) in [#4136](https://github.com/VirtusLab/scala-cli/pull/4136)\n* Bump @algolia/client-search from 5.48.1 to 5.49.0 in /website by @dependabot[bot] in [#4145](https://github.com/VirtusLab/scala-cli/pull/4145)\n* Bump @svta/cml-structured-field-values from 1.0.1 to 1.1.2 in /website by @dependabot[bot] in [#4144](https://github.com/VirtusLab/scala-cli/pull/4144)\n* Bump Scala 3 Next to 3.8.2 by [@Gedochao](https://github.com/Gedochao) in [#4143](https://github.com/VirtusLab/scala-cli/pull/4143)\n* Bump SBT to 1.12.4 by [@Gedochao](https://github.com/Gedochao) in [#4146](https://github.com/VirtusLab/scala-cli/pull/4146)\n* Bump Scala 3 Next RC to 3.8.3-RC1 by [@Gedochao](https://github.com/Gedochao) in [#4147](https://github.com/VirtusLab/scala-cli/pull/4147)\n\n## New Contributors\n* [@sake92](https://github.com/sake92) made their first contribution in [#4117](https://github.com/VirtusLab/scala-cli/pull/4117)\n* [@andrzejressel](https://github.com/andrzejressel) made their first contribution in [#4129](https://github.com/VirtusLab/scala-cli/pull/4129)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.12.2...v1.12.3\n\n## [v1.12.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.12.2)\n\n### Old sonatype snapshot is causing Scala CLI to timeout\n\nUntil the recent migration to new Sonatype Central repository there was an older snapshots repository in use. Unfortuntately, last week it started to make requests to it timeout and hang Scala CLI. We decided remove any references to the old repository, so older (one might say ancient) Scala CLI nightly versions will not be available. This was done by [@tgodzik](https://github.com/tgodzik) in [#4106](https://github.com/VirtusLab/scala-cli/pull/4106).\n\n### Updates\n\n* Bump react and @types/react in /website by @dependabot[bot] in [#4079](https://github.com/VirtusLab/scala-cli/pull/4079)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.12.1...v1.12.2\n\n## [v1.12.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.12.1)\n\n### Change default Scala version to 3.8.1\nThis Scala CLI version switches the default Scala version to 3.8.1.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.12.1\n# Scala version (default): 3.8.1\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4065](https://github.com/VirtusLab/scala-cli/pull/4065)\n\n### Change default Scala Native version to 0.5.10\nThis Scala CLI version switches the default Scala Native version to 0.5.10.\n\n```bash\nscala-cli -e 'println(\"Hello from Scala Native 0.5.10!\")' --native\n# Compiling project (Scala 3.8.1, Scala Native 0.5.10)\n# Compiled project (Scala 3.8.1, Scala Native 0.5.10)\n# [info] Linking (multithreadingEnabled=detect) (725 ms)\n# [info] Discovered 903 classes and 5554 methods after classloading\n# [info] Checking intermediate code (quick) (31 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (273 ms)\n# [info] Discovered 512 classes and 2629 methods after classloading\n# [info] Checking intermediate code (quick) (7 ms)\n# [info] Discovered 493 classes and 2002 methods after optimization\n# [info] Optimizing (debug mode) (447 ms)\n# [info] Produced 12 LLVM IR files\n# [info] Generating intermediate code (826 ms)\n# [info] Compiling to native code (5461 ms)\n# [info] Linking with [pthread, dl, m]\n# [info] Linking native code (immix gc, none lto) (257 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (7688 ms)\n# Hello from Scala Native 0.5.10!\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4078](https://github.com/VirtusLab/scala-cli/pull/4078)\n\n### Fixes\n* fix: openjdk:17-slim is not available. using 17.0.2-slim instead by [@kaplan-shaked](https://github.com/kaplan-shaked) in [#4057](https://github.com/VirtusLab/scala-cli/pull/4057)\n\n### Build and internal changes\n* Fix Scala 2.13 nightly tests by [@Gedochao](https://github.com/Gedochao) in [#4063](https://github.com/VirtusLab/scala-cli/pull/4063)\n* Switch the CI to GitHub Linux arm64 runners by [@Gedochao](https://github.com/Gedochao) in [#4064](https://github.com/VirtusLab/scala-cli/pull/4064)\n\n### Updates\n* Bump @algolia/client-search from 5.46.2 to 5.46.3 in /website by @dependabot[bot] in [#4062](https://github.com/VirtusLab/scala-cli/pull/4062)\n* Update scala-cli.sh launcher for 1.12.0 by @github-actions[bot] in [#4054](https://github.com/VirtusLab/scala-cli/pull/4054)\n* Bump `scalafmt` to 3.10.4 (was 3.10.2) by [@Gedochao](https://github.com/Gedochao) in [#4061](https://github.com/VirtusLab/scala-cli/pull/4061)\n* Bump `jgit` to 7.5.0.202512021534-r (was 7.3.0.202506031305-r) by [@Gedochao](https://github.com/Gedochao) in [#4058](https://github.com/VirtusLab/scala-cli/pull/4058)\n* Bump Scala Toolkit to 0.8.0 (was 0.7.0) by [@Gedochao](https://github.com/Gedochao) in [#4060](https://github.com/VirtusLab/scala-cli/pull/4060)\n* Bump lodash from 4.17.21 to 4.17.23 in /website by @dependabot[bot] in [#4066](https://github.com/VirtusLab/scala-cli/pull/4066)\n* Bump Scala 3 Next to 3.8.1 by [@Gedochao](https://github.com/Gedochao) in [#4065](https://github.com/VirtusLab/scala-cli/pull/4065)\n* Switch from `graalvm-java17` to `graalvm-community` by [@Gedochao](https://github.com/Gedochao) in [#3459](https://github.com/VirtusLab/scala-cli/pull/3459)\n* Bump Ammonite to 3.0.7 (was 3.0.6) by [@Gedochao](https://github.com/Gedochao) in [#4070](https://github.com/VirtusLab/scala-cli/pull/4070)\n* Bump Scala 3 Next RC to 3.8.2-RC1 by [@Gedochao](https://github.com/Gedochao) in [#4071](https://github.com/VirtusLab/scala-cli/pull/4071)\n* Bump Scala Native to 0.5.10 (was 0.5.9) by [@Gedochao](https://github.com/Gedochao) in [#4078](https://github.com/VirtusLab/scala-cli/pull/4078)\n\n## New Contributors\n* [@kaplan-shaked](https://github.com/kaplan-shaked) made their first contribution in [#4057](https://github.com/VirtusLab/scala-cli/pull/4057)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.12.0...v1.12.1\n\n## [v1.12.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.12.0)\n\n### Change default Scala version to 3.8.0\nThis Scala CLI version switches the default Scala version to 3.8.0.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.12.0\n# Scala version (default): 3.8.0\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4049](https://github.com/VirtusLab/scala-cli/pull/4049)\n\n### Support for Scala.js 1.20.2\nThis Scala CLI version adds support for Scala.js 1.20.2.\n\n```bash\nscala-cli -e 'println(\"Hello\")' --js\n# Compiling project (Scala 3.8.0, Scala.js 1.20.2)\n# Compiled project (Scala 3.8.0, Scala.js 1.20.2)\n# Hello\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4040](https://github.com/VirtusLab/scala-cli/pull/4040)\n\n### New aliases for RC and nightly Scala versions\nThis Scala CLI version introduces dedicated aliases for calling the latest Scala Release Candidate versions in a given series (including LTS).\n\n```bash\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S rc\n# 3.8.1-RC1\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S 3.rc\n# 3.8.1-RC1\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S 3.8.rc\n# 3.8.1-RC1\nscala-cli -e 'println(dotty.tools.dotc.config.Properties.simpleVersionString)' -S 3.lts.rc --with-compiler\n# 3.3.7-RC2\nscala-cli -e 'println(dotty.tools.dotc.config.Properties.simpleVersionString)' -S lts.rc --with-compiler\n# 3.3.7-RC2\n```\n\nDedicated default and LTS nightly aliases are also provided.\n\n```bash\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S nightly\n# 3.8.2-RC1-bin-20260115-6151803-NIGHTLY\nscala-cli -e 'println(dotty.tools.dotc.config.Properties.simpleVersionString)' -S lts.nightly --with-compiler\n# 3.3.8-RC1-bin-20260112-d35b2d4-NIGHTLY-git-d35b2d4\nscala-cli -e 'println(dotty.tools.dotc.config.Properties.simpleVersionString)' -S 3.lts.nightly --with-compiler\n# 3.3.8-RC1-bin-20260112-d35b2d4-NIGHTLY-git-d35b2d4\n```\n\nAlso note that there is no easy way to identify an RC for Scala 2 / 2.12 / 2.13 (as a particular nightly serves as the RC for those Scala distributions).\nA reasonable error is also provided when it is requested.\n\n```bash fail\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S 2.rc\n# Invalid Scala version: 2.rc. In the case of Scala 2, a particular nightly version serves as a release candidate.\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S 2.12.rc\n# Invalid Scala version: 2.12.rc. In the case of Scala 2, a particular nightly version serves as a release candidate.\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S 2.13.rc\n# Invalid Scala version: 2.13.rc. In the case of Scala 2, a particular nightly version serves as a release candidate.\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4042](https://github.com/VirtusLab/scala-cli/pull/4042) and [#4016](https://github.com/VirtusLab/scala-cli/pull/4016)\n\n### (⚡️ experimental) Support for exporting to Mill 1.0.x and overriding the Mill version with `--mill-version`\nThe `export` sub-command now allows to export to Mill 1.0.x projects.\n\n```scala title=mill-1-0-6-export.scala\n@main def main() = println(\"Let's export to Mill 1.0.6!\")\n```\n\n```bash\nscala-cli export mill-1-0-6-export.scala --mill --mill-version 1.0.6 --power\n# Exporting to a mill project...\n# Exported to: ~/mill-export/dest\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#4028](https://github.com/VirtusLab/scala-cli/pull/4028)\n\n### Features & improvements\n* Add `rc` & `*.rc` Scala version aliases by [@Gedochao](https://github.com/Gedochao) in [#4016](https://github.com/VirtusLab/scala-cli/pull/4016)\n* Support `export`-ing to Mill 1.x.y & allow to override Mill version with `--mill-version` by [@Gedochao](https://github.com/Gedochao) in [#4028](https://github.com/VirtusLab/scala-cli/pull/4028)\n* Improve Scala nightly version handling & add `lts.nightly`, `3.lts.nightly` and `nightly` Scala version tags by [@Gedochao](https://github.com/Gedochao) in [#4042](https://github.com/VirtusLab/scala-cli/pull/4042)\n\n### Fixes\n* Fix Scala 3.nightly and 3.*latest-minor*.nightly to consistently point to the same version by [@Gedochao](https://github.com/Gedochao) in [#4014](https://github.com/VirtusLab/scala-cli/pull/4014)\n* fix #4005 -  Windows native-image compile failure caused by SUBST collision by [@philwalk](https://github.com/philwalk) in [#4006](https://github.com/VirtusLab/scala-cli/pull/4006)\n\n### Documentation changes\n* Solve docs' website warnings by [@Gedochao](https://github.com/Gedochao) in [#4007](https://github.com/VirtusLab/scala-cli/pull/4007)\n* Back port of documentation changes to main by @github-actions[bot] in [#4015](https://github.com/VirtusLab/scala-cli/pull/4015)\n\n### Build and internal changes\n* Enable Scala Native tests with the `test` command on Scala 3 by [@Gedochao](https://github.com/Gedochao) in [#4018](https://github.com/VirtusLab/scala-cli/pull/4018)\n* Self-contained docker build with ARM64 publishing by [@keynmol](https://github.com/keynmol) in [#3962](https://github.com/VirtusLab/scala-cli/pull/3962)\n\n### Updates\n* Update scala-cli.sh launcher for 1.11.0 by @github-actions[bot] in [#4004](https://github.com/VirtusLab/scala-cli/pull/4004)\n* Bump Ammonite to 3.0.6 (was 3.0.5) by [@Gedochao](https://github.com/Gedochao) in [#4008](https://github.com/VirtusLab/scala-cli/pull/4008)\n* Bump actions/download-artifact from 6 to 7 by @dependabot[bot] in [#4009](https://github.com/VirtusLab/scala-cli/pull/4009)\n* Bump actions/upload-artifact from 5 to 6 by @dependabot[bot] in [#4010](https://github.com/VirtusLab/scala-cli/pull/4010)\n* Bump sass from 1.95.0 to 1.96.0 in /website by @dependabot[bot] in [#4011](https://github.com/VirtusLab/scala-cli/pull/4011)\n* Bump `react` & `react-dom` from 19.2.1 to 19.2.3 in /website by @dependabot[bot] in [#4012](https://github.com/VirtusLab/scala-cli/pull/4012)\n* Bump Mill to 0.12.17 (was 0.12.16) by [@Gedochao](https://github.com/Gedochao) in [#4020](https://github.com/VirtusLab/scala-cli/pull/4020)\n* Bump Scala Next RC to 3.8.0-RC4 by [@Gedochao](https://github.com/Gedochao) in [#4021](https://github.com/VirtusLab/scala-cli/pull/4021)\n* Bump actions/checkout from 4 to 6 by @dependabot[bot] in [#4024](https://github.com/VirtusLab/scala-cli/pull/4024)\n* Bump qs from 6.14.0 to 6.14.1 in /website by @dependabot[bot] in [#4032](https://github.com/VirtusLab/scala-cli/pull/4032)\n* Bump @algolia/client-search from 5.46.0 to 5.46.2 in /website by @dependabot[bot] in [#4030](https://github.com/VirtusLab/scala-cli/pull/4030)\n* Bump sass from 1.96.0 to 1.97.1 in /website by @dependabot[bot] in [#4027](https://github.com/VirtusLab/scala-cli/pull/4027)\n* Bump actions/upload-artifact from 5 to 6 by @dependabot[bot] in [#4023](https://github.com/VirtusLab/scala-cli/pull/4023)\n* Bump actions/download-artifact from 6 to 7 by @dependabot[bot] in [#4022](https://github.com/VirtusLab/scala-cli/pull/4022)\n* Bump react-player from 2.16.1 to 3.4.0 in /website by @dependabot[bot] in [#4025](https://github.com/VirtusLab/scala-cli/pull/4025)\n* Bump Scala 3 Next RC to 3.8.0-RC5 by [@Gedochao](https://github.com/Gedochao) in [#4033](https://github.com/VirtusLab/scala-cli/pull/4033)\n* Migrate to Mill 1.0.6 (was 0.12.17) by [@Gedochao](https://github.com/Gedochao) in [#4019](https://github.com/VirtusLab/scala-cli/pull/4019)\n* Bump libsodium to 1.0.21 (partially, except for static launchers locked at 1.0.18) by [@Gedochao](https://github.com/Gedochao) in [#4041](https://github.com/VirtusLab/scala-cli/pull/4041)\n* Bump `coursier` to 2.1.25-M23 by [@Gedochao](https://github.com/Gedochao) in [#4037](https://github.com/VirtusLab/scala-cli/pull/4037)\n* Bump Scala 3 Next RC to 3.8.0-RC6 by [@Gedochao](https://github.com/Gedochao) in [#4039](https://github.com/VirtusLab/scala-cli/pull/4039)\n* Bump Scala.js to 1.20.2 by [@Gedochao](https://github.com/Gedochao) in [#4040](https://github.com/VirtusLab/scala-cli/pull/4040)\n* Bump sass from 1.97.1 to 1.97.2 in /website by @dependabot[bot] in [#4038](https://github.com/VirtusLab/scala-cli/pull/4038)\n* Bump alpine to 3.23 & relevant libsodium to 1.0.20 by [@Gedochao](https://github.com/Gedochao) in [#4047](https://github.com/VirtusLab/scala-cli/pull/4047)\n* Bump @types/react from 19.2.7 to 19.2.8 in /website by @dependabot[bot] in [#4048](https://github.com/VirtusLab/scala-cli/pull/4048)\n* Bump Scala 3 Next to 3.8.0 by [@Gedochao](https://github.com/Gedochao) in [#4049](https://github.com/VirtusLab/scala-cli/pull/4049)\n* Bump Scala 3 Next RC to 3.8.1-RC1 by [@Gedochao](https://github.com/Gedochao) in [#4051](https://github.com/VirtusLab/scala-cli/pull/4051)\n* Bump undici from 7.16.0 to 7.18.2 in /website by @dependabot[bot] in [#4050](https://github.com/VirtusLab/scala-cli/pull/4050)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.11.0...v1.12.0\n\n## [v1.11.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.11.0)\n\n### Change default Scala versions to 2.13.18 and 2.12.21\nThis Scala CLI version switches the default Scala versions:\n- default Scala 2.13 to 2.13.18\n- default Scala 2.12 to 2.12.21\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3999](https://github.com/VirtusLab/scala-cli/pull/3999) and [#3958](https://github.com/VirtusLab/scala-cli/pull/3958)\n\n### Fall back to a legacy version of the `runner` & `test-runner` modules for JVM < 17\n\nThe newest versions of the `runner` and `test-runner` modules will require Java 17 or newer to run.\nWhen trying to use them with older JVMs, Scala CLI will now print a warning and fall back to using legacy versions of those modules instead.\n\n```bash ignore\nscala-cli test . --jvm 11\n# Compiling project (Scala 3.7.4, JVM (11))\n# Compiled project (Scala 3.7.4, JVM (11))\n# [warn] Java 11 is no longer supported by the test-runner module.\n# [warn] Defaulting to a legacy test-runner module version: 1.7.1.\n# [warn] To use the latest test-runner, upgrade Java to at least 17.\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3965](https://github.com/VirtusLab/scala-cli/pull/3965)\n\n### Features & improvements\n* Improve Python detection on Mac by [@Gedochao](https://github.com/Gedochao) in [#3961](https://github.com/VirtusLab/scala-cli/pull/3961)\n### Fixes\n* fix for 3307 (windows launcher rejects filenames with utf8 chars) by [@philwalk](https://github.com/philwalk) in [#3923](https://github.com/VirtusLab/scala-cli/pull/3923)\n* Allow for repeatable `-XX:*` Java options by [@Gedochao](https://github.com/Gedochao) in [#3976](https://github.com/VirtusLab/scala-cli/pull/3976)\n* fix #3979 by adding save-and-restore to generated run-native-image.bat by [@philwalk](https://github.com/philwalk) in [#3981](https://github.com/VirtusLab/scala-cli/pull/3981)\n* Fix handling of multiple exclude directives by [@tom91136](https://github.com/tom91136) in [#3984](https://github.com/VirtusLab/scala-cli/pull/3984)\n* Fix doc generation for projects with compile-only dependencies by [@Gedochao](https://github.com/Gedochao) in [#3990](https://github.com/VirtusLab/scala-cli/pull/3990)\n* Rewrite Scala.js linking to enable packaging of projects without a main method by [@Gedochao](https://github.com/Gedochao) in [#3992](https://github.com/VirtusLab/scala-cli/pull/3992)\n### Deprecations\n* Fall back to legacy `runner` / `test-runner` for JVM < 17 by [@Gedochao](https://github.com/Gedochao) in [#3965](https://github.com/VirtusLab/scala-cli/pull/3965)\n### Documentation changes\n* Add examples for passing REPL options via using directive to the docs by [@Gedochao](https://github.com/Gedochao) in [#3975](https://github.com/VirtusLab/scala-cli/pull/3975)\n* Back port of documentation changes to main by @github-actions[bot] in [#3978](https://github.com/VirtusLab/scala-cli/pull/3978)\n### Build and internal changes\n* Increase CI timeout for integration tests to 150 minutes by [@Gedochao](https://github.com/Gedochao) in [#3955](https://github.com/VirtusLab/scala-cli/pull/3955)\n* Run the entire REPL test suite on both Ammonite and Scala 3 REPL by [@Gedochao](https://github.com/Gedochao) in [#3982](https://github.com/VirtusLab/scala-cli/pull/3982)\n* Clean cached JDKs on Linux CI runners by [@Gedochao](https://github.com/Gedochao) in [#3995](https://github.com/VirtusLab/scala-cli/pull/3995)\n### Updates\n* Update scala-cli.sh launcher for 1.10.1 by @github-actions[bot] in [#3954](https://github.com/VirtusLab/scala-cli/pull/3954)\n* Bump sass from 1.93.3 to 1.94.0 in /website by @dependabot[bot] in [#3960](https://github.com/VirtusLab/scala-cli/pull/3960)\n* Bump Scala 2.13 to 2.13.18 (was 2.13.17) by [@Gedochao](https://github.com/Gedochao) in [#3958](https://github.com/VirtusLab/scala-cli/pull/3958)\n* Bump `ammonite` to 3.0.4 (was 3.0.3) by [@Gedochao](https://github.com/Gedochao) in [#3969](https://github.com/VirtusLab/scala-cli/pull/3969)\n* Bump actions/checkout from 5 to 6 by @dependabot[bot] in [#3973](https://github.com/VirtusLab/scala-cli/pull/3973)\n* Bump sass from 1.94.0 to 1.94.2 in /website by @dependabot[bot] in [#3974](https://github.com/VirtusLab/scala-cli/pull/3974)\n* Bump misc dependencies by [@Gedochao](https://github.com/Gedochao) in [#3971](https://github.com/VirtusLab/scala-cli/pull/3971)\n* Bump Scala 3 Next RC to 3.8.0-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3957](https://github.com/VirtusLab/scala-cli/pull/3957)\n* Update `bloop` to 2.0.17 (was 2.0.15) and `bloop-config` to 2.3.3 (was 2.3.2) by [@Gedochao](https://github.com/Gedochao) in [#3970](https://github.com/VirtusLab/scala-cli/pull/3970)\n* Bump node-forge from 1.3.1 to 1.3.2 in /website by @dependabot[bot] in [#3980](https://github.com/VirtusLab/scala-cli/pull/3980)\n* Bump Scala 3 Next RC to 3.8.0-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3977](https://github.com/VirtusLab/scala-cli/pull/3977)\n* Bump Scala.js deps by [@Gedochao](https://github.com/Gedochao) in [#3983](https://github.com/VirtusLab/scala-cli/pull/3983)\n* Bump mdast-util-to-hast from 13.0.2 to 13.2.1 in /website by @dependabot[bot] in [#3986](https://github.com/VirtusLab/scala-cli/pull/3986)\n* Bump @easyops-cn/docusaurus-search-local from 0.52.1 to 0.52.2 in /website by @dependabot[bot] in [#3985](https://github.com/VirtusLab/scala-cli/pull/3985)\n* Bump misc dependencies by [@Gedochao](https://github.com/Gedochao) in [#3987](https://github.com/VirtusLab/scala-cli/pull/3987)\n* Bump `scala-js-cli` to 1.20.1.1 (was 1.20.1) by [@Gedochao](https://github.com/Gedochao) in [#3989](https://github.com/VirtusLab/scala-cli/pull/3989)\n* Bump sass from 1.94.2 to 1.95.0 in /website by @dependabot[bot] in [#3998](https://github.com/VirtusLab/scala-cli/pull/3998)\n* Bump `react` & `react-dom` from 19.2.0 to 19.2.1 in /website by @dependabot[bot] in [#3997](https://github.com/VirtusLab/scala-cli/pull/3997)\n* Bump `coursier` to 2.1.25-M21 by [@Gedochao](https://github.com/Gedochao) in [#4000](https://github.com/VirtusLab/scala-cli/pull/4000)\n* Bump `scala-cli-signing` to 0.2.13 & `coursier-publish` to 0.4.4 by [@Gedochao](https://github.com/Gedochao) in [#3988](https://github.com/VirtusLab/scala-cli/pull/3988)\n* Bump Ammonite to 3.0.5 by [@Gedochao](https://github.com/Gedochao) in [#4001](https://github.com/VirtusLab/scala-cli/pull/4001)\n* Bump Scala 3 Next RC to 3.8.0-RC3 by [@Gedochao](https://github.com/Gedochao) in [#3991](https://github.com/VirtusLab/scala-cli/pull/3991)\n* Bump Scala 2.12 to 2.12.21 by [@Gedochao](https://github.com/Gedochao) in [#3999](https://github.com/VirtusLab/scala-cli/pull/3999)\n\n## New Contributors\n* [@tom91136](https://github.com/tom91136) made their first contribution in [#3984](https://github.com/VirtusLab/scala-cli/pull/3984)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.10.1...v1.11.0\n\n## [v1.10.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.10.1)\nThis is a bugfix release, chiefly aiming to mend [#3949](https://github.com/VirtusLab/scala-cli/issues/3949), which affected several of our users.\n### Fixes\n* Ensure Coursier logger gets initialized while downloading JVMs by [@Gedochao](https://github.com/Gedochao) in [#3951](https://github.com/VirtusLab/scala-cli/pull/3951)\n### Documentation changes\n* Back port of documentation changes to main by @github-actions[bot] in [#3948](https://github.com/VirtusLab/scala-cli/pull/3948)\n### Build and internal changes\n* Revert some of expecty 0.17.1 workarounds by [@Gedochao](https://github.com/Gedochao) in [#3937](https://github.com/VirtusLab/scala-cli/pull/3937)\n### Updates\n* Bump Node to 24 for the docs website by [@Gedochao](https://github.com/Gedochao) in [#3947](https://github.com/VirtusLab/scala-cli/pull/3947)\n* Update scala-cli.sh launcher for 1.10.0 by @github-actions[bot] in [#3946](https://github.com/VirtusLab/scala-cli/pull/3946)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.10.0...v1.10.1\n\n## [v1.10.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.10.0)\n\n### Change default Scala versions to 3.7.4 and 2.13.17\nThis Scala CLI version switches the default Scala versions:\n- default Scala 3 to 3.7.4\n- default Scala 2.13 to 2.13.17\n\n```bash\nscala-cli version\n# Scala CLI version: 1.10.0\n# Scala version (default): 3.7.4\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3942](https://github.com/VirtusLab/scala-cli/pull/3942) and [#3895](https://github.com/VirtusLab/scala-cli/pull/3895)\n\n### Support for the new Scala 3.8 REPL\nAs per https://github.com/scala/scala3/pull/24243, Scala 3 REPL has been extracted to [a separate artifact](https://repo.scala-lang.org/ui/packages/gav:%2F%2Forg.scala-lang:scala3-repl_3/3.8.0-RC1-bin-20251101-389483e-NIGHTLY) \nin Scala 3.8, as a result of which the use of the REPL command with Scala 3.8.0-RC1-bin-20251101-389483e-NIGHTLY\nor newer will require upgrading Scala CLI at least to 1.10 to work.\n\n```bash ignore\nscala-cli repl                                          \n# Welcome to Scala 3.8.0-RC1-bin-20251101-389483e-NIGHTLY (23.0.1, Java OpenJDK 64-Bit Server VM).\n# Type in expressions for evaluation. Or try :help.\n#                                                                                                                  \n# scala> \n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3936](https://github.com/VirtusLab/scala-cli/pull/3936)\n\n### Support for adding extra directories to a Docker image\nThis feature adds the ability to include additional directories in Docker images. \nUsers can now specify extra directories to be copied into a Docker image during the build process.\nThe directories can be passed with the `--docker-extra-directories` command line option or `//> using packaging.dockerExtraDirectories` directive.\n\n```scala compile power\n//> using packaging.dockerExtraDirectories path/to/directory1 path/to/directory2\n```\n\n```bash ignore\nscala-cli --power package . --docker --docker-image-repository repo --docker-extra-directories path/to/directory\n```\nAdded by [@btomala](https://github.com/btomala) and [@Gedochao](https://github.com/Gedochao) in [VirtusLab/scala-packager#250]https://github.com/VirtusLab/scala-packager/pull/250 and [#3908](https://github.com/VirtusLab/scala-cli/pull/3908)\n\n### Deprecate support for building GraalVM native images with Scala pre-3.3\nWhen building GraalVM native images with Scala CLI and Scala versions older than 3.3.0, the following warning will now be printed:\n```bash ignore\n# [warning] building native images with Scala 3 older than 3.3.0 is deprecated.\n# [warning] support will be dropped in a future Scala CLI version.\n# [warning] it is advised to upgrade to a more recent Scala version\n```\nWhile the native images will still be built, the functionality will be removed in a future Scala CLI version.\nIt is advised to migrate projects to Scala 3.3 or newer.\n\nAdditionally, the following modules have been dropped and will no longer be published:\n- `scala3-runtime`\n- `scala3-graal`\n- `scala3-graal-processor`\n\nAs they remain necessary for building native images for Scala pre-3.3 projects, \ntheir usage has been deprecated and frozen at respective version 1.9.1.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3929](https://github.com/VirtusLab/scala-cli/pull/3929)\n\n### Stop publishing certain modules for Scala 2\nWhile it is technically an internal change, it is worth noting certain Scala CLI modules will no longer be published for Scala 2.\nThose include:\n- `runner`\n- `test-runner`\n- `tasty-lib`\n- `config`\n- `specification-level`\n\nFrom this point on, they will only be published for Scala 3.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3911](https://github.com/VirtusLab/scala-cli/pull/3911) and [#3912](https://github.com/VirtusLab/scala-cli/pull/3912)\n\n### Features\n* Bump `scala-packager` to 0.2.1 & enable adding extra directories to a docker image by [@Gedochao](https://github.com/Gedochao) & [@btomala](https://github.com/btomala) in [#3908](https://github.com/VirtusLab/scala-cli/pull/3908)\n* Add support for the new Scala 3.8 REPL by [@Gedochao](https://github.com/Gedochao) in [#3936](https://github.com/VirtusLab/scala-cli/pull/3936)\n### Fixes\n* Ensure non-self executable JVM launchers' `setup-ide` produces working BSP connection JSON by [@Gedochao](https://github.com/Gedochao) in [#3876](https://github.com/VirtusLab/scala-cli/pull/3876)\n* Fix test scope resources to not be added to the main scope by [@Gedochao](https://github.com/Gedochao) in [#3898](https://github.com/VirtusLab/scala-cli/pull/3898)\n### Documentation changes\n* Suggest using ivy2Local in the documentation by [@przemek-pokrywka](https://github.com/przemek-pokrywka) in [#3902](https://github.com/VirtusLab/scala-cli/pull/3902)\n### Build and internal changes\n* Run the default (Scala 3 Next) suite with the JVM bootstrapped launcher on the CI by [@Gedochao](https://github.com/Gedochao) in [#3872](https://github.com/VirtusLab/scala-cli/pull/3872)\n* Run JDK tests for Java 25 by [@Gedochao](https://github.com/Gedochao) in [#3874](https://github.com/VirtusLab/scala-cli/pull/3874)\n* Update MacOS CI by [@Gedochao](https://github.com/Gedochao) in [#3885](https://github.com/VirtusLab/scala-cli/pull/3885)\n* Add `.cursor` to `.gitignore` by [@Gedochao](https://github.com/Gedochao) in [#3893](https://github.com/VirtusLab/scala-cli/pull/3893)\n* Unify `cli` module unit tests with consistent logging, timeouts and other settings by [@Gedochao](https://github.com/Gedochao) in [#3896](https://github.com/VirtusLab/scala-cli/pull/3896)\n* Cross compile the `runner` and `test-runner` modules against Scala 3 Next versions by [@Gedochao](https://github.com/Gedochao) in [#3927](https://github.com/VirtusLab/scala-cli/pull/3927)\n* Migrate integration tests to Scala 3 by [@Gedochao](https://github.com/Gedochao) in [#3926](https://github.com/VirtusLab/scala-cli/pull/3926)\n* Misc unit test fixes by [@Gedochao](https://github.com/Gedochao) in [#3931](https://github.com/VirtusLab/scala-cli/pull/3931)\n* Temporarily tag CLI docker image tests as flaky by [@Gedochao](https://github.com/Gedochao) in [#3939](https://github.com/VirtusLab/scala-cli/pull/3939)\n* Temporarily tag CLI docker image documentation tests as flaky by [@Gedochao](https://github.com/Gedochao) in [#3940](https://github.com/VirtusLab/scala-cli/pull/3940)\n* Temporarily disable some more flaky CLI docker image documentation tests by [@Gedochao](https://github.com/Gedochao) in [#3941](https://github.com/VirtusLab/scala-cli/pull/3941)\n* Drop Scala 2 in `runner`, `test-runner` and `tasty-lib` modules by [@Gedochao](https://github.com/Gedochao) in [#3911](https://github.com/VirtusLab/scala-cli/pull/3911)\n* Drop Scala 2 in `config` and `specification-level` modules & bump `jsoniter-scala` to 2.38.2 (was 2.13.5.2) by [@Gedochao](https://github.com/Gedochao) in [#3912](https://github.com/VirtusLab/scala-cli/pull/3912)\n* NIT Fix miscellaneous warnings by [@Gedochao](https://github.com/Gedochao) in [#3913](https://github.com/VirtusLab/scala-cli/pull/3913)\n* NIT Fix more miscellaneous warnings by [@Gedochao](https://github.com/Gedochao) in [#3920](https://github.com/VirtusLab/scala-cli/pull/3920)\n* Drop `scala3-runtime`, `scala3-graal` & `scala3-graal-processor` & deprecate pre-Scala-3.3 native images by [@Gedochao](https://github.com/Gedochao) in [#3929](https://github.com/VirtusLab/scala-cli/pull/3929)\n### Updates\n* Update scala-cli.sh launcher for 1.9.1 by @github-actions[bot] in [#3871](https://github.com/VirtusLab/scala-cli/pull/3871)\n* Bump sass from 1.92.1 to 1.93.0 in /website by @dependabot[bot] in [#3878](https://github.com/VirtusLab/scala-cli/pull/3878)\n* Bump Mill to 0.12.16 (was 0.12.15) by [@Gedochao](https://github.com/Gedochao) in [#3881](https://github.com/VirtusLab/scala-cli/pull/3881)\n* Bump Munit to 1.2.0 by [@Gedochao](https://github.com/Gedochao) in [#3883](https://github.com/VirtusLab/scala-cli/pull/3883)\n* Bump Scala 3 Next RC to 3.7.4-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3887](https://github.com/VirtusLab/scala-cli/pull/3887)\n* Bump sass from 1.93.0 to 1.93.2 in /website by @dependabot[bot] in [#3889](https://github.com/VirtusLab/scala-cli/pull/3889)\n* Bump Scala 2.13 to 2.13.17 by [@Gedochao](https://github.com/Gedochao) in [#3895](https://github.com/VirtusLab/scala-cli/pull/3895)\n* Bump Node to 24 & `@docusaurus/*` to 3.9.1 by [@Gedochao](https://github.com/Gedochao) in [#3899](https://github.com/VirtusLab/scala-cli/pull/3899)\n* Bump Scala 2.13 to 2.13.17 on the CI by [@Gedochao](https://github.com/Gedochao) in [#3900](https://github.com/VirtusLab/scala-cli/pull/3900)\n* Bump `coursier` to 2.1.25-M19 by [@Gedochao](https://github.com/Gedochao) in [#3884](https://github.com/VirtusLab/scala-cli/pull/3884)\n* Bump internal Scala version to 3.3.7 by [@Gedochao](https://github.com/Gedochao) in [#3906](https://github.com/VirtusLab/scala-cli/pull/3906)\n* Bump Ammonite to 3.0.3 by [@Gedochao](https://github.com/Gedochao) in [#3909](https://github.com/VirtusLab/scala-cli/pull/3909)\n* Migrate from old `coursier` APIs by [@Gedochao](https://github.com/Gedochao) in [#3910](https://github.com/VirtusLab/scala-cli/pull/3910)\n* Bump actions/setup-node from 5 to 6 by @dependabot[bot] in [#3915](https://github.com/VirtusLab/scala-cli/pull/3915)\n* Bump `scala-packager` to 0.2.1 & enable adding extra directories to a docker image by [@Gedochao](https://github.com/Gedochao) & [@btomala](https://github.com/btomala) in [#3908](https://github.com/VirtusLab/scala-cli/pull/3908)\n* Bump Scala Native to 0.5.9 by [@Gedochao](https://github.com/Gedochao) in [#3918](https://github.com/VirtusLab/scala-cli/pull/3918)\n* Bump react from 19.1.1 to 19.2.0 in /website by @dependabot[bot] in [#3904](https://github.com/VirtusLab/scala-cli/pull/3904)\n* Bump `docusaurus` to 3.9.2 (was 3.9.1) by [@Gedochao](https://github.com/Gedochao) in [#3919](https://github.com/VirtusLab/scala-cli/pull/3919)\n* Bump actions/download-artifact from 5 to 6 by @dependabot[bot] in [#3924](https://github.com/VirtusLab/scala-cli/pull/3924)\n* Bump actions/upload-artifact from 4 to 5 by @dependabot[bot] in [#3925](https://github.com/VirtusLab/scala-cli/pull/3925)\n* Bump Scala 3 Next RC to 3.7.4-RC3 by [@Gedochao](https://github.com/Gedochao) in [#3928](https://github.com/VirtusLab/scala-cli/pull/3928)\n* Bump `python-native-libs` to 0.2.5 (was 0.2.4) by [@Gedochao](https://github.com/Gedochao) in [#3932](https://github.com/VirtusLab/scala-cli/pull/3932)\n* Bump Scala 3 Next to 3.7.4 by [@Gedochao](https://github.com/Gedochao) in [#3942](https://github.com/VirtusLab/scala-cli/pull/3942)\n* Bump expecty to 0.17.1 (was 0.17.0) by [@Gedochao](https://github.com/Gedochao) in [#3938](https://github.com/VirtusLab/scala-cli/pull/3938)\n* Bump sass from 1.93.2 to 1.93.3 in /website by @dependabot[bot] in [#3935](https://github.com/VirtusLab/scala-cli/pull/3935)\n\n## New Contributors\n* [@przemek-pokrywka](https://github.com/przemek-pokrywka) made their first contribution in [#3902](https://github.com/VirtusLab/scala-cli/pull/3902)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.9.1...v1.10.0\n\n## [v1.9.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.9.1)\n\n### Support for Scala 3.7.3\nThis Scala CLI version switches the default Scala version to 3.7.3.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.9.1\n# Scala version (default): 3.7.3\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3866](https://github.com/VirtusLab/scala-cli/pull/3866)\n\n### Support for Scala.js 1.20.1\nThis Scala CLI version adds support for Scala.js 1.20.1.\n\n```bash\nscala-cli -e 'println(\"Hello\")' --js\n# Compiling project (Scala 3.7.3, Scala.js 1.20.1)\n# Compiled project (Scala 3.7.3, Scala.js 1.20.1)\n# Hello\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3861](https://github.com/VirtusLab/scala-cli/pull/3861) and [scala-js-cli#160](https://github.com/VirtusLab/scala-js-cli/pull/160)\n\n### Fixes\n* Fix completely broken lock during setting up local repo on Linux by [@unlsycn](https://github.com/unlsycn) in [#3846](https://github.com/VirtusLab/scala-cli/pull/3846)\n* Ensure `publish` actually fails on a failed upload by [@Gedochao](https://github.com/Gedochao) in [#3853](https://github.com/VirtusLab/scala-cli/pull/3853)\n\n### Updates\n* Update scala-cli.sh launcher for 1.9.0 by @github-actions[bot] in [#3851](https://github.com/VirtusLab/scala-cli/pull/3851)\n* Bump sass from 1.90.0 to 1.91.0 in /website by @dependabot[bot] in [#3839](https://github.com/VirtusLab/scala-cli/pull/3839)\n* Bump Scala 3 Next RC to 3.7.3-RC3 by [@Gedochao](https://github.com/Gedochao) in [#3854](https://github.com/VirtusLab/scala-cli/pull/3854)\n* Bump `react` & `react-dom` from 19.1.0 to 19.1.1 in /website by @dependabot[bot] in [#3806](https://github.com/VirtusLab/scala-cli/pull/3806)\n* Bump announced Scala 3 Next RC to 3.7.3-RC3 by [@Gedochao](https://github.com/Gedochao) in [#3858](https://github.com/VirtusLab/scala-cli/pull/3858)\n* Bump `jgit` to 7.3.0.202506031305-r by [@Gedochao](https://github.com/Gedochao) in [#3856](https://github.com/VirtusLab/scala-cli/pull/3856)\n* Bump Scala.js to 1.20.1 by [@Gedochao](https://github.com/Gedochao) in [#3861](https://github.com/VirtusLab/scala-cli/pull/3861)\n* Bump actions/setup-python from 5 to 6 by @dependabot[bot] in [#3863](https://github.com/VirtusLab/scala-cli/pull/3863)\n* Bump Scala Next to 3.7.3 by [@Gedochao](https://github.com/Gedochao) in [#3866](https://github.com/VirtusLab/scala-cli/pull/3866)\n* Bump sass from 1.91.0 to 1.92.1 in /website by @dependabot[bot] in [#3864](https://github.com/VirtusLab/scala-cli/pull/3864)\n* Bump @mdx-js/react from 3.1.0 to 3.1.1 in /website by @dependabot[bot] in [#3865](https://github.com/VirtusLab/scala-cli/pull/3865)\n* Bump actions/setup-node from 4 to 5 by @dependabot[bot] in [#3862](https://github.com/VirtusLab/scala-cli/pull/3862)\n* Bump `scalafmt` to 3.9.10 by [@Gedochao](https://github.com/Gedochao) in [#3868](https://github.com/VirtusLab/scala-cli/pull/3868)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.9.0...v1.9.1\n\n## [v1.9.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.9.0)\n\n### Support for the new Scala 3 nightly repository\nThis Scala CLI version supports the new Scala 3 nightly versions repository: \nhttps://repo.scala-lang.org/artifactory/maven-nightlies\n\nThis means that newest Scala 3 nightly versions will become available to use with Scala CLI, \nas well as the `3.nightly` tag will now refer to the actual, newest Scala version.\n\nAs a result, Scala 3.8 features like capture checked Scala 3 library should now be available from Scala CLI.\n\n```scala compile\n//> using scala 3.8.0-RC1-bin-20250901-ca400bd-NIGHTLY\nimport language.experimental.captureChecking\n\ntrait File extends caps.SharedCapability:\n  def count(): Int\n\ndef f(file: File): IterableOnce[Int]^{file} =\n  Iterator(1)\n    .map(_ + file.count())\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3838](https://github.com/VirtusLab/scala-cli/pull/3838)\n\n### Features\n* Add support for the new Scala 3 nightly repository by [@Gedochao](https://github.com/Gedochao) in [#3838](https://github.com/VirtusLab/scala-cli/pull/3838)\n\n### Fixes\n* Fix using directive with URL + query parameters by [@jgoday](https://github.com/jgoday) in [#3835](https://github.com/VirtusLab/scala-cli/pull/3835)\n\n### Documentation changes\n* Update the Scala CLI docs landing page by [@Gedochao](https://github.com/Gedochao) in [#3825](https://github.com/VirtusLab/scala-cli/pull/3825)\n* Back port of documentation changes to main by @github-actions[bot] in [#3826](https://github.com/VirtusLab/scala-cli/pull/3826)\n\n### Build and internal changes\n* Prepare for Mill 1.0.x bump by [@Gedochao](https://github.com/Gedochao) in [#3833](https://github.com/VirtusLab/scala-cli/pull/3833)\n* Temporarily disable flaky Spark tests on Mac CI by [@Gedochao](https://github.com/Gedochao) in [#3842](https://github.com/VirtusLab/scala-cli/pull/3842)\n\n### Updates\n* Update scala-cli.sh launcher for 1.8.5 by @github-actions[bot] in [#3824](https://github.com/VirtusLab/scala-cli/pull/3824)\n* Bump actions/checkout from 4 to 5 by @dependabot[bot] in [#3827](https://github.com/VirtusLab/scala-cli/pull/3827)\n* Bump sass from 1.89.2 to 1.90.0 in /website by @dependabot[bot] in [#3828](https://github.com/VirtusLab/scala-cli/pull/3828)\n* Bump `case-app` to 2.1.0 by [@Gedochao](https://github.com/Gedochao) in [#3830](https://github.com/VirtusLab/scala-cli/pull/3830)\n* Bump actions/download-artifact from 4 to 5 by @dependabot[bot] in [#3829](https://github.com/VirtusLab/scala-cli/pull/3829)\n* Update sbt, scripted-plugin to 1.11.4 by [@scala-steward](https://github.com/scala-steward) in [#3832](https://github.com/VirtusLab/scala-cli/pull/3832)\n* Update Scala 3 Next RC to 3.7.3-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3834](https://github.com/VirtusLab/scala-cli/pull/3834)\n* Bump `scala-packager` to 0.2.0 by [@Gedochao](https://github.com/Gedochao) in [#3843](https://github.com/VirtusLab/scala-cli/pull/3843)\n* Update announced Scala 3 RC to 3.7.3-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3845](https://github.com/VirtusLab/scala-cli/pull/3845)\n* Update jsoup to 1.21.2 by [@scala-steward](https://github.com/scala-steward) in [#3840](https://github.com/VirtusLab/scala-cli/pull/3840)\n\n## New Contributors\n* @jgoday made their first contribution in [#3835](https://github.com/VirtusLab/scala-cli/pull/3835)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.8.5...v1.9.0\n\n## [v1.8.5](https://github.com/VirtusLab/scala-cli/releases/tag/v1.8.5)\n\n### Support for Scala 3.7.2\nThis Scala CLI version switches the default Scala version to 3.7.2.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.8.5\n# Scala version (default): 3.7.2\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3809](https://github.com/VirtusLab/scala-cli/pull/3809)\n\n### Add props with input paths for non-script inputs\n2 new properties have been added, to provide a way to access input paths outside of `.sc` scripts:\n- `scala.sources` - full paths of all inputs in the project, separated by `java.io.File.pathSeparator`\n- `scala.source.names` - names of all inputs in the project, separated by `java.io.File.pathSeparator`\n\n```scala title=PrintSources.scala compile\n//PrintSources.scala\n@main def main() = {\n  println(sys.props(\"scala.sources\"))\n  println(sys.props(\"scala.source.names\"))\n}\n```\n\n```\n~/PrintSources.scala\nPrintSources.scala\n```\n\nThese are meant to be an input type agnostic equivalent for the `scriptPath` method available in `.sc` scripts.\n\nAdded by [@philwalk](https://github.com/philwalk) in [#3799](https://github.com/VirtusLab/scala-cli/pull/3799)\n\n### `sonatype:snapshots` points to the new Sonatype Central snapshots repository\nIt is no longer necessary to manually add https://central.sonatype.com/repository/maven-snapshots, it is added under the `sonatype:snapshots` alias (along with the old snapshot repository).\nIt is also added in all other contexts when snapshots should be used.\n\n```scala compile\n//> using repository sonatype:snapshots\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3797](https://github.com/VirtusLab/scala-cli/pull/3797)\n\n### Scala CLI nightlies are available again\nWe once again publish Scala CLI nightlies. You can use the newest version under the `nightly` tag.\nAs pre-1.8.5 Scala CLI versions do not look for them on the new Sonatype Central snapshots repository, \nthey will not be visible when called from earlier versions.\n```bash ignore\nscala-cli --cli-version nightly version\n# Scala CLI version: 1.8.4-24-g8ca6c960b-SNAPSHOT\n# Scala version (default): 3.7.2\n```\n\n:::info\nThe new Sonatype Central Portal retains snapshot versions for 90 days,\nso individual nightly versions will be available to test for a limited time period.\n:::\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3818](https://github.com/VirtusLab/scala-cli/pull/3818)\n\n### Features\n* Automatically add the new Sonatype Central Portal snapshots repository when snapshots are expected to be used by [@Gedochao](https://github.com/Gedochao) in [#3797](https://github.com/VirtusLab/scala-cli/pull/3797)\n* Adjust Scala CLI nightly version resolution for the new Maven Central Snapshots repository by [@Gedochao](https://github.com/Gedochao) in [#3818](https://github.com/VirtusLab/scala-cli/pull/3818)\n* provide a way to access paths of source files by [@philwalk](https://github.com/philwalk) in [#3799](https://github.com/VirtusLab/scala-cli/pull/3799)\n* Add more logging for publishing by [@Gedochao](https://github.com/Gedochao) in [#3813](https://github.com/VirtusLab/scala-cli/pull/3813)\n\n### Fixes\n* fix for 3789 - updated by [@philwalk](https://github.com/philwalk) in [#3794](https://github.com/VirtusLab/scala-cli/pull/3794)\n* script extension can be either '.sc' or empty string by [@philwalk](https://github.com/philwalk) in [#3802](https://github.com/VirtusLab/scala-cli/pull/3802)\n\n### Build and internal changes\n* Tag tests relying on old snapshots as flaky (or remove them where applicable) by [@Gedochao](https://github.com/Gedochao) in [#3817](https://github.com/VirtusLab/scala-cli/pull/3817)\n* Add `--no-fallback` to graalvm native-image configuration by [@lbialy](https://github.com/lbialy) in [#3820](https://github.com/VirtusLab/scala-cli/pull/3820)\n\n### Updates\n* Bump react-player from 2.16.0 to 2.16.1 in /website by [@dependabot[bot]](https://github.com/dependabot[bot]) in [#3784](https://github.com/VirtusLab/scala-cli/pull/3784)\n* Update scala-cli.sh launcher for 1.8.4 by [@github-actions[bot]](https://github.com/github-actions[bot]) in [#3787](https://github.com/VirtusLab/scala-cli/pull/3787)\n* Update zip-input-stream to 0.1.3 by [@scala-steward](https://github.com/scala-steward) in [#3792](https://github.com/VirtusLab/scala-cli/pull/3792)\n* Update Scala 3 Next RC to 3.7.2-RC2 by [@scala-steward](https://github.com/scala-steward) in [#3791](https://github.com/VirtusLab/scala-cli/pull/3791)\n* Update java-class-name_3 to 0.1.8 by [@scala-steward](https://github.com/scala-steward) in [#3795](https://github.com/VirtusLab/scala-cli/pull/3795)\n* Update Scala 3 Next announced RC to 3.7.2-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3796](https://github.com/VirtusLab/scala-cli/pull/3796)\n* Update publish to 0.4.3 by [@scala-steward](https://github.com/scala-steward) in [#3801](https://github.com/VirtusLab/scala-cli/pull/3801)\n* Update pprint to 0.9.1 by [@scala-steward](https://github.com/scala-steward) in [#3803](https://github.com/VirtusLab/scala-cli/pull/3803)\n* Update pprint to 0.9.3 by [@scala-steward](https://github.com/scala-steward) in [#3805](https://github.com/VirtusLab/scala-cli/pull/3805)\n* Bump @easyops-cn/docusaurus-search-local from 0.51.1 to 0.52.1 in /website by [@dependabot[bot]](https://github.com/dependabot[bot]) in [#3807](https://github.com/VirtusLab/scala-cli/pull/3807)\n* Update fansi to 0.5.1 by [@scala-steward](https://github.com/scala-steward) in [#3804](https://github.com/VirtusLab/scala-cli/pull/3804)\n* Update Scala 3 Next to 3.7.2 by [@Gedochao](https://github.com/Gedochao) in [#3809](https://github.com/VirtusLab/scala-cli/pull/3809)\n* Update mill-main to 0.12.15 by [@scala-steward](https://github.com/scala-steward) in [#3814](https://github.com/VirtusLab/scala-cli/pull/3814)\n* Update `scalameta` to 4.13.9 by [@scala-steward](https://github.com/scala-steward) in [#3816](https://github.com/VirtusLab/scala-cli/pull/3816)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.9.9 by [@scala-steward](https://github.com/scala-steward) in [#3815](https://github.com/VirtusLab/scala-cli/pull/3815)\n* Update os-lib to 0.11.5 by [@scala-steward](https://github.com/scala-steward) in [#3811](https://github.com/VirtusLab/scala-cli/pull/3811)\n* Set Scala 3.7.2 as the latest announced Scala 3 Next by [@Gedochao](https://github.com/Gedochao) in [#3812](https://github.com/VirtusLab/scala-cli/pull/3812)\n* Bump Bloop to 2.0.13 by [@Gedochao](https://github.com/Gedochao) in [#3821](https://github.com/VirtusLab/scala-cli/pull/3821)\n\n## New Contributors\n* [@lbialy](https://github.com/lbialy) made their first contribution in [#3820](https://github.com/VirtusLab/scala-cli/pull/3820)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.8.4...v1.8.5\n\n## [v1.8.4](https://github.com/VirtusLab/scala-cli/releases/tag/v1.8.4)\n\n### (⚡️ experimental) `publish` support for the Sonatype Central Portal\nThis Scala CLI version adds support for publishing artifacts to the Sonatype Central Portal \nvia its [OSSRH Staging API](https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/).\nIt is once again publish artifacts to Maven Central with Scala CLI.\nBoth stable and `*-SNAPSHOT` versions are handled.\nThe only configuration change necessary is to migrate the Sonatype namespace in the UI and regenerate credentials to the new Sonatype Central Portal, as [per Sonatype instructions](https://central.sonatype.org/pages/ossrh-eol/)\n\n```bash ignore\nscala-cli publish . --power\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3774](https://github.com/VirtusLab/scala-cli/pull/3774), [#3776](https://github.com/VirtusLab/scala-cli/pull/3776), [#3778](https://github.com/VirtusLab/scala-cli/pull/3778), [coursier/publish#128](https://github.com/coursier/publish/pull/128) and [coursier/publish#127](https://github.com/coursier/publish/pull/127)\n\n### Better support for the REPL with JDK 24+\nWhen using the REPL with JDK 24 or newer, users should no longer see the warnings about restricted methods of `java.lang.System` being used.\n\n```bash ignore\nscala-cli repl --jvm 24\n# WARNING: A terminally deprecated method in sun.misc.Unsafe has been called\n# WARNING: sun.misc.Unsafe::objectFieldOffset has been called by scala.runtime.LazyVals$ (file:/Users/pchabelski/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.7.1/scala3-library_3-3.7.1.jar)\n# WARNING: Please consider reporting this to the maintainers of class scala.runtime.LazyVals$\n# WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release\n# Welcome to Scala 3.7.1 (24.0.1, Java OpenJDK 64-Bit Server VM).\n# Type in expressions for evaluation. Or try :help.\n#            \n#\n# scala> \n```\n\nNote that the deprecated method from `sun.misc.Unsafe` warning is still present, and will only be addressed in Scala 3.8.0.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3767](https://github.com/VirtusLab/scala-cli/pull/3767)\n\n### Features\n* `publish` command with Sonatype Central Portal OSSRH Staging API by [@Gedochao](https://github.com/Gedochao) in [#3774](https://github.com/VirtusLab/scala-cli/pull/3774)\n* Add support for publishing with Scala CLI to Sonatype Central Portal by [@Gedochao](https://github.com/Gedochao) in [#3776](https://github.com/VirtusLab/scala-cli/pull/3776)\n\n### Fixes\n* Prevent the REPL from warning about restricted `java.lang.System` API on JDK 24 by [@Gedochao](https://github.com/Gedochao) in [#3767](https://github.com/VirtusLab/scala-cli/pull/3767)\n* fix for 3725, 3752, 3766 and 3769 by [@philwalk](https://github.com/philwalk) in [#3726](https://github.com/VirtusLab/scala-cli/pull/3726)\n\n### Documentation changes\n* Update verbosity.md by [@kubukoz](https://github.com/kubukoz) in [#3772](https://github.com/VirtusLab/scala-cli/pull/3772)\n\n### Updates\n* Update scala-cli.sh launcher for 1.8.3 by [@github-actions[bot]](https://github.com/github-actions[bot]) in [#3765](https://github.com/VirtusLab/scala-cli/pull/3765)\n* Update sbt, scripted-plugin to 1.11.3 by [@scala-steward](https://github.com/scala-steward) in [#3773](https://github.com/VirtusLab/scala-cli/pull/3773)\n* Update `scalafmt` to 3.9.8 by [@scala-steward](https://github.com/scala-steward) in [#3771](https://github.com/VirtusLab/scala-cli/pull/3771)\n* Update scalafix-interfaces to 0.14.3 by [@scala-steward](https://github.com/scala-steward) in [#3673](https://github.com/VirtusLab/scala-cli/pull/3673)\n* Bump `coursier/publish` to 0.4.2 by [@Gedochao](https://github.com/Gedochao) in [#3778](https://github.com/VirtusLab/scala-cli/pull/3778)\n* Update semanticdb-shared_2.13 to 4.13.8 by [@scala-steward](https://github.com/scala-steward) in [#3770](https://github.com/VirtusLab/scala-cli/pull/3770)\n* Update jimfs to 1.3.1 by [@scala-steward](https://github.com/scala-steward) in [#3779](https://github.com/VirtusLab/scala-cli/pull/3779)\n* Update `scala-cli-signing` to 0.2.11 by [@scala-steward](https://github.com/scala-steward) in [#3781](https://github.com/VirtusLab/scala-cli/pull/3781)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.8.3...v1.8.4\n\n## [v1.8.3](https://github.com/VirtusLab/scala-cli/releases/tag/v1.8.3)\n\nThis is a small release which aims to fix issues with publishing Scala CLI on Sonatype Central Portal.\n\n### Build and internal changes\n* Add extra logging on Scala CLI artifacts publishing by [@Gedochao](https://github.com/Gedochao) in [#3745](https://github.com/VirtusLab/scala-cli/pull/3745)\n* [NIT] Refactor publishing by [@Gedochao](https://github.com/Gedochao) in [#3743](https://github.com/VirtusLab/scala-cli/pull/3743)\n* Ensure publishing to Sonatype runs without parallelism by [@Gedochao](https://github.com/Gedochao) in [#3755](https://github.com/VirtusLab/scala-cli/pull/3755)\n* Don't use any custom logic when publishing to Sonatype Central by [@Gedochao](https://github.com/Gedochao) in [#3759](https://github.com/VirtusLab/scala-cli/pull/3759)\n* Print artifacts' version when publishing Scala CLI by [@Gedochao](https://github.com/Gedochao) in [#3760](https://github.com/VirtusLab/scala-cli/pull/3760)\n\n### Updates\n* Update scala-cli.sh launcher for 1.8.2 by [@github-actions](https://github.com/github-actions) in [#3744](https://github.com/VirtusLab/scala-cli/pull/3744)\n* Update semanticdb-shared_2.13 to 4.13.7 by [@scala-steward](https://github.com/scala-steward) in [#3746](https://github.com/VirtusLab/scala-cli/pull/3746)\n* Bump @easyops-cn/docusaurus-search-local from 0.50.0 to 0.51.0 in /website by [@dependabot](https://github.com/dependabot) in [#3748](https://github.com/VirtusLab/scala-cli/pull/3748)\n* Bump brace-expansion from 1.1.11 to 1.1.12 in /website by [@dependabot](https://github.com/dependabot) in [#3749](https://github.com/VirtusLab/scala-cli/pull/3749)\n* chore: Set latest RC to 3.7.2-RC1 by [@tgodzik](https://github.com/tgodzik) in [#3750](https://github.com/VirtusLab/scala-cli/pull/3750)\n* Bump @easyops-cn/docusaurus-search-local from 0.51.0 to 0.51.1 in /website by [@dependabot](https://github.com/dependabot) in [#3757](https://github.com/VirtusLab/scala-cli/pull/3757)\n* Update jsoup to 1.21.1 by [@scala-steward](https://github.com/scala-steward) in [#3762](https://github.com/VirtusLab/scala-cli/pull/3762)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.8.2...v1.8.3\n\n## [v1.8.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.8.2)\n\n:::warning\nDue to technical difficulties, this version is not available on Sonatype Central Portal and on coursier. \nIf you care about those installation methods, please be patient as we resolve the issue and work on a subsequent release.\n:::\n\n### Support for Scala Native 0.5.8\nThis Scala CLI version switches the default Scala Native version to 0.5.8.\n\n```bash\nscala-cli -e 'println(\"Hello from Scala Native 0.5.8!\")' --native\n# Compiling project (Scala 3.7.1, Scala Native 0.5.8)\n# Compiled project (Scala 3.7.1, Scala Native 0.5.8)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (1052 ms)\n# [info] Discovered 919 classes and 5640 methods after classloading\n# [info] Checking intermediate code (quick) (59 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (369 ms)\n# [info] Discovered 511 classes and 2553 methods after classloading\n# [info] Checking intermediate code (quick) (7 ms)\n# [info] Discovered 491 classes and 1986 methods after optimization\n# [info] Optimizing (debug mode) (519 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (521 ms)\n# [info] Compiling to native code (1762 ms)\n# [info] Linking with [pthread, dl, m]\n# [info] Linking native code (immix gc, none lto) (98 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (4379 ms)\n# Hello from Scala Native 0.5.8!\n```\n\nAdded in [#3728](https://github.com/VirtusLab/scala-cli/pull/3728)\n\n### Internal and build changes\n* Migrate publishing to Sonatype Maven Central by [@Gedochao](https://github.com/Gedochao) in [#3704](https://github.com/VirtusLab/scala-cli/pull/3704)\n* Fix version string of published artifacts by [@Gedochao](https://github.com/Gedochao) in [#3721](https://github.com/VirtusLab/scala-cli/pull/3721)\n\n### Updates\n* Update scala-cli.sh launcher for 1.8.1 by [@github-actions](https://github.com/github-actions) in [#3719](https://github.com/VirtusLab/scala-cli/pull/3719)\n* Bump `sbt` to 1.11.1 by [@Gedochao](https://github.com/Gedochao) in [#3723](https://github.com/VirtusLab/scala-cli/pull/3723)\n* Bump deps to versions migrated to Sonatype Maven Central by [@Gedochao](https://github.com/Gedochao) in [#3722](https://github.com/VirtusLab/scala-cli/pull/3722)\n* Update publish_2.13 to 0.2.1 by [@scala-steward](https://github.com/scala-steward) in [#3727](https://github.com/VirtusLab/scala-cli/pull/3727)\n* Bump sass from 1.89.1 to 1.89.2 in /website by [@dependabot](https://github.com/dependabot) in [#3733](https://github.com/VirtusLab/scala-cli/pull/3733)\n* Bump @easyops-cn/docusaurus-search-local from 0.49.2 to 0.50.0 in /website by [@dependabot](https://github.com/dependabot) in [#3730](https://github.com/VirtusLab/scala-cli/pull/3730)\n* Bump react and react-dom in /website by [@dependabot](https://github.com/dependabot) in [#3732](https://github.com/VirtusLab/scala-cli/pull/3732)\n* Bump `scala-cli-signing` to 0.2.9 & coursier `publish` to 0.3.0 by [@Gedochao](https://github.com/Gedochao) in [#3734](https://github.com/VirtusLab/scala-cli/pull/3734)\n* Bump `docusaurus` to 3.8.1 by [@Gedochao](https://github.com/Gedochao) in [#3737](https://github.com/VirtusLab/scala-cli/pull/3737)\n* Bump SBT to 1.11.2 by [@Gedochao](https://github.com/Gedochao) in [#3739](https://github.com/VirtusLab/scala-cli/pull/3739)\n* Update Scala Native to 0.5.8 by [@scala-steward](https://github.com/scala-steward) in [#3728](https://github.com/VirtusLab/scala-cli/pull/3728)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.8.1...v1.8.2\n\n## [v1.8.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.8.1)\n\n### Support for Scala 3.7.1\nThis Scala CLI version switches the default Scala version to 3.7.1.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.8.1\n# Scala version (default): 3.7.1\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3707](https://github.com/VirtusLab/scala-cli/pull/3707)\n\n### Support for URLs in `using file` directives\nIt is now possible to use URLs in `using file` directives, which allows linking sources from the net.\n\n```scala compile\n//> using file https://raw.githubusercontent.com/softwaremill/sttp/refs/heads/master/examples/src/main/scala/sttp/client4/examples/json/GetAndParseJsonCatsEffectCirce.scala\n```\n\nAdded during a [Scala Tooling Spree](https://scalameta.org/scala-tooling-spree/) by [@ivan-klass](https://github.com/ivan-klass), [@majk-p](https://github.com/majk-p) and [@tgodzik](https://github.com/tgodzik) in [#3681](https://github.com/VirtusLab/scala-cli/pull/3681)\n\n### Features\n* Support links in using file directive (implements #1328) by [@ivan-klass](https://github.com/ivan-klass), [@majk-p](https://github.com/majk-p) and [@tgodzik](https://github.com/tgodzik) in [#3681](https://github.com/VirtusLab/scala-cli/pull/3681)\n\n### Fixes\n* Fix race condition in local repo setup by [@unlsycn](https://github.com/unlsycn) in [#3693](https://github.com/VirtusLab/scala-cli/pull/3693)\n* Fix for #3481 by [@philwalk](https://github.com/philwalk) in [#3677](https://github.com/VirtusLab/scala-cli/pull/3677)\n\n### Internal and build changes\n* bugfix: Fix mill script on fish by [@tgodzik](https://github.com/tgodzik) in [#3700](https://github.com/VirtusLab/scala-cli/pull/3700)\n* Make `test-fish-shell`, `test-hypothetical-sbt-export` and `bloop-memory-footprint` required for publishing by [@Gedochao](https://github.com/Gedochao) in [#3701](https://github.com/VirtusLab/scala-cli/pull/3701)\n* [NIT] Refactor Scala CLI CI scripts by [@Gedochao](https://github.com/Gedochao) in [#3702](https://github.com/VirtusLab/scala-cli/pull/3702)\n* Remove the `github-dependency-graph` CI workflow by [@Gedochao](https://github.com/Gedochao) in [#3703](https://github.com/VirtusLab/scala-cli/pull/3703)\n* Add more tests for URLs in using file directives by [@Gedochao](https://github.com/Gedochao) in [#3706](https://github.com/VirtusLab/scala-cli/pull/3706)\n\n### Documentation changes\n* Fix Changing Java versions document by [@tmrkw1497](https://github.com/tmrkw1497) in [#3697](https://github.com/VirtusLab/scala-cli/pull/3697)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3699](https://github.com/VirtusLab/scala-cli/pull/3699)\n\n### Updates\n* Update scala-cli.sh launcher for 1.8.0 by [@github-actions](https://github.com/github-actions) in [#3672](https://github.com/VirtusLab/scala-cli/pull/3672)\n* Update semanticdb-shared_2.13 to 4.13.6 by [@scala-steward](https://github.com/scala-steward) in [#3678](https://github.com/VirtusLab/scala-cli/pull/3678)\n* Bump sass from 1.87.0 to 1.88.0 in /website by [@dependabot](https://github.com/dependabot) in [#3676](https://github.com/VirtusLab/scala-cli/pull/3676)\n* Bump `java-class-name` to 0.1.6 by [@Gedochao](https://github.com/Gedochao) in [#3679](https://github.com/VirtusLab/scala-cli/pull/3679)\n* Update `scala-cli-signing` to 0.2.7 by [@scala-steward](https://github.com/scala-steward) in [#3683](https://github.com/VirtusLab/scala-cli/pull/3683)\n* Update `mill` scripts by [@Gedochao](https://github.com/Gedochao) in [#3686](https://github.com/VirtusLab/scala-cli/pull/3686)\n* Bump sass from 1.88.0 to 1.89.0 in /website by [@dependabot](https://github.com/dependabot) in [#3687](https://github.com/VirtusLab/scala-cli/pull/3687)\n* Bump `scala-js-cli` to 1.19.0.1 by [@Gedochao](https://github.com/Gedochao) in [#3689](https://github.com/VirtusLab/scala-cli/pull/3689)\n* Bump Scala 3 Next RC to 3.7.1-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3692](https://github.com/VirtusLab/scala-cli/pull/3692)\n* Update sbt, scripted-plugin to 1.11.0 by [@scala-steward](https://github.com/scala-steward) in [#3696](https://github.com/VirtusLab/scala-cli/pull/3696)\n* Bump `mill` to 0.12 by [@Gedochao](https://github.com/Gedochao) in [#3691](https://github.com/VirtusLab/scala-cli/pull/3691)\n* Bump `scalafmt` to 3.9.7 & reformat by [@Gedochao](https://github.com/Gedochao) in [#3705](https://github.com/VirtusLab/scala-cli/pull/3705)\n* Bump @docusaurus/preset-classic from 3.7.0 to 3.8.0 in /website by [@dependabot](https://github.com/dependabot) in [#3709](https://github.com/VirtusLab/scala-cli/pull/3709)\n* Bump sass from 1.89.0 to 1.89.1 in /website by [@dependabot](https://github.com/dependabot) in [#3710](https://github.com/VirtusLab/scala-cli/pull/3710)\n* Bump Scala Next to 3.7.1 by [@Gedochao](https://github.com/Gedochao) in [#3707](https://github.com/VirtusLab/scala-cli/pull/3707)\n\n## New Contributors\n* [@unlsycn](https://github.com/unlsycn) made their first contribution in [#3693](https://github.com/VirtusLab/scala-cli/pull/3693)\n* [@tmrkw1497](https://github.com/tmrkw1497) made their first contribution in [#3697](https://github.com/VirtusLab/scala-cli/pull/3697)\n* [@ivan-klass](https://github.com/ivan-klass) made their first contribution in [#3681](https://github.com/VirtusLab/scala-cli/pull/3681)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.8.0...v1.8.1\n\n## [v1.8.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.8.0)\n\n### Support for Scala 3.7.0 and 3.3.6\nThis Scala CLI version switches the default Scala version to 3.7.0.\nThe CLI internals are now built with Scala 3.3.6.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.8.0\n# Scala version (default): 3.7.0\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3661](https://github.com/VirtusLab/scala-cli/pull/3661) and [#3671](https://github.com/VirtusLab/scala-cli/pull/3671)\n\n### Support for Scala.js 1.19.0\nThis Scala CLI version adds support for Scala.js 1.19.0.\n\n```bash\nscala-cli -e 'println(\"Hello\")' --js\n# Compiling project (Scala 3.7.0, Scala.js 1.19.0)\n# Compiled project (Scala 3.7.0, Scala.js 1.19.0)\n# Hello\n```\n\nAdded in [#3643](https://github.com/VirtusLab/scala-cli/pull/3643) and [scala-js-cli#134](https://github.com/VirtusLab/scala-js-cli/pull/134)\n\n### Drop support for Scala older than 3.3 in `runner` and `test-runner` modules\nStarting with Scala CLI v1.8.0, the `runner` and `test-runner` modules are built with Scala 3.3.5 LTS (on par with other modules built with Scala 3). \nThey used to be built with Scala 3.0.2, as those modules may get added to the project class path when running, respectively,\nthe main scope and tests. This means that if the application is using pre-3.3 Scala 3, TASTy versions will be incompatible.\n\nThis is mostly informative, as the change should not be breaking for standard Scala CLI usage, even if an older Scala 3 version is being used. \nFor builds using Scala older than 3.3, the CLI will automatically fall back to version 1.7.1 of the modules, with an appropriate warning being printed.\nAs the fallback will not be updated in the future, some Scala CLI features might start breaking at some point, as the APIs will stop being fully in sync.\n\n```bash\nscala-cli -e 'println(\"Hello\")' --runner -S 3.1\n# [warn] Scala 3.1.3 is no longer supported by the runner module.\n# [warn] Defaulting to a legacy runner module version: 1.7.1.\n# [warn] To use the latest runner, upgrade Scala to at least Scala 3.3.\n# Compiling project (Scala 3.1.3, JVM (17))\n# Compiled project (Scala 3.1.3, JVM (17))\n# Hello\n```\n\n```bash ignore\nscala-cli test . -S 3.2\n# [warn] Scala 3.2.2 is no longer supported by the test-runner module.\n# [warn] Defaulting to a legacy test-runner module version: 1.7.1.\n# [warn] To use the latest test-runner, upgrade Scala to at least 3.3.\n# Compiling project (test, Scala 3.2.2, JVM (17))\n# Compiled project (test, Scala 3.2.2, JVM (17))\n# Test run started\n# Test MyTests.foo started\n# Hello, world!\n# Test MyTests.foo finished, took 0.001 sec\n# Test run finished: 0 failed, 0 ignored, 1 total, 0.003s\n```\n\nRealistically, the change is only breaking for apps using those modules directly themselves, either depending on them or using them to run things.\nIn either case, it is recommended to update Scala up to at least 3.3 LTS.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3650](https://github.com/VirtusLab/scala-cli/pull/3650)\n\n### Scala CLI now detects and runs multiple test frameworks, rather than just one\nWhen running tests in a project with multiple test frameworks in use, Scala CLI will now attempt to detect and run all of them, rather than just one.\n\n```bash ignore\nscala-cli test .\n# Compiling project (Scala 3.7.0, JVM (23))\n# Compiled project (Scala 3.7.0, JVM (23))\n# Compiling project (test, Scala 3.7.0, JVM (23))\n# Compiled project (test, Scala 3.7.0, JVM (23))\n# Munit:\n#   + foo 0.007s\n# -------------------------------- Running Tests --------------------------------\n# + MyTests.foo 1ms  \n# Tests: 1, Passed: 1, Failed: 0\n# + SimpleSpec\n# Hello from zio-test\n#   + print hello and assert true\n# 1 tests passed. 0 tests failed. 0 tests ignored.\n# \n# Executed in 97 ms\n# \n# Completed tests\n# ScalaTestSpec:\n# example\n# - should work\n# Run completed in 44 milliseconds.\n# Total number of tests run: 1\n# Suites: completed 1, aborted 0\n# Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0\n# All tests passed.\n```\n\nAdditionally, it is now possible to pre-define multiple test frameworks to use (rather than just one, as was possible before).\n\n```scala compile\n//> using test.frameworks org.scalatest.tools.Framework munit.Framework custom.CustomFramework\n```\n\nPre-defining test frameworks may be preferable for bigger projects, as it allows to skip framework detection and run them directly. \nThis is significant particularly for running tests with Scala Native and Scala.js.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3653](https://github.com/VirtusLab/scala-cli/pull/3653)\n\n### Features\n* Support the `--test` flag with the `publish` & `publish local` sub-commands by [@Gedochao](https://github.com/Gedochao) in [#3538](https://github.com/VirtusLab/scala-cli/pull/3538)\n* Misc no-op and/or error handling for the `--test` command line flag by [@Gedochao](https://github.com/Gedochao) in [#3586](https://github.com/VirtusLab/scala-cli/pull/3586)\n* Add scala-cli version to the BuildInfo by [@yadavan88](https://github.com/yadavan88) in [#3617](https://github.com/VirtusLab/scala-cli/pull/3617)\n* `fix` sub-command tweaks by [@Gedochao](https://github.com/Gedochao) in [#3646](https://github.com/VirtusLab/scala-cli/pull/3646)\n* Run all found test frameworks, rather than just one by [@Gedochao](https://github.com/Gedochao) in [#3621](https://github.com/VirtusLab/scala-cli/pull/3621)\n* Allow to preconfigure multiple test frameworks by [@Gedochao](https://github.com/Gedochao) in [#3653](https://github.com/VirtusLab/scala-cli/pull/3653)\n* Add support for some missing Scala compiler options & aliases without the need for `-O` by [@Gedochao](https://github.com/Gedochao) in [#3665](https://github.com/VirtusLab/scala-cli/pull/3665)\n* Add support for the --repl-quit-after-init REPL option by [@Gedochao](https://github.com/Gedochao) in [#3664](https://github.com/VirtusLab/scala-cli/pull/3664)\n\n### Fixes\n* Fix `fmt` to format the `project.scala` configuration file as any other Scala input by [@Gedochao](https://github.com/Gedochao) in [#3609](https://github.com/VirtusLab/scala-cli/pull/3609)\n* Apply `scalafix` rules to test scope inputs, too by [@Gedochao](https://github.com/Gedochao) in [#3641](https://github.com/VirtusLab/scala-cli/pull/3641)\n\n### Internal and build changes\n* Cross compile everything on the CI by [@Gedochao](https://github.com/Gedochao) in [#3570](https://github.com/VirtusLab/scala-cli/pull/3570)\n* Add tests for the current behaviour of `--cross` by [@Gedochao](https://github.com/Gedochao) in [#3589](https://github.com/VirtusLab/scala-cli/pull/3589)\n* Run `test` sub-command integration tests on default JVM settings by [@Gedochao](https://github.com/Gedochao) in [#3592](https://github.com/VirtusLab/scala-cli/pull/3592)\n* Retry docs' tests on the CI by [@Gedochao](https://github.com/Gedochao) in [#3618](https://github.com/VirtusLab/scala-cli/pull/3618)\n* Move `ScopeOptions` to `SharedOptions` by [@Gedochao](https://github.com/Gedochao) in [#3612](https://github.com/VirtusLab/scala-cli/pull/3612)\n* Include missing Scala `3.6.*` versions in `Scala.listAll` by [@Gedochao](https://github.com/Gedochao) in [#3652](https://github.com/VirtusLab/scala-cli/pull/3652)\n* Check formatting with Scala CLI, rather than the `scalafmt` launcher itself by [@Gedochao](https://github.com/Gedochao) in [#3660](https://github.com/VirtusLab/scala-cli/pull/3660)\n\n### Documentation changes\n* compileOnly option added to the documentation by [@yadavan88](https://github.com/yadavan88) in [#3600](https://github.com/VirtusLab/scala-cli/pull/3600)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3601](https://github.com/VirtusLab/scala-cli/pull/3601)\n* docs: guide for compile only deps by [@scarf005](https://github.com/scarf005) in [#3602](https://github.com/VirtusLab/scala-cli/pull/3602)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3607](https://github.com/VirtusLab/scala-cli/pull/3607)\n* Add missing `using` directive reference docs by [@Gedochao](https://github.com/Gedochao) in [#3608](https://github.com/VirtusLab/scala-cli/pull/3608)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3610](https://github.com/VirtusLab/scala-cli/pull/3610)\n* Fix formatting in directives' reference docs by [@Gedochao](https://github.com/Gedochao) in [#3611](https://github.com/VirtusLab/scala-cli/pull/3611)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3616](https://github.com/VirtusLab/scala-cli/pull/3616)\n* Fixed DEV.md file related to test command by [@yadavan88](https://github.com/yadavan88) in [#3619](https://github.com/VirtusLab/scala-cli/pull/3619)\n* Correct doc with --project-version by [@joan38](https://github.com/joan38) in [#3662](https://github.com/VirtusLab/scala-cli/pull/3662)\n\n### Updates\n* Bump webfactory/ssh-agent from 0.9.0 to 0.9.1 by [@dependabot](https://github.com/dependabot) in [#3576](https://github.com/VirtusLab/scala-cli/pull/3576)\n* Bump react from 18.2.0 to 18.3.1 in /website by [@dependabot](https://github.com/dependabot) in [#3573](https://github.com/VirtusLab/scala-cli/pull/3573)\n* Update scala-cli.sh launcher for 1.7.1 by [@github-actions](https://github.com/github-actions) in [#3579](https://github.com/VirtusLab/scala-cli/pull/3579)\n* Update guava to 33.4.5-jre by [@scala-steward](https://github.com/scala-steward) in [#3581](https://github.com/VirtusLab/scala-cli/pull/3581)\n* Update bloop-rifle_2.13 to 2.0.9 by [@scala-steward](https://github.com/scala-steward) in [#3580](https://github.com/VirtusLab/scala-cli/pull/3580)\n* Update sbt, scripted-plugin to 1.10.11 by [@scala-steward](https://github.com/scala-steward) in [#3582](https://github.com/VirtusLab/scala-cli/pull/3582)\n* Pin & update docker images by [@Gedochao](https://github.com/Gedochao) in [#3558](https://github.com/VirtusLab/scala-cli/pull/3558)\n* Bump clsx from 1.2.1 to 2.1.1 in /website by [@dependabot](https://github.com/dependabot) in [#3560](https://github.com/VirtusLab/scala-cli/pull/3560)\n* Bump `docusaurus` to 3.7.0 by [@Gedochao](https://github.com/Gedochao) in [#3585](https://github.com/VirtusLab/scala-cli/pull/3585)\n* Bump react-dom from 18.2.0 to 18.3.1 in /website by [@dependabot](https://github.com/dependabot) in [#3587](https://github.com/VirtusLab/scala-cli/pull/3587)\n* Bump sass from 1.58.3 to 1.86.0 in /website by [@dependabot](https://github.com/dependabot) in [#3588](https://github.com/VirtusLab/scala-cli/pull/3588)\n* Bump @easyops-cn/docusaurus-search-local from 0.49.1 to 0.49.2 in /website by [@dependabot](https://github.com/dependabot) in [#3604](https://github.com/VirtusLab/scala-cli/pull/3604)\n* Update asm to 9.8 by [@scala-steward](https://github.com/scala-steward) in [#3606](https://github.com/VirtusLab/scala-cli/pull/3606)\n* Update guava to 33.4.6-jre by [@scala-steward](https://github.com/scala-steward) in [#3605](https://github.com/VirtusLab/scala-cli/pull/3605)\n* Bump sass from 1.86.0 to 1.86.1 in /website by [@dependabot](https://github.com/dependabot) in [#3603](https://github.com/VirtusLab/scala-cli/pull/3603)\n* Bump @mdx-js/react from 3.0.0 to 3.1.0 in /website by [@dependabot](https://github.com/dependabot) in [#3575](https://github.com/VirtusLab/scala-cli/pull/3575)\n* Bump sass from 1.86.1 to 1.86.3 in /website by [@dependabot](https://github.com/dependabot) in [#3622](https://github.com/VirtusLab/scala-cli/pull/3622)\n* Bump estree-util-value-to-estree from 3.0.1 to 3.3.3 in /website by [@dependabot](https://github.com/dependabot) in [#3623](https://github.com/VirtusLab/scala-cli/pull/3623)\n* Update guava to 33.4.7-jre by [@scala-steward](https://github.com/scala-steward) in [#3625](https://github.com/VirtusLab/scala-cli/pull/3625)\n* Update Scala Next RC to 3.7.0-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3628](https://github.com/VirtusLab/scala-cli/pull/3628)\n* Update core_2.13 to 3.11.0 by [@scala-steward](https://github.com/scala-steward) in [#3630](https://github.com/VirtusLab/scala-cli/pull/3630)\n* Update scala3-library to 3.7.0-RC3 by [@scala-steward](https://github.com/scala-steward) in [#3638](https://github.com/VirtusLab/scala-cli/pull/3638)\n* Update guava to 33.4.8-jre by [@scala-steward](https://github.com/scala-steward) in [#3637](https://github.com/VirtusLab/scala-cli/pull/3637)\n* Bump announced Scala Next RC to 3.7.0-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3636](https://github.com/VirtusLab/scala-cli/pull/3636)\n* Bump http-proxy-middleware from 2.0.6 to 2.0.9 in /website by [@dependabot](https://github.com/dependabot) in [#3642](https://github.com/VirtusLab/scala-cli/pull/3642)\n* Declare BSP server as capable of providing output paths by [@Gedochao](https://github.com/Gedochao) in [#3645](https://github.com/VirtusLab/scala-cli/pull/3645)\n* Update Scala.js to 1.19.0 by [@scala-steward](https://github.com/scala-steward) in [#3643](https://github.com/VirtusLab/scala-cli/pull/3643)\n* Update metaconfig-typesafe-config to 0.16.0 by [@scala-steward](https://github.com/scala-steward) in [#3649](https://github.com/VirtusLab/scala-cli/pull/3649)\n* Update Scala 3 Next RC to 3.7.0-RC4 by [@scala-steward](https://github.com/scala-steward) in [#3648](https://github.com/VirtusLab/scala-cli/pull/3648)\n* Bump sass from 1.86.3 to 1.87.0 in /website by [@dependabot](https://github.com/dependabot) in [#3651](https://github.com/VirtusLab/scala-cli/pull/3651)\n* Update semanticdb-shared_2.13 to 4.13.5 by [@scala-steward](https://github.com/scala-steward) in [#3658](https://github.com/VirtusLab/scala-cli/pull/3658)\n* Update munit to 1.1.1 by [@scala-steward](https://github.com/scala-steward) in [#3656](https://github.com/VirtusLab/scala-cli/pull/3656)\n* Update jsoup to 1.20.1 by [@scala-steward](https://github.com/scala-steward) in [#3655](https://github.com/VirtusLab/scala-cli/pull/3655)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.9.5 by [@scala-steward](https://github.com/scala-steward) in [#3657](https://github.com/VirtusLab/scala-cli/pull/3657)\n* Bump Scala 3 Next to 3.7.0 by [@Gedochao](https://github.com/Gedochao) in [#3661](https://github.com/VirtusLab/scala-cli/pull/3661)\n* Bump Scala 3 Next RC to 3.7.1-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3663](https://github.com/VirtusLab/scala-cli/pull/3663)\n* Update `runner` & `test-runner` to Scala 3.3.5 LTS (was 3.0.2) by [@Gedochao](https://github.com/Gedochao) in [#3650](https://github.com/VirtusLab/scala-cli/pull/3650)\n* Update `scalafmt` to 3.9.6 by [@scala-steward](https://github.com/scala-steward) in [#3667](https://github.com/VirtusLab/scala-cli/pull/3667)\n* chore: Bump Bloop to 2.0.10 by [@tgodzik](https://github.com/tgodzik) in [#3670](https://github.com/VirtusLab/scala-cli/pull/3670)\n* Bump Scala 3 LTS to 3.3.6 by [@Gedochao](https://github.com/Gedochao) in [#3671](https://github.com/VirtusLab/scala-cli/pull/3671)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.7.1...v1.8.0\n\n## [v1.7.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.7.1)\n\n### Support for Scala 3.6.4\nThis Scala CLI version switches the default Scala version to 3.6.4.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.7.1\n# Scala version (default): 3.6.4\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3544](https://github.com/VirtusLab/scala-cli/pull/3544)\n\n### Support the `--test` command line option for `doc`\n\nIt is now possible to generate docs from the test scope with the `--test` flag.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3539](https://github.com/VirtusLab/scala-cli/pull/3539)\n\n### Features\n* Support the `--test` flag with the `doc` sub-command by [@Gedochao](https://github.com/Gedochao) in [#3539](https://github.com/VirtusLab/scala-cli/pull/3539)\n\n### Internal and build changes\n* Adjust `dependabot` configuration for the docs website by [@Gedochao](https://github.com/Gedochao) in [#3555](https://github.com/VirtusLab/scala-cli/pull/3555)\n\n### Documentation changes\n* docs: document Scala Native flag options by [@scarf005](https://github.com/scarf005) in [#3557](https://github.com/VirtusLab/scala-cli/pull/3557)\n\n### Updates\n* Update jsoup to 1.19.1 by [@scala-steward](https://github.com/scala-steward) in [#3542](https://github.com/VirtusLab/scala-cli/pull/3542)\n* Update scala-cli.sh launcher for 1.7.0 by [@github-actions](https://github.com/github-actions) in [#3543](https://github.com/VirtusLab/scala-cli/pull/3543)\n* Bump Scala 3 Next to 3.6.4 by [@Gedochao](https://github.com/Gedochao) in [#3544](https://github.com/VirtusLab/scala-cli/pull/3544)\n* Bump SBT to 1.10.10 by [@Gedochao](https://github.com/Gedochao) in [#3545](https://github.com/VirtusLab/scala-cli/pull/3545)\n* Update semanticdb-shared_2.13 to 4.13.3 by [@scala-steward](https://github.com/scala-steward) in [#3549](https://github.com/VirtusLab/scala-cli/pull/3549)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.9.3 by [@scala-steward](https://github.com/scala-steward) in [#3548](https://github.com/VirtusLab/scala-cli/pull/3548)\n* Bump `scala-cli-signing` to 0.2.6 by [@Gedochao](https://github.com/Gedochao) in [#3552](https://github.com/VirtusLab/scala-cli/pull/3552)\n* Bump react-player from 2.11.2 to 2.16.0 in /website by [@dependabot](https://github.com/dependabot) in [#3561](https://github.com/VirtusLab/scala-cli/pull/3561)\n* Bump dorny/test-reporter from 1 to 2 by [@dependabot](https://github.com/dependabot) in [#3559](https://github.com/VirtusLab/scala-cli/pull/3559)\n* Bump docusaurus-plugin-sass from 0.2.5 to 0.2.6 in /website by [@dependabot](https://github.com/dependabot) in [#3563](https://github.com/VirtusLab/scala-cli/pull/3563)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3565](https://github.com/VirtusLab/scala-cli/pull/3565)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.9.4 by [@scala-steward](https://github.com/scala-steward) in [#3567](https://github.com/VirtusLab/scala-cli/pull/3567)\n* Bump @easyops-cn/docusaurus-search-local from 0.38.1 to 0.49.1 in /website by [@dependabot](https://github.com/dependabot) in [#3562](https://github.com/VirtusLab/scala-cli/pull/3562)\n* Update announced Scala 3 Next to 3.6.4 by [@Gedochao](https://github.com/Gedochao) in [#3569](https://github.com/VirtusLab/scala-cli/pull/3569)\n* Update semanticdb-shared_2.13 to 4.13.4 by [@scala-steward](https://github.com/scala-steward) in [#3568](https://github.com/VirtusLab/scala-cli/pull/3568)\n\n## [v1.7.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.7.0)\n\n### Switch to `scalameta/scalafmt` images of `scalafmt` 3.9.1+\n\nSince version 3.9.1 `scalafmt` ships with native images built with Scala Native. As a result, \nwe are sunsetting https://github.com/virtuslab/scalafmt-native-image and Scala CLI will use the artifacts \nfrom https://github.com/scalameta/scalafmt for `scalafmt` versions >=3.9.1\n\nNote that older Scala CLI versions may still attempt to download a native image from the old repository for the new versions.\nWe will keep releasing those for a short while to help late upgraders migrate.\n\n```bash\nscala-cli fmt -F -version\n# scalafmt 3.9.2\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3521](https://github.com/VirtusLab/scala-cli/pull/3521)\n\n### Support the `--test` command line option for `run` and `package`\n\nIt is now possible to `run` a main method from the test scope with the `--test` flag.\n\n```scala title=HelloFromTestScope.scala\n//> using target.scope test\n@main def helloFromTestScope(): Unit = println(\"Hello from the test scope!\")\n```\n\n```bash\nscala-cli run HelloFromTestScope.scala --test --power\n\n# Hello from the test scope!  \n```\n\nSimilarly, it is now possible to `package` the main and test scopes together, using the same `--test` flag.\n\n```bash\nscala-cli package HelloFromTestScope.scala --test --power\n# # Wrote /Users/pchabelski/IdeaProjects/scala-cli-tests-2/untitled/v170/helloFromTestScope, run it with                                       \n#  ./helloFromTestScope  \n./helloFromTestScope\n# Hello from the test scope!  \n```\n\nKeep in mind that the test and main scopes are still separate compilation units, where the test scope depends on the main scope (while the reverse isn't true).\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3502](https://github.com/VirtusLab/scala-cli/pull/3502) and [#3519](https://github.com/VirtusLab/scala-cli/pull/3519)\n\n### Detect objects with main class in scripts\n\nScala CLI now detects objects with a main method in scripts and runs them by default.\n\n```scala title=scriptMainObject.sc\nobject Main {\n  def main(args: Array[String]): Unit = println(\"Hello\")\n}\n```\n\nDo note that, this is chiefly a convenience feature for migration of old scripts, using the old, legacy `scala` runner.\n\nIf any top-level code is present alongside an object with a main method, the top-level code will be run instead and a warning printed.\n\n```scala title=scriptWithMainObjectAndTopLevel.sc\nobject Main {\n  def main(args: Array[String]): Unit = println(\"Hello\")\n}\nprintln(\"Top level code says hello\")\n```\n\n```bash\nscala-cli run scriptWithMainObjectAndTopLevel.sc\n# [warn]  Script contains objects with main methods and top-level statements, only the latter will be run.                                   \n# Compiling project (Scala 3.6.3, JVM (23))\n# Compiled project (Scala 3.6.3, JVM (23))\n# Top level code says hello\n```\n\nAdditionally, cases where multiple main methods are present in the same script are not supported, inidicated by a warning.\n\n```scala title=scriptWithMultipleMainObjects.sc\nobject Main {\n  def main(args: Array[String]): Unit = println(\"Hello1\")\n}\n\nobject Main2 {\n  def main(args: Array[String]): Unit = println(\"Hello2\")\n}\n```\n\nNote that no output is printed in this example:\n\n```bash\nscala-cli run scriptWithMultipleMainObjects.sc\n# [warn]  Only a single main is allowed within scripts. Multiple main classes were found in the script: Main, Main2                          \n# Compiling project (Scala 3.6.3, JVM (23))\n# Compiled project (Scala 3.6.3, JVM (23))\n```\n\nFinally, main methods defined in this way cannot be chosen via the `--main-class` command line option directive,\nand neither will they be printed by the `--list-main-methods` flag.\n\nAdded by [@tgodzik](https://github.com/tgodzik) in [#3479](https://github.com/VirtusLab/scala-cli/pull/3479)\n\n### Support for Scala Native 0.5.7\nThis Scala CLI version switches the default Scala Native version to 0.5.7.\n\n```bash\nscala-cli -e 'println(\"Hello from Scala Native 0.5.7!\")' --native\n# Compiling project (Scala 3.6.3, Scala Native 0.5.7)\n# Compiled project (Scala 3.6.3, Scala Native 0.5.7)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (1045 ms)\n# [info] Discovered 915 classes and 5608 methods after classloading\n# [info] Checking intermediate code (quick) (41 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (352 ms)\n# [info] Discovered 498 classes and 2506 methods after classloading\n# [info] Checking intermediate code (quick) (9 ms)\n# [info] Discovered 477 classes and 1930 methods after optimization\n# [info] Optimizing (debug mode) (608 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (650 ms)\n# [info] Compiling to native code (1674 ms)\n# [info] Linking with [pthread, dl, m]\n# [info] Linking native code (immix gc, none lto) (339 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (4655 ms)\n# Hello from Scala Native 0.5.7!\n```\n\nAdded in [#3527](https://github.com/VirtusLab/scala-cli/pull/3527)\n\n### Features\n* improvement: Detect objects with main class in scripts by [@tgodzik](https://github.com/tgodzik) in [#3479](https://github.com/VirtusLab/scala-cli/pull/3479)\n* Add support for running a main method from the test scope by [@Gedochao](https://github.com/Gedochao) in [#3502](https://github.com/VirtusLab/scala-cli/pull/3502)\n* Support the `--test` flag with the `package` sub-command by [@Gedochao](https://github.com/Gedochao) in [#3519](https://github.com/VirtusLab/scala-cli/pull/3519)\n\n### Fixes\n* Improve handling for parallel Scala CLI runs by [@Gedochao](https://github.com/Gedochao) in [#3399](https://github.com/VirtusLab/scala-cli/pull/3399)\n* fix: Don't compile docs if there is no need by [@ghostbuster91](https://github.com/ghostbuster91) in [#3503](https://github.com/VirtusLab/scala-cli/pull/3503)\n* fix: correctly report error position on unknown directive without values by [@kasiaMarek](https://github.com/kasiaMarek) in [#3518](https://github.com/VirtusLab/scala-cli/pull/3518)\n\n### Internal and build changes\n* fix for #3510 by [@philwalk](https://github.com/philwalk) in [#3513](https://github.com/VirtusLab/scala-cli/pull/3513)\n* Fall back to the `cs` command on `PATH` in the `mill` script by [@Gedochao](https://github.com/Gedochao) in [#3517](https://github.com/VirtusLab/scala-cli/pull/3517)\n\n### Documentation changes\n* Curl install launcher in your repo by [@joan38](https://github.com/joan38) in [#3532](https://github.com/VirtusLab/scala-cli/pull/3532)\n\n### Updates\n* Update scala-cli.sh launcher for 1.6.2 by [@github-actions](https://github.com/github-actions) in [#3495](https://github.com/VirtusLab/scala-cli/pull/3495)\n* Update metaconfig-typesafe-config to 0.15.0 by [@scala-steward](https://github.com/scala-steward) in [#3497](https://github.com/VirtusLab/scala-cli/pull/3497)\n* Update bloop-config_2.13 to 2.3.2 by [@scala-steward](https://github.com/scala-steward) in [#3496](https://github.com/VirtusLab/scala-cli/pull/3496)\n* Update semanticdb-shared_2.13 to 4.13.0 by [@scala-steward](https://github.com/scala-steward) in [#3500](https://github.com/VirtusLab/scala-cli/pull/3500)\n* Update ammonite to 3.0.2 by [@scala-steward](https://github.com/scala-steward) in [#3504](https://github.com/VirtusLab/scala-cli/pull/3504)\n* Update semanticdb-shared_2.13 to 4.13.1.1 by [@scala-steward](https://github.com/scala-steward) in [#3508](https://github.com/VirtusLab/scala-cli/pull/3508)\n* Update scalafix-interfaces to 0.14.1 by [@scala-steward](https://github.com/scala-steward) in [#3511](https://github.com/VirtusLab/scala-cli/pull/3511)\n* Update semanticdb-shared_2.13 to 4.13.2 by [@scala-steward](https://github.com/scala-steward) in [#3515](https://github.com/VirtusLab/scala-cli/pull/3515)\n* Update scalafix-interfaces to 0.14.2 by [@scala-steward](https://github.com/scala-steward) in [#3514](https://github.com/VirtusLab/scala-cli/pull/3514)\n* Update slf4j-nop to 2.0.17 by [@scala-steward](https://github.com/scala-steward) in [#3520](https://github.com/VirtusLab/scala-cli/pull/3520)\n* Bump Scala 3 Next RC to 3.6.4-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3525](https://github.com/VirtusLab/scala-cli/pull/3525)\n* Update Scala Native to 0.5.7 by [@scala-steward](https://github.com/scala-steward) in [#3527](https://github.com/VirtusLab/scala-cli/pull/3527)\n* Bump `scala-packager` to 0.1.32 & linux CI runners to `ubuntu-24.04` by [@Gedochao](https://github.com/Gedochao) in [#3528](https://github.com/VirtusLab/scala-cli/pull/3528)\n* Update `scalafmt` to 3.9.1 by [@Gedochao](https://github.com/Gedochao) in [#3521](https://github.com/VirtusLab/scala-cli/pull/3521)\n* Bump `scalafmt` to 3.9.2 by [@Gedochao](https://github.com/Gedochao) in [#3533](https://github.com/VirtusLab/scala-cli/pull/3533)\n* Bump gifs tests Ubuntu Docker image to `ubuntu:24.04` by [@Gedochao](https://github.com/Gedochao) in [#3534](https://github.com/VirtusLab/scala-cli/pull/3534)\n* Update sbt, scripted-plugin to 1.10.9 by [@scala-steward](https://github.com/scala-steward) in [#3537](https://github.com/VirtusLab/scala-cli/pull/3537)\n* Bump Linux ARM64 Docker image to `ubuntu:24.04` by [@Gedochao](https://github.com/Gedochao) in [#3535](https://github.com/VirtusLab/scala-cli/pull/3535)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.6.2...v1.7.0\n\n## [v1.6.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.6.2)\n\n### Support for Scala.js 1.18.2\nThis Scala CLI version adds support for Scala.js 1.18.2.\n\n```bash\nscala-cli -e 'println(\"Hello\")' --js\n# Compiling project (Scala 3.6.3, Scala.js 1.18.2)\n# Compiled project (Scala 3.6.3, Scala.js 1.18.2)\n# Hello\n```\n\nAdded in [#3454](https://github.com/VirtusLab/scala-cli/pull/3454)\n\n### Support for Scala 3.3.5\nThe Scala CLI internals are now built with Scala 3.3.5.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3466](https://github.com/VirtusLab/scala-cli/pull/3466)\n\n### Deprecations\n* Add a deprecation warning for using Scala 2.12.4 with Bloop by [@Gedochao](https://github.com/Gedochao) in [#3470](https://github.com/VirtusLab/scala-cli/pull/3470)\n\n### Fixes\n* Remove conflicting Scala 2.13 `io.get-coursier:dependency` dependency & add a CI check by [@Gedochao](https://github.com/Gedochao) in [#3472](https://github.com/VirtusLab/scala-cli/pull/3472)\n\n### Internal and build changes\n* Fix Scala Steward post-update hook by [@fthomas](https://github.com/fthomas) in [#3465](https://github.com/VirtusLab/scala-cli/pull/3465)\n* Bump Windows runners to windows-2025 on the CI by [@Gedochao](https://github.com/Gedochao) in [#3489](https://github.com/VirtusLab/scala-cli/pull/3489)\n\n### Documentation changes\n* Add warning about test files in publish docs by [@majk-p](https://github.com/majk-p) in [#3486](https://github.com/VirtusLab/scala-cli/pull/3486)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3487](https://github.com/VirtusLab/scala-cli/pull/3487)\n\n## Updates\n* Update scala-cli.sh launcher for 1.6.1 by [@github-actions](https://github.com/github-actions) in [#3452](https://github.com/VirtusLab/scala-cli/pull/3452)\n* Update munit to 1.1.0 by [@scala-steward](https://github.com/scala-steward) in [#3455](https://github.com/VirtusLab/scala-cli/pull/3455)\n* Update latest announced Scala 3 Next RC to 3.6.4-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3458](https://github.com/VirtusLab/scala-cli/pull/3458)\n* Update scalajs-sbt-test-adapter_2.13 to 1.18.2 by [@scala-steward](https://github.com/scala-steward) in [#3454](https://github.com/VirtusLab/scala-cli/pull/3454)\n* Update Scala 3 LTS to 3.3.5 by [@Gedochao](https://github.com/Gedochao) in [#3466](https://github.com/VirtusLab/scala-cli/pull/3466)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.8.6 by [@scala-steward](https://github.com/scala-steward) in [#3462](https://github.com/VirtusLab/scala-cli/pull/3462)\n* Update sbt, scripted-plugin to 1.10.7 by [@scala-steward](https://github.com/scala-steward) in [#3461](https://github.com/VirtusLab/scala-cli/pull/3461)\n* Bump Scala 3 Next announced version to 3.6.3 by [@Gedochao](https://github.com/Gedochao) in [#3469](https://github.com/VirtusLab/scala-cli/pull/3469)\n* Bump `ammonite` to 3.0.1 by [@Gedochao](https://github.com/Gedochao) in [#3468](https://github.com/VirtusLab/scala-cli/pull/3468)\n* Update core_2.13 to 3.10.3 by [@scala-steward](https://github.com/scala-steward) in [#3476](https://github.com/VirtusLab/scala-cli/pull/3476)\n* Update scala-collection-compat to 2.13.0 by [@scala-steward](https://github.com/scala-steward) in [#3478](https://github.com/VirtusLab/scala-cli/pull/3478)\n* Update case-app to 2.1.0-M30 by [@scala-steward](https://github.com/scala-steward) in [#3485](https://github.com/VirtusLab/scala-cli/pull/3485)\n* Bump Scala toolkit to 0.6.0 by [@Gedochao](https://github.com/Gedochao) in [#3471](https://github.com/VirtusLab/scala-cli/pull/3471)\n* Update Scala Toolkit to 0.7.0 by [@Gedochao](https://github.com/Gedochao) in [#3488](https://github.com/VirtusLab/scala-cli/pull/3488)\n\n## New Contributors\n* [@fthomas](https://github.com/fthomas) made their first contribution in [#3465](https://github.com/VirtusLab/scala-cli/pull/3465)\n* [@majk-p](https://github.com/majk-p) made their first contribution in [#3486](https://github.com/VirtusLab/scala-cli/pull/3486)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.6.1...v1.6.2\n\n## [v1.6.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.6.1)\n\n## Pass `--repl-init-script` directly to the Scala REPL\nPassing an initialization script to the REPL with `--repl-init-script` is now allowed directly, rather than after `--` or with `-O`.\nThe `--repl-init-script` is a REPL option introduced in Scala 3.6.4, so it's not available for earlier Scala versions.\n```bash ignore\nscala-cli repl -S 3.6.4-RC1 --repl-init-script 'println(\"Hello\")'\n# Hello\n# Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).\n# Type in expressions for evaluation. Or try :help.\n#                                                                                                                  \n# scala> \n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3447](https://github.com/VirtusLab/scala-cli/pull/3447)\n\n### Hotfix release\nAlthough Scala CLI 1.6.1 includes a few updates and improvements, it is primarily a hotfix release for version 1.6.0, which due to technical limitations wasn't available on some of our distribution channels.\n\nFor extra context refer to:\n* [Scala CLI 1.6.0 release notes](#v160)\n\n## Features\n- Enable direct usage of --repl-init-script with Scala REPL >= 3.6.4-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3447](https://github.com/VirtusLab/scala-cli/pull/3447)\n\n## Internal and build changes\n* Fix `update-packages` step of the release job on the CI by [@Gedochao](https://github.com/Gedochao) in [#3446](https://github.com/VirtusLab/scala-cli/pull/3446)\n\n## Updates\n* Bump Scala CLI launchers to v1.6.0 by [@Gedochao](https://github.com/Gedochao) in [#3450](https://github.com/VirtusLab/scala-cli/pull/3450)\n* chore: Update Bloop to 2.0.8 by [@tgodzik](https://github.com/tgodzik) in [#3449](https://github.com/VirtusLab/scala-cli/pull/3449)\n* Update scalafmt to 3.8.5 by [@scala-steward](https://github.com/scala-steward) in [#3442](https://github.com/VirtusLab/scala-cli/pull/3442)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.6.0...v1.6.1\n\n## [v1.6.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.6.0)\n\n### Scala CLI 1.6.0 will not be available on all distribution channels\nDue to technical difficulties with our release pipeline, Scala CLI 1.6.0 release distribution channels were limited to:\n- its [GitHub release page](https://github.com/VirtusLab/scala-cli/releases/tag/v1.6.0), where launchers for all platforms are available as normal\n- Maven Central\n- WinGet\n- Chocolatey\n\nWhile it can be used as such, we followed it up with [a hotfix 1.6.1 release](#v161), which should be available through all standard distribution channels.\n\n```bash\nscala-cli --cli-version 1.6.0 --version\n# Scala CLI version: 1.6.0\n# Scala version (default): 3.6.3\n```\n\n### Fixed commas being treated as `using` directive value separators & deprecated using them with whitespace\n:::warning\nThese are breaking changes affecting using directives syntax.\nThey're technically fixes + a deprecation, but in a very rare scenario existing builds could break, if they were relying on the erroneous syntax.\n:::\n\nThis Scala CLI version fixes commas (`,`) being treated as `using` directive value separators on their own.\n\nFormerly, a directive like:\n\n```scala compile\n//> using options -Wunused:locals,privates\n```\n\nWould be (erroneously) interpreted as the following 2 options for the compiler: `-Wunused:locals` and `privates`.\nAs a comma will now no longer be treated as a separator (which it never should have been), it will now be interpreted correctly as\na single option: `-Wunused:locals,privates`.\nBefore this change, the only way to pass this value to the `options` directive key was escaping the comma with double quotes:\n```scala compile\n//> using options \"-Wunused:locals,privates\"\n```\nThe escaping is no longer necessary.\n\nAdditionally, using commas along with whitespace as separators is now deprecated for future removal.\n```bash ignore\nscala-cli compile --scala-snippet '//> using options -Wunused:locals, -Wunused:privates'\n# [warn] <snippet>-scala-snippet:1:34\n# [warn] Use of commas as separators is deprecated. Only whitespace is neccessary.\n# Starting compilation server\n# Compiling project (Scala 3.6.3, JVM (23))\n# Compiled project (Scala 3.6.3, JVM (23))\n```\n\nFinally, the use of `/* (..) */` comments in `using` directives is no longer supported.\n```scala fail\n//> using /* some comment */ options -Wunused:locals /* some other comment */ -Wunused:privates\n// this syntax used to be supported, but will now fail.\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3381](https://github.com/VirtusLab/scala-cli/pull/3381) and [#3333](https://github.com/VirtusLab/scala-cli/pull/3333)\n\n### Cap vague Scala versions at defaults\n:::warning\nThis is a breaking change regarding how the Scala version is resolved.\n:::\nWe have changed how a Scala version is picked when `major` or `major.minor` prefixes are passed, rather than the full version tag:\n- `-S 3` will now point to the [launcher default Scala 3 Next version](https://github.com/VirtusLab/scala-cli/blob/72c23a54ac3ef0d3b09aa2646733225a1ac8426a/project/deps.sc#L11), rather than whatever is the latest stable version that `coursier` can find upstream\n- similarly, `-S 3.<current launcher default minor>` will now point to the [launcher default Scala 3 Next version](https://github.com/VirtusLab/scala-cli/blob/72c23a54ac3ef0d3b09aa2646733225a1ac8426a/project/deps.sc#L11)\n- `-S 2.13` will point to the[ launcher default Scala 2.13 version](https://github.com/VirtusLab/scala-cli/blob/72c23a54ac3ef0d3b09aa2646733225a1ac8426a/project/deps.sc#L7) (which up till now only affected tests and generated docs)\n- similarly, `-S 2.12` will now point to the [launcher default Scala 2.12 version](https://github.com/VirtusLab/scala-cli/blob/72c23a54ac3ef0d3b09aa2646733225a1ac8426a/project/deps.sc#L6)\n- launcher defaults are overridden for a particular Scala series with the `--cli-user-scala-version` to accommodate for Scala CLI installed as `scala`\n\nFor example:\n```scala\n//> using 3\n// When compiled with Scala CLI v1.6.0, this snippet will use Scala 3.6.3 (the built-in default), even if a newer version has been released.\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3259](https://github.com/VirtusLab/scala-cli/pull/3259)\n\n### Support for Scala 3.6.3 and 2.13.16\nThis Scala CLI version switches the default Scala version to 3.6.3.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.6.0\n# Scala version (default): 3.6.3\n```\n\nIt has also been tested with Scala 2.13.16.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3426](https://github.com/VirtusLab/scala-cli/pull/3426) and [#3418](https://github.com/VirtusLab/scala-cli/pull/3418)\n\n### Support for Scala.js 1.18.1\nThis Scala CLI version adds support for Scala.js 1.18.1.\n\n```bash\nscala-cli -e 'println(\"Hello\")' --js\n# Compiling project (Scala 3.6.3, Scala.js 1.18.1)\n# Compiled project (Scala 3.6.3, Scala.js 1.18.1)\n# Hello\n```\n\nAdded in [#3440](https://github.com/VirtusLab/scala-cli/pull/3440) and [scala-js-cli#113](https://github.com/VirtusLab/scala-js-cli/pull/113)\n\n### (⚡️ experimental) `scalafix` integration\nWe now support running `scalafix` rules with the `fix` sub-command.\n```bash ignore\nscala-cli fix . --power\n# The `fix` sub-command is experimental\n# Please bear in mind that non-ideal user experience should be expected.\n# If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n# Running built-in rules...\n# Writing project.scala\n# Removing directives from Smth.scala\n# Built-in rules completed.\n# Running scalafix rules...\n# Starting compilation server\n# Compiling project (Scala 3.6.3, JVM (23))\n# [warn] ./Main.scala:2:7\n# [warn] unused local definition\n# [warn]   val unused = \"unused\"\n# [warn]       ^^^^^^\n# Compiled project (Scala 3.6.3, JVM (23))\n# scalafix rules completed.\n```\n\nFormer fix functionalities are now referred to in the code as the built-in rules.\nEffectively, fix now runs 2 separate sets of rules (both enabled by default): built-in and scalafix.\nThey can be controlled via the `--enable-scalafix` and `--enable-built-in` command line options.\n\n`scalafix` rules are ran according to the configuration in `<project-root>/.scalafix.conf`.\n\nIt is possible to run [external scalafix rules](https://scalacenter.github.io/scalafix/docs/rules/external-rules.html) with the (⚡️ experimental) `scalafix.dep` directive:\n```scala compile power\n//> using scalafix.dep com.github.xuwei-k::scalafix-rules:0.6.0\n```\n\nAdded by [@Vigorge](https://github.com/Vigorge) and [@dos65](https://github.com/dos65) in [#2968](https://github.com/VirtusLab/scala-cli/pull/2968)\n\n### Support for running snapshot versions of the build server (Bloop)\nIt is now possible to pass a snapshot version to the `--bloop-version` command line option.\n\n```bash ignore\nscala-cli compile . --bloop-version 2.0.7-8-fe3f53d9-SNAPSHOT\n# Starting compilation server\n# Compiling project (Scala 3.6.3, JVM (23))\n# Compiled project (Scala 3.6.3, JVM (23))\nscala-cli --power bloop about\n# bloop v2.0.7-8-fe3f53d9-SNAPSHOT\n# \n# Using Scala v2.12.20 and Zinc v1.10.7\n# Running on Java JDK v23.0.1 (~/Library/Caches/Coursier/arc/https/github.com/adoptium/temurin23-binaries/releases/download/jdk-23.0.1%252B11/OpenJDK23U-jdk_aarch64_mac_hotspot_23.0.1_11.tar.gz/jdk-23.0.1+11/Contents/Home)\n#   -> Supports debugging user code, Java Debug Interface (JDI) is available.\n# Maintained by the Scala Center and the community.\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3405](https://github.com/VirtusLab/scala-cli/pull/3405)\n\n### Support for suppressing deprecation warnings\nIt is now possible to suppress deprecation warnings with the `--suppress-deprecated-warnings` command line option.\n\n```bash ignore\nscala-cli project-with-deprecated-stuff --suppress-deprecated-warnings\n````\n\nYou can also suppress deprecation warnings globally by setting the `suppress-warning.deprecated-features` configuration key.\n```bash ignore\nscala-cli config suppress-warning.deprecated-features true\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3406](https://github.com/VirtusLab/scala-cli/pull/3406)\n\n### Features\n* Scalafix command for scala-cli with basic options and tests by [@Vigorge](https://github.com/Vigorge) and [@dos65](https://github.com/dos65) in [#2968](https://github.com/VirtusLab/scala-cli/pull/2968)\n* Ensure vague Scala versions are capped at defaults by [@Gedochao](https://github.com/Gedochao) in [#3259](https://github.com/VirtusLab/scala-cli/pull/3259)\n* Merge `scalafix` into `fix` by [@Gedochao](https://github.com/Gedochao) in [#3400](https://github.com/VirtusLab/scala-cli/pull/3400)\n* Allow to use Bloop snapshot versions by [@Gedochao](https://github.com/Gedochao) in [#3405](https://github.com/VirtusLab/scala-cli/pull/3405)\n* Add ways to suppress deprecation warnings by [@Gedochao](https://github.com/Gedochao) in [#3406](https://github.com/VirtusLab/scala-cli/pull/3406)\n\n### Fixes\n* Misc improvements in compiler options handling by [@Gedochao](https://github.com/Gedochao) in [#3253](https://github.com/VirtusLab/scala-cli/pull/3253)\n* Allow shading of single-choice compiler options from the command line regardless of `-`/`--` prefix by [@Gedochao](https://github.com/Gedochao) in [#3279](https://github.com/VirtusLab/scala-cli/pull/3279)\n* Fix dependency main class detection throwing an NPE when JAR manifest doesn't list the main class correctly by [@Gedochao](https://github.com/Gedochao) in [#3319](https://github.com/VirtusLab/scala-cli/pull/3319)\n* Fix commas being treated as `using` directives value separators & deprecate using them with whitespace by [@Gedochao](https://github.com/Gedochao) in [#3333](https://github.com/VirtusLab/scala-cli/pull/3333)\n* Retain Bloop connection when restarting a build with `--watch` by [@Gedochao](https://github.com/Gedochao) in [#3351](https://github.com/VirtusLab/scala-cli/pull/3351)\n* Improve deprecation warnings for commas with whitespace used as using directive value separators by [@Gedochao](https://github.com/Gedochao) in [#3366](https://github.com/VirtusLab/scala-cli/pull/3366)\n* Recover from invalid paths returned from Bloop diagnostics by [@Gedochao](https://github.com/Gedochao) in [#3372](https://github.com/VirtusLab/scala-cli/pull/3372)\n* Add missing support for excluding transient dependencies when publishing by [@Gedochao](https://github.com/Gedochao) in [#3357](https://github.com/VirtusLab/scala-cli/pull/3357)\n* Fix using directives crashing on `*/` by removing `/* (..) */` comments support in `using_directives` by [@Gedochao](https://github.com/Gedochao) in [#3381](https://github.com/VirtusLab/scala-cli/pull/3381)\n* `fix` built-in rules: don't wrap directive values in double quotes if not necessary by [@Gedochao](https://github.com/Gedochao) in [#3414](https://github.com/VirtusLab/scala-cli/pull/3414)\n* Don't migrate directives with `fix` for single-file projects by [@Gedochao](https://github.com/Gedochao) in [#3422](https://github.com/VirtusLab/scala-cli/pull/3422)\n* Temporarily disable built-in rules of `fix` when `--check` is enabled, until fully supported by [@Gedochao](https://github.com/Gedochao) in [#3427](https://github.com/VirtusLab/scala-cli/pull/3427)\n* Ensure test source directives with test scope equivalents are migrated by `fix` to `project.scala` by [@Gedochao](https://github.com/Gedochao) in [#3425](https://github.com/VirtusLab/scala-cli/pull/3425)\n\n### Internal and build changes\n* Retry some of the occasionally flaky tests when failing on the CI by [@Gedochao](https://github.com/Gedochao) in [#3320](https://github.com/VirtusLab/scala-cli/pull/3320)\n* Retry more occasionally flaky tests on the CI by [@Gedochao](https://github.com/Gedochao) in [#3331](https://github.com/VirtusLab/scala-cli/pull/3331)\n* Tag `scalajs-dom` tests as flaky by [@Gedochao](https://github.com/Gedochao) in [#3336](https://github.com/VirtusLab/scala-cli/pull/3336)\n* Tag native packager tests as flaky by [@Gedochao](https://github.com/Gedochao) in [#3344](https://github.com/VirtusLab/scala-cli/pull/3344)\n* Make `generate-junit-reports.sc` script recover from test failures containing no trace data by [@Gedochao](https://github.com/Gedochao) in [#3341](https://github.com/VirtusLab/scala-cli/pull/3341)\n* Support `coursier`-downloaded `scala` wrapper tests on Windows by [@Gedochao](https://github.com/Gedochao) in [#3325](https://github.com/VirtusLab/scala-cli/pull/3325)\n* Get rid of duplicate names for uploaded/downloaded artifacts on the CI by [@Gedochao](https://github.com/Gedochao) in [#3342](https://github.com/VirtusLab/scala-cli/pull/3342)\n* Retry generating the Windows launcher up to 5 times by [@Gedochao](https://github.com/Gedochao) in [#3349](https://github.com/VirtusLab/scala-cli/pull/3349)\n* Retry generating Windows launchers in the `generate-native-image.sh` script directly, rather than the entire CI step by [@Gedochao](https://github.com/Gedochao) in [#3350](https://github.com/VirtusLab/scala-cli/pull/3350)\n* Fix integration tests when run with a Scala 3 LTS RC version by [@Gedochao](https://github.com/Gedochao) in [#3362](https://github.com/VirtusLab/scala-cli/pull/3362)\n* Retry some more flaky tests on the CI by [@Gedochao](https://github.com/Gedochao) in [#3382](https://github.com/VirtusLab/scala-cli/pull/3382)\n* Run extra tests for main supported JVM versions by [@Gedochao](https://github.com/Gedochao) in [#3375](https://github.com/VirtusLab/scala-cli/pull/3375)\n* tests: Add tests for issue with rereporting errors by [@tgodzik](https://github.com/tgodzik) in [#3390](https://github.com/VirtusLab/scala-cli/pull/3390)\n* Add extra logs when retrying flaky tests by [@Gedochao](https://github.com/Gedochao) in [#3433](https://github.com/VirtusLab/scala-cli/pull/3433)\n\n### Deprecations\n* Deprecate `--src` and `--sources` to disambiguate with `--source` compiler option by [@Gedochao](https://github.com/Gedochao) in [#3412](https://github.com/VirtusLab/scala-cli/pull/3412)\n\n### Documentation changes\n* docs: document Scala Native flag options by [@scarf005](https://github.com/scarf005) in [#3386](https://github.com/VirtusLab/scala-cli/pull/3386)\n* docs: document Scala Native flag options by [@scarf005](https://github.com/scarf005) in [#3416](https://github.com/VirtusLab/scala-cli/pull/3416)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3419](https://github.com/VirtusLab/scala-cli/pull/3419)\n* Merge `scalafix` doc into `fix` by [@Gedochao](https://github.com/Gedochao) in [#3420](https://github.com/VirtusLab/scala-cli/pull/3420)\n\n### Updates\n* Update Scala 3 Next RC to 3.6.2-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3305](https://github.com/VirtusLab/scala-cli/pull/3305)\n* Update scala-cli.sh launcher for 1.5.4 by [@github-actions](https://github.com/github-actions) in [#3308](https://github.com/VirtusLab/scala-cli/pull/3308)\n* Update `coursier` to 2.1.18 by [@Gedochao](https://github.com/Gedochao) in [#3312](https://github.com/VirtusLab/scala-cli/pull/3312)\n* Update `scala-packager` to 0.1.31 by [@Gedochao](https://github.com/Gedochao) in [#3311](https://github.com/VirtusLab/scala-cli/pull/3311)\n* Update jsoup to 1.18.2 by [@scala-steward](https://github.com/scala-steward) in [#3323](https://github.com/VirtusLab/scala-cli/pull/3323)\n* Update Scala 3 Next RC to 3.6.2-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3321](https://github.com/VirtusLab/scala-cli/pull/3321)\n* Bump `coursier` to 2.1.19 by [@Gedochao](https://github.com/Gedochao) in [#3326](https://github.com/VirtusLab/scala-cli/pull/3326)\n* Bump Typelevel toolkit to 0.1.29 by [@Gedochao](https://github.com/Gedochao) in [#3332](https://github.com/VirtusLab/scala-cli/pull/3332)\n* Bump Scala 3 Next RC to 3.6.2-RC3 by [@Gedochao](https://github.com/Gedochao) in [#3334](https://github.com/VirtusLab/scala-cli/pull/3334)\n* Update jsoup to 1.18.3 by [@scala-steward](https://github.com/scala-steward) in [#3338](https://github.com/VirtusLab/scala-cli/pull/3338)\n* Update sbt, scripted-plugin to 1.10.6 by [@scala-steward](https://github.com/scala-steward) in [#3339](https://github.com/VirtusLab/scala-cli/pull/3339)\n* Bump actions/upload-artifact & actions/download-artifact from 3 to 4 by [@Gedochao](https://github.com/Gedochao) in [#2701](https://github.com/VirtusLab/scala-cli/pull/2701)\n* Update munit to 1.0.3 by [@scala-steward](https://github.com/scala-steward) in [#3346](https://github.com/VirtusLab/scala-cli/pull/3346)\n* Update dependency to 0.3.2 by [@scala-steward](https://github.com/scala-steward) in [#3353](https://github.com/VirtusLab/scala-cli/pull/3353)\n* Update `coursier` to 2.1.20 by [@Gedochao](https://github.com/Gedochao) in [#3356](https://github.com/VirtusLab/scala-cli/pull/3356)\n* Bump Scala Next to 3.6.2 by [@Gedochao](https://github.com/Gedochao) in [#3358](https://github.com/VirtusLab/scala-cli/pull/3358)\n* Bump Scala Next RC to 3.6.3-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3360](https://github.com/VirtusLab/scala-cli/pull/3360)\n* Update metaconfig-typesafe-config to 0.14.0 by [@scala-steward](https://github.com/scala-steward) in [#3364](https://github.com/VirtusLab/scala-cli/pull/3364)\n* Update coursier-jvm_2.13, ... to 2.1.21 by [@scala-steward](https://github.com/scala-steward) in [#3363](https://github.com/VirtusLab/scala-cli/pull/3363)\n* Set Scala 3.6.2 as the latest Next announced version by [@Gedochao](https://github.com/Gedochao) in [#3365](https://github.com/VirtusLab/scala-cli/pull/3365)\n* Update bloop-rifle_2.13 to 2.0.6 by [@scala-steward](https://github.com/scala-steward) in [#3368](https://github.com/VirtusLab/scala-cli/pull/3368)\n* Update guava to 33.4.0-jre by [@scala-steward](https://github.com/scala-steward) in [#3376](https://github.com/VirtusLab/scala-cli/pull/3376)\n* Update `coursier` to 2.1.22 by [@scala-steward](https://github.com/scala-steward) in [#3378](https://github.com/VirtusLab/scala-cli/pull/3378)\n* Bump the announced Scala 3 Next RC version to 3.6.3-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3380](https://github.com/VirtusLab/scala-cli/pull/3380)\n* Update bloop-config_2.13 to 2.2.0 by [@scala-steward](https://github.com/scala-steward) in [#3392](https://github.com/VirtusLab/scala-cli/pull/3392)\n* Update `coursier` to 2.1.23 by [@scala-steward](https://github.com/scala-steward) in [#3395](https://github.com/VirtusLab/scala-cli/pull/3395)\n* Update Scala 3 Next RC to 3.6.3-RC2 by [@Gedochao](https://github.com/Gedochao) in [#3398](https://github.com/VirtusLab/scala-cli/pull/3398)\n* Set Scala 3.6.3-RC2 as the latest announced RC version by [@Gedochao](https://github.com/Gedochao) in [#3407](https://github.com/VirtusLab/scala-cli/pull/3407)\n* Update Scala 2.13 series to 2.13.16 by [@Gedochao](https://github.com/Gedochao) in [#3418](https://github.com/VirtusLab/scala-cli/pull/3418)\n* Update `coursier` to 2.1.24 by [@Gedochao](https://github.com/Gedochao) in [#3429](https://github.com/VirtusLab/scala-cli/pull/3429)\n* Update Scala Next to 3.6.3 by [@Gedochao](https://github.com/Gedochao) in [#3426](https://github.com/VirtusLab/scala-cli/pull/3426)\n* Update Scala Next RC to 3.6.4-RC1 by [@Gedochao](https://github.com/Gedochao) in [#3431](https://github.com/VirtusLab/scala-cli/pull/3431)\n* Update bloop-config_2.13 to 2.3.1 by [@scala-steward](https://github.com/scala-steward) in [#3435](https://github.com/VirtusLab/scala-cli/pull/3435)\n* Update core_2.13 to 3.10.2 by [@scala-steward](https://github.com/scala-steward) in [#3439](https://github.com/VirtusLab/scala-cli/pull/3439)\n* Update scalafix-interfaces to 0.14.0 by [@scala-steward](https://github.com/scala-steward) in [#3437](https://github.com/VirtusLab/scala-cli/pull/3437)\n* Update munit to 1.0.4 by [@scala-steward](https://github.com/scala-steward) in [#3441](https://github.com/VirtusLab/scala-cli/pull/3441)\n* Update Scala.js to 1.18.1 by [@scala-steward](https://github.com/scala-steward) in [#3440](https://github.com/VirtusLab/scala-cli/pull/3440)\n\n### New Contributors\n* [@Vigorge](https://github.com/Vigorge) made their first contribution in [#2968](https://github.com/VirtusLab/scala-cli/pull/2968)\n* [@scarf005](https://github.com/scarf005) made their first contribution in [#3386](https://github.com/VirtusLab/scala-cli/pull/3386)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.5.4...v1.6.0\n\n## [v1.5.4](https://github.com/VirtusLab/scala-cli/releases/tag/v1.5.4)\n\n### Hotfix release\nAlthough Scala CLI 1.5.4 includes a few updates and improvements, it is primarily a hotfix release for versions 1.5.2 and 1.5.3, which due to technical limitations weren't available on some of our distribution channels.\n\nFor extra context refer to:\n* [Scala CLI 1.5.2 release notes](#v152)\n* [Scala CLI 1.5.3 release notes](#v153)\n\n### Support for Scala Native 0.5.6\nThis Scala CLI version switches the default Scala Native version to 0.5.6.\n\n```bash\nscala-cli -e 'println(\"Hello from Scala Native 0.5.6!\")' --native\n# Compiling project (Scala 3.5.2, Scala Native 0.5.6)\n# Compiled project (Scala 3.5.2, Scala Native 0.5.6)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (949 ms)\n# [info] Discovered 887 classes and 5408 methods after classloading\n# [info] Checking intermediate code (quick) (40 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (285 ms)\n# [info] Discovered 499 classes and 2500 methods after classloading\n# [info] Checking intermediate code (quick) (7 ms)\n# [info] Discovered 478 classes and 1914 methods after optimization\n# [info] Optimizing (debug mode) (429 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (296 ms)\n# [info] Compiling to native code (1464 ms)\n# [info] Linking with [pthread, dl]\n# [info] Linking native code (immix gc, none lto) (208 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (3728 ms)\n# Hello from Scala Native 0.5.6!\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3295](https://github.com/VirtusLab/scala-cli/pull/3295)\n\n### Fixes\n* Pin Fedora docker image at `fedora:40` by [@Gedochao](https://github.com/Gedochao) in [#3283](https://github.com/VirtusLab/scala-cli/pull/3283)\n* Don't fail the `update-packages` and `windows-packages` jobs on individual distributions' steps by [@Gedochao](https://github.com/Gedochao) in [#3288](https://github.com/VirtusLab/scala-cli/pull/3288)\n\n### Documentation changes\n* Fix broken example in `//> using dep` reference doc by [@Gedochao](https://github.com/Gedochao) in [#3281](https://github.com/VirtusLab/scala-cli/pull/3281)\n* Mention distribution limitations in the Scala CLI 1.5.3 release notes by [@Gedochao](https://github.com/Gedochao) in [#3286](https://github.com/VirtusLab/scala-cli/pull/3286)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3287](https://github.com/VirtusLab/scala-cli/pull/3287)\n\n### Updates\n* Update `mill-native-image` to 0.1.29 by [@Gedochao](https://github.com/Gedochao) in [#3278](https://github.com/VirtusLab/scala-cli/pull/3278)\n* Update expecty to 0.17.0 by [@scala-steward](https://github.com/scala-steward) in [#3277](https://github.com/VirtusLab/scala-cli/pull/3277)\n* Update Bloop to 2.0.5 by [@Gedochao](https://github.com/Gedochao) in [#3276](https://github.com/VirtusLab/scala-cli/pull/3276)\n* Update dependency to 0.2.5 by [@scala-steward](https://github.com/scala-steward) in [#3269](https://github.com/VirtusLab/scala-cli/pull/3269)\n* Update `coursier` to 2.1.17 by [@Gedochao](https://github.com/Gedochao) in [#3275](https://github.com/VirtusLab/scala-cli/pull/3275)\n* Update SBT to 1.10.5 by [@Gedochao](https://github.com/Gedochao) in [#3280](https://github.com/VirtusLab/scala-cli/pull/3280)\n* Update `java-class-name` to 0.1.4 by [@Gedochao](https://github.com/Gedochao) in [#3284](https://github.com/VirtusLab/scala-cli/pull/3284)\n* Update scala-cli.sh launcher for 1.5.3 by [@Gedochao](https://github.com/Gedochao) in [#3285](https://github.com/VirtusLab/scala-cli/pull/3285)\n* Update Scala Native to 0.5.6 by [@Gedochao](https://github.com/Gedochao) in [#3295](https://github.com/VirtusLab/scala-cli/pull/3295)\n* Update Mill to 0.11.13 by [@Gedochao](https://github.com/Gedochao) in [#3296](https://github.com/VirtusLab/scala-cli/pull/3296)\n* Update coursier to 2.1.17 for Linux arm64 builds by [@Gedochao](https://github.com/Gedochao) in [#3298](https://github.com/VirtusLab/scala-cli/pull/3298)\n* Update coursier/dependency to 0.3.1 by [@Gedochao](https://github.com/Gedochao) in [#3297](https://github.com/VirtusLab/scala-cli/pull/3297)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.5.3...v1.5.4\n\n## [v1.5.3](https://github.com/VirtusLab/scala-cli/releases/tag/v1.5.3)\n\nThis is a hotfix release, which makes all the fixes and enhancements of Scala CLI 1.5.2 available through most standard distribution channels (rather than just Maven Central).\nFor the main release notes, please refer to the [v1.5.2 ones](#v152).\n\n### Distribution limitations\nDue to technical difficulties within our release pipeline, Scala CLI 1.5.3 is **not** available via the following channels:\n- `yum` (on RedHat/Cent OS/Fedora)\n- `SDKMAN!`\n\nWe have followed up with a 1.5.4 hotfix release to address this issue.\n\n### Hot-fixes \n- Tag failing native packager tests as flaky by [@Gedochao](https://github.com/Gedochao) in [#3270](https://github.com/VirtusLab/scala-cli/pull/3270)\n- Make publishing depend on all integration tests & docs tests by [@Gedochao](https://github.com/Gedochao) in [#3272](https://github.com/VirtusLab/scala-cli/pull/3272)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.5.2...v1.5.3\n\n## [v1.5.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.5.2)\n\n### Scala CLI 1.5.2 will only be available on JVM\nDue to technical difficulties with our release pipeline, Scala CLI 1.5.2 was only released as a JVM launcher on Maven Central. While it can be used as such, we followed it up with a hotfix 1.5.3 release, which should be available through all standard distribution channels.\n\n```bash\nscala-cli --cli-version 1.5.2 --version\n# Scala CLI version: 1.5.2\n# Scala version (default): 3.5.2\n```\n\n### `--source` is now deprecated and scheduled for removal in Scala CLI v1.6.x\nDue to how easy it is to confuse `--source` (the command line option for producing source JARs \nwith the `package` sub-command) and `-source` (the Scala compiler option, which can also be passed \nas `--source` in recent Scala 3 versions), using the former is now deprecated, and will likely be removed \nin Scala CLI v1.6.x.\n\n```bash ignore\nscala-cli --power package --source .                       \n# [warn] The --source option alias has been deprecated and may be removed in a future version.\n# (...)\n```\n\nDo note that the deprecation (and future removal) only affects the option alias.\nThe feature of packaging source JARs remains unchanged.\nIt is now recommended to switch to using the `--src` alias instead.\n\n```bash ignore\nscala-cli --power package --src .  \n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3257](https://github.com/VirtusLab/scala-cli/pull/3257).\n\n### Support for Scala 3.5.2\nThis Scala CLI version switches the default Scala version to 3.5.2.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.5.2\n# Scala version (default): 3.5.2\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3230](https://github.com/VirtusLab/scala-cli/pull/3230).\n\n### (experimental) Initial support for emitting Wasm with a command line option and a directive\nIt is now possible to emit Wasm via Scala.js with the `//> using jsEmitWasm` directive:\n```scala title=wasm.sc compile power\n//> using platform js\n//> using jsEmitWasm\n//> using jsModuleKind es\n//> using jsModuleSplitStyleStr fewestmodules\nprintln(\"Hello\")\n```\nOr with the `--js-emit-wasm` command line option:\n```bash ignore\nscala-cli --power package wasm.sc --js --js-emit-wasm\n# The `--js-emit-wasm` option is experimental\n# Please bear in mind that non-ideal user experience should be expected.\n# If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n# Compiling project (Scala 3.5.2, Scala.js 1.17.0)\n# Compiled project (Scala 3.5.2, Scala.js 1.17.0)\n# Wrote ~/wasm/wasm.js/main.js, run it with\n#   node ./wasm.js/main.js\ntree wasm.js\n# wasm.js\n# ├── __loader.js\n# ├── main.js\n# └── main.wasm\n# \n# 1 directory, 3 files\n```\n\nFor more information about Wasm (WebAssembly) support via Scala.js, refer [here](https://www.scala-js.org/doc/project/webassembly.html).\n\nAdded by [@Quafadas](https://github.com/Quafadas) in [#3255](https://github.com/VirtusLab/scala-cli/pull/3255).\n\n### Features\n* Add a `--js-emit-wasm` option and a corresponding `using` directive by [@Quafadas](https://github.com/Quafadas) in [#3255](https://github.com/VirtusLab/scala-cli/pull/3255)\n\n### Deprecations\n* Deprecate the `--source` command line option for the package sub-command by [@Gedochao](https://github.com/Gedochao) in [#3257](https://github.com/VirtusLab/scala-cli/pull/3257)\n\n### Fixes\n* Fix `--watch` to work correctly with changing `using` directives & sources requiring code generation (scripts, markdown, etc) by [@Gedochao](https://github.com/Gedochao) in [#3218](https://github.com/VirtusLab/scala-cli/pull/3218)\n* Ensure resource directories passed via a using directive aren't ignored in `--watch` mode by [@Gedochao](https://github.com/Gedochao) in [#3221](https://github.com/VirtusLab/scala-cli/pull/3221)\n* Ensure consecutive `-Wconf:*` flags are not ignored by [@Gedochao](https://github.com/Gedochao) in [#3245](https://github.com/VirtusLab/scala-cli/pull/3245)\n\n### Documentation changes\n* Mention the `Fix` command in the `Using directives` guide by [@dabrowski-adam](https://github.com/dabrowski-adam) in [#3239](https://github.com/VirtusLab/scala-cli/pull/3239)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3242](https://github.com/VirtusLab/scala-cli/pull/3242)\n\n### Updates\n* Update scala-cli.sh launcher for 1.5.1 by [@github-actions](https://github.com/github-actions) in [#3217](https://github.com/VirtusLab/scala-cli/pull/3217)\n* Update sttp to 3.10.0 by [@scala-steward](https://github.com/scala-steward) in [#3219](https://github.com/VirtusLab/scala-cli/pull/3219)\n* Update asm to 9.7.1 by [@scala-steward](https://github.com/scala-steward) in [#3223](https://github.com/VirtusLab/scala-cli/pull/3223)\n* Update bloop-rifle_2.13 to 2.0.3 by [@scala-steward](https://github.com/scala-steward) in [#3225](https://github.com/VirtusLab/scala-cli/pull/3225)\n* Update bloop-config_2.13 to 2.1.0 by [@scala-steward](https://github.com/scala-steward) in [#3228](https://github.com/VirtusLab/scala-cli/pull/3228)\n* chore: Update next to 3.5.2-RC2 by [@tgodzik](https://github.com/tgodzik) in [#3224](https://github.com/VirtusLab/scala-cli/pull/3224)\n* Update `coursier` to 2.1.14 by [@scala-steward](https://github.com/scala-steward) in [#3226](https://github.com/VirtusLab/scala-cli/pull/3226)\n* Update core_2.13 to 3.10.1 by [@scala-steward](https://github.com/scala-steward) in [#3229](https://github.com/VirtusLab/scala-cli/pull/3229)\n* Update `os-lib` to 0.11.2 by [@Gedochao](https://github.com/Gedochao) in [#3232](https://github.com/VirtusLab/scala-cli/pull/3232)\n* Update sbt, scripted-plugin to 1.10.3 by [@scala-steward](https://github.com/scala-steward) in [#3235](https://github.com/VirtusLab/scala-cli/pull/3235)\n* Update dependency to 0.2.4 by [@scala-steward](https://github.com/scala-steward) in [#3234](https://github.com/VirtusLab/scala-cli/pull/3234)\n* Bump Scala Next to 3.5.2 by [@Gedochao](https://github.com/Gedochao) in [#3230](https://github.com/VirtusLab/scala-cli/pull/3230)\n* Update os-lib to 0.11.3 by [@scala-steward](https://github.com/scala-steward) in [#3240](https://github.com/VirtusLab/scala-cli/pull/3240)\n* Set Scala 3.5.2 as the latest announced Scala Next version by [@Gedochao](https://github.com/Gedochao) in [#3243](https://github.com/VirtusLab/scala-cli/pull/3243)\n* Set Scala 3.6.1 as the Next RC version (which it effectively is) by [@Gedochao](https://github.com/Gedochao) in [#3244](https://github.com/VirtusLab/scala-cli/pull/3244)\n* Update dependencies in `gh-action.md` examples by [@kubukoz](https://github.com/kubukoz) in [#3249](https://github.com/VirtusLab/scala-cli/pull/3249)\n* Bump `scala-js-cli` to 1.17.0.1 by [@Gedochao](https://github.com/Gedochao) in [#3252](https://github.com/VirtusLab/scala-cli/pull/3252)\n\n## New Contributors\n* [@dabrowski-adam](https://github.com/dabrowski-adam) made their first contribution in [#3239](https://github.com/VirtusLab/scala-cli/pull/3239)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.5.1...v1.5.2\n\n## [v1.5.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.5.1)\n\n### Support for Scala 3.5.1, 3.3.4, 2.13.15 and 2.12.20\nThis Scala CLI version switches the default Scala version to 3.5.1.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.5.1\n# Scala version (default): 3.5.1\n```\n\nIt has also been tested with Scala 3.3.4, 2.13.15 and 2.12.20.\nThe Scala CLI internals are now built with Scala 3.3.4.\n\n### Features\n* Apply increased verbosity when compiling via BSP by [@Gedochao](https://github.com/Gedochao) in [#3202](https://github.com/VirtusLab/scala-cli/pull/3202)\n\n### Fixes\n* improvement: Use distinct on ScalacOpt by [@tgodzik](https://github.com/tgodzik) in [#3139](https://github.com/VirtusLab/scala-cli/pull/3139)\n* bugfix: Check if last segment of path exists by [@tgodzik](https://github.com/tgodzik) in [#3131](https://github.com/VirtusLab/scala-cli/pull/3131)\n* bugfix: Fix duplicate options detection by [@tgodzik](https://github.com/tgodzik) in [#3151](https://github.com/VirtusLab/scala-cli/pull/3151)\n* bugfix: Also deduplicate if options split by space by [@tgodzik](https://github.com/tgodzik) in [#3154](https://github.com/VirtusLab/scala-cli/pull/3154)\n* Fix `setup-ide` for `--cli-version` by [@Gedochao](https://github.com/Gedochao) in [#3161](https://github.com/VirtusLab/scala-cli/pull/3161)\n* Ensure main classes from inputs take precedence before those found in JARs added to the class path by [@Gedochao](https://github.com/Gedochao) in [#3165](https://github.com/VirtusLab/scala-cli/pull/3165)\n* Ensure that passing Java props into Scala CLI as launcher args would also pass it into BSP configuration by [@Gedochao](https://github.com/Gedochao) in [#3169](https://github.com/VirtusLab/scala-cli/pull/3169)\n* NIT fixes for the `export` sub-command by [@Gedochao](https://github.com/Gedochao) in [#3197](https://github.com/VirtusLab/scala-cli/pull/3197)\n* Ensure `--version` passed to the default command works with `--offline` by [@Gedochao](https://github.com/Gedochao) in [#3207](https://github.com/VirtusLab/scala-cli/pull/3207)\n\n### Documentation changes\n* Docs: Fix suppress option for directives-in-multiple-files warning by [@mims-github](https://github.com/mims-github) in [#3133](https://github.com/VirtusLab/scala-cli/pull/3133)\n* Doc: Tips on how to list available JVMs using coursier by [@jatcwang](https://github.com/jatcwang) in [#3129](https://github.com/VirtusLab/scala-cli/pull/3129)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#3160](https://github.com/VirtusLab/scala-cli/pull/3160)\n* Use Scala 3 in the Scala Native gif by [@Gedochao](https://github.com/Gedochao) in [#3195](https://github.com/VirtusLab/scala-cli/pull/3195)\n\n### Build and internal changes\n* Add tests for `setup-ide` with `--cli-version` by [@Gedochao](https://github.com/Gedochao) in [#3163](https://github.com/VirtusLab/scala-cli/pull/3163)\n* Change how help is referenced to avoid initialization oddness & update `case-app` to 2.1.0-M29 by [@coreyoconnor](https://github.com/coreyoconnor) in [#3152](https://github.com/VirtusLab/scala-cli/pull/3152)\n* Adjust tests for Scala 3.3.4 by [@Gedochao](https://github.com/Gedochao) in [#3164](https://github.com/VirtusLab/scala-cli/pull/3164)\n* NIT Refactor existing `--watch` tests by [@Gedochao](https://github.com/Gedochao) in [#3175](https://github.com/VirtusLab/scala-cli/pull/3175)\n* Generate an empty JUnit report when no tests were run, rather than fail by [@Gedochao](https://github.com/Gedochao) in [#3179](https://github.com/VirtusLab/scala-cli/pull/3179)\n* NIT Extract REPL tests relying on Ammonite into dedicated traits by [@Gedochao](https://github.com/Gedochao) in [#3209](https://github.com/VirtusLab/scala-cli/pull/3209)\n\n### Updates\n* Update scala-cli.sh launcher for 1.5.0 by [@github-actions](https://github.com/github-actions) in [#3125](https://github.com/VirtusLab/scala-cli/pull/3125)\n* Bump webpack from 5.89.0 to 5.94.0 in /website by [@dependabot](https://github.com/dependabot) in [#3136](https://github.com/VirtusLab/scala-cli/pull/3136)\n* Bump micromatch from 4.0.5 to 4.0.8 in /website by [@dependabot](https://github.com/dependabot) in [#3135](https://github.com/VirtusLab/scala-cli/pull/3135)\n* Update os-lib to 0.10.5 by [@scala-steward](https://github.com/scala-steward) in [#3140](https://github.com/VirtusLab/scala-cli/pull/3140)\n* Update Scala Next latest announced version to 3.5.0 by [@Gedochao](https://github.com/Gedochao) in [#3145](https://github.com/VirtusLab/scala-cli/pull/3145)\n* Update Scala 2.12 to 2.12.20 by [@Gedochao](https://github.com/Gedochao) in [#3144](https://github.com/VirtusLab/scala-cli/pull/3144)\n* Update Scala CLI as `scala` related docs  by [@Gedochao](https://github.com/Gedochao) in [#3155](https://github.com/VirtusLab/scala-cli/pull/3155)\n* Update os-lib to 0.10.6 by [@scala-steward](https://github.com/scala-steward) in [#3159](https://github.com/VirtusLab/scala-cli/pull/3159)\n* Update coursier to 2.1.11 by [@scala-steward](https://github.com/scala-steward) in [#3166](https://github.com/VirtusLab/scala-cli/pull/3166)\n* Update coursier to 2.1.12 by [@scala-steward](https://github.com/scala-steward) in [#3174](https://github.com/VirtusLab/scala-cli/pull/3174)\n* Update ammonite to 3.0.0-M2-30-486378af by [@scala-steward](https://github.com/scala-steward) in [#3172](https://github.com/VirtusLab/scala-cli/pull/3172)\n* Update sbt to 1.10.2 by [@scala-steward](https://github.com/scala-steward) in [#3180](https://github.com/VirtusLab/scala-cli/pull/3180)\n* Update munit to 1.0.2 by [@scala-steward](https://github.com/scala-steward) in [#3176](https://github.com/VirtusLab/scala-cli/pull/3176)\n* Bump `scala-cli-signing` to 0.2.4 by [@Gedochao](https://github.com/Gedochao) in [#3183](https://github.com/VirtusLab/scala-cli/pull/3183)\n* Bump `coursier` to 2.1.13 and `mill-native-image` to 0.1.26 by [@Gedochao](https://github.com/Gedochao) in [#3182](https://github.com/VirtusLab/scala-cli/pull/3182)\n* Update Scala Next to 3.5.1 by [@Gedochao](https://github.com/Gedochao) in [#3190](https://github.com/VirtusLab/scala-cli/pull/3190)\n* Update Scala 3 Next RC to 3.5.2-RC1 by [@scala-steward](https://github.com/scala-steward) in [#3187](https://github.com/VirtusLab/scala-cli/pull/3187)\n* Update Scala 2.13 to 2.13.15 by [@Gedochao](https://github.com/Gedochao) in [#3201](https://github.com/VirtusLab/scala-cli/pull/3201)\n* Update guava to 33.3.1-jre by [@scala-steward](https://github.com/scala-steward) in [#3203](https://github.com/VirtusLab/scala-cli/pull/3203)\n* chore: Update Bloop to 2.0.2 by [@tgodzik](https://github.com/tgodzik) in [#3192](https://github.com/VirtusLab/scala-cli/pull/3192)\n* Update Scala 3 LTS to 3.3.4 by [@Gedochao](https://github.com/Gedochao) in [#3208](https://github.com/VirtusLab/scala-cli/pull/3208)\n* Set Scala 3.5.1 as the latest announced version by [@Gedochao](https://github.com/Gedochao) in [#3206](https://github.com/VirtusLab/scala-cli/pull/3206)\n\n## New Contributors\n* [@mims-github](https://github.com/mims-github) made their first contribution in [#3133](https://github.com/VirtusLab/scala-cli/pull/3133)\n* [@jatcwang](https://github.com/jatcwang) made their first contribution in [#3129](https://github.com/VirtusLab/scala-cli/pull/3129)\n* [@coreyoconnor](https://github.com/coreyoconnor) made their first contribution in [#3152](https://github.com/VirtusLab/scala-cli/pull/3152)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.5.0...v1.5.1\n\n## [v1.5.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.5.0)\n\n### Support for Scala 3.5.0\nThis Scala CLI version switches the default Scala version to 3.5.0.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.5.0\n# Scala version (default): 3.5.0\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3093](https://github.com/VirtusLab/scala-cli/pull/3093).\n\n### Support for Scala Native 0.5.5\nThis Scala CLI version switches the default Scala Native version to 0.5.5.\n\n```bash\nscala-cli -e 'println(\"Hello from Scala Native 0.5.5!\")' --native\n# Compiling project (Scala 3.5.0, Scala Native 0.5.5)\n# Compiled project (Scala 3.5.0, Scala Native 0.5.5)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (894 ms)\n# [info] Discovered 888 classes and 5407 methods after classloading\n# [info] Checking intermediate code (quick) (31 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (299 ms)\n# [info] Discovered 499 classes and 2497 methods after classloading\n# [info] Checking intermediate code (quick) (5 ms)\n# [info] Discovered 478 classes and 1912 methods after optimization\n# [info] Optimizing (debug mode) (403 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (368 ms)\n# [info] Compiling to native code (1565 ms)\n# [info] Linking with [pthread, dl]\n# [info] Linking native code (immix gc, none lto) (83 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (3625 ms)\n# Hello from Scala Native 0.5.5!\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3117](https://github.com/VirtusLab/scala-cli/pull/3117).\n\n### (⚡️ experimental) Support for exporting to a Maven project\nIt is now possible to export a Scala CLI project to Maven.\n\n```bash\nscala-cli export --script-snippet 'println(\"No need to create the pom.xml yourself!\")' --mvn --power -o mvn-demo\n# Some utilized features are marked as experimental:\n#  - `export` sub-command\n#  - `--mvn` option\n# Please bear in mind that non-ideal user experience should be expected.\n# If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n# Exporting to a maven project...\n# Exported to: ~/scala-cli-tests/mvn-demo\ncd mvn-demo\nmvn scala:run -DmainClass=snippet_sc\n# (...)\n# No need to create the pom.xml yourself!\n# [INFO] ------------------------------------------------------------------------\n# [INFO] BUILD SUCCESS\n# [INFO] ------------------------------------------------------------------------\n# [INFO] Total time:  2.589 s\n# [INFO] Finished at: 2024-08-22T12:08:36+02:00\n# [INFO] ------------------------------------------------------------------------\n```\n\nAdded by [@yadavan88](https://github.com/yadavan88) in [#3003](https://github.com/VirtusLab/scala-cli/pull/3003).\n\n### Support for launching apps from dependencies without other inputs\nIt is now possible to launch an app by just specifying its dependency, without the need to provide any source files.\nIn such a case the build server will not be started, as there's no compilation to be done.\nThere's also no need to specify the main class, as it's now being detected automatically in dependencies as well.\nDo note that explicitly calling the `run` sub-command is necessary here, as otherwise Scala CLI will default to the REPL.\n\n```bash\nscala-cli run --dep io.get-coursier:coursier-cli_2.13:2.1.10 -- version\n# 2.1.10\n```\n\nThis can be used similarly to [Coursier's `cs launch`](https://get-coursier.io/docs/cli-launch).\n\nAdded by [@kasiaMarek](https://github.com/kasiaMarek) in [#3079](https://github.com/VirtusLab/scala-cli/pull/3079).\n\n### (⚡️ experimental) JMH available in various commands and via `using` directives\nSome improvements have been done to the experimental support for JMH (Java Microbenchmark Harness).\n\nThe `--jmh` and `--jmh-version` options can now be passed to a number of commands:\n- `run`, as it was before (note that when `--jmh` is passed to `run`, the project's main class will default to running the benchmarks rather than the project's default main method; this behaviour is likely to be changed in future versions);\n- `compile`, so that a Scala CLI project with benchmarking can be compiled separately from being run;\n- `package`, although the resulting artifacts will run the project as normal for now, rather than benchmarks;\n- `setup-ide`, so that benchmarking projects can be imported to your IDE of choice;\n- `test` and `export` will now also no longer fail with `--jmh`, although no specific implementations for benchmarking are in place there yet.\n\nIt is now also possible to control JMH with `using` directives:\n- `//> using jmh` allows to enable JMH for the project, being the equivalent of the `--jmh` option.\n- `//> using jmhVersion <version>` allows to set the JMH version to use, being the equivalent of the `--jmh-version` option.\n\n```scala compile power\n//> using jmh\n//> using jmhVersion 1.37\npackage bench\n\nimport org.openjdk.jmh.annotations.*\nimport java.util.concurrent.TimeUnit\n\n@BenchmarkMode(Array(Mode.AverageTime))\n@OutputTimeUnit(TimeUnit.NANOSECONDS)\n@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)\n@Measurement(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS)\n@Fork(0)\nclass Benchmarks {\n  @Benchmark\n  def foo(): Unit = {\n    (1L to 1000L).sum\n  }\n}\n```\n\nExpect more improvements in this area in the future.\nAlso, do play with it and give us feedback about the current implementation!\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3091](https://github.com/VirtusLab/scala-cli/pull/3091) and [#3118](https://github.com/VirtusLab/scala-cli/pull/3118).\n\n### Support for auto-completions in `fish`\nWe now have command line auto-completions for the `fish` shell.\n\nAdded by [@KristianLentino99](https://github.com/KristianLentino99) in [#3104](https://github.com/VirtusLab/scala-cli/pull/3104).\n\n### `--js-es-module-import-map` no longer requires `--power` mode\nA bit of a minor thing, but you can now use the `--js-es-module-import-map` option without enabling `--power` mode.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3086](https://github.com/VirtusLab/scala-cli/pull/3086).\n\n### Features\n* Add support for exporting to a Maven project by [@yadavan88](https://github.com/yadavan88) in [#3003](https://github.com/VirtusLab/scala-cli/pull/3003)\n* improvement: allow to run main class from deps with no inputs by [@kasiaMarek](https://github.com/kasiaMarek) in [#3079](https://github.com/VirtusLab/scala-cli/pull/3079)\n* Promote `--js-es-module-import-map` to stable by [@Gedochao](https://github.com/Gedochao) in [#3086](https://github.com/VirtusLab/scala-cli/pull/3086)\n* Tweak benchmarking with JMH by [@Gedochao](https://github.com/Gedochao) in [#3091](https://github.com/VirtusLab/scala-cli/pull/3091)\n* Add support for fish auto-completions by [@KristianLentino99](https://github.com/KristianLentino99) in [#3104](https://github.com/VirtusLab/scala-cli/pull/3104)\n* Add directives for JMH by [@Gedochao](https://github.com/Gedochao) in [#3118](https://github.com/VirtusLab/scala-cli/pull/3118)\n\n### Fixes\n* bugfix: Exclude sourcecode dependency by [@tgodzik](https://github.com/tgodzik) in [#3094](https://github.com/VirtusLab/scala-cli/pull/3094)\n* bugfix: Exclude both sourcecode and collection-compat correctly by [@tgodzik](https://github.com/tgodzik) in [#3105](https://github.com/VirtusLab/scala-cli/pull/3105)\n* Make package command handle directories in extra classpath by [@joan38](https://github.com/joan38) in [#3096](https://github.com/VirtusLab/scala-cli/pull/3096)\n* Add extra try-catch clause + extra logging in `LocalRepo` by [@Gedochao](https://github.com/Gedochao) in [#3114](https://github.com/VirtusLab/scala-cli/pull/3114)\n* Fix/changing options from sources should not require reload by [@MaciejG604](https://github.com/MaciejG604) in [#3112](https://github.com/VirtusLab/scala-cli/pull/3112)\n* fix: remove the --release flag by [@kasiaMarek](https://github.com/kasiaMarek) in [#3119](https://github.com/VirtusLab/scala-cli/pull/3119)\n* Remove adding test options to the project/build target name hash by [@MaciejG604](https://github.com/MaciejG604) in [#3107](https://github.com/VirtusLab/scala-cli/pull/3107)\n\n### Internal changes\n* Make the `publish` CI job depend on `jvm-tests-5` (Scala 3 Next RC test suite) by [@Gedochao](https://github.com/Gedochao) in [#3078](https://github.com/VirtusLab/scala-cli/pull/3078)\n* Include scanning the `.exe` launcher in the release procedure by [@Gedochao](https://github.com/Gedochao) in [#3081](https://github.com/VirtusLab/scala-cli/pull/3081)\n* refactor: Switch to original fork of Bloop by [@tgodzik](https://github.com/tgodzik) in [#3020](https://github.com/VirtusLab/scala-cli/pull/3020)\n* Extract used Java versions to constants by [@Gedochao](https://github.com/Gedochao) in [#3087](https://github.com/VirtusLab/scala-cli/pull/3087)\n* NIT Extract bsp testing utils to a helper trait by [@Gedochao](https://github.com/Gedochao) in [#3092](https://github.com/VirtusLab/scala-cli/pull/3092)\n* Fix/simplify code by [@MaciejG604](https://github.com/MaciejG604) in [#3106](https://github.com/VirtusLab/scala-cli/pull/3106)\n\n### Documentation changes\n* Add more env vars & generate reference docs for them by [@Gedochao](https://github.com/Gedochao) in [#3075](https://github.com/VirtusLab/scala-cli/pull/3075)\n\n### Updates\n* Update scala-cli.sh launcher for 1.4.3 by [@github-actions](https://github.com/github-actions) in [#3073](https://github.com/VirtusLab/scala-cli/pull/3073)\n* Update bloop-config_2.13 to 2.0.3 by [@scala-steward](https://github.com/scala-steward) in [#3072](https://github.com/VirtusLab/scala-cli/pull/3072)\n* Update Scala toolkit to 0.5.0 by [@Gedochao](https://github.com/Gedochao) in [#3076](https://github.com/VirtusLab/scala-cli/pull/3076)\n* Update Typelevel toolkit to 0.1.27 by [@Gedochao](https://github.com/Gedochao) in [#3077](https://github.com/VirtusLab/scala-cli/pull/3077)\n* Update Scala 3 Next RC to 3.5.0-RC7 by [@Gedochao](https://github.com/Gedochao) in [#3080](https://github.com/VirtusLab/scala-cli/pull/3080)\n* Update bloop-rifle_2.13 to 2.0.0 by [@scala-steward](https://github.com/scala-steward) in [#3108](https://github.com/VirtusLab/scala-cli/pull/3108)\n* Update munit to 1.0.1 by [@scala-steward](https://github.com/scala-steward) in [#3100](https://github.com/VirtusLab/scala-cli/pull/3100)\n* Update Scala 3 Next to 3.5.0 by [@Gedochao](https://github.com/Gedochao) in [#3093](https://github.com/VirtusLab/scala-cli/pull/3093)\n* Update sttp to 3.9.8 by [@scala-steward](https://github.com/scala-steward) in [#3098](https://github.com/VirtusLab/scala-cli/pull/3098)\n* Update guava to 33.3.0-jre by [@scala-steward](https://github.com/scala-steward) in [#3113](https://github.com/VirtusLab/scala-cli/pull/3113)\n* Update slf4j-nop to 2.0.16 by [@scala-steward](https://github.com/scala-steward) in [#3101](https://github.com/VirtusLab/scala-cli/pull/3101)\n* Update Scala 3 Next RC to 3.5.1-RC2 by [@scala-steward](https://github.com/scala-steward) in [#3099](https://github.com/VirtusLab/scala-cli/pull/3099)\n* Update Scala Native to 0.5.5 by [@Gedochao](https://github.com/Gedochao) in [#3117](https://github.com/VirtusLab/scala-cli/pull/3117)\n* Update os-lib to 0.10.4 by [@scala-steward](https://github.com/scala-steward) in [#3121](https://github.com/VirtusLab/scala-cli/pull/3121)\n* Update mill-main to 0.11.12 by [@scala-steward](https://github.com/scala-steward) in [#3120](https://github.com/VirtusLab/scala-cli/pull/3120)\n\n## New Contributors\n* @KristianLentino99 made their first contribution in [#3104](https://github.com/VirtusLab/scala-cli/pull/3104)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.4.3...v1.5.0\n\n## [v1.4.3](https://github.com/VirtusLab/scala-cli/releases/tag/v1.4.3)\n\nThis release is a hotfix for v1.4.2, which due to technical difficulties was not released to Maven Central \n(and, as an extension, wasn't available as a JAR). \n\nAll changes introduced by v1.4.2 are included in this release.\n\n### Internal changes\n* Ensure the `publish` step to be necessary for updating the native packages upon release by [@Gedochao](https://github.com/Gedochao) in [#3067](https://github.com/VirtusLab/scala-cli/pull/3067)\n\n### Updates\n* Update mill-main to 0.11.10 by [@scala-steward](https://github.com/scala-steward) in [#3060](https://github.com/VirtusLab/scala-cli/pull/3060)\n* Update mill-main to 0.11.11 by [@Gedochao](https://github.com/Gedochao) in [#3068](https://github.com/VirtusLab/scala-cli/pull/3068)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.4.2...v1.4.3\n\n## [v1.4.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.4.2)\n\n:::caution\nv1.4.2 encountered certain difficulties during its release, which made it unavailable on Maven Central.\nIt is recommended to upgrade directly to v1.4.3 or higher, rather than use it.\n:::\n\n### Environment variable help with `--env-help`\nYou can now list environment variables used internally with the `--envs-help` flag.\nThis does include some environment variable used by Scala CLI's dependencies (like Coursier, Bloop, etc.), but should not be treated as an exhaustive list.\n\n```bash\nscala-cli --env-help --power\n# The following is the list of environment variables used and recognized by Scala CLI.\n# It should by no means be treated as an exhaustive list.\n# Some tools and libraries Scala CLI integrates with may have their own, which may or may not be listed here.\n# \n# Scala CLI\n#   SCALA_CLI_CONFIG              Scala CLI configuration file path\n#   SCALA_CLI_HOME                Scala CLI home directory\n#   SCALA_CLI_INTERACTIVE         Interactive mode toggle\n#   SCALA_CLI_INTERACTIVE_INPUTS  Interactive mode inputs\n#   SCALA_CLI_POWER               Power mode toggle\n#   SCALA_CLI_PRINT_STACK_TRACES  Print stack traces toggle\n#   SCALA_CLI_SODIUM_JNI_ALLOW    Allow to load libsodiumjni\n#   SCALA_CLI_VENDORED_ZIS        Toggle io.github.scala_cli.zip.ZipInputStream\n# \n# Java\n#   JAVA_HOME                     Java installation directory\n#   JAVA_OPTS                     Java options\n#   JDK_JAVA_OPTIONS              JDK Java options\n# \n# Coursier\n#   COURSIER_CACHE                Coursier cache location\n#   COURSIER_MODE                 Coursier mode (can be set to 'offline')\n# \n# Spark\n#   SPARK_HOME                    (power) Spark installation directory\n# \n# Miscellaneous\n#   PATH                          The app path variable\n#   DYLD_LIBRARY_PATH             Runtime library paths on Mac OS X\n#   LD_LIBRARY_PATH               Runtime library paths on Linux\n#   PATHEXT                       Executable file extensions on Windows\n#   SHELL                         The currently used shell\n#   VCVARSALL                     Visual C++ Redistributable Runtimes\n#   ZDOTDIR                       Zsh configuration directory\n# \n# Internal\n#   CI                            (power) Marker for running on the CI\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3055.](https://github.com/VirtusLab/scala-cli/pull/3055.)\n\n### Features\n* Add environment variable help under `--envs-help` & refactor environment variable usage by [@Gedochao](https://github.com/Gedochao) in [#3055](https://github.com/VirtusLab/scala-cli/pull/3055)\n\n### Fixes\n* Fix default scaladoc config, so that id doesn't break all scaladoc links by [@KacperFKorban](https://github.com/KacperFKorban) in [#3041](https://github.com/VirtusLab/scala-cli/pull/3041)\n* Fix the REPL crashing when a dependency's classpath is called by a macro by [@Gedochao](https://github.com/Gedochao) in [#3043](https://github.com/VirtusLab/scala-cli/pull/3043)\n* Fix Mill export for projects with just the test scope by [@Gedochao](https://github.com/Gedochao) in [#3046](https://github.com/VirtusLab/scala-cli/pull/3046)\n* Ensure `--cli-default-scala-version` is respected by `--scalac-help` by [@Gedochao](https://github.com/Gedochao) in [#3048](https://github.com/VirtusLab/scala-cli/pull/3048)\n* Fix `generate-linux-arm64-native-launcher` by [@Gedochao](https://github.com/Gedochao) in [#3053](https://github.com/VirtusLab/scala-cli/pull/3053)\n\n### Internal changes\n* Prevent some flaky tests from failing on the CI by [@Gedochao](https://github.com/Gedochao) in [#3049](https://github.com/VirtusLab/scala-cli/pull/3049)\n* Switch to GitHub M1/`aarch64` runners on the CI by [@Gedochao](https://github.com/Gedochao) in [#3050](https://github.com/VirtusLab/scala-cli/pull/3050)\n* Fix Scala 2 nightly test failures by tagging them as flaky or skipping by [@Gedochao](https://github.com/Gedochao) in [#3064](https://github.com/VirtusLab/scala-cli/pull/3064)\n\n### Updates\n* Update scala-cli.sh launcher for 1.4.1 by [@github-actions](https://github.com/features/actions) in [#3039](https://github.com/VirtusLab/scala-cli/pull/3039)\n* Update ammonite to 3.0.0-M2-15-9bed9700 by [@scala-steward](https://github.com/scala-steward) in [#3059](https://github.com/VirtusLab/scala-cli/pull/3059)\n* Update metaconfig-typesafe-config to 0.13.0 by [@scala-steward](https://github.com/scala-steward) in [#3058](https://github.com/VirtusLab/scala-cli/pull/3058)\n* Update semanticdb-shared_2.13.14 to 4.9.9 by [@scala-steward](https://github.com/scala-steward) in [#3063](https://github.com/VirtusLab/scala-cli/pull/3063)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.8.3 by [@scala-steward](https://github.com/scala-steward) in [#3062](https://github.com/VirtusLab/scala-cli/pull/3062)\n* Update os-lib to 0.10.3 by [@scala-steward](https://github.com/scala-steward) in [#3061](https://github.com/VirtusLab/scala-cli/pull/3061)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.4.1...v1.4.2\n\n## [v1.4.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.4.1)\n\n### Pass compiler args as an `@argument` file\nYou can shorten or simplify a Scala CLI command by using an `@argument` file to specify a text file that contains compiler arguments. \n```text title=args.txt\n-d\noutputDirectory\n```\nThe feature may help to work around the [Windows command line character limit](https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/command-line-string-limitation), \namong other things, making sure your scripts run on any operating system of your choice.\n```bash\nscala-cli run -e 'println(\"Hey, I am using an @args file!\")' @args.txt\n```\nThe feature works similarly to the [command-line argument files feature of Java 9](https://docs.oracle.com/javase/9/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111) \nand fixes backwards compatibility with the old `scala` runner (pre-Scala-3.5.0).\n\nAdded by [@kasiaMarek](https://github.com/kasiaMarek) in [#3012](https://github.com/VirtusLab/scala-cli/pull/3012)\n\n### Explicitly enable or disable multithreading in Scala Native\nIt is now possible to explicitly enable or disable multithreading in Scala Native builds.\nYou can do it by setting the `//> using nativeMultithreading` directive:\n```scala title=native_multithreading.sc\n//> using platform native\n//> using nativeMultithreading\nimport scala.concurrent._\nimport scala.concurrent.duration._\nimport ExecutionContext.Implicits.global\nval promise = Promise[Int]()\nval thread = new Thread(new Runnable {\n    def run(): Unit = {\n      Thread.sleep(100)\n      promise.success(42)\n    }\n  })\nthread.start()\nval result = Await.result(promise.future, 2.seconds)\nprintln(result)\n```\nOr the `--native-multithreading` command line option:\n```bash\nscala-cli run native_multithreading.sc --native --native-multithreading\n```\nAdded by [@Gedochao](https://github.com/Gedochao) in [#3011.](https://github.com/VirtusLab/scala-cli/pull/3011.)\n\n### Features\n* Add a command line option & directive for enabling/disabling Scala Native multithreading by [@Gedochao](https://github.com/Gedochao) in [#3011](https://github.com/VirtusLab/scala-cli/pull/3011)\n* feat: allow to pass scalac options using files by [@kasiaMarek](https://github.com/kasiaMarek) in [#3012](https://github.com/VirtusLab/scala-cli/pull/3012)\n\n### Fixes\n* fix for 2954 running script in root dir by [@philwalk](https://github.com/philwalk) in [#2988](https://github.com/VirtusLab/scala-cli/pull/2988)\n* Pass `javaHome` to Bloop by [@kasiaMarek](https://github.com/kasiaMarek) in [#2985](https://github.com/VirtusLab/scala-cli/pull/2985)\n* bugfix: Print info diagnostics by [@tgodzik](https://github.com/tgodzik) in [#2990](https://github.com/VirtusLab/scala-cli/pull/2990)\n* Ensure BSP respects --power mode by [@Gedochao](https://github.com/Gedochao) in [#2997](https://github.com/VirtusLab/scala-cli/pull/2997)\n* Add Scala to pure Java test builds by [@Gedochao](https://github.com/Gedochao) in [#3009](https://github.com/VirtusLab/scala-cli/pull/3009)\n* Fix --offline mode for scala-cli as scala installation via coursier by [@Gedochao](https://github.com/Gedochao) in [#3029](https://github.com/VirtusLab/scala-cli/pull/3029)\n\n### Documentation changes\n* Fix typo in docs by [@ghostdogpr](https://github.com/ghostdogpr) in [#2996](https://github.com/VirtusLab/scala-cli/pull/2996)\n* docs: remove `.` from command snippet by [@spaceunifyfifty](https://github.com/spaceunifyfifty) in [#2998](https://github.com/VirtusLab/scala-cli/pull/2998)\n\n### Updates\n* Update scala-cli.sh launcher for 1.4.0 by [@github-actions](https://github.com/features/actions) in [#2992](https://github.com/VirtusLab/scala-cli/pull/2992)\n* Update winget-releaser to latest by [@vedantmgoyal9](https://github.com/vedantmgoyal9) in [#2991](https://github.com/VirtusLab/scala-cli/pull/2991)\n* Update ammonite to 3.0.0-M2-13-23a8ef64 by [@scala-steward](https://github.com/scala-steward) in [#2989](https://github.com/VirtusLab/scala-cli/pull/2989)\n* Update Scala 3 Next RC to 3.5.0-RC2 by [@scala-steward](https://github.com/scala-steward) in [#2981](https://github.com/VirtusLab/scala-cli/pull/2981)\n* chore: Bump outdated `javac-semanticdb` plugin to 0.10.0 by [@tgodzik](https://github.com/tgodzik) in [#3004](https://github.com/VirtusLab/scala-cli/pull/3004)\n* Update Scala 3 Next RC to 3.5.0-RC3 by [@scala-steward](https://github.com/scala-steward) in [#3002](https://github.com/VirtusLab/scala-cli/pull/3002)\n* Update sbt to 1.10.1 by [@scala-steward](https://github.com/scala-steward) in [#3015](https://github.com/VirtusLab/scala-cli/pull/3015)\n* Bump Scala 3 Next RC to 3.5.0-RC4 by [@Gedochao](https://github.com/Gedochao) in [#3018](https://github.com/VirtusLab/scala-cli/pull/3018)\n* Swap `scalameta` `trees` for `semanticdb-shared` & bump `scalameta` to 4.9.8 by [@Gedochao](https://github.com/Gedochao) in [#3017](https://github.com/VirtusLab/scala-cli/pull/3017)\n* Update Scala 3 Next RC to 3.5.1-RC1 by [@scala-steward](https://github.com/scala-steward) in [#3027](https://github.com/VirtusLab/scala-cli/pull/3027)\n\n## New Contributors\n* [@vedantmgoyal9](https://github.com/vedantmgoyal9) made their first contribution in [#2991](https://github.com/VirtusLab/scala-cli/pull/2991)\n* [@ghostdogpr](https://github.com/ghostdogpr) made their first contribution in [#2996](https://github.com/VirtusLab/scala-cli/pull/2996)\n* [@spaceunifyfifty](https://github.com/spaceunifyfifty) made their first contribution in [#2998](https://github.com/VirtusLab/scala-cli/pull/2998)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.4.0...v1.4.1\n\n## [v1.4.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.4.0)\n\n### Running the REPL with the test scope included\nIt is now possible to start the Scala REPL with access to the test scope. \nTo do so, it's enough to pass the `--test` flag with the `repl` sub-command.\n\n```scala title=ReplTestScopeExample.test.scala\npackage example\nobject ReplTestScopeExample {\n  def message: String = \"calling test scope from repl\"\n}\n```\n\n```bash ignore\nscala-cli repl ReplTestScopeExample.test.scala --test\n# Compiling project (test, Scala 3.4.2, JVM (17))\n# Compiled project (test, Scala 3.4.2, JVM (17))\n# Welcome to Scala 3.4.2 (17, Java OpenJDK 64-Bit Server VM).\n# Type in expressions for evaluation. Or try :help.\n#                                                                                                                                          \n# scala> example.ReplTestScopeExample.message\n# val res0: String = calling test scope from repl\n#                                                                                                                                          \n# scala> \n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2971](https://github.com/VirtusLab/scala-cli/pull/2971.)\n\n### The `using jvm` directives are now always respected\nFormerly, if the build server (Bloop) was running on an older JVM than the one specified in a `using jvm` directive, the directive wouldn't be respected. We now restart the build server based on both the directive and the respective command line option (`--jvm`).\n\n```java title=Simple.java\n//> using jvm 22\n//> using javacOpt --enable-preview -Xlint:preview\n//> using javaOpt --enable-preview\n//> using mainClass Simple\n\nvoid main() {\n    System.out.println(\"Hello from Java 22\");\n}\n```\n\nAdded by [@kasiaMarek](https://github.com/kasiaMarek) in [#2972](https://github.com/VirtusLab/scala-cli/pull/2972)\n\n### Support for Scala Native 0.5.4\nThis Scala CLI version adds support for Scala Native 0.5.4.\nNative platform builds will now use 0.5.4 as the default version.\n\n```bash\nscala-cli -e 'println(\"Hello, Scala Native!\")' --native\n# Compiling project (Scala 3.4.2, Scala Native 0.5.4)\n# Compiled project (Scala 3.4.2, Scala Native 0.5.4)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (902 ms)\n# [info] Discovered 882 classes and 5384 methods after classloading\n# [info] Checking intermediate code (quick) (37 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (292 ms)\n# [info] Discovered 499 classes and 2497 methods after classloading\n# [info] Checking intermediate code (quick) (10 ms)\n# [info] Discovered 478 classes and 1912 methods after optimization\n# [info] Optimizing (debug mode) (445 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (353 ms)\n# [info] Compiling to native code (1619 ms)\n# [info] Linking with [pthread, dl]\n# [info] Linking native code (immix gc, none lto) (137 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (3753 ms)\n# Hello, Scala Native!\n```\n\nAdded by [@scala-steward](https://github.com/scala-steward) in [#2982.](https://github.com/VirtusLab/scala-cli/pull/2982.)\n\n### Scala Toolkit 0.4.0 & 0.3.0 defaults\nThis Scala CLI version treats Scala Toolkit 0.4.0 as the default version under most circumstances.\n```scala\n//> using toolkit default\n@main def main() = println(os.pwd)\n```\nThis unlocks the Scala Toolkit to be used with Scala Native 0.5.x.\n\n```bash\nscala-cli -e 'println(os.pwd)' --toolkit default --native   \n# Compiling project (Scala 3.4.2, Scala Native 0.5.4)\n# Compiled project (Scala 3.4.2, Scala Native 0.5.4)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (1051 ms)\n# [info] Discovered 1047 classes and 6745 methods after classloading\n# [info] Checking intermediate code (quick) (46 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (543 ms)\n# [info] Discovered 880 classes and 5417 methods after classloading\n# [info] Checking intermediate code (quick) (15 ms)\n# [info] Discovered 857 classes and 4238 methods after optimization\n# [info] Optimizing (debug mode) (651 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (663 ms)\n# [info] Compiling to native code (1621 ms)\n# [info] Linking with [pthread, dl]\n# [info] Linking native code (immix gc, none lto) (81 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (4542 ms)\n```\n\nScala Native 0.4.x has been dropped in Scala Toolkit 0.4.0 and above, so the last version supporting it, 0.3.0 (and lower), will now make the build default to Scala Native 0.4.17.\n\n```bash\nscala-cli -e 'println(os.pwd)' --toolkit 0.3.0 --native                          \n# [warn] Scala Toolkit Version(0.3.0) does not support Scala Native 0.5.3, 0.4.17 should be used instead.\n# [warn] Scala Native default version 0.5.3 is not supported in this build. Using 0.4.17 instead.\n# Compiling project (Scala 3.4.2, Scala Native 0.4.17)\n# Compiled project (Scala 3.4.2, Scala Native 0.4.17)\n# [info] Linking (900 ms)\n# [info] Checking intermediate code (quick) (63 ms)\n# [info] Discovered 888 classes and 5298 methods\n# [info] Optimizing (debug mode) (836 ms)\n# [info] Generating intermediate code (620 ms)\n# [info] Produced 10 files\n# [info] Compiling to native code (1860 ms)\n# [info] Linking with [pthread, dl]\n# [info] Total (4406 ms)\n# ~/scala-cli-tests\n```\n:::caution\nThe troublesome case is when Scala Native 0.4.x is passed explicitly, while the Scala Toolkit is set to the default.\nScala CLI does not currently support downgrading the Scala Toolkit in this case, and fails the build.\n\n```bash fail\nscala-cli -e 'println(os.pwd)' --toolkit default --native --native-version 0.4.17\n# Downloading 4 dependencies and 2 internal dependencies\n# [error]  Error downloading org.scala-lang:toolkit-test_native0.4_3:0.4.0\n# [error]   not found: ~/.ivy2/local/org.scala-lang/toolkit-test_native0.4_3/0.4.0/ivys/ivy.xml\n# [error]   not found: https://repo1.maven.org/maven2/org/scala-lang/toolkit-test_native0.4_3/0.4.0/toolkit-test_native0.4_3-0.4.0.pom\n# [error]   not found: ~/Library/Caches/ScalaCli/local-repo/1.4.0/org.scala-lang/toolkit-test_native0.4_3/0.4.0/ivys/ivy.xml\n# [error]   No fallback URL found\n# [error] COMMAND_LINE\n# [error]  Error downloading org.scala-lang:toolkit_native0.4_3:0.4.0\n# [error]   not found: ~/.ivy2/local/org.scala-lang/toolkit_native0.4_3/0.4.0/ivys/ivy.xml\n# [error]   not found: https://repo1.maven.org/maven2/org/scala-lang/toolkit_native0.4_3/0.4.0/toolkit_native0.4_3-0.4.0.pom\n# [error]   not found: ~/Library/Caches/ScalaCli/local-repo/1.4.0/org.scala-lang/toolkit_native0.4_3/0.4.0/ivys/ivy.xml\n# [error]   No fallback URL found\n# [error] COMMAND_LINE\n```\n:::\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2955](https://github.com/VirtusLab/scala-cli/pull/2955)\n\n### Features\n* Include test scope in the REPL  when the `--test` flag is passed by [@Gedochao](https://github.com/Gedochao) in [#2971](https://github.com/VirtusLab/scala-cli/pull/2971)\n\n### Fixes\n* Fix BSP IllegalArgumentException when loading project in Metals by [@joan38](https://github.com/joan38) in [#2950](https://github.com/VirtusLab/scala-cli/pull/2950)\n* Don't check for newer CLI versions when the `--cli-version` launcher param is passed (v1.4.0 and onwards, only) by [@Gedochao](https://github.com/Gedochao) in [#2957](https://github.com/VirtusLab/scala-cli/pull/2957)\n* fix: start bloop with jvm version from using directives for JVMs > 17 by [@kasiaMarek](https://github.com/kasiaMarek) in [#2972](https://github.com/VirtusLab/scala-cli/pull/2972)\n\n### Documentation changes\n* Typo fixed in scripts.md by [@vaivanov95](https://github.com/vaivanov95) in [#2974](https://github.com/VirtusLab/scala-cli/pull/2974)\n\n### Internal changes\n* Tag flaky docker image with scala.js app test by [@Gedochao](https://github.com/Gedochao) in [#2977](https://github.com/VirtusLab/scala-cli/pull/2977)\n\n### Updates\n* Update scala-cli.sh launcher for 1.3.2 by [@github-actions](https://github.com/github-actions) in [#2938](https://github.com/VirtusLab/scala-cli/pull/2938)\n* Update Scala Native to 0.5.2 by [@scala-steward](https://github.com/scala-steward) in [#2946](https://github.com/VirtusLab/scala-cli/pull/2946)\n* Update guava to 33.2.1-jre by [@scala-steward](https://github.com/scala-steward) in [#2947](https://github.com/VirtusLab/scala-cli/pull/2947)\n* Update os-lib to 0.10.2 by [@scala-steward](https://github.com/scala-steward) in [#2949](https://github.com/VirtusLab/scala-cli/pull/2949)\n* Update ammonite to 3.0.0-M2-8-ba4429a2 by [@scala-steward](https://github.com/scala-steward) in [#2948](https://github.com/VirtusLab/scala-cli/pull/2948)\n* Update Scala Native to 0.5.3 by [@scala-steward](https://github.com/scala-steward) in [#2951](https://github.com/VirtusLab/scala-cli/pull/2951)\n* Update case-app to 2.1.0-M28 by [@scala-steward](https://github.com/scala-steward) in [#2956](https://github.com/VirtusLab/scala-cli/pull/2956)\n* Update Scala Toolkit to 0.4.0 & dynamically adjust Scala Native defaults by [@Gedochao](https://github.com/Gedochao) in [#2955](https://github.com/VirtusLab/scala-cli/pull/2955)\n* Update munit to 1.0.0 by [@scala-steward](https://github.com/scala-steward) in [#2935](https://github.com/VirtusLab/scala-cli/pull/2935)\n* Update ammonite to 3.0.0-M2-9-88291dd8 by [@scala-steward](https://github.com/scala-steward) in [#2960](https://github.com/VirtusLab/scala-cli/pull/2960)\n* Update `scalameta` to 4.9.6 by [@scala-steward](https://github.com/scala-steward) in [#2967](https://github.com/VirtusLab/scala-cli/pull/2967)\n* Update ammonite to 3.0.0-M2-10-f6e2c001 by [@scala-steward](https://github.com/scala-steward) in [#2965](https://github.com/VirtusLab/scala-cli/pull/2965)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.8.2 by [@scala-steward](https://github.com/scala-steward) in [#2966](https://github.com/VirtusLab/scala-cli/pull/2966)\n* Update scalameta to 4.9.7 by [@scala-steward](https://github.com/scala-steward) in [#2983](https://github.com/VirtusLab/scala-cli/pull/2983)\n* Pin `scala-cli-setup` to v1 and update CI scripts' dependencies by [@Gedochao](https://github.com/Gedochao) in [#2984](https://github.com/VirtusLab/scala-cli/pull/2984)\n* Update Scala Native to 0.5.4 by [@scala-steward](https://github.com/scala-steward) in [#2982](https://github.com/VirtusLab/scala-cli/pull/2982)\n* Update mill-main to 0.11.8 by [@scala-steward](https://github.com/scala-steward) in [#2980](https://github.com/VirtusLab/scala-cli/pull/2980)\n* Update bloop-config_2.13 to 2.0.2 by [@scala-steward](https://github.com/scala-steward) in [#2978](https://github.com/VirtusLab/scala-cli/pull/2978)\n* Update ammonite to 3.0.0-M2-12-951bbc1e by [@scala-steward](https://github.com/scala-steward) in [#2979](https://github.com/VirtusLab/scala-cli/pull/2979)\n\n## New Contributors\n* [@vaivanov95](https://github.com/vaivanov95) made their first contribution in [#2974](https://github.com/VirtusLab/scala-cli/pull/2974)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.3.2...v1.4.0\n\n## [v1.3.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.3.2)\n\n### Support for Scala 3.4.2\nThis Scala CLI version adds support for Scala 3.4.2.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.3.2\n# Scala version (default): 3.4.2\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2911](https://github.com/VirtusLab/scala-cli/pull/2911).\n\n### Incremental Scala.js linking\n\nScala CLI now can take advantage of Scala.js' powerful incremental linker, which makes linking very fast for multiple links in a row.\nFor Scala.js builds, the `scala-js-cli` process is now run with the newly added `--longRunning` mode.\nThe process is then reused if the inputs did not change.\n\nAdded by [@lolgab](https://github.com/lolgab) in [#2928](https://github.com/VirtusLab/scala-cli/pull/2928) and [VirtusLab/scala-js-cli#64](https://github.com/VirtusLab/scala-js-cli/pull/64).\n\n### Features\n* Support ARM64 architecture to the launcher script for Mac OS by [@carlosedp](https://github.com/carlosedp) in [#2895](https://github.com/VirtusLab/scala-cli/pull/2895)\n* Incremental Scala.js Linking by [@lolgab](https://github.com/lolgab) in [#2928](https://github.com/VirtusLab/scala-cli/pull/2928)\n\n### Fixes\n* Fix support of multiline shebang by [@sierikov](https://github.com/sierikov) in [#2908](https://github.com/VirtusLab/scala-cli/pull/2908)\n* Pass scala2-sbt-bridge to Bloop explicitly for Scala 2.13.12+ by [@Gedochao](https://github.com/Gedochao) in [#2927](https://github.com/VirtusLab/scala-cli/pull/2927)\n* Ensure `JAVA_HOME` of `setup-ide` is respected by `bsp` by [@Gedochao](https://github.com/Gedochao) in [#2920](https://github.com/VirtusLab/scala-cli/pull/2920)\n* Improve launcher options handling by [@Gedochao](https://github.com/Gedochao) in [#2931](https://github.com/VirtusLab/scala-cli/pull/2931)\n\n### Documentation changes\n* Add docs for `ignore` keyword in snippets in md by [@sierikov](https://github.com/sierikov) in [#2898](https://github.com/VirtusLab/scala-cli/pull/2898)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#2900](https://github.com/VirtusLab/scala-cli/pull/2900)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#2910](https://github.com/VirtusLab/scala-cli/pull/2910)\n* Add Scalafmt Cookbook by [@sierikov](https://github.com/sierikov) in [#2903](https://github.com/VirtusLab/scala-cli/pull/2903)\n* Back port of documentation changes to main by [@github-actions](https://github.com/github-actions) in [#2914](https://github.com/VirtusLab/scala-cli/pull/2914)\n* remove duplicated word by [@naferx](https://github.com/naferx) in [#2915](https://github.com/VirtusLab/scala-cli/pull/2915)\n* Remove unused imports by [@naferx](https://github.com/naferx) in [#2916](https://github.com/VirtusLab/scala-cli/pull/2916)\n* corrected instructions for downloading the launcher in Windows (fixes #2921) by [@philwalk](https://github.com/philwalk) in [#2922](https://github.com/VirtusLab/scala-cli/pull/2922)\n\n### Internal changes\n* Fix instant-startup-scala-scripts.md overeager `docs-tests` by [@Gedochao](https://github.com/Gedochao) in [#2909](https://github.com/VirtusLab/scala-cli/pull/2909)\n\n### Updates\n* Update scala-cli.sh launcher for 1.3.1 by [@github-actions](https://github.com/github-actions) in [#2894](https://github.com/VirtusLab/scala-cli/pull/2894)\n* Update ammonite to 3.0.0-M1-24-26133e66 by [@scala-steward](https://github.com/scala-steward) in [#2896](https://github.com/VirtusLab/scala-cli/pull/2896)\n* Update ammonite to 3.0.0-M2-1-3763a1d4 by [@scala-steward](https://github.com/scala-steward) in [#2905](https://github.com/VirtusLab/scala-cli/pull/2905)\n* Update scalameta to 4.9.4 by [@scala-steward](https://github.com/scala-steward) in [#2906](https://github.com/VirtusLab/scala-cli/pull/2906)\n* Update Scala Next to 3.4.2 by [@Gedochao](https://github.com/Gedochao) in [#2911](https://github.com/VirtusLab/scala-cli/pull/2911)\n* Update ammonite to 3.0.0-M2-2-741e5dbb by [@scala-steward](https://github.com/scala-steward) in [#2913](https://github.com/VirtusLab/scala-cli/pull/2913)\n* Update os-lib to 0.10.1 by [@scala-steward](https://github.com/scala-steward) in [#2918](https://github.com/VirtusLab/scala-cli/pull/2918)\n* Update `scalameta` to 4.9.5 by [@scala-steward](https://github.com/scala-steward) in [#2919](https://github.com/VirtusLab/scala-cli/pull/2919)\n* Update ammonite to 3.0.0-M2-3-b5eb4787 by [@scala-steward](https://github.com/scala-steward) in [#2917](https://github.com/VirtusLab/scala-cli/pull/2917)\n* Update Scala Next RC to 3.5.0-RC1 by [@Gedochao](https://github.com/Gedochao) in [#2912](https://github.com/VirtusLab/scala-cli/pull/2912)\n* Update bloop-rifle_2.13 to 1.5.17-sc-2 by [@scala-steward](https://github.com/scala-steward) in [#2925](https://github.com/VirtusLab/scala-cli/pull/2925)\n* Update sttp core_2.13 to 3.9.7 by [@scala-steward](https://github.com/scala-steward) in [#2924](https://github.com/VirtusLab/scala-cli/pull/2924)\n* Update ammonite to 3.0.0-M2-4-c3312916 by [@scala-steward](https://github.com/scala-steward) in [#2923](https://github.com/VirtusLab/scala-cli/pull/2923)\n* Bump `scala-js-cli` to v1.16.0.1 by [@Gedochao](https://github.com/Gedochao) in [#2929](https://github.com/VirtusLab/scala-cli/pull/2929)\n\n## New Contributors\n* [@sierikov](https://github.com/sierikov) made their first contribution in [#2898](https://github.com/VirtusLab/scala-cli/pull/2898)\n* [@naferx](https://github.com/naferx) made their first contribution in [#2915](https://github.com/VirtusLab/scala-cli/pull/2915)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.3.1...v1.3.2\n\n## [v1.3.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.3.1)\n\n### Scala 2.13.14 Support\nThis Scala CLI version adds support for Scala 2.13.14.\n\n```bash\nscala-cli -e 'println(scala.util.Properties.versionNumberString)' -S 2.13\n# Compiling project (Scala 2.13.14, JVM (17))\n# Compiled project (Scala 2.13.14, JVM (17))\n# 2.13.14\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2882](https://github.com/VirtusLab/scala-cli/pull/2882).\n\n### Fixes\n* Adjust TASTY bump warnings to respect overridden Scala version defaults by [@Gedochao](https://github.com/Gedochao) in [#2888](https://github.com/VirtusLab/scala-cli/pull/2888)\n* Include `scala3-staging` and `scala3-tasty-inspector` artifacts when the `--with-compiler` option is passed in Scala 3 by [@Gedochao](https://github.com/Gedochao) in [#2889](https://github.com/VirtusLab/scala-cli/pull/2889)\n\n### Internal changes\n* Allow to override prog name with a launcher arg by [@Gedochao](https://github.com/Gedochao) in [#2891](https://github.com/VirtusLab/scala-cli/pull/2891)\n\n### Updates\n* Update scala-cli.sh launcher for 1.3.0 by [@github-actions](https://github.com/github-actions) in [#2876](https://github.com/VirtusLab/scala-cli/pull/2876)\n* Update Scala 2 to 2.13.14 by [@Gedochao](https://github.com/Gedochao) in [#2882](https://github.com/VirtusLab/scala-cli/pull/2882)\n* Update guava to 33.2.0-jre by [@scala-steward](https://github.com/scala-steward) in [#2883](https://github.com/VirtusLab/scala-cli/pull/2883)\n* Update `com.softwaremill.sttp.client3:core` to 3.9.6 by [@scala-steward](https://github.com/scala-steward) in [#2885](https://github.com/VirtusLab/scala-cli/pull/2885)\n* Update sbt to 1.10.0 by [@scala-steward](https://github.com/scala-steward) in [#2887](https://github.com/VirtusLab/scala-cli/pull/2887)\n* Update ammonite to 3.0.0-M1-19-a7973e17 by [@scala-steward](https://github.com/scala-steward) in [#2884](https://github.com/VirtusLab/scala-cli/pull/2884)\n* Bump `coursier` to 2.1.10 by [@Gedochao](https://github.com/Gedochao) in [#2890](https://github.com/VirtusLab/scala-cli/pull/2890)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.3.0...v1.3.1\n\n## [v1.3.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.3.0)\n\n### Support for Scala Native 0.5.1\nThis Scala CLI version adds support for Scala Native 0.5.1.\nAll native platform builds will now use 0.5.1 as the default version.\n\n```bash\nscala-cli -e 'println(\"Hello, Scala Native!\")' --native\n# Compiling project (Scala 3.4.1, Scala Native 0.5.1)\n# Compiled project (Scala 3.4.1, Scala Native 0.5.1)\n# [info] Linking (multithreadingEnabled=true, disable if not used) (1059 ms)\n# [info] Discovered 882 classes and 5388 methods after classloading\n# [info] Checking intermediate code (quick) (39 ms)\n# [info] Multithreading was not explicitly enabled - initial class loading has not detected any usage of system threads. Multithreading support will be disabled to improve performance.\n# [info] Linking (multithreadingEnabled=false) (291 ms)\n# [info] Discovered 499 classes and 2501 methods after classloading\n# [info] Checking intermediate code (quick) (6 ms)\n# [info] Discovered 478 classes and 1916 methods after optimization\n# [info] Optimizing (debug mode) (432 ms)\n# [info] Produced 9 LLVM IR files\n# [info] Generating intermediate code (293 ms)\n# [info] Compiling to native code (1504 ms)\n# [info] Linking with [pthread, dl]\n# [info] Linking native code (immix gc, none lto) (351 ms)\n# [info] Postprocessing (0 ms)\n# [info] Total (4012 ms)\n# Hello, Scala Native!\n```\n\nNote that not all the tools Scala CLI integrates with support Scala Native 0.5.x just yet. \nWhen such an integration is being used, the default Scala Native version will get downgraded to 0.4.17.\n\n```bash\nscala-cli -e 'println(\"Hello, Scala Native!\")' --native --toolkit default\n# [warn] Scala Toolkit does not support Scala Native 0.5.1, 0.4.17 should be used instead.\n# [warn] Scala Native default version 0.5.1 is not supported in this build. Using 0.4.17 instead.\n# Compiling project (Scala 3.4.1, Scala Native 0.4.17)\n# Compiled project (Scala 3.4.1, Scala Native 0.4.17)\n# [info] Linking (1017 ms)\n# [info] Checking intermediate code (quick) (53 ms)\n# [info] Discovered 743 classes and 4242 methods\n# [info] Optimizing (debug mode) (654 ms)\n# [info] Generating intermediate code (898 ms)\n# [info] Produced 10 files\n# [info] Compiling to native code (2039 ms)\n# [info] Linking with [pthread, dl]\n# [info] Total (4812 ms)\n# Hello, Scala Native!\n```\n\nEfforts for supporting Scala Native 0.5.x are ongoing, we expect the downgrade to 0.4.17 in such cases to be a temporary solution.\nIf you know for a fact that 0.5.x support has been delivered for a tool, you can always pass the `--native-version` option explicitly, which will prevent the downgrade.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2862](https://github.com/VirtusLab/scala-cli/pull/2862)\n\n### Fixes\n* Add missing Scala 2 compiler print options by [@Gedochao](https://github.com/Gedochao) in [#2848](https://github.com/VirtusLab/scala-cli/pull/2848)\n* Don't recommend `latest` for toolkit version by [@keynmol](https://github.com/keynmol) in [#2852](https://github.com/VirtusLab/scala-cli/pull/2852)\n* Explicitly pass `scala3-sbt-bridge` to Bloop by [@Gedochao](https://github.com/Gedochao) in [#2873](https://github.com/VirtusLab/scala-cli/pull/2873)\n\n### Internal changes\n* Add launcher options allowing to override the default Scala version by [@Gedochao](https://github.com/Gedochao) in [#2860](https://github.com/VirtusLab/scala-cli/pull/2860)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.2.2 by [@github-actions](https://github.com/features/actions)  in [#2847](https://github.com/VirtusLab/scala-cli/pull/2847)\n* Update os-lib to 0.10.0 by [@scala-steward](https://github.com/scala-steward) in [#2854](https://github.com/VirtusLab/scala-cli/pull/2854)\n* Update scala-collection-compat to 2.12.0 by [@scala-steward](https://github.com/scala-steward) in [#2856](https://github.com/VirtusLab/scala-cli/pull/2856)\n* Update slf4j-nop to 2.0.13 by [@scala-steward](https://github.com/scala-steward) in [#2857](https://github.com/VirtusLab/scala-cli/pull/2857)\n* Update pprint to 0.9.0 by [@scala-steward](https://github.com/scala-steward) in [#2855](https://github.com/VirtusLab/scala-cli/pull/2855)\n* Update fansi to 0.5.0 by [@scala-steward](https://github.com/scala-steward) in [#2853](https://github.com/VirtusLab/scala-cli/pull/2853)\n* Update using_directives to 1.1.1 by [@scala-steward](https://github.com/scala-steward) in [#2863](https://github.com/VirtusLab/scala-cli/pull/2863)\n* Update Scala Native to 0.5.1 by [@scala-steward](https://github.com/scala-steward) and [@Gedochao](https://github.com/Gedochao) in [#2862](https://github.com/VirtusLab/scala-cli/pull/2862)\n* Update `bloop-core` to 1.5.17-sc-1 by [@scala-steward](https://github.com/scala-steward) in [#2873](https://github.com/VirtusLab/scala-cli/pull/2873)\n* Update `bloop-config` to 2.0.0 by [@scala-steward](https://github.com/scala-steward) in [#2873](https://github.com/VirtusLab/scala-cli/pull/2873)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.2.2...v1.3.0\n\n## [v1.2.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.2.2)\n\n### Fixed the `Fatal invariant violated` false-positive error coming from Bloop\nThis small update fixes the `Fatal invariant violated` error ([#2829](https://github.com/VirtusLab/scala-cli/issues/2829)). \nThe error was being thrown by Bloop when running Scala CLI repeatedly with the same sources.\n\nFixed by [@Gedochao](https://github.com/Gedochao) in [#2837](https://github.com/VirtusLab/scala-cli/pull/2837)\n\n### Enhancements\n* Log a warning when invalid java properties are being passed by env vars by [@Gedochao](https://github.com/Gedochao) in [#2843](https://github.com/VirtusLab/scala-cli/pull/2843)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.2.1 by [@github-actions](https://github.com/github-actions) in [#2828](https://github.com/VirtusLab/scala-cli/pull/2828)\n* Update `org.scalameta:trees_2.13` to 4.9.3 by [@scala-steward](https://github.com/scala-steward) in [#2831](https://github.com/VirtusLab/scala-cli/pull/2831)\n* Update ammonite to 3.0.0-M1-10-105f9e32 by [@scala-steward](https://github.com/scala-steward) in [#2844](https://github.com/VirtusLab/scala-cli/pull/2844)\n* Bump `bloop-core` to 1.5.16-sc-2 by [@Gedochao](https://github.com/Gedochao) in [#2837](https://github.com/VirtusLab/scala-cli/pull/2837)\n\n## What's Changed\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.2.1...v1.2.2\n\n## [v1.2.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.2.1)\n\n### Support for Scala 3.4.1\nThis Scala CLI version adds support for Scala 3.4.1.\n\n```bash\nscala-cli version\n# Scala CLI version: 1.2.1\n# Scala version (default): 3.4.1\n```\n\nAdditionally, from this version on Scala CLi is being tested against the latest Scala 3 Next RC.\nAnd so, feel free to try out Scala 3.4.2-RC1!\n\n```bash\nscala-cli run -S 3.4.2-RC1 --with-compiler -e 'println(dotty.tools.dotc.config.Properties.simpleVersionString)'\n# Compiling project (Scala 3.4.2-RC1, JVM (17))\n# Compiled project (Scala 3.4.2-RC1, JVM (17))\n# 3.4.2-RC1\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2824](https://github.com/VirtusLab/scala-cli/pull/2824) & [#2822](https://github.com/VirtusLab/scala-cli/pull/2822)\n\n### Support for Scala.js 1.16.0\n\nThis version adds Scala CLI support for Scala.js 1.16.0.\nAdded by [@scala-steward](https://github.com/scala-steward) in [#2807](https://github.com/VirtusLab/scala-cli/pull/2807) & [@Gedochao](https://github.com/Gedochao) in [scala-js-cli#55](https://github.com/VirtusLab/scala-js-cli/pull/55).\n\n### Fixes\n* Fix handling for `-Xlint:help` by [@Gedochao](https://github.com/Gedochao) in [#2781](https://github.com/VirtusLab/scala-cli/pull/2781)\n* Fix `--semanticdb-targetroot` & `--semanticdb-sourceroot` for scripts by [@Gedochao](https://github.com/Gedochao) in [#2784](https://github.com/VirtusLab/scala-cli/pull/2784)\n* Adjust actionable diagnostics for scripts by [@rochala](https://github.com/rochala) in [#2815](https://github.com/VirtusLab/scala-cli/pull/2815)\n* Fix publishing of runner & test-runner artifacts by [@Gedochao](https://github.com/Gedochao) in [#2819](https://github.com/VirtusLab/scala-cli/pull/2819)\n* bugfix: Fix Bloop import by [@tgodzik](https://github.com/tgodzik) in [#2825](https://github.com/VirtusLab/scala-cli/pull/2825)\n\n### Enhancements\n* Ensure external help options are mentioned in short help where available by [@Gedochao](https://github.com/Gedochao) in [#2808](https://github.com/VirtusLab/scala-cli/pull/2808)\n\n### Internal changes\n* Run integration tests for the latest Scala 3 Next RC by [@Gedochao](https://github.com/Gedochao) in [#2824](https://github.com/VirtusLab/scala-cli/pull/2824)\n\n### Documentation changes\n* Add installation guide for FreeBSD by [@spacebanana420](https://github.com/spacebanana420) in [#2793](https://github.com/VirtusLab/scala-cli/pull/2793)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2797](https://github.com/VirtusLab/scala-cli/pull/2797)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.2.0 by [@github-actions](https://github.com/features/actions) in [#2783](https://github.com/VirtusLab/scala-cli/pull/2783)\n* Update core_2.13 to 3.9.4 by [@scala-steward](https://github.com/scala-steward) in [#2787](https://github.com/VirtusLab/scala-cli/pull/2787)\n* Update ammonite to 3.0.0-M1-8-35694880 by [@scala-steward](https://github.com/scala-steward) in [#2786](https://github.com/VirtusLab/scala-cli/pull/2786)\n* Update trees_2.13 to 4.9.2 by [@scala-steward](https://github.com/scala-steward) in [#2795](https://github.com/VirtusLab/scala-cli/pull/2795)\n* Update guava to 33.1.0-jre by [@scala-steward](https://github.com/scala-steward) in [#2801](https://github.com/VirtusLab/scala-cli/pull/2801)\n* Bump follow-redirects from 1.15.4 to 1.15.6 in /website by [@dependabot](https://github.com/dependabot) in [#2803](https://github.com/VirtusLab/scala-cli/pull/2803)\n* Add -unchecked to the list of options that don't require -O by [@joan38](https://github.com/joan38) in [#2800](https://github.com/VirtusLab/scala-cli/pull/2800)\n* Update bloop-rifle_2.13 to 1.5.12-sc-1 by [@scala-steward](https://github.com/scala-steward) in [#2806](https://github.com/VirtusLab/scala-cli/pull/2806)\n* Update sttp.client core to 3.9.5 by [@scala-steward](https://github.com/scala-steward) in [#2810](https://github.com/VirtusLab/scala-cli/pull/2810)\n* Update asm to 9.7 by [@scala-steward](https://github.com/scala-steward) in [#2813](https://github.com/VirtusLab/scala-cli/pull/2813)\n* Update Scala.js to 1.16.0 by [@scala-steward](https://github.com/scala-steward) in [#2807](https://github.com/VirtusLab/scala-cli/pull/2807)\n* Bump express from 4.18.2 to 4.19.2 in /website by [@dependabot](https://github.com/dependabot) in [#2816](https://github.com/VirtusLab/scala-cli/pull/2816)\n* Update Bloop to 1.5.16-sc-1 by [@Gedochao](https://github.com/Gedochao) in [#2818](https://github.com/VirtusLab/scala-cli/pull/2818)\n* Bump Scala Next to 3.4.1 by [@Gedochao](https://github.com/Gedochao) in [#2822](https://github.com/VirtusLab/scala-cli/pull/2822)\n* Bump Typelevel Toolkit to 0.1.23 by [@Gedochao](https://github.com/Gedochao) in [#2823](https://github.com/VirtusLab/scala-cli/pull/2823)\n\n### New Contributors\n* [@joan38](https://github.com/joan38) made their first contribution in [#2800](https://github.com/VirtusLab/scala-cli/pull/2800)\n* [@rochala](https://github.com/rochala) made their first contribution in [#2815](https://github.com/VirtusLab/scala-cli/pull/2815)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.2.0...v1.2.1\n\n## [v1.2.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.2.0)\n\n### Scala 3.3.3, 3.4.0, 2.13.13 & 2.12.19 support\n\nThis version of Scala CLI adds support for a whooping 4 new Scala versions, it's been busy these past few days!\nThe default version used when using the CLI will from now on be the Scala 3 Next version (3.4.0 as of this release).\nUsing the `lts` tag will now point to Scala 3.3.3.\nThe LTS is also the version used for building the internals of Scala CLI (although we now also cross-compile with 3.4.0).\n```bash\nscala-cli version\n# Scala CLI version: 1.2.0\n# Scala version (default): 3.4.0\n```\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2772](https://github.com/VirtusLab/scala-cli/pull/2772), [#2736](https://github.com/VirtusLab/scala-cli/pull/2736), [#2755](https://github.com/VirtusLab/scala-cli/pull/2755), [#2753](https://github.com/VirtusLab/scala-cli/pull/2753) and [#2752](https://github.com/VirtusLab/scala-cli/pull/2752)\n\n### Remapping EsModule imports at link time with Scala.js\nGiven the following `importMap.json` file:\n```json title=importMap.json\n{\n  \"imports\": {\n    \"@stdlib/linspace\": \"https://cdn.skypack.dev/@stdlib/linspace\"\n  }\n}\n```\n\nIt is now possible to remap the imports at link time with the `jsEsModuleImportMap` directive.\n\n```scala title=RemappingEsModuleImports.scala\n//> using jsEsModuleImportMap importMap.json\n//> using jsModuleKind es\n//> using jsMode fastLinkJS\n//> using platform js\n\nimport scala.scalajs.js\nimport scala.scalajs.js.annotation.JSImport\nimport scala.scalajs.js.typedarray.Float64Array\n\nobject Foo {\n  def main(args: Array[String]): Unit = {\n    println(Array(-10.0, 10.0, 10).mkString(\", \"))\n    println(linspace(0, 10, 10).mkString(\", \"))\n  }\n}\n\n@js.native\n@JSImport(\"@stdlib/linspace\", JSImport.Default)\nobject linspace extends js.Object {\n  def apply(start: Double, stop: Double, num: Int): Float64Array = js.native\n}\n```\n\nThe same can be achieved with the `--js-es-module-import-map` command line option.\n```bash\nscala-cli --power package RemappingEsModuleImports.scala --js --js-module-kind ESModule -o main.js --js-es-module-import-map importMap.json\n```\n\nAdded by [@Quafadas](https://github.com/Quafadas) in [#2737](https://github.com/VirtusLab/scala-cli/pull/2737) and [scala-js-cli#47](https://github.com/VirtusLab/scala-js-cli/pull/47)\n\n### Fixes\n* Updated method for choosing a free drive letter (fixes #2743) by [@philwalk](https://github.com/philwalk) in [#2749](https://github.com/VirtusLab/scala-cli/pull/2749)\n* Make sure tasty-lib doesn't warn about Scala 3 Next by [@Gedochao](https://github.com/Gedochao) in [#2775](https://github.com/VirtusLab/scala-cli/pull/2775)\n\n### Enhancements\n* Add the ability to remap EsModule imports at link time by [@Quafadas](https://github.com/Quafadas) in [#2737](https://github.com/VirtusLab/scala-cli/pull/2737)\n\n### Internal changes\n* Fix overeager Scala version docs tests by [@Gedochao](https://github.com/Gedochao) in [#2750](https://github.com/VirtusLab/scala-cli/pull/2750)\n* Lock script wrapper tests on the internally used Scala 2.13 version by [@Gedochao](https://github.com/Gedochao) in [#2754](https://github.com/VirtusLab/scala-cli/pull/2754)\n* Use Scala LTS as the default version while cross compiling all Scala 3 modules on both LTS & Next by [@Gedochao](https://github.com/Gedochao) in [#2752](https://github.com/VirtusLab/scala-cli/pull/2752)\n* Explicitly set sonatype publishing to use the default cross Scala version by [@Gedochao](https://github.com/Gedochao) in [#2757](https://github.com/VirtusLab/scala-cli/pull/2757)\n* Fix publishing of artifacts to include non-cross-compiled modules by [@Gedochao](https://github.com/Gedochao) in [#2759](https://github.com/VirtusLab/scala-cli/pull/2759)\n* Run integration tests with both Scala 3 LTS & Next versions by [@Gedochao](https://github.com/Gedochao) in [#2760](https://github.com/VirtusLab/scala-cli/pull/2760)\n\n### Documentation changes\n* Fix typo by [@imRentable](https://github.com/imRentable) in [#2739](https://github.com/VirtusLab/scala-cli/pull/2739)\n* Add directive examples in Scala Native docs by [@spamegg1](https://github.com/spamegg1) in [#2774](https://github.com/VirtusLab/scala-cli/pull/2774)\n* toolkit latest is deprecated, mention default instead by [@spamegg1](https://github.com/spamegg1) in [#2776](https://github.com/VirtusLab/scala-cli/pull/2776)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.1.3 by [@github-actions](https://github.com/features/actions) in [#2734](https://github.com/VirtusLab/scala-cli/pull/2734)\n* Bump webfactory/ssh-agent from 0.8.0 to 0.9.0 by [@dependabot](https://github.com/dependabot) in [#2731](https://github.com/VirtusLab/scala-cli/pull/2731)\n* Update `coursier` to 2.1.9 by [@Gedochao](https://github.com/Gedochao) in [#2735](https://github.com/VirtusLab/scala-cli/pull/2735)\n* Bump `scala-js-cli` to 1.15.0.1 by [@Gedochao](https://github.com/Gedochao) in [#2738](https://github.com/VirtusLab/scala-cli/pull/2738)\n* Update Scala to 3.4.0 by [@Gedochao](https://github.com/Gedochao) in [#2736](https://github.com/VirtusLab/scala-cli/pull/2736)\n* Update slf4j-nop to 2.0.12 by [@scala-steward](https://github.com/scala-steward) in [#2748](https://github.com/VirtusLab/scala-cli/pull/2748)\n* Update trees_2.13 to 4.9.0 by [@scala-steward](https://github.com/scala-steward) in [#2747](https://github.com/VirtusLab/scala-cli/pull/2747)\n* Update mill-main to 0.11.7 by [@scala-steward](https://github.com/scala-steward) in [#2744](https://github.com/VirtusLab/scala-cli/pull/2744)\n* Update sttp client core_2.13 to 3.9.3 by [@scala-steward](https://github.com/scala-steward) in [#2745](https://github.com/VirtusLab/scala-cli/pull/2745)\n* Bump Scala 2.12 to 2.12.19 by [@Gedochao](https://github.com/Gedochao) in [#2753](https://github.com/VirtusLab/scala-cli/pull/2753)\n* Update sbt to 1.9.9 by [@scala-steward](https://github.com/scala-steward) in [#2756](https://github.com/VirtusLab/scala-cli/pull/2756)\n* Bump Scala 2.13 to 2.13.13 by [@Gedochao](https://github.com/Gedochao) in [#2755](https://github.com/VirtusLab/scala-cli/pull/2755)\n* Update scalameta to 4.9.1 by [@scala-steward](https://github.com/scala-steward) in [#2770](https://github.com/VirtusLab/scala-cli/pull/2770)\n* Bump Scala LTS to 3.3.3 by [@Gedochao](https://github.com/Gedochao) in [#2772](https://github.com/VirtusLab/scala-cli/pull/2772)\n* Update ammonite to 3.0.0-M0-71-1e75159e by [@scala-steward](https://github.com/scala-steward) in [#2773](https://github.com/VirtusLab/scala-cli/pull/2773)\n\n### New Contributors\n* [@imRentable](https://github.com/imRentable) made their first contribution in [#2739](https://github.com/VirtusLab/scala-cli/pull/2739)\n* [@spamegg1](https://github.com/spamegg1) made their first contribution in [#2774](https://github.com/VirtusLab/scala-cli/pull/2774)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.1.3...v1.2.0\n\n## [v1.1.3](https://github.com/VirtusLab/scala-cli/releases/tag/v1.1.3)\n\n### Support for LTS Scala version aliases\nIt is now possible to use `lts` and `3.lts` as Scala version aliases in Scala CLI. \nThey refer to the latest LTS version of Scala (the `3.3.x` line at the time of this release).\n\n```bash\nscala-cli run -S lts --with-compiler -e 'println(dotty.tools.dotc.config.Properties.simpleVersionString)'\n# Compiling project (Scala 3.3.1, JVM (17))\n# Compiled project (Scala 3.3.1, JVM (17))\n# 3.3.1\n```\n\nUsing the `2.lts`, `2.13.lts` & `2.12.lts` aliases returns a meaningful error, too.\n\n```bash fail\nscala-cli run -S 2.lts -e 'println(scala.util.Properties.versionString)'                                 \n# [error]  Invalid Scala version: 2.lts. There is no official LTS version for Scala 2.\n# You can only choose one of the 3.x, 2.13.x, and 2.12.x. versions.\n# The latest supported stable versions are 2.12.18, 2.13.12, 3.3.1.\n# In addition, you can request compilation with the last nightly versions of Scala,\n# by passing the 2.nightly, 2.12.nightly, 2.13.nightly, or 3.nightly arguments.\n# Specific Scala 2 or Scala 3 nightly versions are also accepted.\n# You can also request the latest Scala 3 LTS by passing lts or 3.lts.\n```\n\nAdded by [@kasiaMarek](https://github.com/kasiaMarek) in [#2710](https://github.com/VirtusLab/scala-cli/pull/2710)\n\n### `--semanticdb-targetroot` and `--semanticdb-sourceroot` options\nIt is now possible to set the SemanticDB target root and source root directories with unified syntax,\nindependent of the target Scala and/or Java versions.\n\nFor a given `semanticdb-example.sc` script:\n\n```scala title=src/semanticdb-example.sc\nprintln(\"SemanticDB targetroot gets set to ./targetRootDir, while sourceroot gets set to the current working directory.\")\n```\n\nYou now can specify the `targetroot` and `sourceroot` directories like this:\n\n```bash \nscala-cli compile src/semanticdb-example.sc --semanticdb-targetroot ./targetRootDir --semanticdb-sourceroot .\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2692](https://github.com/VirtusLab/scala-cli/pull/2692)\n\n### Fixes\n* remove `user.home` hack by [@kasiaMarek](https://github.com/kasiaMarek) in [#2710](https://github.com/VirtusLab/scala-cli/pull/2710)\n* Fix ultra-long invalid Scala version errors by [@Gedochao](https://github.com/Gedochao) in [#2724](https://github.com/VirtusLab/scala-cli/pull/2724)\n\n### Documentation changes\n* Add information about `--preamble` in assembly packaging documentation by [@spacebanana420](https://github.com/spacebanana420) in [#2713](https://github.com/VirtusLab/scala-cli/pull/2713)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2717](https://github.com/VirtusLab/scala-cli/pull/2717)\n* Documentation for creation of custom toolkit by [@yadavan88](https://github.com/yadavan88) in [#2715](https://github.com/VirtusLab/scala-cli/pull/2715)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2718](https://github.com/VirtusLab/scala-cli/pull/2718)\n* Fix formatting in custom toolkit doc by [@yadavan88](https://github.com/yadavan88) in [#2719](https://github.com/VirtusLab/scala-cli/pull/2719)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2720](https://github.com/VirtusLab/scala-cli/pull/2720)\n* Added info about repl with toolkit by [@yadavan88](https://github.com/yadavan88) in [#2721](https://github.com/VirtusLab/scala-cli/pull/2721)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2723](https://github.com/VirtusLab/scala-cli/pull/2723)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.1.2 by [@github-actions](https://github.com/features/actions) in [#2688](https://github.com/VirtusLab/scala-cli/pull/2688)\n* Update bsp4j to 2.1.1 by [@scala-steward](https://github.com/scala-steward) in [#2700](https://github.com/VirtusLab/scala-cli/pull/2700)\n* Update Scala Native to 0.4.17 by [@scala-steward](https://github.com/scala-steward) in [#2696](https://github.com/VirtusLab/scala-cli/pull/2696)\n* Bump coursier/setup-action from 1.3.4 to 1.3.5 by [@dependabot](https://github.com/dependabot) in [#2716](https://github.com/VirtusLab/scala-cli/pull/2716)\n\n### New Contributors\n* [@kasiaMarek](https://github.com/kasiaMarek) made their first contribution in [#2710](https://github.com/VirtusLab/scala-cli/pull/2710)\n* [@spacebanana420](https://github.com/spacebanana420) made their first contribution in [#2713](https://github.com/VirtusLab/scala-cli/pull/2713)\n* [@yadavan88](https://github.com/yadavan88) made their first contribution in [#2715](https://github.com/VirtusLab/scala-cli/pull/2715)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.1.2...v1.1.3\n\n## [v1.1.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.1.2)\n\n### Support for Scala.js 1.15.0\n\nThis version adds Scala CLI support for Scala.js 1.15.0.\nAdded by [@scala-steward](https://github.com/scala-steward) in [#2672](https://github.com/VirtusLab/scala-cli/pull/2672) & [@Gedochao](https://github.com/Gedochao) in [scala-js-cli#43](https://github.com/VirtusLab/scala-js-cli/pull/43).\n\n### Fixes\n* Fix repeatable compiler options handling from the command line by [@Gedochao](https://github.com/Gedochao) in [#2666](https://github.com/VirtusLab/scala-cli/pull/2666)\n* Fix script wrapper tests & script object wrapper `using` directive by [@Gedochao](https://github.com/Gedochao) in [#2668](https://github.com/VirtusLab/scala-cli/pull/2668)\n* Prevent consecutive `-language:*` options from being ignored by [@Gedochao](https://github.com/Gedochao) in [#2667](https://github.com/VirtusLab/scala-cli/pull/2667)\n\n### Documentation changes\n* Fix test.md by [@MaciejG604](https://github.com/MaciejG604) in [#2679](https://github.com/VirtusLab/scala-cli/pull/2679)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2681](https://github.com/VirtusLab/scala-cli/pull/2681)\n\n### Build and internal changes\n* Update release procedure steps for `v1.1.x` by [@Gedochao](https://github.com/Gedochao) in [#2665](https://github.com/VirtusLab/scala-cli/pull/2665)\n* Tag `GitHubTests.create secret` as flaky on all Mac tests (including M1) by [@Gedochao](https://github.com/Gedochao) in [#2677](https://github.com/VirtusLab/scala-cli/pull/2677)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.1.1 by [@github-actions](https://github.com/features/actions) in [#2662](https://github.com/VirtusLab/scala-cli/pull/2662)\n* Bump libsodiumjni to 0.0.4 by [@MaciejG604](https://github.com/MaciejG604) in [#2651](https://github.com/VirtusLab/scala-cli/pull/2651)\n* Update guava to 33.0.0-jre by [@scala-steward](https://github.com/scala-steward) in [#2670](https://github.com/VirtusLab/scala-cli/pull/2670)\n* Update os-lib to 0.9.3 by [@scala-steward](https://github.com/scala-steward) in [#2671](https://github.com/VirtusLab/scala-cli/pull/2671)\n* Update sbt to 1.9.8 by [@scala-steward](https://github.com/scala-steward) in [#2673](https://github.com/VirtusLab/scala-cli/pull/2673)\n* Update trees_2.13 to 4.8.15 by [@scala-steward](https://github.com/scala-steward) in [#2674](https://github.com/VirtusLab/scala-cli/pull/2674)\n* Update slf4j-nop to 2.0.11 by [@scala-steward](https://github.com/scala-steward) in [#2675](https://github.com/VirtusLab/scala-cli/pull/2675)\n* Update Scala.js to 1.15.0 by [@scala-steward](https://github.com/scala-steward) in [#2672](https://github.com/VirtusLab/scala-cli/pull/2672)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.1.1...v1.1.2\n\n## [v1.1.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.1.1)\n\n### Deprecate Scala Toolkit `latest` version in favour of `default`\nUsing toolkits with the `latest` version is now deprecated and will cause a warning. \nIt will likely be removed completely in a future release.\n```bash\nscala-cli --toolkit latest -e 'println(os.pwd)'\n# Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour:\n#  --toolkit default\n# Compiling project (Scala 3.3.1, JVM (17))\n# Compiled project (Scala 3.3.1, JVM (17))\n# /home\n```\n\nIt is now advised to either use an explicit toolkit version or rely on the new `default` alias.\n```bash\nscala-cli --toolkit default -e 'println(os.pwd)'\n# Compiling project (Scala 3.3.1, JVM (17))\n# Compiled project (Scala 3.3.1, JVM (17))\n# /home\n```\n\nThe `default` version for toolkits is tied to a particular Scala CLI version.\nYou can check which version is used by referring to Scala CLI help.\n```bash ignore\nscala-cli version                 \n# Scala CLI version: 1.1.1\n# Scala version (default): 3.3.1\nscala-cli run -h|grep toolkit         \n#   --toolkit, --with-toolkit version|default  Add toolkit to classPath (not supported in Scala 2.12), 'default' version for Scala toolkit: 0.2.1, 'default' version for typelevel toolkit: 0.1.20\n```\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2622](https://github.com/VirtusLab/scala-cli/pull/2622)\n\n### Enhancements\n* Remove semantics Compliant for asInstaceOf by [@MaciejG604](https://github.com/MaciejG604) in [#2614](https://github.com/VirtusLab/scala-cli/pull/2614)\n* Scala js mode validation by [@MaciejG604](https://github.com/MaciejG604) in [#2630](https://github.com/VirtusLab/scala-cli/pull/2630)\n* Add missing Scala.js mode aliases by [@Gedochao](https://github.com/Gedochao) in [#2655](https://github.com/VirtusLab/scala-cli/pull/2655)\n* Add deprecation reporting mechanism for using directives by [@MaciejG604](https://github.com/MaciejG604) in [#2622](https://github.com/VirtusLab/scala-cli/pull/2622)\n* Pass java opts to scalac by [@MaciejG604](https://github.com/MaciejG604) in [#2601](https://github.com/VirtusLab/scala-cli/pull/2601)\n\n### Fixes\n* Fallback to UTF-8 in setup-ide by [@JD557](https://github.com/JD557) in [#2599](https://github.com/VirtusLab/scala-cli/pull/2599)\n* Separate Scala REPL classpath from user dependencies by [@Gedochao](https://github.com/Gedochao) in [#2607](https://github.com/VirtusLab/scala-cli/pull/2607)\n* Prevent resource directories from breaking sources hash by [@Gedochao](https://github.com/Gedochao) in [#2654](https://github.com/VirtusLab/scala-cli/pull/2654)\n* Fix special handling for the `-Xplugin-list` compiler option by [@Gedochao](https://github.com/Gedochao) in [#2635](https://github.com/VirtusLab/scala-cli/pull/2635)\n* Remove superfluous traits by [@MaciejG604](https://github.com/MaciejG604) in [#2618](https://github.com/VirtusLab/scala-cli/pull/2618)\n* Prevent the toolkit latest deprecation warning from being logged more than once by [@Gedochao](https://github.com/Gedochao) in [#2657](https://github.com/VirtusLab/scala-cli/pull/2657)\n\n### Documentation changes\n* Unify mentions of Java properties and link to the correct section of guides. by [@MaciejG604](https://github.com/MaciejG604) in [#2603](https://github.com/VirtusLab/scala-cli/pull/2603)\n* Document script wrappers by [@MaciejG604](https://github.com/MaciejG604) in [#2596](https://github.com/VirtusLab/scala-cli/pull/2596)\n* Shorten titles of cookbooks by [@MaciejG604](https://github.com/MaciejG604) in [#2609](https://github.com/VirtusLab/scala-cli/pull/2609)\n* Add docs for bloop interaction by [@MaciejG604](https://github.com/MaciejG604) in [#2608](https://github.com/VirtusLab/scala-cli/pull/2608)\n* Docs/java opts for compiler by [@MaciejG604](https://github.com/MaciejG604) in [#2619](https://github.com/VirtusLab/scala-cli/pull/2619)\n* Add a subcategories layer for guides & cookbooks by [@Gedochao](https://github.com/Gedochao) in [#2612](https://github.com/VirtusLab/scala-cli/pull/2612)\n* Merge documentations about proxy setup by [@MaciejG604](https://github.com/MaciejG604) in [#2597](https://github.com/VirtusLab/scala-cli/pull/2597)\n* Update test framework versions by [@mbovel](https://github.com/mbovel) in [#2625](https://github.com/VirtusLab/scala-cli/pull/2625)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2604](https://github.com/VirtusLab/scala-cli/pull/2604)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2611](https://github.com/VirtusLab/scala-cli/pull/2611)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2615](https://github.com/VirtusLab/scala-cli/pull/2615)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2617](https://github.com/VirtusLab/scala-cli/pull/2617)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2620](https://github.com/VirtusLab/scala-cli/pull/2620)\n\n### Build and internal changes\n* Add debug mode by [@MaciejG604](https://github.com/MaciejG604) in [#2643](https://github.com/VirtusLab/scala-cli/pull/2643)\n* Downgrade Xcode on macos CI runners by [@MaciejG604](https://github.com/MaciejG604) in [#2632](https://github.com/VirtusLab/scala-cli/pull/2632)\n* Revert xcode version downgrade by [@MaciejG604](https://github.com/MaciejG604) in [#2650](https://github.com/VirtusLab/scala-cli/pull/2650)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.1.0 by [@github-actions](https://github.com/features/actions) in [#2594](https://github.com/VirtusLab/scala-cli/pull/2594)\n* Update org.eclipse.jgit to 6.8.0.202311291450-r by [@scala-steward](https://github.com/scala-steward) in [#2613](https://github.com/VirtusLab/scala-cli/pull/2613)\n* Bump docusaurus version by [@MaciejG604](https://github.com/MaciejG604) in [#2610](https://github.com/VirtusLab/scala-cli/pull/2610)\n* Bump actions/setup-python from 4 to 5 by [@dependabot](https://github.com/dependabot) in [#2624](https://github.com/VirtusLab/scala-cli/pull/2624)\n\n## New Contributors\n* [@mbovel](https://github.com/mbovel) made their first contribution in [#2625](https://github.com/VirtusLab/scala-cli/pull/2625)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.1.0...v1.1.1\n\n## [v1.1.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.1.0)\n\n### Breaking update to Scala 2 scripts\n\n**Keep in mind that it ONLY applies to Scala 2! Scala 3 script wrappers are not affected!**\n\nScala CLI now uses a different kind of script wrappers for Scala 2 by default, which support running background threads.\nThis has been introduces as an answer to the [issue #2470](https://github.com/VirtusLab/scala-cli/issues/2470), where a running a script in Scala 2 would end up in a deadlock due to background threads being run.\nAlso the change makes the Scala 2 scripts run significantly faster, as the code can be optimized due to not residing in the object's initialization clause.\n\nHowever, the new solution brings some incompatibilities with the old behaviour:\n- main classes are now named the same as the file they are defined in, they do not have the '_sc' suffix anymore, so any calls like:\n```bash ignore\nscala-cli foo.sc bar.sc --main-class foo_sc\n```\nshould be replaced with\n```bash ignore\nscala-cli foo.sc bar.sc --main-class foo\n```\n- it is impossible to access the contents of a script named `main.sc` from another source, any references to the script object `main` will result in a compilation error.\nE.g. Accessing the contents of `main.sc` using the following code:\n```scala\nprintln(main.somethingDefinedInMainScript)\n```\nWill result in the following compilation error:\n```bash ignore\n[error] ./foo.sc:2:11\n[error] missing argument list for method main in trait App\n[error] Unapplied methods are only converted to functions when a function type is expected.\n[error] You can make this conversion explicit by writing `main _` or `main(_)` instead of `main`.\n```\nWhen `main.sc` is passed as argument together with other scripts, a warning will be displayed:\n```bash ignore\n[warn]  Script file named 'main.sc' detected, keep in mind that accessing it from other scripts is impossible due to a clash of `main` symbols\n```\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2556](https://github.com/VirtusLab/scala-cli/pull/2556)\n\n### \"Drive relative\" paths on Windows\n\nScala CLI now correctly recognizes \"drive relative\" paths on Windows, so paths like `/foo/bar` will be treated as relative from the root of the current drive - e.g. `C:\\foo\\bar`.\nThis allows for compatibility of programs referencing paths with e.g. `//> using file /foo/bar` with Windows.\n\nAdded by [@philwalk](https://github.com/philwalk) in [#2516](https://github.com/VirtusLab/scala-cli/pull/2516)\n\n### UX improvements\n* React to some HTTP responses by [@MaciejG604](https://github.com/MaciejG604) in [#2007](https://github.com/VirtusLab/scala-cli/pull/2007)\n* Chore/group warnings about directives in multiple files by [@MaciejG604](https://github.com/MaciejG604) in [#2550](https://github.com/VirtusLab/scala-cli/pull/2550)\n* Migrate to Docusaurus v3, add local search plugin by [@MaciejG604](https://github.com/MaciejG604) in [#2590](https://github.com/VirtusLab/scala-cli/pull/2590)\n\n### Enhancements\n* Default to publish repository configured for local machine when inferring publish.ci.repository by [@MaciejG604](https://github.com/MaciejG604) in [#2571](https://github.com/VirtusLab/scala-cli/pull/2571)\n* Skip validation for default Scala versions, add build test by [@MaciejG604](https://github.com/MaciejG604) in [#2576](https://github.com/VirtusLab/scala-cli/pull/2576)\n\n### Fixes\n* Take into consideration --project-version when creating BuildInfo by [@MaciejG604](https://github.com/MaciejG604) in [#2548](https://github.com/VirtusLab/scala-cli/pull/2548)\n* Workaround for home.dir property not being set by [@MaciejG604](https://github.com/MaciejG604) in [#2573](https://github.com/VirtusLab/scala-cli/pull/2573)\n* Pass scalac arguments as file by [@MaciejG604](https://github.com/MaciejG604) in [#2584](https://github.com/VirtusLab/scala-cli/pull/2584)\n\n### Documentation changes\n* Add a doc on Windows anti-malware submission procedure by [@Gedochao](https://github.com/Gedochao) in [#2546](https://github.com/VirtusLab/scala-cli/pull/2546)\n* Fix list of licenses URL by [@JD557](https://github.com/JD557) in [#2552](https://github.com/VirtusLab/scala-cli/pull/2552)\n* Fix Windows secrets path in the documentation by [@JD557](https://github.com/JD557) in [#2561](https://github.com/VirtusLab/scala-cli/pull/2561)\n* Update the pgp-pair section of publish setup docs by [@MaciejG604](https://github.com/MaciejG604) in [#2565](https://github.com/VirtusLab/scala-cli/pull/2565)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2569](https://github.com/VirtusLab/scala-cli/pull/2569)\n* Document --python flag by [@MaciejG604](https://github.com/MaciejG604) in [#2574](https://github.com/VirtusLab/scala-cli/pull/2574)\n* Document publishing process configuration by [@MaciejG604](https://github.com/MaciejG604) in [#2580](https://github.com/VirtusLab/scala-cli/pull/2580)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2593](https://github.com/VirtusLab/scala-cli/pull/2593)\n\n### Build and internal changes\n* Exclude conflicting dependencies by [@MaciejG604](https://github.com/MaciejG604) in [#2541](https://github.com/VirtusLab/scala-cli/pull/2541)\n* Generate test reports on the CI by [@Gedochao](https://github.com/Gedochao) in [#2543](https://github.com/VirtusLab/scala-cli/pull/2543)\n* Use the latest `scala-cli` in `macos-m1-tests` by [@Gedochao](https://github.com/Gedochao) in [#2554](https://github.com/VirtusLab/scala-cli/pull/2554)\n* Install `scala-cli` with `cs` on M1 by [@Gedochao](https://github.com/Gedochao) in [#2555](https://github.com/VirtusLab/scala-cli/pull/2555)\n* Fix generating test reports for failed suites by [@Gedochao](https://github.com/Gedochao) in [#2564](https://github.com/VirtusLab/scala-cli/pull/2564)\n* Pin `scala-cli-setup` version to be M1-compatible & use it in `native-macos-m1-tests` by [@Gedochao](https://github.com/Gedochao) in [#2568](https://github.com/VirtusLab/scala-cli/pull/2568)\n* Add log separators for integration and build tests by [@MaciejG604](https://github.com/MaciejG604) in [#2570](https://github.com/VirtusLab/scala-cli/pull/2570)\n* Adjust test report generation to mill 0.11.6 bump changes by [@Gedochao](https://github.com/Gedochao) in [#2577](https://github.com/VirtusLab/scala-cli/pull/2577)\n* Bump MacOS CI to `macOS-13` by [@Gedochao](https://github.com/Gedochao) in [#2579](https://github.com/VirtusLab/scala-cli/pull/2579)\n* Add env for configuring home directory overriding by [@MaciejG604](https://github.com/MaciejG604) in [#2587](https://github.com/VirtusLab/scala-cli/pull/2587)\n\n### Updates and maintenance\n* Update trees_2.13 to 4.8.13 by [@scala-steward](https://github.com/scala-steward) in [#2532](https://github.com/VirtusLab/scala-cli/pull/2532)\n* Update scala-cli.sh launcher for 1.0.6 by [@github-actions](https://github.com/features/actions) in [#2542](https://github.com/VirtusLab/scala-cli/pull/2542)\n* chore: Update Bloop to v1.5.11-sc by [@tgodzik](https://github.com/tgodzik) in [#2557](https://github.com/VirtusLab/scala-cli/pull/2557)\n* Update trees_2.13 to 4.8.14 by [@scala-steward](https://github.com/scala-steward) in [#2560](https://github.com/VirtusLab/scala-cli/pull/2560)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.17 by [@scala-steward](https://github.com/scala-steward) in [#2559](https://github.com/VirtusLab/scala-cli/pull/2559)\n* Bump VirtusLab/scala-cli-setup from 1.0.5 to 1.0.6 by [@dependabot](https://github.com/dependabot) in [#2567](https://github.com/VirtusLab/scala-cli/pull/2567)\n* Update ammonite to 3.0.0-M0-59-cdeaa580 by [@scala-steward](https://github.com/scala-steward) in [#2558](https://github.com/VirtusLab/scala-cli/pull/2558)\n* Update mill-main to 0.11.6 by [@scala-steward](https://github.com/scala-steward) in [#2572](https://github.com/VirtusLab/scala-cli/pull/2572)\n* Update coursier-jvm_2.13, ... to 2.1.8 by [@scala-steward](https://github.com/scala-steward) in [#2575](https://github.com/VirtusLab/scala-cli/pull/2575)\n* Update ammonite to 3.0.0-M0-60-89836cd8 by [@scala-steward](https://github.com/scala-steward) in [#2586](https://github.com/VirtusLab/scala-cli/pull/2586)\n* Bump `coursier` to `v2.1.8` where it wasn't consistent by [@Gedochao](https://github.com/Gedochao) in [#2588](https://github.com/VirtusLab/scala-cli/pull/2588)\n\n## New Contributors\n* [@philwalk](https://github.com/philwalk) made their first contribution in [#2516](https://github.com/VirtusLab/scala-cli/pull/2516)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.6...v1.1.0\n\n## [v1.0.6](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.6)\n\n### Scala CLI won't default to the system JVM if it's not supported anymore\n\nIf your `JAVA_HOME` environment variable has been pointing to a JVM that is no longer supported by Scala CLI \n(so anything below 17, really), you may have run into an error like this one with Scala CLI v1.0.5:\n\n```bash ignore\nscala-cli --power bloop exit \n# Stopped Bloop server.  \nexport JAVA_HOME=$(cs java-home --jvm zulu:8)\nscala-cli -e 'println(System.getProperty(\"java.version\"))'                \n# Starting compilation server\n# Error: bloop.rifle.FailedToStartServerExitCodeException: Server failed with exit code 1\n# For more details, please see '/var/folders/5n/_ggj7kk93czdt_n0jzrk8s780000gn/T/1343202731019130640/.scala-build/stacktraces/1699527280-9858975811713766588.log'\n# Running\n#   scala-cli --power bloop output\n# might give more details.\n```\n\nThis is because we no longer support JVM \\<17 with Scala CLI v1.0.5, but we still have been defaulting to whatever JVM \nwas defined in `JAVA_HOME`. As a result, Bloop has been failing to start when running with, say, `JAVA_HOME` pointing \nto Java 8.\n\nThis is no longer the case. Scala CLI will now automatically download Java 17 for Bloop in such a situation \n(and still use the JVM from `JAVA_HOME` for running the code, while Bloop runs on 17).\n\n```bash ignore\nscala-cli --power bloop exit \n# Stopped Bloop server.  \nexport JAVA_HOME=$(cs java-home --jvm zulu:8)\nscala-cli -e 'println(System.getProperty(\"java.version\"))'                \n# Starting compilation server\n# Compiling project (Scala 3.3.1, JVM (8))\n# Compiled project (Scala 3.3.1, JVM (8))\n# 1.8.0_392\n```\n\nAdded by [@tgodzik](https://github.com/tgodzik) in [#2508](https://github.com/VirtusLab/scala-cli/pull/2508).\n\n## Other changes\n\n### Fixes\n* Fix `--watch` failing on invalid `PathWatchers.Event` & skip wonky tests on Mac CI by [@Gedochao](https://github.com/Gedochao) in [#2515](https://github.com/VirtusLab/scala-cli/pull/2515)\n* bugfix: Don't try to always get system jvm first by [@tgodzik](https://github.com/tgodzik) in [#2508](https://github.com/VirtusLab/scala-cli/pull/2508)\n\n### Documentation changes\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2522](https://github.com/VirtusLab/scala-cli/pull/2522)\n* add cookbook about Emacs integration by [@ag91](https://github.com/ag91) in [#2506](https://github.com/VirtusLab/scala-cli/pull/2506)\n\n### Build and internal changes\n* Bump actions/setup-node from 3 to 4 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2493](https://github.com/VirtusLab/scala-cli/pull/2493)\n* Update scala-cli.sh launcher for 1.0.5 by [@github-actions](https://github.com/features/actions) in [#2500](https://github.com/VirtusLab/scala-cli/pull/2500)\n* Simplify build by [@lolgab](https://github.com/lolgab) in [#2512](https://github.com/VirtusLab/scala-cli/pull/2512)\n* Fix wonky native MacOS CI on `stable` branch by [@Gedochao](https://github.com/Gedochao) in [#2518](https://github.com/VirtusLab/scala-cli/pull/2518)\n* Add regexes for release-notes github reference swapping by [@MaciejG604](https://github.com/MaciejG604) in [#2519](https://github.com/VirtusLab/scala-cli/pull/2519)\n\n### Updates and maintenance\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.15 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2498](https://github.com/VirtusLab/scala-cli/pull/2498)\n* Switch `lightweight-spark-distrib` to the VL fork & bump to `0.0.5` by [@Gedochao](https://github.com/Gedochao) in [#2503](https://github.com/VirtusLab/scala-cli/pull/2503)\n* Bump VirtusLab/scala-cli-setup from 1.0.4 to 1.0.5 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2504](https://github.com/VirtusLab/scala-cli/pull/2504)\n* Switch `java-class-name` to the VL fork & bump to `0.1.3` by [@Gedochao](https://github.com/Gedochao) in [#2502](https://github.com/VirtusLab/scala-cli/pull/2502)\n* Update sbt to 1.9.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2505](https://github.com/VirtusLab/scala-cli/pull/2505)\n* Update os-lib to 0.9.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2514](https://github.com/VirtusLab/scala-cli/pull/2514)\n* Update case-app to 2.1.0-M26 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2513](https://github.com/VirtusLab/scala-cli/pull/2513)\n* Update mill-main to 0.11.5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) & [@MaciejG604](https://github.com/MaciejG604) in [#2446](https://github.com/VirtusLab/scala-cli/pull/2446)\n* Update core_2.13 to 3.9.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2521](https://github.com/VirtusLab/scala-cli/pull/2521)\n* Switch `nocrc32-zip-input-stream` to the VL fork & bump it to `0.1.2` by [@Gedochao](https://github.com/Gedochao) in [#2520](https://github.com/VirtusLab/scala-cli/pull/2520)\n\n## New Contributors\n* [@ag91](https://github.com/ag91) made their first contribution in [#2506](https://github.com/VirtusLab/scala-cli/pull/2506)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.5...v1.0.6\n\n## [v1.0.5](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.5)\n\n## What's new\n\n### Accept `--power` from anywhere\n\nThe `--power` flag used to be a launcher option, which means it used to only be accepted when passed\nbefore the sub-command name. Now, it can be passed anywhere in the command line.\n\n```bash\nscala-cli --power package --help\nscala-cli package --power --help\nscala-cli package --help --power\n```\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2399](https://github.com/VirtusLab/scala-cli/pull/2399)\n\n### Offline mode (experimental)\n\nIt is now possible to run Scala CLI in offline mode for the cases when you don't want the runner \nto make any network requests for whatever reason.\nThis changes Coursier's cache policy to `LocalOnly`, preventing it from downloading anything.\n\n```bash ignore\nscala-cli compile . --offline --power\n```\n\nOf course, this means that you will have to have all the dependencies relevant to your build \nalready downloaded and available in your local cache.\nReasonable fallbacks will be used where possible, \ne.g. the Scala compiler may be used instead of Bloop if Bloop isn't available.\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2404](https://github.com/VirtusLab/scala-cli/pull/2404)\n\n### Shorter install script link\n\nScala CLI's install script is now available behind a conveniently shorter web address:\nhttps://scala-cli.virtuslab.org/get\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2450](https://github.com/VirtusLab/scala-cli/pull/2450)\n\n### The `fix` sub-command (experimental)\n\nThe `fix` sub-command is a new addition to Scala CLI. It allows to scan your project for `using` directives \nand extract them into the `project.scala` file placed in the project root directory. \nThis allows to easily fix warnings tied to having `using` directives present in multiple files.\n\n```bash ignore\nscala-cli fix . --power\n```\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2309](https://github.com/VirtusLab/scala-cli/pull/2309)\n\n### Build static & shared libraries with Scala Native (experimental)\n\nYou can now use the `--native-target` option to build Scala Native projects as static or shared libraries.\n\n```bash ignore\nscala-cli package . --power --native-target static\nscala-cli package . --power --native-target dynamic\n```\n\nAdded by [@keynmol](https://github.com/keynmol) in [#2196](https://github.com/VirtusLab/scala-cli/pull/2196)\n\n### Print platform version\n\nPlatform version is now always logged during compilation.\n\n```bash ignore\nscala-cli compile .\n# Compiling project (Scala 3.3.1, JVM (17))\n# Compiled project (Scala 3.3.1, JVM (17))\nscala-cli compile . --js\n# Compiling project (Scala 3.3.1, Scala.js 1.13.2)\n# Compiled project (Scala 3.3.1, Scala.js 1.13.2)\nscala-cli compile . --native\n# Compiling project (Scala 3.3.1, Scala Native 0.4.16)\n# Compiled project (Scala 3.3.1, Scala Native 0.4.16)\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2465](https://github.com/VirtusLab/scala-cli/pull/2465)\n\n## Other changes\n\n### Enhancements\n* Accumulate exp warnings with logger by [@MaciejG604](https://github.com/MaciejG604) in [#2376](https://github.com/VirtusLab/scala-cli/pull/2376)\n* Remove ComputeVersion.Command, make ComputeVersion classes positioned by [@MaciejG604](https://github.com/MaciejG604) in [#2350](https://github.com/VirtusLab/scala-cli/pull/2350)\n* Add more configuration for publish by [@MaciejG604](https://github.com/MaciejG604) in [#2435](https://github.com/VirtusLab/scala-cli/pull/2435)\n* Warn about transitive using file directive by [@MaciejG604](https://github.com/MaciejG604) in [#2432](https://github.com/VirtusLab/scala-cli/pull/2432)\n* Support Scala Native 0.5.x changes in publishing artifacts by [@WojciechMazur](https://github.com/WojciechMazur) in [#2460](https://github.com/VirtusLab/scala-cli/pull/2460)\n\n### Fixes\n* Fix - set es version into scala-js-cli by [@lwronski](https://github.com/lwronski) in [#2351](https://github.com/VirtusLab/scala-cli/pull/2351)\n* Modify the format of StrictDirective.toString by [@MaciejG604](https://github.com/MaciejG604) in [#2355](https://github.com/VirtusLab/scala-cli/pull/2355)\n* Make explicitly passed scala version use the latest release, not the default one by [@MaciejG604](https://github.com/MaciejG604) in [#2411](https://github.com/VirtusLab/scala-cli/pull/2411)\n* Release flag by [@lwronski](https://github.com/lwronski) in [#2413](https://github.com/VirtusLab/scala-cli/pull/2413)\n* Ensure build resolution is kept when packaging assemblies with provided dependencies by [@Gedochao](https://github.com/Gedochao) in [#2457](https://github.com/VirtusLab/scala-cli/pull/2457)\n* Fix `fmt` sub-command exit code to mirror `scalafmt` by [@Gedochao](https://github.com/Gedochao) in [#2463](https://github.com/VirtusLab/scala-cli/pull/2463)\n* Fix 'JVM too old' as bsp by [@MaciejG604](https://github.com/MaciejG604) in [#2445](https://github.com/VirtusLab/scala-cli/pull/2445)\n* Read java props from env vars by [@MaciejG604](https://github.com/MaciejG604) in [#2356](https://github.com/VirtusLab/scala-cli/pull/2356)\n* Make script wrapper satisfy compiler checks by [@MaciejG604](https://github.com/MaciejG604) in [#2414](https://github.com/VirtusLab/scala-cli/pull/2414)\n* Load local ivy path from ivy.home and user.home system properties by [@JD557](https://github.com/JD557) in [#2484](https://github.com/VirtusLab/scala-cli/pull/2484)\n\n### Documentation changes\n* Fix typo in buildInfo directive docs by [@izzyreal](https://github.com/izzyreal) in [#2357](https://github.com/VirtusLab/scala-cli/pull/2357)\n* configuration.md examples \"using dep\" to current versions by [@SunKing2](https://github.com/SunKing2) in [#2398](https://github.com/VirtusLab/scala-cli/pull/2398)\n* Documentation updates by [@MaciejG604](https://github.com/MaciejG604) in [#2375](https://github.com/VirtusLab/scala-cli/pull/2375)\n* Fix publish directives usage displayed in one line, unify directive docs by [@MaciejG604](https://github.com/MaciejG604) in [#2381](https://github.com/VirtusLab/scala-cli/pull/2381)\n* Backport of docs change (#2391) by [@MaciejG604](https://github.com/MaciejG604) in [#2403](https://github.com/VirtusLab/scala-cli/pull/2403)\n* Add internal docs for scalajs-cli by [@lwronski](https://github.com/lwronski) in [#2434](https://github.com/VirtusLab/scala-cli/pull/2434)\n* Add docs for fix command by [@MaciejG604](https://github.com/MaciejG604) in [#2437](https://github.com/VirtusLab/scala-cli/pull/2437)\n* Add docs for offline mode by [@MaciejG604](https://github.com/MaciejG604) in [#2475](https://github.com/VirtusLab/scala-cli/pull/2475)\n* Update dependencies.md to mention jitpack by [@doofin](https://github.com/doofin) in [#2458](https://github.com/VirtusLab/scala-cli/pull/2458)\n* Update the list of external repositories Scala CLI depends on by [@Gedochao](https://github.com/Gedochao) in [#2476](https://github.com/VirtusLab/scala-cli/pull/2476)\n* Update the docs to no longer treat --power as a launcher-only option by [@Gedochao](https://github.com/Gedochao) in [#2478](https://github.com/VirtusLab/scala-cli/pull/2478)\n\n### Build and internal changes\n* Add test for actionable diagnostics from compiler by [@MaciejG604](https://github.com/MaciejG604) in [#2327](https://github.com/VirtusLab/scala-cli/pull/2327)\n* Pin the versions of Github CI runners by [@MaciejG604](https://github.com/MaciejG604) in [#2370](https://github.com/VirtusLab/scala-cli/pull/2370)\n* Remove bloop timeouts in tests by [@MaciejG604](https://github.com/MaciejG604) in [#2407](https://github.com/VirtusLab/scala-cli/pull/2407)\n* Add post-update hook for reference doc generation by [@MaciejG604](https://github.com/MaciejG604) in [#2406](https://github.com/VirtusLab/scala-cli/pull/2406)\n* Add tests which check availability of scalafmt native launcher for de… by [@lwronski](https://github.com/lwronski) in [#2418](https://github.com/VirtusLab/scala-cli/pull/2418)\n* Default to a Scala version for REPL if there are no Scala artifacts. by [@trilleplay](https://github.com/trilleplay) in [#2431](https://github.com/VirtusLab/scala-cli/pull/2431)\n* Remove unused snippet checker by [@lwronski](https://github.com/lwronski) in [#2423](https://github.com/VirtusLab/scala-cli/pull/2423)\n* Allow to override internal & user default Scala versions for `mill` builds by [@Gedochao](https://github.com/Gedochao) in [#2461](https://github.com/VirtusLab/scala-cli/pull/2461)\n* NIT: Refactor: Rely on global --power option where able in cli commands by [@Gedochao](https://github.com/Gedochao) in [#2480](https://github.com/VirtusLab/scala-cli/pull/2480)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.0.4 by [@github-actions](https://github.com/features/actions) in [#2344](https://github.com/VirtusLab/scala-cli/pull/2344)\n* Update bloop-rifle_2.13 to 1.5.9-sc-2 by [@lwronski](https://github.com/lwronski) in [#2345](https://github.com/VirtusLab/scala-cli/pull/2345)\n* Update core_2.13 to 3.9.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2346](https://github.com/VirtusLab/scala-cli/pull/2346)\n* Update sbt to 1.9.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2349](https://github.com/VirtusLab/scala-cli/pull/2349)\n* Bump VirtusLab/scala-cli-setup from 1.0.2 to 1.0.4 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2348](https://github.com/VirtusLab/scala-cli/pull/2348)\n* Update coursier-jvm_2.13, ... to 2.1.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2360](https://github.com/VirtusLab/scala-cli/pull/2360)\n* Update trees_2.13 to 4.8.9 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2369](https://github.com/VirtusLab/scala-cli/pull/2369)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.13 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2368](https://github.com/VirtusLab/scala-cli/pull/2368)\n* Update bloop-rifle_2.13 to 1.5.11-sc-1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2383](https://github.com/VirtusLab/scala-cli/pull/2383)\n* Update org.eclipse.jgit to 6.6.1.202309021850-r by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2384](https://github.com/VirtusLab/scala-cli/pull/2384)\n* Update trees_2.13 to 4.8.10 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2387](https://github.com/VirtusLab/scala-cli/pull/2387)\n* Update coursier-jvm_2.13, ... to 2.1.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2393](https://github.com/VirtusLab/scala-cli/pull/2393)\n* Bump docker/login-action from 2 to 3 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2400](https://github.com/VirtusLab/scala-cli/pull/2400)\n* Update org.eclipse.jgit to 6.7.0.202309050840-r by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2395](https://github.com/VirtusLab/scala-cli/pull/2395)\n* Update scala3-library to 3.3.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2392](https://github.com/VirtusLab/scala-cli/pull/2392)\n* Update slf4j-nop to 2.0.9 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2388](https://github.com/VirtusLab/scala-cli/pull/2388)\n* Update file-tree-views to 2.1.11 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2410](https://github.com/VirtusLab/scala-cli/pull/2410)\n* Update test-runner, tools to 0.4.15 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2385](https://github.com/VirtusLab/scala-cli/pull/2385)\n* Update scala-library to 2.13.12 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2396](https://github.com/VirtusLab/scala-cli/pull/2396)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.14 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2386](https://github.com/VirtusLab/scala-cli/pull/2386)\n* Update file-tree-views to 2.1.12 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2419](https://github.com/VirtusLab/scala-cli/pull/2419)\n* Update bsp4j to 2.1.0-M6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2401](https://github.com/VirtusLab/scala-cli/pull/2401)\n* Update trees_2.13 to 4.8.11 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2429](https://github.com/VirtusLab/scala-cli/pull/2429)\n* Update asm to 9.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2442](https://github.com/VirtusLab/scala-cli/pull/2442)\n* Update bsp4j to 2.1.0-M7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2438](https://github.com/VirtusLab/scala-cli/pull/2438)\n* Update metaconfig-typesafe-config to 0.12.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2439](https://github.com/VirtusLab/scala-cli/pull/2439)\n* Update ammonite to 3.0.0-M0-56-1bcbe7f6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2440](https://github.com/VirtusLab/scala-cli/pull/2440)\n* Bump Scala Native to 0.4.16 & log platform version by [@Gedochao](https://github.com/Gedochao) in [#2465](https://github.com/VirtusLab/scala-cli/pull/2465)\n* Update guava to 32.1.3-jre by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2467](https://github.com/VirtusLab/scala-cli/pull/2467)\n* Update trees_2.13 to 4.8.12 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2468](https://github.com/VirtusLab/scala-cli/pull/2468)\n* Bump actions/checkout from 3 to 4 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2378](https://github.com/VirtusLab/scala-cli/pull/2378)\n* Bump coursier/setup-action from 1.3.3 to 1.3.4 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2424](https://github.com/VirtusLab/scala-cli/pull/2424)\n* Bump coursier-publish from 0.1.4 to 0.1.5 by [@MaciejG604](https://github.com/MaciejG604) in [#2433](https://github.com/VirtusLab/scala-cli/pull/2433)\n* Bump scalajs-cli to 1.14.0 by [@MaciejG604](https://github.com/MaciejG604) in [#2491](https://github.com/VirtusLab/scala-cli/pull/2491)\n* Bump scala-cli-signing to 0.2.3 by [@Gedochao](https://github.com/Gedochao) in [#2486](https://github.com/VirtusLab/scala-cli/pull/2486)\n* Bump gcbenchmark dependencies by [@Gedochao](https://github.com/Gedochao) in [#2481](https://github.com/VirtusLab/scala-cli/pull/2481)\n\n## New Contributors\n* [@SunKing2](https://github.com/SunKing2) made their first contribution in [#2398](https://github.com/VirtusLab/scala-cli/pull/2398)\n* [@trilleplay](https://github.com/trilleplay) made their first contribution in [#2431](https://github.com/VirtusLab/scala-cli/pull/2431)\n* [@WojciechMazur](https://github.com/WojciechMazur) made their first contribution in [#2460](https://github.com/VirtusLab/scala-cli/pull/2460)\n* [@JD557](https://github.com/JD557) made their first contribution in [#2484](https://github.com/VirtusLab/scala-cli/pull/2484)\n* [@doofin](https://github.com/doofin) made their first contribution in [#2458](https://github.com/VirtusLab/scala-cli/pull/2458)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.4...v1.0.5\n\n## [v1.0.4](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.4)\n\n### Hotfix for buildTarget/jvmRunEnvironment in BSP\n\nWe've addressed a bug that surfaced when opening your ScalaCLI projects in Metals or IntelliJ. If you encountered the following log:\n\n```\n2023.08.09 15:48:34 INFO  BSP server: Caused by: java.lang.IllegalArgumentException: Type ch.epfl.scala.bsp4j.JvmMainClass is instantiated reflectively but was never registered. Register the type by adding \"unsafeAllocated\" for the type in reflect-config.json.\n2023.08.09 15:48:34 INFO  BSP server: \tat com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.instanceHubErrorStub(SubstrateAllocationSnippets.java:309)\n2023.08.09 15:48:34 INFO  BSP server: \tat jdk.unsupported@17.0.6/sun.misc.Unsafe.allocateInstance(Unsafe.java:864)\n2023.08.09 15:48:34 INFO  BSP server: \t... 36 more\n```\n\nthose logs should no longer appear.\n\nThanks to [@lwronski](https://github.com/lwronski) for providing the fix in [#2342](https://github.com/VirtusLab/scala-cli/pull/2342).\n\n\n## [v1.0.3](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.3)\n\n## What's new\n\n### Access project configuration with the new `BuildInfo`\n\n`BuildInfo` access your project's build configuration within your Scala code. This feature automatically gathers and generates build information about your project, making project details instantly accessible at runtime.\n\nTo generate BuildInfo, either use the `--build-info` command line option or include the `//> using buildInfo` directive in your code.\n\nUpon activation, a `BuildInfo` object becomes accessible on your project's classpath. To use it, simply add the following import into your code:\n\n```\nimport scala.cli.build.BuildInfo\n```\n\nThis `BuildInfo` object encapsulates information such as the Scala version used, target platform, main class, scalac options, dependencies, and much more for both Main and Test scopes. The generation ensures up-to-date configuration data from both the console options and using directives in your project's sources.\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2249](https://github.com/VirtusLab/scala-cli/pull/2249).\n\n### CompileOnly Dependencies\n\nNow, users can declare dependencies that are exclusively included at the compile time. These dependencies are added to the classpath during compilation, but won't be included when the application is run, keeping your runtime environment lightweight.\n\nTo declare such a dependency:\n\n1.  Via the using directive:\n```\n//> using compileOnly.dep \"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\"\n```\n2. Via the command line:\n```\nscala-cli Hello.scala --compile-dep \"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:2.23.2\"\n```\n\nAdded by @alexarchambault and [@lwronski](https://github.com/lwronski) in [#2299](https://github.com/VirtusLab/scala-cli/pull/2299), Thanks!\n\n\n### Set globally Java properties\n\nScala CLI allows users to globally set Java properties for its launcher using the `config` command. This will simplify the JVM properties management process, eliminating the need to pass these properties with each `scala-cli` execution.\n\nTo set global Java properties execute the following command:\n\n```\nscala-cli config java.properties Djavax.net.ssl.trustStore=cacerts Dfoo=bar2\n```\n\nWhen modifying Java properties, remember that you must redefine all of them. It's not possible to update just a single property. Essentially, each time you use the `config` command for Java properties, you replace the entire list of properties.\n\nWhenever overwriting existing Java properties Scala CLI will let you know what was the previous value and in interactive mode ensure that you are ok with replacing them.\n\nAdded by [@lwronski](https://github.com/lwronski) in [#2317](https://github.com/VirtusLab/scala-cli/pull/2317), Thanks!\n\n### Rename parameter for `publish` command\n\nWe've updated the `--version` parameter for the publish command. Now, when specifying the project version, use `--project-version` instead.\n\n```bash ignore \nscala-cli publish --project-version 1.0.3 ...\n```\n\n## Other changes\n* Add custom exception and throw it when node not found in the path by [@lwronski](https://github.com/lwronski) in [#2323](https://github.com/VirtusLab/scala-cli/pull/2323)\n* Skip reading ide-options-v2.json if doesn't exist to avoid throwing a… by [@lwronski](https://github.com/lwronski) in [#2333](https://github.com/VirtusLab/scala-cli/pull/2333)\n* Skip setting release flag when user pass directly -release or -java-o… by [@lwronski](https://github.com/lwronski) in [#2321](https://github.com/VirtusLab/scala-cli/pull/2321)\n* Prevent downloading Java 17 when running a REPL without sources by [@lwronski](https://github.com/lwronski) in [#2305](https://github.com/VirtusLab/scala-cli/pull/2305)\n* Extract JAVA_HOME from /usr/libexec/java_home for Mac by [@lwronski](https://github.com/lwronski) in [#2304](https://github.com/VirtusLab/scala-cli/pull/2304)\n* Bump case-app, add names limit to HelpFormat, move some name aliases, add test by [@MaciejG604](https://github.com/MaciejG604) in [#2280](https://github.com/VirtusLab/scala-cli/pull/2280)\n* Build info with compute version [@MaciejG604](https://github.com/MaciejG604) in [#2310](https://github.com/VirtusLab/scala-cli/pull/2310)\n\n\n### Fixes\n* Fix - install ps, which is necessary for starting Bloop by [@lwronski](https://github.com/lwronski) in [#2332](https://github.com/VirtusLab/scala-cli/pull/2332)\n* Load virtual data as byte arrays without encoding using UTF-8 by [@lwronski](https://github.com/lwronski) in [#2313](https://github.com/VirtusLab/scala-cli/pull/2313)\n* Accept directive packageType native when using native platform by [@lwronski](https://github.com/lwronski) in [#2311](https://github.com/VirtusLab/scala-cli/pull/2311)\n* Ignore url query params [@MaciejG604](https://github.com/MaciejG604) in [#2334](https://github.com/VirtusLab/scala-cli/pull/2334)\n\n### Documentation changes\n* Update runner specification by [@MaciejG604](https://github.com/MaciejG604) in [#2301](https://github.com/VirtusLab/scala-cli/pull/2301)\n* Add WinGet to Windows installation methods by [@lwronski](https://github.com/lwronski) in [#2283](https://github.com/VirtusLab/scala-cli/pull/2283)\n* Add missing caution to Password options and fix displaying command in… by [@lwronski](https://github.com/lwronski) in [#2286](https://github.com/VirtusLab/scala-cli/pull/2286)\n* Document BuildInfo [@MaciejG604](https://github.com/MaciejG604) in [#2325](https://github.com/VirtusLab/scala-cli/pull/2325)\n\n### Build and internal changes\n* Add timeout for resolving semanticDbVersion by [@lwronski](https://github.com/lwronski) in [#2322](https://github.com/VirtusLab/scala-cli/pull/2322)\n* Resolve semanticDB for older scala version by [@lwronski](https://github.com/lwronski) in [#2318](https://github.com/VirtusLab/scala-cli/pull/2318)\n* feat: use the new ScalaAction from BSP4J by [@ckipp01](https://github.com/ckipp01)  in [#2284](https://github.com/VirtusLab/scala-cli/pull/2284)\n\n\n### Updates and maintenance\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.12 by [@lwronski](https://github.com/lwronski) in [#2335](https://github.com/VirtusLab/scala-cli/pull/2335)\n* Update trees_2.13 to 4.8.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2329](https://github.com/VirtusLab/scala-cli/pull/2329)\n* Update guava to 32.1.2-jre by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2324](https://github.com/VirtusLab/scala-cli/pull/2324)\n* Update bloop-rifle_2.13 to 1.5.9-sc-1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2314](https://github.com/VirtusLab/scala-cli/pull/2314)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.11 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2315](https://github.com/VirtusLab/scala-cli/pull/2315)\n* Update scalajs-sbt-test-adapter_2.13 to 1.13.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2240](https://github.com/VirtusLab/scala-cli/pull/2240)\n* Bump VirtusLab/scala-cli-setup from 1.0.1 to 1.0.2 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2300](https://github.com/VirtusLab/scala-cli/pull/2300)\n* Update mill 0.11.1 by [@lwronski](https://github.com/lwronski) in [#2297](https://github.com/VirtusLab/scala-cli/pull/2297)\n* deps: update mill-scalafix to 0.3.1 by [@ckipp01](https://github.com/ckipp01)  in [#2285](https://github.com/VirtusLab/scala-cli/pull/2285)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.10 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2295](https://github.com/VirtusLab/scala-cli/pull/2295)\n* Update sbt to 1.9.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2288](https://github.com/VirtusLab/scala-cli/pull/2288)\n* Update trees_2.13 to 4.8.4 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2290](https://github.com/VirtusLab/scala-cli/pull/2290)\n* Update scala-cli.sh launcher for 1.0.2 by [@github-actions](https://github.com/features/actions) in [#2281](https://github.com/VirtusLab/scala-cli/pull/2281)\n* Update trees_2.13 to 4.8.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2279](https://github.com/VirtusLab/scala-cli/pull/2279)\n* Bump semver from 5.7.1 to 5.7.2 in /website by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2276](https://github.com/VirtusLab/scala-cli/pull/2276)\n\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.2...v1.0.3\n\n## [v1.0.2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.2)\n\n## What's new\n\nThis release brings enhancements to Scala CLI:\n- WinGet installation for Windows users\n- better navigation with improved build target names\n- introducing `new` command for Giter8 project generation\n- easier JVM properties management with `.scalaopts` file support.\n\nThe release also includes numerous bug fixes, updates, and new contributors.\n\n### Installation via WinGet on Windows\n\nScala CLI can now be installed via [WinGet](https://learn.microsoft.com/en-gb/windows/package-manager/) on Windows, with\na command such as\n\n```bat\nwinget install virtuslab.scalacli\n```\n\nAdded by [@mimoguz](https://github.com/mimoguz) in [#2239](https://github.com/VirtusLab/scala-cli/pull/2239), Thanks!\n\n### Enhanced build target names\n\nNow, the build target name will be derived from the workspace directory that contains it, making it easier for users to\nnavigate between different projects within a multi-root workspace. Instead of a build target named as `project_XYZ-XYZ`,\nyou will now see the name like `workspace_XYZ-XYZ`, where `workspace` refers to the name of the workspace directory.\n\n```\n.\n├── scripts\n│   ├── .scala-build\n│   │   └── scripts_59f2159dd5\n│   └── one.sc\n├── skan\n│   ├── .scala-build\n│   │   └── skan_88b44a2858\n│   └── main.scala\n└── skan.code-workspace\n```\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2201](https://github.com/VirtusLab/scala-cli/pull/2201)\n\n### Introducing 'new' command for Giter8 project generation\n\nGiter8 is a project templating tool for Scala, and its integration within Scala CLI offers efficient way to set up new\nprojects. By using the `new` command, users can generate new projects based on predefined or custom templates.\n\nFor example:\n\n```bash\nscala-cli --power new VirtusLab/scala-cli.g8\n```\n\nAdded by [@zetashift](https://github.com/zetashift) in [#2202](https://github.com/VirtusLab/scala-cli/pull/2202), Thanks!\n\n### Loading Java Properties from `.scalaopts` into ScalaCLI launcher\n\nScalaCLI allows to load Java properties into `scala-cli` launcher directly from a `.scalaopts` file located in your\ncurrent working directory. This will simplify the JVM properties management process, eliminating the need to pass these\nproperties with each scala-cli execution.\n\nFor instance, if `-Djavax.net.ssl.trustStore=cacerts` and `-Dfoo2=bar2` are defined within your `.scalaopts` file, these\nvalues will be loaded into `scala-cli` launcher:\n\n```bash ignore\n$ cat .scalaopts\n-Djavax.net.ssl.trustStore=cacerts\n-Dfoo2=bar2\n$ scala-cli run ...\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in  [#2267](https://github.com/VirtusLab/scala-cli/pull/2267)\n\nPlease be aware that ScalaCLI will only process Java properties that it recognizes from the `.scalaopts` file. Other JVM\noptions, such as` -Xms1024m`, will be ignored as they can't be used within native image, and users will be alerted with \na warning message when such non-compliant options are passed.\n\n## Other changes\n\n* Add publish.doc directive by [@lwronski](https://github.com/lwronski)\n  in [#2245](https://github.com/VirtusLab/scala-cli/pull/2245)\n* Fix pgp create with no java 17 by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2189](https://github.com/VirtusLab/scala-cli/pull/2189)\n* Support for running standalone launcher of scala-cli with JVM 8 by [@lwronski](https://github.com/lwronski)\n  in [#2253](https://github.com/VirtusLab/scala-cli/pull/2253)\n\n### Fixes\n\n* Make dependencies keep their positions when fetching by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2266](https://github.com/VirtusLab/scala-cli/pull/2266)\n* Fix empty position in DependencyFormatErrors by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2261](https://github.com/VirtusLab/scala-cli/pull/2261)\n* Script wrapper verification by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2227](https://github.com/VirtusLab/scala-cli/pull/2227)\n* Fix - include test.resourceDir into sources for test scope by [@lwronski](https://github.com/lwronski)\n  in [#2235](https://github.com/VirtusLab/scala-cli/pull/2235)\n* Fix markdown - allow running .md files that start with a number by [@lwronski](https://github.com/lwronski)\n  in [#2225](https://github.com/VirtusLab/scala-cli/pull/2225)\n* Fix dep update error by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2211](https://github.com/VirtusLab/scala-cli/pull/2211)\n* Add new mechanism for resolving scoped BuildOptions by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2274](https://github.com/VirtusLab/scala-cli/pull/2274)\n* Fix - download cs from coursier-m1 as an archive by [@lwronski](https://github.com/lwronski)\n  in [#2193](https://github.com/VirtusLab/scala-cli/pull/2193)\n* Fix - Truncate file length to 0 when override content by [@lwronski](https://github.com/lwronski)\n  in [#2188](https://github.com/VirtusLab/scala-cli/pull/2188)\n\n### Documentation changes\n\n* Add mentions that using target directives are experimental by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2262](https://github.com/VirtusLab/scala-cli/pull/2262)\n* Fix inline code in directives docs by [@izzyreal](https://github.com/izzyreal)\n  in [#2233](https://github.com/VirtusLab/scala-cli/pull/2233)\n* Update docs - dependency parameters by [@lwronski](https://github.com/lwronski)\n  in [#2224](https://github.com/VirtusLab/scala-cli/pull/2224)\n* Update directive docs for Platform by [@lwronski](https://github.com/lwronski)\n  in [#2213](https://github.com/VirtusLab/scala-cli/pull/2213)\n\n### Build and internal changes\n\n* Build changes by [@lwronski](https://github.com/lwronski) in [#2263](https://github.com/VirtusLab/scala-cli/pull/2263)\n* Remove file change portion of test by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2251](https://github.com/VirtusLab/scala-cli/pull/2251)\n* Add logging to 'watch with interactive' test by [@MaciejG604](https://github.com/MaciejG604)\n  in [#2229](https://github.com/VirtusLab/scala-cli/pull/2229)\n* Add support for parsing cancel params in native launcher of Scala CLI by [@lwronski](https://github.com/lwronski)\n  in [#2195](https://github.com/VirtusLab/scala-cli/pull/2195)\n\n### Updates and maintenance\n\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.7\n  by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2271](https://github.com/VirtusLab/scala-cli/pull/2271)\n* Update trees_2.13 to 4.8.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2272](https://github.com/VirtusLab/scala-cli/pull/2272)\n* Update core_2.13 to 3.8.16 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2270](https://github.com/VirtusLab/scala-cli/pull/2270)\n* Update jimfs to 1.3.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2269](https://github.com/VirtusLab/scala-cli/pull/2269)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.6\n  by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2264](https://github.com/VirtusLab/scala-cli/pull/2264)\n* Update trees_2.13 to 4.8.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2265](https://github.com/VirtusLab/scala-cli/pull/2265)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.5\n  by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2256](https://github.com/VirtusLab/scala-cli/pull/2256)\n* Update trees_2.13 to 4.8.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2257](https://github.com/VirtusLab/scala-cli/pull/2257)\n* Update guava to 32.1.1-jre by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2259](https://github.com/VirtusLab/scala-cli/pull/2259)\n* Update coursier-jvm_2.13, ... to 2.1.5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2232](https://github.com/VirtusLab/scala-cli/pull/2232)\n* Update sbt to 1.9.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2222](https://github.com/VirtusLab/scala-cli/pull/2222)\n* Update dependency to 0.2.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2219](https://github.com/VirtusLab/scala-cli/pull/2219)\n* Update org.eclipse.jgit to 6.6.0.202305301015-r\n  by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2220](https://github.com/VirtusLab/scala-cli/pull/2220)\n* Updates - `amm` (`2.5.9`), `scala-library` (`2.12.18`, `2.13.11`) by [@lwronski](https://github.com/lwronski)\n  in [#2223](https://github.com/VirtusLab/scala-cli/pull/2223)\n* Update bsp4j to 2.1.0-M5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2216](https://github.com/VirtusLab/scala-cli/pull/2216)\n* Update jsoniter-scala-core, ... to 2.23.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2217](https://github.com/VirtusLab/scala-cli/pull/2217)\n* Update scala-collection-compat to 2.11.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2221](https://github.com/VirtusLab/scala-cli/pull/2221)\n* Update test-runner, tools to 0.4.14 by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2192](https://github.com/VirtusLab/scala-cli/pull/2192)\n* Bump VirtusLab/scala-cli-setup from 1.0.0 to 1.0.1\n  by [@dependabot](https://docs.github.com/en/code-security/dependabot)\n  in [#2207](https://github.com/VirtusLab/scala-cli/pull/2207)\n* Update guava to 32.0.1-jre by [@scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#2197](https://github.com/VirtusLab/scala-cli/pull/2197)\n* Update scala-cli.sh launcher for 1.0.1 by [@github-actions](https://github.com/features/actions) in [#2194](https://github.com/VirtusLab/scala-cli/pull/2194)\n* Upgrade scripts to latest coursier by [@mkurz](https://github.com/mkurz)\n  in [#1728](https://github.com/VirtusLab/scala-cli/pull/1728)\n\n## New Contributors\n\n* [@zetashift](https://github.com/zetashift) made their first contribution\n  in [#2202](https://github.com/VirtusLab/scala-cli/pull/2202)\n* [@izzyreal](https://github.com/izzyreal) made their first contribution\n  in [#2233](https://github.com/VirtusLab/scala-cli/pull/2233)\n* [@mimoguz](https://github.com/mimoguz) made their first contribution\n  in [#2239](https://github.com/VirtusLab/scala-cli/pull/2239)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.1...v1.0.2\n\n## [v1.0.1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.1)\n\n## What's new\nThis release only contains bug fixes and minor internal improvements.\n\n### Fixes\n* Fix - add test to output from name of script example by [@lwronski](https://github.com/lwronski) in [#2153](https://github.com/VirtusLab/scala-cli/pull/2153)\n* Fix publishing with implicit `publish.version` coming from a `git` tag by [@Gedochao](https://github.com/Gedochao) in [#2154](https://github.com/VirtusLab/scala-cli/pull/2154)\n* Fix conflicts when watch and interactive try to read StdIn by [@MaciejG604](https://github.com/MaciejG604) in [#2168](https://github.com/VirtusLab/scala-cli/pull/2168)\n* Bsp wrapper fixes by [@MaciejG604](https://github.com/MaciejG604) in [#2171](https://github.com/VirtusLab/scala-cli/pull/2171)\n* Add the .exe suffix to output provided by user for graalvm-native-image by [@lwronski](https://github.com/lwronski) in [#2182](https://github.com/VirtusLab/scala-cli/pull/2182)\n\n### Build and internal changes\n* refactor: Remove JavaInterface, which causes compilation issues with Bloop by [@tgodzik](https://github.com/tgodzik) in [#2174](https://github.com/VirtusLab/scala-cli/pull/2174)\n* Enforce to use jvm 17 on linux aarch64 by [@lwronski](https://github.com/lwronski) in [#2180](https://github.com/VirtusLab/scala-cli/pull/2180)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.0.0 by [@github-actions](https://github.com/features/actions) in [#2149](https://github.com/VirtusLab/scala-cli/pull/2149)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#2155](https://github.com/VirtusLab/scala-cli/pull/2155)\n* Update jsoniter-scala-core, ... to 2.23.1 by [@scala-steward](https://github.com/scala-steward) in [#2160](https://github.com/VirtusLab/scala-cli/pull/2160)\n* Update guava to 32.0.0-jre by [@scala-steward](https://github.com/scala-steward) in [#2161](https://github.com/VirtusLab/scala-cli/pull/2161)\n* Update coursier-jvm_2.13, ... to 2.1.4 by [@scala-steward](https://github.com/scala-steward) in [#2162](https://github.com/VirtusLab/scala-cli/pull/2162)\n* Update sbt to 1.8.3 by [@scala-steward](https://github.com/scala-steward) in [#2164](https://github.com/VirtusLab/scala-cli/pull/2164)\n* Bump `mill` scripts by [@Gedochao](https://github.com/Gedochao) in [#2167](https://github.com/VirtusLab/scala-cli/pull/2167)\n* Bump VirtusLab/scala-cli-setup from 0.2.1 to 1.0.0 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#2169](https://github.com/VirtusLab/scala-cli/pull/2169)\n* Bump `scala-cli-signing` to `0.2.2` by [@Gedochao](https://github.com/Gedochao) in [#2173](https://github.com/VirtusLab/scala-cli/pull/2173)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.4 by [@scala-steward](https://github.com/scala-steward) in [#2175](https://github.com/VirtusLab/scala-cli/pull/2175)\n* Update trees_2.13 to 4.7.8 by [@scala-steward](https://github.com/scala-steward) in [#2176](https://github.com/VirtusLab/scala-cli/pull/2176)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.0...v1.0.1\n\n## [v1.0.0](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.0)\n\n## The official `scala` runner release\n\nScala CLI has reached the highly anticipated `1.0.0` milestone!\nHaving addressed all the [SIP-46](https://github.com/scala/improvement-proposals/pull/46) requirements,\nthis version is going to become the official `scala` runner, replacing the old `scala` command.\n\nFor a deeper understanding of Scala CLI as the new `scala` runner and to explore its benefits and features,\nwe encourage you to check out our [blogpost](https://virtuslab.com/blog/scala-cli-the-new-scala-runner/).\n\nAlso be sure to get familiar with all the differences introduced by this change in our [migration guide](guides/introduction/old-runner-migration.md).\n\n## What's Changed\n\n### New default Scala version - 3.3.0\n\nScala 3.3.0 is now the default version for Scala CLI projects.\nIt's the first LTS (Long Term Support) release of Scala 3 to be used by Scala CLI.\nRight on time for 1.0.0!\n\nAdded by [@lwronski](https://github.com/lwronski) in [#2140](https://github.com/VirtusLab/scala-cli/pull/2140)\n\n### Toolkit-test\n\nBy incorporating the [Scala Toolkit](https://github.com/scala/toolkit) into your project, you gain the advantage of two additional\ndependencies seamlessly integrated into your classpath:\n- `org.scala-lang:toolkit:<version>` is added to the main scope, allowing its utilization throughout your project.\n- `org.scala-lang:toolkit-test:<version>` is included in the test scope, making it available exclusively for testing purposes.\n\nScala CLI now supports the following features for the toolkit:\n* including e.g. `//> using toolkit latest` in any main scope file will automatically add the `toolkit` dependency to the main scope and the `toolkit-test` dependency to the test scope\n* if you place e.g. `//> using toolkit latest` within a test scope file, both `toolkit` and `toolkit-test` will be limited to the test scope only\n* inserting e.g. `//> using test.toolkit latest` anywhere in the project will add both `toolkit` and `toolkit-test` to the test scope only\n\nThis convention is encouraged for other toolkit-like libraries as well.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#2127](https://github.com/VirtusLab/scala-cli/pull/2127) and [#2137](https://github.com/VirtusLab/scala-cli/pull/2137)\n\n### Forcing an object wrapper for scripts\n\nScala CLI now supports the `//> using objectWrapper` directive, along with the corresponding `--object-wrapper` option,\nwhich allows to force wrapping script code in an object body instead of a class.\n\nUsing object wrappers should be avoided for scripts relying on multi-threading (as it may cause deadlocks), but may prove to be the only option in some cases.\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#2136](https://github.com/VirtusLab/scala-cli/pull/2136)\n\n## Other changes\n* Add alias for snapshots repository in Maven by [@lwronski](https://github.com/lwronski) in [#2125](https://github.com/VirtusLab/scala-cli/pull/2125)\n* Bump typelevel-toolkit to 0.0.11, configure toolkit-test by [@armanbilge](https://github.com/armanbilge) in [#2135](https://github.com/VirtusLab/scala-cli/pull/2135)\n* Fix updating toolkit dependencies by [@Gedochao](https://github.com/Gedochao) in [#2138](https://github.com/VirtusLab/scala-cli/pull/2138)\n* Improve directive parsing errors & special-case `toolkit` directive version parsing by [@Gedochao](https://github.com/Gedochao) in [#2133](https://github.com/VirtusLab/scala-cli/pull/2133)\n* Fix determining position for value in directive without quotes by [@lwronski](https://github.com/lwronski) in [#2141](https://github.com/VirtusLab/scala-cli/pull/2141)\n\n### Fixes\n* Fix line conversion logic by simplifying topWrapperLen to line count of top wrapper by [@MaciejG604](https://github.com/MaciejG604) in [#2101](https://github.com/VirtusLab/scala-cli/pull/2101)\n* Fix test watch infinite loop by [@MaciejG604](https://github.com/MaciejG604) in [#2113](https://github.com/VirtusLab/scala-cli/pull/2113)\n* Fix flaky completions for `zsh` by [@Jasper-M](https://github.com/Jasper-M) in [#2118](https://github.com/VirtusLab/scala-cli/pull/2118)\n* Fix - install certificates for java by [@lwronski](https://github.com/lwronski) in [#2123](https://github.com/VirtusLab/scala-cli/pull/2123)\n* Fix the `--source-jar` option & add corresponding `using` directives by [@Gedochao](https://github.com/Gedochao) in [#2120](https://github.com/VirtusLab/scala-cli/pull/2120)\n\n### Documentation changes\n* Add docs for bootstrapped standalone fat JAR by [@lwronski](https://github.com/lwronski) in [#2122](https://github.com/VirtusLab/scala-cli/pull/2122)\n* Add developer docs on modifying `reflect-config.json` by [@Gedochao](https://github.com/Gedochao) in [#2114](https://github.com/VirtusLab/scala-cli/pull/2114)\n\n### Build and internal changes\n* Update release procedure - update also v1 tag by [@lwronski](https://github.com/lwronski) in [#2107](https://github.com/VirtusLab/scala-cli/pull/2107)\n* NIT Refactor test scope directives by [@Gedochao](https://github.com/Gedochao) in [#2083](https://github.com/VirtusLab/scala-cli/pull/2083)\n* Add main class to jar manifest in assembly by [@romanowski](https://github.com/romanowski) in [#2124](https://github.com/VirtusLab/scala-cli/pull/2124)\n\n### Updates and maintenance\n* Update scala-cli.sh launcher for 1.0.0-RC2 by [@github-actions](https://github.com/features/actions) in [#2105](https://github.com/VirtusLab/scala-cli/pull/2105)\n* Update org.eclipse.jgit to 6.5.0.202303070854-r by [@scala-steward](https://github.com/scala-steward) in [#2090](https://github.com/VirtusLab/scala-cli/pull/2090)\n\n## New Contributors\n* [@Jasper-M](https://github.com/Jasper-M) made their first contribution in [#2118](https://github.com/VirtusLab/scala-cli/pull/2118)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.0-RC2...v1.0.0\n\n## [v1.0.0-RC2](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.0-RC2)\n\n## What's Changed\n\n### Exclude\n\nTo exclude specific source files or entire directories from a Scala CLI project, you can now use the `//> using exclude` directive in your `project.scala` file.\nAlternatively, you can do the same from the command line with the `--exclude` option.\n- absolute path: `/root/path/to/your/project/Main.scala`\n- relative path: `src/main/scala/Main.scala`\n- glob pattern: `*.sc`\n\nFor example, to exclude all files in the `example/scala` directory, add the following directive to your `project.scala` file:\n```scala\n//> using exclude \"example/scala\"\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#2053](https://github.com/VirtusLab/scala-cli/pull/2053).\n\n### Directives with a Test Scope equivalent\n\nSome directives now have a test scope equivalent, such as `using dep` and its test scope counterpart `using test.dep`. This allows you to declare dependencies that are only used in tests outside of test-specific sources.\n\nFor example, you can declare a dependency on `munit` in your `project.scala` file like this:\n```scala\n//> using test.dep \"org.scalameta::munit::0.7.29\"\n```\nThe dependency will only be available in test sources.\n\nHere's a list of directives with a test scope equivalent with example values:\n\n```scala\n //> using test.dep \"org.scalameta::munit::0.7.29\"\n //> using test.jar \"path/to/jar\"\n //> using test.javaOpt \"-Dfoo=bar\"\n //> using test.javacOpt \"source\", \"1.8\", \"target\", \"1.8\"\n //> using test.javaProp \"foo1=bar1\"\n //> using test.option \"-Xfatal-warnings\"\n //> using test.resourceDir \"testResources\"\n //> using test.toolkit \"latest\"\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in  [#2046](https://github.com/VirtusLab/scala-cli/pull/2046)\n\n### Changes to using-directives syntax\n\nWe've made several updates to simplify the using directives syntax in this release:\n\n- allowed omitting commas in lists of values.\n- disallowed multiline comments.\n- removed multiline strings.\n- removed `require` and `@require` syntax support.\n- allowed values without quotes.\n- removed `@using`.\n\nFor example, the following using directives are now valid without the need for commas and quotes:\n\n```scala\n//> using scala 3.2.2\n//> using javacOpt -source 1.8 -target 1.8\n```\n\nAdded by [@tgodzik](https://github.com/tgodzik) in [#2076](https://github.com/VirtusLab/scala-cli/pull/2076)\n\n### Bootstrapped standalone fat JAR.\n\nThe Scala CLI launcher is available as a standalone fat JAR. You can download the stable version of the Scala CLI fat JAR from Maven and try it now:\n\n```bash ignore\ncs launch org.virtuslab.scala-cli:cliBootstrapped:1.0.0-RC2 -M scala.cli.ScalaCli\n```\n\nAdded by [@romanowski](https://github.com/romanowski) in [#2005](https://github.com/VirtusLab/scala-cli/pull/2005).\n\n### Access the path of the script being run from its code\n\nWith the special `scriptPath` function, you can now easily access the path of the script being run from the script code itself.\nHere's an example of how to use the `scriptPath` value:\n\n```scala title=scripts/hello.sc\n#!/usr/bin/env -S scala-cli shebang\n\nprintln(scriptPath)\n```\n\n<ChainedSnippets>\n\n```bash\nchmod +x scripts/hello.sc\n./scripts/hello.sc\n```\n\n```text\n./scripts/hello.sc\n```\n\n</ChainedSnippets>\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1990](https://github.com/VirtusLab/scala-cli/pull/1990)\n\n### Explicit Handling of Paths in using-directives\n\nThe `${.}` pattern in directive values can now be replaced by the parent directory of the file containing the directive. This makes it possible to generate coverage output files relative to the source file location, for example:\n\n```scala\n//> using options \"-coverage-out:${.}\"\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#2040](https://github.com/VirtusLab/scala-cli/pull/2040)\n\n### Fix deadlocks in Script Wrappers\n\nWe have resolved an issue that caused deadlocks when threads were run from the static initializer of the wrapper object \n([#532](https://github.com/VirtusLab/scala-cli/pull/532) and [#1933](https://github.com/VirtusLab/scala-cli/pull/1933)).\nBased on the feedback from the community (Thanks [@dacr](https://github.com/dacr)), we found that encapsulating the script code\ninto a class wrapper fixes the issue. The wrapper is generated by the Scala CLI and is not visible to the user.\n\nThis change alters the behavior of scripts that use the `@main` annotation. The `@main` annotation is no longer supported in `.sc` files.\n\n```scala title=script.sc\n@main def main(args: String*): Unit = println(\"Hello\")\n```\n\n<ChainedSnippets>\n\n```bash run-fail\nscala-cli script.sc\n```\n\n```text\n[warn]  Annotation @main in .sc scripts is not supported, use .scala format instead\nCompiling project (Scala 3.2.2, JVM)\n[error] ./script.sc:1:1\n[error] method main cannot be a main method since it cannot be accessed statically\n[error] @main def main(args: String*): Unit = println(\"Hello\")\n[error] ^^^^^\nError compiling project (Scala 3.2.2, JVM)\nCompilation failed\n```\n\n</ChainedSnippets>\n\nFixed by [@MaciejG604](https://github.com/MaciejG604) in [#2033](https://github.com/VirtusLab/scala-cli/pull/2033)\n\n## Other changes\n\n* Add first-class support for Typelevel and other toolkits by [@armanbilge](https://github.com/armanbilge) in [#2025](https://github.com/VirtusLab/scala-cli/pull/2025)\n* Make shebang run not check dependency updates by [@MaciejG604](https://github.com/MaciejG604) in [#2022](https://github.com/VirtusLab/scala-cli/pull/2022)\n* Make 'export --json' print to stdout by default by [@MaciejG604](https://github.com/MaciejG604) in [#2008](https://github.com/VirtusLab/scala-cli/pull/2008)\n* Don't print the spread directives warning if there's only a single file per scope by [@Gedochao](https://github.com/Gedochao) in [#1988](https://github.com/VirtusLab/scala-cli/pull/1988)\n* Add --as-jar option by [@alexarchambault](https://github.com/alexarchambault) in [#2028](https://github.com/VirtusLab/scala-cli/pull/2028)\n* add newline to topWrapper by [@bishabosha](https://github.com/bishabosha) in [#1998](https://github.com/VirtusLab/scala-cli/pull/1998)\n\n\n### Publishing changes\n* React to secret key decryption error by [@MaciejG604](https://github.com/MaciejG604) in [#1993](https://github.com/VirtusLab/scala-cli/pull/1993)\n* Use ASCII armored secret key by [@MaciejG604](https://github.com/MaciejG604) in [#1991](https://github.com/VirtusLab/scala-cli/pull/1991)\n* Properly handle pgp keychains generated by Scala CLI by [@MaciejG604](https://github.com/MaciejG604) in [#1987](https://github.com/VirtusLab/scala-cli/pull/1987)\n\n#### Fixes\n\n* Fix `ExcludeTests` by [@Gedochao](https://github.com/Gedochao) in [#2082](https://github.com/VirtusLab/scala-cli/pull/2082)\n* bugfix: Properly show unsupported binary version by [@tgodzik](https://github.com/tgodzik) in [#2081](https://github.com/VirtusLab/scala-cli/pull/2081)\n* Allow BSP to start successfully even with unrecognised `using` directives by [@Gedochao](https://github.com/Gedochao) in [#2072](https://github.com/VirtusLab/scala-cli/pull/2072)\n* Fix invalid `scala-cli-signing` artifact downloads by [@Gedochao](https://github.com/Gedochao) in [#2054](https://github.com/VirtusLab/scala-cli/pull/2054)\n* Fix - package js without main method by [@lwronski](https://github.com/lwronski) in [#2038](https://github.com/VirtusLab/scala-cli/pull/2038)\n* Fix completions by [@Gedochao](https://github.com/Gedochao) in [#2004](https://github.com/VirtusLab/scala-cli/pull/2004)\n* Fix export failing on input duplicates [@Gedochao](https://github.com/Gedochao) in [#2098](https://github.com/VirtusLab/scala-cli/pull/2098)\n* Clean up parsing repositories for publishing [@romanowski](https://github.com/romanowski) in [#2084](https://github.com/VirtusLab/scala-cli/pull/2084)\n\n### Documentation changes\n* Docs: Update build output folder in Internal docs by [@amaalali](https://github.com/amaalali) in [#2071](https://github.com/VirtusLab/scala-cli/pull/2071)\n* Add docs for test scope directives by [@Gedochao](https://github.com/Gedochao) in [#2058](https://github.com/VirtusLab/scala-cli/pull/2058)\n* Improve error messages for malformed `config` values by [@Gedochao](https://github.com/Gedochao) in [#2014](https://github.com/VirtusLab/scala-cli/pull/2014)\n* Update export documentation by [@MaciejG604](https://github.com/MaciejG604) in [#2023](https://github.com/VirtusLab/scala-cli/pull/2023)\n* Add weaver test framework instruction by [@lenguyenthanh](https://github.com/lenguyenthanh) in [#2021](https://github.com/VirtusLab/scala-cli/pull/2021)\n\n### Build and internal changes\n* Download cs for aarch64 from coursier-m1 repo by [@lwronski](https://github.com/lwronski) in [#2085](https://github.com/VirtusLab/scala-cli/pull/2085)\n* Pass `invokeData` all the way to pre-processing to give more meaningful error/warning messages by [@Gedochao](https://github.com/Gedochao) in [#2073](https://github.com/VirtusLab/scala-cli/pull/2073)\n* Refactor `using` directives processing by [@Gedochao](https://github.com/Gedochao) in [#2066](https://github.com/VirtusLab/scala-cli/pull/2066)\n* Remove the `examples` directory to fix `scala-steward` runs by [@Gedochao](https://github.com/Gedochao) in [#2067](https://github.com/VirtusLab/scala-cli/pull/2067)\n* Remove some dead code in build by [@alexarchambault](https://github.com/alexarchambault) in [#2069](https://github.com/VirtusLab/scala-cli/pull/2069)\n* NIT Remove dead `BuildDeps` by [@Gedochao](https://github.com/Gedochao) in [#2065](https://github.com/VirtusLab/scala-cli/pull/2065)\n* Clean up build by [@romanowski](https://github.com/romanowski) in [#2017](https://github.com/VirtusLab/scala-cli/pull/2017)\n* Developers reflect 5 active developers in the repo. by [@romanowski](https://github.com/romanowski) in [#2006](https://github.com/VirtusLab/scala-cli/pull/2006)\n* Increase maximum memory allocation for JVM by [@lwronski](https://github.com/lwronski) in [#2012](https://github.com/VirtusLab/scala-cli/pull/2012)\n* Use bloop-rifle module from scala-cli/bloop-core repo by [@alexarchambault](https://github.com/alexarchambault) in [#1989](https://github.com/VirtusLab/scala-cli/pull/1989)\n* Add missing modules for which unit tests are now executed by [@lwronski](https://github.com/lwronski) in [#1992](https://github.com/VirtusLab/scala-cli/pull/1992)\n* Remove dead code for ordering PreprocessedSources by [@MaciejG604](https://github.com/MaciejG604) in [#2103](https://github.com/VirtusLab/scala-cli/pull/2103)\n\n### Updates and maintenance\n* Downgrade GraalVM to `22.3.1` to fix M1 by [@Gedochao](https://github.com/Gedochao) in [#2099](https://github.com/VirtusLab/scala-cli/pull/2099)\n* Update slf4j-nop to 2.0.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2095](https://github.com/VirtusLab/scala-cli/pull/2095)\n* Update sbt to 1.6.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2093](https://github.com/VirtusLab/scala-cli/pull/2093)\n* Update bsp4j to 2.1.0-M4 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2086](https://github.com/VirtusLab/scala-cli/pull/2086)\n* Bump `coursier` to `2.1.3` by [@Gedochao](https://github.com/Gedochao) in [#2077](https://github.com/VirtusLab/scala-cli/pull/2077)\n* Update core_2.13 to 3.8.15 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2087](https://github.com/VirtusLab/scala-cli/pull/2087)\n* Update file-tree-views to 2.1.10 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2088](https://github.com/VirtusLab/scala-cli/pull/2088)\n* Bump `graalvm` to `22.3.2` by [@Gedochao](https://github.com/Gedochao) in [#2078](https://github.com/VirtusLab/scala-cli/pull/2078)\n* Update asm to 9.5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2092](https://github.com/VirtusLab/scala-cli/pull/2092)\n* Bump coursier/setup-action from 1.3.2 to 1.3.3 by [@dependabot](https://docs.github.com/en/code-security/dependabot)  in [#2070](https://github.com/VirtusLab/scala-cli/pull/2070)\n* Bump `jsoniter`, `scalameta`, `os-lib` and `scala-collection-compat` by [@Gedochao](https://github.com/Gedochao) in [#2064](https://github.com/VirtusLab/scala-cli/pull/2064)\n* Bump `coursier` to `2.1.2` by [@Gedochao](https://github.com/Gedochao) in [#2063](https://github.com/VirtusLab/scala-cli/pull/2063)\n* Bump `ammonite` to `2.5.8` by [@Gedochao](https://github.com/Gedochao) in [#2057](https://github.com/VirtusLab/scala-cli/pull/2057)\n* Bump Scala.js to `1.13.1` by [@Gedochao](https://github.com/Gedochao) in [#2062](https://github.com/VirtusLab/scala-cli/pull/2062)\n* Bump coursier/setup-action from 1.3.1 to 1.3.2 by [@dependabot](https://docs.github.com/en/code-security/dependabot)  in [#2055](https://github.com/VirtusLab/scala-cli/pull/2055)\n* Bump coursier/setup-action from 1.3.0 to 1.3.1 by [@dependabot](https://docs.github.com/en/code-security/dependabot)  in [#2042](https://github.com/VirtusLab/scala-cli/pull/2042)\n* Dump bloop core to 1.5.6-sc-8 by [@lwronski](https://github.com/lwronski) in [#2013](https://github.com/VirtusLab/scala-cli/pull/2013)\n* Fix snapshot versions calculation when the current version ends with `-RC.` by [@Gedochao](https://github.com/Gedochao) in [#2002](https://github.com/VirtusLab/scala-cli/pull/2002)\n* Update scala-cli.sh launcher for 1.0.0-RC1 by [@github-actions](https://github.com/features/actions) in [#1995](https://github.com/VirtusLab/scala-cli/pull/1995)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.7.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#2094](https://github.com/VirtusLab/scala-cli/pull/2094)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v1.0.0-RC1...v1.0.0-RC2\n\n## [v1.0.0-RC1](https://github.com/VirtusLab/scala-cli/releases/tag/v1.0.0-RC1)\n\n### Official `scala` runner release candidate\n\n`v1.0.0-RC1` is the first release candidate version of Scala CLI.\n\nEither this or a future release candidate is meant to become the new official `scala` runner to accompany\nthe Scala compiler (`scalac`) and other scripts, replacing the old `scala` command. \n\nTo learn more about Scala CLI as the new `scala` runner, check out our recent blogpost: \nhttps://virtuslab.com/blog/scala-cli-the-new-scala-runner/\n\n### Scala CLI should now have better performance\n\nWith a number of newly added performance tweaks, you can expect Scala CLI to run considerably faster.\nAdded by  [@lwronski](https://github.com/lwronski) in [#1939](https://github.com/VirtusLab/scala-cli/pull/1939)\n\n### Print appropriate warnings when experimental features are used\n\nUsing experimental features will now cause Scala CLI to print an appropriate warning.\n\n```bash\nscala-cli --power -e '//> using publish.name \"my-library\"'\n# The '//> publish.name \"my-library\"' directive is an experimental feature.\n# Please bear in mind that non-ideal user experience should be expected.\n# If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli\n```\n\nThe warning can be suppressed with the `--suppress-experimental-warning` option, or alternatively with the\n`suppress-warning.experimental-features` global config key.\n```bash\nscala-cli config suppress-warning.experimental-features true\n```\n\nAdded by  [@Gedochao](https://github.com/Gedochao) in [#1920](https://github.com/VirtusLab/scala-cli/pull/1920)\n\n### Experimental and restricted configuration keys will now require to be accessed in `--power` mode\n\nSome configuration keys available with the `config` sub-command have been tagged as experimental or restricted and will \nonly be available in `--power` mode.\n\n```bash ignore\nscala-cli config httpProxy.address\n# The 'httpProxy.address' configuration key is restricted.\n# You can run it with the '--power' flag or turn power mode on globally by running:\n#   scala-cli config power true\n```\n\nAdded by  [@Gedochao](https://github.com/Gedochao) in [#1953](https://github.com/VirtusLab/scala-cli/pull/1953)\n\n\n### Dropped deprecated `using` directive syntax \n\nThe following syntax for `using` directives have been dropped:\n- skipping `//>`\n- multiline directives\n- directives in `/*> ... */` comments\n- directives in plain `//` comments\n- `@using`\n\nAdded by  [@tgodzik](https://github.com/tgodzik) in [#1932](https://github.com/VirtusLab/scala-cli/pull/1932)\n\n### Added support for packaging native images from Docker\n\nIt is now possible to package a GraalVM native image with Scala CLI from docker.\n\n```bash ignore\ndocker run -v $(pwd)/Hello.scala:/Hello.scala virtuslab/scala-cli package --native-image /Hello.scala\n```\n\nAdded by  [@lwronski](https://github.com/lwronski) in [#1961](https://github.com/VirtusLab/scala-cli/pull/1961)\n\n### Added support for Scala Native's `LTO`\nIt is now possible to set the Link Time Optimization (LTO) when using Scala CLI with Scala Native.\nThe available options are \"thin\", \"full\" and \"none\".\nYou can do it with the `--native-lto` option from the command line:\n\n```bash\nscala-cli -e 'println(\"Hello\")' --native --native-lto full\n```\n\nOr with a `using` directive:\n\n```scala compile\n//> using platform \"scala-native\"\n//> using nativeLto \"full\"\n@main def main(): Unit = println(\"Hello\")\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1964](https://github.com/VirtusLab/scala-cli/pull/1964)\n\n### Other changes\n\n#### Publishing changes\n* Make credential entries respect the --password-value option by [@MaciejG604](https://github.com/MaciejG604)\n  in [#1949](https://github.com/VirtusLab/scala-cli/pull/1949)\n* Write PGP keys to publish-conf when doing publish setup by [@MaciejG604](https://github.com/MaciejG604)\n  in [#1940](https://github.com/VirtusLab/scala-cli/pull/1940)\n* Comply with optional password in `scala-cli-signing` by [@MaciejG604](https://github.com/MaciejG604)\n  in [#1982](https://github.com/VirtusLab/scala-cli/pull/1982)\n* Support ssh in GitHub repo org&name extraction by [@KuceraMartin](https://github.com/KuceraMartin) \n  in [#1938](https://github.com/VirtusLab/scala-cli/pull/1938)\n\n#### Fixes\n* Print an informative error if the project workspace path contains `File.pathSeparator` by [@Gedochao](https://github.com/Gedochao)\n  in [#1985](https://github.com/VirtusLab/scala-cli/pull/1985)\n* Enable to pass custom docker-cmd to execute application in docker by [@lwronski](https://github.com/lwronski) \n  in [#1980](https://github.com/VirtusLab/scala-cli/pull/1980)\n* Fix - uses show cli.nativeImage command to generate native image by [@lwronski](https://github.com/lwronski)\n  in [#1975](https://github.com/VirtusLab/scala-cli/pull/1975)\n* Vcs.parse fix by [@KuceraMartin](https://github.com/KuceraMartin)\n  in [#1963](https://github.com/VirtusLab/scala-cli/pull/1963)\n* move args definition to the top of the script  by [@bishabosha](https://github.com/bishabosha)\n  in [#1983](https://github.com/VirtusLab/scala-cli/pull/1983)\n\n#### Documentation changes\n\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) \n  in [#1935](https://github.com/VirtusLab/scala-cli/pull/1935)\n* Remove ChainedSnippets by  [@MaciejG604](https://github.com/MaciejG604) \n  in [#1928](https://github.com/VirtusLab/scala-cli/pull/1928)\n* Further document publish command by  [@MaciejG604](https://github.com/MaciejG604) \n  in [#1914](https://github.com/VirtusLab/scala-cli/pull/1914)\n* Add a verbosity guide by  [@Gedochao](https://github.com/Gedochao) \n  in [#1936](https://github.com/VirtusLab/scala-cli/pull/1936)\n* Docs - how to run unit tests in Scala CLI by [@lwronski](https://github.com/lwronski)\n  in [#1977](https://github.com/VirtusLab/scala-cli/pull/1977)\n\n#### Build and internal changes\n\n* Use locally build jvm launcher of scala-cli in gifs generator by  [@lwronski](https://github.com/lwronski)\n  in [#1921](https://github.com/VirtusLab/scala-cli/pull/1921)\n* Clean up after ammonite imports removal by  [@MaciejG604](https://github.com/MaciejG604) \n  in [#1934](https://github.com/VirtusLab/scala-cli/pull/1934)\n* Temporarily disable `PublishTests.secret keys in config` on Windows by  [@Gedochao](https://github.com/Gedochao)\n  in [#1948](https://github.com/VirtusLab/scala-cli/pull/1948)\n* Move toolkit to scalalang org by [@szymon-rd](https://github.com/szymon-rd) \n  in [#1930](https://github.com/VirtusLab/scala-cli/pull/1930)\n\n#### Updates and maintenance\n\n* Update scala-cli.sh launcher for 0.2.1 by [@github-actions](https://github.com/features/actions) \n  in [#1931](https://github.com/VirtusLab/scala-cli/pull/1931)\n* Bump VirtusLab/scala-cli-setup from 0.2.0 to 0.2.1 by [@dependabot](https://docs.github.com/en/code-security/dependabot) \n  in [#1947](https://github.com/VirtusLab/scala-cli/pull/1947)\n* Bump coursier/publish version to 0.1.4 by  [@MaciejG604](https://github.com/MaciejG604) \n  in [#1950](https://github.com/VirtusLab/scala-cli/pull/1950)\n* Bump to the latest weaver & remove expecty by [@lenguyenthanh](https://github.com/lenguyenthanh) \n  in [#1955](https://github.com/VirtusLab/scala-cli/pull/1955)\n* Bump webfactory/ssh-agent from 0.7.0 to 0.8.0 by [@dependabot](https://docs.github.com/en/code-security/dependabot) \n  in [#1967](https://github.com/VirtusLab/scala-cli/pull/1967)\n* chore(dep): bump mill from 0.10.10 to 0.10.12 by [@ckipp01](https://github.com/ckipp01) \n  in [#1970](https://github.com/VirtusLab/scala-cli/pull/1970)\n* Bump Bleep to `1.5.6-sc-4`by [@Gedochao](https://github.com/Gedochao) \n  in [#1973](https://github.com/VirtusLab/scala-cli/pull/1973)\n\n### New Contributors\n\n* [@KuceraMartin](https://github.com/KuceraMartin) made their first contribution \n  in [#1938](https://github.com/VirtusLab/scala-cli/pull/1938)\n* [@lenguyenthanh](https://github.com/lenguyenthanh) made their first contribution \n  in [#1955](https://github.com/VirtusLab/scala-cli/pull/1955)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.2.1...v1.0.0-RC1\n\n## [v0.2.1](https://github.com/VirtusLab/scala-cli/releases/tag/v0.2.1)\n\n### Add a guide for migrating from the old `scala` runner to Scala CLI\n\nAs of [SIP-46](https://github.com/scala/improvement-proposals/pull/46), Scala CLI has been accepted as the new `scala`\ncommand. To make the transition smooth we added a [guide](docs/guides/introduction/old-runner-migration.md) highlighting\nthe differences between the two runners.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1900](https://github.com/VirtusLab/scala-cli/pull/1900)\n\n### Improve the `publish` and `publish setup` sub-commands' user experience\n\nWe're currently focusing on improving the experimental `publish` feature of Scala CLI and making `publish setup` + `publish`\nmore stable and user-friendly.\n\nUsing pgp keys created by `config --create-pgp-key` subcommand is now supported as a default option,\nno additional user input is needed.\n\nAddressed by [@alexarchambault](https://github.com/alexarchambault) in [#1432](https://github.com/VirtusLab/scala-cli/pull/1432)\nand by [@MaciejG604](https://github.com/MaciejG604) in [#1898](https://github.com/VirtusLab/scala-cli/pull/1898)\n\n### Remove unsupported kebab-case style in using directives\n\nAll using directives names are now using camelCase, kebab-case is no longer available.\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1878](https://github.com/VirtusLab/scala-cli/pull/1878)\n\n### Add a reference for available config keys in help & docs\n\nYou can now view the available config keys using `config --help`:\n```bash\nscala-cli config -h\n# Usage: scala-cli config [options]\n# Configure global settings for Scala CLI.\n# \n# Available keys:\n#   actions                                        Globally enables actionable diagnostics. Enabled by default.\n#   interactive                                    Globally enables interactive mode (the '--interactive' flag).\n#   power                                          Globally enables power mode (the '--power' launcher flag).\n#   suppress-warning.directives-in-multiple-files  Globally suppresses warnings about directives declared in multiple source files.\n#   suppress-warning.outdated-dependencies-files   Globally suppresses warnings about outdated dependencies.\n# \n# You are currently viewing the basic help for the config sub-command. You can view the full help by running: \n#    scala-cli config --help-full\n# For detailed documentation refer to our website: https://scala-cli.virtuslab.org/docs/commands/misc/config\n# \n# Config options:\n#   --unset, --remove  Remove an entry from config\n```\nAlso, `config --full-help` will show the list of all keys.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1910](https://github.com/VirtusLab/scala-cli/pull/1910)\n\n### Pass user arguments to JS runner\n\nIt's now possible to pass user arguments to a JS application:\n\n```scala title=ScalaJsArgs.sc\nimport scala.scalajs.js\nimport scala.scalajs.js.Dynamic.global\n\nval process = global.require(\"process\")\nval argv = Option(process.argv)\n  .filterNot(js.isUndefined)\n  .map(_.asInstanceOf[js.Array[String]].drop(2).toSeq)\n  .getOrElse(Nil)\nval console = global.console\nconsole.log(argv.mkString(\" \"))\n```\n\n```bash\nscala-cli ScalaJsArgs.sc --js -- Hello World\n```\n```text\nHello World\n```\n\nAdded by [@alexarchambault](https://github.com/alexarchambault) in [#1826](https://github.com/VirtusLab/scala-cli/pull/1826)\n\n\n### Other changes\n* Tweak error messages for running scripts without file extensions by [@Gedochao](https://github.com/Gedochao) in [#1886](https://github.com/VirtusLab/scala-cli/pull/1886)\n* Exit with Bloop command return code if it's non-zero by [@alexarchambault](https://github.com/alexarchambault) in [#1837](https://github.com/VirtusLab/scala-cli/pull/1837)\n* bloop-rifle: increase timeout values by [@Flowdalic](https://github.com/Flowdalic) in [#1865](https://github.com/VirtusLab/scala-cli/pull/1865)\n* Suggest users to clean working directory when Nailgun server failed by [@lwronski](https://github.com/lwronski) in [#1916](https://github.com/VirtusLab/scala-cli/pull/1916)\n* fix: encode videos in yuv420p to support Firefox by [@danielleontiev](https://github.com/danielleontiev) in [#1904](https://github.com/VirtusLab/scala-cli/pull/1904)\n* Fix reading passwords from commands by [@alexarchambault](https://github.com/alexarchambault) in [#1775](https://github.com/VirtusLab/scala-cli/pull/1775)\n* Add extra class path to generated bootstrap launcher by [@lwronski](https://github.com/lwronski) in [#1897](https://github.com/VirtusLab/scala-cli/pull/1897)\n\n\n#### SIP-related changes\n* Add 'dependency' and 'dependencies' alias for using directive by [@MaciejG604](https://github.com/MaciejG604) in [#1903](https://github.com/VirtusLab/scala-cli/pull/1903)\n\n\n#### Documentation updates\n* Ensure no console-syntax in reference docs and no `md` fenced blocks in `--help` by [@Gedochao](https://github.com/Gedochao) in [#1874](https://github.com/VirtusLab/scala-cli/pull/1874)\n* Document export subcommand by [@MaciejG604](https://github.com/MaciejG604) in [#1875](https://github.com/VirtusLab/scala-cli/pull/1875)\n* Tweak guides' and cookbooks' pages by [@Gedochao](https://github.com/Gedochao) in [#1894](https://github.com/VirtusLab/scala-cli/pull/1894)\n* Fix pgp creation option name by [@MaciejG604](https://github.com/MaciejG604) in [#1909](https://github.com/VirtusLab/scala-cli/pull/1909)\n* Fix using directive docs by [@lwronski](https://github.com/lwronski) in [#1901](https://github.com/VirtusLab/scala-cli/pull/1901)\n* Add docs to classifiers and exclude dependency by [@lwronski](https://github.com/lwronski) in [#1892](https://github.com/VirtusLab/scala-cli/pull/1892)\n\n\n#### Internal changes\n* Fix handling for `experimental` features by [@Gedochao](https://github.com/Gedochao) in [#1915](https://github.com/VirtusLab/scala-cli/pull/1915)\n* Change default home directory for tests integration and docs-test modules to avoid overriding global user config by [@lwronski](https://github.com/lwronski) in [#1917](https://github.com/VirtusLab/scala-cli/pull/1917)\n* NIT Use enums for help groups and help command groups by [@Gedochao](https://github.com/Gedochao) in [#1880](https://github.com/VirtusLab/scala-cli/pull/1880)\n\n\n#### Updates & maintenance\n* Bump dns-packet from 5.3.1 to 5.4.0 in /website by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1906](https://github.com/VirtusLab/scala-cli/pull/1906)\n* Bump VirtusLab/scala-cli-setup from 0.1.20 to 0.2.0 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1890](https://github.com/VirtusLab/scala-cli/pull/1890)\n* Dump docusaurus to 2.3.1 and other docs deps by [@lwronski](https://github.com/lwronski) in [#1907](https://github.com/VirtusLab/scala-cli/pull/1907)\n* Update scala-cli.sh launcher for 0.2.0 by [@github-actions](https://github.com/features/actions) in [#1881](https://github.com/VirtusLab/scala-cli/pull/1881)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#1911](https://github.com/VirtusLab/scala-cli/pull/1911)\n\n\n## New Contributors\n* [@danielleontiev](https://github.com/danielleontiev) made their first contribution in [#1904](https://github.com/VirtusLab/scala-cli/pull/1904)\n\n## [v0.2.0](https://github.com/VirtusLab/scala-cli/releases/tag/v0.2.0)\n\n### Require the `--power` option for restricted features by default\n\nUntil now, Scala CLI has been limiting some of its functionalities in its `scala` distribution.\nStarting with `v0.2.0`, those limitation will be applied to all distributions, including `scala-cli`.\n\nThis was done in order to make the behaviour consistent with Scala CLI acting as the Scala runner.\n\nRestricted features can be accessed by using the `--power` launcher flag. Do note that launcher flags have to be passed **before** the sub-command.\n```bash ignore\nscala-cli --power package .\n```\nAlternatively, the `power` mode can be turned on globally by running:\n\n```bash ignore\nscala-cli config power true \n```\n\nPlease note that this change may affect your existing scripts or workflows that rely on the limited commands from ScalaCLI (such as `package`, `publish`). You can still use those commands with `power` mode enabled.\n\nWhen you try to use a limited command in restricted mode, you will now see a warning message with suggestions on how to enable this command:\n\n\n```bash ignore\n$ scala-cli package Hello.scala\n# This command is restricted and requires setting the `--power` option to be used.\n# You can pass it explicitly or set it globally by running:\n#    scala-cli config power true\n$ scala-cli config power true\n$ scala-cli package Hello.scala\n# Wrote Hello, run it with\n#   ./Hello\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1835](https://github.com/VirtusLab/scala-cli/pull/1835) and [#1849](https://github.com/VirtusLab/scala-cli/pull/1849)\n\n### Allow executable Scala scripts without a file extension\n\nAs of this release Scala scripts without the `*.sc` file extension will be supported for execution when using the `shebang` command.\n\n```scala title=hello\n#!/usr/bin/env -S scala-cli shebang -S 3\n\nprintln(args.size)\nprintln(args.headOption)\n```\n```bash\nchmod +x hello\n./hello Hello World\n#2\n#Some(Hello)\n ```\nNote that files with no extension are always run as scripts even though they may contain e.g. a valid `.scala` program.\n\nAlso, do note that this feature has only been added for `shebang` - the `run` sub-command (which is the default way of running inputs when a sub-command is not specified explicitly) will not support this.\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#1802](https://github.com/VirtusLab/scala-cli/pull/1802)\n\n### Export Project configuration to Json\n\nIt is now possible to export configuration from Scala CLI project to Json format with the `export` sub-command.\n\n```bash ignore\nscala-cli --power export --json .\n```\n\nIt is currently exporting basic information about the project and includes, for example, the following fields:\n \n- ScalaVersion \n- Platform\n- Sources\n- Dependencies\n- Resolvers\n\n\nExample of generated Json output:\n```json\n{\n  \"scalaVersion\": \"3.2.2\",\n  \"platform\": \"JVM\",\n  \"scopes\": {\n    \"main\": {\n      \"sources\": [\n        \"Hello.scala\"\n      ],\n      \"dependencies\": [\n        {\n          \"groupId\": \"com.lihaoyi\",\n          \"artifactId\": {\n            \"name\": \"pprint\",\n            \"fullName\": \"pprint_3\"\n          },\n          \"version\": \"0.6.6\"\n        }\n      ],\n      ...\n    }\n  }\n}\n```\n\nAdded by [@MaciejG604](https://github.com/MaciejG604) in [#1840](https://github.com/VirtusLab/scala-cli/pull/1840)\n\n### Rename `using lib` to `using dep`\n\nTo be more consistent with dependency command line options `--dep`, the dependency using directive is now passed by `using dep`.\nPlease note that we have kept the alias of the old directive (`lib`, `libs`) for backwards compatibility.\n\n```scala compile\n //> using dep \"org.scalameta::munit:0.7.29\"\n```\nRenamed by [@lwronski](https://github.com/lwronski) in [#1827](https://github.com/VirtusLab/scala-cli/pull/1827)\n\n### Other breaking changes\n\n#### Remove ammonite imports support\nThe support for `$ivy` and `$dep` ammonite imports has been removed. \nTo easily convert existing `$ivy` and `$dep` imports into the `using dep` directive in your sources, you can use the provided actionable diagnostic.\n\n![convert_ivy_to_using_dep](/img/gifs/convert_ivy_to_using.gif)\n\nRemoved by [@MaciejG604](https://github.com/MaciejG604) in [#1787](https://github.com/VirtusLab/scala-cli/pull/1787)\n\n#### Drop the `metabrowse` sub-command\n\nWith this release, support for Metabrowse has been removed from Scala CLI. This change was made in order to limit the number of features that we need to support, especially since the `Metabrowse` project is no longer being actively worked on.\n\nRemove by [@lwronski](https://github.com/lwronski) in [#1867](https://github.com/VirtusLab/scala-cli/pull/1867)\n\n### Other changes\n\n* Add cross-platform toolkit dependency by [@bishabosha](https://github.com/bishabosha) in [#1810](https://github.com/VirtusLab/scala-cli/pull/1810)\n* Show explain message when is enabled by [@lwronski](https://github.com/lwronski) in [#1830](https://github.com/VirtusLab/scala-cli/pull/1830)\n* Read home directory from env variable instead of option from command line by [@lwronski](https://github.com/lwronski) in [#1842](https://github.com/VirtusLab/scala-cli/pull/1842)\n* Add build/taskStart and taskFinish to the exception reporting BSP mechanism by [@MaciejG604](https://github.com/MaciejG604) in [#1821](https://github.com/VirtusLab/scala-cli/pull/1821)\n* blooprifle: report exit code in exception by [@Flowdalic](https://github.com/flowdalic) in [#1844](https://github.com/VirtusLab/scala-cli/pull/1844)\n* Suppress lib update warning by [@MaciejG604](https://github.com/MaciejG604) in [#1848](https://github.com/VirtusLab/scala-cli/pull/1848)\n* Invalid subcommand arg by [@MaciejG604](https://github.com/MaciejG604) in [#1811](https://github.com/VirtusLab/scala-cli/pull/1811)\n\n\n#### SIP-related changes\n* Add a warning for the `-run` option of the legacy `scala` runner, instead of failing by [@Gedochao](https://github.com/Gedochao) in [#1801](https://github.com/VirtusLab/scala-cli/pull/1801)\n* Add warnings for the deprecated `-Yscriptrunner` legacy `scala` runner option instead of passing it to `scalac` by [@Gedochao](https://github.com/Gedochao) in [#1804](https://github.com/VirtusLab/scala-cli/pull/1804)\n* Filter out `restricted` & `experimental` options from `SIP` mode help by [@Gedochao](https://github.com/Gedochao) in [#1812](https://github.com/VirtusLab/scala-cli/pull/1812)\n* Warn in sip mode when using restricted command by [@lwronski](https://github.com/lwronski) in [#1862](https://github.com/VirtusLab/scala-cli/pull/1862)\n* Add more detail for sub-commands' help messages by [@Gedochao](https://github.com/Gedochao) in [#1852](https://github.com/VirtusLab/scala-cli/pull/1852)\n* Fix printing not supported option in restricted mode by [@lwronski](https://github.com/lwronski) in [#1861](https://github.com/VirtusLab/scala-cli/pull/1861)\n* Shorter options help by [@Gedochao](https://github.com/Gedochao) in [#1872](https://github.com/VirtusLab/scala-cli/pull/1872)\n\n#### Fixes\n\n* Fix warning about using directives in multiple files when two java files are present by [@MaciejG604](https://github.com/MaciejG604) in [#1796](https://github.com/VirtusLab/scala-cli/pull/1796)\n* Quit flag not suppresses compilation errors by [@lwronski](https://github.com/lwronski) in [#1792](https://github.com/VirtusLab/scala-cli/pull/1792)\n* Dont warn about target directives by [@MaciejG604](https://github.com/MaciejG604) in [#1803](https://github.com/VirtusLab/scala-cli/pull/1803)\n* Fix - actionable actions not suggest update to previous version by [@lwronski](https://github.com/lwronski) in [#1813](https://github.com/VirtusLab/scala-cli/pull/1813)\n* Fix actionable action when uses latest sytanx version in lib by [@lwronski](https://github.com/lwronski) in [#1817](https://github.com/VirtusLab/scala-cli/pull/1817)\n* Prevent NPE from being thrown by the `export` sub-command if `testFramework` isn't defined by [@Gedochao](https://github.com/Gedochao) in [#1814](https://github.com/VirtusLab/scala-cli/pull/1814)\n* Fix message checking in test by [@MaciejG604](https://github.com/MaciejG604) in [#1847](https://github.com/VirtusLab/scala-cli/pull/1847)\n* blooprifle: add -XX:+IgnoreUnrecognizedVMOptions to hardCodedDefaultJavaOpts by [@Flowdalic](https://github.com/flowdalic) in [#1845](https://github.com/VirtusLab/scala-cli/pull/1845)\n* Trim passwords obtained as command result by [@MaciejG604](https://github.com/MaciejG604) in [#1871](https://github.com/VirtusLab/scala-cli/pull/1871)\n\n#### Build and internal changes\n* Ignore Bloop server early exit if it signals an already running server by [@alexarchambault](https://github.com/alexarchambault) in [#1799](https://github.com/VirtusLab/scala-cli/pull/1799)\n* Build aarch64 linux launcher using m1 by [@lwronski](https://github.com/lwronski) in [#1805](https://github.com/VirtusLab/scala-cli/pull/1805)\n* Remove latest supported scala version mechanism by [@lwronski](https://github.com/lwronski) in [#1816](https://github.com/VirtusLab/scala-cli/pull/1816)\n* Switch `scala-cli-signing` to `org.virtuslab` and bump to `0.1.15` by [@Gedochao](https://github.com/Gedochao) in [#1853](https://github.com/VirtusLab/scala-cli/pull/1853)\n* Add clang to scala-cli docker image by [@lwronski](https://github.com/lwronski) in [#1846](https://github.com/VirtusLab/scala-cli/pull/1846)\n* bloop-file: show timeout value in error message by [@Flowdalic](https://github.com/flowdalic) in [#1855](https://github.com/VirtusLab/scala-cli/pull/1855)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#1860](https://github.com/VirtusLab/scala-cli/pull/1860)\n* Run generate reference doc as non sip by [@lwronski](https://github.com/lwronski) in [#1866](https://github.com/VirtusLab/scala-cli/pull/1866)\n* Bump `case-app` to `2.1.0-M23` by [@lwronski](https://github.com/lwronski) in [#1868](https://github.com/VirtusLab/scala-cli/pull/1868)\n\n#### Documentation updates\n* Update docker example command by [@MaciejG604](https://github.com/MaciejG604) in [#1798](https://github.com/VirtusLab/scala-cli/pull/1798)\n* Tweak `--watch`/`--restart` disambiguation in the help messages & docs by [@Gedochao](https://github.com/Gedochao) in [#1819](https://github.com/VirtusLab/scala-cli/pull/1819)\n* Release notes - msi malware analysis by [@lwronski](https://github.com/lwronski) in [#1832](https://github.com/VirtusLab/scala-cli/pull/1832)\n* Improve 'shebang' help message wrt program *arguments* by [@Flowdalic](https://github.com/flowdalic) in [#1829](https://github.com/VirtusLab/scala-cli/pull/1829)\n* docs: Fix Yum manual installation step by [@tgodzik](https://github.com/tgodzik) in [#1850](https://github.com/VirtusLab/scala-cli/pull/1850)\n\n#### Updates & maintenance\n* Update scala-cli.sh launcher for 0.1.20 by [@github-actions](https://github.com/features/actions) in [#1790](https://github.com/VirtusLab/scala-cli/pull/1790)\n* Bump VirtusLab/scala-cli-setup from 0.1.19 to 0.1.20 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1806](https://github.com/VirtusLab/scala-cli/pull/1806)\n\n## New Contributors\n* [@Flowdalic](https://github.com/flowdalic) made their first contribution in [#1829](https://github.com/VirtusLab/scala-cli/pull/1829)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.20...v0.2.0\n\n## [v0.1.20](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.20)\n\n### Add support for Scala Toolkit\nScala CLI now has support for [Scala Toolkit](https://virtuslab.com/blog/scala-toolkit-makes-scala-powerful-straight-out-of-the-box/).\n\nScala Toolkit is an ongoing effort by [Scala Center](https://scala.epfl.ch/) and [VirtusLab](https://www.virtuslab.com/) \nto compose a set of approachable libraries to solve everyday problems.\n\nIt is currently in its pre-release phase and includes the following libraries:\n- [MUnit](https://github.com/scalameta/munit) for testing;\n- [Sttp](https://github.com/softwaremill/sttp) for HTTP client;\n- [UPickle/UJson](https://github.com/com-lihaoyi/upickle) for reading, writing and operating on JSONs;\n- [OS-Lib](https://github.com/com-lihaoyi/os-lib) for operating on files and the operating system.\n\nYou can add it to your Scala CLI build from the command line with the `--with-toolkit` option.\n\n```bash ignore\nscala-cli . --with-toolkit latest\n```\n\nThere's also an appropriate `using` directive.\n\n```scala compile\n//> using toolkit \"0.1.6\"\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1768](https://github.com/VirtusLab/scala-cli/pull/1768)\n\n### Scala CLI is built with Scala `3.2.2`\nWe now rely on Scala `3.2.2` as the default internal Scala version used to build the project.\n\nAdded by [@lwronski](https://github.com/lwronski) and [@Gedochao](https://github.com/Gedochao) in [#1772](https://github.com/VirtusLab/scala-cli/pull/1772)\n\n### Removal of the `about` and `doctor` sub-commands\nThe `about` command has been removed, its features merged back to the `version` command.\nAs a result, the `version` command will now check if your locally installed Scala CLI is up-to-date.\nIt is possible to skip the check with the `--offline` option, or when printing raw CLI or default Scala\nversions with `--cli-version` and `--scala-version`, respectively.\n\n```bash\nscala-cli version --offline                     \n# Scala CLI version: 0.1.20\n# Scala version (default): 3.2.2\n```\n\nSimilarly, the `doctor` sub-command has been removed, with its past and previously planned functionalities to be delivered\nin a more interactive manner in the future.\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1744](https://github.com/VirtusLab/scala-cli/pull/1744)\n\n### The Scala CLI `aarch64/arm64` binary is now available via `sdkman`\nYou can now get the platform-appropriate Scala CLI binary on `aarch64/arm64` architecture via `sdkman`.\n\nAdded by [@mkurz](https://github.com/mkurz) in [#1748](https://github.com/VirtusLab/scala-cli/pull/1748)\n\n### `aarch64/arm64` artifact with the launcher script\nThe `scala-cli.sh` launcher script now correctly downloads the `aarch64/arm64` artifact on the appropriate architecture.\n\nAdded by [@mkurz](https://github.com/mkurz) in [#1745](https://github.com/VirtusLab/scala-cli/pull/1745)\n\n### Run a `.jar` file as a regular input\nJARs can now be run just like any other input, without the need of passing the `-cp` option.\n```bash ignore\nscala-cli Hello.jar\n# Hello\n```\nAdded by [@lwronski](https://github.com/lwronski) in [#1738](https://github.com/VirtusLab/scala-cli/pull/1738)\n\n### Java properties without the need for `--java-prop`\nThe `--java-prop` option can be skipped when passing Java properties to Scala CLI now.\n```bash ignore\nscala-cli Hello.scala -Dfoo=bar\n```\nAdded by [@lwronski](https://github.com/lwronski) in [#1739](https://github.com/VirtusLab/scala-cli/pull/1739)\n\n### Docker packaging with `using` directives\nIt is now possible to configure packaging into a docker image via `using` directives.\n```scala compile power\n//> using packaging.dockerFrom \"openjdk:11\"\n//> using packaging.dockerImageTag \"1.0.0\"\n//> using packaging.dockerImageRegistry \"virtuslab\"\n//> using packaging.dockerImageRepository \"scala-cli\"\n```\nAdded by [@lwronski](https://github.com/lwronski) in [#1753](https://github.com/VirtusLab/scala-cli/pull/1753)\n\n### Pass GraalVM args via a `using` directive\nIt is now possible to pass args to GraalVM via the following `using` directive:\n```scala compile power\n//> using packaging.graalvmArgs \"--no-fallback\", \"--enable-url-protocols=http,https\"\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1767](https://github.com/VirtusLab/scala-cli/pull/1767)\n\n### Other changes\n\n#### SIP-related changes\n* Remove irrelevant options from `version` help message by [@lwronski](https://github.com/lwronski) in [#1737](https://github.com/VirtusLab/scala-cli/pull/1737)\n* Include launcher options in the help for the default and `help` sub-commands by [@Gedochao](https://github.com/Gedochao) in [#1725](https://github.com/VirtusLab/scala-cli/pull/1725)\n* Remove suffix `.aux` from progName when installed by cs by [@lwronski](https://github.com/lwronski) in [#1736](https://github.com/VirtusLab/scala-cli/pull/1736)\n* Don't fail in case of connection errors in the version sub-command by [@Gedochao](https://github.com/Gedochao) in [#1760](https://github.com/VirtusLab/scala-cli/pull/1760)\n* Set workspace dir to `os.tmp.dir` for virtual sources by [@lwronski](https://github.com/lwronski) in [#1771](https://github.com/VirtusLab/scala-cli/pull/1771)\n* Add support for deprecated Scala `2.13.x`-specific `scala` runner options by [@Gedochao](https://github.com/Gedochao) in [#1774](https://github.com/VirtusLab/scala-cli/pull/1774)\n* Add support for the `-with-compiler` runner option by [@Gedochao](https://github.com/Gedochao) in [#1780](https://github.com/VirtusLab/scala-cli/pull/1780)\n\n#### Fixes\n* Take into account interactively picked options when caching binaries by [@alexarchambault](https://github.com/alexarchambault) in [#1701](https://github.com/VirtusLab/scala-cli/pull/1701)\n* Erase things in working dir in publish by [@alexarchambault](https://github.com/alexarchambault) in [#1715](https://github.com/VirtusLab/scala-cli/pull/1715)\n* Improve formatting of generated Mill project by [@lolgab](https://github.com/lolgab) in [#1677](https://github.com/VirtusLab/scala-cli/pull/1677)\n* Restart Bloop server if it exited by [@alexarchambault](https://github.com/alexarchambault) in [#1716](https://github.com/VirtusLab/scala-cli/pull/1716)\n* Add a global configuration for suppressing the warning about directives in multiple files by [@MaciejG604](https://github.com/MaciejG604) in [#1779](https://github.com/VirtusLab/scala-cli/pull/1779)\n* Add CLI option for suppressing the warning about directives in multiple files by [@MaciejG604](https://github.com/MaciejG604) in [#1754](https://github.com/VirtusLab/scala-cli/pull/1754)\n* Set page size for aarch64 Linux binaries to 64k by [@mkurz](https://github.com/mkurz) in [#1726](https://github.com/VirtusLab/scala-cli/pull/1726)\n\n#### Build and internal changes\n* Tweaking by [@alexarchambault](https://github.com/alexarchambault) in [#1711](https://github.com/VirtusLab/scala-cli/pull/1711)\n* Address some native-image warnings by [@alexarchambault](https://github.com/alexarchambault) in [#1719](https://github.com/VirtusLab/scala-cli/pull/1719)\n* Do not generate Linux aarch64 binaries from PRs by [@alexarchambault](https://github.com/alexarchambault) in [#1720](https://github.com/VirtusLab/scala-cli/pull/1720)\n* Derive using directives handlers from case classes by [@alexarchambault](https://github.com/alexarchambault) in [#1637](https://github.com/VirtusLab/scala-cli/pull/1637)\n* Don't run commands upon HelpTests instantiation by [@alexarchambault](https://github.com/alexarchambault) in [#1762](https://github.com/VirtusLab/scala-cli/pull/1762)\n\n#### Documentation updates\n* Add test-only cookbook by [@lwronski](https://github.com/lwronski) in [#1718](https://github.com/VirtusLab/scala-cli/pull/1718)\n* Fixing minor typos and some wordsmithing. by [@medale](https://github.com/medale) in [#1731](https://github.com/VirtusLab/scala-cli/pull/1731)\n* Back port of documentation changes to main by [@github-actions](https://github.com/features/actions) in [#1735](https://github.com/VirtusLab/scala-cli/pull/1735)\n* Explain the differences in using shebang vs scala-cli directly in script by [@lwronski](https://github.com/lwronski) in [#1740](https://github.com/VirtusLab/scala-cli/pull/1740)\n* Add instruction for Intellij JVM version by [@MaciejG604](https://github.com/MaciejG604) in [#1773](https://github.com/VirtusLab/scala-cli/pull/1773)\n* Fix a broken link by [@xerial](https://github.com/xerial) and [@lwronski](https://github.com/lwronski) in [#1777](https://github.com/VirtusLab/scala-cli/pull/1777)\n\n#### Updates & maintenance\n* Update svm to 22.3.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1689](https://github.com/VirtusLab/scala-cli/pull/1689)\n* Update scala-cli.sh launcher for 0.1.19 by [@github-actions](https://github.com/features/actions) in [#1707](https://github.com/VirtusLab/scala-cli/pull/1707)\n* Bump VirtusLab/scala-cli-setup from 0.1.18 to 0.1.19 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1709](https://github.com/VirtusLab/scala-cli/pull/1709)\n* Update Bloop to 1.5.6-sc-1 by [@lwronski](https://github.com/lwronski) in [#1704](https://github.com/VirtusLab/scala-cli/pull/1704)\n* Update trees_2.13 to 4.7.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1717](https://github.com/VirtusLab/scala-cli/pull/1717)\n* Update coursier-jvm_2.13, ... to 2.1.0-RC4 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1723](https://github.com/VirtusLab/scala-cli/pull/1723)\n* Bump uraimo/run-on-arch-action from 2.3.0 to 2.5.0 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1734](https://github.com/VirtusLab/scala-cli/pull/1734)\n* Update jsoniter-scala-core_2.13, ... to 2.20.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1732](https://github.com/VirtusLab/scala-cli/pull/1732)\n* Update jsoniter-scala-core_2.13, ... to 2.20.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1741](https://github.com/VirtusLab/scala-cli/pull/1741)\n* Update scalafmt-cli_2.13, scalafmt-core to 3.6.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1742](https://github.com/VirtusLab/scala-cli/pull/1742)\n* Update core_2.13 to 3.8.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1746](https://github.com/VirtusLab/scala-cli/pull/1746)\n* Update libdaemon to 0.0.11 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1759](https://github.com/VirtusLab/scala-cli/pull/1759)\n* Update jsoniter-scala-core_2.13, ... to 2.20.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1757](https://github.com/VirtusLab/scala-cli/pull/1757)\n* Update core_2.13 to 3.8.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1758](https://github.com/VirtusLab/scala-cli/pull/1758)\n* Update bloop core to 1.5.6-sc-2 by [@lwronski](https://github.com/lwronski) in [#1761](https://github.com/VirtusLab/scala-cli/pull/1761)\n* Update core_2.13 to 3.8.8 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1770](https://github.com/VirtusLab/scala-cli/pull/1770)\n* Update ammonite to 2.5.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1769](https://github.com/VirtusLab/scala-cli/pull/1769)\n* Update jsoniter-scala-core_2.13, ... to 2.20.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1776](https://github.com/VirtusLab/scala-cli/pull/1776)\n* Update amm to 2.5.6-1-f8bff243 by [@lwronski](https://github.com/lwronski) in [#1778](https://github.com/VirtusLab/scala-cli/pull/1778)\n\n### New Contributors\n* [@mkurz](https://github.com/mkurz) made their first contribution in [#1726](https://github.com/VirtusLab/scala-cli/pull/1726)\n* [@medale](https://github.com/medale) made their first contribution in [#1731](https://github.com/VirtusLab/scala-cli/pull/1731)\n* [@MaciejG604](https://github.com/MaciejG604) made their first contribution in [#1773](https://github.com/VirtusLab/scala-cli/pull/1773)\n* [@xerial](https://github.com/xerial) made their first contribution in [#1777](https://github.com/VirtusLab/scala-cli/pull/1777)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.19...v0.1.20\n\n## [v0.1.19](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.19)\n\n### The Linux `aarch64` native launcher is here! (experimental)\n\nWe are happy to announce that there is a new dedicated launcher for the Linux Aarch64. You can find it [here](https://github.com/VirtusLab/scala-cli/releases/download/v0.1.19/scala-cli-aarch64-pc-linux.gz).\n\nAdded in [#1703](https://github.com/VirtusLab/scala-cli/pull/1703) by [@lwronski](https://github.com/lwronski)\n\n### Fix `workspace/reload` for Intellij IDEA\n\nDependencies (and other configurations) from `using` directives should now always be picked up after a BSP project reload.\n\n<ReactPlayer playing controls url='https://user-images.githubusercontent.com/18601388/207319736-534f2d8a-862d-4c0a-8c8a-e52d95ac03e6.mov' />\n\nFixed by [@Gedochao](https://github.com/Gedochao) in [#1681](https://github.com/VirtusLab/scala-cli/pull/1681).\n\n###  `shebang` headers in Markdown\n\nThe `shebang` headers in `scala` code blocks inside a markdown input are always ignored.\n\n````markdown\n# Scala with `shebang`\nA sample code block with the `shebang` header.\n```scala\n#!/usr/bin/env -S scala-cli shebang\nprintln(\"Hello world\")\n```\n````\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1647](https://github.com/VirtusLab/scala-cli/pull/1647)\n\n### Export Scala compiler plugins to Mill projects\nIt is now possible to export `scalac` compiler plugins from a Scala CLI project to Mill with the `export` sub-command.\n\nAdded by [@carlosedp](https://github.com/carlosedp) in [#1626](https://github.com/VirtusLab/scala-cli/pull/1626)\n\n### Other changes\n\n#### SIP Changes\n* Fix the order of help command groups for the default help by [@Gedochao](https://github.com/Gedochao) in [#1697](https://github.com/VirtusLab/scala-cli/pull/1697)\n* Adjust SIP help output & ensure `ScalaSipTests` are run on Windows by [@Gedochao](https://github.com/Gedochao) in [#1695](https://github.com/VirtusLab/scala-cli/pull/1695)\n* Add warnings for `-save` & `-nosave` legacy `scala` runner options instead of failing by [@Gedochao](https://github.com/Gedochao) in [#1679](https://github.com/VirtusLab/scala-cli/pull/1679)\n\n#### Fixes\n* Suggest to update only to stable version by [@lwronski](https://github.com/lwronski) in [#1634](https://github.com/VirtusLab/scala-cli/pull/1634)\n* Fix - Skip checking file order by [@lwronski](https://github.com/lwronski) in [#1696](https://github.com/VirtusLab/scala-cli/pull/1696)\n* fix if else in mill.bat by [@MFujarewicz](https://github.com/MFujarewicz) in [#1661](https://github.com/VirtusLab/scala-cli/pull/1661)\n* Add repositories from build options when validating scala versions by [@lwronski](https://github.com/lwronski) in [#1630](https://github.com/VirtusLab/scala-cli/pull/1630)\n* Fix using directives not working with the shebang line in `.scala` files by [@Gedochao](https://github.com/Gedochao) in [#1639](https://github.com/VirtusLab/scala-cli/pull/1639)\n* Don't clear compilation output dir by [@clutroth](https://github.com/clutroth) in [#1660](https://github.com/VirtusLab/scala-cli/pull/1660)\n\n#### Documentation updates\n\n* Decompose the README & add a contributing guide by [@Gedochao](https://github.com/Gedochao) in [#1650](https://github.com/VirtusLab/scala-cli/pull/1650)\n* Improve IDE support docs by [@Gedochao](https://github.com/Gedochao) in [#1684](https://github.com/VirtusLab/scala-cli/pull/1684)\n\n\n#### Build and internal changes\n* Use snapshot repo to download stubs by [@lwronski](https://github.com/lwronski) in [#1693](https://github.com/VirtusLab/scala-cli/pull/1693)\n* Temporarily rollback CI to `ubuntu-20.04` by [@Gedochao](https://github.com/Gedochao) in [#1640](https://github.com/VirtusLab/scala-cli/pull/1640)\n* Fix - merge extra repos with resolve.repositories by [@lwronski](https://github.com/lwronski) in [#1643](https://github.com/VirtusLab/scala-cli/pull/1643)\n* Use Mill directory convention in mill project by [@lolgab](https://github.com/lolgab) in [#1676](https://github.com/VirtusLab/scala-cli/pull/1676)\n\n\n#### Updates & maintenance\n* Update coursier-jvm_2.13, ... to 2.1.0-RC3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1688](https://github.com/VirtusLab/scala-cli/pull/1688)\n* Update coursier-jvm_2.13, ... to 2.1.0-RC3-1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1702](https://github.com/VirtusLab/scala-cli/pull/1702)\n* Update slf4j-nop to 2.0.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1691](https://github.com/VirtusLab/scala-cli/pull/1691)\n* Ignore `jsoniter` updates for JDK 8 by [@lwronski](https://github.com/lwronski) in [#1694](https://github.com/VirtusLab/scala-cli/pull/1694)\n* Update trees_2.13 to 4.7.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1690](https://github.com/VirtusLab/scala-cli/pull/1690)\n* Update jsoniter-scala-core_2.13 to 2.19.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1674](https://github.com/VirtusLab/scala-cli/pull/1674)\n* Update jsoniter-scala-core_2.13 to 2.19.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1672](https://github.com/VirtusLab/scala-cli/pull/1672)\n* Update os-lib to 0.9.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1653](https://github.com/VirtusLab/scala-cli/pull/1653)\n* Update scala-collection-compat to 2.9.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1657](https://github.com/VirtusLab/scala-cli/pull/1657)\n* Update core_2.13 to 3.8.5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1655](https://github.com/VirtusLab/scala-cli/pull/1655)\n* Update pprint to 0.8.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1654](https://github.com/VirtusLab/scala-cli/pull/1654)\n* Update mill-main to 0.10.10 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1652](https://github.com/VirtusLab/scala-cli/pull/1652)\n* Update org.eclipse.jgit to 6.4.0.202211300538-r by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1656](https://github.com/VirtusLab/scala-cli/pull/1656)\n* Update jsoniter-scala-core_2.13 to 2.18.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1651](https://github.com/VirtusLab/scala-cli/pull/1651)\n* Update slf4j-nop to 2.0.5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1658](https://github.com/VirtusLab/scala-cli/pull/1658)\n* Bump VirtusLab/scala-cli-setup from 0.1.17 to 0.1.18 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1644](https://github.com/VirtusLab/scala-cli/pull/1644)\n* Update scala-cli.sh launcher for 0.1.18 by [@github-actions](https://github.com/features/actions) in [#1624](https://github.com/VirtusLab/scala-cli/pull/1624)\n* Update using_directives to 0.0.10 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1692](https://github.com/VirtusLab/scala-cli/pull/1692)\n* Bumped up com.lihaoyi::os-lib version to 0.9.0 by [@pingu1m](https://github.com/scala-steward-org/pingu1m) in [#1649](https://github.com/VirtusLab/scala-cli/pull/1649)\n\n### New Contributors\n* [@pingu1m](https://github.com/pingu1m) made their first contribution in [#1649](https://github.com/VirtusLab/scala-cli/pull/1649)\n* [@clutroth](https://github.com/clutroth) made their first contribution in [#1660](https://github.com/VirtusLab/scala-cli/pull/1660)\n* [@MFujarewicz](https://github.com/MFujarewicz) made their first contribution in [#1661](https://github.com/VirtusLab/scala-cli/pull/1661)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.18...v0.1.19\n\n## [v0.1.18](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.18)\n\n### Filter tests with `--test-only`\nIt is now possible to filter test suites with the `--test-only` option.\n\n```scala title=BarTests.scala\n//> using dep \"org.scalameta::munit::1.0.0-M7\"\npackage tests.only\nclass Tests extends munit.FunSuite {\n  test(\"bar\") {\n    assert(2 + 2 == 5)\n  }\n  test(\"foo\") {\n    assert(2 + 3 == 5)\n  }\n  test(\"foo-again\") {\n    assert(2 + 3 == 5)\n  }\n}\n```\n\n```scala title=HelloTests.scala\npackage tests\nclass HelloTests extends munit.FunSuite {\n  test(\"hello\") {\n    assert(2 + 2 == 4)\n  }\n}\n```\n\n```bash fail\nscala-cli test BarTests.scala HelloTests.scala --test-only 'tests.only*' \n# tests.only.Tests:\n# ==> X tests.only.Tests.bar  0.037s munit.FailException: ~/project/src/test/BarTests.scala:5 assertion failed\n# 4:  test(\"bar\") {\n# 5:    assert(2 + 2 == 5)\n# 6:  }\n#     at munit.FunSuite.assert(FunSuite.scala:11)\n#     at tests.only.Tests.$init$$$anonfun$1(BarTests.scala:5)\n#     at tests.only.Tests.$init$$$anonfun$adapted$1(BarTests.scala:6)\n#   + foo 0.004s\n#   + foo-again 0.001s\n```\n\nFiltering particular tests by name requires passing args to the test framework.\nFor example, with `munit`:\n\n```bash\nscala-cli test BarTests.scala HelloTests.scala --test-only 'tests.only*'  -- '*foo*'\n# tests.only.Tests:\n#   + foo 0.032s\n#   + foo-again 0.001s\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1604](https://github.com/VirtusLab/scala-cli/pull/1604)\n\n### Accept authenticated proxy params via Scala CLI config\nIf you can only download artifacts through an authenticated proxy, it is now possible to configure it\nwith the `config` subcommand.\n\n```bash ignore\nscala-cli config httpProxy.address https://proxy.company.com\nscala-cli config httpProxy.user _encoded_user_\nscala-cli config httpProxy.password _encoded_password_\n```\n\nReplace `_encoded_user_` and `_encoded_password_` by your actual user and password, following\nthe [password option format](/docs/reference/password-options.md). They should typically look like\n`env:ENV_VAR_NAME`, `file:/path/to/file`, or `command:command to run`.\n\nAdded by [@alexarchambault](https://github.com/alexarchambault) in [#1593](https://github.com/VirtusLab/scala-cli/pull/1593)\n\n### Support for running Markdown sources from zipped archives and gists\nIt is now possible to run `.md` sources inside a `.zip` archive.\nSame as with directories,  `.md` sources inside zipped archives are ignored by default, unless\nthe `--enable-markdown` option is passed.\n\n```bash ignore\nscala-cli archive-with-markdown.zip --enable-markdown\n```\n\nThis also enables running Markdown sources fom GitHub gists, as those are downloaded by Scala CLI as zipped archives.\n\n```bash ignore\nscala-cli https://gist.github.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839 --enable-markdown\n```\n\nIt is also possible to point Scala CLI to a `.md` file with a direct URL.\n\n```bash ignore\nscala-cli https://gist.githubusercontent.com/Gedochao/6415211eeb8ca4d8d6db123f83f0f839/raw/4c5ce7593e19f1390555221e0d076f4b02f4b4fd/example.md\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1581](https://github.com/VirtusLab/scala-cli/pull/1581)\n\n### Support for running piped Markdown sources\nInstead of passing paths to your Markdown sources, you can also pipe your code via standard input:\n\n```bash\necho '# Example Snippet\n```scala\nprintln(\"Hello\")\n```' | scala-cli _.md\n```\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1582](https://github.com/VirtusLab/scala-cli/pull/1582)\n\n### Support for running Markdown snippets\nIt is now possible to pass Markdown code as a snippet directly from the command line.\n\n````bash\nscala-cli run --markdown-snippet '# Markdown snippet\nwith a code block\n```scala\nprintln(\"Hello\")\n```'\n````\n\nAdded by [@Gedochao](https://github.com/Gedochao) in [#1583](https://github.com/VirtusLab/scala-cli/pull/1583)\n\n### Customize exported Mill project name\nIt is now possible to pass the desired name of your Mill project to the `export` sub-command \nwith the `--project` option. \n\n```bash ignore\nscala-cli export . --mill -o mill-proj --project project-name\n```\n\nAdded by [@carlosedp](https://github.com/carlosedp) in [#1563](https://github.com/VirtusLab/scala-cli/pull/1563)\n\n### Export Scala compiler options to Mill projects\nIt is now possible to export `scalac` options from a Scala CLI project to Mill with the `export` sub-command.\n\nAdded by [@lolgab](https://github.com/lolgab) in [#1562](https://github.com/VirtusLab/scala-cli/pull/1562)\n\n### Other changes\n\n#### Fixes\n* Fix overriding settings from tests by [@alexarchambault](https://github.com/alexarchambault) in [#1566](https://github.com/VirtusLab/scala-cli/pull/1566)\n* Print compilation failed in watch mode too in test command by [@alexarchambault](https://github.com/alexarchambault) in [#1548](https://github.com/VirtusLab/scala-cli/pull/1548)\n* Fix error message when running JVM launcher from Java 8 by [@alexarchambault](https://github.com/alexarchambault) in [#1575](https://github.com/VirtusLab/scala-cli/pull/1575)\n* Fix `using` directives for Markdown inputs by [@Gedochao](https://github.com/Gedochao) in [#1598](https://github.com/VirtusLab/scala-cli/pull/1598)\n* Fix - clean up only homebrew-scala-experimental directory by [@lwronski](https://github.com/lwronski) in [#1615](https://github.com/VirtusLab/scala-cli/pull/1615)\n* Warn users when pushing to Sonatype with missing credentials or params by [@alexarchambault](https://github.com/alexarchambault) in [#1545](https://github.com/VirtusLab/scala-cli/pull/1545)\n* Warning for multiple files with using directives by [@wleczny](https://github.com/wleczny) in [#1591](https://github.com/VirtusLab/scala-cli/pull/1591)\n* Make package --python work by [@alexarchambault](https://github.com/alexarchambault) in [#1531](https://github.com/VirtusLab/scala-cli/pull/1531)\n* Better revolver output by [@alexarchambault](https://github.com/alexarchambault) in [#1614](https://github.com/VirtusLab/scala-cli/pull/1614)\n* Make `PackageTestsDefault.reuse run native binary` more robust by [@lwronski](https://github.com/lwronski) in [1621](https://github.com/VirtusLab/scala-cli/pull/1621)\n\n#### Documentation updates\n* Add some explanations on implicit sub-commands in `-help` by [@Gedochao](https://github.com/Gedochao) in [#1587](https://github.com/VirtusLab/scala-cli/pull/1587)\n* Runner specification by [@romanowski](https://github.com/romanowski) in [#1445](https://github.com/VirtusLab/scala-cli/pull/1445)\n* Install documentation update by [@wleczny](https://github.com/wleczny) in [#1595](https://github.com/VirtusLab/scala-cli/pull/1595)\n* Document recent features & changes affecting working with Markdown inputs  by [@Gedochao](https://github.com/Gedochao) in [#1606](https://github.com/VirtusLab/scala-cli/pull/1606)\n* Improve docs coverage with `sclicheck` by [@Gedochao](https://github.com/Gedochao) in [#1612](https://github.com/VirtusLab/scala-cli/pull/1612)\n* Reduce ignore tags in the docs snippets by [@Gedochao](https://github.com/Gedochao) in [#1617](https://github.com/VirtusLab/scala-cli/pull/1617)\n\n#### Build and internal changes\n* Remove superfluous annotation by [@alexarchambault](https://github.com/alexarchambault) in [#1567](https://github.com/VirtusLab/scala-cli/pull/1567)\n* Decompose & refactor `Inputs` by [@Gedochao](https://github.com/Gedochao) in [#1565](https://github.com/VirtusLab/scala-cli/pull/1565)\n* Disable create PGP key test on Windows CI by [@alexarchambault](https://github.com/alexarchambault) in [#1588](https://github.com/VirtusLab/scala-cli/pull/1588)\n* Switch to Scala 3-based case-app by [@alexarchambault](https://github.com/alexarchambault) in [#1568](https://github.com/VirtusLab/scala-cli/pull/1568)\n* Remove cli-options module by [@alexarchambault](https://github.com/alexarchambault) in [#1552](https://github.com/VirtusLab/scala-cli/pull/1552)\n* Enable to force using jvm signing launcher for native launcher of scala-cli by [@lwronski](https://github.com/lwronski) in [#1597](https://github.com/VirtusLab/scala-cli/pull/1597)\n* Run warm up test before running default tests by [@lwronski](https://github.com/lwronski) in [#1599](https://github.com/VirtusLab/scala-cli/pull/1599)\n* Make DefaultTests more robust by [@alexarchambault](https://github.com/alexarchambault) in [#1613](https://github.com/VirtusLab/scala-cli/pull/1613)\n\n#### Updates & maintenance\n* Update scala-cli.sh launcher for 0.1.17 by [@github-actions](https://github.com/features/actions) in [#1564](https://github.com/VirtusLab/scala-cli/pull/1564)\n* Update zip-input-stream to 0.1.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1573](https://github.com/VirtusLab/scala-cli/pull/1573)\n* Update coursier-jvm_2.13, ... to 2.1.0-RC1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1572](https://github.com/VirtusLab/scala-cli/pull/1572)\n* Update mill-main to 0.10.9 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1571](https://github.com/VirtusLab/scala-cli/pull/1571)\n* Update test-runner, tools to 0.4.8 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1574](https://github.com/VirtusLab/scala-cli/pull/1574)\n* Update case-app_2.13 to 2.1.0-M21 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1570](https://github.com/VirtusLab/scala-cli/pull/1570)\n* Bump VirtusLab/scala-cli-setup from 0.1.16 to 0.1.17 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1579](https://github.com/VirtusLab/scala-cli/pull/1579)\n* Bump Ammonite to 2.5.5-17-df243e14 & Scala to 3.2.1 by [@Gedochao](https://github.com/Gedochao) in [#1586](https://github.com/VirtusLab/scala-cli/pull/1586)\n* Update scala-cli-signing to 0.1.13 by [@alexarchambault](https://github.com/alexarchambault) in [#1569](https://github.com/VirtusLab/scala-cli/pull/1569)\n* Update coursier-jvm_2.13, ... to 2.1.0-RC2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1590](https://github.com/VirtusLab/scala-cli/pull/1590)\n* Update scalajs-sbt-test-adapter_2.13 to 1.11.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1477](https://github.com/VirtusLab/scala-cli/pull/1477)\n* Update slf4j-nop to 2.0.4 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1596](https://github.com/VirtusLab/scala-cli/pull/1596)\n* Update jsoniter-scala-core_2.13 to 2.18.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1608](https://github.com/VirtusLab/scala-cli/pull/1608)\n* Update test-runner, tools to 0.4.9 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1610](https://github.com/VirtusLab/scala-cli/pull/1610)\n* Update Bloop to 1.5.4-sc-4 by [@alexarchambault](https://github.com/alexarchambault) in [#1622](https://github.com/VirtusLab/scala-cli/pull/1622)\n\n### New Contributors\n* [@carlosedp](https://github.com/carlosedp) made their first contribution in [#1563](https://github.com/VirtusLab/scala-cli/pull/1563)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.17...v0.1.18\n\n## [v0.1.17](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.17)\n\n### SDKMAN and Homebrew support installation of Scala CLI for M1\n\nTo install Scala CLI via SDKMAN, run the following command from the command line:\n```\nsdk install scalacli\n```\n\nand to install Scala CLI via homebrew:\n```\nbrew install Virtuslab/scala-cli/scala-cli\n```\n\nAdded by [@wleczny](https://github.com/wleczny) in [#1505](https://github.com/VirtusLab/scala-cli/pull/1505) and [#1497](https://github.com/VirtusLab/scala-cli/pull/1497)\n\n\n### Specifying the `--jvm` option via using directives\n\nThe `--jvm` option can now be added via using directives, like\n```scala\n//> using jvm \"temurin:11\"\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1539](https://github.com/VirtusLab/scala-cli/pull/1539)\n\n### Accept more `scalac` options without escaping\n\nScala CLI now accepts options such as `-rewrite`, `-new-syntax`, `-old-syntax`, `-source:<target>`, `-indent` and `-no-indent`, without requiring them to be escaped by `-O`.\n\nFixed by [@Gedochao](https://github.com/Gedochao) in [#1501](https://github.com/VirtusLab/scala-cli/pull/1501)\n\n### Enable `python` support  via using directives\n\nThe `--python` option can now be enabled via a using directive, like\n```scala\n//> using python\n```\n\nAdded by [@alexarchambault](https://github.com/alexarchambault) in [#1492](https://github.com/VirtusLab/scala-cli/pull/1492)\n\n### Other changes\n\n#### Publish\n\n* Various config command tweaks / fixes by [@alexarchambault](https://github.com/alexarchambault)  in [#1460](https://github.com/VirtusLab/scala-cli/pull/1460)\n* Accept email via --email when creating a PGP key in config command by [@alexarchambault](https://github.com/alexarchambault)  in [#1482](https://github.com/VirtusLab/scala-cli/pull/1482)\n* Make publish --python work by [@alexarchambault](https://github.com/alexarchambault)  in [#1494](https://github.com/VirtusLab/scala-cli/pull/1494)\n* Add repositories.credentials config key by [@alexarchambault](https://github.com/alexarchambault)  in [#1466](https://github.com/VirtusLab/scala-cli/pull/1466)\n* Check for missing org and version at the same time in publish by [@alexarchambault](https://github.com/alexarchambault)  in [#1534](https://github.com/VirtusLab/scala-cli/pull/1534)\n* Rename some publish config keys by [@alexarchambault](https://github.com/alexarchambault)  in [#1532](https://github.com/VirtusLab/scala-cli/pull/1532)\n* Add publish.credentials config key, use it to publish by [@alexarchambault](https://github.com/alexarchambault)  in [#1533](https://github.com/VirtusLab/scala-cli/pull/1533)\n\n#### Spark\n\n* Accept spark-submit arguments on the command-line by [@alexarchambault](https://github.com/alexarchambault)  in [#1455](https://github.com/VirtusLab/scala-cli/pull/1455)\n\n#### Fixes\n\n* Fix generating pkg package for M1 by [@lwronski](https://github.com/lwronski) in [#1461](https://github.com/VirtusLab/scala-cli/pull/1461)\n* Return exit code 1 when build fails for test by [@lwronski](https://github.com/lwronski) in [#1518](https://github.com/VirtusLab/scala-cli/pull/1518)\n* Fix the `nativeEmbedResources` using directive by [@Gedochao](https://github.com/Gedochao) in [#1525](https://github.com/VirtusLab/scala-cli/pull/1525)\n\n#### Build and internal changes\n\n* Automate deploy of scala-experimental brew formula by [@wleczny](https://github.com/wleczny) in [#1530](https://github.com/VirtusLab/scala-cli/pull/1530)\n* Decompose RunTestDefinitions by [@Gedochao](https://github.com/Gedochao) in [#1529](https://github.com/VirtusLab/scala-cli/pull/1529)\n* Add some simple tests for running `.md` sources by [@Gedochao](https://github.com/Gedochao) in [#1527](https://github.com/VirtusLab/scala-cli/pull/1527)\n* Run doc tests from munit test suites by [@alexarchambault](https://github.com/alexarchambault)  in [#1435](https://github.com/VirtusLab/scala-cli/pull/1435)\n* Minor refacto around build options stuff by [@alexarchambault](https://github.com/alexarchambault)  in [#1488](https://github.com/VirtusLab/scala-cli/pull/1488)\n* No need to use os.ProcessOutput.ReadLines in test by [@alexarchambault](https://github.com/alexarchambault)  in [#1491](https://github.com/VirtusLab/scala-cli/pull/1491)\n* Enforce logging options for all scala commands by [@Gedochao](https://github.com/Gedochao) in [#1499](https://github.com/VirtusLab/scala-cli/pull/1499)\n* Tweak documentation verification tests by [@Gedochao](https://github.com/Gedochao) in [#1504](https://github.com/VirtusLab/scala-cli/pull/1504)\n* Support `jvmRunEnvironment` and `jvmTestEnvironment` for BSP by [@Gedochao](https://github.com/Gedochao) in [#1519](https://github.com/VirtusLab/scala-cli/pull/1519)\n* Downgrade Scala version in 'scala-cli repl --amm' if needed by [@alexarchambault](https://github.com/alexarchambault)  [#1493](https://github.com/VirtusLab/scala-cli/pull/1493)\n\n#### Documentation / help updates\n* Tweak / fix publish messages by [@alexarchambault](https://github.com/alexarchambault)  in [#1535](https://github.com/VirtusLab/scala-cli/pull/1535)\n* Merge documentation of installing scala-cli on MacOs and MacOs/M1 by [@wleczny](https://github.com/wleczny) in [#1507](https://github.com/VirtusLab/scala-cli/pull/1507)\n* Improve the basics doc by [@Gedochao](https://github.com/Gedochao) in [#1513](https://github.com/VirtusLab/scala-cli/pull/1513)\n* Fix a typo in the `--server` option reference doc by [@Gedochao](https://github.com/Gedochao) in [#1521](https://github.com/VirtusLab/scala-cli/pull/1521)\n* Improve the docs on using Scala compiler options by [@Gedochao](https://github.com/Gedochao) in [#1503](https://github.com/VirtusLab/scala-cli/pull/1503)\n* Add help for repl, scalafmt and scaladoc by [@wleczny](https://github.com/wleczny) in [#1487](https://github.com/VirtusLab/scala-cli/pull/1487)\n* remove paragraph about bug for coursier install by [@bishabosha](https://github.com/bishabosha) in [#1485](https://github.com/VirtusLab/scala-cli/pull/1485)\n* Tell about pressing Enter in watch message by [@alexarchambault](https://github.com/alexarchambault)  in [#1465](https://github.com/VirtusLab/scala-cli/pull/1465)\n\n\n#### Updates / maintainance\n* Update jsoniter-scala-core_2.13 to 2.17.9 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1544](https://github.com/VirtusLab/scala-cli/pull/1544)\n* Bump docusaurus to 2.20 and other docs deps by [@lwronski](https://github.com/lwronski) in [#1540](https://github.com/VirtusLab/scala-cli/pull/1540)\n* Update jsoniter-scala-core_2.13 to 2.17.8 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1537](https://github.com/VirtusLab/scala-cli/pull/1537)\n* Update cli-options_2.13, cli_2.13, ... to 0.1.11 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1538](https://github.com/VirtusLab/scala-cli/pull/1538)\n* Update case-app_2.13 to 2.1.0-M19 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1536](https://github.com/VirtusLab/scala-cli/pull/1536)\n* Bump coursier/setup-action from 1.2.1 to 1.3.0 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1496](https://github.com/VirtusLab/scala-cli/pull/1496)\n* Update scala-cli.sh launcher for 0.1.16 by [@github-actions](https://github.com/features/actions) in [#1458](https://github.com/VirtusLab/scala-cli/pull/1458)\n* Bump VirtusLab/scala-cli-setup from 0.1.15 to 0.1.16 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1462](https://github.com/VirtusLab/scala-cli/pull/1462)\n* Update expecty to 0.16.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1467](https://github.com/VirtusLab/scala-cli/pull/1467)\n* Update jsoniter-scala-core_2.13 to 2.17.5 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1470](https://github.com/VirtusLab/scala-cli/pull/1470)\n* Update mill-main to 0.10.8 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1472](https://github.com/VirtusLab/scala-cli/pull/1472)\n* Update pprint to 0.8.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1473](https://github.com/VirtusLab/scala-cli/pull/1473)\n* Update core_2.13 to 3.8.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1474](https://github.com/VirtusLab/scala-cli/pull/1474)\n* Update publish_2.13 to 0.1.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1475](https://github.com/VirtusLab/scala-cli/pull/1475)\n* Update trees_2.13 to 4.6.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1478](https://github.com/VirtusLab/scala-cli/pull/1478)\n* Update slf4j-nop to 2.0.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1479](https://github.com/VirtusLab/scala-cli/pull/1479)\n* Update asm to 9.4 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1476](https://github.com/VirtusLab/scala-cli/pull/1476)\n* Update using_directives to 0.0.9 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1480](https://github.com/VirtusLab/scala-cli/pull/1480)\n* Update fansi to 0.4.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1471](https://github.com/VirtusLab/scala-cli/pull/1471)\n* Update case-app_2.13 to 2.1.0-M18 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1468](https://github.com/VirtusLab/scala-cli/pull/1468)\n* Bump webfactory/ssh-agent from 0.5.4 to 0.7.0 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1495](https://github.com/VirtusLab/scala-cli/pull/1495)\n* Update jsoniter-scala-core_2.13 to 2.17.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1498](https://github.com/VirtusLab/scala-cli/pull/1498)\n* Update coursier to 2.1.0-M7-39-gb8f3d7532 by [@alexarchambault](https://github.com/alexarchambault)  in [#1520](https://github.com/VirtusLab/scala-cli/pull/1520)\n\n### New Contributors\n* [@bishabosha](https://github.com/bishabosha) made their first contribution in [#1485](https://github.com/VirtusLab/scala-cli/pull/1485)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.16...v0.1.17\n\n## [v0.1.16](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.16)\n\nThis release consists mainly of updates, fixes, and various enhancements of existing features.\n\n### Specifying javac options via using directives\n\njavac options can now be added via using directives, like\n```scala\n//> using javacOpt \"source\", \"1.8\", \"target\", \"1.8\"\n```\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1438](https://github.com/VirtusLab/scala-cli/pull/1438)\n\n### Pressing enter in watch mode proceeds to run / compile / test / … again\n\nIn watch mode (using the `-w` or `--watch` option), pressing Enter when Scala CLI is watching for changes makes it run again what it's supposed to be doing (compiling, running, running tests, or packaging, etc.) This is inspired by Mill's behaviour in watch mode, which supports the same feature.\n\nAdded by [@alexarchambault](https://github.com/alexarchambault) in [#1451](https://github.com/VirtusLab/scala-cli/pull/1451)\n\n### Installation via Scoop on Windows\n\nScala CLI can now be installed via [Scoop](https://scoop.sh) on Windows, with a command such as\n```bat\nscoop install scala-cli\n```\n\nAdded by [@nightscape](https://github.com/nightscape) in [#1416](https://github.com/VirtusLab/scala-cli/pull/1416), thanks to him!\n\n### Actionable diagnostics in Metals\n\nScala CLI should now send text edit suggestions with some of its diagnostics, via BSP, so that editors\ncan suggest those edits to users. This should work in upcoming versions of Metals in particular.\n\nAdded by [@lwronski](https://github.com/lwronski) in [#1448](https://github.com/VirtusLab/scala-cli/pull/1448)\n\n### Other\n\n* Add `--scalapy-version` option by [@alexarchambault](https://github.com/alexarchambault) in [#1397](https://github.com/VirtusLab/scala-cli/pull/1397)\n\n#### Fixes\n\n#### Fixes in Scala Native binaries caching\n\nWhen running a sequence of commands such as\n```bash ignore\n$ scala-cli run --native .\n$ scala-cli --power package --native . -o my-app\n```\nScala CLI should cache a Scala Native binary during the first command, so that the second command can just re-use it, rather than generating a binary again. This also fixes the re-use of compilation artifacts between both commands, so that the Scala CLI project isn't re-compiled during the second command either.\n\nFixed by [@alexarchambault](https://github.com/alexarchambault) in [#1406](https://github.com/VirtusLab/scala-cli/pull/1406)\n\n##### Accept more scalac options without escaping\n\nScala CLI now accepts options such as `-release`, `-encoding`, `-color`, `-feature`, `-deprecation` and `-nowarn`, without requiring them to be escaped by `-O`. It also accepts `--scalac-verbose`, which is equivalent to `-O -verbose` (increases scalac verbosity). Lastly, it warns when `-release` and / or `-target:<target>` are inconsistent with `--jvm`.\n\nFixed by [@Gedochao](https://github.com/Gedochao) in [#1413](https://github.com/VirtusLab/scala-cli/pull/1413)\n\n##### Fix `--java-option` and `--javac-option` handling in `package` sub-command\n\n`--java-option` and `--javac-option` should now be accepted and handled properly in the `package` sub-command.\n\nFixed by [@lwronski](https://github.com/lwronski) in [#1434](https://github.com/VirtusLab/scala-cli/pull/1434)\n\n##### Fix wrong file name when publising Scala.js artifacts locally\n\nThe `publish local` sub-command used to publish Scala.js artifacts with a malformed suffix. This is now fixed.\n\nFixed by [@lwronski](https://github.com/lwronski) in [#1443](https://github.com/VirtusLab/scala-cli/pull/1443)\n\n##### Fix spurious stack traces in the `publish` and `publish local` sub-commands\n\nThe `publish` and `publish local` commands could print spurious stack traces when run with non-default locales, using native Scala CLI binaries. This is now fixed.\n\nFixed by [@romanowski](https://github.com/romanowski) in [#1423](https://github.com/VirtusLab/scala-cli/pull/1423)\n\n##### Make `run --python --native`  work from Python virtualenv\n\nUsing both `--native` and `--python` in the `run` sub-command should work fine from Python virtualenv.\n\nFixed by [@kiendang](https://github.com/kiendang) in [#1399](https://github.com/VirtusLab/scala-cli/pull/1399)\n\n#### Documentation / help updates\n* Dump scala 2 version in docs by [@lwronski](https://github.com/lwronski) in [#1408](https://github.com/VirtusLab/scala-cli/pull/1408)\n* Ensure the the `repl` & default sub-commands respect group help options by [@Gedochao](https://github.com/Gedochao) in [#1417](https://github.com/VirtusLab/scala-cli/pull/1417)\n* Remove stray `_` typo by [@armanbilge](https://github.com/armanbilge) in [#1385](https://github.com/VirtusLab/scala-cli/pull/1385)\n* Add docs on how to install scala-cli for M1 by [@lwronski](https://github.com/lwronski) in [#1431](https://github.com/VirtusLab/scala-cli/pull/1431)\n* Debugging cookbook by [@wleczny](https://github.com/wleczny) in [#1441](https://github.com/VirtusLab/scala-cli/pull/1441)\n\n#### Updates / maintainance\n* Update scala-cli.sh launcher for 0.1.15 by [@github-actions](https://github.com/features/actions) in [#1401](https://github.com/VirtusLab/scala-cli/pull/1401)\n* Revert scalafmt fix by [@lwronski](https://github.com/lwronski) in [#1402](https://github.com/VirtusLab/scala-cli/pull/1402)\n* Bump respective Scala versions to `2.12.17` & `2.13.9` and Ammonite to `2.5.4-33-0af04a5b` by [@Gedochao](https://github.com/Gedochao) in [#1405](https://github.com/VirtusLab/scala-cli/pull/1405)\n* Turn off running tests in PR for M1 runner by [@lwronski](https://github.com/lwronski) in [#1403](https://github.com/VirtusLab/scala-cli/pull/1403)\n* Bump VirtusLab/scala-cli-setup from 0.1.14.1 to 0.1.15 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1414](https://github.com/VirtusLab/scala-cli/pull/1414)\n* Bump coursier/setup-action from f883d08305acbc28e5e5363bf5ec086397627021 to 1.2.1 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1415](https://github.com/VirtusLab/scala-cli/pull/1415)\n* Tweak the release procedure by [@Gedochao](https://github.com/Gedochao) in [#1426](https://github.com/VirtusLab/scala-cli/pull/1426)\n* Update case-app_2.13 to 2.1.0-M17 & scala-cli-signing to v0.1.10 by [@lwronski](https://github.com/lwronski) in [#1427](https://github.com/VirtusLab/scala-cli/pull/1427)\n* Automate choco package deploy by [@wleczny](https://github.com/wleczny) in [#1412](https://github.com/VirtusLab/scala-cli/pull/1412)\n* Generate pkg package for m1 by [@lwronski](https://github.com/lwronski) in [#1410](https://github.com/VirtusLab/scala-cli/pull/1410)\n* Re-enable gif tests by [@alexarchambault](https://github.com/alexarchambault) in [#1436](https://github.com/VirtusLab/scala-cli/pull/1436)\n* Bump Scala 2.13.x to 2.13.10 & Ammonite to 2.5.5 by [@Gedochao](https://github.com/Gedochao) in [#1437](https://github.com/VirtusLab/scala-cli/pull/1437)\n* Remove mill-scala-cli stuff from build by [@alexarchambault](https://github.com/alexarchambault) in [#1433](https://github.com/VirtusLab/scala-cli/pull/1433)\n* Add support for BSP's `buildTarget/outputPaths` and update bsp4j to 2… by [@lwronski](https://github.com/lwronski) in [#1439](https://github.com/VirtusLab/scala-cli/pull/1439)\n* Update bsp4j to 2.1.0-M3 by [@lwronski](https://github.com/lwronski) in [#1444](https://github.com/VirtusLab/scala-cli/pull/1444)\n* Update scala-packager to 0.1.29 and hardcode upgradeCodeGuid by [@lwronski](https://github.com/lwronski) in [#1446](https://github.com/VirtusLab/scala-cli/pull/1446)\n* Refactor `ScalaCommand` to enforce respecting help options by [@Gedochao](https://github.com/Gedochao) in [#1440](https://github.com/VirtusLab/scala-cli/pull/1440)\n* Address compilation warnings by [@alexarchambault](https://github.com/alexarchambault) in [#1452](https://github.com/VirtusLab/scala-cli/pull/1452)\n* Update coursier to 2.1.0-M7 by [@alexarchambault](https://github.com/alexarchambault) in [#1447](https://github.com/VirtusLab/scala-cli/pull/1447)\n* Update bloop to 1.5.4-sc-3 by [@alexarchambault](https://github.com/alexarchambault) in [#1454](https://github.com/VirtusLab/scala-cli/pull/1454)\n\n### New Contributors\n* [@nightscape](https://github.com/nightscape) made their first contribution in [#1416](https://github.com/VirtusLab/scala-cli/pull/1416)\n* [@kiendang](https://github.com/kiendang) made their first contribution in [#1399](https://github.com/VirtusLab/scala-cli/pull/1399)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.15...v0.1.16\n\n## [v0.1.15](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.15)\n\n### The M1 native launcher is here! (experimental)\n\nWe are happy to announce that there is a new dedicated launcher for M1 users. You can find it [here](https://github.com/VirtusLab/scala-cli/releases/download/v0.1.15/scala-cli-aarch64-apple-darwin.gz).\n\nPlease note that the `package` sub-command is unstable for this launcher.\n\nAdded in [#1396](https://github.com/VirtusLab/scala-cli/pull/1396) by [@lwronski](https://github.com/lwronski)\n\n### `--python` option for `repl` sub-command (experimental)\n\nPassing the `--python` option allows using `ScalaPy` with the `repl` sub-command:\n\n```\n▶ scala-cli --python\nWelcome to Scala 3.2.0 (17.0.2, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n\nscala> import me.shadaj.scalapy.py\n\nscala> py.Dynamic.global.range(1, 4)\nval res0: me.shadaj.scalapy.py.Dynamic = range(1, 4)\n```\n\nAdded in [#1336](https://github.com/VirtusLab/scala-cli/pull/1336) by [@alexarchambault](https://github.com/alexarchambault)\n\n### `-d`, `-classpath` and `compile` sub-command's `--output` options changes\n\nTo be backward compatible with the `scala` command, some changes have been made to the following options:\n* The `compile` sub-command's  `--output` option has been renamed to `--compilation-output`. This option is now also available from the `run` and `package` sub-commands.\n\n```\n▶ scala-cli compile Hello.scala --compilation-output out\n▶ scala-cli --main-class Hello -classpath out\nHello\n```\n\n* The `-d` option is no longer an alias for `--dependency`, but for `--compilation-output`.\n  * `-O -d -O path/to/compilation/output` now defaults to `-d path/to/compilation/output`.\n\n```\n▶ scala-cli compile Hello.scala -d out\n▶ scala-cli --main-class Hello -classpath out\nHello\n```\n\n* The old `--classpath` option has been renamed to `--print-classpath`.\n  *  `--classpath`, `--class-path` and `-classpath` options are now aliases for the `--extra jars` option.\n  * `-O -classpath -O path/to/classpath` now defaults to `--extra-jars path/to/classpath`.\n\n```\n▶ scala-cli compile --print-classpath Hello.scala\n# ~/Projects/debug-test/.scala-build/project_103be31561_103be31561-7a1ed8dde0/classes/main:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.2.0/scala3-library_3-3.2.0.jar:~/Library/Caches/ScalaCli/local-repo/v0.1.15/org.virtuslab.scala-cli/runner_3/0.1.15/jars/runner_3.jar:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar\n```\n\nAdded in [#1340](https://github.com/VirtusLab/scala-cli/pull/1340) by [@Gedochao](https://github.com/Gedochao)\n\n### Make inputs optional when `-classpath` and `--main-class` are passed\n\nThe following changes have been made to improve backward compatibility with the `scala` command:\n* Passing the `--main-class` option along with `-classpath` to the default command now defaults to `run` instead of `repl`:\n\n```\n▶ scala-cli --main-class Hello -classpath out\nHello\n```\n\n* If the `run` sub-command is passed explicitly, it's sufficient to have a main class on the classpath (inputs aren't necessary then):\n\n```\n▶ scala-cli compile Hello.scala -d out\n▶ scala-cli run -classpath out\nHello\n```\n\nAdded in [#1369](https://github.com/VirtusLab/scala-cli/pull/1369) by [@Gedochao](https://github.com/Gedochao)\n\n### Debugging with the `run` and `test` sub-commands\n\nIt is now possible to debug code ran by `run` and `test` sub-commands:\n\n```\n▶ scala-cli Main.scala --debug\nListening for transport dt_socket at address: 5005\nHello\n```\n\nThis addresses [#1212](https://github.com/VirtusLab/scala-cli/issues/1212)\n\nAdded in [#1389](https://github.com/VirtusLab/scala-cli/pull/1389) by [@wleczny](https://github.com/wleczny)\n\n## `--platform` option\n\nThis option can be used to choose the platform, which should be used to compile and run the application.\n\n```\n▶ scala-cli Main.scala --platform js\nHello\n```\n\nNote that `--platform js` is an alias for `--js` and `--platform native` is an alias for `--native`.\n\nThis addresses [#1214](https://github.com/VirtusLab/scala-cli/issues/1214)\n\nAdded in [#1347](https://github.com/VirtusLab/scala-cli/pull/1347) by [@wleczny](https://github.com/wleczny)\n\n### Other changes\n\n#### Fixes\n\n* Ensure directories are created recursively when the `package` sub-command is called by [@Gedochao](https://github.com/Gedochao) in [#1371](https://github.com/VirtusLab/scala-cli/pull/1371)\n* Fix calculation of Scala version and turn off the `-release` flag for 2.12.x \\< 2.12.5 by [@Gedochao](https://github.com/Gedochao) in [#1377](https://github.com/VirtusLab/scala-cli/pull/1377)\n* Fix finding main classes in external jars by [@Gedochao](https://github.com/Gedochao) in [#1380](https://github.com/VirtusLab/scala-cli/pull/1380)\n* Fix Js split style SmallModulesFor in pure JVM by [@lwronski](https://github.com/lwronski) in [#1394](https://github.com/VirtusLab/scala-cli/pull/1394)\n\n#### Build and internal changes\n\n* Remove mill-scalafix customization by [@alexarchambault](https://github.com/alexarchambault) in [#1360](https://github.com/VirtusLab/scala-cli/pull/1360)\n* Split config db stuff to a separate config module by [@alexarchambault](https://github.com/alexarchambault) in [#1367](https://github.com/VirtusLab/scala-cli/pull/1367)\n* Detect sip when installed by coursier by [@lwronski](https://github.com/lwronski) in [#1368](https://github.com/VirtusLab/scala-cli/pull/1368)\n* Create empty class to enforce resolving ivy deps by mill for dummy modules by [@lwronski](https://github.com/lwronski) in [#1374](https://github.com/VirtusLab/scala-cli/pull/1374)\n* Use millw launcher instead of running mill by cs by [@lwronski](https://github.com/lwronski) in [#1375](https://github.com/VirtusLab/scala-cli/pull/1375)\n* Add --debug option for integration tests by [@wleczny](https://github.com/wleczny) in [#1378](https://github.com/VirtusLab/scala-cli/pull/1378)\n* NIT ScalaVersionUtil refactor by [@Gedochao](https://github.com/Gedochao) in [#1384](https://github.com/VirtusLab/scala-cli/pull/1384)\n* Make config module compatible with Java 8 by [@alexarchambault](https://github.com/alexarchambault) in [#1387](https://github.com/VirtusLab/scala-cli/pull/1387)\n* Add HTTP proxy-related keys in config module by [@alexarchambault](https://github.com/alexarchambault) in [#1388](https://github.com/VirtusLab/scala-cli/pull/1388)\n* Add repositories-related keys in config module by [@alexarchambault](https://github.com/alexarchambault) in [#1395](https://github.com/VirtusLab/scala-cli/pull/1395)\n\n#### Updates\n\n* Update scala-cli.sh launcher for 0.1.14 by [@github-actions](https://github.com/features/actions) in [#1362](https://github.com/VirtusLab/scala-cli/pull/1362)\n* Update jsoniter-scala-core_2.13 to 2.17.3 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1364](https://github.com/VirtusLab/scala-cli/pull/1364)\n* Update core_2.13 to 3.8.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1365](https://github.com/VirtusLab/scala-cli/pull/1365)\n* Bump VirtusLab/scala-cli-setup from 0.1.13 to 0.1.14.1 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1376](https://github.com/VirtusLab/scala-cli/pull/1376)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.14...v0.1.15\n\n## [v0.1.14](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.14)\n\n### Hotfix printing stacktraces from Scala CLI runner for Scala 3.x \\< 3.2.0\nWe fixed a nasty bug breaking any Scala CLI run using any Scala 3 version earlier than 3.2.0 on printing stacktraces.\nOnly Scala CLI 0.1.13 was affected.\n```\n$ scala-cli about\nScala CLI version: 0.1.13\nScala version (default): 3.2.0\n$ scala-cli -S 3.1.3 -e 'throw Exception(\"Broken\")'\nCompiling project (Scala 3.1.3, JVM)\nCompiled project (Scala 3.1.3, JVM)\nException in thread \"main\" java.lang.NoSuchMethodError: 'long scala.runtime.LazyVals$.getOffsetStatic(java.lang.reflect.Field)'\n        at scala.cli.runner.StackTracePrinter.<clinit>(StackTracePrinter.scala:101)\n        at scala.cli.runner.StackTracePrinter$.coloredStackTraces(StackTracePrinter.scala:104)\n        at scala.cli.runner.StackTracePrinter$.$lessinit$greater$default$4(StackTracePrinter.scala:11)\n        at scala.cli.runner.Runner$.main(Runner.scala:18)\n        at scala.cli.runner.Runner.main(Runner.scala)\n```\nAdded in [#1358](https://github.com/VirtusLab/scala-cli/pull/1358) by [@romanowski](https://github.com/romanowski)\n\n### Build and internal changes\n* Disable mill-scala-cli for now by [@alexarchambault](https://github.com/alexarchambault) in [#1335](https://github.com/VirtusLab/scala-cli/pull/1335)\n* Update scala-cli.sh launcher for 0.1.13 by [@github-actions](https://github.com/features/actions) in [#1351](https://github.com/VirtusLab/scala-cli/pull/1351)\n* Remove backslash which skip execution of `mv` command by [@lwronski](https://github.com/lwronski) in [#1353](https://github.com/VirtusLab/scala-cli/pull/1353)\n* Fix import ordering by [@alexarchambault](https://github.com/alexarchambault) in [#1359](https://github.com/VirtusLab/scala-cli/pull/1359)\n\n### Updates\n* Update scalafix stuff… by [@alexarchambault](https://github.com/alexarchambault) in [#1333](https://github.com/VirtusLab/scala-cli/pull/1333)\n* Bump VirtusLab/scala-cli-setup from 0.1.12 to 0.1.13 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1354](https://github.com/VirtusLab/scala-cli/pull/1354)\n\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.13...v0.1.14\n\n\n## [v0.1.13](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.13)\n\n### Change the default sub-command to `repl` when no args are passed\n\nWe no longer default to the `help` sub-command when no arguments are passed. Starting with `0.1.13` running  Scala CLI with no args will launch the `repl`.\n\n```\n$ scala-cli -S 3\nWelcome to Scala 3.1.3 (17.0.3, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n\nscala>\n```\n\nWhen inputs are provided, Scala CLI defaults to the `run` sub-command, as before.\n\n```\n$ cat hello.sc\nprintln(\"Hello World\")\n$ scala-cli hello.sc\nHello World\n```\n\nThis change was added by [@Gedochao](https://github.com/Gedochao) in [#1268](https://github.com/VirtusLab/scala-cli/pull/1268)\n\n### Marking the project's workspace root with the `project.settings.scala` file\n\nScala CLI now supports marking the workspace root directory with an optional configuration file: `project.settings.scala`.  The workspace root determines where the `.bsp` and `.scala-build` directories will be saved (which mostly affects what path should be opened in your IDE to import the Scala CLI project through BSP).\n\nThe settings file is also the recommended input for your project's `using directives`. Otherwise, it functions similarly to other `.scala` sources.\n\n```\n$ cat project.settings.scala\n//> using scala \"2.13.4\"\n$ cat hello.sc\nprintln(util.Properties.versionString)\n$ scala-cli hello.sc .\nversion 2.13.4\n```\n\nTo see how exactly is the root directory resolved, see [this document](https://github.com/VirtusLab/scala-cli/blob/932c942b78bc35fc0906f2f9e2f6a0c56bef712b/website/docs/reference/root-dir.md)\n\nAdded in [#1260](https://github.com/VirtusLab/scala-cli/pull/1260) by [@wleczny](https://github.com/wleczny)\n\n### Scala CLI is now built with Scala 3.2.0\n\nWe now rely on Scala `3.2.0` as the default internal Scala version used to build the project.\n\nThis change was added by [@lwronski](https://github.com/lwronski) in [#1314](https://github.com/VirtusLab/scala-cli/pull/1314)\n\n### Add resources support for Scala Native\n\nScala CLI now allows embedding resources (by default) in a Scala Native binary with the `--native` flag.\n\n```\n$ cat resources/scala-native/foo.c\nint foo(int i) {\n  return i + 42;\n}\n$ cat hello.scala\n//> using platform \"native\"\n//> using resourceDir \"resources\"\n\nimport scalanative.unsafe.*\n\n@extern\ndef foo(int: CInt): CInt = extern\n\n@main def main =\n  println(foo(3))\n$ scala-cli hello.scala --native\n45\n```\n\nAdded in [#812](https://github.com/VirtusLab/scala-cli/pull/812) by [@jchyb](https://github.com/jchyb)\n\n###  Default to the `run` sub-command instead of `repl` when the `-e`, `--execute-script`, `--execute-scala` or `--execute-java` options are passed.\n\nEven though we default to the `repl` sub-command when no arguments are passed to Scala CLI, an exception to that rule is when a snippet is passed with one of the following options: `-e`, `--execute-script`, `--execute-scala` or `--execute-java`. In that case, the passed snippets are treated as inputs to be executed and switch the default to the `run` sub-command.\n```\n$ scala-cli -e 'println(\"Hello\")'\nHello\n```\n\nIf you still want to pass a snippet to the `repl`, you can either pass the `repl` sub-command explicitly or use one of the following options, as before: `--script-snippet`, `--scala-snippet` or `--java-snippet`.\n```\n$ scala-cli --script-snippet 'println(\"Hello\")'\nWelcome to Scala 3.1.3 (17.0.2, Java OpenJDK 64-Bit Server VM).\nType in expressions for evaluation. Or try :help.\n\nscala> snippet_sc.main(Array.empty)\nHello\n```\nThis change was introduced to make the `-e` option backwards compatible with the `scala` command.\n\nAdded in [#1313](https://github.com/VirtusLab/scala-cli/pull/1313) by [@Gedochao](https://github.com/Gedochao)\n\n### Work in progress\n\n#### Support for Markdown (experimental)\n\nScala CLI can now accept `.md` inputs and run/compile a snippet of Scala code inside the markdown. Markdown sources are ignored by default unless passed explicitly as inputs. You can also enable including non-explicit `.md` inputs by passing the `--enable-markdown` option.\n\nPlain `scala` snippets are treated similarly to `.sc` scripts which can be run by `scala-cli`:\n\n````markdown\n$ cat Example.md\nThis is a simple example of an `.md` file with a Scala snippet.\n\n```scala\nval message = \"Hello from Markdown\"\nprintln(message)\n```\n````\n\n```\nscala-cli Example.md\nHello from Markdown\n```\n\nSee [this document](https://github.com/VirtusLab/scala-cli/blob/5f15ada41fbdcce9b9efd93bd63d513e3476a69a/website/docs/guides/markdown.md) for more details about the experimental Markdown support.\n\nAdded in [#1268](https://github.com/VirtusLab/scala-cli/pull/1268) by [@Gedochao](https://github.com/Gedochao)\n\n#### Add `--python` option for the `run` sub-command (experimental)\n\nThe `run` sub-command can now run ScalaPy when the `--python` option is passed.\n\n```\n$ cat helloscalapy.sc\nimport py.SeqConverters\nval len = py.Dynamic.global.len(List(0, 2, 3).toPythonProxy)\nprintln(s\"Length is $len\")\n$ scala-cli helloscalapy.sc --python -S 2.13\nLength is 3\n```\n\nAdded in [#1295](https://github.com/VirtusLab/scala-cli/pull/1295) by [@alexarchambault](https://github.com/alexarchambault)\n\n### Other changes\n\n#### Documentation\n\n* Correct using directives on configuration.md by [@megri](https://github.com/megri) in [#1278](https://github.com/VirtusLab/scala-cli/pull/1278)\n* Improve dependencies doc by [@Gedochao](https://github.com/Gedochao) in [#1287](https://github.com/VirtusLab/scala-cli/pull/1287)\n\n#### Fixes\n\n* Fix path to sourceMappingURL by [@lwronski](https://github.com/lwronski) in [#1286](https://github.com/VirtusLab/scala-cli/pull/1286)\n\n#### Build and internal changes\n\n* Improve the error message for when a build's main class is ambiguous by [@Gedochao](https://github.com/Gedochao) in [#1323](https://github.com/VirtusLab/scala-cli/pull/1323)\n* Improve the error message for unsupported Scala version with Ammonite by [@Gedochao](https://github.com/Gedochao) in [#1327](https://github.com/VirtusLab/scala-cli/pull/1327)\n* Detect ARM64 macs when downloading coursier launcher by [@keynmol](https://github.com/keynmol) in  [#1282](https://github.com/VirtusLab/scala-cli/pull/1282)\n* Make test(\"...\".only) work again in RunTestDefinitions by [alexarchambault](https://github.com/alexarchambault) in [#1294](https://github.com/VirtusLab/scala-cli/pull/1294)\n* Use os-lib short-hand method trim when possible by [alexarchambault](https://github.com/alexarchambault) in [#1334](https://github.com/VirtusLab/scala-cli/pull/1334)\n* Add missing repl tests by [alexarchambault](https://github.com/alexarchambault) in [#1332](https://github.com/VirtusLab/scala-cli/pull/1332)\n* Scala CLI deb package - Priority and Section flag by [@lwronski](https://github.com/lwronski) in [#1338](https://github.com/VirtusLab/scala-cli/pull/1338)\n\n#### Updates\n\n* Update ammonite to 2.5.4-16-7317286d by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1283](https://github.com/VirtusLab/scala-cli/pull/1283)\n* Update mill-main to 0.10.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1284](https://github.com/VirtusLab/scala-cli/pull/1284)\n* Update scalajs-env-nodejs_2.13 to 1.4.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1303](https://github.com/VirtusLab/scala-cli/pull/1303)\n* Update jsoniter-scala-core_2.13 to 2.16.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1302](https://github.com/VirtusLab/scala-cli/pull/1302)\n* Update core_2.13 to 3.7.6 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1299](https://github.com/VirtusLab/scala-cli/pull/1299)\n* Update ammonite to 2.5.4-19-cd76521f by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1298](https://github.com/VirtusLab/scala-cli/pull/1298)\n* Update bsp4j to 2.1.0-M1 by [@lwronski](https://github.com/lwronski) in [#1277](https://github.com/VirtusLab/scala-cli/pull/1277)\n* Bump VirtusLab/scala-cli-setup from 0.1.11 to 0.1.12 by [@dependabot](https://docs.github.com/en/code-security/dependabot) in [#1306](https://github.com/VirtusLab/scala-cli/pull/1306)\n* Update jsoniter-scala-core_2.13 to 2.17.0 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1311](https://github.com/VirtusLab/scala-cli/pull/1311)\n* Update test-runner, tools to 0.4.7 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1317](https://github.com/VirtusLab/scala-cli/pull/1317)\n* Update jsoniter-scala-core_2.13 to 2.17.1 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1320](https://github.com/VirtusLab/scala-cli/pull/1320)\n* Update ammonite_3.1.3 to 2.5.4-22-4a9e6989 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1329](https://github.com/VirtusLab/scala-cli/pull/1329)\n* Update jsoniter-scala-core_2.13 to 2.17.2 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1343](https://github.com/VirtusLab/scala-cli/pull/1343)\n* Update python-native-libs to 0.2.4 by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1341](https://github.com/VirtusLab/scala-cli/pull/1341)\n* Update org.eclipse.jgit to 6.3.0.202209071007-r by [@scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1344](https://github.com/VirtusLab/scala-cli/pull/1344)\n\n### New Contributors\n* [@megri](https://github.com/megri) made their first contribution in [#1278](https://github.com/VirtusLab/scala-cli/pull/1278)\n* [@keynmol](https://github.com/keynmol) made their first contribution in [#1282](https://github.com/VirtusLab/scala-cli/pull/1282)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.12...v0.1.13\n\n\n## [v0.1.12](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.12)\n\n### Add `--spark`, `--spark-standalone` and `--hadoop` options for the `run` sub-command\nThe `run` sub-command can now run Spark jobs when the `--spark` option is passed.\n```text\n$ scala-cli run --spark SparkJob.scala\n```\nSimilarly, it's possible to run Hadoop jobs by passing the `--hadoop` option.\n```text\nscala-cli run --hadoop HadoopJob.java\n```\nIt's also possible to run Spark jobs without a Spark distribution by passing the `--spark-standalone` option.\n```text\n$ scala-cli run --spark-standalone SparkJob.scala\n```\n\nAdded in [#1129](https://github.com/VirtusLab/scala-cli/pull/1129) by [alexarchambault](https://github.com/alexarchambault)\n\n### Add the default Scala version to the output of the `version` sub-command\nThe `version` sub-command now includes both the Scala CLI version and the default Scala version.\n```text\n$ scala-cli --version\nScala CLI version 0.1.12\nDefault Scala version: 3.1.3\n$ scala-cli -version\nScala CLI version 0.1.12\nDefault Scala version: 3.1.3\n$ scala-cli version\nScala CLI version 0.1.12\nDefault Scala version: 3.1.3\n```\nYou can also pass the `--cli-version` option to only get the Scala CLI version or the `--scala-version` option\nto only get the default Scala version.\n```text\n$ scala-cli version --cli-version\n0.1.12\n$ scala-cli version --scala-version\n3.1.3\n```\nThis is potentially a breaking change if your automation relies on the output of the `version` sub-command.\n\nAdded in [#1262](https://github.com/VirtusLab/scala-cli/pull/1262) by [lwronski](https://github.com/lwronski)\n\n### Enable passing the `scalafmt` configuration with `--scalafmt-conf` and `--scalafmt-conf-str`\nIt is now possible to pass a custom location of the `scalafmt` configuration with the `--scalafmt-conf` option for the\n`fmt` sub-command.\n```text\n$ scala-cli fmt --scalafmt-conf path/to/the/conf/.scalafmt.conf\n```\nYou can also pass the configuration straight from the terminal with `--scalafmt-conf-str`.\n```text\n$ scala-cli fmt --scalafmt-conf-str  \"version=3.5.5\nrunner.dialect=scala213\"\n```\nAdded in [#1227](https://github.com/VirtusLab/scala-cli/pull/1227) by [wleczny](https://github.com/wleczny)\n\n### Enable turning the `--interactive` mode on permanently\nIt is now possible to set the `--interactive` mode on by default, so that passing it explicitly isn't necessary.\n\nThe next time when you run a command with the `--interactive` option set to on, Scala CLI will suggest to turn it on\npermanently.\n\nThis is recommended for environments where `scala-cli` is used by a human user only (and not by any automation).\n\n```text\n$ scala-cli . --interactive\nYou have run the current scala-cli command with the --interactive mode turned on.\nWould you like to leave it on permanently?\n[0] Yes\n[1] No\n0\n--interactive is now set permanently. All future scala-cli commands will run with the flag set to true.\nIf you want to turn this setting off at any point, just run `scala-cli config interactive false`.\nFound several main classes. Which would you like to run?\n[0] ScalaMainClass2\n[1] ScalaMainClass1\n[2] scripts.ScalaScript_sc\n```\n\nYou can also configure it manually with the `config` sub-command, by setting the `interactive` property to `true`.\n```text\n$ scala-cli config interactive true\n```\nAdded in [#1238](https://github.com/VirtusLab/scala-cli/pull/1238) by [Gedochao](https://github.com/Gedochao)\n\n### Other changes\n\n#### Work in progress\n* Actionable diagnostics by [lwronski](https://github.com/lwronski) in [#1229](https://github.com/VirtusLab/scala-cli/pull/1229)\n\n#### [SIP-46](https://github.com/scala/improvement-proposals/pull/46)-related\n* Restrict directives based on the command used by [romanowski](https://github.com/romanowski) in [#1259](https://github.com/VirtusLab/scala-cli/pull/1259)\n\n#### Documentation\n* NIT Improve some website docs by [BlackAnubis7](https://github.com/BlackAnubis7) in [#1243](https://github.com/VirtusLab/scala-cli/pull/1243)\n\n#### Build and internal changes\n* Add 0.1.11 release notes to release_notes.md by [BlackAnubis7](https://github.com/BlackAnubis7) in [#1228](https://github.com/VirtusLab/scala-cli/pull/1228)\n* Temporary disable test gif by [lwronski](https://github.com/lwronski) in [#1261](https://github.com/VirtusLab/scala-cli/pull/1261)\n* aarch64 fixes by [alexarchambault](https://github.com/alexarchambault) in [#1180](https://github.com/VirtusLab/scala-cli/pull/1180)\n\n#### Updates\n* Update mill launcher by [alexarchambault](https://github.com/alexarchambault) in [#1269](https://github.com/VirtusLab/scala-cli/pull/1269)\n* Update scala-cli.sh launcher for 0.1.11 by [github-actions](https://github.com/features/actions) in [#1230](https://github.com/VirtusLab/scala-cli/pull/1230)\n* Update jsoniter-scala-core_2.13 to 2.13.39 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1239](https://github.com/VirtusLab/scala-cli/pull/1239)\n* Update trees_2.13 to 4.5.12 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1242](https://github.com/VirtusLab/scala-cli/pull/1242)\n* Update jsoniter-scala-core_2.13 to 2.14.2 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1241](https://github.com/VirtusLab/scala-cli/pull/1241)\n* Update org name to VirtusLab for downloading scalafmt-native-image by [lwronski](https://github.com/lwronski) in [#1253](https://github.com/VirtusLab/scala-cli/pull/1253)\n* Update core_2.13 to 3.7.4 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1247](https://github.com/VirtusLab/scala-cli/pull/1247)\n* Update case-app_2.13 to 2.1.0-M15 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1245](https://github.com/VirtusLab/scala-cli/pull/1245)\n* Update jsoniter-scala-core_2.13 to 2.15.0 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1246](https://github.com/VirtusLab/scala-cli/pull/1246)\n* Update cli-options_2.13, cli_2.13, ... to 0.1.8 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1248](https://github.com/VirtusLab/scala-cli/pull/1248)\n* Update metaconfig-typesafe-config to 0.11.1 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1254](https://github.com/VirtusLab/scala-cli/pull/1254)\n* Update ammonite to 2.5.4-14-dc4c47bc by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1255](https://github.com/VirtusLab/scala-cli/pull/1255)\n* Update coursier-jvm_2.13, ... to 2.1.0-M6-53-gb4f448130 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1256](https://github.com/VirtusLab/scala-cli/pull/1256)\n* Update scala-packager-cli_2.13, ... to 0.1.27 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1258](https://github.com/VirtusLab/scala-cli/pull/1258)\n* Update bloop-config_2.13 to 1.5.3-sc-1 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1257](https://github.com/VirtusLab/scala-cli/pull/1257)\n* Update ammonite to 2.5.4-15-f4a8969b by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1264](https://github.com/VirtusLab/scala-cli/pull/1264)\n* Update trees_2.13 to 4.5.13 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1265](https://github.com/VirtusLab/scala-cli/pull/1265)\n* Update slf4j-nop to 2.0.0 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1273](https://github.com/VirtusLab/scala-cli/pull/1273)\n* Update cli-options_2.13, cli_2.13, ... to 0.1.9 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1272](https://github.com/VirtusLab/scala-cli/pull/1272)\n* Bump VirtusLab/scala-cli-setup from 0.1.5 to 0.1.11 by [dependabot](https://docs.github.com/en/code-security/dependabot) in [#1274](https://github.com/VirtusLab/scala-cli/pull/1274)\n\n### New Contributors\n* [BlackAnubis7](https://github.com/BlackAnubis7) made their first contribution in [#1228](https://github.com/VirtusLab/scala-cli/pull/1228)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.11...v0.1.12\n\n## [v0.1.11](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.11)\n\n### Make `.scalafmt.conf` optional when running the `fmt` command\nScala CLI can now run the `fmt` command without a `.scalafmt.conf` file present. Previously, if such a file was absent, a `Scalafmt requires explicitly specified version.` error was raised while using the `fmt` command.\n\nThe Scala CLI `fmt` command now supports passing the `scalafmt` version and dialect directly from the command line, using the `--scalafmt-dialect` and `--scalafmt-version` options respectively:\n```\nscala-cli fmt --scalafmt-dialect scala3 --scalafmt-version 3.5.8\n```\nEither of those (or both) can be skipped, which will make Scala CLI infer a default value.\n\nThe configuration used can be saved in the workspace by passing the `--save-scalafmt-conf` option.\n\nAdded in [#1192](https://github.com/VirtusLab/scala-cli/pull/1192) by [wleczny](https://github.com/wleczny)\n\n### Define `output` option for `package` command with using directives\nIt is now possible to pass the `output` option of the `package` command with [using directives](guides/introduction/using-directives) instead of passing it directly from bash.\n\nAdded in [#1213](https://github.com/VirtusLab/scala-cli/pull/1213) by [wleczny](https://github.com/wleczny)\n\n### Add support for running multiple snippets of the same kind\nScala CLI now allows to pass multiple snippets of the same kind.\n\nIt was previously possible to mix different kinds (so to pass a Java snippet alongside a Scala one), but not for example 2 separate Scala snippets. That limitation no longer applies.\n\nWhen passed this way, each snippet is then treated as a separate input by Scala CLI.\n\n```text\n$ scala-cli --scala-snippet '@main def main() = println(Messages.hello)' --scala-snippet 'object Messages { def hello = \"Hello\" }'\nHello\n```\nAdded in [#1182](https://github.com/VirtusLab/scala-cli/pull/1182) by [Gedochao](https://github.com/Gedochao)\n\n### Add bloop sub-command\nScala CLI now has a (hidden for now) bloop sub-command, that runs a command using the Scala CLI Bloop server (while the mainline Bloop bloop CLI uses its default Bloop server). This is handy when debugging things on Scala CLI for example, allowing one to manually run scala-cli bloop projects or scala-cli bloop compile.\n\nAdded in [#1199](https://github.com/VirtusLab/scala-cli/pull/1199) by [alexarchambault](https://github.com/alexarchambault)\n\n### Make main class optional in preamble-less assemblies\nIt is now allowed to generate an assembly, even for code that has no main class, when `--preamble=false` is passed. This can be useful for libraries, if users want to pass the assembly to tools such as proguard. This also accepts a (hidden) `--main-class-in-manifest=false` option if users want not only no preamble, but also no mention of main class in the assembly manifest (`META-INF/MANIFEST.MF` in the assembly JAR). The latter option is useful for tools, such as the hadoop jar command, that behave differently depending on the presence or not of a main class in the manifest.\n\nAdded in [#1200](https://github.com/VirtusLab/scala-cli/pull/1200) by [alexarchambault](https://github.com/alexarchambault)\n\n### Important fixes & enhancements\n#### Prevent erroneous using directives from blocking the initial run of BSP\nUp till now, running the `setup-ide` sub-command on sources containing `using directives` with syntax errors or pointing to dependencies which could not be fetched would create a `BSP` setup which could not be imported correctly by IDEs. This is no longer the case and such a `BSP` connection should now import correctly, so that it's possible to fix the faulty code within the comfort of one's IDE of choice.\n\nThis fixes [#1097](https://github.com/VirtusLab/scala-cli/issues/1097)\n\nAdded in [#1195](https://github.com/VirtusLab/scala-cli/pull/1195) by [Gedochao](https://github.com/Gedochao)\n\n### Work in progress\n#### Allow to globally turn actionable diagnostics on or off\nIt is now possible to globally enable or disable actionable diagnostics using the `config` sub-command.\n\nThe relevant configuration is under the `actions` key.\n```text\n$ scala-cli config actions true\n```\n\nAdded in [#1193](https://github.com/VirtusLab/scala-cli/pull/1193) by [lwronski](https://github.com/lwronski)\n\n#### Publishing-related features\n* Add \"publish setup\" command by [alexarchambault](https://github.com/alexarchambault) in [#926](https://github.com/VirtusLab/scala-cli/pull/926)\n\n### Other changes\n#### Documentation\n* Put the release notes doc on the website by [Gedochao](https://github.com/Gedochao) in [#1196](https://github.com/VirtusLab/scala-cli/pull/1196)\n* Fix typo in Spark docs by [alexarchambault](https://github.com/alexarchambault) in [#1183](https://github.com/VirtusLab/scala-cli/pull/1183)\n* Tweak issue templates & the release procedure by [Gedochao](https://github.com/Gedochao) in [#1188](https://github.com/VirtusLab/scala-cli/pull/1188)\n* Add install and uninstall completions documentation by [wleczny](https://github.com/wleczny) in [#1201](https://github.com/VirtusLab/scala-cli/pull/1201)\n\n#### Build and internal changes\n* ignore *.semanticdb files by [mtk](https://github.com/mtk) in [#1187](https://github.com/VirtusLab/scala-cli/pull/1187)\n* Update scala-cli.sh launcher for 0.1.10 by [github-actions](https://github.com/features/actions) in [#1185](https://github.com/VirtusLab/scala-cli/pull/1185)\n* Force push updating scala-cli in scala-cli-setup by [lwronski](https://github.com/lwronski) in [#1189](https://github.com/VirtusLab/scala-cli/pull/1189)\n* Fix running scala check in scala native by [lwronski](https://github.com/lwronski) in [#1190](https://github.com/VirtusLab/scala-cli/pull/1190)\n* Use manifest JARs in \"run\" command if needed by [alexarchambault](https://github.com/alexarchambault) in [#1198](https://github.com/VirtusLab/scala-cli/pull/1198)\n* Use more lightweight Spark distribs in spark tests by [alexarchambault](https://github.com/alexarchambault) in [#1207](https://github.com/VirtusLab/scala-cli/pull/1207)\n* Update GraalVM to 22.2.0 by [alexarchambault](https://github.com/alexarchambault) in [#1208](https://github.com/VirtusLab/scala-cli/pull/1208)\n* Split integration tests by [alexarchambault](https://github.com/alexarchambault) in [#1202](https://github.com/VirtusLab/scala-cli/pull/1202)\n* Debug macOS CI issue on CI by [alexarchambault](https://github.com/alexarchambault) in [#1215](https://github.com/VirtusLab/scala-cli/pull/1215)\n* Update docusaurus to 2.0.0-rc.1 by [lwronski](https://github.com/lwronski) in [#1224](https://github.com/VirtusLab/scala-cli/pull/1224)\n\n#### Updates\n* Update core_2.13 to 3.7.0 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1186](https://github.com/VirtusLab/scala-cli/pull/1186)\n* Update core_2.13 to 3.7.1 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1194](https://github.com/VirtusLab/scala-cli/pull/1194)\n* Update jsoniter-scala-core_2.13 to 2.13.37 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1197](https://github.com/VirtusLab/scala-cli/pull/1197)\n* Update jsoniter-scala-core_2.13 to 2.13.38 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1217](https://github.com/VirtusLab/scala-cli/pull/1217)\n* Update ammonite to 2.5.4-13-1ebd00a6 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1218](https://github.com/VirtusLab/scala-cli/pull/1218)\n* Update core_2.13 to 3.7.2 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1219](https://github.com/VirtusLab/scala-cli/pull/1219)\n* Update scala-collection-compat to 2.8.1 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1221](https://github.com/VirtusLab/scala-cli/pull/1221)\n* Update trees_2.13 to 4.5.11 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1222](https://github.com/VirtusLab/scala-cli/pull/1222)\n* Update coursier-jvm_2.13, ... to 2.1.0-M6-49-gff26f8e39 by [scala-steward](https://github.com/scala-steward-org/scala-steward) in [#1223](https://github.com/VirtusLab/scala-cli/pull/1223)\n\n**Full Changelog**: [https://github.com/VirtusLab/scala-cli/compare/v0.1.10...v0.1.11](https://github.com/VirtusLab/scala-cli/compare/v0.1.10...v0.1.11)\n\n\n## [v0.1.10](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.10)\n\n### Initial support for importing other sources via `using` directives\n\nIt is now possible to add sources to a Scala CLI project from a source file, with `using file` directives:\n\n```scala\n//> using file \"Other.scala\"\n//> using file \"extra/\"\n```\n\nNote that several sources can be specified in a single directive\n\n```scala\n//> using file \"Other.scala\" \"extra/\"\n```\n\nAdded in [#1157](https://github.com/VirtusLab/scala-cli/pull/1157) by [lwronski](https://github.com/lwronski).\n\n### Add `dependency update` sub-command\n\nScala CLI can now update dependencies in user projects, using the `dependency-update` sub-command, like\n\n```text\nscala-cli dependency-update --all .\n```\n\nWhen updates are available, this sub-command asks whether to update each of those, right where these dependencies are\ndefined.\n\nAdded in [#1055](https://github.com/VirtusLab/scala-cli/pull/1055) by [lwronski](https://github.com/lwronski).\n\n### Running snippets passed as arguments\n\nScala CLI can now run Scala or Java code passed on the command-line, via `-e` / `--script-snippet` / `--scala-snippet`\n/ `--java-snippet`:\n\n```text\n$ scala-cli -e 'println(\"Hello\")'\nHello\n\n$ scala-cli --script-snippet 'println(\"Hello\")'\nHello\n\n$ scala-cli --scala-snippet '@main def run() = println(\"Hello\")'\nHello\n\n$ scala-cli --java-snippet 'public class Main { public static void main(String[] args) { System.out.println(\"Hello\"); } }'\nHello\n```\n\nThese options are meant to be substitutes to the `-e` option of the `scala` script that ships in scalac archives.\n\nAdded in [#1166](https://github.com/VirtusLab/scala-cli/pull/1166) by [Gedochao](https://github.com/Gedochao).\n\n### Uninstall instructions and `uninstall` sub-command\n\nUninstalling Scala CLI is now documented in the main installation page, right after the installation instructions. In\nparticular, when installed via\nthe [installation script](https://github.com/VirtusLab/scala-cli-packages/blob/main/scala-setup.sh), Scala CLI can be\nuninstalled via a newly added `uninstall` sub-command.\n\nAdded in [#1122](https://github.com/VirtusLab/scala-cli/pull/1122) and #1152 by [wleczny](https://github.com/wleczny).\n\n### Important fixes & enhancements\n\n#### ES modules\n\nScala CLI now supports the ES Scala.js module kind, that can be enabled via a `//> using jsModuleKind \"esmodule\"`\ndirective, allowing to import other ES modules in particular.\n\nAdded in [#1142](https://github.com/VirtusLab/scala-cli/pull/1142)\nby [hugo-vrijswijk](https://github.com/hugo-vrijswijk).\n\n#### Putting Java options in assemblies, launchers, and docker images, in `package` sub-command\n\nPassing `--java-opt` and `--java-prop` options to the `package` sub-command is now allowed. The passed options are\nhard-coded in the generated assemblies or launchers, and in docker images.\n\nAdded in [#1167](https://github.com/VirtusLab/scala-cli/pull/1167) by [wleczny](https://github.com/wleczny).\n\n#### `--command` and `--scratch-dir` options in `run` sub-command\n\nThe `run` sub-command can now print the command it would have run, rather than running it. This can be useful for\ndebugging purposes, or if users want to manually tweak commands right before they are run. Pass `--command` to run to\nenable it. This prints one argument per line, for easier automated processing:\n\n```text\n$ scala-cli run --command -e 'println(\"Hello\")' --runner=false\n~/Library/Caches/Coursier/arc/https/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.2%252B8/OpenJDK17U-jdk_x64_mac_hotspot_17.0.2_8.tar.gz/jdk-17.0.2+8/Contents/Home/bin/java\n-cp\n~/Library/Caches/ScalaCli/virtual-projects/ee/project-3c6fdea1/.scala-build/project_ed4bea6d06_ed4bea6d06/classes/main:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.1.3/scala3-library_3-3.1.3.jar:~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar\nsnippet_sc\n```\n\nWhen `run` relies on temporary files (when Scala.js is used for example), one can pass a temporary directory\nvia `--scratch-dir`, so that temporary files are kept even when `scala-cli` doesn't run anymore:\n\n```text\n$ scala-cli run --command -e 'println(\"Hello\")' --js --runner=false --scratch-dir ./tmp\nnode\n./tmp/main1690571004533525773.js\n```\n\nAdded in [#1163](https://github.com/VirtusLab/scala-cli/pull/1163) by\nby [alexarchambault](https://github.com/alexarchambault).\n\n#### Don't put Scala CLI internal modules in packages\n\nScala CLI doesn't put anymore its stubs module and its \"runner\" module in generated packages, in the `package`\nsub-command.\n\nFixed in [#1161](https://github.com/VirtusLab/scala-cli/pull/1161)\nby [alexarchambault](https://github.com/alexarchambault).\n\n#### Don't write preambles in generated assemblies in the `package` sub-command\n\nPassing `--preamble=false` to `scala-cli --power package --assembly` makes it generate assemblies without a shell preamble. As a\nconsequence, these assemblies cannot be made executable, but these look more like \"standard\" JARs, which is required in\nsome contexts.\n\nFixed in [#1161](https://github.com/VirtusLab/scala-cli/pull/1161)\nby [alexarchambault](https://github.com/alexarchambault).\n\n#### Don't put some dependencies in generated assemblies in the `package` sub-command\n\nSome dependencies, alongside all their transitive dependencies, can be excluded from the generated assemblies.\nPass `--provided org:name` to `scala-cli --power package --assembly` to remove a dependency, like\n\n```text\n$ scala-cli --power package SparkJob.scala --assembly --provided org.apache.spark::spark-sql\n```\n\nNote that unlike \"provided\" dependencies in sbt, and compile-time dependencies in Mill, all transitive dependencies are\nexcluded from the assembly. In the Spark example above, for example, as `spark-sql` depends on `scala-library` (the\nScala standard library), the latter gets excluded from the assembly too (which works fine in the context of Spark jobs).\n\nFixed in [#1161](https://github.com/VirtusLab/scala-cli/pull/1161)\nby [alexarchambault](https://github.com/alexarchambault).\n\n### In progress\n\n#### Experimental Spark capabilities\n\nThe `package` sub-command now accepts a `--spark` option, to generate assemblies for Spark jobs, ready to be passed\nto `spark-submit`. This option is hidden (not printed in `scala-cli --power package --help`, only in `--help-full`), and should\nbe considered experimental.\n\nSee [this document](https://github.com/VirtusLab/scala-cli/blob/410f54c01ac5d9cb046461dce07beb5aa008231e/website/src/pages/spark.md)\nfor more details about these experimental Spark features.\n\nAdded in [#1086](https://github.com/VirtusLab/scala-cli/pull/1086)\nby [alexarchambault](https://github.com/alexarchambault).\n\n### Other changes\n\n#### Documentation\n\n* Add cookbooks for working with Scala CLI in IDEA IntelliJ by [Gedochao](https://github.com/Gedochao)\n  in [#1149](https://github.com/VirtusLab/scala-cli/pull/1149)\n* Fix VL branding by [lwronski](https://github.com/lwronski)\n  in [#1151](https://github.com/VirtusLab/scala-cli/pull/1151)\n* Back port of documentation changes to main by [github-actions](https://github.com/features/actions)\n  in [#1154](https://github.com/VirtusLab/scala-cli/pull/1154)\n* Update using directive syntax in scenarios by [lwronski](https://github.com/lwronski)\n  in [#1159](https://github.com/VirtusLab/scala-cli/pull/1159)\n* Back port of documentation changes to main by [github-actions](https://github.com/features/actions)\n  in [#1165](https://github.com/VirtusLab/scala-cli/pull/1165)\n* Add docs depedency-update by [lwronski](https://github.com/lwronski)\n  in [#1178](https://github.com/VirtusLab/scala-cli/pull/1178)\n* Add docs how to install scala-cli via choco by [lwronski](https://github.com/lwronski)\n  in [#1179](https://github.com/VirtusLab/scala-cli/pull/1179)\n\n#### Build and internal changes\n\n* Update scala-cli.sh launcher for 0.1.9 by [github-actions](https://github.com/features/actions)\n  in [#1144](https://github.com/VirtusLab/scala-cli/pull/1144)\n* Update release procedure by [wleczny](https://github.com/wleczny)\n  in [#1156](https://github.com/VirtusLab/scala-cli/pull/1156)\n* chore(ci): add in mill-github-dependency-graph by [ckipp01](https://github.com/ckipp01)\n  in [#1164](https://github.com/VirtusLab/scala-cli/pull/1164)\n* chore(ci): bump version of mill-github-dependency-graph by [ckipp01](https://github.com/ckipp01)\n  in [#1171](https://github.com/VirtusLab/scala-cli/pull/1171)\n* Use Scala CLI 0.1.9 in build by [alexarchambault](https://github.com/alexarchambault)\n  in [#1173](https://github.com/VirtusLab/scala-cli/pull/1173)\n* Stop compiling most stuff with Scala 2 by [alexarchambault](https://github.com/alexarchambault)\n  in [#1113](https://github.com/VirtusLab/scala-cli/pull/1113)\n* Turn the sip mode also for `scala-cli-sip` binary by [romanowski](https://github.com/romanowski)\n  in [#1168](https://github.com/VirtusLab/scala-cli/pull/1168)\n* chore(ci): use mill-dependency-submission action by [ckipp01](https://github.com/ckipp01)\n  in [#1174](https://github.com/VirtusLab/scala-cli/pull/1174)\n* Fix snippet tests for Windows by [Gedochao](https://github.com/Gedochao)\n  in [#1172](https://github.com/VirtusLab/scala-cli/pull/1172)\n\n#### Updates\n\n* Update mill-main to 0.10.5 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1148](https://github.com/VirtusLab/scala-cli/pull/1148)\n* Update snailgun-core, snailgun-core_2.13 to 0.4.1-sc2\n  by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1155](https://github.com/VirtusLab/scala-cli/pull/1155)\n* Update jsoniter-scala-core_2.13 to 2.13.35 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1169](https://github.com/VirtusLab/scala-cli/pull/1169)\n* Update scala-collection-compat to 2.8.0 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1170](https://github.com/VirtusLab/scala-cli/pull/1170)\n* Update jsoniter-scala-core_2.13 to 2.13.36 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1175](https://github.com/VirtusLab/scala-cli/pull/1175)\n\n### New Contributors\n\n* [hugo-vrijswijk](https://github.com/hugo-vrijswijk) made their first contribution\n  in [#1142](https://github.com/VirtusLab/scala-cli/pull/1142)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.9...v0.1.10\n\n## [v0.1.9](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.9)\n\n### `--list-main-classes` for `publish` & `package`\n\n`publish` and `package` sub-commands now support the `--list-main-classes` option, which allows to list all the\navailable main classes. Previously it was only available in the `run` command.\n\nAdded in [#1118](https://github.com/VirtusLab/scala-cli/pull/1118) by [Gedochao](https://github.com/Gedochao)\n\n### Important fixes & enhancements\n\n#### `fmt` options improvement\n\nAdded missing documentation on how to pass native `scalafmt` options in the `fmt` sub-command with the `-F` option.\n\n```\n$ scala-cli fmt -F --version\nscalafmt 3.5.2\n```\n\nAdditionally, a couple of `scalafmt`'s native options received aliases in Scala CLI:\n\n`--respect-project-filters` is an alias for `-F --respect-project-filters`. Because of the way sources are passed by\nScala CLI to `scalafmt` under the hood, we now turn it on by default to respect any `project.excludePaths` settings in\nthe user's `.scalafmt.conf`.\nIt can be disabled by passing `--respect-project-filters=false` to revert to previous behaviour.\nThis addresses [#1121](https://github.com/VirtusLab/scala-cli/issues/1121)\n\n`--scalafmt-help` is an alias for `-F --help`. It shows the `--help` output from `scalafmt`, which might prove as\nhelpful reference when in need of using native `scalafmt` options with `-F`.\n\nAdded in [#1135](https://github.com/VirtusLab/scala-cli/pull/1135) by [Gedochao](https://github.com/Gedochao)\n\n#### Include `libsodium.dll` on Windows\n\nStatic linking of libsodium in Windows launcher has been fixed.\nThis addresses [#1114](https://github.com/VirtusLab/scala-cli/issues/1114)\n\nAdded in [#1115](https://github.com/VirtusLab/scala-cli/pull/1115)\nby [alexarchambault](https://github.com/alexarchambault)\n\n#### Force interactive mode for `update` command\n\nInteractive mode for `update` sub-command is now enabled by default.\n\nAdded in [#1100](https://github.com/VirtusLab/scala-cli/pull/1100) by [lwronski](https://github.com/lwronski)\n\n### In progress\n\n#### Publishing-related features\n\n* Publish tweaks + documentation by [alexarchambault](https://github.com/alexarchambault)\n  in [#1107](https://github.com/VirtusLab/scala-cli/pull/1107)\n\n#### Better BSP support for Scala scripts\n\n* Add scala-sc language to BSP supported languages by [alexarchambault](https://github.com/alexarchambault)\n  in [#1140](https://github.com/VirtusLab/scala-cli/pull/1140)\n\n### Other changes\n\n#### Documentation PRs\n\n* Update scala 2.12 to 2.12.16 in docs by [lwronski](https://github.com/lwronski)\n  in [#1108](https://github.com/VirtusLab/scala-cli/pull/1108)\n* Back port of documentation changes to main by [github-actions](https://github.com/features/actions)\n  in [#1111](https://github.com/VirtusLab/scala-cli/pull/1111)\n* Tweak release procedure by [Gedochao](https://github.com/Gedochao)\n  in [#1112](https://github.com/VirtusLab/scala-cli/pull/1112)\n\n#### Build and internal changes\n\n* Add choco configuration files by [lwronski](https://github.com/lwronski)\n  in [#998](https://github.com/VirtusLab/scala-cli/pull/998)\n* Tweaking by [alexarchambault](https://github.com/alexarchambault)\n  in [#1105](https://github.com/VirtusLab/scala-cli/pull/1105)\n* Add scala-cli-setup deploy key to ssh-agent by [lwronski](https://github.com/lwronski)\n  in [#1117](https://github.com/VirtusLab/scala-cli/pull/1117)\n\n#### Updates\n\n* Update scala-cli.sh launcher for 0.1.8 by [github-actions](https://github.com/features/actions)\n  in [#1106](https://github.com/VirtusLab/scala-cli/pull/1106)\n* Update case-app to 2.1.0-M14 by [alexarchambault](https://github.com/alexarchambault)\n  in [#1120](https://github.com/VirtusLab/scala-cli/pull/1120)\n* Update Scala to 3.1.3 by [alexarchambault](https://github.com/alexarchambault)\n  in [#1124](https://github.com/VirtusLab/scala-cli/pull/1124)\n* Update jsoniter-scala-core_2.13 to 2.13.32 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1125](https://github.com/VirtusLab/scala-cli/pull/1125)\n* Update coursier-jvm_2.13, ... to 2.1.0-M6-28-gbad85693f\n  by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1126](https://github.com/VirtusLab/scala-cli/pull/1126)\n* Update libsodiumjni to 0.0.3 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1127](https://github.com/VirtusLab/scala-cli/pull/1127)\n* Update org.eclipse.jgit to 6.2.0.202206071550-r by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1128](https://github.com/VirtusLab/scala-cli/pull/1128)\n* Update Scala.js to 1.10.1 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1130](https://github.com/VirtusLab/scala-cli/pull/1130)\n* Update Scala Native to 0.4.5 by [alexarchambault](https://github.com/alexarchambault)\n  in [#1133](https://github.com/VirtusLab/scala-cli/pull/1133)\n* Update scala-js-cli to 1.1.1-sc5 by [alexarchambault](https://github.com/alexarchambault)\n  in [#1134](https://github.com/VirtusLab/scala-cli/pull/1134)\n* Update jsoniter-scala-core_2.13 to 2.13.33 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1136](https://github.com/VirtusLab/scala-cli/pull/1136)\n* Update `scalafmt`  to 3.5.8 by [Gedochao](https://github.com/Gedochao)\n  in [#1137](https://github.com/VirtusLab/scala-cli/pull/1137)\n* Update cli-options_2.13, cli_2.13, ... to 0.1.7 by [scala-steward](https://github.com/scala-steward-org/scala-steward)\n  in [#1138](https://github.com/VirtusLab/scala-cli/pull/1138)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.8...v0.1.9\n\n## [v0.1.8](https://github.com/VirtusLab/scala-cli/releases/tag/v0.1.8)\n\n### `--list-main-classes` option for the `run` command\n\nYou can pass the option `--list-main-classes` to the `run` command to list all the available main classes, including\nscripts.\n\n```\n$ scala-cli . --list-main-classes\nHello scripts.AnotherScript_sc scripts.Script_sc\n```\n\nAdded in [#1095](https://github.com/VirtusLab/scala-cli/pull/1095) by [Gedochao](https://github.com/Gedochao)\n\n### Add `config` command\n\nThe `config` sub-command allows to get and set various configuration values, intended for use by\nother Scala CLI sub-commands.\n\nThis feature has been added in preparation for the `publish` command, stay tuned for future announcements.\n\nAdded in [#1056](https://github.com/VirtusLab/scala-cli/pull/1056)\nby [alexarchambault](https://github.com/alexarchambault)\n\n### Prioritise non-script main classes\n\nWhen trying to run a directory containing scripts and just a single non-script main class, the non-script main class\nwill now be prioritised and run by default.\n\n```\n$ scala-cli .\nRunning Hello. Also detected script main classes: scripts.AnotherScript_sc, scripts.Script_sc\nYou can run any one of them by passing option --main-class, i.e. --main-class scripts.AnotherScript_sc\nAll available main classes can always be listed by passing option --list-main-classes\nHello world\n```\n\nChanged in [#1095](https://github.com/VirtusLab/scala-cli/pull/1095) by [Gedochao](https://github.com/Gedochao)\n\n### Important bugfixes\n\n#### Accept latest Scala versions despite stale Scala version listings in cache\n\nScala CLI uses version listings from Maven Central to check if a Scala version is valid. When new Scala versions are\nreleased, users could sometimes have stale version listings in their Coursier cache for a short period of time (the\nCoursier cache TTL, which is 24 hours by default). This prevented these users to use new Scala versions during that\ntime.\nTo work around that, Scala CLI now tries to re-download version listings when they don't have the requested Scala\nversion.\nThis addresses [#1090](https://github.com/VirtusLab/scala-cli/issues/1090)\n\nFixed in [#1096](https://github.com/VirtusLab/scala-cli/pull/1096) by [lwronski](https://github.com/lwronski)\n\n#### Bloop now uses `JAVA_HOME` by default\n\nBloop should now pick up the JDK available in `JAVA_HOME`. It was formerly necessary to pass `--bloop-jvm system`\nexplicitly. This addresses [#1102](https://github.com/VirtusLab/scala-cli/issues/1102)\n\nFixed in [#1084](https://github.com/VirtusLab/scala-cli/pull/1084) by [lwronski](https://github.com/lwronski)\n\n#### The `-coverage-out` option now accepts relative paths\n\nScala CLI now correctly processes relative paths when passed to the `-coverage-out` option. Formerly,\nthe `scoverage.coverage` file would not be properly generated when a relative path was passed.\nThis addresses [#1072](https://github.com/VirtusLab/scala-cli/issues/1072)\n\nFixed in [#1080](https://github.com/VirtusLab/scala-cli/pull/1080) by [lwronski](https://github.com/lwronski)\n\n### Other changes\n\n#### Documentation PRs\n\n* Improve scripts guide by [Gedochao](https://github.com/Gedochao)\n  in [#1074](https://github.com/VirtusLab/scala-cli/pull/1074)\n* Update installation instructions for Nix by [kubukoz](https://github.com/kubukoz)\n  in [#1082](https://github.com/VirtusLab/scala-cli/pull/1082)\n* Tweak docs by [alexarchambault](https://github.com/alexarchambault)\n  in [#1085](https://github.com/VirtusLab/scala-cli/pull/1085)\n* Some typos & rewording on the single-module projects use case page by [Baccata](https://github.com/Baccata)\n  in [#1089](https://github.com/VirtusLab/scala-cli/pull/1089)\n\n#### Fixes\n\n* Add suffix to project name which contains virtual files by [lwronski](https://github.com/lwronski)\n  in [#1070](https://github.com/VirtusLab/scala-cli/pull/1070)\n\n#### Build and internal changes\n\n* Update scala-cli.sh launcher for 0.1.7 by [github-actions](https://github.com/features/actions)\n  in [#1076](https://github.com/VirtusLab/scala-cli/pull/1076)\n* Tweaking by [alexarchambault](https://github.com/alexarchambault)\n  in [#1087](https://github.com/VirtusLab/scala-cli/pull/1087)\n\n**Full Changelog**: https://github.com/VirtusLab/scala-cli/compare/v0.1.7...v0.1.8\n\n## Older versions\n\nThe release notes for all the past versions of Scala CLI can be viewed\non [our releases page on GitHub](https://github.com/VirtusLab/scala-cli/releases).\n"
  },
  {
    "path": "website/docs/under-the-hood.md",
    "content": "---\ntitle: Under the hood\nsidebar_position: 32\n---\n\n:::note\nThis page is for people who are already familiar with the Scala/JVM ecosystem.\n\nIf you just want to learn Scala CLI, head out to the [Commands section](commands/basics.md).\n:::\n\n\n# Under the hood\n\nScala CLI consists of a native executable, generated by [GraalVM](https://www.graalvm.org) [Native Image](https://www.graalvm.org/reference-manual/native-image).\nIt runs fine on Linux and macOS with no prior requirements, and\nonly requires the [Visual C++ Redistributable Runtime](https://www.microsoft.com/en-us/download/details.aspx?id=48145)\non Windows.\nNative Image lets us build Scala CLI as a native image for each platform, and lets Scala CLI be responsive, as a command line application should be.\n\nHowever, Scala CLI is still a JVM application, so it is possible to e.g. set [Java properties](guides/advanced/java-properties.md).\n\n### Caching and incrementality\n\nSince most of the tasks require compilation or dependency resolution under the hood, Scala CLI heavily uses caches and incrementality under the hood to provide output as quickly as possible.\n\nBut note that incremental compilation and caching are not perfect.\nIn some cases, when there's a compilation problem and you don't think it's a problem with the code, it may be the stale state of the project - cleaning the project state might help.\nFor this reason Scala CLI has the [clean](./commands/clean.md) command, which invalidates local caches and forces the next compilation to be a total rebuild from a clean slate.\n\nWe provide a more in-depth overview about how caching works in the [Scala CLI internals guide](guides/advanced/internals.md).\n\n### Bloop\n\nTo ensure the quickest compilation, Scala CLI uses and manages the [Bloop](https://scalacenter.github.io/bloop/) compilation server.\nWe have a [guide](commands/misc/bloop.md) that describes how Scala CLI interacts with the local Bloop server and how a user can do the same.\nThe main point to know is that Scala CLI takes care of fetching and starting Bloop if needed, so you don't have to worry about it.\n\n### Coursier\n\nScala CLI uses [Coursier](https://get-coursier.io/) to manage dependencies.\nIt automatically downloads and unpacks a JVM if none is installed on your system, so that all its commands work fine even if a JVM isn't already installed.\nScala CLI shares Coursier caches with other tools like [sbt](https://www.scala-sbt.org/), [Mill](https://github.com/com-lihaoyi/mill), or [Metals](https://scalameta.org/metals/).\n"
  },
  {
    "path": "website/docusaurus.config.js",
    "content": "/** @type {import('@docusaurus/types').DocusaurusConfig} */\n\nconst lightCodeTheme = require('prism-react-renderer').themes.github;\nconst darkCodeTheme = require('prism-react-renderer').themes.dracula;\n\nmodule.exports = {\n  title: 'Scala CLI',\n  tagline: 'More featureful Scala Command-Line',\n  url: 'https://scala-cli.virtuslab.org/',\n  baseUrl: '/',\n  onBrokenLinks: 'throw',\n  onBrokenAnchors: 'throw',\n  markdown: {\n    hooks: {\n      onBrokenMarkdownLinks: 'throw'\n    }\n  },\n  favicon: 'img/favicon.ico',\n  organizationName: 'Virtuslab',\n  projectName: 'scala-cli',\n  plugins: ['docusaurus-plugin-sass', \"@easyops-cn/docusaurus-search-local\"],\n  themeConfig: {\n    image: \"img/logo.png\",\n    prism: {\n      theme: lightCodeTheme,\n      darkTheme: darkCodeTheme,\n      additionalLanguages: ['java', 'scala', 'bash'],\n    },\n    navbar: {\n      title: 'Scala CLI',\n      logo: {\n        alt: 'Scala Logo',\n        src: 'img/logo.svg',\n      },\n      items: [\n        {\n          to: 'install',\n          label: 'Installation'\n        },\n        {\n         // type: 'doc',\n          label: \"Use cases\",\n          to: '/',\n          items: [\n            {\n              to: '/education',\n              label: 'Education',\n            },\n            {\n              to: '/scripting',\n              label: 'Scripting',\n            },\n            {\n              to: '/prototyping',\n              label: 'prototyping, experimenting, reproducing',\n            },\n            {\n              to: '/projects',\n              label: 'Single-module projects',\n            }\n          ]\n        },\n        {\n          type: 'doc',\n          docId: 'overview',\n          position: 'left',\n          label: 'Documentation',\n        },\n        {\n          to: '/docs/commands/basics',\n          label: 'Commands'\n        },\n        {\n          to: '/docs/guides/intro',\n          label: 'Guides'\n        },\n        {\n          to: '/docs/cookbooks/intro',\n          label: 'Cookbook'\n        },\n        {\n          href: 'https://virtuslab.com/',\n          position: 'right',\n          className: 'header-vl-link',\n          label: \"by\",\n          'aria-label': 'GitHub repository',\n        },\n        {\n          href: 'https://github.com/Virtuslab/scala-cli',\n          label: 'GitHub',\n          position: 'right',\n        },\n      ],\n    },\n    footer: {\n      style: 'dark',\n      links: [\n        {\n          title: 'Documentation',\n          items: [\n            {\n              label: 'Documentation',\n              to: '/docs/overview',\n            },\n          ],\n        },\n        {\n          title: 'Community',\n          items: [\n            {\n              label: 'Discord',\n              href: 'https://discord.gg/ScreHFr957',\n            },\n          ],\n        },\n        {\n          title: 'More',\n          items: [\n            {\n              label: 'GitHub',\n              href: 'https://github.com/Virtuslab/scala-cli',\n            },\n          ],\n        },\n      ],\n      copyright: `Copyright © 2021-2022 VirtusLab Sp. z. o. o.`,\n    },\n  },\n  presets: [\n    [\n      '@docusaurus/preset-classic',\n      {\n        docs: {\n          sidebarPath: require.resolve('./sidebars.js'),\n          // Please change this to your repo.\n          editUrl:\n            'https://github.com/Virtuslab/scala-cli/edit/main/website/',\n        },\n        theme: {\n         // customCss: require.resolve('./src/css/custom.css'),\n          customCss: [require.resolve('./src/scss/style.scss')],\n        },\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"website\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"docusaurus\": \"docusaurus\",\n    \"start\": \"docusaurus start\",\n    \"build\": \"docusaurus build\",\n    \"swizzle\": \"docusaurus swizzle\",\n    \"deploy\": \"docusaurus deploy\",\n    \"clear\": \"docusaurus clear\",\n    \"serve\": \"docusaurus serve\",\n    \"write-translations\": \"docusaurus write-translations\",\n    \"write-heading-ids\": \"docusaurus write-heading-ids\"\n  },\n  \"dependencies\": {\n    \"@algolia/client-search\": \"^5.50.1\",\n    \"@docusaurus/core\": \"^3.9.2\",\n    \"@docusaurus/plugin-content-docs\": \"^3.9.2\",\n    \"@docusaurus/preset-classic\": \"^3.9.2\",\n    \"@docusaurus/theme-common\": \"^3.9.2\",\n    \"@easyops-cn/docusaurus-search-local\": \"^0.55.1\",\n    \"@mdx-js/react\": \"^3.1.1\",\n    \"@types/react\": \"^19.2.14\",\n    \"clsx\": \"^2.1.1\",\n    \"docusaurus-plugin-sass\": \"^0.2.6\",\n    \"react\": \"^19.2.4\",\n    \"react-dom\": \"^19.2.4\",\n    \"react-loadable\": \"^5.5.0\",\n    \"react-player\": \"^3.4.0\",\n    \"sass\": \"^1.99.0\",\n    \"search-insights\": \"^2.17.3\",\n    \"@svta/cml-cta\": \"1.0.5\",\n    \"@svta/cml-structured-field-values\": \"1.1.2\",\n    \"@svta/cml-utils\": \"1.4.0\"\n  },\n  \"resolutions\": {\n    \"react-loadable\": \"^5.5.0\",\n    \"search-insights\": \"^2.17.3\",\n    \"@algolia/client-search\": \"^5.48.1\",\n    \"@types/react\": \"^19.2.14\",\n    \"@svta/cml-cta\": \"1.0.5\",\n    \"@svta/cml-structured-field-values\": \"1.1.2\",\n    \"@svta/cml-utils\": \"1.4.0\"\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.5%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  }\n}\n"
  },
  {
    "path": "website/safe-yarn.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nSCRIPT_DIR=$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\n\ndocker image inspect node:16 > /dev/null || docker pull node:16\n\nif [ \"$1\" == \"start\" ]; then\n  args=\"$1 -h 0.0.0.0 ${@:2}\"\nelse \n  args=\"$@\"\nfi\n\ndocker run -p 127.0.0.1:3000:3000 -it -v $SCRIPT_DIR:/data -w /data  node:16 yarn $args"
  },
  {
    "path": "website/sidebars.js",
    "content": "/**\n * Creating a sidebar enables you to:\n - create an ordered group of docs\n - render a sidebar for each doc of that group\n - provide next/previous navigation\n\n The sidebars can be generated from the filesystem, or explicitly defined here.\n\n Create as many sidebars as you want.\n */\n\nmodule.exports = {\n  // By default, Docusaurus generates a sidebar from the docs folder structure\n  tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],\n\n  // But you can create a sidebar manually\n  /*\n  tutorialSidebar: [\n    {\n      type: 'category',\n      label: 'Tutorial',\n      items: ['hello'],\n    },\n  ],\n   */\n};\n"
  },
  {
    "path": "website/src/components/BasicInstall.js",
    "content": "import React from 'react';\nimport Tabs from '@theme/Tabs';\nimport TabItem from '@theme/TabItem';\nimport BrowserOnly from '@docusaurus/BrowserOnly';\nimport {currentOs} from \"./osUtils\";\n\nexport default function BasicInstall(props){\n  return  <BrowserOnly>{() =>\n      <div>\n        <Tabs\n          groupId=\"operating-systems\"\n          defaultValue={currentOs()}\n          values={[\n          {label: 'macOS', value: 'mac'},\n          {label: 'Linux', value: 'linux'},\n          {label: 'Windows', value: 'windows'},\n          {label: 'GitHub Actions', value: 'gha'}\n        ]}>\n\n          <TabItem value=\"windows\">\n            <p>Install Scala CLI with <a className=\"no_monospace\" href=\"https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget\">WinGet</a> by running the following one-line command in your terminal:</p>\n            <code>\n              winget install virtuslab.scalacli\n            </code>\n          </TabItem>\n\n          <TabItem value=\"linux\">\n            <p>Run the following one-line command in your terminal:</p>\n            <code>\n              curl -sSLf https://scala-cli.virtuslab.org/get | sh\n            </code>\n          </TabItem>\n\n          <TabItem value=\"mac\">\n            <p>Install Scala CLI with <a className=\"no_monospace\" href=\"https://brew.sh/\">Homebrew</a> by running the following one-line command in your terminal:</p>\n            <code>\n              brew install Virtuslab/scala-cli/scala-cli\n            </code>\n          </TabItem>\n\n          <TabItem value=\"gha\">\n            <p>Add the <a href=\"https://github.com/VirtusLab/scala-cli-setup\">scala-cli-setup</a> action to your workflow:</p>\n            <code>\n              steps:<br/>\n              &nbsp;&nbsp;&nbsp;&nbsp;- uses: coursier/cache-action@v6<br/>\n              &nbsp;&nbsp;&nbsp;&nbsp;- uses: VirtusLab/scala-cli-setup@main<br/>\n            </code>\n          </TabItem>\n\n        </Tabs>\n      </div>\n  }</BrowserOnly>\n}\n"
  },
  {
    "path": "website/src/components/BigHeader.js",
    "content": "import React from 'react';\n\nexport default function BigHeader(props){ \n  return <div className={\"col col--\" + props.colsize }>\n\t\t<h1 className={\"section-title\" + (props.promptsign ? \" with-before\" : \"\")}>\n            {props.title}\n        </h1>\n\t</div>\n}"
  },
  {
    "path": "website/src/components/DownloadButton.js",
    "content": "import React from 'react';\n\nclass DownloadButton extends React.Component {\n  \n  constructor(props) {\n    super(props);\n\n    this.handleClick = this.handleClick.bind(this);\n  }\n\n  handleClick(e) {\n      window.location.href=this.props.href;\n  }\n\n  render() {\n    return (\n      <button \n      class=\"button button--danger button--outline\"\n      onClick={this.handleClick}\n      >\n        {this.props.desc}\n      </button>\n    );\n  }\n}\n\nexport default DownloadButton;"
  },
  {
    "path": "website/src/components/IconBox.js",
    "content": "import React from 'react';\n\nexport default function IconBox(props){ \n  return <div className=\"section-features__item col col--4\">\n\t\t<div className=\"section-features__item-wrapper\">\n\t\t\t<div className=\"icon\">\n\t\t\t\t{!props.icon ? \"\" : <img src={props.icon} alt={props.title} />}\n\t\t\t</div>\n\t\t\t<div className=\"title\">{props.title}</div>\n\t\t\t<div className=\"desc\">{props.children}</div>\n\t\t</div>\n    </div>\n}"
  },
  {
    "path": "website/src/components/ImageBox.js",
    "content": "import React from 'react';\nimport ThemedImage from '@theme/ThemedImage';\n\n\nexport default function ImageBox(props){ \n  \treturn <div className={\"section-image-box__row row \"}>\n\t\t\t\t\n\t\t<div className=\"section-image-box__row-text col col--1 left-margin-stub\"/>\n\t\t<div className={\"section-image-box__row-text col col--5 \" }>\n\t\t\t<div className=\"section-image-box__row-text-wrapper\">\n\t\t\t\t<h3>{props.title}</h3>\n\t\t\t\t<div className=\"content\">\n\t\t\t\t\t{props.children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div className = {\"section-image-box__row-image col col--6 \" }>\n\t\t\t<div className=\"section-image-box__row-image-wrapper\">\n\t\t\t\t{!props.image ? \"\" : <div className=\"green_border\">\n\t\t\t\t\t<ThemedImage\n\t\t\t\t\talt={props.image}\n\t\t\t\t\tsources={{\n\t\t\t\t\t\tlight: `/img/${props.image}`,\n\t\t\t\t\t\tdark: `/img/dark/${props.image}`,\n\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</div>}\n\t\t\t</div>\n\t\t</div>\n\t\t<div className=\"section-image-box__row-text col col--1 right-margin-stub\"/>\n\t</div>\n}"
  },
  {
    "path": "website/src/components/Layouts.js",
    "content": "import React from 'react';\n\nexport function HeaderSection(props){ \n  return <div className=\"row headerSection padding--lg margin--lg\">\n  <div className=\"col col--6 text--left\">\n    {props.children}\n  </div>\n  <div className=\"col col--6\">\n    {props.image ? <img src={props.image}/> : \"\"}\n  </div>\n</div>\n}\n\nexport function TitledSection(props){ \n  return <div className=\"row titledSection padding--lg margin--lg\">\n    <div className=\"col col--3\">\n      <h1>{props.title}</h1>\n    </div>\n    <div className=\"col col--9 text--left\">\n      {props.children}\n    </div>\n</div>\n}\n\n"
  },
  {
    "path": "website/src/components/MarkdownComponents.js",
    "content": "import React from 'react';\nimport ReactPlayer from 'react-player'\n\nexport function ChainedSnippets({children}){\n    return (\n        <div className=\"runnable-command\">\n            {children}\n        </div>\n    )\n}\n\nexport function GiflikeVideo({url}){\n  return <ReactPlayer \n    playing loop muted controls \n    width=\"100%\" height=\"\"\n    url={url} />\n}"
  },
  {
    "path": "website/src/components/Section.js",
    "content": "import React from 'react';\n\nexport default function Section(props){ \n  \treturn <section className={\"section \" + props.className}>\n    \t{props.children}\n  \t</section>\n}"
  },
  {
    "path": "website/src/components/SectionAbout.js",
    "content": "import React from 'react';\n\nexport default function SectionAbout(props){\n  const id = props.title.toLowerCase().split(\" \").join(\"-\")\n  const colBigTitle = props.colBigTitle || 3\n  const link = <a href={\"#\" + id} >&gt;_</a> \n  return <div className=\"section-about__wrapper row\" id={id}>\n        <div className=\"col col--1 big-title pre-title\">{link}</div>\n        <div className={`col col--${colBigTitle} big-title`}>\n            <span className=\"pre-title-mobile\">{link}</span> {props.title}\n        </div>\n        <div className=\"col col--8 description\">\n            {props.children}\n        </div>\n</div>\n}"
  },
  {
    "path": "website/src/components/SectionImageBox.js",
    "content": "import React from 'react';\n\nimport Section from \"../components/Section\"\nimport ImageBox from \"../components/ImageBox\"\nimport SmallHeader from \"../components/SmallHeader\"\n\nexport default function SectionImageBox(props){ \n  \treturn <Section className=\"section-image-box\">\n\n        <SmallHeader title=\"Still undecided?\">\n            Here come our <span>main features</span>\n        </SmallHeader>\n\n        <ImageBox title=\"Scala versions, dependencies and JVMs\" image=\"img/envs.gif\" imgpos=\"right\">\n            <p>\n                Scala CLI is built on top of coursier. This allows us to manage Scala versions, dependencies and JVMs so you can test your code in different environments by changing single option.\n            </p>\n            <p>\n                Scala CLI ships with all its dependencies. No need to fluff with installing JVM or setting up PATH.\n            </p>\n        </ImageBox>\n\n        <ImageBox title=\"Scala versions, dependencies and JVMs\" image=\"img/envs.gif\" imgpos=\"left\">\n            <p>\n                Scala CLI is built on top of coursier. This allows us to manage Scala versions, dependencies and JVMs so you can test your code in different environments by changing single option.\n            </p>\n            <p>\n                Scala CLI ships with all its dependencies. No need to fluff with installing JVM or setting up PATH.\n            </p>\n        </ImageBox>\n\n        <ImageBox title=\"Scala versions, dependencies and JVMs\" image=\"img/envs.gif\" imgpos=\"right\">\n            <p>\n                Scala CLI is built on top of coursier. This allows us to manage Scala versions, dependencies and JVMs so you can test your code in different environments by changing single option.\n            </p>\n            <p>\n                Scala CLI ships with all its dependencies. No need to fluff with installing JVM or setting up PATH.\n            </p>\n        </ImageBox>\n\n    </Section>\n}"
  },
  {
    "path": "website/src/components/SmallHeader.js",
    "content": "import React from 'react';\n\nexport default function SmallHeader(props){ \n  return <div className=\"section__header\">\n\t\t<h2>{props.title}</h2>\n\t\t<div className=\"section__header-description\">\n\t\t\t{props.children}\n\t\t</div>\n\t</div>\n}"
  },
  {
    "path": "website/src/components/TitleSection.js",
    "content": "import React from 'react';\n\nexport default function TitleSection(props){ \n  return <div className=\"row\">\n    <div className=\"install-section col col--6 col--offset-2 text--left\">\n      {props.children}\n    </div>\n    <div className=\"install-section col col--4 text--center\">\n      {props.right}\n    </div>\n  </div> \n}"
  },
  {
    "path": "website/src/components/UseCase.js",
    "content": "import React from 'react';\nimport Section from './Section';\nimport YellowBanner from './YellowBanner';\nimport allFeatures from './features';\nimport Layout from '@theme/Layout';\nimport BigHeader from './BigHeader';\n\nexport default function UseCase(props){ \n  return <Layout title={props.title} description={props.description} key={props.title}>\n    <div className=\"container content\">\n      <YellowBanner image={props.image} title={props.headline}>\n        {props.children}\n      </YellowBanner>\n    \n      \n      <BigHeader title={props.title} colsize=\"12\" promptsign={true}></BigHeader>\n\n\n      <Section className=\"section-image-box\">\n        {allFeatures().filter(f => f.props[props.id])}\n      </Section>\n    </div>\n  </Layout>\n}"
  },
  {
    "path": "website/src/components/UseCaseTile.js",
    "content": "import React from 'react';\n\nexport default function UseCaseTile(props) { \n    const isLink = props.slug ? true : false;\n\n    if( isLink ) {\n        return <a href={\"/\" + props.slug } className=\"col col--4 use-box-wrapper\">\n        <div className=\"use-box\">\n  \n          <div className=\"icon-wrapper\">\n            <img src={\"img/ico-\" + props.slug + \".png\"} alt={props.slug + \" icon\"} />\n          </div>\n  \n          <h3>\n            {props.title}\n          </h3>\n  \n          <p>\n            {props.description}\n          </p>\n  \n          <div className=\"read-more-wrap\">\n            <div className=\"read-more with-before\">\n              Read more\n            </div>\n          </div>\n  \n        </div>\n      </a>\n    } else {\n        return <div className=\"col col--4 use-box-wrapper\">\n        <div className=\"use-box your-case\">\n\n          <div className=\"icon-wrapper\">\n            <img className=\"light-theme\" src=\"img/ico-yours.png\" alt=\"your use case icon\" />\n            <img className=\"dark-theme\" src=\"img/ico-yours-dark.png\" alt=\"your use case icon\" />\n          </div>\n\n          <h3>\n            {props.title}\n          </h3>\n\n          <p>\n            {props.description}\n          </p>\n\n        </div>\n      </div>\n    }\n}"
  },
  {
    "path": "website/src/components/YellowBanner.js",
    "content": "import React from 'react';\nimport Section from './Section';\nimport ThemedImage from '@theme/ThemedImage';\n\nexport default function YellowBanner(props){ \n  return <Section className=\"section-yellow-banner\">\n\t\t<div className=\"row row--align-center\">\n\t\t\t<div className=\"col col--6\">\n\t\t\t\t<h1>{props.title}</h1>\n\t\t\t\t<div className=\"description\">\n\t\t\t\t\t{props.children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"col col--6\">\n\t\t\t\t<div className=\"image-wrapper\">\n          <ThemedImage\n            className=\"image\"\n            alt={props.image}\n            sources={{\n              light: `/img/${props.image}`,\n              dark: `/img/dark/${props.image}`,\n            }}\n            />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</Section>\n}"
  },
  {
    "path": "website/src/components/features.js",
    "content": "import React from 'react';\n\nimport ImageBox from '../components/ImageBox';\n\nconst featuresList = [\n    <ImageBox image=\"gifs/versions.gif\" title=\"Scala versions, dependencies and JVMs\" \n      key=\"versions\" projects=\"true\"> \n      <p>\n        Scala CLI is built on top of coursier\n        <br/>\n        This allow us to manage Scala versions, dependencies and JVMs so you can test your code in different environments by changing single option.\n      </p>\n\n      <p>\n        Scala CLI ships with all its dependencies\n        <br/>\n        No need to fluff with installing JVM or setting up PATH. \n      </p>\n      <p>\n        <i>Some additional setup may be required for <a href=\"/install#scala-js\">JS</a> and <a href=\"/install#scala-native\">Native</a></i>\n      </p>\n    </ImageBox>,\n    <ImageBox image=\"gifs/universal_tool.gif\" title=\"Universal tool\" key=\"universal\" \n      projects=\"true\">\n      <p>\n        If you want to use older <b>version of Scala</b> or\n        run your code in <b>JS</b> or <b>Native</b> environments we've got you covered.\n        <br/>\n      </p>\n      <p>Switching between platforms or Scala versions is as easy as changing a parameter.</p>\n      <p> <i>Some additional setup may be required for <a href=\"/install#scala-js\">JS</a> and <a href=\"/install#scala-native\">Native</a></i></p>\n    </ImageBox>,\n    <ImageBox \n      image=\"buildtools.png\" \n      title=\"We do not call Scala CLI a build tool\" key=\"buildtool\" projects=\"true\">\n        <p>\n          Scala CLI shares some similarities with build tools,\n          but doesn't aim at supporting multi-module projects,\n          nor to be extended via a task system known from sbt, mill or bazel.\n        </p>\n        <p>\n          Scala ecosystem has multiple amazing build tools, there is no need to create another multipurpose build tool.\n        </p>\n    </ImageBox>,\n    <ImageBox \n        image=\"gifs/complete-install.gif\" title=\"Complete installation\"\n        key=\"complete-install\" education=\"true\">\n          <p>\n            Scala CLI comes with batteries included. No additional installation is required, no more fluffing with setting up the correct Java version or <code>PATH</code>\n          </p>\n          <p>\n            Scala CLI manages JVMs, Scala and other used tools under the hood.\n          </p>\n      </ImageBox>,\n      <ImageBox \n       image=\"gifs/defaults.gif\" title=\"Solid defaults\" \n       key=\"defaults\" education=\"true\">\n         <p>\n           No additional configuration is needed to most Scala CLI commands.\n         </p>\n         <p>\n           Scala CLI is configured out of the box to use the latest stable versions and other commands such as formatter or compiler contain reasonable defaults.\n         </p>\n     </ImageBox>,\n      <ImageBox \n      image=\"gifs/learning_curve.gif\" title=\"No learning curve\" \n      key=\"curve\" education=\"true\">\n        <p>\n          Scala CLI does not use a complex configuration language, its options are simple and self-explanatory.\n        </p>\n        <p>\n        There are no big differences in running repl or .scala files so expanding the results of repl session into a small project does not require learning new concepts from Scala CLI perspective\n        </p>\n    </ImageBox>,\n    <ImageBox \n      image=\"gifs/powerful_scripts.gif\" title=\"Scripts are as powerful as other programs\" key=\"scripts-as-apps\" scripting=\"true\">\n        <p>\n          Scripts in Scala CLI can use dependencies and other features as standard Scala programs. Scala CLI is command-line first, giving access to all its features without the need for any configuration files or specific project structure.\n        </p>\n    </ImageBox>,\n    <ImageBox \n      image=\"gifs/embeddable_scripts.gif\" title=\"Embeddable Scripts\" key=\"embed-scripts\" scripting=\"true\">\n        <p>\n         Scala CLI can be set up in shebang lines, making your *.scala or *.sc (or even .java or .md!) files runnable.\n        </p>\n        <p>\n         Scala CLI supports piping inputs and is designed to be embeddable in other scripts, turning Scala into proper scripting language.\n        </p>\n    </ImageBox>,\n//    <ImageBox \n//      image=\"fast-scripts.svg\" title=\"Fast Scripts\" key=\"fast-scripts\" scripting=\"true\">\n//        <p>\n//          Scala CLI provides multiple ways to reduce the biggest problem of JVM-based scripting solutions: slow start time. Scala CLI aggressively caches inputs removing need for recompilations.\n//        </p>\n//        <p>\n//          Scripts can be packaged into the native applications (using e.g. Scala Native) for even faster cold startups.\n//        </p>\n//    </ImageBox>,\n\n    // Prototyping\n\n    <ImageBox \n      image=\"gifs/self-contained-examples.gif\" title=\"Self-contained examples\" \n      key=\"self-contained-examples\" prototyping=\"true\">\n      <p>\n        With Scala CLI, configuration can be included in source code so complex examples can be self-contained and shipped as e.g. gist. Moreover, Scala CLI can compile, run and test gists without any manual work!\n      </p>\n      <p>\n        Scala CLI is the perfect tool to submit and reproduce bugs.\n      </p>\n    </ImageBox>\n    \n]\n\nexport default function allImageBoxs() {  \n  return featuresList \n}\n"
  },
  {
    "path": "website/src/components/osUtils.js",
    "content": "export const currentOs = () => {\n    const isCurrentOsEqual = (osShortName) => {\n        if(typeof window !== \"undefined\") {\n            return window.navigator.userAgent.indexOf(osShortName) !== -1\n        }\n        return false\n    }\n    if(isCurrentOsEqual(\"Win\")) return \"windows\"\n    if(isCurrentOsEqual(\"Mac\")) return \"mac\"\n    else return \"linux\"\n}"
  },
  {
    "path": "website/src/css/custom copy.css",
    "content": "/* stylelint-disable docusaurus/copyright-header */\n/**\n * Any CSS included here will be global. The classic template\n * bundles Infima by default. Infima is a CSS framework designed to\n * work well for content-centric websites.\n */\n\n/* You can override the default Infima variables here. */\n:root {\n  --ifm-color-primary: rgb(209, 30, 34);\n  --ifm-color-primary-dark: rgb(33, 175, 144);\n  --ifm-color-primary-darker: rgb(31, 165, 136);\n  --ifm-color-primary-darkest: rgb(26, 136, 112);\n  --ifm-color-primary-light: rgb(70, 203, 174);\n  --ifm-color-primary-lighter: rgb(102, 212, 189);\n  --ifm-color-primary-lightest: rgb(146, 224, 208);\n  --ifm-code-font-size: 95%;\n}\n\n.docusaurus-highlight-code-line {\n  background-color: rgb(72, 77, 91);\n  display: block;\n  margin: 0 calc(-1 * var(--ifm-pre-padding));\n  padding: 0 var(--ifm-pre-padding);\n}\n\n\n.flex-row {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.flex-row-reverse{\n  display: flex;\n  align-items: center;\n  flex-direction: row-reverse;\n}\n\n.img-holder {\n  min-width: 650px;\n}\n\n.img-holder img {\n  max-height: 350px;\n}\n\n@media only screen and (max-width: 1450px) {\n  .img-holder {\n    min-width: 450px;\n  }\n  \n  .img-holder img {\n    max-height: 350px;\n  }\n}\n\n@media only screen and (max-width: 1050px) {\n  .flex-row-reverse{\n    display: block;\n  }\n\n  .flex-row-reverse, .flex-row {\n    display: block; \n  }\n}\n\n.red_border {\n  border: rgb(209, 30, 34) 3px dotted;\n  margin: 10px;\n}\n.green_border {\n  border: green 3px dotted;\n  padding: 10px;\n  margin: 10px;\n}\n\n\n.content p:before {\n  content: \">_ \";\n  color: green;\n}\n\n.content h1:before {\n  content: \">_ \";\n  color: red;\n}\n\n.content h1 {\n  margin: 20px;\n}\n\n.content {\n  font-size: 15px;\n}\n.content h1,h2 {\n  font-family: monospace;\n}\n\n.no_monospace {\n  font-family: auto;\n}\n\n.feature-row:nth-child(even) {\n  flex-direction: row-reverse;\n}\n\n.feature-row:nth-child(even) .col--1:first-child {\n  display: none;\n}"
  },
  {
    "path": "website/src/css/custom.css",
    "content": ".section {\n  margin-top: 130px;\n  margin-bottom: 130px;\n}\n\n.section-image-box.image-left .section-image-box__row-text {\n  order: 2;\n}\n.section-image-box.image-left .section-image-box__row-image {\n  order: 1;\n}\n.section-image-box__row {\n  margin-bottom: 80px;\n}\n\n.section-image-box__row:nth-child(even) {\n  flex-direction: row-reverse;\n}\n\n/*TODO*/\n.feature-row:nth-child(even) .col--1:first-child {\n  display: none;\n}\n\n/*# sourceMappingURL=custom.css.map */\n"
  },
  {
    "path": "website/src/pages/education.js",
    "content": "import React from 'react';\n\nimport UseCase from \"../components/UseCase\"\n\nconst Index = (props) => {\n  return <UseCase\n    title=\"Education with Scala CLI\"\n    description=\"Page describing why Scala CLI is good within educational purposes, mainly learning Scala.\"\n    headline=\"Learn a language not a build tool\"\n    image=\"gifs/education.gif\"\n    id=\"education\"\n    >\n    <p>Scala CLI is designed in a way so you can focus on learning, not struggle with installation or build tool.</p>\n  </UseCase>;\n};\n\nexport default Index;\n"
  },
  {
    "path": "website/src/pages/index.js",
    "content": "import React from 'react';\nimport useDocusaurusContext from '@docusaurus/useDocusaurusContext';\nimport Layout from '@theme/Layout';\n\nimport Section from \"../components/Section\"\nimport ImageBox from \"../components/ImageBox\"\nimport SmallHeader from \"../components/SmallHeader\"\nimport IconBox from \"../components/IconBox\"\nimport SectionAbout from \"../components/SectionAbout\"\nimport UseCaseTile from \"../components/UseCaseTile\"\nimport BigHeader from \"../components/BigHeader\"\nimport BasicInstall from \"../components/BasicInstall\"\nimport YellowBanner from \"../components/YellowBanner\"\nimport allFeatures from '../components/features';\n\n\nconst Index = (props) => {\n  const { siteConfig } = useDocusaurusContext();\n  return (\n    <Layout title={siteConfig.title} description={siteConfig.tagline}>\n\n      <div className=\"container content\">\n\n\t\t{/* Headline */}\n\t\t<YellowBanner title=\"Scala CLI is a command-line tool to interact with the Scala language.\" image=\"gifs/demo.gif\">\n\t\t\t<p>\n\t\t\t\tIt lets you compile, run, test, and package your Scala code (and more!)\n\t\t\t</p>\n\t\t</YellowBanner>\n\t\t\t\n\n\n\t\t{/* About */}\n\t  \t<Section className=\"section-about\">\n\n\t\t\t<SectionAbout title=\"Why Scala CLI?\">\n\t\t\t\t<p>\n\t\t\t\t\tScala CLI combines <em>all</em> of the features you need to learn and use Scala in your scripts, playgrounds and (single-module) projects.\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t\tTo get started you can read <a href=\"/docs/overview\">the documentation</a>, or just <a href=\"/install\">install</a> and enjoy <code>scala-cli</code>.\n\t\t\t\t</p>\n\t\t\t\t<p>\n                    Scala CLI is the default Scala runner and is being shipped as <code>scala</code> along with <code>scalac</code> as part of the official language installation since <a href=\"https://github.com/scala/scala3/releases/tag/3.5.0\">Scala 3.5.0</a>. (<a href=\"/docs/reference/scala-command/\">read more</a>).\n\t\t\t\t</p>\n\t\t\t</SectionAbout>\n\n\t\t</Section>\n\n\n\t\t{/* Who is Scala CLI designed for? */}\n\t\t<Section className=\"section-features\">\n\n\n\n\t\t\t<div className=\"section-features__row row\">\n\t\t\t\t\n\t\t\t\t<IconBox title=\"Intuitive, simple\" icon=\"img/hand.png\">\n\t\t\t\t\t<strong>No complicated mechanisms, tasks, plugins or extensions:</strong> just a single-module. All our commands have multiple aliases and follow well-known conventions.\n\t\t\t\t</IconBox>\n\n\t\t\t\t<IconBox title=\"Fast\" icon=\"img/rocket.png\">\n\t\t\t\t\t<strong>Scala CLI is optimized to be as fast as possible.</strong> CLI is compiled to native code and compilations are <a href=\"/docs/reference/bloop\">offloaded to bloop</a>.\n\t\t\t\t</IconBox>\n\n\t\t\t\t<IconBox title=\"Command-line first\" icon=\"img/monitor.png\">\n\t\t\t\t\t<strong>Scala CLI does not require a configuration file, and all in-file configurations can be overridden by command-line.</strong> No additional installation or setup of an environment (such as a specific working directory) are required.\n\t\t\t\t</IconBox>\n\n\t\t\t</div>\n\t\t\n\t\t</Section>\n\n\n\n\t\t<div id=\"use_cases\"/>\n\n\t\t{/* Who is Scala CLI designed for? */}\n\t\t<Section className=\"section-use-tiles\">\n          <div className=\"row\">\n\n            <BigHeader title=\"Who is Scala CLI designed for?\" colsize=\"12\" promptsign={true}></BigHeader>\n\n            <div className=\"col col--12\">\n              <div className=\"use-boxes row\">\n\n                <UseCaseTile title=\"Education\"\n                             slug=\"education\"\n                             description=\"Scala CLI is a help — not a distraction — while learning Scala, a library or programming in general.\">\n                </UseCaseTile>\n\n                <UseCaseTile title=\"Scripting\"\n                             slug=\"scripting\"\n                             description=\"Scala CLI has all the tools to create (or be integrated into) scripts with the whole power of the Scala ecosystem.\">\n                </UseCaseTile>\n\n                <UseCaseTile title=\"Prototyping, Experimenting, Reproducing\"\n                             slug=\"prototyping\"\n                             description=\"With Scala CLI, experimenting with different libraries, Scala or JVM versions, or compiler options is easy and fun.\">\n                </UseCaseTile>\n\n                <UseCaseTile title=\"Single-module projects\"\n                             slug=\"projects\"\n                             description=\"Scala CLI provides all the tools you need to manage single-module projects like CLI or basic web applications, or server-less lambdas.\">\n                </UseCaseTile>\n\n                <UseCaseTile title=\"Your use case\"\n                             slug={false}\n                             description=\n                             {<span>If you see other use cases for Scala CLI, let us know using <a href=\"https://github.com/VirtusLab/scala-cli/discussions/categories/ideas\">GitHub Discussions!</a></span>}>\n                </UseCaseTile>\n\n              </div>\n            </div>\n\n          </div>\n        </Section>\n\n\n\t\t{/* Install Scala CLI */}\n        <Section className=\"section-install-cli\">\n\t\t\t<div className=\"row\">\n\n\t\t\t\t<BigHeader title=\"Install Scala CLI\" colsize=\"4\" promptsign={true}></BigHeader>\n\n\t\t\t\t<div className=\"col col--8\">\n\t\t\t\t\t<BasicInstall/>\n\t\t\t\t</div>\n\n\t\t\t</div>\n        </Section>\n\n\n\t\t{/* Still undecided? */}\n\t\t<Section className=\"section-image-box\">\n\n\t\t\t<SmallHeader title=\"Still undecided?\">\n\t\t\t\tHere come our <span>main features</span>\n\t\t\t</SmallHeader>\n\t\n\t\t\t\n\t\t\t{allFeatures()}\n\t\t</Section>\n\t\t\t\t\n\t\t</div>\n\n    </Layout>\n  );\n};\n\nexport default Index;\n"
  },
  {
    "path": "website/src/pages/index.module.css",
    "content": "/* stylelint-disable docusaurus/copyright-header */\n\n/**\n * CSS files with the .module.css suffix will be treated as CSS modules\n * and scoped locally.\n */\n\n.heroBanner {\n  padding: 4rem 0;\n  text-align: center;\n  position: relative;\n  overflow: hidden;\n}\n\n@media screen and (max-width: 966px) {\n  .heroBanner {\n    padding: 2rem;\n  }\n}\n\n.buttons {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n"
  },
  {
    "path": "website/src/pages/install.js",
    "content": "import React from 'react';\nimport Layout from '@theme/Layout';\nimport Heading from '@theme/Heading';\n\nimport Section from \"../components/Section\"\nimport BigHeader from \"../components/BigHeader\"\nimport BasicInstall from \"../components/BasicInstall\"\nimport AdvancedInstallation from '../../docs/_advanced_install.mdx'\n\nconst Index = (props) => {\n  return (\n    <Layout title='Install Scala CLI' description=\"How to install Scala CLI\">\n\n      <div className=\"container content\">\n\n\t\t{/* Install Scala CLI */}\n        <Section className=\"section-install-cli\">\n\t\t\t<div className=\"row\">\n\n\t\t\t\t<BigHeader title=\"Quick start\" colsize=\"4\" promptsign={true}></BigHeader>\n\n\t\t\t\t<div className=\"col col--8\">\n\t\t\t\t\t<BasicInstall/>\n\t\t\t\t</div>\n\n\n\t\t\t</div>\n        </Section>\n\t\t\n\t\t\n\n\t\t<Section className=\"section-about advanced-install\">\n\t\t\t{/* Anchor targets for deep linking - using Heading so Docusaurus can detect them */}\n\t\t\t<Heading as=\"h2\" id=\"advanced-installation\" style={{display: 'none', margin: 0, padding: 0}}>Advanced Installation</Heading>\n\t\t\t<Heading as=\"h2\" id=\"scala-js\" style={{display: 'none', margin: 0, padding: 0}}>Scala.js</Heading>\n\t\t\t<Heading as=\"h2\" id=\"scala-native\" style={{display: 'none', margin: 0, padding: 0}}>Scala Native</Heading>\n\t\t\t<AdvancedInstallation/>\n\n\t\t</Section>\n\t\t</div>\n    </Layout>\n  );\n};\n\nexport default Index;\n"
  },
  {
    "path": "website/src/pages/markdown-page.md",
    "content": "---\ntitle: Markdown page example\n---\n\n# Markdown page example\n\nYou don't need React to write simple standalone pages.\n"
  },
  {
    "path": "website/src/pages/projects.js",
    "content": "import React from 'react';\n\nimport UseCase from \"../components/UseCase\"\n\nconst Index = (props) => {\n  return <UseCase\n    title=\"Single-module projects with Scala CLI\"\n    description=\"Page describing why Scala CLI is good for maintaining single-module projects.\"\n    headline=\"Fight with your bugs, not with your build tool\"\n    image=\"gifs/projects.gif\"\n    id=\"projects\"\n    >\n    <p>Scala CLI provides all the functionality to easily maintain single module projects like cli apps or simple microservices.</p>\n  </UseCase>;\n};\n\nexport default Index;\n"
  },
  {
    "path": "website/src/pages/prototyping.js",
    "content": "import React from 'react';\n\nimport UseCase from \"../components/UseCase\"\n\nconst Index = (props) => {\n  return <UseCase\n    title=\"Prototyping, experimenting, reproducing bugs with Scala CLI\"\n    description=\"Page describing why Scala CLI is good for prototyping / experimenting / reproducing bugs.\"\n    headline=\"Move fast and break things but be in control of your build\"\n    image=\"gifs/prototyping.gif\"\n    id=\"prototyping\"\n    >\n    <p>Have you ever wasted time prototyping, experimenting or reproducing a nasty bug by testing in a different environment than you intended?</p>\n\n    <p>With Scala CLI you can explicitly define Scala or JVM versions, platform, compiler options and dependencies by setting them as arguments.</p>\n  </UseCase>;\n};\n\nexport default Index;\n"
  },
  {
    "path": "website/src/pages/scripting.js",
    "content": "import React from 'react';\n\nimport UseCase from \"../components/UseCase\"\n\nconst Index = (props) => {\n  return <UseCase\n    title=\"Scripting with Scala CLI\"\n    description=\"Page describing why Scala CLI is good for scripting with Scala.\"\n    headline=\"Scripting using all the powers of the Scala ecosystem\"\n    image=\"gifs/scripting.gif\"\n    id=\"scripting\"\n    >\n      <p>Scala CLI allows you to use Scala to create and enhance scripts with using all the goodies of Scala.</p>\n\n      <p>Use dependencies, declare tests or even package your scripts into native applications!</p>\n  </UseCase>;\n};\n\nexport default Index;\n\n// const Index = (props) => {\n//   const { siteConfig } = useDocusaurusContext();\n//   return (\n//     <Layout title=\"Scripting with Scala CLI\" description=\"Page describing why Scala CLI is good for scripting with Scala.\">\n//       <div className=\"container padding--sm content\">\n\n//         <HeaderSection image=\"img/fast-scala-cli.gif\">\n//           <h1>Scripting using all powers of Scala ecosystem</h1>\n//           {/* TODO: better text */}\n//           <p>Scala CLI allows you to use Scala to create and enhance scripts with using all the goodies of Scala.</p>\n\n//           <p>Use dependencies, declare tests or even package your scripts into native applications!</p>\n//         </HeaderSection>\n\n//         <h1>Scripting with Scala CLI</h1>\n\n\n//       </div>\n//     </Layout>\n//   );\n// };\n"
  },
  {
    "path": "website/src/pages/spark.md",
    "content": "# Experimental Spark features\n\nimport {ChainedSnippets, GiflikeVideo} from \"../../src/components/MarkdownComponents.js\";\n\n## Packaging\n\nThe `package` sub-command offers to package Scala CLI projects as JARs ready to be passed\nto `spark-submit`, and optimized for it.\n\n<ChainedSnippets>\n\n```scala title=SparkJob.scala\n//> using dep org.apache.spark::spark-sql:3.0.3\n//> using scala 2.12.15\n\nimport org.apache.spark._\nimport org.apache.spark.sql._\n\nobject SparkJob {\n  def main(args: Array[String]): Unit = {\n    val spark = SparkSession.builder()\n      .appName(\"Test job\")\n      .getOrCreate()\n    import spark.implicits._\n    def sc    = spark.sparkContext\n    val accum = sc.longAccumulator\n    sc.parallelize(1 to 10).foreach(x => accum.add(x))\n    println(\"Result: \" + accum.value)\n  }\n}\n```\n\n```bash\nscala-cli --power package --spark SparkJob.scala -o spark-job.jar\n```\n\n```text\nCompiling project (Scala 2.12.15, JVM)\nCompiled project (Scala 2.12.15, JVM)\nWrote spark-job.jar\n```\n\n```bash\nspark-submit spark-job.jar\n```\n\n```text\n…\nResult: 55\n…\n```\n\n</ChainedSnippets>\n\n## Running Spark jobs\n\nThe `run` sub-command can run Spark jobs, when passed `--spark`:\n\n```bash\nscala-cli run --spark SparkJob.scala # same example as above\n```\n\nNote that this requires either\n- `spark-submit` to be in available in `PATH`\n- `SPARK_HOME` to be set in the environment\n\n## Running Spark jobs in a standalone way\n\nThe `run` sub-command can not only run Spark jobs, but it can also work without a Spark\ndistribution. For that to work, it downloads Spark JARs, and calls the main class of\n`spark-submit` itself via these JARs:\n\n```bash\nscala-cli run --spark-standalone SparkJob.scala # same example as above\n```\n\n## Running Hadoop jobs\n\nThe `run` sub-command can run Hadoop jobs, by calling the `hadoop jar` command under-the-hood:\n\n<ChainedSnippets>\n\n```java title=WordCount.java\n//> using dep org.apache.hadoop:hadoop-client-api:3.3.3\n\n// from https://hadoop.apache.org/docs/r3.3.3/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html\n\nimport java.io.IOException;\nimport java.util.StringTokenizer;\n\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.fs.Path;\nimport org.apache.hadoop.io.IntWritable;\nimport org.apache.hadoop.io.Text;\nimport org.apache.hadoop.mapreduce.Job;\nimport org.apache.hadoop.mapreduce.Mapper;\nimport org.apache.hadoop.mapreduce.Reducer;\nimport org.apache.hadoop.mapreduce.lib.input.FileInputFormat;\nimport org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;\n\npublic class WordCount {\n\n  public static class TokenizerMapper\n       extends Mapper<Object, Text, Text, IntWritable>{\n\n    private final static IntWritable one = new IntWritable(1);\n    private Text word = new Text();\n\n    public void map(Object key, Text value, Context context\n                    ) throws IOException, InterruptedException {\n      StringTokenizer itr = new StringTokenizer(value.toString());\n      while (itr.hasMoreTokens()) {\n        word.set(itr.nextToken());\n        context.write(word, one);\n      }\n    }\n  }\n\n  public static class IntSumReducer\n       extends Reducer<Text,IntWritable,Text,IntWritable> {\n    private IntWritable result = new IntWritable();\n\n    public void reduce(Text key, Iterable<IntWritable> values,\n                       Context context\n                       ) throws IOException, InterruptedException {\n      int sum = 0;\n      for (IntWritable val : values) {\n        sum += val.get();\n      }\n      result.set(sum);\n      context.write(key, result);\n    }\n  }\n\n  public static void main(String[] args) throws Exception {\n    Configuration conf = new Configuration();\n    Job job = Job.getInstance(conf, \"word count\");\n    job.setJarByClass(WordCount.class);\n    job.setMapperClass(TokenizerMapper.class);\n    job.setCombinerClass(IntSumReducer.class);\n    job.setReducerClass(IntSumReducer.class);\n    job.setOutputKeyClass(Text.class);\n    job.setOutputValueClass(IntWritable.class);\n    FileInputFormat.addInputPath(job, new Path(args[0]));\n    FileOutputFormat.setOutputPath(job, new Path(args[1]));\n    System.exit(job.waitForCompletion(true) ? 0 : 1);\n  }\n}\n```\n\n```bash\nscala-cli run --hadoop WordCount.java\n```\n\n</ChainedSnippets>\n"
  },
  {
    "path": "website/src/scss/_variables.scss",
    "content": "$theme-red: #F23135;\n$theme-yellow: #FFD583;\n\n"
  },
  {
    "path": "website/src/scss/components/runnable-sample.scss",
    "content": ":root {\n  --sample-background-color: rgb(246, 248, 250);\n}\n\nhtml[data-theme=\"dark\"] {\n  --sample-background-color: rgb(40, 42, 54);\n}\n\n.runnable-command {\n  background-color: var(--sample-background-color);\n  padding-left: var(--ifm-pre-padding);\n  padding-right: var(--ifm-pre-padding);\n  border-radius: var(--ifm-global-radius);\n  box-shadow: var(--ifm-global-shadow-lw);\n  padding-bottom: 8px;\n  padding-top: 8px;\n\n  .codeBlockContainer_node_modules-\\@docusaurus-theme-classic-lib-next-theme-CodeBlock-styles-module {\n    margin-bottom: 0px;\n  }\n\n  margin-bottom: var(--ifm-leading);\n\n  .codeBlockContainer_node_modules-\\@docusaurus-theme-classic-lib-next-theme-CodeBlock-styles-module,\n  div[class^='codeBlockContainer'] {\n    box-shadow: none;\n    border-radius: unset;\n  }\n\n  code[class^=\"codeBlockLines\"] {\n    padding-left: 0;\n    padding-right: 0;\n    padding-bottom: 8px;\n    padding-top: 8px;\n    &:hover {\n      opacity: .80;\n    }\n    \n  }\n}\n\n.bash .prism-code  {\n  .token-line::before {\n    content: \"$ \";\n    color: rgb(189, 147, 249)\n  }\n}"
  },
  {
    "path": "website/src/scss/components/section-about.scss",
    "content": "\n.section-about{\n\n    &__wrapper{\n        @media ( max-width: 375px){\n            padding: 0 20px;\n        }\n    }\n\n    .big-title{\n        font-family: Roboto Mono;\n        font-style: normal;\n        font-weight: bold;\n        font-size: 36px;\n        line-height: 48px;\n\n        @media ( max-width: 998px){\n            font-size: 32px;\n            line-height: 44px;\n        }\n\n        @media ( max-width: 375px){\n            font-size: 28px;\n            line-height: 34px;\n\n            margin-bottom: 15px;\n        }\n\n        .pre-title-mobile a{\n            color: #DE3D3B;\n            text-decoration: none;\n\n            @media ( min-width: 376px){\n                display: none;\n            }\n        }\n\n        &.pre-title a{\n            text-align: right;\n            padding-left: 20px;\n            color: #DE3D3B;\n            text-decoration: none;\n\n            @media ( max-width: 375px){\n                display: none;\n            }\n        }\n    }\n\n    .description{\n        font-family: Segoe UI;\n        font-style: normal;\n        font-weight: normal;\n        font-size: 20px;\n        line-height: 36px;\n\n        @media ( max-width: 375px){\n            font-size: 17px;\n            line-height: 30px;\n        }\n\n        a{\n            color: #F23135;\n\n            &:hover,\n            &:focus{\n                color: #AF3A38;\n            }\n        }\n\n        span{\n            display: inline-block;\n            padding: 3px 10px;\n            background: #FFD583;\n            border-radius: 4px;\n\n            font-family: Roboto Mono;\n            font-style: normal;\n            font-weight: normal;\n            font-size: 16px;\n            line-height: 24px;\n\n            @media ( max-width: 375px){\n                font-size: 15px;\n                line-height: 20px;\n            }\n        }\n\n        p{\n            margin-bottom: 25px;\n\n            @media ( max-width: 375px){\n                margin-bottom: 15px;\n            }\n\n            &:last-child{\n                margin-bottom: 0;\n            }\n        }\n    }\n\n}\n\nhtml[data-theme=\"dark\"]{\n    .section-about{\n        .description{\n            span{\n                background-color: #3F456E;\n            }\n        }\n    }\n\n    .big-title:not(.pre-title){\n        color: #FFD583;\n\n    }\n}\n\n.advanced-install {\n    .big-title {\n        font-size: 32px;\n    }\n\n    .advanced_install_methods {\n        [role=tabpanel] {\n            min-height: 120px;\n        }\n    }\n}\n\n.advanced_install_methods>.tabs-container>.tabs {\n    display: none;\n}"
  },
  {
    "path": "website/src/scss/components/section-base.scss",
    "content": "\n.section{\n    margin-top: 130px;\n    margin-bottom: 130px;\n    cursor: default;\n\n    a{\n        cursor: pointer;\n        transition: 0.3s ease all;\n    }\n\n    @media ( max-width: 375px){\n        margin-top: 80px;\n        margin-bottom: 80px;\n    }\n\n    &__header{\n        font-family: Roboto Mono;\n        font-style: normal;\n        font-weight: normal;\n        text-align: center;\n        margin-bottom: 60px;\n\n        @media ( max-width: 375px){\n            text-align: left;\n            margin-bottom: 40px;\n            padding: 0 20px;\n        }\n\n        h2{\n            font-weight: bold;\n            font-size: 28px;\n            line-height: 44px;\n            margin-bottom: 6px;\n\n            @media ( max-width: 998px){\n                font-size: 28px;\n                line-height: 38px;  \n            }\n\n            @media ( max-width: 375px){\n                font-size: 24px;\n                line-height: 34px;\n                margin-bottom: 4px;\n            }\n        }\n\n        &-description{\n            font-size: 20px;\n            line-height: 36px;\n\n            @media ( max-width: 998px){\n                font-size: 20px;\n                line-height: 36px;\n            }\n\n            @media ( max-width: 375px){\n                font-size: 17px;\n                line-height: 25px;\n            }\n\n            span{\n                position: relative;\n                z-index: 0;\n\n                &:before{\n                    display: block;\n                    content: '';\n                    position: absolute;\n                    bottom: 0; left: 50%;\n                    transform: translateX(-50%);\n                    width: 105%;\n                    height: 8px;\n                    background-color: #F9CF73;\n                    z-index: -1;\n\n                    @media ( max-width: 375px){\n                        height: 5px;\n                    }\n                }\n            }\n        }\n    }\n\n    a{\n        color: #1C1C1C;\n        text-decoration: underline;\n    \n        &:hover, &:focus{\n            color: #F23135;\n        }\n    }\n}\n\n\nhtml[data-theme=\"dark\"] {\n    .section {\n        a:not(:hover):not(:focus) {\n            color: #f5f6f7;\n        }\n    }\n}"
  },
  {
    "path": "website/src/scss/components/section-features.scss",
    "content": "\n.section-features{\n    &__row {\n        margin-bottom: -30px;\n    }\n\n    &__item{\n        margin-bottom: 30px;\n\n        &-wrapper{\n            max-width: 300px;\n            margin: 0 auto;\n            text-align: center;\n \n            @media ( max-width: 375px){\n                text-align: left;\n            }\n\n            .icon{\n                margin-bottom: 25px;\n\n                @media ( max-width: 375px){\n                    margin-bottom: 15px;\n                }\n\n                img{\n                    border-radius: 500px;\n                    width: 65px; height: 65px;\n                    filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));\n                    box-shadow: 0px 10px 15px rgba(227, 50, 47, 0.15), 0px 10px 10px -30px rgba(227, 50, 47, 0.25);\n\n                    @media ( max-width: 375px){\n                        width: 48px; height: 48px;\n                    }\n                }\n            }\n\n            .title{\n                font-family: Roboto Mono;\n                font-style: normal;\n                font-weight: 500;\n                font-size: 20px;\n                line-height: 24px;\n                margin-bottom: 20px;\n\n                @media ( max-width: 998px){\n                    font-size: 18px;\n                    line-height: 24px;\n                }\n\n                @media ( max-width: 375px){\n                    margin-bottom: 10px;\n                }\n                \n            }\n\n            .desc{\n                font-size: 16px;\n                line-height: 28px;\n                letter-spacing: 0.01em;\n                font-weight: 400;\n\n                @media ( max-width: 998px){\n                    font-size: 15px;\n                    line-height: 25px;\n                }\n\n                @media ( max-width: 375px){\n\n                }\n\n                strong{\n                    font-weight: 600;\n                }\n\n                a{\n                    color: #1c1c1c;\n\n                    &:hover{\n                        color: #F23135;\n                    }\n                }\n            }\n        }\n    }\n}\n\nhtml[data-theme=\"dark\"] {\n    .section-features{\n        a{\n            color: #fafafa;\n\n            &:hover{\n                color: #F23135;\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "website/src/scss/components/section-image-box.scss",
    "content": "\n\n.section-image-box{\n\n    &__row{\n        &.image-left{\n            .section-image-box__row-text{\n                order: 2;\n\n                @media ( max-width: 375px){\n                    order: 1;\n               } \n            }\n            .section-image-box__row-image{\n                order: 1;\n\n                @media ( max-width: 375px){\n                     order: 2;\n                } \n            }\n        }\n    }\n    \n    &__row{\n        margin-bottom: 80px;\n        align-items: center;\n\n        &:nth-child(even) {\n            flex-direction: row-reverse;\n            .left-margin-stub {\n                display: none;\n            }\n        }\n\n        &:nth-child(odd) {\n            .right-margin-stub {\n                display: none;\n            }\n        }\n\n\n\n        @media ( max-width: 375px){\n            margin-bottom: 80px;\n        }\n\n        &-image{\n            &-wrapper{\n                border: 1px dashed #828282;\n                border-radius: 12px;\n\n                @media ( max-width: 375px){\n                    border: none;\n                    border-radius: 0;\n                }\n\n                img{\n                    box-shadow: 0px 40px 40px -30px rgba(227, 50, 47, 0.25);\n                    filter: drop-shadow(0px 20px 40px rgba(227, 50, 47, 0.1));\n                    border-radius: 6px;\n                    margin: 20px;\n                    width: calc(100% - 40px);\n                    height: auto;\n\n                    @media ( max-width: 375px){\n                        margin: 30px 0 0 0;\n                        width: 100%;\n                    }\n                }\n            }\n        }\n\n        &-text{\n            &-wrapper{\n                @media ( max-width: 375px){\n                    padding: 0 20px !important;\n                }\n\n                h3{\n                    font-family: Roboto Mono;\n                    font-style: normal;\n                    font-weight: bold;\n\n                    font-size: 20px;\n                    line-height: 24px;\n                    margin-bottom: 30px;\n\n                    @media ( max-width: 375px){\n                        font-size: 18px;\n                        line-height: 24px;\n                        margin-bottom: 15px;\n                    }\n                }\n\n                .content{\n                    font-family: Segoe UI;\n                    font-style: normal;\n                    font-weight: normal;\n                    font-size: 16px;\n                    line-height: 28px;\n\n                    @media ( max-width: 375px){\n                        font-size: 15px;\n                        line-height: 25px;\n                    }\n\n                    p{\n                        margin-bottom: 25px;\n\n                        @media ( max-width: 375px){\n                            margin-bottom: 15px;\n                        }\n\n                        &:last-child{\n                            margin-bottom: 0;\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n}\n\nhtml[data-theme=\"dark\"]{\n    .section-image-box{\n        .section-image-box__row-image-wrapper{\n            border-color: #F23135;\n        }\n    }\n}"
  },
  {
    "path": "website/src/scss/components/section-install-cli.scss",
    "content": "@use '../variables' as *;\n\n.section-install-cli {\n    margin-top: 130px;\n    margin-bottom: 130px;\n\n    padding: 80px 45px 80px 30px;\n\n    border-radius: 8px;\n    border: 1px solid #1c1c1c;\n    background-color: #1c1c1c;\n    box-shadow: 0px 20px 40px rgba(227, 50, 47, 0.1), 0px 40px 40px -30px rgba(227, 50, 47, 0.25);\n\n    @media (max-width: 998px) {\n        margin-top: 110px;\n        margin-bottom: 110px;\n\n        padding: 70px 30px 70px 25px;\n    }\n\n    @media (max-width: 375px) {\n        margin-top: 80px;\n        margin-bottom: 80px;\n\n        padding: 50px 20px;\n    }\n\n    .section-title.with-before {\n        margin-bottom: 20px;\n        color: #F8F9FC;\n\n        @media (min-width: 999px) {\n            padding-right: 22px;\n        }\n    }\n\n    .margin-vert--md {\n        margin-top: 30px !important;\n        margin-bottom: 0 !important;\n\n        @media (max-width: 998px) {\n            margin-top: 20px !important;\n        }\n\n        @media (max-width: 375px) {\n            margin-top: 30px !important;\n        }\n    }\n\n    div[role=tabpanel] {\n        a, p {\n            font-family: 'Segoe UI';\n            font-size: 16px;\n            line-height: 24px;\n        }\n\n        a {\n            color: $theme-red;\n            text-decoration: underline;\n        }\n\n        p {\n            margin-bottom: 15px;\n\n            color: #F2F4F8;\n        }\n\n        code {\n            display: block;\n            padding: 6px 14px;\n\n            background: #453E43;\n            border: 1px solid rgba(229, 229, 229, 0.3);\n            box-sizing: border-box;\n            border-radius: 4px;\n\n            font-family: \"Roboto Mono\", monospace;\n            font-size: 14px;\n            line-height: 24px;\n            color: #ffffff;\n\n            @media (max-width: 998px) {\n                padding: 6px 17px;\n                font-size: 13px;\n            }\n        }\n    }\n\n}\n\n.tabs-container {\n    .tabs {\n        overflow: visible;\n        flex-wrap: wrap;\n\n        margin: -5px;\n\n        @media (max-width: 375px) {\n            margin: -10px;\n        }\n    }\n\n    .tabs__item {\n        margin: 5px !important;\n\n        padding: 9px 25px;\n        background-color: #453E43;\n        border: 1.5px solid #453E43;\n        box-sizing: border-box;\n        border-radius: 30px;\n\n        font-family: \"Roboto Mono\", monospace;\n        font-size: 14px;\n        line-height: 22px;\n        font-weight: 700;\n        color: #ffffff;\n\n        transition: 0.3s background-color ease-out,\n                    0.3s color ease-out,\n                    0.3s border-color ease-out;\n\n        @media (max-width: 375px) {\n            margin: 10px !important;\n            font-size: 13px;\n        }\n\n        &:hover:not(.tabs__item--active) {\n            border: 1.5px solid #FFD583;\n            color: #FFD583;\n        }\n\n        &--active {\n            background: $theme-yellow;\n            border-color: $theme-yellow;\n            color: #1C1C1C;\n\n            box-shadow: 0px 8px 15px rgba(255, 213, 131, 0.1);\n        }\n    }\n\n}\n\nhtml[data-theme=\"dark\"] {\n    .section-install-cli {\n        border-color: $theme-red;\n        background-color: #242526;\n        box-shadow: 0px 20px 40px rgba(227, 50, 47, 0.05), 0px 40px 40px -30px rgba(227, 50, 47, 0.15);\n    }\n}"
  },
  {
    "path": "website/src/scss/components/section-use-tiles.scss",
    "content": "@use '../variables' as *;\n\n.section-use-tiles {\n    margin-top: 130px;\n    margin-bottom: 130px;\n\n    @media (max-width: 998px) {\n        margin-top: 110px;\n        margin-bottom: 110px;\n    }\n\n    @media (max-width: 375px) {\n        margin-top: 80px;\n        margin-bottom: 80px;\n    }\n\n    a{\n        text-decoration: none;\n    }\n\n    .section-title {\n        margin-bottom: 46px;\n\n        @media (max-width: 998px) {\n            margin-bottom: 50px;\n            margin-left: 25px;\n        }\n\n        @media (max-width: 375px) {\n            margin-bottom: 25px;\n            margin-left: 20px;\n        }\n    }\n\n    .use-boxes {\n        margin-bottom: -30px;\n\n        @media (max-width: 375px) {\n            margin-bottom: -20px;\n        }\n\n        .use-box-wrapper {\n            margin-bottom: 30px;\n\n            @media (max-width: 375px) {\n                margin-bottom: 20px;\n            }\n\n            &:hover {\n                text-decoration: none;\n\n                .read-more{\n                    color: #E3322F;\n                }\n            }\n\n            &:active {\n                .read-more {\n                    color: #AF3A38;\n\n                    &:before {\n                        color: #AF3A38;\n                    }\n                }\n            }\n\n            .use-box {\n                display: flex;\n                flex-direction: column;\n\n                height: 100%;\n                padding: 40px 30px 15px;\n\n                border: 1px solid #828282;\n                border-radius: 6px;\n                box-sizing: border-box;\n\n                background: #FFFFFF;\n                color: #444444;;\n                box-shadow: 0px 15px 15px rgba(49, 41, 47, 0.03);\n\n                @media (max-width: 998px) {\n                    padding: 30px 30px 15px;\n                }\n\n                @media (max-width: 375px) {\n                    padding: 30px 23px 20px;\n                }\n\n                &.your-case {\n                    background-color: $theme-yellow;\n                    color: #1C1C1C;\n                    box-shadow: 0px 15px 15px rgba(255, 213, 131, 0.15);\n                }\n\n                .icon-wrapper {\n                    margin-bottom: 25px;\n\n                    @media (max-width: 998px) {\n                        margin-bottom: 18px;\n                    }\n\n                    img {\n                        width: auto;\n                        height: 32px;\n\n                        &.dark-theme {\n                            display: none;\n                        }\n                    }\n                }\n\n                h3 {\n                    margin-bottom: 10px;\n\n                    font-family: \"Roboto Mono\", monospace;\n                    font-size: 16px;\n                    line-height: 24px;\n                    font-weight: 700;\n                    color: #1C1C1C;\n                }\n\n                p {\n                    flex-grow: 1;\n\n                    font-family: 'Segoe UI';\n                    font-size: 16px;\n                    line-height: 28px;\n                    letter-spacing: 0.01em;\n\n                    @media (max-width: 375px) {\n                        font-size: 15px;\n                        line-height: 25px;\n                    }\n                }\n\n                .read-more-wrap {\n                    text-align: right;\n\n                    .read-more {\n                        display: inline-block;\n                        transition: 0.3s ease all;\n\n                        font-family: 'Roboto Mono', monospace;\n                        font-size: 13px;\n                        line-height: 25px;\n                        font-weight: 700;\n                        text-transform: uppercase;\n                    }\n                }\n            }\n        }\n    }\n}\n\nhtml[data-theme=\"dark\"] {\n    .section-use-tiles .use-boxes .use-box-wrapper .use-box {\n        border-color: #4F4F4F;\n        background: #242526;\n        color: #F3F3F3;\n\n        h3 {\n            color: #FFFFFF;\n        }\n\n        &.your-case {\n            border-color: $theme-yellow;\n            background: #242526;\n            color: $theme-yellow;\n            box-shadow: 0px 15px 15px rgba(255, 213, 131, 0.05);\n\n            h3 {\n                color: $theme-yellow;\n            }\n\n            .icon-wrapper img {\n                &.light-theme {\n                    display: none;\n                }\n\n                &.dark-theme {\n                    display: block;\n                }\n            }\n        }\n    }\n\n    .use-box-wrapper {\n        &:hover {\n            .read-more{\n                color: #FFD583 !important;\n\n                &:before{\n                    color: #FFD583 !important;\n                }\n            }\n        }\n\n        &:active {\n            .read-more{\n                color: #FBE5BB !important;\n\n                &:before{\n                    color: #FBE5BB !important;\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "website/src/scss/components/section-yellow-banner.scss",
    "content": "@use '../variables' as *;\n\n.section-yellow-banner {\n    margin-top: 50px;\n    margin-bottom: 130px;\n\n    padding: 80px 68px;\n\n    border-radius: 12px;\n    border: 1px solid $theme-yellow;\n    background: $theme-yellow;\n    box-shadow: 0px 40px 60px rgba(249, 207, 115, 0.25), 0px 50px 60px -30px rgba(249, 207, 115, 0.3);\n\n    @media (max-width: 998px) {\n        margin-top: 40px;\n        margin-bottom: 110px;\n\n        padding: 70px 50px 50px;\n    }\n\n    @media (max-width: 768px) {\n        padding: 70px 50px 0;\n        box-shadow: 0px 30px 60px rgba(249, 207, 115, 0.2), 0px 30px 60px -30px rgba(249, 207, 115, 0.3);\n    }\n\n    @media (max-width: 575px) {\n        padding: 50px 20px 0;\n    }\n\n    @media (max-width: 375px) {\n        margin-top: 30px;\n        margin-bottom: 80px;\n    }\n\n    h1 {\n        margin-bottom: 30px;\n\n        font-family: \"Roboto Mono\", monospace;\n        font-size: 38px;\n        line-height: 49px;\n        font-weight: 500;\n        color: #1c1c1c;\n\n        @media (max-width: 998px) {\n            margin-bottom: 20px;\n\n            font-size: 34px;\n            line-height: 42px;\n        }\n\n        @media (max-width: 375px) {\n            margin-bottom: 30px;\n\n            font-size: 28px;\n            line-height: 34px;\n        }\n    }\n\n    .description {\n        font-family: \"Roboto Mono\", monospace;\n        font-size: 20px;\n        line-height: 30px;\n        font-weight: 400;\n        color: #1c1c1c;\n        opacity: 0.8;\n\n        @media (max-width: 966px) {\n            margin-bottom: 30px;\n        }\n\n        @media (max-width: 375px) {\n            font-size: 18px;\n            line-height: 26px;\n        }\n\n        p{\n            margin-bottom: 25px;\n\n            @media ( max-width: 375px){\n                margin-bottom: 15px;\n            }\n\n            &:last-child{\n                margin-bottom: 0;\n            }\n        }\n    }\n\n    .image-wrapper {\n        display: flex;\n        border-radius: 6px;\n        box-shadow: 0px 40px 40px -30px rgba(227, 50, 47, 0.25);\n        filter: drop-shadow(0px 20px 40px rgba(227, 50, 47, 0.1));\n\n        overflow: hidden;\n\n        @media (max-width: 768px) {\n            width: calc(100% + 100px);\n            margin-left: -50px;\n\n            border-radius: 0 0 6px 6px;\n\n            box-shadow: 0px 10px 10px -30px rgba(227, 50, 47, 0.25);\n            filter: drop-shadow(0px 10px 15px rgba(227, 50, 47, 0.15));\n        }\n\n        @media (max-width: 575px) {\n            width: calc(100% + 40px);\n            margin-left: -20px;\n        }\n\n        .image {\n            width: 100%;\n\n            background-position: center;\n            background-size: cover;\n        }\n    }\n}\n\nhtml[data-theme=\"dark\"] {\n    .section-yellow-banner {\n        background: #242526;\n        box-shadow: none;\n\n        h1 {\n            color: #FAFAFA;\n        }\n\n        p {\n            color: $theme-yellow;\n        }\n    }\n}"
  },
  {
    "path": "website/src/scss/components/tooltip.scss",
    "content": "\n.tooltip{\n    position: relative;\n    display: inline-block;\n\n    &:hover{    \n        &:after{\n            content: attr(data-tooltip);\n            border-radius: 4px;\n            width: 200px;\n            display: block;\n            position: absolute;\n            top: -10px; left: 50%;\n            transform: translate(-50%, -100%);\n            background-color: #1C1C1C;\n            color: #FAFAFA;\n            padding: 10px 15px;\n            text-align: center;\n            font-size: 14px;\n            line-height: 17px;\n        }\n\n        &:before{\n            content: '';\n            display: block;\n            width: 0; \n            height: 0; \n            border-left: 20px solid transparent;\n            border-right: 20px solid transparent;\n            \n            border-top: 20px solid #1C1C1C;\n            position: absolute;\n            top: 0; left: 50%;\n            \n            transform: translate(-50%, -100%);\n        }\n    }\n}\n\nhtml[data-theme=\"dark\"] {\n    .tooltip{\n        background-color: #3F456E !important;\n\n        &:after{\n            background-color: #FFD583 !important;\n            color: #1C1C1C !important;\n        }\n\n        &:before{\n            border-top: 20px solid #FFD583 !important;\n        }\n    }\n}\n"
  },
  {
    "path": "website/src/scss/style.scss",
    "content": "\n@use 'variables' as *;\n@use 'components/section-base' as *;\n@use 'components/section-image-box' as *;\n@use 'components/section-features' as *;\n@use 'components/section-about' as *;\n@use 'components/section-yellow-banner' as *;\n@use 'components/section-use-tiles' as *;\n@use 'components/section-install-cli' as *;\n@use 'components/tooltip' as *;\n@use 'components/runnable-sample' as *;\n\n@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500;700&display=swap');\n\n@font-face {\n    font-family: \"Roboto Mono\", monospace;\n    src: url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500;700&display=swap');\n}\n\n/* You can override the default Infima variables here. */\n:root {\n    --ifm-container-width: 1204px;\n    --ifm-container-width-xl: 1204px;\n}\n\n.section-title {\n    font-family: \"Roboto Mono\", monospace;\n    font-size: 36px;\n    line-height: 48px;\n    font-weight: 700;\n    color: #1C1C1C;\n\n    @media (max-width: 998px) {\n        font-size: 32px;\n        line-height: 44px;\n    }\n\n    @media (max-width: 375px) {\n        font-size: 28px;\n        line-height: 36px;\n    }\n\n    &.with-before {\n        padding-left: 67px;\n\n        @media (max-width: 998px) {\n            padding-left: 53px;\n        }\n    \n        @media (max-width: 375px) {\n            padding-left: 40px;\n        }\n    }\n}\n\n*.with-before {\n    position: relative;\n    padding-left: 24px;\n\n    &:before {\n        content: '>_';\n        position: absolute;\n        top: 0; left: 0;\n        color: #DE3D3B;\n        transition: 0.3s ease all;\n    }\n}\n\nhtml[data-theme=\"dark\"] {\n\n    .section-title {\n        color: $theme-yellow;\n    }\n\n    .section__header{\n        &-description{\n            span{\n                &:before{\n                    background-color: #3F456E;\n                }\n            }\n        }\n    }\n\n    .content{\n        h1, h2, h3, h4{\n            color: #FFD583;\n        }\n    }\n\n    .dropdown__link--active, .dropdown__link--active:hover {\n        --ifm-dropdown-link-color: #FFD583;\n    }\n}\n\n.header-vl-link{\n    content:  url(\"/static/img/vl_logo_small.png\");\n    max-height: 50px;\n    margin-top: -7px;\n    margin-right: -15px;\n}"
  },
  {
    "path": "website/src/theme/Root.js",
    "content": "import React from 'react';\nimport Head from '@docusaurus/Head';\n\n\n// Default implementation, that you can customize\nfunction Root({children}) {\n  return <>\n    <Head>\n      <script>\n        {`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\n  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],\n  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=\n  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);\n  })(window,document,'script','dataLayer','GTM-5TZTCGF');`}\n    </script>\n    </Head>\n    {children}\n  </>;\n}\n\nexport default Root;"
  },
  {
    "path": "website/static/.nojekyll",
    "content": ""
  },
  {
    "path": "website/static/CNAME",
    "content": "scala-cli.virtuslab.org"
  }
]