Skip to content

Commit

Permalink
Merge pull request #676 from alexarchambault/scala-js-linking-windows
Browse files Browse the repository at this point in the history
Run Scala.JS linker in an external process, enable back Scala.JS linking on Windows
  • Loading branch information
alexarchambault authored Mar 23, 2022
2 parents 2d71777 + cc4e896 commit a5d7bd7
Show file tree
Hide file tree
Showing 30 changed files with 657 additions and 418 deletions.
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ out/
.bloop/
.metals/
.vscode/
.scala
.scala-build/
.bsp
.idea/

*.semanticdb
*.semanticdb
15 changes: 11 additions & 4 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,13 @@ trait BuildLikeModule
}

class Core(val crossScalaVersion: String) extends BuildLikeModule {
def moduleDeps = Seq(`bloop-rifle`())
def moduleDeps = Seq(
`bloop-rifle`(),
`build-macros`()
)
def scalacOptions = T {
super.scalacOptions() ++ Seq("-Xasync")
}

def ivyDeps = super.ivyDeps() ++ Agg(
Deps.collectionCompat,
Expand All @@ -205,7 +211,7 @@ class Core(val crossScalaVersion: String) extends BuildLikeModule {
Deps.nativeTools, // Used only for discovery methods. For linking, look for scala-native-cli
Deps.osLib,
Deps.pprint,
Deps.scalaJsLinkerInterface,
Deps.scalaJsLogging,
Deps.swoval
)

Expand Down Expand Up @@ -244,9 +250,11 @@ class Core(val crossScalaVersion: String) extends BuildLikeModule {
| def version = "${publishVersion()}"
| def detailedVersion: Option[String] = $detailedVersionValue
|
| def scalaJsVersion = "${Deps.scalaJsLinker.dep.version}"
| def scalaJsVersion = "${Scala.scalaJs}"
| def scalaNativeVersion = "${Deps.nativeTools.dep.version}"
|
| def scalaJsCliVersion = "${InternalDeps.Versions.scalaJsCli}"
|
| def stubsOrganization = "${stubs.pomSettings().organization}"
| def stubsModuleName = "${stubs.artifactName()}"
| def stubsVersion = "${stubs.publishVersion()}"
Expand Down Expand Up @@ -493,7 +501,6 @@ trait Cli extends SbtModule with CliLaunchers with ScalaCliPublishModule with Fo
Deps.jimfs, // scalaJsEnvNodeJs pulls jimfs:1.1, whose class path seems borked (bin compat issue with the guava version it depends on)
Deps.jniUtils,
Deps.jsoniterCore,
Deps.scalaJsLinker,
Deps.scalaPackager,
Deps.metaconfigTypesafe
)
Expand Down
3 changes: 2 additions & 1 deletion examples/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.scala/
.scala-build/
.bsp/
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package scala.build.errors

final class ScalaJsLinkingError extends BuildException("Error linking Scala.JS")

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package scala.build.tests

import com.eed3si9n.expecty.Expecty.expect
import dependency._
import org.scalajs.linker.interface.{ESVersion, ModuleKind, ModuleSplitStyle}

import scala.build.Ops._
import scala.build.Sources
Expand All @@ -12,6 +11,7 @@ import scala.build.Position
import scala.build.options.{BuildOptions, Scope}
import scala.build.preprocessing.directives.MultiValue
import scala.build.preprocessing.directives.NotABoolean
import scala.build.internal.ScalaJsLinkerConfig

class SourcesTests extends munit.FunSuite {

Expand Down Expand Up @@ -508,15 +508,15 @@ class SourcesTests extends munit.FunSuite {
jsOptions.dom == Some(true)
)
expect(
jsConfig.moduleKind == ModuleKind.CommonJSModule,
jsConfig.moduleKind == ScalaJsLinkerConfig.ModuleKind.CommonJSModule,
jsConfig.checkIR == true,
jsConfig.sourceMap == true,
jsConfig.jsHeader == "#!/usr/bin/env node\n",
jsConfig.jsHeader == Some("#!/usr/bin/env node\n"),
jsConfig.esFeatures.allowBigIntsForLongs == true,
jsConfig.esFeatures.avoidClasses == false,
jsConfig.esFeatures.avoidLetsAndConsts == false,
jsConfig.esFeatures.esVersion == ESVersion.ES2017,
jsConfig.moduleSplitStyle == ModuleSplitStyle.SmallestModules
jsConfig.esFeatures.esVersion == "ES2017",
jsConfig.moduleSplitStyle == ScalaJsLinkerConfig.ModuleSplitStyle.SmallestModules
)
}
}
Expand Down

This file was deleted.

6 changes: 3 additions & 3 deletions modules/cli/src/main/scala/scala/cli/commands/Fmt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ package scala.cli.commands

import caseapp._

import scala.build.internal.{CustomCodeWrapper, Runner}
import scala.build.internal.{CustomCodeWrapper, FetchExternalBinary, Runner}
import scala.build.{CrossSources, Inputs, Logger, Sources}
import scala.cli.CurrentParams
import scala.cli.internal.FetchExternalBinary
import scala.util.control.NonFatal

object Fmt extends ScalaCommand[FmtOptions] {
Expand Down Expand Up @@ -89,7 +88,7 @@ object Fmt extends ScalaCommand[FmtOptions] {
}
CurrentParams.workspaceOpt = Some(workspace)
val (versionMaybe, confExists) = readVersionFromFile(workspace, logger)
val cache = options.shared.coursierCache
val cache = options.shared.buildOptions(false, None).archiveCache

if (sourceFiles.isEmpty)
logger.debug("No source files, not formatting anything")
Expand Down Expand Up @@ -132,6 +131,7 @@ object Fmt extends ScalaCommand[FmtOptions] {
case None =>
val (url, changing) = options.binaryUrl(versionMaybe)
FetchExternalBinary.fetch(url, changing, cache, logger, "scalafmt")
.orExit(logger)
}

logger.debug(s"Using scalafmt launcher $fmtLauncher")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ package scala.cli.commands

import caseapp._

import scala.build.internal.Constants
import scala.build.internal.{Constants, FetchExternalBinary}
import scala.build.options.BuildOptions
import scala.cli.internal.FetchExternalBinary
import scala.util.Properties

// format: off
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import caseapp._
import java.io.File
import java.nio.file.Path

import scala.build.internal.Runner
import scala.build.internal.{FetchExternalBinary, Runner}
import scala.build.{Build, BuildThreads, Logger}
import scala.cli.CurrentParams
import scala.cli.internal.FetchExternalBinary
import scala.cli.packaging.Library

object Metabrowse extends ScalaCommand[MetabrowseOptions] {
Expand Down Expand Up @@ -77,7 +76,7 @@ object Metabrowse extends ScalaCommand[MetabrowseOptions] {
FetchExternalBinary.fetch(
url,
changing,
options.shared.coursierCache,
successfulBuild.options.archiveCache,
logger,
"metabrowse"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package scala.cli.commands

import caseapp._

import scala.build.internal.FetchExternalBinary
import scala.build.options.BuildOptions
import scala.cli.internal.FetchExternalBinary
import scala.util.Properties

// format: off
Expand Down
80 changes: 50 additions & 30 deletions modules/cli/src/main/scala/scala/cli/commands/Package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package scala.cli.commands
import caseapp._
import coursier.launcher._
import dependency.dependencyString
import org.scalajs.linker.interface.StandardConfig
import packager.config._
import packager.deb.DebianPackage
import packager.docker.DockerPackage
Expand All @@ -21,7 +20,7 @@ import java.util.zip.{ZipEntry, ZipOutputStream}
import scala.build.EitherCps.{either, value}
import scala.build._
import scala.build.errors.{BuildException, ScalaNativeBuildError}
import scala.build.internal.{NativeBuilderHelper, Runner, ScalaJsConfig}
import scala.build.internal.{NativeBuilderHelper, Runner, ScalaJsLinkerConfig}
import scala.build.options.{PackageType, Platform}
import scala.cli.CurrentParams
import scala.cli.commands.OptionsHelper._
Expand Down Expand Up @@ -525,7 +524,16 @@ object Package extends ScalaCommand[PackageOptions] {
logger: Logger
): Either[BuildException, Unit] = {
val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger)
linkJs(build, destPath, Some(mainClass), addTestInitializer = false, linkerConfig, logger)
linkJs(
build,
destPath,
Some(mainClass),
addTestInitializer = false,
linkerConfig,
build.options.scalaJsOptions.fullOpt.getOrElse(false),
build.options.scalaJsOptions.noOpt.getOrElse(false),
logger
)
}

private def buildNative(
Expand Down Expand Up @@ -657,38 +665,50 @@ object Package extends ScalaCommand[PackageOptions] {
dest: os.Path,
mainClassOpt: Option[String],
addTestInitializer: Boolean,
config: StandardConfig,
config: ScalaJsLinkerConfig,
fullOpt: Boolean,
noOpt: Boolean,
logger: Logger
): Either[BuildException, Unit] =
Library.withLibraryJar(build, dest.last.toString.stripSuffix(".jar")) { mainJar =>
val classPath = mainJar +: build.artifacts.classPath.map(_.toNIO)
val classPath = os.Path(mainJar, os.pwd) +: build.artifacts.classPath
val linkingDir = os.temp.dir(prefix = "scala-cli-js-linking")
(new ScalaJsLinker).link(
classPath.toArray,
mainClassOpt.orNull,
addTestInitializer,
new ScalaJsConfig(config),
linkingDir.toNIO,
logger.scalaJsLogger
)
val relMainJs = os.rel / "main.js"
val relSourceMapJs = os.rel / "main.js.map"
val mainJs = linkingDir / relMainJs
val sourceMapJs = linkingDir / relSourceMapJs
if (os.exists(mainJs)) {
os.copy(mainJs, dest, replaceExisting = true)
if (build.options.scalaJsOptions.emitSourceMaps && os.exists(sourceMapJs)) {
val sourceMapDest =
build.options.scalaJsOptions.sourceMapsDest.getOrElse(os.Path(s"$dest.map"))
os.copy(sourceMapJs, sourceMapDest, replaceExisting = true)
logger.message(s"Emitted js source maps to: $sourceMapDest")
either {
value {
ScalaJsLinker.link(
build.options.notForBloopOptions.scalaJsLinkerOptions,
build.options.javaHome().value.javaCommand, // FIXME Allow users to use another JVM here?
classPath,
mainClassOpt.orNull,
addTestInitializer,
config,
linkingDir,
fullOpt,
noOpt,
logger,
build.options.finalCache,
build.options.archiveCache,
build.options.scalaJsOptions.finalVersion
)
}
val relMainJs = os.rel / "main.js"
val relSourceMapJs = os.rel / "main.js.map"
val mainJs = linkingDir / relMainJs
val sourceMapJs = linkingDir / relSourceMapJs
if (os.exists(mainJs)) {
os.copy(mainJs, dest, replaceExisting = true)
if (build.options.scalaJsOptions.emitSourceMaps && os.exists(sourceMapJs)) {
val sourceMapDest =
build.options.scalaJsOptions.sourceMapsDest.getOrElse(os.Path(s"$dest.map"))
os.copy(sourceMapJs, sourceMapDest, replaceExisting = true)
logger.message(s"Emitted js source maps to: $sourceMapDest")
}
os.remove.all(linkingDir)
}
else {
val found = os.walk(linkingDir).map(_.relativeTo(linkingDir))
value(Left(new ScalaJsLinkingError(relMainJs, found)))
}
os.remove.all(linkingDir)
Right(())
}
else {
val found = os.walk(linkingDir).map(_.relativeTo(linkingDir))
Left(new ScalaJsLinkingError(relMainJs, found))
}
}

Expand Down
28 changes: 23 additions & 5 deletions modules/cli/src/main/scala/scala/cli/commands/Run.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package scala.cli.commands

import caseapp._
import org.scalajs.linker.interface.StandardConfig

import scala.build.EitherCps.{either, value}
import scala.build.errors.BuildException
import scala.build.internal.{Constants, Runner}
import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig}
import scala.build.options.Platform
import scala.build.{Build, BuildThreads, Inputs, Logger}
import scala.cli.CurrentParams
Expand Down Expand Up @@ -162,7 +161,15 @@ object Run extends ScalaCommand[RunOptions] {
case Platform.JS =>
val linkerConfig = build.options.scalaJsOptions.linkerConfig(logger)
val res =
withLinkedJs(build, Some(mainClass), addTestInitializer = false, linkerConfig, logger) {
withLinkedJs(
build,
Some(mainClass),
addTestInitializer = false,
linkerConfig,
build.options.scalaJsOptions.fullOpt.getOrElse(false),
build.options.scalaJsOptions.noOpt.getOrElse(false),
logger
) {
js =>
Runner.runJs(
js.toIO,
Expand Down Expand Up @@ -215,11 +222,22 @@ object Run extends ScalaCommand[RunOptions] {
build: Build.Successful,
mainClassOpt: Option[String],
addTestInitializer: Boolean,
config: StandardConfig,
config: ScalaJsLinkerConfig,
fullOpt: Boolean,
noOpt: Boolean,
logger: Logger
)(f: os.Path => T): Either[BuildException, T] = {
val dest = os.temp(prefix = "main", suffix = ".js")
try Package.linkJs(build, dest, mainClassOpt, addTestInitializer, config, logger).map { _ =>
try Package.linkJs(
build,
dest,
mainClassOpt,
addTestInitializer,
config,
fullOpt,
noOpt,
logger
).map { _ =>
f(dest)
}
finally if (os.exists(dest)) os.remove(dest)
Expand Down
Loading

0 comments on commit a5d7bd7

Please sign in to comment.