Skip to content

Commit 5ee6ec9

Browse files
authored
Merge pull request #23 from scalacenter/topic/add-case-app
Add reproduced version of case-app inefficiency
2 parents 853a159 + 383a60d commit 5ee6ec9

File tree

9 files changed

+368
-13
lines changed

9 files changed

+368
-13
lines changed

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ lazy val integrations = project
159159
.in(file("integrations"))
160160
.dependsOn(Circe)
161161
.settings(
162+
libraryDependencies += "com.github.alexarchambault" %% "case-app" % "1.2.0",
162163
scalaHome := BuildDefaults.setUpScalaHome.value,
163164
parallelExecution in Test := false,
164165
scalacOptions in Compile := (Def.taskDyn {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package profiling.integrations
2+
3+
import java.nio.file.Path
4+
5+
import caseapp.{ExtraName, HelpMessage, Recurse, ValueDescription}
6+
7+
case class CliOptions(
8+
@ExtraName("c")
9+
@HelpMessage("File path to the bloop config directory.")
10+
@ValueDescription(".bloop")
11+
configDir: Option[Path] = None,
12+
@ExtraName("v")
13+
@HelpMessage("If set, print the about section at the beginning of the execution.")
14+
version: Boolean = false,
15+
@HelpMessage("If set, print out debugging information to stderr.")
16+
verbose: Boolean = false,
17+
@Recurse common: CommonOptions = CommonOptions.default,
18+
)
19+
20+
object CliOptions {
21+
val default = CliOptions()
22+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package profiling.integrations
2+
3+
import java.nio.file.Path
4+
5+
import caseapp.core.ArgParser
6+
import caseapp.{ArgsName, CommandName, ExtraName, HelpMessage, Hidden, Recurse}
7+
import caseapp.core.CommandMessages
8+
9+
object Commands {
10+
11+
/* sealed abstract class Mode(val name: String)
12+
13+
/** The kind of items that should be returned for autocompletion */
14+
object Mode {
15+
case object Commands extends Mode("commands")
16+
case object Projects extends Mode("projects")
17+
case object ProjectBoundCommands extends Mode("project-commands")
18+
case object Flags extends Mode("flags")
19+
case object Reporters extends Mode("reporters")
20+
case object Protocols extends Mode("protocols")
21+
case object TestsFQCN extends Mode("testsfqcn")
22+
case object MainsFQCN extends Mode("mainsfqcn")
23+
24+
implicit val completionModeRead: ArgParser[Mode] = ???
25+
}
26+
27+
sealed abstract class BspProtocol(val name: String)
28+
29+
object BspProtocol {
30+
case object Local extends BspProtocol("local")
31+
case object Tcp extends BspProtocol("tcp")
32+
33+
implicit val bspProtocolRead: ArgParser[BspProtocol] = ???
34+
}
35+
36+
sealed abstract class ReporterKind(val name: String)
37+
case object ScalacReporter extends ReporterKind("scalac")
38+
case object BloopReporter extends ReporterKind("bloop")*/
39+
40+
sealed trait RawCommand {
41+
def cliOptions: CliOptions
42+
}
43+
44+
sealed trait CompilingCommand extends RawCommand {
45+
//def project: String
46+
//def reporter: ReporterKind
47+
}
48+
49+
/* sealed trait Tree[A]
50+
case class Leaf[A](value: A) extends Tree[A]
51+
case class Branch[A](
52+
left: Tree[A],
53+
right: Tree[A]
54+
) extends Tree[A]*/
55+
56+
case class Help(
57+
@Recurse cliOptions: CliOptions = CliOptions.default
58+
) extends RawCommand
59+
60+
case class Autocomplete(
61+
@Recurse cliOptions: CliOptions = CliOptions.default,
62+
//mode: Mode,
63+
//format: Format,
64+
/* command: Option[String],
65+
project: Option[String]*/
66+
) extends RawCommand
67+
68+
case class About(
69+
@Recurse cliOptions: CliOptions = CliOptions.default
70+
) extends RawCommand
71+
72+
case class Projects(
73+
/* @ExtraName("dot")
74+
@HelpMessage("Print out a dot graph you can pipe into `dot`. By default, false.")
75+
dotGraph: Boolean = false,*/
76+
@Recurse cliOptions: CliOptions = CliOptions.default
77+
) extends RawCommand
78+
79+
case class Configure(
80+
/* @ExtraName("parallelism")
81+
@HelpMessage("Set the number of threads used for parallel compilation and test execution.")
82+
threads: Int = 4,*/
83+
@Recurse cliOptions: CliOptions = CliOptions.default
84+
) extends RawCommand
85+
86+
case class Clean(
87+
/* @ExtraName("p")
88+
@HelpMessage("The projects to clean.")
89+
project: List[String] = Nil,
90+
@HelpMessage("Do not run clean for dependencies. By default, false.")
91+
isolated: Boolean = false,*/
92+
@Recurse cliOptions: CliOptions = CliOptions.default,
93+
) extends RawCommand
94+
95+
@CommandName("bsp")
96+
case class Bsp(
97+
/*/* @ExtraName("p")
98+
@HelpMessage("The connection protocol for the bsp server. By default, local.")
99+
protocol: BspProtocol = BspProtocol.Local,*/
100+
@ExtraName("h")
101+
@HelpMessage("The server host for the bsp server (TCP only).")
102+
host: String = "127.0.0.1",
103+
@HelpMessage("The port for the bsp server (TCP only).")
104+
port: Int = 5101,
105+
@ExtraName("s")
106+
@HelpMessage("A path to a socket file to communicate through Unix sockets (local only).")
107+
socket: Option[Path] = None,
108+
@ExtraName("pn")
109+
@HelpMessage(
110+
"A path to a new existing socket file to communicate through Unix sockets (local only)."
111+
)
112+
pipeName: Option[String] = None,*/
113+
@Recurse cliOptions: CliOptions = CliOptions.default
114+
) extends RawCommand
115+
116+
case class Compile(
117+
/* @ExtraName("p")
118+
@HelpMessage("The project to compile (will be inferred from remaining cli args).")
119+
project: String = "",
120+
@HelpMessage("Compile the project incrementally. By default, true.")
121+
incremental: Boolean = true,
122+
/* @HelpMessage("Pick reporter to show compilation messages. By default, bloop's used.")
123+
reporter: ReporterKind = BloopReporter,*/
124+
@ExtraName("w")
125+
@HelpMessage("Run the command when projects' source files change. By default, false.")
126+
watch: Boolean = false,*/
127+
@Recurse cliOptions: CliOptions = CliOptions.default,
128+
) extends CompilingCommand
129+
130+
case class Test(
131+
/* @ExtraName("p")
132+
@HelpMessage("The project to test (will be inferred from remaining cli args).")
133+
project: String = "",
134+
@HelpMessage("Do not run tests for dependencies. By default, false.")
135+
isolated: Boolean = false,
136+
@ExtraName("o")
137+
@HelpMessage("The list of test suite filters to test for only.")
138+
only: List[String] = Nil,
139+
@HelpMessage("The arguments to pass in to the test framework.")
140+
args: List[String] = Nil,
141+
/* @HelpMessage("Pick reporter to show compilation messages. By default, bloop's used.")
142+
reporter: ReporterKind = BloopReporter,*/
143+
@ExtraName("w")
144+
@HelpMessage("Run the command when projects' source files change. By default, false.")
145+
watch: Boolean = false,*/
146+
@Recurse cliOptions: CliOptions = CliOptions.default
147+
) extends CompilingCommand
148+
149+
case class Console(
150+
/* @ExtraName("p")
151+
@HelpMessage("The project to run the console at (will be inferred from remaining cli args).")
152+
project: String = "",
153+
/* @HelpMessage("Pick reporter to show compilation messages. By default, bloop's used.")
154+
reporter: ReporterKind = BloopReporter,*/
155+
@HelpMessage("Start up the console compiling only the target project's dependencies.")
156+
excludeRoot: Boolean = false,*/
157+
@Recurse cliOptions: CliOptions = CliOptions.default
158+
) extends CompilingCommand
159+
160+
case class Run(
161+
/* @ExtraName("p")
162+
@HelpMessage("The project to run (will be inferred from remaining cli args).")
163+
project: String = "",
164+
@ExtraName("m")
165+
@HelpMessage("The main class to run. Leave unset to let bloop select automatically.")
166+
main: Option[String] = None,
167+
/* @HelpMessage("Pick reporter to show compilation messages. By default, bloop's used.")
168+
reporter: ReporterKind = BloopReporter,*/
169+
@HelpMessage("The arguments to pass in to the main class.")
170+
args: List[String] = Nil,
171+
@ExtraName("w")
172+
@HelpMessage("If set, run the command whenever projects' source files change.")
173+
watch: Boolean = false,*/
174+
@Recurse cliOptions: CliOptions = CliOptions.default
175+
) extends CompilingCommand
176+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package profiling.integrations
2+
3+
import java.io.{InputStream, PrintStream}
4+
import java.util.Properties
5+
6+
import caseapp.Hidden
7+
8+
/**
9+
* Describes the common options for any command or CLI operation.
10+
*
11+
* They exist for two purposes: testing and nailgun. In both cases we
12+
* need a precise handling of these parameters because they change
13+
* depending on the environment we're running on.
14+
*
15+
* They are hidden because they are optional.
16+
*/
17+
case class CommonOptions(
18+
@Hidden workingDirectory: String = System.getProperty("user.dir"),
19+
@Hidden out: PrintStream = System.out,
20+
@Hidden in: InputStream = System.in,
21+
@Hidden err: PrintStream = System.err,
22+
@Hidden ngout: PrintStream = System.out,
23+
@Hidden ngerr: PrintStream = System.err
24+
)
25+
26+
object CommonOptions {
27+
final val default = CommonOptions()
28+
29+
// Our own version of properties in which we override `toString`
30+
final class PrettyProperties extends Properties {
31+
override def toString: String = synchronized {
32+
super.keySet().toArray.map(_.toString).mkString(", ")
33+
}
34+
}
35+
36+
object PrettyProperties {
37+
def from(p: Properties): PrettyProperties = {
38+
val pp = new PrettyProperties()
39+
pp.putAll(p)
40+
pp
41+
}
42+
}
43+
44+
final lazy val currentEnv: PrettyProperties = {
45+
import scala.collection.JavaConverters._
46+
System.getenv().asScala.foldLeft(new PrettyProperties()) {
47+
case (props, (key, value)) => props.setProperty(key, value); props
48+
}
49+
}
50+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package profiling.integrations
2+
3+
import java.io.{InputStream, PrintStream}
4+
import java.nio.file.{Path, Paths}
5+
6+
import CommonOptions.PrettyProperties
7+
import caseapp.{CaseApp, CommandParser}
8+
import caseapp.util.{Implicit, AnnotationList}
9+
import caseapp.core.{ArgParser, DefaultBaseCommand, HListParser, Parser, Default}
10+
import shapeless.{Annotations, Coproduct, LabelledGeneric, Strict, Generic, HList}
11+
import caseapp.{Name, ValueDescription, HelpMessage, Hidden, Recurse}
12+
13+
import scala.util.Try
14+
15+
trait CachedImplicits {
16+
implicit val inputStreamRead: ArgParser[InputStream] =
17+
ArgParser.instance[InputStream]("stdin")(_ => Right(System.in))
18+
implicit val printStreamRead: ArgParser[PrintStream] =
19+
ArgParser.instance[PrintStream]("stdout")(_ => Right(System.out))
20+
21+
implicit val pathParser: ArgParser[Path] = ArgParser.instance("A filepath parser") {
22+
case supposedPath: String =>
23+
val toPath = Try(Paths.get(supposedPath)).toEither
24+
toPath.left.map(t => s"The provided path ${supposedPath} is not valid: '${t.getMessage()}'.")
25+
}
26+
27+
implicit val propertiesParser: ArgParser[PrettyProperties] = {
28+
ArgParser.instance("A properties parser") {
29+
case whatever => Left("You cannot pass in properties through the command line.")
30+
}
31+
}
32+
33+
import shapeless.{HNil, CNil, :+:, ::}
34+
implicit val implicitHNil: Implicit[HNil] = Implicit.hnil
35+
implicit val implicitNone: Implicit[None.type] = Implicit.instance(None)
36+
implicit val implicitNoneCnil: Implicit[None.type :+: CNil] =
37+
Implicit.instance(Coproduct(None))
38+
39+
implicit val implicitOptionDefaultString: Implicit[Option[Default[String]]] =
40+
Implicit.instance(Some(caseapp.core.Defaults.string))
41+
42+
implicit val implicitOptionDefaultInt: Implicit[Option[Default[Int]]] =
43+
Implicit.instance(Some(caseapp.core.Defaults.int))
44+
45+
implicit val implicitOptionDefaultBoolean: Implicit[Option[Default[Boolean]]] =
46+
Implicit.instance(Some(caseapp.core.Defaults.boolean))
47+
48+
implicit val implicitDefaultBoolean: Implicit[Default[Boolean]] =
49+
Implicit.instance(caseapp.core.Defaults.boolean)
50+
51+
implicit val implicitOptionDefaultOptionPath: Implicit[Option[Default[Option[Path]]]] =
52+
Implicit.instance(None)
53+
54+
implicit val implicitOptionDefaultPrintStream: Implicit[Option[Default[PrintStream]]] =
55+
Implicit.instance(Some(Default.instance[PrintStream](System.out)))
56+
57+
implicit val implicitOptionDefaultInputStream: Implicit[Option[Default[InputStream]]] =
58+
Implicit.instance(Some(Default.instance[InputStream](System.in)))
59+
}
60+
61+
object Parsers extends CachedImplicits {
62+
63+
import shapeless.{the, HNil, ::}
64+
65+
implicit val labelledGenericCommonOptions: LabelledGeneric.Aux[CommonOptions, _] = LabelledGeneric.materializeProduct
66+
implicit val commonOptionsParser: Parser.Aux[CommonOptions, _] = Parser.generic
67+
implicit val labelledGenericCliOptions: LabelledGeneric.Aux[CliOptions, _] = LabelledGeneric.materializeProduct
68+
implicit val cliOptionsParser: Parser.Aux[CliOptions, _] = Parser.generic
69+
70+
implicit val strictAutocompleteParser: Parser.Aux[Commands.Autocomplete, _] = Parser.generic
71+
implicit val strictAboutParser: Parser.Aux[Commands.About, _] = Parser.generic
72+
implicit val strictBspParser: Parser.Aux[Commands.Bsp, _] = Parser.generic
73+
implicit val strictCleanParser: Parser.Aux[Commands.Clean, _] = Parser.generic
74+
implicit val strictCompileParser: Parser.Aux[Commands.Compile, _] = Parser.generic
75+
implicit val strictConfigureParser: Parser.Aux[Commands.Configure, _] = Parser.generic
76+
implicit val strictConsoleParser: Parser.Aux[Commands.Console, _] = Parser.generic
77+
implicit val strictHelpParser: Parser.Aux[Commands.Help, _] = Parser.generic
78+
implicit val strictProjectsParser: Parser.Aux[Commands.Projects, _] = Parser.generic
79+
implicit val strictRunParser: Parser.Aux[Commands.Run, _] = Parser.generic
80+
implicit val strictTestParser: Parser.Aux[Commands.Test, _] = Parser.generic
81+
82+
val BaseMessages: caseapp.core.Messages[DefaultBaseCommand] =
83+
caseapp.core.Messages[DefaultBaseCommand]
84+
val CommandsMessages: caseapp.core.CommandsMessages[Commands.RawCommand] =
85+
implicitly[caseapp.core.CommandsMessages[Commands.RawCommand]]
86+
val CommandsParser: CommandParser[Commands.RawCommand] =
87+
implicitly[caseapp.core.CommandParser[Commands.RawCommand]]
88+
}
89+
90+
object Main extends App {
91+
import Parsers._
92+
/* assert(CommandsParser != null)
93+
assert(CommandsMessages != null)*/
94+
println("Hello World!")
95+
}

integrations/src/main/scala/profiling/integrations/circe/WebsiteExample.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import io.circe._
1+
/*import io.circe._
22
import io.circe.generic.auto._
33
import io.circe.parser._
44
import io.circe.syntax._
@@ -16,4 +16,4 @@ object WebsiteExample extends App {
1616
import io.circe.generic.semiauto._
1717
implicit val fooDecoder: Decoder[Foo] = deriveDecoder[Foo]
1818
implicit val fooEncoder: Encoder[Foo] = deriveEncoder[Foo]*/
19-
}
19+
}*/

plugin/src/main/scala/ch/epfl/scala/PluginConfig.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ case class PluginConfig(
88
sourceRoot: Option[AbsolutePath],
99
printSearchIds: Set[Int],
1010
generateMacroFlamegraph: Boolean,
11+
printFailedMacroImplicits: Boolean,
1112
concreteTypeParamsInImplicits: Boolean
1213
)

plugin/src/main/scala/ch/epfl/scala/ProfilingPlugin.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ProfilingPlugin(val global: Global) extends Plugin {
3434
private final lazy val SourceRoot = "sourceroot"
3535
private final lazy val PrintSearchResult = "print-search-result"
3636
private final lazy val GenerateMacroFlamegraph = "generate-macro-flamegraph"
37+
private final lazy val PrintFailedMacroImplicits = "print-failed-implicit-macro-candidates"
3738
private final lazy val NoProfileDb = "no-profiledb"
3839
private final lazy val ShowConcreteImplicitTparams = "show-concrete-implicit-tparams"
3940
private final lazy val PrintSearchRegex = s"$PrintSearchResult:(.*)".r
@@ -59,6 +60,7 @@ class ProfilingPlugin(val global: Global) extends Plugin {
5960
findOption(SourceRoot, SourceRootRegex).map(AbsolutePath.apply),
6061
findSearchIds(findOption(PrintSearchResult, PrintSearchRegex)),
6162
super.options.contains(GenerateMacroFlamegraph),
63+
super.options.contains(PrintFailedMacroImplicits),
6264
super.options.contains(ShowConcreteImplicitTparams)
6365
)
6466

0 commit comments

Comments
 (0)