Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snippet compiler #11582

Merged
merged 28 commits into from Jun 2, 2021
Merged

Snippet compiler #11582

merged 28 commits into from Jun 2, 2021

Conversation

pikinier20
Copy link
Contributor

No description provided.

target: AbstractFile = new VirtualDirectory("(memory)")
):

private def newDriver: InteractiveDriver = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In future we can probably stop after the given phases since we probably do not need to produce bytecode.


def compile(
snippets: List[String]
): Either[SnippetCompilerException, AbstractFile] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about warning from successfull compilation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we will need to return something like SnippetCompilationResult:

case class SnippetCompilationResult(result: Option[AbstractFile], messages: Seq[SnippetCompilerMessage])

):
def checkSnippet(snippet: String) = {
val wrapped = wrapper.wrap(snippet)
compiler.compile(snippet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should compile wrapped, shouldn't we?

@pikinier20 pikinier20 force-pushed the snippet-compiler branch 3 times, most recently from 853317f to 04ec4cb Compare March 22, 2021 08:49
@pikinier20 pikinier20 marked this pull request as ready for review March 22, 2021 08:49
private val sep = System.getProperty("path.separator")
private val cp = System.getProperty("java.class.path") + sep +
Paths.get(ctx.args.classpath).toAbsolutePath + sep +
ctx.args.tastyDirs.map(_.getAbsolutePath()).mkString(sep)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Seq(System.getProperty("java.class.path"),  Paths.get(ctx.args.classpath).toAbsolutePath) ++
 ctx.args.tastyDirs.map(_.getAbsolutePath())).mkString(sep)

}

