Instructions on how to contribute to Quill project.
The only dependency you need to build Quill locally is Docker. Instructions on how to install Docker can be found here.
If you are running Linux, you should also install Docker Compose separately, as described here.
After installing Docker and Docker Compose you have to setup databases:
docker-compose run --rm setup
After that you are ready to build and test the project. The following steps describes how to test the project with sbt built within docker image. If you would like to use your local sbt, please visit Building locally. This is highly recommended when running Docker on non-linux OS due to high IO overhead from running Docker in virtualized environments.
To build and test the project:
docker-compose run --rm sbt sbt test
The Scala.js targets are disabled by default, use sbt "project quill-with-js"
to enable them.
The CI build also sets this project quill-with-js
to force the Scala.js compilation.
If any file that creates a database schema was changed then you have to setup the databases again:
docker-compose down && docker-compose run --rm setup
If build/Dockerfile-sbt
, build/Dockerfile-setup
, docker-compose.yml
or any file used by them was changed then you have to rebuild docker images and to setup the databases again:
docker-compose down && docker-compose build && docker-compose run --rm setup
Run all tests:
docker-compose run --rm sbt sbt test
Run specific test:
docker-compose run --rm sbt sbt "test-only io.getquill.context.sql.SqlQuerySpec"
Run all tests in specific sub-project:
docker-compose run --rm sbt sbt "project quill-async" test
Run specific test in specific sub-project:
docker-compose run --rm sbt sbt "project quill-sqlJVM" "test-only io.getquill.context.sql.SqlQuerySpec"
- Run sbt in interactive mode with docker container ports mapped to the host:
docker-compose run --service-ports --rm sbt
- Attach debugger to port 15005 of your docker host. In IntelliJ IDEA you should create Remote Run/Debug Configuration, change it port to 15005.
- In sbt command line run tests with
test
or test specific spec by passing full name totest-only
:
> test-only io.getquill.context.sql.SqlQuerySpec
In order to contribute to the project, just do as follows:
- Fork the project
- Build it locally
- Code
- Compile (file will be formatted)
- Run the tests through
docker-compose run sbt sbt test
- If you made changes in *.md files, run
docker-compose run sbt sbt tut
to validate them - If everything is ok, commit and push to your fork
- Create a Pull Request, we'll be glad to review it
Scalariform is used as file formatting tool in this project. Every time you compile the project in sbt, file formatting will be triggered. To manually format the Quill codebase, run the following command:
sbt scalariformFormat test:scalariformFormat
As you are working on a branch fixing a Quill problem, there will likely be additional changes merged to the main Quill repo (i.e. getquill/quill) before you finish. The solution to this is to squash your commits, and then to replay them on top of the latest changes that have been made to the main Quill repo.
The steps to do that are the following:
- Squash your commits via an interactive git rebase (see here).
- If you have not already, add an 'upstream' remote to your repository:
git remote add upstream '[email protected]:getquill/quill.git'
- Do a git Pull + Rebase from the upstream:
git pull --rebase upstream master
Once you have resolved any conflicts that may have arisen from the rebase, your branch will be capable of becoming a pull-request.
To restart your database service with database ports exposed to your host machine run:
docker-compose down && docker-compose run --rm --service-ports setup
After that we need to set some environment variables in order to run sbt
locally.
export CASSANDRA_HOST=127.0.0.1
export CASSANDRA_PORT=19042
export CASSANDRA_CONTACT_POINT_0=127.0.0.1:19042
export CASSANDRA_DC=datacenter1
export MYSQL_HOST=127.0.0.1
export MYSQL_PORT=13306
export MYSQL_PASSWORD=root
export POSTGRES_HOST=127.0.0.1
export POSTGRES_PORT=15432
export POSTGRES_PASSWORD=postgres
export SQL_SERVER_HOST=127.0.0.1
export SQL_SERVER_PORT=11433
export ORIENTDB_HOST=127.0.0.1
export ORIENTDB_PORT=12424
export ORACLE_HOST=127.0.0.1
export ORACLE_PORT=11521
Where 127.0.0.1
is address of local docker.
If you have non-local docker change it depending on your settings.
Finally, you can use sbt
locally.
To restart the database services, rebuild them, and start with locally explosed ports run:
docker-compose down && docker-compose build && docker-compose run --rm --service-ports setup
Note: Make sure you have exposed all the ports as mentioned above.
Intellij has a comprehensive debugger that also works with macros which is very helpful when working on Quill. There are two ways to debug Quill macros using Intellij. The first way is to launch SBT in debug mode and use Intellij to remote debug it. The second way is to launch a debug session from Intellij from the "Run/Debug Configurations" menu.
In order to use the debugger you need to run sbt manually so follow the instructions
here first. After this you need to edit build.sbt
to disable
forking when running tests, this is done by changing fork := true
to fork := false
for the tests you want to run.
After this you need to launch sbt with sbt -jvm-debug 5005
. Note that since the JVM is no longer forked in tests its
recommended to launch sbt with additional memory, i.e. sbt -jvm-debug 5005 -mem 4096
otherwise sbt may complain about
having memory issues.
Then in Intellij you need to
add a remote configuration. The default
parameters will work fine (note that we started sbt with the debug port 5005
which is also the default debug port
in Intellij). After you have added the configuration you should be able to start it to start debugging! Feel to free
to add breakpoints to step through the code.
Note that its possible to debug macros (you can even evaluate expressions while paused inside a macro), however you need to edit the macro being executed after every debug run to force the macro to recompile since macro invocations are cached on a file basis. You can easily do this just by adding new lines.
Firstly, you will need to build Quill with some additional dependencies that include the file scala.tools.nsc.Main
.
You can do this adding the argument -DdebugMacro=true
to the sbt launcher. You can do this in the Intellij SBT
menu:
In Intellij, go to Run -> Edit Configurations...
click on the Plus (i.e. +
) button (or Add New Configuration
)
and select Application
. Then enter the following settings:
Main Class: scala.tools.nsc.Main
VM Options: -Dscala.usejavacp=true
Program Arguments: -cp io.getquill.MySqlTest.scala /home/me/projects/quill/quill-sql/src/main/scala/io/getquill/MySqlTest.scala
Use classpath of module: quill-sql
Before launch:
Build, no error check (make sure to set this since you will frequently want to debug the macros even if the build fails)
NOTE In this example, our entry-point into Quill-macro-debugging is
MySqlTest.scala
. In our Intellij application configuration this file name is being explicitly specified.
If you wish to easily be able to macro-debug multiple entry-point files, an alternative method would be to use some Intellij variables to automatically pass whatever file is currently selected. You can do this by using the configuration:-cp $FileFQPackage$$FileName$ $FilePath$
Also, instead of specifying the path segment
/home/me/projects/quill/
you can use$ProjectFileDir$
Then create a file quill-sql/src/main/scala/io/getquill/MySqlTest.scala
that has the code you wish to debug.
For example:
object MySqlTest {
val ctx = new SqlMirrorContext(PostgresDialect, Literal)
import ctx._
case class Person(name: String, age:Int)
def main(args:Array[String]):Unit = {
val q = quote {
query[Person].filter(p => p.name == "Joe")
}
println( run(q).string )
}
}
Set a breakpoint anywhere in the Quill codebase and run this configuration from the top-right menu shortcut:
Some additional arguments you can add to your compiler's VM args provide insight into Quill's compilation:
-DdebugMacro=true // Enables libraries needed to debug via an Intellij Application session (default=false)
-DexcludeTests=false // Excludes testing code from being build. Useful during development times that require rapid iteration
-Dquill.macro.log.pretty=true // Pretty print the SQL Queries that Quill produces (default=false)
-Dquill.macro.log=true // Enable/Disable priting of the SQL Queries Quill generates during compile-time (default=true)
-Dquill.trace.enabled=true // Global switch that Enables/Disables printing of Quill ASTs during compilation (default=false)
-Dquill.trace.color=true // Print Quill ASTs in color (default=false)
-Dquill.trace.opinion=false // Print the parts of Quill ASTs not directly used in the main transformation phases (called Opinions). (default=false)
-Dquill.trace.ast.simple=true // Print the raw Quill ASTs elements or a more compact view of the AST code (think `show` vs `showRaw` in Scala macros). (default=true)
-Dquill.trace.types=sql,standard,alias,norm // What parts of the Quill transformations to print during compilation?
In Intellij, add them in the SBT settings if your are compiling using SBT:
Since typically the Quill Query Normalizations and Tokenizations run during compile-time,
break-point debugging them requires one of the above two steps. However, you can 'trick'
Quill into running these during compile time by using the .dynamically
keyword.
For example say you have a query that looks like this, and you would like to see
what happens to the AST in the ExpandDistinct
phase.
val q = quote { query[Person].distinct.map(p => p.name) }
val output = run(q)
Change the quotation to be dynamic:
val output = run(q.dynamically)
Since the query is now dynamic, you can set a breakpoint in the ExpandDistinct class and it will be hit during the runtime of your application.
In situations where the .dynamically keyword is not available, e.g. when the quoted construct is not a query, add a type annotation to the variable holding the quotation and this will effectively cause the same behavior.
val q = quote { query[Person].distinct.map(p => p.name) }
val output = run(q)
Add a type annotation which will cause it to be dynamic
val q: Quoted[Query[String]] = quote { query[Person].distinct.map(p => p.name) }
Same as before, since the query is now dynamic, you can set a breakpoint in the ExpandDistinct class and it will be hit during the runtime of your application.