Skip to content

Commit

Permalink
Project settings file
Browse files Browse the repository at this point in the history
  • Loading branch information
wleczny committed Aug 17, 2022
1 parent ad7782e commit 085a616
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 28 deletions.
2 changes: 2 additions & 0 deletions modules/build/src/main/scala/scala/build/CrossSources.scala
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ object CrossSources {
lazy val dir = sourcePath / os.up
lazy val subPath = sourcePath.subRelativeTo(dir)
if (os.isDir(sourcePath)) Right(Inputs.singleFilesFromDirectory(Inputs.Directory(sourcePath)))
else if (sourcePath.endsWith(os.SubPath("project.settings.scala")))
Right(Seq(Inputs.ProjectSettings(dir, subPath)))
else if (sourcePath.ext == "scala") Right(Seq(Inputs.ScalaFile(dir, subPath)))
else if (sourcePath.ext == "sc") Right(Seq(Inputs.Script(dir, subPath)))
else if (sourcePath.ext == "java") Right(Seq(Inputs.JavaFile(dir, subPath)))
Expand Down
68 changes: 54 additions & 14 deletions modules/build/src/main/scala/scala/build/Inputs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import scala.build.options.Scope
import scala.build.preprocessing.ScopePath
import scala.util.Properties
import scala.util.matching.Regex
import scala.build.Inputs.Element

final case class Inputs(
elements: Seq[Inputs.Element],
Expand Down Expand Up @@ -190,10 +191,11 @@ object Inputs {
extends OnDisk with SourceFile with AnyScalaFile with AnyScript {
lazy val path: os.Path = base / subPath
}
final case class ScalaFile(base: os.Path, subPath: os.SubPath)
case class ScalaFile(base: os.Path, subPath: os.SubPath)
extends OnDisk with SourceFile with AnyScalaFile {
lazy val path: os.Path = base / subPath
}
final class ProjectSettings(base: os.Path, subPath: os.SubPath) extends ScalaFile(base, subPath)
final case class JavaFile(base: os.Path, subPath: os.SubPath)
extends OnDisk with SourceFile with Compiled {
lazy val path: os.Path = base / subPath
Expand Down Expand Up @@ -224,6 +226,8 @@ object Inputs {
.collect {
case p if p.last.endsWith(".java") =>
Inputs.JavaFile(d.path, p.subRelativeTo(d.path))
case p if p.last.endsWith("project.settings.scala") =>
Inputs.ProjectSettings(d.path, p.subRelativeTo(d.path))
case p if p.last.endsWith(".scala") =>
Inputs.ScalaFile(d.path, p.subRelativeTo(d.path))
case p if p.last.endsWith(".sc") =>
Expand All @@ -233,6 +237,11 @@ object Inputs {
.sortBy(_.subPath.segments)
}

def configFileFromDirectory(d: Inputs.Directory): Seq[Inputs.ProjectSettings] =
if (os.exists(d.path / "project.settings.scala"))
Seq(Inputs.ProjectSettings(d.path, os.SubPath("project.settings.scala")))
else Nil

private def inputsHash(elements: Seq[Element]): String = {
def bytes(s: String): Array[Byte] = s.getBytes(StandardCharsets.UTF_8)
val it = elements.iterator.flatMap {
Expand All @@ -241,6 +250,7 @@ object Inputs {
case _: Inputs.Directory => "dir:"
case _: Inputs.ResourceDirectory => "resource-dir:"
case _: Inputs.JavaFile => "java:"
case _: Inputs.ProjectSettings => "config:"
case _: Inputs.ScalaFile => "scala:"
case _: Inputs.Script => "sc:"
}
Expand Down Expand Up @@ -271,22 +281,51 @@ object Inputs {

assert(validElems.nonEmpty)

val (inferredWorkspace, inferredNeedsHash, workspaceOrigin) = validElems
.collectFirst {
case d: Directory => (d.path, true, WorkspaceOrigin.SourcePaths)
val (inferredWorkspace, inferredNeedsHash, workspaceOrigin) = {
val settingsFiles = validElems.flatMap {
case f: ProjectSettings => Seq(f)
case d: Directory => Inputs.configFileFromDirectory(d)
case _ => Nil
}
val dirsAndFiles = validElems.collect {
case d: Directory => d
case f: SourceFile => f
}
.getOrElse {
validElems.head match {
case elem: SourceFile => (elem.path / os.up, true, WorkspaceOrigin.SourcePaths)
case _: Virtual =>
val dir = homeWorkspace(validElems, directories)
(dir, false, WorkspaceOrigin.HomeDir)
case r: ResourceDirectory =>
// Makes us put .scala-build in a resource directory :/
(r.path, true, WorkspaceOrigin.ResourcePaths)
case _: Directory => sys.error("Can't happen")

settingsFiles.collectFirst { s =>
if (settingsFiles.length > 1)
System.err.println(
s"Warning: more than one project.settings.scala file has been found. Setting ${s.base} as the project root directory for this run."
)
(s.base, true, WorkspaceOrigin.SourcePaths)
}.getOrElse {
dirsAndFiles.collectFirst {
case d: Directory =>
if (dirsAndFiles.length > 1)
System.err.println(
s"Warning: setting ${d.path} as the project root directory for this run."
)
(d.path, true, WorkspaceOrigin.SourcePaths)
case f: SourceFile =>
if (dirsAndFiles.length > 1)
System.err.println(
s"Warning: setting ${f.path / os.up} as the project root directory for this run."
)
(f.path / os.up, true, WorkspaceOrigin.SourcePaths)
}.getOrElse {
validElems.head match {
case _: Virtual =>
val dir = homeWorkspace(validElems, directories)
(dir, false, WorkspaceOrigin.HomeDir)
case r: ResourceDirectory =>
// Makes us put .scala-build in a resource directory :/
(r.path, true, WorkspaceOrigin.ResourcePaths)
case _: Directory | _: SourceFile => sys.error("Can't happen")
}
}
}
}

val (workspace, needsHash, workspaceOrigin0) = forcedWorkspace match {
case None => (inferredWorkspace, inferredNeedsHash, workspaceOrigin)
case Some(forcedWorkspace0) =>
Expand Down Expand Up @@ -404,6 +443,7 @@ object Inputs {
List(resolve(url, content))
}
}
else if (arg.endsWith("project.settings.scala")) Right(Seq(ProjectSettings(dir, subPath)))
else if (arg.endsWith(".sc")) Right(Seq(Script(dir, subPath)))
else if (arg.endsWith(".scala")) Right(Seq(ScalaFile(dir, subPath)))
else if (arg.endsWith(".java")) Right(Seq(JavaFile(dir, subPath)))
Expand Down
60 changes: 60 additions & 0 deletions website/docs/reference/root-dir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: Project root directory
sidebar_position: 5
---

## Usage

Scala CLI needs a root directory:
- to write mapped sources
- to write class files
- for Bloop

## Setting root directory

First of all Scala CLI checks every passed input for the `project.settings.scala` file:
- If the `project.settings.scala` file is passed explicitly as a **source**, Scala CLI sets its parent directory as the root directory.
- If the input is a **directory**, Scala CLI looks for the `project.settings.scala` inside this directory. If the file is found, Scala CLI sets the passed directory as the root directory.

If more than one `project.settings.scala` file is found, Scala CLI uses only **the first one** to set the root directory and raises **warning** saying which one was used.

If no `project.settings.scala` files are found, Scala CLI sets the root directory based on the first file/directory input:
- If the input is a **directory**, it is set as the root directory.
- If the input is a **file**, Scala CLI sets its parent directory as the root directory.

If more then one file/directory input has ben passed Scala CLI raises the warning saying which directory has been set as the project root directory.

If no `project.settings.scala` files are found and no file/directory inputs have ben passed, Scala CLI sets **cwd** as the project root directory.

#### Example

Let's say we have the following file structure:

```
project
│ project.settings.scala
└───dir1
│ │ file1.scala
│ │
│ └───dir2
│ │ project.settings.scala
│ │ file2.scala
└───dir3
│ project.settings.scala
│ file3.scala
```

And user runs the following command:
```
project> scala-cli dir1/file1.scala dir1/dir2 dir3/project.settings.scala
```

Scala CLI will find 2 `project.settings.scala` files:
- inside `dir2`, since this directory was passed as an input and it has `project.settings.scala` inside.
- inside `dir3`, since `dir3/project.settings.scala` was passed explicitly as a source

`dir1/dir2` was passed before `dir3/project.settings.scala`, so `dir2` will be set as the **root** directory for this build.

Since more than one `project.settings.scala` has been found, Scala CLI will raise the warning saying that more than one `project.settings.scala` file has been found and `dir1/dir2` has been set as the project root directory.
14 changes: 0 additions & 14 deletions website/docs/reference/working-dir.md

This file was deleted.

0 comments on commit 085a616

Please sign in to comment.