|
2 | 2 |
|
3 | 3 | Visit https://lsif.dev/ to learn about LSIF. |
4 | 4 |
|
5 | | -Please note, lsif-java is currently undergoing a large-scale rewrite. Progress can be followed at [tree/nsc/comsunsource](https://github.com/sourcegraph/lsif-java/tree/nsc/comsunsource). |
6 | | - |
7 | | -## Installation |
8 | | - |
9 | | -- Java 1.8 or higher |
10 | | -- Maven |
11 | | - |
12 | | -### macOS ### |
13 | | - |
14 | | -We recommend using brew to install dependancies on mac OS, you can install brew using the following command: |
15 | | -``` |
16 | | -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" |
17 | | -``` |
18 | | - |
19 | | -If you already have brew installed, you'll need to make sure its up to date: |
20 | | -``` |
21 | | -brew update && brew upgrade brew-cask && brew cleanup && brew cask cleanup |
22 | | -``` |
23 | | - |
24 | | -Install Java: |
25 | | -``` |
26 | | -brew tap homebrew/cask-versions |
27 | | -brew update |
28 | | -brew cask install homebrew/cask-versions/adoptopenjdk8 |
29 | | -``` |
30 | | - |
31 | | -Install Maven: |
32 | | -``` |
33 | | -brew install maven |
34 | | -``` |
35 | | - |
36 | | -### Ubuntu 18.04 ### |
37 | | - |
38 | | -``` |
39 | | -apt-get update && apt-get install -y git default-jdk maven |
40 | | -``` |
41 | | - |
42 | | -## Build the LSIF indexer |
43 | | - |
44 | | -``` |
45 | | -git clone https://github.com/sourcegraph/lsif-java |
46 | | -cd lsif-java |
47 | | -./gradlew installDist |
48 | | -``` |
49 | | - |
50 | | -## Generating an LSIF dump |
51 | | - |
52 | | -### **Step 1** |
53 | | - |
54 | | -Ensure you have a `pom.xml` (Maven projects already have one): |
55 | | - |
56 | | -For single-project Gradle projects **_(experimental)_**: |
57 | | - |
58 | | -1. Add [`maven-publish`](https://docs.gradle.org/current/userguide/publishing_maven.html) to your `plugins` in `build.gradle` |
59 | | -``` |
60 | | -plugins { |
61 | | - id 'maven-publish' |
62 | | -} |
63 | | -``` |
64 | | -2. Specify a publication: |
65 | | - |
66 | | -**_with default sourceSets:_** |
67 | | - |
68 | | -```groovy |
69 | | -publishing { |
70 | | - model { |
71 | | - tasks.generatePomFileForSourcegraphPublication { |
72 | | - destination = file("$projectDir/pom.xml") |
73 | | - } |
74 | | - } |
75 | | - publications { |
76 | | - sourcegraph(MavenPublication) { |
77 | | - from components.java |
78 | | - } |
79 | | - } |
80 | | -} |
81 | | -``` |
82 | | - |
83 | | -**_with non-default sourceSets:_** |
84 | | - |
85 | | -```groovy |
86 | | -publishing { |
87 | | - model { |
88 | | - tasks.generatePomFileForSourcegraphPublication { |
89 | | - destination = file("$projectDir/pom.xml") |
90 | | - } |
91 | | - } |
92 | | - publications { |
93 | | - sourcegraph(MavenPublication) { |
94 | | - from components.java |
95 | | - def sourceSets = project.sourceSets.main.java.srcDirs |
96 | | - pom.withXml { |
97 | | - def node = asNode(); |
98 | | - if (node.get("build").size() == 0) { |
99 | | - node.appendNode("build").with { |
100 | | - if (sourceSets.size() > 0) { |
101 | | - appendNode("sourceDirectory", sourceSets.first()) |
102 | | - } |
103 | | - } |
104 | | - } |
105 | | - } |
106 | | - } |
107 | | - } |
108 | | -} |
109 | | -``` |
110 | | - |
111 | | -3. Run `./gradlew generatePomFileForSourcegraphPublication` |
112 | | -4. You should now see `pom.xml` at the top of your project directory |
113 | | - |
114 | | -For multi-project Gradle projects **_(experimental)_**: |
115 | | - |
116 | | -1. Modify your `allprojects` block in the root `build.gradle` to include the following: |
117 | | - |
118 | | -```groovy |
119 | | -allprojects { |
120 | | - // ... |
121 | | -
|
122 | | - apply plugin: "maven-publish" |
123 | | -
|
124 | | - afterEvaluate { project -> |
125 | | - if (!new File(project.projectDir.toString()+"/build.gradle").exists()) return |
126 | | - publishing { |
127 | | - model { |
128 | | - tasks.generatePomFileForSourcegraphPublication { |
129 | | - destination = file("$projectDir/pom.xml") |
130 | | - } |
131 | | - } |
132 | | - publications { |
133 | | - sourcegraph(MavenPublication) { |
134 | | - def projectDirStr = project.projectDir.toString() |
135 | | - def subprojectSet = project.subprojects |
136 | | - def sourceSetsSet = [] |
137 | | - if (project.hasProperty("sourceSets.main.java.srcDirs")) { |
138 | | - sourceSetsSet = project.sourceSets.main.java.srcDirs |
139 | | - } |
140 | | -
|
141 | | - if (!(new File(projectDirStr+"/build.gradle").exists())) return |
142 | | -
|
143 | | - def javaApplied = components.collect{it.getName()}.contains("java") |
144 | | - if (javaApplied) from components.java |
145 | | -
|
146 | | - pom.withXml { |
147 | | - def node = asNode(); |
148 | | - if (node.get("build").size() == 0 && javaApplied) { |
149 | | - node.appendNode("build").with { |
150 | | - if (sourceSetsSet.size() > 0) { |
151 | | - appendNode("sourceDirectory", sourceSetsSet.first()) |
152 | | - } else { |
153 | | - def dirpath = "${projectDirStr}/src/main/java" |
154 | | - if (new File(dirpath).exists()) appendNode("sourceDirectory", dirpath) |
155 | | - } |
156 | | - } |
157 | | - } |
158 | | -
|
159 | | - if (subprojectSet.size() > 0) { |
160 | | - node.appendNode("modules").with { |
161 | | - for(Project p : subprojectSet) { |
162 | | - if(new File(p.projectDir.toString()+"/build.gradle").exists()) { |
163 | | - def path = p.projectDir.toString().substring(projectDirStr.size()+1) |
164 | | - appendNode("module", path) |
165 | | - } |
166 | | - } |
167 | | - } |
168 | | - } |
169 | | - } |
170 | | - } |
171 | | - } |
172 | | - } |
173 | | - } |
174 | | -} |
175 | | -``` |
176 | | - |
177 | | -2. Run `./gradlew generatePomFileForSourcegraphPublication` |
178 | | -3. You should now see a `pom.xml` file for each `build.gradle` throughout the project |
179 | | - |
180 | | -### **Step 2** |
181 | | -Generate an LSIF dump: |
182 | | - |
183 | | -``` |
184 | | -<absolute path to lsif-java>/build/install/lsifjava/bin/lsifjava \ |
185 | | - -projectRoot <project directory> \ |
186 | | - -out dump.lsif |
187 | | -``` |
188 | | - |
189 | | -## Comparison to [Microsoft/lsif-java](https://github.com/Microsoft/lsif-java) |
190 | | - |
191 | | -- sourcegraph/lsif-java is ~10x faster |
192 | | -- sourcegraph/lsif-java supports cross-file hovers/definitions/references (Microsoft/lsif-java does not) |
193 | | -- sourcegraph/lsif-java uses [Spoon](https://github.com/INRIA/spoon), which is built on [eclipse.jdt.core](https://github.com/eclipse/eclipse.jdt.core) |
194 | | -- Microsoft/lsif-java uses [eclipse.jdt.ls](https://github.com/eclipse/eclipse.jdt.ls), which is also built on [eclipse.jdt.core](https://github.com/eclipse/eclipse.jdt.core) |
195 | | - |
196 | | -See https://github.com/microsoft/lsif-java/issues/61 for the status of collaboration efforts. |
197 | | - |
198 | | -## Development |
199 | | - |
200 | | -``` |
201 | | -./dev |
202 | | -``` |
| 5 | +## Usage |
| 6 | + |
| 7 | +⚠️ This project is under development so there is nothing to try out at the |
| 8 | +moment. |
| 9 | + |
| 10 | +### Supported tools and versions |
| 11 | + |
| 12 | +Currently, only Java 8 with the build tool sbt is supported. We hope to increase |
| 13 | +compatibility with more Java language versions and build tools as the project |
| 14 | +evolves. |
| 15 | + |
| 16 | +| Language version | Support | |
| 17 | +| ---------------- | ------- | |
| 18 | +| Java 7 | ❌ | |
| 19 | +| Java 8 | ✅ | |
| 20 | +| Java 11+ | ❌ | |
| 21 | + |
| 22 | +| Build tool | Support | |
| 23 | +| ---------- | ------- | |
| 24 | +| Gradle | ❌ | |
| 25 | +| Maven | ❌ | |
| 26 | +| Bazel | ❌ | |
| 27 | +| sbt | ✅ | |
| 28 | + |
| 29 | +## Overview |
| 30 | + |
| 31 | +This project is implemented as a |
| 32 | +[Java compiler plugin](https://docs.oracle.com/en/java/javase/11/docs/api/jdk.compiler/com/sun/source/util/Plugin.html) |
| 33 | +that generates one |
| 34 | +[SemanticDB](https://scalameta.org/docs/semanticdb/specification.html) file for |
| 35 | +every `*.java` source file. After compilation completes, the SemanticDB files |
| 36 | +are processed to produce LSIF. |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +### Why Java compiler plugin? |
| 41 | + |
| 42 | +There are several benefits to implementing lsif-java as a compiler plugin: |
| 43 | + |
| 44 | +- **Simple installation**: compiler plugins are enabled with the `-Xplugin` |
| 45 | + compiler option. All Java build tools support a way to customize compiler |
| 46 | + options, simplifying installation. |
| 47 | +- **Language fidelity**: by using the Java compiler to produce semantic |
| 48 | + information, we ensure that the produced LSIF data is accurate even as new |
| 49 | + Java language versions with new language features are released. |
| 50 | +- **Environment fidelity**: by hooking into the compilation process of the build |
| 51 | + tool, we minimize the risk of diverging from the CI build environment such as |
| 52 | + installed system dependencies, custom compiler options and custom annotation |
| 53 | + processors. |
| 54 | + |
| 55 | +### Why SemanticDB? |
| 56 | + |
| 57 | +SemanticDB is Protobuf schema for information about symbols and types in Java |
| 58 | +programs, Scala programs and other languages. There are several benefits to |
| 59 | +using SemanticDB as an intermediary representation for LSIF: |
| 60 | + |
| 61 | +- **Simplicity**: It's easy to translate a single Java source file into a single |
| 62 | + SemanticDB file inside a compiler plugin. It's more complicated to produce |
| 63 | + LSIF because compiler plugins does not have access to a project-wide context, |
| 64 | + which is necessary to produce accurate definitions and hovers in multi-module |
| 65 | + projects with external library dependencies. |
| 66 | +- **Performance**: SemanticDB is fast to write and read. The compiler adds low |
| 67 | + overhead on compilation and the final conversion from SemanticDB to LSIF can |
| 68 | + be safely parallelized. |
| 69 | +- **Cross-language**: SemanticDB has a |
| 70 | + [spec](https://scalameta.org/docs/semanticdb/specification.html) for Java and |
| 71 | + Scala enabling cross-language navigation in hybrid Java/Scala codebases. |
| 72 | +- **Cross-repository**: Compiler plugins have access to both source code and the |
| 73 | + classpath (compiled bytecode of upstream dependencies). SemanticDB has been |
| 74 | + designed so that it's also possible to generate spec-compliant symbols from |
| 75 | + the classpath alone (no source code) and from the syntax tree of an individual |
| 76 | + source file (no classpath). This flexibility allows the |
| 77 | + [Metals](https://scalameta.org/metals/) language server to index codebases |
| 78 | + from a variety of different inputs, and will be helpful for lsif-java in the |
| 79 | + future to unblock cross-repository navigation. |
| 80 | + |
| 81 | +## Contributing |
| 82 | + |
| 83 | +The following sections provide tips on how to contribute to this codebase. |
| 84 | + |
| 85 | +### Project structure |
| 86 | + |
| 87 | +These are the main components of the project. |
| 88 | + |
| 89 | +- `semanticdb-javac/src/main/java`: the Java compiler plugin that creates |
| 90 | + SemanticDB files. |
| 91 | +- `tests/minimized`: minimized Java source files that reproduce interesting test |
| 92 | + cases. |
| 93 | +- `tests/unit`: fast running unit tests that are helpful for local edit-and-test |
| 94 | + workflows. |
| 95 | +- `tests/snapshots`: slow running |
| 96 | + ["snapshot tests"](https://jestjs.io/docs/en/snapshot-testing) that index a |
| 97 | + corpus of published Java libraries. |
| 98 | +- `build.sbt`: the sbt build definition. |
| 99 | +- `project/plugins.sbt`: plugins for the sbt build. |
| 100 | + |
| 101 | +### Helpful commands |
| 102 | + |
| 103 | +| Command | Where | Description | |
| 104 | +| ------------------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------- | |
| 105 | +| `./sbt` | terminal | Start interactive sbt shell with Java 8. Takes a while to load on the first run. | |
| 106 | +| `unit/test` | sbt | Run fast unit tests. | |
| 107 | +| `~unit/test` | sbt | Start watch mode to run tests on file save, good for local edit-and-test workflows. | |
| 108 | +| `snapshot/testOnly tests.MinimizedSnapshotSuite` | sbt | Runs fast snapshot tests. Indexes a small set of files under `tests/minimized`. | |
| 109 | +| `snapshot/testOnly tests.MinimizedSnapshotSuite -- *InnerClasses*` | sbt | Runs only individual tests cases matching the name "InnerClasses". | |
| 110 | +| `snapshot/testOnly tests.LibrarySnapshotSuite` | sbt | Runs slow snapshot tests. Indexes a corpus of external Java libraries. | |
| 111 | +| `snapshot/test` | sbt | Runs all snapshot tests. | |
| 112 | +| `snapshot/run` | sbt | Update snapshot tests. Use this command after you have fixed a bug. | |
| 113 | +| `fixAll` | sbt | Run Scalafmt, Scalafix and Javafmt on all sources. Run this before opening a PR. | |
| 114 | + |
| 115 | +### Import the project into IntelliJ |
| 116 | + |
| 117 | +It's recommended to use IntelliJ when editing code in this codebase. |
| 118 | + |
| 119 | +First, install the |
| 120 | +[IntelliJ Community Edition](https://www.jetbrains.com/idea/download/). The |
| 121 | +community edition is |
| 122 | +[open source](https://github.com/JetBrains/intellij-community) and free to use. |
| 123 | + |
| 124 | +Next, install the IntelliJ Scala plugin. |
| 125 | + |
| 126 | +Finally, run "File > Project From Existing Sources" to import the sbt build into |
| 127 | +IntelliJ. Select the "sbt" option if it asks you to choose between |
| 128 | +sbt/BSP/Bloop. |
| 129 | + |
| 130 | +It's best to run tests from the sbt shell, not from the IntelliJ UI. |
| 131 | + |
| 132 | +### Don't use VS Code/Vim/Sublime Text/Emacs |
| 133 | + |
| 134 | +If you want to use completions and precise code navigation, it's not recommended |
| 135 | +to use other editors than IntelliJ. IntelliJ is the only IDE that properly |
| 136 | +supports hybrid Java/Scala codebases at the moment, although that may change |
| 137 | +soon thanks to lsif-java :) |
| 138 | + |
| 139 | +### Tests are written in Scala |
| 140 | + |
| 141 | +This codebases uses the Scala library [MUnit](https://scalameta.org/munit/) to |
| 142 | +write tests because: |
| 143 | + |
| 144 | +- MUnit has built-in assertions that print readable multiline diffs in color. |
| 145 | +- MUnit makes it easy to implement |
| 146 | + [snapshot testing](https://jestjs.io/docs/en/snapshot-testing), which is a |
| 147 | + testing technique that's heavily used in this codebase. |
| 148 | +- Multiline literal strings in Scala make it easy to write unit tests for source |
| 149 | + code (which is always multiline). Modern versions of Java support multiline |
| 150 | + string literals, but they're not supported in Java 8, which is supported by |
| 151 | + lsif-java. |
0 commit comments