def summary: String = s"""
|Snippet compiler summary:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting is strange

ctx.args.tastyDirs.map(_.getAbsolutePath()).mkString(sep)
private val compiler: SnippetCompiler = SnippetCompiler(classpath = cp)
private val wrapper: SnippetWrapper = SnippetWrapper()
var warningsCount = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be private

package dotty.tools.scaladoc
package snippets

import dotty.tools.io.{ AbstractFile }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove braces

import dotty.tools.dotc.interfaces.Diagnostic._

class SnippetCompiler(
classpath: String = System.getProperty("java.class.path"), //Probably needs to be done better
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need that default here? it looks a bit denegerous.

@pikinier20 pikinier20 changed the title Snippet compiler PoC Snippet compiler Mar 22, 2021
@b-studios
Copy link
Contributor

Hey @pikinier20, is this PR ready to be reviewed again?

@pikinier20
Copy link
Contributor Author

Hey @pikinier20, is this PR ready to be reviewed again?

Yes, it is, apart from the fact that I need to resolve conflicts.

case MessageLevel.Error => "snippet-error"
case MessageLevel.Debug => "snippet-debug"

private def cutBetwenSymbols[A](
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private def cutBetwenSymbols[A](
private def cutBetweenSymbols[A](

// TODO add versions etc.
def srcManaged(v: String, s: String) = s"out/bootstrap/stdlib-bootstrapped/scala-$v/src_managed/main/$s-library-src"
def scalaSrcLink(v: String, s: String) = s"-source-links:$s=github://scala/scala/v$v#src/library"
def dottySrcLink(v: String, s: String) = s"-source-links:$s=github://lampepfl/dotty/$v#library/src"
def bootclasspath: Seq[String] = if(addBootclasspath) Seq(
"-bootclasspath",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need a separate bootclasspath given these libraries will be on the classpath anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to somehow pass path to standard library to the snippet compiler, otherwise we get MissingCoreLibraryException. I've seen that sbt in task doc automatically passes this path in bootclasspath arg so I added support for this arg in scaladoc. Then I realized that in our sbt project we don't pass explicit bootclasspath to generateDocumentation task and snippet compiler didn't work. Previously I wanted to just pass System.getProperty("java.class.path") but it led to errors because we got sbt libs on classpath.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but you should be able to use the regular -classpath and not necessarily -bootclasspath (which isn't really needed anymore)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But -classpath doesn't contain or at least it didn't contain stdlib entry when this was implemented and without stdlib entry I can't instantiate Context. Or maybe is there other way to obtain path to stdlib?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which -classpath? It looks like -classpath isn't passed at all to scaladoc here, so I was just suggesting using that instead of -bootclasspath to pass the stdlib

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually maybe the issue is in the scaladoc shell script? Unlike the scalac shell scrpt it doesn't pass the -Dscala.usejavacp=true flag so that the JVM classpath is available to the compiler: https://github.com/lampepfl/dotty/blob/83e17f110bfaa2fe876bacc90eca208f23930f98/dist/bin/scalac#L77-L78 vs https://github.com/lampepfl/dotty/blob/83e17f110bfaa2fe876bacc90eca208f23930f98/dist/bin/scaladoc#L129

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good point, gonna check that

MultiStringSetting("-snippet-compiler", "snippet-compiler", snippets.SnippetCompilerArgs.usage)

val snippetCompilerDebug: Setting[Boolean] =
BooleanSetting("-snippet-compiler-debug", snippets.SnippetCompilerArgs.debugUsage, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flags which are for debugging purpose only and not intended for others to use should start with -Y

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't aware of this. Thanks!

Comment on lines 79 to 85
lazy val snippetChecker = snippets.SnippetChecker(
args.classpath,
args.bootclasspath,
args.tastyFiles,
compilerContext.settings.scalajs.value(using compilerContext),
compilerContext.settings.usejavacp.value(using compilerContext)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be more settings needed to compile snippets (for example, if a compiler plugin is used), and adding them one by one isn't going to scale, could you pass all of compilerContext.settings instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right and ofc I can do that, but I'm afraid of passing options that can totally break snippet compiler. I don't have knowledge of all possible settings but e.g. what will the compiler do with -from-tasty setting?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what will the compiler do with -from-tasty setting?

It will unpickle the .tasty files and compile them to .class files.

@pikinier20 pikinier20 force-pushed the snippet-compiler branch 2 times, most recently from 6c1086c to 01dacd4 Compare May 21, 2021 12:12
@pikinier20 pikinier20 requested a review from smarter May 21, 2021 13:59
@@ -127,7 +127,6 @@ eval "\"$JAVACMD\"" \
${JAVA_OPTS:-$default_java_opts} \
"${java_args[@]}" \
"${jvm_cp_args-}" \
-Dscala.usejavacp=true \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason for removing this flag? It's in dist/bin/scala and dist/bin/scalac, so it makes sense to have it here too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I removed this flag intentionally. It helped me a lot and made most of things working but then I realized that with this flag set, generating standard library documentation is failing. I'm not sure about the exact reason but I believe it's because we've got then two standard libraries on classpath and there was some conflict between them. Instead of setting the flag there, I set it directly in tasks that needed it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but that means the scaladoc binary isn't really usable for external users. I believe the best solution is to not call the scaladoc script from Build.scala at all. Instead you should be able to run the scaladoc main class from the sbt project directly: (Compile / runMain).toTask("-foo -bla -bar")

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution ! scaladoc binary MUST BE usable for external users, as are javadoc and haddock for instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So probably I need to implement @smarter solution. The funny part is that the -usejavacp option is not there on master, I added it some commits before in this PR, but now I realized it breaks doc generation for one task.

Also, can you provide me more information about usage of this binary? Let's say I've got a example scala3 project and I want to call scaladoc on it without sbt. Where should I look for binary and how to use it?

Comment on lines +27 to +29
private val snippetCompilerSettings: Seq[SnippetCompilerSetting[_]] = cctx.settings.userSetSettings(cctx.settingsState).filter(_ != cctx.settings.classpath).map( s =>
SnippetCompilerSetting(s, s.valueIn(cctx.settingsState))
) :+ SnippetCompilerSetting(cctx.settings.classpath, fullClasspath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of SnippetCompilerSetting? If it's just storing settings it seems that the existing SettingsState would suffice and you could write something like :

  private val snippetCompilerSettings = cctx.settings.classpath.updateIn(cctx.settingsState, fullClasspath)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change classpath of compiler context used to load TASTY to produce documentation from, won't it affect loading process?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since updateIn returns a new SettingsState, I was expecting it to not mutate the existing settingsState of the compiler context, but in fact it's slightly more tricky than that, it will be mutated if no one tried to read a setting previously: https://github.com/lampepfl/dotty/blob/e2e77b56d012b00faf16a86ad1c8437404bb0812/compiler/src/dotty/tools/dotc/config/Settings.scala#L36-L40 (this is a really bad idea and that code that should be replaced)

Comment on lines 30 to 34
rootCtx.setSetting(rootCtx.settings.YretainTrees, true)
rootCtx.setSetting(rootCtx.settings.YcookComments, true)
rootCtx.setSetting(rootCtx.settings.YreadComments, true)
val defaultFlags =
List("-color:never", "-unchecked", "-deprecation", "-Ximport-suggestion-timeout", "0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some settings are set with setSetting and some are strings passed to setup, it would be nicer and more type-safe to always use setSetting:

rootCtx.setSetting(rootCtx.settings.color, "never")
  .setSetting(...) // etc

Also it seems weird to explicitly enable -unchecked and -deprecation here, why those settings in particular? If the user wants them they can add them in scalacOptions themselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason behind exactly these flags is that mdoc uses them. But I think it's OK to remove them and let user choose.

@BarkingBad BarkingBad linked an issue May 26, 2021 that may be closed by this pull request
@@ -1243,7 +1243,7 @@ object Build {
libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.1.0").cross(CrossVersion.for3Use2_13)
)

def generateDocumentation(targets: Seq[String], name: String, outDir: String, ref: String, params: Seq[String] = Nil) =
def generateDocumentation(targets: Seq[String], name: String, outDir: String, ref: String, params: Seq[String] = Nil, usingScript: Boolean = true) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be fixed later but do you know why we need usingScript in some situations and couldn't just always use Compile/run ?

Copy link
Contributor

@romanowski romanowski Jun 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was my idea to have some coverage for scripts. Beside that there are no blockers to switch all cases to running Compile/run

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, for consistency I suggest always using Compile/run and instead test the scaladoc binary from https://github.com/lampepfl/dotty/blob/master/project/scripts/cmdTests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pikinier20 can we change that in follow up PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pikinier20 can we change that in follow up PR?

Sure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scaladoc text breaking bug for narrow screens
9 participants